Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Improved the detection and handling of missed recordings.

Previously, the scheduler had some rudimentary support for detecting
missed recordings.  The scheduler now saves the state of future
recordings in the (now inappropriately named) oldrecorded table.  It
then uses that information when it is restarted after a crash or hang
to determine what was missed.
  • Loading branch information...
commit 543d7836c99adc4817fbb65553aef421426ef6cd 1 parent 0bab8fb
@gigem gigem authored
View
2  mythtv/bindings/perl/MythTV.pm
@@ -114,7 +114,7 @@ package MythTV;
# schema version supported in the main code. We need to check that the schema
# version in the database is as expected by the bindings, which are expected
# to be kept in sync with the main code.
- our $SCHEMA_VERSION = "1270";
+ our $SCHEMA_VERSION = "1271";
# NUMPROGRAMLINES is defined in mythtv/libs/libmythtv/programinfo.h and is
# the number of items in a ProgramInfo QStringList group used by
View
2  mythtv/bindings/python/MythTV/static.py
@@ -5,7 +5,7 @@
"""
OWN_VERSION = (0,25,-1,1)
-SCHEMA_VERSION = 1270
+SCHEMA_VERSION = 1271
NVSCHEMA_VERSION = 1007
MUSICSCHEMA_VERSION = 1018
PROTO_VERSION = '65'
View
2  mythtv/libs/libmyth/programinfo.cpp
@@ -4119,6 +4119,7 @@ static bool FromProgramQuery(
"FROM program "
"LEFT JOIN channel ON program.chanid = channel.chanid "
"LEFT JOIN oldrecorded AS oldrecstatus ON "
+ " oldrecstatus.future = 0 AND "
" program.title = oldrecstatus.title AND "
" channel.callsign = oldrecstatus.station AND "
" program.starttime = oldrecstatus.starttime "
@@ -4224,6 +4225,7 @@ bool LoadFromOldRecorded(
" duplicate "
" FROM oldrecorded "
" LEFT JOIN channel ON oldrecorded.chanid = channel.chanid "
+ " WHERE oldrecorded.future = 0 "
+ sql;
query.prepare(querystr);
View
16 mythtv/libs/libmyth/programtypes.cpp
@@ -54,7 +54,7 @@ QString toUIState(RecStatusType recstatus)
if (recstatus == rsConflict || recstatus == rsOffLine ||
recstatus == rsTunerBusy || recstatus == rsFailed ||
- recstatus == rsAborted)
+ recstatus == rsAborted || recstatus == rsMissed)
{
return "error";
}
@@ -114,6 +114,9 @@ QChar toQChar(RecStatusType recstatus, uint cardid)
case rsMissed:
ret = QObject::tr("M", "RecStatusChar rsMissed");
break;
+ case rsMissedFuture:
+ ret = QObject::tr("m", "RecStatusChar rsMissedFuture");
+ break;
case rsConflict:
ret = QObject::tr("C", "RecStatusChar rsConflict");
break;
@@ -184,6 +187,8 @@ QString toString(RecStatusType recstatus, RecordingType rectype)
return QObject::tr("Manual Cancel");
case rsMissed:
return QObject::tr("Missed");
+ case rsMissedFuture:
+ return QObject::tr("Missed Future");
case rsConflict:
return QObject::tr("Conflicting");
case rsLaterShowing:
@@ -240,8 +245,13 @@ QString toDescription(RecStatusType recstatus, const QDateTime &recstartts)
break;
case rsMissed:
message = QObject::tr(
- "This showing was not recorded because it "
- "was scheduled after it would have ended.");
+ "This showing was not recorded because the "
+ "master backend was hung or not running.");
+ break;
+ case rsMissedFuture:
+ message = QObject::tr(
+ "This showing was not recorded because the "
+ "master backend was hung or not running.");
break;
case rsCancelled:
message = QObject::tr(
View
1  mythtv/libs/libmyth/programtypes.h
@@ -186,6 +186,7 @@ typedef enum SubtitleTypes {
} SubtitleType; // has 4 bits in ProgramInfo::properties
typedef enum RecStatusTypes {
+ rsMissedFuture = -11,
rsTuning = -10,
rsFailed = -9,
rsTunerBusy = -8,
View
13 mythtv/libs/libmythtv/dbcheck.cpp
@@ -21,7 +21,7 @@ using namespace std;
mythtv/bindings/perl/MythTV.pm
*/
/// This is the DB schema version expected by the running MythTV instance.
-const QString currentDatabaseVersion = "1270";
+const QString currentDatabaseVersion = "1271";
static bool UpdateDBVersionNumber(const QString &newnumber, QString &dbver);
static bool performActualUpdate(
@@ -5607,6 +5607,17 @@ NULL
return false;
}
+ if (dbver == "1270")
+ {
+ const char *updates[] = {
+"ALTER TABLE oldrecorded ADD future TINYINT(1) NOT NULL DEFAULT 0;",
+"UPDATE oldrecorded SET future=0;",
+NULL
+};
+ if (!performActualUpdate(updates, "1271", dbver))
+ return false;
+ }
+
return true;
}
View
13 mythtv/libs/libmythtv/recordinginfo.cpp
@@ -1129,12 +1129,13 @@ void RecordingInfo::ReactivateRecording(void)
/**
* \brief Adds recording history, creating "record" it if necessary.
*/
-void RecordingInfo::AddHistory(bool resched, bool forcedup)
+void RecordingInfo::AddHistory(bool resched, bool forcedup, bool future)
{
bool dup = (GetRecordingStatus() == rsRecorded || forcedup);
RecStatusType rs = (GetRecordingStatus() == rsCurrentRecording) ?
rsPreviousRecording : GetRecordingStatus();
- oldrecstatus = GetRecordingStatus();
+ if (!future)
+ oldrecstatus = GetRecordingStatus();
if (dup)
SetReactivated(false);
uint erecid = parentid ? parentid : recordid;
@@ -1144,10 +1145,11 @@ void RecordingInfo::AddHistory(bool resched, bool forcedup)
result.prepare("REPLACE INTO oldrecorded (chanid,starttime,"
"endtime,title,subtitle,description,category,"
"seriesid,programid,findid,recordid,station,"
- "rectype,recstatus,duplicate,reactivate) "
+ "rectype,recstatus,duplicate,reactivate,future) "
"VALUES(:CHANID,:START,:END,:TITLE,:SUBTITLE,:DESC,"
":CATEGORY,:SERIESID,:PROGRAMID,:FINDID,:RECORDID,"
- ":STATION,:RECTYPE,:RECSTATUS,:DUPLICATE,:REACTIVATE);");
+ ":STATION,:RECTYPE,:RECSTATUS,:DUPLICATE,:REACTIVATE,"
+ ":FUTURE);");
result.bindValue(":CHANID", chanid);
result.bindValue(":START", startts);
result.bindValue(":END", endts);
@@ -1164,6 +1166,7 @@ void RecordingInfo::AddHistory(bool resched, bool forcedup)
result.bindValue(":RECSTATUS", rs);
result.bindValue(":DUPLICATE", dup);
result.bindValue(":REACTIVATE", IsReactivated());
+ result.bindValue(":FUTURE", future);
if (!result.exec())
MythDB::DBError("addHistory", result);
@@ -1291,7 +1294,7 @@ void RecordingInfo::SetDupHistory(void)
MSqlQuery result(MSqlQuery::InitCon());
result.prepare("UPDATE oldrecorded SET duplicate = 1 "
- "WHERE duplicate = 0 "
+ "WHERE future = 0 AND duplicate = 0 "
"AND title = :TITLE AND "
"((programid = '' AND subtitle = :SUBTITLE"
" AND description = :DESC) OR "
View
3  mythtv/libs/libmythtv/recordinginfo.h
@@ -192,7 +192,8 @@ class MTV_PUBLIC RecordingInfo : public ProgramInfo
void ToggleRecord(void);
// these five can be moved to programinfo
- void AddHistory(bool resched = true, bool forcedup = false);//pi
+ void AddHistory(bool resched = true, bool forcedup = false,
+ bool future = false);//pi
void DeleteHistory(void);//pi
void ForgetHistory(void);//pi
void SetDupHistory(void);//pi
View
72 mythtv/programs/mythbackend/scheduler.cpp
@@ -1343,6 +1343,7 @@ void Scheduler::PruneRedundants(void)
// change history, can we?
if (p->GetRecordingStatus() != rsRecording &&
p->GetRecordingStatus() != rsTuning &&
+ p->GetRecordingStatus() != rsMissedFuture &&
p->GetScheduledEndTime() < schedTime &&
p->GetRecordingEndTime() < schedTime)
{
@@ -1361,7 +1362,12 @@ void Scheduler::PruneRedundants(void)
p->oldrecstatus != rsNotListed &&
!p->IsReactivated())
{
+ RecStatusType rs = p->GetRecordingStatus();
p->SetRecordingStatus(p->oldrecstatus);
+ // Re-mark rsMissedFuture entries so non-future history
+ // will be saved in the scheduler thread.
+ if (rs == rsMissedFuture)
+ p->oldrecstatus = rsMissedFuture;
}
if (!Recording(p))
@@ -1699,9 +1705,10 @@ void Scheduler::RunScheduler(void)
struct timeval fillstart, fillend;
float matchTime, placeTime;
- // Mark anything that was recording as aborted. We'll fix it up.
- // if possible, after the slaves connect and we start scheduling.
MSqlQuery query(dbConn);
+
+ // Mark anything that was recording as aborted. We'll fix it, if
+ // needed, after the slaves connect and we start scheduling.
query.prepare("UPDATE oldrecorded SET recstatus = :RSABORTED "
" WHERE recstatus = :RSRECORDING OR recstatus = :RSTUNING");
query.bindValue(":RSABORTED", rsAborted);
@@ -1710,6 +1717,24 @@ void Scheduler::RunScheduler(void)
if (!query.exec())
MythDB::DBError("UpdateAborted", query);
+ // Mark anything that was going to record as missed. We'll fix
+ // it, if needed, after we start scheduling.
+ query.prepare("UPDATE oldrecorded SET recstatus = :RSMISSED "
+ "WHERE recstatus = :RSWILLRECORD");
+ query.bindValue(":RSMISSED", rsMissed);
+ query.bindValue(":RSWILLRECORD", rsWillRecord);
+ if (!query.exec())
+ MythDB::DBError("UpdateMissed1", query);
+
+ // Clear the "future" status of anything older than the maximum
+ // endoffset. Anything more recent will bee handled elsewhere
+ // during normal processing.
+ query.prepare("UPDATE oldrecorded SET future = 0 "
+ "WHERE future > 0 AND "
+ " endtime < (NOW() - INTERVAL 8 HOUR)");
+ if (!query.exec())
+ MythDB::DBError("UpdateMissed2", query);
+
// wait for slaves to connect
sleep(3);
@@ -1863,6 +1888,22 @@ void Scheduler::RunScheduler(void)
PutInactiveSlavesToSleep();
lastSleepCheck = QDateTime::currentDateTime();
+ // Delete old, future entries from oldrecorded and
+ // write new ones as well as new missed entries.
+ query.prepare("DELETE FROM oldrecorded WHERE future > 0");
+ if (!query.exec())
+ MythDB::DBError("DeleteFuture", query);
+
+ RecIter it = reclist.begin();
+ for ( ; it != reclist.end(); ++it)
+ {
+ RecordingInfo *p = *it;
+ if (p->oldrecstatus == rsMissedFuture)
+ p->AddHistory(false, false, false);
+ else if (p->oldrecstatus == rsUnknown)
+ p->AddHistory(false, false, true);
+ }
+
SendMythSystemEvent("SCHEDULER_RAN");
}
}
@@ -3357,7 +3398,7 @@ void Scheduler::AddNewRecords(void)
" recduplicate = (recorded.endtime IS NOT NULL), "
" findduplicate = (oldfind.findid IS NOT NULL), "
" oldrecstatus = oldrecorded.recstatus "
-" WHERE program.endtime >= NOW() - INTERVAL 1 DAY "
+" WHERE program.endtime >= NOW() - INTERVAL 9 HOUR "
);
rmquery.replace("RECTABLE", schedTmpRecord);
@@ -3388,6 +3429,7 @@ void Scheduler::AddNewRecords(void)
" p.subtitletypes+0, p.audioprop+0, RECTABLE.storagegroup, "//39-41
" capturecard.hostname, recordmatch.oldrecstatus, "
" RECTABLE.avg_delay, "//42-44
+ " oldrecstatus.future, " //45
+ pwrpri + QString(
"FROM recordmatch "
"INNER JOIN RECTABLE ON (recordmatch.recordid = RECTABLE.recordid) "
@@ -3488,6 +3530,15 @@ void Scheduler::AddNewRecords(void)
result.value(38).toUInt(),//videoproperties
result.value(40).toUInt());//audioproperties
+ // If this was previously a futrue recording and it's still
+ // viable, start over.
+ bool future = result.value(45).toInt();
+ if (future && p->GetRecordingEndTime() >= schedTime)
+ {
+ p->SetRecordingStatus(rsUnknown);
+ p->oldrecstatus = rsUnknown;
+ }
+
if (!recTypeRecPriorityMap.contains(p->GetRecordingRuleType()))
{
recTypeRecPriorityMap[p->GetRecordingRuleType()] =
@@ -3496,7 +3547,7 @@ void Scheduler::AddNewRecords(void)
p->SetRecordingPriority(
p->GetRecordingPriority() + recTypeRecPriorityMap[p->GetRecordingRuleType()] +
- result.value(45).toInt() +
+ result.value(46).toInt() +
((autopriority) ?
autopriority - (result.value(44).toInt() * autostrata / 200) : 0));
@@ -3568,11 +3619,16 @@ void Scheduler::AddNewRecords(void)
if (inactive)
newrecstatus = rsInactive;
- // Mark anything that has already passed as missed. If it
- // survives PruneOverlaps, it will get deleted or have its old
- // status restored in PruneRedundants.
+ // Mark anything that has already passed as some type of
+ // missed. If it survives PruneOverlaps, it will get deleted
+ // or have its old status restored in PruneRedundants.
if (p->GetRecordingEndTime() < schedTime)
- newrecstatus = rsMissed;
+ {
+ if (future)
+ newrecstatus = rsMissedFuture;
+ else
+ newrecstatus = rsMissed;
+ }
p->SetRecordingStatus(newrecstatus);
View
1  mythtv/programs/mythfrontend/progdetails.cpp
@@ -520,6 +520,7 @@ void ProgDetails::loadPage(void)
{
query.prepare("SELECT recstatus, starttime "
"FROM oldrecorded WHERE duplicate > 0 AND "
+ "future = 0 AND "
"((programid <> '' AND programid = :PROGRAMID) OR "
" (title <> '' AND title = :TITLE AND "
" subtitle <> '' AND subtitle = :SUBTITLE AND "
View
3  mythtv/programs/mythfrontend/proglist.cpp
@@ -728,7 +728,8 @@ void ProgLister::DeleteOldSeries(bool ok)
return;
MSqlQuery query(MSqlQuery::InitCon());
- query.prepare("DELETE FROM oldrecorded WHERE title = :TITLE");
+ query.prepare("DELETE FROM oldrecorded "
+ "WHERE title = :TITLE AND future = 0");
query.bindValue(":TITLE", pi->GetTitle());
if (!query.exec())
MythDB::DBError("ProgLister::DeleteOldSeries -- delete", query);
View
3  mythtv/programs/mythfrontend/viewscheduled.cpp
@@ -414,7 +414,8 @@ void ViewScheduled::FillList()
recstatus == rsOffLine ||
recstatus == rsTunerBusy ||
recstatus == rsFailed ||
- recstatus == rsAborted)
+ recstatus == rsAborted ||
+ recstatus == rsMissed)
state = "error";
else if (recstatus == rsWillRecord)
{
Please sign in to comment.
Something went wrong with that request. Please try again.