00001 #include <qapplication.h>
00002 #include <qstring.h>
00003 #include <qregexp.h>
00004 #include <qsqldatabase.h>
00005 #include <qsqlquery.h>
00006 #include <qdir.h>
00007
00008 #include <iostream>
00009 #include <fstream>
00010 #include <string>
00011 #include <unistd.h>
00012 #include <cstdlib>
00013 #include <cstdio>
00014 #include <ctime>
00015 #include <cmath>
00016 #include <sys/time.h>
00017
00018 #include "libmyth/exitcodes.h"
00019 #include "libmyth/mythcontext.h"
00020 #include "libmythtv/NuppelVideoPlayer.h"
00021 #include "libmythtv/programinfo.h"
00022 #include "libmythtv/remoteutil.h"
00023 #include "libmythtv/jobqueue.h"
00024 #include "libmythtv/remoteencoder.h"
00025 #include "libmyth/mythdbcon.h"
00026
00027 #include "CommDetector.h"
00028 #include "CommDetectorBase.h"
00029 #include "CommDetectorFactory.h"
00030 #include "SlotRelayer.h"
00031 #include "CustomEventRelayer.h"
00032
00033 using namespace std;
00034
00035 bool quiet = false;
00036 bool force = false;
00037
00038 bool showPercentage = true;
00039 bool fullSpeed = true;
00040 bool rebuildSeekTable = false;
00041 bool beNice = true;
00042 bool inJobQueue = false;
00043 bool watchingRecording = false;
00044 CommDetectorBase* commDetector = NULL;
00045 RemoteEncoder* recorder = NULL;
00046 ProgramInfo* program_info = NULL;
00047 enum SkipTypes commDetectMethod = COMM_DETECT_UNINIT;
00048 int recorderNum = -1;
00049 bool dontSubmitCommbreakListToDB = false;
00050 QString outputfilename;
00051 bool onlyDumpDBCommercialBreakList = false;
00052
00053 int jobID = -1;
00054 int lastCmd = -1;
00055
00056 int BuildVideoMarkup(QString& filename)
00057 {
00058 program_info = new ProgramInfo;
00059 program_info->recstartts = QDateTime::currentDateTime().addSecs( -180 * 60);
00060 program_info->recendts = QDateTime::currentDateTime().addSecs(-1);
00061 program_info->isVideo = true;
00062 program_info->pathname = QFileInfo(filename).absFilePath();
00063
00064 RingBuffer *tmprbuf = new RingBuffer(filename, false);
00065 if (!tmprbuf)
00066 {
00067 VERBOSE(VB_IMPORTANT,
00068 QString("Unable to create RingBuffer for %1").arg(filename));
00069 delete program_info;
00070 return COMMFLAG_EXIT_NO_RINGBUFFER;
00071 }
00072
00073 if (!MSqlQuery::testDBConnection())
00074 {
00075 VERBOSE(VB_IMPORTANT, "Unable to open DB connection for commercial flagging.");
00076 delete tmprbuf;
00077 delete program_info;
00078 return COMMFLAG_EXIT_DB_ERROR;
00079 }
00080
00081 NuppelVideoPlayer *nvp = new NuppelVideoPlayer("seektable rebuilder",
00082 program_info);
00083 nvp->SetRingBuffer(tmprbuf);
00084
00085 nvp->RebuildSeekTable(!quiet);
00086
00087 cerr << "Rebuilt\n";
00088
00089 delete nvp;
00090 delete tmprbuf;
00091 delete program_info;
00092
00093 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00094 }
00095
00096 int QueueCommFlagJob(QString chanid, QString starttime)
00097 {
00098 ProgramInfo *pginfo =
00099 ProgramInfo::GetProgramFromRecorded(chanid, starttime);
00100
00101 if (!pginfo)
00102 {
00103 if (!quiet)
00104 cerr << "Unable to find program info for chanid " << chanid
00105 << " @ " << starttime << endl;
00106 return COMMFLAG_EXIT_NO_PROGRAM_DATA;
00107 }
00108
00109 bool result = JobQueue::QueueJob(JOB_COMMFLAG, pginfo->chanid,
00110 pginfo->recstartts);
00111
00112 if (result)
00113 {
00114 if (!quiet)
00115 cerr << "Job Queued for chanid " << chanid << " @ "
00116 << starttime << endl;
00117 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00118 }
00119 else
00120 {
00121 if (!quiet)
00122 cerr << "Error queueing job for chanid " << chanid
00123 << " @ " << starttime << endl;
00124 return COMMFLAG_EXIT_DB_ERROR;
00125 }
00126
00127 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00128 }
00129
00130 int CopySkipListToCutList(QString chanid, QString starttime)
00131 {
00132 QMap<long long, int> cutlist;
00133 QMap<long long, int>::Iterator it;
00134
00135 ProgramInfo *pginfo =
00136 ProgramInfo::GetProgramFromRecorded(chanid, starttime);
00137
00138 if (!pginfo)
00139 {
00140 VERBOSE(VB_IMPORTANT,
00141 QString("No program data exists for channel %1 at %2")
00142 .arg(chanid.ascii()).arg(starttime.ascii()));
00143 return COMMFLAG_BUGGY_EXIT_NO_CHAN_DATA;
00144 }
00145
00146 pginfo->GetCommBreakList(cutlist);
00147 for (it = cutlist.begin(); it != cutlist.end(); ++it)
00148 if (it.data() == MARK_COMM_START)
00149 cutlist[it.key()] = MARK_CUT_START;
00150 else
00151 cutlist[it.key()] = MARK_CUT_END;
00152 pginfo->SetCutList(cutlist);
00153
00154 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00155 }
00156
00157 int SetCutList(QString chanid, QString starttime, QString newCutList)
00158 {
00159 QMap<long long, int> cutlist;
00160
00161 newCutList.replace(QRegExp(" "), "");
00162
00163 QStringList tokens = QStringList::split(",", newCutList);
00164
00165 for (unsigned int i = 0; i < tokens.size(); i++)
00166 {
00167 QStringList cutpair = QStringList::split("-", tokens[i]);
00168 cutlist[cutpair[0].toInt()] = MARK_CUT_START;
00169 cutlist[cutpair[1].toInt()] = MARK_CUT_END;
00170 }
00171
00172 ProgramInfo *pginfo =
00173 ProgramInfo::GetProgramFromRecorded(chanid, starttime);
00174
00175 if (!pginfo)
00176 {
00177 VERBOSE(VB_IMPORTANT,
00178 QString("No program data exists for channel %1 at %2")
00179 .arg(chanid.ascii()).arg(starttime.ascii()));
00180 return COMMFLAG_BUGGY_EXIT_NO_CHAN_DATA;
00181 }
00182
00183 pginfo->SetCutList(cutlist);
00184
00185 VERBOSE(VB_IMPORTANT, QString("Cutlist set to: %1").arg(newCutList));
00186
00187 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00188 }
00189
00190 int GetMarkupList(QString list, QString chanid, QString starttime)
00191 {
00192 QMap<long long, int> cutlist;
00193 QMap<long long, int>::Iterator it;
00194 QString result;
00195
00196 ProgramInfo *pginfo =
00197 ProgramInfo::GetProgramFromRecorded(chanid, starttime);
00198
00199 if (!pginfo)
00200 {
00201 VERBOSE(VB_IMPORTANT,
00202 QString("No program data exists for channel %1 at %2")
00203 .arg(chanid.ascii()).arg(starttime.ascii()));
00204 return COMMFLAG_BUGGY_EXIT_NO_CHAN_DATA;
00205 }
00206
00207 if (list == "cutlist")
00208 pginfo->GetCutList(cutlist);
00209 else
00210 pginfo->GetCommBreakList(cutlist);
00211
00212 for (it = cutlist.begin(); it != cutlist.end(); ++it)
00213 {
00214 if ((it.data() == MARK_COMM_START) ||
00215 (it.data() == MARK_CUT_START))
00216 {
00217 if (result != "")
00218 result += ",";
00219 result += QString("%1-").arg(it.key());
00220 }
00221 else
00222 result += QString("%1").arg(it.key());
00223 }
00224
00225 if (list == "cutlist")
00226 cout << QString("Cutlist: %1\n").arg(result);
00227 else
00228 cout << QString("Commercial Skip List: %1\n").arg(result);
00229
00230 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00231 }
00232
00233 void streamOutCommercialBreakList(ostream& output,
00234 QMap<long long, int>& commercialBreakList)
00235 {
00236 if (commercialBreakList.empty())
00237 output << "No breaks" << endl;
00238 else
00239 {
00240 for (QMap<long long, int>::iterator it = commercialBreakList.begin();
00241 it!=commercialBreakList.end(); it++)
00242 {
00243 output << "framenum: " << it.key() << "\tmarktype: " << it.data()
00244 << endl;
00245 }
00246 }
00247 output << "----------------------------" << endl;
00248 }
00249
00250 void commDetectorBreathe()
00251 {
00252
00253
00254 qApp->processEvents();
00255
00256 if (jobID != -1)
00257 {
00258 int curCmd = JobQueue::GetJobCmd(jobID);
00259 if (curCmd == lastCmd)
00260 return;
00261
00262 switch (curCmd)
00263 {
00264 case JOB_STOP:
00265 {
00266 commDetector->stop();
00267 break;
00268 }
00269 case JOB_PAUSE:
00270 {
00271 JobQueue::ChangeJobStatus(jobID, JOB_PAUSED,
00272 QObject::tr("Paused"));
00273 commDetector->pause();
00274 break;
00275 }
00276 case JOB_RESUME:
00277 {
00278 JobQueue::ChangeJobStatus(jobID, JOB_RUNNING,
00279 QObject::tr("Running"));
00280 commDetector->resume();
00281 break;
00282 }
00283 }
00284 }
00285 }
00286
00287 void commDetectorStatusUpdate(const QString& status)
00288 {
00289 if (jobID != -1)
00290 JobQueue::ChangeJobStatus(jobID, JOB_RUNNING, status);
00291 }
00292
00293 void commDetectorGotNewCommercialBreakList()
00294 {
00295 QMap<long long, int> newCommercialMap;
00296 commDetector->getCommercialBreakList(newCommercialMap);
00297
00298 QMap<long long, int>::Iterator it = newCommercialMap.begin();
00299 QString message = "COMMFLAG_UPDATE ";
00300 message += program_info->chanid + " " +
00301 program_info->recstartts.toString(Qt::ISODate);
00302
00303 for (it = newCommercialMap.begin();
00304 it != newCommercialMap.end(); ++it)
00305 {
00306 if (it != newCommercialMap.begin())
00307 message += ",";
00308 else
00309 message += " ";
00310 message += QString("%1:%2").arg(it.key())
00311 .arg(it.data());
00312 }
00313
00314 VERBOSE(VB_COMMFLAG,
00315 QString("mythcommflag sending update: %1").arg(message));
00316
00317 RemoteSendMessage(message);
00318 }
00319
00320 void incomingCustomEvent(QCustomEvent* e)
00321 {
00322 if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
00323 {
00324 MythEvent *me = (MythEvent *)e;
00325 QString message = me->Message();
00326
00327 message = message.simplifyWhiteSpace();
00328 QStringList tokens = QStringList::split(" ", message);
00329
00330 VERBOSE(VB_COMMFLAG,
00331 QString("mythcommflag: Received Event: '%1'")
00332 .arg(message));
00333
00334 if ((watchingRecording) &&
00335 (tokens[0] == "DONE_RECORDING"))
00336 {
00337 int cardnum = tokens[1].toInt();
00338 int filelen = tokens[2].toInt();
00339
00340 message = QString("mythcommflag: Received a "
00341 "DONE_RECORDING event for card %1. ")
00342 .arg(cardnum);
00343
00344 if (recorderNum != -1 && cardnum == recorderNum)
00345 {
00346 commDetector->recordingFinished(filelen);
00347 watchingRecording = false;
00348 message += "Informed CommDetector that recording has finished.";
00349 VERBOSE(VB_COMMFLAG, message);
00350 }
00351 }
00352
00353 if (tokens[0] == "COMMFLAG_REQUEST")
00354 {
00355 QString chanid = tokens[1];
00356 QString starttime = tokens[2];
00357 QDateTime startts = QDateTime::fromString(starttime, Qt::ISODate);
00358
00359 message = QString("mythcommflag: Received a "
00360 "COMMFLAG_REQUEST event for chanid %1 @ %2. ")
00361 .arg(chanid).arg(starttime);
00362
00363 if ((program_info->chanid == chanid) &&
00364 (program_info->recstartts == startts))
00365 {
00366 commDetector->requestCommBreakMapUpdate();
00367 message += "Requested CommDetector to generate new break list.";
00368 VERBOSE(VB_COMMFLAG, message);
00369 }
00370 }
00371 }
00372 }
00373
00374 int DoFlagCommercials(bool showPercentage, bool fullSpeed, bool inJobQueue,
00375 NuppelVideoPlayer* nvp, enum SkipTypes commDetectMethod)
00376 {
00377 CommDetectorFactory factory;
00378 commDetector = factory.makeCommDetector(commDetectMethod, showPercentage,
00379 fullSpeed, nvp,
00380 program_info->chanid.toInt(),
00381 program_info->startts,
00382 program_info->endts,
00383 program_info->recstartts,
00384 program_info->recendts);
00385
00386 if (inJobQueue)
00387 {
00388 jobID = JobQueue::GetJobID(JOB_COMMFLAG, program_info->chanid,
00389 program_info->recstartts);
00390
00391 if (jobID != -1)
00392 VERBOSE(VB_COMMFLAG,
00393 QString("mythcommflag processing JobID %1").arg(jobID));
00394 else
00395 VERBOSE(VB_COMMFLAG, "mythcommflag: Unable to determine jobID");
00396 }
00397
00398 program_info->SetCommFlagged(COMM_FLAG_PROCESSING);
00399
00400 CustomEventRelayer cer(incomingCustomEvent);
00401 SlotRelayer a(commDetectorBreathe);
00402 SlotRelayer b(commDetectorStatusUpdate);
00403 SlotRelayer c(commDetectorGotNewCommercialBreakList);
00404 QObject::connect(commDetector, SIGNAL(breathe()), &a, SLOT(relay()));
00405 QObject::connect(commDetector, SIGNAL(statusUpdate(const QString&)), &b,
00406 SLOT(relay(const QString&)));
00407 QObject::connect(commDetector, SIGNAL(gotNewCommercialBreakList()), &c,
00408 SLOT(relay()));
00409
00410 VERBOSE(VB_COMMFLAG, "mythcommflag sending COMMFLAG_START notification");
00411 QString message = "COMMFLAG_START ";
00412 message += program_info->chanid + " " +
00413 program_info->recstartts.toString(Qt::ISODate);
00414 RemoteSendMessage(message);
00415
00416 bool result = commDetector->go();
00417 int comms_found = 0;
00418
00419 if (result)
00420 {
00421 QMap<long long, int> commBreakList;
00422 commDetector->getCommercialBreakList(commBreakList);
00423 comms_found = commBreakList.size() / 2;
00424
00425 if (!dontSubmitCommbreakListToDB)
00426 {
00427 program_info->SetMarkupFlag(MARK_UPDATED_CUT, true);
00428 program_info->SetCommBreakList(commBreakList);
00429 program_info->SetCommFlagged(COMM_FLAG_DONE);
00430 }
00431 if (outputfilename.length())
00432 {
00433 fstream outputstream(outputfilename, ios::app | ios::out );
00434 outputstream << "commercialBreakListFor: " << program_info->title
00435 << " on " << program_info->chanid << " @ "
00436 << program_info->recstartts.toString(Qt::ISODate)
00437 << endl;
00438 outputstream << "totalframecount: " << nvp->GetTotalFrameCount()
00439 << endl;
00440 streamOutCommercialBreakList(outputstream, commBreakList);
00441 }
00442 }
00443 else
00444 {
00445 if (!dontSubmitCommbreakListToDB)
00446 program_info->SetCommFlagged(COMM_FLAG_NOT_FLAGGED);
00447 }
00448
00449 delete commDetector;
00450
00451 return comms_found;
00452 }
00453
00454 int FlagCommercials(QString chanid, QString starttime)
00455 {
00456 int breaksFound = 0;
00457
00458 if (commDetectMethod == COMM_DETECT_UNINIT)
00459 {
00460 MSqlQuery query(MSqlQuery::InitCon());
00461 query.prepare("SELECT commmethod FROM channel "
00462 "WHERE chanid = :CHANID;");
00463 query.bindValue(":CHANID", chanid);
00464
00465 if (query.exec() && query.isActive() && query.size() > 0)
00466 {
00467 query.next();
00468 commDetectMethod = (enum SkipTypes)query.value(0).toInt();
00469 if (commDetectMethod == COMM_DETECT_COMMFREE)
00470 {
00471 commDetectMethod = COMM_DETECT_UNINIT;
00472 VERBOSE(VB_COMMFLAG,
00473 QString("Chanid %1 is marked as being Commercial Free, "
00474 "we will use the default commercial detection "
00475 "method").arg(chanid));
00476 }
00477 else if (commDetectMethod != COMM_DETECT_UNINIT)
00478 VERBOSE(VB_COMMFLAG, QString("Using method: %1 from channel %2")
00479 .arg(commDetectMethod).arg(chanid));
00480 }
00481 }
00482
00483 if (commDetectMethod == COMM_DETECT_UNINIT)
00484 commDetectMethod = (enum SkipTypes)gContext->GetNumSetting(
00485 "CommercialSkipMethod", COMM_DETECT_ALL);
00486 QMap<long long, int> blanks;
00487 recorder = NULL;
00488 program_info = ProgramInfo::GetProgramFromRecorded(chanid, starttime);
00489
00490 if (!program_info)
00491 {
00492 VERBOSE(VB_IMPORTANT,
00493 QString("No program data exists for channel %1 at %2")
00494 .arg(chanid.ascii()).arg(starttime.ascii()));
00495 return COMMFLAG_EXIT_NO_PROGRAM_DATA;
00496 }
00497
00498 if (onlyDumpDBCommercialBreakList)
00499 {
00500 QMap<long long, int> commBreakList;
00501 program_info->GetCommBreakList(commBreakList);
00502 if (outputfilename.length())
00503 {
00504 fstream output(outputfilename, ios::app | ios::out );
00505 output << "commercialBreakListFor: " << program_info->title
00506 << " on " << program_info->chanid << " @ "
00507 << program_info->recstartts.toString(Qt::ISODate) << endl;
00508 streamOutCommercialBreakList(output, commBreakList);
00509 }
00510 else
00511 {
00512 cout << "commercialBreakListFor: " << program_info->title
00513 << " on " << program_info->chanid << " @ "
00514 << program_info->recstartts.toString(Qt::ISODate) << endl;
00515 streamOutCommercialBreakList(cout, commBreakList);
00516 }
00517 delete program_info;
00518 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00519 }
00520
00521
00522 if (!quiet)
00523 {
00524 cerr << chanid.leftJustify(6, ' ', true) << " "
00525 << starttime.leftJustify(14, ' ', true) << " "
00526 << program_info->title.leftJustify(41, ' ', true) << " ";
00527 cerr.flush();
00528 }
00529
00530 if (!force && JobQueue::IsJobRunning(JOB_COMMFLAG, program_info))
00531 {
00532 if (!quiet)
00533 {
00534 cerr << "IN USE\n";
00535 cerr << " "
00536 "(the program is already being flagged elsewhere)\n";
00537 }
00538 return COMMFLAG_EXIT_IN_USE;
00539 }
00540
00541 QString filename = program_info->GetPlaybackURL(true);
00542
00543 RingBuffer *tmprbuf = new RingBuffer(filename, false);
00544 if (!tmprbuf)
00545 {
00546 VERBOSE(VB_IMPORTANT,
00547 QString("Unable to create RingBuffer for %1").arg(filename));
00548 delete program_info;
00549 return COMMFLAG_EXIT_NO_RINGBUFFER;
00550 }
00551
00552 if (!MSqlQuery::testDBConnection())
00553 {
00554 VERBOSE(VB_IMPORTANT, "Unable to open commflag DB connection");
00555 delete tmprbuf;
00556 delete program_info;
00557 return COMMFLAG_EXIT_DB_ERROR;
00558 }
00559
00560 NuppelVideoPlayer* nvp = new NuppelVideoPlayer("flagger", program_info);
00561 nvp->SetRingBuffer(tmprbuf);
00562
00563 if (rebuildSeekTable)
00564 {
00565 nvp->RebuildSeekTable();
00566
00567 if (!quiet)
00568 cerr << "Rebuilt\n";
00569
00570 delete nvp;
00571 delete tmprbuf;
00572 delete program_info;
00573
00574 return COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00575 }
00576
00577 if (program_info->recendts > QDateTime::currentDateTime())
00578 {
00579 gContext->ConnectToMasterServer();
00580
00581 recorder = RemoteGetExistingRecorder(program_info);
00582 if (recorder && (recorder->GetRecorderNumber() != -1))
00583 {
00584 recorderNum = recorder->GetRecorderNumber();
00585 watchingRecording = true;
00586 nvp->SetRecorder(recorder);
00587
00588 VERBOSE(VB_COMMFLAG, QString("mythcommflag will flag recording "
00589 "currently in progress on cardid %1").arg(recorderNum));
00590 }
00591 else
00592 {
00593 recorderNum = -1;
00594 watchingRecording = false;
00595
00596 VERBOSE(VB_IMPORTANT, "Unable to find active recorder for this "
00597 "recording, realtime flagging will not be enabled.");
00598 }
00599 nvp->SetWatchingRecording(watchingRecording);
00600 }
00601
00602 int fakeJobID = -1;
00603 if (!inJobQueue)
00604 {
00605 JobQueue::QueueJob(JOB_COMMFLAG, program_info->chanid,
00606 program_info->recstartts, "", "",
00607 gContext->GetHostName(), JOB_EXTERNAL,
00608 JOB_RUNNING);
00609 fakeJobID = JobQueue::GetJobID(JOB_COMMFLAG, program_info->chanid,
00610 program_info->recstartts);
00611 jobID = fakeJobID;
00612 VERBOSE(VB_COMMFLAG,
00613 QString("Not in JobQueue, creating fake Job ID %1").arg(jobID));
00614 }
00615 else
00616 jobID = -1;
00617
00618
00619 breaksFound = DoFlagCommercials(showPercentage, fullSpeed, inJobQueue,
00620 nvp, commDetectMethod);
00621
00622 if (fakeJobID >= 0)
00623 {
00624 jobID = -1;
00625 JobQueue::ChangeJobStatus(fakeJobID, JOB_FINISHED,
00626 QObject::tr("Finished, %1 break(s) found.").arg(breaksFound));
00627 }
00628
00629 if (!quiet)
00630 cerr << breaksFound << "\n";
00631
00632 delete nvp;
00633 delete tmprbuf;
00634 delete program_info;
00635
00636 return breaksFound;
00637 }
00638
00639 int main(int argc, char *argv[])
00640 {
00641 QApplication a(argc, argv, false);
00642 int argpos = 1;
00643 bool isVideo = false;
00644 int result = COMMFLAG_EXIT_NO_ERROR_WITH_NO_BREAKS;
00645
00646 QString filename;
00647
00648 QString chanid;
00649 QString starttime;
00650 QString allStart = "19700101000000";
00651 QString allEnd = QDateTime::currentDateTime().toString("yyyyMMddhhmmss");
00652 int jobID = -1;
00653 int jobType = JOB_NONE;
00654 QDir fullfile;
00655 time_t time_now;
00656 bool allRecorded = false;
00657 bool queueJobInstead = false;
00658 bool copyToCutlist = false;
00659 bool clearCutlist = false;
00660 bool getCutlist = false;
00661 bool getSkipList = false;
00662 QString newCutList = QString::null;
00663 QMap<QString, QString> settingsOverride;
00664
00665 QFileInfo finfo(a.argv()[0]);
00666
00667 QString binname = finfo.baseName();
00668
00669 print_verbose_messages = VB_IMPORTANT;
00670 verboseString = "important";
00671
00672 while (argpos < a.argc())
00673 {
00674 if (!strcmp(a.argv()[argpos],"-c") ||
00675 !strcmp(a.argv()[argpos],"--chanid"))
00676 {
00677 if (((argpos + 1) >= a.argc()) ||
00678 !strncmp(a.argv()[argpos + 1], "--", 2))
00679 {
00680 VERBOSE(VB_IMPORTANT,
00681 "Missing or invalid parameters for --chanid option");
00682 return COMMFLAG_EXIT_INVALID_CHANID;
00683 }
00684
00685 chanid += a.argv()[++argpos];
00686 }
00687 else if (!strcmp(a.argv()[argpos],"-s") ||
00688 !strcmp(a.argv()[argpos],"--starttime"))
00689 {
00690 if (((argpos + 1) >= a.argc()) ||
00691 !strncmp(a.argv()[argpos + 1], "--", 2))
00692 {
00693 VERBOSE(VB_IMPORTANT,
00694 "Missing or invalid parameters for --starttime option");
00695 return COMMFLAG_EXIT_INVALID_STARTTIME;
00696 }
00697
00698 starttime += a.argv()[++argpos];
00699 }
00700 else if (!strcmp(a.argv()[argpos],"-f") ||
00701 !strcmp(a.argv()[argpos],"--file"))
00702 fullfile = a.argv()[++argpos];
00703 else if (!strcmp(a.argv()[argpos],"--video"))
00704 {
00705 filename = (a.argv()[++argpos]);
00706 VERBOSE(VB_GENERAL, filename);
00707 isVideo = true;
00708 rebuildSeekTable = true;
00709 beNice = false;
00710 }
00711 else if (!strcmp(a.argv()[argpos],"--method"))
00712 {
00713 QString method = (a.argv()[++argpos]);
00714 bool ok;
00715 commDetectMethod = (enum SkipTypes)method.toInt(&ok);
00716 if (!ok)
00717 commDetectMethod = COMM_DETECT_UNINIT;
00718 }
00719 else if (!strcmp(a.argv()[argpos], "--gencutlist"))
00720 copyToCutlist = true;
00721 else if (!strcmp(a.argv()[argpos], "--clearcutlist"))
00722 clearCutlist = true;
00723 else if (!strcmp(a.argv()[argpos], "--getcutlist"))
00724 getCutlist = true;
00725 else if (!strcmp(a.argv()[argpos], "--getskiplist"))
00726 getSkipList = true;
00727 else if (!strcmp(a.argv()[argpos], "--setcutlist"))
00728 newCutList = (a.argv()[++argpos]);
00729 else if (!strcmp(a.argv()[argpos], "-j"))
00730 jobID = QString(a.argv()[++argpos]).toInt();
00731 else if (!strcmp(a.argv()[argpos], "--all"))
00732 {
00733 allRecorded = true;
00734 }
00735 else if (!strcmp(a.argv()[argpos], "--allstart"))
00736 {
00737 if (((argpos + 1) >= a.argc()) ||
00738 !strncmp(a.argv()[argpos + 1], "--", 2))
00739 {
00740 cerr << "Missing or invalid parameter for --allstart\n";
00741 return COMMFLAG_EXIT_INVALID_STARTTIME;
00742 }
00743
00744 allStart = a.argv()[++argpos];
00745 }
00746 else if (!strcmp(a.argv()[argpos], "--allend"))
00747 {
00748 if (((argpos + 1) >= a.argc()) ||
00749 !strncmp(a.argv()[argpos + 1], "--", 2))
00750 {
00751 cerr << "Missing or invalid parameter for --allend\n";
00752 return COMMFLAG_EXIT_INVALID_STARTTIME;
00753 }
00754
00755 allEnd = a.argv()[++argpos];
00756 }
00757 else if (!strcmp(a.argv()[argpos], "--quiet"))
00758 {
00759 quiet = true;
00760 showPercentage = false;
00761 }
00762 else if (!strcmp(a.argv()[argpos], "--queue"))
00763 {
00764 queueJobInstead = true;
00765 }
00766 else if (!strcmp(a.argv()[argpos], "--sleep"))
00767 {
00768 fullSpeed = false;
00769 }
00770 else if (!strcmp(a.argv()[argpos], "--nopercentage"))
00771 {
00772 showPercentage = false;
00773 }
00774 else if (!strcmp(a.argv()[argpos], "--rebuild"))
00775 {
00776 rebuildSeekTable = true;
00777 beNice = false;
00778 }
00779 else if (!strcmp(a.argv()[argpos], "--force"))
00780 {
00781 force = true;
00782 }
00783 else if (!strcmp(a.argv()[argpos], "--hogcpu"))
00784 {
00785 beNice = false;
00786 }
00787 else if (!strcmp(a.argv()[argpos], "--dontwritetodb"))
00788 {
00789 dontSubmitCommbreakListToDB = true;
00790 }
00791 else if (!strcmp(a.argv()[argpos], "--onlydumpdb"))
00792 {
00793 onlyDumpDBCommercialBreakList = true;
00794 }
00795 else if (!strcmp(a.argv()[argpos], "--outputfile"))
00796 {
00797 if (a.argc() > argpos)
00798 {
00799 outputfilename = a.argv()[++argpos];
00800
00801 fstream output(outputfilename, ios::out);
00802 }
00803 else
00804 {
00805 cerr << "Missing argument to --outputfile option\n";
00806 return -1;
00807 }
00808 }
00809
00810 else if (!strcmp(a.argv()[argpos],"-V"))
00811 {
00812 if (a.argc() > argpos)
00813 {
00814 QString temp = a.argv()[++argpos];
00815 print_verbose_messages = temp.toInt();
00816 }
00817 else
00818 {
00819 VERBOSE(VB_IMPORTANT, "Missing argument to -V option");
00820 return COMMFLAG_EXIT_INVALID_CMDLINE;
00821 }
00822 }
00823 else if (!strcmp(a.argv()[argpos],"-v") ||
00824 !strcmp(a.argv()[argpos],"--verbose"))
00825 {
00826 if (a.argc()-1 > argpos)
00827 {
00828 if (parse_verbose_arg(a.argv()[argpos+1]) ==
00829 GENERIC_EXIT_INVALID_CMDLINE)
00830 return COMMFLAG_EXIT_INVALID_CMDLINE;
00831
00832 ++argpos;
00833 }
00834 else
00835 {
00836 VERBOSE(VB_IMPORTANT,
00837 "Missing argument to -v/--verbose option");
00838 return COMMFLAG_EXIT_INVALID_CMDLINE;
00839 }
00840 }
00841 else if (!strcmp(a.argv()[argpos],"-O") ||
00842 !strcmp(a.argv()[argpos],"--override-setting"))
00843 {
00844 if ((a.argc() - 1) > argpos)
00845 {
00846 QString tmpArg = a.argv()[argpos+1];
00847 if (tmpArg.startsWith("-"))
00848 {
00849 cerr << "Invalid or missing argument to "
00850 "-O/--override-setting option\n";
00851 return BACKEND_EXIT_INVALID_CMDLINE;
00852 }
00853
00854 QStringList pairs = QStringList::split(",", tmpArg);
00855 for (unsigned int index = 0; index < pairs.size(); ++index)
00856 {
00857 QStringList tokens = QStringList::split("=", pairs[index]);
00858 tokens[0].replace(QRegExp("^[\"']"), "");
00859 tokens[0].replace(QRegExp("[\"']$"), "");
00860 tokens[1].replace(QRegExp("^[\"']"), "");
00861 tokens[1].replace(QRegExp("[\"']$"), "");
00862 settingsOverride[tokens[0]] = tokens[1];
00863 }
00864 }
00865 else
00866 {
00867 cerr << "Invalid or missing argument to -O/--override-setting "
00868 "option\n";
00869 return GENERIC_EXIT_INVALID_CMDLINE;
00870 }
00871
00872 ++argpos;
00873 }
00874 else if (!strcmp(a.argv()[argpos],"-h") ||
00875 !strcmp(a.argv()[argpos],"--help"))
00876 {
00877 VERBOSE(VB_IMPORTANT,
00878 "Valid Options are:\n"
00879 "-c OR --chanid chanid Flag recording with given channel ID\n"
00880 "-s OR --starttime starttime Flag recording with given starttime\n"
00881 "-f OR --file filename Flag recording with specific filename\n"
00882 "--video filename Rebuild the seektable for a video (non-recording) file\n"
00883 "--sleep Give up some CPU time after processing each frame\n"
00884 "--nopercentage Don't print percentage done\n"
00885 "--rebuild Do not flag commercials, just rebuild seektable\n"
00886 "--gencutlist Copy the commercial skip list to the cutlist\n"
00887 "--clearcutlist Clear the cutlist\n"
00888 "--setcutlist CUTLIST Set a new cutlist. CUTLIST is of the form:\n"
00889 " #-#[,#-#]... (ie, 1-100,1520-3012,4091-5094\n"
00890 "--getcutlist Display the current cutlist\n"
00891 "--getskiplist Display the current Commercial Skip list\n"
00892 "-v or --verbose debug-level Use '-v help' for level info\n"
00893 "--queue Insert flagging job into the JobQueue rather than\n"
00894 " running flagging in the foreground.\n"
00895 " WARNING: This option does NOT work with --rebuild\n"
00896 "--quiet Turn OFF display (also causes the program to\n"
00897 " sleep a little every frame so it doesn't hog CPU)\n"
00898 "--all Re-run commercial flagging for all recorded\n"
00899 " programs using current detection method.\n"
00900 "--allstart YYYYMMDDHHMMSS when using --all, only flag programs starting\n"
00901 " after the 'allstart' date (default = Jan 1, 1970).\n"
00902 "--allend YYYYMMDDHHMMSS when using --all, only flag programs ending\n"
00903 " before the 'allend' date (default is now).\n"
00904 "--force Force flagging of a video even if mythcommflag\n"
00905 " thinks it is already in use by another instance.\n"
00906 "--hogcpu Do not nice the flagging process.\n"
00907 " WARNING: This will consume all free CPU time.\n"
00908 "-h OR --help This text\n\n"
00909 "Note: both --chanid and --starttime must be used together\n"
00910 " if either is used.\n\n"
00911 "If no command line arguments are specified, all\n"
00912 "unflagged videos will be flagged.\n\n");
00913 return COMMFLAG_EXIT_INVALID_CMDLINE;
00914 }
00915 else
00916 {
00917 VERBOSE(VB_IMPORTANT, QString("Illegal option: '%1' (use --help)")
00918 .arg(a.argv()[argpos]));
00919 return COMMFLAG_EXIT_INVALID_CMDLINE;
00920 }
00921
00922 ++argpos;
00923 }
00924
00925 gContext = NULL;
00926 gContext = new MythContext(MYTH_BINARY_VERSION);
00927 if (!gContext->Init(false))
00928 {
00929 VERBOSE(VB_IMPORTANT, "Failed to init MythContext, exiting.");
00930 return COMMFLAG_EXIT_NO_MYTHCONTEXT;
00931 }
00932
00933 if (settingsOverride.size())
00934 {
00935 QMap<QString, QString>::iterator it;
00936 for (it = settingsOverride.begin(); it != settingsOverride.end(); ++it)
00937 {
00938 VERBOSE(VB_IMPORTANT, QString("Setting '%1' being forced to '%2'")
00939 .arg(it.key()).arg(it.data()));
00940 gContext->OverrideSettingForSession(it.key(), it.data());
00941 }
00942 }
00943
00944 if (jobID != -1)
00945 {
00946 if (JobQueue::GetJobInfoFromID(jobID, jobType, chanid, starttime))
00947 {
00948 inJobQueue = true;
00949 force = true;
00950 }
00951 else
00952 {
00953 cerr << "mythcommflag: ERROR: Unable to find DB info for "
00954 << "JobQueue ID# " << jobID << endl;
00955 return COMMFLAG_EXIT_NO_PROGRAM_DATA;
00956 }
00957 }
00958
00959 if (fullfile.path() != ".")
00960 {
00961 MSqlQuery query(MSqlQuery::InitCon());
00962 query.prepare("SELECT chanid, starttime FROM recorded "
00963 "WHERE basename = :BASENAME ;");
00964 query.bindValue(":BASENAME", fullfile.dirName());
00965
00966 if (query.exec() && query.isActive() && query.size() > 0 &&
00967 query.next())
00968 {
00969 chanid = query.value(0).toString();
00970 starttime = query.value(1).toDateTime().toString("yyyyMMddhhmmss");
00971 }
00972 else
00973 {
00974 cerr << "mythcommflag: ERROR: Unable to find DB info for "
00975 << fullfile.dirName() << endl;
00976 return COMMFLAG_EXIT_NO_PROGRAM_DATA;
00977 }
00978 }
00979
00980 if ((chanid.isEmpty() && !starttime.isEmpty()) ||
00981 (!chanid.isEmpty() && starttime.isEmpty()))
00982 {
00983 VERBOSE(VB_IMPORTANT, "You must specify both the Channel ID "
00984 "and the Start Time.");
00985 return COMMFLAG_EXIT_INVALID_CMDLINE;
00986 }
00987
00988 if (copyToCutlist)
00989 return CopySkipListToCutList(chanid, starttime);
00990
00991 if (clearCutlist)
00992 return SetCutList(chanid, starttime, "");
00993
00994 if (!newCutList.isNull())
00995 return SetCutList(chanid, starttime, newCutList);
00996
00997 if (getCutlist)
00998 return GetMarkupList("cutlist", chanid, starttime);
00999
01000 if (getSkipList)
01001 return GetMarkupList("commflag", chanid, starttime);
01002
01003 if (inJobQueue)
01004 {
01005 int jobQueueCPU = gContext->GetNumSetting("JobQueueCPU", 0);
01006
01007 if (jobQueueCPU < 2)
01008 nice(17);
01009
01010 if (jobQueueCPU)
01011 fullSpeed = true;
01012 else
01013 fullSpeed = false;
01014
01015 quiet = true;
01016 isVideo = false;
01017 showPercentage = false;
01018
01019 int breaksFound = FlagCommercials(chanid, starttime);
01020
01021 delete gContext;
01022
01023 return breaksFound;
01024 }
01025
01026
01027 if (beNice)
01028 nice(17);
01029
01030 time_now = time(NULL);
01031 if (!quiet)
01032 {
01033 VERBOSE(VB_IMPORTANT, QString("%1 version: %2 www.mythtv.org")
01034 .arg(binname).arg(MYTH_BINARY_VERSION));
01035
01036 VERBOSE(VB_IMPORTANT, QString("Enabled verbose msgs: %1").arg(verboseString));
01037
01038 cerr << "\nMythTV Commercial Flagger, started at "
01039 << ctime(&time_now);
01040
01041 if (!isVideo)
01042 {
01043 if (!rebuildSeekTable)
01044 {
01045 cerr << "Flagging commercial breaks for:\n";
01046 if (a.argc() == 1)
01047 cerr << "ALL Un-flagged programs\n";
01048 }
01049 else
01050 {
01051 cerr << "Rebuilding SeekTable(s) for:\n";
01052 }
01053
01054 cerr << "ChanID Start Time "
01055 "Title ";
01056 if (rebuildSeekTable)
01057 cerr << "Status\n";
01058 else
01059 cerr << "Breaks\n";
01060
01061 cerr << "------ -------------- "
01062 "----------------------------------------- ------\n";
01063 }
01064 else
01065 {
01066 cerr << "Building seek table for: " << filename << "\n";
01067 }
01068 }
01069
01070 if (isVideo)
01071 {
01072 result = BuildVideoMarkup(filename);
01073 }
01074 else if (!chanid.isEmpty() && !starttime.isEmpty())
01075 {
01076 if (queueJobInstead)
01077 QueueCommFlagJob(chanid, starttime);
01078 else
01079 result = FlagCommercials(chanid, starttime);
01080 }
01081 else
01082 {
01083 MSqlQuery query(MSqlQuery::InitCon());
01084 query.prepare(
01085 "SELECT chanid, starttime "
01086 "FROM recorded "
01087 "WHERE starttime >= :STARTTIME AND endtime <= :ENDTIME "
01088 "ORDER BY starttime;");
01089 query.bindValue(":STARTTIME", allStart);
01090 query.bindValue(":ENDTIME", allEnd);
01091
01092 if (query.exec() && query.isActive() && query.size() > 0)
01093 {
01094 while (query.next())
01095 {
01096 QDateTime m_starttime =
01097 QDateTime::fromString(query.value(1).toString(),
01098 Qt::ISODate);
01099 starttime = m_starttime.toString("yyyyMMddhhmmss");
01100
01101 chanid = query.value(0).toString();
01102
01103 if ( allRecorded )
01104 {
01105 if (queueJobInstead)
01106 QueueCommFlagJob(chanid, starttime);
01107 else
01108 FlagCommercials(chanid, starttime);
01109 }
01110 else
01111 {
01112
01113 MSqlQuery mark_query(MSqlQuery::InitCon());
01114 mark_query.prepare("SELECT commflagged, count(rm.type) "
01115 "FROM recorded r "
01116 "LEFT JOIN recordedmarkup rm ON "
01117 "( r.chanid = rm.chanid AND "
01118 "r.starttime = rm.starttime AND "
01119 "type in (:MARK_START,:MARK_END)) "
01120 "WHERE r.chanid = :CHANID AND "
01121 "r.starttime = :STARTTIME "
01122 "GROUP BY COMMFLAGGED;");
01123 mark_query.bindValue(":MARK_START", MARK_COMM_START);
01124 mark_query.bindValue(":MARK_END", MARK_COMM_END);
01125 mark_query.bindValue(":CHANID", chanid);
01126 mark_query.bindValue(":STARTTIME", m_starttime);
01127
01128 if (mark_query.exec() && mark_query.isActive() &&
01129 mark_query.size() > 0)
01130 {
01131 if (mark_query.next())
01132 {
01133 int flagStatus = mark_query.value(0).toInt();
01134 int marksFound = mark_query.value(1).toInt();
01135
01136 QString flagStatusStr = "UNKNOWN";
01137 switch (flagStatus) {
01138 case COMM_FLAG_NOT_FLAGGED:
01139 flagStatusStr = "Not Flagged";
01140 break;
01141 case COMM_FLAG_DONE:
01142 flagStatusStr =
01143 QString("Flagged with %1 breaks")
01144 .arg(marksFound / 2);
01145 break;
01146 case COMM_FLAG_PROCESSING:
01147 flagStatusStr = "Flagging";
01148 break;
01149 case COMM_FLAG_COMMFREE:
01150 flagStatusStr = "Commercial Free";
01151 break;
01152 }
01153
01154 VERBOSE(VB_COMMFLAG,
01155 QString("Status for chanid %1 @ %2 is '%3'")
01156 .arg(chanid)
01157 .arg(starttime)
01158 .arg(flagStatusStr));
01159
01160 if ((flagStatus == COMM_FLAG_NOT_FLAGGED) &&
01161 (marksFound == 0))
01162 {
01163 if (queueJobInstead)
01164 QueueCommFlagJob(chanid, starttime);
01165 else
01166 FlagCommercials(chanid, starttime);
01167 }
01168 }
01169 }
01170 }
01171 }
01172 }
01173 else
01174 {
01175 MythContext::DBError("Querying recorded programs", query);
01176 return COMMFLAG_EXIT_DB_ERROR;
01177 }
01178 }
01179
01180 delete gContext;
01181
01182 time_now = time(NULL);
01183
01184 cerr << "\nFinished commercial break flagging at "
01185 << ctime(&time_now) << "\n";
01186
01187 return result;
01188 }
01189
01190
01191