From 23d986657a5a4e7d086932d528715aaf8875bad3 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Tue, 6 Apr 2021 08:48:35 +0200 Subject: [PATCH] Spawned timers that don't use VPS now automatically adjust their start/stop times to changes in the respective event's times --- HISTORY | 4 +++- MANUAL | 3 +++ timers.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++------- timers.h | 8 +++++-- vdr.c | 6 +++--- 5 files changed, 70 insertions(+), 14 deletions(-) diff --git a/HISTORY b/HISTORY index c9221d39..caf31276 100644 --- a/HISTORY +++ b/HISTORY @@ -9578,7 +9578,7 @@ Video Disk Recorder Revision History given (reported by Manuel Reimer). - Fixed handling $(PKG_CONFIG) in newplugin (thanks to Winfried Köhler). -2021-04-04: +2021-04-05: - Fixed strreplace() to handle NULL strings (reported by Jürgen Schneider). - Somewhere down the road the 'x' bit of Doxyfile.filter got lost, so the @@ -9633,3 +9633,5 @@ Video Disk Recorder Revision History is changed while the recording is already going on. - The margins for timer recordings are now always limited to the duration of the previous and next event. +- Spawned timers that don't use VPS now automatically adjust their start/stop times + to changes in the respective event's times. diff --git a/MANUAL b/MANUAL index aee21821..640a9094 100644 --- a/MANUAL +++ b/MANUAL @@ -556,6 +556,9 @@ The following rules apply to pattern timers: - Recording is done according to the event's begin/end times, either by adding the start/stop margins (for non-VPS timers) or by using the event's running status (for VPS timers). +- If the times of the event change, a non-VPS pattern timer automatically adjusts + itself to the new times. This also happens if the start/stop margins are changed + in the setup. - The recording of a pattern timer is stored under the given file name, just like regular timers do. In addition to the "TITLE" and "EPISODE" macros the file name of a pattern timer can also use "{<}" and "{>}" to reference the part of the diff --git a/timers.c b/timers.c index 6b881fd1..769041b3 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 5.6 2021/04/04 13:38:13 kls Exp $ + * $Id: timers.c 5.7 2021/04/06 08:48:35 kls Exp $ */ #include "timers.h" @@ -27,7 +27,7 @@ cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel) { id = 0; startTime = stopTime = 0; - scheduleState = -1; + scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = -1; deferred = 0; pending = inVpsMargin = false; flags = tfNone; @@ -176,7 +176,7 @@ cTimer::cTimer(const cEvent *Event, const char *FileName, const cTimer *PatternT { id = 0; startTime = stopTime = 0; - scheduleState = -1; + scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = -1; deferred = 0; pending = inVpsMargin = false; flags = tfActive; @@ -242,7 +242,7 @@ cTimer& cTimer::operator= (const cTimer &Timer) id = Timer.id; startTime = Timer.startTime; stopTime = Timer.stopTime; - scheduleState = -1; + scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = -1; deferred = 0; pending = Timer.pending; inVpsMargin = Timer.inVpsMargin; @@ -724,7 +724,7 @@ bool cTimer::SpawnPatternTimers(const cSchedules *Schedules, cTimers *Timers) bool TimersSpawned = false; const cSchedule *Schedule = Schedules->GetSchedule(Channel()); if (Schedule && Schedule->Events()->First()) { - if (Schedule->Modified(scheduleState)) { + if (Schedule->Modified(scheduleStateSpawn)) { time_t Now = time(NULL); for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) { if (Matches(e) != tmNone) { @@ -757,6 +757,41 @@ bool cTimer::SpawnPatternTimers(const cSchedules *Schedules, cTimers *Timers) return TimersSpawned; } +bool cTimer::AdjustSpawnedTimer(void) +{ + if (Event()) { + if (const cSchedule *Schedule = Event()->Schedule()) { // events may be deleted from their schedule in cSchedule::DropOutdated()! + if (Schedule->Modified(scheduleStateAdjust)) { + // Adjust the timer to shifted start/stop times of the event if necessary: + time_t tstart = Event()->StartTime(); + time_t tstop = Event()->EndTime(); + int MarginStart = 0; + int MarginStop = 0; + CalcMargins(MarginStart, MarginStop, Event()); + tstart -= MarginStart; + tstop += MarginStop; + // Event start/end times are given in "seconds since the epoch". Some broadcasters use values + // that result in full minutes (with zero seconds), while others use any values. VDR's timers + // use times given in full minutes, truncating any seconds. Thus we only react if the start/stop + // times of the timer are off by at least one minute: + if (abs(StartTime() - tstart) >= 60 || abs(StopTime() - tstop) >= 60) { + cTimer OldTimer = *this; + struct tm tm_r; + struct tm *time = localtime_r(&tstart, &tm_r); + SetDay(cTimer::SetTime(tstart, 0)); + SetStart(time->tm_hour * 100 + time->tm_min); + time = localtime_r(&tstop, &tm_r); + SetStop(time->tm_hour * 100 + time->tm_min); + Matches(); + isyslog("timer %s times changed to %s-%s", *ToDescr(), *TimeString(tstart), *TimeString(tstop)); + return true; + } + } + } + } + return false; +} + void cTimer::TriggerRespawn(void) { if (HasFlags(tfSpawned) || IsPatternTimer()) { @@ -777,7 +812,7 @@ bool cTimer::SetEventFromSchedule(const cSchedules *Schedules) return SetEvent(NULL); const cSchedule *Schedule = Schedules->GetSchedule(Channel()); if (Schedule && Schedule->Events()->First()) { - if (Schedule->Modified(scheduleState)) { + if (Schedule->Modified(scheduleStateSet)) { const cEvent *Event = NULL; if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) { // VPS timers only match if their start time exactly matches the event's VPS time: @@ -828,11 +863,11 @@ bool cTimer::SetEvent(const cEvent *Event) if (Event) { isyslog("timer %s set to event %s", *ToDescr(), *Event->ToDescr()); Event->IncNumTimers(); - Event->Schedule()->Modified(scheduleState); // to get the current state + Event->Schedule()->Modified(scheduleStateSet); // to get the current state } else { isyslog("timer %s set to no event", *ToDescr()); - scheduleState = -1; + scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = -1; } event = Event; return true; @@ -1132,6 +1167,18 @@ bool cTimers::SpawnPatternTimers(const cSchedules *Schedules) return TimersModified; } +bool cTimers::AdjustSpawnedTimers(void) +{ + bool TimersModified = false; + for (cTimer *ti = First(); ti; ti = Next(ti)) { + if (ti->Local()) { + if (ti->HasFlags(tfSpawned) && !ti->HasFlags(tfVps)) + TimersModified |= ti->AdjustSpawnedTimer(); + } + } + return TimersModified; +} + bool cTimers::DeleteExpired(void) { if (time(NULL) - lastDeleteExpired < 30) diff --git a/timers.h b/timers.h index ae3ab2f6..0c707f7b 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 5.3 2021/04/04 13:38:13 kls Exp $ + * $Id: timers.h 5.4 2021/04/06 08:48:35 kls Exp $ */ #ifndef __TIMERS_H @@ -33,7 +33,9 @@ class cTimer : public cListObject { private: int id; mutable time_t startTime, stopTime; - int scheduleState; + int scheduleStateSet; + int scheduleStateSpawn; + int scheduleStateAdjust; mutable time_t deferred; ///< Matches(time_t, ...) will return false if the current time is before this value bool pending, inVpsMargin; uint flags; @@ -99,6 +101,7 @@ public: void SetId(int Id); void SpawnPatternTimer(const cEvent *Event, cTimers *Timers); bool SpawnPatternTimers(const cSchedules *Schedules, cTimers *Timers); + bool AdjustSpawnedTimer(void); void TriggerRespawn(void); bool SetEventFromSchedule(const cSchedules *Schedules); bool SetEvent(const cEvent *Event); @@ -196,6 +199,7 @@ public: const cTimer *UsesChannel(const cChannel *Channel) const; bool SetEvents(const cSchedules *Schedules); bool SpawnPatternTimers(const cSchedules *Schedules); + bool AdjustSpawnedTimers(void); bool DeleteExpired(void); void Add(cTimer *Timer, cTimer *After = NULL); void Ins(cTimer *Timer, cTimer *Before = NULL); diff --git a/vdr.c b/vdr.c index 8b8cca39..d3a29872 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.tvdr.de * - * $Id: vdr.c 5.1 2020/12/26 15:49:01 kls Exp $ + * $Id: vdr.c 5.2 2021/04/06 08:48:35 kls Exp $ */ #include @@ -1109,8 +1109,8 @@ int main(int argc, char *argv[]) Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll); // setting events shall not trigger a remote timer poll... if (Timers->SetEvents(Schedules)) TimersModified = true; - if (Timers->SpawnPatternTimers(Schedules)) { - StateKeySVDRPRemoteTimersPoll.Reset(); // ...but spawning new timers must! + if (Timers->SpawnPatternTimers(Schedules) | Timers->AdjustSpawnedTimers()) { // this really is '|', not '||'! + StateKeySVDRPRemoteTimersPoll.Reset(); // ...but spawning new timers or adjusting spawned timers must! TimersModified = true; } SchedulesStateKey.Remove();