Go to the documentation of this file.
13 #include <QCoreApplication>
18 #include "libmythbase/mythconfig.h"
33 #define LOC QString("JobQueue: ")
40 m_runningJobsLock(new QRecursiveMutex()),
42 m_queueThread(new
MThread(
"JobQueue", this))
46 #ifndef USING_VALGRIND
51 LOG(VB_GENERAL, LOG_ERR,
LOC +
52 "The JobQueue has been disabled because "
53 "you compiled with the --enable-valgrind option.");
54 #endif // USING_VALGRIND
82 QString message = me->
Message();
84 if (message.startsWith(
"LOCAL_JOB"))
89 message = message.simplified();
90 QStringList tokens = message.split(
" ", Qt::SkipEmptyParts);
91 QString
action = tokens[1];
94 if (tokens[2] ==
"ID")
95 jobID = tokens[3].toInt();
107 msg = QString(
"Unable to determine jobID for message: "
108 "%1. Program will not be flagged.")
110 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
116 msg = QString(
"Received message '%1'").arg(message);
117 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
128 else if (
action ==
"PAUSE")
130 else if (
action ==
"RESUME")
132 else if (
action ==
"RESTART")
158 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
"ProcessQueue() started");
164 QMap<int, int> jobStatus;
166 QMap<int, JobQueueEntry> jobs;
168 QMap<int, RunningJobInfo>::Iterator rjiter;
175 bool startedJobAlready =
false;
178 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
179 QString(
"Currently set to run up to %1 job(s) max.")
188 if ((*rjiter).pginfo)
189 (*rjiter).pginfo->UpdateInUseMark();
199 for (
const auto & job : std::as_const(jobs))
201 int status = job.status;
204 if (((status == JOB_RUNNING) ||
205 (status == JOB_STARTING) ||
206 (status == JOB_PAUSED)) &&
211 message = QString(
"Currently Running %1 jobs.")
215 message += QString(
" Jobs in Queue, but we are outside of the "
216 "Job Queue time window, no new jobs can be "
218 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
222 message +=
" (At Maximum, no new jobs can be started until "
223 "a running job completes)";
226 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
232 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
240 int jobID = jobs[x].id;
241 int cmds = jobs[x].cmds;
243 int status = jobs[x].status;
247 logInfo = QString(
"jobID #%1").arg(
jobID);
249 logInfo = QString(
"chanid %1 @ %2").arg(jobs[x].chanid)
250 .arg(jobs[x].startts);
253 if ((inTimeWindow) &&
260 jobStatus[
jobID] = status;
262 message = QString(
"Skipping '%1' job for %2, "
263 "should run on '%3' instead")
266 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
275 if (otherJobID && (jobStatus.contains(otherJobID)) &&
276 (!(jobStatus[otherJobID] & JOB_DONE)))
279 QString(
"Skipping '%1' job for %2, "
280 "Job ID %3 is already running for "
281 "this recording with a status of '%4'")
283 QString::number(otherJobID),
285 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
290 jobStatus[
jobID] = status;
295 message = QString(
"Skipping '%1' job for %2, "
296 "not allowed to run on this backend.")
298 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
305 message = QString(
"Skipping '%1' job for %2, this job is "
306 "not scheduled to run until %3.")
310 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
318 if (status != JOB_QUEUED) {
319 message = QString(
"Stopping '%1' job for %2")
321 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
336 message = QString(
"Cancelling '%1' job for %2")
338 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
346 message = QString(
"Unable to claim '%1' job for %2")
348 LOG(VB_JOBQUEUE, LOG_ERR,
LOC + message);
357 if ((cmds &
JOB_PAUSE) && (status != JOB_QUEUED))
359 message = QString(
"Pausing '%1' job for %2")
361 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
372 if ((cmds &
JOB_RESTART) && (status != JOB_QUEUED))
374 message = QString(
"Restart '%1' job for %2")
376 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
387 if (status != JOB_QUEUED)
392 message = QString(
"Resetting '%1' job for %2 to %3 "
393 "status, because no hostname is set.")
397 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
402 else if (inTimeWindow)
404 message = QString(
"Skipping '%1' job for %2, "
405 "current job status is '%3'")
409 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
415 if (startedJobAlready)
418 if ((inTimeWindow) &&
422 message = QString(
"Unable to claim '%1' job for %2")
424 LOG(VB_JOBQUEUE, LOG_ERR,
LOC + message);
430 message = QString(
"Skipping '%1' job for %2, "
431 "current time is outside of the "
432 "Job Queue processing window.")
434 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
438 message = QString(
"Processing '%1' job for %2, "
439 "current status is '%3'")
442 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
446 startedJobAlready =
true;
457 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"%1 jobs running. "
466 LOG(VB_JOBQUEUE, LOG_INFO,
"No jobs running. "
467 "Allowing shutdown.");
476 std::chrono::milliseconds st = (startedJobAlready) ? 5s : sleepTime;
493 QString jobHost = QString(
"");
506 const QString&
args,
const QString& comment, QString host,
507 int flags,
int status, QDateTime schedruntime)
509 int tmpStatus = JOB_UNKNOWN;
510 int tmpCmd = JOB_UNKNOWN;
513 if(!schedruntime.isValid())
522 query.
prepare(
"SELECT status, id, cmds FROM jobqueue "
523 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
524 "AND type = :JOBTYPE;");
526 query.
bindValue(
":STARTTIME", recstartts);
536 tmpStatus = query.
value(0).toInt();
538 tmpCmd = query.
value(2).toInt();
555 if (! (tmpStatus & JOB_DONE) && (tmpCmd &
JOB_STOP))
564 query.
prepare(
"INSERT INTO jobqueue (chanid, starttime, inserttime, type, "
565 "status, statustime, schedruntime, hostname, args, comment, "
567 "VALUES (:CHANID, :STARTTIME, now(), :JOBTYPE, :STATUS, "
568 "now(), :SCHEDRUNTIME, :HOST, :ARGS, :COMMENT, :FLAGS);");
571 query.
bindValue(
":STARTTIME", recstartts);
574 query.
bindValue(
":SCHEDRUNTIME", schedruntime);
590 const QString&
args,
const QString& comment,
const QString& host)
614 schedruntime = QDateTime(schedruntime.addDays(defer).date(),
615 QTime(0,0,0), Qt::UTC);
619 0, JOB_QUEUED, schedruntime);
639 query.
prepare(
"SELECT id FROM jobqueue "
640 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
641 "AND type = :JOBTYPE;");
643 query.
bindValue(
":STARTTIME", recstartts);
652 return query.
value(0).toInt();
657 int jobID,
int &jobType,
uint &chanid, QDateTime &recstartts)
661 query.
prepare(
"SELECT type, chanid, starttime FROM jobqueue "
673 jobType = query.
value(0).toInt();
674 chanid = query.
value(1).toUInt();
682 int jobID,
int &jobType,
uint &chanid, QString &recstartts)
684 QDateTime tmpStarttime;
687 jobID, jobType, chanid, tmpStarttime);
699 LOG(VB_GENERAL, LOG_ERR, QString(
"'%1' is an invalid Job Name.")
708 QString message = QString(
"GLOBAL_JOB PAUSE ID %1").arg(
jobID);
717 QString message = QString(
"GLOBAL_JOB RESUME ID %1").arg(
jobID);
726 QString message = QString(
"GLOBAL_JOB RESTART ID %1").arg(
jobID);
735 QString message = QString(
"GLOBAL_JOB STOP ID %1").arg(
jobID);
747 query.
prepare(
"UPDATE jobqueue SET status = :CANCELLED "
748 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
749 "AND status = :QUEUED;");
751 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
753 query.
bindValue(
":STARTTIME", recstartts);
759 query.
prepare(
"UPDATE jobqueue SET cmds = :CMD "
760 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
761 "AND status <> :CANCELLED;");
764 query.
bindValue(
":STARTTIME", recstartts);
765 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
774 bool jobsAreRunning =
true;
775 std::chrono::seconds totalSlept = 0s;
776 std::chrono::seconds maxSleep = 90s;
777 while (jobsAreRunning && totalSlept < maxSleep)
780 query.
prepare(
"SELECT id FROM jobqueue "
781 "WHERE chanid = :CHANID and starttime = :STARTTIME "
783 "(:FINISHED,:ABORTED,:ERRORED,:CANCELLED);");
785 query.
bindValue(
":STARTTIME", recstartts);
786 query.
bindValue(
":FINISHED", JOB_FINISHED);
787 query.
bindValue(
":ABORTED", JOB_ABORTED);
788 query.
bindValue(
":ERRORED", JOB_ERRORED);
789 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
797 if (query.
size() == 0)
799 jobsAreRunning =
false;
802 if ((totalSlept % 5s) == 0s)
804 message = QString(
"Waiting on %1 jobs still running for "
805 "chanid %2 @ %3").arg(query.
size())
806 .arg(chanid).arg(recstartts.toString(
Qt::ISODate));
807 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
814 if (totalSlept <= maxSleep)
816 query.
prepare(
"DELETE FROM jobqueue "
817 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
819 query.
bindValue(
":STARTTIME", recstartts);
826 query.
prepare(
"SELECT id, type, status, comment FROM jobqueue "
827 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
828 "AND status <> :CANCELLED ORDER BY id;");
831 query.
bindValue(
":STARTTIME", recstartts);
832 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
837 "to query list of Jobs left in Queue.", query);
841 LOG(VB_GENERAL, LOG_ERR,
LOC +
842 QString(
"In DeleteAllJobs: There are Jobs "
843 "left in the JobQueue that are still running for "
844 "chanid %1 @ %2.").arg(chanid)
849 LOG(VB_GENERAL, LOG_ERR,
LOC +
850 QString(
"Job ID %1: '%2' with status '%3' and comment '%4'")
851 .arg(query.
value(0).toString(),
854 query.
value(3).toString()));
869 const QDateTime& recstartts)
877 int thisJob =
GetJobID(jobType, chanid, recstartts);
880 if( thisJob !=
jobID)
882 msg = QString(
"JobType, chanid and starttime don't match jobID %1");
889 msg = QString(
"Can't remove running JobID %1");
897 query.
prepare(
"DELETE FROM jobqueue WHERE id = :ID;");
917 query.
prepare(
"UPDATE jobqueue SET cmds = :CMDS WHERE id = :ID;");
932 const QDateTime &recstartts,
int newCmds)
936 query.
prepare(
"UPDATE jobqueue SET cmds = :CMDS WHERE type = :TYPE "
937 "AND chanid = :CHANID AND starttime = :STARTTIME;");
942 query.
bindValue(
":STARTTIME", recstartts);
960 query.
prepare(
"UPDATE jobqueue SET flags = :FLAGS WHERE id = :ID;");
979 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"ChangeJobStatus(%1, %2, '%3')")
984 query.
prepare(
"UPDATE jobqueue SET status = :STATUS, comment = :COMMENT "
985 "WHERE id = :ID AND status <> :NEWSTATUS;");
990 query.
bindValue(
":NEWSTATUS", newStatus);
1006 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"ChangeJobComment(%1, '%2')")
1007 .arg(
jobID).arg(comment));
1011 query.
prepare(
"UPDATE jobqueue SET comment = :COMMENT "
1033 query.
prepare(
"UPDATE jobqueue SET args = :ARGS "
1053 if ((jInfo.pginfo->GetChanID() == chanid) &&
1054 (jInfo.pginfo->GetRecordingStartTime() == recstartts))
1068 return (status == JOB_QUEUED);
1073 return ((status != JOB_UNKNOWN) && (status != JOB_QUEUED) &&
1074 ((status & JOB_DONE) == 0));
1078 uint chanid,
const QDateTime &recstartts)
1090 int jobType,
uint chanid,
const QDateTime &recstartts)
1092 int tmpStatus =
GetJobStatus(jobType, chanid, recstartts);
1094 return (tmpStatus != JOB_UNKNOWN) && ((tmpStatus & JOB_DONE) == 0);
1098 int jobType,
uint chanid,
const QDateTime &recstartts)
1110 case JOB_PREVIEW:
return tr(
"Preview Generation");
1115 QString settingName =
1120 return tr(
"Unknown Job");
1124 #define JOBSTATUS_STATUSTEXT(A,B,C) case A: return C;
1133 return tr(
"Undefined");
1138 QString queueStartTimeStr;
1139 QString queueEndTimeStr;
1140 QTime queueStartTime;
1142 QTime curTime = QTime::currentTime();
1143 bool inTimeWindow =
false;
1144 orStartsWithinMins = orStartsWithinMins < 0min ? 0min : orStartsWithinMins;
1150 if (!queueStartTime.isValid())
1152 LOG(VB_GENERAL, LOG_ERR,
1153 QString(
"Invalid JobQueueWindowStart time '%1', using 00:00")
1154 .arg(queueStartTimeStr));
1155 queueStartTime = QTime(0, 0);
1159 if (!queueEndTime.isValid())
1161 LOG(VB_GENERAL, LOG_ERR,
1162 QString(
"Invalid JobQueueWindowEnd time '%1', using 23:59")
1163 .arg(queueEndTimeStr));
1164 queueEndTime = QTime(23, 59);
1167 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1168 QString(
"Currently set to run new jobs from %1 to %2")
1169 .arg(queueStartTimeStr, queueEndTimeStr));
1171 if ((queueStartTime <= curTime) && (curTime < queueEndTime))
1173 inTimeWindow =
true;
1175 else if ((queueStartTime > queueEndTime) &&
1176 ((curTime < queueEndTime) || (queueStartTime <= curTime)))
1178 inTimeWindow =
true;
1180 else if (orStartsWithinMins > 0min)
1183 if (curTime <= queueStartTime)
1186 if (queueStartTime.secsTo(curTime) <= duration_cast<std::chrono::seconds>(orStartsWithinMins).count())
1188 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1189 QString(
"Job run window will start within %1 minutes")
1190 .arg(orStartsWithinMins.count()));
1191 inTimeWindow =
true;
1198 QDateTime startDateTime = QDateTime(
1199 curDateTime.date(), queueStartTime, Qt::UTC).addDays(1);
1201 if (curDateTime.secsTo(startDateTime) <= duration_cast<std::chrono::seconds>(orStartsWithinMins).count())
1203 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1204 QString(
"Job run window will start "
1205 "within %1 minutes (tomorrow)")
1206 .arg(orStartsWithinMins.count()));
1207 inTimeWindow =
true;
1212 return inTimeWindow;
1219 QMap<int, JobQueueEntry> jobs;
1220 QMap<int, JobQueueEntry>::Iterator it;
1222 bool checkForQueuedJobs = (startingWithinMins <= 0min
1225 if (checkForQueuedJobs && startingWithinMins > 0min) {
1226 maxSchedRunTime = maxSchedRunTime.addSecs(duration_cast<std::chrono::seconds>(startingWithinMins).count());
1227 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1228 QString(
"HasRunningOrPendingJobs: checking for jobs "
1229 "starting before: %1")
1235 if (!jobs.empty()) {
1236 for (it = jobs.begin(); it != jobs.end(); ++it)
1238 int tmpStatus = (*it).status;
1239 if (tmpStatus == JOB_RUNNING) {
1240 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1241 QString(
"HasRunningOrPendingJobs: found running job"));
1245 if (checkForQueuedJobs) {
1246 if ((tmpStatus != JOB_UNKNOWN) && (!(tmpStatus & JOB_DONE))) {
1247 if (startingWithinMins <= 0min) {
1248 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1249 "HasRunningOrPendingJobs: found pending job");
1252 if ((*it).schedruntime <= maxSchedRunTime) {
1253 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1254 QString(
"HasRunningOrPendingJobs: found pending "
1255 "job scheduled to start at: %1")
1274 bool commflagWhileRecording =
1279 query.
prepare(
"SELECT j.id, j.chanid, j.starttime, j.inserttime, j.type, "
1280 "j.cmds, j.flags, j.status, j.statustime, j.hostname, "
1281 "j.args, j.comment, r.endtime, j.schedruntime "
1283 "LEFT JOIN recorded r "
1284 " ON j.chanid = r.chanid AND j.starttime = r.starttime "
1285 "ORDER BY j.schedruntime, j.id;");
1290 "query list of Jobs in Queue.", query);
1294 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1295 QString(
"GetJobsInQueue: findJobs search bitmask %1, "
1296 "found %2 total jobs")
1297 .arg(findJobs).arg(query.
size()));
1299 while (query.
next())
1301 bool wantThisJob =
false;
1303 thisJob.
id = query.
value(0).toInt();
1313 if (query.
value(1).toInt() == -1)
1316 logInfo = QString(
"jobID #%1").arg(thisJob.
id);
1321 logInfo = QString(
"chanid %1 @ %2").arg(thisJob.
chanid)
1326 ((!commflagWhileRecording) ||
1330 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1331 QString(
"GetJobsInQueue: Ignoring '%1' Job "
1332 "for %2 in %3 state. Endtime in future.")
1340 (thisJob.
status & JOB_DONE)) ||
1342 (!(thisJob.
status & JOB_DONE))) ||
1344 (thisJob.
status == JOB_ERRORED)) ||
1351 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1352 QString(
"GetJobsInQueue: Ignore '%1' Job for %2 in %3 state.")
1358 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1359 QString(
"GetJobsInQueue: Found '%1' Job for %2 in %3 state.")
1367 thisJob.
args = query.
value(10).toString();
1374 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1375 QString(
"GetJobsInQueue: Unknown Job Type: %1")
1376 .arg(thisJob.
type));
1380 jobs[jobCount++] = thisJob;
1390 if (!newHostname.isEmpty())
1392 query.
prepare(
"UPDATE jobqueue SET hostname = :NEWHOSTNAME "
1393 "WHERE hostname = :EMPTY AND id = :ID;");
1394 query.
bindValue(
":NEWHOSTNAME", newHostname);
1400 query.
prepare(
"UPDATE jobqueue SET hostname = :EMPTY "
1409 "Unable to set hostname to '%1' for "
1410 "job %2.").arg(newHostname).arg(
jobID),
1420 QString allowSetting;
1441 case JOB_PREVIEW: allowSetting =
"JobAllowPreview";
1443 default:
return false;
1454 query.
prepare(
"SELECT cmds FROM jobqueue WHERE id = :ID;");
1475 query.
prepare(
"SELECT args FROM jobqueue WHERE id = :ID;");
1482 return query.
value(0).toString();
1496 query.
prepare(
"SELECT flags FROM jobqueue WHERE id = :ID;");
1517 query.
prepare(
"SELECT status FROM jobqueue WHERE id = :ID;");
1534 int jobType,
uint chanid,
const QDateTime &recstartts)
1538 query.
prepare(
"SELECT status FROM jobqueue WHERE type = :TYPE "
1539 "AND chanid = :CHANID AND starttime = :STARTTIME;");
1543 query.
bindValue(
":STARTTIME", recstartts);
1559 QMap<int, JobQueueEntry> jobs;
1563 msg = QString(
"RecoverQueue: Checking for unfinished jobs to "
1565 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
1571 QMap<int, JobQueueEntry>::Iterator it;
1575 for (it = jobs.begin(); it != jobs.end(); ++it)
1577 int tmpCmds = (*it).cmds;
1578 int tmpStatus = (*it).status;
1581 logInfo = QString(
"jobID #%1").arg((*it).id);
1583 logInfo = QString(
"chanid %1 @ %2").arg((*it).chanid)
1584 .arg((*it).startts);
1586 if (((tmpStatus == JOB_STARTING) ||
1587 (tmpStatus == JOB_RUNNING) ||
1588 (tmpStatus == JOB_PAUSED) ||
1590 (tmpStatus == JOB_STOPPING)) &&
1593 ((*it).statustime < oldDate)))
1595 msg = QString(
"RecoverQueue: Recovering '%1' for %2 "
1599 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
1609 msg = QString(
"RecoverQueue: Ignoring '%1' for %2 "
1613 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
1626 delquery.
prepare(
"DELETE FROM jobqueue "
1627 "WHERE (status in (:FINISHED, :ABORTED, :CANCELLED) "
1628 "AND statustime < :DONEPURGEDATE) "
1629 "OR (status in (:ERRORED) "
1630 "AND statustime < :ERRORSPURGEDATE) ");
1631 delquery.
bindValue(
":FINISHED", JOB_FINISHED);
1632 delquery.
bindValue(
":ABORTED", JOB_ABORTED);
1633 delquery.
bindValue(
":CANCELLED", JOB_CANCELLED);
1634 delquery.
bindValue(
":ERRORED", JOB_ERRORED);
1635 delquery.
bindValue(
":DONEPURGEDATE", donePurgeDate);
1636 delquery.
bindValue(
":ERRORSPURGEDATE", errorsPurgeDate);
1638 if (!delquery.
exec())
1641 "old finished jobs.", delquery);
1647 if (!jobstarttsRaw.isValid())
1649 jobstarttsRaw = QDateTime::currentDateTime();
1650 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Invalid date/time passed, "
1652 jobstarttsRaw.toString()));
1658 "JobQueueWindowStart",
hostname,
"00:00")));
1661 "JobQueueWindowEnd",
hostname,
"23:59")));
1665 if (scheduleTime < windowStart || scheduleTime > windowEnd)
1667 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
"Time not within job queue window, " +
1681 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
1682 "ProcessJob(): Unable to open database connection");
1695 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
1696 QString(
"Unable to retrieve program info for chanid %1 @ %2")
1701 tr(
"Unable to retrieve program info from database"));
1731 tr(
"Program has been deleted"));
1756 tr(
"UNKNOWN JobType, unable to process!"));
1769 pthread_t childThread = 0;
1770 pthread_attr_t attr;
1771 pthread_attr_init(&attr);
1772 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1773 pthread_create(&childThread, &attr, ChildThreadRoutine, jts);
1774 pthread_attr_destroy(&attr);
1782 return "Commercial Detection";
1784 return "Unknown Job";
1786 QString descSetting =
1800 if (command.trimmed().isEmpty())
1801 command =
"mythtranscode";
1803 if (command ==
"mythtranscode")
1809 if (command.trimmed().isEmpty())
1810 command =
"mythcommflag";
1812 if (command ==
"mythcommflag")
1821 if (!command.isEmpty())
1823 command.replace(
"%JOBID%", QString(
"%1").arg(
id));
1826 if (!command.isEmpty() && tmpInfo)
1830 command.replace(
"%VERBOSELEVEL%", QString(
"%1").arg(
verboseMask));
1834 command.replace(
"%TRANSPROFILE%",
1836 "autodetect" : QString::number(transcoder));
1866 const char *m_suffix;
1870 static constexpr std::array<const PpTab_t,9> kPpTab {{
1871 {
"bytes", 9999, 0 },
1881 float fbytes =
bytes;
1883 unsigned int ii = 0;
1884 while (kPpTab[ii].m_max && fbytes > kPpTab[ii].m_max) {
1889 return QString(
"%1 %2")
1890 .arg(fbytes, 0,
'f', kPpTab[ii].m_precision)
1891 .arg(kPpTab[ii].m_suffix);
1914 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
1915 "The JobQueue cannot currently transcode files that do not "
1916 "have a chanid/starttime in the recorded table.");
1931 bool useCutlist = program_info->
HasCutlist() &&
1935 QString profilearg =
1937 "autodetect" : QString::number(transcoder);
1946 command = QString(
"%1 -j %2 --profile %3")
1947 .arg(path).arg(
jobID).arg(profilearg);
1949 command +=
" --honorcutlist";
1956 QStringList tokens = command.split(
" ", Qt::SkipEmptyParts);
1957 if (!tokens.empty())
1968 QString transcoderName;
1971 transcoderName =
"Autodetect";
1976 query.
prepare(
"SELECT name FROM recordingprofiles WHERE id = :ID;");
1980 transcoderName = query.
value(0).toString();
1985 transcoderName = QString(
"Autodetect(%1)").arg(transcoder);
2000 long long filesize = 0;
2001 long long origfilesize = QFileInfo(
filename).size();
2003 QString msg = QString(
"Transcode %1")
2006 QString details = QString(
"%1: %2 (%3)")
2010 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"%1 for %2")
2011 .arg(msg, details));
2013 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2016 GetMythDB()->GetDBManager()->CloseDatabases();
2024 tr(
"ERROR: Unable to find mythtranscode, check backend logs."));
2028 details = QString(
"%1: %2 does not exist or is not executable")
2031 LOG(VB_GENERAL, LOG_ERR,
LOC +
2032 QString(
"%1 for %2").arg(msg, details));
2036 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
"Transcode command restarting");
2044 if (status == JOB_FINISHED)
2055 filesize = st.size();
2058 QString comment = tr(
"%1: %2 => %3")
2059 .arg(transcoderName,
2067 details = QString(
"%1: %2 (%3)")
2076 QString(
"could not stat '%1'").arg(
filename);
2080 details = QString(
"%1: %2")
2092 QString comment = tr(
"exit status %1, job status was \"%2\"")
2098 details = QString(
"%1: %2 (%3)")
2106 LOG(VB_GENERAL, LOG_INFO,
LOC + msg +
": " + details);
2110 if (retrylimit == 0)
2112 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
"Retry limit exceeded for transcoder, "
2113 "setting job status to errored.");
2140 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
2141 "The JobQueue cannot currently perform lookups for items which do "
2142 "not have a chanid/starttime in the recorded table.");
2152 QString details = QString(
"%1 recorded from channel %3")
2158 QString msg = QString(
"Metadata Lookup failed. Could not open "
2159 "new database connection for %1. "
2160 "Program cannot be looked up.")
2162 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2165 tr(
"Could not open new database connection for "
2166 "metadata lookup."));
2168 delete program_info;
2172 LOG(VB_GENERAL, LOG_INFO,
2173 LOC +
"Metadata Lookup Starting for " + details);
2180 command = QString(
"%1 -j %2")
2181 .arg(path).arg(
jobID);
2184 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2187 GetMythDB()->GetDBManager()->CloseDatabases();
2189 int priority = LOG_NOTICE;
2197 comment = tr(
"Unable to find mythmetadatalookup");
2199 priority = LOG_WARNING;
2203 comment = tr(
"Aborted by user");
2205 priority = LOG_WARNING;
2209 comment = tr(
"Unable to open file or init decoder");
2211 priority = LOG_WARNING;
2215 comment = tr(
"Failed with exit status %1").arg(retVal);
2217 priority = LOG_WARNING;
2221 comment = tr(
"Metadata Lookup Complete.");
2227 QString msg = tr(
"Metadata Lookup %1",
"Job ID")
2230 if (!comment.isEmpty())
2231 details += QString(
" (%1)").arg(comment);
2233 if (priority <= LOG_WARNING)
2234 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
": " + details);
2260 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
2261 "The JobQueue cannot currently commflag files that do not "
2262 "have a chanid/starttime in the recorded table.");
2272 QString details = QString(
"%1 recorded from channel %3")
2278 QString msg = QString(
"Commercial Detection failed. Could not open "
2279 "new database connection for %1. "
2280 "Program cannot be flagged.")
2282 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2285 tr(
"Could not open new database connection for "
2286 "commercial detector."));
2288 delete program_info;
2292 LOG(VB_GENERAL, LOG_INFO,
2293 LOC +
"Commercial Detection Starting for " + details);
2295 uint breaksFound = 0;
2303 command = QString(
"%1 -j %2 --noprogress")
2304 .arg(path).arg(
jobID);
2310 QStringList tokens = command.split(
" ", Qt::SkipEmptyParts);
2311 if (!tokens.empty())
2316 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2319 GetMythDB()->GetDBManager()->CloseDatabases();
2321 int priority = LOG_NOTICE;
2329 comment = tr(
"Unable to find mythcommflag");
2331 priority = LOG_WARNING;
2335 comment = tr(
"Aborted by user");
2337 priority = LOG_WARNING;
2341 comment = tr(
"Unable to open file or init decoder");
2343 priority = LOG_WARNING;
2347 comment = tr(
"Failed with exit status %1").arg(breaksFound);
2349 priority = LOG_WARNING;
2353 comment = tr(
"%n commercial break(s)",
"", breaksFound);
2369 QString msg = tr(
"Commercial Detection %1",
"Job ID")
2372 if (!comment.isEmpty())
2373 details += QString(
" (%1)").arg(comment);
2375 if (priority <= LOG_WARNING)
2376 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
": " + details);
2410 msg = QString(
"Started %1 for %2 recorded from channel %3")
2416 msg = QString(
"Started %1 for jobID %2").arg(jobDesc).arg(
jobID);
2418 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(msg.toLocal8Bit().constData()));
2432 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2434 GetMythDB()->GetDBManager()->CloseDatabases();
2440 msg = QString(
"User Job '%1' failed, unable to find "
2441 "executable, check your PATH and backend logs.")
2443 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2444 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Current PATH: '%1'")
2445 .arg(qEnvironmentVariable(
"PATH")));
2448 tr(
"ERROR: Unable to find executable, check backend logs."));
2450 else if (result != 0)
2452 msg = QString(
"User Job '%1' failed.").arg(command);
2453 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2456 tr(
"ERROR: User Job returned non-zero, check logs."));
2462 msg = QString(
"Finished %1 for %2 recorded from channel %3")
2468 msg = QString(
"Finished %1 for jobID %2").arg(jobDesc).arg(
jobID);
2470 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(msg.toLocal8Bit().constData()));
2487 while ((x != 0) && ((x & 0x01) == 0))
static enum JobCmds GetJobCmd(int jobID)
static QString PrettyPrint(off_t bytes)
void SaveTranscodeStatus(TranscodingStatus trans)
Set "transcoded" field in "recorded" table to "trans".
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
virtual void SaveFilesize(uint64_t fsize)
Sets recording file size in database, and sets "filesize" field.
const QString kJobQueueInUseID
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
static const Type kMythEventMessage
static QMap< QString, int > JobNameToType
@ GENERIC_EXIT_CMD_NOT_FOUND
Command not found.
QString GetHostname(void) const
bool IsBlockingClient(void) const
is this client blocking shutdown
static bool QueueJob(int jobType, uint chanid, const QDateTime &recstartts, const QString &args="", const QString &comment="", QString host="", int flags=0, int status=JOB_QUEUED, QDateTime schedruntime=QDateTime())
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
void customEvent(QEvent *e) override
bool myth_ioprio(int)
Allows setting the I/O priority of the current process/thread.
static bool ChangeJobCmds(int jobID, int newCmds)
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Holds information on a TV Program one might wish to record.
This class creates a preview image of a recording.
void StartChildJob(void *(*ChildThreadRoutine)(void *), int jobID)
This class is used as a container for messages.
static bool QueueRecordingJobs(const RecordingInfo &recinfo, int jobTypes=JOB_NONE)
QVariant value(int i) const
static bool SafeDeleteJob(int jobID, int jobType, int chanid, const QDateTime &recstartts)
static bool GetJobInfoFromID(int jobID, int &jobType, uint &chanid, QDateTime &recstartts)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void RemoveRunningJob(int id)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static bool StopJob(int jobID)
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
QString GetRecordingGroup(void) const
#define JOBSTATUS_STATUSTEXT(A, B, C)
@ TRANSCODING_NOT_TRANSCODED
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
const QString & Message() const
static bool IsJobRunning(int jobType, uint chanid, const QDateTime &recstartts)
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
int GetRunningJobID(uint chanid, const QDateTime &recstartts)
static bool InJobRunWindow(QDateTime jobstarttsRaw)
void addListener(QObject *listener)
Add a listener to the observable.
static QString JobText(int jobType)
static bool PauseJob(int jobID)
void MarkAsInUse(bool inuse, const QString &usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
static int GetJobsInQueue(QMap< int, JobQueueEntry > &jobs, int findJobs=JOB_LIST_NOT_DONE)
void SetPathname(const QString &pn)
static bool IsJobQueuedOrRunning(int jobType, uint chanid, const QDateTime &recstartts)
@ kFilename
Default UTC, "yyyyMMddhhmmss".
static QString GetJobCommand(int id, int jobType, ProgramInfo *tmpInfo)
static bool ChangeJobComment(int jobID, const QString &comment="")
static QString GetJobArgs(int jobID)
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
static enum JobStatus GetJobStatus(int jobID)
static void DBError(const QString &where, const MSqlQuery &query)
static bool DeleteAllJobs(uint chanid, const QDateTime &recstartts)
static void CleanupOldJobsInQueue()
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
static bool IsJobQueued(int jobType, uint chanid, const QDateTime &recstartts)
bool HasCutlist(void) const
static constexpr int64_t kRecentInterval
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
@ GENERIC_EXIT_NO_RECORDING_DATA
No program/recording data.
QMutex m_queueThreadCondLock
static bool ChangeJobHost(int jobID, const QString &newHostname)
static const uint kTranscoderAutodetect
sentinel value
@ GENERIC_EXIT_NOT_OK
Exited with error.
static bool HasRunningOrPendingJobs(std::chrono::minutes startingWithinMins=0min)
uint QueryTranscoderID(void) const
QMap< int, RunningJobInfo > m_runningJobs
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
@ GENERIC_EXIT_DAEMONIZING_ERROR
Error daemonizing or execl.
int GetNumSetting(const QString &key, int defaultval=0)
static bool DeleteJob(int jobID)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
bool GetBoolSetting(const QString &key, bool defaultval=false)
static void * FlagCommercialsThread(void *param)
bool AllowedToRun(const JobQueueEntry &job)
static constexpr const char * MYTH_APPNAME_MYTHJOBQUEUE
static bool ChangeJobFlags(int jobID, int newFlags)
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
QRecursiveMutex * m_runningJobsLock
static void * TranscodeThread(void *param)
Holds information on recordings and videos.
static int GetJobTypeFromName(const QString &name)
static bool IsJobStatusRunning(int status)
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
static int GetJobID(int jobType, uint chanid, const QDateTime &recstartts)
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false)
Returns filename or URL to be used to play back this recording.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
static enum JobFlags GetJobFlags(int jobID)
static bool RestartJob(int jobID)
virtual void SubstituteMatches(QString &str)
Subsitute MATCH% type variable names in the given string.
void DoFlagCommercialsThread(int jobID)
@ kMSLowExitVal
allow exit values 0-127 only
@ GENERIC_EXIT_RESTART
Need to restart transcoding.
This is a wrapper around QThread that does several additional things.
void SendUpdateEvent(void) const
Sends event out that the ProgramInfo should be reloaded.
QString GetAppBinDir(void)
static bool ResumeJob(int jobID)
void DoMetadataLookupThread(int jobID)
static void ThreadCleanup(void)
This is to be called on exit in those few threads that haven't been ported to MThread.
static bool IsJobStatusQueued(int status)
static void RecoverQueue(bool justOld=false)
int numRowsAffected() const
QWaitCondition m_queueThreadCond
QString GetHostName(void)
static void ThreadSetup(const QString &name)
This is to be called on startup in those few threads that haven't been ported to MThread.
void DoTranscodeThread(int jobID)
static void * MetadataLookupThread(void *param)
static bool ChangeJobArgs(int jobID, const QString &args="")
void ProcessJob(const JobQueueEntry &job)
static bool ChangeJobStatus(int jobID, int newStatus, const QString &comment="")
static void * UserJobThread(void *param)
static QString GetJobDescription(int jobType)
void dispatch(const MythEvent &event)
void removeListener(QObject *listener)
Remove a listener to the observable.
bool IsCommercialFree(void) const
static bool QueueJobs(int jobTypes, uint chanid, const QDateTime &recstartts, const QString &args="", const QString &comment="", const QString &host="")
static int UserJobTypeToIndex(int JobType)
void DoUserJobThread(int jobID)
static QString StatusText(int status)
QString GetSetting(const QString &key, const QString &defaultval="")
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.