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 "ringbuffer.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  if (*it == MARK_COMM_START)
199  cutlist[it.key()] = MARK_CUT_START;
200  else
201  cutlist[it.key()] = MARK_CUT_END;
202  pginfo.SaveCutList(cutlist);
203 
204  return GENERIC_EXIT_OK;
205 }
206 
207 static int ClearSkipList(uint chanid, const QDateTime& starttime)
208 {
209  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
210  const ProgramInfo pginfo(chanid, starttime);
211 
212  if (!pginfo.GetChanID())
213  {
214  LOG(VB_GENERAL, LOG_ERR,
215  QString("No program data exists for channel %1 at %2")
216  .arg(chanid).arg(startstring));
218  }
219 
220  frm_dir_map_t skiplist;
221  pginfo.SaveCommBreakList(skiplist);
222 
223  LOG(VB_GENERAL, LOG_NOTICE, "Commercial skip list cleared");
224 
225  return GENERIC_EXIT_OK;
226 }
227 
228 static int SetCutList(uint chanid, const QDateTime& starttime, QString newCutList)
229 {
230  frm_dir_map_t cutlist;
231 
232  newCutList.replace(QRegExp(" "), "");
233 
234  QStringList tokens = newCutList.split(",", QString::SkipEmptyParts);
235 
236  for (int i = 0; i < tokens.size(); i++)
237  {
238  QStringList cutpair = tokens[i].split("-", QString::SkipEmptyParts);
239  cutlist[cutpair[0].toInt()] = MARK_CUT_START;
240  cutlist[cutpair[1].toInt()] = MARK_CUT_END;
241  }
242 
243  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
244  const ProgramInfo pginfo(chanid, starttime);
245 
246  if (!pginfo.GetChanID())
247  {
248  LOG(VB_GENERAL, LOG_ERR,
249  QString("No program data exists for channel %1 at %2")
250  .arg(chanid).arg(startstring));
252  }
253 
254  pginfo.SaveCutList(cutlist);
255 
256  LOG(VB_GENERAL, LOG_NOTICE, QString("Cutlist set to: %1").arg(newCutList));
257 
258  return GENERIC_EXIT_OK;
259 }
260 
261 static int GetMarkupList(const QString& list, uint chanid, const QDateTime& starttime)
262 {
263  frm_dir_map_t cutlist;
264  frm_dir_map_t::const_iterator it;
265  QString result;
266 
267  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
268  const ProgramInfo pginfo(chanid, starttime);
269 
270  if (!pginfo.GetChanID())
271  {
272  LOG(VB_GENERAL, LOG_ERR,
273  QString("No program data exists for channel %1 at %2")
274  .arg(chanid).arg(startstring));
276  }
277 
278  if (list == "cutlist")
279  pginfo.QueryCutList(cutlist);
280  else
281  pginfo.QueryCommBreakList(cutlist);
282 
283  uint64_t lastStart = 0;
284  for (it = cutlist.begin(); it != cutlist.end(); ++it)
285  {
286  if ((*it == MARK_COMM_START) ||
287  (*it == MARK_CUT_START))
288  {
289  if (!result.isEmpty())
290  result += ",";
291  lastStart = it.key();
292  result += QString("%1-").arg(lastStart);
293  }
294  else
295  {
296  if (result.isEmpty())
297  result += "0-";
298  result += QString("%1").arg(it.key());
299  }
300  }
301 
302  if (result.endsWith('-'))
303  {
304  uint64_t lastFrame = pginfo.QueryLastFrameInPosMap() + 60;
305  if (lastFrame > lastStart)
306  result += QString("%1").arg(lastFrame);
307  }
308 
309  if (list == "cutlist")
310  cout << QString("Cutlist: %1\n").arg(result).toLocal8Bit().constData();
311  else
312  {
313  cout << QString("Commercial Skip List: %1\n")
314  .arg(result).toLocal8Bit().constData();
315  }
316 
317  return GENERIC_EXIT_OK;
318 }
319 
321  ostream &output, const frm_dir_map_t &commercialBreakList)
322 {
323  if (progress)
324  output << "----------------------------" << endl;
325 
326  if (commercialBreakList.empty())
327  {
328  if (progress)
329  output << "No breaks" << endl;
330  }
331  else
332  {
333  frm_dir_map_t::const_iterator it = commercialBreakList.begin();
334  for (; it != commercialBreakList.end(); ++it)
335  {
336  output << "framenum: " << it.key() << "\tmarktype: " << *it
337  << endl;
338  }
339  }
340 
341  if (progress)
342  output << "----------------------------" << endl;
343 }
344 
346  const ProgramInfo *program_info,
347  const frm_dir_map_t &commBreakList,
348  uint64_t frame_count,
349  const CommDetectorBase *commDetect,
350  const QString &output_filename)
351 {
352  if (output_filename.isEmpty())
353  return;
354 
355  ostream *out = &cout;
356  if (output_filename != "-")
357  {
358  QByteArray tmp = output_filename.toLocal8Bit();
359  out = new fstream(tmp.constData(), ios::app | ios::out );
360  }
361 
362  if (progress)
363  {
364  QString tmp = "";
365  if (program_info->GetChanID())
366  {
367  tmp = QString("commercialBreakListFor: %1 on %2 @ %3")
368  .arg(program_info->GetTitle())
369  .arg(program_info->GetChanID())
370  .arg(program_info->GetRecordingStartTime(MythDate::ISODate));
371  }
372  else
373  {
374  tmp = QString("commercialBreakListFor: %1")
375  .arg(program_info->GetPathname());
376  }
377 
378  const QByteArray tmp2 = tmp.toLocal8Bit();
379  *out << tmp2.constData() << endl;
380 
381  if (frame_count)
382  *out << "totalframecount: " << frame_count << endl;
383  }
384 
385  if (commDetect)
386  commDetect->PrintFullMap(*out, &commBreakList, progress);
387  else
388  streamOutCommercialBreakList(*out, commBreakList);
389 
390  if (out != &cout)
391  delete out;
392 }
393 
394 static void commDetectorBreathe()
395 {
396  //this is connected to the commdetectors breathe signal so we get a chance
397  //while its busy to see if the user already told us to stop.
398  qApp->processEvents();
399 
400  if (jobID != -1)
401  {
402  int curCmd = JobQueue::GetJobCmd(jobID);
403  if (curCmd == lastCmd)
404  return;
405 
406  switch (curCmd)
407  {
408  case JOB_STOP:
409  {
410  commDetector->stop();
411  break;
412  }
413  case JOB_PAUSE:
414  {
415  JobQueue::ChangeJobStatus(jobID, JOB_PAUSED,
416  QCoreApplication::translate("(mythcommflag)",
417  "Paused", "Job status"));
418  commDetector->pause();
419  break;
420  }
421  case JOB_RESUME:
422  {
423  JobQueue::ChangeJobStatus(jobID, JOB_RUNNING,
424  QCoreApplication::translate("(mythcommflag)",
425  "Running", "Job status"));
426  commDetector->resume();
427  break;
428  }
429  }
430  }
431 }
432 
433 static void commDetectorStatusUpdate(const QString& status)
434 {
435  if (jobID != -1)
436  {
437  JobQueue::ChangeJobStatus(jobID, JOB_RUNNING, status);
439  }
440 }
441 
443 {
444  frm_dir_map_t newCommercialMap;
445  commDetector->GetCommercialBreakList(newCommercialMap);
446 
447  frm_dir_map_t::Iterator it = newCommercialMap.begin();
448  QString message = "COMMFLAG_UPDATE ";
449  message += global_program_info->MakeUniqueKey();
450 
451  for (it = newCommercialMap.begin();
452  it != newCommercialMap.end(); ++it)
453  {
454  if (it != newCommercialMap.begin())
455  message += ",";
456  else
457  message += " ";
458  message += QString("%1:%2").arg(it.key())
459  .arg(*it);
460  }
461 
462  LOG(VB_COMMFLAG, LOG_INFO,
463  QString("mythcommflag sending update: %1").arg(message));
464 
465  gCoreContext->SendMessage(message);
466 }
467 
468 static void incomingCustomEvent(QEvent* e)
469 {
470  if (e->type() == MythEvent::MythEventMessage)
471  {
472  auto *me = static_cast<MythEvent *>(e);
473  QString message = me->Message();
474 
475  message = message.simplified();
476  QStringList tokens = message.split(" ", QString::SkipEmptyParts);
477 
478  LOG(VB_COMMFLAG, LOG_INFO,
479  QString("mythcommflag: Received Event: '%1'") .arg(message));
480 
481  if ((watchingRecording) && (tokens.size() >= 3) &&
482  (tokens[0] == "DONE_RECORDING"))
483  {
484  int cardnum = tokens[1].toInt();
485  int filelen = tokens[2].toInt();
486 
487  message = QString("mythcommflag: Received a "
488  "DONE_RECORDING event for card %1. ")
489  .arg(cardnum);
490 
491  if (recorderNum != -1 && cardnum == recorderNum)
492  {
494  watchingRecording = false;
495  message += "Informed CommDetector that recording has finished.";
496  LOG(VB_COMMFLAG, LOG_INFO, message);
497  }
498  }
499 
500  if ((tokens.size() >= 2) && (tokens[0] == "COMMFLAG_REQUEST"))
501  {
502  uint chanid = 0;
503  QDateTime recstartts;
504  ProgramInfo::ExtractKey(tokens[1], chanid, recstartts);
505 
506  message = QString("mythcommflag: Received a "
507  "COMMFLAG_REQUEST event for chanid %1 @ %2. ")
508  .arg(chanid).arg(recstartts.toString(Qt::ISODate));
509 
510  if ((global_program_info->GetChanID() == chanid) &&
511  (global_program_info->GetRecordingStartTime() == recstartts))
512  {
514  message += "Requested CommDetector to generate new break list.";
515  LOG(VB_COMMFLAG, LOG_INFO, message);
516  }
517  }
518  }
519 }
520 
521 static int DoFlagCommercials(
522  ProgramInfo *program_info,
523  bool showPercentage, bool fullSpeed, int jobid,
524  MythCommFlagPlayer* cfp, SkipType commDetectMethod,
525  const QString &outputfilename, bool useDB)
526 {
528  commDetectMethod, showPercentage,
529  fullSpeed, cfp,
530  program_info->GetChanID(),
531  program_info->GetScheduledStartTime(),
532  program_info->GetScheduledEndTime(),
533  program_info->GetRecordingStartTime(),
534  program_info->GetRecordingEndTime(), useDB);
535 
536  if (jobid > 0)
537  LOG(VB_COMMFLAG, LOG_INFO,
538  QString("mythcommflag processing JobID %1").arg(jobid));
539 
540  if (useDB)
541  program_info->SaveCommFlagged(COMM_FLAG_PROCESSING);
542 
543  auto *cer = new CustomEventRelayer(incomingCustomEvent);
544  auto *a = new SlotRelayer(commDetectorBreathe);
545  auto *b = new SlotRelayer(commDetectorStatusUpdate);
547  QObject::connect(commDetector, SIGNAL(breathe()),
548  a, SLOT(relay()));
549  QObject::connect(commDetector, SIGNAL(statusUpdate(const QString&)),
550  b, SLOT(relay(const QString&)));
551  QObject::connect(commDetector, SIGNAL(gotNewCommercialBreakList()),
552  c, SLOT(relay()));
553 
554  if (useDB)
555  {
556  LOG(VB_COMMFLAG, LOG_INFO,
557  "mythcommflag sending COMMFLAG_START notification");
558  QString message = "COMMFLAG_START ";
559  message += program_info->MakeUniqueKey();
560  gCoreContext->SendMessage(message);
561  }
562 
563  bool result = commDetector->go();
564  int comms_found = 0;
565 
566  if (result)
567  {
568  cfp->SaveTotalDuration();
569 
570  frm_dir_map_t commBreakList;
571  commDetector->GetCommercialBreakList(commBreakList);
572  comms_found = commBreakList.size() / 2;
573 
574  if (useDB)
575  {
576  program_info->SaveMarkupFlag(MARK_UPDATED_CUT);
577  program_info->SaveCommBreakList(commBreakList);
578  program_info->SaveCommFlagged(COMM_FLAG_DONE);
579  }
580 
582  program_info, commBreakList, cfp->GetTotalFrameCount(),
584  outputfilename);
585  }
586  else
587  {
588  if (useDB)
589  program_info->SaveCommFlagged(COMM_FLAG_NOT_FLAGGED);
590  }
591 
593  commDetector = nullptr;
594  sleep(1);
595  tmp->deleteLater();
596 
597  cer->deleteLater();
598  c->deleteLater();
599  b->deleteLater();
600  a->deleteLater();
601 
602  return comms_found;
603 }
604 
605 static qint64 GetFileSize(ProgramInfo *program_info)
606 {
607  QString filename = get_filename(program_info);
608  qint64 size = -1;
609 
610  if (filename.startsWith("myth://"))
611  {
612  RemoteFile remotefile(filename, false, false, 0);
613  size = remotefile.GetFileSize();
614  }
615  else
616  {
617  QFile file(filename);
618  if (file.exists())
619  {
620  size = file.size();
621  }
622  }
623 
624  return size;
625 }
626 
627 static bool DoesFileExist(ProgramInfo *program_info)
628 {
629  QString filename = get_filename(program_info);
630  qint64 size = GetFileSize(program_info);
631 
632  if (size < 0)
633  {
634  LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find file %1, aborting.")
635  .arg(filename));
636  return false;
637  }
638 
639  if (size == 0)
640  {
641  LOG(VB_GENERAL, LOG_ERR, QString("File %1 is zero-byte, aborting.")
642  .arg(filename));
643  return false;
644  }
645 
646  return true;
647 }
648 
649 static void UpdateFileSize(ProgramInfo *program_info)
650 {
651  qint64 size = GetFileSize(program_info);
652 
653  if (size != (qint64)program_info->GetFilesize())
654  program_info->SaveFilesize(size);
655 }
656 
657 static bool IsMarked(uint chanid, const QDateTime& starttime)
658 {
659  MSqlQuery mark_query(MSqlQuery::InitCon());
660  mark_query.prepare("SELECT commflagged, count(rm.type) "
661  "FROM recorded r "
662  "LEFT JOIN recordedmarkup rm ON "
663  "( r.chanid = rm.chanid AND "
664  "r.starttime = rm.starttime AND "
665  "type in (:MARK_START,:MARK_END)) "
666  "WHERE r.chanid = :CHANID AND "
667  "r.starttime = :STARTTIME "
668  "GROUP BY COMMFLAGGED;");
669  mark_query.bindValue(":MARK_START", MARK_COMM_START);
670  mark_query.bindValue(":MARK_END", MARK_COMM_END);
671  mark_query.bindValue(":CHANID", chanid);
672  mark_query.bindValue(":STARTTIME", starttime);
673 
674  if (mark_query.exec() && mark_query.isActive() &&
675  mark_query.size() > 0)
676  {
677  if (mark_query.next())
678  {
679  int flagStatus = mark_query.value(0).toInt();
680  int marksFound = mark_query.value(1).toInt();
681 
682  QString flagStatusStr = "UNKNOWN";
683  switch (flagStatus) {
685  flagStatusStr = "Not Flagged";
686  break;
687  case COMM_FLAG_DONE:
688  flagStatusStr = QString("Flagged with %1 breaks")
689  .arg(marksFound / 2);
690  break;
692  flagStatusStr = "Flagging";
693  break;
694  case COMM_FLAG_COMMFREE:
695  flagStatusStr = "Commercial Free";
696  break;
697  }
698 
699  LOG(VB_COMMFLAG, LOG_INFO,
700  QString("Status for chanid %1 @ %2 is '%3'")
701  .arg(chanid).arg(starttime.toString(Qt::ISODate))
702  .arg(flagStatusStr));
703 
704  if ((flagStatus == COMM_FLAG_NOT_FLAGGED) && (marksFound == 0))
705  return false;
706  }
707  }
708  return true;
709 }
710 
711 static int FlagCommercials(ProgramInfo *program_info, int jobid,
712  const QString &outputfilename, bool useDB, bool fullSpeed)
713 {
714  global_program_info = program_info;
715 
716  int breaksFound = 0;
717 
718  // configure commercial detection method
719  SkipType commDetectMethod = (SkipType)gCoreContext->GetNumSetting(
720  "CommercialSkipMethod", COMM_DETECT_ALL);
721 
722  if (cmdline.toBool("commmethod"))
723  {
724  // pull commercial detection method from command line
725  QString commmethod = cmdline.toString("commmethod");
726 
727  // assume definition as integer value
728  bool ok = true;
729  commDetectMethod = (SkipType) commmethod.toInt(&ok);
730  if (!ok)
731  {
732  // not an integer, attempt comma separated list
733  commDetectMethod = COMM_DETECT_UNINIT;
734 
735  QStringList list = commmethod.split(",", QString::SkipEmptyParts);
736  QStringList::const_iterator it = list.begin();
737  for (; it != list.end(); ++it)
738  {
739  QString val = (*it).toLower();
740  if (val == "off")
741  {
742  commDetectMethod = COMM_DETECT_OFF;
743  break;
744  }
745 
746  if (!skipTypes->contains(val))
747  {
748  cerr << "Failed to decode --method option '"
749  << val.toLatin1().constData()
750  << "'" << endl;
752  }
753 
754  if (commDetectMethod == COMM_DETECT_UNINIT) {
755  commDetectMethod = skipTypes->value(val);
756  } else {
757  commDetectMethod = (SkipType) ((int)commDetectMethod
758  | (int)skipTypes->value(val));
759  }
760  }
761 
762  }
763  if (commDetectMethod == COMM_DETECT_UNINIT)
765  }
766  else if (useDB)
767  {
768  // if not manually specified, and we have a database to access
769  // pull the commflag type from the channel
770  MSqlQuery query(MSqlQuery::InitCon());
771  query.prepare("SELECT commmethod FROM channel "
772  "WHERE chanid = :CHANID;");
773  query.bindValue(":CHANID", program_info->GetChanID());
774 
775  if (!query.exec())
776  {
777  // if the query fails, return with an error
778  commDetectMethod = COMM_DETECT_UNINIT;
779  MythDB::DBError("FlagCommercials", query);
780  }
781  else if (query.next())
782  {
783  commDetectMethod = (SkipType)query.value(0).toInt();
784  if (commDetectMethod == COMM_DETECT_COMMFREE)
785  {
786  // if the channel is commercial free, drop to the default instead
787  commDetectMethod = (SkipType)gCoreContext->GetNumSetting(
788  "CommercialSkipMethod", COMM_DETECT_ALL);
789  LOG(VB_COMMFLAG, LOG_INFO,
790  QString("Chanid %1 is marked as being Commercial Free, "
791  "we will use the default commercial detection "
792  "method").arg(program_info->GetChanID()));
793  }
794  else if (commDetectMethod == COMM_DETECT_UNINIT)
795  // no value set, so use the database default
796  commDetectMethod = (SkipType)gCoreContext->GetNumSetting(
797  "CommercialSkipMethod", COMM_DETECT_ALL);
798  LOG(VB_COMMFLAG, LOG_INFO,
799  QString("Using method: %1 from channel %2")
800  .arg(commDetectMethod).arg(program_info->GetChanID()));
801  }
802 
803  }
804  else if (!useDB)
805  {
806  // default to a cheaper method for debugging purposes
807  commDetectMethod = COMM_DETECT_BLANK;
808  }
809 
810  // if selection has failed, or intentionally disabled, drop out
811  if (commDetectMethod == COMM_DETECT_UNINIT)
812  return GENERIC_EXIT_NOT_OK;
813  if (commDetectMethod == COMM_DETECT_OFF)
814  return GENERIC_EXIT_OK;
815 
816  frm_dir_map_t blanks;
817  recorder = nullptr;
818 
819 /*
820  * is there a purpose to this not fulfilled by --getskiplist?
821  if (onlyDumpDBCommercialBreakList)
822  {
823  frm_dir_map_t commBreakList;
824  program_info->QueryCommBreakList(commBreakList);
825 
826  print_comm_flag_output(program_info, commBreakList,
827  0, nullptr, outputfilename);
828 
829  global_program_info = nullptr;
830  return GENERIC_EXIT_OK;
831  }
832 */
833 
834  if (!DoesFileExist(program_info))
835  {
836  LOG(VB_GENERAL, LOG_ERR,
837  "Unable to find file in defined storage paths.");
839  }
840 
841  QString filename = get_filename(program_info);
842 
843  RingBuffer *tmprbuf = RingBuffer::Create(filename, false);
844  if (!tmprbuf)
845  {
846  LOG(VB_GENERAL, LOG_ERR,
847  QString("Unable to create RingBuffer for %1").arg(filename));
848  global_program_info = nullptr;
850  }
851 
852  if (useDB)
853  {
855  {
856  LOG(VB_GENERAL, LOG_ERR, "Unable to open commflag DB connection");
857  delete tmprbuf;
858  global_program_info = nullptr;
859  return GENERIC_EXIT_DB_ERROR;
860  }
861  }
862 
863  auto flags = (PlayerFlags)(kAudioMuted |
864  kVideoIsNull |
865  kDecodeLowRes |
868  kNoITV);
869  /* blank detector needs to be only sample center for this optimization. */
870  if ((COMM_DETECT_BLANKS == commDetectMethod) ||
871  (COMM_DETECT_2_BLANK == commDetectMethod))
872  {
873  flags = (PlayerFlags) (flags | kDecodeFewBlocks);
874  }
875 
876  auto *cfp = new MythCommFlagPlayer(flags);
877  auto *ctx = new PlayerContext(kFlaggerInUseID);
878  ctx->SetPlayingInfo(program_info);
879  ctx->SetRingBuffer(tmprbuf);
880  ctx->SetPlayer(cfp);
881  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
882 
883  if (useDB)
884  {
885  if (program_info->GetRecordingEndTime() > MythDate::current())
886  {
888 
889  recorder = RemoteGetExistingRecorder(program_info);
890  if (recorder && (recorder->GetRecorderNumber() != -1))
891  {
893  watchingRecording = true;
894  ctx->SetRecorder(recorder);
895 
896  LOG(VB_COMMFLAG, LOG_INFO,
897  QString("mythcommflag will flag recording "
898  "currently in progress on cardid %1")
899  .arg(recorderNum));
900  }
901  else
902  {
903  recorderNum = -1;
904  watchingRecording = false;
905 
906  LOG(VB_GENERAL, LOG_ERR,
907  "Unable to find active recorder for this "
908  "recording, realtime flagging will not be enabled.");
909  }
910  cfp->SetWatchingRecording(watchingRecording);
911  }
912  }
913 
914  // TODO: Add back insertion of job if not in jobqueue
915 
916  breaksFound = DoFlagCommercials(
917  program_info, progress, fullSpeed, jobid,
918  cfp, commDetectMethod, outputfilename, useDB);
919 
920  if (progress)
921  cerr << breaksFound << "\n";
922 
923  LOG(VB_GENERAL, LOG_NOTICE, QString("Finished, %1 break(s) found.")
924  .arg(breaksFound));
925 
926  delete ctx;
927  global_program_info = nullptr;
928 
929  return breaksFound;
930 }
931 
932 static int FlagCommercials( uint chanid, const QDateTime &starttime,
933  int jobid, const QString &outputfilename,
934  bool fullSpeed )
935 {
936  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
937  ProgramInfo pginfo(chanid, starttime);
938 
939  if (!pginfo.GetChanID())
940  {
941  LOG(VB_GENERAL, LOG_ERR,
942  QString("No program data exists for channel %1 at %2")
943  .arg(chanid).arg(startstring));
945  }
946 
947  if (!force && JobQueue::IsJobRunning(JOB_COMMFLAG, pginfo))
948  {
949  if (progress)
950  {
951  cerr << "IN USE\n";
952  cerr << " "
953  "(the program is already being flagged elsewhere)\n";
954  }
955  LOG(VB_GENERAL, LOG_ERR, "Program is already being flagged elsewhere");
956  return GENERIC_EXIT_IN_USE;
957  }
958 
959 
960  if (progress)
961  {
962  cerr << "MythTV Commercial Flagger, flagging commercials for:" << endl;
963  if (pginfo.GetSubtitle().isEmpty())
964  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << endl;
965  else
966  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << " - "
967  << pginfo.GetSubtitle().toLocal8Bit().constData() << endl;
968  }
969 
970  return FlagCommercials(&pginfo, jobid, outputfilename, true, fullSpeed);
971 }
972 
973 static int FlagCommercials(const QString& filename, int jobid,
974  const QString &outputfilename, bool useDB,
975  bool fullSpeed)
976 {
977 
978  if (progress)
979  {
980  cerr << "MythTV Commercial Flagger, flagging commercials for:" << endl
981  << " " << filename.toLatin1().constData() << endl;
982  }
983 
984  ProgramInfo pginfo(filename);
985  return FlagCommercials(&pginfo, jobid, outputfilename, useDB, fullSpeed);
986 }
987 
988 static int RebuildSeekTable(ProgramInfo *pginfo, int jobid, bool writefile = false)
989 {
990  QString filename = get_filename(pginfo);
991 
992  if (!DoesFileExist(pginfo))
993  {
994  // file not found on local filesystem
995  // assume file is in Video storage group on local backend
996  // and try again
997 
998  filename = QString("myth://Videos@%1/%2")
999  .arg(gCoreContext->GetHostName()).arg(filename);
1000  pginfo->SetPathname(filename);
1001  if (!DoesFileExist(pginfo))
1002  {
1003  LOG(VB_GENERAL, LOG_ERR,
1004  QString("Unable to find file in defined storage "
1005  "paths for JobQueue ID# %1.").arg(jobid));
1007  }
1008  }
1009 
1010  // Update the file size since mythcommflag --rebuild is often used in user
1011  // scripts after transcoding or other size-changing operations
1012  UpdateFileSize(pginfo);
1013 
1014  RingBuffer *tmprbuf = RingBuffer::Create(filename, false);
1015  if (!tmprbuf)
1016  {
1017  LOG(VB_GENERAL, LOG_ERR,
1018  QString("Unable to create RingBuffer for %1").arg(filename));
1020  }
1021 
1023  kDecodeNoDecode | kNoITV));
1024  auto *ctx = new PlayerContext(kFlaggerInUseID);
1025  ctx->SetPlayingInfo(pginfo);
1026  ctx->SetRingBuffer(tmprbuf);
1027  ctx->SetPlayer(cfp);
1028  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
1029 
1030  if (progress)
1031  {
1032  QString time = QDateTime::currentDateTime().toString(Qt::TextDate);
1033  cerr << "Rebuild started at " << qPrintable(time) << endl;
1034  }
1035 
1036  if (writefile)
1038  cfp->RebuildSeekTable(progress);
1039  if (writefile)
1041 
1042  if (progress)
1043  {
1044  QString time = QDateTime::currentDateTime().toString(Qt::TextDate);
1045  cerr << "Rebuild completed at " << qPrintable(time) << endl;
1046  }
1047 
1048  delete ctx;
1049 
1050  return GENERIC_EXIT_OK;
1051 }
1052 
1053 static int RebuildSeekTable(const QString& filename, int jobid, bool writefile = false)
1054 {
1055  if (progress)
1056  {
1057  cerr << "MythTV Commercial Flagger, building seek table for:" << endl
1058  << " " << filename.toLatin1().constData() << endl;
1059  }
1060  ProgramInfo pginfo(filename);
1061  return RebuildSeekTable(&pginfo, jobid, writefile);
1062 }
1063 
1064 static int RebuildSeekTable(uint chanid, const QDateTime& starttime, int jobid, bool writefile = false)
1065 {
1066  ProgramInfo pginfo(chanid, starttime);
1067  if (progress)
1068  {
1069  cerr << "MythTV Commercial Flagger, building seek table for:" << endl;
1070  if (pginfo.GetSubtitle().isEmpty())
1071  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << endl;
1072  else
1073  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << " - "
1074  << pginfo.GetSubtitle().toLocal8Bit().constData() << endl;
1075  }
1076  return RebuildSeekTable(&pginfo, jobid, writefile);
1077 }
1078 
1079 int main(int argc, char *argv[])
1080 {
1081  int result = GENERIC_EXIT_OK;
1082 
1083 // QString allStart = "19700101000000";
1084 // QString allEnd = MythDate::current().toString("yyyyMMddhhmmss");
1085  int jobType = JOB_NONE;
1086 
1087  if (!cmdline.Parse(argc, argv))
1088  {
1089  cmdline.PrintHelp();
1091  }
1092 
1093  if (cmdline.toBool("showhelp"))
1094  {
1095  cmdline.PrintHelp();
1096  return GENERIC_EXIT_OK;
1097  }
1098 
1099  if (cmdline.toBool("showversion"))
1100  {
1102  return GENERIC_EXIT_OK;
1103  }
1104 
1105  QCoreApplication a(argc, argv);
1106  QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHCOMMFLAG);
1107  int retval = cmdline.ConfigureLogging("general",
1108  !cmdline.toBool("noprogress"));
1109  if (retval != GENERIC_EXIT_OK)
1110  return retval;
1111 
1112  CleanupGuard callCleanup(cleanup);
1113 
1114 #ifndef _WIN32
1115  QList<int> signallist;
1116  signallist << SIGINT << SIGTERM << SIGSEGV << SIGABRT << SIGBUS << SIGFPE
1117  << SIGILL;
1118 #if ! CONFIG_DARWIN
1119  signallist << SIGRTMIN;
1120 #endif
1121  SignalHandler::Init(signallist);
1122  SignalHandler::SetHandler(SIGHUP, logSigHup);
1123 #endif
1124 
1126  if (!gContext->Init( false, /*use gui*/
1127  false, /*prompt for backend*/
1128  false, /*bypass auto discovery*/
1129  cmdline.toBool("skipdb"))) /*ignoreDB*/
1130  {
1131  LOG(VB_GENERAL, LOG_EMERG, "Failed to init MythContext, exiting.");
1133  }
1135 
1136  MythTranslation::load("mythfrontend");
1137 
1138  if (cmdline.toBool("outputmethod"))
1139  {
1140  QString om = cmdline.toString("outputmethod");
1141  if (outputTypes->contains(om))
1142  outputMethod = outputTypes->value(om);
1143  }
1144 
1145  if (cmdline.toBool("chanid") && cmdline.toBool("starttime"))
1146  {
1147  // operate on a recording in the database
1148  uint chanid = cmdline.toUInt("chanid");
1149  QDateTime starttime = cmdline.toDateTime("starttime");
1150 
1151  if (cmdline.toBool("clearskiplist"))
1152  return ClearSkipList(chanid, starttime);
1153  if (cmdline.toBool("gencutlist"))
1154  return CopySkipListToCutList(chanid, starttime);
1155  if (cmdline.toBool("clearcutlist"))
1156  return SetCutList(chanid, starttime, "");
1157  if (cmdline.toBool("setcutlist"))
1158  return SetCutList(chanid, starttime, cmdline.toString("setcutlist"));
1159  if (cmdline.toBool("getcutlist"))
1160  return GetMarkupList("cutlist", chanid, starttime);
1161  if (cmdline.toBool("getskiplist"))
1162  return GetMarkupList("commflag", chanid, starttime);
1163 
1164  // TODO: check for matching jobid
1165  // create temporary id to operate off of if not
1166 
1167  if (cmdline.toBool("queue"))
1168  QueueCommFlagJob(chanid, starttime, cmdline.toBool("rebuild"));
1169  else if (cmdline.toBool("rebuild"))
1170  result = RebuildSeekTable(chanid, starttime, -1);
1171  else
1172  result = FlagCommercials(chanid, starttime, -1,
1173  cmdline.toString("outputfile"), true);
1174  }
1175  else if (cmdline.toBool("jobid"))
1176  {
1177  jobID = cmdline.toInt("jobid");
1178  uint chanid = 0;
1179  QDateTime starttime;
1180 
1181  if (!JobQueue::GetJobInfoFromID(jobID, jobType, chanid, starttime))
1182  {
1183  cerr << "mythcommflag: ERROR: Unable to find DB info for "
1184  << "JobQueue ID# " << jobID << endl;
1186  }
1187  force = true;
1188  int jobQueueCPU = gCoreContext->GetNumSetting("JobQueueCPU", 0);
1189 
1190  if (jobQueueCPU < 2)
1191  {
1192  myth_nice(17);
1193  myth_ioprio((0 == jobQueueCPU) ? 8 : 7);
1194  }
1195 
1196  progress = false;
1197 
1198  int ret = 0;
1199 
1201  RebuildSeekTable(chanid, starttime, jobID);
1202  else
1203  ret = FlagCommercials(chanid, starttime, jobID, "", jobQueueCPU != 0);
1204 
1205  if (ret > GENERIC_EXIT_NOT_OK)
1206  JobQueue::ChangeJobStatus(jobID, JOB_ERRORED,
1207  QCoreApplication::translate("(mythcommflag)",
1208  "Failed with exit status %1",
1209  "Job status").arg(ret));
1210  else
1211  JobQueue::ChangeJobStatus(jobID, JOB_FINISHED,
1212  QCoreApplication::translate("(mythcommflag)",
1213  "%n commercial break(s)",
1214  "Job status",
1215  ret));
1216  }
1217  else if (cmdline.toBool("video"))
1218  {
1219  // build skiplist for video file
1220  return RebuildSeekTable(cmdline.toString("video"), -1);
1221  }
1222  else if (cmdline.toBool("file"))
1223  {
1224  if (cmdline.toBool("skipdb"))
1225  {
1226  if (cmdline.toBool("rebuild"))
1227  {
1228  cerr << "The --rebuild parameter builds the seektable for "
1229  "internal MythTV use only. It cannot be used in "
1230  "combination with --skipdb." << endl;
1232  }
1233 
1234  if (!cmdline.toBool("outputfile"))
1235  cmdline.SetValue("outputfile", "-");
1236 
1237  // perform commercial flagging on file outside the database
1238  FlagCommercials(cmdline.toString("file"), -1,
1239  cmdline.toString("outputfile"),
1240  !cmdline.toBool("skipdb"),
1241  true);
1242  }
1243  else
1244  {
1245  ProgramInfo pginfo(cmdline.toString("file"));
1246  // pass chanid and starttime
1247  // inefficient, but it lets the other function
1248  // handle sanity checking
1249  if (cmdline.toBool("rebuild"))
1250  result = RebuildSeekTable(pginfo.GetChanID(),
1251  pginfo.GetRecordingStartTime(),
1252  -1, cmdline.toBool("writefile"));
1253  else
1254  result = FlagCommercials(pginfo.GetChanID(),
1255  pginfo.GetRecordingStartTime(),
1256  -1, cmdline.toString("outputfile"),
1257  true);
1258  }
1259  }
1260  else if (cmdline.toBool("queue"))
1261  {
1262  // run flagging for all recordings with no skiplist
1263  MSqlQuery query(MSqlQuery::InitCon());
1264  query.prepare("SELECT r.chanid, r.starttime, c.commmethod "
1265  "FROM recorded AS r "
1266  "LEFT JOIN channel AS c ON r.chanid=c.chanid "
1267 // "WHERE startime >= :STARTTIME AND endtime <= :ENDTIME "
1268  "ORDER BY starttime;");
1269  //query.bindValue(":STARTTIME", allStart);
1270  //query.bindValue(":ENDTIME", allEnd);
1271 
1272  if (query.exec() && query.isActive() && query.size() > 0)
1273  {
1274  QDateTime starttime;
1275 
1276  while (query.next())
1277  {
1278  starttime = MythDate::fromString(query.value(1).toString());
1279  uint chanid = query.value(0).toUInt();
1280 
1281  if (!cmdline.toBool("force") && !cmdline.toBool("rebuild"))
1282  {
1283  // recording is already flagged
1284  if (IsMarked(chanid, starttime))
1285  continue;
1286 
1287  // channel is marked as commercial free
1288  if (query.value(2).toInt() == COMM_DETECT_COMMFREE)
1289  continue;
1290 
1291  // recording rule did not enable commflagging
1292 #if 0
1293  RecordingInfo recinfo(chanid, starttime);
1294  if (!(recinfo.GetAutoRunJobs() & JOB_COMMFLAG))
1295  continue;
1296 #endif
1297  }
1298 
1299  QueueCommFlagJob(chanid, starttime, cmdline.toBool("rebuild"));
1300  }
1301  }
1302 
1303  }
1304  else
1305  {
1306  LOG(VB_GENERAL, LOG_ERR,
1307  "No valid combination of command inputs received.");
1308  cmdline.PrintHelp();
1310  }
1311 
1312  return result;
1313 }
1314 
1315 
1316 /* 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:781
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:862
static int FlagCommercials(ProgramInfo *program_info, int jobid, const QString &outputfilename, bool useDB, bool fullSpeed)
static void commDetectorGotNewCommercialBreakList(void)
PlayerFlags
Definition: mythplayer.h:86
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:66
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.
bool QueryCutList(frm_dir_map_t &, bool loadAutosave=false) const
QString GetTitle(void) const
Definition: programinfo.h:353
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:1464
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)
void QueryCommBreakList(frm_dir_map_t &) const
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static RingBuffer * Create(const QString &xfilename, bool write, bool usereadahead=true, int timeout_ms=kDefaultOpenTimeout, bool stream_only=false)
Creates a RingBuffer instance.
Definition: ringbuffer.cpp:104
virtual void requestCommBreakMapUpdate(void)
void SaveCommBreakList(frm_dir_map_t &) const
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.c:35
#define GENERIC_EXIT_IN_USE
Recording in use, can't flag.
Definition: exitcodes.h:34
int main(int argc, char *argv[])
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:1016
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
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:331
void ApplySettingsOverride(void)
Apply all overrides to the global context.
Holds information on recordings and videos.
Definition: programinfo.h:66
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:671
#define GENERIC_EXIT_NO_RECORDING_DATA
No program/recording data.
Definition: exitcodes.h:29
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:355
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:850
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 MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
static void SetHandler(int signum, SigHandlerFunc handler)
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
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:1094
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)
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)
void SaveCutList(frm_dir_map_t &, bool isAutoSave=false) const
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:806
bool myth_nice(int val)
ProgramInfo * global_program_info
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
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:364
OutputMethod outputMethod
int GetRecorderNumber(void) const
#define SIGHUP
Definition: compat.h:213
uint64_t GetTotalFrameCount(void) const
Definition: mythplayer.h:207
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:1506
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:396
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:404
RemoteEncoder * recorder
static QString cleanup(const QString &str)
static bool ChangeJobStatus(int jobID, int newStatus, const QString &comment="")
Definition: jobqueue.cpp:989
bool Init(const bool gui=true, const bool promptForBackend=false, const bool disableAutoDiscovery=false, const bool ignoreDB=false)
Default UTC.
Definition: mythdate.h:14
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:81
void SetPathname(const QString &)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
Implements a file/stream reader/writer.
#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)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
QString GetPathname(void) const
Definition: programinfo.h:335
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:520
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.
int ConfigureLogging(const QString &mask="general", unsigned int progress=0)
Read in logging options and initialize the logging interface.
RemoteEncoder * RemoteGetExistingRecorder(const ProgramInfo *pginfo)
#define output