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,SkipTypes> *init_skip_types();
82 QMap<QString,SkipTypes> *skipTypes = init_skip_types();
83 
84 static QMap<QString,SkipTypes> *init_skip_types(void)
85 {
86  QMap<QString,SkipTypes> *tmp = new QMap<QString,SkipTypes>;
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 
105 typedef enum
106 {
109 } OutputMethod;
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  QMap<QString,OutputMethod> *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  MythEvent *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, enum SkipTypes commDetectMethod,
525  const QString &outputfilename, bool useDB)
526 {
527  CommDetectorFactory factory;
528  commDetector = factory.makeCommDetector(
529  commDetectMethod, showPercentage,
530  fullSpeed, cfp,
531  program_info->GetChanID(),
532  program_info->GetScheduledStartTime(),
533  program_info->GetScheduledEndTime(),
534  program_info->GetRecordingStartTime(),
535  program_info->GetRecordingEndTime(), useDB);
536 
537  if (jobid > 0)
538  LOG(VB_COMMFLAG, LOG_INFO,
539  QString("mythcommflag processing JobID %1").arg(jobid));
540 
541  if (useDB)
542  program_info->SaveCommFlagged(COMM_FLAG_PROCESSING);
543 
548  QObject::connect(commDetector, SIGNAL(breathe()),
549  a, SLOT(relay()));
550  QObject::connect(commDetector, SIGNAL(statusUpdate(const QString&)),
551  b, SLOT(relay(const QString&)));
552  QObject::connect(commDetector, SIGNAL(gotNewCommercialBreakList()),
553  c, SLOT(relay()));
554 
555  if (useDB)
556  {
557  LOG(VB_COMMFLAG, LOG_INFO,
558  "mythcommflag sending COMMFLAG_START notification");
559  QString message = "COMMFLAG_START ";
560  message += program_info->MakeUniqueKey();
561  gCoreContext->SendMessage(message);
562  }
563 
564  bool result = commDetector->go();
565  int comms_found = 0;
566 
567  if (result)
568  {
569  cfp->SaveTotalDuration();
570 
571  frm_dir_map_t commBreakList;
572  commDetector->GetCommercialBreakList(commBreakList);
573  comms_found = commBreakList.size() / 2;
574 
575  if (useDB)
576  {
577  program_info->SaveMarkupFlag(MARK_UPDATED_CUT);
578  program_info->SaveCommBreakList(commBreakList);
579  program_info->SaveCommFlagged(COMM_FLAG_DONE);
580  }
581 
583  program_info, commBreakList, cfp->GetTotalFrameCount(),
585  outputfilename);
586  }
587  else
588  {
589  if (useDB)
590  program_info->SaveCommFlagged(COMM_FLAG_NOT_FLAGGED);
591  }
592 
594  commDetector = nullptr;
595  sleep(1);
596  tmp->deleteLater();
597 
598  cer->deleteLater();
599  c->deleteLater();
600  b->deleteLater();
601  a->deleteLater();
602 
603  return comms_found;
604 }
605 
606 static qint64 GetFileSize(ProgramInfo *program_info)
607 {
608  QString filename = get_filename(program_info);
609  qint64 size = -1;
610 
611  if (filename.startsWith("myth://"))
612  {
613  RemoteFile remotefile(filename, false, false, 0);
614  size = remotefile.GetFileSize();
615  }
616  else
617  {
618  QFile file(filename);
619  if (file.exists())
620  {
621  size = file.size();
622  }
623  }
624 
625  return size;
626 }
627 
628 static bool DoesFileExist(ProgramInfo *program_info)
629 {
630  QString filename = get_filename(program_info);
631  qint64 size = GetFileSize(program_info);
632 
633  if (size < 0)
634  {
635  LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find file %1, aborting.")
636  .arg(filename));
637  return false;
638  }
639 
640  if (size == 0)
641  {
642  LOG(VB_GENERAL, LOG_ERR, QString("File %1 is zero-byte, aborting.")
643  .arg(filename));
644  return false;
645  }
646 
647  return true;
648 }
649 
650 static void UpdateFileSize(ProgramInfo *program_info)
651 {
652  qint64 size = GetFileSize(program_info);
653 
654  if (size != (qint64)program_info->GetFilesize())
655  program_info->SaveFilesize(size);
656 }
657 
658 static bool IsMarked(uint chanid, const QDateTime& starttime)
659 {
660  MSqlQuery mark_query(MSqlQuery::InitCon());
661  mark_query.prepare("SELECT commflagged, count(rm.type) "
662  "FROM recorded r "
663  "LEFT JOIN recordedmarkup rm ON "
664  "( r.chanid = rm.chanid AND "
665  "r.starttime = rm.starttime AND "
666  "type in (:MARK_START,:MARK_END)) "
667  "WHERE r.chanid = :CHANID AND "
668  "r.starttime = :STARTTIME "
669  "GROUP BY COMMFLAGGED;");
670  mark_query.bindValue(":MARK_START", MARK_COMM_START);
671  mark_query.bindValue(":MARK_END", MARK_COMM_END);
672  mark_query.bindValue(":CHANID", chanid);
673  mark_query.bindValue(":STARTTIME", starttime);
674 
675  if (mark_query.exec() && mark_query.isActive() &&
676  mark_query.size() > 0)
677  {
678  if (mark_query.next())
679  {
680  int flagStatus = mark_query.value(0).toInt();
681  int marksFound = mark_query.value(1).toInt();
682 
683  QString flagStatusStr = "UNKNOWN";
684  switch (flagStatus) {
686  flagStatusStr = "Not Flagged";
687  break;
688  case COMM_FLAG_DONE:
689  flagStatusStr = QString("Flagged with %1 breaks")
690  .arg(marksFound / 2);
691  break;
693  flagStatusStr = "Flagging";
694  break;
695  case COMM_FLAG_COMMFREE:
696  flagStatusStr = "Commercial Free";
697  break;
698  }
699 
700  LOG(VB_COMMFLAG, LOG_INFO,
701  QString("Status for chanid %1 @ %2 is '%3'")
702  .arg(chanid).arg(starttime.toString(Qt::ISODate))
703  .arg(flagStatusStr));
704 
705  if ((flagStatus == COMM_FLAG_NOT_FLAGGED) && (marksFound == 0))
706  return false;
707  }
708  }
709  return true;
710 }
711 
712 static int FlagCommercials(ProgramInfo *program_info, int jobid,
713  const QString &outputfilename, bool useDB, bool fullSpeed)
714 {
715  global_program_info = program_info;
716 
717  int breaksFound = 0;
718 
719  // configure commercial detection method
720  SkipTypes commDetectMethod = (SkipTypes)gCoreContext->GetNumSetting(
721  "CommercialSkipMethod", COMM_DETECT_ALL);
722 
723  if (cmdline.toBool("commmethod"))
724  {
725  // pull commercial detection method from command line
726  QString commmethod = cmdline.toString("commmethod");
727 
728  // assume definition as integer value
729  bool ok = true;
730  commDetectMethod = (SkipTypes) commmethod.toInt(&ok);
731  if (!ok)
732  {
733  // not an integer, attempt comma separated list
734  commDetectMethod = COMM_DETECT_UNINIT;
735 
736  QStringList list = commmethod.split(",", QString::SkipEmptyParts);
737  QStringList::const_iterator it = list.begin();
738  for (; it != list.end(); ++it)
739  {
740  QString val = (*it).toLower();
741  if (val == "off")
742  {
743  commDetectMethod = COMM_DETECT_OFF;
744  break;
745  }
746 
747  if (!skipTypes->contains(val))
748  {
749  cerr << "Failed to decode --method option '"
750  << val.toLatin1().constData()
751  << "'" << endl;
753  }
754 
755  if (commDetectMethod == COMM_DETECT_UNINIT) {
756  commDetectMethod = skipTypes->value(val);
757  } else {
758  commDetectMethod = (SkipTypes) ((int)commDetectMethod
759  | (int)skipTypes->value(val));
760  }
761  }
762 
763  }
764  if (commDetectMethod == COMM_DETECT_UNINIT)
766  }
767  else if (useDB)
768  {
769  // if not manually specified, and we have a database to access
770  // pull the commflag type from the channel
771  MSqlQuery query(MSqlQuery::InitCon());
772  query.prepare("SELECT commmethod FROM channel "
773  "WHERE chanid = :CHANID;");
774  query.bindValue(":CHANID", program_info->GetChanID());
775 
776  if (!query.exec())
777  {
778  // if the query fails, return with an error
779  commDetectMethod = COMM_DETECT_UNINIT;
780  MythDB::DBError("FlagCommercials", query);
781  }
782  else if (query.next())
783  {
784  commDetectMethod = (enum SkipTypes)query.value(0).toInt();
785  if (commDetectMethod == COMM_DETECT_COMMFREE)
786  {
787  // if the channel is commercial free, drop to the default instead
788  commDetectMethod =
790  "CommercialSkipMethod", COMM_DETECT_ALL);
791  LOG(VB_COMMFLAG, LOG_INFO,
792  QString("Chanid %1 is marked as being Commercial Free, "
793  "we will use the default commercial detection "
794  "method").arg(program_info->GetChanID()));
795  }
796  else if (commDetectMethod == COMM_DETECT_UNINIT)
797  // no value set, so use the database default
798  commDetectMethod =
800  "CommercialSkipMethod", COMM_DETECT_ALL);
801  LOG(VB_COMMFLAG, LOG_INFO,
802  QString("Using method: %1 from channel %2")
803  .arg(commDetectMethod).arg(program_info->GetChanID()));
804  }
805 
806  }
807  else if (!useDB)
808  {
809  // default to a cheaper method for debugging purposes
810  commDetectMethod = COMM_DETECT_BLANK;
811  }
812 
813  // if selection has failed, or intentionally disabled, drop out
814  if (commDetectMethod == COMM_DETECT_UNINIT)
815  return GENERIC_EXIT_NOT_OK;
816  if (commDetectMethod == COMM_DETECT_OFF)
817  return GENERIC_EXIT_OK;
818 
819  frm_dir_map_t blanks;
820  recorder = nullptr;
821 
822 /*
823  * is there a purpose to this not fulfilled by --getskiplist?
824  if (onlyDumpDBCommercialBreakList)
825  {
826  frm_dir_map_t commBreakList;
827  program_info->QueryCommBreakList(commBreakList);
828 
829  print_comm_flag_output(program_info, commBreakList,
830  0, nullptr, outputfilename);
831 
832  global_program_info = nullptr;
833  return GENERIC_EXIT_OK;
834  }
835 */
836 
837  if (!DoesFileExist(program_info))
838  {
839  LOG(VB_GENERAL, LOG_ERR,
840  "Unable to find file in defined storage paths.");
842  }
843 
844  QString filename = get_filename(program_info);
845 
846  RingBuffer *tmprbuf = RingBuffer::Create(filename, false);
847  if (!tmprbuf)
848  {
849  LOG(VB_GENERAL, LOG_ERR,
850  QString("Unable to create RingBuffer for %1").arg(filename));
851  global_program_info = nullptr;
853  }
854 
855  if (useDB)
856  {
858  {
859  LOG(VB_GENERAL, LOG_ERR, "Unable to open commflag DB connection");
860  delete tmprbuf;
861  global_program_info = nullptr;
862  return GENERIC_EXIT_DB_ERROR;
863  }
864  }
865 
867  kVideoIsNull |
868  kDecodeLowRes |
871  kNoITV);
872  /* blank detector needs to be only sample center for this optimization. */
873  if ((COMM_DETECT_BLANKS == commDetectMethod) ||
874  (COMM_DETECT_2_BLANK == commDetectMethod))
875  {
876  flags = (PlayerFlags) (flags | kDecodeFewBlocks);
877  }
878 
879  MythCommFlagPlayer *cfp = new MythCommFlagPlayer(flags);
881  ctx->SetPlayingInfo(program_info);
882  ctx->SetRingBuffer(tmprbuf);
883  ctx->SetPlayer(cfp);
884  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
885 
886  if (useDB)
887  {
888  if (program_info->GetRecordingEndTime() > MythDate::current())
889  {
891 
892  recorder = RemoteGetExistingRecorder(program_info);
893  if (recorder && (recorder->GetRecorderNumber() != -1))
894  {
896  watchingRecording = true;
897  ctx->SetRecorder(recorder);
898 
899  LOG(VB_COMMFLAG, LOG_INFO,
900  QString("mythcommflag will flag recording "
901  "currently in progress on cardid %1")
902  .arg(recorderNum));
903  }
904  else
905  {
906  recorderNum = -1;
907  watchingRecording = false;
908 
909  LOG(VB_GENERAL, LOG_ERR,
910  "Unable to find active recorder for this "
911  "recording, realtime flagging will not be enabled.");
912  }
914  }
915  }
916 
917  // TODO: Add back insertion of job if not in jobqueue
918 
919  breaksFound = DoFlagCommercials(
920  program_info, progress, fullSpeed, jobid,
921  cfp, commDetectMethod, outputfilename, useDB);
922 
923  if (progress)
924  cerr << breaksFound << "\n";
925 
926  LOG(VB_GENERAL, LOG_NOTICE, QString("Finished, %1 break(s) found.")
927  .arg(breaksFound));
928 
929  delete ctx;
930  global_program_info = nullptr;
931 
932  return breaksFound;
933 }
934 
935 static int FlagCommercials( uint chanid, const QDateTime &starttime,
936  int jobid, const QString &outputfilename,
937  bool fullSpeed )
938 {
939  QString startstring = MythDate::toString(starttime, MythDate::kFilename);
940  ProgramInfo pginfo(chanid, starttime);
941 
942  if (!pginfo.GetChanID())
943  {
944  LOG(VB_GENERAL, LOG_ERR,
945  QString("No program data exists for channel %1 at %2")
946  .arg(chanid).arg(startstring));
948  }
949 
950  if (!force && JobQueue::IsJobRunning(JOB_COMMFLAG, pginfo))
951  {
952  if (progress)
953  {
954  cerr << "IN USE\n";
955  cerr << " "
956  "(the program is already being flagged elsewhere)\n";
957  }
958  LOG(VB_GENERAL, LOG_ERR, "Program is already being flagged elsewhere");
959  return GENERIC_EXIT_IN_USE;
960  }
961 
962 
963  if (progress)
964  {
965  cerr << "MythTV Commercial Flagger, flagging commercials for:" << endl;
966  if (pginfo.GetSubtitle().isEmpty())
967  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << endl;
968  else
969  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << " - "
970  << pginfo.GetSubtitle().toLocal8Bit().constData() << endl;
971  }
972 
973  return FlagCommercials(&pginfo, jobid, outputfilename, true, fullSpeed);
974 }
975 
976 static int FlagCommercials(const QString& filename, int jobid,
977  const QString &outputfilename, bool useDB,
978  bool fullSpeed)
979 {
980 
981  if (progress)
982  {
983  cerr << "MythTV Commercial Flagger, flagging commercials for:" << endl
984  << " " << filename.toLatin1().constData() << endl;
985  }
986 
987  ProgramInfo pginfo(filename);
988  return FlagCommercials(&pginfo, jobid, outputfilename, useDB, fullSpeed);
989 }
990 
991 static int RebuildSeekTable(ProgramInfo *pginfo, int jobid, bool writefile = false)
992 {
993  QString filename = get_filename(pginfo);
994 
995  if (!DoesFileExist(pginfo))
996  {
997  // file not found on local filesystem
998  // assume file is in Video storage group on local backend
999  // and try again
1000 
1001  filename = QString("myth://Videos@%1/%2")
1002  .arg(gCoreContext->GetHostName()).arg(filename);
1003  pginfo->SetPathname(filename);
1004  if (!DoesFileExist(pginfo))
1005  {
1006  LOG(VB_GENERAL, LOG_ERR,
1007  QString("Unable to find file in defined storage "
1008  "paths for JobQueue ID# %1.").arg(jobid));
1010  }
1011  }
1012 
1013  // Update the file size since mythcommflag --rebuild is often used in user
1014  // scripts after transcoding or other size-changing operations
1015  UpdateFileSize(pginfo);
1016 
1017  RingBuffer *tmprbuf = RingBuffer::Create(filename, false);
1018  if (!tmprbuf)
1019  {
1020  LOG(VB_GENERAL, LOG_ERR,
1021  QString("Unable to create RingBuffer for %1").arg(filename));
1023  }
1024 
1027  kDecodeNoDecode | kNoITV));
1029  ctx->SetPlayingInfo(pginfo);
1030  ctx->SetRingBuffer(tmprbuf);
1031  ctx->SetPlayer(cfp);
1032  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
1033 
1034  if (progress)
1035  {
1036  QString time = QDateTime::currentDateTime().toString(Qt::TextDate);
1037  cerr << "Rebuild started at " << qPrintable(time) << endl;
1038  }
1039 
1040  if (writefile)
1042  cfp->RebuildSeekTable(progress);
1043  if (writefile)
1045 
1046  if (progress)
1047  {
1048  QString time = QDateTime::currentDateTime().toString(Qt::TextDate);
1049  cerr << "Rebuild completed at " << qPrintable(time) << endl;
1050  }
1051 
1052  delete ctx;
1053 
1054  return GENERIC_EXIT_OK;
1055 }
1056 
1057 static int RebuildSeekTable(const QString& filename, int jobid, bool writefile = false)
1058 {
1059  if (progress)
1060  {
1061  cerr << "MythTV Commercial Flagger, building seek table for:" << endl
1062  << " " << filename.toLatin1().constData() << endl;
1063  }
1064  ProgramInfo pginfo(filename);
1065  return RebuildSeekTable(&pginfo, jobid, writefile);
1066 }
1067 
1068 static int RebuildSeekTable(uint chanid, const QDateTime& starttime, int jobid, bool writefile = false)
1069 {
1070  ProgramInfo pginfo(chanid, starttime);
1071  if (progress)
1072  {
1073  cerr << "MythTV Commercial Flagger, building seek table for:" << endl;
1074  if (pginfo.GetSubtitle().isEmpty())
1075  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << endl;
1076  else
1077  cerr << " " << pginfo.GetTitle().toLocal8Bit().constData() << " - "
1078  << pginfo.GetSubtitle().toLocal8Bit().constData() << endl;
1079  }
1080  return RebuildSeekTable(&pginfo, jobid, writefile);
1081 }
1082 
1083 int main(int argc, char *argv[])
1084 {
1085  int result = GENERIC_EXIT_OK;
1086 
1087 // QString allStart = "19700101000000";
1088 // QString allEnd = MythDate::current().toString("yyyyMMddhhmmss");
1089  int jobType = JOB_NONE;
1090 
1091  if (!cmdline.Parse(argc, argv))
1092  {
1093  cmdline.PrintHelp();
1095  }
1096 
1097  if (cmdline.toBool("showhelp"))
1098  {
1099  cmdline.PrintHelp();
1100  return GENERIC_EXIT_OK;
1101  }
1102 
1103  if (cmdline.toBool("showversion"))
1104  {
1106  return GENERIC_EXIT_OK;
1107  }
1108 
1109  QCoreApplication a(argc, argv);
1110  QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHCOMMFLAG);
1111  int retval = cmdline.ConfigureLogging("general",
1112  !cmdline.toBool("noprogress"));
1113  if (retval != GENERIC_EXIT_OK)
1114  return retval;
1115 
1116  CleanupGuard callCleanup(cleanup);
1117 
1118 #ifndef _WIN32
1119  QList<int> signallist;
1120  signallist << SIGINT << SIGTERM << SIGSEGV << SIGABRT << SIGBUS << SIGFPE
1121  << SIGILL;
1122 #if ! CONFIG_DARWIN
1123  signallist << SIGRTMIN;
1124 #endif
1125  SignalHandler::Init(signallist);
1126  SignalHandler::SetHandler(SIGHUP, logSigHup);
1127 #endif
1128 
1130  if (!gContext->Init( false, /*use gui*/
1131  false, /*prompt for backend*/
1132  false, /*bypass auto discovery*/
1133  cmdline.toBool("skipdb"))) /*ignoreDB*/
1134  {
1135  LOG(VB_GENERAL, LOG_EMERG, "Failed to init MythContext, exiting.");
1137  }
1139 
1140  MythTranslation::load("mythfrontend");
1141 
1142  if (cmdline.toBool("outputmethod"))
1143  {
1144  QString om = cmdline.toString("outputmethod");
1145  if (outputTypes->contains(om))
1146  outputMethod = outputTypes->value(om);
1147  }
1148 
1149  if (cmdline.toBool("chanid") && cmdline.toBool("starttime"))
1150  {
1151  // operate on a recording in the database
1152  uint chanid = cmdline.toUInt("chanid");
1153  QDateTime starttime = cmdline.toDateTime("starttime");
1154 
1155  if (cmdline.toBool("clearskiplist"))
1156  return ClearSkipList(chanid, starttime);
1157  if (cmdline.toBool("gencutlist"))
1158  return CopySkipListToCutList(chanid, starttime);
1159  if (cmdline.toBool("clearcutlist"))
1160  return SetCutList(chanid, starttime, "");
1161  if (cmdline.toBool("setcutlist"))
1162  return SetCutList(chanid, starttime, cmdline.toString("setcutlist"));
1163  if (cmdline.toBool("getcutlist"))
1164  return GetMarkupList("cutlist", chanid, starttime);
1165  if (cmdline.toBool("getskiplist"))
1166  return GetMarkupList("commflag", chanid, starttime);
1167 
1168  // TODO: check for matching jobid
1169  // create temporary id to operate off of if not
1170 
1171  if (cmdline.toBool("queue"))
1172  QueueCommFlagJob(chanid, starttime, cmdline.toBool("rebuild"));
1173  else if (cmdline.toBool("rebuild"))
1174  result = RebuildSeekTable(chanid, starttime, -1);
1175  else
1176  result = FlagCommercials(chanid, starttime, -1,
1177  cmdline.toString("outputfile"), true);
1178  }
1179  else if (cmdline.toBool("jobid"))
1180  {
1181  jobID = cmdline.toInt("jobid");
1182  uint chanid;
1183  QDateTime starttime;
1184 
1185  if (!JobQueue::GetJobInfoFromID(jobID, jobType, chanid, starttime))
1186  {
1187  cerr << "mythcommflag: ERROR: Unable to find DB info for "
1188  << "JobQueue ID# " << jobID << endl;
1190  }
1191  force = true;
1192  int jobQueueCPU = gCoreContext->GetNumSetting("JobQueueCPU", 0);
1193 
1194  if (jobQueueCPU < 2)
1195  {
1196  myth_nice(17);
1197  myth_ioprio((0 == jobQueueCPU) ? 8 : 7);
1198  }
1199 
1200  progress = false;
1201 
1202  int ret = 0;
1203 
1205  RebuildSeekTable(chanid, starttime, jobID);
1206  else
1207  ret = FlagCommercials(chanid, starttime, jobID, "", jobQueueCPU != 0);
1208 
1209  if (ret > GENERIC_EXIT_NOT_OK)
1210  JobQueue::ChangeJobStatus(jobID, JOB_ERRORED,
1211  QCoreApplication::translate("(mythcommflag)",
1212  "Failed with exit status %1",
1213  "Job status").arg(ret));
1214  else
1215  JobQueue::ChangeJobStatus(jobID, JOB_FINISHED,
1216  QCoreApplication::translate("(mythcommflag)",
1217  "%n commercial break(s)",
1218  "Job status",
1219  ret));
1220  }
1221  else if (cmdline.toBool("video"))
1222  {
1223  // build skiplist for video file
1224  return RebuildSeekTable(cmdline.toString("video"), -1);
1225  }
1226  else if (cmdline.toBool("file"))
1227  {
1228  if (cmdline.toBool("skipdb"))
1229  {
1230  if (cmdline.toBool("rebuild"))
1231  {
1232  cerr << "The --rebuild parameter builds the seektable for "
1233  "internal MythTV use only. It cannot be used in "
1234  "combination with --skipdb." << endl;
1236  }
1237 
1238  if (!cmdline.toBool("outputfile"))
1239  cmdline.SetValue("outputfile", "-");
1240 
1241  // perform commercial flagging on file outside the database
1242  FlagCommercials(cmdline.toString("file"), -1,
1243  cmdline.toString("outputfile"),
1244  !cmdline.toBool("skipdb"),
1245  true);
1246  }
1247  else
1248  {
1249  ProgramInfo pginfo(cmdline.toString("file"));
1250  // pass chanid and starttime
1251  // inefficient, but it lets the other function
1252  // handle sanity checking
1253  if (cmdline.toBool("rebuild"))
1254  result = RebuildSeekTable(pginfo.GetChanID(),
1255  pginfo.GetRecordingStartTime(),
1256  -1, cmdline.toBool("writefile"));
1257  else
1258  result = FlagCommercials(pginfo.GetChanID(),
1259  pginfo.GetRecordingStartTime(),
1260  -1, cmdline.toString("outputfile"),
1261  true);
1262  }
1263  }
1264  else if (cmdline.toBool("queue"))
1265  {
1266  // run flagging for all recordings with no skiplist
1267  MSqlQuery query(MSqlQuery::InitCon());
1268  query.prepare("SELECT r.chanid, r.starttime, c.commmethod "
1269  "FROM recorded AS r "
1270  "LEFT JOIN channel AS c ON r.chanid=c.chanid "
1271 // "WHERE startime >= :STARTTIME AND endtime <= :ENDTIME "
1272  "ORDER BY starttime;");
1273  //query.bindValue(":STARTTIME", allStart);
1274  //query.bindValue(":ENDTIME", allEnd);
1275 
1276  if (query.exec() && query.isActive() && query.size() > 0)
1277  {
1278  QDateTime starttime;
1279 
1280  while (query.next())
1281  {
1282  starttime = MythDate::fromString(query.value(1).toString());
1283  uint chanid = query.value(0).toUInt();
1284 
1285  if (!cmdline.toBool("force") && !cmdline.toBool("rebuild"))
1286  {
1287  // recording is already flagged
1288  if (IsMarked(chanid, starttime))
1289  continue;
1290 
1291  // channel is marked as commercial free
1292  if (query.value(2).toInt() == COMM_DETECT_COMMFREE)
1293  continue;
1294 
1295  // recording rule did not enable commflagging
1296 #if 0
1297  RecordingInfo recinfo(chanid, starttime);
1298  if (!(recinfo.GetAutoRunJobs() & JOB_COMMFLAG))
1299  continue;
1300 #endif
1301  }
1302 
1303  QueueCommFlagJob(chanid, starttime, cmdline.toBool("rebuild"));
1304  }
1305  }
1306 
1307  }
1308  else
1309  {
1310  LOG(VB_GENERAL, LOG_ERR,
1311  "No valid combination of command inputs received.");
1312  cmdline.PrintHelp();
1314  }
1315 
1316  return result;
1317 }
1318 
1319 
1320 /* 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:782
static int CopySkipListToCutList(uint chanid, const QDateTime &starttime)
SkipTypes
This is used as a bitmask.
Definition: programtypes.h:91
Startup context for MythTV.
Definition: mythcontext.h:42
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
static int FlagCommercials(ProgramInfo *program_info, int jobid, const QString &outputfilename, bool useDB, bool fullSpeed)
static void commDetectorGotNewCommercialBreakList(void)
PlayerFlags
Definition: mythplayer.h:88
static int SetCutList(uint chanid, const QDateTime &starttime, QString newCutList)
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
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.
void SetPathname(const QString &) const
static void incomingCustomEvent(QEvent *e)
static Type MythEventMessage
Definition: mythevent.h:66
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
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:15
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:1462
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
void SetRingBuffer(RingBuffer *buf)
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
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:81
unsigned int uint
Definition: compat.h:140
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)
void SetRecorder(RemoteEncoder *rec)
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:63
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)
unsigned char b
Definition: ParseText.cpp:329
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:1014
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()
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
virtual void SaveFilesize(uint64_t fsize)
Sets recording file size in database, and sets "filesize" field.
This class is used as a container for messages.
Definition: mythevent.h:16
bool RebuildSeekTable(bool showPercentage=true, StatusCallback cb=nullptr, void *cbData=nullptr)
void SetPlayer(MythPlayer *newplayer)
static bool GetJobInfoFromID(int jobID, int &jobType, uint &chanid, QDateTime &recstartts)
Definition: jobqueue.cpp:669
#define GENERIC_EXIT_NO_RECORDING_DATA
No program/recording data.
Definition: exitcodes.h:29
static int DoFlagCommercials(ProgramInfo *program_info, bool showPercentage, bool fullSpeed, int jobid, MythCommFlagPlayer *cfp, enum SkipTypes commDetectMethod, const QString &outputfilename, bool useDB)
static bool ExtractKey(const QString &uniquekey, uint &chanid, QDateTime &recstartts)
Extracts chanid and recstartts from a unique key generated by MakeUniqueKey().
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:851
static int RebuildSeekTable(ProgramInfo *pginfo, int jobid, bool writefile=false)
static void Done(void)
static QMap< QString, OutputMethod > * init_output_types()
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
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:1092
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)
void SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
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:807
bool myth_nice(int val)
ProgramInfo * global_program_info
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QMap< QString, SkipTypes > * skipTypes
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
static void cleanup(VideoFilter *filter)
int GetRecorderNumber(void) const
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false) const
Returns filename or URL to be used to play back this recording.
#define SIGHUP
Definition: compat.h:213
uint64_t GetTotalFrameCount(void) const
Definition: mythplayer.h:194
static void commDetectorStatusUpdate(const QString &status)
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
static enum JobFlags GetJobFlags(int jobID)
Definition: jobqueue.cpp:1504
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 bool ChangeJobStatus(int jobID, int newStatus, const QString &comment="")
Definition: jobqueue.cpp:987
bool Init(const bool gui=true, const bool promptForBackend=false, const bool disableAutoDiscovery=false, const bool ignoreDB=false)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
Implements a file/stream reader/writer.
int ConfigureLogging(QString mask="general", unsigned int progress=0)
Read in logging options and initialize the logging interface.
#define GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:13
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
static qint64 GetFileSize(ProgramInfo *program_info)
const QString & Message() const
Definition: mythevent.h:58
#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
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:518
void SetWatchingRecording(bool mode)
Definition: mythplayer.cpp:343
static int GetMarkupList(const QString &list, uint chanid, const QDateTime &starttime)
virtual void deleteLater(void)
static QMap< QString, SkipTypes > * init_skip_types()
void PrintVersion(void) const
Print application version information.
Default UTC.
Definition: mythdate.h:14
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