MythTV  master
mythtv/programs/mythcommflag/main.cpp
Go to the documentation of this file.
1 // POSIX headers
2 #include <unistd.h>
3 #include <sys/time.h> // for gettimeofday
4 
5 // ANSI C headers
6 #include <cstdlib>
7 #include <cstdio>
8 #include <ctime>
9 #include <cmath>
10 
11 // C++ headers
12 #include <string>
13 #include <iostream>
14 #include <fstream>
15 using namespace std;
16 
17 // Qt headers
18 #include <QCoreApplication>
19 #include <QString>
20 #include <QRegExp>
21 #include <QDir>
22 #include <QEvent>
23 
24 // MythTV headers
25 #include "mythmiscutil.h"
26 #include "mythdate.h"
27 #include "exitcodes.h"
28 #include "mythcontext.h"
29 #include "mythdb.h"
30 #include "mythversion.h"
31 #include "mythcommflagplayer.h"
32 #include "programinfo.h"
33 #include "remoteutil.h"
34 #include "remotefile.h"
35 #include "tvremoteutil.h"
36 #include "jobqueue.h"
37 #include "remoteencoder.h"
38 #include "io/mythmediabuffer.h"
39 #include "commandlineparser.h"
40 #include "mythtranslation.h"
41 #include "loggingserver.h"
42 #include "mythlogging.h"
43 #include "signalhandling.h"
44 #include "cleanupguard.h"
45 
46 // Commercial Flagging headers
47 #include "CommDetectorBase.h"
48 #include "CommDetectorFactory.h"
49 #include "SlotRelayer.h"
50 #include "CustomEventRelayer.h"
51 
52 #define LOC QString("MythCommFlag: ")
53 #define LOC_WARN QString("MythCommFlag, Warning: ")
54 #define LOC_ERR QString("MythCommFlag, Error: ")
55 
56 namespace
57 {
58  void cleanup()
59  {
60  delete gContext;
61  gContext = nullptr;
63  }
64 }
65 
66 int quiet = 0;
67 bool progress = true;
68 bool force = false;
69 
71 
72 bool watchingRecording = false;
76 int recorderNum = -1;
77 
78 int jobID = -1;
79 int lastCmd = -1;
80 
81 static QMap<QString,SkipType> *init_skip_types();
82 QMap<QString,SkipType> *skipTypes = init_skip_types();
83 
84 static QMap<QString,SkipType> *init_skip_types(void)
85 {
86  auto *tmp = new QMap<QString,SkipType>;
87  (*tmp)["commfree"] = COMM_DETECT_COMMFREE;
88  (*tmp)["uninit"] = COMM_DETECT_UNINIT;
89  (*tmp)["off"] = COMM_DETECT_OFF;
90  (*tmp)["blank"] = COMM_DETECT_BLANKS;
91  (*tmp)["blanks"] = COMM_DETECT_BLANKS;
92  (*tmp)["scene"] = COMM_DETECT_SCENE;
93  (*tmp)["blankscene"] = COMM_DETECT_BLANK_SCENE;
94  (*tmp)["blank_scene"] = COMM_DETECT_BLANK_SCENE;
95  (*tmp)["logo"] = COMM_DETECT_LOGO;
96  (*tmp)["all"] = COMM_DETECT_ALL;
97  (*tmp)["d2"] = COMM_DETECT_2;
98  (*tmp)["d2_logo"] = COMM_DETECT_2_LOGO;
99  (*tmp)["d2_blank"] = COMM_DETECT_2_BLANK;
100  (*tmp)["d2_scene"] = COMM_DETECT_2_SCENE;
101  (*tmp)["d2_all"] = COMM_DETECT_2_ALL;
102  return tmp;
103 }
104 
106 {
109 };
111 
112 static QMap<QString,OutputMethod> *init_output_types();
113 QMap<QString,OutputMethod> *outputTypes = init_output_types();
114 
115 static QMap<QString,OutputMethod> *init_output_types(void)
116 {
117  auto *tmp = new QMap<QString,OutputMethod>;
118  (*tmp)["essentials"] = kOutputMethodEssentials;
119  (*tmp)["full"] = kOutputMethodFull;
120  return tmp;
121 }
122 
123 static QString get_filename(ProgramInfo *program_info)
124 {
125  QString filename = program_info->GetPathname();
126  if (!QFile::exists(filename))
127  filename = program_info->GetPlaybackURL(true);
128  return filename;
129 }
130 
131 static int QueueCommFlagJob(uint chanid, const QDateTime& starttime, bool rebuild)
132 {
133  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
134  const ProgramInfo pginfo(chanid, starttime);
135 
136  if (!pginfo.GetChanID())
137  {
138  if (progress)
139  {
140  QString tmp = QString(
141  "Unable to find program info for chanid %1 @ %2")
142  .arg(chanid).arg(startstring);
143  cerr << tmp.toLocal8Bit().constData() << endl;
144  }
146  }
147 
148  if (cmdline.toBool("dryrun"))
149  {
150  QString tmp = QString("Job have been queued for chanid %1 @ %2")
151  .arg(chanid).arg(startstring);
152  cerr << tmp.toLocal8Bit().constData() << endl;
153  return GENERIC_EXIT_OK;
154  }
155 
156  bool result = JobQueue::QueueJob(JOB_COMMFLAG,
157  pginfo.GetChanID(), pginfo.GetRecordingStartTime(), "", "", "",
158  rebuild ? JOB_REBUILD : 0, JOB_QUEUED, QDateTime());
159 
160  if (result)
161  {
162  if (progress)
163  {
164  QString tmp = QString("Job Queued for chanid %1 @ %2")
165  .arg(chanid).arg(startstring);
166  cerr << tmp.toLocal8Bit().constData() << endl;
167  }
168  return GENERIC_EXIT_OK;
169  }
170 
171  if (progress)
172  {
173  QString tmp = QString("Error queueing job for chanid %1 @ %2")
174  .arg(chanid).arg(startstring);
175  cerr << tmp.toLocal8Bit().constData() << endl;
176  }
177  return GENERIC_EXIT_DB_ERROR;
178 }
179 
180 static int CopySkipListToCutList(uint chanid, const QDateTime& starttime)
181 {
182  frm_dir_map_t cutlist;
183  frm_dir_map_t::const_iterator it;
184 
185  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
186  const ProgramInfo pginfo(chanid, starttime);
187 
188  if (!pginfo.GetChanID())
189  {
190  LOG(VB_GENERAL, LOG_ERR,
191  QString("No program data exists for channel %1 at %2")
192  .arg(chanid).arg(startstring));
194  }
195 
196  pginfo.QueryCommBreakList(cutlist);
197  for (it = cutlist.begin(); it != cutlist.end(); ++it)
198  {
199  if (*it == MARK_COMM_START)
200  cutlist[it.key()] = MARK_CUT_START;
201  else
202  cutlist[it.key()] = MARK_CUT_END;
203  }
204  pginfo.SaveCutList(cutlist);
205 
206  return GENERIC_EXIT_OK;
207 }
208 
209 static int ClearSkipList(uint chanid, const QDateTime& starttime)
210 {
211  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
212  const ProgramInfo pginfo(chanid, starttime);
213 
214  if (!pginfo.GetChanID())
215  {
216  LOG(VB_GENERAL, LOG_ERR,
217  QString("No program data exists for channel %1 at %2")
218  .arg(chanid).arg(startstring));
220  }
221 
222  frm_dir_map_t skiplist;
223  pginfo.SaveCommBreakList(skiplist);
224 
225  LOG(VB_GENERAL, LOG_NOTICE, "Commercial skip list cleared");
226 
227  return GENERIC_EXIT_OK;
228 }
229 
230 static int SetCutList(uint chanid, const QDateTime& starttime, QString newCutList)
231 {
232  frm_dir_map_t cutlist;
233 
234  newCutList.replace(QRegExp(" "), "");
235 
236  QStringList tokens = newCutList.split(",", QString::SkipEmptyParts);
237 
238  for (int i = 0; i < tokens.size(); i++)
239  {
240  QStringList cutpair = tokens[i].split("-", QString::SkipEmptyParts);
241  cutlist[cutpair[0].toInt()] = MARK_CUT_START;
242  cutlist[cutpair[1].toInt()] = MARK_CUT_END;
243  }
244 
245  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
246  const ProgramInfo pginfo(chanid, starttime);
247 
248  if (!pginfo.GetChanID())
249  {
250  LOG(VB_GENERAL, LOG_ERR,
251  QString("No program data exists for channel %1 at %2")
252  .arg(chanid).arg(startstring));
254  }
255 
256  pginfo.SaveCutList(cutlist);
257 
258  LOG(VB_GENERAL, LOG_NOTICE, QString("Cutlist set to: %1").arg(newCutList));
259 
260  return GENERIC_EXIT_OK;
261 }
262 
263 static int GetMarkupList(const QString& list, uint chanid, const QDateTime& starttime)
264 {
265  frm_dir_map_t cutlist;
266  frm_dir_map_t::const_iterator it;
267  QString result;
268 
269  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
270  const ProgramInfo pginfo(chanid, starttime);
271 
272  if (!pginfo.GetChanID())
273  {
274  LOG(VB_GENERAL, LOG_ERR,
275  QString("No program data exists for channel %1 at %2")
276  .arg(chanid).arg(startstring));
278  }
279 
280  if (list == "cutlist")
281  pginfo.QueryCutList(cutlist);
282  else
283  pginfo.QueryCommBreakList(cutlist);
284 
285  uint64_t lastStart = 0;
286  for (it = cutlist.begin(); it != cutlist.end(); ++it)
287  {
288  if ((*it == MARK_COMM_START) ||
289  (*it == MARK_CUT_START))
290  {
291  if (!result.isEmpty())
292  result += ",";
293  lastStart = it.key();
294  result += QString("%1-").arg(lastStart);
295  }
296  else
297  {
298  if (result.isEmpty())
299  result += "0-";
300  result += QString("%1").arg(it.key());
301  }
302  }
303 
304  if (result.endsWith('-'))
305  {
306  uint64_t lastFrame = pginfo.QueryLastFrameInPosMap() + 60;
307  if (lastFrame > lastStart)
308  result += QString("%1").arg(lastFrame);
309  }
310 
311  if (list == "cutlist")
312  cout << QString("Cutlist: %1\n").arg(result).toLocal8Bit().constData();
313  else
314  {
315  cout << QString("Commercial Skip List: %1\n")
316  .arg(result).toLocal8Bit().constData();
317  }
318 
319  return GENERIC_EXIT_OK;
320 }
321 
323  ostream &output, const frm_dir_map_t &commercialBreakList)
324 {
325  if (progress)
326  output << "----------------------------" << endl;
327 
328  if (commercialBreakList.empty())
329  {
330  if (progress)
331  output << "No breaks" << endl;
332  }
333  else
334  {
335  frm_dir_map_t::const_iterator it = commercialBreakList.begin();
336  for (; it != commercialBreakList.end(); ++it)
337  {
338  output << "framenum: " << it.key() << "\tmarktype: " << *it
339  << endl;
340  }
341  }
342 
343  if (progress)
344  output << "----------------------------" << endl;
345 }
346 
348  const ProgramInfo *program_info,
349  const frm_dir_map_t &commBreakList,
350  uint64_t frame_count,
351  const CommDetectorBase *commDetect,
352  const QString &output_filename)
353 {
354  if (output_filename.isEmpty())
355  return;
356 
357  ostream *out = &cout;
358  if (output_filename != "-")
359  {
360  QByteArray tmp = output_filename.toLocal8Bit();
361  out = new fstream(tmp.constData(), ios::app | ios::out );
362  }
363 
364  if (progress)
365  {
366  QString tmp = "";
367  if (program_info->GetChanID())
368  {
369  tmp = QString("commercialBreakListFor: %1 on %2 @ %3")
370  .arg(program_info->GetTitle())
371  .arg(program_info->GetChanID())
372  .arg(program_info->GetRecordingStartTime(MythDate::ISODate));
373  }
374  else
375  {
376  tmp = QString("commercialBreakListFor: %1")
377  .arg(program_info->GetPathname());
378  }
379 
380  const QByteArray tmp2 = tmp.toLocal8Bit();
381  *out << tmp2.constData() << endl;
382 
383  if (frame_count)
384  *out << "totalframecount: " << frame_count << endl;
385  }
386 
387  if (commDetect)
388  commDetect->PrintFullMap(*out, &commBreakList, progress);
389  else
390  streamOutCommercialBreakList(*out, commBreakList);
391 
392  if (out != &cout)
393  delete out;
394 }
395 
396 static void commDetectorBreathe()
397 {
398  //this is connected to the commdetectors breathe signal so we get a chance
399  //while its busy to see if the user already told us to stop.
400  qApp->processEvents();
401 
402  if (jobID != -1)
403  {
404  int curCmd = JobQueue::GetJobCmd(jobID);
405  if (curCmd == lastCmd)
406  return;
407 
408  switch (curCmd)
409  {
410  case JOB_STOP:
411  {
412  commDetector->stop();
413  break;
414  }
415  case JOB_PAUSE:
416  {
417  JobQueue::ChangeJobStatus(jobID, JOB_PAUSED,
418  QCoreApplication::translate("(mythcommflag)",
419  "Paused", "Job status"));
420  commDetector->pause();
421  break;
422  }
423  case JOB_RESUME:
424  {
425  JobQueue::ChangeJobStatus(jobID, JOB_RUNNING,
426  QCoreApplication::translate("(mythcommflag)",
427  "Running", "Job status"));
428  commDetector->resume();
429  break;
430  }
431  }
432  }
433 }
434 
435 static void commDetectorStatusUpdate(const QString& status)
436 {
437  if (jobID != -1)
438  {
439  JobQueue::ChangeJobStatus(jobID, JOB_RUNNING, status);
441  }
442 }
443 
445 {
446  frm_dir_map_t newCommercialMap;
447  commDetector->GetCommercialBreakList(newCommercialMap);
448 
449  frm_dir_map_t::Iterator it = newCommercialMap.begin();
450  QString message = "COMMFLAG_UPDATE ";
451  message += global_program_info->MakeUniqueKey();
452 
453  for (it = newCommercialMap.begin();
454  it != newCommercialMap.end(); ++it)
455  {
456  if (it != newCommercialMap.begin())
457  message += ",";
458  else
459  message += " ";
460  message += QString("%1:%2").arg(it.key())
461  .arg(*it);
462  }
463 
464  LOG(VB_COMMFLAG, LOG_INFO,
465  QString("mythcommflag sending update: %1").arg(message));
466 
467  gCoreContext->SendMessage(message);
468 }
469 
470 static void incomingCustomEvent(QEvent* e)
471 {
472  if (e->type() == MythEvent::MythEventMessage)
473  {
474  auto *me = dynamic_cast<MythEvent *>(e);
475  if (me == nullptr)
476  return;
477 
478  QString message = me->Message();
479 
480  message = message.simplified();
481  QStringList tokens = message.split(" ", QString::SkipEmptyParts);
482 
483  LOG(VB_COMMFLAG, LOG_INFO,
484  QString("mythcommflag: Received Event: '%1'") .arg(message));
485 
486  if ((watchingRecording) && (tokens.size() >= 3) &&
487  (tokens[0] == "DONE_RECORDING"))
488  {
489  int cardnum = tokens[1].toInt();
490  int filelen = tokens[2].toInt();
491 
492  message = QString("mythcommflag: Received a "
493  "DONE_RECORDING event for card %1. ")
494  .arg(cardnum);
495 
496  if (recorderNum != -1 && cardnum == recorderNum)
497  {
499  watchingRecording = false;
500  message += "Informed CommDetector that recording has finished.";
501  LOG(VB_COMMFLAG, LOG_INFO, message);
502  }
503  }
504 
505  if ((tokens.size() >= 2) && (tokens[0] == "COMMFLAG_REQUEST"))
506  {
507  uint chanid = 0;
508  QDateTime recstartts;
509  ProgramInfo::ExtractKey(tokens[1], chanid, recstartts);
510 
511  message = QString("mythcommflag: Received a "
512  "COMMFLAG_REQUEST event for chanid %1 @ %2. ")
513  .arg(chanid).arg(recstartts.toString(Qt::ISODate));
514 
515  if ((global_program_info->GetChanID() == chanid) &&
516  (global_program_info->GetRecordingStartTime() == recstartts))
517  {
519  message += "Requested CommDetector to generate new break list.";
520  LOG(VB_COMMFLAG, LOG_INFO, message);
521  }
522  }
523  }
524 }
525 
526 static int DoFlagCommercials(
527  ProgramInfo *program_info,
528  bool showPercentage, bool fullSpeed, int jobid,
529  MythCommFlagPlayer* cfp, SkipType commDetectMethod,
530  const QString &outputfilename, bool useDB)
531 {
533  commDetectMethod, showPercentage,
534  fullSpeed, cfp,
535  program_info->GetChanID(),
536  program_info->GetScheduledStartTime(),
537  program_info->GetScheduledEndTime(),
538  program_info->GetRecordingStartTime(),
539  program_info->GetRecordingEndTime(), useDB);
540 
541  if (jobid > 0)
542  LOG(VB_COMMFLAG, LOG_INFO,
543  QString("mythcommflag processing JobID %1").arg(jobid));
544 
545  if (useDB)
546  program_info->SaveCommFlagged(COMM_FLAG_PROCESSING);
547 
548  auto *cer = new CustomEventRelayer(incomingCustomEvent);
549  auto *a = new SlotRelayer(commDetectorBreathe);
550  auto *b = new SlotRelayer(commDetectorStatusUpdate);
552  QObject::connect(commDetector, SIGNAL(breathe()),
553  a, SLOT(relay()));
554  QObject::connect(commDetector, SIGNAL(statusUpdate(const QString&)),
555  b, SLOT(relay(const QString&)));
556  QObject::connect(commDetector, SIGNAL(gotNewCommercialBreakList()),
557  c, SLOT(relay()));
558 
559  if (useDB)
560  {
561  LOG(VB_COMMFLAG, LOG_INFO,
562  "mythcommflag sending COMMFLAG_START notification");
563  QString message = "COMMFLAG_START ";
564  message += program_info->MakeUniqueKey();
565  gCoreContext->SendMessage(message);
566  }
567 
568  bool result = commDetector->go();
569  int comms_found = 0;
570 
571  if (result)
572  {
573  cfp->SaveTotalDuration();
574 
575  frm_dir_map_t commBreakList;
576  commDetector->GetCommercialBreakList(commBreakList);
577  comms_found = commBreakList.size() / 2;
578 
579  if (useDB)
580  {
581  program_info->SaveMarkupFlag(MARK_UPDATED_CUT);
582  program_info->SaveCommBreakList(commBreakList);
583  program_info->SaveCommFlagged(COMM_FLAG_DONE);
584  }
585 
587  program_info, commBreakList, cfp->GetTotalFrameCount(),
589  outputfilename);
590  }
591  else
592  {
593  if (useDB)
594  program_info->SaveCommFlagged(COMM_FLAG_NOT_FLAGGED);
595  }
596 
598  commDetector = nullptr;
599  sleep(1);
600  tmp->deleteLater();
601 
602  cer->deleteLater();
603  c->deleteLater();
604  b->deleteLater();
605  a->deleteLater();
606 
607  return comms_found;
608 }
609 
610 static qint64 GetFileSize(ProgramInfo *program_info)
611 {
612  QString filename = get_filename(program_info);
613  qint64 size = -1;
614 
615  if (filename.startsWith("myth://"))
616  {
617  RemoteFile remotefile(filename, false, false, 0);
618  size = remotefile.GetFileSize();
619  }
620  else
621  {
622  QFile file(filename);
623  if (file.exists())
624  {
625  size = file.size();
626  }
627  }
628 
629  return size;
630 }
631 
632 static bool DoesFileExist(ProgramInfo *program_info)
633 {
634  QString filename = get_filename(program_info);
635  qint64 size = GetFileSize(program_info);
636 
637  if (size < 0)
638  {
639  LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find file %1, aborting.")
640  .arg(filename));
641  return false;
642  }
643 
644  if (size == 0)
645  {
646  LOG(VB_GENERAL, LOG_ERR, QString("File %1 is zero-byte, aborting.")
647  .arg(filename));
648  return false;
649  }
650 
651  return true;
652 }
653 
654 static void UpdateFileSize(ProgramInfo *program_info)
655 {
656  qint64 size = GetFileSize(program_info);
657 
658  if (size != (qint64)program_info->GetFilesize())
659  program_info->SaveFilesize(size);
660 }
661 
662 static bool IsMarked(uint chanid, const QDateTime& starttime)
663 {
664  MSqlQuery mark_query(MSqlQuery::InitCon());
665  mark_query.prepare("SELECT commflagged, count(rm.type) "
666  "FROM recorded r "
667  "LEFT JOIN recordedmarkup rm ON "
668  "( r.chanid = rm.chanid AND "
669  "r.starttime = rm.starttime AND "
670  "type in (:MARK_START,:MARK_END)) "
671  "WHERE r.chanid = :CHANID AND "
672  "r.starttime = :STARTTIME "
673  "GROUP BY COMMFLAGGED;");
674  mark_query.bindValue(":MARK_START", MARK_COMM_START);
675  mark_query.bindValue(":MARK_END", MARK_COMM_END);
676  mark_query.bindValue(":CHANID", chanid);
677  mark_query.bindValue(":STARTTIME", starttime);
678 
679  if (mark_query.exec() && mark_query.isActive() &&
680  mark_query.size() > 0)
681  {
682  if (mark_query.next())
683  {
684  int flagStatus = mark_query.value(0).toInt();
685  int marksFound = mark_query.value(1).toInt();
686 
687  QString flagStatusStr = "UNKNOWN";
688  switch (flagStatus) {
690  flagStatusStr = "Not Flagged";
691  break;
692  case COMM_FLAG_DONE:
693  flagStatusStr = QString("Flagged with %1 breaks")
694  .arg(marksFound / 2);
695  break;
697  flagStatusStr = "Flagging";
698  break;
699  case COMM_FLAG_COMMFREE:
700  flagStatusStr = "Commercial Free";
701  break;
702  }
703 
704  LOG(VB_COMMFLAG, LOG_INFO,
705  QString("Status for chanid %1 @ %2 is '%3'")
706  .arg(chanid).arg(starttime.toString(Qt::ISODate))
707  .arg(flagStatusStr));
708 
709  if ((flagStatus == COMM_FLAG_NOT_FLAGGED) && (marksFound == 0))
710  return false;
711  }
712  }
713  return true;
714 }
715 
716 static int FlagCommercials(ProgramInfo *program_info, int jobid,
717  const QString &outputfilename, bool useDB, bool fullSpeed)
718 {
719  global_program_info = program_info;
720 
721  int breaksFound = 0;
722 
723  // configure commercial detection method
724  SkipType commDetectMethod = (SkipType)gCoreContext->GetNumSetting(
725  "CommercialSkipMethod", COMM_DETECT_ALL);
726 
727  if (cmdline.toBool("commmethod"))
728  {
729  // pull commercial detection method from command line
730  QString commmethod = cmdline.toString("commmethod");
731 
732  // assume definition as integer value
733  bool ok = true;
734  commDetectMethod = (SkipType) commmethod.toInt(&ok);
735  if (!ok)
736  {
737  // not an integer, attempt comma separated list
738  commDetectMethod = COMM_DETECT_UNINIT;
739 
740  QStringList list = commmethod.split(",", QString::SkipEmptyParts);
741  for (const auto & it : qAsConst(list))
742  {
743  QString val = it.toLower();
744  if (val == "off")
745  {
746  commDetectMethod = COMM_DETECT_OFF;
747  break;
748  }
749 
750  if (!skipTypes->contains(val))
751  {
752  cerr << "Failed to decode --method option '"
753  << val.toLatin1().constData()
754  << "'" << endl;
756  }
757 
758  if (commDetectMethod == COMM_DETECT_UNINIT) {
759  commDetectMethod = skipTypes->value(val);
760  } else {
761  commDetectMethod = (SkipType) ((int)commDetectMethod
762  | (int)skipTypes->value(val));
763  }
764  }
765 
766  }
767  if (commDetectMethod == COMM_DETECT_UNINIT)
769  }
770  else if (useDB)
771  {
772  // if not manually specified, and we have a database to access
773  // pull the commflag type from the channel
775  query.prepare("SELECT commmethod FROM channel "
776  "WHERE chanid = :CHANID;");
777  query.bindValue(":CHANID", program_info->GetChanID());
778 
779  if (!query.exec())
780  {
781  // if the query fails, return with an error
782  commDetectMethod = COMM_DETECT_UNINIT;
783  MythDB::DBError("FlagCommercials", query);
784  }
785  else if (query.next())
786  {
787  commDetectMethod = (SkipType)query.value(0).toInt();
788  if (commDetectMethod == COMM_DETECT_COMMFREE)
789  {
790  // if the channel is commercial free, drop to the default instead
791  commDetectMethod = (SkipType)gCoreContext->GetNumSetting(
792  "CommercialSkipMethod", COMM_DETECT_ALL);
793  LOG(VB_COMMFLAG, LOG_INFO,
794  QString("Chanid %1 is marked as being Commercial Free, "
795  "we will use the default commercial detection "
796  "method").arg(program_info->GetChanID()));
797  }
798  else if (commDetectMethod == COMM_DETECT_UNINIT)
799  {
800  // no value set, so use the database default
801  commDetectMethod = (SkipType)gCoreContext->GetNumSetting(
802  "CommercialSkipMethod", COMM_DETECT_ALL);
803  }
804  LOG(VB_COMMFLAG, LOG_INFO,
805  QString("Using method: %1 from channel %2")
806  .arg(commDetectMethod).arg(program_info->GetChanID()));
807  }
808 
809  }
810  else if (!useDB)
811  {
812  // default to a cheaper method for debugging purposes
813  commDetectMethod = COMM_DETECT_BLANK;
814  }
815 
816  // if selection has failed, or intentionally disabled, drop out
817  if (commDetectMethod == COMM_DETECT_UNINIT)
818  return GENERIC_EXIT_NOT_OK;
819  if (commDetectMethod == COMM_DETECT_OFF)
820  return GENERIC_EXIT_OK;
821 
822  frm_dir_map_t blanks;
823  recorder = nullptr;
824 
825 /*
826  * is there a purpose to this not fulfilled by --getskiplist?
827  if (onlyDumpDBCommercialBreakList)
828  {
829  frm_dir_map_t commBreakList;
830  program_info->QueryCommBreakList(commBreakList);
831 
832  print_comm_flag_output(program_info, commBreakList,
833  0, nullptr, outputfilename);
834 
835  global_program_info = nullptr;
836  return GENERIC_EXIT_OK;
837  }
838 */
839 
840  if (!DoesFileExist(program_info))
841  {
842  LOG(VB_GENERAL, LOG_ERR,
843  "Unable to find file in defined storage paths.");
845  }
846 
847  QString filename = get_filename(program_info);
848 
850  if (!tmprbuf)
851  {
852  LOG(VB_GENERAL, LOG_ERR,
853  QString("Unable to create RingBuffer for %1").arg(filename));
854  global_program_info = nullptr;
856  }
857 
858  if (useDB)
859  {
861  {
862  LOG(VB_GENERAL, LOG_ERR, "Unable to open commflag DB connection");
863  delete tmprbuf;
864  global_program_info = nullptr;
865  return GENERIC_EXIT_DB_ERROR;
866  }
867  }
868 
869  auto flags = static_cast<PlayerFlags>(kAudioMuted | kVideoIsNull | kNoITV);
870 
871  int flagfast = gCoreContext->GetNumSetting("CommFlagFast", 0);
872  if (flagfast)
873  {
874  // Note: These additional flags replicate the intent of the original
875  // commit that enabled lowres support - but I'm not sure why it requires
876  // single threaded decoding - which surely slows everything down? Though
877  // there is probably no profile to enable multi-threaded decoding anyway.
878  LOG(VB_GENERAL, LOG_INFO, "Enabling experimental flagging speedup (low resolution)");
879  flags = static_cast<PlayerFlags>(flags | kDecodeLowRes | kDecodeSingleThreaded | kDecodeNoLoopFilter);
880  }
881 
882  // blank detector needs to be only sample center for this optimization.
883  if (flagfast && ((COMM_DETECT_BLANKS == commDetectMethod) ||
884  (COMM_DETECT_2_BLANK == commDetectMethod)))
885  {
886  flags = static_cast<PlayerFlags>(flags | kDecodeFewBlocks);
887  }
888 
889  auto *cfp = new MythCommFlagPlayer(flags);
890  auto *ctx = new PlayerContext(kFlaggerInUseID);
891  ctx->SetPlayingInfo(program_info);
892  ctx->SetRingBuffer(tmprbuf);
893  ctx->SetPlayer(cfp);
894  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
895 
896  if (useDB)
897  {
898  if (program_info->GetRecordingEndTime() > MythDate::current())
899  {
901 
902  recorder = RemoteGetExistingRecorder(program_info);
903  if (recorder && (recorder->GetRecorderNumber() != -1))
904  {
906  watchingRecording = true;
907  ctx->SetRecorder(recorder);
908 
909  LOG(VB_COMMFLAG, LOG_INFO,
910  QString("mythcommflag will flag recording "
911  "currently in progress on cardid %1")
912  .arg(recorderNum));
913  }
914  else
915  {
916  recorderNum = -1;
917  watchingRecording = false;
918 
919  LOG(VB_GENERAL, LOG_ERR,
920  "Unable to find active recorder for this "
921  "recording, realtime flagging will not be enabled.");
922  }
923  cfp->SetWatchingRecording(watchingRecording);
924  }
925  }
926 
927  // TODO: Add back insertion of job if not in jobqueue
928 
929  breaksFound = DoFlagCommercials(
930  program_info, progress, fullSpeed, jobid,
931  cfp, commDetectMethod, outputfilename, useDB);
932 
933  if (progress)
934  cerr << breaksFound << "\n";
935 
936  LOG(VB_GENERAL, LOG_NOTICE, QString("Finished, %1 break(s) found.")
937  .arg(breaksFound));
938 
939  delete ctx;
940  global_program_info = nullptr;
941 
942  return breaksFound;
943 }
944 
945 static int FlagCommercials( uint chanid, const QDateTime &starttime,
946  int jobid, const QString &outputfilename,
947  bool fullSpeed )
948 {
949  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
950  ProgramInfo pginfo(chanid, starttime);
951 
952  if (!pginfo.GetChanID())
953  {
954  LOG(VB_GENERAL, LOG_ERR,
955  QString("No program data exists for channel %1 at %2")
956  .arg(chanid).arg(startstring));
958  }
959 
960  if (!force && JobQueue::IsJobRunning(JOB_COMMFLAG, pginfo))
961  {
962  if (progress)
963  {
964  cerr << "IN USE\n";
965  cerr << " "
966  "(the program is already being flagged elsewhere)\n";
967  }
968  LOG(VB_GENERAL, LOG_ERR, "Program is already being flagged elsewhere");
969  return GENERIC_EXIT_IN_USE;
970  }
971 
972 
973  if (progress)
974  {
975  cerr << "MythTV Commercial Flagger, flagging commercials for:" << endl;
976  if (pginfo.GetSubtitle().isEmpty())
977  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << endl;
978  else
979  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << " - "
980  << pginfo.GetSubtitle().toLocal8Bit().constData() << endl;
981  }
982 
983  return FlagCommercials(&pginfo, jobid, outputfilename, true, fullSpeed);
984 }
985 
986 static int FlagCommercials(const QString& filename, int jobid,
987  const QString &outputfilename, bool useDB,
988  bool fullSpeed)
989 {
990 
991  if (progress)
992  {
993  cerr << "MythTV Commercial Flagger, flagging commercials for:" << endl
994  << " " << filename.toLatin1().constData() << endl;
995  }
996 
997  ProgramInfo pginfo(filename);
998  return FlagCommercials(&pginfo, jobid, outputfilename, useDB, fullSpeed);
999 }
1000 
1001 static int RebuildSeekTable(ProgramInfo *pginfo, int jobid, bool writefile = false)
1002 {
1003  QString filename = get_filename(pginfo);
1004 
1005  if (!DoesFileExist(pginfo))
1006  {
1007  // file not found on local filesystem
1008  // assume file is in Video storage group on local backend
1009  // and try again
1010 
1011  filename = QString("myth://Videos@%1/%2")
1012  .arg(gCoreContext->GetHostName()).arg(filename);
1013  pginfo->SetPathname(filename);
1014  if (!DoesFileExist(pginfo))
1015  {
1016  LOG(VB_GENERAL, LOG_ERR,
1017  QString("Unable to find file in defined storage "
1018  "paths for JobQueue ID# %1.").arg(jobid));
1020  }
1021  }
1022 
1023  // Update the file size since mythcommflag --rebuild is often used in user
1024  // scripts after transcoding or other size-changing operations
1025  UpdateFileSize(pginfo);
1026 
1028  if (!tmprbuf)
1029  {
1030  LOG(VB_GENERAL, LOG_ERR,
1031  QString("Unable to create RingBuffer for %1").arg(filename));
1033  }
1034 
1036  kDecodeNoDecode | kNoITV));
1037  auto *ctx = new PlayerContext(kFlaggerInUseID);
1038  ctx->SetPlayingInfo(pginfo);
1039  ctx->SetRingBuffer(tmprbuf);
1040  ctx->SetPlayer(cfp);
1041  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
1042 
1043  if (progress)
1044  {
1045  QString time = QDateTime::currentDateTime().toString(Qt::TextDate);
1046  cerr << "Rebuild started at " << qPrintable(time) << endl;
1047  }
1048 
1049  if (writefile)
1051  cfp->RebuildSeekTable(progress);
1052  if (writefile)
1054 
1055  if (progress)
1056  {
1057  QString time = QDateTime::currentDateTime().toString(Qt::TextDate);
1058  cerr << "Rebuild completed at " << qPrintable(time) << endl;
1059  }
1060 
1061  delete ctx;
1062 
1063  return GENERIC_EXIT_OK;
1064 }
1065 
1066 static int RebuildSeekTable(const QString& filename, int jobid, bool writefile = false)
1067 {
1068  if (progress)
1069  {
1070  cerr << "MythTV Commercial Flagger, building seek table for:" << endl
1071  << " " << filename.toLatin1().constData() << endl;
1072  }
1073  ProgramInfo pginfo(filename);
1074  return RebuildSeekTable(&pginfo, jobid, writefile);
1075 }
1076 
1077 static int RebuildSeekTable(uint chanid, const QDateTime& starttime, int jobid, bool writefile = false)
1078 {
1079  ProgramInfo pginfo(chanid, starttime);
1080  if (progress)
1081  {
1082  cerr << "MythTV Commercial Flagger, building seek table for:" << endl;
1083  if (pginfo.GetSubtitle().isEmpty())
1084  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << endl;
1085  else
1086  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << " - "
1087  << pginfo.GetSubtitle().toLocal8Bit().constData() << endl;
1088  }
1089  return RebuildSeekTable(&pginfo, jobid, writefile);
1090 }
1091 
1092 int main(int argc, char *argv[])
1093 {
1094  int result = GENERIC_EXIT_OK;
1095 
1096 // QString allStart = "19700101000000";
1097 // QString allEnd = MythDate::current().toString("yyyyMMddhhmmss");
1098  int jobType = JOB_NONE;
1099 
1100  if (!cmdline.Parse(argc, argv))
1101  {
1102  cmdline.PrintHelp();
1104  }
1105 
1106  if (cmdline.toBool("showhelp"))
1107  {
1108  cmdline.PrintHelp();
1109  return GENERIC_EXIT_OK;
1110  }
1111 
1112  if (cmdline.toBool("showversion"))
1113  {
1115  return GENERIC_EXIT_OK;
1116  }
1117 
1118  QCoreApplication a(argc, argv);
1119  QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHCOMMFLAG);
1120  int retval = cmdline.ConfigureLogging("general",
1121  !cmdline.toBool("noprogress"));
1122  if (retval != GENERIC_EXIT_OK)
1123  return retval;
1124 
1125  CleanupGuard callCleanup(cleanup);
1126 
1127 #ifndef _WIN32
1128  QList<int> signallist;
1129  signallist << SIGINT << SIGTERM << SIGSEGV << SIGABRT << SIGBUS << SIGFPE
1130  << SIGILL;
1131 #if ! CONFIG_DARWIN
1132  signallist << SIGRTMIN;
1133 #endif
1134  SignalHandler::Init(signallist);
1135  SignalHandler::SetHandler(SIGHUP, logSigHup);
1136 #endif
1137 
1139  if (!gContext->Init( false, /*use gui*/
1140  false, /*prompt for backend*/
1141  false, /*bypass auto discovery*/
1142  cmdline.toBool("skipdb"))) /*ignoreDB*/
1143  {
1144  LOG(VB_GENERAL, LOG_EMERG, "Failed to init MythContext, exiting.");
1146  }
1148 
1149  MythTranslation::load("mythfrontend");
1150 
1151  if (cmdline.toBool("outputmethod"))
1152  {
1153  QString om = cmdline.toString("outputmethod");
1154  if (outputTypes->contains(om))
1155  outputMethod = outputTypes->value(om);
1156  }
1157 
1158  if (cmdline.toBool("chanid") && cmdline.toBool("starttime"))
1159  {
1160  // operate on a recording in the database
1161  uint chanid = cmdline.toUInt("chanid");
1162  QDateTime starttime = cmdline.toDateTime("starttime");
1163 
1164  if (cmdline.toBool("clearskiplist"))
1165  return ClearSkipList(chanid, starttime);
1166  if (cmdline.toBool("gencutlist"))
1167  return CopySkipListToCutList(chanid, starttime);
1168  if (cmdline.toBool("clearcutlist"))
1169  return SetCutList(chanid, starttime, "");
1170  if (cmdline.toBool("setcutlist"))
1171  return SetCutList(chanid, starttime, cmdline.toString("setcutlist"));
1172  if (cmdline.toBool("getcutlist"))
1173  return GetMarkupList("cutlist", chanid, starttime);
1174  if (cmdline.toBool("getskiplist"))
1175  return GetMarkupList("commflag", chanid, starttime);
1176 
1177  // TODO: check for matching jobid
1178  // create temporary id to operate off of if not
1179 
1180  if (cmdline.toBool("queue"))
1181  QueueCommFlagJob(chanid, starttime, cmdline.toBool("rebuild"));
1182  else if (cmdline.toBool("rebuild"))
1183  result = RebuildSeekTable(chanid, starttime, -1);
1184  else
1185  result = FlagCommercials(chanid, starttime, -1,
1186  cmdline.toString("outputfile"), true);
1187  }
1188  else if (cmdline.toBool("jobid"))
1189  {
1190  jobID = cmdline.toInt("jobid");
1191  uint chanid = 0;
1192  QDateTime starttime;
1193 
1194  if (!JobQueue::GetJobInfoFromID(jobID, jobType, chanid, starttime))
1195  {
1196  cerr << "mythcommflag: ERROR: Unable to find DB info for "
1197  << "JobQueue ID# " << jobID << endl;
1199  }
1200  force = true;
1201  int jobQueueCPU = gCoreContext->GetNumSetting("JobQueueCPU", 0);
1202 
1203  if (jobQueueCPU < 2)
1204  {
1205  myth_nice(17);
1206  myth_ioprio((0 == jobQueueCPU) ? 8 : 7);
1207  }
1208 
1209  progress = false;
1210 
1211  int ret = 0;
1212 
1214  RebuildSeekTable(chanid, starttime, jobID);
1215  else
1216  ret = FlagCommercials(chanid, starttime, jobID, "", jobQueueCPU != 0);
1217 
1218  if (ret > GENERIC_EXIT_NOT_OK)
1219  {
1220  JobQueue::ChangeJobStatus(jobID, JOB_ERRORED,
1221  QCoreApplication::translate("(mythcommflag)",
1222  "Failed with exit status %1",
1223  "Job status").arg(ret));
1224  }
1225  else
1226  {
1227  JobQueue::ChangeJobStatus(jobID, JOB_FINISHED,
1228  QCoreApplication::translate("(mythcommflag)",
1229  "%n commercial break(s)",
1230  "Job status",
1231  ret));
1232  }
1233  }
1234  else if (cmdline.toBool("video"))
1235  {
1236  // build skiplist for video file
1237  return RebuildSeekTable(cmdline.toString("video"), -1);
1238  }
1239  else if (cmdline.toBool("file"))
1240  {
1241  if (cmdline.toBool("skipdb"))
1242  {
1243  if (cmdline.toBool("rebuild"))
1244  {
1245  cerr << "The --rebuild parameter builds the seektable for "
1246  "internal MythTV use only. It cannot be used in "
1247  "combination with --skipdb." << endl;
1249  }
1250 
1251  if (!cmdline.toBool("outputfile"))
1252  cmdline.SetValue("outputfile", "-");
1253 
1254  // perform commercial flagging on file outside the database
1255  FlagCommercials(cmdline.toString("file"), -1,
1256  cmdline.toString("outputfile"),
1257  !cmdline.toBool("skipdb"),
1258  true);
1259  }
1260  else
1261  {
1262  ProgramInfo pginfo(cmdline.toString("file"));
1263  // pass chanid and starttime
1264  // inefficient, but it lets the other function
1265  // handle sanity checking
1266  if (cmdline.toBool("rebuild"))
1267  {
1268  result = RebuildSeekTable(pginfo.GetChanID(),
1269  pginfo.GetRecordingStartTime(),
1270  -1, cmdline.toBool("writefile"));
1271  }
1272  else
1273  {
1274  result = FlagCommercials(pginfo.GetChanID(),
1275  pginfo.GetRecordingStartTime(),
1276  -1, cmdline.toString("outputfile"),
1277  true);
1278  }
1279  }
1280  }
1281  else if (cmdline.toBool("queue"))
1282  {
1283  // run flagging for all recordings with no skiplist
1285  query.prepare("SELECT r.chanid, r.starttime, c.commmethod "
1286  "FROM recorded AS r "
1287  "LEFT JOIN channel AS c ON r.chanid=c.chanid "
1288 // "WHERE startime >= :STARTTIME AND endtime <= :ENDTIME "
1289  "ORDER BY starttime;");
1290  //query.bindValue(":STARTTIME", allStart);
1291  //query.bindValue(":ENDTIME", allEnd);
1292 
1293  if (query.exec() && query.isActive() && query.size() > 0)
1294  {
1295  QDateTime starttime;
1296 
1297  while (query.next())
1298  {
1299  starttime = MythDate::fromString(query.value(1).toString());
1300  uint chanid = query.value(0).toUInt();
1301 
1302  if (!cmdline.toBool("force") && !cmdline.toBool("rebuild"))
1303  {
1304  // recording is already flagged
1305  if (IsMarked(chanid, starttime))
1306  continue;
1307 
1308  // channel is marked as commercial free
1309  if (query.value(2).toInt() == COMM_DETECT_COMMFREE)
1310  continue;
1311 
1312  // recording rule did not enable commflagging
1313 #if 0
1314  RecordingInfo recinfo(chanid, starttime);
1315  if (!(recinfo.GetAutoRunJobs() & JOB_COMMFLAG))
1316  continue;
1317 #endif
1318  }
1319 
1320  QueueCommFlagJob(chanid, starttime, cmdline.toBool("rebuild"));
1321  }
1322  }
1323 
1324  }
1325  else
1326  {
1327  LOG(VB_GENERAL, LOG_ERR,
1328  "No valid combination of command inputs received.");
1329  cmdline.PrintHelp();
1331  }
1332 
1333  return result;
1334 }
1335 
1336 
1337 /* vim: set expandtab tabstop=4 shiftwidth=4: */
QDateTime toDateTime(const QString &key) const
Returns stored QVariant as a QDateTime, falling to default if not provided.
#define GENERIC_EXIT_DB_ERROR
Database error.
Definition: exitcodes.h:17
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
static int CopySkipListToCutList(uint chanid, const QDateTime &starttime)
Startup context for MythTV.
Definition: mythcontext.h:42
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
static int FlagCommercials(ProgramInfo *program_info, int jobid, const QString &outputfilename, bool useDB, bool fullSpeed)
static void commDetectorGotNewCommercialBreakList(void)
PlayerFlags
Definition: mythplayer.h:87
static int SetCutList(uint chanid, const QDateTime &starttime, QString newCutList)
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
QMap< QString, SkipType > * skipTypes
virtual uint64_t GetFilesize(void) const
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
#define GENERIC_EXIT_PERMISSIONS_ERROR
File permissions error.
Definition: exitcodes.h:19
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
static void incomingCustomEvent(QEvent *e)
static Type MythEventMessage
Definition: mythevent.h:73
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
static void PrintVersion(void)
Print application version information.
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
static int ClearSkipList(uint chanid, const QDateTime &starttime)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
bool myth_ioprio(int)
Allows setting the I/O priority of the current process/thread.
void PrintHelp(void) const
Print command line option help.
void SaveMarkupFlag(MarkTypes type) const
Clears the specified flag, then if sets it.
QString GetTitle(void) const
Definition: programinfo.h:355
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
int size(void) const
Definition: mythdbcon.h:203
static void Init(QList< int > &signallist, QObject *parent=nullptr)
static enum JobCmds GetJobCmd(int jobID)
Definition: jobqueue.cpp:1459
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
const char * kFlaggerInUseID
virtual void GetCommercialBreakList(frm_dir_map_t &comms)=0
static void UpdateFileSize(ProgramInfo *program_info)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
virtual void requestCommBreakMapUpdate(void)
static void streamOutCommercialBreakList(ostream &output, const frm_dir_map_t &commercialBreakList)
bool SetValue(const QString &key, const QVariant &value)
Set a new stored value for an existing argument definition, or spawn a new definition store value in.
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:62
uint64_t QueryLastFrameInPosMap(void) const
Returns last frame in position map or 0.
static guint32 * tmp
Definition: goom_core.cpp:30
#define GENERIC_EXIT_IN_USE
Recording in use, can't flag.
Definition: exitcodes.h:34
int main(int argc, char *argv[])
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, int Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
unsigned sleep(unsigned int x)
Definition: compat.h:159
QMap< QString, OutputMethod > * outputTypes
void SendMessage(const QString &message)
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
static bool ChangeJobComment(int jobID, const QString &comment="")
Definition: jobqueue.cpp:1013
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:384
QVariant value(int i) const
Definition: mythdbcon.h:198
static QString get_filename(ProgramInfo *program_info)
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:333
void SaveCommBreakList(frm_dir_map_t &frames) const
void ApplySettingsOverride(void)
Apply all overrides to the global context.
Holds information on recordings and videos.
Definition: programinfo.h:67
static void commDetectorBreathe()
virtual void SaveFilesize(uint64_t fsize)
Sets recording file size in database, and sets "filesize" field.
static bool GetJobInfoFromID(int jobID, int &jobType, uint &chanid, QDateTime &recstartts)
Definition: jobqueue.cpp:668
#define GENERIC_EXIT_NO_RECORDING_DATA
No program/recording data.
Definition: exitcodes.h:29
MSqlQuery query(MSqlQuery::InitCon())
void QueryCommBreakList(frm_dir_map_t &frames) const
static bool ExtractKey(const QString &uniquekey, uint &chanid, QDateTime &recstartts)
Extracts chanid and recstartts from a unique key generated by MakeUniqueKey().
SkipType
This is used as a bitmask.
Definition: programtypes.h:91
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
QString GetSubtitle(void) const
Definition: programinfo.h:357
bool isActive(void) const
Definition: mythdbcon.h:204
CommDetectorBase * commDetector
static void load(const QString &module_name)
Load a QTranslator for the user's preferred language.
Abstract base class for all CommDetectors.
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:852
static int RebuildSeekTable(ProgramInfo *pginfo, int jobid, bool writefile=false)
static void Done(void)
static QMap< QString, OutputMethod > * init_output_types()
unsigned int uint
Definition: compat.h:140
static void SetHandler(int signum, SigHandlerFunc handler)
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:391
void SaveTotalDuration(void)
MythCommFlagCommandLineParser cmdline
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
static bool IsJobRunning(int jobType, uint chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:1089
long long GetFileSize(void) const
GetFileSize: returns the remote file's size at the time it was first opened Will query the server in ...
#define MYTH_APPNAME_MYTHCOMMFLAG
void UnregisterFileForWrite(const QString &file)
int ConfigureLogging(const QString &mask="general", bool progress=false)
Read in logging options and initialize the logging interface.
static int QueueCommFlagJob(uint chanid, const QDateTime &starttime, bool rebuild)
static int DoFlagCommercials(ProgramInfo *program_info, bool showPercentage, bool fullSpeed, int jobid, MythCommFlagPlayer *cfp, SkipType commDetectMethod, const QString &outputfilename, bool useDB)
int GetNumSetting(const QString &key, int defaultval=0)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
bool myth_nice(int val)
ProgramInfo * global_program_info
virtual void recordingFinished(long long totalFileSize)
virtual void PrintFullMap(ostream &out, const frm_dir_map_t *comm_breaks, bool verbose) const =0
virtual bool go()=0
static void print_comm_flag_output(const ProgramInfo *program_info, const frm_dir_map_t &commBreakList, uint64_t frame_count, const CommDetectorBase *commDetect, const QString &output_filename)
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
static bool DoesFileExist(ProgramInfo *program_info)
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:366
OutputMethod outputMethod
int GetRecorderNumber(void) const
#define SIGHUP
Definition: compat.h:213
uint64_t GetTotalFrameCount(void) const
Definition: mythplayer.h:236
static void commDetectorStatusUpdate(const QString &status)
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
static QMap< QString, SkipType > * init_skip_types()
static enum JobFlags GetJobFlags(int jobID)
Definition: jobqueue.cpp:1501
bool Init(bool gui=true, bool promptForBackend=false, bool disableAutoDiscovery=false, bool ignoreDB=false)
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:398
static bool IsMarked(uint chanid, const QDateTime &starttime)
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:406
RemoteEncoder * recorder
static QString cleanup(const QString &str)
static bool ChangeJobStatus(int jobID, int newStatus, const QString &comment="")
Definition: jobqueue.cpp:986
Default UTC.
Definition: mythdate.h:14
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:81
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
#define GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:13
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false)
Returns filename or URL to be used to play back this recording.
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
#define GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:15
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:15
static qint64 GetFileSize(ProgramInfo *program_info)
#define GENERIC_EXIT_NOT_OK
Exited with error.
Definition: exitcodes.h:11
uint toUInt(const QString &key) const
Returns stored QVariant as an unsigned integer, falling to default if not provided.
QString GetHostName(void)
bool QueryCutList(frm_dir_map_t &delMap, bool loadAutosave=false) const
QString GetPathname(void) const
Definition: programinfo.h:337
static CommDetectorBase * makeCommDetector(SkipType commDetectMethod, bool showProgress, bool fullSpeed, MythPlayer *player, int chanid, const QDateTime &startedAt, const QDateTime &stopsAt, const QDateTime &recordingStartedAt, const QDateTime &recordingStopsAt, bool useDB)
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())
Definition: jobqueue.cpp:517
static int GetMarkupList(const QString &list, uint chanid, const QDateTime &starttime)
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
RemoteEncoder * RemoteGetExistingRecorder(const ProgramInfo *pginfo)
#define output
void SaveCutList(frm_dir_map_t &delMap, bool isAutoSave=false) const
void SetPathname(const QString &pn)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23