45 time_t t = time(NULL);
47 struct tm *now = localtime_r(&t, &tm_r);
50 start = now->tm_hour * 100 + now->tm_min;
54 if (!
Setup.InstantRecordTime &&
channel && (Instant || Pause)) {
57 if (
const cEvent *
Event = Schedule->GetPresentEvent()) {
58 time_t tstart =
Event->StartTime();
59 time_t tstop =
Event->EndTime();
62 tstart =
Event->Vps();
68 tstart -= MarginStart;
72 struct tm *time = localtime_r(&tstart, &tm_r);
73 start = time->tm_hour * 100 + time->tm_min;
74 time = localtime_r(&tstop, &tm_r);
75 stop = time->tm_hour * 100 + time->tm_min;
98 if (strcmp(Pattern,
"*") == 0) {
113 nt.
Set(
const_cast<char *
>(Pattern + strlen(Pattern) - 1));
114 if (AnchorBegin && AnchorEnd) {
115 if (strcmp(Title, Pattern) == 0) {
125 else if (AnchorBegin) {
126 if (strstr(Title, Pattern) == Title) {
132 *After =
cString(Title + strlen(Pattern));
136 else if (AnchorEnd) {
139 *Before =
cString(Title, Title + strlen(Title) - strlen(Pattern));
147 else if (
const char *p = strstr(Title, Pattern)) {
153 *After =
cString(p + strlen(Pattern));
162 if (!Pattern || !Title || !File)
167 if (
MatchPattern(Pattern, Title, &Before, &Match, &After)) {
168 char *Result = strdup(File);
198 if (
Event->Vps() && (PatternTimer ||
Setup.UseVps))
202 channel = Channels->GetByChannelID(
Event->ChannelID(),
true);
204 time_t tstop = tstart +
Event->Duration();
209 tstart -= MarginStart;
213 struct tm *time = localtime_r(&tstart, &tm_r);
216 start = time->tm_hour * 100 + time->tm_min;
217 time = localtime_r(&tstop, &tm_r);
218 stop = time->tm_hour * 100 + time->tm_min;
224 FileName =
Event->Title();
243 event->DecNumTimers();
250 if (&Timer !=
this) {
271 aux = Timer.
aux ? strdup(Timer.
aux) : NULL;
275 event->DecNumTimers();
285 MarginStart =
Setup.MarginStart * 60;
286 MarginStop =
Setup.MarginStop * 60;
291 MarginStart =
max(0,
min(MarginStart, e->Duration() - 60));
293 MarginStop =
max(0,
min(MarginStop, e->Duration() - 60));
327 cString buffer =
cString::sprintf(
"%u:%s:%s:%04d:%04d:%d:%d:%s:%s",
flags, UseChannelID ? *
Channel()->GetChannelID().ToString() : *
itoa(
Channel()->Number()), *
PrintDay(
day,
weekdays,
true),
start,
stop,
priority,
lifetime, *
PatternAndFile(),
aux ?
aux :
"");
340 return (t / 100 * 60 + t % 100) * 60;
357 const char *a = strchr(s,
'@');
358 const char *d = a ? a + 1 : isdigit(*s) ? s : NULL;
360 if (strlen(d) == 10) {
362 if (3 == sscanf(d,
"%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
363 tm_r.tm_year -= 1900;
365 tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
375 int day = strtol(d, &tail, 10);
378 time_t t = time(NULL);
379 int DaysToCheck = 61;
380 for (
int i = -1; i <= DaysToCheck; i++) {
389 if (a || !isdigit(*s)) {
390 if ((a && a - s == 7) || strlen(s) == 7) {
391 for (
const char *p = s + 6; p >= s; p--) {
404#define DAYBUFFERSIZE 64
409 const char *w =
trNOOP(
"MTWTFSS");
410 if (!SingleByteChars)
415 for (
int i = 0; i < sl; i++)
429 localtime_r(&
Day, &tm_r);
430 b += strftime(b,
DAYBUFFERSIZE - (b - buffer),
"%Y-%m-%d", &tm_r);
448 char *channelbuffer = NULL;
449 char *daybuffer = NULL;
450 char *filebuffer = NULL;
461 while (l2 > 0 && isspace(s[l2 - 1]))
463 if (s[l2 - 1] ==
':') {
464 s2 =
MALLOC(
char, l2 + 3);
465 strcat(
strn0cpy(s2, s, l2 + 1),
" \n");
469 if (8 <= sscanf(s,
"%u :%m[^:]:%m[^:]:%d :%d :%d :%d :%m[^:\n]:%m[^\n]", &
flags, &channelbuffer, &daybuffer, &
start, &
stop, &
priority, &
lifetime, &filebuffer, &
aux)) {
476 char *fb = filebuffer;
478 if (
char *p = strchr(fb,
'}')) {
491 channel = Channels->GetByNumber(atoi(channelbuffer));
495 esyslog(
"ERROR: channel %s not defined", channelbuffer);
509 return fprintf(f,
"%s\n", *
ToText(
true)) > 0;
521 return localtime_r(&t, &tm_r)->tm_mday;
527 int weekday = localtime_r(&t, &tm_r)->tm_wday;
528 return weekday == 0 ? 6 : weekday - 1;
539 tm tm = *localtime_r(&t, &tm_r);
551 tm tm = *localtime_r(&t, &tm_r);
552 tm.tm_hour = SecondsFromMidnight / 3600;
553 tm.tm_min = (SecondsFromMidnight % 3600) / 60;
554 tm.tm_sec = SecondsFromMidnight % 60;
570#define EITPRESENTFOLLOWINGRATE 10
571#define EITPRESENTFOLLOWINGGRACE 60
581 int length = end - begin;
592 for (
int i = -1; i <= 7; i++) {
596 time_t b = a + length;
599 if ((!
day || a >=
day) && t < b) {
618#if DEPRECATED_TIMER_MATCHES
622 static bool MatchesDirectlyReported =
false;
623 if (!MatchesDirectlyReported) {
624 esyslog(
"ERROR: cTimer::Matches() called with Directly==true - use cTimer::CalcStartStopTime() instead");
626 MatchesDirectlyReported =
true;
638 static bool MatchesDirectlyReported =
false;
639 if (!MatchesDirectlyReported) {
640 esyslog(
"ERROR: cTimer::Matches() called with Directly==true - use cTimer::CalcStartStopTime() instead");
642 MatchesDirectlyReported =
true;
654 static bool TimeDiffReported =
false;
660 else if (!TimeDiffReported && abs(t - time(NULL)) > 10) {
661 esyslog(
"ERROR: cTimer::Matches() called with invalid time - use cTimer::CalcStartStopTime() instead");
663 TimeDiffReported =
true;
684 const cSchedule *Schedule =
event->Schedule();
691 bool running =
event->IsRunning(
true);
702 return event->IsRunning(
true);
720 return event->StartTime() -
Setup.MarginStart * 60 <= t && t <
event->EndTime() +
Setup.MarginStop * 60;
729#define FULLMATCH 1000
761 if (
Event->IsRunning())
785#define EXPIRELATENCY 60
790 time_t Now = time(NULL);
794 if (ExpireTime <= Now) {
796 const cSchedule *Schedule =
event ?
event->Schedule() : NULL;
799 FirstEvent = Schedule->
Events()->
Next(FirstEvent);
800 else if ((Schedule = Schedules->GetSchedule(
Channel())) != NULL) {
809 if (e->Vps() == Vps) {
811 dsyslog(
"timer %s is waiting for next VPS event %s", *
ToDescr(), *e->ToDescr());
818 dsyslog(
"timer %s has no event, setting expiration to +24h", *
ToDescr());
819 ExpireTime += 3600 * 24;
823 return ExpireTime <= Now;
848 return event->StartTime();
850 return event->StartTime() -
Setup.MarginStart * 60;
859 return event->EndTime();
861 return event->EndTime() +
Setup.MarginStop * 60;
866#define EPGLIMITBEFORE (1 * 3600)
867#define EPGLIMITAFTER (1 * 3600)
888 bool TimersSpawned =
false;
892 time_t Now = time(NULL);
898 if (!Timer && e->EndTime() > Now) {
900 TimersSpawned =
true;
910 Limit +=
Setup.MarginStart * 60;
912 if (e->StartTime() <= Limit) {
915 TimersSpawned =
true;
929 return TimersSpawned;
943 tstart -= MarginStart;
952 struct tm *time = localtime_r(&tstart, &tm_r);
954 SetStart(time->tm_hour * 100 + time->tm_min);
955 time = localtime_r(&tstop, &tm_r);
956 SetStop(time->tm_hour * 100 + time->tm_min);
975 const_cast<cSchedule *
>(Schedule)->SetModified();
992 if (e->StartTime()) {
1011 if (e->EndTime() < TimeFrameBegin)
1013 if (e->StartTime() > TimeFrameEnd)
1017 if (overlap && overlap >= Overlap) {
1018 if (
Event && overlap == Overlap && e->Duration() <=
Event->Duration())
1035 event->DecNumTimers();
1038 Event->IncNumTimers();
1117 isyslog(
"timer %s deferred for %d seconds", *
ToDescr(), Seconds);
1180 Timers->SetExplicitModify();
1185 Timers->SetModified();
1200 if (ti->Id() == Id) {
1201 if (!Remote && !ti->Remote() || Remote && ti->Remote() && strcmp(Remote, ti->Remote()) == 0)
1211 if (!ti->Remote() &&
1212 ti->Channel() == Timer->
Channel() &&
1213 (ti->WeekDays() && ti->WeekDays() == Timer->
WeekDays() || !ti->WeekDays() && ti->Day() == Timer->
Day()) &&
1214 ti->Start() == Timer->
Start() &&
1215 ti->Stop() == Timer->
Stop())
1223 static int LastPending = -1;
1227 if (!ti->Remote() && !ti->Recording() && ti->Matches(t)) {
1228 if (ti->Pending()) {
1229 if (ti->Index() > LastPending) {
1230 LastPending = ti->Index();
1265 if (ti->Event() == Event && ti->Local() && ti->HasFlags(Flags))
1276 if (!ti->Remote() && ti->Recording())
1277 n =
max(n, ti->Priority());
1286 if (!ti->Remote() && !ti->IsPatternTimer()) {
1288 if ((ti->HasFlags(
tfActive)) && (!t0 || ti->
StopTime() > time(NULL) && ti->Compare(*t0) < 0))
1297 return timers.Lock(StateKey,
false, TimeoutMs) ? &
timers : NULL;
1302 return timers.Lock(StateKey,
true, TimeoutMs) ? &
timers : NULL;
1328 if (Timer->Channel() == Channel)
1336 bool TimersModified =
false;
1338 if (!ti->IsPatternTimer())
1339 TimersModified |= ti->SetEventFromSchedule(Schedules);
1341 return TimersModified;
1346 bool TimersModified =
false;
1348 if (ti->IsPatternTimer() && ti->Local()) {
1350 TimersModified |= ti->SpawnPatternTimers(Schedules,
this);
1353 return TimersModified;
1358 bool TimersModified =
false;
1362 TimersModified |= ti->AdjustSpawnedTimer();
1365 return TimersModified;
1368#define DELETE_EXPIRED_TIMEOUT 30
1374 bool TimersModified =
false;
1383 TimersModified =
true;
1388 return TimersModified;
1393 bool Result =
false;
1394 if (!ServerName || !RemoteTimers || RemoteTimers->
Size() == 0) {
1399 if (Timer->
Remote() && (!ServerName || strcmp(Timer->
Remote(), ServerName) == 0)) {
1410 if (ti->Remote() && strcmp(ti->Remote(), ServerName) == 0)
1418 int sr = RemoteTimers->
Size();
1423 int nl = atoi(tl[il]);
1426 int nr = atoi((*RemoteTimers)[ir]);
1428 AddTimer = DelTimer = nl;
1438 AddTimer = atoi((*RemoteTimers)[ir]);
1440 esyslog(
"ERROR: %s: error in timer settings: %s", ServerName, (*RemoteTimers)[ir]);
1447 if (AddTimer && DelTimer) {
1448 if (strcmp(tl[il], (*RemoteTimers)[ir]) != 0) {
1450 char *v = (*RemoteTimers)[ir];
1451 while (*v && *v !=
' ')
1462 esyslog(
"ERROR: %d@%s: error in timer settings: %s", DelTimer, ServerName, v);
1470 else if (AddTimer) {
1471 char *v = (*RemoteTimers)[ir];
1472 while (*v && *v !=
' ')
1475 if (Timer->
Parse(v)) {
1477 Timer->
SetId(AddTimer);
1482 esyslog(
"ERROR: %s: error in timer settings: %s", ServerName, v);
1487 else if (DelTimer) {
1495 esyslog(
"ERROR: oops while storing remote timers!");
1514 if (OldTimer->
Remote() && OldTimer->
Id()) {
1521 else if (!OldTimer || OldTimer->
Local() || !OldTimer->
Id()) {
1522 if (NewTimer->
Local()) {
1523 if (OldTimer && OldTimer->
Id())
1531 int RemoteId = atoi(
SVDRPValue(Response[0]));
1534 NewTimer->
SetId(RemoteId);
1535 if (OldTimer && OldTimer->
Id()) {
1542 else if (NewTimer->
Local()) {
1549 else if (strcmp(OldTimer->
Remote(), NewTimer->
Remote()) == 0) {
1557 int RemoteId = atoi(
SVDRPValue(Response[0]));
1560 NewTimer->
SetId(RemoteId);
1572 return (*(
const cTimer **)a)->Compare(**(
const cTimer **)b);
1578 for (
const cTimer *Timer = Timers->
First(); Timer; Timer = Timers->
Next(Timer))
#define LOCK_CHANNELS_READ
static void BackTrace(cStringList &StringList, int Level=0, bool Mangled=false)
Produces a backtrace and stores it in the given StringList.
cConfig(const char *NeedsLocking=NULL)
const char * FileName(void)
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
cString ToDescr(void) const
time_t EndTime(void) const
void IncNumTimers(void) const
time_t StartTime(void) const
bool HasTimer(void) const
void Ins(cListObject *Object, cListObject *Before=NULL)
void Del(cListObject *Object, bool DeleteObject=true)
void Add(cListObject *Object, cListObject *After=NULL)
cListObject(const cListObject &ListObject)
cListObject * Next(void) const
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
bool Modified(int &State) const
bool PresentSeenWithin(int Seconds) const
const cList< cEvent > * Events(void) const
const cSchedule * GetSchedule(tChannelID ChannelID) const
cSortedTimers(const cTimers *Timers)
static void MsgTimerChange(const cTimer *Timer, eTimerChange Change)
void SortNumerically(void)
static cString sprintf(const char *fmt,...) __attribute__((format(printf
void SetAux(const char *Aux)
time_t stopTime
the time_t value calculated from 'day', 'start' and 'stop'
const char * Aux(void) const
void SetLifetime(int Lifetime)
const char * File(void) const
cString PrintFirstDay(void) const
time_t day
midnight of the day this timer shall hit, or of the first day it shall hit in case of a repeating tim...
int weekdays
bitmask, lowest bits: SSFTWTM (the 'M' is the LSB)
bool IsSingleEvent(void) const
void SetPending(bool Pending)
virtual ~cTimer() override
cTimer(bool Instant=false, bool Pause=false, const cChannel *Channel=NULL)
time_t StopTime(void) const
The stop time of this timer, which is the time as given by the user (for normal timers) or the end ti...
cString PatternAndFile(void) const
bool Recording(void) const
static time_t SetTime(time_t t, int SecondsFromMidnight)
void ClrFlags(uint Flags)
void SetFile(const char *File)
void SetFlags(uint Flags)
int start
the start and stop time of this timer as given by the user,
void SetPriority(int Priority)
void SetDeferred(int Seconds)
time_t StopTimeEvent(void) const
or by the user (for normal timers)
bool AdjustSpawnedTimer(void)
void SetInVpsMargin(bool InVpsMargin)
bool IsPatternTimer(void) const
static int GetWDay(time_t t)
const char * Pattern(void) const
static cString PrintDay(time_t Day, int WeekDays, bool SingleByteChars)
void TriggerRespawn(void)
bool DayMatches(time_t t) const
void SetRemote(const char *Remote)
bool InVpsMargin(void) const
bool SetEvent(const cEvent *Event)
void InvFlags(uint Flags)
int stop
in the form hhmm, with hh (00..23) and mm (00..59) added as hh*100+mm
const cEvent * Event(void) const
static bool ParseDay(const char *s, time_t &Day, int &WeekDays)
bool vpsActive
true if this is a VPS timer and the event is current
cTimer * SpawnPatternTimer(const cEvent *Event, cTimers *Timers)
time_t StartTime(void) const
The start time of this timer, which is the time as given by the user (for normal timers) or the start...
const cChannel * Channel(void) const
void CalcMargins(int &MarginStart, int &MarginStop, const cEvent *Event)
cString ToDescr(void) const
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
bool SetEventFromSchedule(const cSchedules *Schedules)
void SetRecording(bool Recording)
time_t StartTimeEvent(void) const
the start/stop times as given by the event (for VPS timers), by event plus margins (for spawned non-V...
void SetPattern(const char *Pattern)
char pattern[NAME_MAX *2+1]
static int TimeToInt(int t)
time_t deferred
Matches(time_t, ...) will return false if the current time is before this value.
static int GetMDay(time_t t)
bool HasFlags(uint Flags) const
void CalcStartStopTime(time_t &startTime, time_t &stopTime, time_t t=0) const
Calculates the raw start and stop time of this timer, as given by the user in the timer definition.
const char * Remote(void) const
cTimer & operator=(const cTimer &Timer)
time_t vpsNotRunning
the time when a VPS event's running status changed to "not running"
void SetWeekDays(int WeekDays)
time_t VpsTime(time_t t=0) const
Returns the VPS time of this timer.
bool Parse(const char *s)
cString ToText(bool UseChannelID=false) const
static time_t IncDay(time_t t, int Days)
bool SpawnPatternTimers(const cSchedules *Schedules, cTimers *Timers)
static bool Load(const char *FileName)
int GetMaxPriority(void) const
Returns the maximum priority of all local timers that are currently recording.
const cTimer * UsesChannel(const cChannel *Channel) const
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.
const cTimer * GetById(int Id, const char *Remote=NULL) const
void Add(cTimer *Timer, cTimer *After=NULL)
static cTimers * GetTimersWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for write access.
void Del(cTimer *Timer, bool DeleteObject=true)
static const cTimers * GetTimersRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for read access.
const cTimer * GetTimer(const cTimer *Timer) const
const cTimer * GetMatch(time_t t) const
const cTimer * GetTimerForEvent(const cEvent *Event, eTimerFlags Flags=tfNone) const
void Ins(cTimer *Timer, cTimer *Before=NULL)
bool SpawnPatternTimers(const cSchedules *Schedules)
const cTimer * GetNextActiveTimer(void) const
bool DeleteExpired(bool Force)
bool SetEvents(const cSchedules *Schedules)
bool AdjustSpawnedTimers(void)
static int NewTimerId(void)
void Sort(__compar_fn_t Compare)
virtual void Append(T Data)
cVector(const cVector &Vector)
#define TIMERPATTERN_BEGIN
#define TIMERMACRO_BEFORE
#define TIMERMACRO_EPISODE
#define TIMERPATTERN_AVOID
#define LOCK_SCHEDULES_READ
#define LOCK_SCHEDULES_WRITE
@ RunningStatusNotRunning
cDoneRecordings DoneRecordingsPattern
static tChannelID FromString(const char *s)
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response)
Sends the given SVDRP Command string to the remote VDR identified by ServerName and collects all of t...
const char * SVDRPValue(const char *s)
Returns the actual value of the given SVDRP response string, skipping the three digit reply code and ...
int SVDRPCode(const char *s)
Returns the value of the three digit reply code of the given SVDRP response string.
#define DELETE_EXPIRED_TIMEOUT
static bool RemoteTimerError(const cTimer *Timer, cString *Msg)
static cString MakePatternFileName(const char *Pattern, const char *Title, const char *Episode, const char *File)
static bool MatchPattern(const char *Pattern, const char *Title, cString *Before=NULL, cString *Match=NULL, cString *After=NULL)
#define EITPRESENTFOLLOWINGGRACE
#define EITPRESENTFOLLOWINGRATE
static int CompareTimers(const void *a, const void *b)
bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer, cString *Msg)
Performs any operations necessary to synchronize changes to a timer between peer VDR machines.
#define LOCK_TIMERS_WRITE
bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer=NULL, cString *Msg=NULL)
Performs any operations necessary to synchronize changes to a timer between peer VDR machines.