From cbb8eb1ee32a658a519d2d5fb751ace114f63bf9 Mon Sep 17 00:00:00 2001 From: David Engel Date: Wed, 11 Apr 2012 09:40:25 -0500 Subject: [PATCH] Add duplicate checking and limited matching optimizations and other scheduler related changes. The three major changes are as follows. Split the checks against the oldrecorded and recorded tables for previous recordings away from the "place" phase of scheduling and call it the "check" phase. This makes it easier to report how much time is spent doing this and leads to the next major change. Drastically reduce the number of checks against the oldrecorded and recorded tables for previous recordings. Historically, this has been the most significant contributor to long scheduler runs. Not every unnecessary check is eliminated, but the vast majority of them are. Trying to remove the few remaining ones would probably take longer than simply rechecking them. Allow for checking a subset of the program table when new guide data is available. This is primarily aimed at EIT scanning where typically only a few hours of guide data for specific channels is updated in an orderly fashion. Not rechecking the unchanged guide data greatly reduces the amount of work the scheduler has to do. Please note the EIT scanner has not been updated yet to take advantage of this change. The other changes are as follows. Use more expressive reschedule requests. This is primarily to support the changes listed above. It also makes it easier to understand why reschedules occurred when reading the logs and might lead to the elimination of unnecessary reschedules in the future. MATCH reschedule requests should be used when the guide data or a specific recording rule is changed. The syntax is as follows. RESCHEDULE_RECORDINGS MATCH maxstarttime should be in ISO format. If recordid, sourceid or mplexid are non-zero or maxstarttime is a valid time, the scheduler will restrict itself to recording rules and guide data matching those parameters. reason is purely for purposes of logging. CHECK reschedule requests should be used when the status of a specific episode is affected such as when "never record" or "allow re-record" are selected or a recording finishes or is deleted. The syntax is as follows. RESCHEDULE_RECORDINGS CHECK <subtitle> <description> <programid> recordid should be the parent recordid, when applicable, otherwise, the normal recordid. Setting programid to '**any**" and title to "" is a special case used to emulate an old reschedule request for recordid 0. Setting programid to "**any**" and title to other than "" is another special case used when all entries for a title are deleted from oldrecorded. recstatus and reason are purely for purposes of logging. PLACE reschedule request should be used in all other cases. The syntax is as follows. RESCHEDULE_RECORDINGS PLACE <reason> reason is purely for purposes of logging. Update the PHP and Python bindings to use the new reschedule requests. Please note the bindings API has not changed to make full use of the new requests. I'm leaving it to the bindings maintainers to decide how best to do that. Clear record.duplicate when a recording is queued for deletion. It should have been this way all along and avoids a redundant reschedule when the file is actually unlinked. Lower the very detailed scheduler placement logging to LOG_DEBUG. This makes VB_SCHEDULE/LOG_INFO more useful for less voluminous purposes. Add findid to the recordmatch table. This avoids having to calculate it in multiple places. Tighten the window for rescheduling long running programs from "24 hours ago" to "~8 hours ago". Avoid creating unnecessary RecordingInfo objects that are going to get deleted later anyway. Fixes #10533 --- mythtv/bindings/perl/MythTV.pm | 6 +- mythtv/bindings/php/MythBackend.php | 16 +- mythtv/bindings/python/MythTV/mythproto.py | 12 +- mythtv/bindings/python/MythTV/static.py | 6 +- mythtv/libs/libmyth/programinfo.cpp | 3 +- mythtv/libs/libmythbase/mythversion.h | 8 +- mythtv/libs/libmythtv/dbcheck.cpp | 12 + mythtv/libs/libmythtv/eitscanner.cpp | 2 +- mythtv/libs/libmythtv/recordinginfo.cpp | 10 +- mythtv/libs/libmythtv/recordinginfo.h | 2 - mythtv/libs/libmythtv/recordingrule.cpp | 8 +- mythtv/libs/libmythtv/scheduledrecording.cpp | 44 +- mythtv/libs/libmythtv/scheduledrecording.h | 35 +- mythtv/libs/libmythtv/tv_rec.cpp | 2 +- mythtv/programs/mythbackend/main_helpers.cpp | 6 +- mythtv/programs/mythbackend/mainserver.cpp | 44 +- mythtv/programs/mythbackend/mainserver.h | 3 +- mythtv/programs/mythbackend/scheduler.cpp | 808 +++++++++++------- mythtv/programs/mythbackend/scheduler.h | 44 +- mythtv/programs/mythfilldatabase/main.cpp | 3 +- .../mythfrontend/channelrecpriority.cpp | 2 +- .../programs/mythfrontend/custompriority.cpp | 4 +- mythtv/programs/mythfrontend/main.cpp | 2 +- mythtv/programs/mythfrontend/proglist.cpp | 8 +- .../mythfrontend/programrecpriority.cpp | 5 +- 25 files changed, 707 insertions(+), 388 deletions(-) diff --git a/mythtv/bindings/perl/MythTV.pm b/mythtv/bindings/perl/MythTV.pm index ff9777a19fe..93ce207ae1e 100644 --- a/mythtv/bindings/perl/MythTV.pm +++ b/mythtv/bindings/perl/MythTV.pm @@ -106,15 +106,15 @@ package MythTV; # Note: as of July 21, 2010, this is actually a string, to account for proto # versions of the form "58a". This will get used if protocol versions are # changed on a fixes branch ongoing. - our $PROTO_VERSION = "72"; - our $PROTO_TOKEN = "D78EFD6F"; + our $PROTO_VERSION = "73"; + our $PROTO_TOKEN = "D7FE8D6F"; # currentDatabaseVersion is defined in libmythtv in # mythtv/libs/libmythtv/dbcheck.cpp and should be the current MythTV core # 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 = "1299"; + our $SCHEMA_VERSION = "1300"; # NUMPROGRAMLINES is defined in mythtv/libs/libmythtv/programinfo.h and is # the number of items in a ProgramInfo QStringList group used by diff --git a/mythtv/bindings/php/MythBackend.php b/mythtv/bindings/php/MythBackend.php index 73e5c04647b..9b43d4ec6b7 100644 --- a/mythtv/bindings/php/MythBackend.php +++ b/mythtv/bindings/php/MythBackend.php @@ -11,8 +11,8 @@ class MythBackend { // MYTH_PROTO_VERSION is defined in libmyth in mythtv/libs/libmyth/mythcontext.h // and should be the current MythTV protocol version. - static $protocol_version = '72'; - static $protocol_token = 'D78EFD6F'; + static $protocol_version = '73'; + static $protocol_token = 'D7FE8D6F'; // The character string used by the backend to separate records static $backend_separator = '[]:[]'; @@ -205,7 +205,17 @@ public function queryProgramRows($query = null, $offset = 1) { * need to indicate every record rule is affected, then use -1. /**/ public function rescheduleRecording($recordid = -1) { - $this->sendCommand('RESCHEDULE_RECORDINGS '.$recordid); + if ($recordid == 0) { + $this->sendCommand(array('RESCHEDULE_RECORDINGS ', + 'CHECK 0 0 0 PHP', + '', '', '', '**any**')); + } + else { + if ($recordid == -1) + $recordid = 0; + $this->sendCommand(array('RESCHEDULE_RECORDINGS ', + 'MATCH '.$recordid.' 0 0 - PHP')); + } Cache::clear(); if ($this->listenForEvent('SCHEDULE_CHANGE')) return true; diff --git a/mythtv/bindings/python/MythTV/mythproto.py b/mythtv/bindings/python/MythTV/mythproto.py index 41a39e8ee5e..a91ec80322a 100644 --- a/mythtv/bindings/python/MythTV/mythproto.py +++ b/mythtv/bindings/python/MythTV/mythproto.py @@ -670,7 +670,17 @@ def reschedule(self, recordid=-1, wait=False): ['BACKEND_MESSAGE', 'SCHEDULE_CHANGE', 'empty'])) - self.backendCommand('RESCHEDULE_RECORDINGS '+str(recordid)) + if recordid == 0: + self.backendCommand(BACKEND_SEP.join(\ + ['RESCHEDULE_RECORDINGS', + 'CHECK 0 0 0 Python', + '', '', '', '**any**'])) + else: + if recordid == -1: + recordid = 0 + self.backendCommand(BACKEND_SEP.join(\ + ['RESCHEDULE_RECORDINGS', + 'MATCH ' + str(recordid) + ' 0 0 - Python'])) if wait: eventlock.wait() diff --git a/mythtv/bindings/python/MythTV/static.py b/mythtv/bindings/python/MythTV/static.py index 6b587b25e76..504f5a117fb 100644 --- a/mythtv/bindings/python/MythTV/static.py +++ b/mythtv/bindings/python/MythTV/static.py @@ -5,11 +5,11 @@ """ OWN_VERSION = (0,25,-1,3) -SCHEMA_VERSION = 1299 +SCHEMA_VERSION = 1300 NVSCHEMA_VERSION = 1007 MUSICSCHEMA_VERSION = 1018 -PROTO_VERSION = '72' -PROTO_TOKEN = 'D78EFD6F' +PROTO_VERSION = '73' +PROTO_TOKEN = 'D7FE8D6F' BACKEND_SEP = '[]:[]' INSTALL_PREFIX = '/usr/local' diff --git a/mythtv/libs/libmyth/programinfo.cpp b/mythtv/libs/libmyth/programinfo.cpp index 83e0d213be8..b6c24a1dd08 100644 --- a/mythtv/libs/libmyth/programinfo.cpp +++ b/mythtv/libs/libmyth/programinfo.cpp @@ -2678,7 +2678,8 @@ void ProgramInfo::SaveDeletePendingFlag(bool deleteFlag) MSqlQuery query(MSqlQuery::InitCon()); query.prepare("UPDATE recorded" - " SET deletepending = :DELETEFLAG" + " SET deletepending = :DELETEFLAG, " + " duplicate = 0 " " WHERE chanid = :CHANID" " AND starttime = :STARTTIME ;"); query.bindValue(":CHANID", chanid); diff --git a/mythtv/libs/libmythbase/mythversion.h b/mythtv/libs/libmythbase/mythversion.h index 10fe5bd37b3..da78bf18c6d 100644 --- a/mythtv/libs/libmythbase/mythversion.h +++ b/mythtv/libs/libmythbase/mythversion.h @@ -12,7 +12,7 @@ /// Update this whenever the plug-in API changes. /// Including changes in the libmythbase, libmyth, libmythtv, libmythav* and /// libmythui class methods used by plug-ins. -#define MYTH_BINARY_VERSION "0.25.20120408-1" +#define MYTH_BINARY_VERSION "0.26.20120411-1" /** \brief Increment this whenever the MythTV network protocol changes. * @@ -35,8 +35,8 @@ * mythtv/bindings/python/MythTV/static.py (version number) * mythtv/bindings/python/MythTV/mythproto.py (layout) */ -#define MYTH_PROTO_VERSION "72" -#define MYTH_PROTO_TOKEN "D78EFD6F" +#define MYTH_PROTO_VERSION "73" +#define MYTH_PROTO_TOKEN "D7FE8D6F" /** \brief Increment this whenever the MythTV core database schema changes. * @@ -57,7 +57,7 @@ * mythtv/bindings/php/MythBackend.php #endif -#define MYTH_DATABASE_VERSION "1299" +#define MYTH_DATABASE_VERSION "1300" MBASE_PUBLIC const char *GetMythSourceVersion(); diff --git a/mythtv/libs/libmythtv/dbcheck.cpp b/mythtv/libs/libmythtv/dbcheck.cpp index 8e466d814fe..eb4e5509c92 100644 --- a/mythtv/libs/libmythtv/dbcheck.cpp +++ b/mythtv/libs/libmythtv/dbcheck.cpp @@ -1948,6 +1948,18 @@ NULL return false; } + if (dbver == "1299") + { + const char *updates[] = { +"ALTER TABLE recordmatch ADD COLUMN findid INT NOT NULL DEFAULT 0", +"ALTER TABLE recordmatch ADD INDEX (recordid, findid)", +NULL +}; + + if (!performActualUpdate(updates, "1300", dbver)) + return false; + } + return true; } diff --git a/mythtv/libs/libmythtv/eitscanner.cpp b/mythtv/libs/libmythtv/eitscanner.cpp index 6135e08df6d..cde2f6996e5 100644 --- a/mythtv/libs/libmythtv/eitscanner.cpp +++ b/mythtv/libs/libmythtv/eitscanner.cpp @@ -177,7 +177,7 @@ void EITScanner::RescheduleRecordings(void) QDateTime::currentDateTime().addSecs(kMinRescheduleInterval); resched_lock.unlock(); - ScheduledRecording::signalChange(-1); + ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(), "EITScanner"); } /** \fn EITScanner::StartPassiveScan(ChannelBase*, EITSource*, bool) diff --git a/mythtv/libs/libmythtv/recordinginfo.cpp b/mythtv/libs/libmythtv/recordinginfo.cpp index 73cc9efa3db..2c42e2dc014 100644 --- a/mythtv/libs/libmythtv/recordinginfo.cpp +++ b/mythtv/libs/libmythtv/recordinginfo.cpp @@ -1155,7 +1155,7 @@ void RecordingInfo::ReactivateRecording(void) if (!result.exec()) MythDB::DBError("ReactivateRecording", result); - ScheduledRecording::signalChange(0); + ScheduledRecording::ReschedulePlace("Reactivate"); } /** @@ -1223,7 +1223,7 @@ void RecordingInfo::AddHistory(bool resched, bool forcedup, bool future) // The adding of an entry to oldrecorded may affect near-future // scheduling decisions, so recalculate if told if (resched) - ScheduledRecording::signalChange(0); + ScheduledRecording::RescheduleCheck(*this, "AddHistory"); } /** \fn RecordingInfo::DeleteHistory(void) @@ -1257,7 +1257,7 @@ void RecordingInfo::DeleteHistory(void) // The removal of an entry from oldrecorded may affect near-future // scheduling decisions, so recalculate - ScheduledRecording::signalChange(0); + ScheduledRecording::RescheduleCheck(*this, "DeleteHistory"); } /** \fn RecordingInfo::ForgetHistory(void) @@ -1321,7 +1321,7 @@ void RecordingInfo::ForgetHistory(void) // The removal of an entry from oldrecorded may affect near-future // scheduling decisions, so recalculate - ScheduledRecording::signalChange(0); + ScheduledRecording::RescheduleCheck(*this, "ForgetHistory"); } /** \fn RecordingInfo::SetDupHistory(void) @@ -1347,7 +1347,7 @@ void RecordingInfo::SetDupHistory(void) if (!result.exec()) MythDB::DBError("setDupHistory", result); - ScheduledRecording::signalChange(0); + ScheduledRecording::RescheduleCheck(*this, "SetHistory"); } /** diff --git a/mythtv/libs/libmythtv/recordinginfo.h b/mythtv/libs/libmythtv/recordinginfo.h index 5ca56bf7010..4c8378adcc6 100644 --- a/mythtv/libs/libmythtv/recordinginfo.h +++ b/mythtv/libs/libmythtv/recordinginfo.h @@ -228,8 +228,6 @@ class MTV_PUBLIC RecordingInfo : public ProgramInfo void ApplyTranscoderProfileChange(const QString &profile) const;//pi void ApplyTranscoderProfileChangeById(int); - static void signalChange(int recordid); - RecStatusType oldrecstatus; RecStatusType savedrecstatus; bool future; diff --git a/mythtv/libs/libmythtv/recordingrule.cpp b/mythtv/libs/libmythtv/recordingrule.cpp index f7a99819158..907f92b0e1d 100644 --- a/mythtv/libs/libmythtv/recordingrule.cpp +++ b/mythtv/libs/libmythtv/recordingrule.cpp @@ -8,7 +8,7 @@ #include "mythcorecontext.h" // libmythtv -#include "scheduledrecording.h" // For signalChange() +#include "scheduledrecording.h" // For RescheduleMatch() #include "playgroup.h" // For GetInitialName() #include "recordingprofile.h" // For constants #include "mythmiscutil.h" @@ -380,7 +380,8 @@ bool RecordingRule::Save(bool sendSig) m_recordID = query.lastInsertId().toInt(); if (sendSig) - ScheduledRecording::signalChange(m_recordID); + ScheduledRecording::RescheduleMatch(m_recordID, 0, 0, QDateTime(), + QString("SaveRule %1").arg(m_title)); return true; } @@ -408,7 +409,8 @@ bool RecordingRule::Delete(bool sendSig) } if (sendSig) - ScheduledRecording::signalChange(m_recordID); + ScheduledRecording::RescheduleMatch(m_recordID, 0, 0, QDateTime(), + QString("DeleteRule %1").arg(m_title)); // Set m_recordID to zero, the rule is no longer in the database so it's // not valid. Should you want, this allows a rule to be removed from the diff --git a/mythtv/libs/libmythtv/scheduledrecording.cpp b/mythtv/libs/libmythtv/scheduledrecording.cpp index 4e162b2ea25..0c0fa1c777e 100644 --- a/mythtv/libs/libmythtv/scheduledrecording.cpp +++ b/mythtv/libs/libmythtv/scheduledrecording.cpp @@ -9,22 +9,56 @@ ScheduledRecording::~ScheduledRecording() { } -void ScheduledRecording::signalChange(int recordid) +void ScheduledRecording::SendReschedule(const QStringList &request) { if (gCoreContext->IsBackend()) { - MythEvent me(QString("RESCHEDULE_RECORDINGS %1").arg(recordid)); + MythEvent me(QString("RESCHEDULE_RECORDINGS"), request); gCoreContext->dispatch(me); } else { QStringList slist; - slist << QString("RESCHEDULE_RECORDINGS %1").arg(recordid); + slist << QString("RESCHEDULE_RECORDINGS"); + slist << request; if (!gCoreContext->SendReceiveStringList(slist)) LOG(VB_GENERAL, LOG_ERR, - QString("Error rescheduling id %1 in " - "ScheduledRecording::signalChange") .arg(recordid)); + QString("Error rescheduling %1 in " + "ScheduledRecording::SendReschedule").arg(request[0])); } } +QStringList ScheduledRecording::BuildMatchRequest(uint recordid, + uint sourceid, uint mplexid, const QDateTime &maxstarttime, + const QString &why) +{ + return QStringList(QString("MATCH %1 %2 %3 %4 %5") + .arg(recordid).arg(sourceid).arg(mplexid) + .arg(maxstarttime.isValid() ? + maxstarttime.toString(Qt::ISODate) : + "-") + .arg(why)); +}; + +QStringList ScheduledRecording::BuildCheckRequest(const RecordingInfo &recinfo, + const QString &why) +{ + return QStringList(QString("CHECK %1 %2 %3 %4") + .arg(recinfo.GetRecordingStatus()) + .arg(recinfo.GetParentRecordingRuleID() ? + recinfo.GetParentRecordingRuleID() : + recinfo.GetRecordingRuleID()) + .arg(recinfo.GetFindID()) + .arg(why)) + << recinfo.GetTitle() + << recinfo.GetSubtitle() + << recinfo.GetDescription() + << recinfo.GetProgramID(); +}; + +QStringList ScheduledRecording::BuildPlaceRequest(const QString &why) +{ + return QStringList(QString("PLACE %1").arg(why)); +}; + /* vim: set expandtab tabstop=4 shiftwidth=4: */ diff --git a/mythtv/libs/libmythtv/scheduledrecording.h b/mythtv/libs/libmythtv/scheduledrecording.h index 4a230fbdba0..2f14d2e679a 100644 --- a/mythtv/libs/libmythtv/scheduledrecording.h +++ b/mythtv/libs/libmythtv/scheduledrecording.h @@ -2,18 +2,43 @@ #define SCHEDULEDRECORDING_H #include "mythtvexp.h" +#include "qdatetime.h" +#include "recordinginfo.h" class MTV_PUBLIC ScheduledRecording { + friend class Scheduler; + public: + // Use when a recording rule or program data changes. Use 0 for + // recordid when all recordids are potentially affected, Use + // invalid starttime and 0 for chanids when not time nor channel + // specific. + static void RescheduleMatch(uint recordid, uint sourceid, uint mplexid, + const QDateTime &maxstarttime, const QString &why) + { SendReschedule(BuildMatchRequest(recordid, sourceid, mplexid, + maxstarttime, why)); }; + + // Use when previous or current recorded duplicate status changes. + static void RescheduleCheck(const RecordingInfo &recinfo, + const QString &why) + { SendReschedule(BuildCheckRequest(recinfo, why)); }; + + // Use when none of recording rule, program data or duplicate + // status changes. + static void ReschedulePlace(const QString &why) + { SendReschedule(BuildPlaceRequest(why)); }; + + private: ScheduledRecording(); ~ScheduledRecording(); - static void signalChange(int recordid); - // Use -1 for recordid when all recordids are potentially - // affected, such as when the program table is updated. - // Use 0 for recordid when a reschdule isn't specific to a single - // recordid, such as when a recording type priority is changed. + static void SendReschedule(const QStringList &request); + static QStringList BuildMatchRequest(uint recordid, uint sourceid, + uint mplexid, const QDateTime &maxstarttime, const QString &why); + static QStringList BuildCheckRequest(const RecordingInfo &recinfo, + const QString &why); + static QStringList BuildPlaceRequest(const QString &why); }; #endif diff --git a/mythtv/libs/libmythtv/tv_rec.cpp b/mythtv/libs/libmythtv/tv_rec.cpp index bf15151f89f..1e880809a62 100644 --- a/mythtv/libs/libmythtv/tv_rec.cpp +++ b/mythtv/libs/libmythtv/tv_rec.cpp @@ -2668,7 +2668,7 @@ void TVRec::NotifySchedulerOfRecording(RecordingInfo *rec) rec->AddHistory(false); // + save RecordingRule so that we get a recordid - // (don't allow signalChange(), avoiding unneeded reschedule) + // (don't allow RescheduleMatch(), avoiding unneeded reschedule) rec->GetRecordingRule()->Save(false); // + save recordid to recorded entry diff --git a/mythtv/programs/mythbackend/main_helpers.cpp b/mythtv/programs/mythbackend/main_helpers.cpp index 5017af41951..1d2740f9630 100644 --- a/mythtv/programs/mythbackend/main_helpers.cpp +++ b/mythtv/programs/mythbackend/main_helpers.cpp @@ -370,7 +370,10 @@ int handle_command(const MythBackendCommandLineParser &cmdline) } verboseMask |= VB_SCHEDULE; + LogLevel_t oldLogLevel = logLevel; + logLevel = LOG_DEBUG; sched->PrintList(true); + logLevel = oldLogLevel; delete sched; return GENERIC_EXIT_OK; } @@ -381,7 +384,8 @@ int handle_command(const MythBackendCommandLineParser &cmdline) if (gCoreContext->ConnectToMasterServer()) { LOG(VB_GENERAL, LOG_INFO, "Connected to master for reschedule"); - ScheduledRecording::signalChange(-1); + ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(), + "MythBackendCommand"); ok = true; } else diff --git a/mythtv/programs/mythbackend/mainserver.cpp b/mythtv/programs/mythbackend/mainserver.cpp index ff002b4b79a..802232c4f84 100644 --- a/mythtv/programs/mythbackend/mainserver.cpp +++ b/mythtv/programs/mythbackend/mainserver.cpp @@ -525,10 +525,8 @@ void MainServer::ProcessRequestWork(MythSocket *sock) } else if (command == "RESCHEDULE_RECORDINGS") { - if (tokens.size() != 2) - LOG(VB_GENERAL, LOG_ERR, "Bad RESCHEDULE_RECORDINGS request"); - else - HandleRescheduleRecordings(tokens[1].toInt(), pbs); + listline.pop_front(); + HandleRescheduleRecordings(listline, pbs); } else if (command == "FORGET_RECORDING") { @@ -1007,17 +1005,8 @@ void MainServer::customEvent(QEvent *e) if (me->Message().left(21) == "RESCHEDULE_RECORDINGS" && m_sched) { - QStringList tokens = me->Message() - .split(" ", QString::SkipEmptyParts); - - if (tokens.size() != 2) - { - LOG(VB_GENERAL, LOG_ERR, "Bad RESCHEDULE_RECORDINGS message"); - return; - } - - int recordid = tokens[1].toInt(); - m_sched->Reschedule(recordid); + QStringList request = me->ExtraDataList(); + m_sched->Reschedule(request); return; } @@ -1444,7 +1433,7 @@ void MainServer::HandleAnnounce(QStringList &slist, QStringList commands, } if (!wasAsleep && m_sched) - m_sched->Reschedule(0); + m_sched->ReschedulePlace("SlaveConnected"); QString message = QString("LOCAL_SLAVE_BACKEND_ONLINE %2") .arg(commands[2]); @@ -2001,9 +1990,6 @@ void MainServer::DoDeleteThread(DeleteStruct *ds) DoDeleteInDB(ds); - if (pginfo.GetRecordingGroup() != "LiveTV") - ScheduledRecording::signalChange(0); - deletelock.unlock(); if (slowDeletes && fd >= 0) @@ -2513,7 +2499,7 @@ void MainServer::DoHandleDeleteRecording( if (forgetHistory) recinfo.ForgetHistory(); else if (m_sched) - m_sched->Reschedule(0); + m_sched->RescheduleCheck(recinfo, "DoHandleDelete1"); QStringList outputlist( QString::number(0) ); SendResponse(pbssock, outputlist); return; @@ -2534,7 +2520,7 @@ void MainServer::DoHandleDeleteRecording( else if (m_sched && recinfo.GetRecordingGroup() != "Deleted" && recinfo.GetRecordingGroup() != "LiveTV") - m_sched->Reschedule(0); + m_sched->RescheduleCheck(recinfo, "DoHandleDelete2"); if (pbssock) { @@ -2594,7 +2580,7 @@ void MainServer::DoHandleDeleteRecording( else if (m_sched && recinfo.GetRecordingGroup() != "Deleted" && recinfo.GetRecordingGroup() != "LiveTV") - m_sched->Reschedule(0); + m_sched->RescheduleCheck(recinfo, "DoHandleDelete3"); // Tell MythTV frontends that the recording list needs to be updated. if (fileExists || !recinfo.GetFilesize() || forceMetadataDelete) @@ -2651,12 +2637,13 @@ void MainServer::DoHandleUndeleteRecording( SendResponse(pbssock, outputlist); } -void MainServer::HandleRescheduleRecordings(int recordid, PlaybackSock *pbs) +void MainServer::HandleRescheduleRecordings(const QStringList &request, + PlaybackSock *pbs) { QStringList result; if (m_sched) { - m_sched->Reschedule(recordid); + m_sched->Reschedule(request); result = QStringList( QString::number(1) ); } else @@ -3151,7 +3138,8 @@ void MainServer::HandleGetPendingRecordings(PlaybackSock *pbs, record->m_recordID = recordid; if (record->Load() && record->m_searchType == kManualSearch) - HandleRescheduleRecordings(recordid, NULL); + m_sched->RescheduleMatch(recordid, 0, 0, QDateTime(), + "Speculation"); delete record; } query.prepare("DELETE FROM program WHERE manualid = :RECID;"); @@ -3413,7 +3401,7 @@ void MainServer::HandleLockTuner(PlaybackSock *pbs, int cardid) << query.value(2).toString(); if (m_sched) - m_sched->Reschedule(0); + m_sched->ReschedulePlace("LockTuner"); SendResponse(pbssock, strlist); return; @@ -3459,7 +3447,7 @@ void MainServer::HandleFreeTuner(int cardid, PlaybackSock *pbs) LOG(VB_GENERAL, LOG_INFO, msg); if (m_sched) - m_sched->Reschedule(0); + m_sched->ReschedulePlace("FreeTuner"); strlist << "OK"; } @@ -6224,7 +6212,7 @@ void MainServer::HandleSlaveDisconnectedEvent(const MythEvent &event) m_sched->SlaveDisconnected(event.ExtraData(i).toUInt()); if (needsReschedule) - m_sched->Reschedule(0); + m_sched->ReschedulePlace("SlaveDisconnected"); } } diff --git a/mythtv/programs/mythbackend/mainserver.h b/mythtv/programs/mythbackend/mainserver.h index 0349f8eb142..e27c9ae8378 100644 --- a/mythtv/programs/mythbackend/mainserver.h +++ b/mythtv/programs/mythbackend/mainserver.h @@ -163,7 +163,8 @@ class MainServer : public QObject, public MythSocketCBs void HandleUndeleteRecording(QStringList &slist, PlaybackSock *pbs); void DoHandleUndeleteRecording(RecordingInfo &recinfo, PlaybackSock *pbs); void HandleForgetRecording(QStringList &slist, PlaybackSock *pbs); - void HandleRescheduleRecordings(int recordid, PlaybackSock *pbs); + void HandleRescheduleRecordings(const QStringList &request, + PlaybackSock *pbs); void HandleGoToSleep(PlaybackSock *pbs); void HandleQueryFreeSpace(PlaybackSock *pbs, bool allBackends); void HandleQueryFreeSpaceSummary(PlaybackSock *pbs); diff --git a/mythtv/programs/mythbackend/scheduler.cpp b/mythtv/programs/mythbackend/scheduler.cpp index 23db70cae7e..c165b27ea09 100644 --- a/mythtv/programs/mythbackend/scheduler.cpp +++ b/mythtv/programs/mythbackend/scheduler.cpp @@ -287,7 +287,10 @@ static bool comp_redundant(RecordingInfo *a, RecordingInfo *b) Qt::CaseInsensitive); if (cmp != 0) return cmp < 0; - return a->GetRecordingStatus() < b->GetRecordingStatus(); + if (a->GetRecordingStatus() != b->GetRecordingStatus()) + return a->GetRecordingStatus() < b->GetRecordingStatus(); + cmp = a->GetChanNum().compare(b->GetChanNum(), Qt::CaseInsensitive); + return cmp < 0; } static bool comp_recstart(RecordingInfo *a, RecordingInfo *b) @@ -400,19 +403,19 @@ bool Scheduler::FillRecordList(void) /** \fn Scheduler::FillRecordListFromDB(int) * \param recordid Record ID of recording that has changed, - * or -1 if anything might have been changed. + * or 0 if anything might have been changed. */ -void Scheduler::FillRecordListFromDB(int recordid) +void Scheduler::FillRecordListFromDB(uint recordid) { struct timeval fillstart, fillend; - float matchTime, placeTime; + float matchTime, checkTime, placeTime; MSqlQuery query(dbConn); QString thequery; QString where = ""; // This will cause our temp copy of recordmatch to be empty - if (recordid == -1) + if (recordid == 0) where = "WHERE recordid IS NULL "; thequery = QString("CREATE TEMPORARY TABLE recordmatch ") + @@ -449,17 +452,30 @@ void Scheduler::FillRecordListFromDB(int recordid) QMutexLocker locker(&schedLock); gettimeofday(&fillstart, NULL); - UpdateMatches(recordid); + UpdateMatches(recordid, 0, 0, QDateTime()); gettimeofday(&fillend, NULL); matchTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; + LOG(VB_SCHEDULE, LOG_INFO, "CreateTempTables..."); + CreateTempTables(); + + gettimeofday(&fillstart, NULL); + LOG(VB_SCHEDULE, LOG_INFO, "UpdateDuplicates..."); + UpdateDuplicates(); + gettimeofday(&fillend, NULL); + checkTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + + (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; + gettimeofday(&fillstart, NULL); FillRecordList(); gettimeofday(&fillend, NULL); placeTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; + LOG(VB_SCHEDULE, LOG_INFO, "DeleteTempTables..."); + DeleteTempTables(); + MSqlQuery queryDrop(dbConn); queryDrop.prepare("DROP TABLE recordmatch;"); if (!queryDrop.exec()) @@ -469,9 +485,11 @@ void Scheduler::FillRecordListFromDB(int recordid) } QString msg; - msg.sprintf("Speculative scheduled %d items in " - "%.1f = %.2f match + %.2f place", (int)reclist.size(), - matchTime + placeTime, matchTime, placeTime); + msg.sprintf("Speculative scheduled %d items in %.1f " + "= %.2f match + %.2f check + %.2f place", + (int)reclist.size(), + matchTime + checkTime + placeTime, + matchTime, checkTime, placeTime); LOG(VB_GENERAL, LOG_INFO, msg); } @@ -490,7 +508,7 @@ void Scheduler::FillRecordListFromMaster(void) void Scheduler::PrintList(RecList &list, bool onlyFutureRecordings) { - if (!VERBOSE_LEVEL_CHECK(VB_SCHEDULE, LOG_INFO)) + if (!VERBOSE_LEVEL_CHECK(VB_SCHEDULE, LOG_DEBUG)) return; QDateTime now = QDateTime::currentDateTime(); @@ -518,7 +536,7 @@ void Scheduler::PrintList(RecList &list, bool onlyFutureRecordings) void Scheduler::PrintRec(const RecordingInfo *p, const char *prefix) { - if (!VERBOSE_LEVEL_CHECK(VB_SCHEDULE, LOG_INFO)) + if (!VERBOSE_LEVEL_CHECK(VB_SCHEDULE, LOG_DEBUG)) return; QString outstr; @@ -593,7 +611,7 @@ void Scheduler::UpdateRecStatus(RecordingInfo *pginfo) p->AddHistory(false); if (resched) { - reschedQueue.enqueue(0); + EnqueueCheck(*p, "UpdateRecStatus1"); reschedWait.wakeOne(); } else @@ -643,7 +661,7 @@ void Scheduler::UpdateRecStatus(uint cardid, uint chanid, p->AddHistory(false); if (resched) { - reschedQueue.enqueue(0); + EnqueueCheck(*p, "UpdateRecStatus2"); reschedWait.wakeOne(); } else @@ -665,7 +683,7 @@ bool Scheduler::ChangeRecordingEnd(RecordingInfo *oldp, RecordingInfo *newp) return false; RecordingType oldrectype = oldp->GetRecordingRuleType(); - int oldrecordid = oldp->GetRecordingRuleID(); + uint oldrecordid = oldp->GetRecordingRuleID(); QDateTime oldrecendts = oldp->GetRecordingEndTime(); oldp->SetRecordingRuleType(newp->GetRecordingRuleType()); @@ -1225,27 +1243,25 @@ bool Scheduler::TryAnotherShowing(RecordingInfo *p, bool samePriority, void Scheduler::SchedNewRecords(void) { - LOG(VB_SCHEDULE, LOG_INFO, "Scheduling:"); - - if (VERBOSE_LEVEL_CHECK(VB_SCHEDULE, LOG_INFO)) + if (VERBOSE_LEVEL_CHECK(VB_SCHEDULE, LOG_DEBUG)) { - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "+ = schedule this showing to be recorded"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "# = could not schedule this showing, retry later"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "! = conflict caused by this showing"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "/ = retry this showing, same priority pass"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "? = retry this showing, lower priority pass"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "> = try another showing for this program"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "% = found another showing, same priority required"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "$ = found another showing, lower priority allowed"); - LOG(VB_SCHEDULE, LOG_INFO, + LOG(VB_SCHEDULE, LOG_DEBUG, "- = unschedule a showing in favor of another one"); } @@ -1613,16 +1629,10 @@ void Scheduler::GetAllScheduled(QStringList &strList) } } -void Scheduler::Reschedule(int recordid) +void Scheduler::Reschedule(const QStringList &request) { QMutexLocker locker(&schedLock); - - if (recordid == -1) - reschedQueue.clear(); - - if (recordid != 0 || reschedQueue.empty()) - reschedQueue.enqueue(recordid); - + reschedQueue.enqueue(request); reschedWait.wakeOne(); } @@ -1661,7 +1671,8 @@ void Scheduler::AddRecording(const RecordingInfo &pi) new_pi->GetRecordingRule(); // Trigger reschedule.. - reschedQueue.enqueue(pi.GetRecordingRuleID()); + EnqueueMatch(pi.GetRecordingRuleID(), 0, 0, QDateTime(), + QString("AddRecording %1").arg(pi.GetTitle())); reschedWait.wakeOne(); } @@ -1743,7 +1754,7 @@ void Scheduler::OldRecordedFixups(void) // during normal processing. query.prepare("UPDATE oldrecorded SET future = 0 " "WHERE future > 0 AND " - " endtime < (NOW() - INTERVAL 8 HOUR)"); + " endtime < (NOW() - INTERVAL 475 MINUTE)"); if (!query.exec()) MythDB::DBError("UpdateFuture", query); } @@ -1768,7 +1779,7 @@ void Scheduler::run(void) QMutexLocker lockit(&schedLock); reschedQueue.clear(); - reschedQueue.enqueue(-1); + EnqueueMatch(0, 0, 0, QDateTime(), "SchedulerInit"); int prerollseconds = 0; int wakeThreshold = 300; @@ -1992,36 +2003,164 @@ int Scheduler::CalcTimeToNextHandleRecordingEvent( return min(msecs, max_sleep); } +void Scheduler::ResetDuplicates(uint recordid, uint findid, + const QString &title, const QString &subtitle, + const QString &descrip, + const QString &programid) +{ + MSqlQuery query(dbConn); + QString filterClause; + MSqlBindings bindings; + + if (!title.isEmpty()) + { + filterClause += "AND p.title = :TITLE "; + bindings[":TITLE"] = title; + } + + // "**any**" is special value set in ProgLister::DeleteOldSeries() + if (programid != "**any**") + { + filterClause += "AND (0 "; + if (!subtitle.isEmpty()) + { + // Need to check both for kDupCheckSubThenDesc + filterClause += "OR p.subtitle = :SUBTITLE " + "OR p.description = :SUBTITLE "; + bindings[":SUBTITLE"] = subtitle; + } + if (!descrip.isEmpty()) + { + // Need to check both for kDupCheckSubThenDesc + filterClause += "OR p.description = :DESCRIP " + "OR p.subtitle = :DESCRIP "; + bindings[":DESCRIP"] = descrip; + } + if (!programid.isEmpty()) + { + filterClause += "OR p.programid = :PROGRAMID "; + bindings[":PROGRAMID"] = programid; + } + filterClause += ") "; + } + + query.prepare(QString("UPDATE recordmatch rm " + "INNER JOIN %1 r " + " ON rm.recordid = r.recordid " + "INNER JOIN program p " + " ON rm.chanid = p.chanid " + " AND rm.starttime = p.starttime " + " AND rm.manualid = p.manualid " + "SET oldrecduplicate = -1 " + "WHERE p.generic = 0 " + " AND r.type NOT IN (%2, %3, %4) ") + .arg(recordTable) + .arg(kSingleRecord) + .arg(kOverrideRecord) + .arg(kDontRecord) + + filterClause); + MSqlBindings::const_iterator it; + for (it = bindings.begin(); it != bindings.end(); ++it) + query.bindValue(it.key(), it.value()); + if (!query.exec()) + MythDB::DBError("ResetDuplicates1", query); + + if (findid && programid != "**any**") + { + query.prepare("UPDATE recordmatch rm " + "SET oldrecduplicate = -1 " + "WHERE rm.recordid = :RECORDID " + " AND rm.findid = :FINDID"); + query.bindValue("RECORDID", recordid); + query.bindValue("FINDID", findid); + if (!query.exec()) + MythDB::DBError("ResetDuplicates2", query); + } + } + bool Scheduler::HandleReschedule(void) { // We might have been inactive for a long time, so make // sure our DB connection is fresh before continuing. dbConn = MSqlQuery::SchedCon(); - struct timeval fillstart; + struct timeval fillstart, fillend; + float matchTime, checkTime, placeTime; + gettimeofday(&fillstart, NULL); QString msg; bool deleteFuture = false; + bool runCheck = false; while (!reschedQueue.empty()) { - int recordid = reschedQueue.dequeue(); + QStringList request = reschedQueue.dequeue(); + QStringList tokens; + if (request.size() >= 1) + tokens = request[0].split(' ', QString::SkipEmptyParts); + + if (request.size() < 1 || tokens.size() < 1) + { + LOG(VB_GENERAL, LOG_ERR, "Empty Reschedule request received"); + return false; + } - LOG(VB_GENERAL, LOG_INFO, QString("Reschedule requested for id %1.") - .arg(recordid)); + LOG(VB_GENERAL, LOG_INFO, QString("Reschedule requested for %1 %2") + .arg(request[0]).arg((request.size() > 1) ? request[1] : "")); - if (recordid != 0) + if (tokens[0] == "MATCH") { - if (recordid == -1) - reschedQueue.clear(); + if (tokens.size() < 5) + { + LOG(VB_GENERAL, LOG_ERR, + QString("Invalid RescheduleMatch request received (%1)") + .arg(request[0])); + continue; + } + uint recordid = tokens[1].toUInt(); + uint sourceid = tokens[2].toUInt(); + uint mplexid = tokens[3].toUInt(); + QDateTime maxstarttime = + QDateTime::fromString(tokens[4], Qt::ISODate); deleteFuture = true; + runCheck = true; + schedLock.unlock(); + recordmatchLock.lock(); + UpdateMatches(recordid, sourceid, mplexid, maxstarttime); + recordmatchLock.unlock(); + schedLock.lock(); + } + else if (tokens[0] == "CHECK") + { + if (tokens.size() < 4 || request.size() < 5) + { + LOG(VB_GENERAL, LOG_ERR, + QString("Invalid RescheduleCheck request received (%1)") + .arg(request[0])); + continue; + } + + uint recordid = tokens[2].toUInt(); + uint findid = tokens[3].toUInt(); + QString title = request[1]; + QString subtitle = request[2]; + QString descrip = request[3]; + QString programid = request[4]; + runCheck = true; schedLock.unlock(); recordmatchLock.lock(); - UpdateMatches(recordid); + ResetDuplicates(recordid, findid, title, subtitle, descrip, + programid); recordmatchLock.unlock(); schedLock.lock(); } + else if (tokens[0] != "PLACE") + { + LOG(VB_GENERAL, LOG_ERR, + QString("Unknown Reschedule request received (%1)") + .arg(request[0])); + } } // Delete future oldrecorded entries that no longer @@ -2039,15 +2178,32 @@ bool Scheduler::HandleReschedule(void) MythDB::DBError("DeleteFuture", query); } - struct timeval fillend; gettimeofday(&fillend, NULL); + matchTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + + (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; - float matchTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + - (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; + LOG(VB_SCHEDULE, LOG_INFO, "CreateTempTables..."); + CreateTempTables(); + + gettimeofday(&fillstart, NULL); + if (runCheck) + { + LOG(VB_SCHEDULE, LOG_INFO, "UpdateDuplicates..."); + UpdateDuplicates(); + } + gettimeofday(&fillend, NULL); + checkTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + + (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; gettimeofday(&fillstart, NULL); bool worklistused = FillRecordList(); gettimeofday(&fillend, NULL); + placeTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + + (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; + + LOG(VB_SCHEDULE, LOG_INFO, "DeleteTempTables..."); + DeleteTempTables(); + if (worklistused) { UpdateNextRecord(); @@ -2056,16 +2212,14 @@ bool Scheduler::HandleReschedule(void) else { LOG(VB_GENERAL, LOG_INFO, "Reschedule interrupted, will retry"); - reschedQueue.enqueue(0); + EnqueuePlace("Interrupted"); return false; } - float placeTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 + - (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0; - - msg.sprintf("Scheduled %d items in %.1f = %.2f match + %.2f place", - (int)reclist.size(), matchTime + placeTime, matchTime, - placeTime); + msg.sprintf("Scheduled %d items in %.1f " + "= %.2f match + %.2f check + %.2f place", + (int)reclist.size(), matchTime + checkTime + placeTime, + matchTime, checkTime, placeTime); LOG(VB_GENERAL, LOG_INFO, msg); fsInfoCacheFillTime = QDateTime::currentDateTime().addSecs(-1000); @@ -2216,7 +2370,7 @@ void Scheduler::HandleWakeSlave(RecordingInfo &ri, int prerollseconds) .arg(nexttv->GetHostName()).arg(ri.GetTitle())); if (!WakeUpSlave(nexttv->GetHostName())) - reschedQueue.enqueue(0); + EnqueuePlace("HandleWakeSlave1"); } else if ((nexttv->IsWaking()) && ((secsleft - prerollseconds) < 210) && @@ -2229,7 +2383,7 @@ void Scheduler::HandleWakeSlave(RecordingInfo &ri, int prerollseconds) .arg(nexttv->GetHostName())); if (!WakeUpSlave(nexttv->GetHostName(), false)) - reschedQueue.enqueue(0); + EnqueuePlace("HandleWakeSlave2"); } else if ((nexttv->IsWaking()) && ((secsleft - prerollseconds) < 150) && @@ -2249,7 +2403,7 @@ void Scheduler::HandleWakeSlave(RecordingInfo &ri, int prerollseconds) (*it)->SetSleepStatus(sStatus_Undefined); } - reschedQueue.enqueue(0); + EnqueuePlace("HandleWakeSlave3"); } } @@ -2287,7 +2441,7 @@ bool Scheduler::HandleRecording( livetvTime = (livetvTime < nextrectime) ? nextrectime : livetvTime; - reschedQueue.enqueue(0); + EnqueuePlace("PrepareToRecord"); } } @@ -2370,7 +2524,7 @@ bool Scheduler::HandleRecording( enc->SetSleepStatus(sStatus_Undefined); } - reschedQueue.enqueue(0); + EnqueuePlace("SlaveNotAwake"); } return false; @@ -3029,7 +3183,7 @@ void Scheduler::WakeUpSlaves(void) } } -void Scheduler::UpdateManuals(int recordid) +void Scheduler::UpdateManuals(uint recordid) { MSqlQuery query(dbConn); @@ -3108,10 +3262,10 @@ void Scheduler::UpdateManuals(int recordid) if (weekday && startdt.date().dayOfWeek() >= 6) continue; - query.prepare("REPLACE INTO program (chanid,starttime,endtime," - " title,subtitle,manualid) " - "VALUES (:CHANID,:STARTTIME,:ENDTIME,:TITLE," - " :SUBTITLE,:RECORDID)"); + query.prepare("REPLACE INTO program (chanid, starttime, endtime," + " title, subtitle, manualid, generic) " + "VALUES (:CHANID, :STARTTIME, :ENDTIME, :TITLE," + " :SUBTITLE, :RECORDID, 1)"); query.bindValue(":CHANID", chanidlist[i]); query.bindValue(":STARTTIME", startdt); query.bindValue(":ENDTIME", startdt.addSecs(duration * 60)); @@ -3128,7 +3282,7 @@ void Scheduler::UpdateManuals(int recordid) } } -void Scheduler::BuildNewRecordsQueries(int recordid, QStringList &from, +void Scheduler::BuildNewRecordsQueries(uint recordid, QStringList &from, QStringList &where, MSqlBindings &bindings) { @@ -3138,7 +3292,7 @@ void Scheduler::BuildNewRecordsQueries(int recordid, QStringList &from, query = QString("SELECT recordid,search,subtitle,description " "FROM %1 WHERE search <> %2 AND " - "(recordid = %3 OR %4 = -1) ") + "(recordid = %3 OR %4 = 0) ") .arg(recordTable).arg(kNoSearch).arg(recordid).arg(recordid); result.prepare(query); @@ -3231,10 +3385,10 @@ void Scheduler::BuildNewRecordsQueries(int recordid, QStringList &from, count++; } - if (recordid == -1 || from.count() == 0) + if (recordid == 0 || from.count() == 0) { QString recidmatch = ""; - if (recordid != -1) + if (recordid != 0) recidmatch = "RECTABLE.recordid = :NRRECORDID AND "; QString s1 = recidmatch + "RECTABLE.search = :NRST AND " @@ -3253,54 +3407,96 @@ void Scheduler::BuildNewRecordsQueries(int recordid, QStringList &from, from << ""; where << s2; bindings[":NRST"] = kNoSearch; - if (recordid != -1) + if (recordid != 0) bindings[":NRRECORDID"] = recordid; } } -void Scheduler::UpdateMatches(int recordid) { - struct timeval dbstart, dbend; +static QString progdupinit = QString( +"(CASE " +" WHEN RECTABLE.type IN (%1, %2, %3) THEN 0 " +" WHEN RECTABLE.type IN (%4, %5, %6) THEN -1 " +" ELSE (program.generic - 1) " +" END) ") + .arg(kSingleRecord).arg(kOverrideRecord).arg(kDontRecord) + .arg(kFindOneRecord).arg(kFindDailyRecord).arg(kFindWeeklyRecord); - if (recordid == 0) - return; +static QString progfindid = QString( +"(CASE RECTABLE.type " +" WHEN %1 " +" THEN RECTABLE.findid " +" WHEN %2 " +" THEN to_days(date_sub(program.starttime, interval " +" time_format(RECTABLE.findtime, '%H:%i') hour_minute)) " +" WHEN %3 " +" THEN floor((to_days(date_sub(program.starttime, interval " +" time_format(RECTABLE.findtime, '%H:%i') hour_minute)) - " +" RECTABLE.findday)/7) * 7 + RECTABLE.findday " +" WHEN %4 " +" THEN RECTABLE.findid " +" ELSE 0 " +" END) ") + .arg(kFindOneRecord) + .arg(kFindDailyRecord) + .arg(kFindWeeklyRecord) + .arg(kOverrideRecord); + +void Scheduler::UpdateMatches(uint recordid, uint sourceid, uint mplexid, + const QDateTime maxstarttime) +{ + struct timeval dbstart, dbend; MSqlQuery query(dbConn); + MSqlBindings bindings; + QString deleteClause; + QString filterClause = QString(" AND program.endtime > " + "(NOW() - INTERVAL 480 MINUTE)"); - if (recordid == -1) - query.prepare("DELETE FROM recordmatch"); - else + if (recordid) { - query.prepare("DELETE FROM recordmatch WHERE recordid = :RECORDID"); - query.bindValue(":RECORDID", recordid); + deleteClause += " AND recordmatch.recordid = :RECORDID"; + bindings[":RECORDID"] = recordid; } - - if (!query.exec()) + if (sourceid) { - MythDB::DBError("UpdateMatches", query); - return; + deleteClause += " AND channel.sourceid = :SOURCEID"; + filterClause += " AND channel.sourceid = :SOURCEID"; + bindings[":SOURCEID"] = sourceid; } - - if (recordid == -1) - query.prepare("DELETE FROM program WHERE manualid <> 0"); - else + if (mplexid) + { + deleteClause += " AND channel.mplexid = :MPLEXID"; + filterClause += " AND channel.mplexid = :MPLEXID"; + bindings[":MPLEXID"] = mplexid; + } + if (maxstarttime.isValid()) { - query.prepare("DELETE FROM program WHERE manualid = :RECORDID"); - query.bindValue(":RECORDID", recordid); + deleteClause += " AND recordmatch.starttime <= :MAXSTARTTIME"; + filterClause += " AND program.starttime <= :MAXSTARTTIME"; + bindings[":MAXSTARTTIME"] = maxstarttime; } + + query.prepare(QString("DELETE recordmatch FROM recordmatch, channel " + "WHERE recordmatch.chanid = channel.chanid") + + deleteClause); + MSqlBindings::const_iterator it; + for (it = bindings.begin(); it != bindings.end(); ++it) + query.bindValue(it.key(), it.value()); if (!query.exec()) { - MythDB::DBError("UpdateMatches", query); + MythDB::DBError("UpdateMatches1", query); return; } + if (recordid) + bindings.remove(":RECORDID"); - QString filterClause; query.prepare("SELECT filterid, clause FROM recordfilter " "WHERE filterid >= 0 AND filterid < :NUMFILTERS AND " " TRIM(clause) <> ''"); query.bindValue(":NUMFILTERS", RecordingRule::kNumFilters); if (!query.exec()) { - MythDB::DBError("UpdateMatches", query); + MythDB::DBError("UpdateMatches2", query); return; } while (query.next()) @@ -3315,7 +3511,7 @@ void Scheduler::UpdateMatches(int recordid) { query.bindValue(":FINDONE", kFindOneRecord); if (!query.exec()) { - MythDB::DBError("UpdateMatches", query); + MythDB::DBError("UpdateMatches3", query); return; } else if (query.size()) @@ -3327,12 +3523,11 @@ void Scheduler::UpdateMatches(int recordid) { query.bindValue(":FINDID", findtoday); query.bindValue(":FINDONE", kFindOneRecord); if (!query.exec()) - MythDB::DBError("UpdateMatches", query); + MythDB::DBError("UpdateMatches4", query); } int clause; QStringList fromclauses, whereclauses; - MSqlBindings bindings; BuildNewRecordsQueries(recordid, fromclauses, whereclauses, bindings); @@ -3349,9 +3544,11 @@ void Scheduler::UpdateMatches(int recordid) { for (clause = 0; clause < fromclauses.count(); ++clause) { QString query = QString( -"REPLACE INTO recordmatch (recordid, chanid, starttime, manualid) " +"REPLACE INTO recordmatch (recordid, chanid, starttime, manualid, " +" oldrecduplicate, findid) " "SELECT RECTABLE.recordid, program.chanid, program.starttime, " -" IF(search = %1, RECTABLE.recordid, 0) ").arg(kManualSearch) + QString( +" IF(search = %1, RECTABLE.recordid, 0), ").arg(kManualSearch) + + progdupinit + ", " + progfindid + QString( "FROM (RECTABLE, program INNER JOIN channel " " ON channel.chanid = program.chanid) ") + fromclauses[clause] + QString( " WHERE ") + whereclauses[clause] + @@ -3395,12 +3592,7 @@ void Scheduler::UpdateMatches(int recordid) { .arg(kWeekslotRecord) .arg(kNotRecording); - while (1) - { - int i = query.indexOf("RECTABLE"); - if (i == -1) break; - query = query.replace(i, strlen("RECTABLE"), recordTable); - } + query.replace("RECTABLE", recordTable); LOG(VB_SCHEDULE, LOG_INFO, QString(" |-- Start DB Query %1...") .arg(clause)); @@ -3409,7 +3601,6 @@ void Scheduler::UpdateMatches(int recordid) { MSqlQuery result(dbConn); result.prepare(query); - MSqlBindings::const_iterator it; for (it = bindings.begin(); it != bindings.end(); ++it) { if (query.contains(it.key())) @@ -3435,8 +3626,184 @@ void Scheduler::UpdateMatches(int recordid) { LOG(VB_SCHEDULE, LOG_INFO, " +-- Done."); } +void Scheduler::CreateTempTables(void) +{ + MSqlQuery result(dbConn); + + if (recordTable == "record") + { + result.prepare("DROP TABLE IF EXISTS sched_temp_record;"); + if (!result.exec()) + { + MythDB::DBError("Dropping sched_temp_record table", result); + return; + } + result.prepare("CREATE TEMPORARY TABLE sched_temp_record " + "LIKE record;"); + if (!result.exec()) + { + MythDB::DBError("Creating sched_temp_record table", result); + return; + } + result.prepare("INSERT sched_temp_record SELECT * from record;"); + if (!result.exec()) + { + MythDB::DBError("Populating sched_temp_record table", result); + return; + } + } + + result.prepare("DROP TABLE IF EXISTS sched_temp_recorded;"); + if (!result.exec()) + { + MythDB::DBError("Dropping sched_temp_recorded table", result); + return; + } + result.prepare("CREATE TEMPORARY TABLE sched_temp_recorded " + "LIKE recorded;"); + if (!result.exec()) + { + MythDB::DBError("Creating sched_temp_recorded table", result); + return; + } + result.prepare("INSERT sched_temp_recorded SELECT * from recorded;"); + if (!result.exec()) + { + MythDB::DBError("Populating sched_temp_recorded table", result); + return; + } +} + +void Scheduler::DeleteTempTables(void) +{ + MSqlQuery result(dbConn); + + if (recordTable == "record") + { + result.prepare("DROP TABLE IF EXISTS sched_temp_record;"); + if (!result.exec()) + MythDB::DBError("DeleteTempTables sched_temp_record", result); + } + + result.prepare("DROP TABLE IF EXISTS sched_temp_recorded;"); + if (!result.exec()) + MythDB::DBError("DeleteTempTables drop table", result); +} + +void Scheduler::UpdateDuplicates(void) +{ + QString schedTmpRecord = recordTable; + if (schedTmpRecord == "record") + schedTmpRecord = "sched_temp_record"; + + QString rmquery = QString( +"UPDATE recordmatch " +" INNER JOIN RECTABLE ON (recordmatch.recordid = RECTABLE.recordid) " +" INNER JOIN program p ON (recordmatch.chanid = p.chanid AND " +" recordmatch.starttime = p.starttime AND " +" recordmatch.manualid = p.manualid) " +" LEFT JOIN oldrecorded ON " +" ( " +" RECTABLE.dupmethod > 1 AND " +" oldrecorded.duplicate <> 0 AND " +" p.title = oldrecorded.title AND " +" p.generic = 0 " +" AND " +" ( " +" (p.programid <> '' " +" AND p.programid = oldrecorded.programid) " +" OR " +" ( ") + + (ProgramInfo::UsingProgramIDAuthority() ? +" (p.programid = '' OR oldrecorded.programid = '' OR " +" LEFT(p.programid, LOCATE('/', p.programid)) <> " +" LEFT(oldrecorded.programid, LOCATE('/', oldrecorded.programid))) " : +" (p.programid = '' OR oldrecorded.programid = '') " ) + + QString( +" AND " +" (((RECTABLE.dupmethod & 0x02) = 0) OR (p.subtitle <> '' " +" AND p.subtitle = oldrecorded.subtitle)) " +" AND " +" (((RECTABLE.dupmethod & 0x04) = 0) OR (p.description <> '' " +" AND p.description = oldrecorded.description)) " +" AND " +" (((RECTABLE.dupmethod & 0x08) = 0) OR " +" (p.subtitle <> '' AND " +" (p.subtitle = oldrecorded.subtitle OR " +" (oldrecorded.subtitle = '' AND " +" p.subtitle = oldrecorded.description))) OR " +" (p.subtitle = '' AND p.description <> '' AND " +" (p.description = oldrecorded.subtitle OR " +" (oldrecorded.subtitle = '' AND " +" p.description = oldrecorded.description)))) " +" ) " +" ) " +" ) " +" LEFT JOIN sched_temp_recorded recorded ON " +" ( " +" RECTABLE.dupmethod > 1 AND " +" recorded.duplicate <> 0 AND " +" p.title = recorded.title AND " +" p.generic = 0 AND " +" recorded.recgroup NOT IN ('LiveTV','Deleted') " +" AND " +" ( " +" (p.programid <> '' " +" AND p.programid = recorded.programid) " +" OR " +" ( ") + + (ProgramInfo::UsingProgramIDAuthority() ? +" (p.programid = '' OR recorded.programid = '' OR " +" LEFT(p.programid, LOCATE('/', p.programid)) <> " +" LEFT(recorded.programid, LOCATE('/', recorded.programid))) " : +" (p.programid = '' OR recorded.programid = '') ") + + QString( +" AND " +" (((RECTABLE.dupmethod & 0x02) = 0) OR (p.subtitle <> '' " +" AND p.subtitle = recorded.subtitle)) " +" AND " +" (((RECTABLE.dupmethod & 0x04) = 0) OR (p.description <> '' " +" AND p.description = recorded.description)) " +" AND " +" (((RECTABLE.dupmethod & 0x08) = 0) OR " +" (p.subtitle <> '' AND " +" (p.subtitle = recorded.subtitle OR " +" (recorded.subtitle = '' AND " +" p.subtitle = recorded.description))) OR " +" (p.subtitle = '' AND p.description <> '' AND " +" (p.description = recorded.subtitle OR " +" (recorded.subtitle = '' AND " +" p.description = recorded.description)))) " +" ) " +" ) " +" ) " +" LEFT JOIN oldfind ON " +" (oldfind.recordid = recordmatch.recordid AND " +" oldfind.findid = recordmatch.findid) " +" SET oldrecduplicate = (oldrecorded.endtime IS NOT NULL), " +" recduplicate = (recorded.endtime IS NOT NULL), " +" findduplicate = (oldfind.findid IS NOT NULL), " +" oldrecstatus = oldrecorded.recstatus " +" WHERE p.endtime >= (NOW() - INTERVAL 480 MINUTE) " +" AND oldrecduplicate = -1 " +); + rmquery.replace("RECTABLE", schedTmpRecord); + + MSqlQuery result(dbConn); + result.prepare(rmquery); + if (!result.exec()) + { + MythDB::DBError("UpdateDuplicates", result); + return; + } +} + void Scheduler::AddNewRecords(void) { + QString schedTmpRecord = recordTable; + if (schedTmpRecord == "record") + schedTmpRecord = "sched_temp_record"; + struct timeval dbstart, dbend; QMap<RecordingType, int> recTypeRecPriorityMap; @@ -3456,7 +3823,8 @@ void Scheduler::AddNewRecords(void) schedAfterStartMap.clear(); MSqlQuery rlist(dbConn); - rlist.prepare(QString("SELECT recordid,title,maxepisodes,maxnewest FROM %1;").arg(recordTable)); + rlist.prepare(QString("SELECT recordid, title, maxepisodes, maxnewest " + "FROM %1").arg(schedTmpRecord)); if (!rlist.exec()) { @@ -3547,67 +3915,8 @@ void Scheduler::AddNewRecords(void) pwrpri += QString(" + " "(FIND_IN_SET('VISUALIMPAIR', program.audioprop) > 0) * %1").arg(adpriority); - QString schedTmpRecord = recordTable; - MSqlQuery result(dbConn); - if (schedTmpRecord == "record") - { - schedTmpRecord = "sched_temp_record"; - - result.prepare("DROP TABLE IF EXISTS sched_temp_record;"); - - if (!result.exec()) - { - MythDB::DBError("Dropping sched_temp_record table", result); - return; - } - - result.prepare("CREATE TEMPORARY TABLE sched_temp_record " - "LIKE record;"); - - if (!result.exec()) - { - MythDB::DBError("Creating sched_temp_record table", - result); - return; - } - - result.prepare("INSERT sched_temp_record SELECT * from record;"); - - if (!result.exec()) - { - MythDB::DBError("Populating sched_temp_record table", - result); - return; - } - } - - result.prepare("DROP TABLE IF EXISTS sched_temp_recorded;"); - - if (!result.exec()) - { - MythDB::DBError("Dropping sched_temp_recorded table", result); - return; - } - - result.prepare("CREATE TEMPORARY TABLE sched_temp_recorded " - "LIKE recorded;"); - - if (!result.exec()) - { - MythDB::DBError("Creating sched_temp_recorded table", result); - return; - } - - result.prepare("INSERT sched_temp_recorded SELECT * from recorded;"); - - if (!result.exec()) - { - MythDB::DBError("Populating sched_temp_recorded table", result); - return; - } - result.prepare(QString("SELECT recpriority, selectclause FROM %1;") .arg(priorityTable)); @@ -3630,122 +3939,8 @@ void Scheduler::AddNewRecords(void) } pwrpri += QString(" AS powerpriority "); - QString progfindid = QString( -"(CASE RECTABLE.type " -" WHEN %1 " -" THEN RECTABLE.findid " -" WHEN %2 " -" THEN to_days(date_sub(program.starttime, interval " -" time_format(RECTABLE.findtime, '%H:%i') hour_minute)) " -" WHEN %3 " -" THEN floor((to_days(date_sub(program.starttime, interval " -" time_format(RECTABLE.findtime, '%H:%i') hour_minute)) - " -" RECTABLE.findday)/7) * 7 + RECTABLE.findday " -" WHEN %4 " -" THEN RECTABLE.findid " -" ELSE 0 " -" END) ") - .arg(kFindOneRecord) - .arg(kFindDailyRecord) - .arg(kFindWeeklyRecord) - .arg(kOverrideRecord); - - QString rmquery = QString( -"UPDATE recordmatch " -" INNER JOIN RECTABLE ON (recordmatch.recordid = RECTABLE.recordid) " -" INNER JOIN program ON (recordmatch.chanid = program.chanid AND " -" recordmatch.starttime = program.starttime AND " -" recordmatch.manualid = program.manualid) " -" LEFT JOIN oldrecorded ON " -" ( " -" RECTABLE.dupmethod > 1 AND " -" oldrecorded.duplicate <> 0 AND " -" program.title = oldrecorded.title AND " -" program.generic = 0 " -" AND " -" ( " -" (program.programid <> '' " -" AND program.programid = oldrecorded.programid) " -" OR " -" ( ") + - (ProgramInfo::UsingProgramIDAuthority() ? -" (program.programid = '' OR oldrecorded.programid = '' OR " -" LEFT(program.programid, LOCATE('/', program.programid)) <> " -" LEFT(oldrecorded.programid, LOCATE('/', oldrecorded.programid))) " : -" (program.programid = '' OR oldrecorded.programid = '') " ) - + QString( -" AND " -" (((RECTABLE.dupmethod & 0x02) = 0) OR (program.subtitle <> '' " -" AND program.subtitle = oldrecorded.subtitle)) " -" AND " -" (((RECTABLE.dupmethod & 0x04) = 0) OR (program.description <> '' " -" AND program.description = oldrecorded.description)) " -" AND " -" (((RECTABLE.dupmethod & 0x08) = 0) OR " -" (program.subtitle <> '' AND " -" (program.subtitle = oldrecorded.subtitle OR " -" (oldrecorded.subtitle = '' AND " -" program.subtitle = oldrecorded.description))) OR " -" (program.subtitle = '' AND program.description <> '' AND " -" (program.description = oldrecorded.subtitle OR " -" (oldrecorded.subtitle = '' AND " -" program.description = oldrecorded.description)))) " -" ) " -" ) " -" ) " -" LEFT JOIN sched_temp_recorded recorded ON " -" ( " -" RECTABLE.dupmethod > 1 AND " -" recorded.duplicate <> 0 AND " -" program.title = recorded.title AND " -" program.generic = 0 AND " -" recorded.recgroup NOT IN ('LiveTV','Deleted') " -" AND " -" ( " -" (program.programid <> '' " -" AND program.programid = recorded.programid) " -" OR " -" ( ") + - (ProgramInfo::UsingProgramIDAuthority() ? -" (program.programid = '' OR recorded.programid = '' OR " -" LEFT(program.programid, LOCATE('/', program.programid)) <> " -" LEFT(recorded.programid, LOCATE('/', recorded.programid))) " : -" (program.programid = '' OR recorded.programid = '') ") - + QString( -" AND " -" (((RECTABLE.dupmethod & 0x02) = 0) OR (program.subtitle <> '' " -" AND program.subtitle = recorded.subtitle)) " -" AND " -" (((RECTABLE.dupmethod & 0x04) = 0) OR (program.description <> '' " -" AND program.description = recorded.description)) " -" AND " -" (((RECTABLE.dupmethod & 0x08) = 0) OR " -" (program.subtitle <> '' AND " -" (program.subtitle = recorded.subtitle OR " -" (recorded.subtitle = '' AND " -" program.subtitle = recorded.description))) OR " -" (program.subtitle = '' AND program.description <> '' AND " -" (program.description = recorded.subtitle OR " -" (recorded.subtitle = '' AND " -" program.description = recorded.description)))) " -" ) " -" ) " -" ) " -" LEFT JOIN oldfind ON " -" (oldfind.recordid = recordmatch.recordid AND " -" oldfind.findid = ") + progfindid + QString(") " -" SET oldrecduplicate = (oldrecorded.endtime IS NOT NULL), " -" recduplicate = (recorded.endtime IS NOT NULL), " -" findduplicate = (oldfind.findid IS NOT NULL), " -" oldrecstatus = oldrecorded.recstatus " -" WHERE program.endtime >= NOW() - INTERVAL 9 HOUR " -); - rmquery.replace("RECTABLE", schedTmpRecord); - pwrpri.replace("program.","p."); pwrpri.replace("channel.","c."); - progfindid.replace("program.","p."); - progfindid.replace("channel.","c."); QString query = QString( "SELECT " " c.chanid, c.sourceid, p.starttime, "// 0-2 @@ -3763,14 +3958,14 @@ void Scheduler::AddNewRecords(void) " capturecard.cardid, cardinput.cardinputid,p.seriesid, "//24-26 " p.programid, RECTABLE.inetref, p.category_type, "//27-29 " p.airdate, p.stars, p.originalairdate, "//30-32 - " RECTABLE.inactive, RECTABLE.parentid,") + progfindid + ", "//33-35 + " RECTABLE.inactive, RECTABLE.parentid, recordmatch.findid, "//33-35 " RECTABLE.playgroup, oldrecstatus.recstatus, "//36-37 " oldrecstatus.reactivate, p.videoprop+0, "//38-39 " p.subtitletypes+0, p.audioprop+0, RECTABLE.storagegroup, "//40-42 " capturecard.hostname, recordmatch.oldrecstatus, " " RECTABLE.avg_delay, "//43-45 - " oldrecstatus.future, cardinput.schedorder, " //46-47 - + pwrpri + QString( + " oldrecstatus.future, cardinput.schedorder, ") + //46-47 + pwrpri + QString( "FROM recordmatch " "INNER JOIN RECTABLE ON (recordmatch.recordid = RECTABLE.recordid) " "INNER JOIN program AS p " @@ -3785,19 +3980,14 @@ void Scheduler::AddNewRecords(void) "ON ( oldrecstatus.station = c.callsign AND " " oldrecstatus.starttime = p.starttime AND " " oldrecstatus.title = p.title ) " - "WHERE p.endtime >= NOW() - INTERVAL 1 DAY " - "ORDER BY RECTABLE.recordid DESC "); + "WHERE p.endtime > (NOW() - INTERVAL 480 MINUTE) " + "ORDER BY RECTABLE.recordid DESC, p.starttime, p.title, c.callsign, " + " c.channum "); query.replace("RECTABLE", schedTmpRecord); LOG(VB_SCHEDULE, LOG_INFO, QString(" |-- Start DB Query...")); gettimeofday(&dbstart, NULL); - result.prepare(rmquery); - if (!result.exec()) - { - MythDB::DBError("AddNewRecords recordmatch", result); - return; - } result.prepare(query); if (!result.exec()) { @@ -3812,10 +4002,29 @@ void Scheduler::AddNewRecords(void) .arg(((dbend.tv_sec - dbstart.tv_sec) * 1000000 + (dbend.tv_usec - dbstart.tv_usec)) / 1000000.0)); + RecordingInfo *lastp = NULL; + while (result.next()) { + // If this is the same program we saw in the last pass and it + // wasn't a viable candidate, then neither is this one so + // don't bother with it. This is essentially an early call to + // PruneRedundants(). + uint recordid = result.value(17).toUInt(); + QDateTime startts = result.value(2).toDateTime(); + QString title = result.value(4).toString(); + QString callsign = result.value(8).toString(); + if (lastp && lastp->GetRecordingStatus() != rsUnknown + && lastp->GetRecordingStatus() != rsOffLine + && lastp->GetRecordingStatus() != rsDontRecord + && recordid == lastp->GetRecordingRuleID() + && startts == lastp->GetScheduledStartTime() + && title == lastp->GetTitle() + && callsign == lastp->GetChannelSchedulingID()) + continue; + RecordingInfo *p = new RecordingInfo( - result.value(4).toString(),//title + title, result.value(5).toString(),//subtitle result.value(6).toString(),//description 0, // season @@ -3824,7 +4033,7 @@ void Scheduler::AddNewRecords(void) result.value(0).toUInt(),//chanid result.value(7).toString(),//channum - result.value(8).toString(),//chansign + callsign, result.value(9).toString(),//channame result.value(21).toString(),//recgroup @@ -3842,7 +4051,7 @@ void Scheduler::AddNewRecords(void) result.value(12).toInt(),//recpriority - result.value(2).toDateTime(),//startts + startts, result.value(3).toDateTime(),//endts result.value(18).toDateTime(),//recstartts result.value(19).toDateTime(),//recendts @@ -3857,7 +4066,7 @@ void Scheduler::AddNewRecords(void) RecStatusType(result.value(37).toInt()),//oldrecstatus result.value(38).toInt(),//reactivate - result.value(17).toUInt(),//recordid + recordid, result.value(34).toUInt(),//parentid RecordingType(result.value(16).toInt()),//rectype RecordingDupInType(result.value(13).toInt()),//dupin @@ -3919,6 +4128,8 @@ void Scheduler::AddNewRecords(void) if (p == NULL) continue; + lastp = p; + if (p->GetRecordingStatus() != rsUnknown) { tmpList.push_back(p); @@ -3989,17 +4200,6 @@ void Scheduler::AddNewRecords(void) RecIter tmp = tmpList.begin(); for ( ; tmp != tmpList.end(); ++tmp) worklist.push_back(*tmp); - - if (schedTmpRecord == "sched_temp_record") - { - result.prepare("DROP TABLE IF EXISTS sched_temp_record;"); - if (!result.exec()) - MythDB::DBError("AddNewRecords sched_temp_record", result); - } - - result.prepare("DROP TABLE IF EXISTS sched_temp_recorded;"); - if (!result.exec()) - MythDB::DBError("AddNewRecords drop table", result); } void Scheduler::AddNotListed(void) { diff --git a/mythtv/programs/mythbackend/scheduler.h b/mythtv/programs/mythbackend/scheduler.h index 8203c9fdbea..2df7b680eab 100644 --- a/mythtv/programs/mythbackend/scheduler.h +++ b/mythtv/programs/mythbackend/scheduler.h @@ -22,6 +22,7 @@ using namespace std; #include "mythdeque.h" #include "mythscheduler.h" #include "mthread.h" +#include "scheduledrecording.h" class EncoderLink; class MainServer; @@ -41,9 +42,18 @@ class Scheduler : public MThread, public MythScheduler void SetExpirer(AutoExpire *autoExpirer) { m_expirer = autoExpirer; } - void Reschedule(int recordid); + void Reschedule(const QStringList &request); + void RescheduleMatch(uint recordid, uint sourceid, uint mplexid, + const QDateTime &maxstarttime, const QString &why) + { Reschedule(ScheduledRecording::BuildMatchRequest(recordid, sourceid, + mplexid, maxstarttime, why)); }; + void RescheduleCheck(const RecordingInfo &recinfo, const QString &why) + { Reschedule(ScheduledRecording::BuildCheckRequest(recinfo, why)); }; + void ReschedulePlace(const QString &why) + { Reschedule(ScheduledRecording::BuildPlaceRequest(why)); }; + void AddRecording(const RecordingInfo&); - void FillRecordListFromDB(int recordid = -1); + void FillRecordListFromDB(uint recordid = 0); void FillRecordListFromMaster(void); void UpdateRecStatus(RecordingInfo *pginfo); @@ -92,15 +102,19 @@ class Scheduler : public MThread, public MythScheduler bool VerifyCards(void); + void CreateTempTables(void); + void DeleteTempTables(void); + void UpdateDuplicates(void); bool FillRecordList(void); - void UpdateMatches(int recordid); - void UpdateManuals(int recordid); + void UpdateMatches(uint recordid, uint sourceid, uint mplexid, + const QDateTime maxstarttime); + void UpdateManuals(uint recordid); void BuildWorkList(void); bool ClearWorkList(void); void AddNewRecords(void); void AddNotListed(void); - void BuildNewRecordsQueries(int recordid, QStringList &from, QStringList &where, - MSqlBindings &bindings); + void BuildNewRecordsQueries(uint recordid, QStringList &from, + QStringList &where, MSqlBindings &bindings); void PruneOverlaps(void); void BuildListMaps(void); void ClearListMaps(void); @@ -150,6 +164,9 @@ class Scheduler : public MThread, public MythScheduler RecConstIter startIter, const RecList &reclist, int prerollseconds, int max_sleep /*ms*/); void OldRecordedFixups(void); + void ResetDuplicates(uint recordid, uint findid, const QString &title, + const QString &subtitle, const QString &descrip, + const QString &programid); bool HandleReschedule(void); bool HandleRunSchedulerStartup( int prerollseconds, int idleWaitForRecordingTime); @@ -166,8 +183,17 @@ class Scheduler : public MThread, public MythScheduler int idleTimeoutSecs, int idleWaitForRecordingTime, bool &statuschanged); - - MythDeque<int> reschedQueue; + void EnqueueMatch(uint recordid, uint sourceid, uint mplexid, + const QDateTime maxstarttime, const QString &why) + { reschedQueue.enqueue(ScheduledRecording::BuildMatchRequest(recordid, + sourceid, mplexid, maxstarttime, why)); }; + void EnqueueCheck(const RecordingInfo &recinfo, const QString &why) + { reschedQueue.enqueue(ScheduledRecording::BuildCheckRequest(recinfo, + why)); }; + void EnqueuePlace(const QString &why) + { reschedQueue.enqueue(ScheduledRecording::BuildPlaceRequest(why)); }; + + MythDeque<QStringList> reschedQueue; mutable QMutex schedLock; QMutex recordmatchLock; QWaitCondition reschedWait; @@ -175,7 +201,7 @@ class Scheduler : public MThread, public MythScheduler RecList worklist; RecList retrylist; RecList conflictlist; - QMap<int, RecList> recordidlistmap; + QMap<uint, RecList> recordidlistmap; QMap<QString, RecList> titlelistmap; InputGroupMap igrp; diff --git a/mythtv/programs/mythfilldatabase/main.cpp b/mythtv/programs/mythfilldatabase/main.cpp index 8ceeb9a1381..4b01f6c93df 100644 --- a/mythtv/programs/mythfilldatabase/main.cpp +++ b/mythtv/programs/mythfilldatabase/main.cpp @@ -740,7 +740,8 @@ int main(int argc, char *argv[]) "==============================================================="); if (grab_data || mark_repeats) - ScheduledRecording::signalChange(-1); + ScheduledRecording::RescheduleMatch(0, 0, 0, QDateTime(), + "MythFillDatabase"); gCoreContext->SendMessage("CLEAR_SETTINGS_CACHE"); diff --git a/mythtv/programs/mythfrontend/channelrecpriority.cpp b/mythtv/programs/mythfrontend/channelrecpriority.cpp index 180bd7eed25..085ed0f3c0b 100644 --- a/mythtv/programs/mythfrontend/channelrecpriority.cpp +++ b/mythtv/programs/mythfrontend/channelrecpriority.cpp @@ -238,7 +238,7 @@ void ChannelRecPriority::saveRecPriority(void) applyChannelRecPriorityChange(QString::number(chanInfo->chanid), chanInfo->recpriority); } - ScheduledRecording::signalChange(0); + ScheduledRecording::ReschedulePlace("SaveChannelPriority"); } void ChannelRecPriority::FillList(void) diff --git a/mythtv/programs/mythfrontend/custompriority.cpp b/mythtv/programs/mythfrontend/custompriority.cpp index e59509054c5..4c5c2b096ee 100644 --- a/mythtv/programs/mythfrontend/custompriority.cpp +++ b/mythtv/programs/mythfrontend/custompriority.cpp @@ -306,7 +306,7 @@ void CustomPriority::installClicked(void) if (!query.exec()) MythDB::DBError("Install power search insert", query); else - ScheduledRecording::signalChange(0); + ScheduledRecording::ReschedulePlace("InstallCustomPriority"); Close(); } @@ -324,7 +324,7 @@ void CustomPriority::deleteClicked(void) if (!query.exec()) MythDB::DBError("Delete power search query", query); else - ScheduledRecording::signalChange(0); + ScheduledRecording::ReschedulePlace("DeleteCustomPriority"); Close(); } diff --git a/mythtv/programs/mythfrontend/main.cpp b/mythtv/programs/mythfrontend/main.cpp index 244db78e7a2..2dbb68935e5 100644 --- a/mythtv/programs/mythfrontend/main.cpp +++ b/mythtv/programs/mythfrontend/main.cpp @@ -1044,7 +1044,7 @@ static void TVMenuCallback(void *data, QString &selection) if (sel == "settings general" || sel == "settings generalrecpriorities") - ScheduledRecording::signalChange(0); + ScheduledRecording::ReschedulePlace("TVMenuCallback"); GetMythMainWindow()->ShowPainterWindow(); } } diff --git a/mythtv/programs/mythfrontend/proglist.cpp b/mythtv/programs/mythfrontend/proglist.cpp index 0c57da4c563..2506f6593ce 100644 --- a/mythtv/programs/mythfrontend/proglist.cpp +++ b/mythtv/programs/mythfrontend/proglist.cpp @@ -675,7 +675,7 @@ void ProgLister::DeleteOldEpisode(bool ok) if (!query.exec()) MythDB::DBError("ProgLister::DeleteOldEpisode", query); - ScheduledRecording::signalChange(0); + ScheduledRecording::RescheduleCheck(*pi, "DeleteOldEpisode"); FillItemList(true); } @@ -704,7 +704,11 @@ void ProgLister::DeleteOldSeries(bool ok) if (!query.exec()) MythDB::DBError("ProgLister::DeleteOldSeries -- delete", query); - ScheduledRecording::signalChange(0); + // Set the programid to the special value of "**any**" which the + // scheduler recognizes to mean the entire series was deleted. + RecordingInfo tempri(*pi); + tempri.SetProgramID("**any**"); + ScheduledRecording::RescheduleCheck(tempri, "DeleteOldSeries"); FillItemList(true); } diff --git a/mythtv/programs/mythfrontend/programrecpriority.cpp b/mythtv/programs/mythfrontend/programrecpriority.cpp index 1ea874c714d..a33acf8555f 100644 --- a/mythtv/programs/mythfrontend/programrecpriority.cpp +++ b/mythtv/programs/mythfrontend/programrecpriority.cpp @@ -1027,7 +1027,10 @@ void ProgramRecPriority::deactivate(void) } else { - ScheduledRecording::signalChange(0); + ScheduledRecording::ReschedulePlace( + QString("DeactivateRule %1 %2") + .arg(pgRecInfo->GetRecordingRuleID()) + .arg(pgRecInfo->GetTitle())); pgRecInfo->recstatus = inactive ? rsInactive : rsUnknown; item->DisplayState("disabled", "status"); }