summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <Klaus (dot) Schmidinger (at) tvdr (dot) de>2018-03-18 13:54:32 (GMT)
committerManuel Reimer <manuel.reimer@gmx.de>2018-03-26 15:59:29 (GMT)
commit553bc142754863250ddce11803201a278a18be86 (patch)
tree9b7a0934efa33b06e5d6c0ec65c89048b7669c8f
parent3bfd5c028e3685a103c969d3753f5f937dcb95a4 (diff)
downloadvdr-553bc142754863250ddce11803201a278a18be86.tar.gz
vdr-553bc142754863250ddce11803201a278a18be86.tar.bz2
Version 2.3.9vdr-2.3.9
VDR developer version 2.3.9 is now available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.3.9.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.3.8-2.3.9.diff MD5 checksums: 9e4202b046df9ea960d930ce99e967ab vdr-2.3.9.tar.bz2 38a0f436fbed219665725aa2e54a9ca0 vdr-2.3.8-2.3.9.diff Approaching version 2.4.0: ========================== If there are no more serious bug reports, the final version 2.4.0 of VDR shall be released on April 15. So please test this developer version intensely and report any problems you might encounter as soon as possible. The following language files still have the given number of untranslated texts: PLUGINS/src/hello/po/ca_ES.po: 6 PLUGINS/src/hello/po/da_DK.po: 6 PLUGINS/src/hello/po/el_GR.po: 6 PLUGINS/src/hello/po/es_ES.po: 6 PLUGINS/src/hello/po/fr_FR.po: 6 PLUGINS/src/hello/po/hu_HU.po: 6 PLUGINS/src/hello/po/nl_NL.po: 6 PLUGINS/src/hello/po/nn_NO.po: 6 PLUGINS/src/hello/po/pt_PT.po: 6 PLUGINS/src/hello/po/ro_RO.po: 6 PLUGINS/src/hello/po/sl_SI.po: 6 PLUGINS/src/hello/po/sv_SE.po: 6 po/ar.po: 76 po/ca_ES.po: 76 po/cs_CZ.po: 22 po/da_DK.po: 208 po/el_GR.po: 271 po/es_ES.po: 22 po/et_EE.po: 7 po/fi_FI.po: 3 po/fr_FR.po: 22 po/hr_HR.po: 208 po/hu_HU.po: 22 po/it_IT.po: 3 po/lt_LT.po: 22 po/mk_MK.po: 22 po/nl_NL.po: 22 po/nn_NO.po: 336 po/pt_PT.po: 104 po/ro_RO.po: 22 po/ru_RU.po: 10 po/sk_SK.po: 22 po/sl_SI.po: 77 po/sr_RS.po: 76 po/sv_SE.po: 22 po/tr_TR.po: 208 po/uk_UA.po: 22 po/zh_CN.po: 76 If nobody takes care of these, they will remain untranslated in version 2.4.0. The changes since version 2.3.8: - Updated the Italian OSD texts (thanks to Diego Pierotto). - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). - Fixed a possible crash when stopping VDR (thanks to Matthias Senzel for reporting and helping to debug this one). - Fixed handling VPS events outside the LingerLimit, which could cause recordings to stop prematurely (thanks to Johann Friedrichs). - Fixed an invalid lock sequence when trying to remove a deleted recording in case of low disk space. - Now making sure that AssertFreeDiskSpace() is called with the maximum timer priority in case there are several timers recording with different priorities. - The MTD mapper now avoids immediately reusing unique PIDs when switching channels, to prevent possible problems with old data in buffers (thanks to Onur Sentürk). - The function cDevice::GetVideoSystem() (which has been deprecated since version 2.1.6) has been finally removed. - The macros used to control deprecated code or functions have been changed to hold numeric values (0 and 1), so that they can be controlled at compile time, without having to edit the actual source code (suggested by Jasmin Jessich). - The default for DEPRECATED_VDR_CHARSET_OVERRIDE has been set to 0, which means VDR no longer reacts on the environment variable VDR_CHARSET_OVERRIDE. You can add 'DEPRECATED_VDR_CHARSET_OVERRIDE=1' when compiling in order to restore this functionality. However, it is recommended to use the command line option --chartab instead. - The timeout for the channel display is now reset whenever the channel or EPG data changes. - OSD menus now try to keep the offset of the list cursor at a constant position on the screen, even if the list is modified while being displayed. - The LCARS skin's main menu now reacts to changes of the current channel's name. - If an event in the Schedules menu is marked with a 'T' or 'I' and the user presses the Red button to edit the timer, local timers are now preferred over remote timers in case there is more than one timer that will record that event. - Switching the primary device is no longer done via osSwitchDvb (which has been removed), but rather by the main program loop reacting to changes in Setup.PrimaryDVB. - The new SVDRP commands 'LSTD' and 'PRIM' can be used to list all available devices and to switch the primary device (thanks to Thomas Reufer). - Added some comments regarding font height (thanks to Thomas Reufer). - Fixed handling timers during the change from DST to winter time (thanks to Johann Friedrichs). - Added missing checks of 'player' in member functions of cControl, and setting cControl::player to NULL in cDvbPlayerControl::Stop() to avoid a possible crash with plugins that retrieve player information after a replay has been stopped, but before the replay control has been destroyed (thanks to Johann Friedrich). - Now calling Hide() and cStatus::MsgReplaying(..., false) from cReplayControl::Stop(), to inform plugins about an ending replay session before the replay control gets destroyed. - Fixed a possible crash when moving a recording between different volumes (reported by Matthias Senzel). - Fixed positioning the cursor in the Recordings menu when moving a recording between different volumes. - Added a note to PLUGINS.html about writing log messages in English. - Fixed a deadlock when moving a folder containing several recordings between different volumes (reported by Matthias Senzel). - Fixed positioning to the current item when changing the sort mode in the Recordings menu, in case there is a LastReplayed recording. - The CAM menu is now automatically closed when the current channel is switched (suggested by Dietmar Spingler). - Fixed a lengthy write lock on the Recordings list in case of moving a folder with more than one recording (thanks to Matthias Senzel). - If TS packets are not accepted by the output device in Transfer Mode, this is now reported only once per minute in the log file. - The new setup option "OSD/Sorting direction for recordings" can be used to switch the sequence in which recordings are presented in the "Recordings" menu between ascending (oldest first) and descendeng (newest first) (thanks to Matthias Senzel). - When moving recordings between volumes, the "Recordings" menu now displays those items that have not yet been moved completely as non-selectable. This avoids situations where trying to play such a recording might fail. - Fixed canceling moving a folder with several recordings between volumes. - When moving a recording to a different folder, the cursor is no longer placed on the new location of the recording, but rather stays in the original folder (suggested by Matthias Senzel). If the original folder got empty by moving away the last recording it contained, the cursor is moved up until a non empty folder is found. - Changed the log message ""ERROR: copying directory '%s' to '%s' ended prematurely" from "error" to "info", because any actual error would have already been reported before this (suggested by Matthias Senzel). - When selecting a folder for a recording or timer, it is now possible to open a folder even if it doesn't contain any subfolders (suggested by Matthias Senzel). - Fixed a possible deadlock when detaching a receiver from a device. - Moved any locking from cutter.c into recording.c, to avoid a problem with locking the Recordings list (reported by Matthias Senzel). - Now using the 'example' macro in vdr.5 (thanks to Chris Mayo). - Now unlocking the Recordings list before displaying an error message in cMenuPathEdit::ApplyChanges() and cReplayControl::Stop() (reported by Matthias Senzel). - Fixed a possible deadlock when quickly zapping through encrypted channels (reported by Jörg Wendel). - The new function cStatus::MarksModified() can be implemented by plugins to get informed about any modifications to the editing marks of the currently played recording (based on a patch from Jörg Wendel). - Fixed handling editing marks in the replay progress display, in case the marks are deleted via the Info/Edit menu of the currently played recording (the progress display still displayed them). - Limited some CAM related log messages to the actual master CAM, if any. - The Perl script 'peerdemo' shows how one can find all the VDRs in the local network using the peer connection mechanism. - Added the UPDATE-2.4.0 file. - Making sure cSVDRPClient::Process() reads the entire reply once it started reading, even if no Response parameter is given. - Replaced the warning regarding the open SVDRP port in the INSTALL file with a remark about using svdrphosts.conf to completely disable SVDRP access. - Added a note about the fixed UDP port for SVDRP discovery to vdr.1. - Fixed updating the Timers menu after turning a local timer on/off with the Red button. - Fixed keeping the cursor position in the Recordings menu in case a timer starts recording while the menu is open. - When a timer is newly created in the Timers menu, it now immediately appears at the correct position in the list, rather than first being added at the end and then jumping to the proper offset. - Fixed getting the info of a newly edited recording (reported by Matthias Senzel). - Improved calculating signal strength and quality (thanks to Helmut Binder). - While a timer is recording, the file '.timer' in the recording directory now contains the full id of the timer that is currently recording into this directory. This is used to determine whether a timer is still recording on a remote VDR when deleting a recording from the Recordings menu. - Fixed handling SVDRP peering for more than one instance of VDR on the same machine, and improved logging and debug output. - Fixed case inconsistency with SVDRPDefaultHost in config.c. - Added a section about the '.sort' file to vdr.5. - Initiating the client side of a peer-to-peer SVDRP connection is now done with the new SVDRP command CONN instead of using the UDP port with the server's address. This change requires that all VDRs that shall take part in a peer-to-peer network need to be updated to this version. - Moved handling remote timers into cSVDRPClientHandler::ProcessConnections(). - Combined Start/StopSVDRPServer/ClientHandler() into Start/StopSVDRPHandler(). - Updated the Polish OSD texts (thanks to Tomasz Maciej Nowak). - When remote timers are fetched from a peer VDR, we no longer blindly delete and re-add them, but rather compare them and make only the minimum necessary changes. - Fixed the CompareInts() function. - Disabled the use of posix_fadvise() when reading (i.e. replaying), since it caused stuttering replay in fast forward and fast rewind mode in case the video directory is mounted via NFS. You can re-enable it by setting the macro USE_FADVISE_READ to 1 in tools.c. - Modified cStateLock's SetExplicitModify() and IncState() (changed to SetModified()) to allow for the introduction of syncing a separate cStateKey to a cStateLock. - Assigning events to timers no longer triggers sending a POLL to all peer VDRs. - When making modifications to remote timers, the local VDR no longer sends a POLL to all remote VDRs. - Fixed removing a cStateKey from a cStateLock (setting StateKey.stateLock = NULL was done too late, after the lock had already been released). - Now writing the info file before attaching the recorder to the device, to make sure it is present when the recorder needs to update the fps value. - Making sure the Schedules menu has a proper title, even if it is empty. - Removed sending the SVDRP command UPDR to peer VDRs whenever a change is made to the recordings in the video directory (which was introduced in version 2.3.8), because it triggered re-reading the video directory too fast. - Improved handling VPS timers to better react to EPG changes during an ongoing recording. - Commented out the logging in cMarks::Align(), to avoid log entries in case of editing marks that are not generated by VDR itself, and thus may be a little off (suggested by Jörg Wendel). You can activate this line again for debugging if necessary. - Made the input buffer in cSVDRPClient dynamic. - Fixed handling parameters in the S2SatelliteDeliverySystemDescriptor and T2DeliverySystemDescriptor that were overwritten when parsing the SatelliteDeliverySystemDescriptor or TerrestrialDeliverySystemDescriptor, respectively. - Modified cMenuTimers::Delete() to avoid a lengthy lock on the Timers list while prompting the user.
-rw-r--r--CONTRIBUTORS41
-rw-r--r--HISTORY159
-rw-r--r--INSTALL9
-rw-r--r--MANUAL7
-rw-r--r--PLUGINS.html5
-rw-r--r--PLUGINS/src/hello/po/pl_PL.po23
-rw-r--r--PLUGINS/src/pictures/HISTORY7
-rwxr-xr-xPLUGINS/src/pictures/pic2mpg22
-rw-r--r--PLUGINS/src/pictures/pictures.c4
-rw-r--r--PLUGINS/src/pictures/po/pl_PL.po34
-rw-r--r--PLUGINS/src/skincurses/po/pl_PL.po32
-rw-r--r--UPDATE-2.4.0440
-rw-r--r--ci.c39
-rw-r--r--ci.h6
-rw-r--r--config.c7
-rw-r--r--config.h11
-rw-r--r--cutter.c7
-rw-r--r--device.c11
-rw-r--r--device.h21
-rw-r--r--dvbdevice.c261
-rw-r--r--dvbplayer.c3
-rw-r--r--eit.c4
-rw-r--r--font.h14
-rw-r--r--menu.c380
-rw-r--r--menu.h3
-rw-r--r--mtd.c11
-rw-r--r--nit.c19
-rw-r--r--osd.c4
-rw-r--r--osd.h8
-rw-r--r--osdbase.c9
-rw-r--r--osdbase.h4
-rwxr-xr-xpeerdemo136
-rw-r--r--player.h15
-rw-r--r--po/ar.po26
-rw-r--r--po/ca_ES.po26
-rw-r--r--po/cs_CZ.po26
-rw-r--r--po/da_DK.po26
-rw-r--r--po/de_DE.po26
-rw-r--r--po/el_GR.po26
-rw-r--r--po/es_ES.po26
-rw-r--r--po/et_EE.po26
-rw-r--r--po/fi_FI.po34
-rw-r--r--po/fr_FR.po26
-rw-r--r--po/hr_HR.po26
-rw-r--r--po/hu_HU.po26
-rw-r--r--po/it_IT.po34
-rw-r--r--po/lt_LT.po26
-rw-r--r--po/mk_MK.po26
-rw-r--r--po/nl_NL.po26
-rw-r--r--po/nn_NO.po26
-rw-r--r--po/pl_PL.po572
-rw-r--r--po/pt_PT.po26
-rw-r--r--po/ro_RO.po26
-rw-r--r--po/ru_RU.po26
-rw-r--r--po/sk_SK.po26
-rw-r--r--po/sl_SI.po26
-rw-r--r--po/sr_RS.po26
-rw-r--r--po/sv_SE.po26
-rw-r--r--po/tr_TR.po26
-rw-r--r--po/uk_UA.po26
-rw-r--r--po/zh_CN.po26
-rw-r--r--recording.c170
-rw-r--r--recording.h7
-rw-r--r--skinlcars.c6
-rw-r--r--skins.h8
-rw-r--r--status.c8
-rw-r--r--status.h8
-rw-r--r--svdrp.c612
-rw-r--r--svdrp.h22
-rw-r--r--thread.c65
-rw-r--r--thread.h22
-rw-r--r--timers.c232
-rw-r--r--timers.h27
-rw-r--r--tools.c35
-rw-r--r--tools.h25
-rw-r--r--transfer.c12
-rw-r--r--transfer.h4
-rw-r--r--vdr.17
-rw-r--r--vdr.591
-rw-r--r--vdr.c86
80 files changed, 3186 insertions, 1307 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 04487a6..a906532 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1847,7 +1847,7 @@ Lucian Muresan <lucianm@users.sourceforge.net>
for exporting some libsi functions
for suggesting to add functions to cDevice that allow derived output devices to
implement scaling the video to a given size and location
- fpr sorting sources.conf by continous azimuth
+ for sorting sources.conf by continuous azimuth
Mattias Grnlund <Mattias@Gronlund.net>
for pointing out a missing cleanup at program exit in case there is a problem
@@ -2627,6 +2627,9 @@ Jrg Wendel <vdr-ml@jwendel.de>
for making cEpgHandlers::BeginSegmentTransfer() boolean
for suggesting to change tEventID back to u_int32_t
for adding the 'aux' member to cEvent
+ for reporting a possible deadlock when quickly zapping through encrypted channels
+ for a patch that was used to implement cStatus::MarksModified()
+ for suggesting to no longer log unaligned marks in cMarks::Align()
Peter Pinnau <vdr@unterbrecher.de>
for reporting that 'uint32_t' requires including stdint.h in font.h on some systems
@@ -2837,6 +2840,11 @@ Johann Friedrichs <johann.friedrichs@web.de>
false
for reporting an invalid lock sequence in the epgsearch plugin, which turned out to
be an abandoned member of class cSchedulesLock
+ for fixing handling VPS events outside the LingerLimit, which could cause recordings to
+ stop prematurely
+ for fixing handling timers during the change from DST to winter time
+ for fixing a possible crash with plugins that retrieve player information after a
+ replay has been stopped, but before the replay control has been destroyed
Timo Helkio <timolavi@mbnet.fi>
for reporting a hangup when replaying a TS recording with subtitles activated
@@ -3106,6 +3114,7 @@ Chris Mayo <aklhfex@gmail.com>
for fixing the link to "svdrpsend (1)" in the vdr.1 man page
for updating links in the INSTALL file
for reporting double slashes in file names processed with AddDirectory()
+ for using the 'example' macro in vdr.5
Dominic Evans <oldmanuk@gmail.com>
for making the SVDRP command LSTC accepts channel IDs
@@ -3277,6 +3286,24 @@ Matthias Senzel <matthias.senzel@t-online.de>
various Now and Schedule menus for different channels
for the "jumpingseconds" patch
for reporting a bug in drawing very long menu titles in the LCARS skin
+ for reporting and helping to debug a crash when stopping VDR
+ for reporting a crash when moving a recording between different volumes
+ for reporting a deadlock when moving a folder containing several recordings between
+ different volumes
+ for fixing a lengthy write lock on the Recordings list in case of moving a folder with
+ more than one recording
+ for implementing the parameter "OSD/Sorting direction for recordings"
+ for suggesting to stay in the original folder when moving a recording to a different
+ folder
+ for reporting problem with locking the Recordings list in the cutting process
+ for suggesting to change the log message ""ERROR: copying directory '%s' to '%s' ended
+ prematurely" from "error" to "info"
+ for suggesting to allow opening a folder when selecting a folder for a recording or
+ timer, even if it doesn't contain any subfolders
+ for reporting a possible locking problem in cMenuPathEdit::ApplyChanges() when the
+ lock is held while the error message is displayed
+ for reporting that the info of a newly edited recording was not available immediately
+ after starting the editing process
Marek Nazarko <mnazarko@gmail.com>
for translating OSD texts to the Polish language
@@ -3370,6 +3397,8 @@ Thomas Reufer <thomas@reufer.ch>
for adding cFont::Width(void) to get the default character width and allow stretched
font drawing in high level OSDs
for fixing regenerating the index of audio recordings
+ for implementing the SVDRP commands 'LSTD' and 'PRIM'
+ for adding some comments regarding font height
Eike Sauer <EikeSauer@t-online.de>
for reporting a problem with channels that need more than 5 TS packets for detecting
@@ -3414,6 +3443,7 @@ Dietmar Spingler <d_spingler@gmx.de>
for suggesting to optionally list the channels with channel ids in the SVDRP command LSTC
for suggesting to include the channel ID in log messages about switching channels
for reporting a problem with the SVDRP command CHAN while the channel display is open
+ for suggesting to automatically close the CAM menu when the current channel is switched
Stefan Schallenberg <infos@nafets.de>
for adding the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement()
@@ -3443,6 +3473,9 @@ Jasmin Jessich <jasmin@anw.at>
for suggesting to use $(Q) to control Makefile verbosity
for adding handling DEBUG to the Make.config.template file, in order to control
code optimization
+ for suggesting to change the macros used to control deprecated code or functions
+ to numeric values (0 and 1), so that they can be controlled at compile time, without
+ having to edit the actual source code
Martin Schirrmacher <schirrmie@gmail.com>
for suggesting to provide a way for skin plugins to get informed about the currently
@@ -3511,3 +3544,9 @@ Frank Richter <kulpstur@t-online.de>
Daniel Scheller <d.scheller@gmx.net>
for reporting a problem with detecting whether a CAM replies to queries, which didn't
work on some systems since the implementation of RI_HOST_CONTROL
+
+Onur Sentrk <onur@sentek.org>
+ for making the MTD mapper avoid immediately reusing unique PIDs when switching channels
+
+Helmut Binder <cco@aon.at>
+ for improving calculating signal strength and quality
diff --git a/HISTORY b/HISTORY
index f5fea54..2611da6 100644
--- a/HISTORY
+++ b/HISTORY
@@ -3905,7 +3905,7 @@ Video Disk Recorder Revision History
- Updated the French OSD texts (thanks to Nicolas Huillard).
- Fixed the cFilter example in PLUGINS.html (reported by Patrick Fischer).
- The new class cUnbufferedFile is used for the recording files to avoid
- trashing the file system cache (based on a patch by Ralf Mller).
+ thrashing the file system cache (based on a patch by Ralf Mller).
2005-11-06: Version 1.3.36
@@ -8866,7 +8866,7 @@ Video Disk Recorder Revision History
font drawing in high level OSDs (thanks to Thomas Reufer).
- Fixed regenerating the index of audio recordings (thanks to Thomas Reufer).
- Fixed building VDR with systemd >= 230 (thanks to Ville Skytt).
-- Sorted sources.conf by continous azimuth (thanks to Lucian Muresan).
+- Sorted sources.conf by continuous azimuth (thanks to Lucian Muresan).
- Added 'S58.5E Kazsat 3' to sources.conf (thanks to Aitugan Sarbassov).
- Fixed truncated date/time strings in the skins on multi-byte UTF-8 systems
(reported by Sergey Chernyavskiy).
@@ -9161,3 +9161,158 @@ Video Disk Recorder Revision History
This is especially useful if one VDR mounts the video directory of an other one into
a subdirectory.
- SVDRP peering can now be limited to the default SVDRP host (see MANUAL for details).
+
+2018-03-18: Version 2.3.9
+
+- Updated the Italian OSD texts (thanks to Diego Pierotto).
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Fixed a possible crash when stopping VDR (thanks to Matthias Senzel for reporting and
+ helping to debug this one).
+- Fixed handling VPS events outside the LingerLimit, which could cause recordings to
+ stop prematurely (thanks to Johann Friedrichs).
+- Fixed an invalid lock sequence when trying to remove a deleted recording in case
+ of low disk space.
+- Now making sure that AssertFreeDiskSpace() is called with the maximum timer
+ priority in case there are several timers recording with different priorities.
+- The MTD mapper now avoids immediately reusing unique PIDs when switching channels,
+ to prevent possible problems with old data in buffers (thanks to Onur Sentrk).
+- The function cDevice::GetVideoSystem() (which has been deprecated since version 2.1.6)
+ has been finally removed.
+- The macros used to control deprecated code or functions have been changed to hold
+ numeric values (0 and 1), so that they can be controlled at compile time, without
+ having to edit the actual source code (suggested by Jasmin Jessich).
+- The default for DEPRECATED_VDR_CHARSET_OVERRIDE has been set to 0, which means VDR
+ no longer reacts on the environment variable VDR_CHARSET_OVERRIDE. You can add
+ 'DEPRECATED_VDR_CHARSET_OVERRIDE=1' when compiling in order to restore this
+ functionality. However, it is recommended to use the command line option --chartab
+ instead.
+- The timeout for the channel display is now reset whenever the channel or EPG data
+ changes.
+- OSD menus now try to keep the offset of the list cursor at a constant position on
+ the screen, even if the list is modified while being displayed.
+- The LCARS skin's main menu now reacts to changes of the current channel's name.
+- If an event in the Schedules menu is marked with a 'T' or 'I' and the user presses the
+ Red button to edit the timer, local timers are now preferred over remote timers
+ in case there is more than one timer that will record that event.
+- Switching the primary device is no longer done via osSwitchDvb (which has been
+ removed), but rather by the main program loop reacting to changes in Setup.PrimaryDVB.
+- The new SVDRP commands 'LSTD' and 'PRIM' can be used to list all available devices
+ and to switch the primary device (thanks to Thomas Reufer).
+- Added some comments regarding font height (thanks to Thomas Reufer).
+- Fixed handling timers during the change from DST to winter time (thanks to Johann
+ Friedrichs).
+- Added missing checks of 'player' in member functions of cControl, and setting
+ cControl::player to NULL in cDvbPlayerControl::Stop() to avoid a possible crash
+ with plugins that retrieve player information after a replay has been stopped, but
+ before the replay control has been destroyed (thanks to Johann Friedrich).
+- Now calling Hide() and cStatus::MsgReplaying(..., false) from cReplayControl::Stop(),
+ to inform plugins about an ending replay session before the replay control gets
+ destroyed.
+- Fixed a possible crash when moving a recording between different volumes (reported by
+ Matthias Senzel).
+- Fixed positioning the cursor in the Recordings menu when moving a recording between
+ different volumes.
+- Added a note to PLUGINS.html about writing log messages in English.
+- Fixed a deadlock when moving a folder containing several recordings between
+ different volumes (reported by Matthias Senzel).
+- Fixed positioning to the current item when changing the sort mode in the Recordings
+ menu, in case there is a LastReplayed recording.
+- The CAM menu is now automatically closed when the current channel is switched
+ (suggested by Dietmar Spingler).
+- Fixed a lengthy write lock on the Recordings list in case of moving a folder with
+ more than one recording (thanks to Matthias Senzel).
+- If TS packets are not accepted by the output device in Transfer Mode, this is now
+ reported only once per minute in the log file.
+- The new setup option "OSD/Sorting direction for recordings" can be used to switch
+ the sequence in which recordings are presented in the "Recordings" menu between
+ ascending (oldest first) and descendeng (newest first) (thanks to Matthias Senzel).
+- When moving recordings between volumes, the "Recordings" menu now displays those items
+ that have not yet been moved completely as non-selectable. This avoids situations
+ where trying to play such a recording might fail.
+- Fixed canceling moving a folder with several recordings between volumes.
+- When moving a recording to a different folder, the cursor is no longer placed on the
+ new location of the recording, but rather stays in the original folder (suggested by
+ Matthias Senzel). If the original folder got empty by moving away the last recording
+ it contained, the cursor is moved up until a non empty folder is found.
+- Changed the log message ""ERROR: copying directory '%s' to '%s' ended prematurely" from
+ "error" to "info", because any actual error would have already been reported before this
+ (suggested by Matthias Senzel).
+- When selecting a folder for a recording or timer, it is now possible to open a folder
+ even if it doesn't contain any subfolders (suggested by Matthias Senzel).
+- Fixed a possible deadlock when detaching a receiver from a device.
+- Moved any locking from cutter.c into recording.c, to avoid a problem with locking
+ the Recordings list (reported by Matthias Senzel).
+- Now using the 'example' macro in vdr.5 (thanks to Chris Mayo).
+- Now unlocking the Recordings list before displaying an error message in
+ cMenuPathEdit::ApplyChanges() and cReplayControl::Stop() (reported by Matthias Senzel).
+- Fixed a possible deadlock when quickly zapping through encrypted channels (reported
+ by Jrg Wendel).
+- The new function cStatus::MarksModified() can be implemented by plugins to get
+ informed about any modifications to the editing marks of the currently played
+ recording (based on a patch from Jrg Wendel).
+- Fixed handling editing marks in the replay progress display, in case the marks are
+ deleted via the Info/Edit menu of the currently played recording (the progress
+ display still displayed them).
+- Limited some CAM related log messages to the actual master CAM, if any.
+- The Perl script 'peerdemo' shows how one can find all the VDRs in the local network
+ using the peer connection mechanism.
+- Added the UPDATE-2.4.0 file.
+- Making sure cSVDRPClient::Process() reads the entire reply once it started reading,
+ even if no Response parameter is given.
+- Replaced the warning regarding the open SVDRP port in the INSTALL file with a remark
+ about using svdrphosts.conf to completely disable SVDRP access.
+- Added a note about the fixed UDP port for SVDRP discovery to vdr.1.
+- Fixed updating the Timers menu after turning a local timer on/off with the Red
+ button.
+- Fixed keeping the cursor position in the Recordings menu in case a timer starts
+ recording while the menu is open.
+- When a timer is newly created in the Timers menu, it now immediately appears at the
+ correct position in the list, rather than first being added at the end and then
+ jumping to the proper offset.
+- Fixed getting the info of a newly edited recording (reported by Matthias Senzel).
+- Improved calculating signal strength and quality (thanks to Helmut Binder).
+- While a timer is recording, the file '.timer' in the recording directory now contains
+ the full id of the timer that is currently recording into this directory. This is used
+ to determine whether a timer is still recording on a remote VDR when deleting a recording
+ from the Recordings menu.
+- Fixed handling SVDRP peering for more than one instance of VDR on the same machine, and
+ improved logging and debug output.
+- Fixed case inconsistency with SVDRPDefaultHost in config.c.
+- Added a section about the '.sort' file to vdr.5.
+- Initiating the client side of a peer-to-peer SVDRP connection is now done with the new
+ SVDRP command CONN instead of using the UDP port with the server's address.
+ This change requires that all VDRs that shall take part in a peer-to-peer network need
+ to be updated to this version.
+- Moved handling remote timers into cSVDRPClientHandler::ProcessConnections().
+- Combined Start/StopSVDRPServer/ClientHandler() into Start/StopSVDRPHandler().
+- Updated the Polish OSD texts (thanks to Tomasz Maciej Nowak).
+- When remote timers are fetched from a peer VDR, we no longer blindly delete and re-add
+ them, but rather compare them and make only the minimum necessary changes.
+- Fixed the CompareInts() function.
+- Disabled the use of posix_fadvise() when reading (i.e. replaying), since it caused
+ stuttering replay in fast forward and fast rewind mode in case the video directory
+ is mounted via NFS. You can re-enable it by setting the macro USE_FADVISE_READ to 1
+ in tools.c.
+- Modified cStateLock's SetExplicitModify() and IncState() (changed to SetModified()) to
+ allow for the introduction of syncing a separate cStateKey to a cStateLock.
+- Assigning events to timers no longer triggers sending a POLL to all peer VDRs.
+- When making modifications to remote timers, the local VDR no longer sends a POLL to
+ all remote VDRs.
+- Fixed removing a cStateKey from a cStateLock (setting StateKey.stateLock = NULL was
+ done too late, after the lock had already been released).
+- Now writing the info file before attaching the recorder to the device, to make sure it
+ is present when the recorder needs to update the fps value.
+- Making sure the Schedules menu has a proper title, even if it is empty.
+- Removed sending the SVDRP command UPDR to peer VDRs whenever a change is made to the
+ recordings in the video directory (which was introduced in version 2.3.8), because it
+ triggered re-reading the video directory too fast.
+- Improved handling VPS timers to better react to EPG changes during an ongoing recording.
+- Commented out the logging in cMarks::Align(), to avoid log entries in case of editing
+ marks that are not generated by VDR itself, and thus may be a little off (suggested by
+ Jrg Wendel). You can activate this line again for debugging if necessary.
+- Made the input buffer in cSVDRPClient dynamic.
+- Fixed handling parameters in the S2SatelliteDeliverySystemDescriptor and
+ T2DeliverySystemDescriptor that were overwritten when parsing the
+ SatelliteDeliverySystemDescriptor or TerrestrialDeliverySystemDescriptor, respectively.
+- Modified cMenuTimers::Delete() to avoid a lengthy lock on the Timers list while prompting
+ the user.
diff --git a/INSTALL b/INSTALL
index 74caf30..e75c038 100644
--- a/INSTALL
+++ b/INSTALL
@@ -73,16 +73,15 @@ port ("Simple Video Disk Recorder Protocol"). By default, it listens
on port 6419 (use the --port=PORT option to change this). For details
about the SVDRP syntax see the source file 'svdrp.c'.
-WARNING: DUE TO THE OPEN SVDRP PORT THIS PROGRAM MAY CONSTITUTE A
-======= POTENTIAL SECURITY HAZARD! IF YOU ARE NOT RUNNING VDR IN
- A CONTROLLED ENVIRONMENT, YOU MAY WANT TO DISABLE SVDRP
- BY USING '--port=0'!
-
The file 'svdrphosts.conf' can be used to define which hosts are allowed
to access the SVDRP port. By default only localhost (127.0.0.1) is granted
access. If you want to give other hosts access to your SVDRP port you need to
add their IP numbers to 'svdrphosts.conf'.
+You can disable SVDRP access entirely by either running VDR with '--port=0',
+or by removing all entries (including 127.0.0.1 for the localhost) from
+'svdrphosts.conf'.
+
If the program shall run as a daemon, use the --daemon option. This
will completely detach it from the terminal and will continue as a
background process.
diff --git a/MANUAL b/MANUAL
index 47b2fdd..4020fe3 100644
--- a/MANUAL
+++ b/MANUAL
@@ -665,6 +665,13 @@ Version 2.2
If a particular sort mode has been selected for a folder by
pressing '0', the default no longer applies to that folder.
+ Sorting direction for recordings = ascending
+ When recordings are sorted "by time", they appear in ascending
+ order (i.e. "oldest" to "newest"). If this parameter is set to
+ "descending", they will be presented "newest" to "oldest.
+ Note that in the latter case, if "Always sort folders first"
+ is "yes", folders will appear in reverse alphabetical order.
+
Number keys for characters = yes
Controls whether the number keys can be used to enter
characters in a text input field. You may want to set this
diff --git a/PLUGINS.html b/PLUGINS.html
index 92a4d31..fb6ff2a 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -605,7 +605,10 @@ Under no circumstances must a plugin print anything to stdout or stderr during
normal operation! The only exceptions being special debug information as described
above, fatal error messages that will cause VDR to abort, or if it is the sole
purpose of the plugin to display something on stdout, like for instance the
-<i>skincurses</i> plugin, which displays the OSD at the console.
+<i>skincurses</i> plugin, which displays the OSD at the console.<br>
+<br>
+Please make any log messages in <b>ENGLISH</b>! Logs are usually sent to the developers
+of a program, and they may not be able to read them if they are in an exotic language.
</modified>
<hr><h2><a name="Main menu entry">Main menu entry</a></h2>
diff --git a/PLUGINS/src/hello/po/pl_PL.po b/PLUGINS/src/hello/po/pl_PL.po
index c00efdc..71c152e 100644
--- a/PLUGINS/src/hello/po/pl_PL.po
+++ b/PLUGINS/src/hello/po/pl_PL.po
@@ -2,34 +2,37 @@
# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
# This file is distributed under the same license as the VDR package.
# Michael Rakowski <mrak@gmx.de>, 2002
+# Tomasz Maciej Nowak <tmn505@gmail.com>, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-hello 2.2.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-12-18 14:04+0100\n"
-"PO-Revision-Date: 2007-08-11 12:34+0200\n"
-"Last-Translator: Michael Rakowski <mrak@gmx.de>\n"
+"PO-Revision-Date: 2018-02-19 00:41+0100\n"
+"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
"Language-Team: Polish <vdr@linuxtv.org>\n"
-"Language: pl\n"
+"Language: pl_PL\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 2.0.6\n"
msgid "A friendly greeting"
-msgstr ""
+msgstr "Przyjazne pozdrowienie"
msgid "Hello"
-msgstr ""
+msgstr "Witaj"
msgid "Greeting time (s)"
-msgstr ""
+msgstr "Czas pozdrowienia (s)"
msgid "Use alternate greeting"
-msgstr ""
+msgstr "Użyj alternatywnego pozdrowienia"
msgid "Howdy folks!"
-msgstr ""
+msgstr "Siema ziomy!"
msgid "Hello world!"
-msgstr ""
+msgstr "Witaj świecie!"
diff --git a/PLUGINS/src/pictures/HISTORY b/PLUGINS/src/pictures/HISTORY
index b9e476e..08fabb1 100644
--- a/PLUGINS/src/pictures/HISTORY
+++ b/PLUGINS/src/pictures/HISTORY
@@ -100,6 +100,11 @@ VDR Plugin 'pictures' Revision History
- Official release.
-2015-07.23: Version 2.3.1
+2015-07-23: Version 2.3.1
- Added a missing 'const'.
+
+2017-10-06: Version 2.3.2
+
+- Adapted the pic2mpg script to new ffmpeg options.
+- No longer using 'convert' to scale/rotate the pictures.
diff --git a/PLUGINS/src/pictures/pic2mpg b/PLUGINS/src/pictures/pic2mpg
index 27d0264..1c7d31b 100755
--- a/PLUGINS/src/pictures/pic2mpg
+++ b/PLUGINS/src/pictures/pic2mpg
@@ -7,7 +7,7 @@
#
# See the README file for copyright information and how to reach the author.
#
-# $Id: pic2mpg 4.0 2013/07/01 08:33:38 kls Exp $
+# $Id: pic2mpg 4.1 2017/10/06 14:42:18 kls Exp $
use File::Path;
use File::Spec;
@@ -128,7 +128,7 @@ for ($i = 0; $i < 10; $i++) { # dirs might become empty when removing empty subd
for $dir (@Dirs) {
$dir = EscapeMeta($dir);
print "removing $dir\n";
- !system("rm -rf $dir") || die "$dir: $!\n";
+ Exec("rm -rf $dir");
}
}
@@ -142,17 +142,18 @@ sub ConvertFile
my $Exif = ImageInfo($Pict);
my $Orientation = $$Exif{"Orientation"};
my ($Degrees) = $Orientation =~ /Rotate ([0-9]+)/;
- my $Rotate = $Degrees ? "-rotate $Degrees" : "";
+ my $Rotate = ($Degrees == 90) ? "transpose=clock" : ($Degrees == 180) ? "hflip,vflip" : ($Degrees == 270) ? "transpose=cclock" : "";
+ $Rotate .= ',' if ($Rotate);
+ my $Background = '#000000@1';
print "orientation = '$Orientation' -> rotation = $Rotate\n" if ($Detailed);
$Pict = EscapeMeta($Pict);
$Mpeg = EscapeMeta($Mpeg);
print "$Pict -> $Mpeg $Rotate\n" if $ListFiles;
- my $Cmd = "convert $Pict -background '#000000' $Rotate -resize $Size -gravity center -extent $Extent ppm:- | "
- . "ffmpeg -f image2pipe -vcodec ppm -i pipe:0 -an -vcodec libx264 -vpre baseline -s $Size -qscale 2 -f mpegts -y $Mpeg "
+ my $Cmd = "ffmpeg -i $Pict -vf '${Rotate}scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:$Background' -c:v libx264 -pix_fmt yuv420p -f mpegts -y $Mpeg "
. ($Detailed ? "" : "2>/dev/null");
- !system($Cmd) || die "$Cmd: $!\n";
+ Exec($Cmd);
$Cmd = "touch -r $Pict $Mpeg";
- !system($Cmd) || die "$Cmd: $!\n";
+ Exec($Cmd);
}
sub EscapeMeta
@@ -162,3 +163,10 @@ sub EscapeMeta
$s =~ s/([$META])/\\$1/g;
return $s;
}
+
+sub Exec
+{
+ my $Cmd = shift;
+ print "==> '$Cmd'\n" if ($Verbose);
+ !system($Cmd) || die "$Cmd: $!\n";
+}
diff --git a/PLUGINS/src/pictures/pictures.c b/PLUGINS/src/pictures/pictures.c
index 509514e..430ebe2 100644
--- a/PLUGINS/src/pictures/pictures.c
+++ b/PLUGINS/src/pictures/pictures.c
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: pictures.c 4.1 2015/07/23 10:14:22 kls Exp $
+ * $Id: pictures.c 4.2 2017/10/06 15:10:44 kls Exp $
*/
#include <getopt.h>
@@ -11,7 +11,7 @@
#include "menu.h"
#include "player.h"
-static const char *VERSION = "2.3.1";
+static const char *VERSION = "2.3.2";
static const char *DESCRIPTION = trNOOP("A simple picture viewer");
static const char *MAINMENUENTRY = trNOOP("Pictures");
diff --git a/PLUGINS/src/pictures/po/pl_PL.po b/PLUGINS/src/pictures/po/pl_PL.po
new file mode 100644
index 0000000..2d0de10
--- /dev/null
+++ b/PLUGINS/src/pictures/po/pl_PL.po
@@ -0,0 +1,34 @@
+# VDR plugin language source file.
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
+# This file is distributed under the same license as the VDR package.
+# Tomasz Maciej Nowak <tmn505@gmail.com>, 2018
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-pictures 2.2.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2018-02-19 00:48+0100\n"
+"PO-Revision-Date: 2018-02-19 00:53+0100\n"
+"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
+"Language-Team: Polish <vdr@linuxtv.org>\n"
+"Language: pl_PL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 2.0.6\n"
+
+msgid "Pictures"
+msgstr "Zdjęcia"
+
+msgid "A simple picture viewer"
+msgstr "Prosta przeglądarka zdjęć"
+
+msgid "Picture directory"
+msgstr "Katalog ze zdjęciami"
+
+msgid "Slide show delay (s)"
+msgstr "Pokaz slajdów opóźnienie (s)"
+
+msgid "No picture directory has been defined!"
+msgstr "Nie zdefiniowano katalogu ze zdjęciami!"
diff --git a/PLUGINS/src/skincurses/po/pl_PL.po b/PLUGINS/src/skincurses/po/pl_PL.po
new file mode 100644
index 0000000..fd02724
--- /dev/null
+++ b/PLUGINS/src/skincurses/po/pl_PL.po
@@ -0,0 +1,32 @@
+# VDR plugin language source file.
+# Copyright (C) 2015 Klaus Schmidinger <vdr@tvdr.de>
+# This file is distributed under the same license as the VDR package.
+# Tomasz Maciej Nowak <tmn505@gmail.com>, 2018
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-skincurses 2.2.0\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2018-02-19 00:48+0100\n"
+"PO-Revision-Date: 2018-02-19 01:02+0100\n"
+"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
+"Language-Team: Polish <vdr@linuxtv.org>\n"
+"Language: pl_PL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 2.0.6\n"
+
+msgid "A text only skin"
+msgstr "Skóra tekstowa"
+
+msgid "Key$Mute"
+msgstr "Wycisz"
+
+#. TRANSLATORS: note the trailing blank!
+msgid "Volume "
+msgstr "Głośność "
+
+msgid "Text mode"
+msgstr "Tryb tekstowy"
diff --git a/UPDATE-2.4.0 b/UPDATE-2.4.0
new file mode 100644
index 0000000..cdf56dc
--- /dev/null
+++ b/UPDATE-2.4.0
@@ -0,0 +1,440 @@
+This is a summary of the changes in VDR 2.4.0 since the last stable
+version 2.2.0. It only contains things that are of actual importance
+to the user and doesn't mention the many fixes and improvements that
+have been made "behind the scenes".
+
+See the file HISTORY for a detailed list of all changes.
+
+Peering:
+
+- If there is more than one VDR in the local network, they can now form a peer-to-peer
+ network, so that timers can be moved freely between them.
+ The following changes have been made to implement this:
+ + VDR now sends out a broadcast to port 6419/udp, which was assigned to 'svdrp-disc'
+ by the IANA. VDRs listening on that port will automatically initiate an SVDRP
+ connection to the broadcasting VDR, and in turn send out a broadcast to make
+ other VDRs connect to them. That way all VDRs within the local network will
+ have permanent "peer-to-peer" SVDRP connections between each other. The
+ configuration in the svdrphosts.conf file is taken into account when considering
+ whether or not to respond to an SVDRP discover broadcast.
+ + The new SVDRP command PING is used by automatically established peer-to-peer
+ connections to keep them alive.
+ + The new function GetSVDRPServerNames() can be used to get a list of all VDRs
+ this VDR is connected to via SVDRP.
+ + The new function ExecSVDRPCommand() can be used to execute an SVDRP command on
+ one of the servers this VDR is connected to, and retrieve the result.
+ The helper functions SVDRPCode() and SVDRPValue() can be used to easily access
+ the codes and values returned by ExecSVDRPCommand().
+ + The new SVDRP command POLL is used by automatically established peer-to-peer
+ connections to trigger fetching remote timers.
+ + The new options "Setup/Miscellaneous/SVDRP peering", ".../SVDRP host name" and
+ ".../SVDRP default host" can be used to configure automatic peering between VDRs
+ in the same network. Peering is disabled by default and can be enabled by setting
+ "SVDRP peering" to "yes".
+ + The "Edit timer" menu now has a new parameter "Record on", which can be used to
+ select the VDR on which this timer shall record. Timers can be freely moved
+ between connected VDRs by simply selecting the desired machine in this field.
+ + The cTimer class now has a new member named 'remote', which holds the name of the
+ remote server this timer will record on. If this is NULL, it is a local timer.
+ + Added a note to the "Pausing live video" section of the MANUAL, stating that
+ the timer for paused live video will always record on the local VDR, even if
+ an "SVDRP default host" has been set for normal timer recordings.
+ + The Perl script 'peerdemo' shows how one can find all the VDRs in the local network
+ using the peer connection mechanism.
+
+Conditional Access:
+
+- Implemented support for MTD ("Multi Transponder Decryption"). This allows a CAM
+ that is capable of decrypting more than one channel ("Multi Channel Decryption")
+ to decrypt channels from different transponders. See the remarks in mtd.h on
+ what a derived cCamSlot class needs to do in order to activate MTD.
+- The Setup/CAM menu now displays which device an individual CAM is currently
+ assigned to.
+- The channel/CAM relations (i.e. the information which CAM can decrypt a given
+ channel) are now stored in the file 'cam.data' in the cache directory.
+ This speeds up switching to encrypted channels after newly starting VDR, in case
+ there is more than one CAM in the system.
+ The file 'cam.data' is not written if it is read-only.
+- The mechanism of trying different CAMs when switching to an encrypted channel is
+ now only triggered if there acually is more than one CAM in the system.
+- CAMs that can handle multiple devices at the same time can now indicate this
+ by creating the first cCamSlot as usual, and every other cCamSlot by giving
+ it the first one as its "MasterSlot". To VDR this means that when searching
+ for a CAM that can decrypt a particular channel, it only needs to ask the
+ master CAM slot whether it is suitable for decrypting, and can skip all the
+ other slots belonging to the same master. This can greatly speed up channel
+ switching on systems with more than one CAM (that can handle multiple devices).
+- The LCARS skin now displays the master CAM's number when a device is tuned to
+ an encrypted channel.
+- The Setup/CAM menu now only displays master CAMs.
+- Detecting whether a particular CAM actually decrypts a given channel is now
+ done separately for each receiver.
+- The function cCamSlot::Decrypt() can now also be called with Data == NULL.
+ This is necessary to allow CAMs that copy the incoming data into a separate buffer
+ to return previously received and decrypted TS packets. See ci.h for details.
+ Plugins that implement a derived cCamSlot need to properly handle this case, and
+ plugins that implement a derived cDevice need to call Decrypt() in their
+ GetTSPacket() function even if the incoming buffer is currently empty (see
+ cDvbDevice::GetTSPacket()).
+- CAMs are now sent a generated EIT packet that contains a single 'present event' for
+ the current SID, in order to avoid any parental rating dialogs.
+- When selecting a device/CAM combination for live viewing, CAMs that are known to
+ decrypt the requested channel are now given a higher priority than prefering the
+ primary device.
+- Extended the CI API to allow plugins to implement additional CAM resources.
+- The new configuration file 'camresponses.conf' can be used to define automatic
+ responses to CAM menus, for instance to avoid annyoing popup messages or entering
+ the parental rating PIN. See vdr.5 for details.
+
+Timers:
+
+- The cTimer class now has a new member named 'remote', which holds the name of the
+ remote server this timer will record on. If this is NULL, it is a local timer.
+- Timers from other VDRs that are connected to this VDR via SVDRP are now
+ automatically fetched and stored in the global Timers list. In order for this
+ to work, all of the channels used by timers on the remote VDR must also be
+ defined on the local VDR (however, not necessarily in the same sequence).
+- Accessing the global Timers list now has to be protected by proper locking,
+ because SVDRP commands are now executed in a separate thread.
+ The introduction of this locking mechanism required the following changes:
+ + The new classes cStateLock and cStateKey are used to implement locking
+ with quick detection of state changes.
+ + cConfig::cConfig() now has a parameter that indicates whether this list
+ requires locking.
+ + The global lists of Timers, Channels, Schedules and Recordings are no longer
+ static variables. They are now pointers that need to be retrieved through
+ a call to cTimers::GetTimersRead/Write(), cChannels::GetChannelsRead/Write(),
+ cSchedules::GetSchedulesRead/Write() and cRecordings::GetRecordingsRead/Write(),
+ respectively.
+ + References from/to link channels are now removed in cChannels::Del() rather
+ than cChannel::~cChannel(), to make sure the caller holds a proper lock.
+ + cChannel::HasTimer() has been removed. This information is now retrieved
+ via cSchedule::HasTimer().
+ + Several member functions of cChannel, cTimer, cMarks and cRecording have
+ been made 'const', and some of them are now available as both 'const' and
+ 'non-const' versions.
+ + The cChannel::Set...() functions are now 'bool' and return true if they have
+ actually changed any of the channels's members.
+ + cChannels::SetModified() has been renamed to cChannels::SetModifiedByUser().
+ + cChannels::Modified() has been renamed to cChannels::ModifiedByUser(), and
+ now has a 'State' parameter that allows the caller to see whether a channel
+ has been modified since the last call to this function with the same State
+ variable.
+ + The macros CHANNELSMOD_NONE/_AUTO/_USER have been removed.
+ + cMarks now requires locking via cStateKey.
+ + cSortedTimers now requires a pointer to the list of timers.
+ + cEvent::HasTimer() no longer scans the list of timers to check whether an event
+ is referenced by a timer, but rather keeps score of how many timers reference
+ it. This was necessary in order to avoid having to lock the list of timers from
+ within a cEvent.
+ + The new class cListGarbageCollector is used to temporary store any objects deleted
+ from cLists that require locking. This allows pointers to such objects to be
+ dereferenced even if the objects are no longer part of the list.
+ + cListBase::Contains() can be used to check whether a particular object is still
+ contained in that list.
+ + Outdated events are no longer "phased out", but rather deleted right away and thus
+ taken care of by the new "garbage collector" of the list.
+ + Deleted cRecording objects are no longer kept in a list of "vanished" recordings,
+ but are rather taken care of by the new "garbage collector" of the list.
+ + cSchedules::ClearAll() has been removed. The functionality is now implemented
+ directly in cSVDRPServer::CmdCLRE().
+ + cSchedule now has a member Modified(), which can be used with a State variable
+ to quickly determine whether this schedule has been modified since the last call
+ to this function with the same State variable.
+ + cSchedulesLock has been removed. Locking the list of schedules is now done via
+ the cList's new locking mechanism.
+ + The 'OnlyRunningStatus' parameters in cEpgHandler::BeginSegmentTransfer() and
+ cEpgHandler::EndSegmentTransfer() are now obsolete. They are still present in
+ the interface for backward compatibility, but may be removed in a future version.
+ Their value is always 'false'.
+ + The constant tcMod is no longer used in cStatus::TimerChange(). The definition is
+ still there for backward compatibility.
+- Plugins that access the global lists of Timers, Channels, Recordings or Schedules
+ will need to be adapted as follows:
+ + Instead of directly accessing the global variables Timers, Channels or Recordings,
+ they need to set up a cStateKey variable and call the proper getter function,
+ as in
+ cStateKey StateKey;
+ if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) {
+ // access the timers
+ StateKey.Remove();
+ }
+ and
+ cStateKey StateKey;
+ if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) {
+ // access the timers
+ StateKey.Remove();
+ }
+ See timers.h, thread.h and tools.h for details on this new locking mechanism.
+ + There are convenience macros for easily accessing these lists without having
+ to explicitly set up a cStateKey and calling its Remove() function. These macros
+ have the form LOCK_*_READ/WRITE (with '*' being TIMERS, CHANNELS, SCHEDULES or
+ RECORDINGS). Simply put such a macro before the point where you need to access
+ the respective list, and there will be a pointer named Timers, Channels, Schedules
+ or Recordings, respectively, which is valid until the end of the current block.
+ + If a plugin needs to access several of the global lists in parallel, locking must
+ always be done in the sequence Timers, Channels, Recordings, Schedules. This is
+ necessary to make sure that different threads that need to lock several lists at
+ the same time don't end up in a deadlock.
+ + Some pointer variables may need to be made 'const'. The compiler will tell you
+ about these.
+- If a timer is newly created with the Red button in the Schedule menu, and the timer
+ is presented to the user in the "Edit timer" menu because it will start immediately,
+ it now *must* be confirmed with "Ok" to set the timer. Otherwise the timer will not
+ be created.
+- The function cTimer::ToText() no longer returns a newline character at the end of
+ the string. The newline is now added by the caller as necessary. This was changed
+ because cTimer::ToText() is now also needed in a context where the terminating
+ newline can't be used. Consequently, cChannel::ToText() and cMark::ToText() have
+ been modified accordingly.
+- Timers now have unique ids instead of numbers, which remain valid as long as this
+ instance of VDR is running. This means that timers are no longer continuously
+ numbered from 1 to N in LSTT. There may be
+ gaps in the sequence, in case timers have been deleted.
+- Timers are now linked to EPG events even if they are inactive. By default Events that
+ are linked to inactive timers are marked with 'I' and 'i', depending on whether the
+ timer would record the entire Event or only part of it.
+ The function cSkinDisplayMenu::SetItemEvent() now has an additional parameter named
+ TimerActive, which indicates whether the timer that would record this event (if any)
+ is active. A plugin may react on this when displaying a menu line for an event.
+ The old version of cSkinDisplayMenu::SetItemEvent() (without the TimerActive
+ parameter) is still there for backwards compatibility. It may be removed in a future
+ version, so plugin authors should switch to the new one.
+- Improved handling VPS timers to better react to EPG changes during an ongoing recording.
+
+Plugins:
+
+- The dvbhddevice plugin is no longer part of the VDR source archive.
+ You can get the latest version of this plugin from the author's repository at
+ https://bitbucket.org/powARman/dvbhddevice.
+- The dvbsddevice and rcu plugins are no longer part of the VDR source archive.
+ You can get the latest versions of these plugins from ftp://ftp.tvdr.de/vdr/Plugins.
+- The -V and -h options now list the plugins in alphabetical order.
+- Added some guidelines and recommendations to the 'Logging' section of PLUGINS.html.
+ The most important being: implement a command line option to control the level
+ of logging (in particular allow turning off logging completely!) and never print
+ anything to stdout or stderr (unless one of the listed exceptions applies).
+- Added a note to PLUGINS.html about writing log messages in English.
+- The new function cStatus::MarksModified() can be implemented by plugins to get
+ informed about any modifications to the editing marks of the currently played
+ recording.
+
+Skins:
+
+- The main menu of the LCARS skin now displays a small rectangle on the left side
+ of a timer if this is a remote timer. The color of that rectangle changes if
+ the timer is currently recording on the remote VDR.
+- Skins can now implement cSkinDisplayMenu::MenuOrientation() to display horizontal
+ menus.
+- The LCARS skin now displays the master CAM's number when a device is tuned to
+ an encrypted channel.
+
+Remote control:
+
+- The new setup option "Recording/Record key handling" can be used to define
+ what happens if the Record key on the remote control is pressed during
+ live tv.
+- If the Channel+/- keys are pressed while in the Schedules menu, the menu is now
+ switched to the EPG of the new current channel.
+
+Devices:
+
+- The command line option -D now accepts the value '-' (as in -D-), which prevents
+ VDR from using any DVB devices.
+- The function cDevice::SetCurrentChannel(const cChannel *Channel) is now deprecated
+ and may be removed in a future version. Use SetCurrentChannel(int ChannelNumber)
+ instead.
+- Signal strength and quality (CNR) are now determined via DVB API 5 (if available).
+ Fallback is the old DVB API 3 method.
+- The new function cDevice::SignalStats() (if implemented by an actual device) returns
+ statistics about the currently received signal.
+- The function cDevice::GetVideoSystem() (which has been deprecated since version 2.1.6)
+ has been finally removed.
+- Switching the primary device is no longer done via osSwitchDvb (which has been
+ removed), but rather by the main program loop reacting to changes in Setup.PrimaryDVB.
+
+EPG:
+
+- The character 0x0D is now stripped from EPG texts.
+- The EPG scanner no longer moves the dish if there is a positioner.
+- The function cEpgHandlers::BeginSegmentTransfer() is now boolean.
+ See the description in epg.h for the meaning of the return value.
+- The cEvent class now has a new member 'aux', in which external applications can
+ store auxiliary information with an event. This string has no meaning whatsoever to
+ VDR itself, and it will not be written into the info file of a recording that is
+ made for such an event.
+- Changed the default return value of cEpgHandler::BeginSegmentTransfer() to true, to
+ avoid problems with derived classes that don't implement this function.
+
+OSD:
+
+- The new function cOsd::MaxPixmapSize() can be called to determine the maximum size
+ a cPixmap may have on the current OSD. The 'osddemo' example has been modified
+ accordingly. Plugin authors may want to use this function in case they use pixmaps
+ that are larger than the full OSD size. The default implementation sets this limit
+ to 2048x2048 pixel.
+- Added some comment to cPixmap about the relation between OSD, ViewPort and DrawPort.
+- The new setup option "OSD/Default sort mode for recordings" can be used to define
+ how recordings shall be sorted by default (either by time or by name, with "by time"
+ being the default). If a particular sort mode has been selected for a folder by
+ pressing '0', the default no longer applies to that folder. Repeating timers no
+ longer write a ".sort" file into a recordings folder to have the recordings sorted
+ by time.
+- The function cOsd::GetBitmap() is now 'protected'. If a plugin doesn't compile with
+ this version of VDR, you can uncomment the line
+ //#define DEPRECATED_GETBITMAP
+ in osd.h as a quick workaround. In the long run the plugin will need to be adapted.
+- Background modifications of channels, timers and events are now displayed immediately
+ in the corresponding menus.
+- The Timers menu now displays the name of the remote VDR in front of the timer's
+ file name, if this is a remote timer.
+- The width and height of the OSD are now limited to the actual maximum dimensions
+ of the output device, taking into account the top and left offset.
+- Added a note to the description of cFont::Size(), regarding possible differences
+ between it and cFont::Height().
+- Added cFont::Width(void) to get the default character width and allow stretched
+ font drawing in high level OSDs.
+- cOsdMenu::Display() now checks whether the OSD size has changed and if so calls
+ SetDisplayMenu().
+- The option "Setup/Miscellaneous/Show channel names with source" can now be set to
+ "type" or "full" to show either the type or the full name of the source.
+- The "Channels" menu now indicates whether a channel is encrypted ('X') or a radio
+ channel ('R').
+- The timeout for the channel display is now reset whenever the channel or EPG data
+ changes.
+- OSD menus now try to keep the offset of the list cursor at a constant position on
+ the screen, even if the list is modified while being displayed.
+- If an event in the Schedules menu is marked with a 'T' or 'I' and the user presses the
+ Red button to edit the timer, local timers are now preferred over remote timers
+ in case there is more than one timer that will record that event.
+- The new setup option "OSD/Sorting direction for recordings" can be used to switch
+ the sequence in which recordings are presented in the "Recordings" menu between
+ ascending (oldest first) and descendeng (newest first).
+- When selecting a folder for a recording or timer, it is now possible to open a folder
+ even if it doesn't contain any subfolders.
+
+Recordings:
+
+- Recordings now have unique ids instead of numbers, which remain valid as long as
+ this instance of VDR is running. This means that recordings are no longer continuously
+ numbered from 1 to N in LSTR. There may be gaps in the sequence, in case recordings
+ have been deleted, and they are not necessarily listed in numeric order.
+- Added detection of 24fps.
+- The script that gets called for recordings is now also called right before a
+ recording is edited, with the first parameter being "editing".
+- Implemented a frame parser for H.265 (HEVC) recordings.
+- When moving recordings between volumes, the "Recordings" menu now displays those items
+ that have not yet been moved completely as non-selectable. This avoids situations
+ where trying to play such a recording might fail.
+- When moving a recording to a different folder, the cursor is no longer placed on the
+ new location of the recording, but rather stays in the original folder.
+ If the original folder got empty by moving away the last recording
+ it contained, the cursor is moved up until a non empty folder is found.
+
+SVDRP:
+
+- The SVDRP port now accepts multiple concurrent connections. You can now keep an
+ SVDRP connection open as long as you wish, without preventing others from
+ connecting. Note, though, that SVDRP connections still get closed automatically
+ if there has been no activity for 300 seconds (configurable via
+ "Setup/Miscellaneous/SVDRP timeout (s)").
+- The SVDRP log messages have been unified and now always contain the IP and port
+ number of the remote host.
+- SVDRP connections are now handled in a separate "SVDRP server handler" thread,
+ which makes them more responsive. Note that there is only one thread that handles
+ all concurrent SVDRP connections. That way each SVDRP command is guaranteed to be
+ processed separately, without interfering with any other SVDRP commands that might
+ be issued at the same time. Plugins that implement SVDRP commands may need to take
+ care of proper locking if the commands access global data.
+- You can now set DumpSVDRPDataTransfer in svdrp.c to true to have all SVDRP
+ communication printed to the console for debugging.
+- The SVDRP commands that deal with timers (DELT, LSTT, MODT, NEWT, NEXT and UPDT)
+ as well as any log messages that refer to timers, now use a unique id for each
+ timer, which remains valid as long as this instance of VDR is running. This means
+ that timers are no longer continuously numbered from 1 to N in LSTT. There may be
+ gaps in the sequence, in case timers have been deleted.
+- All timer related response strings from SVDRP commands now use the channel ID
+ instead of channel numbers.
+- The SVDRP command DELT no longer checks whether the timer that shall be deleted
+ is currently recording.
+- The SVDRP command DELC now refuses to delete the very last channel in the list,
+ to avoid ending up with an empty channel list.
+- The SVDRP commands that deal with recordings (DELR, EDIT, LSTR, MOVR, and PLAY)
+ now use a unique id for each recording, which remains valid as long as this
+ instance of VDR is running. This means that recordings are no longer continuously
+ numbered from 1 to N in LSTR. There may be gaps in the sequence, in case recordings
+ have been deleted, and they are not necessarily listed in numeric order.
+- Changed 'number' to 'id' in the help texts of SVDRP commands that deal with
+ timers.
+- The SVDRP command LSTC can now list the channels with channel ids if the option
+ ':ids' is given.
+- If 0 is given as the channel number in the SVDRP command LSTC, the data of the
+ current channel is listed.
+- The new SVDRP commands 'LSTD' and 'PRIM' can be used to list all available devices
+ and to switch the primary device.
+
+Misc:
+
+- Added a section about Output Devices to the INSTALL file.
+- The -u option now also accepts a numerical user id.
+- The cRwLock class now allows nested read locks within a write lock from the
+ same thread. This fixes possible crashes when moving or deleting channels in
+ the menu or through SVDRP (as well as other operations that try to acquire a
+ read lock within a write lock).
+- Added support for the systemd watchdog.
+- PIDs can now be added to and deleted from a cReceiver while it is attached to
+ a cDevice, without having to detach it first and re-attach it afterwards.
+- Log messages about switching channels now include the channel ID.
+- The constructor of cHash (via cHashBase) now has an additional parameter (OwnObjects)
+ which, if set to true, makes the hash take ownership of the hashed objects, so that
+ they are deleted when the hash is cleared or destroyed.
+- cListObject now implements a private copy constructor and assignment operator, to keep
+ derived objects from calling them implicitly.
+- The Makefiles have been modified so that during the build process they no longer
+ display the actual (lengthy) commands, but rather just the name of the file that
+ is being built, as in
+ CC vdr.o
+ The first two characters indicate the kind of operation (CC=compile, LD=link,
+ AR=archive, MO=msgfmt, GT=xgettext, PO=msgmerge, IN=install).
+ This way it is much easier to spot error messages and warnings, since they are not
+ buried under tons of text.
+ Add VERBOSE=1 to the 'make' call in the VDR source directory to see the
+ actual commands that are executed.
+ Plugin authors should modify their makefiles accordingly, by simply preceeding
+ the respective commands with '$(Q)' and inserting '@echo XX $@' (where XX is one
+ of the character combinations listed above) before the command.
+ The newplugin script has also been modified accordingly.
+ Note that if you build a plugin directly in the plugin's own source directory,
+ the $(Q) macro won't be defined and commands will be displayed. You can add
+ Q=@ to the make call to have it less verbose (provided the plugin's Makefile
+ was modified as described above).
+- Added backtrace functions for debugging (see cBackTrace in thread.h).
+- Added checking the correct sequence of locking global lists.
+ At the first occurrence of an invalid locking
+ sequence, the 20 most recent locks will be written to the log file, followed by a
+ backtrace that led to the call in question. This code can be activated by defining
+ the macro DEBUG_LOCKSEQ in thread.c (which is on by default).
+ When debugging an actual invalid locking sequence, you can additionally define
+ the macro DEBUG_LOCKCALL in thread.c, which will add information about the caller
+ of each lock. Note that this may cause some stress on the CPU, therefore it is off
+ by default.
+- The file Make.config.template now reacts on DEBUG=1 in the 'make' command line,
+ and disables code optimizations by setting -O0.
+ This can be helpful when backtracing highly optimized code. You may want to
+ 'make distclean' before running 'make' with a modified setting of DEBUG, to make
+ sure all object files are newly compiled.
+- Introduced the new macro DISABLE_TEMPLATES_COLLIDING_WITH_STL, which can be defined
+ before including tools.h in case some plugin needs to use the STL and gets error
+ messages regarding one of the template functions defined in tools.h.
+- The macros used to control deprecated code or functions have been changed to hold
+ numeric values (0 and 1), so that they can be controlled at compile time, without
+ having to edit the actual source code.
+- The default for DEPRECATED_VDR_CHARSET_OVERRIDE has been set to 0, which means VDR
+ no longer reacts on the environment variable VDR_CHARSET_OVERRIDE. You can add
+ 'DEPRECATED_VDR_CHARSET_OVERRIDE=1' when compiling in order to restore this
+ functionality. However, it is recommended to use the command line option --chartab
+ instead.
+- Disabled the use of posix_fadvise() when reading (i.e. replaying), since it caused
+ stuttering replay in fast forward and fast rewind mode in case the video directory
+ is mounted via NFS. You can re-enable it by setting the macro USE_FADVISE_READ to 1
+ in tools.c.
diff --git a/ci.c b/ci.c
index b6ea326..8ad50e4 100644
--- a/ci.c
+++ b/ci.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ci.c 4.18 2017/06/19 12:13:38 kls Exp $
+ * $Id: ci.c 4.20 2018/02/03 12:55:03 kls Exp $
*/
#include "ci.h"
@@ -122,6 +122,8 @@ private:
uchar *bufp;
uchar mtdCatBuffer[TS_SIZE]; // TODO: handle multi packet CATs!
int length;
+ cMutex mutex;
+ bool handlingPid;
void AddEmmPid(int Pid);
void DelEmmPids(void);
public:
@@ -130,6 +132,15 @@ public:
virtual void Receive(const uchar *Data, int Length);
bool HasCaPids(void) const { return NumPids() - emmPids.Size() - 1 > 0; }
void Reset(void) { DelEmmPids(); catVersion = -1; }
+ bool HandlingPid(void);
+ ///< The cCaPidReceiver adds/deletes PIDs to/from the base class cReceiver,
+ ///< which in turn does the same on the cDevice it is attached to. The cDevice
+ ///< then sets the PIDs on the assigned cCamSlot, which can cause a deadlock on the
+ ///< cCamSlot's mutex if a cReceiver is detached from the device at the same time.
+ ///< Since these PIDs, however, are none that have to be decrypted,
+ ///< it is not necessary to set them in the CAM. Therefore this function is
+ ///< used in cCamSlot::SetPid() to detect this situation, and thus avoid the
+ ///< deadlock.
};
cCaPidReceiver::cCaPidReceiver(void)
@@ -137,7 +148,11 @@ cCaPidReceiver::cCaPidReceiver(void)
catVersion = -1;
bufp = NULL;
length = 0;
+ handlingPid = false;
+ cMutexLock MutexLock(&mutex);
+ handlingPid = true;
AddPid(CATPID);
+ handlingPid = false;
}
void cCaPidReceiver::AddEmmPid(int Pid)
@@ -147,14 +162,20 @@ void cCaPidReceiver::AddEmmPid(int Pid)
return;
}
emmPids.Append(Pid);
+ cMutexLock MutexLock(&mutex);
+ handlingPid = true;
AddPid(Pid);
+ handlingPid = false;
}
void cCaPidReceiver::DelEmmPids(void)
{
+ cMutexLock MutexLock(&mutex);
+ handlingPid = true;
for (int i = 0; i < emmPids.Size(); i++)
DelPid(emmPids[i]);
emmPids.Clear();
+ handlingPid = false;
}
void cCaPidReceiver::Receive(const uchar *Data, int Length)
@@ -239,6 +260,12 @@ void cCaPidReceiver::Receive(const uchar *Data, int Length)
}
}
+bool cCaPidReceiver::HandlingPid(void)
+{
+ cMutexLock MutexLock(&mutex);
+ return handlingPid;
+}
+
// --- cCaActivationReceiver -------------------------------------------------
// A receiver that is used to make the device stay on a given channel and
@@ -279,7 +306,7 @@ void cCaActivationReceiver::Receive(const uchar *Data, int Length)
if (TsIsScrambled(Data))
lastScrambledTime = Now;
else if (Now - lastScrambledTime > UNSCRAMBLE_TIME) {
- dsyslog("CAM %d: activated!", camSlot->SlotNumber());
+ dsyslog("CAM %d: activated!", camSlot->MasterSlotNumber());
Skins.QueueMessage(mtInfo, tr("CAM activated!"));
cDevice *d = Device();
Detach();
@@ -1111,10 +1138,12 @@ void cCiConditionalAccessSupport::Process(int Length, const uint8_t *Data)
case AOT_CA_PMT_REPLY: {
dbgprotocol("Slot %d: <== Ca Pmt Reply (%d)", CamSlot()->SlotNumber(), SessionId());
if (!repliesToQuery) {
- dsyslog("CAM %d: replies to QUERY - multi channel decryption (MCD) possible", CamSlot()->SlotNumber());
+ if (CamSlot()->IsMasterSlot())
+ dsyslog("CAM %d: replies to QUERY - multi channel decryption (MCD) possible", CamSlot()->SlotNumber());
repliesToQuery = true;
if (CamSlot()->MtdAvailable()) {
- dsyslog("CAM %d: supports multi transponder decryption (MTD)", CamSlot()->SlotNumber());
+ if (CamSlot()->IsMasterSlot())
+ dsyslog("CAM %d: supports multi transponder decryption (MTD)", CamSlot()->SlotNumber());
CamSlot()->MtdActivate(true);
}
}
@@ -2588,6 +2617,8 @@ void cCamSlot::AddPid(int ProgramNumber, int Pid, int StreamType)
void cCamSlot::SetPid(int Pid, bool Active)
{
+ if (caPidReceiver && caPidReceiver->HandlingPid())
+ return;
cMutexLock MutexLock(&mutex);
for (cCiCaProgramData *p = caProgramList.First(); p; p = caProgramList.Next(p)) {
for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
diff --git a/ci.h b/ci.h
index 827ce57..02e59d2 100644
--- a/ci.h
+++ b/ci.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ci.h 4.10 2017/06/09 14:21:26 kls Exp $
+ * $Id: ci.h 4.12 2018/03/17 12:17:37 kls Exp $
*/
#ifndef __CI_H
@@ -304,7 +304,7 @@ public:
virtual ~cCamSlot();
bool IsMasterSlot(void) { return !masterSlot; }
///< Returns true if this CAM slot itself is a master slot (which means that
- ///< it doesn't have pointer to another CAM slot that's its master).
+ ///< it doesn't have a pointer to another CAM slot that's its master).
cCamSlot *MasterSlot(void) { return masterSlot ? masterSlot : this; }
///< Returns this CAM slot's master slot, or a pointer to itself if it is a
///< master slot.
@@ -402,7 +402,7 @@ public:
///< call to AddPid()) to Active. A later call to StartDecrypting() will
///< send the full list of currently active CA_PMT entries to the CAM.
virtual void AddChannel(const cChannel *Channel);
- ///< Adds all PIDs if the given Channel to the current list of PIDs.
+ ///< Adds all PIDs of the given Channel to the current list of PIDs.
///< If the source or transponder of the channel are different than
///< what was given in a previous call to AddChannel(), any previously
///< added PIDs will be cleared.
diff --git a/config.c b/config.c
index 0b5a102..43c3cbb 100644
--- a/config.c
+++ b/config.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.c 4.6 2017/05/21 10:25:26 kls Exp $
+ * $Id: config.c 4.8 2018/02/15 14:40:36 kls Exp $
*/
#include "config.h"
@@ -432,6 +432,7 @@ cSetup::cSetup(void)
FoldersInTimerMenu = 1;
AlwaysSortFoldersFirst = 1;
DefaultSortModeRec = rsmTime;
+ RecSortingDirection = rsdAscending;
NumberKeysForChars = 1;
ColorKey0 = 0;
ColorKey1 = 1;
@@ -641,7 +642,7 @@ bool cSetup::Parse(const char *Name, const char *Value)
else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value);
else if (!strcasecmp(Name, "SVDRPPeering")) SVDRPPeering = atoi(Value);
else if (!strcasecmp(Name, "SVDRPHostName")) { if (*Value) strn0cpy(SVDRPHostName, Value, sizeof(SVDRPHostName)); }
- else if (!strcasecmp(Name, "SVDRPdefaultHost")) strn0cpy(SVDRPDefaultHost, Value, sizeof(SVDRPDefaultHost));
+ else if (!strcasecmp(Name, "SVDRPDefaultHost")) strn0cpy(SVDRPDefaultHost, Value, sizeof(SVDRPDefaultHost));
else if (!strcasecmp(Name, "ZapTimeout")) ZapTimeout = atoi(Value);
else if (!strcasecmp(Name, "ChannelEntryTimeout")) ChannelEntryTimeout= atoi(Value);
else if (!strcasecmp(Name, "RcRepeatDelay")) RcRepeatDelay = atoi(Value);
@@ -658,6 +659,7 @@ bool cSetup::Parse(const char *Name, const char *Value)
else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value);
else if (!strcasecmp(Name, "FoldersInTimerMenu")) FoldersInTimerMenu = atoi(Value);
else if (!strcasecmp(Name, "AlwaysSortFoldersFirst")) AlwaysSortFoldersFirst = atoi(Value);
+ else if (!strcasecmp(Name, "RecSortingDirection")) RecSortingDirection= atoi(Value);
else if (!strcasecmp(Name, "DefaultSortModeRec")) DefaultSortModeRec = atoi(Value);
else if (!strcasecmp(Name, "NumberKeysForChars")) NumberKeysForChars = atoi(Value);
else if (!strcasecmp(Name, "ColorKey0")) ColorKey0 = atoi(Value);
@@ -789,6 +791,7 @@ bool cSetup::Save(void)
Store("RecordingDirs", RecordingDirs);
Store("FoldersInTimerMenu", FoldersInTimerMenu);
Store("AlwaysSortFoldersFirst", AlwaysSortFoldersFirst);
+ Store("RecSortingDirection",RecSortingDirection);
Store("DefaultSortModeRec", DefaultSortModeRec);
Store("NumberKeysForChars", NumberKeysForChars);
Store("ColorKey0", ColorKey0);
diff --git a/config.h b/config.h
index 93488be..6d25a14 100644
--- a/config.h
+++ b/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 4.12 2017/06/12 08:58:26 kls Exp $
+ * $Id: config.h 4.14 2017/12/09 14:15:58 kls Exp $
*/
#ifndef __CONFIG_H
@@ -22,13 +22,13 @@
// VDR's own version number:
-#define VDRVERSION "2.3.8"
-#define VDRVERSNUM 20308 // Version * 10000 + Major * 100 + Minor
+#define VDRVERSION "2.3.9"
+#define VDRVERSNUM 20309 // Version * 10000 + Major * 100 + Minor
// The plugin API's version number:
-#define APIVERSION "2.3.8"
-#define APIVERSNUM 20308 // Version * 10000 + Major * 100 + Minor
+#define APIVERSION "2.3.9"
+#define APIVERSNUM 20309 // Version * 10000 + Major * 100 + Minor
// When loading plugins, VDR searches them by their APIVERSION, which
// may be smaller than VDRVERSION in case there have been no changes to
@@ -306,6 +306,7 @@ public:
int FoldersInTimerMenu;
int AlwaysSortFoldersFirst;
int DefaultSortModeRec;
+ int RecSortingDirection;
int NumberKeysForChars;
int ColorKey0, ColorKey1, ColorKey2, ColorKey3;
int VideoDisplayFormat;
diff --git a/cutter.c b/cutter.c
index 67dc4ef..8fda941 100644
--- a/cutter.c
+++ b/cutter.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: cutter.c 4.3 2017/05/21 09:45:06 kls Exp $
+ * $Id: cutter.c 4.6 2018/01/18 12:19:31 kls Exp $
*/
#include "cutter.h"
@@ -677,8 +677,6 @@ bool cCutter::Start(void)
cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName);
if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) {
Recording.WriteInfo(editedVersionName);
- LOCK_RECORDINGS_WRITE;
- Recordings->AddByName(editedVersionName, false);
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName);
return true;
}
@@ -702,9 +700,6 @@ void cCutter::Stop(void)
esyslog("ERROR: '%s' during editing process", Error);
if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), editedVersionName) == 0)
cControl::Shutdown();
- cVideoDirectory::RemoveVideoFile(editedVersionName);
- LOCK_RECORDINGS_WRITE;
- Recordings->DelByName(editedVersionName);
}
}
diff --git a/device.c b/device.c
index 3c67692..a8cb0b5 100644
--- a/device.c
+++ b/device.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.c 4.23 2017/05/30 11:06:11 kls Exp $
+ * $Id: device.c 4.26 2018/01/27 14:45:08 kls Exp $
*/
#include "device.h"
@@ -120,6 +120,7 @@ cDevice::~cDevice()
delete dvbSubtitleConverter;
if (this == primaryDevice)
primaryDevice = NULL;
+ Cancel(3);
}
bool cDevice::WaitForAllDevicesReady(int Timeout)
@@ -1673,6 +1674,7 @@ void cDevice::Action(void)
int Pid = TsPid(b);
bool IsScrambled = TsIsScrambled(b);
for (int i = 0; i < MAXRECEIVERS; i++) {
+ cMutexLock MutexLock(&mutexReceiver);
cReceiver *Receiver = receiver[i];
if (Receiver && Receiver->WantsPid(Pid)) {
Receiver->Receive(b, TS_SIZE);
@@ -1768,10 +1770,8 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
}
}
Receiver->Activate(true);
- Lock();
Receiver->device = this;
receiver[i] = Receiver;
- Unlock();
if (camSlot && Receiver->priority > MINPRIORITY) { // priority check to avoid an infinite loop with the CAM slot's caPidReceiver
camSlot->StartDecrypting();
if (camSlot->WantsTsData()) {
@@ -1801,13 +1801,11 @@ void cDevice::Detach(cReceiver *Receiver)
if (!Receiver || Receiver->device != this)
return;
bool receiversLeft = false;
- cMutexLock MutexLock(&mutexReceiver);
+ mutexReceiver.Lock();
for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i] == Receiver) {
- Lock();
receiver[i] = NULL;
Receiver->device = NULL;
- Unlock();
Receiver->Activate(false);
for (int n = 0; n < Receiver->numPids; n++)
DelPid(Receiver->pids[n]);
@@ -1815,6 +1813,7 @@ void cDevice::Detach(cReceiver *Receiver)
else if (receiver[i])
receiversLeft = true;
}
+ mutexReceiver.Unlock();
if (camSlot) {
if (Receiver->priority > MINPRIORITY) { // priority check to avoid an infinite loop with the CAM slot's caPidReceiver
camSlot->StartDecrypting();
diff --git a/device.h b/device.h
index 83e5178..ce0b0d2 100644
--- a/device.h
+++ b/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 4.10 2017/05/30 11:06:11 kls Exp $
+ * $Id: device.h 4.12 2017/11/02 14:47:33 kls Exp $
*/
#ifndef __DEVICE_H
@@ -55,13 +55,6 @@ enum ePlayMode { pmNone, // audio/video from decoder
// KNOWN TO YOUR PLAYER.
};
-//#define DEPRECATED_VIDEOSYSTEM
-#ifdef DEPRECATED_VIDEOSYSTEM
-enum eVideoSystem { vsPAL,
- vsNTSC
- };
-#endif
-
enum eVideoDisplayFormat { vdfPanAndScan,
vdfLetterBox,
vdfCenterCutOut
@@ -356,8 +349,10 @@ protected:
public:
static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
///< Returns the number of the current channel on the primary device.
-#define DEPRECATED_SETCURRENTCHANNEL
-#ifdef DEPRECATED_SETCURRENTCHANNEL
+#ifndef DEPRECATED_SETCURRENTCHANNEL
+#define DEPRECATED_SETCURRENTCHANNEL 1
+#endif
+#if DEPRECATED_SETCURRENTCHANNEL
static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
#endif
static void SetCurrentChannel(int ChannelNumber) { currentChannel = ChannelNumber; }
@@ -510,12 +505,6 @@ public:
///< if this device has an MPEG decoder).
///< NOTE: this is only for SD devices. HD devices shall implement their
///< own setup menu with the necessary parameters for controlling output.
-#ifdef DEPRECATED_VIDEOSYSTEM
- virtual eVideoSystem GetVideoSystem(void) { return vsPAL; }
- ///< Returns the video system of the currently displayed material
- ///< (default is PAL).
- ///< This function is deprecated and will be removed in a future version!
-#endif
virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect);
///< Returns the Width, Height and VideoAspect ratio of the currently
///< displayed video material. Width and Height are given in pixel
diff --git a/dvbdevice.c b/dvbdevice.c
index 9e07106..8710e77 100644
--- a/dvbdevice.c
+++ b/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 4.13 2017/05/09 11:50:38 kls Exp $
+ * $Id: dvbdevice.c 4.16 2018/02/15 15:37:01 kls Exp $
*/
#include "dvbdevice.h"
@@ -699,6 +699,231 @@ int dB1000toPercent(int dB1000, int Low, int High)
return 100 - v * v;
}
+#define REF_S1(q1) (mod == QPSK) ? q1 : 0
+#define REF_S2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == PSK_8) ? q2 : (mod == APSK_16) ? q3 : (mod == APSK_32) ? q4 : 0
+#define REF_T1(q1, q2, q3) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : 0
+#define REF_T2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_256) ? q4 : 0
+#define REF_C1(q1, q2, q3, q4, q5) (mod == QAM_16) ? q1 : (mod == QAM_32) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_128) ? q4 : (mod == QAM_256) ? q5: 0
+
+int StrengthToSSI(const cChannel *Channel, int Strength, int FeModulation, int FeCoderateH, int FeFec)
+{
+ // Strength in 0.001dBm (dBm x 1000)
+ cDvbTransponderParameters dtp(Channel->Parameters());
+ int ssi = 0; // 0-100
+ int mod = (FeModulation >= 0) ? FeModulation : dtp.Modulation();
+ int cod = (FeCoderateH >= 0) ? FeCoderateH : dtp.CoderateH(); // DVB-T
+ int fec = (FeFec >= 0) ? FeFec : dtp.CoderateH();
+ if (Channel->IsTerr()) {
+ int pref = 0;
+ // NorDig Unified Ver. 2.6 - 3.4.4.6 Page 43 ff.
+ // reference values : pref-15dBm = 0%, pref+35dBm = 100%
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
+ fec = cod; // adjustment for DVB-T
+ if (mod == QAM_AUTO) mod = QPSK;
+ switch (fec) { // dBm: Q4 Q16 Q64
+ case FEC_1_2: pref = REF_T1(-93, -87, -82); break;
+ default:
+ case FEC_2_3: pref = REF_T1(-91, -85, -80); break;
+ case FEC_3_4: pref = REF_T1(-90, -84, -78); break;
+ case FEC_5_6: pref = REF_T1(-89, -83, -77); break;
+ case FEC_7_8: pref = REF_T1(-88, -82, -76); break;
+ }
+ }
+ else { // DVB-T2
+ if (mod == QAM_AUTO) mod = QAM_64;
+ switch (fec) { // dBm: Q4 Q16 Q64 Q256
+ case FEC_1_2: pref = REF_T2(-96, -91, -86, -82); break;
+ default:
+ case FEC_3_5: pref = REF_T2(-95, -89, -85, -80); break;
+ case FEC_2_3: pref = REF_T2(-94, -88, -83, -78); break;
+ case FEC_3_4: pref = REF_T2(-93, -87, -82, -76); break;
+ case FEC_4_5: pref = REF_T2(-92, -86, -81, -75); break;
+ case FEC_5_6: pref = REF_T2(-92, -86, -80, -74); break;
+ }
+ }
+ if (pref) {
+ int prel = (Strength / 1000) - pref;
+ ssi = (prel < -15) ? 0 :
+ (prel < 0) ? (prel + 15) * 2 / 3 : // 0% - 10%
+ (prel < 20) ? prel * 4 + 10 : // 10% - 90%
+ (prel < 35) ? (prel - 20) * 2 / 3 + 90 : // 90% - 100%
+ 100;
+#ifdef DEBUG_SIGNALSTRENGTH
+ fprintf(stderr, "SSI-T: STR:%d, Pref:%d, Prel:%d, ssi:%d%%(sys:%d, mod:%d, fec:%d)\n", Strength, pref, prel, ssi, dtp.System(), mod, fec);
+#endif
+ }
+ }
+ else if (Channel->IsCable()) { // ! COMPLETELY UNTESTED !
+ // Formula: pref(dB) = -174.0 + NoiseFigure + SymRef + CnRef
+ // NoiseFigure = 6.5 dB; -> Tuner specific - range: 3.5 .. 9.0 dB
+ // SymRef = 10*log(6900000) = 68.5 dB; -> for Symbolrate of 6900 kSym/sec (TV: 6900, 6750 or 6111 kSym/sec)
+ // ==> pref(dB) = -174.0 + 6.5 + 68.5 + CnRef[modulation]{20,23,26,29,32}; (+/- 3 dB tuner specific)
+ if (mod == QAM_AUTO) mod = QAM_256;
+ // Q16 Q32 Q64 Q128 Q256
+ int pref = REF_C1(-79, -76, -73, -70, -67);
+ if (pref) {
+ int prel = (Strength / 1000) - pref;
+ ssi = (prel < -15) ? 0 :
+ (prel < 0) ? (prel + 15) * 2 / 3 : // 0% - 10%
+ (prel < 20) ? prel * 4 + 10 : // 10% - 90%
+ (prel < 35) ? (prel - 20) * 2 / 3 + 90 : // 90% - 100%
+ 100;
+#ifdef DEBUG_SIGNALSTRENGTH
+ fprintf(stderr, "SSI-C: STR:%d, Pref:%d, Prel:%d, ssi:%d%%(mod:%d)\n", Strength, pref, prel, ssi, mod);
+#endif
+ }
+ }
+ else if (Channel->IsSat())
+ ssi = dB1000toPercent(Strength, -95000, -20000); // defaults
+ return ssi;
+}
+
+// Due to missing values or the different meanings of the reported error rate, ber_sqi is currently not taken into account
+#define IGNORE_BER 1
+#define BER_ERROR_FREE (1000*1000*1000) // 1/10^-9
+
+int SignalToSQI(const cChannel *Channel, int Signal, int Ber, int FeModulation, int FeCoderateH, int FeFec)
+{
+#if IGNORE_BER
+ Ber = BER_ERROR_FREE; // assume/pretend to be biterror free
+#endif
+ // Signal in 0.001dB (dB x 1000)
+ cDvbTransponderParameters dtp(Channel->Parameters());
+ int sqi = 0; // 0-100
+ int mod = (FeModulation >= 0) ? FeModulation : dtp.Modulation();
+ int cod = (FeCoderateH >= 0) ? FeCoderateH : dtp.CoderateH(); // DVB-T
+ int fec = (FeFec >= 0) ? FeFec : dtp.CoderateH();
+ if (Channel->IsTerr()) { // QEF: BER 10^-6
+ int cnref = 0;
+ // NorDig Unified Ver. 2.6 - 3.4.4.7 Page 45 ff.
+ // reference values for QEF (BER 10^-11 at MPEG2 demux input)
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
+ fec = cod; // adjustment for DVB-T
+ if (mod == QAM_AUTO) mod = QPSK;
+ switch (fec) { // 0.1 dB Q4 Q16 Q64 (Hierarchy=None)
+ case FEC_1_2: cnref = REF_T1(51, 108, 165); break;
+ default:
+ case FEC_2_3: cnref = REF_T1(69, 131, 187); break;
+ case FEC_3_4: cnref = REF_T1(79, 146, 202); break;
+ case FEC_5_6: cnref = REF_T1(89, 156, 216); break;
+ case FEC_7_8: cnref = REF_T1(97, 160, 225); break;
+ }
+ }
+ else { // DVB-T2
+ if (mod == QAM_AUTO) mod = QAM_64;
+ switch (fec) { // 0.1 dB Q4 Q16 Q64 Q256
+ case FEC_1_2: cnref = REF_T2(35, 87, 130, 170); break;
+ default:
+ case FEC_3_5: cnref = REF_T2(47, 101, 148, 194); break;
+ case FEC_2_3: cnref = REF_T2(56, 114, 162, 208); break;
+ case FEC_3_4: cnref = REF_T2(66, 125, 177, 229); break;
+ case FEC_4_5: cnref = REF_T2(72, 133, 187, 243); break;
+ case FEC_5_6: cnref = REF_T2(77, 138, 194, 251); break;
+ }
+ }
+ if (cnref) {
+ int cnrel = (Signal/100) - cnref; // 0.1 dB
+ int ber_sqi = 100; // 100%
+ int cnr_sqi = 0; // 0%
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
+ ber_sqi = (Ber < 1000) ? 0 : // > 10^-3
+ (Ber >= 10000000) ? 100 : // <= 10^-7
+ (int)(20 * log10(Ber)) - 40; // 20*log10(1/BER)-40 -> 20% .. 100%
+ // scale: -7dB/+3dB to reference-value
+ cnr_sqi = (cnrel < -70) ? 0 :
+ (cnrel < +30) ? (100 + (cnrel - 30)) :
+ 100;
+ sqi = (cnr_sqi * ber_sqi) / 100;
+ // alternative: stretched scale: cnref-7dB = 0%, 30dB = 100%
+ // sqi = dB1000toPercent(Signal, (100*cnref)-7000, 30000);
+ }
+ else { // DVB-T2
+ ber_sqi = (Ber < 10000) ? 0 : // > 10^-4
+ (Ber >= 10000000) ? 100 * 100 / 6 : // <= 10^-7 : 16.67% -> SQI 0% .. 100%
+ (100 * 100 / 15); // 6.67% -> SQI 0% .. 40% || 100%
+ // scale: -3dB/+3dB to reference-value
+ sqi = (cnrel < -30) ? 0 :
+ (cnrel <= +30) ? (cnrel + 30) * ber_sqi / 1000 : // (0 .. 6) * 16,67 || 6.67
+ 100;
+ // alternative: stretched scale: cnref-3dB = 0%, 32dB = 100%
+ // sqi = dB1000toPercent(Signal, (100*cnref)-3000, 32000);
+ }
+#ifdef DEBUG_SIGNALQUALITY
+ fprintf(stderr, "SQI-T: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d)\n", Signal, Ber, cnref, cnrel, ber_sqi, sqi, dtp.System(), mod, fec);
+#endif
+ }
+ }
+ else if (Channel->IsCable()) { // ! COMPLETELY UNTESTED !
+ if (mod == QAM_AUTO) mod = QAM_256;
+ // 0.1 dB Q16 Q32 Q64 Q128 Q256
+ int cnref = REF_C1(200, 230, 260, 290, 320); // minimum for BER<10^-4
+ if (cnref) {
+ int cnrel = (Signal / 100) - cnref; // 0.1 dB
+ int ber_sqi = (Ber < 1000) ? 0 : // > 10^-3
+ (Ber >= 10000000) ? 100 : // <= 10^-7
+ (int)(20 * log10(Ber)) - 40; // 20*log10(1/BER)-40 -> 20% .. 100%
+ // scale: -7dB/+3dB to reference-value
+ int cnr_sqi = (cnrel < -70) ? 0 :
+ (cnrel < +30) ? (100 + (cnrel - 30)) :
+ 100;
+ sqi = (cnr_sqi * ber_sqi) / 100;
+ // alternative: stretched scale: cnref-7dB = 0%, 40dB = 100%
+ // sqi = dB1000toPercent(Signal, (100*cnref)-7000, 40000);
+#ifdef DEBUG_SIGNALQUALITY
+ dsyslog("SQI-C: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d)\n", Signal, Ber, cnref, cnrel, ber_sqi, sqi, dtp.System(), mod, fec);
+#endif
+ }
+ }
+ else if (Channel->IsSat()) {
+ int cnref = 0;
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-S
+ if (mod == QAM_AUTO) mod = QPSK;
+ switch (fec) { // 0.1 dB: Q4 : 10^-7
+ case FEC_1_2: cnref = REF_S1(38); break;
+ default:
+ case FEC_2_3: cnref = REF_S1(56); break;
+ case FEC_3_4: cnref = REF_S1(67); break;
+ case FEC_5_6: cnref = REF_S1(77); break;
+ case FEC_7_8: cnref = REF_S1(84); break;
+ }
+ if (cnref) {
+ //cnrel = (Signal/100) - cnref; // 0.1 dB
+ // scale: cnref-4dB = 0%, 15dB = 100%
+ sqi = dB1000toPercent(Signal, (100*cnref)-4000, 15000);
+#ifdef DEBUG_SIGNALQUALITY
+ dsyslog("SQI-S1: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d)\n", Signal, Ber, cnref, sqi, mod, fec);
+#endif
+ }
+ }
+ else { // DVB-S2
+ if (mod == QAM_AUTO) mod = QAM_64;
+ switch (fec) { // 0.1 dB Q4 Q8 16A* 32A*
+ //case FEC_1_4: cnref = REF_S2(-14, 65, 90, 126); break;
+ //case FEC_1_3: cnref = REF_S2( -2, 65, 90, 126); break;
+ case FEC_2_5: cnref = REF_S2( 7, 65, 90, 126); break;
+ case FEC_1_2: cnref = REF_S2( 20, 65, 90, 126); break;
+ case FEC_3_5: cnref = REF_S2( 32, 65, 90, 126); break;
+ default:
+ case FEC_2_3: cnref = REF_S2( 41, 76, 90, 126); break;
+ case FEC_3_4: cnref = REF_S2( 50, 66, 102, 126); break;
+ case FEC_4_5: cnref = REF_S2( 57, 89, 110, 136); break;
+ case FEC_5_6: cnref = REF_S2( 62, 104, 116, 143); break;
+ case FEC_8_9: cnref = REF_S2( 72, 117, 129, 157); break;
+ case FEC_9_10: cnref = REF_S2( 74, 120, 131, 161); break;
+ }
+ if (cnref) {
+ // cnrel = (Signal/100) - cnref; // 0.1 dB
+ // scale: cnref-4dB = 0%, 20dB = 100%
+ sqi = dB1000toPercent(Signal, (100*cnref)-4000, 20000);
+#ifdef DEBUG_SIGNALQUALITY
+ dsyslog("SQI-S2: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d)\n", Signal, Ber, cnref, sqi, mod, fec);
+#endif
+ }
+ }
+ }
+ return sqi;
+}
+
int cDvbTuner::GetSignalStrength(void) const
{
ClearEventQueue();
@@ -710,14 +935,20 @@ int cDvbTuner::GetSignalStrength(void) const
memset(&CmdSeq, 0, sizeof(CmdSeq));
CmdSeq.props = Props;
SETCMD(DTV_STAT_SIGNAL_STRENGTH, 0);
+ SETCMD(DTV_MODULATION, 0);
+ SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only
+ SETCMD(DTV_INNER_FEC, 0);
if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
return -1;
}
- int Signal = 0;;
+ int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
+ int FeCod = (Props[2].u.st.len > 0) ? (int)Props[2].u.data : -1;
+ int FeFec = (Props[3].u.st.len > 0) ? (int)Props[3].u.data : -1;
+ int Signal = 0;
if (Props[0].u.st.len > 0) {
switch (Props[0].u.st.stat[0].scale) {
- case FE_SCALE_DECIBEL: Signal = dB1000toPercent(Props[0].u.st.stat[0].svalue, -95000, -20000); // TODO use different values for DVB-S, -T, -C?
+ case FE_SCALE_DECIBEL: Signal = StrengthToSSI(&channel, Props[0].u.st.stat[0].svalue, FeMod, FeCod, FeFec);
break;
case FE_SCALE_RELATIVE: Signal = 100 * Props[0].u.st.stat[0].uvalue / 0xFFFF;
break;
@@ -768,14 +999,32 @@ int cDvbTuner::GetSignalQuality(void) const
memset(&CmdSeq, 0, sizeof(CmdSeq));
CmdSeq.props = Props;
SETCMD(DTV_STAT_CNR, 0);
+ SETCMD(DTV_MODULATION, 0);
+ SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only
+ SETCMD(DTV_INNER_FEC, 0);
+ SETCMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0);
+ SETCMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0);
if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
return -1;
}
- int Cnr = 0;;
+ int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
+ int FeCod = (Props[2].u.st.len > 0) ? (int)Props[2].u.data : -1;
+ int FeFec = (Props[3].u.st.len > 0) ? (int)Props[3].u.data : -1;
+ int Ber = BER_ERROR_FREE; // 1/10^-9
+ if (Props[4].u.st.len > 0 && Props[4].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[5].u.st.len > 0 && Props[5].u.st.stat[0].scale == FE_SCALE_COUNTER) {
+ uint64_t ebc = Props[4].u.st.stat[0].uvalue; // error bit count
+ uint64_t tbc = Props[5].u.st.stat[0].uvalue; // total bit count
+ if (ebc > 0) {
+ uint64_t BerRev = tbc / ebc; // reversed, for integer arithmetic
+ if (BerRev < BER_ERROR_FREE)
+ Ber = (int)BerRev;
+ }
+ }
+ int Cnr = 0;
if (Props[0].u.st.len > 0) {
switch (Props[0].u.st.stat[0].scale) {
- case FE_SCALE_DECIBEL: Cnr = dB1000toPercent(Props[0].u.st.stat[0].svalue, 5000, 20000); // TODO use different values for DVB-S, -T, -C?
+ case FE_SCALE_DECIBEL: Cnr = SignalToSQI(&channel, Props[0].u.st.stat[0].svalue, Ber, FeMod, FeCod, FeFec);
break;
case FE_SCALE_RELATIVE: Cnr = 100 * Props[0].u.st.stat[0].uvalue / 0xFFFF;
break;
@@ -1116,13 +1365,13 @@ bool cDvbTuner::SetFrontend(void)
SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
if (frontendType == SYS_DVBT2) {
// DVB-T2
+ SETCMD(DTV_INNER_FEC, dtp.CoderateH());
if (DvbApiVersion >= 0x0508) {
SETCMD(DTV_STREAM_ID, dtp.StreamId());
}
else if (DvbApiVersion >= 0x0503)
SETCMD(DTV_DVBT2_PLP_ID_LEGACY, dtp.StreamId());
}
-
tuneTimeout = DVBT_TUNE_TIMEOUT;
lockTimeout = DVBT_LOCK_TIMEOUT;
}
diff --git a/dvbplayer.c b/dvbplayer.c
index ca00735..d1c02f9 100644
--- a/dvbplayer.c
+++ b/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 4.4 2016/12/22 11:34:31 kls Exp $
+ * $Id: dvbplayer.c 4.5 2017/11/26 14:55:03 kls Exp $
*/
#include "dvbplayer.h"
@@ -1003,6 +1003,7 @@ bool cDvbPlayerControl::Active(void)
void cDvbPlayerControl::Stop(void)
{
+ cControl::player = NULL;
delete player;
player = NULL;
}
diff --git a/eit.c b/eit.c
index ade7928..6887252 100644
--- a/eit.c
+++ b/eit.c
@@ -8,7 +8,7 @@
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
*
- * $Id: eit.c 4.3 2017/05/03 08:58:41 kls Exp $
+ * $Id: eit.c 4.4 2017/10/11 09:19:42 kls Exp $
*/
#include "eit.h"
@@ -94,10 +94,10 @@ cEIT::cEIT(cSectionSyncerHash &SectionSyncerHash, int Source, u_char Tid, const
// Drop bogus events - but keep NVOD reference events, where all bits of the start time field are set to 1, resulting in a negative number.
if (StartTime == 0 || StartTime > 0 && Duration == 0)
continue;
+ Empty = false;
// Ignore events that ended before the "EPG linger time":
if (StartTime + Duration < LingerLimit)
continue;
- Empty = false;
if (!SegmentStart)
SegmentStart = StartTime;
SegmentEnd = StartTime + Duration;
diff --git a/font.h b/font.h
index 9d18bde..3bdf7e7 100644
--- a/font.h
+++ b/font.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: font.h 4.2 2016/12/22 12:43:24 kls Exp $
+ * $Id: font.h 4.3 2017/11/11 14:05:07 kls Exp $
*/
#ifndef __FONT_H
@@ -54,6 +54,8 @@ public:
///< Returns the width of the given string in pixel.
virtual int Height(void) const = 0;
///< Returns the height of this font in pixel (all characters have the same height).
+ ///< If the font contains descenders, its total height may be higher than the character
+ ///< height specified during creation.
int Height(const char *s) const { return Height(); }
///< Returns the height of this font in pixel (obsolete, just for backwards compatibility).
virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const = 0;
@@ -75,10 +77,12 @@ public:
///< function that called GetFont() has returned.
static cFont *CreateFont(const char *Name, int CharHeight, int CharWidth = 0);
///< Creates a new font object with the given Name and makes its characters
- ///< CharHeight pixels high. If CharWidth is given, it overwrites the font's
- ///< default width. Name is of the form "Family:Style", for instance
- ///< "Verdana:Bold Italic" or "Times New Roman". See GetAvailableFontNames()
- ///< for how to get a list of all available font names.
+ ///< CharHeight pixels high. The actual height of the required drawing area
+ ///< might be higher than CharHeight if the font contains descenders.
+ ///< If CharWidth is given, it overwrites the font's default width.
+ ///< Name is of the form "Family:Style", for instance "Verdana:Bold Italic" or
+ ///< "Times New Roman". See GetAvailableFontNames() for how to get a list of
+ ///< all available font names.
///< If the requested font can't be created, a dummy font is returned.
///< The caller must delete the font when it is no longer needed.
static bool GetAvailableFontNames(cStringList *FontNames, bool Monospaced = false);
diff --git a/menu.c b/menu.c
index c2c12dd..d530015 100644
--- a/menu.c
+++ b/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 4.41 2017/06/30 09:49:39 kls Exp $
+ * $Id: menu.c 4.69 2018/03/18 12:01:09 kls Exp $
*/
#include "menu.h"
@@ -656,6 +656,7 @@ class cMenuFolderItem : public cOsdItem {
private:
cNestedItem *folder;
public:
+ virtual void Set(void);
cMenuFolderItem(cNestedItem *Folder);
cNestedItem *Folder(void) { return folder; }
};
@@ -664,8 +665,15 @@ cMenuFolderItem::cMenuFolderItem(cNestedItem *Folder)
:cOsdItem(Folder->Text())
{
folder = Folder;
- if (folder->SubItems())
+ Set();
+}
+
+void cMenuFolderItem::Set(void)
+{
+ if (folder->SubItems() && folder->SubItems()->Count())
SetText(cString::sprintf("%s...", folder->Text()));
+ else
+ SetText(folder->Text());
}
// --- cMenuEditFolder -------------------------------------------------------
@@ -675,7 +683,6 @@ private:
cList<cNestedItem> *list;
cNestedItem *folder;
char name[PATH_MAX];
- int subFolder;
eOSState Confirm(void);
public:
cMenuEditFolder(const char *Dir, cList<cNestedItem> *List, cNestedItem *Folder = NULL);
@@ -689,13 +696,10 @@ cMenuEditFolder::cMenuEditFolder(const char *Dir, cList<cNestedItem> *List, cNes
SetMenuCategory(mcFolder);
list = List;
folder = Folder;
- if (folder) {
+ if (folder)
strn0cpy(name, folder->Text(), sizeof(name));
- subFolder = folder->SubItems() != NULL;
- }
else {
*name = 0;
- subFolder = 0;
cRemote::Put(kRight, true); // go right into string editing mode
}
if (!isempty(Dir)) {
@@ -704,7 +708,6 @@ cMenuEditFolder::cMenuEditFolder(const char *Dir, cList<cNestedItem> *List, cNes
Add(DirItem);
}
Add(new cMenuEditStrItem( tr("Name"), name, sizeof(name)));
- Add(new cMenuEditBoolItem(tr("Sub folder"), &subFolder));
}
cString cMenuEditFolder::GetFolder(void)
@@ -728,12 +731,10 @@ eOSState cMenuEditFolder::Confirm(void)
return osContinue;
}
}
- if (folder) {
+ if (folder)
folder->SetText(name);
- folder->SetSubItems(subFolder);
- }
else
- list->Add(folder = new cNestedItem(name, subFolder));
+ list->Add(folder = new cNestedItem(name));
return osEnd;
}
@@ -791,12 +792,8 @@ void cMenuFolder::SetHelpKeys(void)
if (HasSubMenu())
return;
int NewHelpKeys = 0;
- if (firstFolder) {
- if (cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current())) {
- if (Folder->Folder()->SubItems())
- NewHelpKeys = 1;
- }
- }
+ if (firstFolder)
+ NewHelpKeys = 1;
if (NewHelpKeys != helpKeys) {
helpKeys = NewHelpKeys;
SetHelp(NewHelpKeys > 0 ? tr("Button$Open") : NULL, tr("Button$New"), firstFolder ? tr("Button$Delete") : NULL, firstFolder ? tr("Button$Edit") : NULL);
@@ -885,8 +882,10 @@ eOSState cMenuFolder::Select(bool Open)
if (firstFolder) {
cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current());
if (Folder) {
- if (Open && Folder->Folder()->SubItems())
+ if (Open) {
+ Folder->Folder()->SetSubItems(true);
return AddSubMenu(new cMenuFolder(Title(), Folder->Folder()->SubItems(), nestedItemList, !isempty(dir) ? *cString::sprintf("%s%c%s", *dir, FOLDERDELIMCHAR, Folder->Folder()->Text()) : Folder->Folder()->Text()));
+ }
else
return osEnd;
}
@@ -1123,6 +1122,8 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
return osContinue;
if (timer->Local() && timer->Recording() && data.Remote())
cRecordControls::Stop(timer);
+ if (timer->Remote() && data.Remote())
+ Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
*timer = data;
}
LOCK_SCHEDULES_READ;
@@ -1296,11 +1297,13 @@ eOSState cMenuTimers::OnOff(void)
{
if (HasSubMenu())
return osContinue;
- cTimers::GetTimersWrite(timersStateKey);
+ cStateKey StateKey;
+ cTimers *Timers = cTimers::GetTimersWrite(StateKey);
cTimer *Timer = GetTimer();
if (Timer) {
Timer->OnOff();
if (Timer->Remote()) {
+ Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
cStringList Response;
if (!ExecSVDRPCommand(Timer->Remote(), cString::sprintf("MODT %d %s", Timer->Id(), *Timer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
RemoteTimerError(Timer);
@@ -1314,7 +1317,7 @@ eOSState cMenuTimers::OnOff(void)
else
isyslog("%sactivated timer %s", Timer->HasFlags(tfActive) ? "" : "de", *Timer->ToDescr());
}
- timersStateKey.Remove(Timer != NULL);
+ StateKey.Remove(Timer != NULL);
return osContinue;
}
@@ -1341,27 +1344,27 @@ eOSState cMenuTimers::Delete(void)
// Check if this timer is active:
cTimer *Timer = GetTimer();
if (Timer) {
- if (Interface->Confirm(tr("Delete timer?"))) {
- if (Timer->Recording()) {
- if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
- if (!Timer->Remote()) {
- Timer->Skip();
- cRecordControls::Process(Timers, time(NULL));
- }
- }
- else
- Timer = NULL;
- }
+ bool TimerRecording = Timer->Recording();
+ timersStateKey.Remove(false); // must release lock while prompting!
+ if (Interface->Confirm(tr("Delete timer?")) && (!TimerRecording || Interface->Confirm(tr("Timer still recording - really delete?")))) {
+ Timers = cTimers::GetTimersWrite(timersStateKey);
+ Timer = GetTimer();
if (Timer) {
- if (!HandleRemoteModifications(NULL, Timer)) {
- timersStateKey.Remove();
- return osContinue;
+ if (!Timer->Remote()) {
+ Timer->Skip();
+ cRecordControls::Process(Timers, time(NULL));
+ }
+ if (HandleRemoteModifications(NULL, Timer)) {
+ if (Timer->Remote())
+ Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
+ Timers->Del(Timer);
+ cOsdMenu::Del(Current());
+ Display();
}
- Timers->Del(Timer);
- cOsdMenu::Del(Current());
- Display();
}
}
+ else
+ return osContinue;
}
timersStateKey.Remove(Timer != NULL);
return osContinue;
@@ -1399,7 +1402,11 @@ eOSState cMenuTimers::ProcessKey(eKeys Key)
if (const cTimer *Timer = cMenuEditTimer::AddedTimer()) {
// a newly created timer was confirmed with Ok and the proper item needs to be added:
LOCK_TIMERS_READ;
- Add(new cMenuTimerItem(Timer), true);
+ cMenuTimerItem *CurrentItem = new cMenuTimerItem(Timer);
+ Add(CurrentItem, true);
+ Sort();
+ SetCurrent(CurrentItem);
+ SetHelpKeys();
Display();
}
if (Key != kNone)
@@ -1680,6 +1687,8 @@ eOSState cMenuWhatsOn::Record(void)
// must add the timer before HandleRemoteModifications to get proper log messages with timer ids
Timers->Del(Timer);
}
+ else if (Timer->Remote())
+ Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
if (HasSubMenu())
CloseSubMenu();
if (Update())
@@ -1779,7 +1788,7 @@ public:
};
cMenuSchedule::cMenuSchedule(void)
-:cOsdMenu("")
+:cOsdMenu(tr("Schedule"))
{
SetMenuCategory(mcSchedule);
scheduleState = -1;
@@ -1986,6 +1995,8 @@ eOSState cMenuSchedule::Record(void)
// must add the timer before HandleRemoteModifications to get proper log messages with timer ids
Timers->Del(Timer);
}
+ else if (Timer->Remote())
+ Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
if (HasSubMenu())
CloseSubMenu();
if (Update())
@@ -2407,9 +2418,15 @@ bool CamMenuActive(void)
// --- cMenuPathEdit ---------------------------------------------------------
+#define osUserRecRenamed osUser1
+#define osUserRecMoved osUser2
+#define osUserRecRemoved osUser3
+#define osUserRecEmpty osUser4
+
class cMenuPathEdit : public cOsdMenu {
private:
cString path;
+ cString oldFolder;
char folder[PATH_MAX];
char name[NAME_MAX];
cMenuEditStrItem *folderItem;
@@ -2441,6 +2458,7 @@ cMenuPathEdit::cMenuPathEdit(const char *Path)
LOCK_RECORDINGS_READ;
pathIsInUse = Recordings->PathIsInUse(path);
}
+ oldFolder = folder;
cOsdItem *p;
Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder)));
p->SetSelectable(!pathIsInUse);
@@ -2479,18 +2497,28 @@ eOSState cMenuPathEdit::ApplyChanges(void)
cString NewPath = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name;
NewPath.CompactChars(FOLDERDELIMCHAR);
if (strcmp(NewPath, path)) {
- LOCK_RECORDINGS_WRITE;
- Recordings->SetExplicitModify();
- int NumRecordings = Recordings->GetNumRecordingsInPath(path);
+ int NumRecordings = 0;
+ {
+ LOCK_RECORDINGS_READ;
+ NumRecordings = Recordings->GetNumRecordingsInPath(path);
+ }
if (NumRecordings > 1 && !Interface->Confirm(cString::sprintf(tr("Move entire folder containing %d recordings?"), NumRecordings)))
return osContinue;
- if (!Recordings->MoveRecordings(path, NewPath)) {
+ bool Error = false;
+ {
+ LOCK_RECORDINGS_WRITE;
+ Recordings->SetExplicitModify();
+ Error = !Recordings->MoveRecordings(path, NewPath);
+ if (!Error)
+ Recordings->SetModified();
+ }
+ if (Error) {
Skins.Message(mtError, tr("Error while moving folder!"));
return osContinue;
}
- cMenuRecordings::SetPath(NewPath); // makes sure the Recordings menu will reposition to the new path
- Recordings->SetModified();
- return osUser1;
+ if (strcmp(folder, oldFolder))
+ return osUserRecMoved;
+ return osUserRecRenamed;
}
return osBack;
}
@@ -2678,8 +2706,15 @@ eOSState cMenuRecordingEdit::RemoveName(void)
eOSState cMenuRecordingEdit::DeleteMarks(void)
{
if (buttonDeleteMarks && Interface->Confirm(tr("Delete editing marks for this recording?"))) {
- if (cMarks::DeleteMarksFile(recording))
+ if (cMarks::DeleteMarksFile(recording)) {
SetHelpKeys();
+ if (cControl *Control = cControl::Control(true)) {
+ if (const cRecording *Recording = Control->GetRecording()) {
+ if (strcmp(recording->FileName(), Recording->FileName()) == 0)
+ Control->ClearEditingMarks();
+ }
+ }
+ }
else
Skins.Message(mtError, tr("Error while deleting editing marks!"));
}
@@ -2709,6 +2744,7 @@ eOSState cMenuRecordingEdit::ApplyChanges(void)
*name = ' '; // name must not be empty!
name[1] = 0;
}
+ cString OldFolder = Recording->Folder();
cString NewName = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name;
NewName.CompactChars(FOLDERDELIMCHAR);
if (strcmp(NewName, Recording->Name())) {
@@ -2720,10 +2756,12 @@ eOSState cMenuRecordingEdit::ApplyChanges(void)
Modified = true;
}
if (Modified) {
- cMenuRecordings::SetRecording(Recording->FileName()); // makes sure the Recordings menu will reposition to the renamed recording
+ eOSState state = osUserRecRenamed;
+ if (strcmp(Recording->Folder(), OldFolder))
+ state = osUserRecMoved;
Recordings->TouchUpdate();
StateKey.Remove(Modified);
- return osUser1;
+ return state;
}
StateKey.Remove(Modified);
return osBack;
@@ -2858,10 +2896,10 @@ public:
cMenuRecordingItem(const cRecording *Recording, int Level);
~cMenuRecordingItem();
void IncrementCounter(bool New);
- const char *Name(void) { return name; }
- int Level(void) { return level; }
- const cRecording *Recording(void) { return recording; }
- bool IsDirectory(void) { return name != NULL; }
+ const char *Name(void) const { return name; }
+ int Level(void) const { return level; }
+ const cRecording *Recording(void) const { return recording; }
+ bool IsDirectory(void) const { return name != NULL; }
void SetRecording(const cRecording *Recording) { recording = Recording; }
virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable);
};
@@ -2873,8 +2911,13 @@ cMenuRecordingItem::cMenuRecordingItem(const cRecording *Recording, int Level)
name = NULL;
totalEntries = newEntries = 0;
SetText(Recording->Title('\t', true, Level));
- if (*Text() == '\t')
+ if (*Text() == '\t') // this is a folder
name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t'
+ else { // this is an actual recording
+ int Usage = Recording->IsInUse();
+ if ((Usage & ruDst) != 0 && (Usage & (ruMove | ruCopy)) != 0)
+ SetSelectable(false);
+ }
}
cMenuRecordingItem::~cMenuRecordingItem()
@@ -2957,15 +3000,17 @@ void cMenuRecordings::Set(bool Refresh)
{
if (cRecordings::GetRecordingsRead(recordingsStateKey)) {
recordingsStateKey.Remove();
- const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed();
cRecordings *Recordings = cRecordings::GetRecordingsWrite(recordingsStateKey); // write access is necessary for sorting!
- cMenuRecordingItem *LastItem = NULL;
+ const char *CurrentRecording = NULL;
if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()))
CurrentRecording = ri->Recording()->FileName();
+ if (!CurrentRecording)
+ CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed();
int current = Current();
Clear();
GetRecordingsSortMode(DirectoryName());
Recordings->Sort();
+ cMenuRecordingItem *LastItem = NULL;
for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) {
if ((!filter || filter->Filter(Recording)) && (!base || (strstr(Recording->Name(), base) == Recording->Name() && Recording->Name()[strlen(base)] == FOLDERDELIMCHAR))) {
cMenuRecordingItem *Item = new cMenuRecordingItem(Recording, level);
@@ -3073,28 +3118,67 @@ eOSState cMenuRecordings::Rewind(void)
return osContinue;
}
-eOSState cMenuRecordings::Delete(void)
+static bool TimerStillRecording(const char *FileName)
{
- if (HasSubMenu() || Count() == 0)
- return osContinue;
- cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
- if (ri && !ri->IsDirectory()) {
- if (Interface->Confirm(tr("Delete recording?"))) {
- if (cRecordControl *rc = cRecordControls::GetRecordControl(ri->Recording()->FileName())) {
+ if (cRecordControl *rc = cRecordControls::GetRecordControl(FileName)) {
+ // local timer
+ if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
+ LOCK_TIMERS_WRITE;
+ if (cTimer *Timer = rc->Timer()) {
+ Timer->Skip();
+ cRecordControls::Process(Timers, time(NULL));
+ if (Timer->IsSingleEvent()) {
+ Timers->Del(Timer);
+ isyslog("deleted timer %s", *Timer->ToDescr());
+ }
+ }
+ }
+ else
+ return true; // user didn't confirm deletion
+ }
+ else {
+ // remote timer
+ cString TimerId = GetRecordingTimerId(FileName);
+ if (*TimerId) {
+ int Id;
+ char *RemoteBuf = NULL;
+ cString Remote;
+ if (2 == sscanf(TimerId, "%d@%m[^ \n]", &Id, &RemoteBuf)) {
+ Remote = RemoteBuf;
+ free(RemoteBuf);
if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
- if (cTimer *Timer = rc->Timer()) {
- LOCK_TIMERS_WRITE;
+ LOCK_TIMERS_WRITE;
+ if (cTimer *Timer = Timers->GetById(Id, Remote)) {
+ cTimer OldTimer = *Timer;
Timer->Skip();
- cRecordControls::Process(Timers, time(NULL));
+ Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
if (Timer->IsSingleEvent()) {
- Timers->Del(Timer);
- isyslog("deleted timer %s", *Timer->ToDescr());
+ if (HandleRemoteModifications(NULL, Timer))
+ Timers->Del(Timer);
+ else
+ return true; // error while deleting remote timer
}
+ else if (!HandleRemoteModifications(Timer, &OldTimer))
+ return true; // error while modifying remote timer
}
}
else
- return osContinue;
+ return true; // user didn't confirm deletion
}
+ }
+ }
+ return false;
+}
+
+eOSState cMenuRecordings::Delete(void)
+{
+ if (HasSubMenu() || Count() == 0)
+ return osContinue;
+ cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+ if (ri && !ri->IsDirectory()) {
+ if (Interface->Confirm(tr("Delete recording?"))) {
+ if (TimerStillRecording(ri->Recording()->FileName()))
+ return osContinue;
cString FileName;
{
LOCK_RECORDINGS_READ;
@@ -3122,8 +3206,8 @@ eOSState cMenuRecordings::Delete(void)
recordingsStateKey.Remove();
Display();
if (!Count())
- return osBack;
- return osUser2;
+ return osUserRecEmpty;
+ return osUserRecRemoved;
}
else
Skins.Message(mtError, tr("Error while deleting recording!"));
@@ -3165,6 +3249,8 @@ eOSState cMenuRecordings::Sort(void)
{
if (HasSubMenu())
return osContinue;
+ if (const cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()))
+ SetRecording(ri->Recording()->FileName()); // makes sure the Recordings menu will reposition to the current recording
IncRecordingsSortMode(DirectoryName());
recordingsStateKey.Reset();
Set(true);
@@ -3173,7 +3259,6 @@ eOSState cMenuRecordings::Sort(void)
eOSState cMenuRecordings::ProcessKey(eKeys Key)
{
- bool HadSubMenu = HasSubMenu();
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
@@ -3191,37 +3276,46 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
default: break;
}
}
- else if (state == osUser1) {
- // a recording or path was renamed, so let's refresh the menu
- CloseSubMenu(false);
- if (base)
- return state; // closes all recording menus except for the top one
- Set(); // this is the top level menu, so we refresh it...
- Open(true); // ...and open any necessary submenus to show the new name
- if (!HasSubMenu()) {
- LOCK_RECORDINGS_READ;
- Display();
- }
+ else if (state == osUserRecRenamed) {
+ // a recording was renamed (within the same folder), so let's refresh the menu
+ CloseSubMenu(false); // this is the cMenuRecordingEdit/cMenuPathEdit
path = NULL;
fileName = NULL;
+ state = osContinue;
}
- else if (state == osUser2) {
- // a recording in a sub folder was deleted, so update the current item
- cOsdMenu *m = HasSubMenu() ? SubMenu() : this;
- if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) {
- if (cMenuRecordingItem *riSub = (cMenuRecordingItem *)m->Get(m->Current()))
- ri->SetRecording(riSub->Recording());
+ else if (state == osUserRecMoved) {
+ // a recording was moved to a different folder, so let's delete the old item
+ CloseSubMenu(false); // this is the cMenuRecordingEdit/cMenuPathEdit
+ path = NULL;
+ fileName = NULL;
+ cOsdMenu::Del(Current());
+ Set(); // the recording might have been moved into a new subfolder of this folder
+ if (!Count())
+ return osUserRecEmpty;
+ Display();
+ state = osUserRecRemoved;
+ }
+ else if (state == osUserRecRemoved) {
+ // a recording was removed from a sub folder, so update the current item
+ if (cOsdMenu *m = SubMenu()) {
+ if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) {
+ if (cMenuRecordingItem *riSub = (cMenuRecordingItem *)m->Get(m->Current()))
+ ri->SetRecording(riSub->Recording());
+ }
}
+ // no state change here, this report goes upstream!
+ }
+ else if (state == osUserRecEmpty) {
+ // a subfolder became empty, so let's go back up
+ CloseSubMenu(false); // this is the now empty submenu
+ cOsdMenu::Del(Current()); // the menu entry of the now empty subfolder
+ Set(); // in case a recording was moved into a new subfolder of this folder
+ if (base && !Count()) // base: don't go up beyond the top level Recordings menu
+ return state;
+ Display();
+ state = osContinue;
}
if (!HasSubMenu()) {
- if (HadSubMenu) {
- if (Key == kYellow) {
- // the last recording in a subdirectory was deleted, so let's go back up
- cOsdMenu::Del(Current());
- if (!Count())
- return osBack;
- }
- }
Set(true);
if (Key != kNone)
SetHelpKeys();
@@ -3257,6 +3351,7 @@ class cMenuSetupOSD : public cMenuSetupBase {
private:
const char *useSmallFontTexts[3];
const char *recSortModeTexts[2];
+ const char *recSortDirTexts[2];
const char *keyColorTexts[4];
int osdLanguageIndex;
int numSkins;
@@ -3311,6 +3406,8 @@ void cMenuSetupOSD::Set(void)
useSmallFontTexts[2] = tr("always");
recSortModeTexts[0] = tr("by name");
recSortModeTexts[1] = tr("by time");
+ recSortDirTexts[0] = tr("ascending");
+ recSortDirTexts[1] = tr("descending");
keyColorTexts[0] = tr("Key$Red");
keyColorTexts[1] = tr("Key$Green");
keyColorTexts[2] = tr("Key$Yellow");
@@ -3345,6 +3442,7 @@ void cMenuSetupOSD::Set(void)
Add(new cMenuEditBoolItem(tr("Setup.OSD$Folders in timer menu"), &data.FoldersInTimerMenu));
Add(new cMenuEditBoolItem(tr("Setup.OSD$Always sort folders first"), &data.AlwaysSortFoldersFirst));
Add(new cMenuEditStraItem(tr("Setup.OSD$Default sort mode for recordings"), &data.DefaultSortModeRec, 2, recSortModeTexts));
+ Add(new cMenuEditStraItem(tr("Setup.OSD$Sorting direction for recordings"), &data.RecSortingDirection, 2, recSortDirTexts));
Add(new cMenuEditBoolItem(tr("Setup.OSD$Number keys for characters"), &data.NumberKeysForChars));
Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 0"), &data.ColorKey0, 4, keyColorTexts));
Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 1"), &data.ColorKey1, 4, keyColorTexts));
@@ -3386,7 +3484,7 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys Key)
ModifiedAppearance = true;
if (strcmp(data.FontFix, Setup.FontFix) || !DoubleEqual(data.FontFixSizeP, Setup.FontFixSizeP))
ModifiedAppearance = true;
- if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs) {
+ if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs || data.RecSortingDirection != Setup.RecSortingDirection) {
LOCK_RECORDINGS_WRITE;
Recordings->ClearSortNames();
}
@@ -3587,7 +3685,6 @@ void cMenuSetupDVB::Setup(void)
eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
{
- int oldPrimaryDVB = ::Setup.PrimaryDVB;
int oldVideoDisplayFormat = ::Setup.VideoDisplayFormat;
bool oldVideoFormat = ::Setup.VideoFormat;
bool newVideoFormat = data.VideoFormat;
@@ -3650,8 +3747,6 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
}
}
if (state == osBack && Key == kOk) {
- if (::Setup.PrimaryDVB != oldPrimaryDVB)
- state = osSwitchDvb;
if (::Setup.VideoDisplayFormat != oldVideoDisplayFormat)
cDevice::PrimaryDevice()->SetVideoDisplayFormat(eVideoDisplayFormat(::Setup.VideoDisplayFormat));
if (::Setup.VideoFormat != oldVideoFormat)
@@ -3797,6 +3892,7 @@ bool cMenuSetupCAMItem::Changed(void)
class cMenuSetupCAM : public cMenuSetupBase {
private:
+ int currentChannel;
const char *activationHelp;
eOSState Menu(void);
eOSState Reset(void);
@@ -3809,6 +3905,7 @@ public:
cMenuSetupCAM::cMenuSetupCAM(void)
{
+ currentChannel = cDevice::CurrentChannel();
activationHelp = NULL;
SetMenuCategory(mcSetupCam);
SetSection(tr("CAM"));
@@ -3934,6 +4031,8 @@ eOSState cMenuSetupCAM::ProcessKey(eKeys Key)
}
SetHelpKeys();
}
+ if (currentChannel != cDevice::CurrentChannel())
+ state = osEnd;
return state;
}
@@ -4081,22 +4180,19 @@ eOSState cMenuSetupMisc::ProcessKey(eKeys Key)
bool OldSVDRPPeering = data.SVDRPPeering;
bool ModifiedSVDRPSettings = false;
if (Key == kOk)
- ModifiedSVDRPSettings = data.SVDRPPeering != Setup.SVDRPPeering | strcmp(data.SVDRPHostName, Setup.SVDRPHostName);
+ ModifiedSVDRPSettings = data.SVDRPPeering != Setup.SVDRPPeering || strcmp(data.SVDRPHostName, Setup.SVDRPHostName);
eOSState state = cMenuSetupBase::ProcessKey(Key);
if (data.SVDRPPeering != OldSVDRPPeering)
Set();
if (ModifiedSVDRPSettings) {
- StopSVDRPClientHandler();
- StopSVDRPServerHandler();
- StartSVDRPServerHandler();
- if (data.SVDRPPeering)
- StartSVDRPClientHandler();
- else {
- LOCK_TIMERS_WRITE;
- Timers->SetExplicitModify();
- if (Timers->DelRemoteTimers())
- Timers->SetModified();
- }
+ StopSVDRPHandler();
+ {
+ LOCK_TIMERS_WRITE;
+ Timers->SetExplicitModify();
+ if (Timers->StoreRemoteTimers(NULL, NULL))
+ Timers->SetModified();
+ }
+ StartSVDRPHandler();
}
return state;
}
@@ -4563,6 +4659,7 @@ void cDisplayChannel::DisplayChannel(void)
displayChannel->SetChannel(channel, number);
cStatus::MsgOsdChannel(ChannelString(channel, number));
lastPresent = lastFollowing = NULL;
+ lastTime.Set();
}
void cDisplayChannel::DisplayInfo(void)
@@ -4578,6 +4675,7 @@ void cDisplayChannel::DisplayInfo(void)
cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL);
lastPresent = Present;
lastFollowing = Following;
+ lastTime.Set();
}
}
}
@@ -5133,15 +5231,16 @@ cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer,
cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) {
+ Recording.WriteInfo(); // we write this *before* attaching the recorder to the device, to make sure the info file is present when the recorder needs to update the fps value!
const cChannel *ch = timer->Channel();
recorder = new cRecorder(fileName, ch, timer->Priority());
if (device->AttachReceiver(recorder)) {
- Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
if (!Timer && !LastReplayed) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
cReplayControl::SetRecording(fileName);
SchedulesStateKey.Remove();
LOCK_RECORDINGS_WRITE;
+ SetRecordingTimerId(fileName, cString::sprintf("%d@%s", timer->Id(), Setup.SVDRPHostName));
Recordings->AddByName(fileName);
return;
}
@@ -5195,6 +5294,7 @@ void cRecordControl::Stop(bool ExecuteUserCommand)
DELETENULL(recorder);
timer->SetRecording(false);
timer = NULL;
+ SetRecordingTimerId(fileName, NULL);
cStatus::MsgRecording(device, NULL, fileName, false);
if (ExecuteUserCommand)
cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
@@ -5208,7 +5308,6 @@ bool cRecordControl::Process(time_t t)
timer->SetPending(false);
return false;
}
- AssertFreeDiskSpace(timer->Priority());
return true;
}
@@ -5478,8 +5577,6 @@ cReplayControl::cReplayControl(bool PauseLive)
cReplayControl::~cReplayControl()
{
cDevice::PrimaryDevice()->SetKeepTracks(false);
- Hide();
- cStatus::MsgReplaying(this, NULL, fileName, false);
Stop();
if (currentReplayControl == this)
currentReplayControl = NULL;
@@ -5487,6 +5584,8 @@ cReplayControl::~cReplayControl()
void cReplayControl::Stop(void)
{
+ Hide();
+ cStatus::MsgReplaying(this, NULL, fileName, false);
if (Setup.DelTimeshiftRec && *fileName) {
cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
if (rc && rc->InstantId()) {
@@ -5504,17 +5603,22 @@ void cReplayControl::Stop(void)
}
}
cDvbPlayerControl::Stop();
- LOCK_RECORDINGS_WRITE;
- Recordings->SetExplicitModify();
- if (cRecording *Recording = Recordings->GetByName(fileName)) {
- if (Recording->Delete()) {
- Recordings->DelByName(fileName);
- ClearLastReplayed(fileName);
- Recordings->SetModified();
- }
- else
- Skins.Message(mtError, tr("Error while deleting recording!"));
- }
+ bool Error = false;
+ {
+ LOCK_RECORDINGS_WRITE;
+ Recordings->SetExplicitModify();
+ if (cRecording *Recording = Recordings->GetByName(fileName)) {
+ if (Recording->Delete()) {
+ Recordings->DelByName(fileName);
+ ClearLastReplayed(fileName);
+ Recordings->SetModified();
+ }
+ else
+ Error = true;
+ }
+ }
+ if (Error)
+ Skins.Message(mtError, tr("Error while deleting recording!"));
return;
}
}
@@ -5524,6 +5628,16 @@ void cReplayControl::Stop(void)
cMenuRecordings::SetRecording(NULL); // make sure opening the Recordings menu navigates to the last replayed recording
}
+void cReplayControl::ClearEditingMarks(void)
+{
+ cStateKey StateKey;
+ marks.Lock(StateKey);
+ while (cMark *m = marks.First())
+ marks.Del(m);
+ StateKey.Remove();
+ cStatus::MsgMarksModified(NULL);
+}
+
void cReplayControl::SetRecording(const char *FileName)
{
fileName = FileName;
@@ -5764,6 +5878,7 @@ void cReplayControl::MarkToggle(void)
StateKey.Remove();
ShowTimed(2);
marksModified = true;
+ cStatus::MsgMarksModified(&marks);
}
}
@@ -5823,6 +5938,7 @@ void cReplayControl::MarkMove(int Frames, bool MarkRequired)
m->SetPosition(p);
Goto(m->Position(), true);
marksModified = true;
+ cStatus::MsgMarksModified(&marks);
}
else if (!MarkRequired)
Goto(SkipFrames(Frames), !Play);
@@ -5942,8 +6058,7 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
SkipSeconds(Setup.SkipSecondsRepeat); break;
case kYellow: SkipSeconds(Setup.SkipSeconds); break;
case kStop:
- case kBlue: Hide();
- Stop();
+ case kBlue: Stop();
return osEnd;
default: {
DoShowMode = false;
@@ -5987,8 +6102,7 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
else
Show();
break;
- case kBack: Hide();
- Stop();
+ case kBack: Stop();
return osRecordings;
default: return osUnknown;
}
diff --git a/menu.h b/menu.h
index 9a971ad..08c51f7 100644
--- a/menu.h
+++ b/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 4.5 2016/12/22 10:55:36 kls Exp $
+ * $Id: menu.h 4.6 2018/02/01 15:35:48 kls Exp $
*/
#ifndef __MENU_H
@@ -326,6 +326,7 @@ public:
virtual void Show(void);
virtual void Hide(void);
bool Visible(void) { return visible; }
+ virtual void ClearEditingMarks(void);
static void SetRecording(const char *FileName);
static const char *NowReplaying(void);
static const char *LastReplayed(void);
diff --git a/mtd.c b/mtd.c
index 345288e..04bf09d 100644
--- a/mtd.c
+++ b/mtd.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: mtd.c 1.11 2017/05/01 09:19:52 kls Exp $
+ * $Id: mtd.c 1.12 2017/10/31 12:16:39 kls Exp $
*/
#include "mtd.h"
@@ -148,6 +148,7 @@ class cMtdMapper {
private:
int number;
int masterCamSlotNumber;
+ int nextUniqPid;
uint16_t uniqPids[MAX_REAL_PIDS]; // maps a real PID to a unique PID
uint16_t realPids[MAX_UNIQ_PIDS]; // maps a unique PID to a real PID
cVector<uint16_t> uniqSids;
@@ -165,6 +166,7 @@ cMtdMapper::cMtdMapper(int Number, int MasterCamSlotNumber)
{
number = Number;
masterCamSlotNumber = MasterCamSlotNumber;
+ nextUniqPid = 0;
Clear();
}
@@ -179,11 +181,15 @@ uint16_t cMtdMapper::MakeUniqPid(uint16_t RealPid)
DBGMTD("CAM %d/%d: mapped PID %d (%04X) to %d (%04X)", masterCamSlotNumber, number, RealPid, RealPid, uniqPids[RealPid], uniqPids[RealPid]);
return uniqPids[RealPid];
#else
- for (int i = 0; i < MAX_UNIQ_PIDS; i++) {
+ for (int p = 0; p < MAX_UNIQ_PIDS; p++) {
+ int i = nextUniqPid + p;
+ if (i >= MAX_UNIQ_PIDS)
+ i -= MAX_UNIQ_PIDS;
if (realPids[i] == MTD_INVALID_PID) { // 0x0000 is a valid PID (PAT)!
realPids[i] = RealPid;
uniqPids[RealPid] = (number << UNIQ_PID_SHIFT) | i;
DBGMTD("CAM %d/%d: mapped PID %d (%04X) to %d (%04X)", masterCamSlotNumber, number, RealPid, RealPid, uniqPids[RealPid], uniqPids[RealPid]);
+ nextUniqPid = i + 1;
return uniqPids[RealPid];
}
}
@@ -212,6 +218,7 @@ void cMtdMapper::Clear(void)
DBGMTD("CAM %d/%d: MTD mapper cleared", masterCamSlotNumber, number);
memset(uniqPids, 0, sizeof(uniqPids));
memset(realPids, MTD_INVALID_PID, sizeof(realPids));
+ // do not reset nextUniqPid here!
uniqSids.Clear();
}
diff --git a/nit.c b/nit.c
index 874367f..299a406 100644
--- a/nit.c
+++ b/nit.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: nit.c 4.4 2016/12/23 14:16:59 kls Exp $
+ * $Id: nit.c 4.5 2018/03/18 10:52:21 kls Exp $
*/
#include "nit.h"
@@ -120,6 +120,10 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
bool forceTransponderUpdate = false;
for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
+ // Preserve parameters set separately in S2SatelliteDeliverySystemDescriptor:
+ cDvbTransponderParameters dtpc(Channel->Parameters());
+ dtp.SetStreamId(dtpc.StreamId());
+ //
int transponder = Channel->Transponder();
found = true;
if (!ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), transponder)) {
@@ -157,8 +161,8 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
if (!Channel->GroupSep() && cSource::IsSat(Channel->Source()) && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
SI::S2SatelliteDeliverySystemDescriptor *sd = (SI::S2SatelliteDeliverySystemDescriptor *)d;
cDvbTransponderParameters dtp(Channel->Parameters());
- dtp.SetSystem(DVB_SYSTEM_2);
dtp.SetStreamId(sd->getInputStreamIdentifier());
+ dbgnit(" stream id = %d\n", dtp.StreamId());
ChannelsModified |= Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), Channel->Srate(), dtp.ToString('S'));
break;
}
@@ -239,6 +243,16 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
bool forceTransponderUpdate = false;
for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
+ // Preserve parameters set separately in T2DeliverySystemDescripto:
+ cDvbTransponderParameters dtpc(Channel->Parameters());
+ dtp.SetSystem(dtpc.System());
+ dtp.SetStreamId(dtpc.StreamId());
+ dtp.SetT2SystemId(dtp.T2SystemId());
+ dtp.SetSisoMiso(dtpc.SisoMiso());
+ dtp.SetBandwidth(dtpc.Bandwidth());
+ dtp.SetGuard(dtpc.Guard());
+ dtp.SetTransmission(dtpc.Transmission());
+ //
int transponder = Channel->Transponder();
found = true;
if (!ISTRANSPONDER(Frequency / 1000000, transponder)) {
@@ -295,6 +309,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
dtp.SetTransmission(T2TransmissionModes[td->getTransmissionMode()]);
//TODO add parsing of frequencies
}
+ dbgnit(" stream id = %d\n", dtp.StreamId());
ChannelsModified |= Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('T'));
}
}
diff --git a/osd.c b/osd.c
index 524700a..d29a2c4 100644
--- a/osd.c
+++ b/osd.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.c 4.3 2015/09/10 14:12:06 kls Exp $
+ * $Id: osd.c 4.4 2018/01/25 15:09:09 kls Exp $
*/
#include "osd.h"
@@ -1183,7 +1183,7 @@ void cPixmap::DrawPixmap(const cPixmap *Pixmap, const cRect &Dirty)
t0.Shift(-Pixmap->DrawPort().Width(), 0);
while (t0.Y() > Pixmap->ViewPort().Top())
t0.Shift(0, -Pixmap->DrawPort().Height());
- cPoint t = t0;;
+ cPoint t = t0;
while (t.Y() <= Pixmap->ViewPort().Bottom()) {
while (t.X() <= Pixmap->ViewPort().Right()) {
cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
diff --git a/osd.h b/osd.h
index 61b318d..559819e 100644
--- a/osd.h
+++ b/osd.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.h 4.4 2015/04/19 12:18:25 kls Exp $
+ * $Id: osd.h 4.5 2017/11/02 14:59:19 kls Exp $
*/
#ifndef __OSD_H
@@ -785,8 +785,10 @@ protected:
///< If there are no dirty pixmaps, or if this is not a true color OSD,
///< this function returns NULL.
///< The caller must call DestroyPixmap() for the returned pixmap after use.
-//#define DEPRECATED_GETBITMAP
-#ifdef DEPRECATED_GETBITMAP
+#ifndef DEPRECATED_GETBITMAP
+#define DEPRECATED_GETBITMAP 0
+#endif
+#if DEPRECATED_GETBITMAP
public:
#endif
cBitmap *GetBitmap(int Area);
diff --git a/osdbase.c b/osdbase.c
index ac8026f..1720022 100644
--- a/osdbase.c
+++ b/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 4.2 2017/04/03 12:30:52 kls Exp $
+ * $Id: osdbase.c 4.4 2018/03/06 10:38:18 kls Exp $
*/
#include "osdbase.h"
@@ -92,6 +92,7 @@ cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
SetTitle(Title);
SetCols(c0, c1, c2, c3, c4);
first = 0;
+ lastOffset = -1;
current = marked = -1;
subMenu = NULL;
helpRed = helpGreen = helpYellow = helpBlue = NULL;
@@ -253,7 +254,7 @@ void cOsdMenu::Display(void)
}
if (current < 0)
current = 0; // just for safety - there HAS to be a current item!
- first = min(first, max(0, count - displayMenuItems)); // in case the menu size has changed
+ first = max(0, min(first, max(0, count - displayMenuItems))); // in case the menu size has changed
if (current - first >= displayMenuItems || current < first) {
first = current - displayMenuItems / 2;
if (first + displayMenuItems > count)
@@ -281,6 +282,9 @@ void cOsdMenu::Display(void)
void cOsdMenu::SetCurrent(cOsdItem *Item)
{
current = Item ? Item->Index() : -1;
+ if (current >= 0 && lastOffset >= 0)
+ first = max(0, current - lastOffset);
+ lastOffset = -1;
}
void cOsdMenu::RefreshCurrent(void)
@@ -326,6 +330,7 @@ void cOsdMenu::Clear(void)
{
if (marked >= 0)
SetStatus(NULL);
+ lastOffset = (current > first) ? current - first : 0;
first = 0;
current = marked = -1;
cList<cOsdItem>::Clear();
diff --git a/osdbase.h b/osdbase.h
index 91b795d..cbb0e87 100644
--- a/osdbase.h
+++ b/osdbase.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osdbase.h 4.2 2017/04/03 12:02:16 kls Exp $
+ * $Id: osdbase.h 4.5 2018/01/25 15:09:23 kls Exp $
*/
#ifndef __OSDBASE_H
@@ -30,7 +30,6 @@ enum eOSState { osUnknown,
osStopRecord,
osStopReplay,
osCancelEdit,
- osSwitchDvb,
osBack,
osEnd,
os_User, // the following values can be used locally
@@ -92,6 +91,7 @@ private:
char *title;
int cols[cSkinDisplayMenu::MaxTabs];
int first, current, marked;
+ int lastOffset;
eMenuCategory menuCategory;
eMenuSortMode menuSortMode;
eMenuOrientation menuOrientation;
diff --git a/peerdemo b/peerdemo
new file mode 100755
index 0000000..7129207
--- /dev/null
+++ b/peerdemo
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+
+# VDR SVDRP Peer Demo
+#
+# This script broadcasts an SVDRP discover datagram on the SVDRP UDP port and
+# then listens for replies from peer VDRs on both the UDP and TCP port.
+# It reacts properly to the SVDRP commands CONN, LSTT, POLL, PING and QUIT,
+# and thus seems like a regular VDR to other VDRs.
+#
+# (C) 2018 by Klaus Schmidinger <Klaus.Schmidinger@tvdr.de>
+
+use Getopt::Std;
+use IO::Socket;
+use IO::Select;
+
+$DefaultSvdrpPort = 6419;
+$DefaultSvdrpName = "peerdemo";
+
+$Usage = qq{
+Usage: $0 options
+
+Options: -n name use the given VDR name (default: $DefaultSvdrpName)
+ -p port use the given TCP port (default: $DefaultSvdrpPort)
+ -v be verbose
+};
+
+die $Usage if (!getopts("n:p:v"));
+
+$Name = $opt_n || $DefaultSvdrpName;
+$Port = $opt_p || $DefaultSvdrpPort;
+$Verbose = $opt_v || 0;
+
+# Open TCP and UDP sockets:
+
+$TcpPort = $Port;
+$UdpPort = $DefaultSvdrpPort;
+
+$TcpSocket = new IO::Socket::INET(Listen => 5, LocalPort => $TcpPort, Proto => "tcp", ReusePort => 1) || die "$!";
+$UdpSocket = new IO::Socket::INET( LocalPort => $UdpPort, Proto => "udp", ReusePort => 1) || die "$!";
+$SvdrpSelect = new IO::Select($TcpSocket);
+setsockopt($UdpSocket, SOL_SOCKET, SO_RCVTIMEO, pack('L!L!', 0, 1000)); # 1ms timeout on UDP socket
+
+# Send UDP broadcast:
+
+$BcastSocket = new IO::Socket::INET(PeerAddr => '255.255.255.255', PeerPort => $UdpPort, Proto => "udp", Broadcast => 1) || die "$!";
+$BcastMsg = "SVDRP:discover name:$Name port:$TcpPort vdrversion:20309 apiversion:20309 timeout:300";
+Log('>', $BcastSocket, $BcastMsg);
+print($BcastSocket $BcastMsg);
+$BcastSocket->close();
+
+# Listen on UDP and TCP socket:
+
+while (1) {
+ if ($UdpSocket->recv($Request, 1024)) {
+ if (Extract($Request, "name") ne $Name) {
+ Log('<', $UdpSocket, $Request);
+ ReportVDR($Request, $UdpSocket->peerhost());
+ }
+ }
+ if (my @Ready = $SvdrpSelect->can_read(0.01)) {
+ for my $fh (@Ready) {
+ if ($fh == $TcpSocket) {
+ # accept connection:
+ my $new = $TcpSocket->accept();
+ Log('<', $new, "incoming TCP connection");
+ # send mandatory response to simulate an SVDRP host:
+ my $Prompt = "220 $Name SVDRP VideoDiskRecorder 2.3.9; Wed Nov 29 17:00:29 2017; ISO-8859-1";
+ Log('>', $new, $Prompt);
+ print($new "$Prompt\n");
+ # add incoming connection to select:
+ $SvdrpSelect->add($new);
+ }
+ else {
+ # process connection:
+ my $Request = "";
+ $fh->recv($Request, 1024);
+ chomp($Request);
+ Log('<', $fh, $Request) if ($Request);
+ if ($Request =~ /^CONN/i) {
+ Reply($fh, "250 OK");
+ ReportVDR($Request, $fh->peerhost());
+ }
+ elsif ($Request =~ /^LSTT/i) {
+ Reply($fh, "550 No timers defined");
+ }
+ elsif ($Request =~ /^POLL/i) {
+ Reply($fh, "250 OK");
+ }
+ elsif ($Request =~ /^PING/i) {
+ Reply($fh, "250 $Name is alive");
+ }
+ elsif ($Request =~ /^QUIT/i || !$Request) {
+ # close connection:
+ Log('<', $fh, "connection closed");
+ $SvdrpSelect->remove($fh);
+ $fh->close;
+ }
+ }
+ }
+ }
+ }
+
+# Tools:
+
+sub Reply
+{
+ my ($fh, $s) = @_;
+ Log('>', $fh, $s);
+ print($fh "$s\n");
+}
+
+sub ReportVDR
+{
+ my $s = shift;
+ my $PeerHost = shift;
+ $s .= " "; # for easier parsing
+ my $Name = Extract($s, "name");
+ my $Port = Extract($s, "port");
+ my $VdrVersion = Extract($s, "vdrversion");
+ my $ApiVersion = Extract($s, "apiversion");
+ my $Timeout = Extract($s, "timeout");
+ print("found VDR '$Name' at $PeerHost with SVDRP port '$Port'\n");
+}
+
+sub Extract
+{
+ my ($s, $n) = @_;
+ return ($s =~ / $n:([^ ]*) /)[0];
+}
+
+sub Log
+{
+ return unless ($Verbose);
+ my ($Dir, $Socket, $Msg) = @_;
+ printf("SVDRP %s [%s:%s] %s\n", $Dir, $Socket->peerhost(), $Socket->peerport(), $Msg);
+}
diff --git a/player.h b/player.h
index aeb8af8..d67bf2a 100644
--- a/player.h
+++ b/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 4.2 2016/12/22 10:38:11 kls Exp $
+ * $Id: player.h 4.4 2018/02/01 15:34:51 kls Exp $
*/
#ifndef __PLAYER_H
@@ -102,10 +102,15 @@ public:
///< skins as a last resort, in case they want to display the state of the
///< current player. The return value is expected to be a short, single line
///< string. The default implementation returns an empty string.
- double FramesPerSecond(void) const { return player->FramesPerSecond(); }
- bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) const { return player->GetIndex(Current, Total, SnapToIFrame); }
- bool GetFrameNumber(int &Current, int &Total) const { return player->GetFrameNumber(Current, Total); }
- bool GetReplayMode(bool &Play, bool &Forward, int &Speed) const { return player->GetReplayMode(Play, Forward, Speed); }
+ virtual void ClearEditingMarks(void) {}
+ ///< Clears any editing marks this player might be showing.
+ ///< Deletion of the marks themselves is handled separately, calling
+ ///< this function merely tells the player to no longer display the
+ ///< marks, if it has any.
+ double FramesPerSecond(void) const { return player ? player->FramesPerSecond() : DEFAULTFRAMESPERSECOND; }
+ bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) const { return player ? player->GetIndex(Current, Total, SnapToIFrame) : false; }
+ bool GetFrameNumber(int &Current, int &Total) const { return player ? player->GetFrameNumber(Current, Total) : false; }
+ bool GetReplayMode(bool &Play, bool &Forward, int &Speed) const { return player ? player->GetReplayMode(Play, Forward, Speed) : false; }
static void Launch(cControl *Control);
static void Attach(void);
static void Shutdown(void);
diff --git a/po/ar.po b/po/ar.po
index f8b0394..4994973 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -615,9 +615,6 @@ msgstr "تحرير مجلد"
msgid "New folder"
msgstr "مجلد جديد"
-msgid "Sub folder"
-msgstr "مجلد فرعى"
-
msgid "Folder name already exists!"
msgstr "المجلد موجود بالفعل"
@@ -733,6 +730,9 @@ msgstr "جدولة"
msgid "Can't switch channel!"
msgstr "لا يمكن تبديل القناة"
+msgid "Schedule"
+msgstr "جدولة"
+
#, c-format
msgid "Schedule - %s"
msgstr "%sجدولة"
@@ -866,6 +866,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "عرص على الشاشة"
@@ -956,6 +962,9 @@ msgstr "قم دائما بسرد المجلدات اولا"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Number keys for characters"
@@ -1390,9 +1399,6 @@ msgstr "هل حقا تريد اعادة التشغيل"
msgid " Stop recording "
msgstr " ايقاف التسجيل "
-msgid "Schedule"
-msgstr "جدولة"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " ايقاف الاعادة"
@@ -1600,15 +1606,15 @@ msgstr "البرنامج سيقوم بالاغلاق لاحقا اضغط زر ا
msgid "Press any key to cancel shutdown"
msgstr "اضغط اى زر لالغاء اقفال البرنامج"
-msgid "Switching primary DVB..."
-msgstr "تم الانتقال لكرت الستالايت الافتراضى"
-
msgid "Editing process failed!"
msgstr "تم فشل عملية التعديل"
msgid "Editing process finished"
msgstr "تم الانتهاء من التعديل"
+msgid "Switching primary DVB..."
+msgstr "تم الانتقال لكرت الستالايت الافتراضى"
+
msgid "Press any key to cancel restart"
msgstr "اضغط اى زر لالغاء اعادة التشغيل"
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 285f7e3..cabcfbd 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -614,9 +614,6 @@ msgstr "Modificar carpeta"
msgid "New folder"
msgstr "Nova carpeta"
-msgid "Sub folder"
-msgstr "Sub carpeta"
-
msgid "Folder name already exists!"
msgstr "La carpeta ja existeix!"
@@ -732,6 +729,9 @@ msgstr "Programar"
msgid "Can't switch channel!"
msgstr "No puc canviar de canal!"
+msgid "Schedule"
+msgstr "Guia de Programaci"
+
#, c-format
msgid "Schedule - %s"
msgstr "Guia de Programaci - %s"
@@ -865,6 +865,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "Informaci en pantalla"
@@ -955,6 +961,9 @@ msgstr "Sempre ordenar primer carpetes"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Tecles numriques per a carcters"
@@ -1389,9 +1398,6 @@ msgstr "Segur que voleu reiniciar?"
msgid " Stop recording "
msgstr " Aturar la gravaci "
-msgid "Schedule"
-msgstr "Guia de Programaci"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Aturar la reproducci"
@@ -1599,15 +1605,15 @@ msgstr "VDR s'apagar ms tard, prem Power per a forar"
msgid "Press any key to cancel shutdown"
msgstr "Prem qualsevol tecla per cancellar l'aturada"
-msgid "Switching primary DVB..."
-msgstr "Canviant a la interfcie DVB primria..."
-
msgid "Editing process failed!"
msgstr "Procs d'edici fallit!"
msgid "Editing process finished"
msgstr "Procs d'edici finalitzat"
+msgid "Switching primary DVB..."
+msgstr "Canviant a la interfcie DVB primria..."
+
msgid "Press any key to cancel restart"
msgstr "Prem qualsevol tecla per cancellar el reinici"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index 3defcbd..0905bdb 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -614,9 +614,6 @@ msgstr "Změna složky"
msgid "New folder"
msgstr "Nová složka"
-msgid "Sub folder"
-msgstr "Podsložka"
-
msgid "Folder name already exists!"
msgstr "Složka již existuje!"
@@ -732,6 +729,9 @@ msgstr "Program"
msgid "Can't switch channel!"
msgstr "Kanál nelze přepnout!"
+msgid "Schedule"
+msgstr "Program (EPG)"
+
#, c-format
msgid "Schedule - %s"
msgstr "Program - %s"
@@ -865,6 +865,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -955,6 +961,9 @@ msgstr "Adresáře řadit vždy na začátek"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Psát písmena pomocí číselných kláves"
@@ -1389,9 +1398,6 @@ msgstr "Opravdu restartovat?"
msgid " Stop recording "
msgstr " Zastavit nahrávání "
-msgid "Schedule"
-msgstr "Program (EPG)"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Zastavit přehrávání"
@@ -1599,15 +1605,15 @@ msgstr "Vypnutí VDR bude odloženo - vypnete klávesou Power"
msgid "Press any key to cancel shutdown"
msgstr "Jakákoliv klávesa zruší vypnutí"
-msgid "Switching primary DVB..."
-msgstr "Přepnout primární DVB..."
-
msgid "Editing process failed!"
msgstr "Editační proces selhal!"
msgid "Editing process finished"
msgstr "Editační proces skončil"
+msgid "Switching primary DVB..."
+msgstr "Přepnout primární DVB..."
+
msgid "Press any key to cancel restart"
msgstr "Jakákoliv klávesa zruší restart"
diff --git a/po/da_DK.po b/po/da_DK.po
index 906aa61..23612ea 100644
--- a/po/da_DK.po
+++ b/po/da_DK.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -611,9 +611,6 @@ msgstr ""
msgid "New folder"
msgstr ""
-msgid "Sub folder"
-msgstr ""
-
msgid "Folder name already exists!"
msgstr ""
@@ -729,6 +726,9 @@ msgstr "Program"
msgid "Can't switch channel!"
msgstr "Kan ikke skifte kanal!"
+msgid "Schedule"
+msgstr "Programoversigt"
+
#, c-format
msgid "Schedule - %s"
msgstr "Program - %s"
@@ -862,6 +862,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -952,6 +958,9 @@ msgstr ""
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr ""
@@ -1386,9 +1395,6 @@ msgstr "Vil du virkelig genstarte?"
msgid " Stop recording "
msgstr " Stop optagelse "
-msgid "Schedule"
-msgstr "Programoversigt"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Stop afspilning"
@@ -1596,15 +1602,15 @@ msgstr "VDR slukker senere - tryk Sluk for at tvinge"
msgid "Press any key to cancel shutdown"
msgstr "Tryk vilkrlig tast for at annullere sluk"
-msgid "Switching primary DVB..."
-msgstr "Skifter primr DVB enhed..."
-
msgid "Editing process failed!"
msgstr "Redigeringsproces fejlede!"
msgid "Editing process finished"
msgstr "Redigeringsproces afsluttet"
+msgid "Switching primary DVB..."
+msgstr "Skifter primr DVB enhed..."
+
msgid "Press any key to cancel restart"
msgstr "Tryk vilkrlig knap for at annullere genstart"
diff --git a/po/de_DE.po b/po/de_DE.po
index d7e6576..7f3d2d6 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr "Verzeichnis editieren"
msgid "New folder"
msgstr "Neues Verzeichnis"
-msgid "Sub folder"
-msgstr "Unterverzeichnis"
-
msgid "Folder name already exists!"
msgstr "Verzeichnisname existiert bereits!"
@@ -730,6 +727,9 @@ msgstr "Programm"
msgid "Can't switch channel!"
msgstr "Kanal kann nicht umgeschaltet werden!"
+msgid "Schedule"
+msgstr "Programm"
+
#, c-format
msgid "Schedule - %s"
msgstr "Programm - %s"
@@ -863,6 +863,12 @@ msgstr "nach Namen"
msgid "by time"
msgstr "nach Zeit"
+msgid "ascending"
+msgstr "aufsteigend"
+
+msgid "descending"
+msgstr "absteigend"
+
msgid "OSD"
msgstr "OSD"
@@ -953,6 +959,9 @@ msgstr "Verzeichnisse immer zuerst einsortieren"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr "Standard Sortierreihenfolge fr Aufnahmen"
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr "Sortierreihenfolge fr Aufnahmen"
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Nummerntasten fr Zeichen"
@@ -1387,9 +1396,6 @@ msgstr "Wirklich neu starten?"
msgid " Stop recording "
msgstr " Aufzeichnung beenden "
-msgid "Schedule"
-msgstr "Programm"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Wiedergabe beenden"
@@ -1597,15 +1603,15 @@ msgstr "VDR schaltet spter aus - Power zum Erzwingen"
msgid "Press any key to cancel shutdown"
msgstr "Taste drcken, um Ausschalten abzubrechen"
-msgid "Switching primary DVB..."
-msgstr "Primres Interface wird umgeschaltet..."
-
msgid "Editing process failed!"
msgstr "Bearbeitung gescheitert!"
msgid "Editing process finished"
msgstr "Bearbeitung beendet"
+msgid "Switching primary DVB..."
+msgstr "Primres Interface wird umgeschaltet..."
+
msgid "Press any key to cancel restart"
msgstr "Taste drcken, um Neustart abzubrechen"
diff --git a/po/el_GR.po b/po/el_GR.po
index db382e8..1574a9a 100644
--- a/po/el_GR.po
+++ b/po/el_GR.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -611,9 +611,6 @@ msgstr ""
msgid "New folder"
msgstr ""
-msgid "Sub folder"
-msgstr ""
-
msgid "Folder name already exists!"
msgstr ""
@@ -729,6 +726,9 @@ msgstr ""
msgid "Can't switch channel!"
msgstr " !"
+msgid "Schedule"
+msgstr ""
+
#, c-format
msgid "Schedule - %s"
msgstr " - %s"
@@ -862,6 +862,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -952,6 +958,9 @@ msgstr ""
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr ""
@@ -1386,9 +1395,6 @@ msgstr "N ?"
msgid " Stop recording "
msgstr " "
-msgid "Schedule"
-msgstr ""
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " "
@@ -1596,15 +1602,15 @@ msgstr ""
msgid "Press any key to cancel shutdown"
msgstr " "
-msgid "Switching primary DVB..."
-msgstr " DVB ..."
-
msgid "Editing process failed!"
msgstr " !"
msgid "Editing process finished"
msgstr " "
+msgid "Switching primary DVB..."
+msgstr " DVB ..."
+
msgid "Press any key to cancel restart"
msgstr ""
diff --git a/po/es_ES.po b/po/es_ES.po
index e61ed66..fa15902 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr "Modificar carpeta"
msgid "New folder"
msgstr "Nueva carpeta"
-msgid "Sub folder"
-msgstr "Sub carpeta"
-
msgid "Folder name already exists!"
msgstr "La carpeta ya existe!"
@@ -730,6 +727,9 @@ msgstr "Gua"
msgid "Can't switch channel!"
msgstr "No se puede cambiar de canal!"
+msgid "Schedule"
+msgstr "Gua de Programacin"
+
#, c-format
msgid "Schedule - %s"
msgstr "Gua de programacin - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "Mens en pantalla"
@@ -953,6 +959,9 @@ msgstr "Siempre ordenar primero carpetas"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Teclas numricas para caracteres"
@@ -1387,9 +1396,6 @@ msgstr "Reiniciar realmente?"
msgid " Stop recording "
msgstr " Parar grabacin "
-msgid "Schedule"
-msgstr "Gua de Programacin"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Parar reproducin"
@@ -1597,15 +1603,15 @@ msgstr "VDR se apagar ms tarde - pulse Apagar para forzar"
msgid "Press any key to cancel shutdown"
msgstr "Pulse una tecla para interrumpir apagar"
-msgid "Switching primary DVB..."
-msgstr "Cambiando el interfaz DVB primario..."
-
msgid "Editing process failed!"
msgstr "Proceso de edicin fallido!"
msgid "Editing process finished"
msgstr "Proceso de edicin terminado"
+msgid "Switching primary DVB..."
+msgstr "Cambiando el interfaz DVB primario..."
+
msgid "Press any key to cancel restart"
msgstr "Pulse cualquier tecla para cancelar reinicio"
diff --git a/po/et_EE.po b/po/et_EE.po
index e58f97e..857bc25 100644
--- a/po/et_EE.po
+++ b/po/et_EE.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -611,9 +611,6 @@ msgstr "Muuda kausta"
msgid "New folder"
msgstr "Uus kaust"
-msgid "Sub folder"
-msgstr "Uus alamkaust"
-
msgid "Folder name already exists!"
msgstr "Sellenimeline kaust juba olemas!"
@@ -729,6 +726,9 @@ msgstr "Ajakava"
msgid "Can't switch channel!"
msgstr "Kanali vahetus ei ole võimalik!"
+msgid "Schedule"
+msgstr "Ajakava"
+
#, c-format
msgid "Schedule - %s"
msgstr "Ajakava - %s"
@@ -862,6 +862,12 @@ msgstr "nime järgi"
msgid "by time"
msgstr "aja järgi"
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -952,6 +958,9 @@ msgstr "Järjesta kaustad alati ette"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr "Salvestiste vaikimisi järjestus"
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Teksti sisestamine numbriklahvidega"
@@ -1386,9 +1395,6 @@ msgstr "Taaskäivitada?"
msgid " Stop recording "
msgstr " Lõpeta salvestamine "
-msgid "Schedule"
-msgstr "Ajakava"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Lõpeta taasesitus"
@@ -1596,15 +1602,15 @@ msgstr "VDR lülitub välja hiljem - vajuta Power kiirendamiseks"
msgid "Press any key to cancel shutdown"
msgstr "Vajuta suvalist klahvi väljalülitamise tühistamiseks"
-msgid "Switching primary DVB..."
-msgstr "Esmase DVB seadme ümberlülitus..."
-
msgid "Editing process failed!"
msgstr "Töötlemine ebaõnnestus!"
msgid "Editing process finished"
msgstr "Töötlemine lõpetatud"
+msgid "Switching primary DVB..."
+msgstr "Esmase DVB seadme ümberlülitus..."
+
msgid "Press any key to cancel restart"
msgstr "Taaskäivitamise katkestamiseks vajuta suvalist klahvi"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 964d8d0..5707d9b 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -615,9 +615,6 @@ msgstr "Muokkaa kansiota"
msgid "New folder"
msgstr "Uusi kansio"
-msgid "Sub folder"
-msgstr "Alikansio"
-
msgid "Folder name already exists!"
msgstr "Kansio on jo olemassa!"
@@ -733,6 +730,9 @@ msgstr "Ohjelmisto"
msgid "Can't switch channel!"
msgstr "Kanavan vaihtaminen ei mahdollista!"
+msgid "Schedule"
+msgstr "Ohjelmisto"
+
#, c-format
msgid "Schedule - %s"
msgstr "Ohjelmisto - %s"
@@ -866,6 +866,12 @@ msgstr "nimen mukaan"
msgid "by time"
msgstr "ajan mukaan"
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "Kuvaruutunäyttö"
@@ -956,6 +962,9 @@ msgstr "Näytä kansiot ensin"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr "Järjestä tallenteet oletuksena"
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Käytä numeronäppäimiä tekstisyötteessä"
@@ -1303,16 +1312,16 @@ msgid "Setup.Replay$Resume ID"
msgstr "Tallenteen paluutunniste"
msgid "any hosts"
-msgstr ""
+msgstr "mihin tahansa palvelimeen"
msgid "only default host"
-msgstr ""
+msgstr "vain oletuspalvelimeen"
msgid "type"
-msgstr ""
+msgstr "tyyppi"
msgid "full"
-msgstr ""
+msgstr "kokonaan"
msgid "Miscellaneous"
msgstr "Sekalaiset"
@@ -1390,9 +1399,6 @@ msgstr "Käynnistetäänkö uudelleen?"
msgid " Stop recording "
msgstr " Lopeta tallennus "
-msgid "Schedule"
-msgstr "Ohjelmisto"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Lopeta toisto"
@@ -1600,15 +1606,15 @@ msgstr "VDR sammuu myöhemmin - pakota virtanäppäimellä"
msgid "Press any key to cancel shutdown"
msgstr "Peru sammutus painamalla mitä tahansa näppäintä"
-msgid "Switching primary DVB..."
-msgstr "Vaihdetaan ensisijaista DVB-sovitinta..."
-
msgid "Editing process failed!"
msgstr "Muokkaus epäonnistui!"
msgid "Editing process finished"
msgstr "Muokkaus valmis"
+msgid "Switching primary DVB..."
+msgstr "Vaihdetaan ensisijaista DVB-sovitinta..."
+
msgid "Press any key to cancel restart"
msgstr "Peru uudelleenkäynnistys painamalla mitä tahansa näppäintä"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index e3c7e08..af8e4ed 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -18,7 +18,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\n"
"PO-Revision-Date: 2015-02-18 20:16+0100\n"
"Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n"
"Language-Team: French <vdr@linuxtv.org>\n"
@@ -622,9 +622,6 @@ msgstr "Modifier le dossier"
msgid "New folder"
msgstr "Nouveau dossier"
-msgid "Sub folder"
-msgstr "Sous-dossier"
-
msgid "Folder name already exists!"
msgstr "Ce nom de dossier existe déjà !"
@@ -740,6 +737,9 @@ msgstr "Programme"
msgid "Can't switch channel!"
msgstr "Impossible de changer de chaîne !"
+msgid "Schedule"
+msgstr "Programme"
+
#, c-format
msgid "Schedule - %s"
msgstr "Programme - %s"
@@ -873,6 +873,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "Affichage à l'écran"
@@ -963,6 +969,9 @@ msgstr "Toujours trier les dossiers en premier"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Touches numériques pour caractères"
@@ -1397,9 +1406,6 @@ msgstr "Confirmer le redémarrage ?"
msgid " Stop recording "
msgstr " Arrêter l'enregistrement "
-msgid "Schedule"
-msgstr "Programme"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Arrêter la lecture"
@@ -1607,15 +1613,15 @@ msgstr "VDR doit s'arrêter plus tard - Appuyer sur Arrêt pour forcer"
msgid "Press any key to cancel shutdown"
msgstr "Appuyer sur une touche pour annuler l'arrêt"
-msgid "Switching primary DVB..."
-msgstr "Changement de périphérique DVB primaire..."
-
msgid "Editing process failed!"
msgstr "Échec du montage !"
msgid "Editing process finished"
msgstr "Montage terminé"
+msgid "Switching primary DVB..."
+msgstr "Changement de périphérique DVB primaire..."
+
msgid "Press any key to cancel restart"
msgstr "Appuyer sur une touche pour annuler le redémarrage"
diff --git a/po/hr_HR.po b/po/hr_HR.po
index c70a5c3..9f625c7 100644
--- a/po/hr_HR.po
+++ b/po/hr_HR.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -613,9 +613,6 @@ msgstr ""
msgid "New folder"
msgstr ""
-msgid "Sub folder"
-msgstr ""
-
msgid "Folder name already exists!"
msgstr ""
@@ -731,6 +728,9 @@ msgstr "Raspored"
msgid "Can't switch channel!"
msgstr "Nemogue prebaciti program!"
+msgid "Schedule"
+msgstr "Raspored"
+
#, c-format
msgid "Schedule - %s"
msgstr "Raspored - %s"
@@ -864,6 +864,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -954,6 +960,9 @@ msgstr ""
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr ""
@@ -1388,9 +1397,6 @@ msgstr "Zaista ponovo pokrenuti?"
msgid " Stop recording "
msgstr " Prekini snimanje "
-msgid "Schedule"
-msgstr "Raspored"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Prekini reprodukciju"
@@ -1598,15 +1604,15 @@ msgstr "VDR e se iskljuiti kasnije - pritisnite Power za prisilno iskljuenje"
msgid "Press any key to cancel shutdown"
msgstr "Pritisnite tipku za prekid iskljuivanja"
-msgid "Switching primary DVB..."
-msgstr "Prebacivanje primarnog DVB ureaja..."
-
msgid "Editing process failed!"
msgstr "Rezanje neuspjeno!"
msgid "Editing process finished"
msgstr "Rezanje zavreno"
+msgid "Switching primary DVB..."
+msgstr "Prebacivanje primarnog DVB ureaja..."
+
msgid "Press any key to cancel restart"
msgstr "Pritisnite jednu tipku za ponitenje ponovnog podizanja"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index d1f80b7..bdb6fec 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\n"
"PO-Revision-Date: 2015-02-13 09:36+0200\n"
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
"Language-Team: Hungarian <vdr@linuxtv.org>\n"
@@ -616,9 +616,6 @@ msgstr "Könyvtár szerkesztése"
msgid "New folder"
msgstr "Új könyvtár"
-msgid "Sub folder"
-msgstr "Alkönyvtár"
-
msgid "Folder name already exists!"
msgstr "Ez a könyvtárnév már létezik!"
@@ -734,6 +731,9 @@ msgstr "Műsorújság"
msgid "Can't switch channel!"
msgstr "Csatornaváltás nem lehetséges!"
+msgid "Schedule"
+msgstr "Műsorújság"
+
#, c-format
msgid "Schedule - %s"
msgstr "Műsorújság - %s"
@@ -867,6 +867,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -957,6 +963,9 @@ msgstr "Könyvtárakat rendezd előre"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Betűk a számgombokon"
@@ -1391,9 +1400,6 @@ msgstr "Tényleg újraindítani?"
msgid " Stop recording "
msgstr " Felvételt befejezni "
-msgid "Schedule"
-msgstr "Műsorújság"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Lejátszást befejzni"
@@ -1601,15 +1607,15 @@ msgstr "VDR később fog leállni - Kikapcs gombbal kényszerített leállítás
msgid "Press any key to cancel shutdown"
msgstr "Nyomj egy gombot a leállás megszakításához"
-msgid "Switching primary DVB..."
-msgstr "Elsődleges DVB eszköz váltása folyamatban..."
-
msgid "Editing process failed!"
msgstr "Vágás sikertelen!"
msgid "Editing process finished"
msgstr "Vágás befejezve"
+msgid "Switching primary DVB..."
+msgstr "Elsődleges DVB eszköz váltása folyamatban..."
+
msgid "Press any key to cancel restart"
msgstr "Nyomj egy gombot az újraindítás megszakításához"
diff --git a/po/it_IT.po b/po/it_IT.po
index 5e214c5..6e7815c 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -11,8 +11,8 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
-"PO-Revision-Date: 2017-06-23 00:00+0100\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\n"
+"PO-Revision-Date: 2017-07-02 23:49+0100\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n"
"Language: it\n"
@@ -617,9 +617,6 @@ msgstr "Modifica cartella"
msgid "New folder"
msgstr "Nuova cartella"
-msgid "Sub folder"
-msgstr "Sotto cartella"
-
msgid "Folder name already exists!"
msgstr "Nome cartella già esistente!"
@@ -735,6 +732,9 @@ msgstr "Programmi"
msgid "Can't switch channel!"
msgstr "Impossibile cambiare canale!"
+msgid "Schedule"
+msgstr "Programmi"
+
#, c-format
msgid "Schedule - %s"
msgstr "Programmi - %s"
@@ -868,6 +868,12 @@ msgstr "per nome"
msgid "by time"
msgstr "per ora"
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -958,6 +964,9 @@ msgstr "Ordina sempre per prima le cartelle"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr "Modalità ordinamento predefinito per registrazioni"
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Tasti numerici per i caratteri"
@@ -1305,16 +1314,16 @@ msgid "Setup.Replay$Resume ID"
msgstr "ID di ripristino"
msgid "any hosts"
-msgstr ""
+msgstr "qualsiasi sistema"
msgid "only default host"
-msgstr ""
+msgstr "solo sistema predefinito"
msgid "type"
msgstr "tipo"
msgid "full"
-msgstr "completo"
+msgstr "tutto"
msgid "Miscellaneous"
msgstr "Generici"
@@ -1392,9 +1401,6 @@ msgstr "Eseguire un riavvio?"
msgid " Stop recording "
msgstr " Ferma registrazione "
-msgid "Schedule"
-msgstr "Programmi"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Ferma riproduzione"
@@ -1602,15 +1608,15 @@ msgstr "VDR si spegnerà più tardi - premi Power per forzare"
msgid "Press any key to cancel shutdown"
msgstr "Premi un tasto per annullare lo spegnimento"
-msgid "Switching primary DVB..."
-msgstr "Cambio della scheda DVB primaria..."
-
msgid "Editing process failed!"
msgstr "Processo di modifica fallito!"
msgid "Editing process finished"
msgstr "Processo di modifica terminato"
+msgid "Switching primary DVB..."
+msgstr "Cambio della scheda DVB primaria..."
+
msgid "Press any key to cancel restart"
msgstr "Premi un tasto per annullare il riavvio"
diff --git a/po/lt_LT.po b/po/lt_LT.po
index 2c1f3d7..b0855d1 100644
--- a/po/lt_LT.po
+++ b/po/lt_LT.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -611,9 +611,6 @@ msgstr "Koreguoti katalogą"
msgid "New folder"
msgstr "Naujas katalogas"
-msgid "Sub folder"
-msgstr "Pakatalogis"
-
msgid "Folder name already exists!"
msgstr "Toks katalogo vardas jau egzistuoja!"
@@ -729,6 +726,9 @@ msgstr "Programa"
msgid "Can't switch channel!"
msgstr "Negali perjunti kanalo!"
+msgid "Schedule"
+msgstr "Programa"
+
#, c-format
msgid "Schedule - %s"
msgstr "Programa - %s"
@@ -862,6 +862,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD (ekrano užsklanda)"
@@ -952,6 +958,9 @@ msgstr "Visada pirmiau rūšiuoti katalogus"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Skaičių mygtukai simboliams"
@@ -1386,9 +1395,6 @@ msgstr "Tikrai perkrauti?"
msgid " Stop recording "
msgstr " Sustabdyti įrašinėjimą "
-msgid "Schedule"
-msgstr "Programa"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Sustabdyti kartojimą"
@@ -1596,15 +1602,15 @@ msgstr "VDR išsijungs vėliau - paspauskite Power mygtuką kad padarytumėte ta
msgid "Press any key to cancel shutdown"
msgstr "Paspauskit bet kurį mygtuką kad sustabdytumėte sistemos išjungimą"
-msgid "Switching primary DVB..."
-msgstr "Sukeičiamas pirminis DVB įrenginys..."
-
msgid "Editing process failed!"
msgstr "Koregavimas nepavyko!"
msgid "Editing process finished"
msgstr "Koregavimas baigtas"
+msgid "Switching primary DVB..."
+msgstr "Sukeičiamas pirminis DVB įrenginys..."
+
msgid "Press any key to cancel restart"
msgstr "Paspauskit bet kurį mygtuką kad sustabdytumėte perkrovimą"
diff --git a/po/mk_MK.po b/po/mk_MK.po
index d07af18..bb2a860 100644
--- a/po/mk_MK.po
+++ b/po/mk_MK.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\n"
"PO-Revision-Date: 2015-02-08 15:18+0100\n"
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
"Language-Team: Macedonian <en@li.org>\n"
@@ -612,9 +612,6 @@ msgstr "Уреди директориум"
msgid "New folder"
msgstr "Нов директориум"
-msgid "Sub folder"
-msgstr "Поддиректориум"
-
msgid "Folder name already exists!"
msgstr "Името веќе постои"
@@ -730,6 +727,9 @@ msgstr "Распоред"
msgid "Can't switch channel!"
msgstr "Невозможно е да се смени канал!"
+msgid "Schedule"
+msgstr "Распоред"
+
#, c-format
msgid "Schedule - %s"
msgstr "Распоред - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -953,6 +959,9 @@ msgstr "Секогаш сортирај прво папки"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Нумерички копчиња за букви"
@@ -1387,9 +1396,6 @@ msgstr "Рестартирање?"
msgid " Stop recording "
msgstr " Запри снимање "
-msgid "Schedule"
-msgstr "Распоред"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Запри пуштање"
@@ -1597,15 +1603,15 @@ msgstr "VDR ќе се исклучи подоцна - притиснете Ис
msgid "Press any key to cancel shutdown"
msgstr "Притиснете копче за отказ на исклучување"
-msgid "Switching primary DVB..."
-msgstr "Промена на примарниот DVB уред..."
-
msgid "Editing process failed!"
msgstr "Сечењето е неуспешно!"
msgid "Editing process finished"
msgstr "Сечењето заврши"
+msgid "Switching primary DVB..."
+msgstr "Промена на примарниот DVB уред..."
+
msgid "Press any key to cancel restart"
msgstr "Притиснете копче за откажување на рестарт"
diff --git a/po/nl_NL.po b/po/nl_NL.po
index ed6223e..9c46bb6 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -13,7 +13,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -617,9 +617,6 @@ msgstr "Bewerk map"
msgid "New folder"
msgstr "Nieuwe map"
-msgid "Sub folder"
-msgstr "Submap"
-
msgid "Folder name already exists!"
msgstr "Map bestaat al"
@@ -735,6 +732,9 @@ msgstr "Programmagids"
msgid "Can't switch channel!"
msgstr "Kan geen kanaal wisselen!"
+msgid "Schedule"
+msgstr "Programmagids"
+
#, c-format
msgid "Schedule - %s"
msgstr "Programma - %s"
@@ -868,6 +868,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -958,6 +964,9 @@ msgstr "Altijd mappen eerst sorteren"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Nummertoetsen voor karakters"
@@ -1392,9 +1401,6 @@ msgstr "Werkelijk opnieuw starten?"
msgid " Stop recording "
msgstr " Stop opnemen "
-msgid "Schedule"
-msgstr "Programmagids"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Stop afspelen"
@@ -1602,15 +1608,15 @@ msgstr "VDR zal later uitschakelen - Druk 'Power' om te forceren"
msgid "Press any key to cancel shutdown"
msgstr "Druk een toets om shutdown af te breken"
-msgid "Switching primary DVB..."
-msgstr "Eerste DVB-kaart wordt omgeschakeld..."
-
msgid "Editing process failed!"
msgstr "Bewerken is mislukt!"
msgid "Editing process finished"
msgstr "Bewerken is klaar"
+msgid "Switching primary DVB..."
+msgstr "Eerste DVB-kaart wordt omgeschakeld..."
+
msgid "Press any key to cancel restart"
msgstr "Druk een willekeurige toets om herstarten af te breken"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 37eb906..244f7a5 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr ""
msgid "New folder"
msgstr ""
-msgid "Sub folder"
-msgstr ""
-
msgid "Folder name already exists!"
msgstr ""
@@ -730,6 +727,9 @@ msgstr "Programmer"
msgid "Can't switch channel!"
msgstr "Ikke mulig skifte kanal!"
+msgid "Schedule"
+msgstr "Programmer"
+
#, c-format
msgid "Schedule - %s"
msgstr "Program Guide - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -953,6 +959,9 @@ msgstr ""
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr ""
@@ -1387,9 +1396,6 @@ msgstr "Vil du virkelig starte p nytt?"
msgid " Stop recording "
msgstr " Stopp opptak fra "
-msgid "Schedule"
-msgstr "Programmer"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Stopp avspilling"
@@ -1597,15 +1603,15 @@ msgstr ""
msgid "Press any key to cancel shutdown"
msgstr "Trykk en tast for ikke sl av"
-msgid "Switching primary DVB..."
-msgstr "Bytter frste DVB-enhet..."
-
msgid "Editing process failed!"
msgstr "Feil under redigering!"
msgid "Editing process finished"
msgstr "Redigeringsprosess avsluttet"
+msgid "Switching primary DVB..."
+msgstr "Bytter frste DVB-enhet..."
+
msgid "Press any key to cancel restart"
msgstr ""
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 3a6ec65..8675955 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -4,38 +4,40 @@
# Michael Rakowski <mrak@gmx.de>, 2002, 2003, 2008
# Jaroslaw Swierczynski <swiergot@gmail.com>, 2006
# Marek Nazarko <mnazarko@gmail.com>, 2013
-# Tomasz Maciej Nowak <tmn505@gmail.com>, 2015
+# Tomasz Maciej Nowak <tmn505@gmail.com>, 2015, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
-"PO-Revision-Date: 2015-02-12 00:59+0100\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
-"Language: pl\n"
+"Language: pl_PL\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 2.0.6\n"
msgid "*** Invalid Channel ***"
-msgstr "*** Niepoprawny kana ***"
+msgstr "*** Niepoprawny kanał ***"
msgid "CAM activated!"
msgstr "CAM aktywowany!"
msgid "Channel not available!"
-msgstr "Kana nie jest dostpny!"
+msgstr "Kanał nie jest dostępny!"
msgid "Can't start Transfer Mode!"
-msgstr "Nie mona uruchomi trybu transferu!"
+msgstr "Nie można uruchomić trybu transferu!"
msgid "off"
-msgstr "wycz"
+msgstr "wyłącz"
msgid "on"
-msgstr "wcz"
+msgstr "włącz"
msgid "auto"
msgstr "auto"
@@ -98,7 +100,7 @@ msgid "Content$Movie/Drama"
msgstr "Film/Dramat"
msgid "Content$Detective/Thriller"
-msgstr "Krymina/Thriller"
+msgstr "Kryminał/Thriller"
msgid "Content$Adventure/Western/War"
msgstr "Przygodowy/Western/Wojenny"
@@ -116,13 +118,13 @@ msgid "Content$Romance"
msgstr "Romans"
msgid "Content$Serious/Classical/Religious/Historical Movie/Drama"
-msgstr "Powane/Klasyczne/Religijne/Historyczne/Dramaty"
+msgstr "Poważne/Klasyczne/Religijne/Historyczne/Dramaty"
msgid "Content$Adult Movie/Drama"
-msgstr "Filmy dla dorosych/Dramaty"
+msgstr "Filmy dla dorosłych/Dramaty"
msgid "Content$News/Current Affairs"
-msgstr "Wiadomoci/Informacje"
+msgstr "Wiadomości/Informacje"
msgid "Content$News/Weather Report"
msgstr "Prognoza pogody"
@@ -158,13 +160,13 @@ msgid "Content$Sport Magazine"
msgstr "Magazyny sportowe"
msgid "Content$Football/Soccer"
-msgstr "Pika nona"
+msgstr "Piłka nożna"
msgid "Content$Tennis/Squash"
msgstr "Squash"
msgid "Content$Team Sports"
-msgstr "Sporty zespoowe"
+msgstr "Sporty zespołowe"
msgid "Content$Athletics"
msgstr "Lekkoatletyka"
@@ -185,7 +187,7 @@ msgid "Content$Martial Sports"
msgstr "Sporty walki"
msgid "Content$Children's/Youth Programme"
-msgstr "Dziecice"
+msgstr "Dziecięce"
msgid "Content$Pre-school Children's Programme"
msgstr "Przedszkolne"
@@ -200,7 +202,7 @@ msgid "Content$Informational/Educational/School Programme"
msgstr "Infromacyjne/Edukacyjne/Szkolne"
msgid "Content$Cartoons/Puppets"
-msgstr "Kreskwki/Kukiekowe"
+msgstr "Kreskówki/Kukiełkowe"
msgid "Content$Music/Ballet/Dance"
msgstr "Muzyka/Balet/Taniec"
@@ -227,10 +229,10 @@ msgid "Content$Arts/Culture"
msgstr "Sztuka/Kultura"
msgid "Content$Performing Arts"
-msgstr "Rzemioso"
+msgstr "Rzemiosło"
msgid "Content$Fine Arts"
-msgstr "Dziea sztuki"
+msgstr "Dzieła sztuki"
msgid "Content$Religion"
msgstr "Religia"
@@ -260,22 +262,22 @@ msgid "Content$Fashion"
msgstr "Moda"
msgid "Content$Social/Political/Economics"
-msgstr "Spoeczne/Polityka/Ekonomia"
+msgstr "Społeczne/Polityka/Ekonomia"
msgid "Content$Magazine/Report/Documentary"
-msgstr "Reportae/Dokumentalne"
+msgstr "Reportaże/Dokumentalne"
msgid "Content$Economics/Social Advisory"
msgstr "Ekonomia"
msgid "Content$Remarkable People"
-msgstr "Sawni ludzie"
+msgstr "Sławni ludzie"
msgid "Content$Education/Science/Factual"
msgstr "Edukacja/Nauka"
msgid "Content$Nature/Animals/Environment"
-msgstr "Natura/Zwierzta/rodowisko"
+msgstr "Natura/Zwierzęta/Środowisko"
msgid "Content$Technology/Natural Sciences"
msgstr "Technologia/Nauki przyrodnicze"
@@ -287,22 +289,22 @@ msgid "Content$Foreign Countries/Expeditions"
msgstr "Obce kraje/Wyprawy"
msgid "Content$Social/Spiritual Sciences"
-msgstr "Spoeczne/Duchowe"
+msgstr "Społeczne/Duchowe"
msgid "Content$Further Education"
msgstr "Dalsza edukacja"
msgid "Content$Languages"
-msgstr "Jzyki"
+msgstr "Języki"
msgid "Content$Leisure/Hobbies"
msgstr "Czas wolny/Hobby"
msgid "Content$Tourism/Travel"
-msgstr "Turystyka/Podre"
+msgstr "Turystyka/Podróże"
msgid "Content$Handicraft"
-msgstr "Rkodzieo"
+msgstr "Rękodzieło"
msgid "Content$Motoring"
msgstr "Automobilizm"
@@ -320,23 +322,23 @@ msgid "Content$Gardening"
msgstr "Ogrody"
msgid "Content$Original Language"
-msgstr "Oryginalny jzyk"
+msgstr "Oryginalny język"
msgid "Content$Black & White"
-msgstr "Czarne i biae"
+msgstr "Czarne i białe"
msgid "Content$Unpublished"
msgstr "Niepublikowane"
msgid "Content$Live Broadcast"
-msgstr "Transmisje na ywo"
+msgstr "Transmisje na żywo"
#, c-format
msgid "ParentalRating$from %d"
msgstr "od %d"
msgid "No title"
-msgstr "Bez tytuu"
+msgstr "Bez tytułu"
#. TRANSLATORS: The name of the language, as written natively
msgid "LanguageName$English"
@@ -350,50 +352,50 @@ msgid "Phase 1: Detecting RC code type"
msgstr "Etap 1: Wykrywanie rodzaju kodu pilota"
msgid "Press any key on the RC unit"
-msgstr "Nacinij dowolny klawisz na pilocie"
+msgstr "Naciśnij dowolny klawisz na pilocie"
msgid "RC code detected!"
msgstr "Wykryto kod pilota!"
msgid "Do not press any key..."
-msgstr "Nie naciskaj teraz adnego klawisza..."
+msgstr "Nie naciskaj teraz żadnego klawisza..."
msgid "Phase 2: Learning specific key codes"
-msgstr "Etap 2: Nauka konkretnych kodw klawiszy"
+msgstr "Etap 2: Nauka konkretnych kodów klawiszy"
#, c-format
msgid "Press key for '%s'"
-msgstr "Nacinij klawisz dla '%s'"
+msgstr "Naciśnij klawisz dla '%s'"
msgid "Press 'Up' to confirm"
-msgstr "Nacinij 'Do gry' aby potwierdzi"
+msgstr "Naciśnij 'Do góry' aby potwierdzić"
msgid "Press 'Down' to continue"
-msgstr "Nacinij 'Na d' aby kontynuowa"
+msgstr "Naciśnij 'Na dół' aby kontynuować"
msgid "(press 'Up' to go back)"
-msgstr "(nacinij 'Do gry' aby si cofn)"
+msgstr "(naciśnij 'Do góry' aby się cofnąć)"
msgid "(press 'Down' to end key definition)"
-msgstr "(nacinij 'Na d' aby zakoczy definiowanie)"
+msgstr "(naciśnij 'Na dół' aby zakończyć definiowanie)"
msgid "(press 'Menu' to skip this key)"
-msgstr "(nacinij 'Menu' aby pomin ten klawisz)"
+msgstr "(naciśnij 'Menu' aby pominąć ten klawisz)"
msgid "Learning Remote Control Keys"
msgstr "Nauka klawiszy pilota"
msgid "Phase 3: Saving key codes"
-msgstr "Etap 3: Zapisywanie kodw klawiszy"
+msgstr "Etap 3: Zapisywanie kodów klawiszy"
msgid "Press 'Up' to save, 'Down' to cancel"
-msgstr "Nacinij 'Do gry' aby zapisa, 'Na d' - aby anulowa"
+msgstr "Naciśnij 'Do góry' aby zapisać, 'Na dół' - aby anulować"
msgid "Key$Up"
-msgstr "Do gry"
+msgstr "Do góry"
msgid "Key$Down"
-msgstr "Na d"
+msgstr "Na dół"
msgid "Key$Menu"
msgstr "Menu"
@@ -417,7 +419,7 @@ msgid "Key$Green"
msgstr "Zielony"
msgid "Key$Yellow"
-msgstr "ty"
+msgstr "Żółty"
msgid "Key$Blue"
msgstr "Niebieski"
@@ -426,10 +428,10 @@ msgid "Key$Info"
msgstr "Info"
msgid "Key$Play/Pause"
-msgstr "Odtwrz/Pauza"
+msgstr "Odtwórz/Pauza"
msgid "Key$Play"
-msgstr "Odtwrz"
+msgstr "Odtwórz"
msgid "Key$Pause"
msgstr "Pauza"
@@ -444,7 +446,7 @@ msgid "Key$FastFwd"
msgstr "Do przodu"
msgid "Key$FastRew"
-msgstr "Do tyu"
+msgstr "Do tyłu"
msgid "Key$Next"
msgstr "Dalej"
@@ -453,19 +455,19 @@ msgid "Key$Prev"
msgstr "Wstecz"
msgid "Key$Power"
-msgstr "Wycz"
+msgstr "Wyłącz"
msgid "Key$Channel+"
-msgstr "Kana+"
+msgstr "Kanał+"
msgid "Key$Channel-"
-msgstr "Kana-"
+msgstr "Kanał-"
msgid "Key$PrevChannel"
-msgstr "Poprzedni kana"
+msgstr "Poprzedni kanał"
msgid "Key$Volume+"
-msgstr "Goniej"
+msgstr "Głośniej"
msgid "Key$Volume-"
msgstr "Ciszej"
@@ -474,16 +476,16 @@ msgid "Key$Mute"
msgstr "Wycisz"
msgid "Key$Audio"
-msgstr "Dwik"
+msgstr "Dźwięk"
msgid "Key$Subtitles"
-msgstr "Podtytuy"
+msgstr "Podtytuły"
msgid "Key$Schedule"
msgstr "Program"
msgid "Key$Channels"
-msgstr "Kanay"
+msgstr "Kanały"
msgid "Key$Timers"
msgstr "Timery"
@@ -498,34 +500,34 @@ msgid "Key$Commands"
msgstr "Polecenia"
msgid "Key$User0"
-msgstr "Uytkownik 0"
+msgstr "Użytkownik 0"
msgid "Key$User1"
-msgstr "Uytkownik 1"
+msgstr "Użytkownik 1"
msgid "Key$User2"
-msgstr "Uytkownik 2"
+msgstr "Użytkownik 2"
msgid "Key$User3"
-msgstr "Uytkownik 3"
+msgstr "Użytkownik 3"
msgid "Key$User4"
-msgstr "Uytkownik 4"
+msgstr "Użytkownik 4"
msgid "Key$User5"
-msgstr "Uytkownik 5"
+msgstr "Użytkownik 5"
msgid "Key$User6"
-msgstr "Uytkownik 6"
+msgstr "Użytkownik 6"
msgid "Key$User7"
-msgstr "Uytkownik 7"
+msgstr "Użytkownik 7"
msgid "Key$User8"
-msgstr "Uytkownik 8"
+msgstr "Użytkownik 8"
msgid "Key$User9"
-msgstr "Uytkownik 9"
+msgstr "Użytkownik 9"
msgid "Free To Air"
msgstr "nieszyfrowany"
@@ -534,16 +536,16 @@ msgid "encrypted"
msgstr "szyfrowany"
msgid "Edit channel"
-msgstr "Edycja kanau"
+msgstr "Edycja kanału"
msgid "Name"
msgstr "Nazwa"
msgid "Source"
-msgstr "rdo"
+msgstr "Źródło"
msgid "Frequency"
-msgstr "Czstotliwo"
+msgstr "Częstotliwość"
msgid "Vpid"
msgstr "Vpid"
@@ -585,10 +587,10 @@ msgid "Tid"
msgstr "Tid"
msgid "Channel settings are not unique!"
-msgstr "Ustawienia kanau nie s unikalne!"
+msgstr "Ustawienia kanału nie są unikalne!"
msgid "Channels"
-msgstr "Kanay"
+msgstr "Kanały"
msgid "Button$Edit"
msgstr "Edytuj"
@@ -597,16 +599,16 @@ msgid "Button$New"
msgstr "Nowy"
msgid "Button$Delete"
-msgstr "Usu"
+msgstr "Usuń"
msgid "Button$Mark"
msgstr "Zaznacz"
msgid "Channel is being used by a timer!"
-msgstr "Kana jest uywany przez timer!"
+msgstr "Kanał jest używany przez timer!"
msgid "Delete channel?"
-msgstr "Usun kana?"
+msgstr "Usunąć kanał?"
msgid "Edit folder"
msgstr "Edutuj katalog"
@@ -614,24 +616,21 @@ msgstr "Edutuj katalog"
msgid "New folder"
msgstr "Nowy katalog"
-msgid "Sub folder"
-msgstr "Podkatalog"
-
msgid "Folder name already exists!"
msgstr "Taka nazwa katalogu juz istnieje"
#, c-format
msgid "Folder name must not contain '%c'!"
-msgstr "Folder nie moe zawiera '%c'!"
+msgstr "Folder nie może zawierać '%c'!"
msgid "Button$Open"
-msgstr "Otwrz"
+msgstr "Otwórz"
msgid "Delete folder and all sub folders?"
-msgstr "Czy skasowa katalog i wszystkie podkatalogi?"
+msgstr "Czy skasować katalog i wszystkie podkatalogi?"
msgid "Delete folder?"
-msgstr "Czy skasowa katalog?"
+msgstr "Czy skasować katalog?"
msgid "Edit timer"
msgstr "Edycja timera"
@@ -640,13 +639,13 @@ msgid "Active"
msgstr "Aktywny"
msgid "Channel"
-msgstr "Kana"
+msgstr "Kanał"
msgid "Day"
-msgstr "Dzie"
+msgstr "Dzień"
msgid "Start"
-msgstr "Pocztek"
+msgstr "Początek"
msgid "Stop"
msgstr "Koniec"
@@ -658,13 +657,13 @@ msgid "Priority"
msgstr "Priorytet"
msgid "Lifetime"
-msgstr "Czas ycia"
+msgstr "Czas życia"
msgid "File"
msgstr "Plik"
msgid "Record on"
-msgstr ""
+msgstr "Nagrywanie włączone"
msgid "Button$Folder"
msgstr "Katalog"
@@ -676,13 +675,13 @@ msgid "Button$Repeating"
msgstr "Powtarzanie"
msgid "First day"
-msgstr "Pierwszy dzie"
+msgstr "Pierwszy dzień"
msgid "Error while accessing remote timer"
-msgstr ""
+msgstr "Błąd podczas dostępu do zdalnego timera"
msgid "Timer has been deleted!"
-msgstr ""
+msgstr "Timer został usunięty!"
msgid "Select folder"
msgstr "Wybierz katalog"
@@ -691,16 +690,16 @@ msgid "Timers"
msgstr "Timery"
msgid "Button$On/Off"
-msgstr "W/Wy"
+msgstr "Wł/Wył"
msgid "Button$Info"
msgstr "Info"
msgid "Delete timer?"
-msgstr "Usun timer?"
+msgstr "Usunąć timer?"
msgid "Timer still recording - really delete?"
-msgstr "Trwa nagrywanie - na pewno usun?"
+msgstr "Trwa nagrywanie - na pewno usunąć?"
msgid "Event"
msgstr "Audycja"
@@ -712,16 +711,16 @@ msgid "Button$Record"
msgstr "Nagraj"
msgid "Button$Switch"
-msgstr "Przecz"
+msgstr "Przełącz"
msgid "What's on now?"
-msgstr "Program biecy"
+msgstr "Program bieżący"
msgid "What's on next?"
-msgstr "Nastpne audycje"
+msgstr "Następne audycje"
msgid "Button$Next"
-msgstr "Nastpnie"
+msgstr "Następnie"
msgid "Button$Now"
msgstr "Teraz"
@@ -730,7 +729,10 @@ msgid "Button$Schedule"
msgstr "Program"
msgid "Can't switch channel!"
-msgstr "Nie mona przeczy kanau!"
+msgstr "Nie można przełączyć kanału!"
+
+msgid "Schedule"
+msgstr "Program"
#, c-format
msgid "Schedule - %s"
@@ -741,39 +743,39 @@ msgid "This event - %s"
msgstr "Ta audycja - %s"
msgid "This event - all channels"
-msgstr "Ta audycja - wszystkie kanay"
+msgstr "Ta audycja - wszystkie kanały"
msgid "All events - all channels"
-msgstr "Wszystkie audycje - wszystkie kanay"
+msgstr "Wszystkie audycje - wszystkie kanały"
#, c-format
msgid "Please enter %d digits!"
-msgstr "Prosz poda %d cyfr!"
+msgstr "Proszę podać %d cyfr!"
msgid "CAM not responding!"
msgstr "CAM nie reaguje!"
msgid "Edit path"
-msgstr "Edytuj ciek"
+msgstr "Edytuj ścieżkę"
msgid "Folder"
msgstr "Katalog"
msgid "This folder is currently in use - no changes are possible!"
-msgstr "Ten katalog jest w uyciu - zmiany nie s moliwe!"
+msgstr "Ten katalog jest w użyciu - zmiany nie są możliwe!"
#, c-format
msgid "Move entire folder containing %d recordings?"
-msgstr "Przenie cay katalog zawirajcy %d nagra?"
+msgstr "Przenieść cały katalog zawirający %d nagrań?"
msgid "Error while moving folder!"
-msgstr "Bd podczas przenoszenia katalogu!"
+msgstr "Błąd podczas przenoszenia katalogu!"
msgid "Edit recording"
msgstr "Edytuj nagranie"
msgid "This recording is currently in use - no changes are possible!"
-msgstr "To nagranie jest w uyciu - zmiany nie s moliwe!"
+msgstr "To nagranie jest w użyciu - zmiany nie są możliwe!"
msgid "Button$Cancel cutting"
msgstr "Anuluj przycinanie"
@@ -797,40 +799,40 @@ msgid "Button$Cut"
msgstr "Wytnij"
msgid "Button$Delete marks"
-msgstr "Usu zaznaczone"
+msgstr "Usuń zaznaczone"
msgid "Recording vanished!"
-msgstr "Nagranie znikno!"
+msgstr "Nagranie zniknęło!"
msgid "Edited version already exists - overwrite?"
-msgstr "Edytowana wersja ju istnieje - nadpisa?"
+msgstr "Edytowana wersja już istnieje - nadpisać?"
msgid "Error while queueing recording for cutting!"
-msgstr "Bd podczas zakolejkowania nagrania do przycinania!"
+msgstr "Błąd podczas zakolejkowania nagrania do przycinania!"
msgid "Rename recording to folder name?"
-msgstr "Zmieni nazw nagrania na nazw katalogu?"
+msgstr "Zmienić nazwę nagrania na nazwę katalogu?"
msgid "Delete editing marks for this recording?"
-msgstr "Usun zaznaczenia z edycji dla tego nagrania?"
+msgstr "Usunąć zaznaczenia z edycji dla tego nagrania?"
msgid "Error while deleting editing marks!"
-msgstr "Bd podczas usuwania zaznacze edycji!"
+msgstr "Błąd podczas usuwania zaznaczeń edycji!"
msgid "Error while changing priority/lifetime!"
-msgstr "Bd podczas zmiany priorytetu/czasu ycia!"
+msgstr "Błąd podczas zmiany priorytetu/czasu życia!"
msgid "Error while changing folder/name!"
-msgstr "Bd podczas zmiany katalogu/nazwy!"
+msgstr "Błąd podczas zmiany katalogu/nazwy!"
msgid "Recording info"
msgstr "Informacje o nagraniu"
msgid "Button$Play"
-msgstr "Odtwrz"
+msgstr "Odtwórz"
msgid "Button$Rewind"
-msgstr "Pocztek"
+msgstr "Początek"
msgid "Recordings"
msgstr "Nagrania"
@@ -839,40 +841,46 @@ msgid "Commands"
msgstr "Polecenia"
msgid "Delete recording?"
-msgstr "Usun nagranie?"
+msgstr "Usunąć nagranie?"
msgid "Recording is being edited - really delete?"
-msgstr "Nagranie jest edytowane - jeste pewien e chcesz je usun?"
+msgstr "Nagranie jest edytowane - jesteś pewien że chcesz je usunąć?"
msgid "Error while deleting recording!"
-msgstr "Bd podczas usuwania nagrania!"
+msgstr "Błąd podczas usuwania nagrania!"
msgid "Recording commands"
-msgstr "Polecenia nagra"
+msgstr "Polecenia nagrań"
msgid "never"
msgstr "nigdy"
msgid "skin dependent"
-msgstr "zal. od skrki"
+msgstr "zal. od skórki"
msgid "always"
msgstr "zawsze"
msgid "by name"
-msgstr ""
+msgstr "wg nazwy"
msgid "by time"
-msgstr ""
+msgstr "wg czasu"
+
+msgid "ascending"
+msgstr "rosnąco"
+
+msgid "descending"
+msgstr "malejąco"
msgid "OSD"
msgstr "OSD"
msgid "Setup.OSD$Language"
-msgstr "Jzyk"
+msgstr "Język"
msgid "Setup.OSD$Skin"
-msgstr "Skrka"
+msgstr "Skórka"
msgid "Setup.OSD$Theme"
msgstr "Motyw"
@@ -881,55 +889,55 @@ msgid "Setup.OSD$Left (%)"
msgstr "Od lewej (%)"
msgid "Setup.OSD$Top (%)"
-msgstr "Od gry (%)"
+msgstr "Od góry (%)"
msgid "Setup.OSD$Width (%)"
-msgstr "Szeroko (%)"
+msgstr "Szerokość (%)"
msgid "Setup.OSD$Height (%)"
-msgstr "Wysoko (%)"
+msgstr "Wysokość (%)"
msgid "Setup.OSD$Message time (s)"
-msgstr "Czas trwania wiadomoci (s)"
+msgstr "Czas trwania wiadomości (s)"
msgid "Setup.OSD$Use small font"
-msgstr "Uywaj maej czcionki"
+msgstr "Używaj małej czcionki"
msgid "Setup.OSD$Anti-alias"
-msgstr "Wygadzanie"
+msgstr "Wygładzanie"
msgid "Setup.OSD$Default font"
msgstr "Standardowa czcionka"
msgid "Setup.OSD$Small font"
-msgstr "Maa czcionka"
+msgstr "Mała czcionka"
msgid "Setup.OSD$Fixed font"
-msgstr "Staa czcionka"
+msgstr "Stała czcionka"
msgid "Setup.OSD$Default font size (%)"
-msgstr "Standardowa czcionka wyjciowa (%)"
+msgstr "Standardowa czcionka wyjściowa (%)"
msgid "Setup.OSD$Small font size (%)"
-msgstr "Maa czcionka wyjciowa (%)"
+msgstr "Mała czcionka wyjściowa (%)"
msgid "Setup.OSD$Fixed font size (%)"
-msgstr "Ustalona czcionka wyjciowa (%)"
+msgstr "Ustalona czcionka wyjściowa (%)"
msgid "Setup.OSD$Channel info position"
msgstr "Pozycja informacji o kanale"
msgid "bottom"
-msgstr "d"
+msgstr "dół"
msgid "top"
-msgstr "gra"
+msgstr "góra"
msgid "Setup.OSD$Channel info time (s)"
msgstr "Czas trwania informacji o kanale (s)"
msgid "Setup.OSD$Info on channel switch"
-msgstr "Informacja przy zmianie kanau"
+msgstr "Informacja przy zmianie kanału"
msgid "Setup.OSD$Timeout requested channel info"
msgstr "Czas oczekiwania na informacje"
@@ -938,13 +946,13 @@ msgid "Setup.OSD$Scroll pages"
msgstr "Przesuwaj stronami"
msgid "Setup.OSD$Scroll wraps"
-msgstr "Skrajne pozycje ssiaduj"
+msgstr "Skrajne pozycje sąsiadują"
msgid "Setup.OSD$Menu key closes"
msgstr "Przycisk Menu zamyka"
msgid "Setup.OSD$Recording directories"
-msgstr "Katalogi nagra"
+msgstr "Katalogi nagrań"
msgid "Setup.OSD$Folders in timer menu"
msgstr "Katalogi w menu timera"
@@ -953,7 +961,10 @@ msgid "Setup.OSD$Always sort folders first"
msgstr "Sortuj najpierw katalogi"
msgid "Setup.OSD$Default sort mode for recordings"
-msgstr ""
+msgstr "Domyślny tryb sortowania dla nagrań"
+
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr "Kierunek sortowania nagrań"
msgid "Setup.OSD$Number keys for characters"
msgstr "Klawisze numeryczne dla liter"
@@ -980,7 +991,7 @@ msgid "Setup.EPG$EPG scan timeout (h)"
msgstr "Czas skanowania EPG (h)"
msgid "Setup.EPG$EPG bugfix level"
-msgstr "Stopie poprawek bdw EPG"
+msgstr "Stopień poprawek błędów EPG"
msgid "Setup.EPG$EPG linger time (min)"
msgstr "Czas przechowywania EPG (min)"
@@ -989,15 +1000,15 @@ msgid "Setup.EPG$Set system time"
msgstr "Ustawiaj czas systemowy"
msgid "Setup.EPG$Use time from transponder"
-msgstr "Uyj czasu z transpondera"
+msgstr "Użyj czasu z transpondera"
#. TRANSLATORS: note the plural!
msgid "Setup.EPG$Preferred languages"
-msgstr "Preferowanych jzykw"
+msgstr "Preferowanych języków"
#. TRANSLATORS: note the singular!
msgid "Setup.EPG$Preferred language"
-msgstr "Preferowany jzyk"
+msgstr "Preferowany język"
msgid "pan&scan"
msgstr "pan&scan"
@@ -1021,7 +1032,7 @@ msgid "names and PIDs"
msgstr "nazwy i PID-y"
msgid "add new channels"
-msgstr "dodawaj nowe kanay"
+msgstr "dodawaj nowe kanały"
msgid "add new transponders"
msgstr "dodawaj nowe transpondery"
@@ -1030,7 +1041,7 @@ msgid "DVB"
msgstr "DVB"
msgid "Button$Audio"
-msgstr "Dwik"
+msgstr "Dźwięk"
msgid "Button$Subtitles"
msgstr "Napisy"
@@ -1039,92 +1050,92 @@ msgid "Setup.DVB$Primary DVB interface"
msgstr "Pierwszy interfejs DVB"
msgid "Setup.DVB$Standard compliance"
-msgstr "Zgodno ze standardem"
+msgstr "Zgodność ze standardem"
msgid "Setup.DVB$Video format"
msgstr "Format obrazu"
msgid "Setup.DVB$Video display format"
-msgstr "Format wywietlania obrazu"
+msgstr "Format wyświetlania obrazu"
msgid "Setup.DVB$Use Dolby Digital"
-msgstr "Uywaj Dolby Digital"
+msgstr "Używaj Dolby Digital"
msgid "Setup.DVB$Update channels"
-msgstr "Aktualizuj kanay"
+msgstr "Aktualizuj kanały"
msgid "Setup.DVB$Audio languages"
-msgstr "Jzykw dwiku"
+msgstr "Języków dźwięku"
msgid "Setup.DVB$Audio language"
-msgstr "Jzyk dwiku"
+msgstr "Język dźwięku"
msgid "Setup.DVB$Display subtitles"
-msgstr "Obraz napisw"
+msgstr "Obraz napisów"
msgid "Setup.DVB$Subtitle languages"
-msgstr "Jzyki napisu"
+msgstr "Języki napisu"
msgid "Setup.DVB$Subtitle language"
-msgstr "Jzyk napisu"
+msgstr "Język napisu"
msgid "Setup.DVB$Subtitle offset"
-msgstr "Zrwnoway napis"
+msgstr "Zrównoważyć napis"
msgid "Setup.DVB$Subtitle foreground transparency"
-msgstr "Przerocze podtytuw: Przd"
+msgstr "Przeźrocze podtytułów: Przód"
msgid "Setup.DVB$Subtitle background transparency"
-msgstr "Przerocze podtytuw: To"
+msgstr "Przeźrocze podtytułów: Tło"
msgid "LNB"
msgstr "LNB"
msgid "Setup.LNB$Use DiSEqC"
-msgstr "Uywaj DiSEqC"
+msgstr "Używaj DiSEqC"
msgid "Setup.LNB$SLOF (MHz)"
msgstr "SLOF (MHz)"
msgid "Setup.LNB$Low LNB frequency (MHz)"
-msgstr "Dolna czstotliwo LNB (MHz)"
+msgstr "Dolna częstotliwość LNB (MHz)"
msgid "Setup.LNB$High LNB frequency (MHz)"
-msgstr "Grna czstotliwo LNB (MHz)"
+msgstr "Górna częstotliwość LNB (MHz)"
#, c-format
msgid "Setup.LNB$Device %d connected to sat cable"
-msgstr "Urzdzenie %d podczone do kabla satelitarnego"
+msgstr "Urządzenie %d podłączone do kabla satelitarnego"
msgid "Setup.LNB$own"
-msgstr "Wasny"
+msgstr "Własny"
msgid "Setup.LNB$Use dish positioner"
-msgstr "Uywaj obrotnicy"
+msgstr "Używaj obrotnicy"
msgid "Setup.LNB$Site latitude (degrees)"
-msgstr "Szeroko geograficzna stopnie"
+msgstr "Szerokość geograficzna stopnie"
msgid "South"
-msgstr "Poudnie"
+msgstr "Południe"
msgid "North"
-msgstr "Pnoc"
+msgstr "Północ"
msgid "Setup.LNB$Site longitude (degrees)"
-msgstr "Dugo geograficzna (stopnie)"
+msgstr "Długość geograficzna (stopnie)"
msgid "West"
-msgstr "Zachd"
+msgstr "Zachód"
msgid "East"
-msgstr "Wschd"
+msgstr "Wschód"
msgid "Setup.LNB$Max. positioner swing (degrees)"
-msgstr "Max. obrt pozycjonera (stopnie)"
+msgstr "Max. obrót pozycjonera (stopnie)"
msgid "Setup.LNB$Positioner speed (degrees/s)"
-msgstr "Prdko pozycjonera (stopnie/s)"
+msgstr "Prędkość pozycjonera (stopnie/s)"
msgid "CAM reset"
msgstr "CAM zresetowany"
@@ -1137,16 +1148,16 @@ msgstr "CAM gotowy"
#. TRANSLATORS: note the leading blank!
msgid " (activating)"
-msgstr " (aktywuj)"
+msgstr " (aktywuję)"
msgid "@ device"
-msgstr ""
+msgstr "@ urządzenie"
msgid "CAM"
msgstr "CAM"
msgid "Button$Cancel activation"
-msgstr "Anuluj aktywacj"
+msgstr "Anuluj aktywację"
msgid "Button$Activate"
msgstr "Aktywuj"
@@ -1161,37 +1172,37 @@ msgid "Opening CAM menu..."
msgstr "Otwieram menu CAM..."
msgid "Can't open CAM menu!"
-msgstr "Nie mona otworzy menu CAM!"
+msgstr "Nie można otworzyć menu CAM!"
msgid "Can't activate CAM!"
-msgstr "Nie mona aktywowa CAM!"
+msgstr "Nie można aktywować CAM!"
msgid "CAM is in use - really reset?"
-msgstr "CAM jest w uyciu - naprawd zresetowa?"
+msgstr "CAM jest w użyciu - naprawdę zresetować?"
msgid "Can't reset CAM!"
-msgstr "Nie mona zresetowa CAM!"
+msgstr "Nie można zresetować CAM!"
msgid "no instant recording"
-msgstr ""
+msgstr "bez natychmiastowego nagrywania"
msgid "confirm instant recording"
-msgstr ""
+msgstr "potwierdź natychmiastowe nagrywanie"
msgid "record instantly"
-msgstr ""
+msgstr "nagraj natychmiast"
msgid "do not pause live video"
-msgstr "nie wstrzymuj transmisji na ywo"
+msgstr "nie wstrzymuj transmisji na żywo"
msgid "confirm pause live video"
-msgstr "potwierd wstrzymanie transmisji na ywo"
+msgstr "potwierdź wstrzymanie transmisji na żywo"
msgid "pause live video"
-msgstr "wstrzymaj transmisj na ywo"
+msgstr "wstrzymaj transmisję na żywo"
msgid "confirm"
-msgstr "potwierd"
+msgstr "potwierdź"
msgid "yes"
msgstr "tak"
@@ -1200,34 +1211,34 @@ msgid "Recording"
msgstr "Nagranie"
msgid "Setup.Recording$Margin at start (min)"
-msgstr "Margines na pocztku (min)"
+msgstr "Margines na początku (min)"
msgid "Setup.Recording$Margin at stop (min)"
-msgstr "Margines na kocu (min)"
+msgstr "Margines na końcu (min)"
msgid "Setup.Recording$Default priority"
-msgstr "Domylny priorytet"
+msgstr "Domyślny priorytet"
msgid "Setup.Recording$Default lifetime (d)"
-msgstr "Domylny czas ycia (d)"
+msgstr "Domyślny czas życia (d)"
msgid "Setup.Recording$Record key handling"
-msgstr ""
+msgstr "Obsługa klawisza nagrywania"
msgid "Setup.Recording$Pause key handling"
-msgstr "Obsuga klawisza pauzy"
+msgstr "Obsługa klawisza pauzy"
msgid "Setup.Recording$Pause priority"
msgstr "Priorytet pauzy"
msgid "Setup.Recording$Pause lifetime (d)"
-msgstr "Czas ycia pauzy (d)"
+msgstr "Czas życia pauzy (d)"
msgid "Setup.Recording$Use episode name"
-msgstr "Uywaj nazwy epizodu"
+msgstr "Używaj nazwy epizodu"
msgid "Setup.Recording$Use VPS"
-msgstr "Uywaj VPS"
+msgstr "Używaj VPS"
msgid "Setup.Recording$VPS margin (s)"
msgstr "Margines VPS (s)"
@@ -1251,22 +1262,22 @@ msgid "Setup.Recording$Split edited files"
msgstr "Dziel edytowane pliki"
msgid "Setup.Recording$Delete timeshift recording"
-msgstr "Usu nagranie timeshift"
+msgstr "Usuń nagranie timeshift"
msgid "Replay"
msgstr "Odtwarzanie"
msgid "Setup.Replay$Multi speed mode"
-msgstr "Tryb wieloprdkociowy"
+msgstr "Tryb wieloprędkościowy"
msgid "Setup.Replay$Show replay mode"
msgstr "Pokazuj tryb odtwarzania"
msgid "Setup.Replay$Show remaining time"
-msgstr "Poka pozostay czas"
+msgstr "Pokaż pozostały czas"
msgid "Setup.Replay$Progress display time (s)"
-msgstr "Wywietl czas odtwarzania (s)"
+msgstr "Wyświetl czas odtwarzania (s)"
msgid "Setup.Replay$Pause replay when setting mark"
msgstr "Wstrzymaj odtwarzanie podczas ustawiania zaznaczania"
@@ -1275,106 +1286,106 @@ msgid "Setup.Replay$Pause replay when jumping to a mark"
msgstr "Wstrzymaj odtwarzanie przy przeskoku do zaznaczenia"
msgid "Setup.Replay$Skip edited parts"
-msgstr "Przeskocz edytowan cz"
+msgstr "Przeskocz edytowaną część"
msgid "Setup.Replay$Pause replay at last mark"
msgstr "Wstrzymaj odtwarzanie przy ostatnim zaznaczeniu"
msgid "Setup.Replay$Initial duration for adaptive skipping (s)"
-msgstr "Pocztkowy czas trwania dla przeskoku adaptacyjnego (s)"
+msgstr "Początkowy czas trwania dla przeskoku adaptacyjnego (s)"
msgid "Setup.Replay$Reset timeout for adaptive skipping (s)"
msgstr "Czas oczekiwania na wyzerowanie dla przeskoku adaptacyjnego (s)"
msgid "Setup.Replay$Alternate behavior for adaptive skipping"
-msgstr "Zmie zachowanie dla przeskoku adaptacyjnego"
+msgstr "Zmień zachowanie dla przeskoku adaptacyjnego"
msgid "Setup.Replay$Use Prev/Next keys for adaptive skipping"
-msgstr "Uyj klawiszy Poprzedni/Nastpny dla przeskoku adaptacyjnego"
+msgstr "Użyj klawiszy Poprzedni/Następny dla przeskoku adaptacyjnego"
msgid "Setup.Replay$Skip distance with Green/Yellow keys (s)"
-msgstr "Pomi dystans klawiszami Zielony/ty (s)"
+msgstr "Pomiń dystans klawiszami Zielony/Żółty (s)"
msgid "Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"
-msgstr "Pomi dystans klawiszami Zielony/ty w powtrce (s)"
+msgstr "Pomiń dystans klawiszami Zielony/Żółty w powtórce (s)"
msgid "Setup.Replay$Resume ID"
msgstr "ID wznowienia"
msgid "any hosts"
-msgstr ""
+msgstr "każdy host"
msgid "only default host"
-msgstr ""
+msgstr "tylko domyślny host"
msgid "type"
-msgstr ""
+msgstr "typ"
msgid "full"
-msgstr ""
+msgstr "pełny"
msgid "Miscellaneous"
-msgstr "Rne"
+msgstr "Różne"
msgid "Setup.Miscellaneous$Min. event timeout (min)"
msgstr "Minimalny czas audycji (min)"
msgid "Setup.Miscellaneous$Min. user inactivity (min)"
-msgstr "Minimalny czas nieaktywnoci (min)"
+msgstr "Minimalny czas nieaktywności (min)"
msgid "Setup.Miscellaneous$SVDRP timeout (s)"
msgstr "Czas oczekiwania na SVDRP (s)"
msgid "Setup.Miscellaneous$SVDRP peering"
-msgstr ""
+msgstr "Dzielenie zadań przez SVDRP"
msgid "Setup.Miscellaneous$SVDRP host name"
-msgstr ""
+msgstr "Nazwa hosta SVDRP"
msgid "Setup.Miscellaneous$SVDRP default host"
-msgstr ""
+msgstr "Domyślny host SVDRP"
msgid "Setup.Miscellaneous$Zap timeout (s)"
msgstr "Czas oczekiwania na zap (s)"
msgid "Setup.Miscellaneous$Channel entry timeout (ms)"
-msgstr "Min czas wejcia do kanau"
+msgstr "Minął czas wejścia do kanału"
msgid "Setup.Miscellaneous$Remote control repeat delay (ms)"
-msgstr "Czas opnienia powtarzania dla pilota (ms)"
+msgstr "Czas opóźnienia powtarzania dla pilota (ms)"
msgid "Setup.Miscellaneous$Remote control repeat delta (ms)"
-msgstr "Delta opnienia powtarzania dla pilota (ms)"
+msgstr "Delta opóźnienia powtarzania dla pilota (ms)"
msgid "Setup.Miscellaneous$Initial channel"
-msgstr "Pocztkowy kana"
+msgstr "Początkowy kanał"
msgid "Setup.Miscellaneous$as before"
msgstr "jak ostatnio"
msgid "Setup.Miscellaneous$Initial volume"
-msgstr "Pocztkowa gono"
+msgstr "Początkowa głośność"
msgid "Setup.Miscellaneous$Volume steps"
-msgstr "Przeskok gonoci"
+msgstr "Przeskok głośności"
msgid "Setup.Miscellaneous$Volume linearize"
-msgstr "Linearyzacja gonoci"
+msgstr "Linearyzacja głośności"
msgid "Setup.Miscellaneous$Channels wrap"
-msgstr "Zawijanie kanaw"
+msgstr "Zawijanie kanałów"
msgid "Setup.Miscellaneous$Show channel names with source"
-msgstr "Poka nazwy kanaw ze rdem"
+msgstr "Pokaż nazwy kanałów ze źródłem"
msgid "Setup.Miscellaneous$Emergency exit"
-msgstr "Wyjcie awaryjne"
+msgstr "Wyjście awaryjne"
msgid "Plugins"
msgstr "Wtyczki"
msgid "This plugin has no setup parameters!"
-msgstr "Ta wtyczka nie ma adnych ustawie!"
+msgstr "Ta wtyczka nie ma żadnych ustawień!"
msgid "Setup"
msgstr "Ustawienia"
@@ -1383,15 +1394,12 @@ msgid "Restart"
msgstr "Restart"
msgid "Really restart?"
-msgstr "Na pewno zrestartowa?"
+msgstr "Na pewno zrestartować?"
#. TRANSLATORS: note the leading and trailing blanks!
msgid " Stop recording "
msgstr " Zatrzymaj nagrywanie "
-msgid "Schedule"
-msgstr "Program"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Zatrzymaj odtwarzanie"
@@ -1403,26 +1411,26 @@ msgid "Button$Stop"
msgstr "Zatrzymaj"
msgid "Button$Resume"
-msgstr "Wznw"
+msgstr "Wznów"
#. TRANSLATORS: note the leading blank!
msgid " Cancel editing"
-msgstr " Anuluj edycj"
+msgstr " Anuluj edycję"
msgid "Stop recording?"
-msgstr "Zatrzyma nagrywanie?"
+msgstr "Zatrzymać nagrywanie?"
msgid "Cancel editing?"
-msgstr "Anulowa monta?"
+msgstr "Anulować montaż?"
msgid "No audio available!"
-msgstr "Dwik nie jest dostpny!"
+msgstr "Dźwięk nie jest dostępny!"
msgid "No subtitles"
-msgstr "Brak napisw"
+msgstr "Brak napisów"
msgid "No subtitles available!"
-msgstr "Napisy nie s dostpne"
+msgstr "Napisy nie są dostępne"
msgid "Not enough disk space to start recording!"
msgstr "Brak miejsca na dysku do nagrywania!"
@@ -1431,35 +1439,35 @@ msgid "No free DVB device to record!"
msgstr "Brak wolnej karty DVB do nagrywania!"
msgid "Pausing live video..."
-msgstr "Pauzuj program na ywo..."
+msgstr "Pauzuję program na żywo..."
msgid "Delete timeshift recording?"
-msgstr "Czy usun nagranie timeshift?"
+msgstr "Czy usunąć nagranie timeshift?"
#. TRANSLATORS: note the trailing blank!
msgid "Jump: "
msgstr "Skok: "
msgid "No editing marks defined!"
-msgstr "Nie zdefiniowano znacznikw montau!"
+msgstr "Nie zdefiniowano znaczników montażu!"
msgid "No editing sequences defined!"
msgstr "Nie zdefiniowano sekwencji edycji!"
msgid "Can't start editing process!"
-msgstr "Nie mona uruchomi procesu edycji!"
+msgstr "Nie można uruchomić procesu edycji!"
msgid "Editing process started"
-msgstr "Proces edycji rozpoczty"
+msgstr "Proces edycji rozpoczęty"
msgid "Editing process already active!"
-msgstr "Proces edycji jest ju aktywny!"
+msgstr "Proces edycji jest już aktywny!"
msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
-msgstr " abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&"
+msgstr " aąbcćdeęfghijklłmnńoópqrsśtuvwxyzźż0123456789-.,#~\\^$[]|()*+?{}/:%@&"
msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
-msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"
+msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2ąć\tdef3ę\tghi4\tjkl5ł\tmno6ńó\tpqrs7ś\ttuv8\twxyz9źż"
msgid "Button$ABC/abc"
msgstr "ABC/abc"
@@ -1474,55 +1482,55 @@ msgid "Plugin"
msgstr "Wtyczka"
msgid "Up/Dn for new location - OK to move"
-msgstr "Do gry/Na d na now pozycj - Ok zatwierdza"
+msgstr "Do góry/Na dół na nową pozycję - Ok zatwierdza"
msgid "Channel locked (recording)!"
-msgstr "Kana zablokowany (trwa nagrywanie)!"
+msgstr "Kanał zablokowany (trwa nagrywanie)!"
msgid "Low disk space!"
-msgstr "Mao miejsca na dysku!"
+msgstr "Mało miejsca na dysku!"
msgid "Regenerating index file"
msgstr "Odtwarzanie pliku z indeksem"
msgid "Index file regeneration complete"
-msgstr "Odtwarzanie pliku z indeksem zakoczone"
+msgstr "Odtwarzanie pliku z indeksem zakończone"
msgid "Index file regeneration failed!"
msgstr "Odtwarzanie pliku z indeksem nieudane!"
msgid "Can't shutdown - option '-s' not given!"
-msgstr "Nie mona wyczy - nie podano opcji '-s'!"
+msgstr "Nie można wyłączyć - nie podano opcji '-s'!"
msgid "Editing - shut down anyway?"
-msgstr "Monta w trakcie - Wyczy mimo to?"
+msgstr "Montaż w trakcie - Wyłączyć mimo to?"
msgid "Recording - shut down anyway?"
-msgstr "Trwa nagrywanie - wyczy mimo to?"
+msgstr "Trwa nagrywanie - wyłączyć mimo to?"
#, c-format
msgid "Recording in %ld minutes, shut down anyway?"
-msgstr "Nagrywanie za %ld minut - wyczy mimo to?"
+msgstr "Nagrywanie za %ld minut - wyłączyć mimo to?"
msgid "shut down anyway?"
-msgstr "wyczy mimo to?"
+msgstr "wyłączyć mimo to?"
#, c-format
msgid "Plugin %s wakes up in %ld min, continue?"
-msgstr "Wtyczka %s obudzi si za %ld min, kontynuowa?"
+msgstr "Wtyczka %s obudzi się za %ld min, kontynuować?"
msgid "Editing - restart anyway?"
-msgstr "Monta w trakcie - Zrestartowa mimo to?"
+msgstr "Montaż w trakcie - Zrestartować mimo to?"
msgid "Recording - restart anyway?"
-msgstr "Trwa nagrywanie - zrestartowa mimo to?"
+msgstr "Trwa nagrywanie - zrestartować mimo to?"
msgid "restart anyway?"
-msgstr "zrestartowa mimo to?"
+msgstr "zrestartować mimo to?"
#. TRANSLATORS: note the trailing blank!
msgid "Volume "
-msgstr "Gono "
+msgstr "Głośność "
msgid "Classic VDR"
msgstr "Klasyczny VDR"
@@ -1531,16 +1539,16 @@ msgid "DISK"
msgstr "DYSK"
msgid "LOAD"
-msgstr "OBCIENIE"
+msgstr "OBCIĄŻENIE"
msgid "TIMERS"
msgstr "TIMERY"
msgid "DEVICES"
-msgstr "URZDZENIA"
+msgstr "URZĄDZENIA"
msgid "LIVE"
-msgstr "NA YWO"
+msgstr "NA ŻYWO"
msgid "PLAY"
msgstr "ODTWARZA"
@@ -1554,26 +1562,26 @@ msgstr "Panel ST:TNG"
#. TRANSLATORS: the first character of each weekday, beginning with monday
msgid "MTWTFSS"
-msgstr "PWCPSN"
+msgstr "PWŚCPSN"
#. TRANSLATORS: abbreviated weekdays, beginning with monday (must all be 3 letters!)
msgid "MonTueWedThuFriSatSun"
-msgstr "PonWtoroCzwPiSobNie"
+msgstr "PonWtoŚroCzwPiąSobNie"
msgid "Monday"
-msgstr "poniedziaek"
+msgstr "poniedziałek"
msgid "Tuesday"
msgstr "Wtorek"
msgid "Wednesday"
-msgstr "roda"
+msgstr "środa"
msgid "Thursday"
msgstr "Czwartek"
msgid "Friday"
-msgstr "Pitek"
+msgstr "Piątek"
msgid "Saturday"
msgstr "Sobota"
@@ -1582,38 +1590,38 @@ msgid "Sunday"
msgstr "Niedziela"
msgid "Upcoming recording!"
-msgstr "Wkrtce nagranie!"
+msgstr "Wkrótce nagranie!"
msgid "Pause live video?"
-msgstr "Zatrzyma transmisj na ywo?"
+msgstr "Zatrzymać transmisję na żywo?"
msgid "Start recording?"
-msgstr ""
+msgstr "Rozpocząć nagrywanie?"
msgid "Recording started"
-msgstr "Rozpoczto nagrywanie"
+msgstr "Rozpoczęto nagrywanie"
msgid "VDR will shut down later - press Power to force"
-msgstr "VDR zostanie wyczony pniej - Nacinij Wycz (Power) aby wymusi"
+msgstr "VDR zostanie wyłączony później - Naciśnij Wyłącz (Power) aby wymusić"
msgid "Press any key to cancel shutdown"
-msgstr "Nacinij dowolny klawisz aby nie wycza"
-
-msgid "Switching primary DVB..."
-msgstr "Prz곱czam na pierwszy interfejs DVB..."
+msgstr "Naciśnij dowolny klawisz aby nie wyłączać"
msgid "Editing process failed!"
-msgstr "Proces edycji nie powid si!"
+msgstr "Proces edycji nie powiódł się!"
msgid "Editing process finished"
-msgstr "Proces edycji zakoczony"
+msgstr "Proces edycji zakończony"
+
+msgid "Switching primary DVB..."
+msgstr "Przęłączam na pierwszy interfejs DVB..."
msgid "Press any key to cancel restart"
-msgstr "Nacinij dowolny klawisz aby nie zrestartowa"
+msgstr "Naciśnij dowolny klawisz aby nie zrestartować"
#, c-format
msgid "VDR will shut down in %s minutes"
-msgstr "VDR zostanie wyczony za %s minut"
+msgstr "VDR zostanie wyłączony za %s minut"
msgid "Disk"
msgstr "Dysk"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index ae37d28..a173746 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr "Editar pasta"
msgid "New folder"
msgstr "Nova pasta"
-msgid "Sub folder"
-msgstr "Subpasta"
-
msgid "Folder name already exists!"
msgstr "Nome da pasta j existe!"
@@ -730,6 +727,9 @@ msgstr "Programao"
msgid "Can't switch channel!"
msgstr "Impossvel mudar de canal!"
+msgid "Schedule"
+msgstr "Programao"
+
#, c-format
msgid "Schedule - %s"
msgstr "Programao - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -953,6 +959,9 @@ msgstr ""
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Teclas numricas para caracteres"
@@ -1387,9 +1396,6 @@ msgstr "Quer mesmo reiniciar?"
msgid " Stop recording "
msgstr " Parar gravao "
-msgid "Schedule"
-msgstr "Programao"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Parar reproduo"
@@ -1597,15 +1603,15 @@ msgstr "O VDR vai desligar mais tarde - pressione On/Off para forar"
msgid "Press any key to cancel shutdown"
msgstr "Pressione qualquer tecla para cancelar o encerramento"
-msgid "Switching primary DVB..."
-msgstr "A mudar placa de DVB primria..."
-
msgid "Editing process failed!"
msgstr "Processo de edio falhou!"
msgid "Editing process finished"
msgstr "Processo de edio terminou"
+msgid "Switching primary DVB..."
+msgstr "A mudar placa de DVB primria..."
+
msgid "Press any key to cancel restart"
msgstr "Pressione qualquer tecla para cancelar o reinicio"
diff --git a/po/ro_RO.po b/po/ro_RO.po
index 69364c1..ad40d83 100644
--- a/po/ro_RO.po
+++ b/po/ro_RO.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -613,9 +613,6 @@ msgstr "Editează directorul"
msgid "New folder"
msgstr "Director nou"
-msgid "Sub folder"
-msgstr "Sub-director"
-
msgid "Folder name already exists!"
msgstr "Un director cu acelaşi nume există!"
@@ -731,6 +728,9 @@ msgstr "Program"
msgid "Can't switch channel!"
msgstr "Nu pot comuta canalul!"
+msgid "Schedule"
+msgstr "Program (EPG)"
+
#, c-format
msgid "Schedule - %s"
msgstr "Programul canalului %s"
@@ -864,6 +864,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -954,6 +960,9 @@ msgstr "Sortează întotdeauna directoarele la început"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Caractere pe tastele numerice"
@@ -1388,9 +1397,6 @@ msgstr "Sigur repornesc?"
msgid " Stop recording "
msgstr " Opreşte înregistrarea "
-msgid "Schedule"
-msgstr "Program (EPG)"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Opreşte redarea"
@@ -1598,15 +1604,15 @@ msgstr "VDR se va închide mai târziu - apăsaţi 'Power' pentru a forţa"
msgid "Press any key to cancel shutdown"
msgstr "Apasă orice tastă pentru a anula închiderea"
-msgid "Switching primary DVB..."
-msgstr "Comut dispozitiv DVB primar..."
-
msgid "Editing process failed!"
msgstr "Editarea înregistrării a eşuat"
msgid "Editing process finished"
msgstr "Editarea înregistrării s-a încheiat"
+msgid "Switching primary DVB..."
+msgstr "Comut dispozitiv DVB primar..."
+
msgid "Press any key to cancel restart"
msgstr "Apăsaţi orice tastă pentru a anula repornirea"
diff --git a/po/ru_RU.po b/po/ru_RU.po
index f907ccc..3987381 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr "Редакция директории"
msgid "New folder"
msgstr "Новая директория"
-msgid "Sub folder"
-msgstr "поддиректория"
-
msgid "Folder name already exists!"
msgstr "Директория уже существует!"
@@ -730,6 +727,9 @@ msgstr "Программа"
msgid "Can't switch channel!"
msgstr "Невозможно переключить канал!"
+msgid "Schedule"
+msgstr "Телегид"
+
#, c-format
msgid "Schedule - %s"
msgstr "Программа - %s"
@@ -863,6 +863,12 @@ msgstr "по имени"
msgid "by time"
msgstr "по времени"
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "Меню"
@@ -953,6 +959,9 @@ msgstr "Директории всегда в первую очередь сор
msgid "Setup.OSD$Default sort mode for recordings"
msgstr "Сортировка по умолчанию"
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Количество кнопок для символов"
@@ -1387,9 +1396,6 @@ msgstr "Действительно перезапустить?"
msgid " Stop recording "
msgstr " Прекратить запись "
-msgid "Schedule"
-msgstr "Телегид"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Прекратить воспроизведение"
@@ -1597,15 +1603,15 @@ msgstr "VDR выключится позже - нажмите Питание дл
msgid "Press any key to cancel shutdown"
msgstr "Нажмите любую кнопку чтобы отменить выключение"
-msgid "Switching primary DVB..."
-msgstr "Смена основного DVB-устройства..."
-
msgid "Editing process failed!"
msgstr "Ошибка во время монтажа записи!"
msgid "Editing process finished"
msgstr "Монтаж окончен"
+msgid "Switching primary DVB..."
+msgstr "Смена основного DVB-устройства..."
+
msgid "Press any key to cancel restart"
msgstr "Нажмите любую кнопку для отмены перезагрузки"
diff --git a/po/sk_SK.po b/po/sk_SK.po
index d5a70e3..7124a30 100644
--- a/po/sk_SK.po
+++ b/po/sk_SK.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr "Upravi zloku"
msgid "New folder"
msgstr "Nov zloka"
-msgid "Sub folder"
-msgstr "Pod zloka"
-
msgid "Folder name already exists!"
msgstr "Nzov zloky u existuje!"
@@ -730,6 +727,9 @@ msgstr "TV program"
msgid "Can't switch channel!"
msgstr "Kanl nejde prepn!"
+msgid "Schedule"
+msgstr "TV program"
+
#, c-format
msgid "Schedule - %s"
msgstr "TV program - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD (Menu na obrazovke)"
@@ -953,6 +959,9 @@ msgstr "Zloky vdy najprv usporiada"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Psa znaky selnmi tlaidlami"
@@ -1387,9 +1396,6 @@ msgstr "Naozaj retartova?"
msgid " Stop recording "
msgstr " Zastavi nahrvanie "
-msgid "Schedule"
-msgstr "TV program"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Zastavi prehrvanie"
@@ -1597,15 +1603,15 @@ msgstr "VDR sa vypne neskorie. - Vyntenie potvrdte klvesou Power"
msgid "Press any key to cancel shutdown"
msgstr "Ktorkovek klvesa zru vypnutie"
-msgid "Switching primary DVB..."
-msgstr "Prepna hlavn DVB..."
-
msgid "Editing process failed!"
msgstr "Zostrihvanie zlyhalo!"
msgid "Editing process finished"
msgstr "Proces zostrihvania dokonen"
+msgid "Switching primary DVB..."
+msgstr "Prepna hlavn DVB..."
+
msgid "Press any key to cancel restart"
msgstr "ktorkovek klvesa zru retart"
diff --git a/po/sl_SI.po b/po/sl_SI.po
index d2e4630..66b7225 100644
--- a/po/sl_SI.po
+++ b/po/sl_SI.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr "Uredi direktorij"
msgid "New folder"
msgstr "Nov direktorij"
-msgid "Sub folder"
-msgstr "Pod direktorij"
-
msgid "Folder name already exists!"
msgstr "Ime direktorija e obstaja"
@@ -730,6 +727,9 @@ msgstr "Program"
msgid "Can't switch channel!"
msgstr "Ne morem preklopiti kanala!"
+msgid "Schedule"
+msgstr "Program"
+
#, c-format
msgid "Schedule - %s"
msgstr "Program - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -953,6 +959,9 @@ msgstr ""
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "tevilo tipk za znake"
@@ -1387,9 +1396,6 @@ msgstr "Zares ponoven zagon?"
msgid " Stop recording "
msgstr " Prekini snemanje "
-msgid "Schedule"
-msgstr "Program"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Prekini predvajanje"
@@ -1597,15 +1603,15 @@ msgstr "VDR se bo zaustavil kasneje - pritisni Power za takojen izklop"
msgid "Press any key to cancel shutdown"
msgstr "Pritisnite katerikoli gumb za preklic izklopa"
-msgid "Switching primary DVB..."
-msgstr "Preklapljanje primarne DVB naprave..."
-
msgid "Editing process failed!"
msgstr "Napaka pri procesu urejanja!"
msgid "Editing process finished"
msgstr "Proces urejanja je konan"
+msgid "Switching primary DVB..."
+msgstr "Preklapljanje primarne DVB naprave..."
+
msgid "Press any key to cancel restart"
msgstr "Pritisni katerokoli tipko za preklic ponovnega zagona"
diff --git a/po/sr_RS.po b/po/sr_RS.po
index 069c1ec..e880cad 100644
--- a/po/sr_RS.po
+++ b/po/sr_RS.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -612,9 +612,6 @@ msgstr "Izmeni direktorijum"
msgid "New folder"
msgstr "Novi direktorijum"
-msgid "Sub folder"
-msgstr "Poddirektorijum"
-
msgid "Folder name already exists!"
msgstr "Naziv direktorijuma ve postoji!"
@@ -730,6 +727,9 @@ msgstr "Raspored"
msgid "Can't switch channel!"
msgstr "Nemogue promeniti kanal!"
+msgid "Schedule"
+msgstr "Raspored"
+
#, c-format
msgid "Schedule - %s"
msgstr "Raspored - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -953,6 +959,9 @@ msgstr "Uvek sortiraj direktorijume prve"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Piite pomou numerikih dugmia (kao SMS)"
@@ -1387,9 +1396,6 @@ msgstr "Stvarno restartovati sistem?"
msgid " Stop recording "
msgstr " Zaustavi snimanje "
-msgid "Schedule"
-msgstr "Raspored"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Zaustavi reprodukciju"
@@ -1597,15 +1603,15 @@ msgstr "VDR e se iskljuiti kasnije - pritisnite dugme 'Power' za prisilno iskl
msgid "Press any key to cancel shutdown"
msgstr "Pritisnite bilo koje dugme da otkaete iskljuenje"
-msgid "Switching primary DVB..."
-msgstr "Promena primarnog DVB ureaja..."
-
msgid "Editing process failed!"
msgstr "Ureivanje neuspeeno!"
msgid "Editing process finished"
msgstr "Ureivanje zavreno"
+msgid "Switching primary DVB..."
+msgstr "Promena primarnog DVB ureaja..."
+
msgid "Press any key to cancel restart"
msgstr "Pritisnite bilo koje dugme da otkaete ponovno pokretanje"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index ded70cf..bd856a2 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -616,9 +616,6 @@ msgstr "Redigera mapp"
msgid "New folder"
msgstr "Ny mapp"
-msgid "Sub folder"
-msgstr "Undermapp"
-
msgid "Folder name already exists!"
msgstr "Mappnamnet finns redan!"
@@ -734,6 +731,9 @@ msgstr "Program"
msgid "Can't switch channel!"
msgstr "Omjligt att byta kanal!"
+msgid "Schedule"
+msgstr "Program"
+
#, c-format
msgid "Schedule - %s"
msgstr "Program - %s"
@@ -867,6 +867,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -957,6 +963,9 @@ msgstr "Sortera alltid mappar frst"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Anvnd sifferknappar fr bokstavsinmatning"
@@ -1391,9 +1400,6 @@ msgstr "Vill du verkligen starta om?"
msgid " Stop recording "
msgstr " Avsluta inspelning "
-msgid "Schedule"
-msgstr "Program"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Avsluta uppspelning"
@@ -1601,15 +1607,15 @@ msgstr "VDR kommer att stngas av senare - tryck p/av fr att stnga av nu"
msgid "Press any key to cancel shutdown"
msgstr "Tryck valfri knapp fr att terkalla avstngningen"
-msgid "Switching primary DVB..."
-msgstr "Byter primr DVB enhet..."
-
msgid "Editing process failed!"
msgstr "Redigeringsprocessen misslyckades"
msgid "Editing process finished"
msgstr "Redigering avslutad"
+msgid "Switching primary DVB..."
+msgstr "Byter primr DVB enhet..."
+
msgid "Press any key to cancel restart"
msgstr "Tryck valfri knapp fr att avbryta omstart"
diff --git a/po/tr_TR.po b/po/tr_TR.po
index 749ca47..c93c5e2 100644
--- a/po/tr_TR.po
+++ b/po/tr_TR.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\n"
"PO-Revision-Date: 2008-02-28 00:33+0100\n"
"Last-Translator: Oktay Yolgeen <oktay_73@yahoo.de>\n"
"Language-Team: Turkish <vdr@linuxtv.org>\n"
@@ -611,9 +611,6 @@ msgstr ""
msgid "New folder"
msgstr ""
-msgid "Sub folder"
-msgstr ""
-
msgid "Folder name already exists!"
msgstr ""
@@ -729,6 +726,9 @@ msgstr "Program"
msgid "Can't switch channel!"
msgstr "Kanala deitirelemiyor!"
+msgid "Schedule"
+msgstr "Program"
+
#, c-format
msgid "Schedule - %s"
msgstr "Program - %s"
@@ -862,6 +862,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "OSD"
@@ -952,6 +958,9 @@ msgstr ""
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr ""
@@ -1386,9 +1395,6 @@ msgstr "Gerekten yeniden balat?"
msgid " Stop recording "
msgstr " ekimi bitir "
-msgid "Schedule"
-msgstr "Program"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Gsterii bitir"
@@ -1596,15 +1602,15 @@ msgstr "VDR daha sonra kapanacak - zorlamak iin Kapat'a bas"
msgid "Press any key to cancel shutdown"
msgstr "Kapatmay iptal etmek iin herhangi bir tua bas"
-msgid "Switching primary DVB..."
-msgstr "Primer DVB arayz deitiriliyor..."
-
msgid "Editing process failed!"
msgstr "Kesim baarsz!"
msgid "Editing process finished"
msgstr "Kesim bitti"
+msgid "Switching primary DVB..."
+msgstr "Primer DVB arayz deitiriliyor..."
+
msgid "Press any key to cancel restart"
msgstr "Yeniden balatmay iptal etmek iin herhangi bir tua bas"
diff --git a/po/uk_UA.po b/po/uk_UA.po
index d9f15ca..51103f8 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\n"
"PO-Revision-Date: 2015-02-13 18:14+0100\n"
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
"Language-Team: Ukrainian <vdr@linuxtv.org>\n"
@@ -612,9 +612,6 @@ msgstr "Редагувати теку"
msgid "New folder"
msgstr "Нова тека"
-msgid "Sub folder"
-msgstr "Підтека"
-
msgid "Folder name already exists!"
msgstr "Назва теки вже існує!"
@@ -730,6 +727,9 @@ msgstr "Програма"
msgid "Can't switch channel!"
msgstr "Неможливо перемкнути канал!"
+msgid "Schedule"
+msgstr "Телегід"
+
#, c-format
msgid "Schedule - %s"
msgstr "Програма - %s"
@@ -863,6 +863,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "Меню"
@@ -953,6 +959,9 @@ msgstr "Завжди сортувати теки першими"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "Кількість клавіш для символів"
@@ -1387,9 +1396,6 @@ msgstr "Дійсно перезапустити?"
msgid " Stop recording "
msgstr " Зупинити запис "
-msgid "Schedule"
-msgstr "Телегід"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " Зупинити програвання"
@@ -1597,15 +1603,15 @@ msgstr "VDR виключиться ппізніше - натисніть Power
msgid "Press any key to cancel shutdown"
msgstr "Натисніть будь-яку кнопку для відміни виключення."
-msgid "Switching primary DVB..."
-msgstr "Зміна основного DVB-пристрою..."
-
msgid "Editing process failed!"
msgstr "Помилка під час монтажу запису!"
msgid "Editing process finished"
msgstr "Монтаж закінчено"
+msgid "Switching primary DVB..."
+msgstr "Зміна основного DVB-пристрою..."
+
msgid "Press any key to cancel restart"
msgstr "Натисніть будь-яку кнопку для відміни перезавантаження"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index dbeea3f..a3861e9 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 2.2.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
-"POT-Creation-Date: 2017-06-30 11:46+0200\n"
+"POT-Creation-Date: 2018-03-09 16:02+0100\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"
@@ -613,9 +613,6 @@ msgstr "编辑文件夹"
msgid "New folder"
msgstr "新文件夹"
-msgid "Sub folder"
-msgstr "子文件夹"
-
msgid "Folder name already exists!"
msgstr "文件夹名称已经存在!"
@@ -731,6 +728,9 @@ msgstr "任务列表"
msgid "Can't switch channel!"
msgstr "不能切换频道"
+msgid "Schedule"
+msgstr "任务列表"
+
#, c-format
msgid "Schedule - %s"
msgstr "任务列表 - %s"
@@ -864,6 +864,12 @@ msgstr ""
msgid "by time"
msgstr ""
+msgid "ascending"
+msgstr ""
+
+msgid "descending"
+msgstr ""
+
msgid "OSD"
msgstr "系统菜单设置"
@@ -954,6 +960,9 @@ msgstr "总是排序文件夹"
msgid "Setup.OSD$Default sort mode for recordings"
msgstr ""
+msgid "Setup.OSD$Sorting direction for recordings"
+msgstr ""
+
msgid "Setup.OSD$Number keys for characters"
msgstr "数字键的字符"
@@ -1388,9 +1397,6 @@ msgstr "是否重启?"
msgid " Stop recording "
msgstr " 是否停止录像? "
-msgid "Schedule"
-msgstr "任务列表"
-
#. TRANSLATORS: note the leading blank!
msgid " Stop replaying"
msgstr " 停止回放"
@@ -1598,15 +1604,15 @@ msgstr "VDR即将关闭 - 按电源强制关闭!"
msgid "Press any key to cancel shutdown"
msgstr "按下任何键即可取消关机"
-msgid "Switching primary DVB..."
-msgstr "切换主要的DVB设备."
-
msgid "Editing process failed!"
msgstr "编辑过程失败"
msgid "Editing process finished"
msgstr "编辑过程完毕"
+msgid "Switching primary DVB..."
+msgstr "切换主要的DVB设备."
+
msgid "Press any key to cancel restart"
msgstr "请按任意键重启"
diff --git a/recording.c b/recording.c
index ca0ec79..0cf265c 100644
--- a/recording.c
+++ b/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 4.10 2017/06/25 12:31:46 kls Exp $
+ * $Id: recording.c 4.22 2018/03/17 10:56:13 kls Exp $
*/
#include "recording.h"
@@ -57,6 +57,7 @@
#define MARKSFILESUFFIX "/marks"
#define SORTMODEFILE ".sort"
+#define TIMERRECFILE ".timer"
#define MINDISKSPACE 1024 // MB
@@ -117,7 +118,7 @@ void cRemoveDeletedRecordingsThread::Action(void)
r = DeletedRecordings->Next(r);
}
if (deleted) {
- const char *IgnoreFiles[] = { SORTMODEFILE, NULL };
+ const char *IgnoreFiles[] = { SORTMODEFILE, TIMERRECFILE, NULL };
cVideoDirectory::RemoveEmptyVideoDirectories(IgnoreFiles);
}
}
@@ -962,8 +963,8 @@ char *cRecording::StripEpisodeName(char *s, bool Strip)
// To have folders sorted before plain recordings, the '/' s1 points to
// is replaced by the character '1'. All other slashes will be replaced
// by '0' in SortName() (see below), which will result in the desired
- // sequence:
- *s1 = '1';
+ // sequence ('0' and '1' are reversed in case of rsdDescending):
+ *s1 = (Setup.RecSortingDirection == rsdAscending) ? '1' : '0';
if (Strip) {
s1++;
memmove(s1, s2, t - s2 + 1);
@@ -986,7 +987,7 @@ char *cRecording::SortName(void) const
char *s = strdup(FileName() + strlen(cVideoDirectory::Name()));
if (RecordingsSortMode != rsmName || Setup.AlwaysSortFoldersFirst)
s = StripEpisodeName(s, RecordingsSortMode != rsmName);
- strreplace(s, '/', '0'); // some locales ignore '/' when sorting
+ strreplace(s, '/', (Setup.RecSortingDirection == rsdAscending) ? '0' : '1'); // some locales ignore '/' when sorting
int l = strxfrm(NULL, s, 0) + 1;
*sb = MALLOC(char, l);
strxfrm(*sb, s, l);
@@ -1020,7 +1021,10 @@ int cRecording::GetResume(void) const
int cRecording::Compare(const cListObject &ListObject) const
{
cRecording *r = (cRecording *)&ListObject;
- return strcasecmp(SortName(), r->SortName());
+ if (Setup.RecSortingDirection == rsdAscending)
+ return strcasecmp(SortName(), r->SortName());
+ else
+ return strcasecmp(r->SortName(), SortName());
}
bool cRecording::IsInPath(const char *Path) const
@@ -1234,7 +1238,10 @@ bool cRecording::ChangeName(const char *NewName)
free(name);
name = strdup(NewName);
cString NewFileName = FileName();
- if (!(MakeDirs(NewFileName, true) && cVideoDirectory::MoveVideoFile(OldFileName, NewFileName))) {
+ bool Exists = access(NewFileName, F_OK) == 0;
+ if (Exists)
+ esyslog("ERROR: recording '%s' already exists", NewName);
+ if (Exists || !(MakeDirs(NewFileName, true) && cVideoDirectory::MoveVideoFile(OldFileName, NewFileName))) {
free(name);
name = strdup(OldName);
free(fileName);
@@ -1494,7 +1501,6 @@ void cRecordings::TouchUpdate(void)
TouchFile(UpdateFileName());
if (!needsUpdate)
lastUpdate = time(NULL); // make sure we don't trigger ourselves
- BroadcastSVDRPCommand("UPDR");
}
bool cRecordings::NeedsUpdate(void)
@@ -1679,7 +1685,6 @@ private:
public:
cDirCopier(const char *DirNameSrc, const char *DirNameDst);
virtual ~cDirCopier();
- void Stop(void);
bool Error(void) { return error; }
};
@@ -1694,7 +1699,7 @@ cDirCopier::cDirCopier(const char *DirNameSrc, const char *DirNameDst)
cDirCopier::~cDirCopier()
{
- Stop();
+ Cancel(3);
}
bool cDirCopier::Throttled(void)
@@ -1725,6 +1730,7 @@ void cDirCopier::Action(void)
int From = -1;
int To = -1;
size_t BufferSize = BUFSIZ;
+ uchar *Buffer = NULL;
while (Running()) {
// Suspend copying if we have severe throughput problems:
if (Throttled()) {
@@ -1734,8 +1740,11 @@ void cDirCopier::Action(void)
// Copy all files in the source directory to the destination directory:
if (e) {
// We're currently copying a file:
- uchar Buffer[BufferSize];
- size_t Read = safe_read(From, Buffer, sizeof(Buffer));
+ if (!Buffer) {
+ esyslog("ERROR: no buffer");
+ break;
+ }
+ size_t Read = safe_read(From, Buffer, BufferSize);
if (Read > 0) {
size_t Written = safe_write(To, Buffer, Read);
if (Written != Read) {
@@ -1784,7 +1793,14 @@ void cDirCopier::Action(void)
break;
}
dsyslog("copying file '%s' to '%s'", *FileNameSrc, *FileNameDst);
- BufferSize = max(size_t(st.st_blksize * 10), size_t(BUFSIZ));
+ if (!Buffer) {
+ BufferSize = max(size_t(st.st_blksize * 10), size_t(BUFSIZ));
+ Buffer = MALLOC(uchar, BufferSize);
+ if (!Buffer) {
+ esyslog("ERROR: out of memory");
+ break;
+ }
+ }
if (access(FileNameDst, F_OK) == 0) {
esyslog("ERROR: destination file '%s' already exists", *FileNameDst);
break;
@@ -1801,14 +1817,16 @@ void cDirCopier::Action(void)
}
else {
// We're done:
+ free(Buffer);
dsyslog("done copying directory '%s' to '%s'", *dirNameSrc, *dirNameDst);
error = false;
return;
}
}
+ free(Buffer);
close(From); // just to be absolutely sure
close(To);
- esyslog("ERROR: copying directory '%s' to '%s' ended prematurely", *dirNameSrc, *dirNameDst);
+ isyslog("copying directory '%s' to '%s' ended prematurely", *dirNameSrc, *dirNameDst);
}
else
esyslog("ERROR: can't open '%s'", *dirNameSrc);
@@ -1817,17 +1835,6 @@ void cDirCopier::Action(void)
esyslog("ERROR: can't access '%s'", *dirNameDst);
}
-void cDirCopier::Stop(void)
-{
- Cancel(3);
- if (error) {
- cVideoDirectory::RemoveVideoFile(dirNameDst);
- LOCK_RECORDINGS_WRITE;
- Recordings->AddByName(dirNameSrc);
- Recordings->DelByName(dirNameDst);
- }
-}
-
// --- cRecordingsHandlerEntry -----------------------------------------------
class cRecordingsHandlerEntry : public cListObject {
@@ -1837,14 +1844,18 @@ private:
cString fileNameDst;
cCutter *cutter;
cDirCopier *copier;
+ bool error;
void ClearPending(void) { usage &= ~ruPending; }
public:
cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst);
~cRecordingsHandlerEntry();
int Usage(const char *FileName = NULL) const;
+ bool Error(void) const { return error; }
+ void SetCanceled(void) { usage |= ruCanceled; }
const char *FileNameSrc(void) const { return fileNameSrc; }
const char *FileNameDst(void) const { return fileNameDst; }
- bool Active(bool &Error);
+ bool Active(cRecordings *Recordings);
+ void Cleanup(cRecordings *Recordings);
};
cRecordingsHandlerEntry::cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst)
@@ -1854,6 +1865,7 @@ cRecordingsHandlerEntry::cRecordingsHandlerEntry(int Usage, const char *FileName
fileNameDst = FileNameDst;
cutter = NULL;
copier = NULL;
+ error = false;
}
cRecordingsHandlerEntry::~cRecordingsHandlerEntry()
@@ -1874,22 +1886,22 @@ int cRecordingsHandlerEntry::Usage(const char *FileName) const
return u;
}
-bool cRecordingsHandlerEntry::Active(bool &Error)
+bool cRecordingsHandlerEntry::Active(cRecordings *Recordings)
{
- bool CopierFinishedOk = false;
+ if ((usage & ruCanceled) != 0)
+ return false;
// First test whether there is an ongoing operation:
if (cutter) {
if (cutter->Active())
return true;
- Error |= cutter->Error();
+ error = cutter->Error();
delete cutter;
cutter = NULL;
}
else if (copier) {
if (copier->Active())
return true;
- Error |= copier->Error();
- CopierFinishedOk = !copier->Error();
+ error = copier->Error();
delete copier;
copier = NULL;
}
@@ -1898,28 +1910,52 @@ bool cRecordingsHandlerEntry::Active(bool &Error)
if ((Usage() & ruCut) != 0) {
cutter = new cCutter(FileNameSrc());
cutter->Start();
+ Recordings->AddByName(FileNameDst(), false);
}
else if ((Usage() & (ruMove | ruCopy)) != 0) {
copier = new cDirCopier(FileNameSrc(), FileNameDst());
copier->Start();
}
ClearPending();
- LOCK_RECORDINGS_WRITE; // to trigger a state change
+ Recordings->SetModified(); // to trigger a state change
return true;
}
- // Clean up:
- if (CopierFinishedOk && (Usage() & ruMove) != 0) {
+ // We're done:
+ if (!error && (usage & ruMove) != 0) {
cRecording Recording(FileNameSrc());
- if (Recording.Delete()) {
- LOCK_RECORDINGS_WRITE;
+ if (Recording.Delete())
Recordings->DelByName(Recording.FileName());
- }
}
- LOCK_RECORDINGS_WRITE; // to trigger a state change
+ Recordings->SetModified(); // to trigger a state change
Recordings->TouchUpdate();
return false;
}
+void cRecordingsHandlerEntry::Cleanup(cRecordings *Recordings)
+{
+ if ((usage & ruCut)) { // this was a cut operation...
+ if (cutter) { // ...which had not yet ended
+ delete cutter;
+ cutter = NULL;
+ cVideoDirectory::RemoveVideoFile(fileNameDst);
+ Recordings->DelByName(fileNameDst);
+ }
+ }
+ if ((usage & (ruMove | ruCopy)) // this was a move/copy operation...
+ && ((usage & ruPending) // ...which had not yet started...
+ || copier // ...or not yet finished...
+ || error)) { // ...or finished with error
+ if (copier) {
+ delete copier;
+ copier = NULL;
+ }
+ cVideoDirectory::RemoveVideoFile(fileNameDst);
+ if ((usage & ruMove) != 0)
+ Recordings->AddByName(fileNameSrc);
+ Recordings->DelByName(fileNameDst);
+ }
+}
+
// --- cRecordingsHandler ----------------------------------------------------
cRecordingsHandler RecordingsHandler;
@@ -1941,10 +1977,15 @@ void cRecordingsHandler::Action(void)
while (Running()) {
bool Sleep = false;
{
+ LOCK_RECORDINGS_WRITE;
+ Recordings->SetExplicitModify();
cMutexLock MutexLock(&mutex);
if (cRecordingsHandlerEntry *r = operations.First()) {
- if (!r->Active(error))
+ if (!r->Active(Recordings)) {
+ error |= r->Error();
+ r->Cleanup(Recordings);
operations.Del(r);
+ }
else
Sleep = true;
}
@@ -1960,6 +2001,8 @@ cRecordingsHandlerEntry *cRecordingsHandler::Get(const char *FileName)
{
if (FileName && *FileName) {
for (cRecordingsHandlerEntry *r = operations.First(); r; r = operations.Next(r)) {
+ if ((r->Usage() & ruCanceled) != 0)
+ continue;
if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0)
return r;
}
@@ -2002,16 +2045,14 @@ void cRecordingsHandler::Del(const char *FileName)
{
cMutexLock MutexLock(&mutex);
if (cRecordingsHandlerEntry *r = Get(FileName))
- operations.Del(r);
+ r->SetCanceled();
}
void cRecordingsHandler::DelAll(void)
{
- {
- cMutexLock MutexLock(&mutex);
- operations.Clear();
- }
- Cancel(3);
+ cMutexLock MutexLock(&mutex);
+ for (cRecordingsHandlerEntry *r = operations.First(); r; r = operations.Next(r))
+ r->SetCanceled();
}
int cRecordingsHandler::GetUsage(const char *FileName)
@@ -2149,8 +2190,8 @@ void cMarks::Align(void)
cIndexFile IndexFile(recordingFileName, false, isPesRecording);
for (cMark *m = First(); m; m = Next(m)) {
int p = IndexFile.GetClosestIFrame(m->Position());
- if (int d = m->Position() - p) {
- isyslog("aligned editing mark %s to %s (off by %d frame%s)", *IndexToHMSF(m->Position(), true, framesPerSecond), *IndexToHMSF(p, true, framesPerSecond), d, abs(d) > 1 ? "s" : "");
+ if (m->Position() - p) {
+ //isyslog("aligned editing mark %s to %s (off by %d frame%s)", *IndexToHMSF(m->Position(), true, framesPerSecond), *IndexToHMSF(p, true, framesPerSecond), m->Position() - p, abs(m->Position() - p) > 1 ? "s" : "");
m->SetPosition(p);
}
}
@@ -3081,3 +3122,38 @@ void IncRecordingsSortMode(const char *Directory)
RecordingsSortMode = eRecordingsSortMode(0);
SetRecordingsSortMode(Directory, RecordingsSortMode);
}
+
+// --- Recording Timer Indicator ---------------------------------------------
+
+void SetRecordingTimerId(const char *Directory, const char *TimerId)
+{
+ cString FileName = AddDirectory(Directory, TIMERRECFILE);
+ if (TimerId) {
+ dsyslog("writing timer id '%s' to %s", TimerId, *FileName);
+ if (FILE *f = fopen(FileName, "w")) {
+ fprintf(f, "%s\n", TimerId);
+ fclose(f);
+ }
+ else
+ LOG_ERROR_STR(*FileName);
+ }
+ else {
+ dsyslog("removing %s", *FileName);
+ unlink(FileName);
+ }
+}
+
+cString GetRecordingTimerId(const char *Directory)
+{
+ cString FileName = AddDirectory(Directory, TIMERRECFILE);
+ const char *Id = NULL;
+ if (FILE *f = fopen(FileName, "r")) {
+ char buf[HOST_NAME_MAX + 10]; // +10 for numeric timer id and '@'
+ if (fgets(buf, sizeof(buf), f)) {
+ stripspace(buf);
+ Id = buf;
+ }
+ fclose(f);
+ }
+ return Id;
+}
diff --git a/recording.h b/recording.h
index 200af45..2e2a305 100644
--- a/recording.h
+++ b/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 4.5 2017/04/03 13:31:16 kls Exp $
+ * $Id: recording.h 4.8 2018/02/12 12:28:24 kls Exp $
*/
#ifndef __RECORDING_H
@@ -38,6 +38,7 @@ enum eRecordingUsage {
ruDst = 0x0040, // the recording is the destination of a cut, move or copy process
//
ruPending = 0x0080, // the recording is pending a cut, move or copy process
+ ruCanceled = 0x8000, // the operation has been canceled, waiting for cleanup
};
void RemoveDeletedRecordings(void);
@@ -526,6 +527,7 @@ bool GenerateIndex(const char *FileName, bool Update = false);
///< complete, and will be updated if it isn't. Otherwise an existing index
///< file will be removed before a new one is generated.
+enum eRecordingsSortDir { rsdAscending, rsdDescending };
enum eRecordingsSortMode { rsmName, rsmTime };
extern eRecordingsSortMode RecordingsSortMode;
bool HasRecordingsSortMode(const char *Directory);
@@ -533,4 +535,7 @@ void GetRecordingsSortMode(const char *Directory);
void SetRecordingsSortMode(const char *Directory, eRecordingsSortMode SortMode);
void IncRecordingsSortMode(const char *Directory);
+void SetRecordingTimerId(const char *Directory, const char *TimerId);
+cString GetRecordingTimerId(const char *Directory);
+
#endif //__RECORDING_H
diff --git a/skinlcars.c b/skinlcars.c
index a7c28f1..aedca5d 100644
--- a/skinlcars.c
+++ b/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 4.5 2017/06/23 15:52:03 kls Exp $
+ * $Id: skinlcars.c 4.6 2017/11/08 10:10:30 kls Exp $
*/
// "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures,
@@ -715,6 +715,7 @@ private:
int lastLiveIndicatorY;
bool lastLiveIndicatorTransferring;
const cChannel *lastChannel;
+ cString lastChannelName;
const cEvent *lastEvent;
const cRecording *lastRecording;
cString lastHeader;
@@ -1426,12 +1427,13 @@ void cSkinLCARSDisplayMenu::DrawLive(const cChannel *Channel)
}
if (!Channel)
return;
- if (initial || Channel != lastChannel) {
+ if (initial || Channel != lastChannel || strcmp(Channel->Name(), lastChannelName)) {
osd->DrawText(xa00, yt00, itoa(Channel->Number()), Theme.Color(clrChannelFrameFg), Theme.Color(clrChannelFrameBg), tallFont, xa02 - xa00, yt02 - yt00, taTop | taRight | taBorder);
osd->DrawText(xa03, yt00, Channel->Name(), Theme.Color(clrChannelName), Theme.Color(clrBackground), tallFont, xd00 - xa03, yd01 - yd00, taTop | taLeft);
int x = xa00 + (yc03 - yc02); // compensate for the arc
osd->DrawText(x, yc00, cSource::ToString(Channel->Source()), Theme.Color(clrChannelFrameFg), Theme.Color(clrChannelFrameBg), cFont::GetFont(fontOsd), xa02 - x, yc03 - yc00, taTop | taRight | taBorder);
lastChannel = Channel;
+ lastChannelName = Channel->Name();
DrawSeen(0, 0);
}
// The current programme:
diff --git a/skins.h b/skins.h
index 81d68ff..bd8e15d 100644
--- a/skins.h
+++ b/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 4.4 2017/06/25 10:02:09 kls Exp $
+ * $Id: skins.h 4.5 2017/11/02 15:04:56 kls Exp $
*/
#ifndef __SKINS_H
@@ -244,8 +244,10 @@ 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.
-#define DEPRECATED_SKIN_SETITEMEVENT
-#ifdef DEPRECATED_SKIN_SETITEMEVENT
+#ifndef DEPRECATED_SKIN_SETITEMEVENT
+#define DEPRECATED_SKIN_SETITEMEVENT 1
+#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
diff --git a/status.c b/status.c
index 846c10c..b358878 100644
--- a/status.c
+++ b/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.0 2014/01/25 10:47:39 kls Exp $
+ * $Id: status.c 4.1 2018/01/29 13:36:53 kls Exp $
*/
#include "status.h"
@@ -53,6 +53,12 @@ void cStatus::MsgReplaying(const cControl *Control, const char *Name, const char
sm->Replaying(Control, Name, FileName, On);
}
+void cStatus::MsgMarksModified(const cMarks* Marks)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->MarksModified(Marks);
+}
+
void cStatus::MsgSetVolume(int Volume, bool Absolute)
{
for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
diff --git a/status.h b/status.h
index c8a9e1f..ee8a1ac 100644
--- a/status.h
+++ b/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.3 2017/06/23 09:08:24 kls Exp $
+ * $Id: status.h 4.4 2018/01/29 13:42:17 kls Exp $
*/
#ifndef __STATUS_H
@@ -59,6 +59,11 @@ protected:
// a name, Name can be a string that identifies the player type (like, e.g., "DVD").
// The full file name of the recording is given in FileName, which may be NULL in case there is no
// actual file involved. If On is false, Name may be NULL.
+ virtual void MarksModified(const cMarks *Marks) {}
+ // If the editing marks of the recording that is currently being played
+ // are modified in any way, this function is called with the list of
+ // Marks. If Marks is NULL, the editing marks for the currently played
+ // recording have been deleted entirely.
virtual void SetVolume(int Volume, bool Absolute) {}
// The volume has been set to the given value, either
// absolutely or relative to the current volume.
@@ -103,6 +108,7 @@ public:
static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView);
static void MsgRecording(const cDevice *Device, const char *Name, const char *FileName, bool On);
static void MsgReplaying(const cControl *Control, const char *Name, const char *FileName, bool On);
+ static void MsgMarksModified(const cMarks* Marks);
static void MsgSetVolume(int Volume, bool Absolute);
static void MsgSetAudioTrack(int Index, const char * const *Tracks);
static void MsgSetAudioChannel(int AudioChannel);
diff --git a/svdrp.c b/svdrp.c
index 3769575..4f74c8f 100644
--- a/svdrp.c
+++ b/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 4.22 2017/06/30 09:49:39 kls Exp $
+ * $Id: svdrp.c 4.36 2018/03/18 10:43:53 kls Exp $
*/
#include "svdrp.h"
@@ -37,7 +37,6 @@
#include "recording.h"
#include "remote.h"
#include "skins.h"
-#include "thread.h"
#include "timers.h"
#include "videodir.h"
@@ -48,6 +47,13 @@ static bool DumpSVDRPDataTransfer = false;
static int SVDRPTcpPort = 0;
static int SVDRPUdpPort = 0;
+enum eSvdrpFetchFlags {
+ sffNone = 0b00000000,
+ sffConn = 0b00000001,
+ sffPing = 0b00000010,
+ sffTimers = 0b00000100,
+ };
+
// --- cIpAddress ------------------------------------------------------------
class cIpAddress {
@@ -98,7 +104,6 @@ private:
bool tcp;
int sock;
cIpAddress lastIpAddress;
- bool IsOwnInterface(sockaddr_in *Addr);
public:
cSocket(int Port, bool Tcp);
~cSocket();
@@ -107,7 +112,7 @@ public:
void Close(void);
int Port(void) const { return port; }
int Socket(void) const { return sock; }
- static bool SendDgram(const char *Dgram, int Port, const char *Address = NULL);
+ static bool SendDgram(const char *Dgram, int Port);
int Accept(void);
cString Discover(void);
const cIpAddress *LastIpAddress(void) const { return &lastIpAddress; }
@@ -125,30 +130,6 @@ cSocket::~cSocket()
Close();
}
-bool cSocket::IsOwnInterface(sockaddr_in *Addr)
-{
- ifaddrs *ifaddr;
- if (getifaddrs(&ifaddr) >= 0) {
- bool Own = false;
- for (ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr) {
- if (ifa->ifa_addr->sa_family == AF_INET) {
- sockaddr_in *addr = (sockaddr_in *)ifa->ifa_addr;
- if (addr->sin_addr.s_addr == Addr->sin_addr.s_addr) {
- Own = true;
- break;
- }
- }
- }
- }
- freeifaddrs(ifaddr);
- return Own;
- }
- else
- LOG_ERROR;
- return false;
-}
-
void cSocket::Close(void)
{
if (sock >= 0) {
@@ -160,6 +141,7 @@ void cSocket::Close(void)
bool cSocket::Listen(void)
{
if (sock < 0) {
+ isyslog("SVDRP %s opening port %d/%s", Setup.SVDRPHostName, port, tcp ? "tcp" : "udp");
// create socket:
sock = tcp ? socket(PF_INET, SOCK_STREAM, IPPROTO_IP) : socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
@@ -198,7 +180,7 @@ bool cSocket::Listen(void)
return false;
}
}
- isyslog("SVDRP listening on port %d/%s", port, tcp ? "tcp" : "udp");
+ isyslog("SVDRP %s listening on port %d/%s", Setup.SVDRPHostName, port, tcp ? "tcp" : "udp");
}
return true;
}
@@ -234,13 +216,14 @@ bool cSocket::Connect(const char *Address)
LOG_ERROR;
return false;
}
- isyslog("SVDRP > %s:%d server connection established", Address, port);
+ dbgsvdrp("> %s:%d server connection established\n", Address, port);
+ isyslog("SVDRP %s > %s:%d server connection established", Setup.SVDRPHostName, Address, port);
return true;
}
return false;
}
-bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address)
+bool cSocket::SendDgram(const char *Dgram, int Port)
{
// Create a socket:
int Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
@@ -248,23 +231,22 @@ bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address)
LOG_ERROR;
return false;
}
- if (!Address) {
- // Enable broadcast:
- int One = 1;
- if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &One, sizeof(One)) < 0) {
- LOG_ERROR;
- close(Socket);
- return false;
- }
+ // Enable broadcast:
+ int One = 1;
+ if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &One, sizeof(One)) < 0) {
+ LOG_ERROR;
+ close(Socket);
+ return false;
}
// Configure port and ip:
sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET;
- Addr.sin_addr.s_addr = Address ? inet_addr(Address) : htonl(INADDR_BROADCAST);
+ Addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
Addr.sin_port = htons(Port);
// Send datagram:
- dsyslog("SVDRP > %s:%d send dgram '%s'", inet_ntoa(Addr.sin_addr), Port, Dgram);
+ dbgsvdrp("> %s:%d %s\n", inet_ntoa(Addr.sin_addr), Port, Dgram);
+ dsyslog("SVDRP %s > %s:%d send dgram '%s'", Setup.SVDRPHostName, inet_ntoa(Addr.sin_addr), Port, Dgram);
int Length = strlen(Dgram);
int Sent = sendto(Socket, Dgram, Length, 0, (sockaddr *)&Addr, sizeof(Addr));
if (Sent < 0)
@@ -289,7 +271,8 @@ int cSocket::Accept(void)
NewSock = -1;
}
lastIpAddress.Set((sockaddr *)&Addr);
- isyslog("SVDRP < %s client connection %s", lastIpAddress.Connection(), Accepted ? "accepted" : "DENIED");
+ dbgsvdrp("< %s client connection %s\n", lastIpAddress.Connection(), Accepted ? "accepted" : "DENIED");
+ isyslog("SVDRP %s < %s client connection %s", Setup.SVDRPHostName, lastIpAddress.Connection(), Accepted ? "accepted" : "DENIED");
}
else if (FATALERRNO)
LOG_ERROR;
@@ -307,17 +290,18 @@ cString cSocket::Discover(void)
int NumBytes = recvfrom(sock, buf, sizeof(buf), 0, (sockaddr *)&Addr, &Size);
if (NumBytes >= 0) {
buf[NumBytes] = 0;
- if (!IsOwnInterface(&Addr)) {
- lastIpAddress.Set((sockaddr *)&Addr);
- if (!SVDRPhosts.Acceptable(Addr.sin_addr.s_addr)) {
- dsyslog("SVDRP < %s discovery ignored (%s)", lastIpAddress.Connection(), buf);
- return NULL;
- }
- if (!startswith(buf, "SVDRP:discover")) {
- dsyslog("SVDRP < %s discovery unrecognized (%s)", lastIpAddress.Connection(), buf);
- return NULL;
- }
- isyslog("SVDRP < %s discovery received (%s)", lastIpAddress.Connection(), buf);
+ lastIpAddress.Set((sockaddr *)&Addr);
+ if (!SVDRPhosts.Acceptable(Addr.sin_addr.s_addr)) {
+ dsyslog("SVDRP %s < %s discovery ignored (%s)", Setup.SVDRPHostName, lastIpAddress.Connection(), buf);
+ return NULL;
+ }
+ if (!startswith(buf, "SVDRP:discover")) {
+ dsyslog("SVDRP %s < %s discovery unrecognized (%s)", Setup.SVDRPHostName, lastIpAddress.Connection(), buf);
+ return NULL;
+ }
+ if (strcmp(strgetval(buf, "name", ':'), Setup.SVDRPHostName) != 0) { // ignore our own broadcast
+ dbgsvdrp("< %s discovery received (%s)\n", lastIpAddress.Connection(), buf);
+ isyslog("SVDRP %s < %s discovery received (%s)", Setup.SVDRPHostName, lastIpAddress.Connection(), buf);
return buf;
}
}
@@ -331,51 +315,60 @@ cString cSocket::Discover(void)
class cSVDRPClient {
private:
- cIpAddress ipAddress;
+ cIpAddress serverIpAddress;
cSocket socket;
cString serverName;
+ int length;
+ char *input;
int timeout;
cTimeMs pingTime;
cFile file;
int fetchFlags;
+ bool connected;
+ bool Send(const char *Command);
void Close(void);
public:
cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout);
~cSVDRPClient();
const char *ServerName(void) const { return serverName; }
- const char *Connection(void) const { return ipAddress.Connection(); }
+ const char *Connection(void) const { return serverIpAddress.Connection(); }
bool HasAddress(const char *Address, int Port) const;
- bool Send(const char *Command);
bool Process(cStringList *Response = NULL);
bool Execute(const char *Command, cStringList *Response = NULL);
- void SetFetchFlag(eSvdrpFetchFlags Flag);
- bool HasFetchFlag(eSvdrpFetchFlags Flag);
+ bool Connected(void) const { return connected; }
+ void SetFetchFlag(int Flag);
+ bool HasFetchFlag(int Flag);
+ bool GetRemoteTimers(cStringList &Response);
};
static cPoller SVDRPClientPoller;
cSVDRPClient::cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout)
-:ipAddress(Address, Port)
+:serverIpAddress(Address, Port)
,socket(Port, true)
{
serverName = ServerName;
+ length = BUFSIZ;
+ input = MALLOC(char, length);
timeout = Timeout * 1000 * 9 / 10; // ping after 90% of timeout
pingTime.Set(timeout);
- fetchFlags = sffTimers;
+ fetchFlags = sffNone;
+ connected = false;
if (socket.Connect(Address)) {
if (file.Open(socket.Socket())) {
SVDRPClientPoller.Add(file, false);
- dsyslog("SVDRP > %s client created for '%s'", ipAddress.Connection(), *serverName);
+ dsyslog("SVDRP %s > %s client created for '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
return;
}
}
- esyslog("SVDRP > %s ERROR: failed to create client for '%s'", ipAddress.Connection(), *serverName);
+ esyslog("SVDRP %s > %s ERROR: failed to create client for '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
}
cSVDRPClient::~cSVDRPClient()
{
Close();
- dsyslog("SVDRP > %s client destroyed for '%s'", ipAddress.Connection(), *serverName);
+ free(input);
+ dsyslog("SVDRP %s > %s client destroyed for '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
}
void cSVDRPClient::Close(void)
@@ -384,21 +377,18 @@ void cSVDRPClient::Close(void)
SVDRPClientPoller.Del(file, false);
file.Close();
socket.Close();
- LOCK_TIMERS_WRITE;
- if (Timers)
- Timers->DelRemoteTimers(serverName);
}
}
bool cSVDRPClient::HasAddress(const char *Address, int Port) const
{
- return strcmp(ipAddress.Address(), Address) == 0 && ipAddress.Port() == Port;
+ return strcmp(serverIpAddress.Address(), Address) == 0 && serverIpAddress.Port() == Port;
}
bool cSVDRPClient::Send(const char *Command)
{
pingTime.Set(timeout);
- dbgsvdrp("> %s: %s\n", *serverName, Command);
+ dbgsvdrp("> C %s: %s\n", *serverName, Command);
if (safe_write(file, Command, strlen(Command) + 1) < 0) {
LOG_ERROR;
return false;
@@ -409,7 +399,6 @@ bool cSVDRPClient::Send(const char *Command)
bool cSVDRPClient::Process(cStringList *Response)
{
if (file.IsOpen()) {
- char input[BUFSIZ];
int numChars = 0;
#define SVDRPResonseTimeout 5000 // ms
cTimeMs Timeout(SVDRPResonseTimeout);
@@ -424,12 +413,9 @@ bool cSVDRPClient::Process(cStringList *Response)
input[--numChars] = 0;
// make sure the string is terminated:
input[numChars] = 0;
- dbgsvdrp("< %s: %s\n", *serverName, input);
- if (Response) {
+ dbgsvdrp("< C %s: %s\n", *serverName, input);
+ if (Response)
Response->Append(strdup(input));
- if (numChars >= 4 && input[3] != '-') // no more lines will follow
- break;
- }
else {
switch (atoi(input)) {
case 220: if (numChars > 4) {
@@ -438,23 +424,35 @@ bool cSVDRPClient::Process(cStringList *Response)
*t = 0;
if (strcmp(n, serverName) != 0) {
serverName = n;
- dsyslog("SVDRP < %s remote server name is '%s'", ipAddress.Connection(), *serverName);
+ dsyslog("SVDRP %s < %s remote server name is '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
}
+ SetFetchFlag(sffConn | sffTimers);
+ connected = true;
}
}
break;
- case 221: dsyslog("SVDRP < %s remote server closed connection to '%s'", ipAddress.Connection(), *serverName);
+ case 221: dsyslog("SVDRP %s < %s remote server closed connection to '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
+ connected = false;
Close();
break;
}
}
+ if (numChars >= 4 && input[3] != '-') // no more lines will follow
+ break;
numChars = 0;
}
else {
- if (numChars >= int(sizeof(input))) {
- esyslog("SVDRP < %s ERROR: out of memory", ipAddress.Connection());
- Close();
- break;
+ if (numChars >= length - 1) {
+ int NewLength = length + BUFSIZ;
+ if (char *NewBuffer = (char *)realloc(input, NewLength)) {
+ length = NewLength;
+ input = NewBuffer;
+ }
+ else {
+ esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, serverIpAddress.Connection());
+ Close();
+ break;
+ }
}
input[numChars++] = c;
input[numChars] = 0;
@@ -462,20 +460,20 @@ bool cSVDRPClient::Process(cStringList *Response)
Timeout.Set(SVDRPResonseTimeout);
}
else if (r <= 0) {
- isyslog("SVDRP < %s lost connection to remote server '%s'", ipAddress.Connection(), *serverName);
+ isyslog("SVDRP %s < %s lost connection to remote server '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
Close();
return false;
}
}
- else if (!Response)
- break;
else if (Timeout.TimedOut()) {
- esyslog("SVDRP < %s timeout while waiting for response from '%s'", ipAddress.Connection(), *serverName);
+ esyslog("SVDRP %s < %s timeout while waiting for response from '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
return false;
}
+ else if (!Response && numChars == 0)
+ break; // we read all or nothing!
}
if (pingTime.TimedOut())
- Execute("PING");
+ SetFetchFlag(sffPing);
}
return file.IsOpen();
}
@@ -487,26 +485,114 @@ bool cSVDRPClient::Execute(const char *Command, cStringList *Response)
return Send(Command) && Process(Response);
}
-void cSVDRPClient::SetFetchFlag(eSvdrpFetchFlags Flags)
+void cSVDRPClient::SetFetchFlag(int Flags)
{
fetchFlags |= Flags;
}
-bool cSVDRPClient::HasFetchFlag(eSvdrpFetchFlags Flag)
+bool cSVDRPClient::HasFetchFlag(int Flag)
{
bool Result = (fetchFlags & Flag);
fetchFlags &= ~Flag;
return Result;
}
+bool cSVDRPClient::GetRemoteTimers(cStringList &Response)
+{
+ if (Execute("LSTT ID", &Response)) {
+ for (int i = 0; i < Response.Size(); i++) {
+ char *s = Response[i];
+ int Code = SVDRPCode(s);
+ if (Code == 250)
+ strshift(s, 4);
+ else {
+ if (Code != 550)
+ esyslog("ERROR: %s: %s", ServerName(), s);
+ return false;
+ }
+ }
+ Response.SortNumerically();
+ return true;
+ }
+ return false;
+}
+
+
+// --- cSVDRPServerParams ----------------------------------------------------
+
+class cSVDRPServerParams {
+private:
+ cString name;
+ int port;
+ cString vdrversion;
+ cString apiversion;
+ int timeout;
+ cString host;
+ cString error;
+public:
+ cSVDRPServerParams(const char *Params);
+ const char *Name(void) const { return name; }
+ const int Port(void) const { return port; }
+ const char *VdrVersion(void) const { return vdrversion; }
+ const char *ApiVersion(void) const { return apiversion; }
+ const int Timeout(void) const { return timeout; }
+ const char *Host(void) const { return host; }
+ bool Ok(void) const { return !*error; }
+ const char *Error(void) const { return error; }
+ };
+
+cSVDRPServerParams::cSVDRPServerParams(const char *Params)
+{
+ if (Params && *Params) {
+ name = strgetval(Params, "name", ':');
+ if (*name) {
+ cString p = strgetval(Params, "port", ':');
+ if (*p) {
+ port = atoi(p);
+ vdrversion = strgetval(Params, "vdrversion", ':');
+ if (*vdrversion) {
+ apiversion = strgetval(Params, "apiversion", ':');
+ if (*apiversion) {
+ cString t = strgetval(Params, "timeout", ':');
+ if (*t) {
+ timeout = atoi(t);
+ if (timeout > 10) { // don't let it get too small
+ host = strgetval(Params, "host", ':');
+ // no error if missing - this parameter is optional!
+ }
+ else
+ error = "invalid timeout";
+ }
+ else
+ error = "missing server timeout";
+ }
+ else
+ error = "missing server apiversion";
+ }
+ else
+ error = "missing server vdrversion";
+ }
+ else
+ error = "missing server port";
+ }
+ else
+ error = "missing server name";
+ }
+ else
+ error = "missing server parameters";
+}
+
// --- cSVDRPClientHandler ---------------------------------------------------
+cStateKey StateKeySVDRPRemoteTimersPoll(true);
+
class cSVDRPClientHandler : public cThread {
private:
cMutex mutex;
int tcpPort;
cSocket udpSocket;
cVector<cSVDRPClient *> clientConnections;
+ void SendDiscover(void);
void HandleClientConnection(void);
void ProcessConnections(void);
cSVDRPClient *GetClientForServer(const char *ServerName);
@@ -515,9 +601,11 @@ protected:
public:
cSVDRPClientHandler(int TcpPort, int UdpPort);
virtual ~cSVDRPClientHandler();
- void SendDiscover(const char *Address = NULL);
+ void Lock(void) { mutex.Lock(); }
+ void Unlock(void) { mutex.Unlock(); }
+ void AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress);
bool Execute(const char *ServerName, const char *Command, cStringList *Response = NULL);
- bool GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone);
+ bool GetServerNames(cStringList *ServerNames);
bool TriggerFetchingTimers(const char *ServerName);
};
@@ -546,64 +634,78 @@ cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName)
return NULL;
}
-void cSVDRPClientHandler::SendDiscover(const char *Address)
+void cSVDRPClientHandler::SendDiscover(void)
{
- cMutexLock MutexLock(&mutex);
cString Dgram = cString::sprintf("SVDRP:discover name:%s port:%d vdrversion:%d apiversion:%d timeout:%d%s", Setup.SVDRPHostName, tcpPort, VDRVERSNUM, APIVERSNUM, Setup.SVDRPTimeout, (Setup.SVDRPPeering == spmOnly && *Setup.SVDRPDefaultHost) ? *cString::sprintf(" host:%s", Setup.SVDRPDefaultHost) : "");
- udpSocket.SendDgram(Dgram, udpSocket.Port(), Address);
+ udpSocket.SendDgram(Dgram, udpSocket.Port());
}
void cSVDRPClientHandler::ProcessConnections(void)
{
- cMutexLock MutexLock(&mutex);
+ cString PollTimersCmd;
+ if (cTimers::GetTimersRead(StateKeySVDRPRemoteTimersPoll, 100)) {
+ PollTimersCmd = cString::sprintf("POLL %s TIMERS", Setup.SVDRPHostName);
+ StateKeySVDRPRemoteTimersPoll.Remove();
+ }
+ else if (StateKeySVDRPRemoteTimersPoll.TimedOut())
+ return; // try again next time
for (int i = 0; i < clientConnections.Size(); i++) {
- if (!clientConnections[i]->Process()) {
- delete clientConnections[i];
+ cSVDRPClient *Client = clientConnections[i];
+ if (Client->Process()) {
+ if (Client->HasFetchFlag(sffConn))
+ Client->Execute(cString::sprintf("CONN name:%s port:%d vdrversion:%d apiversion:%d timeout:%d", Setup.SVDRPHostName, SVDRPTcpPort, VDRVERSNUM, APIVERSNUM, Setup.SVDRPTimeout));
+ if (Client->HasFetchFlag(sffPing))
+ Client->Execute("PING");
+ if (Client->HasFetchFlag(sffTimers)) {
+ cStringList RemoteTimers;
+ if (Client->GetRemoteTimers(RemoteTimers)) {
+ if (cTimers *Timers = cTimers::GetTimersWrite(StateKeySVDRPRemoteTimersPoll, 100)) {
+ bool TimersModified = Timers->StoreRemoteTimers(Client->ServerName(), &RemoteTimers);
+ StateKeySVDRPRemoteTimersPoll.Remove(TimersModified);
+ }
+ else
+ Client->SetFetchFlag(sffTimers); // try again next time
+ }
+ }
+ if (*PollTimersCmd) {
+ if (!Client->Execute(PollTimersCmd))
+ esyslog("ERROR: can't send '%s' to '%s'", *PollTimersCmd, Client->ServerName());
+ }
+ }
+ else {
+ cTimers *Timers = cTimers::GetTimersWrite(StateKeySVDRPRemoteTimersPoll);
+ bool TimersModified = Timers->StoreRemoteTimers(Client->ServerName(), NULL);
+ StateKeySVDRPRemoteTimersPoll.Remove(TimersModified);
+ delete Client;
clientConnections.Remove(i);
i--;
}
}
}
+void cSVDRPClientHandler::AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress)
+{
+ cMutexLock MutexLock(&mutex);
+ for (int i = 0; i < clientConnections.Size(); i++) {
+ if (clientConnections[i]->HasAddress(IpAddress, ServerParams.Port()))
+ return;
+ }
+ if (Setup.SVDRPPeering == spmOnly && strcmp(ServerParams.Name(), Setup.SVDRPDefaultHost) != 0)
+ return; // we only want to peer with the default host, but this isn't the default host
+ if (ServerParams.Host() && strcmp(ServerParams.Host(), Setup.SVDRPHostName) != 0)
+ return; // the remote VDR requests a specific host, but it's not us
+ clientConnections.Append(new cSVDRPClient(IpAddress, ServerParams.Port(), ServerParams.Name(), ServerParams.Timeout()));
+}
+
void cSVDRPClientHandler::HandleClientConnection(void)
{
cString NewDiscover = udpSocket.Discover();
if (*NewDiscover) {
- cString p = strgetval(NewDiscover, "port", ':');
- if (*p) {
- int Port = atoi(p);
- for (int i = 0; i < clientConnections.Size(); i++) {
- if (clientConnections[i]->HasAddress(udpSocket.LastIpAddress()->Address(), Port)) {
- dsyslog("SVDRP < %s connection to '%s' confirmed", clientConnections[i]->Connection(), clientConnections[i]->ServerName());
- return;
- }
- }
- cString ServerName = strgetval(NewDiscover, "name", ':');
- if (*ServerName) {
- if (Setup.SVDRPPeering == spmOnly && strcmp(ServerName, Setup.SVDRPDefaultHost) != 0)
- return; // we only want to peer with the default host, but this isn't the default host
- cString HostName = strgetval(NewDiscover, "host", ':');
- if (*HostName && strcmp(HostName, Setup.SVDRPHostName) != 0)
- return; // the remote VDR requests a specific host, but it's not us
- cString t = strgetval(NewDiscover, "timeout", ':');
- if (*t) {
- int Timeout = atoi(t);
- if (Timeout > 10) { // don't let it get too small
- const char *Address = udpSocket.LastIpAddress()->Address();
- clientConnections.Append(new cSVDRPClient(Address, Port, ServerName, Timeout));
- SendDiscover(Address);
- }
- else
- esyslog("SVDRP < %s ERROR: invalid timeout (%d)", udpSocket.LastIpAddress()->Connection(), Timeout);
- }
- else
- esyslog("SVDRP < %s ERROR: missing timeout", udpSocket.LastIpAddress()->Connection());
- }
- else
- esyslog("SVDRP < %s ERROR: missing server name", udpSocket.LastIpAddress()->Connection());
- }
+ cSVDRPServerParams ServerParams(NewDiscover);
+ if (ServerParams.Ok())
+ AddClient(ServerParams, udpSocket.LastIpAddress()->Address());
else
- esyslog("SVDRP < %s ERROR: missing port number", udpSocket.LastIpAddress()->Connection());
+ esyslog("SVDRP %s < %s ERROR: %s", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection(), ServerParams.Error());
}
}
@@ -631,13 +733,13 @@ bool cSVDRPClientHandler::Execute(const char *ServerName, const char *Command, c
return false;
}
-bool cSVDRPClientHandler::GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag)
+bool cSVDRPClientHandler::GetServerNames(cStringList *ServerNames)
{
cMutexLock MutexLock(&mutex);
ServerNames->Clear();
for (int i = 0; i < clientConnections.Size(); i++) {
cSVDRPClient *Client = clientConnections[i];
- if (FetchFlag == sffNone || Client->HasFetchFlag(FetchFlag))
+ if (Client->Connected())
ServerNames->Append(strdup(Client->ServerName()));
}
return ServerNames->Size() > 0;
@@ -730,6 +832,10 @@ const char *HelpPages[] = {
" After a CLRE command, no further EPG processing is done for 10\n"
" seconds, so that data sent with subsequent PUTE commands doesn't\n"
" interfere with data from the broadcasters.",
+ "CONN name:<name> port:<port> vdrversion:<vdrversion> apiversion:<apiversion> timeout:<timeout>\n"
+ " Used by peer-to-peer connections between VDRs to tell the other VDR\n"
+ " to establish a connection to this VDR. The name is the SVDRP host name\n"
+ " of this VDR, which may differ from its DNS name.",
"DELC <number>\n"
" Delete channel.",
"DELR <id>\n"
@@ -771,6 +877,10 @@ const char *HelpPages[] = {
" separators. The channel number of a group separator is always 0.\n"
" With ':ids' the channel ids are listed following the channel numbers.\n"
" The special number 0 can be given to list the current channel.",
+ "LSTD\n"
+ " List all available devices. Each device is listed with its name and\n"
+ " whether it is currently the primary device ('P') or it implements a\n"
+ " decoder ('D') and can be used as output device.",
"LSTE [ <channel> ] [ now | next | at <time> ]\n"
" List EPG data. Without any parameters all data of all channels is\n"
" listed. If a channel is given (either by number or by channel ID),\n"
@@ -847,10 +957,15 @@ const char *HelpPages[] = {
" If 'help' is followed by a command, the detailed help for that command is\n"
" given. The keyword 'main' initiates a call to the main menu function of the\n"
" given plugin.\n",
- "POLL timers\n"
+ "POLL <name> timers\n"
" Used by peer-to-peer connections between VDRs to inform other machines\n"
" about changes to timers. The receiving VDR shall use LSTT to query the\n"
- " remote machine's timers and update its list of timers accordingly.\n",
+ " remote machine with the given name about its timers and update its list\n"
+ " of timers accordingly.\n",
+ "PRIM [ <number> ]\n"
+ " Make the device with the given number the primary device.\n"
+ " Without option it returns the currently active primary device in the same\n"
+ " format as used by the LSTD command.",
"PUTE [ file ]\n"
" Put data into the EPG list. The data entered has to strictly follow the\n"
" format defined in vdr(5) for the 'epg.data' file. A '.' on a line\n"
@@ -943,7 +1058,8 @@ static cString grabImageDir;
class cSVDRPServer {
private:
int socket;
- cString connection;
+ cIpAddress clientIpAddress;
+ cString clientName;
cFile file;
cPUTEhandler *PUTEhandler;
int numChars;
@@ -951,11 +1067,12 @@ private:
char *cmdLine;
time_t lastActivity;
void Close(bool SendReply = false, bool Timeout = false);
- bool Send(const char *s, int length = -1);
+ bool Send(const char *s);
void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
void PrintHelpTopics(const char **hp);
void CmdCHAN(const char *Option);
void CmdCLRE(const char *Option);
+ void CmdCONN(const char *Option);
void CmdDELC(const char *Option);
void CmdDELR(const char *Option);
void CmdDELT(const char *Option);
@@ -964,6 +1081,7 @@ private:
void CmdHELP(const char *Option);
void CmdHITK(const char *Option);
void CmdLSTC(const char *Option);
+ void CmdLSTD(const char *Option);
void CmdLSTE(const char *Option);
void CmdLSTR(const char *Option);
void CmdLSTT(const char *Option);
@@ -979,6 +1097,7 @@ private:
void CmdPLAY(const char *Option);
void CmdPLUG(const char *Option);
void CmdPOLL(const char *Option);
+ void CmdPRIM(const char *Option);
void CmdPUTE(const char *Option);
void CmdREMO(const char *Option);
void CmdSCAN(const char *Option);
@@ -988,18 +1107,20 @@ private:
void CmdVOLU(const char *Option);
void Execute(char *Cmd);
public:
- cSVDRPServer(int Socket, const char *Connection);
+ cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress);
~cSVDRPServer();
+ const char *ClientName(void) const { return clientName; }
bool HasConnection(void) { return file.IsOpen(); }
bool Process(void);
};
static cPoller SVDRPServerPoller;
-cSVDRPServer::cSVDRPServer(int Socket, const char *Connection)
+cSVDRPServer::cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress)
{
socket = Socket;
- connection = Connection;
+ clientIpAddress = *ClientIpAddress;
+ clientName = clientIpAddress.Connection(); // will be set to actual name by a CONN command
PUTEhandler = NULL;
numChars = 0;
length = BUFSIZ;
@@ -1010,14 +1131,14 @@ cSVDRPServer::cSVDRPServer(int Socket, const char *Connection)
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s; %s", Setup.SVDRPHostName, VDRVERSION, *TimeToString(now), cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8");
SVDRPServerPoller.Add(file, false);
}
- dsyslog("SVDRP < %s server created", *connection);
+ dsyslog("SVDRP %s > %s server created", Setup.SVDRPHostName, *clientName);
}
cSVDRPServer::~cSVDRPServer()
{
Close(true);
free(cmdLine);
- dsyslog("SVDRP < %s server destroyed", *connection);
+ dsyslog("SVDRP %s < %s server destroyed", Setup.SVDRPHostName, *clientName);
}
void cSVDRPServer::Close(bool SendReply, bool Timeout)
@@ -1026,7 +1147,7 @@ void cSVDRPServer::Close(bool SendReply, bool Timeout)
if (SendReply) {
Reply(221, "%s closing connection%s", Setup.SVDRPHostName, Timeout ? " (timeout)" : "");
}
- isyslog("SVDRP < %s connection closed", *connection);
+ isyslog("SVDRP %s < %s connection closed", Setup.SVDRPHostName, *clientName);
SVDRPServerPoller.Del(file, false);
file.Close();
DELETENULL(PUTEhandler);
@@ -1034,11 +1155,10 @@ void cSVDRPServer::Close(bool SendReply, bool Timeout)
close(socket);
}
-bool cSVDRPServer::Send(const char *s, int length)
+bool cSVDRPServer::Send(const char *s)
{
- if (length < 0)
- length = strlen(s);
- if (safe_write(file, s, length) < 0) {
+ dbgsvdrp("> S %s: %s", *clientName, s); // terminating newline is already in the string!
+ if (safe_write(file, s, strlen(s)) < 0) {
LOG_ERROR;
Close();
return false;
@@ -1050,26 +1170,33 @@ void cSVDRPServer::Reply(int Code, const char *fmt, ...)
{
if (file.IsOpen()) {
if (Code != 0) {
+ char *buffer = NULL;
va_list ap;
va_start(ap, fmt);
- cString buffer = cString::vsprintf(fmt, ap);
+ if (vasprintf(&buffer, fmt, ap) >= 0) {
+ char *s = buffer;
+ while (s && *s) {
+ char *n = strchr(s, '\n');
+ if (n)
+ *n = 0;
+ char cont = ' ';
+ if (Code < 0 || n && *(n + 1)) // trailing newlines don't count!
+ cont = '-';
+ if (!Send(cString::sprintf("%03d%c%s\r\n", abs(Code), cont, s)))
+ break;
+ s = n ? n + 1 : NULL;
+ }
+ }
+ else {
+ Reply(451, "Bad format - looks like a programming error!");
+ esyslog("SVDRP %s < %s bad format!", Setup.SVDRPHostName, *clientName);
+ }
va_end(ap);
- const char *s = buffer;
- while (s && *s) {
- const char *n = strchr(s, '\n');
- char cont = ' ';
- if (Code < 0 || n && *(n + 1)) // trailing newlines don't count!
- cont = '-';
- char number[16];
- sprintf(number, "%03d%c", abs(Code), cont);
- if (!(Send(number) && Send(s, n ? n - s : -1) && Send("\r\n")))
- break;
- s = n ? n + 1 : NULL;
- }
+ free(buffer);
}
else {
Reply(451, "Zero return code - looks like a programming error!");
- esyslog("SVDRP < %s zero return code!", *connection);
+ esyslog("SVDRP %s < %s zero return code!", Setup.SVDRPHostName, *clientName);
}
}
}
@@ -1225,6 +1352,26 @@ void cSVDRPServer::CmdCLRE(const char *Option)
}
}
+void cSVDRPServer::CmdCONN(const char *Option)
+{
+ if (*Option) {
+ if (SVDRPClientHandler) {
+ cSVDRPServerParams ServerParams(Option);
+ if (ServerParams.Ok()) {
+ clientName = ServerParams.Name();
+ Reply(250, "OK"); // must finish this transaction before creating the new client
+ SVDRPClientHandler->AddClient(ServerParams, clientIpAddress.Address());
+ }
+ else
+ Reply(501, "Error in server parameters: %s", ServerParams.Error());
+ }
+ else
+ Reply(451, "No SVDRP client handler");
+ }
+ else
+ Reply(501, "Missing server parameters");
+}
+
void cSVDRPServer::CmdDELC(const char *Option)
{
if (*Option) {
@@ -1254,7 +1401,7 @@ void cSVDRPServer::CmdDELC(const char *Option)
Channels->ReNumber();
Channels->SetModifiedByUser();
Channels->SetModified();
- isyslog("SVDRP < %s channel %s deleted", *connection, Option);
+ isyslog("SVDRP %s < %s deleted channel %s", Setup.SVDRPHostName, *clientName, Option);
if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
Channels->SwitchTo(CurrentChannel->Number());
@@ -1302,6 +1449,7 @@ void cSVDRPServer::CmdDELR(const char *Option)
if (Recording->Delete()) {
Recordings->DelByName(Recording->FileName());
Recordings->SetModified();
+ isyslog("SVDRP %s < %s deleted recording %s", Setup.SVDRPHostName, *clientName, Option);
Reply(250, "Recording \"%s\" deleted", Option);
}
else
@@ -1325,11 +1473,13 @@ void cSVDRPServer::CmdDELT(const char *Option)
LOCK_TIMERS_WRITE;
Timers->SetExplicitModify();
if (cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10))) {
- if (Timer->Recording())
+ if (Timer->Recording()) {
Timer->Skip();
+ cRecordControls::Process(Timers, time(NULL));
+ }
Timers->Del(Timer);
Timers->SetModified();
- isyslog("SVDRP < %s deleted timer %s", *connection, *Timer->ToDescr());
+ isyslog("SVDRP %s < %s deleted timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
Reply(250, "Timer \"%s\" deleted", Option);
}
else
@@ -1475,7 +1625,7 @@ void cSVDRPServer::CmdGRAB(const char *Option)
int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
if (fd >= 0) {
if (safe_write(fd, Image, ImageSize) == ImageSize) {
- dsyslog("SVDRP < %s grabbed image to %s", *connection, FileName);
+ dsyslog("SVDRP %s < %s grabbed image to %s", Setup.SVDRPHostName, *clientName, FileName);
Reply(250, "Grabbed image %s", Option);
}
else {
@@ -1620,6 +1770,18 @@ void cSVDRPServer::CmdLSTC(const char *Option)
Reply(550, "No channels defined");
}
+void cSVDRPServer::CmdLSTD(const char *Option)
+{
+ if (cDevice::NumDevices()) {
+ for (int i = 0; i < cDevice::NumDevices(); i++) {
+ if (const cDevice *d = cDevice::GetDevice(i))
+ Reply(d->DeviceNumber() + 1 == cDevice::NumDevices() ? 250 : -250, "%d [%s%s] %s", d->DeviceNumber() + 1, d->HasDecoder() ? "D" : "-", d->DeviceNumber() + 1 == Setup.PrimaryDVB ? "P" : "-", *d->DeviceName());
+ }
+ }
+ else
+ Reply(550, "No devices found");
+}
+
void cSVDRPServer::CmdLSTE(const char *Option)
{
LOCK_CHANNELS_READ;
@@ -1817,7 +1979,7 @@ void cSVDRPServer::CmdLSTT(const char *Option)
void cSVDRPServer::CmdMESG(const char *Option)
{
if (*Option) {
- isyslog("SVDRP < %s message '%s'", *connection, Option);
+ isyslog("SVDRP %s < %s message '%s'", Setup.SVDRPHostName, *clientName, Option);
Skins.QueueMessage(mtInfo, Option);
Reply(250, "Message queued");
}
@@ -1842,7 +2004,7 @@ void cSVDRPServer::CmdMODC(const char *Option)
Channels->ReNumber();
Channels->SetModifiedByUser();
Channels->SetModified();
- isyslog("SVDRP < %s modifed channel %d %s", *connection, Channel->Number(), *Channel->ToText());
+ isyslog("SVDRP %s < %s modifed channel %d %s", Setup.SVDRPHostName, *clientName, Channel->Number(), *Channel->ToText());
Reply(250, "%d %s", Channel->Number(), *Channel->ToText());
}
else
@@ -1882,7 +2044,7 @@ void cSVDRPServer::CmdMODT(const char *Option)
}
*Timer = t;
Timers->SetModified();
- isyslog("SVDRP < %s modified timer %s (%s)", *connection, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive");
+ isyslog("SVDRP %s < %s modified timer %s (%s)", Setup.SVDRPHostName, *clientName, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive");
Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true));
}
else
@@ -1926,7 +2088,7 @@ void cSVDRPServer::CmdMOVC(const char *Option)
else
cDevice::SetCurrentChannel(CurrentChannel->Number());
}
- isyslog("SVDRP < %s channel %d moved to %d", *connection, FromNumber, ToNumber);
+ isyslog("SVDRP %s < %s moved channel %d to %d", Setup.SVDRPHostName, *clientName, FromNumber, ToNumber);
Reply(250,"Channel \"%d\" moved to \"%d\"", From, To);
}
else
@@ -2006,7 +2168,7 @@ void cSVDRPServer::CmdNEWC(const char *Option)
Channels->ReNumber();
Channels->SetModifiedByUser();
Channels->SetModified();
- isyslog("SVDRP < %s new channel %d %s", *connection, channel->Number(), *channel->ToText());
+ isyslog("SVDRP %s < %s new channel %d %s", Setup.SVDRPHostName, *clientName, channel->Number(), *channel->ToText());
Reply(250, "%d %s", channel->Number(), *channel->ToText());
}
else
@@ -2027,7 +2189,7 @@ void cSVDRPServer::CmdNEWT(const char *Option)
LOCK_TIMERS_WRITE;
Timer->ClrFlags(tfRecording);
Timers->Add(Timer);
- isyslog("SVDRP < %s added timer %s", *connection, *Timer->ToDescr());
+ isyslog("SVDRP %s < %s added timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true));
return;
}
@@ -2216,6 +2378,32 @@ void cSVDRPServer::CmdPOLL(const char *Option)
Reply(501, "Missing parameters");
}
+void cSVDRPServer::CmdPRIM(const char *Option)
+{
+ int n = -1;
+ if (*Option) {
+ if (isnumber(Option)) {
+ int o = strtol(Option, NULL, 10);
+ if (o > 0 && o <= cDevice::NumDevices())
+ n = o;
+ else
+ Reply(501, "Invalid device number \"%s\"", Option);
+ }
+ else
+ Reply(501, "Invalid parameter \"%s\"", Option);
+ if (n >= 0) {
+ Setup.PrimaryDVB = n;
+ Reply(250, "Primary device set to %d", n);
+ }
+ }
+ else {
+ if (const cDevice *d = cDevice::PrimaryDevice())
+ Reply(250, "%d [%s%s] %s", d->DeviceNumber() + 1, d->HasDecoder() ? "D" : "-", d->DeviceNumber() + 1 == Setup.PrimaryDVB ? "P" : "-", *d->DeviceName());
+ else
+ Reply(501, "Failed to get primary device");
+ }
+}
+
void cSVDRPServer::CmdPUTE(const char *Option)
{
if (*Option) {
@@ -2290,11 +2478,11 @@ void cSVDRPServer::CmdUPDT(const char *Option)
t->Parse(Option);
delete Timer;
Timer = t;
- isyslog("SVDRP < %s updated timer %s", *connection, *Timer->ToDescr());
+ isyslog("SVDRP %s < %s updated timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
}
else {
Timers->Add(Timer);
- isyslog("SVDRP < %s added timer %s", *connection, *Timer->ToDescr());
+ isyslog("SVDRP %s < %s added timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
}
Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true));
return;
@@ -2360,6 +2548,7 @@ void cSVDRPServer::Execute(char *Cmd)
s = skipspace(s);
if (CMD("CHAN")) CmdCHAN(s);
else if (CMD("CLRE")) CmdCLRE(s);
+ else if (CMD("CONN")) CmdCONN(s);
else if (CMD("DELC")) CmdDELC(s);
else if (CMD("DELR")) CmdDELR(s);
else if (CMD("DELT")) CmdDELT(s);
@@ -2368,6 +2557,7 @@ void cSVDRPServer::Execute(char *Cmd)
else if (CMD("HELP")) CmdHELP(s);
else if (CMD("HITK")) CmdHITK(s);
else if (CMD("LSTC")) CmdLSTC(s);
+ else if (CMD("LSTD")) CmdLSTD(s);
else if (CMD("LSTE")) CmdLSTE(s);
else if (CMD("LSTR")) CmdLSTR(s);
else if (CMD("LSTT")) CmdLSTT(s);
@@ -2383,6 +2573,7 @@ void cSVDRPServer::Execute(char *Cmd)
else if (CMD("PLAY")) CmdPLAY(s);
else if (CMD("PLUG")) CmdPLUG(s);
else if (CMD("POLL")) CmdPOLL(s);
+ else if (CMD("PRIM")) CmdPRIM(s);
else if (CMD("PUTE")) CmdPUTE(s);
else if (CMD("REMO")) CmdREMO(s);
else if (CMD("SCAN")) CmdSCAN(s);
@@ -2408,6 +2599,7 @@ bool cSVDRPServer::Process(void)
// make sure the string is terminated:
cmdLine[numChars] = 0;
// showtime!
+ dbgsvdrp("< S %s: %s\n", *clientName, cmdLine);
Execute(cmdLine);
numChars = 0;
if (length > BUFSIZ) {
@@ -2436,7 +2628,7 @@ bool cSVDRPServer::Process(void)
cmdLine = NewBuffer;
}
else {
- esyslog("SVDRP < %s ERROR: out of memory", *connection);
+ esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, *clientName);
Close();
break;
}
@@ -2447,12 +2639,12 @@ bool cSVDRPServer::Process(void)
lastActivity = time(NULL);
}
else if (r <= 0) {
- isyslog("SVDRP < %s lost connection to client", *connection);
+ isyslog("SVDRP %s < %s lost connection to client", Setup.SVDRPHostName, *clientName);
Close();
}
}
if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) {
- isyslog("SVDRP < %s timeout on connection", *connection);
+ isyslog("SVDRP %s < %s timeout on connection", Setup.SVDRPHostName, *clientName);
Close(true, true);
}
}
@@ -2474,7 +2666,6 @@ void SetSVDRPGrabImageDir(const char *GrabImageDir)
class cSVDRPServerHandler : public cThread {
private:
- cMutex mutex;
bool ready;
cSocket tcpSocket;
cVector<cSVDRPServer *> serverConnections;
@@ -2513,7 +2704,6 @@ void cSVDRPServerHandler::WaitUntilReady(void)
void cSVDRPServerHandler::ProcessConnections(void)
{
- cMutexLock MutexLock(&mutex);
for (int i = 0; i < serverConnections.Size(); i++) {
if (!serverConnections[i]->Process()) {
delete serverConnections[i];
@@ -2527,7 +2717,7 @@ void cSVDRPServerHandler::HandleServerConnection(void)
{
int NewSocket = tcpSocket.Accept();
if (NewSocket >= 0)
- serverConnections.Append(new cSVDRPServer(NewSocket, tcpSocket.LastIpAddress()->Connection()));
+ serverConnections.Append(new cSVDRPServer(NewSocket, tcpSocket.LastIpAddress()));
}
void cSVDRPServerHandler::Action(void)
@@ -2537,7 +2727,6 @@ void cSVDRPServerHandler::Action(void)
ready = true;
while (Running()) {
SVDRPServerPoller.Poll(1000);
- cMutexLock MutexLock(&mutex);
HandleServerConnection();
ProcessConnections();
}
@@ -2550,60 +2739,53 @@ void cSVDRPServerHandler::Action(void)
static cMutex SVDRPHandlerMutex;
-void StartSVDRPServerHandler(void)
-{
- cMutexLock MutexLock(&SVDRPHandlerMutex);
- if (SVDRPTcpPort && !SVDRPServerHandler) {
- SVDRPServerHandler = new cSVDRPServerHandler(SVDRPTcpPort);
- SVDRPServerHandler->Start();
- SVDRPServerHandler->WaitUntilReady();
- }
-}
-
-void StartSVDRPClientHandler(void)
+void StartSVDRPHandler(void)
{
cMutexLock MutexLock(&SVDRPHandlerMutex);
- if (SVDRPTcpPort && SVDRPUdpPort && !SVDRPClientHandler) {
- SVDRPClientHandler = new cSVDRPClientHandler(SVDRPTcpPort, SVDRPUdpPort);
- SVDRPClientHandler->Start();
+ if (SVDRPTcpPort) {
+ if (!SVDRPServerHandler) {
+ SVDRPServerHandler = new cSVDRPServerHandler(SVDRPTcpPort);
+ SVDRPServerHandler->Start();
+ SVDRPServerHandler->WaitUntilReady();
+ }
+ if (Setup.SVDRPPeering && SVDRPUdpPort && !SVDRPClientHandler) {
+ SVDRPClientHandler = new cSVDRPClientHandler(SVDRPTcpPort, SVDRPUdpPort);
+ SVDRPClientHandler->Start();
+ }
}
}
-void StopSVDRPServerHandler(void)
-{
- cMutexLock MutexLock(&SVDRPHandlerMutex);
- delete SVDRPServerHandler;
- SVDRPServerHandler = NULL;
-}
-
-void StopSVDRPClientHandler(void)
+void StopSVDRPHandler(void)
{
cMutexLock MutexLock(&SVDRPHandlerMutex);
delete SVDRPClientHandler;
SVDRPClientHandler = NULL;
+ delete SVDRPServerHandler;
+ SVDRPServerHandler = NULL;
}
-void SendSVDRPDiscover(const char *Address)
-{
- cMutexLock MutexLock(&SVDRPHandlerMutex);
- if (SVDRPClientHandler)
- SVDRPClientHandler->SendDiscover(Address);
-}
-
-bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag)
+bool GetSVDRPServerNames(cStringList *ServerNames)
{
+ bool Result = false;
cMutexLock MutexLock(&SVDRPHandlerMutex);
- if (SVDRPClientHandler)
- return SVDRPClientHandler->GetServerNames(ServerNames, FetchFlag);
- return false;
+ if (SVDRPClientHandler) {
+ SVDRPClientHandler->Lock();
+ Result = SVDRPClientHandler->GetServerNames(ServerNames);
+ SVDRPClientHandler->Unlock();
+ }
+ return Result;
}
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response)
{
+ bool Result = false;
cMutexLock MutexLock(&SVDRPHandlerMutex);
- if (SVDRPClientHandler)
- return SVDRPClientHandler->Execute(ServerName, Command, Response);
- return false;
+ if (SVDRPClientHandler) {
+ SVDRPClientHandler->Lock();
+ Result = SVDRPClientHandler->Execute(ServerName, Command, Response);
+ SVDRPClientHandler->Unlock();
+ }
+ return Result;
}
void BroadcastSVDRPCommand(const char *Command)
@@ -2611,9 +2793,11 @@ void BroadcastSVDRPCommand(const char *Command)
cMutexLock MutexLock(&SVDRPHandlerMutex);
cStringList ServerNames;
if (SVDRPClientHandler) {
+ SVDRPClientHandler->Lock();
if (SVDRPClientHandler->GetServerNames(&ServerNames)) {
for (int i = 0; i < ServerNames.Size(); i++)
ExecSVDRPCommand(ServerNames[i], Command);
}
+ SVDRPClientHandler->Unlock();
}
}
diff --git a/svdrp.h b/svdrp.h
index e0d3616..5330b9a 100644
--- a/svdrp.h
+++ b/svdrp.h
@@ -4,12 +4,13 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: svdrp.h 4.7 2017/06/30 09:49:39 kls Exp $
+ * $Id: svdrp.h 4.11 2018/03/15 16:30:29 kls Exp $
*/
#ifndef __SVDRP_H
#define __SVDRP_H
+#include "thread.h"
#include "tools.h"
enum eSvdrpPeerModes {
@@ -18,25 +19,18 @@ enum eSvdrpPeerModes {
spmOnly = 2,
};
-enum eSvdrpFetchFlags {
- sffNone = 0b0000,
- sffTimers = 0b0001,
- };
+extern cStateKey StateKeySVDRPRemoteTimersPoll;
+ ///< Controls whether a change to the local list of timers needs to result in
+ ///< sending a POLL to the remote clients.
void SetSVDRPPorts(int TcpPort, int UdpPort);
void SetSVDRPGrabImageDir(const char *GrabImageDir);
-void StartSVDRPServerHandler(void);
-void StartSVDRPClientHandler(void);
-void StopSVDRPServerHandler(void);
-void StopSVDRPClientHandler(void);
-void SendSVDRPDiscover(const char *Address = NULL);
-bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag = sffNone);
+void StartSVDRPHandler(void);
+void StopSVDRPHandler(void);
+bool GetSVDRPServerNames(cStringList *ServerNames);
///< Gets a list of all available VDRs this VDR is connected to via SVDRP,
///< and stores it in the given ServerNames list. The list is cleared
///< before getting the server names.
- ///< If FetchFlag is given, only the server names for which the local
- ///< client has this flag set will be returned, and the client's flag
- ///< will be cleared.
///< Returns true if the resulting list is not empty.
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response = NULL);
///< Sends the given SVDRP Command string to the remote VDR identified
diff --git a/thread.c b/thread.c
index 8fc2340..2a5a8e9 100644
--- a/thread.c
+++ b/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 4.11 2017/06/25 12:08:16 kls Exp $
+ * $Id: thread.c 4.14 2018/03/05 22:38:10 kls Exp $
*/
#include "thread.h"
@@ -716,7 +716,8 @@ cStateLock::cStateLock(const char *Name)
name = Name;
threadId = 0;
state = 0;
- explicitModify = false;
+ explicitModify = emDisabled;
+ syncStateKey = NULL;
}
bool cStateLock::Lock(cStateKey &StateKey, bool Write, int TimeoutMs)
@@ -764,30 +765,71 @@ void cStateLock::Unlock(cStateKey &StateKey, bool IncState)
return;
}
if (StateKey.write && threadId != cThread::ThreadId()) {
- esyslog("ERROR: cStateLock::Unlock() called without holding a lock (tid=%d, lock=%s)", threadId, name);
+ esyslog("ERROR: cStateLock::Unlock() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
ABORT;
return;
}
- if (StateKey.write && IncState && !explicitModify)
+ if (StateKey.write && (IncState && explicitModify != emArmed || explicitModify == emEnabled)) {
+ if (syncStateKey && syncStateKey->state == state)
+ syncStateKey->state++;
state++;
+ }
StateKey.state = state;
+ StateKey.stateLock = NULL;
if (StateKey.write) {
StateKey.write = false;
threadId = 0;
- explicitModify = false;
+ explicitModify = emDisabled;
+ syncStateKey = NULL;
}
dbglockseq(name, false, false);
rwLock.Unlock();
}
-void cStateLock::IncState(void)
+void cStateLock::SetSyncStateKey(cStateKey &StateKey)
{
+ dbglocking("%5d %-12s %10p SetSyncStateKey\n", cThread::ThreadId(), name, &StateKey);
if (threadId != cThread::ThreadId()) {
- esyslog("ERROR: cStateLock::IncState() called without holding a lock (tid=%d, lock=%s)", threadId, name);
+ esyslog("ERROR: cStateLock::SetSyncStateKey() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
ABORT;
+ return;
}
- else
- state++;
+ if (StateKey.stateLock == this) {
+ esyslog("ERROR: cStateLock::SetSyncStateKey() called with locked key (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ return;
+ }
+ if (syncStateKey) {
+ esyslog("ERROR: cStateLock::SetSyncStateKey() called twice (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ return;
+ }
+ syncStateKey = &StateKey;
+}
+
+void cStateLock::SetExplicitModify(void)
+{
+ if (threadId != cThread::ThreadId()) {
+ esyslog("ERROR: cStateLock::SetExplicitModify() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ return;
+ }
+ if (explicitModify != emDisabled) {
+ esyslog("ERROR: cStateLock::SetExplicitModify() called twice (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ return;
+ }
+ explicitModify = emArmed;
+}
+
+void cStateLock::SetModified(void)
+{
+ if (threadId != cThread::ThreadId()) {
+ esyslog("ERROR: cStateLock::SetModified() called without holding a write lock (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ return;
+ }
+ explicitModify = emEnabled;
}
// --- cStateKey -------------------------------------------------------------
@@ -816,10 +858,8 @@ void cStateKey::Reset(void)
void cStateKey::Remove(bool IncState)
{
- if (stateLock) {
+ if (stateLock)
stateLock->Unlock(*this, IncState);
- stateLock = NULL;
- }
else {
esyslog("ERROR: cStateKey::Remove() called without holding a lock (key=%p)", this);
ABORT;
@@ -836,7 +876,6 @@ bool cStateKey::StateChanged(void)
return state != stateLock->state;
else
return true;
- return false;
}
// --- cIoThrottle -----------------------------------------------------------
diff --git a/thread.h b/thread.h
index 33f90c7..790c29a 100644
--- a/thread.h
+++ b/thread.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.h 4.3 2017/05/31 11:39:11 kls Exp $
+ * $Id: thread.h 4.4 2018/03/04 11:27:55 kls Exp $
*/
#ifndef __THREAD_H
@@ -171,11 +171,13 @@ class cStateKey;
class cStateLock {
friend class cStateKey;
private:
+ enum { emDisabled = 0, emArmed, emEnabled };
const char *name;
tThreadId threadId;
cRwLock rwLock;
int state;
- bool explicitModify;
+ int explicitModify;
+ cStateKey *syncStateKey;
void Unlock(cStateKey &StateKey, bool IncState = true);
///< Releases a lock that has been obtained by a previous call to Lock()
///< with the given StateKey. If this was a write-lock, and IncState is true,
@@ -211,13 +213,21 @@ public:
///< If Write is true (i.e. a write-lock is requested), the states of the
///< lock and the given StateKey don't matter, it will always try to obtain
///< a write lock.
- void SetExplicitModify(void) { explicitModify = true; }
+ void SetSyncStateKey(cStateKey &StateKey);
+ ///< Sets the given StateKey to be synchronized to the state of this lock.
+ ///< The caller must currenty hold a write lock on this lock, with a cStateKey
+ ///< that is different from the given StateKey. If, when removing the key that
+ ///< is holding the write lock, the StateKey's current state is the same as that
+ ///< of the lock, it will be increased together with the lock's state.
+ void SetExplicitModify(void);
///< If you have obtained a write lock on this lock, and you don't want its
///< state to be automatically incremented when the lock is released, a call to
- ///< this function will disable this, and you can explicitly call IncState()
+ ///< this function will disable this, and you can explicitly call SetModified()
///< to increment the state.
- void IncState(void);
- ///< Increments the state of this lock.
+ void SetModified(void);
+ ///< Sets this lock to have its state incremented when the current write lock
+ ///< state key is removed. Must have called SetExplicitModify() before calling
+ ///< this function.
};
class cStateKey {
diff --git a/timers.c b/timers.c
index 8987b12..8119b85 100644
--- a/timers.c
+++ b/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 4.11 2017/06/25 10:02:09 kls Exp $
+ * $Id: timers.c 4.18 2018/03/17 10:07:19 kls Exp $
*/
#include "timers.h"
@@ -419,20 +419,25 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
t = time(NULL);
int begin = TimeToInt(start); // seconds from midnight
- int length = TimeToInt(stop) - begin;
- if (length < 0)
- length += SECSINDAY;
+ int end = TimeToInt(stop);
+ int length = end - begin;
if (IsSingleEvent()) {
- startTime = SetTime(day, begin);
- stopTime = startTime + length;
+ time_t t0 = day;
+ startTime = SetTime(t0, begin);
+ if (length < 0)
+ t0 = IncDay(day, 1);
+ stopTime = SetTime(t0, end);
}
else {
+ time_t d = day ? max(day, t) : t;
for (int i = -1; i <= 7; i++) {
- time_t t0 = IncDay(day ? max(day, t) : t, i);
+ time_t t0 = IncDay(d, i);
if (DayMatches(t0)) {
time_t a = SetTime(t0, begin);
- time_t b = a + length;
+ if (length < 0)
+ t0 = IncDay(d, i + 1);
+ time_t b = SetTime(t0, end);
if ((!day || a >= day) && t < b) {
startTime = a;
stopTime = b;
@@ -480,9 +485,16 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
bool UseVps = HasFlags(tfVps) && Event->Vps();
Matches(UseVps ? Event->Vps() : Event->StartTime(), true);
int overlap = 0;
- if (UseVps)
- overlap = (startTime == Event->Vps()) ? FULLMATCH + (Event->IsRunning() ? 200 : 100) : 0;
- if (!overlap) {
+ if (UseVps) {
+ if (startTime == Event->Vps()) {
+ overlap = FULLMATCH;
+ if (Event->IsRunning())
+ overlap += 200;
+ else if (Event->RunningStatus() != SI::RunningStatusNotRunning)
+ overlap += 100;
+ }
+ }
+ else {
if (startTime <= Event->StartTime() && Event->EndTime() <= stopTime)
overlap = FULLMATCH;
else if (stopTime <= Event->StartTime() || Event->EndTime() <= startTime)
@@ -493,8 +505,6 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
startTime = stopTime = 0;
if (Overlap)
*Overlap = overlap;
- if (UseVps)
- return overlap > FULLMATCH ? tmFull : tmNone;
return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone;
}
return tmNone;
@@ -504,7 +514,10 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
bool cTimer::Expired(void) const
{
- return IsSingleEvent() && !Recording() && StopTime() + EXPIRELATENCY <= time(NULL) && (!HasFlags(tfVps) || !event || !event->Vps());
+ return IsSingleEvent()
+ && !Recording()
+ && StopTime() + EXPIRELATENCY <= time(NULL)
+ && (!HasFlags(tfVps) || !event || !event->Vps() || event->EndTime() + EXPIRELATENCY <= time(NULL));
}
time_t cTimer::StartTime(void) const
@@ -538,12 +551,12 @@ bool cTimer::SetEventFromSchedule(const cSchedules *Schedules)
if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) {
// VPS timers only match if their start time exactly matches the event's VPS time:
for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
- if (e->StartTime() && e->RunningStatus() != SI::RunningStatusNotRunning) { // skip outdated events
+ if (e->StartTime()) {
int overlap = 0;
- Matches(e, &overlap);
- if (overlap > FULLMATCH) {
+ if (Matches(e, &overlap) == tmFull) {
Event = e;
- break; // take the first matching event
+ if (overlap > FULLMATCH)
+ break; // take the first matching event
}
}
}
@@ -792,10 +805,10 @@ const cTimer *cTimers::GetMatch(const cEvent *Event, eTimerMatch *Match) const
eTimerMatch m = tmNone;
for (const cTimer *ti = First(); ti; ti = Next(ti)) {
eTimerMatch tm = ti->Matches(Event);
- if (tm > m) {
+ if (tm > m || tm == tmFull && ti->Local()) {
t = ti;
m = tm;
- if (m == tmFull)
+ if (m == tmFull && ti->Local())
break;
}
}
@@ -804,6 +817,16 @@ const cTimer *cTimers::GetMatch(const cEvent *Event, eTimerMatch *Match) const
return t;
}
+int cTimers::GetMaxPriority(void) const
+{
+ int n = 0;
+ for (const cTimer *ti = First(); ti; ti = Next(ti)) {
+ if (!ti->Remote() && ti->Recording())
+ n = max(n, ti->Priority());
+ }
+ return n;
+}
+
const cTimer *cTimers::GetNextActiveTimer(void) const
{
const cTimer *t0 = NULL;
@@ -883,80 +906,117 @@ bool cTimers::DeleteExpired(void)
return TimersModified;
}
-bool cTimers::GetRemoteTimers(const char *ServerName)
+bool cTimers::StoreRemoteTimers(const char *ServerName, const cStringList *RemoteTimers)
{
bool Result = false;
- if (ServerName) {
- Result = DelRemoteTimers(ServerName);
- cStringList Response;
- if (ExecSVDRPCommand(ServerName, "LSTT ID", &Response)) {
- for (int i = 0; i < Response.Size(); i++) {
- const char *s = Response[i];
- int Code = SVDRPCode(s);
- if (Code == 250) {
- if (const char *v = SVDRPValue(s)) {
- int Id = atoi(v);
- while (*v && *v != ' ')
- v++; // skip id
- cTimer *Timer = new cTimer;
- if (Timer->Parse(v)) {
- Timer->SetRemote(ServerName);
- Timer->SetId(Id);
- Add(Timer);
- Result = true;
- }
- else {
- esyslog("ERROR: %s: error in timer settings: %s", ServerName, v);
- delete Timer;
- }
+ if (!ServerName || !RemoteTimers || RemoteTimers->Size() == 0) {
+ // Remove remote timers from this list:
+ cTimer *Timer = First();
+ while (Timer) {
+ cTimer *t = Next(Timer);
+ if (Timer->Remote() && (!ServerName || strcmp(Timer->Remote(), ServerName) == 0)) {
+ Del(Timer);
+ Result = true;
+ }
+ Timer = t;
+ }
+ return Result;
+ }
+ // Collect all locally stored remote timers from ServerName:
+ cStringList tl;
+ for (cTimer *ti = First(); ti; ti = Next(ti)) {
+ if (ti->Remote() && strcmp(ti->Remote(), ServerName) == 0)
+ tl.Append(strdup(cString::sprintf("%d %s", ti->Id(), *ti->ToText(true))));
+ }
+ tl.SortNumerically(); // RemoteTimers is also sorted numerically!
+ // Compare the two lists and react accordingly:
+ int il = 0; // index into the local ("left") list of remote timers
+ int ir = 0; // index into the remote ("right") list of timers
+ int sl = tl.Size();
+ int sr = RemoteTimers->Size();
+ for (;;) {
+ int AddTimer = 0;
+ int DelTimer = 0;
+ if (il < sl) { // still have left entries
+ int nl = atoi(tl[il]);
+ if (ir < sr) { // still have right entries
+ // Compare timers:
+ int nr = atoi((*RemoteTimers)[ir]);
+ if (nl == nr) // same timer id
+ AddTimer = DelTimer = nl;
+ else if (nl < nr) // left entry not in right list
+ DelTimer = nl;
+ else // right entry not in left list
+ AddTimer = nr;
+ }
+ else // processed all right entries
+ DelTimer = nl;
+ }
+ else if (ir < sr) { // still have right entries
+ AddTimer = atoi((*RemoteTimers)[ir]);
+ if (!AddTimer) {
+ esyslog("ERROR: %s: error in timer settings: %s", ServerName, (*RemoteTimers)[ir]);
+ ir++;
+ continue; // let's see if we can process the rest
+ }
+ }
+ else // processed all left and right entries
+ break;
+ if (AddTimer && DelTimer) {
+ if (strcmp(tl[il], (*RemoteTimers)[ir]) != 0) {
+ // Overwrite timer:
+ char *v = (*RemoteTimers)[ir];
+ while (*v && *v != ' ')
+ v++; // skip id
+ if (cTimer *l = GetById(DelTimer, ServerName)) {
+ cTimer r;
+ if (r.Parse(v)) {
+ r.SetRemote(ServerName);
+ r.SetId(AddTimer);
+ *l = r;
+ Result = true;
}
+ else
+ esyslog("ERROR: %d@%s: error in timer settings: %s", DelTimer, ServerName, v);
}
- else if (Code != 550)
- esyslog("ERROR: %s: %s", ServerName, s);
}
- return Result;
- }
- }
- else {
- cStringList ServerNames;
- if (GetSVDRPServerNames(&ServerNames, sffTimers)) {
- for (int i = 0; i < ServerNames.Size(); i++)
- Result |= GetRemoteTimers(ServerNames[i]);
- }
- }
+ else // identical timer, nothing to do
+ ;
+ il++;
+ ir++;
+ }
+ else if (AddTimer) {
+ char *v = (*RemoteTimers)[ir];
+ while (*v && *v != ' ')
+ v++; // skip id
+ cTimer *Timer = new cTimer;
+ if (Timer->Parse(v)) {
+ Timer->SetRemote(ServerName);
+ Timer->SetId(AddTimer);
+ Add(Timer);
+ Result = true;
+ }
+ else {
+ esyslog("ERROR: %s: error in timer settings: %s", ServerName, v);
+ delete Timer;
+ }
+ ir++;
+ }
+ else if (DelTimer) {
+ if (cTimer *t = GetById(DelTimer, ServerName)) {
+ Del(t);
+ Result = true;
+ }
+ il++;
+ }
+ else {
+ esyslog("ERROR: oops while storing remote timers!");
+ break; // let's not get stuck here!
+ }
+ }
return Result;
}
-bool cTimers::DelRemoteTimers(const char *ServerName)
-{
- bool Deleted = false;
- cTimer *Timer = First();
- while (Timer) {
- cTimer *t = Next(Timer);
- if (Timer->Remote() && (!ServerName || strcmp(Timer->Remote(), ServerName) == 0)) {
- Del(Timer);
- Deleted = true;
- }
- Timer = t;
- }
- return Deleted;
-}
-
-void cTimers::TriggerRemoteTimerPoll(const char *ServerName)
-{
- if (ServerName) {
- if (!ExecSVDRPCommand(ServerName, cString::sprintf("POLL %s TIMERS", Setup.SVDRPHostName)))
- esyslog("ERROR: can't send 'POLL %s TIMERS' to '%s'", Setup.SVDRPHostName, ServerName);
- }
- else {
- cStringList ServerNames;
- if (GetSVDRPServerNames(&ServerNames)) {
- for (int i = 0; i < ServerNames.Size(); i++)
- TriggerRemoteTimerPoll(ServerNames[i]);
- }
- }
-}
-
static bool RemoteTimerError(const cTimer *Timer, cString *Msg)
{
if (Msg)
diff --git a/timers.h b/timers.h
index c4932ba..93d5a44 100644
--- a/timers.h
+++ b/timers.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: timers.h 4.8 2017/04/20 09:09:45 kls Exp $
+ * $Id: timers.h 4.11 2018/02/27 13:57:26 kls Exp $
*/
#ifndef __TIMERS_H
@@ -176,6 +176,8 @@ public:
cTimer *GetMatch(time_t t) { return const_cast<cTimer *>(static_cast<const cTimers *>(this)->GetMatch(t)); };
const cTimer *GetMatch(const cEvent *Event, eTimerMatch *Match = NULL) const;
cTimer *GetMatch(const cEvent *Event, eTimerMatch *Match = NULL) { return const_cast<cTimer *>(static_cast<const cTimers *>(this)->GetMatch(Event, Match)); }
+ int GetMaxPriority(void) const;
+ ///< Returns the maximum priority of all local timers that are currently recording.
const cTimer *GetNextActiveTimer(void) const;
const cTimer *UsesChannel(const cChannel *Channel) const;
bool SetEvents(const cSchedules *Schedules);
@@ -183,21 +185,14 @@ public:
void Add(cTimer *Timer, cTimer *After = NULL);
void Ins(cTimer *Timer, cTimer *Before = NULL);
void Del(cTimer *Timer, bool DeleteObject = true);
- bool GetRemoteTimers(const char *ServerName = NULL);
- ///< Gets the timers from the given remote machine and adds them to this
- ///< list. If no ServerName is given, all timers from all known remote
- ///< machines will be fetched. This function calls DelRemoteTimers() with
- ///< the given ServerName first.
- ///< Returns true if any remote timers have been added or deleted
- bool DelRemoteTimers(const char *ServerName = NULL);
- ///< Deletes all timers of the given remote machine from this list (leaves
- ///< them untouched on the remote machine). If no ServerName is given, the
- ///< timers of all remote machines will be deleted from the list.
- ///< Returns true if any remote timers have been deleted.
- void TriggerRemoteTimerPoll(const char *ServerName = NULL);
- ///< Sends an SVDRP POLL command to the given remote machine.
- ///< If no ServerName is given, the POLL command will be sent to all
- ///< known remote machines.
+ bool StoreRemoteTimers(const char *ServerName = NULL, const cStringList *RemoteTimers = NULL);
+ ///< Stores the given list of RemoteTimers, which come from the VDR ServerName, in
+ ///< this list. If no ServerName is given, all remote timers from all peer machines
+ ///< will be removed from this list. If no RemoteTimers are given, only the remote
+ ///< timers from ServerName will be removed from this list.
+ ///< The given list of RemoteTimers must be sorted numerically (by a call to its
+ ///< SortNumerically() function).
+ ///< Returns true if any remote timers have been added, deleted or modified.
};
bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer = NULL, cString *Msg = NULL);
diff --git a/tools.c b/tools.c
index ac2f9eb..7330fce 100644
--- a/tools.c
+++ b/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 4.8 2017/06/25 11:45:39 kls Exp $
+ * $Id: tools.c 4.11 2018/03/04 10:28:04 kls Exp $
*/
#include "tools.h"
@@ -296,6 +296,18 @@ cString strgetval(const char *s, const char *name, char d)
return NULL;
}
+char *strshift(char *s, int n)
+{
+ if (s && n > 0) {
+ int l = strlen(s);
+ if (n < l)
+ memmove(s, s + n, l - n + 1); // we also copy the terminating 0!
+ else
+ *s = 0;
+ }
+ return s;
+}
+
bool startswith(const char *s, const char *p)
{
while (*p) {
@@ -1762,7 +1774,12 @@ bool cSafeFile::Close(void)
// --- cUnbufferedFile -------------------------------------------------------
-#define USE_FADVISE
+#ifndef USE_FADVISE_READ
+#define USE_FADVISE_READ 0
+#endif
+#ifndef USE_FADVISE_WRITE
+#define USE_FADVISE_WRITE 1
+#endif
#define WRITE_BUFFER KILOBYTE(800)
@@ -1781,7 +1798,7 @@ int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
Close();
fd = open(FileName, Flags, Mode);
curpos = 0;
-#ifdef USE_FADVISE
+#if USE_FADVISE_READ || USE_FADVISE_WRITE
begin = lastpos = ahead = 0;
cachedstart = 0;
cachedend = 0;
@@ -1797,7 +1814,7 @@ int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
int cUnbufferedFile::Close(void)
{
if (fd >= 0) {
-#ifdef USE_FADVISE
+#if USE_FADVISE_READ || USE_FADVISE_WRITE
if (totwritten) // if we wrote anything make sure the data has hit the disk before
fdatasync(fd); // calling fadvise, as this is our last chance to un-cache it.
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
@@ -1840,7 +1857,7 @@ off_t cUnbufferedFile::Seek(off_t Offset, int Whence)
ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
{
if (fd >= 0) {
-#ifdef USE_FADVISE
+#if USE_FADVISE_READ
off_t jumped = curpos-lastpos; // nonzero means we're not at the last offset
if ((cachedstart < cachedend) && (curpos < cachedstart || curpos > cachedend)) {
// current position is outside the cached window -- invalidate it.
@@ -1853,7 +1870,7 @@ ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
ssize_t bytesRead = safe_read(fd, Data, Size);
if (bytesRead > 0) {
curpos += bytesRead;
-#ifdef USE_FADVISE
+#if USE_FADVISE_READ
cachedend = max(cachedend, curpos);
// Read ahead:
@@ -1875,7 +1892,7 @@ ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
ahead = curpos; // jumped -> we really don't want any readahead, otherwise e.g. fast-rewind gets in trouble.
#endif
}
-#ifdef USE_FADVISE
+#if USE_FADVISE_READ
if (cachedstart < cachedend) {
if (curpos - cachedstart > READCHUNK * 2) {
// current position has moved forward enough, shrink tail window.
@@ -1899,7 +1916,7 @@ ssize_t cUnbufferedFile::Write(const void *Data, size_t Size)
{
if (fd >=0) {
ssize_t bytesWritten = safe_write(fd, Data, Size);
-#ifdef USE_FADVISE
+#if USE_FADVISE_WRITE
if (bytesWritten > 0) {
begin = min(begin, curpos);
curpos += bytesWritten;
@@ -2236,7 +2253,7 @@ void cListBase::SetExplicitModify(void)
void cListBase::SetModified(void)
{
- stateLock.IncState();
+ stateLock.SetModified();
}
const cListObject *cListBase::Get(int Index) const
diff --git a/tools.h b/tools.h
index aaba602..614130d 100644
--- a/tools.h
+++ b/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 4.13 2017/06/25 11:45:38 kls Exp $
+ * $Id: tools.h 4.16 2018/03/04 14:06:36 kls Exp $
*/
#ifndef __TOOLS_H
@@ -226,6 +226,12 @@ cString strgetval(const char *s, const char *name, char d = '=');
///< If an other delimiter shall be used (like, e.g., ':'), it can be given
///< as the third parameter.
///< If name occurs more than once in s, only the first occurrence is taken.
+char *strshift(char *s, int n);
+ ///< Shifts the given string to the left by the given number of bytes, thus
+ ///< removing the first n bytes from s.
+ ///< If n is greater than the length of s, the resulting string will be empty.
+ ///< If n is <= 0 s will be unchanged.
+ ///< Returns s.
bool startswith(const char *s, const char *p);
bool endswith(const char *s, const char *p);
bool isempty(const char *s);
@@ -553,6 +559,11 @@ public:
///< Contains() function to check whether an object you are holding a pointer
///< to is still in the list. Note that the garbage collector is purged when
///< the usual housekeeping is done.
+ void SetSyncStateKey(cStateKey &StateKey) { stateLock.SetSyncStateKey(StateKey); }
+ ///< When making changes to this list (while holding a write lock) that shall
+ ///< not affect some other code that reacts to such changes, this function can
+ ///< be called with the StateKey used by that other code.
+ ///< See cStateLock::SetSyncStateKey() for details.
void SetUseGarbageCollector(void) { useGarbageCollector = true; }
void SetExplicitModify(void);
///< If you have obtained a write lock on this list, and you don't want it to
@@ -768,7 +779,7 @@ public:
inline int CompareInts(const void *a, const void *b)
{
- return *(const int *)a > *(const int *)b;
+ return *(const int *)a - *(const int *)b;
}
inline int CompareStrings(const void *a, const void *b)
@@ -781,6 +792,12 @@ inline int CompareStringsIgnoreCase(const void *a, const void *b)
return strcasecmp(*(const char **)a, *(const char **)b);
}
+inline int CompareStringsNumerically(const void *a, const void *b)
+{
+ int d = atoi(*(const char **)a) - atoi(*(const char **)b);
+ return d ? d : CompareStrings(a, b);
+}
+
class cStringList : public cVector<char *> {
public:
cStringList(int Allocated = 10): cVector<char *>(Allocated) {}
@@ -793,6 +810,10 @@ public:
else
cVector<char *>::Sort(CompareStrings);
}
+ void SortNumerically(void)
+ {
+ cVector<char *>::Sort(CompareStringsNumerically);
+ }
virtual void Clear(void);
};
diff --git a/transfer.c b/transfer.c
index d0e7ed4..88931e5 100644
--- a/transfer.c
+++ b/transfer.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: transfer.c 4.1 2015/09/05 11:43:58 kls Exp $
+ * $Id: transfer.c 4.2 2017/12/07 15:00:33 kls Exp $
*/
#include "transfer.h"
@@ -14,6 +14,8 @@
cTransfer::cTransfer(const cChannel *Channel)
:cReceiver(Channel, TRANSFERPRIORITY)
{
+ lastErrorReport = 0;
+ numLostPackets = 0;
patPmtGenerator.SetChannel(Channel);
}
@@ -37,6 +39,7 @@ void cTransfer::Activate(bool On)
#define MAXRETRIES 20 // max. number of retries for a single TS packet
#define RETRYWAIT 5 // time (in ms) between two retries
+#define ERRORDELTA 60 // seconds before reporting lost TS packets again
void cTransfer::Receive(const uchar *Data, int Length)
{
@@ -51,7 +54,12 @@ void cTransfer::Receive(const uchar *Data, int Length)
cCondWait::SleepMs(RETRYWAIT);
}
DeviceClear();
- esyslog("ERROR: TS packet not accepted in Transfer Mode");
+ numLostPackets++;
+ if (time(NULL) - lastErrorReport > ERRORDELTA) {
+ esyslog("ERROR: %d TS packet(s) not accepted in Transfer Mode", numLostPackets);
+ numLostPackets = 0;
+ lastErrorReport = time(NULL);
+ }
}
}
diff --git a/transfer.h b/transfer.h
index 21d9dd8..a10ed7b 100644
--- a/transfer.h
+++ b/transfer.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: transfer.h 4.1 2015/09/05 11:43:08 kls Exp $
+ * $Id: transfer.h 4.2 2017/12/07 14:56:22 kls Exp $
*/
#ifndef __TRANSFER_H
@@ -16,6 +16,8 @@
class cTransfer : public cReceiver, public cPlayer {
private:
+ time_t lastErrorReport;
+ int numLostPackets;
cPatPmtGenerator patPmtGenerator;
protected:
virtual void Activate(bool On);
diff --git a/vdr.1 b/vdr.1
index 0ce3eed..3278451 100644
--- a/vdr.1
+++ b/vdr.1
@@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
-.\" $Id: vdr.1 4.2 2015/04/19 12:39:13 kls Exp $
+.\" $Id: vdr.1 4.3 2018/02/09 15:09:07 kls Exp $
.\"
.TH vdr 1 "19 Feb 2015" "2.2" "Video Disk Recorder"
.SH NAME
@@ -158,7 +158,10 @@ Don't use the keyboard as an input device.
Use \fIport\fR for SVDRP. A value of \fB0\fR turns off SVDRP.
The default SVDRP port is \fB6419\fR.
You need to edit the file \fIsvdrphosts.conf\fR in order to enable
-access to the SVDRP port.
+access to the SVDRP port from hosts other than the localhosts.
+Note that this option only changes the TCP port used for SVDRP commands.
+The UDP port for discovering peer VDRs in the same network is always set
+to 6419 and can't be changed.
.TP
.BI \-P\ options ,\ \-\-plugin= options
Load a plugin, defined by the given \fIoptions\fR.
diff --git a/vdr.5 b/vdr.5
index 80d60de..b04b20d 100644
--- a/vdr.5
+++ b/vdr.5
@@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
-.\" $Id: vdr.5 4.3 2017/06/10 11:47:27 kls Exp $
+.\" $Id: vdr.5 4.7 2018/02/15 16:00:42 kls Exp $
.\"
.TH vdr 5 "19 Feb 2015" "2.2" "Video Disk Recorder Files"
.SH NAME
@@ -522,23 +522,18 @@ frequency of the given channel, and pin is an optional pin number (0-255). The
actual values are device specific and can be found in the SCR device's manual.
Examples:
-
+.PP
+.EX
0 1284
-.br
1 1400
-.br
2 1516
-.br
3 1632
-.br
4 1748
-.br
5 1864
-.br
6 1980
-.br
7 2096
-
+.EE
+.PP
By default it is assumed that the SCR configurations apply to all devices, and
each device will pick one. If you have several SCR sat cables connected to one
VDR machine, or if you want to explicitly assign the SCR channels to your devices,
@@ -596,35 +591,24 @@ defines a sub folder (i.e. a folder that contains other folders), and a line
consisting of only '}' ends the definition of a sub folder.
Example:
-
+.PP
+.EX
Daily {
-.br
News
-.br
Soaps
-.br
}
-.br
Archive {
-.br
Movies
-.br
Sports
-.br
Sci-Fi {
-.br
Star Trek
-.br
U.F.O.
-.br
}
-.br
}
-.br
Comedy
-.br
Science
-
+.EE
+.PP
Note that these folder definitions are only used to set the file name under which
a timer will store its recording. Changing these definitions in any way has no
effect on existing timers or recordings.
@@ -647,9 +631,9 @@ Everything following (and including) a '#' character is considered to be comment
You can have nested layers of command menus by surrounding a sequence of
commands with '{'...'}' and giving it a title, as in
-
+.PP
+.EX
My Commands {
-.br
First list {
Do something: some command
Do something else: another command
@@ -658,9 +642,9 @@ My Commands {
Even more: yet another command
So much more: and yet another one
}
-.br
}
-
+.EE
+.PP
Command lists can be nested to any depth.
By default the menu entries in the "Commands" menu will be numbered '1'...'9'
@@ -675,15 +659,14 @@ In order to avoid error messages to the console, every command should have
\fIstdout\fR will be displayed in a result window, with \fBtitle\fR as its title.
Examples:
-
+.PP
+.EX
Check for new mail?: /usr/local/bin/checkmail 2>&1
-.br
CPU status: /usr/local/bin/cpustatus 2>&1
-.br
Disk space: df \-h | grep '/video' | awk '{ print 100 \- $5 "% free"; }'
-.br
Calendar: date;echo;cal
-
+.EE
+.PP
Note that the commands 'checkmail' and 'cpustatus' are only \fBexamples\fR!
Don't send emails to the author asking where to find these ;\-)
.br
@@ -713,14 +696,13 @@ the \fBIP-Address\fR is 0.0.0.0, because this will give access to any host
Everything following (and including) a '#' character is considered to be comment.
Examples:
-
+.PP
+.EX
127.0.0.1 # always accept localhost
-.br
192.168.100.0/24 # any host on the local net
-.br
204.152.189.113 # a specific host
-.br
0.0.0.0/0 # any host on any net (\fBUSE WITH CARE!\fR)
+.EE
.SS SETUP
The file \fIsetup.conf\fR contains the basic configuration options for \fBvdr\fR.
Each line contains one option in the format "Name = Value".
@@ -839,6 +821,26 @@ closer).
\fBCURRENT RESTRICTIONS:\fR
-\ the comment is currently not used by VDR
+.SS SORT MODE
+The file \fI.sort\fR (if present in a directory) contains an integer number
+defining the mode by which this directory shall be sorted when presented in a menu.
+
+The following values are defined:
+.TS
+tab (@);
+l l.
+\fB0\fR@sort by name
+\fB1\fR@sort by time
+.TE
+.SS RECORDING TIMER
+The file \fI.timer\fR (if present in a recording directory) contains
+the full id of the timer that is currently recording into this directory.
+Timer ids are of the form
+
+\fBid@hostname\fR
+
+where \fBid\fR is the timer's numerical id on the VDR with the name \fBhostname\fR.
+This file is created when the timer starts recording, and is deleted when it ends.
.SS EPG DATA
The file \fIepg.data\fR contains the EPG data in an easily parsable format.
The first character of each line defines what kind of data this line contains.
@@ -918,6 +920,7 @@ in this file, other CAMs will be tried just as well. The main purpose of this
file is to speed up channel switching in systems with more than one CAM.
This file will be read at program startup and saved when the program ends.
+If the file is read-only, it will not be overwritten.
.SS CAM AUTO RESPONSE
If your CAM keeps popping up annoying messages or you want to make sure VDR
can record programmes with parental rating without having to enter the PIN
@@ -961,20 +964,16 @@ Everything following (and including) a '#' character is considered to be comment
If started without any options, vdr tries to read any files in the directory
/etc/vdr/conf.d with names that do not begin with a '.' and that end with '.conf'.
These files are read in alphabetical order. The format of these files is
-
+.PP
+.EX
# comment
-.br
[name]
-.br
-a
-.br
-b 123
-.br
--long
-.br
--longarg=123
-.br
-
+.EE
+.PP
Any lines that begin with '#' as the first non-whitespace character are considered
comments and are ignored.
A command line option file consists of one or more sections, indicated by '[name]',
diff --git a/vdr.c b/vdr.c
index 7628f47..f9e5bb8 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.tvdr.de
*
- * $Id: vdr.c 4.18 2017/06/08 16:10:34 kls Exp $
+ * $Id: vdr.c 4.24 2018/03/04 14:00:29 kls Exp $
*/
#include <getopt.h>
@@ -221,8 +221,10 @@ int main(int argc, char *argv[])
int WatchdogTimeout = DEFAULTWATCHDOG;
const char *Terminal = NULL;
const char *OverrideCharacterTable = NULL;
-#define DEPRECATED_VDR_CHARSET_OVERRIDE
-#ifdef DEPRECATED_VDR_CHARSET_OVERRIDE
+#ifndef DEPRECATED_VDR_CHARSET_OVERRIDE
+#define DEPRECATED_VDR_CHARSET_OVERRIDE 0
+#endif
+#if DEPRECATED_VDR_CHARSET_OVERRIDE
OverrideCharacterTable = getenv("VDR_CHARSET_OVERRIDE");
const char *DeprecatedVdrCharsetOverride = OverrideCharacterTable;
#endif
@@ -703,7 +705,7 @@ int main(int argc, char *argv[])
isyslog("codeset is '%s' - %s", CodeSet, known ? "known" : "unknown");
cCharSetConv::SetSystemCharacterTable(CodeSet);
}
-#ifdef DEPRECATED_VDR_CHARSET_OVERRIDE
+#if DEPRECATED_VDR_CHARSET_OVERRIDE
if (DeprecatedVdrCharsetOverride)
isyslog("use of environment variable VDR_CHARSET_OVERRIDE (%s) is deprecated!", DeprecatedVdrCharsetOverride);
#endif
@@ -730,6 +732,7 @@ int main(int argc, char *argv[])
bool InhibitEpgScan = false;
bool IsInfoMenu = false;
cSkin *CurrentSkin = NULL;
+ int OldPrimaryDVB = 0;
// Load plugins:
@@ -832,6 +835,7 @@ int main(int argc, char *argv[])
}
}
}
+ OldPrimaryDVB = Setup.PrimaryDVB;
// Check for timers in automatic start time window:
@@ -942,9 +946,7 @@ int main(int argc, char *argv[])
// SVDRP:
SetSVDRPPorts(SVDRPport, DEFAULTSVDRPPORT);
- StartSVDRPServerHandler();
- if (Setup.SVDRPPeering)
- StartSVDRPClientHandler();
+ StartSVDRPHandler();
// Main program loop:
@@ -1076,32 +1078,28 @@ int main(int argc, char *argv[])
PreviousChannel[PreviousChannelIndex ^= 1] = LastChannel;
{
// Timers and Recordings:
- bool TimersModified = false;
- bool TriggerRemoteTimerPoll = false;
- static cStateKey TimersStateKey(true);
- if (cTimers::GetTimersRead(TimersStateKey)) {
- TriggerRemoteTimerPoll = true;
- TimersStateKey.Remove();
- }
+ static cStateKey TimersStateKey;
cTimers *Timers = cTimers::GetTimersWrite(TimersStateKey);
- // Get remote timers:
- TimersModified |= Timers->GetRemoteTimers();
- // Assign events to timers:
- static cStateKey SchedulesStateKey;
- if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(SchedulesStateKey))
- TimersModified |= Timers->SetEvents(Schedules);
+ {
+ // Assign events to timers:
+ static cStateKey SchedulesStateKey;
+ if (TimersStateKey.StateChanged())
+ SchedulesStateKey.Reset(); // we assign events if either the Timers or the Schedules have changed
+ bool TimersModified = false;
+ if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(SchedulesStateKey)) {
+ Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
+ if (Timers->SetEvents(Schedules))
+ TimersModified = true;
+ SchedulesStateKey.Remove();
+ }
+ TimersStateKey.Remove(TimersModified); // we need to remove the key here, so that syncing StateKeySVDRPRemoteTimersPoll takes effect!
+ }
// Must do all following calls with the exact same time!
// Process ongoing recordings:
- if (cRecordControls::Process(Timers, Now)) {
+ Timers = cTimers::GetTimersWrite(TimersStateKey);
+ bool TimersModified = false;
+ if (cRecordControls::Process(Timers, Now))
TimersModified = true;
- TriggerRemoteTimerPoll = true;
- }
- // Must keep the lock on the schedules until after processing the record
- // controls, in order to avoid short interrupts in case the current event
- // is replaced by a new one (which some broadcasters do, instead of just
- // modifying the current event's data):
- if (SchedulesStateKey.InLock())
- SchedulesStateKey.Remove();
// Start new recordings:
if (cTimer *Timer = Timers->GetMatch(Now)) {
if (!cRecordControls::Start(Timers, Timer))
@@ -1109,7 +1107,6 @@ int main(int argc, char *argv[])
else
LastTimerChannel = Timer->Channel()->Number();
TimersModified = true;
- TriggerRemoteTimerPoll = true;
}
// Make sure timers "see" their channel early enough:
static time_t LastTimerCheck = 0;
@@ -1164,13 +1161,10 @@ int main(int argc, char *argv[])
LastTimerCheck = Now;
}
// Delete expired timers:
- if (Timers->DeleteExpired()) {
+ if (Timers->DeleteExpired())
TimersModified = true;
- TriggerRemoteTimerPoll = true;
- }
- // Trigger remote timer polls:
- if (TriggerRemoteTimerPoll)
- Timers->TriggerRemoteTimerPoll();
+ // Make sure there is enough free disk space for ongoing recordings:
+ AssertFreeDiskSpace(Timers->GetMaxPriority());
TimersStateKey.Remove(TimersModified);
}
// Recordings:
@@ -1422,12 +1416,6 @@ int main(int argc, char *argv[])
DELETE_MENU;
cControl::Shutdown();
break;
- case osSwitchDvb:
- DELETE_MENU;
- cControl::Shutdown();
- Skins.QueueMessage(mtInfo, tr("Switching primary DVB..."));
- cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
- break;
case osPlugin: DELETE_MENU;
Menu = cMenuMain::PluginOsdObject();
if (Menu)
@@ -1503,6 +1491,17 @@ int main(int argc, char *argv[])
}
}
+ // Change primary device:
+ int NewPrimaryDVB = Setup.PrimaryDVB;
+ if (NewPrimaryDVB != OldPrimaryDVB) {
+ DELETE_MENU;
+ cControl::Shutdown();
+ Skins.QueueMessage(mtInfo, tr("Switching primary DVB..."));
+ cOsdProvider::Shutdown();
+ cDevice::SetPrimaryDevice(NewPrimaryDVB);
+ OldPrimaryDVB = NewPrimaryDVB;
+ }
+
// SIGHUP shall cause a restart:
if (LastSignal == SIGHUP) {
if (ShutdownHandler.ConfirmRestart(true) && Interface->Confirm(tr("Press any key to cancel restart"), RESTARTCANCELPROMPT, true))
@@ -1564,8 +1563,7 @@ Exit:
signal(SIGPIPE, SIG_DFL);
signal(SIGALRM, SIG_DFL);
- StopSVDRPClientHandler();
- StopSVDRPServerHandler();
+ StopSVDRPHandler();
ChannelCamRelations.Save();
cRecordControls::Shutdown();
PluginManager.StopPlugins();