mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dd71a004e2 | ||
|
45091fbd72 | ||
|
988d5aebfa | ||
|
8c3671fae6 | ||
|
2a12af481a | ||
|
7817e64695 | ||
|
ebbaa39098 | ||
|
d3dcbbd4f2 | ||
|
3045995bbc | ||
|
1b4233d6ad | ||
|
34aa8fe8b4 | ||
|
baa97e9ff1 | ||
|
03afc4a353 | ||
|
ef4ebeb7ee | ||
|
80d8851e62 | ||
|
ead135f716 | ||
|
49dc61a92c | ||
|
af0309cc40 | ||
|
4ed7421b1c | ||
|
3058354dba | ||
|
20a8c5d240 | ||
|
0749a34342 | ||
|
e595eed57d | ||
|
a7576f0b6c | ||
|
657e5dda5d | ||
|
8fb6a2b24b | ||
|
53cac302d8 | ||
|
2c6c014dd8 | ||
|
a7071f580e | ||
|
de5327a048 | ||
|
7ab94c7bcb | ||
|
0f80fc5e86 | ||
|
d169f30e5c | ||
|
7a1842cba6 | ||
|
e4e9d7a55f | ||
|
ccbef6ce6c | ||
|
7461a1ba3a | ||
|
bb55e3036e | ||
|
bbf2cca198 | ||
|
8ce034d124 | ||
|
4030698007 | ||
|
66fea5c9f1 | ||
|
285574eeaa | ||
|
55cfb057e0 | ||
|
b4c538cff7 | ||
|
5a626fef9f | ||
|
2bcd8ba8f3 | ||
|
2dacc776bd | ||
|
a91d687a1a | ||
|
0d3882d43e | ||
|
72ad601328 | ||
|
2c6fd804f6 | ||
|
c590444b7d | ||
|
4805af7915 | ||
|
171b20a80d | ||
|
d00ae923ab | ||
|
d8ab5dc5c6 | ||
|
292af5d4f4 | ||
|
3d6b31b115 | ||
|
9e523073aa | ||
|
32d8e473fb | ||
|
5cd25df60c | ||
|
52c4816c9c | ||
|
6f6b05ffcb | ||
|
6dd5854b7a | ||
|
83425df0b6 | ||
|
82b09eaa8e | ||
|
ec5b1aadab | ||
|
f786510ba2 | ||
|
f006884e57 | ||
|
c0a005b3cd | ||
|
0c91893643 | ||
|
796da9e0f6 | ||
|
5d539be071 | ||
|
1df138d876 | ||
|
71b0140003 | ||
|
a33adf365d | ||
|
8d82b05071 | ||
|
930e3b4200 | ||
|
2543f2c486 | ||
|
d919817c35 | ||
|
6bbb596968 | ||
|
8aec1974bb | ||
|
b3ad9ec699 | ||
|
87410442b6 |
44
CONTRIBUTORS
44
CONTRIBUTORS
@ -2210,6 +2210,8 @@ Marko M
|
||||
for avoiding unnecessary processing in cDvbSubtitleConverter::FinishPage() if there
|
||||
are no areas
|
||||
for avoiding a zero sized array in cDevice::GetDevice()
|
||||
for reporting a crash when deleting a recording that is currently being edited, and
|
||||
then immediately deleting the edited version, too
|
||||
|
||||
Patrick Rother <krd-vdr@gulu.net>
|
||||
for reporting a bug in defining timers that only differ in the day of week
|
||||
@ -2574,6 +2576,26 @@ Markus Ehrnsperger <markus.ehrnsperger@googlemail.com>
|
||||
for suggesting to enable unused devices to be put into a power save mode
|
||||
for a patch that was used to implement power save mode for cDvbDevice
|
||||
for suggesting to add 'lnbPowerTurnedOn = false' to cDvbTuner::ProvidesFrontend()
|
||||
for reporting a crash in strreplace() for multiple replacements with strings of
|
||||
different lengths
|
||||
for suggesting to add the lines from 'Fixed a timeout in cDvbDevice while tuning after
|
||||
the frontend has been reopened' to cDvbTuner::ProvidesFrontend()
|
||||
for improving the error message when closing a frontend
|
||||
for reporting a problem in cSchedule::Sort(), in case hasRunning was true, but there
|
||||
was no event with RunningStatus() >= SI::RunningStatusPausing
|
||||
for reporting problem with duplicate events if they are moved to a lower table ID and
|
||||
at the same time get a new event ID
|
||||
for reporting a bug in handling negative values in cSource::Position() on
|
||||
systems where 'int' is 64 bit
|
||||
for suggesting a fix for expiring of one-time VPS timers in case there is more than
|
||||
one event with the same VPS time
|
||||
for fixing handling margins for timers that are not VPS controlled and not spawned
|
||||
for implementing cStatus::OsdItem2() with the information whether the item is selectable
|
||||
for reporting an improper call of cStatus::OsdCurrentItem() before cStatus::OsdItem2()
|
||||
for fixing unnecessary calls to DisplayCurrent() for editable menu items
|
||||
for implementing cStatus::OsdCurrentItem2() with the index of the current item
|
||||
for adding missing calls to cStatus::MsgOsdStatusMessage() and implementing
|
||||
cStatus::OsdStatusMessage2() with the type of the message
|
||||
|
||||
Werner Färber <w.faerber@gmx.de>
|
||||
for reporting a bug in handling the cPluginManager::Active() result when pressing
|
||||
@ -2832,6 +2854,12 @@ Winfried K
|
||||
for adding missing rounding when dividing frequencies in processing the NIT
|
||||
for suggesting to add note about not messing with event ids in EPG handlers
|
||||
for adding a missing initialization of cChannel::nameSourceMode
|
||||
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
|
||||
for reporting a compiler warning with gcc 14.1.0
|
||||
for suggesting to remove defining DEPRECATED_* macros with value 0, because this
|
||||
is the preprocessor's default
|
||||
for suggesting a fix for handling negative values in cSource::Position() on
|
||||
systems where 'int' is 64 bit
|
||||
|
||||
Hans-Werner Hilse <hilse@web.de>
|
||||
for adding the command line option --userdump to enable core dumps in case VDR
|
||||
@ -3002,6 +3030,7 @@ Manuel Reimer <Manuel.Reimer@gmx.de>
|
||||
number is given
|
||||
for reporting that LSTE doesn't work after PUTE in case a channel didn't already
|
||||
have EPG data
|
||||
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
|
||||
|
||||
Rene van den Braken <rene@vandenbraken.name>
|
||||
for reporting a bug in writing the PCR pid into the PMT in
|
||||
@ -3055,6 +3084,7 @@ Lars Hanisch <dvb@flensrocker.de>
|
||||
for fixing a typo in the description of cTimers::GetTimersRead()
|
||||
for suggesting to use dynamic buffering in handling CA descriptors to avoid a
|
||||
possible buffer overflow
|
||||
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
|
||||
|
||||
Alex Lasnier <alex@fepg.org>
|
||||
for adding tuning support for ATSC devices
|
||||
@ -3344,6 +3374,16 @@ Mike Hay <mike.hay@linenshorts.com>
|
||||
Stefan Hofmann <stefan.hofmann@t-online.de>
|
||||
for suggesting to implement support for remote controls that only have a combined
|
||||
"Play/Pause" key instead of separate keys for "Play" and "Pause"
|
||||
for a fix for compilers that don't like non-constant format strings
|
||||
for suggesting to implement jumping between errors while replaying a recording
|
||||
for adding vdrrootdir and incdir to vdr.pc
|
||||
for fixing some typos in the translation files
|
||||
for adding 1 to Utf8BufSize() for worst case
|
||||
for adding a header to the backtrace
|
||||
for adding parameter checks to strn0cpy()
|
||||
for making the info files of recordings only be re-read if they have been modified
|
||||
for reporting missing setting the file name of the info file after renaming a recording
|
||||
for adding '~' to the list of delimiters in cTextWrapper
|
||||
|
||||
Stefan Blochberger <Stefan.Blochberger@gmx.de>
|
||||
for suggesting to automatically display the progress display whenever replay of a
|
||||
@ -3412,6 +3452,8 @@ Matthias Senzel <matthias.senzel@t-online.de>
|
||||
for fixing height calculation in progress display
|
||||
for fixing an unnecessary double display of the current menu item in page up/down
|
||||
for fixing an unnecessary double display of menu items in the Recordings menu
|
||||
for reporting a bug in handling cSkinDisplayMenu::GetTextAreaFont()
|
||||
for reporting characters being cut off while editing in the LCARS skin
|
||||
|
||||
Marek Nazarko <mnazarko@gmail.com>
|
||||
for translating OSD texts to the Polish language
|
||||
@ -3774,3 +3816,5 @@ Jose Angel <joseangelpp@gmail.com>
|
||||
|
||||
Andreas Baierl <post@andreasbaierl.de>
|
||||
for implementing scaling images
|
||||
for reporting a problem in the progress display when switching from "pause" to
|
||||
"slow back"
|
||||
|
150
HISTORY
150
HISTORY
@ -9939,3 +9939,153 @@ Video Disk Recorder Revision History
|
||||
Ehrnsperger).
|
||||
- Added 'lnbPowerTurnedOn = false' to cDvbTuner::ProvidesFrontend() (suggested by
|
||||
Markus Ehrnsperger).
|
||||
|
||||
2024-07-15: Version 2.6.9
|
||||
|
||||
- Fixed a crash in strreplace() for multiple replacements with strings of different
|
||||
lengths (reported by Markus Ehrnsperger).
|
||||
- Fixed handling of cSkinDisplayMenu::GetTextAreaFont() (reported by Matthias Senzel).
|
||||
- Fixed a timeout in cDvbDevice while tuning after the frontend has been reopened.
|
||||
- Fixed setting the editable width in the LCARS skin (reported by Matthias Senzel).
|
||||
- Fixed restarting the EPG scan and keeping the frequency of calls to
|
||||
Device->SetPowerSaveIfUnused() low.
|
||||
- Added the lines from 'Fixed a timeout in cDvbDevice while tuning after the frontend
|
||||
has been reopened' to cDvbTuner::ProvidesFrontend() (suggested by Markus Ehrnsperger).
|
||||
|
||||
2024-09-09: Version 2.7.1
|
||||
|
||||
- Removed deprecated function cDevice::SetCurrentChannel(const cChannel *Channel).
|
||||
- Removed deprecated function cSkinDisplayMenu::SetItemEvent(const cEvent *Event, int Index,
|
||||
bool Current, bool Selectable, const cChannel *Channel, bool WithDate, eTimerMatch TimerMatch).
|
||||
- Removed deprecated functions from cFile.
|
||||
- The default for DEPRECATED_SCHEDULE_GET_EVENT has been set to 0, which means that
|
||||
the function GetEvent(tEventID EventID, time_t StartTime = 0) is no longer available.
|
||||
You can add 'DEPRECATED_SCHEDULE_GET_EVENT=1' when compiling in order to restore this
|
||||
functionality. However, it is recommended to use GetEventById() and GetEventByTime()
|
||||
instead.
|
||||
- The default for DEPRECATED_SECTIONSYNCER_SYNC_REPEAT has been set to 0, which means that
|
||||
the functions Repeat() and Sync() are no longer available.
|
||||
You can add 'DEPRECATED_SECTIONSYNCER_SYNC_REPEAT=1' when compiling in order to restore this
|
||||
functionality. However, it is recommended to use Check() and Processed() instead.
|
||||
- The default for DEPRECATED_CCONTROL has been set to 0, which means that
|
||||
the function Control(bool Hidden) is no longer available.
|
||||
You can add 'DEPRECATED_CCONTROL=1' when compiling in order to restore this
|
||||
functionality. However, it is recommended to use Control(cMutexLock &MutexLock, bool Hidden)
|
||||
instead.
|
||||
- Changed the error message when trying to attach a player to a primary device without
|
||||
an MPEG decoder.
|
||||
- The new SVDRP command 'AUDI' can be used to list the currently available audio tracks
|
||||
and select one of them.
|
||||
- Fixed a crash when deleting a recording that is currently being edited, and then
|
||||
immediately deleting the edited version, too (reported by Marko Mäkelä).
|
||||
- The '.update' file in the video directory is now created if it doesn't already exist.
|
||||
- Improved the error message when closing a frontend (thanks to Markus Ehrnsperger).
|
||||
- There will be no more distinction between "developer" and "stable" versions regarding
|
||||
version numbering. Version numbers are simply counted upwards, with each of the three
|
||||
parts ("version", "major", "minor") always being a single digit, and '0' being skipped.
|
||||
|
||||
2024-09-27: Version 2.7.2
|
||||
|
||||
- Fix for compilers that don't like non-constant format strings (thanks to Stefan Hofmann).
|
||||
- Deprecated code is now marked with [[deprecated]] to issue a compile time warning when
|
||||
used.
|
||||
- Made APIVERSION independent from VDRVERSION to avoid irritation in case only
|
||||
VDRVERSION is incremented (originally suggested by Winfried Köhler, with contributions
|
||||
from Lars Hanisch and Manuel Reimer).
|
||||
APIVERSNUM is now 30003.
|
||||
- Fixed a problem in cSchedule::Sort(), in case hasRunning was true, but there was no event
|
||||
with RunningStatus() >= SI::RunningStatusPausing (reported by Markus Ehrnsperger).
|
||||
- Silenced a compiler warning with gcc 14.1.0 (reported by Winfried Köhler).
|
||||
- Updated the Italian OSD texts (thanks to Diego Pierotto).
|
||||
- Moved error checking from recorder.c to remux.c.
|
||||
- The number of errors in a recording now represents the number of broken frames.
|
||||
- Now distinguishing between frames with errors and completely missing frames.
|
||||
- Recording errors are now marked in the index file.
|
||||
- Fixed description of cSkinDisplayReplay::SetRecording().
|
||||
- Errors are now shown as diamond shaped markers in the replay progress display of the
|
||||
default skins. Plugin authors can switch to the new constructor of cProgressBar to
|
||||
benefit from this feature. Of course this only works with recordings that have error
|
||||
information stored in their index file. If you have existing recordings with errors
|
||||
and want them to be displayed in the progress bar, you can re-generate their index
|
||||
file, either by deleting the old index, or by running vdr with the --genindex option.
|
||||
APIVERSNUM is now 30004.
|
||||
- When the index file of a recording is regenerated, errors in the recording are now
|
||||
stored in the index file.
|
||||
- The info file of an edited recording now contains the number of errors in the edited
|
||||
version. Note that this applies only to recordings that have errors stored in their
|
||||
index file. If errors are not stored in the index file, the edited version will have
|
||||
its number of errors set to that of the original recording.
|
||||
APIVERSNUM is now 30005.
|
||||
- Fixed singular when displaying number of errors in the recording info.
|
||||
- Increased the bpp of cProgressBar to 4 to handle all different colors.
|
||||
- Fixed a problem with duplicate events if they are moved to a lower table ID and at the
|
||||
same time get a new event ID (reported by Markus Ehrnsperger).
|
||||
|
||||
2024-10-12: Version 2.7.3
|
||||
|
||||
- Removed defining DEPRECATED_* macros with value 0, because this is the preprocessor's
|
||||
default (suggested by Winfried Köhler).
|
||||
- Fixed error checking in case of large PTS discontinuities.
|
||||
- Fixed handling negative values in cSource::Position() on systems where 'int' is 64 bit
|
||||
(reported by Markus Ehrnsperger, fix suggested by Winfried Köhler).
|
||||
- Fixed expiring of one-time VPS timers in case there is more than one event with the
|
||||
same VPS time (suggested by Markus Ehrnsperger).
|
||||
- The Channel+/- keys can now be used to jump between errors while replaying a recording
|
||||
(suggested by Stefan Hofmann).
|
||||
- Added vdrrootdir and incdir to vdr.pc (thanks to Stefan Hofmann).
|
||||
|
||||
2025-02-26: Version 2.7.4
|
||||
|
||||
- Removed all DEPRECATED_* code.
|
||||
- Fixed error checking in case the fps value can't be determined by the frame parser.
|
||||
- Updated the Italian OSD texts (thanks to Diego Pierotto).
|
||||
- The VDR homepage is now accessible via HTTPS.
|
||||
- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
|
||||
- Fixed some typos in the translation files (thanks to Stefan Hofmann).
|
||||
- Added some missing locking.
|
||||
- TS packets with errors are now skipped when parsing for frames.
|
||||
- Fixed handling the fps value if it can't be determined from the video data.
|
||||
- Fixed accessing a timer's event schedule in case the event has been removed from the
|
||||
schedule.
|
||||
- Fixed a possible deadlock when canceling an editing process.
|
||||
- Checking for VPS control is now limited to local timers.
|
||||
- Added 1 to Utf8BufSize() for worst case (thanks to Stefan Hofmann).
|
||||
- Fixed handling margins for timers that are not VPS controlled and not spawned (thanks
|
||||
to Markus Ehrnsperger).
|
||||
- Added a header to the backtrace (thanks to Stefan Hofmann).
|
||||
- Added parameter checks to strn0cpy() (thanks to Stefan Hofmann). Same for Utf8Strn0Cpy().
|
||||
- The info files of recordings are now only re-read if they have been modified (thanks
|
||||
to Stefan Hofmann).
|
||||
- The new virtual function cStatus::OsdItem2() can be used to get the information whether
|
||||
a menu item is selectable (thanks to Markus Ehrnsperger). Plugins that implemented
|
||||
cStatus::OsdItem() will still work as before, because the default implementation of
|
||||
cStatus::OsdItem2() calls cStatus::OsdItem().
|
||||
APIVERSNUM is now 30006.
|
||||
- Fixed setting the file name of the info file after renaming a recording (reported by
|
||||
Stefan Hofmann).
|
||||
- Fixed an improper call of cStatus::OsdCurrentItem() before cStatus::OsdItem2() (reported
|
||||
by Markus Ehrnsperger).
|
||||
- Fixed an unnecessary redisplay of the menu when pressing a hotkey.
|
||||
- Fixed unnecessary calls to DisplayCurrent() for editable menu items (thanks to Markus
|
||||
Ehrnsperger).
|
||||
- The new virtual function cStatus::OsdCurrentItem2() can be used to get the index of the
|
||||
current menu item (thanks to Markus Ehrnsperger). Plugins that implemented
|
||||
cStatus::OsdCurrentItem() will still work as before, because the default implementation
|
||||
of cStatus::OsdCurrentItem2() calls cStatus::OsdCurrentItem().
|
||||
- Fixed unnecessary calls to cStatus::OsdCurrentItem2() when scrolling.
|
||||
- Added missing calls to cStatus::MsgOsdStatusMessage() and added the new virtual function
|
||||
cStatus::OsdStatusMessage2(), which can be used to get the type of the message (thanks
|
||||
to Markus Ehrnsperger). Plugins that implemented cStatus::OsdStatusMessage() will still
|
||||
work as before, because the default implementation of cStatus::OsdStatusMessage2() calls
|
||||
cStatus::OsdStatusMessage().
|
||||
- Adjusted PLUGINS.html to the new API version numbering introduced in version 2.7.2.
|
||||
- The function cPlugin::MainThreadHook() has been deprecated and may be removed in future
|
||||
versions. Use proper locking instead.
|
||||
- Fixed unnecessary redisplays of menus.
|
||||
- Added '~' to the list of delimiters in cTextWrapper (thanks to Stefan Hofmann).
|
||||
- Fixed progress display when switching from "pause" to "slow back" (reported by Andreas
|
||||
Baierl).
|
||||
- Fixed spurious fast frames when switching from "slow back" to "slow forward".
|
||||
- Fixed cPtsIndex::FindFrameNumber() to handle the case where Pts points to an I-frame.
|
||||
- Added missing locks to SetMenuItem() functions.
|
||||
- Revised locking in cMenuSchedule and cMenuWhatsOn.
|
||||
|
2
INSTALL
2
INSTALL
@ -1,7 +1,7 @@
|
||||
Installation of the Video Disk Recorder
|
||||
---------------------------------------
|
||||
|
||||
Version 2.6
|
||||
Version 2.7
|
||||
-----------
|
||||
|
||||
Compiling and running the program:
|
||||
|
6
MANUAL
6
MANUAL
@ -1,7 +1,7 @@
|
||||
Video Disk Recorder User's Manual
|
||||
---------------------------------
|
||||
|
||||
Version 2.6
|
||||
Version 2.7
|
||||
-----------
|
||||
|
||||
* Remote Control Keys
|
||||
@ -50,8 +50,8 @@ Version 2.6
|
||||
Next Next/previous channel group (in live tv mode)
|
||||
Prev or next/previous editing mark (in replay mode)
|
||||
|
||||
Channel+ channel up
|
||||
Channel- channel down
|
||||
Channel+ channel up (live view), next error (replay)
|
||||
Channel- channel down (live view), previous error (replay)
|
||||
PrevChannel previous channel
|
||||
|
||||
Power shutdown
|
||||
|
@ -6,7 +6,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# how to reach the author.
|
||||
#
|
||||
# $Id: Make.config.template 5.1 2022/11/26 13:37:06 kls Exp $
|
||||
# $Id: Make.config.template 5.2 2024/10/11 14:21:04 kls Exp $
|
||||
|
||||
### The C compiler and options:
|
||||
|
||||
@ -33,14 +33,15 @@ endif
|
||||
# Default directories (adjust as necessary or desired):
|
||||
|
||||
#PREFIX = /usr/local
|
||||
#BINDIR = $(PREFIX)/bin
|
||||
#INCDIR = $(PREFIX)/include
|
||||
#LIBDIR = $(PREFIX)/lib/vdr
|
||||
#LOCDIR = $(PREFIX)/share/locale
|
||||
#MANDIR = $(PREFIX)/share/man
|
||||
#PCDIR = $(PREFIX)/lib/pkgconfig
|
||||
#RESDIR = $(PREFIX)/share/vdr
|
||||
#DVBDIR = /usr/src/v4l-dvb/linux/include/uapi
|
||||
#VDRROOT = $(PREFIX)
|
||||
#BINDIR = $(VDRROOT)/bin
|
||||
#INCDIR = $(VDRROOT)/include
|
||||
#LIBDIR = $(VDRROOT)/lib
|
||||
#LOCDIR = $(VDRROOT)/locale
|
||||
#MANDIR = $(VDRROOT)/man
|
||||
#PCDIR = $(VDRROOT)/pkgconfig
|
||||
#RESDIR = $(VDRROOT)/share
|
||||
#DVBDIR = /usr/include
|
||||
|
||||
#VIDEODIR = /srv/vdr/video
|
||||
#CONFDIR = /var/lib/vdr
|
||||
|
26
Makefile
26
Makefile
@ -4,7 +4,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# how to reach the author.
|
||||
#
|
||||
# $Id: Makefile 5.2 2024/01/05 14:16:16 kls Exp $
|
||||
# $Id: Makefile 5.4 2024/10/21 19:01:16 kls Exp $
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
@ -46,13 +46,14 @@ ARGSDIR ?= /etc/vdr/conf.d
|
||||
CACHEDIR ?= /var/cache/vdr
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
INCDIR ?= $(PREFIX)/include
|
||||
LIBDIR ?= $(PREFIX)/lib/vdr
|
||||
LOCDIR ?= $(PREFIX)/share/locale
|
||||
MANDIR ?= $(PREFIX)/share/man
|
||||
PCDIR ?= $(PREFIX)/lib/pkgconfig
|
||||
RESDIR ?= $(PREFIX)/share/vdr
|
||||
VDRROOT ?= $(PREFIX)
|
||||
BINDIR ?= $(VDRROOT)/bin
|
||||
INCDIR ?= $(VDRROOT)/include
|
||||
LIBDIR ?= $(VDRROOT)/lib/vdr
|
||||
LOCDIR ?= $(VDRROOT)/share/locale
|
||||
MANDIR ?= $(VDRROOT)/share/man
|
||||
PCDIR ?= $(VDRROOT)/lib/pkgconfig
|
||||
RESDIR ?= $(VDRROOT)/share/vdr
|
||||
|
||||
# Source documentation
|
||||
|
||||
@ -169,7 +170,9 @@ make-libsi: # empty rule makes sure the sub-make for libsi is always called
|
||||
|
||||
.PHONY: vdr.pc
|
||||
vdr.pc:
|
||||
@echo "bindir=$(BINDIR)" > $@
|
||||
@echo "vdrrootdir=$(VDRROOT)" > $@
|
||||
@echo "bindir=$(BINDIR)" >> $@
|
||||
@echo "incdir=$(INCDIR)" >> $@
|
||||
@echo "mandir=$(MANDIR)" >> $@
|
||||
@echo "videodir=$(VIDEODIR)" >> $@
|
||||
@echo "configdir=$(CONFDIR)" >> $@
|
||||
@ -185,7 +188,7 @@ vdr.pc:
|
||||
@echo "" >> $@
|
||||
@echo "Name: VDR" >> $@
|
||||
@echo "Description: Video Disk Recorder" >> $@
|
||||
@echo "URL: http://www.tvdr.de/" >> $@
|
||||
@echo "URL: https://www.tvdr.de/" >> $@
|
||||
@echo "Version: $(VDRVERSION)" >> $@
|
||||
@echo "Cflags: \$${cflags}" >> $@
|
||||
|
||||
@ -320,6 +323,7 @@ install-doc:
|
||||
|
||||
install-plugins: plugins
|
||||
@-for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do\
|
||||
echo; echo "*** Plugin $$i:";\
|
||||
$(MAKE) --no-print-directory -C "$(PLUGINDIR)/src/$$i" VDRDIR=$(CWD) DESTDIR=$(DESTDIR) install;\
|
||||
done
|
||||
@if [ -d $(PLUGINDIR)/lib ] ; then\
|
||||
@ -356,7 +360,7 @@ srcdoc:
|
||||
clean:
|
||||
@$(MAKE) --no-print-directory -C $(LSIDIR) clean
|
||||
@-rm -f $(OBJS) $(DEPFILE) vdr vdr.pc core* *~
|
||||
@-rm -rf $(LOCALEDIR) $(PODIR)/*.mo $(PODIR)/*.pot
|
||||
@-rm -rf $(LOCALEDIR) $(PODIR)/*~ $(PODIR)/*.mo $(PODIR)/*.pot
|
||||
@-rm -rf include
|
||||
@-rm -rf srcdoc
|
||||
CLEAN: clean
|
||||
|
43
PLUGINS.html
43
PLUGINS.html
@ -31,11 +31,11 @@ modified {
|
||||
<div class="center">
|
||||
<h1>The VDR Plugin System</h1>
|
||||
|
||||
<b>Version 2.6</b>
|
||||
<b>Version 2.7</b>
|
||||
<p>
|
||||
Copyright © 2021 Klaus Schmidinger<br>
|
||||
<a href="mailto:vdr@tvdr.de">vdr@tvdr.de</a><br>
|
||||
<a href="http://www.tvdr.de">www.tvdr.de</a>
|
||||
<a href="https://www.tvdr.de">www.tvdr.de</a>
|
||||
</div>
|
||||
<p>
|
||||
VDR provides an easy to use plugin interface that allows additional functionality
|
||||
@ -74,7 +74,6 @@ structures and allows it to hook itself into specific areas to perform special a
|
||||
<li><a href="#Main menu entry">Main menu entry</a>
|
||||
<li><a href="#User interaction">User interaction</a>
|
||||
<li><a href="#Housekeeping">Housekeeping</a>
|
||||
<li><a href="#Main thread hook">Main thread hook</a>
|
||||
<li><a href="#Activity">Activity</a>
|
||||
<li><a href="#Wakeup">Wakeup</a>
|
||||
<li><a href="#Setup parameters">Setup parameters</a>
|
||||
@ -166,7 +165,7 @@ is used:
|
||||
VDR/PLUGINS/src
|
||||
VDR/PLUGINS/src/hello
|
||||
VDR/PLUGINS/lib
|
||||
VDR/PLUGINS/lib/libvdr-hello.so.1.1.0
|
||||
VDR/PLUGINS/lib/libvdr-hello.so.1
|
||||
</pre></td></tr></table><p>
|
||||
|
||||
The <tt>src</tt> directory contains one subdirectory for each plugin, which carries
|
||||
@ -185,7 +184,7 @@ The <tt>lib</tt> directory contains the dynamically loadable libraries of all
|
||||
available plugins. Note that the names of these files are created by concatenating
|
||||
<p>
|
||||
<table border=2>
|
||||
<tr><td align=center><b><tt>libvdr-</tt></b></td><td align=center><b><tt>hello</tt></b></td><td align=center><b><tt>.so.</tt></b></td><td align=center><b><tt>1.1.0</tt></b></td></tr>
|
||||
<tr><td align=center><b><tt>libvdr-</tt></b></td><td align=center><b><tt>hello</tt></b></td><td align=center><b><tt>.so.</tt></b></td><td align=center><b><tt>1</tt></b></td></tr>
|
||||
<tr><td align=center><small>VDR plugin<br>library prefix</small></td><td align=center><small>name of<br>the plugin</small></td><td align=center><small>shared object<br>indicator</small></td><td align=center><small>API version number<br>this plugin was<br>compiled for</small></td></tr>
|
||||
</table>
|
||||
<p>
|
||||
@ -196,6 +195,11 @@ the current VDR version. That way minor fixes to VDR, that don't require changes
|
||||
to the VDR header files, can be made without requiring all plugins to be
|
||||
recompiled.
|
||||
<p>
|
||||
While in earlier versions of VDR the API version number was closely related to the
|
||||
VDR version number, starting with VDR version 2.7.2 the API version number was changed
|
||||
from a dot separated, three part number to a single integer, completely unrelated to
|
||||
the VDR version. This was done to avoid confusion.
|
||||
<p>
|
||||
The plugin library files can be stored in any directory. If the default organization
|
||||
is not used, the path to the plugin directory has be be given to VDR through the
|
||||
<b><tt>-L</tt></b> option.
|
||||
@ -391,13 +395,7 @@ just like shown in the above example. This is a convention that allows the <tt>M
|
||||
to extract the version number when generating the file name for the distribution archive.
|
||||
<p>
|
||||
A new plugin project should start with version number <tt>0.0.1</tt> and should reach
|
||||
version <tt>1.0.0</tt> once it is completely operative and well tested. Following the
|
||||
Linux kernel version numbering scheme, versions with <i>even</i> release numbers
|
||||
(like <tt>1.0.x</tt>, <tt>1.2.x</tt>, <tt>1.4.x</tt>...) should be stable releases,
|
||||
while those with <i>odd</i> release numbers (like <tt>1.1.x</tt>, <tt>1.3.x</tt>,
|
||||
<tt>1.5.x</tt>...) are usually considered "under development". The three parts of
|
||||
a version number are not limited to single digits, so a version number of <tt>1.2.15</tt>
|
||||
would be acceptable.
|
||||
version <tt>1.0.0</tt> once it is completely operative and well tested.
|
||||
|
||||
<hr><h2><a name="Description">Description</a></h2>
|
||||
|
||||
@ -693,27 +691,6 @@ interaction is possible. If a specific action takes longer than a few seconds,
|
||||
the plugin should launch a separate thread to do this.
|
||||
</b>
|
||||
|
||||
<hr><h2><a name="Main thread hook">Main thread hook</a></h2>
|
||||
|
||||
<div class="blurb">Pushing in...</div><p>
|
||||
|
||||
Normally a plugin only reacts on user input if directly called through its
|
||||
<a href="#Main menu entry">main menu entry</a>, or performs some background
|
||||
activity in a separate thread. However, sometimes a plugin may need to do
|
||||
something in the context of the main program thread, without being explicitly
|
||||
called up by the user. In such a case it can implement the function
|
||||
|
||||
<p><table><tr><td class="code"><pre>
|
||||
virtual void MainThreadHook(void);
|
||||
</pre></td></tr></table><p>
|
||||
|
||||
in which it can do this. This function is called for every plugin once during
|
||||
every cycle of VDR's main program loop, which typically happens once every
|
||||
second.
|
||||
<b>Be very careful when using this function, and make sure you return from it
|
||||
as soon as possible! If you spend too much time in this function, the user
|
||||
interface performance will become sluggish!</b>
|
||||
|
||||
<hr><h2><a name="Activity">Activity</a></h2>
|
||||
|
||||
<div class="blurb">Now is not a good time!</div><p>
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Klaus Schmidinger <vdr@tvdr.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Klaus Schmidinger <vdr@tvdr.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Klaus Schmidinger <vdr@tvdr.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Klaus Schmidinger <vdr@tvdr.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Udo Richter <udo_richter@gmx.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Klaus Schmidinger <vdr@tvdr.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -76,3 +76,10 @@ VDR Plugin 'status' Revision History
|
||||
2021-12-27: Version 2.6.0
|
||||
|
||||
- Official release.
|
||||
|
||||
2025-02-10: Version 2.6.1
|
||||
|
||||
- Added cStatus::OsdItem2().
|
||||
- Activated logging of OsdItem2().
|
||||
- Added cStatus::OsdCurrentItem2().
|
||||
- Added cStatus::OsdStatusMessage2().
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Klaus Schmidinger <vdr@tvdr.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -3,13 +3,13 @@
|
||||
*
|
||||
* See the README file for copyright information and how to reach the author.
|
||||
*
|
||||
* $Id: status.c 4.1 2018/04/10 13:01:03 kls Exp $
|
||||
* $Id: status.c 5.4 2025/02/12 21:18:53 kls Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/plugin.h>
|
||||
#include <vdr/status.h>
|
||||
|
||||
static const char *VERSION = "2.4.0";
|
||||
static const char *VERSION = "2.6.1";
|
||||
static const char *DESCRIPTION = "Status monitor test";
|
||||
static const char *MAINMENUENTRY = NULL;
|
||||
|
||||
@ -27,10 +27,10 @@ protected:
|
||||
virtual void SetSubtitleTrack(int Index, const char * const *Tracks);
|
||||
virtual void OsdClear(void);
|
||||
virtual void OsdTitle(const char *Title);
|
||||
virtual void OsdStatusMessage(const char *Message);
|
||||
virtual void OsdStatusMessage2(eMessageType Type, const char *Message);
|
||||
virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
|
||||
virtual void OsdItem(const char *Text, int Index);
|
||||
virtual void OsdCurrentItem(const char *Text);
|
||||
virtual void OsdItem2(const char *Text, int Index, bool Selectable);
|
||||
virtual void OsdCurrentItem2(const char *Text, int Index);
|
||||
virtual void OsdTextItem(const char *Text, bool Scroll);
|
||||
virtual void OsdChannel(const char *Text);
|
||||
virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
|
||||
@ -86,9 +86,9 @@ void cStatusTest::OsdTitle(const char *Title)
|
||||
dsyslog("status: cStatusTest::OsdTitle '%s'", Title);
|
||||
}
|
||||
|
||||
void cStatusTest::OsdStatusMessage(const char *Message)
|
||||
void cStatusTest::OsdStatusMessage2(eMessageType Type, const char *Message)
|
||||
{
|
||||
dsyslog("status: cStatusTest::OsdStatusMessage '%s'", Message);
|
||||
dsyslog("status: cStatusTest::OsdStatusMessage2 %d '%s'", Type, Message);
|
||||
}
|
||||
|
||||
void cStatusTest::OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
|
||||
@ -96,14 +96,14 @@ void cStatusTest::OsdHelpKeys(const char *Red, const char *Green, const char *Ye
|
||||
dsyslog("status: cStatusTest::OsdHelpKeys %s - %s - %s - %s", Red, Green, Yellow, Blue);
|
||||
}
|
||||
|
||||
void cStatusTest::OsdItem(const char *Text, int Index)
|
||||
void cStatusTest::OsdItem2(const char *Text, int Index, bool Selected)
|
||||
{
|
||||
//dsyslog("status: cStatusTest::OsdItem %s %d", Text, Index);
|
||||
dsyslog("status: cStatusTest::OsdItem2 %s %d %d", Text, Index, Selected);
|
||||
}
|
||||
|
||||
void cStatusTest::OsdCurrentItem(const char *Text)
|
||||
void cStatusTest::OsdCurrentItem2(const char *Text, int Index)
|
||||
{
|
||||
dsyslog("status: cStatusTest::OsdCurrentItem %s", Text);
|
||||
dsyslog("status: cStatusTest::OsdCurrentItem %s %d", Text, Index);
|
||||
}
|
||||
|
||||
void cStatusTest::OsdTextItem(const char *Text, bool Scroll)
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
Written by: Klaus Schmidinger <Klaus.Schmidinger@tvdr.de>
|
||||
|
||||
Project's homepage: http://www.tvdr.de
|
||||
Project's homepage: https://www.tvdr.de
|
||||
|
||||
Latest version available at: http://www.tvdr.de
|
||||
Latest version available at: https://www.tvdr.de
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
4
README
4
README
@ -4,7 +4,7 @@ Video Disk Recorder ('VDR')
|
||||
These files contain the source code of the "Video Disk Recorder",
|
||||
which is based on the DVB driver of the LinuxTV project (http://linuxtv.org).
|
||||
For details about the "Video Disk Recorder" project please
|
||||
refer to http://www.tvdr.de.
|
||||
refer to https://www.tvdr.de.
|
||||
|
||||
Please see the INSTALL file for details on how to install
|
||||
this program on your computer.
|
||||
@ -33,7 +33,7 @@ the ones in this system, but here we have the full source code
|
||||
and can modify the menus in whatever way desired.
|
||||
|
||||
If you actually use VDR, please add yourself to the "VDR User Counter"
|
||||
at http://www.tvdr.de/counter.htm. You can also like VDR on facebook
|
||||
at https://www.tvdr.de/counter.htm. You can also like VDR on facebook
|
||||
at https://www.facebook.com/VideoDiskRecorder.
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ Plugins:
|
||||
- Implemented a universal plugin interface. See the file PLUGINS.html
|
||||
for a detailed description. The man page vdr(1) describes the new options '-L'
|
||||
and '-P' used to load plugins.
|
||||
See http://www.tvdr.de/plugins.htm for a list of available plugins.
|
||||
See https://www.tvdr.de/plugins.htm for a list of available plugins.
|
||||
- Rearranged the remote control key handling to allow plugins to implement
|
||||
additional types of remote controls (see PLUGINS.html, section "Remote Control").
|
||||
The previously used files 'keys.conf' and 'keys-pc.conf' have been replaced
|
||||
|
20
config.h
20
config.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.h 5.17 2024/07/08 18:40:20 kls Exp $
|
||||
* $Id: config.h 5.26 2025/02/26 10:35:03 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H
|
||||
@ -22,19 +22,21 @@
|
||||
|
||||
// VDR's own version number:
|
||||
|
||||
#define VDRVERSION "2.6.8"
|
||||
#define VDRVERSNUM 20608 // Version * 10000 + Major * 100 + Minor
|
||||
#define VDRVERSION "2.7.4"
|
||||
#define VDRVERSNUM 20704 // Version * 10000 + Major * 100 + Minor
|
||||
|
||||
// The plugin API's version number:
|
||||
|
||||
#define APIVERSION "2.6.8"
|
||||
#define APIVERSNUM 20608 // Version * 10000 + Major * 100 + Minor
|
||||
#define APIVERSION "6"
|
||||
#define APIVERSNUM 30006
|
||||
|
||||
// When loading plugins, VDR searches them by their APIVERSION, which
|
||||
// may be smaller than VDRVERSION in case there have been no changes to
|
||||
// VDR header files since the last APIVERSION. This allows compiled
|
||||
// When loading plugins, VDR searches files by their APIVERSION, which
|
||||
// is different from VDRVERSION. APIVERSION is a plain number, incremented
|
||||
// only when there are changes to the plugin API. This allows compiled
|
||||
// plugins to work with newer versions of the core VDR as long as no
|
||||
// VDR header files have changed.
|
||||
// interfaces have changed. APIVERSNUM begins with "300.." for backwards
|
||||
// compatibility and can be used in #if preprocessor statements to handle
|
||||
// version dependent code.
|
||||
|
||||
#define MAXPRIORITY 99
|
||||
#define MINPRIORITY (-MAXPRIORITY)
|
||||
|
64
cutter.c
64
cutter.c
@ -4,12 +4,11 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: cutter.c 5.1 2022/11/06 11:25:13 kls Exp $
|
||||
* $Id: cutter.c 5.4 2025/01/10 13:12:04 kls Exp $
|
||||
*/
|
||||
|
||||
#include "cutter.h"
|
||||
#include "menu.h"
|
||||
#include "recording.h"
|
||||
#include "remux.h"
|
||||
#include "videodir.h"
|
||||
|
||||
@ -232,6 +231,10 @@ private:
|
||||
int numSequences;
|
||||
off_t maxVideoFileSize;
|
||||
off_t fileSize;
|
||||
int frameErrors;
|
||||
time_t lastErrorHandling;
|
||||
cString editedRecordingName;
|
||||
cRecordingInfo *recordingInfo;
|
||||
bool suspensionLogged;
|
||||
int sequence; // cutting sequence
|
||||
int delta; // time between two frames (PTS ticks)
|
||||
@ -246,7 +249,7 @@ private:
|
||||
cPatPmtParser patPmtParser;
|
||||
bool Throttled(void);
|
||||
bool SwitchFile(bool Force = false);
|
||||
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length);
|
||||
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length, bool *Errors = NULL, bool *Missing = NULL);
|
||||
bool FramesAreEqual(int Index1, int Index2);
|
||||
void GetPendingPackets(uchar *Buffer, int &Length, int Index);
|
||||
// Gather all non-video TS packets from Index upward that either belong to
|
||||
@ -254,15 +257,16 @@ private:
|
||||
// and add them to the end of the given Data.
|
||||
bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut);
|
||||
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
|
||||
void HandleErrors(bool Force = false);
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cCuttingThread(const char *FromFileName, const char *ToFileName);
|
||||
cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo);
|
||||
virtual ~cCuttingThread();
|
||||
const char *Error(void) { return error; }
|
||||
};
|
||||
|
||||
cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
||||
cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo)
|
||||
:cThread("video cutting", true)
|
||||
{
|
||||
error = NULL;
|
||||
@ -274,6 +278,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
||||
framesPerSecond = Recording.FramesPerSecond();
|
||||
suspensionLogged = false;
|
||||
fileSize = 0;
|
||||
frameErrors = 0;
|
||||
lastErrorHandling = 0;
|
||||
editedRecordingName = ToFileName;
|
||||
recordingInfo = RecordingInfo;
|
||||
sequence = 0;
|
||||
delta = int(round(PTSTICKS / framesPerSecond));
|
||||
lastVidPts = -1;
|
||||
@ -294,6 +302,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
||||
maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize);
|
||||
if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES))
|
||||
maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES);
|
||||
if (fromIndex->GetErrors()->Size() > 0) {
|
||||
recordingInfo->SetErrors(0); // the fromIndex has error indicators, so we reset the error count
|
||||
recordingInfo->Write();
|
||||
}
|
||||
Start();
|
||||
}
|
||||
else
|
||||
@ -328,11 +340,11 @@ bool cCuttingThread::Throttled(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length)
|
||||
bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length, bool *Errors, bool *Missing)
|
||||
{
|
||||
uint16_t FileNumber;
|
||||
off_t FileOffset;
|
||||
if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) {
|
||||
if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length, Errors, Missing)) {
|
||||
fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
|
||||
if (fromFile) {
|
||||
fromFile->SetReadAhead(MEGABYTE(20));
|
||||
@ -555,7 +567,9 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
|
||||
for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) {
|
||||
bool Independent;
|
||||
int Length;
|
||||
if (LoadFrame(Index, Buffer, Independent, Length)) {
|
||||
bool Errors;
|
||||
bool Missing;
|
||||
if (LoadFrame(Index, Buffer, Independent, Length, &Errors, &Missing)) {
|
||||
// Make sure there is enough disk space:
|
||||
AssertFreeDiskSpace(-1);
|
||||
bool CutIn = !SeamlessBegin && Index == BeginIndex;
|
||||
@ -572,10 +586,12 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
|
||||
return false;
|
||||
}
|
||||
// Write index:
|
||||
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize)) {
|
||||
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize, Errors, Missing)) {
|
||||
error = "toIndex";
|
||||
return false;
|
||||
}
|
||||
frameErrors += Errors + Missing;
|
||||
HandleErrors();
|
||||
// Write data:
|
||||
if (toFile->Write(Buffer, Length) < 0) {
|
||||
error = "safe_write";
|
||||
@ -596,6 +612,27 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
|
||||
return true;
|
||||
}
|
||||
|
||||
#define ERROR_HANDLING_DELTA 1 // seconds between handling errors
|
||||
|
||||
void cCuttingThread::HandleErrors(bool Force)
|
||||
{
|
||||
if (Force || time(NULL) - lastErrorHandling >= ERROR_HANDLING_DELTA) {
|
||||
if (frameErrors > recordingInfo->Errors()) {
|
||||
recordingInfo->SetErrors(frameErrors);
|
||||
recordingInfo->Write();
|
||||
Force = true;
|
||||
}
|
||||
if (Force) {
|
||||
cStateKey StateKey;
|
||||
if (cRecordings *Recordings = cRecordings::GetRecordingsWrite(StateKey, 1)) {
|
||||
Recordings->UpdateByName(editedRecordingName);
|
||||
StateKey.Remove();
|
||||
}
|
||||
}
|
||||
lastErrorHandling = time(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void cCuttingThread::Action(void)
|
||||
{
|
||||
if (cMark *BeginMark = fromMarks.GetNextBegin()) {
|
||||
@ -604,6 +641,7 @@ void cCuttingThread::Action(void)
|
||||
if (!fromFile || !toFile)
|
||||
return;
|
||||
int LastEndIndex = -1;
|
||||
HandleErrors(true); // to make sure an initially reset error count is displayed correctly
|
||||
while (BeginMark && Running()) {
|
||||
// Suspend cutting if we have severe throughput problems:
|
||||
if (Throttled()) {
|
||||
@ -634,6 +672,7 @@ void cCuttingThread::Action(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
HandleErrors(true);
|
||||
}
|
||||
else
|
||||
esyslog("no editing marks found!");
|
||||
@ -642,6 +681,7 @@ void cCuttingThread::Action(void)
|
||||
// --- cCutter ---------------------------------------------------------------
|
||||
|
||||
cCutter::cCutter(const char *FileName)
|
||||
:recordingInfo(FileName)
|
||||
{
|
||||
cuttingThread = NULL;
|
||||
error = false;
|
||||
@ -676,9 +716,11 @@ bool cCutter::Start(void)
|
||||
if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
|
||||
cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName);
|
||||
if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) {
|
||||
Recording.WriteInfo(editedVersionName);
|
||||
recordingInfo.Read();
|
||||
recordingInfo.SetFileName(editedVersionName);
|
||||
recordingInfo.Write();
|
||||
SetRecordingTimerId(editedVersionName, cString::sprintf("%d@%s", 0, Setup.SVDRPHostName));
|
||||
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName);
|
||||
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName, &recordingInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
4
cutter.h
4
cutter.h
@ -4,12 +4,13 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: cutter.h 3.1 2013/10/05 11:34:55 kls Exp $
|
||||
* $Id: cutter.h 5.1 2024/09/19 20:21:58 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CUTTER_H
|
||||
#define __CUTTER_H
|
||||
|
||||
#include "recording.h"
|
||||
#include "thread.h"
|
||||
#include "tools.h"
|
||||
|
||||
@ -19,6 +20,7 @@ class cCutter {
|
||||
private:
|
||||
cString originalVersionName;
|
||||
cString editedVersionName;
|
||||
cRecordingInfo recordingInfo;
|
||||
cCuttingThread *cuttingThread;
|
||||
bool error;
|
||||
public:
|
||||
|
8
device.h
8
device.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: device.h 5.5 2024/07/06 11:19:21 kls Exp $
|
||||
* $Id: device.h 5.6 2024/07/15 14:42:22 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DEVICE_H
|
||||
@ -370,12 +370,6 @@ protected:
|
||||
public:
|
||||
static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
|
||||
///< Returns the number of the current channel on the primary device.
|
||||
#ifndef DEPRECATED_SETCURRENTCHANNEL
|
||||
#define DEPRECATED_SETCURRENTCHANNEL 0
|
||||
#endif
|
||||
#if DEPRECATED_SETCURRENTCHANNEL
|
||||
static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
|
||||
#endif
|
||||
static void SetCurrentChannel(int ChannelNumber) { currentChannel = ChannelNumber; }
|
||||
///< Sets the number of the current channel on the primary device, without
|
||||
///< actually switching to it. This can be used to correct the current
|
||||
|
12
dvbdevice.c
12
dvbdevice.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbdevice.c 5.5 2024/07/08 18:39:18 kls Exp $
|
||||
* $Id: dvbdevice.c 5.8 2024/09/09 08:53:57 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbdevice.h"
|
||||
@ -394,7 +394,7 @@ void cDvbFrontend::Close(void)
|
||||
{
|
||||
if (fd_frontend >= 0) {
|
||||
if (close(fd_frontend) != 0)
|
||||
esyslog("ERROR: frontend %d/%d", adapter, frontend);
|
||||
esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
|
||||
fd_frontend = -1;
|
||||
}
|
||||
}
|
||||
@ -549,9 +549,9 @@ private:
|
||||
mutable uint32_t lastUncDelta;
|
||||
mutable time_t lastUncChange;
|
||||
cChannel channel;
|
||||
const cDiseqc *lastDiseqc;
|
||||
mutable const cDiseqc *lastDiseqc;
|
||||
int diseqcOffset;
|
||||
int lastSource;
|
||||
mutable int lastSource;
|
||||
cPositioner *positioner;
|
||||
const cScr *scr;
|
||||
mutable bool lnbPowerTurnedOn;
|
||||
@ -697,6 +697,8 @@ bool cDvbTuner::ProvidesFrontend(const cChannel *Channel, bool Activate) const
|
||||
fd_frontend = dvbFrontend->Open();
|
||||
frontend = i;
|
||||
dsyslog("using frontend %d/%d", adapter, frontend);
|
||||
lastDiseqc = NULL;
|
||||
lastSource = 0;
|
||||
lastUncValue = 0;
|
||||
lastUncDelta = 0;
|
||||
lastUncChange = 0;
|
||||
@ -1796,6 +1798,8 @@ void cDvbTuner::SetPowerSaveMode(bool On)
|
||||
if (fd_frontend == -1) {
|
||||
dsyslog("opening frontend %d/%d", adapter, frontend);
|
||||
fd_frontend = dvbFrontend->Open();
|
||||
lastDiseqc = NULL;
|
||||
lastSource = 0;
|
||||
lastUncValue = 0;
|
||||
lastUncDelta = 0;
|
||||
lastUncChange = 0;
|
||||
|
48
dvbplayer.c
48
dvbplayer.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbplayer.c 5.3 2022/12/27 15:57:20 kls Exp $
|
||||
* $Id: dvbplayer.c 5.7 2025/02/19 15:39:16 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbplayer.h"
|
||||
@ -36,7 +36,7 @@ public:
|
||||
bool IsEmpty(void);
|
||||
void Put(uint32_t Pts, int Index, bool Independent);
|
||||
int FindIndex(uint32_t Pts);
|
||||
int FindFrameNumber(uint32_t Pts);
|
||||
int FindFrameNumber(uint32_t Pts, bool Forward);
|
||||
};
|
||||
|
||||
cPtsIndex::cPtsIndex(void)
|
||||
@ -90,30 +90,30 @@ int cPtsIndex::FindIndex(uint32_t Pts)
|
||||
return Index;
|
||||
}
|
||||
|
||||
int cPtsIndex::FindFrameNumber(uint32_t Pts)
|
||||
int cPtsIndex::FindFrameNumber(uint32_t Pts, bool Forward)
|
||||
{
|
||||
if (!Forward)
|
||||
return FindIndex(Pts); // there are only I frames in backward
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (w == r)
|
||||
return lastFound; // replay always starts at an I frame
|
||||
bool Valid = false;
|
||||
int d;
|
||||
int FrameNumber = 0;
|
||||
int UnplayedIFrame = 2; // GOPs may intersect, so we're looping until we found two unplayed I frames
|
||||
int UnplayedIFrame = 2; // GOPs may intersect, so we loop until we processed a complete unplayed GOP
|
||||
for (int i = r; i != w && UnplayedIFrame; ) {
|
||||
d = Pts - pi[i].pts;
|
||||
if (d > 0x7FFFFFFF)
|
||||
d = 0xFFFFFFFF - d; // handle rollover
|
||||
if (d > 0) {
|
||||
int32_t d = int32_t(Pts - pi[i].pts); // typecast handles rollover
|
||||
if (d >= 0) {
|
||||
if (pi[i].independent) {
|
||||
FrameNumber = pi[i].index; // an I frame's index represents its frame number
|
||||
Valid = true;
|
||||
if (d == 0)
|
||||
UnplayedIFrame = 1; // if Pts is at an I frame we only need to check up to the next I frame
|
||||
}
|
||||
else
|
||||
FrameNumber++; // for every played non-I frame, increase frame number
|
||||
}
|
||||
else
|
||||
if (pi[i].independent)
|
||||
--UnplayedIFrame;
|
||||
else if (pi[i].independent)
|
||||
--UnplayedIFrame;
|
||||
if (++i >= PTSINDEX_ENTRIES)
|
||||
i = 0;
|
||||
}
|
||||
@ -283,6 +283,7 @@ public:
|
||||
void Goto(int Position, bool Still = false);
|
||||
virtual double FramesPerSecond(void) { return framesPerSecond; }
|
||||
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId);
|
||||
virtual const cErrors *GetErrors(void);
|
||||
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
|
||||
virtual bool GetFrameNumber(int &Current, int &Total);
|
||||
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
|
||||
@ -792,15 +793,17 @@ void cDvbPlayer::Forward(void)
|
||||
Pause();
|
||||
break;
|
||||
}
|
||||
Empty();
|
||||
// run into pmPause
|
||||
case pmStill:
|
||||
case pmPause:
|
||||
case pmPause: {
|
||||
LOCK_THREAD;
|
||||
Empty();
|
||||
DeviceMute();
|
||||
playMode = pmSlow;
|
||||
playDir = pdForward;
|
||||
trickSpeed = NORMAL_SPEED;
|
||||
TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
|
||||
}
|
||||
break;
|
||||
default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
|
||||
}
|
||||
@ -841,7 +844,6 @@ void cDvbPlayer::Backward(void)
|
||||
Pause();
|
||||
break;
|
||||
}
|
||||
Empty();
|
||||
// run into pmPause
|
||||
case pmStill:
|
||||
case pmPause: {
|
||||
@ -940,6 +942,13 @@ void cDvbPlayer::SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
|
||||
resyncAfterPause = true;
|
||||
}
|
||||
|
||||
const cErrors *cDvbPlayer::GetErrors(void)
|
||||
{
|
||||
if (index)
|
||||
return index->GetErrors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
{
|
||||
if (index) {
|
||||
@ -959,7 +968,7 @@ bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
bool cDvbPlayer::GetFrameNumber(int &Current, int &Total)
|
||||
{
|
||||
if (index) {
|
||||
Current = ptsIndex.FindFrameNumber(DeviceGetSTC());
|
||||
Current = ptsIndex.FindFrameNumber(DeviceGetSTC(), playDir == pdForward);
|
||||
Total = index->Last();
|
||||
return true;
|
||||
}
|
||||
@ -1047,6 +1056,13 @@ int cDvbPlayerControl::SkipFrames(int Frames)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const cErrors *cDvbPlayerControl::GetErrors(void)
|
||||
{
|
||||
if (player)
|
||||
return player->GetErrors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
{
|
||||
if (player) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbplayer.h 4.2 2016/12/22 10:36:50 kls Exp $
|
||||
* $Id: dvbplayer.h 5.1 2024/09/19 09:49:02 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DVBPLAYER_H
|
||||
@ -47,6 +47,8 @@ public:
|
||||
// The sign of 'Seconds' determines the direction in which to skip.
|
||||
// Use a very large negative value to go all the way back to the
|
||||
// beginning of the recording.
|
||||
const cErrors *GetErrors(void);
|
||||
// Returns the frame indexes of errors in the recording (if any).
|
||||
bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
|
||||
// Returns the current and total frame index, optionally snapped to the
|
||||
// nearest I-frame.
|
||||
|
11
eitscan.c
11
eitscan.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: eitscan.c 5.6 2024/07/06 11:19:21 kls Exp $
|
||||
* $Id: eitscan.c 5.7 2024/07/13 20:12:24 kls Exp $
|
||||
*/
|
||||
|
||||
#include "eitscan.h"
|
||||
@ -152,11 +152,12 @@ void cEITScanner::Process(void)
|
||||
paused = true;
|
||||
}
|
||||
// Allow unused devices to go into power save mode:
|
||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||
if (cDevice *Device = cDevice::GetDevice(i))
|
||||
Device->SetPowerSaveIfUnused();
|
||||
if ((now - lastScan) % 10 == 0) { // let's not do this too often
|
||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||
if (cDevice *Device = cDevice::GetDevice(i))
|
||||
Device->SetPowerSaveIfUnused();
|
||||
}
|
||||
}
|
||||
lastScan = time(NULL); // let's not do this too often
|
||||
return; // pause for Setup.EPGScanTimeout hours
|
||||
}
|
||||
else if (paused) {
|
||||
|
64
epg.c
64
epg.c
@ -7,7 +7,7 @@
|
||||
* Original version (as used in VDR before 1.3.0) written by
|
||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||
*
|
||||
* $Id: epg.c 5.9 2024/06/21 06:27:20 kls Exp $
|
||||
* $Id: epg.c 5.13 2024/11/30 14:30:46 kls Exp $
|
||||
*/
|
||||
|
||||
#include "epg.h"
|
||||
@ -145,7 +145,10 @@ cEvent::~cEvent()
|
||||
int cEvent::Compare(const cListObject &ListObject) const
|
||||
{
|
||||
cEvent *e = (cEvent *)&ListObject;
|
||||
return startTime - e->startTime;
|
||||
int d = startTime - e->startTime;
|
||||
if (d == 0)
|
||||
d = int(tableID) - int(e->tableID);
|
||||
return d;
|
||||
}
|
||||
|
||||
tChannelID cEvent::ChannelID(void) const
|
||||
@ -300,7 +303,7 @@ const char *cEvent::ContentToString(uchar Content)
|
||||
case 0x01: return tr("Content$News/Weather Report");
|
||||
case 0x02: return tr("Content$News Magazine");
|
||||
case 0x03: return tr("Content$Documentary");
|
||||
case 0x04: return tr("Content$Discussion/Inverview/Debate");
|
||||
case 0x04: return tr("Content$Discussion/Interview/Debate");
|
||||
}
|
||||
break;
|
||||
case ecgShow:
|
||||
@ -346,7 +349,7 @@ const char *cEvent::ContentToString(uchar Content)
|
||||
case 0x00: return tr("Content$Music/Ballet/Dance");
|
||||
case 0x01: return tr("Content$Rock/Pop");
|
||||
case 0x02: return tr("Content$Serious/Classical Music");
|
||||
case 0x03: return tr("Content$Folk/Tradional Music");
|
||||
case 0x03: return tr("Content$Folk/Traditional Music");
|
||||
case 0x04: return tr("Content$Jazz");
|
||||
case 0x05: return tr("Content$Musical/Opera");
|
||||
case 0x06: return tr("Content$Ballet");
|
||||
@ -928,7 +931,6 @@ cSchedule::cSchedule(tChannelID ChannelID)
|
||||
channelID = ChannelID;
|
||||
events.SetUseGarbageCollector();
|
||||
numTimers = 0;
|
||||
hasRunning = false;
|
||||
modified = 0;
|
||||
onActualTp = false;
|
||||
presentSeen = 0;
|
||||
@ -1028,18 +1030,6 @@ const cEvent *cSchedule::GetFollowingEvent(void) const
|
||||
return p;
|
||||
}
|
||||
|
||||
#if DEPRECATED_SCHEDULE_GET_EVENT
|
||||
const cEvent *cSchedule::GetEvent(tEventID EventID, time_t StartTime) const
|
||||
{
|
||||
// Returns the event info with the given StartTime or, if no actual StartTime
|
||||
// is given, the one with the given EventID.
|
||||
if (StartTime > 0) // 'StartTime < 0' is apparently used with NVOD channels
|
||||
return eventsHashStartTime.Get(StartTime);
|
||||
else
|
||||
return eventsHashID.Get(EventID);
|
||||
}
|
||||
#endif
|
||||
|
||||
const cEvent *cSchedule::GetEventById(tEventID EventID) const
|
||||
{
|
||||
return eventsHashID.Get(EventID);
|
||||
@ -1068,7 +1058,6 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const
|
||||
|
||||
void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel)
|
||||
{
|
||||
hasRunning = false;
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p == Event) {
|
||||
if (p->RunningStatus() > SI::RunningStatusNotRunning || RunningStatus > SI::RunningStatusNotRunning) {
|
||||
@ -1078,24 +1067,19 @@ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChanne
|
||||
}
|
||||
else if (RunningStatus >= SI::RunningStatusPausing && p->StartTime() < Event->StartTime())
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing)
|
||||
hasRunning = true;
|
||||
}
|
||||
SetPresentSeen();
|
||||
}
|
||||
|
||||
void cSchedule::ClrRunningStatus(cChannel *Channel)
|
||||
{
|
||||
if (hasRunning) {
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing) {
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
|
||||
hasRunning = false;
|
||||
SetModified();
|
||||
break;
|
||||
}
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing) {
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
|
||||
SetModified();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cSchedule::ResetVersions(void)
|
||||
@ -1107,14 +1091,6 @@ void cSchedule::ResetVersions(void)
|
||||
void cSchedule::Sort(void)
|
||||
{
|
||||
events.Sort();
|
||||
// Make sure there are no RunningStatusUndefined before the currently running event:
|
||||
if (hasRunning) {
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing)
|
||||
break;
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning);
|
||||
}
|
||||
}
|
||||
SetModified();
|
||||
}
|
||||
|
||||
@ -1141,6 +1117,20 @@ void cSchedule::DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar Table
|
||||
p = n;
|
||||
}
|
||||
}
|
||||
// Make sure there are no two events with the same start time:
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
cEvent *n = events.Next(p);
|
||||
if (n && n->StartTime() == p->StartTime())
|
||||
DelEvent(n);
|
||||
}
|
||||
// Make sure there are no RunningStatusUndefined before the currently running event:
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing) {
|
||||
for (p = events.Prev(p); p; p = events.Prev(p))
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cSchedule::Cleanup(void)
|
||||
|
7
epg.h
7
epg.h
@ -7,7 +7,7 @@
|
||||
* Original version (as used in VDR before 1.3.0) written by
|
||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||
*
|
||||
* $Id: epg.h 5.3 2022/12/24 11:37:17 kls Exp $
|
||||
* $Id: epg.h 5.8 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __EPG_H
|
||||
@ -157,7 +157,6 @@ private:
|
||||
cHash<cEvent> eventsHashID;
|
||||
cHash<cEvent> eventsHashStartTime;
|
||||
mutable u_int16_t numTimers;// The number of timers that use this schedule
|
||||
bool hasRunning;
|
||||
bool onActualTp;
|
||||
int modified;
|
||||
time_t presentSeen;
|
||||
@ -187,10 +186,6 @@ public:
|
||||
const cList<cEvent> *Events(void) const { return &events; }
|
||||
const cEvent *GetPresentEvent(void) const;
|
||||
const cEvent *GetFollowingEvent(void) const;
|
||||
#define DEPRECATED_SCHEDULE_GET_EVENT 1
|
||||
#if DEPRECATED_SCHEDULE_GET_EVENT
|
||||
const cEvent *GetEvent(tEventID EventID, time_t StartTime = 0) const;
|
||||
#endif
|
||||
const cEvent *GetEventById(tEventID EventID) const;
|
||||
const cEvent *GetEventByTime(time_t StartTime) const;
|
||||
const cEvent *GetEventAround(time_t Time) const;
|
||||
|
31
filter.c
31
filter.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: filter.c 5.1 2021/03/16 15:10:54 kls Exp $
|
||||
* $Id: filter.c 5.2 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#include "filter.h"
|
||||
@ -73,35 +73,6 @@ bool cSectionSyncer::Processed(int SectionNumber, int LastSectionNumber, int Seg
|
||||
return complete;
|
||||
}
|
||||
|
||||
#if DEPRECATED_SECTIONSYNCER_SYNC_REPEAT
|
||||
void cSectionSyncer::Repeat(void)
|
||||
{
|
||||
SetSectionFlag(currentSection, false);
|
||||
synced = false;
|
||||
complete = false;
|
||||
}
|
||||
|
||||
bool cSectionSyncer::Sync(uchar Version, int Number, int LastNumber)
|
||||
{
|
||||
if (Version != currentVersion) {
|
||||
Reset();
|
||||
currentVersion = Version;
|
||||
}
|
||||
if (!synced) {
|
||||
if (Number != 0)
|
||||
return false;
|
||||
else
|
||||
synced = true;
|
||||
}
|
||||
currentSection = Number;
|
||||
bool Result = !GetSectionFlag(Number);
|
||||
SetSectionFlag(Number, true);
|
||||
if (Number == LastNumber)
|
||||
complete = true;
|
||||
return Result;
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- cFilterData -----------------------------------------------------------
|
||||
|
||||
cFilterData::cFilterData(void)
|
||||
|
8
filter.h
8
filter.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: filter.h 5.1 2021/03/16 15:10:54 kls Exp $
|
||||
* $Id: filter.h 5.5 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __FILTER_H
|
||||
@ -13,8 +13,6 @@
|
||||
#include <sys/types.h>
|
||||
#include "tools.h"
|
||||
|
||||
#define DEPRECATED_SECTIONSYNCER_SYNC_REPEAT 1
|
||||
|
||||
class cSectionSyncer {
|
||||
private:
|
||||
int currentVersion;
|
||||
@ -49,10 +47,6 @@ public:
|
||||
///< Returns true if all sections have been processed.
|
||||
bool Complete(void) { return complete; }
|
||||
///< Returns true if all sections have been processed.
|
||||
#if DEPRECATED_SECTIONSYNCER_SYNC_REPEAT
|
||||
void Repeat(void);
|
||||
bool Sync(uchar Version, int Number, int LastNumber);
|
||||
#endif
|
||||
};
|
||||
|
||||
class cSectionSyncerRandom : public cSectionSyncer {
|
||||
|
4
font.c
4
font.c
@ -6,7 +6,7 @@
|
||||
*
|
||||
* BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya.
|
||||
*
|
||||
* $Id: font.c 5.2 2022/12/06 12:30:13 kls Exp $
|
||||
* $Id: font.c 5.3 2025/02/17 11:13:13 kls Exp $
|
||||
*/
|
||||
|
||||
#include "font.h"
|
||||
@ -618,7 +618,7 @@ void cTextWrapper::Set(const char *Text, const cFont *Font, int Width)
|
||||
}
|
||||
}
|
||||
w += cw;
|
||||
if (strchr("-.,:;!?_", *p)) {
|
||||
if (strchr("-.,:;!?_~", *p)) {
|
||||
Delim = p;
|
||||
Blank = NULL;
|
||||
}
|
||||
|
123
menu.c
123
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 5.14 2024/06/25 19:00:06 kls Exp $
|
||||
* $Id: menu.c 5.23 2025/02/25 15:53:43 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -342,6 +342,7 @@ void cMenuChannelItem::Set(void)
|
||||
|
||||
void cMenuChannelItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
if (!DisplayMenu->SetItemChannel(channel, Index, Current, Selectable, sortMode == csmProvider))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -443,8 +444,9 @@ eOSState cMenuChannels::Number(eKeys Key)
|
||||
number = number * 10 + Key - k0;
|
||||
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) {
|
||||
if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) {
|
||||
DisplayCurrent(false);
|
||||
SetCurrent(ci);
|
||||
Display();
|
||||
DisplayCurrent(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1263,6 +1265,7 @@ void cMenuTimerItem::Set(void)
|
||||
}
|
||||
const char *File = timer->Pattern();
|
||||
if (!*File) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (timer->HasFlags(tfSpawned) && timer->Event() && timer->Event()->Title())
|
||||
File = timer->Event()->Title();
|
||||
else {
|
||||
@ -1291,6 +1294,7 @@ void cMenuTimerItem::Set(void)
|
||||
|
||||
void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
|
||||
{
|
||||
LOCK_TIMERS_READ;
|
||||
if (!DisplayMenu->SetItemTimer(timer, Index, Current, Selectable))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -1334,12 +1338,15 @@ void cMenuTimers::Set(void)
|
||||
const cTimer *CurrentTimer = GetTimer();
|
||||
cMenuTimerItem *CurrentItem = NULL;
|
||||
Clear();
|
||||
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
|
||||
cMenuTimerItem *Item = new cMenuTimerItem(Timer);
|
||||
Add(Item);
|
||||
if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0))
|
||||
CurrentItem = Item;
|
||||
}
|
||||
{
|
||||
LOCK_SCHEDULES_READ;
|
||||
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
|
||||
cMenuTimerItem *Item = new cMenuTimerItem(Timer);
|
||||
Add(Item);
|
||||
if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0))
|
||||
CurrentItem = Item;
|
||||
}
|
||||
}
|
||||
Sort();
|
||||
SetCurrent(CurrentItem ? CurrentItem : First());
|
||||
SetHelpKeys();
|
||||
@ -1454,6 +1461,7 @@ eOSState cMenuTimers::Info(void)
|
||||
return osContinue;
|
||||
LOCK_TIMERS_READ;
|
||||
LOCK_CHANNELS_READ;
|
||||
LOCK_SCHEDULES_READ;
|
||||
cTimer *Timer = GetTimer();
|
||||
if (Timer && Timer->Event())
|
||||
return AddSubMenu(new cMenuEvent(Timers, Channels, Timer->Event()));
|
||||
@ -1513,6 +1521,7 @@ cMenuEvent::cMenuEvent(const cTimers *Timers, const cChannels *Channels, const c
|
||||
|
||||
void cMenuEvent::Display(void)
|
||||
{
|
||||
LOCK_SCHEDULES_READ;
|
||||
cOsdMenu::Display();
|
||||
DisplayMenu()->SetEvent(event);
|
||||
if (event->Description())
|
||||
@ -1599,6 +1608,8 @@ static const char *TimerMatchChars = " tT iI";
|
||||
|
||||
bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
LOCK_SCHEDULES_READ;
|
||||
eTimerMatch OldTimerMatch = timerMatch;
|
||||
bool OldTimerActive = timerActive;
|
||||
const cTimer *Timer = Timers->GetMatch(event, &timerMatch);
|
||||
@ -1626,6 +1637,8 @@ bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force)
|
||||
|
||||
void cMenuScheduleItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (!DisplayMenu->SetItemEvent(event, Index, Current, Selectable, channel, withDate, timerMatch, timerActive))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -1671,7 +1684,6 @@ cMenuWhatsOn::cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, con
|
||||
}
|
||||
}
|
||||
currentChannel = CurrentChannelNr;
|
||||
Display();
|
||||
SetHelpKeys(Channels);
|
||||
}
|
||||
|
||||
@ -1772,10 +1784,8 @@ eOSState cMenuWhatsOn::Record(void)
|
||||
if (HasSubMenu())
|
||||
CloseSubMenu();
|
||||
}
|
||||
if (Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (Update())
|
||||
Display();
|
||||
}
|
||||
LOCK_CHANNELS_READ;
|
||||
SetHelpKeys(Channels);
|
||||
return osContinue;
|
||||
@ -1795,6 +1805,7 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
|
||||
case kGreen: {
|
||||
cMenuScheduleItem *mi = (cMenuScheduleItem *)Get(Current());
|
||||
if (mi) {
|
||||
LOCK_CHANNELS_READ;
|
||||
scheduleEvent = mi->event;
|
||||
currentChannel = mi->channel->Number();
|
||||
}
|
||||
@ -1807,14 +1818,12 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
|
||||
case kChanUp:
|
||||
case kChanDn|k_Repeat:
|
||||
case kChanDn: if (!HasSubMenu()) {
|
||||
LOCK_CHANNELS_READ;
|
||||
for (cOsdItem *item = First(); item; item = Next(item)) {
|
||||
if (((cMenuScheduleItem *)item)->channel->Number() == cDevice::CurrentChannel()) {
|
||||
DisplayCurrent(false);
|
||||
SetCurrent(item);
|
||||
{
|
||||
LOCK_SCHEDULES_READ;
|
||||
Display();
|
||||
}
|
||||
LOCK_CHANNELS_READ;
|
||||
DisplayCurrent(true);
|
||||
SetHelpKeys(Channels);
|
||||
break;
|
||||
}
|
||||
@ -1832,10 +1841,8 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
|
||||
}
|
||||
}
|
||||
else if (!HasSubMenu()) {
|
||||
if (HadSubMenu && Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (HadSubMenu && Update())
|
||||
Display();
|
||||
}
|
||||
if (Key != kNone) {
|
||||
LOCK_CHANNELS_READ;
|
||||
SetHelpKeys(Channels);
|
||||
@ -2083,10 +2090,8 @@ eOSState cMenuSchedule::Record(void)
|
||||
if (HasSubMenu())
|
||||
CloseSubMenu();
|
||||
}
|
||||
if (Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (Update())
|
||||
Display();
|
||||
}
|
||||
SetHelpKeys();
|
||||
return osContinue;
|
||||
}
|
||||
@ -2180,10 +2185,8 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
|
||||
Set(Timers, Channels, Channel, true);
|
||||
}
|
||||
}
|
||||
else if (HadSubMenu && Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
else if (HadSubMenu && Update())
|
||||
Display();
|
||||
}
|
||||
if (Key != kNone)
|
||||
SetHelpKeys();
|
||||
}
|
||||
@ -3042,6 +3045,7 @@ void cMenuRecordingItem::IncrementCounter(bool New)
|
||||
|
||||
void cMenuRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
|
||||
{
|
||||
LOCK_RECORDINGS_READ;
|
||||
if (!DisplayMenu->SetItemRecording(recording, Index, Current, Selectable, level, totalEntries, newEntries))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -3290,8 +3294,11 @@ eOSState cMenuRecordings::Delete(void)
|
||||
if (RecordingsHandler.GetUsage(FileName)) {
|
||||
if (!Interface->Confirm(tr("Recording is being edited - really delete?")))
|
||||
return osContinue;
|
||||
SetNeedsFastResponse(true); // makes sure the edited version is removed from the menu ASAP
|
||||
}
|
||||
}
|
||||
else
|
||||
return osContinue; // recording has already been deleted
|
||||
}
|
||||
RecordingsHandler.Del(FileName); // must do this w/o holding a lock, because the cleanup section in cDirCopier::Action() might request one!
|
||||
if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName) == 0)
|
||||
@ -4663,21 +4670,22 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (!HasSubMenu() && Update(HadSubMenu))
|
||||
Display();
|
||||
bool DoDisplay = Update();
|
||||
if (Key != kNone) {
|
||||
if (I18nCurrentLanguage() != osdLanguage) {
|
||||
Set();
|
||||
if (!HasSubMenu())
|
||||
Display();
|
||||
DoDisplay = true;
|
||||
}
|
||||
}
|
||||
if (DoDisplay)
|
||||
Display();
|
||||
return state;
|
||||
}
|
||||
|
||||
// --- SetTrackDescriptions --------------------------------------------------
|
||||
|
||||
static void SetTrackDescriptions(int LiveChannel)
|
||||
void SetTrackDescriptions(int LiveChannel)
|
||||
{
|
||||
cDevice::PrimaryDevice()->ClrAvailableTracks(true);
|
||||
const cComponents *Components = NULL;
|
||||
@ -5706,6 +5714,7 @@ cReplayControl::cReplayControl(bool PauseLive)
|
||||
displayReplay = NULL;
|
||||
marksModified = false;
|
||||
visible = modeOnly = shown = displayFrames = false;
|
||||
lastErrors = 0;
|
||||
lastCurrent = lastTotal = -1;
|
||||
lastPlay = lastForward = false;
|
||||
lastSpeed = -2; // an invalid value
|
||||
@ -5879,6 +5888,7 @@ bool cReplayControl::ShowProgress(bool Initial)
|
||||
if (!visible) {
|
||||
displayReplay = Skins.Current()->DisplayReplay(modeOnly);
|
||||
displayReplay->SetMarks(&marks);
|
||||
displayReplay->SetErrors(GetErrors());
|
||||
SetNeedsFastResponse(true);
|
||||
visible = true;
|
||||
}
|
||||
@ -5890,7 +5900,9 @@ bool cReplayControl::ShowProgress(bool Initial)
|
||||
}
|
||||
lastCurrent = lastTotal = -1;
|
||||
}
|
||||
if (Current != lastCurrent || Total != lastTotal) {
|
||||
const cErrors *Errors = GetErrors();
|
||||
int NumErrors = Errors ? Errors->Size() : 0;
|
||||
if (Current != lastCurrent || Total != lastTotal || NumErrors != lastErrors) {
|
||||
if (Setup.ShowRemainingTime || Total != lastTotal) {
|
||||
int Index = Total;
|
||||
if (Setup.ShowRemainingTime)
|
||||
@ -5899,10 +5911,12 @@ bool cReplayControl::ShowProgress(bool Initial)
|
||||
}
|
||||
displayReplay->SetProgress(Current, Total);
|
||||
displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, FramesPerSecond()));
|
||||
displayReplay->SetErrors(Errors);
|
||||
displayReplay->Flush();
|
||||
lastCurrent = Current;
|
||||
lastTotal = Total;
|
||||
lastErrors = NumErrors;
|
||||
}
|
||||
lastTotal = Total;
|
||||
ShowMode();
|
||||
updateTimer.Set(PROGRESSTIMEOUT);
|
||||
return true;
|
||||
@ -6088,6 +6102,47 @@ void cReplayControl::MarkMove(int Frames, bool MarkRequired)
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayControl::ErrorJump(bool Forward)
|
||||
{
|
||||
const cErrors *Errors = GetErrors();
|
||||
int NumErrors = Errors ? Errors->Size() : 0;
|
||||
if (NumErrors > 0) {
|
||||
int Current, Total;
|
||||
if (GetIndex(Current, Total)) {
|
||||
if (Forward) {
|
||||
int Offset = 0;
|
||||
for (int i = 0; i < NumErrors; i++) {
|
||||
int Position = Errors->At(i);
|
||||
if (Position > Current + Offset) {
|
||||
int NextIFrame = SkipFrames(Position - Current) + Offset; // this takes us to the I-frame at or right after Position
|
||||
if (NextIFrame > Position) {
|
||||
if (SkipFrames(Offset + 1) == NextIFrame) { // means Current is the I-frame right before Position
|
||||
Offset = NextIFrame - Current;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Goto(Position, true); // this takes us to the I-frame at or right before Position
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Current < Total)
|
||||
Goto(Total, true);
|
||||
}
|
||||
else {
|
||||
for (int i = NumErrors - 1; i >= 0; i--) {
|
||||
if (Errors->At(i) < Current) {
|
||||
int Position = Errors->At(i);
|
||||
Goto(Position, true); // this takes us to the I-frame at or right before Position
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Current > 0)
|
||||
Goto(0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayControl::EditCut(void)
|
||||
{
|
||||
if (*fileName) {
|
||||
@ -6232,6 +6287,10 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
|
||||
case kMarkSkipBack: MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
|
||||
case kMarkSkipForward|k_Repeat:
|
||||
case kMarkSkipForward: MarkMove(+adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
|
||||
case kChanUp|k_Repeat:
|
||||
case kChanUp: ErrorJump(true); break;
|
||||
case kChanDn|k_Repeat:
|
||||
case kChanDn: ErrorJump(false); break;
|
||||
case kEditCut: EditCut(); break;
|
||||
case kEditTest: EditTest(); break;
|
||||
default: {
|
||||
|
6
menu.h
6
menu.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.h 5.2 2024/06/25 19:00:06 kls Exp $
|
||||
* $Id: menu.h 5.5 2024/10/11 14:10:50 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MENU_H
|
||||
@ -297,6 +297,7 @@ private:
|
||||
cMarks marks;
|
||||
bool marksModified;
|
||||
bool visible, modeOnly, shown, displayFrames;
|
||||
int lastErrors;
|
||||
int lastCurrent, lastTotal;
|
||||
bool lastPlay, lastForward;
|
||||
int lastSpeed;
|
||||
@ -315,6 +316,7 @@ private:
|
||||
void MarkToggle(void);
|
||||
void MarkJump(bool Forward);
|
||||
void MarkMove(int Frames, bool MarkRequired);
|
||||
void ErrorJump(bool Forward);
|
||||
void EditCut(void);
|
||||
void EditTest(void);
|
||||
public:
|
||||
@ -334,4 +336,6 @@ public:
|
||||
static void ClearLastReplayed(const char *FileName);
|
||||
};
|
||||
|
||||
void SetTrackDescriptions(int LiveChannel);
|
||||
|
||||
#endif //__MENU_H
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menuitems.c 5.1 2020/12/26 15:49:01 kls Exp $
|
||||
* $Id: menuitems.c 5.3 2025/01/29 10:20:17 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menuitems.h"
|
||||
@ -38,7 +38,6 @@ void cMenuEditItem::SetValue(const char *Value)
|
||||
{
|
||||
cString buffer = cString::sprintf("%s:\t%s", name, Value);
|
||||
SetText(buffer);
|
||||
cStatus::MsgOsdCurrentItem(buffer);
|
||||
}
|
||||
|
||||
void cMenuEditItem::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
|
||||
@ -497,10 +496,7 @@ void cMenuEditStrItem::AdvancePos(void)
|
||||
void cMenuEditStrItem::Set(void)
|
||||
{
|
||||
if (InEditMode()) {
|
||||
// This is an ugly hack to make editing strings work with the 'skincurses' plugin.
|
||||
const cFont *font = dynamic_cast<cSkinDisplayMenu *>(cSkinDisplay::Current())->GetTextAreaFont(false);
|
||||
if (!font || font->Width("W") != 1) // all characters have width == 1 in the font used by 'skincurses'
|
||||
font = cFont::GetFont(fontOsd);
|
||||
|
||||
int width = cSkinDisplay::Current()->EditableWidth();
|
||||
width -= font->Width("[]");
|
||||
|
@ -12,7 +12,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# how to reach the author.
|
||||
#
|
||||
# $Id: newplugin 5.1 2021/01/02 14:32:20 kls Exp $
|
||||
# $Id: newplugin 5.2 2025/02/12 22:22:20 kls Exp $
|
||||
|
||||
$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
|
||||
|
||||
@ -216,7 +216,6 @@ public:
|
||||
virtual bool Start(void);
|
||||
virtual void Stop(void);
|
||||
virtual void Housekeeping(void);
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cString Active(void);
|
||||
virtual time_t WakeupTime(void);
|
||||
virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
|
||||
@ -274,12 +273,6 @@ void cPlugin${PLUGIN_CLASS}::Housekeeping(void)
|
||||
// Perform any cleanup or other regular tasks.
|
||||
}
|
||||
|
||||
void cPlugin${PLUGIN_CLASS}::MainThreadHook(void)
|
||||
{
|
||||
// Perform actions in the context of the main program thread.
|
||||
// WARNING: Use with great care - see PLUGINS.html!
|
||||
}
|
||||
|
||||
cString cPlugin${PLUGIN_CLASS}::Active(void)
|
||||
{
|
||||
// Return a message string if shutdown should be postponed
|
||||
|
82
osdbase.c
82
osdbase.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osdbase.c 5.1 2024/01/19 12:10:47 kls Exp $
|
||||
* $Id: osdbase.c 5.9 2025/02/17 10:49:10 kls Exp $
|
||||
*/
|
||||
|
||||
#include "osdbase.h"
|
||||
@ -78,12 +78,16 @@ void cOsdObject::Show(void)
|
||||
cSkinDisplayMenu *cOsdMenu::displayMenu = NULL;
|
||||
int cOsdMenu::displayMenuCount = 0;
|
||||
int cOsdMenu::osdState = 0;
|
||||
cOsdMenu *cOsdMenu::topMenu = NULL;
|
||||
|
||||
cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
|
||||
{
|
||||
isMenu = true;
|
||||
digit = 0;
|
||||
hasHotkeys = false;
|
||||
if (!topMenu)
|
||||
topMenu = this;
|
||||
active = this == topMenu;
|
||||
displayMenuItems = 0;
|
||||
title = NULL;
|
||||
menuCategory = mcUnknown;
|
||||
@ -93,6 +97,7 @@ cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
|
||||
SetCols(c0, c1, c2, c3, c4);
|
||||
first = 0;
|
||||
lastOffset = -1;
|
||||
conveyStatus = true;
|
||||
current = marked = -1;
|
||||
subMenu = NULL;
|
||||
helpRed = helpGreen = helpYellow = helpBlue = NULL;
|
||||
@ -113,6 +118,8 @@ cOsdMenu::~cOsdMenu()
|
||||
cStatus::MsgOsdClear();
|
||||
if (!--displayMenuCount)
|
||||
DELETENULL(displayMenu);
|
||||
if (this == topMenu)
|
||||
topMenu = NULL;
|
||||
}
|
||||
|
||||
void cOsdMenu::SetMenuCategory(eMenuCategory MenuCategory)
|
||||
@ -125,6 +132,11 @@ void cOsdMenu::SetMenuSortMode(eMenuSortMode MenuSortMode)
|
||||
menuSortMode = MenuSortMode;
|
||||
}
|
||||
|
||||
void cOsdMenu::SetActive(bool Active)
|
||||
{
|
||||
active = Active;
|
||||
}
|
||||
|
||||
void cOsdMenu::SetDisplayMenu(void)
|
||||
{
|
||||
if (displayMenu) {
|
||||
@ -169,6 +181,7 @@ void cOsdMenu::SetStatus(const char *s)
|
||||
free(status);
|
||||
status = s ? strdup(s) : NULL;
|
||||
displayMenu->SetMessage(mtStatus, s);
|
||||
cStatus::MsgOsdStatusMessage(mtStatus, s);
|
||||
}
|
||||
|
||||
void cOsdMenu::SetTitle(const char *Title)
|
||||
@ -181,7 +194,8 @@ void cOsdMenu::DisplayHelp(bool Force)
|
||||
{
|
||||
if (!helpDisplayed || Force) {
|
||||
displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
|
||||
cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
|
||||
if (conveyStatus)
|
||||
cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
|
||||
helpDisplayed = true;
|
||||
}
|
||||
}
|
||||
@ -224,17 +238,28 @@ void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
|
||||
current = Item->Index();
|
||||
}
|
||||
|
||||
void cOsdMenu::DisplayNoStatus(void)
|
||||
{
|
||||
conveyStatus = false;
|
||||
Display();
|
||||
conveyStatus = true;
|
||||
}
|
||||
|
||||
void cOsdMenu::Display(void)
|
||||
{
|
||||
if (subMenu) {
|
||||
subMenu->Display();
|
||||
return;
|
||||
}
|
||||
if (!active)
|
||||
return;
|
||||
if (cOsdProvider::OsdSizeChanged(osdState))
|
||||
SetDisplayMenu();
|
||||
displayMenu->SetMessage(mtStatus, NULL);
|
||||
cStatus::MsgOsdStatusMessage(mtStatus, NULL);
|
||||
displayMenu->Clear();
|
||||
cStatus::MsgOsdClear();
|
||||
if (conveyStatus)
|
||||
cStatus::MsgOsdClear();
|
||||
if (menuCategory != displayMenu->MenuCategory())
|
||||
displayMenu->SetMenuCategory(menuCategory);
|
||||
displayMenu->SetMenuSortMode(menuSortMode);
|
||||
@ -242,13 +267,15 @@ void cOsdMenu::Display(void)
|
||||
displayMenuItems = displayMenu->MaxItems();
|
||||
displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
|
||||
displayMenu->SetTitle(title);
|
||||
cStatus::MsgOsdTitle(title);
|
||||
if (conveyStatus)
|
||||
cStatus::MsgOsdTitle(title);
|
||||
DisplayHelp(true);
|
||||
int count = Count();
|
||||
if (count > 0) {
|
||||
int ni = 0;
|
||||
for (cOsdItem *item = First(); item; item = Next(item)) {
|
||||
cStatus::MsgOsdItem(item->Text(), ni++);
|
||||
if (conveyStatus)
|
||||
cStatus::MsgOsdItem(item->Text(), ni++, item->Selectable());
|
||||
if (current < 0 && item->Selectable())
|
||||
current = item->Index();
|
||||
}
|
||||
@ -267,16 +294,18 @@ void cOsdMenu::Display(void)
|
||||
for (cOsdItem *item = Get(first); item; item = Next(item)) {
|
||||
bool CurrentSelectable = (i == current) && item->Selectable();
|
||||
item->SetMenuItem(displayMenu, i - first, CurrentSelectable, item->Selectable());
|
||||
if (CurrentSelectable)
|
||||
cStatus::MsgOsdCurrentItem(item->Text());
|
||||
if (CurrentSelectable) // not checking conveyStatus here!
|
||||
cStatus::MsgOsdCurrentItem(item->Text(), i);
|
||||
if (++n == displayMenuItems)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
displayMenu->SetScrollbar(count, first);
|
||||
if (!isempty(status))
|
||||
if (!isempty(status)) {
|
||||
displayMenu->SetMessage(mtStatus, status);
|
||||
cStatus::MsgOsdStatusMessage(mtStatus, status);
|
||||
}
|
||||
}
|
||||
|
||||
void cOsdMenu::SetCurrent(cOsdItem *Item)
|
||||
@ -299,9 +328,13 @@ void cOsdMenu::DisplayCurrent(bool Current)
|
||||
cOsdItem *item = Get(current);
|
||||
if (item) {
|
||||
item->SetMenuItem(displayMenu, current - first, Current && item->Selectable(), item->Selectable());
|
||||
if (Current && item->Selectable())
|
||||
cStatus::MsgOsdCurrentItem(item->Text());
|
||||
if (!Current)
|
||||
if (Current) {
|
||||
if (current - first >= displayMenuItems || current < first)
|
||||
DisplayNoStatus();
|
||||
else if (item->Selectable())
|
||||
cStatus::MsgOsdCurrentItem(item->Text(), current);
|
||||
}
|
||||
else
|
||||
item->SetFresh(true); // leaving the current item resets 'fresh'
|
||||
if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
|
||||
if (!MenuEditItem->DisplayHelp(Current))
|
||||
@ -321,7 +354,7 @@ void cOsdMenu::DisplayItem(cOsdItem *Item)
|
||||
bool Current = Index == current;
|
||||
Item->SetMenuItem(displayMenu, Offset, Current && Item->Selectable(), Item->Selectable());
|
||||
if (Current && Item->Selectable())
|
||||
cStatus::MsgOsdCurrentItem(Item->Text());
|
||||
cStatus::MsgOsdCurrentItem(Item->Text(), Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -355,7 +388,7 @@ void cOsdMenu::CursorUp(void)
|
||||
if (first > 0) {
|
||||
// make non-selectable items at the beginning visible:
|
||||
first = 0;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
return;
|
||||
}
|
||||
if (Setup.MenuScrollWrap)
|
||||
@ -371,11 +404,11 @@ void cOsdMenu::CursorUp(void)
|
||||
current = tmpCurrent;
|
||||
if (current < first) {
|
||||
first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else if (current > lastOnScreen) {
|
||||
first = max(0, current - displayMenuItems + 1);
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else
|
||||
DisplayCurrent(true);
|
||||
@ -393,7 +426,7 @@ void cOsdMenu::CursorDown(void)
|
||||
if (first < last - displayMenuItems) {
|
||||
// make non-selectable items at the end visible:
|
||||
first = last - displayMenuItems + 1;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
return;
|
||||
}
|
||||
if (Setup.MenuScrollWrap)
|
||||
@ -411,11 +444,11 @@ void cOsdMenu::CursorDown(void)
|
||||
first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
|
||||
if (first + displayMenuItems > last)
|
||||
first = max(0, last - displayMenuItems + 1);
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else if (current < first) {
|
||||
first = current;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else
|
||||
DisplayCurrent(true);
|
||||
@ -448,7 +481,7 @@ void cOsdMenu::PageUp(void)
|
||||
first = current - displayMenuItems + 1;
|
||||
}
|
||||
if (current != oldCurrent || first != oldFirst)
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
else if (Setup.MenuScrollWrap)
|
||||
CursorUp();
|
||||
}
|
||||
@ -480,7 +513,7 @@ void cOsdMenu::PageDown(void)
|
||||
first = current - displayMenuItems + 1;
|
||||
}
|
||||
if (current != oldCurrent || first != oldFirst)
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
else if (Setup.MenuScrollWrap)
|
||||
CursorDown();
|
||||
}
|
||||
@ -499,9 +532,10 @@ eOSState cOsdMenu::HotKey(eKeys Key)
|
||||
const char *s = item->Text();
|
||||
if (s && (s = skipspace(s)) != NULL) {
|
||||
if (*s == Key - k1 + '1') {
|
||||
DisplayCurrent(false);
|
||||
current = item->Index();
|
||||
RefreshCurrent();
|
||||
Display();
|
||||
DisplayCurrent(true);
|
||||
cRemote::Put(kOk, true);
|
||||
break;
|
||||
}
|
||||
@ -512,8 +546,10 @@ eOSState cOsdMenu::HotKey(eKeys Key)
|
||||
|
||||
eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
|
||||
{
|
||||
SetActive(false);
|
||||
delete subMenu;
|
||||
subMenu = SubMenu;
|
||||
subMenu->SetActive(true);
|
||||
subMenu->Display();
|
||||
return osContinue; // convenience return value
|
||||
}
|
||||
@ -522,6 +558,7 @@ eOSState cOsdMenu::CloseSubMenu(bool ReDisplay)
|
||||
{
|
||||
delete subMenu;
|
||||
subMenu = NULL;
|
||||
SetActive(true);
|
||||
if (ReDisplay) {
|
||||
RefreshCurrent();
|
||||
Display();
|
||||
@ -542,7 +579,8 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
|
||||
if (marked < 0 && item) {
|
||||
eOSState state = item->ProcessKey(Key);
|
||||
if (state != osUnknown) {
|
||||
DisplayCurrent(true);
|
||||
if (Key != kNone)
|
||||
DisplayCurrent(true);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osdbase.h 4.5 2018/01/25 15:09:23 kls Exp $
|
||||
* $Id: osdbase.h 5.2 2025/02/17 10:49:10 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __OSDBASE_H
|
||||
@ -87,11 +87,13 @@ private:
|
||||
static cSkinDisplayMenu *displayMenu;
|
||||
static int displayMenuCount;
|
||||
static int osdState;
|
||||
static cOsdMenu *topMenu;
|
||||
int displayMenuItems;
|
||||
char *title;
|
||||
int cols[cSkinDisplayMenu::MaxTabs];
|
||||
int first, current, marked;
|
||||
int lastOffset;
|
||||
bool conveyStatus;
|
||||
eMenuCategory menuCategory;
|
||||
eMenuSortMode menuSortMode;
|
||||
eMenuOrientation menuOrientation;
|
||||
@ -101,7 +103,10 @@ private:
|
||||
char *status;
|
||||
int digit;
|
||||
bool hasHotkeys;
|
||||
bool active;
|
||||
void SetActive(bool Active);
|
||||
void DisplayHelp(bool Force = false);
|
||||
void DisplayNoStatus(void);
|
||||
protected:
|
||||
void SetDisplayMenu(void);
|
||||
cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
|
||||
|
15
player.c
15
player.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: player.c 4.1 2020/05/18 16:47:29 kls Exp $
|
||||
* $Id: player.c 5.2 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#include "player.h"
|
||||
@ -70,14 +70,6 @@ cString cControl::GetHeader(void)
|
||||
return "";
|
||||
}
|
||||
|
||||
#if DEPRECATED_CCONTROL
|
||||
cControl *cControl::Control(bool Hidden)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
return (control && (!control->hidden || Hidden)) ? control : NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
cControl *cControl::Control(cMutexLock &MutexLock, bool Hidden)
|
||||
{
|
||||
MutexLock.Lock(&mutex);
|
||||
@ -87,9 +79,8 @@ cControl *cControl::Control(cMutexLock &MutexLock, bool Hidden)
|
||||
void cControl::Launch(cControl *Control)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
cControl *c = control; // keeps control from pointing to uninitialized memory TODO obsolete once DEPRECATED_CCONTROL is gone
|
||||
delete control;
|
||||
control = Control;
|
||||
delete c;
|
||||
}
|
||||
|
||||
void cControl::Attach(void)
|
||||
@ -99,7 +90,7 @@ void cControl::Attach(void)
|
||||
if (cDevice::PrimaryDevice()->AttachPlayer(control->player))
|
||||
control->attached = true;
|
||||
else {
|
||||
Skins.Message(mtError, tr("Channel locked (recording)!"));
|
||||
Skins.Message(mtError, tr("Primary device has no MPEG decoder, can't attach player!"));
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
13
player.h
13
player.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: player.h 5.1 2022/12/05 14:45:51 kls Exp $
|
||||
* $Id: player.h 5.6 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __PLAYER_H
|
||||
@ -54,6 +54,8 @@ public:
|
||||
bool IsAttached(void) { return device != NULL; }
|
||||
virtual double FramesPerSecond(void) { return DEFAULTFRAMESPERSECOND; }
|
||||
// Returns the number of frames per second of the currently played material.
|
||||
virtual const cErrors *GetErrors(void) { return NULL; }
|
||||
// Returns the frame indexes of errors in the recording (if any).
|
||||
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { return false; }
|
||||
// Returns the current and total frame index, optionally snapped to the
|
||||
// nearest I-frame.
|
||||
@ -115,15 +117,6 @@ public:
|
||||
static void Launch(cControl *Control);
|
||||
static void Attach(void);
|
||||
static void Shutdown(void);
|
||||
#define DEPRECATED_CCONTROL 1
|
||||
#if DEPRECATED_CCONTROL
|
||||
static cControl *Control(bool Hidden = false);
|
||||
///< Old version of this function, for backwards compatibility with plugins.
|
||||
///< Plugins should be changed to use the new version below, which does
|
||||
///< proper locking.
|
||||
///< Use of this function may result in program crashes in case replay is
|
||||
///< stopped immediately after starting it.
|
||||
#endif
|
||||
static cControl *Control(cMutexLock &MutexLock, bool Hidden = false);
|
||||
///< Returns the current replay control (if any) in case it is currently
|
||||
///< visible. If Hidden is true, the control will be returned even if it is
|
||||
|
3
plugin.c
3
plugin.c
@ -4,9 +4,10 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: plugin.c 4.4 2020/12/16 11:54:06 kls Exp $
|
||||
* $Id: plugin.c 5.1 2025/02/12 22:22:20 kls Exp $
|
||||
*/
|
||||
|
||||
#define MUTE_DEPRECATED_MAINTHREADHOOK
|
||||
#include "plugin.h"
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
|
5
plugin.h
5
plugin.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: plugin.h 4.1 2020/06/29 09:29:06 kls Exp $
|
||||
* $Id: plugin.h 5.1 2025/02/12 22:22:20 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __PLUGIN_H
|
||||
@ -43,6 +43,9 @@ public:
|
||||
virtual bool Start(void);
|
||||
virtual void Stop(void);
|
||||
virtual void Housekeeping(void);
|
||||
#ifndef MUTE_DEPRECATED_MAINTHREADHOOK
|
||||
[[deprecated("use proper locking instead")]]
|
||||
#endif
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cString Active(void);
|
||||
virtual time_t WakeupTime(void);
|
||||
|
18
po/ar.po
18
po/ar.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-10-16 11:16-0400\n"
|
||||
"Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
|
||||
"Language-Team: Arabic <ar@li.org>\n"
|
||||
@ -134,8 +134,8 @@ msgstr "News Magazine"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentary"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgstr "Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussion/Interview/Debate"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
msgstr "Show/Game Show"
|
||||
@ -212,8 +212,8 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Serious/Classical Music"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgstr "Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Traditional Music"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
msgstr "Jazz"
|
||||
@ -1534,8 +1534,8 @@ msgstr "الملحق"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "اعلى او اسفل ل الموضع الجديد و موافق للتحريك"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "القناة مقفلة للتسجيل"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "مساحة القرص غير كافية"
|
||||
@ -1578,9 +1578,13 @@ msgstr "Recording - restart anyway?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "restart anyway?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "الصوت "
|
||||
|
14
po/ca_ES.po
14
po/ca_ES.po
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-03-02 19:02+0100\n"
|
||||
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
|
||||
"Language-Team: Catalan <vdr@linuxtv.org>\n"
|
||||
@ -133,7 +133,7 @@ msgstr "Revista de not
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documental"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussió/Entrevista/Debat"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -211,7 +211,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Música clàssica/seriosa"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Música folklòrica/tradicional"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1533,8 +1533,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Amunt/Avall per una nova localització - OK per moure"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canal bloquejat (gravant)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Disc gairebé ple!"
|
||||
@ -1577,9 +1577,13 @@ msgstr "Gravant - Reiniciar de totes maneres?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "Reiniciar de totes maneres?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volum "
|
||||
|
14
po/cs_CZ.po
14
po/cs_CZ.po
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2010-05-06 11:00+0200\n"
|
||||
"Last-Translator: Aleš Juřík <ajurik@quick.cz>\n"
|
||||
"Language-Team: Czech <vdr@linuxtv.org>\n"
|
||||
@ -133,7 +133,7 @@ msgstr "zprávy"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "dokumentární"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "diskuze/rozhovor/debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -211,7 +211,7 @@ msgstr "rock/pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "vážná/klasická hudba"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "folk/country"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1533,8 +1533,8 @@ msgstr "Modul"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Nahoru/Dolu pro novou pozici - Ok přesune"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanál je blokovaný (nahrává se)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Disk bude brzy zaplněn!"
|
||||
@ -1577,9 +1577,13 @@ msgstr "Nahrávání - přesto restartovat?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "přesto restartovat?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Hlasitost "
|
||||
|
14
po/da_DK.po
14
po/da_DK.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
|
||||
"Language-Team: Danish <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1530,8 +1530,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Op/Ned for ny placering - OK for at flytte"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal blokeret (optagelse i gang)"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Kun lidt diskplads tilbage!"
|
||||
@ -1574,9 +1574,13 @@ msgstr "Optagelse igang - genstart alligevel?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "genstart alligevel?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Lydstyrke "
|
||||
|
16
po/de_DE.po
16
po/de_DE.po
@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-10 13:45+0100\n"
|
||||
"Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n"
|
||||
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr "Nachrichtenmagazin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentation"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskussion/Interview/Debatte"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -142,7 +142,7 @@ msgid "Content$Game Show/Quiz/Contest"
|
||||
msgstr "Spielshow/Quiz/Wettbewerb"
|
||||
|
||||
msgid "Content$Variety Show"
|
||||
msgstr "Variete-Show"
|
||||
msgstr "Varieté-Show"
|
||||
|
||||
msgid "Content$Talk Show"
|
||||
msgstr "Talkshow"
|
||||
@ -210,7 +210,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Ernste/Klassische Musik"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Volks/Traditionelle Musik"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1532,8 +1532,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Auf/Ab für neue Position - dann OK"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal blockiert (zeichnet auf)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr "Primäres Device hat keinen MPEG-Decoder, Wiedergabe nicht möglich!"
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Platte beinahe voll!"
|
||||
@ -1576,9 +1576,13 @@ msgstr "Aufnahme l
|
||||
msgid "restart anyway?"
|
||||
msgstr "trotzdem neu starten?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr "Fehler"
|
||||
|
||||
msgid "error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Lautstärke "
|
||||
|
14
po/el_GR.po
14
po/el_GR.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
|
||||
"Language-Team: Greek <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1530,8 +1530,8 @@ msgstr "
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "ÐÜíù/ÊÜôù ãéÜ íÝá èÝóç. ÌåôÜ ÏÊ"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Ôï êáíÜëé åßíáé áðïó÷ïëéìÝíï (Ãßíåôå åããñáöÞ)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Ï óêëçñüò êïíôåýåé íÜ ãåìßóåé!"
|
||||
@ -1574,9 +1574,13 @@ msgstr "
|
||||
msgid "restart anyway?"
|
||||
msgstr "ÔåëéêÜ íá ãßíåé åðáíåêêßíçóç?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "¸íôáóç "
|
||||
|
14
po/es_ES.po
14
po/es_ES.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-19 23:00+0100\n"
|
||||
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
|
||||
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Revista de noticias"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documental"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discusión/Entrevista/Debate"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Musica clásica"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Musica Folk/Tradicional"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Arriba/Abajo para mover - OK para confirmar"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "¡Canal bloqueado (grabando)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "¡Poco espacio en disco!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Grabaci
|
||||
msgid "restart anyway?"
|
||||
msgstr "¿reiniciar igualmente?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volumen "
|
||||
|
14
po/et_EE.po
14
po/et_EE.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
|
||||
"Language-Team: Estonian <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr "Uudisteajakiri"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentaal"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskussioon/intervjuu/debatt"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr "Rock/pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Klassikaline muusika"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/rahvamuusika"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1530,8 +1530,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "'Üles/Alla' uus asukoht - 'OK' kinnitus"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal lukus (salvestamine aktiivne)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Kõvaketas täis!"
|
||||
@ -1574,9 +1574,13 @@ msgstr "Salvestamine aktiivne - taaskäivitada ikkagi?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "taaskäivitada ikkagi?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Helitugevus "
|
||||
|
22
po/fi_FI.po
22
po/fi_FI.po
@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-15 15:52+0200\n"
|
||||
"Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n"
|
||||
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||
@ -134,7 +134,7 @@ msgstr "Uutismakasiini"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentti"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Keskustelu/haastattelu/väittely"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -212,7 +212,7 @@ msgstr "Rock/pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Vakava/klassinen musiikki"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/kansanmusiikki"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -797,7 +797,7 @@ msgid "Folder"
|
||||
msgstr "Kansio"
|
||||
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
msgstr "Koko"
|
||||
|
||||
msgid "This folder is currently in use - no changes are possible!"
|
||||
msgstr "Kansio on käytössä - muokkaukset eivät mahdollisia!"
|
||||
@ -846,7 +846,7 @@ msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Muokattava versio on jo olemassa - ylikirjoitetaanko?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
msgstr "Tallennustila ei riitä muokkaamiseen!"
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Tallenteen lisääminen leikkausjonoon epäonnistui!"
|
||||
@ -1526,7 +1526,7 @@ msgid "Button$Insert"
|
||||
msgstr "Lisää"
|
||||
|
||||
msgid "Button$Macro"
|
||||
msgstr ""
|
||||
msgstr "Makro"
|
||||
|
||||
msgid "Plugin"
|
||||
msgstr "Laajennos"
|
||||
@ -1534,8 +1534,8 @@ msgstr "Laajennos"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "'Ylös/Alas' uusi paikka - 'OK' hyväksy"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanava lukittu (tallennus käynnissä)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr "Ensisijaissa laitteessa ei ole MPEG-toistinta!"
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Tallennustila loppumassa!"
|
||||
@ -1578,8 +1578,12 @@ msgstr "Tallennus kesken - käynnistetäänkö uudelleen?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "käynnistetäänkö uudelleen?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
msgstr "virhettä"
|
||||
|
||||
msgid "error"
|
||||
msgstr "virhe"
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
|
14
po/fr_FR.po
14
po/fr_FR.po
@ -18,7 +18,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-04-14 10:16+0100\n"
|
||||
"Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n"
|
||||
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||
@ -141,7 +141,7 @@ msgstr "Magazine d'information"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentaire"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussion/Interview/Débat"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -219,7 +219,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Sérieux/Musique Classique"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Musique Traditionnelle"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1541,8 +1541,8 @@ msgstr "Module"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Haut/Bas -> nouvelle place - OK -> déplacer"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Chaîne verrouillée (enregistrement en cours) !"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Disque presque plein !"
|
||||
@ -1585,9 +1585,13 @@ msgstr "Enregistrement en cours - confirmer redémarrage ?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "confirmer redémarrage ?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
14
po/hr_HR.po
14
po/hr_HR.po
@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-03-17 19:00+0100\n"
|
||||
"Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
|
||||
"Language-Team: Croatian <vdr@linuxtv.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -210,7 +210,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1532,8 +1532,8 @@ msgstr "Dodatak"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gore/Dolje na novu poziciju - OK za potvdru"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Program zakljuèan (snimanje)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Malo prostora na disku!"
|
||||
@ -1576,9 +1576,13 @@ msgstr "Snimanje je u tijeku - ponovno pokreni unato
|
||||
msgid "restart anyway?"
|
||||
msgstr "ponovno pokreni unatoè?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Glasnoæa "
|
||||
|
14
po/hu_HU.po
14
po/hu_HU.po
@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.6\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2024-02-09 06:29+0000\n"
|
||||
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
|
||||
"Language-Team: Hungarian\n"
|
||||
@ -138,7 +138,7 @@ msgstr "Hírmagazin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentumfilm"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Beszélgetés/Interjú/Vita"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -216,7 +216,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Komolyzene/Klasszikus zene"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Népzene/Hagyományos zene"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1538,8 +1538,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Fel/Le egy új pozícióért - aztán OK"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Csatorna blokkolva (felvétel)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Adattároló majdnem tele!"
|
||||
@ -1582,9 +1582,13 @@ msgstr "Felvétel folyamatban - mégis újraindítani?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "mégis újraindítani?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr "hiba"
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Hangerő "
|
||||
|
18
po/it_IT.po
18
po/it_IT.po
@ -11,9 +11,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"PO-Revision-Date: 2024-04-02 23:20+0200\n"
|
||||
"Last-Translator: Gringo <vdr-italian@tiscali.it>\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2024-10-15 00:35+0200\n"
|
||||
"Last-Translator: Gringo <openpli@tiscali.it>\n"
|
||||
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -136,7 +136,7 @@ msgstr "Settimanale di attualità"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentario"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussione/Intervista/Dibattito"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -214,7 +214,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Musica Classica/Seria"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Musica Tradizionale/Folclore"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1536,8 +1536,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Su/Giù per nuova posizione - OK per spostare"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canale bloccato (in registrazione)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr "Il dispositivo primario non ha un decoder MPEG, impossibile collegare il lettore!"
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Poco spazio su disco!"
|
||||
@ -1580,9 +1580,13 @@ msgstr "Registrazione in corso - riavviare comunque?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "riavviare comunque?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr "errori"
|
||||
|
||||
msgid "error"
|
||||
msgstr "errore"
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
14
po/lt_LT.po
14
po/lt_LT.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-11 14:02+0200\n"
|
||||
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
||||
"Language-Team: Lithuanian <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr "Naujienų žurnalas"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentika"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskusijos/Interviu"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Klasikinė muzika"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Tradizinė muzika"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1530,8 +1530,8 @@ msgstr "Įskiepas"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Spauskite mygtukus į viršų / į apačią ir OK kad perkelti į kitą vietą"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanalas užblokuotas (įrašinėjama)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Mažai vietos diske!"
|
||||
@ -1574,9 +1574,13 @@ msgstr "Įrašinėjama - perkrauti vistiek?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "perkrati bet kokiu atveju?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Garsas "
|
||||
|
14
po/mk_MK.po
14
po/mk_MK.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-03-31 21:47+0100\n"
|
||||
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
|
||||
"Language-Team: Macedonian <kde-i18n-doc@kde.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr "Магазин"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Документарен"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Дискусија/Интервју/Дебата"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -210,7 +210,7 @@ msgstr "Рок/Поп"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Сериозна/Класична музика"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Фолк/Народна музика"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1532,8 +1532,8 @@ msgstr "Додаток"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Горе/Доле за нова позиција - OK за потврда"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Каналот е заклучен (снимање)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Малку простор на дискот!"
|
||||
@ -1576,9 +1576,13 @@ msgstr "Снимање во тек - рестартирај?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "сеедно рестартирај?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Глас "
|
||||
|
14
po/nl_NL.po
14
po/nl_NL.po
@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-10 19:43+0100\n"
|
||||
"Last-Translator: Erik Oomen <oomen.e@gmail.com>\n"
|
||||
"Language-Team: Dutch <vdr@linuxtv.org>\n"
|
||||
@ -136,7 +136,7 @@ msgstr "Nieuwsbulletin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentaire"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussie/Interview/Debat"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -214,7 +214,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Serieuze muziek/Klassieke muziek"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Traditionele muziek"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1536,8 +1536,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gebruik Omhoog/Omlaag - daarna Ok"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanaal geblokkeerd (neemt op)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Hardeschijf bijna vol!"
|
||||
@ -1580,9 +1580,13 @@ msgstr "Opname loopt - toch opnieuw starten?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "toch opnieuw starten?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
14
po/nn_NO.po
14
po/nn_NO.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
|
||||
"Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Opp/Ned for ny plass - OK for å flytte"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanalen er låst (opptak)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Lite ledig diskplass!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Gj
|
||||
msgid "restart anyway?"
|
||||
msgstr "starte på nytt likevel?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volum "
|
||||
|
14
po/pl_PL.po
14
po/pl_PL.po
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-02-19 00:42+0100\n"
|
||||
"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
|
||||
"Language-Team: Polish <vdr@linuxtv.org>\n"
|
||||
@ -135,7 +135,7 @@ msgstr "Magazyny informacyjne"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentalne"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Dyskusje/Wywiady/Debaty"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -213,7 +213,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Muzyka klasyczna"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Muzyka tradycyjna"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1535,8 +1535,8 @@ msgstr "Wtyczka"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Do góry/Na dół na nową pozycję - Ok zatwierdza"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanał zablokowany (trwa nagrywanie)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Mało miejsca na dysku!"
|
||||
@ -1579,9 +1579,13 @@ msgstr "Trwa nagrywanie - zrestartować mimo to?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "zrestartować mimo to?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Głośność "
|
||||
|
14
po/pt_PT.po
14
po/pt_PT.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2010-03-28 22:49+0100\n"
|
||||
"Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
|
||||
"Language-Team: Portuguese <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Notici
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentário"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussão/Entrevista/Debate"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Música Clássica/Séria"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Música Tradicional/Folclore"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Cima/Baixo para nova posição - OK para mover"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canal bloqueado (a gravar)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Espaço em disco baixo!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Grava
|
||||
msgid "restart anyway?"
|
||||
msgstr "reiniciar mesmo assim?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
14
po/ro_RO.po
14
po/ro_RO.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-11 22:26+0100\n"
|
||||
"Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
|
||||
"Language-Team: Romanian <vdr@linuxtv.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr "Magazin de ştiri"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentar"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discuţie/Interviu/Dezbatere"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -210,7 +210,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Muzică clasică/serioasă"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Muzică folk/tradiţională"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1532,8 +1532,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Sus/Jos pentru noua locaţie - OK pentru a muta"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canal blocat (înregistrare)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Spaţiul pe disc e foarte scăzut!"
|
||||
@ -1576,9 +1576,13 @@ msgstr "Tocmai se înregistrează - repornesc, totuşi?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "repornesc, totuşi?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volum "
|
||||
|
14
po/ru_RU.po
14
po/ru_RU.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2016-12-27 17:13+0100\n"
|
||||
"Last-Translator: Pridvorov Andrey <ua0lnj@bk.ru>\n"
|
||||
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Новостной журнал"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Документальные"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Дискуссия/Интервью/Дебаты"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Рок/Поп"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Классическая музыка"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Фольклор/Традиционная музыка"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Модуль"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Нажимайте \"Вверх\"/\"Вниз\" для выбора позиции, а затем \"OK\""
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Канал заблокирован (идёт запись)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Недостаточно места на диске!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Идёт запись - действительно перезапуст
|
||||
msgid "restart anyway?"
|
||||
msgstr "действительно перезапустить?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Громкость "
|
||||
|
14
po/sk_SK.po
14
po/sk_SK.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-17 18:59+0100\n"
|
||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||
"Language-Team: Slovak <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Spravodajsk
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentárny"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskusia/Rozhovor/debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Vá¾na/Klasická hudba"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "¥udová/Tradièná Hudba"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Modul"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Hore/Dole na novú pozíciu - Ok presune"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanál je zamknutý (nahráva sa)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Nedostatok miesta na disku!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Nahr
|
||||
msgid "restart anyway?"
|
||||
msgstr "Urèite re¹tartova»?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Hlasitos» "
|
||||
|
14
po/sl_SI.po
14
po/sl_SI.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2013-03-04 12:46+0100\n"
|
||||
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
|
||||
"Language-Team: Slovenian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Poro
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentarec"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskudija/Interviju/Debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rok/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Resna/Klasièna Glasba"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Narodna/Tradicionalna glasba"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Vstavek"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gor/Dol za novo poz. - Ok za premik"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Zaklenjen kanal (snemanje)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Premalo prostora na disku!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Snemanje - zares ponoven zagon?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "zares ponoven zagon?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Glasnost "
|
||||
|
14
po/sr_RS.po
14
po/sr_RS.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2013-03-16 15:05+0100\n"
|
||||
"Last-Translator: Zoran Turalija <zoran.turalija@gmail.com>\n"
|
||||
"Language-Team: Serbian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Novosti"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentarni"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskusija/Razgovor/Debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rok/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Ozbiljna/Klasièna muzika"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Narodna/Izvorna muzika"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Dodatak"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gore/Dole za novu poziciju - OK za potvrdu"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal zakljuèan (snimanje)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Malo prostora na disku!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Snimanje u toku - svejedno ponovno pokreni!!!?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "svejedno ponovo pokreni?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Jaèina "
|
||||
|
14
po/sv_SE.po
14
po/sv_SE.po
@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-12 21:58+0100\n"
|
||||
"Last-Translator: Magnus Sirviö <sirwio@hotmail.com>\n"
|
||||
"Language-Team: Swedish <vdr@linuxtv.org>\n"
|
||||
@ -135,7 +135,7 @@ msgstr "Nyhetsmagasin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentär"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskussion/Intervju/Debatt"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -213,7 +213,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Klassisk musik"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folkmusik"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1535,8 +1535,8 @@ msgstr "Modul"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Upp/Ner för nya platsen - därefter OK för att flytta"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanalen är låst (inspelning pågår)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Lågt diskutrymme!"
|
||||
@ -1579,9 +1579,13 @@ msgstr "Inspelning p
|
||||
msgid "restart anyway?"
|
||||
msgstr "vill du starta om ändå?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volym "
|
||||
|
14
po/tr_TR.po
14
po/tr_TR.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-02-28 00:33+0100\n"
|
||||
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
|
||||
"Language-Team: Turkish <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1530,8 +1530,8 @@ msgstr "Eklenti"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "'Yukarý/Aþaðý' yeni posizyon seç - 'Tamam' taþý"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal geçersiz (kayýt ediliyor)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Kayýt kapasitesi az!"
|
||||
@ -1574,9 +1574,13 @@ msgstr "Kay
|
||||
msgid "restart anyway?"
|
||||
msgstr "buna raðmen yeniden baþlat?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volüm "
|
||||
|
14
po/uk_UA.po
14
po/uk_UA.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-03-18 20:00+0100\n"
|
||||
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
|
||||
"Language-Team: Ukrainian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Журнал новин"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Документальне"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Дискусія/Інтерв’ю/Дебати"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Рок/Поп"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Серйозна/Класична музика"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Фльк/Традиційна музика"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1531,8 +1531,8 @@ msgstr "Модуль"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Натискайте \"Вверх\"/\"Вниз\" для вибору позиції, а потім \"OK\""
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Канал заблоковано (йде запис)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Недостатньо місця на диску!"
|
||||
@ -1575,9 +1575,13 @@ msgstr "Йде запис - дійсно перезапустити?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "дійсно перезапустити?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Гучність "
|
||||
|
16
po/zh_CN.po
16
po/zh_CN.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2024-06-13 11:21+0200\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2013-03-04 14:52+0800\n"
|
||||
"Last-Translator: NFVDR <nfvdr@live.com>\n"
|
||||
"Language-Team: Chinese (simplified) <nfvdr@live.com>\n"
|
||||
@ -132,8 +132,8 @@ msgstr "新闻杂志"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "记录片"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgstr "讨论/ Inverview/辩论"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "讨论/ Interview/辩论"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
msgstr "显示/游戏展"
|
||||
@ -210,7 +210,7 @@ msgstr "摇滚/流行"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "严重/古典音乐"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "民谣/传统体育专业课程设置的音乐"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -1532,8 +1532,8 @@ msgstr "插件"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "上/下 新的定位 - OK 移动"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "频道已经锁定 (录像)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "磁盘空间不足!"
|
||||
@ -1576,9 +1576,13 @@ msgstr "录像中,是否现在重启?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "现在重启?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "音量调整 "
|
||||
|
197
recorder.c
197
recorder.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recorder.c 5.6 2024/01/20 20:04:03 kls Exp $
|
||||
* $Id: recorder.c 5.10 2024/09/19 09:49:02 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recorder.h"
|
||||
@ -19,160 +19,18 @@
|
||||
#define MINFREEDISKSPACE (512) // MB
|
||||
#define DISKCHECKINTERVAL 100 // seconds
|
||||
|
||||
static bool DebugChecks = false;
|
||||
|
||||
// cTsChecker and cFrameChecker are used to detect errors in the recorded data stream.
|
||||
// While cTsChecker checks the continuity counter of the incoming TS packets, cFrameChecker
|
||||
// works on entire frames, checking their PTS (Presentation Time Stamps) to see whether
|
||||
// all expected frames arrive. The resulting number of errors is not a precise value.
|
||||
// If it is zero, the recording can be safely considered error free. The higher the value,
|
||||
// the more damaged the recording is.
|
||||
|
||||
// --- cTsChecker ------------------------------------------------------------
|
||||
|
||||
#define TS_CC_UNKNOWN 0xFF
|
||||
|
||||
class cTsChecker {
|
||||
private:
|
||||
uchar counter[MAXPID];
|
||||
int errors;
|
||||
void Report(int Pid, const char *Message);
|
||||
public:
|
||||
cTsChecker(void);
|
||||
void CheckTs(const uchar *Data, int Length);
|
||||
int Errors(void) { return errors; }
|
||||
};
|
||||
|
||||
cTsChecker::cTsChecker(void)
|
||||
{
|
||||
memset(counter, TS_CC_UNKNOWN, sizeof(counter));
|
||||
errors = 0;
|
||||
}
|
||||
|
||||
void cTsChecker::Report(int Pid, const char *Message)
|
||||
{
|
||||
errors++;
|
||||
if (DebugChecks)
|
||||
fprintf(stderr, "%s: TS error #%d on PID %d (%s)\n", *TimeToString(time(NULL)), errors, Pid, Message);
|
||||
}
|
||||
|
||||
void cTsChecker::CheckTs(const uchar *Data, int Length)
|
||||
{
|
||||
int Pid = TsPid(Data);
|
||||
uchar Cc = TsContinuityCounter(Data);
|
||||
if (TsHasPayload(Data)) {
|
||||
if (TsError(Data))
|
||||
Report(Pid, "tei");
|
||||
else if (TsIsScrambled(Data))
|
||||
Report(Pid, "scrambled");
|
||||
else {
|
||||
uchar OldCc = counter[Pid];
|
||||
if (OldCc != TS_CC_UNKNOWN) {
|
||||
uchar NewCc = (OldCc + 1) & TS_CONT_CNT_MASK;
|
||||
if (Cc != NewCc)
|
||||
Report(Pid, "continuity");
|
||||
}
|
||||
}
|
||||
}
|
||||
counter[Pid] = Cc;
|
||||
}
|
||||
|
||||
// --- cFrameChecker ---------------------------------------------------------
|
||||
|
||||
#define MAX_BACK_REFS 32
|
||||
|
||||
class cFrameChecker {
|
||||
private:
|
||||
int frameDelta;
|
||||
int64_t lastPts;
|
||||
uint32_t backRefs;
|
||||
int lastFwdRef;
|
||||
int errors;
|
||||
void Report(const char *Message, int NumErrors = 1);
|
||||
public:
|
||||
cFrameChecker(void);
|
||||
void SetFrameDelta(int FrameDelta) { frameDelta = FrameDelta; }
|
||||
void CheckFrame(const uchar *Data, int Length);
|
||||
void ReportBroken(void);
|
||||
int Errors(void) { return errors; }
|
||||
};
|
||||
|
||||
cFrameChecker::cFrameChecker(void)
|
||||
{
|
||||
frameDelta = PTSTICKS / DEFAULTFRAMESPERSECOND;
|
||||
lastPts = -1;
|
||||
backRefs = 0;
|
||||
lastFwdRef = 0;
|
||||
errors = 0;
|
||||
}
|
||||
|
||||
void cFrameChecker::Report(const char *Message, int NumErrors)
|
||||
{
|
||||
errors += NumErrors;
|
||||
if (DebugChecks)
|
||||
fprintf(stderr, "%s: frame error #%d (%s)\n", *TimeToString(time(NULL)), errors, Message);
|
||||
}
|
||||
|
||||
void cFrameChecker::CheckFrame(const uchar *Data, int Length)
|
||||
{
|
||||
int64_t Pts = TsGetPts(Data, Length);
|
||||
if (Pts >= 0) {
|
||||
if (lastPts >= 0) {
|
||||
int Diff = int(round((PtsDiff(lastPts, Pts) / double(frameDelta))));
|
||||
if (Diff > 0) {
|
||||
if (Diff <= MAX_BACK_REFS) {
|
||||
if (lastFwdRef > 1) {
|
||||
if (backRefs != uint32_t((1 << (lastFwdRef - 1)) - 1))
|
||||
Report("missing backref");
|
||||
}
|
||||
}
|
||||
else
|
||||
Report("missed", Diff);
|
||||
backRefs = 0;
|
||||
lastFwdRef = Diff;
|
||||
lastPts = Pts;
|
||||
}
|
||||
else if (Diff < 0) {
|
||||
Diff = -Diff;
|
||||
if (Diff <= MAX_BACK_REFS) {
|
||||
int b = 1 << (Diff - 1);
|
||||
if ((backRefs & b) != 0)
|
||||
Report("duplicate backref");
|
||||
backRefs |= b;
|
||||
}
|
||||
else
|
||||
Report("rev diff too big");
|
||||
}
|
||||
else
|
||||
Report("zero diff");
|
||||
}
|
||||
else
|
||||
lastPts = Pts;
|
||||
}
|
||||
else
|
||||
Report("no PTS");
|
||||
}
|
||||
|
||||
void cFrameChecker::ReportBroken(void)
|
||||
{
|
||||
int MissedFrames = MAXBROKENTIMEOUT / 1000 * PTSTICKS / frameDelta;
|
||||
Report("missed", MissedFrames);
|
||||
}
|
||||
|
||||
// --- cRecorder -------------------------------------------------------------
|
||||
|
||||
cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
|
||||
:cReceiver(Channel, Priority)
|
||||
,cThread("recording")
|
||||
{
|
||||
tsChecker = new cTsChecker;
|
||||
frameChecker = new cFrameChecker;
|
||||
recordingName = strdup(FileName);
|
||||
recordingInfo = new cRecordingInfo(recordingName);
|
||||
recordingInfo->Read();
|
||||
oldErrors = max(0, recordingInfo->Errors()); // in case this is a re-started recording
|
||||
errors = oldErrors;
|
||||
lastErrors = errors;
|
||||
errors = 0;
|
||||
lastErrors = 0;
|
||||
firstIframeSeen = false;
|
||||
|
||||
// Make sure the disk is up and running:
|
||||
@ -220,8 +78,6 @@ cRecorder::~cRecorder()
|
||||
delete fileName;
|
||||
delete frameDetector;
|
||||
delete ringBuffer;
|
||||
delete frameChecker;
|
||||
delete tsChecker;
|
||||
free(recordingName);
|
||||
}
|
||||
|
||||
@ -231,18 +87,15 @@ void cRecorder::HandleErrors(bool Force)
|
||||
{
|
||||
// We don't log every single error separately, to avoid spamming the log file:
|
||||
if (Force || time(NULL) - lastErrorLog >= ERROR_LOG_DELTA) {
|
||||
errors = tsChecker->Errors() + frameChecker->Errors();
|
||||
if (errors > lastErrors) {
|
||||
int d = errors - lastErrors;
|
||||
if (DebugChecks)
|
||||
fprintf(stderr, "%s: %s: %d new error%s (total %d)\n", *TimeToString(time(NULL)), recordingName, d, d > 1 ? "s" : "", errors);
|
||||
esyslog("%s: %d new error%s (total %d)", recordingName, d, d > 1 ? "s" : "", errors);
|
||||
esyslog("%s: %d new error%s (total %d)", recordingName, d, d > 1 ? "s" : "", oldErrors + errors);
|
||||
recordingInfo->SetErrors(oldErrors + errors);
|
||||
recordingInfo->Write();
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->UpdateByName(recordingName);
|
||||
lastErrors = errors;
|
||||
}
|
||||
lastErrors = errors;
|
||||
lastErrorLog = time(NULL);
|
||||
}
|
||||
}
|
||||
@ -307,8 +160,6 @@ void cRecorder::Receive(const uchar *Data, int Length)
|
||||
int p = ringBuffer->Put(Data, Length);
|
||||
if (p != Length && Running())
|
||||
ringBuffer->ReportOverflow(Length - p);
|
||||
else if (firstIframeSeen) // we ignore any garbage before the first I-frame
|
||||
tsChecker->CheckTs(Data, Length);
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,6 +167,15 @@ void cRecorder::Action(void)
|
||||
{
|
||||
cTimeMs t(MAXBROKENTIMEOUT);
|
||||
bool InfoWritten = false;
|
||||
bool pendIndependentFrame = false;
|
||||
uint16_t pendNumber = 0;
|
||||
off_t pendFileSize = 0;
|
||||
bool pendErrors = false;
|
||||
bool pendMissing = false;
|
||||
// Check if this is a resumed recording, in which case we definitely missed frames:
|
||||
NextFile();
|
||||
if (fileName->Number() > 1 || oldErrors)
|
||||
frameDetector->SetMissing();
|
||||
while (Running()) {
|
||||
int r;
|
||||
uchar *b = ringBuffer->Get(r);
|
||||
@ -338,17 +198,27 @@ void cRecorder::Action(void)
|
||||
}
|
||||
InfoWritten = true;
|
||||
cRecordingUserCommand::InvokeCommand(RUC_STARTRECORDING, recordingName);
|
||||
frameChecker->SetFrameDelta(PTSTICKS / frameDetector->FramesPerSecond());
|
||||
}
|
||||
if (firstIframeSeen || frameDetector->IndependentFrame()) {
|
||||
firstIframeSeen = true; // start recording with the first I-frame
|
||||
if (!NextFile())
|
||||
break;
|
||||
if (frameDetector->NewFrame()) {
|
||||
if (index)
|
||||
index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize);
|
||||
if (frameChecker)
|
||||
frameChecker->CheckFrame(b, Count);
|
||||
int PreviousErrors = 0;
|
||||
int MissingFrames = 0;
|
||||
if (frameDetector->NewFrame(&PreviousErrors, &MissingFrames)) {
|
||||
if (index) {
|
||||
if (pendNumber > 0)
|
||||
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
pendIndependentFrame = frameDetector->IndependentFrame();
|
||||
pendNumber = fileName->Number();
|
||||
pendFileSize = fileSize;
|
||||
pendErrors = PreviousErrors;
|
||||
pendMissing = MissingFrames;
|
||||
}
|
||||
if (PreviousErrors)
|
||||
errors++;
|
||||
if (MissingFrames)
|
||||
errors++;
|
||||
}
|
||||
if (frameDetector->IndependentFrame()) {
|
||||
recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE);
|
||||
@ -372,12 +242,17 @@ void cRecorder::Action(void)
|
||||
}
|
||||
}
|
||||
if (t.TimedOut()) {
|
||||
frameChecker->ReportBroken();
|
||||
if (pendNumber > 0)
|
||||
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
frameDetector->SetMissing();
|
||||
errors += MAXBROKENTIMEOUT / 1000 * frameDetector->FramesPerSecond();
|
||||
HandleErrors(true);
|
||||
esyslog("ERROR: video data stream broken");
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
t.Set(MAXBROKENTIMEOUT);
|
||||
}
|
||||
}
|
||||
if (pendNumber > 0)
|
||||
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
HandleErrors(true);
|
||||
}
|
||||
|
10
recorder.h
10
recorder.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recorder.h 5.1 2021/05/19 11:22:20 kls Exp $
|
||||
* $Id: recorder.h 5.3 2024/09/17 09:39:50 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RECORDER_H
|
||||
@ -16,13 +16,8 @@
|
||||
#include "ringbuffer.h"
|
||||
#include "thread.h"
|
||||
|
||||
class cTsChecker;
|
||||
class cFrameChecker;
|
||||
|
||||
class cRecorder : public cReceiver, cThread {
|
||||
private:
|
||||
cTsChecker *tsChecker;
|
||||
cFrameChecker *frameChecker;
|
||||
cRingBufferLinear *ringBuffer;
|
||||
cFrameDetector *frameDetector;
|
||||
cPatPmtGenerator patPmtGenerator;
|
||||
@ -56,6 +51,9 @@ public:
|
||||
virtual ~cRecorder();
|
||||
int Errors(void) { return oldErrors + errors; };
|
||||
///< Returns the number of errors that were detected during recording.
|
||||
///< Each frame that is missing or contains (any number of) errors counts as one error.
|
||||
///< If this is a resumed recording, this includes errors that occurred
|
||||
///< in the previous parts.
|
||||
};
|
||||
|
||||
#endif //__RECORDER_H
|
||||
|
84
recording.c
84
recording.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.c 5.28 2024/06/13 09:31:11 kls Exp $
|
||||
* $Id: recording.c 5.37 2025/01/18 20:57:06 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recording.h"
|
||||
@ -356,6 +356,7 @@ void cResumeFile::Delete(void)
|
||||
|
||||
cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
|
||||
{
|
||||
modified = 0;
|
||||
channelID = Channel ? Channel->GetChannelID() : tChannelID::InvalidID;
|
||||
channelName = Channel ? strdup(Channel->Name()) : NULL;
|
||||
ownEvent = Event ? NULL : new cEvent(0);
|
||||
@ -420,6 +421,7 @@ cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
|
||||
|
||||
cRecordingInfo::cRecordingInfo(const char *FileName)
|
||||
{
|
||||
modified = 0;
|
||||
channelID = tChannelID::InvalidID;
|
||||
channelName = NULL;
|
||||
ownEvent = new cEvent(0);
|
||||
@ -488,6 +490,12 @@ void cRecordingInfo::SetErrors(int Errors)
|
||||
bool cRecordingInfo::Read(FILE *f)
|
||||
{
|
||||
if (ownEvent) {
|
||||
struct stat st;
|
||||
if (fstat(fileno(f), &st))
|
||||
return false;
|
||||
if (modified == st.st_mtime)
|
||||
return true;
|
||||
modified = st.st_mtime;
|
||||
cReadLine ReadLine;
|
||||
char *s;
|
||||
int line = 0;
|
||||
@ -1346,6 +1354,7 @@ bool cRecording::ChangeName(const char *NewName)
|
||||
fileName = strdup(OldFileName);
|
||||
return false;
|
||||
}
|
||||
info->SetFileName(NewFileName);
|
||||
isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system
|
||||
ClearSortName();
|
||||
}
|
||||
@ -1631,7 +1640,7 @@ const char *cRecordings::UpdateFileName(void)
|
||||
void cRecordings::TouchUpdate(void)
|
||||
{
|
||||
bool needsUpdate = NeedsUpdate();
|
||||
TouchFile(UpdateFileName());
|
||||
TouchFile(UpdateFileName(), true);
|
||||
if (!needsUpdate)
|
||||
lastUpdate = time(NULL); // make sure we don't trigger ourselves
|
||||
}
|
||||
@ -2078,8 +2087,10 @@ void cRecordingsHandlerEntry::Cleanup(cRecordings *Recordings)
|
||||
delete cutter;
|
||||
cutter = NULL;
|
||||
}
|
||||
cVideoDirectory::RemoveVideoFile(fileNameDst);
|
||||
if (cRecording *Recording = Recordings->GetByName(fileNameDst))
|
||||
Recording->Delete();
|
||||
Recordings->DelByName(fileNameDst);
|
||||
Recordings->SetModified();
|
||||
}
|
||||
}
|
||||
if ((usage & (ruMove | ruCopy)) // this was a move/copy operation...
|
||||
@ -2090,10 +2101,12 @@ void cRecordingsHandlerEntry::Cleanup(cRecordings *Recordings)
|
||||
delete copier;
|
||||
copier = NULL;
|
||||
}
|
||||
cVideoDirectory::RemoveVideoFile(fileNameDst);
|
||||
if (cRecording *Recording = Recordings->GetByName(fileNameDst))
|
||||
Recording->Delete();
|
||||
if ((usage & ruMove) != 0)
|
||||
Recordings->AddByName(fileNameSrc);
|
||||
Recordings->DelByName(fileNameDst);
|
||||
Recordings->SetModified();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2546,6 +2559,12 @@ void cIndexFileGenerator::Action(void)
|
||||
uint16_t FileNumber = 1;
|
||||
off_t FileOffset = 0;
|
||||
int Last = -1;
|
||||
bool pendIndependentFrame = false;
|
||||
uint16_t pendNumber = 0;
|
||||
off_t pendFileSize = 0;
|
||||
bool pendErrors = false;
|
||||
bool pendMissing = false;
|
||||
int Errors = 0;
|
||||
if (update) {
|
||||
// Look for current index and position to end of it if present:
|
||||
bool Independent;
|
||||
@ -2581,11 +2600,24 @@ void cIndexFileGenerator::Action(void)
|
||||
FrameOffset = FileSize; // the PAT/PMT is at the beginning of an I-frame
|
||||
int Processed = FrameDetector.Analyze(Data, Length);
|
||||
if (Processed > 0) {
|
||||
if (FrameDetector.NewFrame()) {
|
||||
if (IndexFileWritten || Last < 0) // check for first frame and do not write if in update mode
|
||||
IndexFile.Write(FrameDetector.IndependentFrame(), FileName.Number(), FrameOffset >= 0 ? FrameOffset : FileSize);
|
||||
int PreviousErrors = 0;
|
||||
int MissingFrames = 0;
|
||||
if (FrameDetector.NewFrame(&PreviousErrors, &MissingFrames)) {
|
||||
if (IndexFileWritten || Last < 0) { // check for first frame and do not write if in update mode
|
||||
if (pendNumber > 0)
|
||||
IndexFile.Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
pendIndependentFrame = FrameDetector.IndependentFrame();
|
||||
pendNumber = FileName.Number();
|
||||
pendFileSize = FrameOffset >= 0 ? FrameOffset : FileSize;
|
||||
pendErrors = PreviousErrors;
|
||||
pendMissing = MissingFrames;
|
||||
}
|
||||
FrameOffset = -1;
|
||||
IndexFileWritten = true;
|
||||
if (PreviousErrors)
|
||||
Errors++;
|
||||
if (MissingFrames)
|
||||
Errors++;
|
||||
}
|
||||
FileSize += Processed;
|
||||
Buffer.Del(Processed);
|
||||
@ -2593,7 +2625,7 @@ void cIndexFileGenerator::Action(void)
|
||||
}
|
||||
else if (PatPmtParser.Completed()) {
|
||||
// Step 2 - sync FrameDetector:
|
||||
int Processed = FrameDetector.Analyze(Data, Length);
|
||||
int Processed = FrameDetector.Analyze(Data, Length, false);
|
||||
if (Processed > 0) {
|
||||
if (FrameDetector.Synced()) {
|
||||
// Synced FrameDetector, so rewind for actual processing:
|
||||
@ -2651,6 +2683,8 @@ void cIndexFileGenerator::Action(void)
|
||||
}
|
||||
// Recording has been processed:
|
||||
else {
|
||||
if (pendNumber > 0)
|
||||
IndexFile.Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
IndexFileComplete = true;
|
||||
break;
|
||||
}
|
||||
@ -2663,9 +2697,11 @@ void cIndexFileGenerator::Action(void)
|
||||
if ((FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) ||
|
||||
FrameDetector.FrameWidth() != RecordingInfo.FrameWidth() ||
|
||||
FrameDetector.FrameHeight() != RecordingInfo.FrameHeight() ||
|
||||
FrameDetector.AspectRatio() != RecordingInfo.AspectRatio()) {
|
||||
FrameDetector.AspectRatio() != RecordingInfo.AspectRatio() ||
|
||||
Errors != RecordingInfo.Errors()) {
|
||||
RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond());
|
||||
RecordingInfo.SetFrameParams(FrameDetector.FrameWidth(), FrameDetector.FrameHeight(), FrameDetector.ScanType(), FrameDetector.AspectRatio());
|
||||
RecordingInfo.SetErrors(Errors);
|
||||
RecordingInfo.Write();
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->UpdateByName(recordingName);
|
||||
@ -2698,13 +2734,17 @@ struct __attribute__((packed)) tIndexPes {
|
||||
|
||||
struct __attribute__((packed)) tIndexTs {
|
||||
uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!)
|
||||
int reserved:7; // reserved for future use
|
||||
int reserved:5; // reserved for future use
|
||||
int errors:1; // 1=this frame contains errors
|
||||
int missing:1; // 1=there are frames missing after this one
|
||||
int independent:1; // marks frames that can be displayed by themselves (for trick modes)
|
||||
uint16_t number:16; // up to 64K files per recording
|
||||
tIndexTs(off_t Offset, bool Independent, uint16_t Number)
|
||||
tIndexTs(off_t Offset, bool Independent, uint16_t Number, bool Errors, bool Missing)
|
||||
{
|
||||
offset = Offset;
|
||||
reserved = 0;
|
||||
errors = Errors;
|
||||
missing = Missing;
|
||||
independent = Independent;
|
||||
number = Number;
|
||||
}
|
||||
@ -2720,6 +2760,7 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, b
|
||||
f = -1;
|
||||
size = 0;
|
||||
last = -1;
|
||||
lastErrorIndex = last;
|
||||
index = NULL;
|
||||
isPesRecording = IsPesRecording;
|
||||
indexFileGenerator = NULL;
|
||||
@ -2897,10 +2938,10 @@ bool cIndexFile::CatchUp(int Index)
|
||||
return index != NULL;
|
||||
}
|
||||
|
||||
bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
|
||||
bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors, bool Missing)
|
||||
{
|
||||
if (f >= 0) {
|
||||
tIndexTs i(FileOffset, Independent, FileNumber);
|
||||
tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
|
||||
if (isPesRecording)
|
||||
ConvertToPes(&i, 1);
|
||||
if (safe_write(f, &i, sizeof(i)) < 0) {
|
||||
@ -2914,7 +2955,7 @@ bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
|
||||
return f >= 0;
|
||||
}
|
||||
|
||||
bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length)
|
||||
bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length, bool *Errors, bool *Missing)
|
||||
{
|
||||
if (CatchUp(Index)) {
|
||||
if (Index >= 0 && Index <= last) {
|
||||
@ -2934,12 +2975,27 @@ bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *I
|
||||
else
|
||||
*Length = -1;
|
||||
}
|
||||
if (Errors)
|
||||
*Errors = index[Index].errors;
|
||||
if (Missing)
|
||||
*Missing = index[Index].missing;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const cErrors *cIndexFile::GetErrors(void)
|
||||
{
|
||||
for (int Index = lastErrorIndex + 1; Index <= last; Index++) {
|
||||
tIndexTs *p = &index[Index];
|
||||
if (p->errors || p->missing)
|
||||
errors.Append(Index);
|
||||
}
|
||||
lastErrorIndex = last;
|
||||
return &errors;
|
||||
}
|
||||
|
||||
int cIndexFile::GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber, off_t *FileOffset, int *Length)
|
||||
{
|
||||
if (CatchUp()) {
|
||||
|
14
recording.h
14
recording.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.h 5.8 2024/06/13 09:31:11 kls Exp $
|
||||
* $Id: recording.h 5.12 2025/01/15 10:50:29 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RECORDING_H
|
||||
@ -64,6 +64,7 @@ public:
|
||||
class cRecordingInfo {
|
||||
friend class cRecording;
|
||||
private:
|
||||
time_t modified;
|
||||
tChannelID channelID;
|
||||
char *channelName;
|
||||
const cEvent *event;
|
||||
@ -446,6 +447,9 @@ public:
|
||||
cMark *GetNextEnd(const cMark *BeginMark) { return const_cast<cMark *>(static_cast<const cMarks *>(this)->GetNextEnd(BeginMark)); }
|
||||
};
|
||||
|
||||
class cErrors : public cVector<int> {
|
||||
};
|
||||
|
||||
#define RUC_BEFORERECORDING "before"
|
||||
#define RUC_STARTRECORDING "started"
|
||||
#define RUC_AFTERRECORDING "after"
|
||||
@ -486,9 +490,11 @@ private:
|
||||
int f;
|
||||
cString fileName;
|
||||
int size, last;
|
||||
int lastErrorIndex;
|
||||
tIndexTs *index;
|
||||
bool isPesRecording;
|
||||
cResumeFile resumeFile;
|
||||
cErrors errors;
|
||||
cIndexFileGenerator *indexFileGenerator;
|
||||
cMutex mutex;
|
||||
void ConvertFromPes(tIndexTs *IndexTs, int Count);
|
||||
@ -498,8 +504,10 @@ public:
|
||||
cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false, bool PauseLive = false, bool Update = false);
|
||||
~cIndexFile();
|
||||
bool Ok(void) { return index != NULL; }
|
||||
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset);
|
||||
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL);
|
||||
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors = false, bool Missing = false);
|
||||
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL, bool *Errors = NULL, bool *Missing = NULL);
|
||||
const cErrors *GetErrors(void);
|
||||
///< Returns the frame indexes of errors in the recording (if any).
|
||||
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL);
|
||||
int GetClosestIFrame(int Index);
|
||||
///< Returns the index of the I-frame that is closest to the given Index (or Index itself,
|
||||
|
199
remux.c
199
remux.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: remux.c 5.7 2024/01/23 19:33:45 kls Exp $
|
||||
* $Id: remux.c 5.18 2024/12/05 10:37:15 kls Exp $
|
||||
*/
|
||||
|
||||
#include "remux.h"
|
||||
@ -287,6 +287,8 @@ uchar cTsPayload::GetByte(void)
|
||||
if (TsPid(p) == pid) { // only handle TS packets for the initial PID
|
||||
if (++numPacketsPid > MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION)
|
||||
return SetEof();
|
||||
if (TsError(p))
|
||||
return SetEof(); // don't parse TS packets with errors
|
||||
if (TsHasPayload(p)) {
|
||||
if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet
|
||||
return SetEof();
|
||||
@ -1743,7 +1745,7 @@ void cH265Parser::ParseSequenceParameterSet(void)
|
||||
uint8_t sub_layer_profile_present_flag[8];
|
||||
uint8_t sub_layer_level_present_flag[8];
|
||||
GetBits(4); // sps_video_parameter_set_id
|
||||
int sps_max_sub_layers_minus1 = GetBits(3); // sps_max_sub_layers_minus1
|
||||
int sps_max_sub_layers_minus1 = GetBits(3) & 7; // sps_max_sub_layers_minus1 ("& 7" to silence a compiler warning with gcc 14.1.0)
|
||||
GetBit(); // sps_temporal_id_nesting_flag
|
||||
// begin profile_tier_level(1, sps_max_sub_layers_minus1)
|
||||
GetByte();
|
||||
@ -1931,6 +1933,163 @@ void cH265Parser::ParseSequenceParameterSet(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool DebugChecks = false;
|
||||
|
||||
// cTsChecker and cFrameChecker are used to detect errors in the recorded data stream.
|
||||
// While cTsChecker checks the continuity counter of the incoming TS packets, cFrameChecker
|
||||
// works on entire frames, checking their PTS (Presentation Time Stamps) to see whether
|
||||
// all expected frames arrive. The resulting number of errors is not a precise value.
|
||||
// If it is zero, the recording can be safely considered error free. The higher the value,
|
||||
// the more damaged the recording is.
|
||||
|
||||
// --- cTsChecker ------------------------------------------------------------
|
||||
|
||||
#define TS_CC_UNKNOWN 0xFF
|
||||
|
||||
class cTsChecker {
|
||||
private:
|
||||
uchar counter[MAXPID];
|
||||
int errors;
|
||||
void Report(int Pid, const char *Message);
|
||||
public:
|
||||
cTsChecker(void);
|
||||
void CheckTs(const uchar *Data, int Length);
|
||||
int Errors(void) { return errors; }
|
||||
void Clear(void) { errors = 0; }
|
||||
};
|
||||
|
||||
cTsChecker::cTsChecker(void)
|
||||
{
|
||||
memset(counter, TS_CC_UNKNOWN, sizeof(counter));
|
||||
errors = 0;
|
||||
}
|
||||
|
||||
void cTsChecker::Report(int Pid, const char *Message)
|
||||
{
|
||||
errors++;
|
||||
if (DebugChecks)
|
||||
fprintf(stderr, "%s: TS error #%d on PID %d (%s)\n", *TimeToString(time(NULL)), errors, Pid, Message);
|
||||
}
|
||||
|
||||
void cTsChecker::CheckTs(const uchar *Data, int Length)
|
||||
{
|
||||
while (Length >= TS_SIZE) {
|
||||
int Pid = TsPid(Data);
|
||||
uchar Cc = TsContinuityCounter(Data);
|
||||
if (TsHasPayload(Data)) {
|
||||
if (TsError(Data))
|
||||
Report(Pid, "tei");
|
||||
else if (TsIsScrambled(Data))
|
||||
Report(Pid, "scrambled");
|
||||
else {
|
||||
uchar OldCc = counter[Pid];
|
||||
if (OldCc != TS_CC_UNKNOWN) {
|
||||
uchar NewCc = (OldCc + 1) & TS_CONT_CNT_MASK;
|
||||
if (Cc != NewCc)
|
||||
Report(Pid, "continuity");
|
||||
}
|
||||
}
|
||||
}
|
||||
counter[Pid] = Cc;
|
||||
Data += TS_SIZE;
|
||||
Length -= TS_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// --- cFrameChecker ---------------------------------------------------------
|
||||
|
||||
#define MAX_BACK_REFS 32
|
||||
|
||||
class cFrameChecker {
|
||||
private:
|
||||
cTsChecker tsChecker;
|
||||
int frameDelta;
|
||||
int64_t lastPts;
|
||||
uint32_t backRefs;
|
||||
int lastFwdRef;
|
||||
int errors;
|
||||
int previousErrors;
|
||||
int missingFrames;
|
||||
void Report(const char *Message, int NumErrors = 1);
|
||||
public:
|
||||
cFrameChecker(void);
|
||||
void SetMissing(void) { missingFrames++; }
|
||||
void SetFrameDelta(int FrameDelta) { frameDelta = FrameDelta; }
|
||||
void CheckTs(const uchar *Data, int Length);
|
||||
void CheckFrame(const uchar *Data, int Length, bool IndependentFrame);
|
||||
int PreviousErrors(void) { return previousErrors; }
|
||||
int MissingFrames(void) { return missingFrames; }
|
||||
};
|
||||
|
||||
cFrameChecker::cFrameChecker(void)
|
||||
{
|
||||
frameDelta = PTSTICKS / DEFAULTFRAMESPERSECOND;
|
||||
lastPts = -1;
|
||||
backRefs = 0;
|
||||
lastFwdRef = 0;
|
||||
errors = 0;
|
||||
previousErrors = 0;
|
||||
missingFrames = 0;
|
||||
}
|
||||
|
||||
void cFrameChecker::Report(const char *Message, int NumErrors)
|
||||
{
|
||||
errors += NumErrors;
|
||||
if (DebugChecks)
|
||||
fprintf(stderr, "%s: frame error #%d (%s)\n", *TimeToString(time(NULL)), errors, Message);
|
||||
}
|
||||
|
||||
void cFrameChecker::CheckTs(const uchar *Data, int Length)
|
||||
{
|
||||
tsChecker.CheckTs(Data, Length);
|
||||
}
|
||||
|
||||
void cFrameChecker::CheckFrame(const uchar *Data, int Length, bool IndependentFrame)
|
||||
{
|
||||
previousErrors = tsChecker.Errors();
|
||||
missingFrames = errors;
|
||||
errors = 0;
|
||||
tsChecker.Clear();
|
||||
int64_t Pts = TsGetPts(Data, Length);
|
||||
if (Pts >= 0) {
|
||||
if (lastPts >= 0) {
|
||||
int Diff = int(round((PtsDiff(lastPts, Pts) / double(frameDelta))));
|
||||
if (Diff > 0) {
|
||||
if (Diff <= MAX_BACK_REFS) {
|
||||
if (lastFwdRef > 1) {
|
||||
if (backRefs != uint32_t((1 << (lastFwdRef - 1)) - 1))
|
||||
Report("missing backref");
|
||||
}
|
||||
}
|
||||
else
|
||||
Report("missed", Diff);
|
||||
backRefs = 0;
|
||||
lastFwdRef = Diff;
|
||||
lastPts = Pts;
|
||||
}
|
||||
else if (Diff < 0) {
|
||||
Diff = -Diff;
|
||||
if (Diff <= MAX_BACK_REFS) {
|
||||
int b = 1 << (Diff - 1);
|
||||
if ((backRefs & b) != 0)
|
||||
Report("duplicate backref");
|
||||
backRefs |= b;
|
||||
}
|
||||
else {
|
||||
Report("rev diff too big");
|
||||
lastPts = Pts;
|
||||
}
|
||||
}
|
||||
else
|
||||
Report("zero diff");
|
||||
}
|
||||
else if (IndependentFrame)
|
||||
lastPts = Pts;
|
||||
}
|
||||
else
|
||||
Report("no PTS");
|
||||
}
|
||||
|
||||
// --- cFrameDetector --------------------------------------------------------
|
||||
|
||||
const char *ScanTypeChars = "-pi"; // index is eScanType
|
||||
@ -1958,6 +2117,13 @@ cFrameDetector::cFrameDetector(int Pid, int Type)
|
||||
aspectRatio = arUnknown;
|
||||
framesInPayloadUnit = framesPerPayloadUnit = 0;
|
||||
scanning = false;
|
||||
firstIframeSeen = false;
|
||||
frameChecker = new cFrameChecker;
|
||||
}
|
||||
|
||||
cFrameDetector::~cFrameDetector()
|
||||
{
|
||||
delete frameChecker;
|
||||
}
|
||||
|
||||
static int CmpUint32(const void *p1, const void *p2)
|
||||
@ -1986,7 +2152,23 @@ void cFrameDetector::SetPid(int Pid, int Type)
|
||||
esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
|
||||
}
|
||||
|
||||
int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
void cFrameDetector::SetMissing(void)
|
||||
{
|
||||
frameChecker->SetMissing();
|
||||
}
|
||||
|
||||
bool cFrameDetector::NewFrame(int *PreviousErrors, int *MissingFrames)
|
||||
{
|
||||
if (newFrame) {
|
||||
if (PreviousErrors)
|
||||
*PreviousErrors = frameChecker->PreviousErrors();
|
||||
if (MissingFrames)
|
||||
*MissingFrames = frameChecker->MissingFrames();
|
||||
}
|
||||
return newFrame;
|
||||
}
|
||||
|
||||
int cFrameDetector::Analyze(const uchar *Data, int Length, bool ErrorCheck)
|
||||
{
|
||||
if (!parser)
|
||||
return 0;
|
||||
@ -2016,7 +2198,10 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
if (parser->NewFrame()) {
|
||||
newFrame = true;
|
||||
independentFrame = parser->IndependentFrame();
|
||||
firstIframeSeen |= independentFrame;
|
||||
if (synced) {
|
||||
if (ErrorCheck)
|
||||
frameChecker->CheckFrame(Data, n, independentFrame);
|
||||
if (framesPerPayloadUnit <= 1)
|
||||
scanning = false;
|
||||
}
|
||||
@ -2027,6 +2212,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
frameHeight = parser->FrameHeight();
|
||||
scanType = parser->ScanType();
|
||||
aspectRatio = parser->AspectRatio();
|
||||
frameChecker->SetFrameDelta(PTSTICKS / framesPerSecond);
|
||||
synced = true;
|
||||
parser->SetDebug(false);
|
||||
}
|
||||
@ -2043,7 +2229,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
if (framesPerSecond <= 0.0) {
|
||||
// frame rate unknown, so collect a sequence of PTS values:
|
||||
if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
|
||||
if (newFrame) { // only take PTS values at the beginning of a frame (in case if fields!)
|
||||
if (newFrame) { // only take PTS values at the beginning of a frame (in case of fields!)
|
||||
const uchar *Pes = Data + TsPayloadOffset(Data);
|
||||
if (numIFrames && PesHasPts(Pes)) {
|
||||
ptsValues[numPtsValues] = PesGetPts(Pes);
|
||||
@ -2085,11 +2271,12 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
framesPerSecond = 60.0 / 1.001;
|
||||
else {
|
||||
framesPerSecond = DEFAULTFRAMESPERSECOND;
|
||||
dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, DEFAULTFRAMESPERSECOND);
|
||||
dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, framesPerSecond);
|
||||
}
|
||||
}
|
||||
else // audio
|
||||
framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing
|
||||
frameChecker->SetFrameDelta(PTSTICKS / framesPerSecond);
|
||||
dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d TRO = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1, parser->IFrameTemporalReferenceOffset());
|
||||
synced = true;
|
||||
parser->SetDebug(false);
|
||||
@ -2100,6 +2287,8 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
else if (Pid == PATPID && synced && Processed)
|
||||
return Processed; // allow the caller to see any PAT packets
|
||||
}
|
||||
if (firstIframeSeen && ErrorCheck)
|
||||
frameChecker->CheckTs(Data, Handled);
|
||||
Data += Handled;
|
||||
Length -= Handled;
|
||||
Processed += Handled;
|
||||
|
22
remux.h
22
remux.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: remux.h 5.3 2023/12/27 09:30:42 kls Exp $
|
||||
* $Id: remux.h 5.8 2024/12/04 14:33:22 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __REMUX_H
|
||||
@ -523,6 +523,8 @@ enum eAspectRatio {
|
||||
extern const char *ScanTypeChars;
|
||||
extern const char *AspectRatioTexts[];
|
||||
|
||||
class cFrameChecker;
|
||||
|
||||
class cFrameDetector {
|
||||
private:
|
||||
enum { MaxPtsValues = 150 };
|
||||
@ -544,25 +546,37 @@ private:
|
||||
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
|
||||
// while others put an entire GOP into one payload unit (> 1).
|
||||
bool scanning;
|
||||
bool firstIframeSeen;
|
||||
cFrameParser *parser;
|
||||
public:
|
||||
cFrameChecker *frameChecker;
|
||||
cFrameDetector(int Pid = 0, int Type = 0);
|
||||
///< Sets up a frame detector for the given Pid and stream Type.
|
||||
///< If no Pid and Type is given, they need to be set by a separate
|
||||
///< call to SetPid().
|
||||
~cFrameDetector();
|
||||
void SetPid(int Pid, int Type);
|
||||
///< Sets the Pid and stream Type to detect frames for.
|
||||
int Analyze(const uchar *Data, int Length);
|
||||
void SetMissing(void);
|
||||
///< Call if this is a resumed recording, which has missing frames.
|
||||
int Analyze(const uchar *Data, int Length, bool ErrorCheck = true);
|
||||
///< Analyzes the TS packets pointed to by Data. Length is the number of
|
||||
///< bytes Data points to, and must be a multiple of TS_SIZE.
|
||||
///< Returns the number of bytes that have been analyzed.
|
||||
///< If the return value is 0, the data was not sufficient for analyzing and
|
||||
///< Analyze() needs to be called again with more actual data.
|
||||
///< Error checks can be turned off by setting ErrorCheck to false. This is
|
||||
///< used when regenerating the index file, where the first chunk of data
|
||||
///< is scanned for the PAT/PMT and then a rewind is done on the file.
|
||||
bool Synced(void) { return synced; }
|
||||
///< Returns true if the frame detector has synced on the data stream.
|
||||
bool NewFrame(void) { return newFrame; }
|
||||
bool NewFrame(int *PreviousErrors = NULL, int *MissingFrames = NULL);
|
||||
///< Returns true if the data given to the last call to Analyze() started a
|
||||
///< new frame.
|
||||
///< new frame. If PreviousErrors is given, it will be set to the number of errors in
|
||||
///< the previous frame. If MissingFrames is given, it will be set to the number of
|
||||
///< missing frames between the previous frame and this one.
|
||||
///< The results returned in PreviousErrors and MissingFrames are only valid if the
|
||||
///< function returns true.
|
||||
bool IndependentFrame(void) { return independentFrame; }
|
||||
///< Returns true if a new frame was detected and this is an independent frame
|
||||
///< (i.e. one that can be displayed by itself, without using data from any
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skinclassic.c 5.2 2023/12/29 10:48:40 kls Exp $
|
||||
* $Id: skinclassic.c 5.4 2024/09/21 10:53:07 kls Exp $
|
||||
*/
|
||||
|
||||
#include "skinclassic.h"
|
||||
@ -71,6 +71,7 @@ THEME_CLR(Theme, clrReplayProgressRest, clrWhite);
|
||||
THEME_CLR(Theme, clrReplayProgressSelected, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressMark, clrBlack);
|
||||
THEME_CLR(Theme, clrReplayProgressCurrent, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressError, clrBlack);
|
||||
|
||||
// --- cSkinClassicDisplayChannel --------------------------------------------
|
||||
|
||||
@ -402,7 +403,8 @@ void cSkinClassicDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
xt -= w + 5;
|
||||
}
|
||||
if (Info->Errors() > 0) {
|
||||
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), tr("errors"));
|
||||
// TRANSLATORS: note the plural/singular!
|
||||
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), Info->Errors() > 1 ? tr("errors") : tr("error"));
|
||||
const cFont *font = cFont::GetFont(fontSml);
|
||||
int w = font->Width(buffer);
|
||||
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVpsFg), Theme.Color(clrMenuEventVpsBg), font, w);
|
||||
@ -534,7 +536,7 @@ void cSkinClassicDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
|
||||
|
||||
void cSkinClassicDisplayReplay::SetProgress(int Current, int Total)
|
||||
{
|
||||
cProgressBar pb(x1 - x0, y2 - y1, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent));
|
||||
cProgressBar pb(x1 - x0, y2 - y1, Current, Total, marks, errors, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent), Theme.Color(clrReplayProgressError));
|
||||
osd->DrawBitmap(x0, y1, pb);
|
||||
}
|
||||
|
||||
|
11
skinlcars.c
11
skinlcars.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skinlcars.c 5.4 2023/12/29 10:48:40 kls Exp $
|
||||
* $Id: skinlcars.c 5.8 2024/12/02 12:40:56 kls Exp $
|
||||
*/
|
||||
|
||||
// "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures,
|
||||
@ -188,6 +188,7 @@ THEME_CLR(Theme, clrReplayProgressRest, RgbShade(CLR_WHITE, -0.2));
|
||||
THEME_CLR(Theme, clrReplayProgressSelected, CLR_EXPOSED);
|
||||
THEME_CLR(Theme, clrReplayProgressMark, CLR_BLACK);
|
||||
THEME_CLR(Theme, clrReplayProgressCurrent, CLR_EXPOSED);
|
||||
THEME_CLR(Theme, clrReplayProgressError, CLR_BLACK);
|
||||
|
||||
// Track display:
|
||||
|
||||
@ -1258,6 +1259,7 @@ void cSkinLCARSDisplayMenu::DrawTimers(void)
|
||||
int NumDevices = 0;
|
||||
int y = ys04;
|
||||
// Timers and recording devices:
|
||||
LOCK_SCHEDULES_READ;
|
||||
while (1) {
|
||||
int NumTimers = 0;
|
||||
const cDevice *Device = NULL;
|
||||
@ -1627,7 +1629,7 @@ void cSkinLCARSDisplayMenu::SetItem(const char *Text, int Index, bool Current, b
|
||||
if (!Tab(i + 1))
|
||||
break;
|
||||
}
|
||||
SetEditableWidth(xi02 - xi00 - TextSpacing - Tab(1));
|
||||
SetEditableWidth(xi01 - xi00 - TextSpacing - Tab(1));
|
||||
}
|
||||
|
||||
void cSkinLCARSDisplayMenu::SetScrollbar(int Total, int Offset)
|
||||
@ -1702,7 +1704,8 @@ void cSkinLCARSDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
xt -= w + xi02 - xi01;
|
||||
}
|
||||
if (Info->Errors() > 0) {
|
||||
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), tr("errors"));
|
||||
// TRANSLATORS: note the plural/singular!
|
||||
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), Info->Errors() > 1 ? tr("errors") : tr("error"));
|
||||
const cFont *font = cFont::GetFont(fontSml);
|
||||
int w = font->Width(buffer);
|
||||
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuFrameFg), frameColor, font, w);
|
||||
@ -1928,7 +1931,7 @@ void cSkinLCARSDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
|
||||
|
||||
void cSkinLCARSDisplayReplay::SetProgress(int Current, int Total)
|
||||
{
|
||||
cProgressBar pb(xp13 - xp03, lineHeight, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent));
|
||||
cProgressBar pb(xp13 - xp03, lineHeight, Current, Total, marks, errors, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent), Theme.Color(clrReplayProgressError));
|
||||
osd->DrawBitmap(xp03, yp02, pb);
|
||||
}
|
||||
|
||||
|
46
skins.c
46
skins.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skins.c 5.1 2024/01/17 21:15:48 kls Exp $
|
||||
* $Id: skins.c 5.5 2025/02/12 21:18:53 kls Exp $
|
||||
*/
|
||||
|
||||
#include "skins.h"
|
||||
@ -139,15 +139,20 @@ int cSkinDisplayMenu::GetTextAreaWidth(void) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
const cFont *cSkinDisplayMenu::GetTextAreaFont(bool) const
|
||||
const cFont *cSkinDisplayMenu::GetTextAreaFont(bool FixedFont) const
|
||||
{
|
||||
return NULL;
|
||||
return cFont::GetFont(FixedFont ? fontFix : fontOsd);
|
||||
}
|
||||
|
||||
// --- cSkinDisplayReplay::cProgressBar --------------------------------------
|
||||
|
||||
cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
|
||||
:cBitmap(Width, Height, 2)
|
||||
:cSkinDisplayReplay::cProgressBar::cProgressBar(Width, Height, Current, Total, Marks, NULL, ColorSeen, ColorRest, ColorSelected, ColorMark, ColorCurrent, clrBlack)
|
||||
{
|
||||
}
|
||||
|
||||
cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, const cErrors *Errors, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent, tColor ColorError)
|
||||
:cBitmap(Width, Height, 4)
|
||||
{
|
||||
total = Total;
|
||||
if (total > 0) {
|
||||
@ -168,6 +173,16 @@ cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Curren
|
||||
Start = !Start;
|
||||
}
|
||||
}
|
||||
if (Errors) {
|
||||
int LastPos = -1;
|
||||
for (int i = 0; i < Errors->Size(); i++) {
|
||||
int p1 = Errors->At(i);
|
||||
if (p1 != LastPos) {
|
||||
Error(Pos(Errors->At(i)), ColorError);
|
||||
LastPos = p1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,11 +196,25 @@ void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tCo
|
||||
}
|
||||
}
|
||||
|
||||
void cSkinDisplayReplay::cProgressBar::Error(int x, tColor ColorError)
|
||||
{
|
||||
const int d = (Height() / 9) & ~0x01; // must be even
|
||||
const int h = Height() / 2;
|
||||
const int e = Height() / 4;
|
||||
DrawRectangle(x, e, x, Height() -e - 1, ColorError);
|
||||
DrawRectangle(x - d, h, x + d, h, ColorError);
|
||||
for (int i = 1; i <= d; i++) {
|
||||
DrawRectangle(x - d + i, h - i, x + d - i, h - i, ColorError);
|
||||
DrawRectangle(x - d + i, h + i, x + d - i, h + i, ColorError);
|
||||
}
|
||||
}
|
||||
|
||||
// --- cSkinDisplayReplay ----------------------------------------------------
|
||||
|
||||
cSkinDisplayReplay::cSkinDisplayReplay(void)
|
||||
{
|
||||
marks = NULL;
|
||||
errors = NULL;
|
||||
}
|
||||
|
||||
void cSkinDisplayReplay::SetRecording(const cRecording *Recording)
|
||||
@ -198,6 +227,11 @@ void cSkinDisplayReplay::SetMarks(const cMarks *Marks)
|
||||
marks = Marks;
|
||||
}
|
||||
|
||||
void cSkinDisplayReplay::SetErrors(const cErrors *Errors)
|
||||
{
|
||||
errors = Errors;
|
||||
}
|
||||
|
||||
// --- cSkin -----------------------------------------------------------------
|
||||
|
||||
cSkin::cSkin(const char *Name, cTheme *Theme)
|
||||
@ -271,7 +305,7 @@ eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
|
||||
}
|
||||
cSkinDisplay::Current()->SetMessage(Type, s);
|
||||
cSkinDisplay::Current()->Flush();
|
||||
cStatus::MsgOsdStatusMessage(s);
|
||||
cStatus::MsgOsdStatusMessage(Type, s);
|
||||
eKeys k = kNone;
|
||||
if (Type != mtStatus) {
|
||||
k = Interface->Wait(Seconds);
|
||||
@ -282,7 +316,7 @@ eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
|
||||
}
|
||||
else {
|
||||
cSkinDisplay::Current()->SetMessage(Type, NULL);
|
||||
cStatus::MsgOsdStatusMessage(NULL);
|
||||
cStatus::MsgOsdStatusMessage(Type, NULL);
|
||||
}
|
||||
}
|
||||
else if (!s && displayMessage) {
|
||||
|
26
skins.h
26
skins.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skins.h 5.2 2024/06/11 12:43:25 kls Exp $
|
||||
* $Id: skins.h 5.6 2024/09/19 09:49:02 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __SKINS_H
|
||||
@ -244,15 +244,6 @@ public:
|
||||
///< If the skin displays the Event item in its own way, it shall return true.
|
||||
///< The default implementation does nothing and returns false, which results in
|
||||
///< a call to SetItem() with a proper text.
|
||||
#ifndef DEPRECATED_SKIN_SETITEMEVENT
|
||||
#define DEPRECATED_SKIN_SETITEMEVENT 0
|
||||
#endif
|
||||
#if DEPRECATED_SKIN_SETITEMEVENT
|
||||
virtual bool SetItemEvent(const cEvent *Event, int Index, bool Current, bool Selectable, const cChannel *Channel, bool WithDate, eTimerMatch TimerMatch) { return SetItemEvent(Event, Index, Current, Selectable, Channel, WithDate, TimerMatch, true); }
|
||||
///< This function is here for comaptibility with older plugins and may be removed
|
||||
///< in a future version. Use the above version of SetItemEvent() with the TimerActive
|
||||
///< parameter instead.
|
||||
#endif
|
||||
virtual bool SetItemTimer(const cTimer *Timer, int Index, bool Current, bool Selectable) { return false; }
|
||||
///< Sets the item at the given Index to Timer. See SetItem() for more information.
|
||||
///< If a derived skin class implements this function, it can display a Timer item
|
||||
@ -310,8 +301,7 @@ public:
|
||||
virtual const cFont *GetTextAreaFont(bool FixedFont) const;
|
||||
///< Returns a pointer to the font which is used to display text with SetText().
|
||||
///< The parameter FixedFont has the same meaning as in SetText(). The default
|
||||
///< implementation returns NULL. Therefore a caller of this method must be
|
||||
///< prepared to receive NULL if the plugin doesn't implement this method.
|
||||
///< implementation returns the font defined in the setup.
|
||||
///< The returned pointer is valid a long as the instance of cSkinDisplayMenu
|
||||
///< exists.
|
||||
};
|
||||
@ -321,23 +311,29 @@ class cSkinDisplayReplay : public cSkinDisplay {
|
||||
///< a recording.
|
||||
protected:
|
||||
const cMarks *marks;
|
||||
const cErrors *errors;
|
||||
class cProgressBar : public cBitmap {
|
||||
protected:
|
||||
int total;
|
||||
int Pos(int p) { return int(int64_t(p) * Width() / total); }
|
||||
void Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent);
|
||||
void Error(int x, tColor ColorError);
|
||||
public:
|
||||
cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent);
|
||||
cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent); // for backwards compatibility
|
||||
cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, const cErrors *Errors, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent, tColor ColorError);
|
||||
};
|
||||
public:
|
||||
cSkinDisplayReplay(void);
|
||||
virtual void SetMarks(const cMarks *Marks);
|
||||
///< Sets the editing marks to Marks, which shall be used to display the
|
||||
///< progress bar through a cProgressBar object.
|
||||
virtual void SetErrors(const cErrors *Errors);
|
||||
///< Sets the errors found in the recording to Errors, which shall be used to display the
|
||||
///< progress bar through a cProgressBar object.
|
||||
virtual void SetRecording(const cRecording *Recording);
|
||||
///< Sets the recording that is currently being played.
|
||||
///< The default implementation calls SetTitle() with the title and short
|
||||
///< text of the Recording. A derived class can use any information provided
|
||||
///< The default implementation calls SetTitle() with the title
|
||||
///< of the Recording. A derived class can use any information provided
|
||||
///< by the given Recording and display it.
|
||||
virtual void SetTitle(const char *Title) = 0;
|
||||
///< Sets the title of the recording.
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skinsttng.c 5.2 2023/12/29 10:48:40 kls Exp $
|
||||
* $Id: skinsttng.c 5.4 2024/09/21 10:53:07 kls Exp $
|
||||
*/
|
||||
|
||||
// "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures
|
||||
@ -122,6 +122,7 @@ THEME_CLR(Theme, clrReplayProgressRest, clrWhite);
|
||||
THEME_CLR(Theme, clrReplayProgressSelected, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressMark, clrBlack);
|
||||
THEME_CLR(Theme, clrReplayProgressCurrent, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressError, clrBlack);
|
||||
|
||||
// --- cSkinSTTNGDisplayChannel ----------------------------------------------
|
||||
|
||||
@ -718,7 +719,8 @@ void cSkinSTTNGDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
xt -= w + x5 - x4;
|
||||
}
|
||||
if (Info->Errors() > 0) {
|
||||
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), tr("errors"));
|
||||
// TRANSLATORS: note the plural/singular!
|
||||
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), Info->Errors() > 1 ? tr("errors") : tr("error"));
|
||||
const cFont *font = cFont::GetFont(fontSml);
|
||||
int w = font->Width(buffer);
|
||||
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVps), frameColor, font, w);
|
||||
@ -904,7 +906,7 @@ void cSkinSTTNGDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
|
||||
|
||||
void cSkinSTTNGDisplayReplay::SetProgress(int Current, int Total)
|
||||
{
|
||||
cProgressBar pb(x4 - x3, y4 - y3, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent));
|
||||
cProgressBar pb(x4 - x3, y4 - y3, Current, Total, marks, errors, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent), Theme.Color(clrReplayProgressError));
|
||||
osd->DrawBitmap(x3, y3, pb);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: sources.c 3.6 2014/03/09 12:05:42 kls Exp $
|
||||
* $Id: sources.c 5.1 2024/10/09 10:36:16 kls Exp $
|
||||
*/
|
||||
|
||||
#include "sources.h"
|
||||
@ -46,10 +46,7 @@ bool cSource::Matches(int Code1, int Code2)
|
||||
|
||||
int cSource::Position(int Code)
|
||||
{
|
||||
int n = (Code & st_Pos);
|
||||
if (n > 0x00007FFF)
|
||||
n |= 0xFFFF0000;
|
||||
return n;
|
||||
return int16_t(Code & st_Pos);
|
||||
}
|
||||
|
||||
cString cSource::ToString(int Code)
|
||||
|
14
status.c
14
status.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: status.c 4.1 2018/01/29 13:36:53 kls Exp $
|
||||
* $Id: status.c 5.3 2025/02/12 21:18:53 kls Exp $
|
||||
*/
|
||||
|
||||
#include "status.h"
|
||||
@ -95,10 +95,10 @@ void cStatus::MsgOsdTitle(const char *Title)
|
||||
sm->OsdTitle(Title);
|
||||
}
|
||||
|
||||
void cStatus::MsgOsdStatusMessage(const char *Message)
|
||||
void cStatus::MsgOsdStatusMessage(eMessageType Type, const char *Message)
|
||||
{
|
||||
for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
|
||||
sm->OsdStatusMessage(Message);
|
||||
sm->OsdStatusMessage2(Type, Message);
|
||||
}
|
||||
|
||||
void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
|
||||
@ -107,16 +107,16 @@ void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yel
|
||||
sm->OsdHelpKeys(Red, Green, Yellow, Blue);
|
||||
}
|
||||
|
||||
void cStatus::MsgOsdItem(const char *Text, int Index)
|
||||
void cStatus::MsgOsdItem(const char *Text, int Index, bool Selectable)
|
||||
{
|
||||
for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
|
||||
sm->OsdItem(Text, Index);
|
||||
sm->OsdItem2(Text, Index, Selectable);
|
||||
}
|
||||
|
||||
void cStatus::MsgOsdCurrentItem(const char *Text)
|
||||
void cStatus::MsgOsdCurrentItem(const char *Text, int Index)
|
||||
{
|
||||
for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
|
||||
sm->OsdCurrentItem(Text);
|
||||
sm->OsdCurrentItem2(Text, Index);
|
||||
}
|
||||
|
||||
void cStatus::MsgOsdTextItem(const char *Text, bool Scroll)
|
||||
|
18
status.h
18
status.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: status.h 4.4 2018/01/29 13:42:17 kls Exp $
|
||||
* $Id: status.h 5.3 2025/02/12 21:18:53 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __STATUS_H
|
||||
@ -13,6 +13,7 @@
|
||||
#include "config.h"
|
||||
#include "device.h"
|
||||
#include "player.h"
|
||||
#include "skins.h"
|
||||
#include "tools.h"
|
||||
|
||||
// Several member functions of the following classes are called with a pointer to
|
||||
@ -81,14 +82,21 @@ protected:
|
||||
virtual void OsdTitle(const char *Title) {}
|
||||
// Title has been displayed in the title line of the menu.
|
||||
virtual void OsdStatusMessage(const char *Message) {}
|
||||
virtual void OsdStatusMessage2(eMessageType Type, const char *Message) { OsdStatusMessage(Message); }
|
||||
// Message has been displayed in the status line of the menu.
|
||||
// If Message is NULL, the status line has been cleared.
|
||||
virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {}
|
||||
// The help keys have been set to the given values (may be NULL).
|
||||
virtual void OsdItem(const char *Text, int Index) {}
|
||||
// The OSD displays the given single line Text as menu item at Index.
|
||||
virtual void OsdItem2(const char *Text, int Index, bool Selectable) { OsdItem(Text, Index); }
|
||||
// The OSD displays the given single line Text as menu item at Index.
|
||||
// Selectable is true if this item can be selected.
|
||||
virtual void OsdCurrentItem(const char *Text) {}
|
||||
// The OSD displays the given single line Text as the current menu item.
|
||||
virtual void OsdCurrentItem2(const char *Text, int Index) { OsdCurrentItem(Text); }
|
||||
// The OSD displays the given single line Text as the current menu item.
|
||||
// Index is the one that was given in OsdItem[2]() for this item.
|
||||
virtual void OsdTextItem(const char *Text, bool Scroll) {}
|
||||
// The OSD displays the given multi line text. If Text points to an
|
||||
// actual string, that text shall be displayed and Scroll has no
|
||||
@ -115,10 +123,12 @@ public:
|
||||
static void MsgSetSubtitleTrack(int Index, const char * const *Tracks);
|
||||
static void MsgOsdClear(void);
|
||||
static void MsgOsdTitle(const char *Title);
|
||||
static void MsgOsdStatusMessage(const char *Message);
|
||||
[[deprecated("use MsgOsdStatusMessage(eMessageType Type, const char *Message) instead")]]
|
||||
static void MsgOsdStatusMessage(const char *Message) { MsgOsdStatusMessage(mtStatus, Message); }
|
||||
static void MsgOsdStatusMessage(eMessageType Type, const char *Message);
|
||||
static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
|
||||
static void MsgOsdItem(const char *Text, int Index);
|
||||
static void MsgOsdCurrentItem(const char *Text);
|
||||
static void MsgOsdItem(const char *Text, int Index, bool Selectable = true);
|
||||
static void MsgOsdCurrentItem(const char *Text, int Index = -1);
|
||||
static void MsgOsdTextItem(const char *Text, bool Scroll = false);
|
||||
static void MsgOsdChannel(const char *Text);
|
||||
static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
|
||||
|
53
svdrp.c
53
svdrp.c
@ -10,7 +10,7 @@
|
||||
* and interact with the Video Disk Recorder - or write a full featured
|
||||
* graphical interface that sits on top of an SVDRP connection.
|
||||
*
|
||||
* $Id: svdrp.c 5.8 2024/06/13 09:31:11 kls Exp $
|
||||
* $Id: svdrp.c 5.10 2024/09/09 13:39:05 kls Exp $
|
||||
*/
|
||||
|
||||
#include "svdrp.h"
|
||||
@ -823,6 +823,14 @@ bool cPUTEhandler::Process(const char *s)
|
||||
// adjust the help for CLRE accordingly if changing this!
|
||||
|
||||
const char *HelpPages[] = {
|
||||
"AUDI [ <number> ]\n"
|
||||
" Lists the currently available audio tracks in the format 'number language description'.\n"
|
||||
" The number indicates the track type (1..32 = MP2, 33..48 = Dolby).\n"
|
||||
" The currently selected track has its description prefixed with '*'.\n"
|
||||
" If a number is given (which must be one of the track numbers listed)\n"
|
||||
" audio is switched to that track.\n"
|
||||
" Note that the list may not be fully available or current immediately after\n"
|
||||
" switching the channel or starting a replay.",
|
||||
"CHAN [ + | - | <number> | <name> | <id> ]\n"
|
||||
" Switch channel up, down or to the given channel number, name or id.\n"
|
||||
" Without option (or after successfully switching to the channel)\n"
|
||||
@ -1071,6 +1079,7 @@ private:
|
||||
bool Send(const char *s);
|
||||
void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
void PrintHelpTopics(const char **hp);
|
||||
void CmdAUDI(const char *Option);
|
||||
void CmdCHAN(const char *Option);
|
||||
void CmdCLRE(const char *Option);
|
||||
void CmdCONN(const char *Option);
|
||||
@ -1228,6 +1237,45 @@ void cSVDRPServer::PrintHelpTopics(const char **hp)
|
||||
}
|
||||
}
|
||||
|
||||
void cSVDRPServer::CmdAUDI(const char *Option)
|
||||
{
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
int o = strtol(Option, NULL, 10);
|
||||
if (o >= ttAudioFirst && o <= ttDolbyLast) {
|
||||
const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(o));
|
||||
if (TrackId && TrackId->id) {
|
||||
cDevice::PrimaryDevice()->SetCurrentAudioTrack(eTrackType(o));
|
||||
Reply(250, "%d %s %s", eTrackType(o), *TrackId->language ? TrackId->language : "---", *TrackId->description ? TrackId->description : "-");
|
||||
}
|
||||
else
|
||||
Reply(501, "Audio track \"%s\" not available", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Invalid audio track \"%s\"", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in audio track \"%s\"", Option);
|
||||
}
|
||||
else {
|
||||
SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring() ? cDevice::CurrentChannel() : 0);
|
||||
eTrackType CurrentAudioTrack = cDevice::PrimaryDevice()->GetCurrentAudioTrack();
|
||||
cString s;
|
||||
for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
|
||||
const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i));
|
||||
if (TrackId && TrackId->id) {
|
||||
if (*s)
|
||||
Reply(-250, "%s", *s);
|
||||
s = cString::sprintf("%d %s %s%s", eTrackType(i), *TrackId->language ? TrackId->language : "---", i == CurrentAudioTrack ? "*" : "", *TrackId->description ? TrackId->description : "-");
|
||||
}
|
||||
}
|
||||
if (*s)
|
||||
Reply(250, "%s", *s);
|
||||
else
|
||||
Reply(550, "No audio tracks available");
|
||||
}
|
||||
}
|
||||
|
||||
void cSVDRPServer::CmdCHAN(const char *Option)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
@ -2571,7 +2619,8 @@ void cSVDRPServer::Execute(char *Cmd)
|
||||
if (*s)
|
||||
*s++ = 0;
|
||||
s = skipspace(s);
|
||||
if (CMD("CHAN")) CmdCHAN(s);
|
||||
if (CMD("AUDI")) CmdAUDI(s);
|
||||
else if (CMD("CHAN")) CmdCHAN(s);
|
||||
else if (CMD("CLRE")) CmdCLRE(s);
|
||||
else if (CMD("CONN")) CmdCONN(s);
|
||||
else if (CMD("DELC")) CmdDELC(s);
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "svdrpsend" "1" "27 Dec 2021" "2.6" "Video Disk Recorder"
|
||||
.TH "svdrpsend" "1" "27 Dec 2021" "2.7" "Video Disk Recorder"
|
||||
.SH NAME
|
||||
.LP
|
||||
svdrpsend \- sends commands to VDR
|
||||
|
3
thread.c
3
thread.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: thread.c 5.2 2024/01/18 13:01:07 kls Exp $
|
||||
* $Id: thread.c 5.3 2025/01/15 08:43:12 kls Exp $
|
||||
*/
|
||||
|
||||
#include "thread.h"
|
||||
@ -597,6 +597,7 @@ cStateLockLog::cStateLockLog(void)
|
||||
void cStateLockLog::Dump(const char *Name, tThreadId ThreadId)
|
||||
{
|
||||
dsyslog("--- begin invalid lock sequence report");
|
||||
dsyslog("TID T C R DR S ST");
|
||||
int LastFlags = 0;
|
||||
for (int i = 0; i < SLL_SIZE; i++) {
|
||||
if (tThreadId tid = logThreadIds[logIndex]) {
|
||||
|
56
timers.c
56
timers.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: timers.c 5.20 2024/03/06 14:37:15 kls Exp $
|
||||
* $Id: timers.c 5.25 2025/01/13 14:44:18 kls Exp $
|
||||
*/
|
||||
|
||||
#include "timers.h"
|
||||
@ -185,6 +185,8 @@ cTimer::cTimer(const cEvent *Event, const char *FileName, const cTimer *PatternT
|
||||
deferred = 0;
|
||||
pending = inVpsMargin = false;
|
||||
flags = tfActive;
|
||||
if (PatternTimer)
|
||||
SetFlags(tfSpawned);
|
||||
*pattern = 0;
|
||||
*file = 0;
|
||||
aux = NULL;
|
||||
@ -284,10 +286,12 @@ void cTimer::CalcMargins(int &MarginStart, int &MarginStop, const cEvent *Event)
|
||||
MarginStop = Setup.MarginStop * 60;
|
||||
// To make sure the timer gets assigned to the correct event, we must
|
||||
// make sure that this is the only event that overlaps 100%:
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Prev()))
|
||||
MarginStart = max(0, min(MarginStart, e->Duration() - 60));
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Next()))
|
||||
MarginStop = max(0, min(MarginStop, e->Duration() - 60));
|
||||
if (HasFlags(tfSpawned)) {
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Prev()))
|
||||
MarginStart = max(0, min(MarginStart, e->Duration() - 60));
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Next()))
|
||||
MarginStop = max(0, min(MarginStop, e->Duration() - 60));
|
||||
}
|
||||
}
|
||||
|
||||
int cTimer::Compare(const cListObject &ListObject) const
|
||||
@ -612,7 +616,7 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
|
||||
return false;
|
||||
deferred = 0;
|
||||
|
||||
if (HasFlags(tfActive)) {
|
||||
if (HasFlags(tfActive) && !Remote()) {
|
||||
if (event) {
|
||||
if (HasFlags(tfVps)) {
|
||||
if (event->Vps()) {
|
||||
@ -620,7 +624,8 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
|
||||
startTime = event->StartTime();
|
||||
stopTime = event->EndTime();
|
||||
if (!Margin) { // this is an actual check
|
||||
if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events...
|
||||
const cSchedule *Schedule = event->Schedule();
|
||||
if (Schedule && Schedule->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events...
|
||||
if (!vpsActive) {
|
||||
vpsActive = true;
|
||||
if (Recording())
|
||||
@ -636,7 +641,7 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
|
||||
return running || time(NULL) < vpsNotRunning + VPSGRACE;
|
||||
}
|
||||
if (Recording()) {
|
||||
if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGGRACE))
|
||||
if (Schedule && Schedule->PresentSeenWithin(EITPRESENTFOLLOWINGGRACE))
|
||||
return event->IsRunning(true); // give it a chance to recover - worst case: the recording will be 60 seconds too long
|
||||
if (vpsActive) {
|
||||
vpsActive = false;
|
||||
@ -726,10 +731,39 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
|
||||
bool cTimer::Expired(void) const
|
||||
{
|
||||
if (IsSingleEvent() && !Recording()) {
|
||||
time_t Now = time(NULL);
|
||||
time_t ExpireTime = StopTimeEvent();
|
||||
if (HasFlags(tfVps))
|
||||
if (HasFlags(tfVps)) {
|
||||
ExpireTime += EXPIRELATENCY;
|
||||
return ExpireTime <= time(NULL);
|
||||
if (ExpireTime <= Now) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
const cSchedule *Schedule = event ? event->Schedule() : NULL;
|
||||
const cEvent *FirstEvent = event;
|
||||
if (Schedule)
|
||||
FirstEvent = Schedule->Events()->Next(FirstEvent);
|
||||
else if ((Schedule = Schedules->GetSchedule(Channel())) != NULL) {
|
||||
FirstEvent = Schedule->Events()->First();
|
||||
if (FirstEvent)
|
||||
dsyslog("timer %s had no event, got %s from channel/schedule", *ToDescr(), *FirstEvent->ToDescr());
|
||||
}
|
||||
if (FirstEvent) {
|
||||
if (Schedule) {
|
||||
for (const cEvent *e = FirstEvent; e; e = Schedule->Events()->Next(e)) {
|
||||
if (e->Vps() == startTime) {
|
||||
ExpireTime = e->EndTime() + EXPIRELATENCY;
|
||||
dsyslog("timer %s is waiting for next VPS event %s", *ToDescr(), *e->ToDescr());
|
||||
// no break here - let's play it safe and look at *all* events
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dsyslog("timer %s has no event, setting expiration to +24h", *ToDescr());
|
||||
ExpireTime += 3600 * 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ExpireTime <= Now;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -783,7 +817,6 @@ cTimer *cTimer::SpawnPatternTimer(const cEvent *Event, cTimers *Timers)
|
||||
cString FileName = MakePatternFileName(Pattern(), Event->Title(), Event->ShortText(), File());
|
||||
isyslog("spawning timer %s for event %s", *ToDescr(), *Event->ToDescr());
|
||||
cTimer *t = new cTimer(Event, FileName, this);
|
||||
t->SetFlags(tfSpawned);
|
||||
if (startswith(Pattern(), TIMERPATTERN_AVOID))
|
||||
t->SetFlags(tfAvoid);
|
||||
Timers->Add(t);
|
||||
@ -1128,6 +1161,7 @@ const cTimer *cTimers::GetMatch(time_t t) const
|
||||
{
|
||||
static int LastPending = -1;
|
||||
const cTimer *t0 = NULL;
|
||||
LOCK_SCHEDULES_READ;
|
||||
for (const cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
if (!ti->Remote() && !ti->Recording() && ti->Matches(t)) {
|
||||
if (ti->Pending()) {
|
||||
|
110
tools.c
110
tools.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.c 5.11 2024/06/21 06:27:20 kls Exp $
|
||||
* $Id: tools.c 5.15 2025/01/15 08:57:45 kls Exp $
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
@ -131,8 +131,11 @@ char *strcpyrealloc(char *dest, const char *src)
|
||||
char *strn0cpy(char *dest, const char *src, size_t n)
|
||||
{
|
||||
char *s = dest;
|
||||
for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
|
||||
*dest = 0;
|
||||
if (dest && n) {
|
||||
if (src)
|
||||
for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
|
||||
*dest = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -169,8 +172,10 @@ char *strreplace(char *s, const char *s1, const char *s2)
|
||||
}
|
||||
}
|
||||
char *sof = s + of;
|
||||
if (l2 != l1)
|
||||
if (l2 != l1) {
|
||||
memmove(sof + l2, sof + l1, l - of - l1 + 1);
|
||||
l += l2 - l1;
|
||||
}
|
||||
memcpy(sof, s2, l2);
|
||||
q = sof + l2;
|
||||
} while (p = strstr(q, s1));
|
||||
@ -717,9 +722,17 @@ bool SpinUpDisk(const char *FileName)
|
||||
return false;
|
||||
}
|
||||
|
||||
void TouchFile(const char *FileName)
|
||||
void TouchFile(const char *FileName, bool Create)
|
||||
{
|
||||
if (utime(FileName, NULL) == -1 && errno != ENOENT)
|
||||
if (Create && access(FileName, F_OK) != 0) { // the file does not exist
|
||||
isyslog("creating file '%s'", FileName);
|
||||
int f = open(FileName, O_WRONLY | O_CREAT, DEFFILEMODE);
|
||||
if (f >= 0)
|
||||
close(f);
|
||||
else
|
||||
LOG_ERROR_STR(FileName);
|
||||
}
|
||||
if (utime(FileName, NULL) == -1 && errno != ENOENT)
|
||||
LOG_ERROR_STR(FileName);
|
||||
}
|
||||
|
||||
@ -904,17 +917,21 @@ char *Utf8Strn0Cpy(char *Dest, const char *Src, int n)
|
||||
if (cCharSetConv::SystemCharacterTable())
|
||||
return strn0cpy(Dest, Src, n);
|
||||
char *d = Dest;
|
||||
while (*Src) {
|
||||
int sl = Utf8CharLen(Src);
|
||||
n -= sl;
|
||||
if (n > 0) {
|
||||
while (sl--)
|
||||
*d++ = *Src++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
if (Dest && n > 0) {
|
||||
if (Src) {
|
||||
while (*Src) {
|
||||
int sl = Utf8CharLen(Src);
|
||||
n -= sl;
|
||||
if (n > 0) {
|
||||
while (sl--)
|
||||
*d++ = *Src++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
*d = 0;
|
||||
*d = 0;
|
||||
}
|
||||
return Dest;
|
||||
}
|
||||
|
||||
@ -1662,11 +1679,6 @@ bool cFileNameList::Load(const char *Directory, bool DirsOnly)
|
||||
|
||||
// --- cFile -----------------------------------------------------------------
|
||||
|
||||
#if DEPRECATED_CFILE
|
||||
bool cFile::files[FD_SETSIZE] = { false };
|
||||
int cFile::maxFiles = 0;
|
||||
#endif
|
||||
|
||||
cFile::cFile(void)
|
||||
{
|
||||
f = -1;
|
||||
@ -1688,24 +1700,8 @@ bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
|
||||
bool cFile::Open(int FileDes)
|
||||
{
|
||||
if (FileDes >= 0) {
|
||||
if (!IsOpen()) {
|
||||
if (!IsOpen())
|
||||
f = FileDes;
|
||||
#if DEPRECATED_CFILE
|
||||
if (f >= 0) {
|
||||
if (f < FD_SETSIZE) {
|
||||
if (f >= maxFiles)
|
||||
maxFiles = f + 1;
|
||||
if (!files[f])
|
||||
files[f] = true;
|
||||
else
|
||||
esyslog("ERROR: file descriptor %d already in files[]", f);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: attempt to re-open file descriptor %d", FileDes);
|
||||
}
|
||||
@ -1716,9 +1712,6 @@ void cFile::Close(void)
|
||||
{
|
||||
if (f >= 0) {
|
||||
close(f);
|
||||
#if DEPRECATED_CFILE
|
||||
files[f] = false;
|
||||
#endif
|
||||
f = -1;
|
||||
}
|
||||
}
|
||||
@ -1728,26 +1721,6 @@ bool cFile::Ready(bool Wait)
|
||||
return f >= 0 && FileReady(f, Wait ? 1000 : 0);
|
||||
}
|
||||
|
||||
#if DEPRECATED_CFILE
|
||||
bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
|
||||
{
|
||||
fd_set set;
|
||||
FD_ZERO(&set);
|
||||
for (int i = 0; i < maxFiles; i++) {
|
||||
if (files[i])
|
||||
FD_SET(i, &set);
|
||||
}
|
||||
if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
|
||||
FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
|
||||
if (TimeoutMs == 0)
|
||||
TimeoutMs = 10; // load gets too heavy with 0
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = TimeoutMs / 1000;
|
||||
timeout.tv_usec = (TimeoutMs % 1000) * 1000;
|
||||
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool cFile::FileReady(int FileDes, int TimeoutMs)
|
||||
{
|
||||
fd_set set;
|
||||
@ -1763,21 +1736,6 @@ bool cFile::FileReady(int FileDes, int TimeoutMs)
|
||||
return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
|
||||
}
|
||||
|
||||
#if DEPRECATED_CFILE
|
||||
bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
|
||||
{
|
||||
fd_set set;
|
||||
struct timeval timeout;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(FileDes, &set);
|
||||
if (TimeoutMs < 100)
|
||||
TimeoutMs = 100;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = TimeoutMs * 1000;
|
||||
return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- cSafeFile -------------------------------------------------------------
|
||||
|
||||
cSafeFile::cSafeFile(const char *FileName)
|
||||
|
19
tools.h
19
tools.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.h 5.8 2024/01/20 13:59:55 kls Exp $
|
||||
* $Id: tools.h 5.11 2025/01/13 13:18:42 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TOOLS_H
|
||||
@ -140,7 +140,7 @@ int Utf8FromArray(const uint *a, char *s, int Size, int Max = -1);
|
||||
// When allocating buffer space, make sure we reserve enough space to hold
|
||||
// a string in UTF-8 representation:
|
||||
|
||||
#define Utf8BufSize(s) ((s) * 4)
|
||||
#define Utf8BufSize(s) ((s) * 4 + 1)
|
||||
|
||||
// The following macros automatically use the correct versions of the character
|
||||
// class functions:
|
||||
@ -319,7 +319,7 @@ bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false, const
|
||||
int DirSizeMB(const char *DirName); ///< returns the total size of the files in the given directory, or -1 in case of an error
|
||||
char *ReadLink(const char *FileName); ///< returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error)
|
||||
bool SpinUpDisk(const char *FileName);
|
||||
void TouchFile(const char *FileName);
|
||||
void TouchFile(const char *FileName, bool Create = false);
|
||||
time_t LastModifiedTime(const char *FileName);
|
||||
off_t FileSize(const char *FileName); ///< returns the size of the given file, or -1 in case of an error (e.g. if the file doesn't exist)
|
||||
cString WeekDayName(int WeekDay);
|
||||
@ -460,15 +460,8 @@ public:
|
||||
struct dirent *Next(void);
|
||||
};
|
||||
|
||||
#ifndef DEPRECATED_CFILE
|
||||
#define DEPRECATED_CFILE 0
|
||||
#endif
|
||||
class cFile {
|
||||
private:
|
||||
#if DEPRECATED_CFILE
|
||||
static bool files[];
|
||||
static int maxFiles;
|
||||
#endif
|
||||
int f;
|
||||
public:
|
||||
cFile(void);
|
||||
@ -479,13 +472,7 @@ public:
|
||||
void Close(void);
|
||||
bool IsOpen(void) { return f >= 0; }
|
||||
bool Ready(bool Wait = true);
|
||||
#if DEPRECATED_CFILE
|
||||
static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
|
||||
#endif
|
||||
static bool FileReady(int FileDes, int TimeoutMs = 1000);
|
||||
#if DEPRECATED_CFILE
|
||||
static bool FileReadyForWriting(int FileDes, int TimeoutMs = 1000);
|
||||
#endif
|
||||
};
|
||||
|
||||
class cSafeFile {
|
||||
|
11
vdr.1
11
vdr.1
@ -8,9 +8,9 @@
|
||||
.\" License as specified in the file COPYING that comes with the
|
||||
.\" vdr distribution.
|
||||
.\"
|
||||
.\" $Id: vdr.1 5.2 2021/12/27 13:31:04 kls Exp $
|
||||
.\" $Id: vdr.1 5.4 2024/09/09 10:58:55 kls Exp $
|
||||
.\"
|
||||
.TH vdr 1 "27 Dec 2021" "2.6" "Video Disk Recorder"
|
||||
.TH vdr 1 "27 Dec 2021" "2.7" "Video Disk Recorder"
|
||||
.SH NAME
|
||||
vdr \- the Video Disk Recorder
|
||||
.SH SYNOPSIS
|
||||
@ -311,8 +311,11 @@ as the first character of the pattern. File names are appended to this file afte
|
||||
a recording has finished, and the entire file is read upon startup of VDR.
|
||||
.TP
|
||||
.I .update
|
||||
If this file is present in the video directory, its last modification time will
|
||||
be used to trigger an update of the list of recordings in the "Recordings" menu.
|
||||
This file (in the video directory) will be touched whenever VDR makes changes
|
||||
to the content of the directory. Its last modification time will
|
||||
be used to trigger an update of the list of recordings in any VDRs that use
|
||||
the same video directory.
|
||||
The file will be created if it doesn't already exist.
|
||||
.SH SEE ALSO
|
||||
.BR vdr (5), svdrpsend (1)
|
||||
.SH AUTHOR
|
||||
|
10
vdr.5
10
vdr.5
@ -8,9 +8,9 @@
|
||||
.\" License as specified in the file COPYING that comes with the
|
||||
.\" vdr distribution.
|
||||
.\"
|
||||
.\" $Id: vdr.5 5.9 2023/12/25 20:53:04 kls Exp $
|
||||
.\" $Id: vdr.5 5.12 2024/09/20 21:34:18 kls Exp $
|
||||
.\"
|
||||
.TH vdr 5 "27 Dec 2021" "2.6" "Video Disk Recorder Files"
|
||||
.TH vdr 5 "27 Dec 2021" "2.7" "Video Disk Recorder Files"
|
||||
.SH NAME
|
||||
vdr_files \- the Video Disk Recorder Files
|
||||
.SH DESCRIPTION
|
||||
@ -824,8 +824,10 @@ l l.
|
||||
The 'O' tag contains the number of errors that occurred during recording.
|
||||
If it is zero, the recording can be safely considered error free. The higher the value,
|
||||
the more damaged the recording is.
|
||||
If this is an edited recording, the number of errors is that of the original
|
||||
recording.
|
||||
If this is an edited recording, the number of errors is that of the edited
|
||||
recording, if the index of the original recording contains error indicators
|
||||
(i.e. the original recording's index was created with VDR version 2.7.2 or
|
||||
later). Otherwise it's the number of errors in the original recording.
|
||||
.SS RESUME
|
||||
The file \fIresume\fR (if present in a recording directory) contains
|
||||
the position within the recording where the last replay session left off.
|
||||
|
5
vdr.c
5
vdr.c
@ -20,9 +20,9 @@
|
||||
*
|
||||
* The author can be reached at vdr@tvdr.de
|
||||
*
|
||||
* The project's page is at http://www.tvdr.de
|
||||
* The project's page is at https://www.tvdr.de
|
||||
*
|
||||
* $Id: vdr.c 5.16 2024/03/29 21:46:50 kls Exp $
|
||||
* $Id: vdr.c 5.18 2024/12/02 12:40:56 kls Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
@ -1140,6 +1140,7 @@ int main(int argc, char *argv[])
|
||||
if (Timer->Matches(Now, true, Setup.VpsMargin))
|
||||
InVpsMargin = true;
|
||||
else if (Timer->Event()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
InVpsMargin = Timer->Event()->StartTime() <= Now && Now < Timer->Event()->EndTime();
|
||||
NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user