From e101df04a2e19c49293965223e6d4bbf719ed78c Mon Sep 17 00:00:00 2001 From: Johann Friedrichs Date: Sun, 30 Jan 2022 18:03:42 +0100 Subject: Changed handling of finished recordings Allow missing timer-Object (vdr > 2.5.x deletes non-VPS timers immediately after recording end) We now use the Recording-Filename to find Start- and Stoptimes --- HISTORY | 10 +++++++ HISTORY.DE | 52 ++++++++++++++++++++------------- menu_commands.c | 2 +- recdone.c | 14 ++++++++- recdone.h | 12 +++++--- recdone_thread.c | 89 ++++++++++++++++++++++++++++++++++---------------------- recstatus.c | 16 ++++++---- 7 files changed, 127 insertions(+), 68 deletions(-) diff --git a/HISTORY b/HISTORY index ff78347..3edad7f 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,16 @@ VDR Plugin 'epgsearch' Revision History --------------------------------------- +2022-01-30 +new: +- Allow missing timer-Object in Recording-Done-Processing + (vdr > 2.5.x compatibility) + +2021-12-10 +fixes: +- Wrong device selection for conflict check +- compareSubtitle supports more than 2 values + 2021-05-24; Version 2.4.1 fixes: - Fixed conflictcheck for encrypted channels with internal CAMs diff --git a/HISTORY.DE b/HISTORY.DE index 0b2e9be..8704979 100644 --- a/HISTORY.DE +++ b/HISTORY.DE @@ -1,27 +1,37 @@ VDR Plugin 'epgsearch' Revision History --------------------------------------- --2021-05-24; Version 2.4.1 --fixes: --- Fixed conflictcheck for encrypted channels with internal CAMs --- Fixed compiling with gcc11 --- Updated deprecated calls to SetItemEvent --- Temporally(?) added #define DISABLE_TEMPLATES_COLLIDING_WITH_STL for --- compatibility with vdr 2.5.4 -- --2021-04-12 --neu: --- Improved handling of remote timers --- Replace auto_ptr with unique_ptr for c++11 (kfb77@vdr-portal.de) --- Delay threads after pluginstart 10 secs (configurable) --- Changed fgets to allow compiling with clang --- Clarified "avoid repeats". Forced subtitle-comparison is named "yes" again -- New option "allow empty" --fixes: --- Fixed several lock sequence errors --- Fix utf-8 encoding in docs and manpages (kfb77 and seahawk1986@vdr-portal.de) --- Fixed possible format overflow --- Fixed displaying NAME in generated manpages thanks to etobi +2022-01-30 +neu: +- Allow missing timer-Object in Recording-Done-Processing + (vdr > 2.5.x compatibility) + +2021-12-10 +fixes: +- Wrong device selection for conflict check +- compareSubtitle supports more than 2 values + +2021-05-24; Version 2.4.1 +fixes: +- Fixed conflictcheck for encrypted channels with internal CAMs +- Fixed compiling with gcc11 +- Updated deprecated calls to SetItemEvent +- Temporally(?) added #define DISABLE_TEMPLATES_COLLIDING_WITH_STL for +- compatibility with vdr 2.5.4 + +2021-04-12 +neu: +- Improved handling of remote timers +- Replace auto_ptr with unique_ptr for c++11 (kfb77@vdr-portal.de) +- Delay threads after pluginstart 10 secs (configurable) +- Changed fgets to allow compiling with clang +- Clarified "avoid repeats". Forced subtitle-comparison is named "yes" again + New option "allow empty" +fixes: +- Fixed several lock sequence errors +- Fix utf-8 encoding in docs and manpages (kfb77 and seahawk1986@vdr-portal.de) +- Fixed possible format overflow +- Fixed displaying NAME in generated manpages thanks to etobi 2018-04-16; Version 2.4.0 neu: diff --git a/menu_commands.c b/menu_commands.c index 99ca744..028bd99 100644 --- a/menu_commands.c +++ b/menu_commands.c @@ -182,7 +182,7 @@ eOSState cMenuSearchCommands::MarkAsRecorded(void) return osContinue; cTimer* dummyTimer = new cTimer(event); cMutexLock RecsDoneLock(&RecsDone); - RecsDone.Add(new cRecDone(dummyTimer, event, search)); + RecsDone.Add(new cRecDone(dummyTimer, event, search, "Unused")); RecsDone.Save(); delete dummyTimer; return osBack; diff --git a/recdone.c b/recdone.c index 53780cd..ce63ffe 100644 --- a/recdone.c +++ b/recdone.c @@ -48,15 +48,20 @@ cRecDone::cRecDone() buffer = NULL; searchID = -1; rawdescription = NULL; + fileName = NULL; } -cRecDone::cRecDone(const cTimer* Timer, const cEvent* Event, cSearchExt* Search) +cRecDone::cRecDone(const cTimer* Timer, const cEvent* Event, cSearchExt* Search, const char* Name) { title = shortText = description = aux = rawdescription = NULL; startTime = 0; + timerStart = 0; + timerStop = 0; + vpsused = false; duration = 0; searchID = Search ? Search->ID : -1; buffer = NULL; + fileName = strdup(Name); if (Event) { if (Event->Title()) @@ -71,6 +76,9 @@ cRecDone::cRecDone(const cTimer* Timer, const cEvent* Event, cSearchExt* Search) channelID = Timer->Channel()->GetChannelID(); if (!isempty(Timer->Aux())) aux = strdup(Timer->Aux()); + timerStart = Timer->StartTime(); + timerStop = Timer->StopTime(); + vpsused = Timer->HasFlags(tfVps) && Event->Vps(); } else { channelID = tChannelID::InvalidID; aux = NULL; @@ -104,6 +112,10 @@ cRecDone::~cRecDone() free(aux); aux = NULL; } + if (fileName) { + free(fileName); + fileName = NULL; + } } diff --git a/recdone.h b/recdone.h index cfad67f..6cfa50b 100644 --- a/recdone.h +++ b/recdone.h @@ -38,17 +38,21 @@ public: char *title; // Title of this event char *shortText; // Short description of this event char *description; // Description of this event + char *fileName; // Name used in MsgRecording char *aux; // Aux info - time_t startTime; // Start time of the timer - int duration; - int searchID; // ID of the search, that triggered this recording + time_t startTime; // Start time of the event + time_t timerStart; // Start time of the timer + time_t timerStop; // Stop time of the timer + bool vpsused; + int duration; // Duration of event + int searchID; // ID of the search, that triggered this recording tChannelID channelID; char* rawdescription; static char *buffer; cRecDone(); - cRecDone(const cTimer*, const cEvent* event, cSearchExt* search); + cRecDone(const cTimer*, const cEvent* event, cSearchExt* search, const char* name); ~cRecDone(); static bool Read(FILE *f); diff --git a/recdone_thread.c b/recdone_thread.c index 6b0e02b..97f0991 100644 --- a/recdone_thread.c +++ b/recdone_thread.c @@ -80,7 +80,7 @@ void cRecdoneThread::Action(void) time_t now = time(NULL); // remove timers that finished recording from TimersRecording // incomplete recordings are kept for a while, perhaps they will be resumed - LOCK_TIMERS_READ; + LOCK_TIMERS_READ; // must be done before TimersRecordingLock while (m_fnames.size()) { vector::iterator it = m_fnames.begin(); const char *m_filename = (*it).c_str(); @@ -88,49 +88,72 @@ void cRecdoneThread::Action(void) cMutexLock TimersRecordingLock(&TimersRecording); cRecDoneTimerObj *tiR = TimersRecording.First(); while (tiR) { - // check if timer still exists + // check if a timer still exists + // if we find a timer we take its values, else from recDone bool found = false; - for (const cTimer *ti = Timers->First(); ti; ti = Timers->Next(ti)) if (ti == tiR->timer) { found = true; break; } - if (found && !tiR->timer->Recording()) { - if (tiR->recDone) { - cSearchExt* search = SearchExts.GetSearchFromID(tiR->recDone->searchID); - if (!search) return; - // check if recording has ended before timer end + if (!tiR->recDone) { + LogFile.Log(3, "recdone: this tiR has no recDone struct"); + tiR = TimersRecording.Next(tiR); + continue; + } + if (found && tiR->timer->Recording()) { + LogFile.Log(3, "recdone: tiR for %s is recording",tiR->recDone->fileName); + tiR = TimersRecording.Next(tiR); + continue; + } - bool complete = true; + LogFile.Log(2, "recdone: processing tiR for %s",tiR->recDone->fileName); + if (strcmp(tiR->recDone->fileName,m_filename) == 0) { + // we have the correct entry for the check + bool complete = true; + cSearchExt* search = SearchExts.GetSearchFromID(tiR->recDone->searchID); + int recFraction = 100; + time_t stopTime = found ? tiR->timer->StopTime() : tiR->recDone->timerStop; + + if (tiR->lastBreak == -1 || !search) { // started too late or missing searchID + LogFile.Log(2, "started too late : '%s' or missing searchID %d", found?tiR->timer->File():m_filename, tiR->recDone->searchID); + tiR->lastBreak = 0; // triggers deletion + } + else { + // check if recording length is as expected const cRecording *pRecording; { LOCK_RECORDINGS_READ; pRecording = Recordings->GetByName(m_filename); + long timerLengthSecs = found ? tiR->timer->StopTime() - tiR->timer->StartTime() : tiR->recDone->timerStop - tiR->recDone->timerStart; + int recLen = 0; + if (pRecording && timerLengthSecs) { + recLen = RecLengthInSecs(pRecording); + recFraction = double(recLen) * 100 / timerLengthSecs; + } } - long timerLengthSecs = tiR->timer->StopTime() - tiR->timer->StartTime(); - int recFraction = 100; - if (pRecording && timerLengthSecs) { - int recLen = RecLengthInSecs(pRecording); - recFraction = double(recLen) * 100 / timerLengthSecs; - } - bool vpsUsed = tiR->timer->HasFlags(tfVps) && tiR->timer->Event() && tiR->timer->Event()->Vps(); - if ((!vpsUsed && now < tiR->timer->StopTime()) || recFraction < (vpsUsed ? 90 : 98)) { // assure timer has reached its end or at least 98% were recorded + bool vpsUsed = found ? tiR->timer->HasFlags(tfVps) && tiR->timer->Event() && tiR->timer->Event()->Vps():tiR->recDone->vpsused; + if ((!vpsUsed && now < stopTime) || recFraction < (vpsUsed ? 90 : 98)) { // assure timer has reached its end or at least 98% were recorded complete = false; - LogFile.Log(1, "finished: '%s' (not complete! - recorded only %d%%); search timer: '%s'; VPS used: %s", tiR->timer->File(), recFraction, search->search, vpsUsed ? "Yes" : "No"); + LogFile.Log(1, "finished: '%s' (not complete! - recorded only %d%%); search timer: '%s'; VPS used: %s", found?tiR->timer->File():m_filename, recFraction, search->search, vpsUsed ? "Yes" : "No"); + dsyslog("epgsearch: finished: '%s' (not complete! - recorded only %d%%); search timer: '%s'; VPS used: %s", found?tiR->timer->File():m_filename, recFraction, search->search, vpsUsed ? "Yes" : "No"); } else { - LogFile.Log(1, "finished: '%s'; search timer: '%s'; VPS used: %s", tiR->timer->File(), search->search, vpsUsed ? "Yes" : "No"); - if (recFraction < 100) + LogFile.Log(1, "finished: '%s' (complete); search timer: '%s'; VPS used: %s", found?tiR->timer->File():m_filename, search->search, vpsUsed ? "Yes" : "No"); + if (recFraction < 100) { LogFile.Log(2, "recorded %d%%'", recFraction); + dsyslog("epgsearch: finished: '%s' (complete) recorded %d%%", found?tiR->timer->File():m_filename, (recFraction<100) ? recFraction : 100); + } + else + dsyslog("epgsearch: finished: '%s' (complete)", found?tiR->timer->File():m_filename); } - if (complete) { + if (complete) { // add to epgsearchdone.data RecsDone.Add(tiR->recDone); LogFile.Log(1, "added rec done for '%s~%s';%s", tiR->recDone->title ? tiR->recDone->title : "unknown title", tiR->recDone->shortText ? tiR->recDone->shortText : "unknown subtitle", search->search); RecsDone.Save(); - tiR->recDone = NULL; // prevent deletion + tiR->recDone = NULL; // prevent deletion (now in RecsDone) tiR->lastBreak = 0; // check for search timers to delete automatically @@ -139,28 +162,24 @@ void cRecdoneThread::Action(void) // trigger a search timer update (skip running events) search->skipRunningEvents = true; updateForced = 1; - } else if (tiR->lastBreak == 0) // store first break + } else if (tiR->lastBreak == 0) // not complete: assure break is set tiR->lastBreak = now; } - if (tiR->lastBreak == 0 || (now - tiR->lastBreak) > ALLOWED_BREAK_INSECS) { - // remove finished recordings or those with an unallowed break - if (tiR->recDone) delete tiR->recDone; // clean up - cRecDoneTimerObj *tiRNext = TimersRecording.Next(tiR); - TimersRecording.Del(tiR); - tiR = tiRNext; - continue; + } // handled only tiR with correct filename + // cleanup + if (!tiR->lastBreak || (now - tiR->lastBreak) > ALLOWED_BREAK_INSECS) { + // remove finished recordings or those with an unallowed break + if (tiR->recDone) { // if added to searchdone recDone is NULL + LogFile.Log(3, "recdone: remove tiR and recdone for %s", tiR->recDone->fileName); + delete tiR->recDone; // clean up } - break; - } - if (!found) { - if (tiR->recDone) delete tiR->recDone; // clean up cRecDoneTimerObj *tiRNext = TimersRecording.Next(tiR); TimersRecording.Del(tiR); tiR = tiRNext; continue; } tiR = TimersRecording.Next(tiR); - } + } // while tiR m_fnames.erase(it); } // while fnames LogFile.Log(1, "recdone_thread ended"); diff --git a/recstatus.c b/recstatus.c index 408f159..be07db4 100644 --- a/recstatus.c +++ b/recstatus.c @@ -47,12 +47,12 @@ void cRecStatusMonitor::Recording(const cDevice *Device, const char *Name, const if (EPGSearchConfig.checkTimerConflOnRecording) cConflictCheckThread::Init((cPluginEpgsearch*)cPluginManager::GetPlugin("epgsearch"), true); - LOCK_TIMERS_READ; + LOCK_TIMERS_READ; // already set (thread is vdr-main) for (const cTimer *ti = Timers->First(); ti; ti = Timers->Next(ti)) if (ti->Recording()) { // check if this is a new entry cRecDoneTimerObj *tiRFound = NULL; - cMutexLock TimersRecordingLock(&TimersRecording); + cMutexLock TimersRecordingLock(&TimersRecording); // lock after TIMERS for (cRecDoneTimerObj *tiR = TimersRecording.First(); tiR; tiR = TimersRecording.Next(tiR)) if (tiR->timer == ti) { tiRFound = tiR; @@ -61,7 +61,7 @@ void cRecStatusMonitor::Recording(const cDevice *Device, const char *Name, const if (tiRFound) { // already handled, perhaps a resume if (tiRFound->lastBreak > 0 && now - tiRFound->lastBreak <= ALLOWED_BREAK_INSECS) { - LogFile.Log(1, "accepting resume of '%s' on device %d", Name, Device->CardIndex()); + LogFile.Log(1, "assuming resume of '%s' on device %d", Name, Device->CardIndex()); tiRFound->lastBreak = 0; } continue; @@ -70,8 +70,9 @@ void cRecStatusMonitor::Recording(const cDevice *Device, const char *Name, const cRecDoneTimerObj* timerObj = new cRecDoneTimerObj(ti, Device->DeviceNumber()); TimersRecording.Add(timerObj); + // ignore if not avoid repeats and no auto-delete cSearchExt* search = TriggeredFromSearchTimer(ti); - if (!search || (search->avoidRepeats == 0 && search->delMode == 0)) // ignore if not avoid repeats and no auto-delete + if (!search || (search->avoidRepeats == 0 && search->delMode == 0)) continue; bool vpsUsed = ti->HasFlags(tfVps) && ti->Event() && ti->Event()->Vps(); @@ -87,11 +88,14 @@ void cRecStatusMonitor::Recording(const cDevice *Device, const char *Name, const continue; } time_t now = time(NULL); + timerObj->recDone = new cRecDone(ti, event, search, Filename); + LogFile.Log(3, "epgsearch: created recDone for %s",Filename); if (vpsUsed || now < ti->StartTime() + 60) { // allow a delay of one minute - timerObj->recDone = new cRecDone(ti, event, search); return; - } else + } else { + timerObj->lastBreak = -1; // mark as incomplete LogFile.Log(1, "recording started too late! will be ignored"); + } } } -- cgit v1.2.3