diff options
author | Johann Friedrichs <johann.friedrichs@web.de> | 2018-03-21 12:14:55 +0100 |
---|---|---|
committer | Johann Friedrichs <johann.friedrichs@web.de> | 2018-03-21 12:14:55 +0100 |
commit | e8a0e569152c50d6084f252d12854b8fd4e74466 (patch) | |
tree | 5a90ef7ea08ff2096df157ca109c5268cdc04903 /searchtimer_thread.c | |
parent | 9c7d95ff8d6ba965cb23147507a859b1fd0491d6 (diff) | |
download | vdr-plugin-epgsearch-e8a0e569152c50d6084f252d12854b8fd4e74466.tar.gz vdr-plugin-epgsearch-e8a0e569152c50d6084f252d12854b8fd4e74466.tar.bz2 |
unified indentation
Diffstat (limited to 'searchtimer_thread.c')
-rw-r--r-- | searchtimer_thread.c | 1708 |
1 files changed, 812 insertions, 896 deletions
diff --git a/searchtimer_thread.c b/searchtimer_thread.c index ba54566..a879487 100644 --- a/searchtimer_thread.c +++ b/searchtimer_thread.c @@ -58,996 +58,912 @@ cSearchResults cSearchTimerThread::announceList; bool cSearchTimerThread::justRunning = false; cSearchTimerThread::cSearchTimerThread(cPluginEpgsearch* thePlugin) - : cThread("EPGSearch: searchtimer") + : cThread("EPGSearch: searchtimer") { - m_plugin = thePlugin; - m_Active = false; - m_lastUpdate = time(NULL); + m_plugin = thePlugin; + m_Active = false; + m_lastUpdate = time(NULL); } -cSearchTimerThread::~cSearchTimerThread() { - if (m_Active) - Stop(); +cSearchTimerThread::~cSearchTimerThread() +{ + if (m_Active) + Stop(); } void cSearchTimerThread::Init(cPluginEpgsearch* thePlugin, bool activatePermanently) { - if (activatePermanently) - { - EPGSearchConfig.useSearchTimers = 1; - thePlugin->SetupStore("UseSearchTimers", EPGSearchConfig.useSearchTimers); - } - if (!EPGSearchConfig.useSearchTimers) - return; - if (m_Instance == NULL) { - m_Instance = new cSearchTimerThread(thePlugin); - m_Instance->Start(); - } + if (activatePermanently) { + EPGSearchConfig.useSearchTimers = 1; + thePlugin->SetupStore("UseSearchTimers", EPGSearchConfig.useSearchTimers); + } + if (!EPGSearchConfig.useSearchTimers) + return; + if (m_Instance == NULL) { + m_Instance = new cSearchTimerThread(thePlugin); + m_Instance->Start(); + } } -void cSearchTimerThread::Exit(void) { - if (m_Instance != NULL) { - m_Instance->Stop(); - DELETENULL(m_Instance); - } +void cSearchTimerThread::Exit(void) +{ + if (m_Instance != NULL) { + m_Instance->Stop(); + DELETENULL(m_Instance); + } } -void cSearchTimerThread::Stop(void) { - m_Active = false; - Wait.Signal(); - Cancel(6); +void cSearchTimerThread::Stop(void) +{ + m_Active = false; + Wait.Signal(); + Cancel(6); } const cTimer *cSearchTimerThread::GetTimer(cSearchExt *searchExt, const cEvent *pEvent, bool& bTimesMatchExactly) { - LOCK_TIMERS_READ; - LOCK_CHANNELS_READ; - const cChannel *channel = Channels->GetByChannelID(pEvent->ChannelID(), true, true); - if (!channel) - return NULL; - - struct tm tm_r; - - bool UseVPS = searchExt->useVPS && pEvent->Vps() && Setup.UseVps; - time_t eStart = pEvent->StartTime(); - time_t eStop = pEvent->EndTime(); - int eDuration = pEvent->Duration(); - - int AllowedDiff = (eDuration < 10*60) ? (eDuration / 60): 10; // allowed start/stop difference - - int testVpsStart = 0; - int testVpsStop = 0; - time_t testVpsDay = 0; - if (UseVPS) - { - eStart = pEvent->Vps(); - eStop = eStart + pEvent->Duration(); - cTimer VpsTestTimer(pEvent); - testVpsStart = cTimer::TimeToInt(VpsTestTimer.Start()); - testVpsStop = cTimer::TimeToInt(VpsTestTimer.Stop()); - testVpsDay = VpsTestTimer.Day(); - } - - tm *tmStartEv = localtime_r(&eStart, &tm_r); - - for (const cTimer *ti = Timers->First(); ti; ti = Timers->Next(ti)) - { - if (ti->Channel() != channel) - continue; - - if (ti->WeekDays()) // ignore serial timers - continue; - - if (ti->Remote()) // ignore remote timers - continue; - // ignore manual timers if this search could modify them - if ((searchExt->action == searchTimerActionRecord || searchExt->action == searchTimerActionInactiveRecord) && TriggeredFromSearchTimerID(ti) == -1) // manual timer - continue; - - if (UseVPS && ti->HasFlags(tfVps)) - { - if (testVpsDay != ti->Day()) continue; - int timerVpsStart = cTimer::TimeToInt(ti->Start()); - int timerVpsStop = cTimer::TimeToInt(ti->Stop()); - - if (abs(testVpsStart - timerVpsStart) > AllowedDiff * 60) continue; - if (abs(testVpsStop - timerVpsStop) > AllowedDiff * 60) continue; - - bTimesMatchExactly = (testVpsStart == timerVpsStart && testVpsStop == timerVpsStop); - return ti; - } - else - { - time_t tStart = ti->StartTime() + searchExt->MarginStart * 60; - time_t tStop = ti->StopTime() - searchExt->MarginStop * 60; - tm *tmStartTi = localtime_r(&tStart, &tm_r); - if (tmStartEv->tm_mday != tmStartTi->tm_mday) + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + const cChannel *channel = Channels->GetByChannelID(pEvent->ChannelID(), true, true); + if (!channel) + return NULL; + + struct tm tm_r; + + bool UseVPS = searchExt->useVPS && pEvent->Vps() && Setup.UseVps; + time_t eStart = pEvent->StartTime(); + time_t eStop = pEvent->EndTime(); + int eDuration = pEvent->Duration(); + + int AllowedDiff = (eDuration < 10 * 60) ? (eDuration / 60) : 10; // allowed start/stop difference + + int testVpsStart = 0; + int testVpsStop = 0; + time_t testVpsDay = 0; + if (UseVPS) { + eStart = pEvent->Vps(); + eStop = eStart + pEvent->Duration(); + cTimer VpsTestTimer(pEvent); + testVpsStart = cTimer::TimeToInt(VpsTestTimer.Start()); + testVpsStop = cTimer::TimeToInt(VpsTestTimer.Stop()); + testVpsDay = VpsTestTimer.Day(); + } + + tm *tmStartEv = localtime_r(&eStart, &tm_r); + + for (const cTimer *ti = Timers->First(); ti; ti = Timers->Next(ti)) { + if (ti->Channel() != channel) + continue; + + if (ti->WeekDays()) // ignore serial timers continue; - // some providers change EPG times only for a few seconds - // ignore this to avoid search timer mails because of such changes - bTimesMatchExactly = (abs(tStart -eStart) < 60 && abs(tStop - eStop) < 60); + if (ti->Remote()) // ignore remote timers + continue; + // ignore manual timers if this search could modify them + if ((searchExt->action == searchTimerActionRecord || searchExt->action == searchTimerActionInactiveRecord) && TriggeredFromSearchTimerID(ti) == -1) // manual timer + continue; + + if (UseVPS && ti->HasFlags(tfVps)) { + if (testVpsDay != ti->Day()) continue; + int timerVpsStart = cTimer::TimeToInt(ti->Start()); + int timerVpsStop = cTimer::TimeToInt(ti->Stop()); - if (abs(tStart - eStart) < AllowedDiff * 60 && abs(tStop - eStop) < AllowedDiff * 60) // accept a difference of max 10 min., but only if the event duration is more than 10 minutes + if (abs(testVpsStart - timerVpsStart) > AllowedDiff * 60) continue; + if (abs(testVpsStop - timerVpsStop) > AllowedDiff * 60) continue; + + bTimesMatchExactly = (testVpsStart == timerVpsStart && testVpsStop == timerVpsStop); return ti; - } - } - return NULL; + } else { + time_t tStart = ti->StartTime() + searchExt->MarginStart * 60; + time_t tStop = ti->StopTime() - searchExt->MarginStop * 60; + tm *tmStartTi = localtime_r(&tStart, &tm_r); + if (tmStartEv->tm_mday != tmStartTi->tm_mday) + continue; + + // some providers change EPG times only for a few seconds + // ignore this to avoid search timer mails because of such changes + bTimesMatchExactly = (abs(tStart - eStart) < 60 && abs(tStop - eStop) < 60); + + if (abs(tStart - eStart) < AllowedDiff * 60 && abs(tStop - eStop) < AllowedDiff * 60) // accept a difference of max 10 min., but only if the event duration is more than 10 minutes + return ti; + } + } + return NULL; } bool cSearchTimerThread::TimerWasModified(const cTimer* t) { - if (!t) return false; - if (t->HasFlags(tfVps)) return false; // if timer uses VPS we ignore user changes - - char* start = GetAuxValue(t, "start"); - char* stop = GetAuxValue(t, "stop"); - bool bMod = false; - if (start || stop) - { - time_t StartTime = time_t(atol(start)); - time_t StopTime = time_t(atol(stop)); - if (abs(t->StartTime() - StartTime) >= 60 || abs(t->StopTime() -StopTime) >= 60) - bMod = true; - } - if (start) free(start); - if (stop) free(stop); - return bMod; + if (!t) return false; + if (t->HasFlags(tfVps)) return false; // if timer uses VPS we ignore user changes + + char* start = GetAuxValue(t, "start"); + char* stop = GetAuxValue(t, "stop"); + bool bMod = false; + if (start || stop) { + time_t StartTime = time_t(atol(start)); + time_t StopTime = time_t(atol(stop)); + if (abs(t->StartTime() - StartTime) >= 60 || abs(t->StopTime() - StopTime) >= 60) + bMod = true; + } + if (start) free(start); + if (stop) free(stop); + return bMod; } void cSearchTimerThread::Action(void) { - if (EPGSearchConfig.useExternalSVDRP && !epgsSVDRP::cSVDRPClient::SVDRPSendCmd) - { - LogFile.eSysLog("ERROR - SVDRPSend script not specified or does not exist (use -f option)"); - return; - } - SetPriority(SEARCHTIMER_NICE); - - m_Active = true; - // let VDR do its startup - if (!cPluginEpgsearch::VDR_readyafterStartup) - LogFile.Log(2, "SearchTimerThread: waiting for VDR to become ready..."); - while(Running() && m_Active && !cPluginEpgsearch::VDR_readyafterStartup) - Wait.Wait(1000); - - time_t nextUpdate = time(NULL); - while (m_Active && Running()) - { - time_t now = time(NULL); - bool needUpdate = NeedUpdate(); - if (now >= nextUpdate || needUpdate) - { - justRunning = true; - - if (updateForced & UPDS_WITH_EPGSCAN) - { - LogFile.Log(1,"starting EPG scan before search timer update"); - EITScanner.ForceScan(); - do - { - Wait.Wait(1000); - } - while(EITScanner.Active() && m_Active && Running()); - LogFile.Log(1,"EPG scan finished"); - } - // wait if TimersWriteLock is set or waited for - { - LOCK_TIMERS_WRITE; - Timers->SetExplicitModify(); - } - LogFile.iSysLog("search timer update started"); - - UserVars.ResetCache(); // reset internal cache of user vars - cTimerObjList* pOutdatedTimers = NULL; - - // for thread safeness we work with a copy of the current searches, - // because SVDRP would not work if the main thread would be locked - cSearchExts* localSearchExts = SearchExts.Clone(); - localSearchExts->SortBy(CompareSearchExtPrioDescTerm); - cSearchExt *searchExt = localSearchExts->First(); - // reset announcelist - announceList.Clear(); - while (searchExt && m_Active && Running()) - { - if (!searchExt->IsActiveAt(now)) - { - searchExt = localSearchExts->Next(searchExt); - continue; + if (EPGSearchConfig.useExternalSVDRP && !epgsSVDRP::cSVDRPClient::SVDRPSendCmd) { + LogFile.eSysLog("ERROR - SVDRPSend script not specified or does not exist (use -f option)"); + return; + } + SetPriority(SEARCHTIMER_NICE); + + m_Active = true; + // let VDR do its startup + if (!cPluginEpgsearch::VDR_readyafterStartup) + LogFile.Log(2, "SearchTimerThread: waiting for VDR to become ready..."); + while (Running() && m_Active && !cPluginEpgsearch::VDR_readyafterStartup) + Wait.Wait(1000); + + time_t nextUpdate = time(NULL); + while (m_Active && Running()) { + time_t now = time(NULL); + bool needUpdate = NeedUpdate(); + if (now >= nextUpdate || needUpdate) { + justRunning = true; + + if (updateForced & UPDS_WITH_EPGSCAN) { + LogFile.Log(1, "starting EPG scan before search timer update"); + EITScanner.ForceScan(); + do { + Wait.Wait(1000); + } while (EITScanner.Active() && m_Active && Running()); + LogFile.Log(1, "EPG scan finished"); } - pOutdatedTimers = searchExt->GetTimerList(pOutdatedTimers); - - cSearchResults* pSearchResults = searchExt->Run(-1, true); - if (!pSearchResults) + // wait if TimersWriteLock is set or waited for { - searchExt = localSearchExts->Next(searchExt); - continue; + LOCK_TIMERS_WRITE; + Timers->SetExplicitModify(); + } + LogFile.iSysLog("search timer update started"); + + UserVars.ResetCache(); // reset internal cache of user vars + cTimerObjList* pOutdatedTimers = NULL; + + // for thread safeness we work with a copy of the current searches, + // because SVDRP would not work if the main thread would be locked + cSearchExts* localSearchExts = SearchExts.Clone(); + localSearchExts->SortBy(CompareSearchExtPrioDescTerm); + cSearchExt *searchExt = localSearchExts->First(); + // reset announcelist + announceList.Clear(); + while (searchExt && m_Active && Running()) { + if (!searchExt->IsActiveAt(now)) { + searchExt = localSearchExts->Next(searchExt); + continue; + } + pOutdatedTimers = searchExt->GetTimerList(pOutdatedTimers); + + cSearchResults* pSearchResults = searchExt->Run(-1, true); + if (!pSearchResults) { + searchExt = localSearchExts->Next(searchExt); + continue; + } + pSearchResults->SortBy(CompareEventTime); + + if (searchExt->pauseOnNrRecordings > 0) + searchExt->CheckExistingRecordings(pSearchResults); + + for (cSearchResult* pResultObj = pSearchResults->First(); + pResultObj; + pResultObj = pSearchResults->Next(pResultObj)) { + if (!Running()) break; + const cEvent* pEvent = pResultObj->event; + if (!pEvent) + continue; + + { + LOCK_CHANNELS_READ; + const cChannel *channel = Channels->GetByChannelID(pEvent->ChannelID(), true, true); + if (!channel) + continue; + } + + int index = 0; + cTimer *timer = new cTimer(pEvent); + + // create the file + char* file = NULL; + if ((file = searchExt->BuildFile(pEvent)) != NULL) { + while (strstr(file, "!^pipe^!")) file = strreplace(file, "!^pipe^!", "|"); // revert the translation of '|' in BuildFile + if (strstr(file, "!^invalid^!") || strlen(file) == 0) { + LogFile.eSysLog("Skipping timer due to invalid or empty filename"); + if (time(NULL) <= timer->StopTime()) + pOutdatedTimers->DelTimer(timer); + delete timer; + free(file); + continue; + } + timer->SetFile(file); + free(file); + } + int Priority = searchExt->Priority; + int Lifetime = searchExt->Lifetime; + + // search for an already existing timer + bool bTimesMatchExactly = false; + const cTimer *t = GetTimer(searchExt, pEvent, bTimesMatchExactly); + + char* Summary = NULL; + uint timerMod = tmNoChange; + + if (t) { + // already exists + pOutdatedTimers->DelTimer(t); + + if (!t->HasFlags(tfActive)) { + // do not update inactive timers + LogFile.Log(2, "timer for '%s~%s' (%s - %s, channel %d) not active - won't be touched", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); + delete timer; + continue; + } + + int triggerID = TriggeredFromSearchTimerID(t); + if (!pResultObj->needsTimer && !t->Recording()) { // not needed + if (triggerID == searchExt->ID) { + LogFile.Log(1, "delete timer for '%s~%s' (%s - %s, channel %d)", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); + RemoveTimer(t, pEvent); + } else if (triggerID == -1) { //manual timer + LogFile.Log(2, "keep obsolete timer for '%s~%s' (%s - %s, channel %d) - was manually created", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); + } + delete timer; + continue; + } + if (TimerWasModified(t)) { // don't touch timer modified by user + LogFile.Log(2, "timer for '%s~%s' (%s - %s, channel %d) modified by user - won't be touched", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); + delete timer; + continue; + } + if (triggerID > -1 && triggerID != searchExt->ID) { + LogFile.Log(2, "timer for '%s~%s' (%s - %s, channel %d) already created by search id %d - won't be touched", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent), triggerID); + delete timer; + continue; + } + + char* pFile = NULL; // File is prepared for svdrp, so prepare t->File for comparison too + msprintf(&pFile, "%s", t->File()); + pFile = strreplace(pFile, ':', '|'); + pFile = strreplace(pFile, " ~", "~"); + pFile = strreplace(pFile, "~ ", "~"); + + Summary = SummaryExtended(searchExt, t, pEvent); + + if (bTimesMatchExactly && strcmp(pFile, timer->File()) == 0 + && (t->Aux() != NULL && strcmp(t->Aux(), Summary) == 0) + ) { + // dir, title, episode name and summary have not changed + if (Summary) free(Summary); + delete timer; + free(pFile); + continue; + } else { + if (!bTimesMatchExactly) timerMod = (uint)timerMod | tmStartStop; + if (strcmp(pFile, timer->File()) != 0) timerMod |= tmFile; + if (t->Aux() != NULL && strcmp(t->Aux(), Summary) != 0) { + char* oldEventID = GetAuxValue(t, "eventid"); + char* newEventID = GetAuxValue(Summary, "eventid"); + if (oldEventID && newEventID && strcmp(oldEventID, newEventID) != 0) + timerMod |= tmAuxEventID; + free(oldEventID); + free(newEventID); + } + + if (LogFile.Level() >= 3) { // output reasons for a timer modification + if (timerMod & tmStartStop) + LogFile.Log(3, "timer for '%s~%s' (%s - %s, channel %d) : start/stop has changed", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); + if (timerMod & tmFile) + LogFile.Log(3, "timer for '%s~%s' (%s - %s, channel %d) : title and/or episdode has changed (old: %s, new: %s", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent), timer ? timer->File() : "", pFile); + if (timerMod & tmAuxEventID) + LogFile.Log(3, "timer for '%s~%s' (%s - %s, channel %d) : aux info for event id has changed", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); + } + index = t->Id(); + Priority = t->Priority(); + Lifetime = t->Lifetime(); + } + free(pFile); + + if (t->Recording() && t->StopTime() == timer->StopTime()) { + // only update recording timers if stop time has changed, since all other settings can't be modified + LogFile.Log(2, "timer for '%s~%s' (%s - %s, channel %d) already recording - no changes possible", pEvent->Title() ? pEvent->Title() : "no title", pEvent->ShortText() ? pEvent->ShortText() : "no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); + delete timer; + continue; + } + } else { + if (!pResultObj->needsTimer) { + delete timer; + continue; + } + } + + if (searchExt->action == searchTimerActionAnnounceViaOSD) { + if (t || // timer already exists or + NoAnnounces.InList(pEvent) || // announcement not wanted anymore or + (EPGSearchConfig.noAnnounceWhileReplay && + cDevice::PrimaryDevice()->Replaying() && + !(updateForced & UPDS_WITH_OSD)) // no announce while replay within automatic updates + ) { + if (Summary) free(Summary); + delete timer; + continue; + } + if (!announceList.Lookup(pEvent)) + announceList.Add(new cSearchResult(pEvent, searchExt->ID)); + + if (Summary) free(Summary); + delete timer; + continue; + } + + if (searchExt->action == searchTimerActionAnnounceViaMail) { + if (t || // timer already exists or + NoAnnounces.InList(pEvent) || + pEvent->StartTime() < time(NULL)) { // already started? + if (Summary) free(Summary); + delete timer; + continue; + } + mailNotifier.AddAnnounceEventNotification(pEvent->EventID(), pEvent->ChannelID(), searchExt->ID); + + if (Summary) free(Summary); + delete timer; + continue; + } + if (searchExt->action == searchTimerActionSwitchOnly || + searchExt->action == searchTimerActionAnnounceAndSwitch) { // add to switch list + time_t now = time(NULL); + if (now < pEvent->StartTime()) { + if (!SwitchTimers.InSwitchList(pEvent)) { + cMutexLock SwitchTimersLock(&SwitchTimers); + int mode = 0; + if (searchExt->action == searchTimerActionAnnounceAndSwitch) + mode = 2; + LogFile.Log(3, "adding switch timer event for '%s~%s' (%s - %s); search timer: '%s'", pEvent->Title(), pEvent->ShortText() ? pEvent->ShortText() : "", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); + SwitchTimers.Add(new cSwitchTimer(pEvent, searchExt->switchMinsBefore, mode, + searchExt->unmuteSoundOnSwitch)); + SwitchTimers.Save(); + cSwitchTimerThread::Init(); + } + } + if (Summary) free(Summary); + delete timer; + continue; + } + + if (AddModTimer(timer, index, searchExt, pEvent, Priority, Lifetime, Summary, timerMod)) { + if (index == 0) + LogFile.Log(1, "added timer for '%s~%s' (%s - %s); search timer: '%s'", pEvent->Title(), pEvent->ShortText() ? pEvent->ShortText() : "", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); + else + LogFile.Log(1, "modified timer %d for '%s~%s' (%s - %s); search timer: '%s'", index, pEvent->Title(), pEvent->ShortText() ? pEvent->ShortText() : "", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); + } + if (Summary) free(Summary); + delete timer; + } + delete pSearchResults; + searchExt = localSearchExts->Next(searchExt); } - pSearchResults->SortBy(CompareEventTime); - - if (searchExt->pauseOnNrRecordings > 0) - searchExt->CheckExistingRecordings(pSearchResults); - for (cSearchResult* pResultObj = pSearchResults->First(); - pResultObj; - pResultObj = pSearchResults->Next(pResultObj)) - { - if (!Running()) break; - const cEvent* pEvent = pResultObj->event; - if (!pEvent) - continue; - - { - LOCK_CHANNELS_READ; - const cChannel *channel = Channels->GetByChannelID(pEvent->ChannelID(), true, true); - if (!channel) - continue; - } - - int index = 0; - cTimer *timer = new cTimer(pEvent); - - // create the file - char* file = NULL; - if ((file = searchExt->BuildFile(pEvent)) != NULL) - { - while(strstr(file, "!^pipe^!")) file = strreplace(file, "!^pipe^!", "|"); // revert the translation of '|' in BuildFile - if (strstr(file, "!^invalid^!") || strlen(file) == 0) - { - LogFile.eSysLog("Skipping timer due to invalid or empty filename"); - if (time(NULL) <= timer->StopTime()) - pOutdatedTimers->DelTimer(timer); - delete timer; - free(file); - continue; - } - timer->SetFile(file); - free(file); - } - int Priority = searchExt->Priority; - int Lifetime = searchExt->Lifetime; - - // search for an already existing timer - bool bTimesMatchExactly = false; - const cTimer *t = GetTimer(searchExt, pEvent, bTimesMatchExactly); - - char* Summary = NULL; - uint timerMod = tmNoChange; - - if (t) - { // already exists - pOutdatedTimers->DelTimer(t); - - if (!t->HasFlags(tfActive)) - { // do not update inactive timers - LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) not active - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); - delete timer; - continue; - } - - int triggerID = TriggeredFromSearchTimerID(t); - if (!pResultObj->needsTimer && !t->Recording()) // not needed - { - if (triggerID == searchExt->ID) - { - LogFile.Log(1,"delete timer for '%s~%s' (%s - %s, channel %d)", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); - RemoveTimer(t, pEvent); - } - else if (triggerID == -1) //manual timer - { - LogFile.Log(2,"keep obsolete timer for '%s~%s' (%s - %s, channel %d) - was manually created", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); - } - delete timer; - continue; - } - if (TimerWasModified(t)) // don't touch timer modified by user - { - LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) modified by user - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); - delete timer; - continue; - } - if (triggerID > -1 && triggerID != searchExt->ID) - { - LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) already created by search id %d - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent), triggerID); - delete timer; - continue; - } - - char* pFile = NULL; // File is prepared for svdrp, so prepare t->File for comparison too - msprintf(&pFile, "%s", t->File()); - pFile = strreplace(pFile, ':', '|'); - pFile = strreplace(pFile, " ~", "~"); - pFile = strreplace(pFile, "~ ", "~"); - - Summary = SummaryExtended(searchExt, t, pEvent); - - if (bTimesMatchExactly && strcmp(pFile, timer->File()) == 0 - && (t->Aux() != NULL && strcmp(t->Aux(), Summary) == 0) - ) - { // dir, title, episode name and summary have not changed - if (Summary) free(Summary); - delete timer; - free(pFile); - continue; - } - else - { - if (!bTimesMatchExactly) timerMod = (uint)timerMod | tmStartStop; - if (strcmp(pFile, timer->File()) != 0) timerMod |= tmFile; - if (t->Aux() != NULL && strcmp(t->Aux(), Summary) != 0) - { - char* oldEventID = GetAuxValue(t, "eventid"); - char* newEventID = GetAuxValue(Summary, "eventid"); - if (oldEventID && newEventID && strcmp(oldEventID, newEventID) != 0) - timerMod |= tmAuxEventID; - free(oldEventID); - free(newEventID); - } - - if (LogFile.Level() >= 3) // output reasons for a timer modification - { - if (timerMod & tmStartStop) - LogFile.Log(3,"timer for '%s~%s' (%s - %s, channel %d) : start/stop has changed", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); - if (timerMod & tmFile) - LogFile.Log(3,"timer for '%s~%s' (%s - %s, channel %d) : title and/or episdode has changed (old: %s, new: %s", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent), timer?timer->File():"", pFile); - if (timerMod & tmAuxEventID) - LogFile.Log(3,"timer for '%s~%s' (%s - %s, channel %d) : aux info for event id has changed", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); - } - index = t->Id(); - Priority = t->Priority(); - Lifetime = t->Lifetime(); - } - free(pFile); - - if (t->Recording() && t->StopTime() == timer->StopTime()) - { - // only update recording timers if stop time has changed, since all other settings can't be modified - LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) already recording - no changes possible", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent)); - delete timer; - continue; - } - } - else - { - if (!pResultObj->needsTimer) - { - delete timer; - continue; - } - } - - if (searchExt->action == searchTimerActionAnnounceViaOSD) - { - if (t || // timer already exists or - NoAnnounces.InList(pEvent) || // announcement not wanted anymore or - (EPGSearchConfig.noAnnounceWhileReplay && - cDevice::PrimaryDevice()->Replaying() && - !(updateForced & UPDS_WITH_OSD)) // no announce while replay within automatic updates - ) - { - if (Summary) free(Summary); - delete timer; - continue; - } - if (!announceList.Lookup(pEvent)) - announceList.Add(new cSearchResult(pEvent, searchExt->ID)); - - if (Summary) free(Summary); - delete timer; - continue; - } - - if (searchExt->action == searchTimerActionAnnounceViaMail) - { - if (t || // timer already exists or - NoAnnounces.InList(pEvent) || - pEvent->StartTime() < time(NULL)) // already started? - { - if (Summary) free(Summary); - delete timer; - continue; - } - mailNotifier.AddAnnounceEventNotification(pEvent->EventID(), pEvent->ChannelID(), searchExt->ID); - - if (Summary) free(Summary); - delete timer; - continue; - } - if (searchExt->action == searchTimerActionSwitchOnly || - searchExt->action == searchTimerActionAnnounceAndSwitch) // add to switch list - { - time_t now = time(NULL); - if (now < pEvent->StartTime()) - { - if (!SwitchTimers.InSwitchList(pEvent)) - { - cMutexLock SwitchTimersLock(&SwitchTimers); - int mode = 0; - if (searchExt->action == searchTimerActionAnnounceAndSwitch) - mode = 2; - LogFile.Log(3,"adding switch timer event for '%s~%s' (%s - %s); search timer: '%s'", pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); - SwitchTimers.Add(new cSwitchTimer(pEvent, searchExt->switchMinsBefore, mode, - searchExt->unmuteSoundOnSwitch)); - SwitchTimers.Save(); - cSwitchTimerThread::Init(); - } - } - if (Summary) free(Summary); - delete timer; - continue; - } - - if (AddModTimer(timer, index, searchExt, pEvent, Priority, Lifetime, Summary, timerMod)) - { - if (index == 0) - LogFile.Log(1,"added timer for '%s~%s' (%s - %s); search timer: '%s'", pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); - else - LogFile.Log(1,"modified timer %d for '%s~%s' (%s - %s); search timer: '%s'", index, pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); - } - if (Summary) free(Summary); - delete timer; + if (localSearchExts) delete localSearchExts; + + if (pOutdatedTimers) { + if (pOutdatedTimers->Count() > 0) { + LogFile.Log(1, "removing outdated timers"); + for (cTimerObj *tObj = pOutdatedTimers->First(); tObj; tObj = pOutdatedTimers->Next(tObj)) { + const cTimer* t = tObj->timer; + // timer could have been deleted meanwhile, so check if its still there + { + LOCK_TIMERS_READ; + if (!Timers->Contains(t)) + continue; + } + + if (TimerWasModified(t)) continue; + if (!t->Event()) continue; // if there is no event, we keep the timer, since EPG could have been cleared + if (time(NULL) > t->StopTime()) continue; // if this timer has (just) finished, let VDR do the cleanup + if (t->Recording()) continue; // do not remove recording timers + LogFile.Log(1, "delete timer for '%s' (%s, channel %s)", t->File(), DAYDATETIME(t->StartTime()), CHANNELNAME(t->Channel())); + RemoveTimer(t, t->Event()); + } + LogFile.Log(1, "removing outdated timers - done"); + } + delete pOutdatedTimers; } - delete pSearchResults; - searchExt = localSearchExts->Next(searchExt); - } - if (localSearchExts) delete localSearchExts; + TimersDone.ClearOutdated(); + TimersDone.Save(); - if (pOutdatedTimers) - { - if (pOutdatedTimers->Count() > 0) - { - LogFile.Log(1,"removing outdated timers"); - for(cTimerObj *tObj = pOutdatedTimers->First(); tObj; tObj = pOutdatedTimers->Next(tObj)) - { - const cTimer* t = tObj->timer; - // timer could have been deleted meanwhile, so check if its still there - { - LOCK_TIMERS_READ; - if (!Timers->Contains(t)) - continue; - } - - if (TimerWasModified(t)) continue; - if (!t->Event()) continue; // if there is no event, we keep the timer, since EPG could have been cleared - if (time(NULL) > t->StopTime()) continue; // if this timer has (just) finished, let VDR do the cleanup - if (t->Recording()) continue; // do not remove recording timers - LogFile.Log(1,"delete timer for '%s' (%s, channel %s)", t->File(), DAYDATETIME(t->StartTime()), CHANNELNAME(t->Channel())); - RemoveTimer(t, t->Event()); - } - LogFile.Log(1,"removing outdated timers - done"); - } - delete pOutdatedTimers; - } - - TimersDone.ClearOutdated(); - TimersDone.Save(); - - if (announceList.Count() > 0) - { - cString msgfmt = cString::sprintf(tr("%d new broadcast(s) found! Show them?"), announceList.Count()); - if (SendMsg(msgfmt, true, 7) == kOk) - { - m_plugin->showAnnounces = true; - cRemote::CallPlugin("epgsearch"); - } - } - - CheckEPGHours(); - - LogFile.iSysLog("search timer update finished"); - - // check for conflicts - if (EPGSearchConfig.checkTimerConflictsAfterUpdate && m_Active && Running()) - { - LogFile.iSysLog("check for timer conflicts"); - cConflictCheck conflictCheck; - conflictCheck.Check(); - - if (conflictCheck.relevantConflicts > 0) - { - if (EPGSearchConfig.sendMailOnConflicts) - { - cMailConflictNotifier mailNotifier; - mailNotifier.SendConflictNotifications(conflictCheck); - } - - conflictCheck.EvaluateConflCheckCmd(); - - cString msgfmt = ""; - if (conflictCheck.relevantConflicts == 1) - msgfmt = cString::sprintf(tr("timer conflict at %s! Show it?"), - *DateTime(conflictCheck.nextRelevantConflictDate)); - else - msgfmt = cString::sprintf(tr("%d timer conflicts! First at %s. Show them?"), - conflictCheck.relevantConflicts, - *DateTime(conflictCheck.nextRelevantConflictDate)); - - bool doMessage = EPGSearchConfig.noConflMsgWhileReplay == 0 || - !cDevice::PrimaryDevice()->Replaying() || - conflictCheck.nextRelevantConflictDate - now < 2*60*60 || - (updateForced & UPDS_WITH_OSD); - if (doMessage && SendMsg(msgfmt, true,7,mtWarning) == kOk) - { - m_plugin->showConflicts = true; - cRemote::CallPlugin("epgsearch"); - } + if (announceList.Count() > 0) { + cString msgfmt = cString::sprintf(tr("%d new broadcast(s) found! Show them?"), announceList.Count()); + if (SendMsg(msgfmt, true, 7) == kOk) { + m_plugin->showAnnounces = true; + cRemote::CallPlugin("epgsearch"); + } } - LogFile.iSysLog("check for timer conflicts - done"); - } + CheckEPGHours(); + + LogFile.iSysLog("search timer update finished"); + + // check for conflicts + if (EPGSearchConfig.checkTimerConflictsAfterUpdate && m_Active && Running()) { + LogFile.iSysLog("check for timer conflicts"); + cConflictCheck conflictCheck; + conflictCheck.Check(); + + if (conflictCheck.relevantConflicts > 0) { + if (EPGSearchConfig.sendMailOnConflicts) { + cMailConflictNotifier mailNotifier; + mailNotifier.SendConflictNotifications(conflictCheck); + } + + conflictCheck.EvaluateConflCheckCmd(); + + cString msgfmt = ""; + if (conflictCheck.relevantConflicts == 1) + msgfmt = cString::sprintf(tr("timer conflict at %s! Show it?"), + *DateTime(conflictCheck.nextRelevantConflictDate)); + else + msgfmt = cString::sprintf(tr("%d timer conflicts! First at %s. Show them?"), + conflictCheck.relevantConflicts, + *DateTime(conflictCheck.nextRelevantConflictDate)); + + bool doMessage = EPGSearchConfig.noConflMsgWhileReplay == 0 || + !cDevice::PrimaryDevice()->Replaying() || + conflictCheck.nextRelevantConflictDate - now < 2 * 60 * 60 || + (updateForced & UPDS_WITH_OSD); + if (doMessage && SendMsg(msgfmt, true, 7, mtWarning) == kOk) { + m_plugin->showConflicts = true; + cRemote::CallPlugin("epgsearch"); + } + } + + LogFile.iSysLog("check for timer conflicts - done"); + } - // delete expired recordings - CheckExpiredRecs(); + // delete expired recordings + CheckExpiredRecs(); - // check for updates for manual timers - CheckManualTimers(); + // check for updates for manual timers + CheckManualTimers(); - if (m_Active) - mailNotifier.SendUpdateNotifications(); + if (m_Active) + mailNotifier.SendUpdateNotifications(); - if ((updateForced & UPDS_WITH_OSD) && m_Active) - SendMsg(tr("Search timer update done!")); + if ((updateForced & UPDS_WITH_OSD) && m_Active) + SendMsg(tr("Search timer update done!")); - // reset service call flag - updateForced = 0; + // reset service call flag + updateForced = 0; - m_lastUpdate = time(NULL); - nextUpdate = long(m_lastUpdate/60)*60 + (EPGSearchConfig.UpdateIntervall * 60); - justRunning = false; - } - if (m_Active && Running()) - Wait.Wait(2000); // to avoid high system load if time%30==0 - while (Running() && m_Active && !NeedUpdate() && time(NULL)%30 != 0) // sync heart beat to a multiple of 5secs - Wait.Wait(1000); - }; - LogFile.iSysLog("Leaving search timer thread"); + m_lastUpdate = time(NULL); + nextUpdate = long(m_lastUpdate / 60) * 60 + (EPGSearchConfig.UpdateIntervall * 60); + justRunning = false; + } + if (m_Active && Running()) + Wait.Wait(2000); // to avoid high system load if time%30==0 + while (Running() && m_Active && !NeedUpdate() && time(NULL) % 30 != 0) // sync heart beat to a multiple of 5secs + Wait.Wait(1000); + }; + LogFile.iSysLog("Leaving search timer thread"); } bool cSearchTimerThread::NeedUpdate() { - return (m_lastUpdate <= LastModifiedTime(AddDirectory(CONFIGDIR, ".epgsearchupdate")) || updateForced>0); + return (m_lastUpdate <= LastModifiedTime(AddDirectory(CONFIGDIR, ".epgsearchupdate")) || updateForced > 0); } char* cSearchTimerThread::SummaryExtended(cSearchExt* searchExt, const cTimer* Timer, const cEvent* pEvent) { - bool UseVPS = searchExt->useVPS && pEvent->Vps() && Setup.UseVps; - time_t eStart; - if (!UseVPS) - eStart = pEvent->StartTime(); - else - eStart = pEvent->Vps(); - time_t eStop; - if (!UseVPS) - eStop = pEvent->EndTime(); - else - eStop = pEvent->Vps() + pEvent->Duration(); - // make sure that eStart and eStop represent a full minute - eStart = (eStart / 60) * 60; - eStop = (eStop / 60) * 60; - - time_t start = eStart - (UseVPS?0:(searchExt->MarginStart * 60)); - time_t stop = eStop + (UseVPS?0:(searchExt->MarginStop * 60)); - - char* addSummaryFooter = NULL; - msprintf(&addSummaryFooter, "<channel>%d - %s</channel><searchtimer>%s</searchtimer><start>%ld</start><stop>%ld</stop><s-id>%d</s-id><eventid>%ld</eventid>", - Timer->Channel()->Number(), CHANNELNAME(Timer->Channel()), - searchExt->search, - start, - stop, - searchExt->ID, - (long) pEvent->EventID()); - - const char* aux = Timer->Aux(); - // remove epgsearch entries - char* tmpaux = NULL; - if (!isempty(aux)) - { - tmpaux = strdup(aux); - const char* begin = strstr(aux, "<epgsearch>"); - const char* end = strstr(aux, "</epgsearch>"); - if (begin && end) - { - if (begin == aux) strcpy(tmpaux, ""); else strn0cpy(tmpaux, aux, begin-aux+1); - strcat(tmpaux, end + strlen("</epgsearch>")); - } - } - - char* tmpSummary = NULL; - msprintf(&tmpSummary, "<epgsearch>%s</epgsearch>%s", addSummaryFooter, tmpaux?tmpaux:""); - free(addSummaryFooter); - if (tmpaux) free(tmpaux); - return tmpSummary; + bool UseVPS = searchExt->useVPS && pEvent->Vps() && Setup.UseVps; + time_t eStart; + if (!UseVPS) + eStart = pEvent->StartTime(); + else + eStart = pEvent->Vps(); + time_t eStop; + if (!UseVPS) + eStop = pEvent->EndTime(); + else + eStop = pEvent->Vps() + pEvent->Duration(); + // make sure that eStart and eStop represent a full minute + eStart = (eStart / 60) * 60; + eStop = (eStop / 60) * 60; + + time_t start = eStart - (UseVPS ? 0 : (searchExt->MarginStart * 60)); + time_t stop = eStop + (UseVPS ? 0 : (searchExt->MarginStop * 60)); + + char* addSummaryFooter = NULL; + msprintf(&addSummaryFooter, "<channel>%d - %s</channel><searchtimer>%s</searchtimer><start>%ld</start><stop>%ld</stop><s-id>%d</s-id><eventid>%ld</eventid>", + Timer->Channel()->Number(), CHANNELNAME(Timer->Channel()), + searchExt->search, + start, + stop, + searchExt->ID, + (long) pEvent->EventID()); + + const char* aux = Timer->Aux(); + // remove epgsearch entries + char* tmpaux = NULL; + if (!isempty(aux)) { + tmpaux = strdup(aux); + const char* begin = strstr(aux, "<epgsearch>"); + const char* end = strstr(aux, "</epgsearch>"); + if (begin && end) { + if (begin == aux) strcpy(tmpaux, ""); + else strn0cpy(tmpaux, aux, begin - aux + 1); + strcat(tmpaux, end + strlen("</epgsearch>")); + } + } + + char* tmpSummary = NULL; + msprintf(&tmpSummary, "<epgsearch>%s</epgsearch>%s", addSummaryFooter, tmpaux ? tmpaux : ""); + free(addSummaryFooter); + if (tmpaux) free(tmpaux); + return tmpSummary; } bool cSearchTimerThread::AddModTimer(cTimer* Timer, int index, cSearchExt* searchExt, const cEvent* pEvent, int Prio, int Lifetime, char* Summary, uint timerMod) { - char *cmdbuf = NULL; - - static char bufStart[25]; - static char bufEnd[25]; - - struct tm tm_r; time_t eStart = pEvent->StartTime(); - time_t eStop = pEvent->EndTime(); - time_t start = eStart - (searchExt->MarginStart * 60); - time_t stop = eStop + (searchExt->MarginStop * 60); - int Flags = Timer->Flags(); - if (searchExt->useVPS && pEvent->Vps() && Setup.UseVps) - { - start = pEvent->Vps(); - stop = start + pEvent->Duration(); - } - else - Flags = 1; // don't use VPS, if not set in this search - - if (searchExt->action == searchTimerActionInactiveRecord) - Flags &= ~tfActive; - - // already done the same timer? - if (!EPGSearchConfig.TimerProgRepeat && index == 0 && TimersDone.InList(start, stop, pEvent, -1)) - { - LogFile.Log(2,"skip timer for '%s~%s' (%s - %s); search timer: '%s' - already done", pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); - return false; - } - - strftime(bufStart, sizeof(bufStart), "%H%M", localtime_r(&start, &tm_r)); - strftime(bufEnd, sizeof(bufEnd), "%H%M", localtime_r(&stop, &tm_r)); - - // add additional info - char* tmpSummary = NULL; - if (Summary) - { - tmpSummary = strdup(Summary); - strreplace(tmpSummary, '\n', '|'); - } - else - tmpSummary = SummaryExtended(searchExt, Timer, pEvent); - if (index==0) - msprintf(&cmdbuf, "NEWT %d:%d:%s:%s:%s:%d:%d:%s:%s", - Flags, - Timer->Channel()->Number(), - *Timer->PrintDay(start, Timer->WeekDays(), true), - bufStart, - bufEnd, - Prio, - Lifetime, - Timer->File(), - tmpSummary?tmpSummary:""); - else - msprintf(&cmdbuf, "MODT %d %d:%d:%s:%s:%s:%d:%d:%s:%s", - index, - Flags, - Timer->Channel()->Number(), - *Timer->PrintDay(start, Timer->WeekDays(), true), - bufStart, - bufEnd, - Prio, - Lifetime, - Timer->File(), - tmpSummary?tmpSummary:""); - - if (!SendViaSVDRP(cmdbuf)) - return false; - - if (gl_timerStatusMonitor) gl_timerStatusMonitor->SetConflictCheckAdvised(); - - cTimerDone* timerdone = new cTimerDone(start, stop, pEvent, searchExt->ID); - if (index==0) - TimersDone.Add(timerdone); - else - TimersDone.Update(start, stop, pEvent, searchExt->ID, timerdone); - - if (EPGSearchConfig.sendMailOnSearchtimers) - { - if (index==0) // new - mailNotifier.AddNewTimerNotification(pEvent->EventID(), pEvent->ChannelID()); - else - mailNotifier.AddModTimerNotification(pEvent->EventID(), pEvent->ChannelID(), timerMod); - } - free(cmdbuf); - if (tmpSummary) free(tmpSummary); - - return true; + char *cmdbuf = NULL; + + static char bufStart[25]; + static char bufEnd[25]; + + struct tm tm_r; + time_t eStart = pEvent->StartTime(); + time_t eStop = pEvent->EndTime(); + time_t start = eStart - (searchExt->MarginStart * 60); + time_t stop = eStop + (searchExt->MarginStop * 60); + int Flags = Timer->Flags(); + if (searchExt->useVPS && pEvent->Vps() && Setup.UseVps) { + start = pEvent->Vps(); + stop = start + pEvent->Duration(); + } else + Flags = 1; // don't use VPS, if not set in this search + + if (searchExt->action == searchTimerActionInactiveRecord) + Flags &= ~tfActive; + + // already done the same timer? + if (!EPGSearchConfig.TimerProgRepeat && index == 0 && TimersDone.InList(start, stop, pEvent, -1)) { + LogFile.Log(2, "skip timer for '%s~%s' (%s - %s); search timer: '%s' - already done", pEvent->Title(), pEvent->ShortText() ? pEvent->ShortText() : "", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search); + return false; + } + + strftime(bufStart, sizeof(bufStart), "%H%M", localtime_r(&start, &tm_r)); + strftime(bufEnd, sizeof(bufEnd), "%H%M", localtime_r(&stop, &tm_r)); + + // add additional info + char* tmpSummary = NULL; + if (Summary) { + tmpSummary = strdup(Summary); + strreplace(tmpSummary, '\n', '|'); + } else + tmpSummary = SummaryExtended(searchExt, Timer, pEvent); + if (index == 0) + msprintf(&cmdbuf, "NEWT %d:%d:%s:%s:%s:%d:%d:%s:%s", + Flags, + Timer->Channel()->Number(), + *Timer->PrintDay(start, Timer->WeekDays(), true), + bufStart, + bufEnd, + Prio, + Lifetime, + Timer->File(), + tmpSummary ? tmpSummary : ""); + else + msprintf(&cmdbuf, "MODT %d %d:%d:%s:%s:%s:%d:%d:%s:%s", + index, + Flags, + Timer->Channel()->Number(), + *Timer->PrintDay(start, Timer->WeekDays(), true), + bufStart, + bufEnd, + Prio, + Lifetime, + Timer->File(), + tmpSummary ? tmpSummary : ""); + + if (!SendViaSVDRP(cmdbuf)) + return false; + + if (gl_timerStatusMonitor) gl_timerStatusMonitor->SetConflictCheckAdvised(); + + cTimerDone* timerdone = new cTimerDone(start, stop, pEvent, searchExt->ID); + if (index == 0) + TimersDone.Add(timerdone); + else + TimersDone.Update(start, stop, pEvent, searchExt->ID, timerdone); + + if (EPGSearchConfig.sendMailOnSearchtimers) { + if (index == 0) // new + mailNotifier.AddNewTimerNotification(pEvent->EventID(), pEvent->ChannelID()); + else + mailNotifier.AddModTimerNotification(pEvent->EventID(), pEvent->ChannelID(), timerMod); + } + free(cmdbuf); + if (tmpSummary) free(tmpSummary); + + return true; } void cSearchTimerThread::RemoveTimer(const cTimer* t, const cEvent* e) { - if (!t) return; - if (EPGSearchConfig.sendMailOnSearchtimers) - mailNotifier.AddRemoveTimerNotification(t,e); - if (!EPGSearchConfig.TimerProgRepeat) - { - cTimerDone * TimerDone = TimersDone.InList(t->StartTime(), t->StopTime(), e, -1); - if (TimerDone) - { - cMutexLock TimersDoneLock(&TimersDone); - TimersDone.Del(TimerDone); - TimersDone.Save(); - } - } - DelTimer(t->Id()); + if (!t) return; + if (EPGSearchConfig.sendMailOnSearchtimers) + mailNotifier.AddRemoveTimerNotification(t, e); + if (!EPGSearchConfig.TimerProgRepeat) { + cTimerDone * TimerDone = TimersDone.InList(t->StartTime(), t->StopTime(), e, -1); + if (TimerDone) { + cMutexLock TimersDoneLock(&TimersDone); + TimersDone.Del(TimerDone); + TimersDone.Save(); + } + } + DelTimer(t->Id()); } void cSearchTimerThread::DelRecording(int index) { - cString cmdbuf = cString::sprintf("DELR %d", index); - LogFile.Log(2, "delete recording %d", index); - SendViaSVDRP(cmdbuf); + cString cmdbuf = cString::sprintf("DELR %d", index); + LogFile.Log(2, "delete recording %d", index); + SendViaSVDRP(cmdbuf); } void cSearchTimerThread::CheckExpiredRecs() { - LogFile.Log(1, "check for expired recordings started"); - LOCK_RECORDINGS_WRITE; - Recordings->SetExplicitModify(); - cList<cRecordingObj> DelRecordings; - for (cRecording *recording = Recordings->First(); recording && m_Active; recording = Recordings->Next(recording)) - { - LogFile.Log(3, "check recording %s from %s for expiration", recording->Name(), DAYDATETIME(recording->Start())); - if (recording->Start() == 0) - { - LogFile.Log(2, "oops, recording %s has no start time, skipped", recording->Name()); - continue; - } - if (recording->IsEdited()) continue; - - if (!recording->Info()) continue; - char* searchID = GetAuxValue(recording, "s-id"); - char* searchName = GetAuxValue(recording, "searchtimer"); - if (!searchName) - searchName = GetAuxValue(recording, "search timer"); - - if (!searchID || !searchName) - { - if (searchID) free(searchID); - if (searchName) free(searchName); - continue; - } - cSearchExt* search = SearchExts.GetSearchFromID(atoi(searchID)); - if (!search || strcmp(search->search, searchName) != 0) - { - if (searchID) free(searchID); - if (searchName) free(searchName); - continue; - } - free(searchID); - free(searchName); - LogFile.Log(3, "recording triggered from search timer %s", search->search); - if (search->delAfterDays == 0) continue; - time_t now = time(NULL); - - int daysBetween = int(double((now - recording->Start())) / (60*60*24)); - if (daysBetween >= search->delAfterDays) - DelRecordings.Add(new cRecordingObj(recording, search)); - else - LogFile.Log(3, "recording will expire in %d days, search timer %s", search->delAfterDays - daysBetween, search->search); - } - for (cRecordingObj *recordingObj = DelRecordings.First(); recordingObj && m_Active; recordingObj = DelRecordings.Next(recordingObj)) - { - cRecording* recording = recordingObj->recording; - cSearchExt* search = recordingObj->search; - if (search->recordingsKeep > 0 && search->recordingsKeep >= search->GetCountRecordings()) - { - LogFile.Log(1, "recording '%s' from %s expired, but will be kept, search timer %s", recording->Name(), DAYDATETIME(recording->Start()), recordingObj->search->search); - continue; - } - LogFile.Log(1, "delete expired recording '%s' from %s, search timer %s", recording->Name(), DAYDATETIME(recording->Start()), recordingObj->search->search); - cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); - if (!rc) - { - if (!recording->Delete()) - LogFile.Log(1, "error deleting recording!"); - else - { - Recordings->DelByName(recording->FileName()); - Recordings->SetModified(); - } - } - else - LogFile.Log(1, "recording already in use by a timer!"); - } - LogFile.Log(1, "check for expired recordings finished"); + LogFile.Log(1, "check for expired recordings started"); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + cList<cRecordingObj> DelRecordings; + for (cRecording *recording = Recordings->First(); recording && m_Active; recording = Recordings->Next(recording)) { + LogFile.Log(3, "check recording %s from %s for expiration", recording->Name(), DAYDATETIME(recording->Start())); + if (recording->Start() == 0) { + LogFile.Log(2, "oops, recording %s has no start time, skipped", recording->Name()); + continue; + } + if (recording->IsEdited()) continue; + + if (!recording->Info()) continue; + char* searchID = GetAuxValue(recording, "s-id"); + char* searchName = GetAuxValue(recording, "searchtimer"); + if (!searchName) + searchName = GetAuxValue(recording, "search timer"); + + if (!searchID || !searchName) { + if (searchID) free(searchID); + if (searchName) free(searchName); + continue; + } + cSearchExt* search = SearchExts.GetSearchFromID(atoi(searchID)); + if (!search || strcmp(search->search, searchName) != 0) { + if (searchID) free(searchID); + if (searchName) free(searchName); + continue; + } + free(searchID); + free(searchName); + LogFile.Log(3, "recording triggered from search timer %s", search->search); + if (search->delAfterDays == 0) continue; + time_t now = time(NULL); + + int daysBetween = int(double((now - recording->Start())) / (60 * 60 * 24)); + if (daysBetween >= search->delAfterDays) + DelRecordings.Add(new cRecordingObj(recording, search)); + else + LogFile.Log(3, "recording will expire in %d days, search timer %s", search->delAfterDays - daysBetween, search->search); + } + for (cRecordingObj *recordingObj = DelRecordings.First(); recordingObj && m_Active; recordingObj = DelRecordings.Next(recordingObj)) { + cRecording* recording = recordingObj->recording; + cSearchExt* search = recordingObj->search; + if (search->recordingsKeep > 0 && search->recordingsKeep >= search->GetCountRecordings()) { + LogFile.Log(1, "recording '%s' from %s expired, but will be kept, search timer %s", recording->Name(), DAYDATETIME(recording->Start()), recordingObj->search->search); + continue; + } + LogFile.Log(1, "delete expired recording '%s' from %s, search timer %s", recording->Name(), DAYDATETIME(recording->Start()), recordingObj->search->search); + cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); + if (!rc) { + if (!recording->Delete()) + LogFile.Log(1, "error deleting recording!"); + else { + Recordings->DelByName(recording->FileName()); + Recordings->SetModified(); + } + } else + LogFile.Log(1, "recording already in use by a timer!"); + } + LogFile.Log(1, "check for expired recordings finished"); } void cSearchTimerThread::ModifyManualTimer(const cEvent* event, const cTimer* timer, int bstart, int bstop) { - LogFile.Log(1,"modified manual timer %d for '%s~%s' (%s - %s)", timer->Id(), event->Title(), event->ShortText()?event->ShortText():"", GETDATESTRING(event), GETTIMESTRING(event)); - - time_t start = event->StartTime() - bstart; - time_t stop = event->EndTime() + bstop; - struct tm tm_r_start; - struct tm tm_r_stop; - localtime_r(&start, &tm_r_start); - localtime_r(&stop, &tm_r_stop); - char daybuffer[DAYBUFFERSIZE]; - char startbuffer[DAYBUFFERSIZE]; - char stopbuffer[DAYBUFFERSIZE]; - strftime(daybuffer, DAYBUFFERSIZE, "%Y-%m-%d", &tm_r_start); - strftime(startbuffer, DAYBUFFERSIZE, "%H%M", &tm_r_start); - strftime(stopbuffer, DAYBUFFERSIZE, "%H%M", &tm_r_stop); - - char* cmdbuf = NULL; - msprintf(&cmdbuf, "MODT %d %d:%d:%s:%s:%s:%d:%d:%s:%s", - timer->Id(), - timer->Flags(), - timer->Channel()->Number(), - daybuffer, - startbuffer, - stopbuffer, - timer->Priority(), - timer->Lifetime(), - timer->File(), - timer->Aux()); - - if (EPGSearchConfig.sendMailOnSearchtimers) - mailNotifier.AddModTimerNotification(event->EventID(), event->ChannelID()); - - cTimerThread timerThread; - timerThread.Init(cmdbuf); - free(cmdbuf); + LogFile.Log(1, "modified manual timer %d for '%s~%s' (%s - %s)", timer->Id(), event->Title(), event->ShortText() ? event->ShortText() : "", GETDATESTRING(event), GETTIMESTRING(event)); + + time_t start = event->StartTime() - bstart; + time_t stop = event->EndTime() + bstop; + struct tm tm_r_start; + struct tm tm_r_stop; + localtime_r(&start, &tm_r_start); + localtime_r(&stop, &tm_r_stop); + char daybuffer[DAYBUFFERSIZE]; + char startbuffer[DAYBUFFERSIZE]; + char stopbuffer[DAYBUFFERSIZE]; + strftime(daybuffer, DAYBUFFERSIZE, "%Y-%m-%d", &tm_r_start); + strftime(startbuffer, DAYBUFFERSIZE, "%H%M", &tm_r_start); + strftime(stopbuffer, DAYBUFFERSIZE, "%H%M", &tm_r_stop); + + char* cmdbuf = NULL; + msprintf(&cmdbuf, "MODT %d %d:%d:%s:%s:%s:%d:%d:%s:%s", + timer->Id(), + timer->Flags(), + timer->Channel()->Number(), + daybuffer, + startbuffer, + stopbuffer, + timer->Priority(), + timer->Lifetime(), + timer->File(), + timer->Aux()); + + if (EPGSearchConfig.sendMailOnSearchtimers) + mailNotifier.AddModTimerNotification(event->EventID(), event->ChannelID()); + + cTimerThread timerThread; + timerThread.Init(cmdbuf); + free(cmdbuf); } void cSearchTimerThread::CheckManualTimers(void) { - LogFile.Log(1, "manual timer check started"); + LogFile.Log(1, "manual timer check started"); LOCK_TIMERS_READ; LOCK_SCHEDULES_READ; - for (const cTimer *ti = Timers->First(); ti && m_Active; ti = Timers->Next(ti)) - { - if (TriggeredFromSearchTimerID(ti) != -1) continue; // manual timer? - - if (TimerWasModified(ti)) - { - LogFile.Log(2,"timer for '%s' (%s, channel %s) modified by user - won't be touched", ti->File(), DAYDATETIME(ti->StartTime()), CHANNELNAME(ti->Channel())); - continue; // don't update timers modified by user - } - - char* szbstart = GetAuxValue(ti, "bstart"); - int bstart = szbstart? atoi(szbstart) : 0; - free(szbstart); - char* szbstop = GetAuxValue(ti, "bstop"); - int bstop = szbstop? atoi(szbstop) : 0; - free(szbstop); - - // how to check? - char* updateMethod = GetAuxValue(ti, "update"); - if (updateMethod && atoi(updateMethod) == UPD_EVENTID) // by event ID? - { - // get the channels schedule - const cSchedule* schedule = Schedules->GetSchedule(ti->Channel()); - if (schedule) - { - tEventID eventID = 0; - char* szEventID = GetAuxValue(ti, "eventid"); - if (szEventID) - eventID = atol(szEventID); - LogFile.Log(3,"checking manual timer %d by event ID %u", ti->Id(), eventID); - const cEvent* event = schedule->GetEvent(eventID); - if (event) - { - if (event->StartTime() - bstart != ti->StartTime() || event->EndTime() + bstop != ti->StopTime()) - ModifyManualTimer(event, ti, bstart, bstop); + for (const cTimer *ti = Timers->First(); ti && m_Active; ti = Timers->Next(ti)) { + if (TriggeredFromSearchTimerID(ti) != -1) continue; // manual timer? + + if (TimerWasModified(ti)) { + LogFile.Log(2, "timer for '%s' (%s, channel %s) modified by user - won't be touched", ti->File(), DAYDATETIME(ti->StartTime()), CHANNELNAME(ti->Channel())); + continue; // don't update timers modified by user + } + + char* szbstart = GetAuxValue(ti, "bstart"); + int bstart = szbstart ? atoi(szbstart) : 0; + free(szbstart); + char* szbstop = GetAuxValue(ti, "bstop"); + int bstop = szbstop ? atoi(szbstop) : 0; + free(szbstop); + + // how to check? + char* updateMethod = GetAuxValue(ti, "update"); + if (updateMethod && atoi(updateMethod) == UPD_EVENTID) { // by event ID? + // get the channels schedule + const cSchedule* schedule = Schedules->GetSchedule(ti->Channel()); + if (schedule) { + tEventID eventID = 0; + char* szEventID = GetAuxValue(ti, "eventid"); + if (szEventID) + eventID = atol(szEventID); + LogFile.Log(3, "checking manual timer %d by event ID %u", ti->Id(), eventID); + const cEvent* event = schedule->GetEvent(eventID); + if (event) { + if (event->StartTime() - bstart != ti->StartTime() || event->EndTime() + bstop != ti->StopTime()) + ModifyManualTimer(event, ti, bstart, bstop); + } else + LogFile.Log(1, "ooops - no event found with id %u for manual timer %d", eventID, ti->Id()); + + if (szEventID) free(szEventID); } - else - LogFile.Log(1,"ooops - no event found with id %u for manual timer %d", eventID, ti->Id()); - - if (szEventID) free(szEventID); - } - } - if (updateMethod && atoi(updateMethod) == UPD_CHDUR) // by channel and time? - { - // get the channels schedule - const cSchedule* schedule = Schedules->GetSchedule(ti->Channel()); - if (schedule) - { - // collect all events touching the old timer margins - cSearchResults eventlist; - for (const cEvent *testevent = schedule->Events()->First(); testevent; testevent = schedule->Events()->Next(testevent)) - { - if (testevent->StartTime() < ti->StopTime() && testevent->EndTime() > ti->StartTime()) - eventlist.Add(new cSearchResult(testevent, (const cSearchExt*)NULL)); + } + if (updateMethod && atoi(updateMethod) == UPD_CHDUR) { // by channel and time? + // get the channels schedule + const cSchedule* schedule = Schedules->GetSchedule(ti->Channel()); + if (schedule) { + // collect all events touching the old timer margins + cSearchResults eventlist; + for (const cEvent *testevent = schedule->Events()->First(); testevent; testevent = schedule->Events()->Next(testevent)) { + if (testevent->StartTime() < ti->StopTime() && testevent->EndTime() > ti->StartTime()) + eventlist.Add(new cSearchResult(testevent, (const cSearchExt*)NULL)); + } + LogFile.Log(3, "checking manual timer %d by channel and time, found %d candidates", ti->Id(), eventlist.Count()); + if (eventlist.Count() > 0) { + // choose the event with the best match by duration + long origlen = (ti->StopTime() - bstop) - (ti->StartTime() + bstart); + double maxweight = 0; + const cEvent* event = eventlist.First()->event; + for (cSearchResult* pResultObj = eventlist.First(); pResultObj; pResultObj = eventlist.Next(pResultObj)) { + const cEvent* testevent = pResultObj->event; + time_t start = (testevent->StartTime() < ti->StartTime()) ? ti->StartTime() : testevent->StartTime(); + time_t stop = (testevent->EndTime() > ti->StopTime()) ? ti->StopTime() : testevent->EndTime(); + double weight = double(stop - start) / double(testevent->EndTime() - testevent->StartTime()); + LogFile.Log(3, "candidate '%s~%s' (%s - %s) timer match: %f, duration match: %f", testevent->Title(), testevent->ShortText() ? testevent->ShortText() : "", GETDATESTRING(testevent), GETTIMESTRING(testevent), weight, (double(testevent->EndTime() - testevent->StartTime()) / origlen)); + if (weight > maxweight && (double(testevent->EndTime() - testevent->StartTime()) / origlen) >= 0.9) { + maxweight = weight; + event = testevent; + } + } + LogFile.Log(3, "selected candidate is '%s~%s' (%s - %s)", event->Title(), event->ShortText() ? event->ShortText() : "", GETDATESTRING(event), GETTIMESTRING(event)); + if ((maxweight > 0 && event->StartTime() - bstart != ti->StartTime()) || (event->EndTime() + bstop != ti->StopTime())) + ModifyManualTimer(event, ti, bstart, bstop); + else if (maxweight <= 0) + LogFile.Log(3, "selected candidate is too bad"); + } else + LogFile.Log(1, "ooops - no events found touching manual timer %d", ti->Id()); } - LogFile.Log(3,"checking manual timer %d by channel and time, found %d candidates", ti->Id(), eventlist.Count()); - if (eventlist.Count() > 0) - { - // choose the event with the best match by duration - long origlen = (ti->StopTime() - bstop) - (ti->StartTime() + bstart); - double maxweight = 0; - const cEvent* event = eventlist.First()->event; - for (cSearchResult* pResultObj = eventlist.First(); pResultObj; pResultObj = eventlist.Next(pResultObj)) - { - const cEvent* testevent = pResultObj->event; - time_t start = (testevent->StartTime() < ti->StartTime()) ? ti->StartTime() : testevent->StartTime(); - time_t stop = (testevent->EndTime() > ti->StopTime()) ? ti->StopTime() : testevent->EndTime(); - double weight = double(stop - start) / double(testevent->EndTime() - testevent->StartTime()); - LogFile.Log(3,"candidate '%s~%s' (%s - %s) timer match: %f, duration match: %f", testevent->Title(), testevent->ShortText()?testevent->ShortText():"", GETDATESTRING(testevent), GETTIMESTRING(testevent), weight, (double(testevent->EndTime() - testevent->StartTime()) / origlen)); - if (weight > maxweight && (double(testevent->EndTime() - testevent->StartTime()) / origlen) >= 0.9) - { - maxweight = weight; - event = testevent; - } - } - LogFile.Log(3,"selected candidate is '%s~%s' (%s - %s)", event->Title(), event->ShortText()?event->ShortText():"", GETDATESTRING(event), GETTIMESTRING(event)); - if ((maxweight > 0 && event->StartTime() - bstart != ti->StartTime()) || (event->EndTime() + bstop != ti->StopTime())) - ModifyManualTimer(event, ti, bstart, bstop); - else if (maxweight <= 0) - LogFile.Log(3,"selected candidate is too bad"); - } - else - LogFile.Log(1,"ooops - no events found touching manual timer %d", ti->Id()); - } - if (updateMethod) free(updateMethod); - } - } - LogFile.Log(1, "manual timer check finished"); + if (updateMethod) free(updateMethod); + } + } + LogFile.Log(1, "manual timer check finished"); } // check if EPG is present for the configured hours void cSearchTimerThread::CheckEPGHours() { - if (EPGSearchConfig.checkEPGHours <= 0 || - (EPGSearchConfig.checkEPGWarnByOSD == 0 && EPGSearchConfig.checkEPGWarnByMail == 0) || - EPGSearchConfig.checkEPGchannelGroupNr <= 0) - return; - - LogFile.Log(2,"check if relevant EPG exists for the next %d hours", EPGSearchConfig.checkEPGHours); - cChannelGroup* channelGroup = ChannelGroups.Get(EPGSearchConfig.checkEPGchannelGroupNr-1); // not zero-based! - if (!channelGroup) - { - LogFile.Log(1,"channel group with index %d does not exist!", EPGSearchConfig.checkEPGchannelGroupNr); - return; + if (EPGSearchConfig.checkEPGHours <= 0 || + (EPGSearchConfig.checkEPGWarnByOSD == 0 && EPGSearchConfig.checkEPGWarnByMail == 0) || + EPGSearchConfig.checkEPGchannelGroupNr <= 0) + return; + + LogFile.Log(2, "check if relevant EPG exists for the next %d hours", EPGSearchConfig.checkEPGHours); + cChannelGroup* channelGroup = ChannelGroups.Get(EPGSearchConfig.checkEPGchannelGroupNr - 1); // not zero-based! + if (!channelGroup) { + LogFile.Log(1, "channel group with index %d does not exist!", EPGSearchConfig.checkEPGchannelGroupNr); + return; } - LogFile.Log(2,"checking channel group '%s'", channelGroup->name); + LogFile.Log(2, "checking channel group '%s'", channelGroup->name); - time_t checkTime = time(NULL) + EPGSearchConfig.checkEPGHours * 60 * 60; + time_t checkTime = time(NULL) + EPGSearchConfig.checkEPGHours * 60 * 60; LOCK_SCHEDULES_READ; - cChannelGroup channelsWithSmallEPG; - cChannelGroupItem* channelInGroup = channelGroup->channels.First(); - while (channelInGroup) - { - const cChannel* channel = channelInGroup->channel; - // get the channels schedule - const cSchedule* schedule = Schedules->GetSchedule(channel); - if (!schedule || !schedule->GetEventAround(checkTime)) - { - LogFile.Log(3,"less than %d hours of EPG for channel %s!", EPGSearchConfig.checkEPGHours, channel->Name()); - cChannelGroupItem* channelitem = new cChannelGroupItem(channel); - channelsWithSmallEPG.channels.Add(channelitem); - } - channelInGroup = channelGroup->channels.Next(channelInGroup); + cChannelGroup channelsWithSmallEPG; + cChannelGroupItem* channelInGroup = channelGroup->channels.First(); + while (channelInGroup) { + const cChannel* channel = channelInGroup->channel; + // get the channels schedule + const cSchedule* schedule = Schedules->GetSchedule(channel); + if (!schedule || !schedule->GetEventAround(checkTime)) { + LogFile.Log(3, "less than %d hours of EPG for channel %s!", EPGSearchConfig.checkEPGHours, channel->Name()); + cChannelGroupItem* channelitem = new cChannelGroupItem(channel); + channelsWithSmallEPG.channels.Add(channelitem); + } + channelInGroup = channelGroup->channels.Next(channelInGroup); } - // create a string list of the channels found - if (channelsWithSmallEPG.channels.Count() > 0) - { - string sBuffer; - channelInGroup = channelsWithSmallEPG.channels.First(); - while (channelInGroup) - { - const cChannel* channel = channelInGroup->channel; - if (channel) - sBuffer += " " + string(channel->ShortName(true)); - channelInGroup = channelsWithSmallEPG.channels.Next(channelInGroup); - } - - - if (EPGSearchConfig.checkEPGWarnByOSD) - { - cString msgfmt = cString::sprintf(tr("small EPG content on:%s"), sBuffer.c_str()); - SendMsg(msgfmt, false, 0, mtWarning); - } - if (EPGSearchConfig.checkEPGWarnByMail) - { - cString msgfmt = cString::sprintf(tr("small EPG content on:%s"), sBuffer.c_str()); - cMailNotifier M(string(tr("VDR EPG check warning")), string(msgfmt)); - } + // create a string list of the channels found + if (channelsWithSmallEPG.channels.Count() > 0) { + string sBuffer; + channelInGroup = channelsWithSmallEPG.channels.First(); + while (channelInGroup) { + const cChannel* channel = channelInGroup->channel; + if (channel) + sBuffer += " " + string(channel->ShortName(true)); + channelInGroup = channelsWithSmallEPG.channels.Next(channelInGroup); + } + + + if (EPGSearchConfig.checkEPGWarnByOSD) { + cString msgfmt = cString::sprintf(tr("small EPG content on:%s"), sBuffer.c_str()); + SendMsg(msgfmt, false, 0, mtWarning); + } + if (EPGSearchConfig.checkEPGWarnByMail) { + cString msgfmt = cString::sprintf(tr("small EPG content on:%s"), sBuffer.c_str()); + cMailNotifier M(string(tr("VDR EPG check warning")), string(msgfmt)); + } } - LogFile.Log(2,"check for relevant EPG finished - %d channels with small EPG content", channelsWithSmallEPG.channels.Count()); + LogFile.Log(2, "check for relevant EPG finished - %d channels with small EPG content", channelsWithSmallEPG.channels.Count()); } |