15 #include <QCoreApplication> 44 #define LOC QString("JobQueue: ") 51 runningJobsLock(new QMutex(QMutex::Recursive)),
53 queueThread(new
MThread(
"JobQueue", this)),
58 #ifndef USING_VALGRIND 63 LOG(VB_GENERAL, LOG_ERR,
LOC +
64 "The JobQueue has been disabled because " 65 "you compiled with the --enable-valgrind option.");
66 #endif // USING_VALGRIND 91 MythEvent *me = static_cast<MythEvent *>(e);
92 QString message = me->
Message();
94 if (message.startsWith(
"LOCAL_JOB"))
99 message = message.simplified();
100 QStringList tokens = message.split(
" ", QString::SkipEmptyParts);
101 QString
action = tokens[1];
104 if (tokens[2] ==
"ID")
105 jobID = tokens[3].toInt();
117 msg = QString(
"Unable to determine jobID for message: " 118 "%1. Program will not be flagged.")
120 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
126 msg = QString(
"Received message '%1'").arg(message);
127 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
138 else if (
action ==
"PAUSE")
140 else if (
action ==
"RESUME")
142 else if (
action ==
"RESTART")
168 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
"ProcessQueue() started");
177 QMap<int, int> jobStatus;
179 QMap<int, JobQueueEntry> jobs;
181 bool inTimeWindow =
true;
182 QMap<int, RunningJobInfo>::Iterator rjiter;
189 bool startedJobAlready =
false;
192 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
193 QString(
"Currently set to run up to %1 job(s) max.")
202 if ((*rjiter).pginfo)
203 (*rjiter).pginfo->UpdateInUseMark();
213 for (
int x = 0; x < jobs.size(); x++)
215 status = jobs[x].status;
218 if (((status == JOB_RUNNING) ||
219 (status == JOB_STARTING) ||
220 (status == JOB_PAUSED)) &&
225 message = QString(
"Currently Running %1 jobs.")
229 message += QString(
" Jobs in Queue, but we are outside of the " 230 "Job Queue time window, no new jobs can be " 232 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
236 message +=
" (At Maximum, no new jobs can be started until " 237 "a running job completes)";
240 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
246 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
257 status = jobs[x].status;
261 logInfo = QString(
"jobID #%1").arg(
jobID);
263 logInfo = QString(
"chanid %1 @ %2").arg(jobs[x].chanid)
264 .arg(jobs[x].startts);
267 if ((inTimeWindow) &&
274 jobStatus[
jobID] = status;
276 message = QString(
"Skipping '%1' job for %2, " 277 "should run on '%3' instead")
280 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
289 if (otherJobID && (jobStatus.contains(otherJobID)) &&
290 (!(jobStatus[otherJobID] & JOB_DONE)))
293 QString(
"Skipping '%1' job for %2, " 294 "Job ID %3 is already running for " 295 "this recording with a status of '%4'")
299 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
304 jobStatus[
jobID] = status;
309 message = QString(
"Skipping '%1' job for %2, " 310 "not allowed to run on this backend.")
312 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
319 message = QString(
"Skipping '%1' job for %2, this job is " 320 "not scheduled to run until %3.")
322 .arg(jobs[x].schedruntime
324 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
332 if (status != JOB_QUEUED) {
333 message = QString(
"Stopping '%1' job for %2")
336 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
350 message = QString(
"Cancelling '%1' job for %2")
353 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
361 message = QString(
"Unable to claim '%1' job for %2")
364 LOG(VB_JOBQUEUE, LOG_ERR,
LOC + message);
374 if ((cmds &
JOB_PAUSE) && (status != JOB_QUEUED))
376 message = QString(
"Pausing '%1' job for %2")
378 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
389 if ((cmds &
JOB_RESTART) && (status != JOB_QUEUED))
391 message = QString(
"Restart '%1' job for %2")
393 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
404 if (status != JOB_QUEUED)
409 message = QString(
"Resetting '%1' job for %2 to %3 " 410 "status, because no hostname is set.")
414 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
419 else if (inTimeWindow)
421 message = QString(
"Skipping '%1' job for %2, " 422 "current job status is '%3'")
426 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
432 if (startedJobAlready)
435 if ((inTimeWindow) &&
439 message = QString(
"Unable to claim '%1' job for %2")
441 LOG(VB_JOBQUEUE, LOG_ERR,
LOC + message);
447 message = QString(
"Skipping '%1' job for %2, " 448 "current time is outside of the " 449 "Job Queue processing window.")
451 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
455 message = QString(
"Processing '%1' job for %2, " 456 "current status is '%3'")
459 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
463 startedJobAlready =
true;
474 LOG(VB_JOBQUEUE, LOG_INFO, QString(
"%1 jobs running. " 483 LOG(VB_JOBQUEUE, LOG_INFO,
"No jobs running. " 484 "Allowing shutdown.");
493 int st = (startedJobAlready) ? (5 * 1000) : (sleepTime * 1000);
510 QString jobHost = QString(
"");
526 QString
args, QString comment, QString host,
527 int flags,
int status, QDateTime schedruntime)
529 int tmpStatus = JOB_UNKNOWN;
530 int tmpCmd = JOB_UNKNOWN;
533 if(!schedruntime.isValid())
542 query.
prepare(
"SELECT status, id, cmds FROM jobqueue " 543 "WHERE chanid = :CHANID AND starttime = :STARTTIME " 544 "AND type = :JOBTYPE;");
546 query.
bindValue(
":STARTTIME", recstartts);
558 tmpStatus = query.
value(0).toInt();
560 tmpCmd = query.
value(2).toInt();
578 if (! (tmpStatus & JOB_DONE) && (tmpCmd &
JOB_STOP))
587 query.
prepare(
"INSERT INTO jobqueue (chanid, starttime, inserttime, type, " 588 "status, statustime, schedruntime, hostname, args, comment, " 590 "VALUES (:CHANID, :STARTTIME, now(), :JOBTYPE, :STATUS, " 591 "now(), :SCHEDRUNTIME, :HOST, :ARGS, :COMMENT, :FLAGS);");
594 query.
bindValue(
":STARTTIME", recstartts);
597 query.
bindValue(
":SCHEDRUNTIME", schedruntime);
613 QString
args, QString comment, QString host)
637 schedruntime = QDateTime(schedruntime.addDays(defer).date(),
638 QTime(0,0,0), Qt::UTC);
642 0, JOB_QUEUED, schedruntime);
662 query.
prepare(
"SELECT id FROM jobqueue " 663 "WHERE chanid = :CHANID AND starttime = :STARTTIME " 664 "AND type = :JOBTYPE;");
666 query.
bindValue(
":STARTTIME", recstartts);
677 return query.
value(0).toInt();
684 int jobID,
int &jobType,
uint &chanid, QDateTime &recstartts)
688 query.
prepare(
"SELECT type, chanid, starttime FROM jobqueue " 702 jobType = query.
value(0).toInt();
703 chanid = query.
value(1).toUInt();
713 int jobID,
int &jobType,
uint &chanid, QString &recstartts)
715 QDateTime tmpStarttime;
718 jobID, jobType, chanid, tmpStarttime);
730 LOG(VB_GENERAL, LOG_ERR, QString(
"'%1' is an invalid Job Name.")
740 QString message = QString(
"GLOBAL_JOB PAUSE ID %1").arg(
jobID);
749 QString message = QString(
"GLOBAL_JOB RESUME ID %1").arg(
jobID);
758 QString message = QString(
"GLOBAL_JOB RESTART ID %1").arg(
jobID);
767 QString message = QString(
"GLOBAL_JOB STOP ID %1").arg(
jobID);
779 query.
prepare(
"UPDATE jobqueue SET status = :CANCELLED " 780 "WHERE chanid = :CHANID AND starttime = :STARTTIME " 781 "AND status = :QUEUED;");
783 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
785 query.
bindValue(
":STARTTIME", recstartts);
791 query.
prepare(
"UPDATE jobqueue SET cmds = :CMD " 792 "WHERE chanid = :CHANID AND starttime = :STARTTIME " 793 "AND status <> :CANCELLED;");
796 query.
bindValue(
":STARTTIME", recstartts);
797 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
806 bool jobsAreRunning =
true;
809 while (jobsAreRunning && totalSlept < maxSleep)
812 query.
prepare(
"SELECT id FROM jobqueue " 813 "WHERE chanid = :CHANID and starttime = :STARTTIME " 815 "(:FINISHED,:ABORTED,:ERRORED,:CANCELLED);");
817 query.
bindValue(
":STARTTIME", recstartts);
818 query.
bindValue(
":FINISHED", JOB_FINISHED);
819 query.
bindValue(
":ABORTED", JOB_ABORTED);
820 query.
bindValue(
":ERRORED", JOB_ERRORED);
821 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
829 if (query.
size() == 0)
831 jobsAreRunning =
false;
834 else if ((totalSlept % 5) == 0)
836 message = QString(
"Waiting on %1 jobs still running for " 837 "chanid %2 @ %3").arg(query.
size())
838 .arg(chanid).arg(recstartts.toString(
Qt::ISODate));
839 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + message);
846 if (totalSlept <= maxSleep)
848 query.
prepare(
"DELETE FROM jobqueue " 849 "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
851 query.
bindValue(
":STARTTIME", recstartts);
858 query.
prepare(
"SELECT id, type, status, comment FROM jobqueue " 859 "WHERE chanid = :CHANID AND starttime = :STARTTIME " 860 "AND status <> :CANCELLED ORDER BY id;");
863 query.
bindValue(
":STARTTIME", recstartts);
864 query.
bindValue(
":CANCELLED", JOB_CANCELLED);
869 "to query list of Jobs left in Queue.", query);
873 LOG(VB_GENERAL, LOG_ERR,
LOC +
874 QString(
"In DeleteAllJobs: There are Jobs " 875 "left in the JobQueue that are still running for " 876 "chanid %1 @ %2.").arg(chanid)
881 LOG(VB_GENERAL, LOG_ERR,
LOC +
882 QString(
"Job ID %1: '%2' with status '%3' and comment '%4'")
883 .arg(query.
value(0).toInt())
886 .arg(query.
value(3).toString()));
901 QDateTime recstartts)
909 int thisJob =
GetJobID(jobType, chanid, recstartts);
912 if( thisJob !=
jobID)
914 msg = QString(
"JobType, chanid and starttime don't match jobID %1");
921 msg = QString(
"Can't remove running JobID %1");
929 query.
prepare(
"DELETE FROM jobqueue WHERE id = :ID;");
949 query.
prepare(
"UPDATE jobqueue SET cmds = :CMDS WHERE id = :ID;");
964 const QDateTime &recstartts,
int newCmds)
968 query.
prepare(
"UPDATE jobqueue SET cmds = :CMDS WHERE type = :TYPE " 969 "AND chanid = :CHANID AND starttime = :STARTTIME;");
974 query.
bindValue(
":STARTTIME", recstartts);
992 query.
prepare(
"UPDATE jobqueue SET flags = :FLAGS WHERE id = :ID;");
1011 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"ChangeJobStatus(%1, %2, '%3')")
1016 query.
prepare(
"UPDATE jobqueue SET status = :STATUS, comment = :COMMENT " 1017 "WHERE id = :ID AND status <> :NEWSTATUS;");
1022 query.
bindValue(
":NEWSTATUS", newStatus);
1038 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"ChangeJobComment(%1, '%2')")
1039 .arg(
jobID).arg(comment));
1043 query.
prepare(
"UPDATE jobqueue SET comment = :COMMENT " 1065 query.
prepare(
"UPDATE jobqueue SET args = :ARGS " 1083 QMap<int, RunningJobInfo>::iterator it =
runningJobs.begin();
1103 return (status == JOB_QUEUED);
1108 return ((status != JOB_UNKNOWN) && (status != JOB_QUEUED) &&
1109 (!(status & JOB_DONE)));
1113 uint chanid,
const QDateTime &recstartts)
1125 int jobType,
uint chanid,
const QDateTime &recstartts)
1127 int tmpStatus =
GetJobStatus(jobType, chanid, recstartts);
1129 if ((tmpStatus != JOB_UNKNOWN) && (!(tmpStatus & JOB_DONE)))
1136 int jobType,
uint chanid,
const QDateTime &recstartts)
1152 QString settingName =
1157 return tr(
"Unknown Job");
1164 #define JOBSTATUS_STATUSTEXT(A,B,C) case A: return C; 1168 return tr(
"Undefined");
1173 QString queueStartTimeStr;
1174 QString queueEndTimeStr;
1175 QTime queueStartTime;
1177 QTime curTime = QTime::currentTime();
1178 bool inTimeWindow =
false;
1179 orStartsWithinMins = orStartsWithinMins < 0 ? 0 : orStartsWithinMins;
1185 if (!queueStartTime.isValid())
1187 LOG(VB_GENERAL, LOG_ERR,
1188 QString(
"Invalid JobQueueWindowStart time '%1', using 00:00")
1189 .arg(queueStartTimeStr));
1190 queueStartTime = QTime(0, 0);
1194 if (!queueEndTime.isValid())
1196 LOG(VB_GENERAL, LOG_ERR,
1197 QString(
"Invalid JobQueueWindowEnd time '%1', using 23:59")
1198 .arg(queueEndTimeStr));
1199 queueEndTime = QTime(23, 59);
1202 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1203 QString(
"Currently set to run new jobs from %1 to %2")
1204 .arg(queueStartTimeStr).arg(queueEndTimeStr));
1206 if ((queueStartTime <= curTime) && (curTime < queueEndTime))
1208 inTimeWindow =
true;
1210 else if ((queueStartTime > queueEndTime) &&
1211 ((curTime < queueEndTime) || (queueStartTime <= curTime)))
1213 inTimeWindow =
true;
1215 else if (orStartsWithinMins > 0)
1218 if (curTime <= queueStartTime)
1221 if (queueStartTime.secsTo(curTime) <= (orStartsWithinMins * 60))
1223 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1224 QString(
"Job run window will start within %1 minutes")
1225 .arg(orStartsWithinMins));
1226 inTimeWindow =
true;
1233 QDateTime startDateTime = QDateTime(
1234 curDateTime.date(), queueStartTime, Qt::UTC).addDays(1);
1236 if (curDateTime.secsTo(startDateTime) <= (orStartsWithinMins * 60))
1238 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1239 QString(
"Job run window will start " 1240 "within %1 minutes (tomorrow)")
1241 .arg(orStartsWithinMins));
1242 inTimeWindow =
true;
1247 return inTimeWindow;
1254 QMap<int, JobQueueEntry> jobs;
1255 QMap<int, JobQueueEntry>::Iterator it;
1257 bool checkForQueuedJobs = (startingWithinMins <= 0
1260 if (checkForQueuedJobs && startingWithinMins > 0) {
1261 maxSchedRunTime = maxSchedRunTime.addSecs(startingWithinMins * 60);
1262 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1263 QString(
"HasRunningOrPendingJobs: checking for jobs " 1264 "starting before: %1")
1271 for (it = jobs.begin(); it != jobs.end(); ++it)
1273 int tmpStatus = (*it).status;
1274 if (tmpStatus == JOB_RUNNING) {
1275 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1276 QString(
"HasRunningOrPendingJobs: found running job"));
1280 if (checkForQueuedJobs) {
1281 if ((tmpStatus != JOB_UNKNOWN) && (!(tmpStatus & JOB_DONE))) {
1282 if (startingWithinMins <= 0) {
1283 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1284 "HasRunningOrPendingJobs: found pending job");
1287 else if ((*it).schedruntime <= maxSchedRunTime) {
1288 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1289 QString(
"HasRunningOrPendingJobs: found pending " 1290 "job scheduled to start at: %1")
1309 bool commflagWhileRecording =
1314 query.
prepare(
"SELECT j.id, j.chanid, j.starttime, j.inserttime, j.type, " 1315 "j.cmds, j.flags, j.status, j.statustime, j.hostname, " 1316 "j.args, j.comment, r.endtime, j.schedruntime " 1318 "LEFT JOIN recorded r " 1319 " ON j.chanid = r.chanid AND j.starttime = r.starttime " 1320 "ORDER BY j.schedruntime, j.id;");
1325 "query list of Jobs in Queue.", query);
1329 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1330 QString(
"GetJobsInQueue: findJobs search bitmask %1, " 1331 "found %2 total jobs")
1332 .arg(findJobs).arg(query.
size()));
1334 while (query.
next())
1336 bool wantThisJob =
false;
1338 thisJob.
id = query.
value(0).toInt();
1348 if (query.
value(1).toInt() == -1)
1351 logInfo = QString(
"jobID #%1").arg(thisJob.
id);
1356 logInfo = QString(
"chanid %1 @ %2").arg(thisJob.
chanid)
1361 ((!commflagWhileRecording) ||
1365 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1366 QString(
"GetJobsInQueue: Ignoring '%1' Job " 1367 "for %2 in %3 state. Endtime in future.")
1375 (thisJob.
status & JOB_DONE)) ||
1377 (!(thisJob.
status & JOB_DONE))) ||
1379 (thisJob.
status == JOB_ERRORED)) ||
1386 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1387 QString(
"GetJobsInQueue: Ignore '%1' Job for %2 in %3 state.")
1393 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1394 QString(
"GetJobsInQueue: Found '%1' Job for %2 in %3 state.")
1402 thisJob.
args = query.
value(10).toString();
1409 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
1410 QString(
"GetJobsInQueue: Unknown Job Type: %1")
1411 .arg(thisJob.
type));
1415 jobs[jobCount++] = thisJob;
1425 if (!newHostname.isEmpty())
1427 query.
prepare(
"UPDATE jobqueue SET hostname = :NEWHOSTNAME " 1428 "WHERE hostname = :EMPTY AND id = :ID;");
1429 query.
bindValue(
":NEWHOSTNAME", newHostname);
1435 query.
prepare(
"UPDATE jobqueue SET hostname = :EMPTY " 1444 "Unable to set hostname to '%1' for " 1445 "job %2.").arg(newHostname).arg(
jobID),
1458 QString allowSetting;
1479 case JOB_PREVIEW: allowSetting =
"JobAllowPreview";
1481 default:
return false;
1492 query.
prepare(
"SELECT cmds FROM jobqueue WHERE id = :ID;");
1513 query.
prepare(
"SELECT args FROM jobqueue WHERE id = :ID;");
1520 return query.
value(0).toString();
1534 query.
prepare(
"SELECT flags FROM jobqueue WHERE id = :ID;");
1555 query.
prepare(
"SELECT status FROM jobqueue WHERE id = :ID;");
1572 int jobType,
uint chanid,
const QDateTime &recstartts)
1576 query.
prepare(
"SELECT status FROM jobqueue WHERE type = :TYPE " 1577 "AND chanid = :CHANID AND starttime = :STARTTIME;");
1581 query.
bindValue(
":STARTTIME", recstartts);
1597 QMap<int, JobQueueEntry> jobs;
1601 msg = QString(
"RecoverQueue: Checking for unfinished jobs to " 1603 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
1609 QMap<int, JobQueueEntry>::Iterator it;
1613 for (it = jobs.begin(); it != jobs.end(); ++it)
1615 int tmpCmds = (*it).cmds;
1616 int tmpStatus = (*it).status;
1619 logInfo = QString(
"jobID #%1").arg((*it).id);
1621 logInfo = QString(
"chanid %1 @ %2").arg((*it).chanid)
1622 .arg((*it).startts);
1624 if (((tmpStatus == JOB_STARTING) ||
1625 (tmpStatus == JOB_RUNNING) ||
1626 (tmpStatus == JOB_PAUSED) ||
1628 (tmpStatus == JOB_STOPPING)) &&
1631 ((*it).statustime < oldDate)))
1633 msg = QString(
"RecoverQueue: Recovering '%1' for %2 " 1637 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
1647 msg = QString(
"RecoverQueue: Ignoring '%1' for %2 " 1651 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + msg);
1664 delquery.
prepare(
"DELETE FROM jobqueue " 1665 "WHERE (status in (:FINISHED, :ABORTED, :CANCELLED) " 1666 "AND statustime < :DONEPURGEDATE) " 1667 "OR (status in (:ERRORED) " 1668 "AND statustime < :ERRORSPURGEDATE) ");
1669 delquery.
bindValue(
":FINISHED", JOB_FINISHED);
1670 delquery.
bindValue(
":ABORTED", JOB_ABORTED);
1671 delquery.
bindValue(
":CANCELLED", JOB_CANCELLED);
1672 delquery.
bindValue(
":ERRORED", JOB_ERRORED);
1673 delquery.
bindValue(
":DONEPURGEDATE", donePurgeDate);
1674 delquery.
bindValue(
":ERRORSPURGEDATE", errorsPurgeDate);
1676 if (!delquery.
exec())
1679 "old finished jobs.", delquery);
1685 if (!jobstarttsRaw.isValid())
1687 jobstarttsRaw = QDateTime::currentDateTime();
1688 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Invalid date/time passed, " 1690 jobstarttsRaw.toString()));
1696 "JobQueueWindowStart",
hostname,
"00:00")));
1699 "JobQueueWindowEnd",
hostname,
"23:59")));
1703 if (scheduleTime < windowStart || scheduleTime > windowEnd)
1705 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
"Time not within job queue window, " +
1720 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
1721 "ProcessJob(): Unable to open database connection");
1734 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
1735 QString(
"Unable to retrieve program info for chanid %1 @ %2")
1740 tr(
"Unable to retrieve program info from database"));
1770 tr(
"Program has been deleted"));
1795 tr(
"UNKNOWN JobType, unable to process!"));
1808 pthread_t childThread;
1809 pthread_attr_t attr;
1810 pthread_attr_init(&attr);
1811 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1812 pthread_create(&childThread, &attr, ChildThreadRoutine, jts);
1813 pthread_attr_destroy(&attr);
1821 return "Commercial Detection";
1823 return "Unknown Job";
1825 QString descSetting =
1839 if (command.trimmed().isEmpty())
1840 command =
"mythtranscode";
1842 if (command ==
"mythtranscode")
1848 if (command.trimmed().isEmpty())
1849 command =
"mythcommflag";
1851 if (command ==
"mythcommflag")
1860 if (!command.isEmpty())
1862 command.replace(
"%JOBID%", QString(
"%1").arg(
id));
1865 if (!command.isEmpty() && tmpInfo)
1869 command.replace(
"%VERBOSELEVEL%", QString(
"%1").arg(
verboseMask));
1873 command.replace(
"%TRANSPROFILE%",
1875 "autodetect" : QString::number(transcoder));
1904 static const struct {
1909 {
"bytes", 9999, 0 },
1920 float fbytes =
bytes;
1923 while (pptab[ii].max && fbytes > pptab[ii].max) {
1928 return QString(
"%1 %2")
1929 .arg(fbytes, 0,
'f', pptab[ii].precision)
1930 .arg(pptab[ii].suffix);
1953 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
1954 "The JobQueue cannot currently transcode files that do not " 1955 "have a chanid/starttime in the recorded table.");
1970 bool useCutlist = program_info->
HasCutlist() &&
1974 QString profilearg =
1976 "autodetect" : QString::number(transcoder);
1985 command = QString(
"%1 -j %2 --profile %3")
1986 .arg(path).arg(
jobID).arg(profilearg);
1988 command +=
" --honorcutlist";
1995 QStringList tokens = command.split(
" ", QString::SkipEmptyParts);
1996 if (!tokens.empty())
2007 QString transcoderName;
2010 transcoderName =
"Autodetect";
2015 query.
prepare(
"SELECT name FROM recordingprofiles WHERE id = :ID;");
2019 transcoderName = query.
value(0).toString();
2024 transcoderName = QString(
"Autodetect(%1)").arg(transcoder);
2039 long long filesize = 0;
2040 long long origfilesize = QFileInfo(filename).size();
2042 QString msg = QString(
"Transcode %1")
2045 QString detailstr = QString(
"%1: %2 (%3)")
2047 .arg(transcoderName)
2049 QByteArray details = detailstr.toLocal8Bit();
2051 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"%1 for %2")
2052 .arg(msg).arg(details.constData()));
2054 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2057 GetMythDB()->GetDBManager()->CloseDatabases();
2065 tr(
"ERROR: Unable to find mythtranscode, check backend logs."));
2069 detailstr = QString(
"%1: %2 does not exist or is not executable")
2072 details = detailstr.toLocal8Bit();
2074 LOG(VB_GENERAL, LOG_ERR,
LOC +
2075 QString(
"%1 for %2").arg(msg).arg(details.constData()));
2079 LOG(VB_JOBQUEUE, LOG_INFO,
LOC +
"Transcode command restarting");
2087 if (status == JOB_FINISHED)
2094 QFileInfo st(filename);
2098 filesize = st.size();
2101 QString comment = tr(
"%1: %2 => %3")
2102 .arg(transcoderName)
2110 details = (QString(
"%1: %2 (%3)")
2113 .arg(transcoderName)
2119 QString(
"could not stat '%1'").arg(filename);
2123 details = (QString(
"%1: %2")
2126 .arg(comment)).toLocal8Bit();
2135 QString comment = tr(
"exit status %1, job status was \"%2\"")
2141 details = (QString(
"%1: %2 (%3)")
2144 .arg(transcoderName)
2145 .arg(comment)).toLocal8Bit().constData();
2149 LOG(VB_GENERAL, LOG_INFO,
LOC + msg +
": " + details);
2153 if (retrylimit == 0)
2155 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
"Retry limit exceeded for transcoder, " 2156 "setting job status to errored.");
2183 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
2184 "The JobQueue cannot currently perform lookups for items which do " 2185 "not have a chanid/starttime in the recorded table.");
2195 QString detailstr = QString(
"%1 recorded from channel %3")
2198 QByteArray details = detailstr.toLocal8Bit();
2202 QString msg = QString(
"Metadata Lookup failed. Could not open " 2203 "new database connection for %1. " 2204 "Program cannot be looked up.")
2205 .arg(details.constData());
2206 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2209 tr(
"Could not open new database connection for " 2210 "metadata lookup."));
2212 delete program_info;
2216 QString msg = tr(
"Metadata Lookup Starting");
2217 LOG(VB_GENERAL, LOG_INFO,
2218 LOC +
"Metadata Lookup Starting for " + detailstr);
2225 command = QString(
"%1 -j %2")
2226 .arg(path).arg(
jobID);
2229 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2232 GetMythDB()->GetDBManager()->CloseDatabases();
2234 int priority = LOG_NOTICE;
2242 comment = tr(
"Unable to find mythmetadatalookup");
2244 priority = LOG_WARNING;
2248 comment = tr(
"Aborted by user");
2250 priority = LOG_WARNING;
2254 comment = tr(
"Unable to open file or init decoder");
2256 priority = LOG_WARNING;
2260 comment = tr(
"Failed with exit status %1").arg(retVal);
2262 priority = LOG_WARNING;
2266 comment = tr(
"Metadata Lookup Complete.");
2272 msg = tr(
"Metadata Lookup %1",
"Job ID")
2275 if (!comment.isEmpty())
2277 detailstr += QString(
" (%1)").arg(comment);
2278 details = detailstr.toLocal8Bit();
2281 if (priority <= LOG_WARNING)
2282 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
": " + details.constData());
2308 LOG(VB_JOBQUEUE, LOG_ERR,
LOC +
2309 "The JobQueue cannot currently commflag files that do not " 2310 "have a chanid/starttime in the recorded table.");
2320 QString detailstr = QString(
"%1 recorded from channel %3")
2323 QByteArray details = detailstr.toLocal8Bit();
2327 QString msg = QString(
"Commercial Detection failed. Could not open " 2328 "new database connection for %1. " 2329 "Program cannot be flagged.")
2330 .arg(details.constData());
2331 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2334 tr(
"Could not open new database connection for " 2335 "commercial detector."));
2337 delete program_info;
2341 QString msg = tr(
"Commercial Detection Starting");
2342 LOG(VB_GENERAL, LOG_INFO,
2343 LOC +
"Commercial Detection Starting for " + detailstr);
2345 uint breaksFound = 0;
2353 command = QString(
"%1 -j %2 --noprogress")
2354 .arg(path).arg(
jobID);
2360 QStringList tokens = command.split(
" ", QString::SkipEmptyParts);
2361 if (!tokens.empty())
2366 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2369 GetMythDB()->GetDBManager()->CloseDatabases();
2371 int priority = LOG_NOTICE;
2379 comment = tr(
"Unable to find mythcommflag");
2381 priority = LOG_WARNING;
2385 comment = tr(
"Aborted by user");
2387 priority = LOG_WARNING;
2391 comment = tr(
"Unable to open file or init decoder");
2393 priority = LOG_WARNING;
2397 comment = tr(
"Failed with exit status %1").arg(breaksFound);
2399 priority = LOG_WARNING;
2403 comment = tr(
"%n commercial break(s)",
"", breaksFound);
2419 msg = tr(
"Commercial Detection %1",
"Job ID")
2422 if (!comment.isEmpty())
2424 detailstr += QString(
" (%1)").arg(comment);
2425 details = detailstr.toLocal8Bit();
2428 if (priority <= LOG_WARNING)
2429 LOG(VB_GENERAL, LOG_ERR,
LOC + msg +
": " + details.constData());
2463 msg = QString(
"Started %1 for %2 recorded from channel %3")
2469 msg = QString(
"Started %1 for jobID %2").arg(jobDesc).arg(
jobID);
2471 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(msg.toLocal8Bit().constData()));
2485 LOG(VB_JOBQUEUE, LOG_INFO,
LOC + QString(
"Running command: '%1'")
2487 GetMythDB()->GetDBManager()->CloseDatabases();
2493 msg = QString(
"User Job '%1' failed, unable to find " 2494 "executable, check your PATH and backend logs.")
2496 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2497 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Current PATH: '%1'")
2498 .arg(getenv(
"PATH")));
2501 tr(
"ERROR: Unable to find executable, check backend logs."));
2503 else if (result != 0)
2505 msg = QString(
"User Job '%1' failed.").arg(command);
2506 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
2509 tr(
"ERROR: User Job returned non-zero, check logs."));
2515 msg = QString(
"Finished %1 for %2 recorded from channel %3")
2521 msg = QString(
"Finished %1 for jobID %2").arg(jobDesc).arg(
jobID);
2523 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(msg.toLocal8Bit().constData()));
2540 while ((x != 0) && ((x & 0x01) == 0))
static void * TranscodeThread(void *param)
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
struct JobQueue::jobthreadstruct JobThreadStruct
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
static int UserJobTypeToIndex(int JobType)
QString GetJobCommand(int id, int jobType, ProgramInfo *tmpInfo)
void RemoveRunningJob(int id)
This is a wrapper around QThread that does several additional things.
void bindValue(const QString &placeholder, const QVariant &val)
static bool IsJobQueued(int jobType, uint chanid, const QDateTime &recstartts)
static bool ChangeJobCmds(int jobID, int newCmds)
int GetRunningJobID(uint chanid, const QDateTime &recstartts)
#define JOBSTATUS_STATUSTEXT(A, B, C)
static bool DeleteJob(int jobID)
static bool HasRunningOrPendingJobs(int startingWithinMins=0)
#define GENERIC_EXIT_DAEMONIZING_ERROR
Error daemonizing or execl.
void DoTranscodeThread(int jobID)
QString toString(MarkTypes type)
virtual void SubstituteMatches(QString &str)
Subsitute MATCH% type variable names in the given string.
static void * UserJobThread(void *param)
void SetPathname(const QString &) const
static bool IsJobStatusQueued(int status)
static Type MythEventMessage
void removeListener(QObject *listener)
Remove a listener to the observable.
static QString JobText(int jobType)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool IsBlockingClient(void) const
is this client blocking shutdown
void MarkAsInUse(bool inuse, QString usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
static QString PrettyPrint(off_t bytes)
bool myth_ioprio(int)
Allows setting the I/O priority of the current process/thread.
static bool IsJobStatusRunning(int status)
#define GENERIC_EXIT_RESTART
Need to restart transcoding.
QString toString(Verbosity v=kLongDescription, QString sep=":", QString grp="\"") const
Default UTC, "yyyyMMddhhmmss".
static bool ChangeJobArgs(int jobID, QString args="")
static bool ChangeJobStatus(int jobID, int newStatus, QString comment="")
static enum JobCmds GetJobCmd(int jobID)
Holds information on a TV Program one might wish to record.
static bool IsJobQueuedOrRunning(int jobType, uint chanid, const QDateTime &recstartts)
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
void SendUpdateEvent(void)
Sends event out that the ProgramInfo should be reloaded.
bool IsCommercialFree(void) const
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void addListener(QObject *listener)
Add a listener to the observable.
void customEvent(QEvent *e) override
bool AllowedToRun(JobQueueEntry job)
static bool ChangeJobHost(int jobID, QString newHostname)
QMap< int, RunningJobInfo > runningJobs
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
static int GetJobsInQueue(QMap< int, JobQueueEntry > &jobs, int findJobs=JOB_LIST_NOT_DONE)
This class creates a preview image of a recording.
unsigned sleep(unsigned int x)
static bool StopJob(int jobID)
static void * FlagCommercialsThread(void *param)
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
QVariant value(int i) const
static bool ResumeJob(int jobID)
static void RecoverQueue(bool justOld=false)
Holds information on recordings and videos.
virtual void SaveFilesize(uint64_t fsize)
Sets recording file size in database, and sets "filesize" field.
This class is used as a container for messages.
static bool QueueJob(int jobType, uint chanid, const QDateTime &recstartts, QString args="", QString comment="", QString host="", int flags=0, int status=JOB_QUEUED, QDateTime schedruntime=QDateTime())
static bool GetJobInfoFromID(int jobID, int &jobType, uint &chanid, QDateTime &recstartts)
allow exit values 0-127 only
#define GENERIC_EXIT_NO_RECORDING_DATA
No program/recording data.
static bool QueueRecordingJobs(const RecordingInfo &, int jobTypes=JOB_NONE)
uint QueryTranscoderID(void) const
QString GetAppBinDir(void)
static QMap< QString, int > JobNameToType
#define GENERIC_EXIT_CMD_NOT_FOUND
Command not found.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
QString GetSetting(const QString &key, const QString &defaultval="")
void DoUserJobThread(int jobID)
const char * kJobQueueInUseID
bool HasCutlist(void) const
static const uint TranscoderAutodetect
sentinel value
static QString StatusText(int status)
QString GetRecordingGroup(void) const
static bool ChangeJobComment(int jobID, QString comment="")
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
static void * MetadataLookupThread(void *param)
static void ThreadCleanup(void)
This is to be called on exit in those few threads that haven't been ported to MThread.
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
void SaveTranscodeStatus(TranscodingStatus trans)
Set "transcoded" field in "recorded" table to "trans".
uint myth_system(const QString &command, uint flags, uint timeout)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
void dispatch(const MythEvent &event)
static bool IsJobRunning(int jobType, uint chanid, const QDateTime &recstartts)
void ProcessJob(JobQueueEntry job)
int GetNumSetting(const QString &key, int defaultval=0)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
#define LOG(_MASK_, _LEVEL_, _STRING_)
bool GetBoolSetting(const QString &key, bool defaultval=false)
QWaitCondition queueThreadCond
static bool PauseJob(int jobID)
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
QString GetJobDescription(int jobType)
static int GetJobTypeFromName(const QString &name)
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false) const
Returns filename or URL to be used to play back this recording.
static void CleanupOldJobsInQueue()
static enum JobFlags GetJobFlags(int jobID)
static void ThreadSetup(const QString &)
This is to be called on startup in those few threads that haven't been ported to MThread.
static QString GetJobArgs(int jobID)
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
static bool DeleteAllJobs(uint chanid, const QDateTime &recstartts)
QMutex queueThreadCondLock
int numRowsAffected() const
void StartChildJob(void *(*start_routine)(void *), int jobID)
void DoFlagCommercialsThread(int jobID)
QString GetHostname(void) const
#define MYTH_APPNAME_MYTHJOBQUEUE
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
static long int random(void)
void DoMetadataLookupThread(int jobID)
static void DBError(const QString &where, const MSqlQuery &query)
const QString & Message() const
static enum JobStatus GetJobStatus(int jobID)
static int GetJobID(int jobType, uint chanid, const QDateTime &recstartts)
#define GENERIC_EXIT_NOT_OK
Exited with error.
static bool InJobRunWindow(QDateTime jobstarttsRaw)
QString GetHostName(void)
static bool QueueJobs(int jobTypes, uint chanid, const QDateTime &recstartts, QString args="", QString comment="", QString host="")
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
static bool SafeDeleteJob(int jobID, int jobType, int chanid, QDateTime recstartts)
static bool RestartJob(int jobID)
static bool ChangeJobFlags(int jobID, int newFlags)