diff --git a/HISTORY b/HISTORY index 74d22fb8..b142d45c 100644 --- a/HISTORY +++ b/HISTORY @@ -8924,3 +8924,9 @@ Video Disk Recorder Revision History reading thread, without additional locking. - Now stopping any ongoing recordings before stopping the plugins, to avoid a crash when stopping VDR while recording. + +2017-03-30: Version 2.3.4 + +- The functionality of HandleRemoteModifications(), which synchronizes changes to + timers between peer VDR machines, has been moved to timers.[ch] and renamed to + HandleRemoteTimerModifications(). It now also handles deleting remote timers. diff --git a/config.h b/config.h index 4bfdba56..e9a9f11d 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.7 2016/12/27 11:45:25 kls Exp $ + * $Id: config.h 4.8 2017/03/30 13:42:15 kls Exp $ */ #ifndef __CONFIG_H @@ -22,13 +22,13 @@ // VDR's own version number: -#define VDRVERSION "2.3.3" -#define VDRVERSNUM 20303 // Version * 10000 + Major * 100 + Minor +#define VDRVERSION "2.3.4" +#define VDRVERSNUM 20304 // Version * 10000 + Major * 100 + Minor // The plugin API's version number: -#define APIVERSION "2.3.3" -#define APIVERSNUM 20303 // Version * 10000 + Major * 100 + Minor +#define APIVERSION "2.3.4" +#define APIVERSNUM 20304 // 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 diff --git a/menu.c b/menu.c index 685e94f6..b53678a9 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.22 2017/03/18 14:27:50 kls Exp $ + * $Id: menu.c 4.23 2017/03/30 15:15:03 kls Exp $ */ #include "menu.h" @@ -1071,52 +1071,10 @@ static bool RemoteTimerError(const cTimer *Timer) static bool HandleRemoteModifications(cTimer *NewTimer, cTimer *OldTimer = NULL) { - cStringList Response; - if (!OldTimer || OldTimer->Local() || !OldTimer->Id()) { - if (NewTimer->Local()) { // timer stays local, nothing to do - if (OldTimer && OldTimer->Id()) - isyslog("modified timer %s", *NewTimer->ToDescr()); - else - isyslog("added timer %s", *NewTimer->ToDescr()); - } - else { // timer is new, or moved from local to remote - if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) - return RemoteTimerError(NewTimer); - int RemoteId = atoi(SVDRPValue(Response[0])); - if (RemoteId <= 0) - return RemoteTimerError(NewTimer); - NewTimer->SetId(RemoteId); - if (OldTimer && OldTimer->Id()) { - if (OldTimer->Recording()) - cRecordControls::Stop(OldTimer); - isyslog("moved timer %d to %s", OldTimer->Id(), *NewTimer->ToDescr()); - } - else - isyslog("added timer %s", *NewTimer->ToDescr()); - } - } - else if (NewTimer->Local()) { // timer is moved from remote to local - if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250) - return RemoteTimerError(OldTimer); - NewTimer->SetId(cTimers::NewTimerId()); - NewTimer->ClrFlags(tfRecording); // in case it was recording on the remote machine - isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr()); - } - else if (strcmp(OldTimer->Remote(), NewTimer->Remote()) == 0) { // timer stays remote on same machine - if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("MODT %d %s", OldTimer->Id(), *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) - return RemoteTimerError(NewTimer); - isyslog("modified timer %s", *NewTimer->ToDescr()); - } - else { // timer is moved from one remote machine to an other - if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) - return RemoteTimerError(NewTimer); - int RemoteId = atoi(SVDRPValue(Response[0])); - if (RemoteId <= 0) - return RemoteTimerError(NewTimer); - NewTimer->SetId(RemoteId); - if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250) - return RemoteTimerError(OldTimer); - isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr()); + cString ErrorMessage; + if (!HandleRemoteTimerModifications(NewTimer, OldTimer, &ErrorMessage)) { + Skins.Message(mtError, ErrorMessage); + return false; } return true; } @@ -1157,6 +1115,8 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key) else { if (!HandleRemoteModifications(&data, timer)) return osContinue; + if (timer->Local() && timer->Recording() && data.Remote()) + cRecordControls::Stop(timer); *timer = data; } LOCK_SCHEDULES_READ; @@ -1387,15 +1347,13 @@ eOSState cMenuTimers::Delete(void) Timer = NULL; } if (Timer) { - if (Timer->Remote()) { - cStringList Response; - if (!ExecSVDRPCommand(Timer->Remote(), cString::sprintf("DELT %d", Timer->Id()), &Response) || SVDRPCode(Response[0]) != 250) - RemoteTimerError(Timer); + if (!HandleRemoteModifications(NULL, Timer)) { + timersStateKey.Remove(); + return osContinue; } Timers->Del(Timer); cOsdMenu::Del(Current()); Display(); - isyslog("deleted timer %s", *Timer->ToDescr()); } } } diff --git a/timers.c b/timers.c index 5bc11036..917be80c 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.7 2016/12/23 09:48:39 kls Exp $ + * $Id: timers.c 4.8 2017/03/30 15:08:11 kls Exp $ */ #include "timers.h" @@ -955,6 +955,72 @@ void cTimers::TriggerRemoteTimerPoll(const char *ServerName) } } +static bool RemoteTimerError(const cTimer *Timer, cString *Msg) +{ + if (Msg) + *Msg = cString::sprintf("%s %d@%s!", tr("Error while accessing remote timer"), Timer->Id(), Timer->Remote()); + return false; // convenience return code +} + +bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer, cString *Msg) +{ + cStringList Response; + if (!NewTimer) { + if (OldTimer) { // timer shall be deleted from remote machine + if (OldTimer->Remote() && OldTimer->Id()) { + if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(OldTimer, Msg); + } + isyslog("deleted timer %s", *OldTimer->ToDescr()); + } + } + else if (!OldTimer || OldTimer->Local() || !OldTimer->Id()) { + if (NewTimer->Local()) { // timer stays local, nothing to do + if (OldTimer && OldTimer->Id()) + isyslog("modified timer %s", *NewTimer->ToDescr()); + else + isyslog("added timer %s", *NewTimer->ToDescr()); + } + else { // timer is new, or moved from local to remote + if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(NewTimer, Msg); + int RemoteId = atoi(SVDRPValue(Response[0])); + if (RemoteId <= 0) + return RemoteTimerError(NewTimer, Msg); + NewTimer->SetId(RemoteId); + if (OldTimer && OldTimer->Id()) { + isyslog("moved timer %d to %s", OldTimer->Id(), *NewTimer->ToDescr()); + } + else + isyslog("added timer %s", *NewTimer->ToDescr()); + } + } + else if (NewTimer->Local()) { // timer is moved from remote to local + if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(OldTimer, Msg); + NewTimer->SetId(cTimers::NewTimerId()); + NewTimer->ClrFlags(tfRecording); // in case it was recording on the remote machine + isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr()); + } + else if (strcmp(OldTimer->Remote(), NewTimer->Remote()) == 0) { // timer stays remote on same machine + if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("MODT %d %s", OldTimer->Id(), *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(NewTimer, Msg); + isyslog("modified timer %s", *NewTimer->ToDescr()); + } + else { // timer is moved from one remote machine to an other + if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(NewTimer, Msg); + int RemoteId = atoi(SVDRPValue(Response[0])); + if (RemoteId <= 0) + return RemoteTimerError(NewTimer, Msg); + NewTimer->SetId(RemoteId); + if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(OldTimer, Msg); + isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr()); + } + return true; +} + // --- cSortedTimers --------------------------------------------------------- static int CompareTimers(const void *a, const void *b) diff --git a/timers.h b/timers.h index 7ee116c8..1bffa84f 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.6 2016/12/23 09:49:31 kls Exp $ + * $Id: timers.h 4.7 2017/03/30 15:22:36 kls Exp $ */ #ifndef __TIMERS_H @@ -200,6 +200,22 @@ public: ///< known remote machines. }; +bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer = NULL, cString *Msg = NULL); + ///< Performs any operations necessary to synchronize changes to a timer + ///< between peer VDR machines. OldTimer must point to the old version + ///< of the timer, while NewTimer points to the new version. If either + ///< of the two is a remote timer, the necessary SVDRP commands are executed + ///< to reflect the changes on the remote machine(s). If NewTimer is NULL, + ///< OldTimer will be removed from the remote machine (if applicable). + ///< If OldTimer is NULL, NewTimer will be added to the remote machine (if + ///< applicable). If anything goes wrong, an error message is generated in the + ///< optional Msg string, which should be presented to the user. + ///< Any necessary local operations (like adding/deleting the timer to the + ///< local list of timers etc.) must be done before and/or after the call to this + ///< function. Proper log messages will be generated by this function, even + ///< if no remote operations are required. + ///< Returns true if successful. + // Provide lock controlled access to the list: DEF_LIST_LOCK(Timers);