00001
00002
00003
00004 #include <sys/types.h>
00005 #include <unistd.h>
00006
00007
00008 #include <cstdlib>
00009
00010
00011 #include <iostream>
00012 #include <algorithm>
00013 using namespace std;
00014
00015
00016 #include <QRegExp>
00017 #include <QMap>
00018 #include <QUrl>
00019 #include <QFile>
00020 #include <QFileInfo>
00021 #include <QDir>
00022
00023
00024 #include "programinfo.h"
00025 #include "mythmiscutil.h"
00026 #include "mythcorecontext.h"
00027 #include "dialogbox.h"
00028 #include "remoteutil.h"
00029 #include "netutils.h"
00030 #include "mythdb.h"
00031 #include "mythlogging.h"
00032 #include "storagegroup.h"
00033 #include "programinfoupdater.h"
00034 #include "mythscheduler.h"
00035 #include "remotefile.h"
00036
00037 #define LOC QString("ProgramInfo(%1): ").arg(GetBasename())
00038
00039
00040
00041 static int init_tr(void);
00042
00043 int pginfo_init_statics() { return ProgramInfo::InitStatics(); }
00044 QMutex ProgramInfo::staticDataLock;
00045 ProgramInfoUpdater *ProgramInfo::updater;
00046 int dummy = pginfo_init_statics();
00047 bool ProgramInfo::usingProgIDAuth = true;
00048
00049
00050 const QString ProgramInfo::kFromRecordedQuery =
00051 "SELECT r.title, r.subtitle, r.description, "
00052 " r.season, r.episode, r.category, "
00053 " r.chanid, c.channum, c.callsign, "
00054 " c.name, c.outputfilters,r.recgroup, "
00055 " r.playgroup, r.storagegroup, r.basename, "
00056 " r.hostname, r.recpriority, r.seriesid, "
00057 " r.programid, r.inetref, r.filesize, "
00058 " r.progstart, r.progend, r.stars, "
00059 " r.starttime, r.endtime, p.airdate+0, "
00060 " r.originalairdate, r.lastmodified, r.recordid, "
00061 " c.commmethod, r.commflagged, r.previouslyshown, "
00062 " r.transcoder, r.transcoded, r.deletepending, "
00063 " r.preserve, r.cutlist, r.autoexpire, "
00064 " r.editing, r.bookmark, r.watched, "
00065 " p.audioprop+0, p.videoprop+0, p.subtitletypes+0, "
00066 " r.findid, rec.dupin, rec.dupmethod "
00067 "FROM recorded AS r "
00068 "LEFT JOIN channel AS c "
00069 "ON (r.chanid = c.chanid) "
00070 "LEFT JOIN recordedprogram AS p "
00071 "ON (r.chanid = p.chanid AND "
00072 " r.progstart = p.starttime) "
00073 "LEFT JOIN record AS rec "
00074 "ON (r.recordid = rec.recordid) ";
00075
00076 static void set_flag(uint32_t &flags, int flag_to_set, bool is_set)
00077 {
00078 flags &= ~flag_to_set;
00079 if (is_set)
00080 flags |= flag_to_set;
00081 }
00082
00086 ProgramInfo::ProgramInfo(void) :
00087 title(),
00088 subtitle(),
00089 description(),
00090 season(0),
00091 episode(0),
00092 category(),
00093
00094 recpriority(0),
00095
00096 chanid(0),
00097 chanstr(),
00098 chansign(),
00099 channame(),
00100 chanplaybackfilters(),
00101
00102 recgroup("Default"),
00103 playgroup("Default"),
00104
00105 pathname(),
00106
00107 hostname(),
00108 storagegroup("Default"),
00109
00110 seriesid(),
00111 programid(),
00112 inetref(),
00113 catType(),
00114
00115
00116 filesize(0ULL),
00117
00118 startts(mythCurrentDateTime()),
00119 endts(startts),
00120 recstartts(startts),
00121 recendts(startts),
00122
00123 stars(0.0f),
00124
00125 originalAirDate(),
00126 lastmodified(startts),
00127 lastInUseTime(startts.addSecs(-4 * 60 * 60)),
00128
00129 prefinput(0),
00130 recpriority2(0),
00131 recordid(0),
00132 parentid(0),
00133
00134 sourceid(0),
00135 inputid(0),
00136 cardid(0),
00137
00138 findid(0),
00139
00140 programflags(FL_NONE),
00141 properties(0),
00142 year(0),
00143
00144 recstatus(rsUnknown),
00145 oldrecstatus(rsUnknown),
00146 rectype(kNotRecording),
00147 dupin(kDupsInAll),
00148 dupmethod(kDupCheckSubDesc),
00149
00150
00151 availableStatus(asAvailable),
00152 spread(-1),
00153 startCol(-1),
00154 sortTitle(),
00155
00156
00157 inUseForWhat(),
00158 positionMapDBReplacement(NULL)
00159 {
00160 }
00161
00165 ProgramInfo::ProgramInfo(const ProgramInfo &other) :
00166 title(other.title),
00167 subtitle(other.subtitle),
00168 description(other.description),
00169 season(other.season),
00170 episode(other.episode),
00171 category(other.category),
00172
00173 recpriority(other.recpriority),
00174
00175 chanid(other.chanid),
00176 chanstr(other.chanstr),
00177 chansign(other.chansign),
00178 channame(other.channame),
00179 chanplaybackfilters(other.chanplaybackfilters),
00180
00181 recgroup(other.recgroup),
00182 playgroup(other.playgroup),
00183
00184 pathname(other.pathname),
00185
00186 hostname(other.hostname),
00187 storagegroup(other.storagegroup),
00188
00189 seriesid(other.seriesid),
00190 programid(other.programid),
00191 inetref(other.inetref),
00192 catType(other.catType),
00193
00194 filesize(other.filesize),
00195
00196 startts(other.startts),
00197 endts(other.endts),
00198 recstartts(other.recstartts),
00199 recendts(other.recendts),
00200
00201 stars(other.stars),
00202
00203 originalAirDate(other.originalAirDate),
00204 lastmodified(other.lastmodified),
00205 lastInUseTime(QDateTime::currentDateTime().addSecs(-4 * 60 * 60)),
00206
00207 prefinput(other.prefinput),
00208 recpriority2(other.recpriority2),
00209 recordid(other.recordid),
00210 parentid(other.parentid),
00211
00212 sourceid(other.sourceid),
00213 inputid(other.inputid),
00214 cardid(other.cardid),
00215
00216 findid(other.findid),
00217 programflags(other.programflags),
00218 properties(other.properties),
00219 year(other.year),
00220
00221 recstatus(other.recstatus),
00222 oldrecstatus(other.oldrecstatus),
00223 rectype(other.rectype),
00224 dupin(other.dupin),
00225 dupmethod(other.dupmethod),
00226
00227
00228 availableStatus(other.availableStatus),
00229 spread(other.spread),
00230 startCol(other.startCol),
00231 sortTitle(other.sortTitle),
00232
00233
00234 inUseForWhat(),
00235 positionMapDBReplacement(other.positionMapDBReplacement)
00236 {
00237 }
00238
00239 ProgramInfo::ProgramInfo(uint _chanid, const QDateTime &_recstartts) :
00240 chanid(0),
00241 positionMapDBReplacement(NULL)
00242 {
00243 LoadProgramFromRecorded(_chanid, _recstartts);
00244 }
00245
00246 ProgramInfo::ProgramInfo(
00247 const QString &_title,
00248 const QString &_subtitle,
00249 const QString &_description,
00250 uint _season,
00251 uint _episode,
00252 const QString &_category,
00253
00254 uint _chanid,
00255 const QString &_channum,
00256 const QString &_chansign,
00257 const QString &_channame,
00258 const QString &_chanplaybackfilters,
00259
00260 const QString &_recgroup,
00261 const QString &_playgroup,
00262
00263 const QString &_pathname,
00264
00265 const QString &_hostname,
00266 const QString &_storagegroup,
00267
00268 const QString &_seriesid,
00269 const QString &_programid,
00270 const QString &_inetref,
00271
00272 int _recpriority,
00273
00274 uint64_t _filesize,
00275
00276 const QDateTime &_startts,
00277 const QDateTime &_endts,
00278 const QDateTime &_recstartts,
00279 const QDateTime &_recendts,
00280
00281 float _stars,
00282
00283 uint _year,
00284 const QDate &_originalAirDate,
00285 const QDateTime &_lastmodified,
00286
00287 RecStatusType _recstatus,
00288
00289 uint _recordid,
00290
00291 RecordingDupInType _dupin,
00292 RecordingDupMethodType _dupmethod,
00293
00294 uint _findid,
00295
00296 uint _programflags,
00297 uint _audioproperties,
00298 uint _videoproperties,
00299 uint _subtitleType) :
00300 title(_title),
00301 subtitle(_subtitle),
00302 description(_description),
00303 season(_season),
00304 episode(_episode),
00305 category(_category),
00306
00307 recpriority(_recpriority),
00308
00309 chanid(_chanid),
00310 chanstr(_channum),
00311 chansign(_chansign),
00312 channame(_channame),
00313 chanplaybackfilters(_chanplaybackfilters),
00314
00315 recgroup(_recgroup),
00316 playgroup(_playgroup),
00317
00318 pathname(_pathname),
00319
00320 hostname(_hostname),
00321 storagegroup(_storagegroup),
00322
00323 seriesid(_seriesid),
00324 programid(_programid),
00325 inetref(_inetref),
00326 catType(),
00327
00328 filesize(_filesize),
00329
00330 startts(_startts),
00331 endts(_endts),
00332 recstartts(_recstartts),
00333 recendts(_recendts),
00334
00335 stars(clamp(_stars, 0.0f, 1.0f)),
00336
00337 originalAirDate(_originalAirDate),
00338 lastmodified(_lastmodified),
00339 lastInUseTime(QDateTime::currentDateTime().addSecs(-4 * 60 * 60)),
00340
00341 prefinput(0),
00342 recpriority2(0),
00343 recordid(_recordid),
00344 parentid(0),
00345
00346 sourceid(0),
00347 inputid(0),
00348 cardid(0),
00349
00350 findid(_findid),
00351
00352 programflags(_programflags),
00353 properties((_subtitleType << kSubtitlePropertyOffset) |
00354 (_videoproperties << kVideoPropertyOffset) |
00355 (_audioproperties << kAudioPropertyOffset)),
00356 year(_year),
00357
00358 recstatus(_recstatus),
00359 oldrecstatus(rsUnknown),
00360 rectype(kNotRecording),
00361 dupin(_dupin),
00362 dupmethod(_dupmethod),
00363
00364
00365 availableStatus(asAvailable),
00366 spread(-1),
00367 startCol(-1),
00368 sortTitle(),
00369
00370
00371 inUseForWhat(),
00372 positionMapDBReplacement(NULL)
00373 {
00374 if (originalAirDate.isValid() && originalAirDate < QDate(1940, 1, 1))
00375 originalAirDate = QDate();
00376
00377 SetPathname(_pathname);
00378 }
00379
00380 ProgramInfo::ProgramInfo(
00381 const QString &_title,
00382 const QString &_subtitle,
00383 const QString &_description,
00384 uint _season,
00385 uint _episode,
00386 const QString &_category,
00387
00388 uint _chanid,
00389 const QString &_channum,
00390 const QString &_chansign,
00391 const QString &_channame,
00392
00393 const QString &_seriesid,
00394 const QString &_programid,
00395 const QString &_inetref,
00396
00397 const QDateTime &_startts,
00398 const QDateTime &_endts,
00399 const QDateTime &_recstartts,
00400 const QDateTime &_recendts,
00401
00402 RecStatusType _recstatus,
00403
00404 uint _recordid,
00405
00406 RecordingType _rectype,
00407
00408 uint _findid,
00409
00410 bool duplicate) :
00411 title(_title),
00412 subtitle(_subtitle),
00413 description(_description),
00414 season(_season),
00415 episode(_episode),
00416 category(_category),
00417
00418 recpriority(0),
00419
00420 chanid(_chanid),
00421 chanstr(_channum),
00422 chansign(_chansign),
00423 channame(_channame),
00424 chanplaybackfilters(),
00425
00426 recgroup("Default"),
00427 playgroup("Default"),
00428
00429 pathname(),
00430
00431 hostname(),
00432 storagegroup("Default"),
00433
00434 seriesid(_seriesid),
00435 programid(_programid),
00436 inetref(_inetref),
00437 catType(),
00438
00439 filesize(0ULL),
00440
00441 startts(_startts),
00442 endts(_endts),
00443 recstartts(_recstartts),
00444 recendts(_recendts),
00445
00446 stars(0.0f),
00447
00448 originalAirDate(),
00449 lastmodified(startts),
00450 lastInUseTime(QDateTime::currentDateTime().addSecs(-4 * 60 * 60)),
00451
00452 prefinput(0),
00453 recpriority2(0),
00454 recordid(_recordid),
00455 parentid(0),
00456
00457 sourceid(0),
00458 inputid(0),
00459 cardid(0),
00460
00461 findid(_findid),
00462
00463 programflags((duplicate) ? FL_DUPLICATE : 0),
00464 properties(0),
00465 year(0),
00466
00467 recstatus(_recstatus),
00468 oldrecstatus(rsUnknown),
00469 rectype(_rectype),
00470 dupin(kDupsInAll),
00471 dupmethod(kDupCheckSubDesc),
00472
00473
00474 availableStatus(asAvailable),
00475 spread(-1),
00476 startCol(-1),
00477 sortTitle(),
00478
00479
00480 inUseForWhat(),
00481 positionMapDBReplacement(NULL)
00482 {
00483 }
00484
00485 ProgramInfo::ProgramInfo(
00486 const QString &_title,
00487 const QString &_subtitle,
00488 const QString &_description,
00489 const QString &_category,
00490
00491 uint _chanid,
00492 const QString &_channum,
00493 const QString &_chansign,
00494 const QString &_channame,
00495 const QString &_chanplaybackfilters,
00496
00497 const QDateTime &_startts,
00498 const QDateTime &_endts,
00499 const QDateTime &_recstartts,
00500 const QDateTime &_recendts,
00501
00502 const QString &_seriesid,
00503 const QString &_programid,
00504 const QString &_catType,
00505
00506 float _stars,
00507 uint _year,
00508 const QDate &_originalAirDate,
00509 RecStatusType _recstatus,
00510 uint _recordid,
00511 RecordingType _rectype,
00512 uint _findid,
00513
00514 bool commfree,
00515 bool repeat,
00516
00517 uint _videoproperties,
00518 uint _audioproperties,
00519 uint _subtitleType,
00520
00521 const ProgramList &schedList) :
00522 title(_title),
00523 subtitle(_subtitle),
00524 description(_description),
00525 season(0),
00526 episode(0),
00527 category(_category),
00528
00529 recpriority(0),
00530
00531 chanid(_chanid),
00532 chanstr(_channum),
00533 chansign(_chansign),
00534 channame(_channame),
00535 chanplaybackfilters(_chanplaybackfilters),
00536
00537 recgroup("Default"),
00538 playgroup("Default"),
00539
00540 pathname(),
00541
00542 hostname(),
00543 storagegroup("Default"),
00544
00545 seriesid(_seriesid),
00546 programid(_programid),
00547 inetref(),
00548 catType(_catType),
00549
00550 filesize(0ULL),
00551
00552 startts(_startts),
00553 endts(_endts),
00554 recstartts(_recstartts),
00555 recendts(_recendts),
00556
00557 stars(clamp(_stars, 0.0f, 1.0f)),
00558
00559 originalAirDate(_originalAirDate),
00560 lastmodified(startts),
00561 lastInUseTime(startts.addSecs(-4 * 60 * 60)),
00562
00563 prefinput(0),
00564 recpriority2(0),
00565 recordid(_recordid),
00566 parentid(0),
00567
00568 sourceid(0),
00569 inputid(0),
00570 cardid(0),
00571
00572 findid(_findid),
00573
00574 programflags(FL_NONE),
00575 properties((_subtitleType << kSubtitlePropertyOffset) |
00576 (_videoproperties << kVideoPropertyOffset) |
00577 (_audioproperties << kAudioPropertyOffset)),
00578 year(_year),
00579
00580 recstatus(_recstatus),
00581 oldrecstatus(rsUnknown),
00582 rectype(_rectype),
00583 dupin(kDupsInAll),
00584 dupmethod(kDupCheckSubDesc),
00585
00586
00587 availableStatus(asAvailable),
00588 spread(-1),
00589 startCol(-1),
00590 sortTitle(),
00591
00592
00593 inUseForWhat(),
00594 positionMapDBReplacement(NULL)
00595 {
00596 programflags |= (commfree) ? FL_CHANCOMMFREE : 0;
00597 programflags |= (repeat) ? FL_REPEAT : 0;
00598
00599 if (originalAirDate.isValid() && originalAirDate < QDate(1940, 1, 1))
00600 originalAirDate = QDate();
00601
00602 ProgramList::const_iterator it = schedList.begin();
00603 for (; it != schedList.end(); ++it)
00604 {
00605 if (!IsSameTimeslot(**it))
00606 continue;
00607
00608 const ProgramInfo &s = **it;
00609 recordid = s.recordid;
00610 recstatus = s.recstatus;
00611 rectype = s.rectype;
00612 recpriority = s.recpriority;
00613 recstartts = s.recstartts;
00614 recendts = s.recendts;
00615 cardid = s.cardid;
00616 inputid = s.inputid;
00617 dupin = s.dupin;
00618 dupmethod = s.dupmethod;
00619 findid = s.findid;
00620
00621 if (chanstr != s.chanstr)
00622 {
00623 if (s.recstatus == rsWillRecord)
00624 recstatus = rsOtherShowing;
00625 else if (s.recstatus == rsRecording)
00626 recstatus = rsOtherRecording;
00627 else if (s.recstatus == rsTuning)
00628 recstatus = rsOtherTuning;
00629 }
00630
00631
00632
00633
00634
00635
00636 if (s.recstatus == rsRecording ||
00637 s.recstatus == rsTuning)
00638 {
00639 chanid = s.chanid;
00640 }
00641 }
00642 }
00643
00644 ProgramInfo::ProgramInfo(
00645 const QString &_title,
00646 const QString &_subtitle,
00647 const QString &_description,
00648 uint _season,
00649 uint _episode,
00650 const QString &_category,
00651
00652 uint _chanid,
00653 const QString &_channum,
00654 const QString &_chansign,
00655 const QString &_channame,
00656 const QString &_chanplaybackfilters,
00657
00658 const QString &_recgroup,
00659 const QString &_playgroup,
00660
00661 const QDateTime &_startts,
00662 const QDateTime &_endts,
00663 const QDateTime &_recstartts,
00664 const QDateTime &_recendts,
00665
00666 const QString &_seriesid,
00667 const QString &_programid,
00668 const QString &_inetref) :
00669 title(_title),
00670 subtitle(_subtitle),
00671 description(_description),
00672 season(_season),
00673 episode(_episode),
00674 category(_category),
00675
00676 recpriority(0),
00677
00678 chanid(_chanid),
00679 chanstr(_channum),
00680 chansign(_chansign),
00681 channame(_channame),
00682 chanplaybackfilters(_chanplaybackfilters),
00683
00684 recgroup(_recgroup),
00685 playgroup(_playgroup),
00686
00687 pathname(),
00688
00689 hostname(),
00690 storagegroup("Default"),
00691
00692 seriesid(_seriesid),
00693 programid(_programid),
00694 inetref(_inetref),
00695 catType(),
00696
00697 filesize(0ULL),
00698
00699 startts(_startts),
00700 endts(_endts),
00701 recstartts(_recstartts),
00702 recendts(_recendts),
00703
00704 stars(0.0f),
00705
00706 originalAirDate(),
00707 lastmodified(QDateTime::currentDateTime()),
00708 lastInUseTime(lastmodified.addSecs(-4 * 60 * 60)),
00709
00710 prefinput(0),
00711 recpriority2(0),
00712 recordid(0),
00713 parentid(0),
00714
00715 sourceid(0),
00716 inputid(0),
00717 cardid(0),
00718
00719 findid(0),
00720
00721 programflags(FL_NONE),
00722 properties(0),
00723 year(0),
00724
00725 recstatus(rsUnknown),
00726 oldrecstatus(rsUnknown),
00727 rectype(kNotRecording),
00728 dupin(kDupsInAll),
00729 dupmethod(kDupCheckSubDesc),
00730
00731
00732 availableStatus(asAvailable),
00733 spread(-1),
00734 startCol(-1),
00735 sortTitle(),
00736
00737
00738 inUseForWhat(),
00739 positionMapDBReplacement(NULL)
00740 {
00741 }
00742
00743 ProgramInfo::ProgramInfo(const QString &_pathname) :
00744 chanid(0),
00745 positionMapDBReplacement(NULL)
00746 {
00747 if (_pathname.isEmpty())
00748 {
00749 clear();
00750 return;
00751 }
00752
00753 uint _chanid;
00754 QDateTime _recstartts;
00755 if (!gCoreContext->IsDatabaseIgnored() &&
00756 QueryKeyFromPathname(_pathname, _chanid, _recstartts) &&
00757 LoadProgramFromRecorded(_chanid, _recstartts))
00758 {
00759 return;
00760 }
00761
00762 clear();
00763
00764 QDateTime cur = QDateTime::currentDateTime();
00765 recstartts = startts = cur.addSecs(-4 * 60 * 60 - 1);
00766 recendts = endts = cur.addSecs(-1);
00767
00768 QString basename = _pathname.section('/', -1);
00769 if (_pathname == basename)
00770 SetPathname(QDir::currentPath() + '/' + _pathname);
00771 else if (_pathname.contains("./") && !_pathname.contains(":"))
00772 SetPathname(QFileInfo(_pathname).absoluteFilePath());
00773 else
00774 SetPathname(_pathname);
00775 }
00776
00777 ProgramInfo::ProgramInfo(const QString &_pathname,
00778 const QString &_plot,
00779 const QString &_title,
00780 const QString &_subtitle,
00781 const QString &_director,
00782 int _season, int _episode,
00783 const QString &_inetref,
00784 uint _length_in_minutes,
00785 uint _year,
00786 const QString &_programid) :
00787 positionMapDBReplacement(NULL)
00788 {
00789 clear();
00790
00791 QDateTime cur = QDateTime::currentDateTime();
00792 recstartts = cur.addSecs((_length_in_minutes + 1) * -60);
00793 recendts = recstartts.addSecs(_length_in_minutes * 60);
00794 startts.setDate(
00795 QDate::fromString(QString("%1-01-01").arg(year), Qt::ISODate));
00796 startts.setTime(QTime(0,0,0));
00797 endts = startts.addSecs(_length_in_minutes * 60);
00798
00799 QString pn = _pathname;
00800 if ((!_pathname.startsWith("myth://")) &&
00801 (_pathname.right(4).toLower() == ".iso" ||
00802 _pathname.right(4).toLower() == ".img" ||
00803 QDir(_pathname + "/VIDEO_TS").exists()))
00804 {
00805 pn = QString("dvd:%1").arg(_pathname);
00806 }
00807 else if (QDir(_pathname+"/BDMV").exists())
00808 {
00809 pn = QString("bd:%1").arg(_pathname);
00810 }
00811 SetPathname(pn);
00812
00813 if (!_director.isEmpty())
00814 {
00815 description = QString("%1: %2. ")
00816 .arg(QObject::tr("Directed By")).arg(_director);
00817 }
00818
00819 description += _plot;
00820
00821 if (!_subtitle.isEmpty())
00822 subtitle = _subtitle;
00823
00824 season = _season;
00825 episode = _episode;
00826 inetref = _inetref;
00827 title = _title;
00828 programid = _programid;
00829 }
00830
00831 ProgramInfo::ProgramInfo(const QString &_title, uint _chanid,
00832 const QDateTime &_startts,
00833 const QDateTime &_endts) :
00834 positionMapDBReplacement(NULL)
00835 {
00836 clear();
00837
00838 MSqlQuery query(MSqlQuery::InitCon());
00839 query.prepare(
00840 "SELECT chanid, channum, callsign, name, outputfilters, commmethod "
00841 "FROM channel "
00842 "WHERE chanid=:CHANID");
00843 query.bindValue(":CHANID", _chanid);
00844 if (query.exec() && query.next())
00845 {
00846 chanstr = query.value(1).toString();
00847 chansign = query.value(2).toString();
00848 channame = query.value(3).toString();
00849 chanplaybackfilters = query.value(4).toString();
00850 set_flag(programflags, FL_CHANCOMMFREE,
00851 query.value(5).toInt() == COMM_DETECT_COMMFREE);
00852 }
00853
00854 chanid = _chanid;
00855 startts = _startts;
00856 endts = _endts;
00857
00858 title = _title;
00859 if (title.isEmpty())
00860 {
00861 QString channelFormat =
00862 gCoreContext->GetSetting("ChannelFormat", "<num> <sign>");
00863
00864 title = QString("%1 - %2").arg(ChannelText(channelFormat))
00865 .arg(MythDateTimeToString(startts, kTime));
00866 }
00867
00868 description = title =
00869 QString("%1 (%2)").arg(title).arg(QObject::tr("Manual Record"));
00870 }
00871
00872 ProgramInfo::ProgramInfo(
00873 const QString &_title, const QString &_category,
00874 const QDateTime &_startts, const QDateTime &_endts) :
00875 positionMapDBReplacement(NULL)
00876 {
00877 clear();
00878 title = _title;
00879 category = _category;
00880 startts = _startts;
00881 endts = _endts;
00882 }
00883
00884
00888 ProgramInfo &ProgramInfo::operator=(const ProgramInfo &other)
00889 {
00890 clone(other);
00891 return *this;
00892 }
00893
00895 void ProgramInfo::clone(const ProgramInfo &other,
00896 bool ignore_non_serialized_data)
00897 {
00898 bool is_same =
00899 (chanid && recstartts.isValid() && startts.isValid() &&
00900 chanid == other.chanid && recstartts == other.recstartts &&
00901 startts == other.startts);
00902
00903 title = other.title;
00904 subtitle = other.subtitle;
00905 description = other.description;
00906 season = other.season;
00907 episode = other.episode;
00908 category = other.category;
00909
00910 chanid = other.chanid;
00911 chanstr = other.chanstr;
00912 chansign = other.chansign;
00913 channame = other.channame;
00914 chanplaybackfilters = other.chanplaybackfilters;
00915
00916 recgroup = other.recgroup;
00917 playgroup = other.playgroup;
00918
00919 if (!ignore_non_serialized_data || !is_same ||
00920 (GetBasename() != other.GetBasename()))
00921 {
00922 pathname = other.pathname;
00923 }
00924
00925 hostname = other.hostname;
00926 storagegroup = other.storagegroup;
00927
00928 seriesid = other.seriesid;
00929 programid = other.programid;
00930 inetref = other.inetref;
00931 catType = other.catType;
00932
00933 recpriority = other.recpriority;
00934
00935 filesize = other.filesize;
00936
00937 startts = other.startts;
00938 endts = other.endts;
00939 recstartts = other.recstartts;
00940 recendts = other.recendts;
00941
00942 stars = other.stars;
00943
00944 year = other.year;
00945 originalAirDate = other.originalAirDate;
00946 lastmodified = other.lastmodified;
00947 lastInUseTime = QDateTime::currentDateTime().addSecs(-4 * 60 * 60);
00948
00949 recstatus = other.recstatus;
00950
00951 prefinput = other.prefinput;
00952 recpriority2 = other.recpriority2;
00953 recordid = other.recordid;
00954 parentid = other.parentid;
00955
00956 rectype = other.rectype;
00957 dupin = other.dupin;
00958 dupmethod = other.dupmethod;
00959
00960 sourceid = other.sourceid;
00961 inputid = other.inputid;
00962 cardid = other.cardid;
00963
00964 findid = other.findid;
00965 programflags = other.programflags;
00966 properties = other.properties;
00967
00968 if (!ignore_non_serialized_data)
00969 {
00970 spread = other.spread;
00971 startCol = other.startCol;
00972 sortTitle = other.sortTitle;
00973 availableStatus = other.availableStatus;
00974
00975 inUseForWhat = other.inUseForWhat;
00976 positionMapDBReplacement = other.positionMapDBReplacement;
00977 }
00978
00979 title.detach();
00980 subtitle.detach();
00981 description.detach();
00982 category.detach();
00983
00984 chanstr.detach();
00985 chansign.detach();
00986 channame.detach();
00987 chanplaybackfilters.detach();
00988
00989 recgroup.detach();
00990 playgroup.detach();
00991
00992 pathname.detach();
00993 hostname.detach();
00994 storagegroup.detach();
00995
00996 seriesid.detach();
00997 programid.detach();
00998 inetref.detach();
00999 catType.detach();
01000
01001 sortTitle.detach();
01002 inUseForWhat.detach();
01003 }
01004
01005 void ProgramInfo::clear(void)
01006 {
01007 title.clear();
01008 subtitle.clear();
01009 description.clear();
01010 season = 0;
01011 episode = 0;
01012 category.clear();
01013
01014 chanid = 0;
01015 chanstr.clear();
01016 chansign.clear();
01017 channame.clear();
01018 chanplaybackfilters.clear();
01019
01020 recgroup = "Default";
01021 playgroup = "Default";
01022
01023 pathname.clear();
01024
01025 hostname.clear();
01026 storagegroup = "Default";
01027
01028 year = 0;
01029
01030 seriesid.clear();
01031 programid.clear();
01032 inetref.clear();
01033 catType.clear();
01034
01035 sortTitle.clear();
01036
01037 recpriority = 0;
01038
01039 filesize = 0ULL;
01040
01041 startts = mythCurrentDateTime();
01042 endts = startts;
01043 recstartts = startts;
01044 recendts = startts;
01045
01046 stars = 0.0f;
01047
01048 originalAirDate = QDate();
01049 lastmodified = startts;
01050 lastInUseTime = startts.addSecs(-4 * 60 * 60);
01051
01052 recstatus = rsUnknown;
01053
01054 prefinput = 0;
01055 recpriority2 = 0;
01056 recordid = 0;
01057 parentid = 0;
01058
01059 rectype = kNotRecording;
01060 dupin = kDupsInAll;
01061 dupmethod = kDupCheckSubDesc;
01062
01063 sourceid = 0;
01064 inputid = 0;
01065 cardid = 0;
01066
01067 findid = 0;
01068
01069 programflags = FL_NONE;
01070 properties = 0;
01071
01072
01073 spread = -1;
01074 startCol = -1;
01075 availableStatus = asAvailable;
01076
01077
01078 inUseForWhat.clear();
01079 positionMapDBReplacement = NULL;
01080 }
01081
01085 ProgramInfo::~ProgramInfo()
01086 {
01087 }
01088
01090 QString ProgramInfo::MakeUniqueKey(
01091 uint chanid, const QDateTime &recstartts)
01092 {
01093 return QString("%1_%2").arg(chanid).arg(recstartts.toString(Qt::ISODate));
01094 }
01095
01099 bool ProgramInfo::ExtractKey(const QString &uniquekey,
01100 uint &chanid, QDateTime &recstartts)
01101 {
01102 QStringList keyParts = uniquekey.split('_');
01103 if (keyParts.size() != 2)
01104 return false;
01105 chanid = keyParts[0].toUInt();
01106 recstartts = QDateTime::fromString(keyParts[1], Qt::ISODate);
01107 return chanid && recstartts.isValid();
01108 }
01109
01110 bool ProgramInfo::ExtractKeyFromPathname(
01111 const QString &pathname, uint &chanid, QDateTime &recstartts)
01112 {
01113 QString basename = pathname.section('/', -1);
01114 if (basename.isEmpty())
01115 return false;
01116
01117 QStringList lr = basename.split("_");
01118 if (lr.size() == 2)
01119 {
01120 chanid = lr[0].toUInt();
01121 QStringList ts = lr[1].split(".");
01122 if (chanid && !ts.empty())
01123 {
01124 recstartts = myth_dt_from_string(ts[0]);
01125 return recstartts.isValid();
01126 }
01127 }
01128
01129 return false;
01130 }
01131
01132 bool ProgramInfo::QueryKeyFromPathname(
01133 const QString &pathname, uint &chanid, QDateTime &recstartts)
01134 {
01135 QString basename = pathname.section('/', -1);
01136 if (basename.isEmpty())
01137 return false;
01138
01139 MSqlQuery query(MSqlQuery::InitCon());
01140 query.prepare(
01141 "SELECT chanid, starttime "
01142 "FROM recorded "
01143 "WHERE basename = :BASENAME");
01144 query.bindValue(":BASENAME", basename);
01145 if (query.exec() && query.next())
01146 {
01147 chanid = query.value(0).toUInt();
01148 recstartts = query.value(1).toDateTime();
01149 return true;
01150 }
01151
01152 return ExtractKeyFromPathname(pathname, chanid, recstartts);
01153 }
01154
01155 #define INT_TO_LIST(x) do { list << QString::number(x); } while (0)
01156
01157 #define DATETIME_TO_LIST(x) INT_TO_LIST((x).toTime_t())
01158
01159 #define LONGLONG_TO_LIST(x) do { list << QString::number(x); } while (0)
01160
01161 #define STR_TO_LIST(x) do { list << (x); } while (0)
01162 #define DATE_TO_LIST(x) do { list << (x).toString(Qt::ISODate); } while (0)
01163
01164 #define FLOAT_TO_LIST(x) do { list << QString("%1").arg(x); } while (0)
01165
01172 void ProgramInfo::ToStringList(QStringList &list) const
01173 {
01174 STR_TO_LIST(title);
01175 STR_TO_LIST(subtitle);
01176 STR_TO_LIST(description);
01177 INT_TO_LIST(season);
01178 INT_TO_LIST(episode);
01179 STR_TO_LIST(category);
01180 INT_TO_LIST(chanid);
01181 STR_TO_LIST(chanstr);
01182 STR_TO_LIST(chansign);
01183 STR_TO_LIST(channame);
01184 STR_TO_LIST(pathname);
01185 INT_TO_LIST(filesize);
01186
01187 DATETIME_TO_LIST(startts);
01188 DATETIME_TO_LIST(endts);
01189 INT_TO_LIST(findid);
01190 STR_TO_LIST(hostname);
01191 INT_TO_LIST(sourceid);
01192 INT_TO_LIST(cardid);
01193 INT_TO_LIST(inputid);
01194 INT_TO_LIST(recpriority);
01195 INT_TO_LIST(recstatus);
01196 INT_TO_LIST(recordid);
01197
01198 INT_TO_LIST(rectype);
01199 INT_TO_LIST(dupin);
01200 INT_TO_LIST(dupmethod);
01201 DATETIME_TO_LIST(recstartts);
01202 DATETIME_TO_LIST(recendts);
01203 INT_TO_LIST(programflags);
01204 STR_TO_LIST((!recgroup.isEmpty()) ? recgroup : "Default");
01205 STR_TO_LIST(chanplaybackfilters);
01206 STR_TO_LIST(seriesid);
01207 STR_TO_LIST(programid);
01208 STR_TO_LIST(inetref);
01209
01210 DATETIME_TO_LIST(lastmodified);
01211 FLOAT_TO_LIST(stars);
01212 DATE_TO_LIST(originalAirDate);
01213 STR_TO_LIST((!playgroup.isEmpty()) ? playgroup : "Default");
01214 INT_TO_LIST(recpriority2);
01215 INT_TO_LIST(parentid);
01216 STR_TO_LIST((!storagegroup.isEmpty()) ? storagegroup : "Default");
01217 INT_TO_LIST(GetAudioProperties());
01218 INT_TO_LIST(GetVideoProperties());
01219 INT_TO_LIST(GetSubtitleType());
01220
01221 INT_TO_LIST(year);
01222
01223 }
01224
01225
01226
01227 #define NEXT_STR() do { if (it == listend) \
01228 { \
01229 LOG(VB_GENERAL, LOG_ERR, listerror); \
01230 clear(); \
01231 return false; \
01232 } \
01233 ts = *it++; } while (0)
01234
01235 #define INT_FROM_LIST(x) do { NEXT_STR(); (x) = ts.toLongLong(); } while (0)
01236 #define ENUM_FROM_LIST(x, y) do { NEXT_STR(); (x) = ((y)ts.toInt()); } while (0)
01237
01238 #define DATETIME_FROM_LIST(x) \
01239 do { NEXT_STR(); (x).setTime_t(ts.toUInt()); } while (0)
01240 #define DATE_FROM_LIST(x) \
01241 do { NEXT_STR(); (x) = ((ts.isEmpty()) || (ts == "0000-00-00")) ? \
01242 QDate() : QDate::fromString(ts, Qt::ISODate); \
01243 } while (0)
01244
01245 #define STR_FROM_LIST(x) do { NEXT_STR(); (x) = ts; } while (0)
01246
01247 #define FLOAT_FROM_LIST(x) do { NEXT_STR(); (x) = ts.toFloat(); } while (0)
01248
01260 bool ProgramInfo::FromStringList(QStringList::const_iterator &it,
01261 QStringList::const_iterator listend)
01262 {
01263 QString listerror = LOC + "FromStringList, not enough items in list.";
01264 QString ts;
01265
01266 uint origChanid = chanid;
01267 QDateTime origRecstartts = recstartts;
01268
01269 STR_FROM_LIST(title);
01270 STR_FROM_LIST(subtitle);
01271 STR_FROM_LIST(description);
01272 INT_FROM_LIST(season);
01273 INT_FROM_LIST(episode);
01274 STR_FROM_LIST(category);
01275 INT_FROM_LIST(chanid);
01276 STR_FROM_LIST(chanstr);
01277 STR_FROM_LIST(chansign);
01278 STR_FROM_LIST(channame);
01279 STR_FROM_LIST(pathname);
01280 INT_FROM_LIST(filesize);
01281
01282 DATETIME_FROM_LIST(startts);
01283 DATETIME_FROM_LIST(endts);
01284 INT_FROM_LIST(findid);
01285 STR_FROM_LIST(hostname);
01286 INT_FROM_LIST(sourceid);
01287 INT_FROM_LIST(cardid);
01288 INT_FROM_LIST(inputid);
01289 INT_FROM_LIST(recpriority);
01290 ENUM_FROM_LIST(recstatus, RecStatusType);
01291 INT_FROM_LIST(recordid);
01292
01293 ENUM_FROM_LIST(rectype, RecordingType);
01294 ENUM_FROM_LIST(dupin, RecordingDupInType);
01295 ENUM_FROM_LIST(dupmethod, RecordingDupMethodType);
01296 DATETIME_FROM_LIST(recstartts);
01297 DATETIME_FROM_LIST(recendts);
01298 INT_FROM_LIST(programflags);
01299 STR_FROM_LIST(recgroup);
01300 STR_FROM_LIST(chanplaybackfilters);
01301 STR_FROM_LIST(seriesid);
01302 STR_FROM_LIST(programid);
01303 STR_FROM_LIST(inetref);
01304
01305 DATETIME_FROM_LIST(lastmodified);
01306 FLOAT_FROM_LIST(stars);
01307 DATE_FROM_LIST(originalAirDate);;
01308 STR_FROM_LIST(playgroup);
01309 INT_FROM_LIST(recpriority2);
01310 INT_FROM_LIST(parentid);
01311 STR_FROM_LIST(storagegroup);
01312 uint audioproperties, videoproperties, subtitleType;
01313 INT_FROM_LIST(audioproperties);
01314 INT_FROM_LIST(videoproperties);
01315 INT_FROM_LIST(subtitleType);
01316 properties = ((subtitleType << kSubtitlePropertyOffset) |
01317 (videoproperties << kVideoPropertyOffset) |
01318 (audioproperties << kAudioPropertyOffset));
01319
01320 INT_FROM_LIST(year);
01321
01322 if (!origChanid || !origRecstartts.isValid() ||
01323 (origChanid != chanid) || (origRecstartts != recstartts))
01324 {
01325 availableStatus = asAvailable;
01326 spread = -1;
01327 startCol = -1;
01328 sortTitle = QString();
01329 inUseForWhat = QString();
01330 positionMapDBReplacement = NULL;
01331 }
01332
01333 return true;
01334 }
01335
01339 void ProgramInfo::ToMap(InfoMap &progMap,
01340 bool showrerecord,
01341 uint star_range) const
01342 {
01343 QLocale locale = gCoreContext->GetQLocale();
01344
01345
01346 QString channelFormat =
01347 gCoreContext->GetSetting("ChannelFormat", "<num> <sign>");
01348 QString longChannelFormat =
01349 gCoreContext->GetSetting("LongChannelFormat", "<num> <name>");
01350
01351 QDateTime timeNow = QDateTime::currentDateTime();
01352
01353 int hours, minutes, seconds;
01354
01355 progMap["title"] = title;
01356 progMap["subtitle"] = subtitle;
01357
01358 QString tempSubTitle = title;
01359 if (!subtitle.trimmed().isEmpty())
01360 {
01361 tempSubTitle = QString("%1 - \"%2\"")
01362 .arg(tempSubTitle).arg(subtitle);
01363 }
01364
01365 progMap["titlesubtitle"] = tempSubTitle;
01366
01367 progMap["description"] = description;
01368
01369 if (season > 0 || episode > 0)
01370 {
01371 progMap["season"] = GetDisplaySeasonEpisode(season, 1);
01372 progMap["episode"] = GetDisplaySeasonEpisode(episode, 1);
01373 progMap["s00e00"] = QString("s%1e%2").arg(GetDisplaySeasonEpisode
01374 (GetSeason(), 2))
01375 .arg(GetDisplaySeasonEpisode(GetEpisode(), 2));
01376 progMap["00x00"] = QString("%1x%2").arg(GetDisplaySeasonEpisode
01377 (GetSeason(), 1))
01378 .arg(GetDisplaySeasonEpisode(GetEpisode(), 2));
01379 }
01380 else
01381 {
01382 progMap["season"] = progMap["episode"] = "";
01383 progMap["s00e00"] = progMap["00x00"] = "";
01384 }
01385
01386 progMap["category"] = category;
01387 progMap["callsign"] = chansign;
01388 progMap["commfree"] = (programflags & FL_CHANCOMMFREE) ? 1 : 0;
01389 progMap["outputfilters"] = chanplaybackfilters;
01390 if (IsVideo())
01391 {
01392 progMap["starttime"] = "";
01393 progMap["startdate"] = "";
01394 progMap["endtime"] = "";
01395 progMap["enddate"] = "";
01396 progMap["recstarttime"] = "";
01397 progMap["recstartdate"] = "";
01398 progMap["recendtime"] = "";
01399 progMap["recenddate"] = "";
01400
01401 if (startts.date().year() == 1895)
01402 {
01403 progMap["startdate"] = "";
01404 progMap["recstartdate"] = "";
01405 }
01406 else
01407 {
01408 progMap["startdate"] = startts.toString("yyyy");
01409 progMap["recstartdate"] = startts.toString("yyyy");
01410 }
01411 }
01412 else
01413 {
01414 progMap["starttime"] = MythDateTimeToString(startts, kTime);
01415 progMap["startdate"] = MythDateTimeToString(startts, kDateFull | kSimplify);
01416 progMap["shortstartdate"] = MythDateTimeToString(startts, kDateShort);
01417 if (timeNow.date().year() != startts.date().year())
01418 progMap["startyear"] = startts.toString("yyyy");
01419 progMap["endtime"] = MythDateTimeToString(endts, kTime);
01420 progMap["enddate"] = MythDateTimeToString(endts, kDateFull | kSimplify);
01421 progMap["shortenddate"] = MythDateTimeToString(endts, kDateShort);
01422 if (timeNow.date().year() != endts.date().year())
01423 progMap["endyear"] = endts.toString("yyyy");
01424 progMap["recstarttime"] = MythDateTimeToString(recstartts, kTime);
01425 progMap["recstartdate"] = MythDateTimeToString(recstartts, kDateShort);
01426 progMap["recendtime"] = MythDateTimeToString(recendts, kTime);
01427 progMap["recenddate"] = MythDateTimeToString(recendts, kDateShort);
01428 progMap["startts"] = QString::number(startts.toTime_t());
01429 progMap["endts"] = QString::number(endts.toTime_t());
01430 }
01431
01432 progMap["timedate"] = MythDateTimeToString(recstartts,
01433 kDateTimeFull | kSimplify) + " - " +
01434 MythDateTimeToString(recendts, kTime);
01435
01436 progMap["shorttimedate"] = MythDateTimeToString(recstartts,
01437 kDateTimeShort | kSimplify) + " - " +
01438 MythDateTimeToString(recendts, kTime);
01439
01440 progMap["starttimedate"] = MythDateTimeToString(recstartts,
01441 kDateTimeFull | kSimplify);
01442
01443 progMap["shortstarttimedate"] = MythDateTimeToString(recstartts,
01444 kDateTimeShort | kSimplify);
01445
01446 progMap["lastmodifiedtime"] = MythDateTimeToString(lastmodified, kTime);
01447 progMap["lastmodifieddate"] = MythDateTimeToString(lastmodified,
01448 kDateFull | kSimplify);
01449 progMap["lastmodified"] = MythDateTimeToString(lastmodified,
01450 kDateTimeFull | kSimplify);
01451
01452 progMap["channum"] = chanstr;
01453 progMap["chanid"] = chanid;
01454 progMap["channame"] = channame;
01455 progMap["channel"] = ChannelText(channelFormat);
01456 progMap["longchannel"] = ChannelText(longChannelFormat);
01457
01458 QString tmpSize = locale.toString(filesize * (1.0 / (1024.0 * 1024.0 * 1024.0)), 'f', 2);
01459 progMap["filesize_str"] = QObject::tr("%1 GB", "GigaBytes").arg(tmpSize);
01460
01461 progMap["filesize"] = locale.toString((quint64)filesize);
01462
01463 seconds = recstartts.secsTo(recendts);
01464 minutes = seconds / 60;
01465
01466 QString min_str = QObject::tr("%n minute(s)","",minutes);
01467
01468 progMap["lenmins"] = min_str;
01469 hours = minutes / 60;
01470 minutes = minutes % 60;
01471
01472 progMap["lentime"] = min_str;
01473 if (hours > 0 && minutes > 0)
01474 {
01475 min_str = QObject::tr("%n minute(s)","",minutes);
01476 progMap["lentime"] = QString("%1 %2")
01477 .arg(QObject::tr("%n hour(s)","", hours))
01478 .arg(min_str);
01479 }
01480 else if (hours > 0)
01481 {
01482 progMap["lentime"] = QObject::tr("%n hour(s)","", hours);
01483 }
01484
01485 progMap["rectypechar"] = toQChar(GetRecordingRuleType());
01486 progMap["rectype"] = ::toString(GetRecordingRuleType());
01487 QString tmp_rec = progMap["rectype"];
01488 if (GetRecordingRuleType() != kNotRecording)
01489 {
01490 if (((recendts > timeNow) && (recstatus <= rsWillRecord)) ||
01491 (recstatus == rsConflict) || (recstatus == rsLaterShowing))
01492 {
01493 tmp_rec += QString().sprintf(" %+d", recpriority);
01494 if (recpriority2)
01495 tmp_rec += QString().sprintf("/%+d", recpriority2);
01496 tmp_rec += " ";
01497 }
01498 else
01499 {
01500 tmp_rec += " -- ";
01501 }
01502 if (showrerecord && (GetRecordingStatus() == rsRecorded) &&
01503 !IsDuplicate())
01504 {
01505 tmp_rec += QObject::tr("Re-Record");
01506 }
01507 else
01508 {
01509 tmp_rec += ::toString(GetRecordingStatus(), GetRecordingRuleType());
01510 }
01511 }
01512 progMap["rectypestatus"] = tmp_rec;
01513
01514 progMap["card"] = ::toString(GetRecordingStatus(), cardid);
01515 progMap["input"] = ::toString(GetRecordingStatus(), inputid);
01516 progMap["inputname"] = QueryInputDisplayName();
01517
01518 progMap["recpriority"] = recpriority;
01519 progMap["recpriority2"] = recpriority2;
01520 progMap["recordinggroup"] = (recgroup == "Default")
01521 ? QObject::tr("Default") : recgroup;
01522 progMap["playgroup"] = playgroup;
01523
01524 if (storagegroup == "Default")
01525 progMap["storagegroup"] = QObject::tr("Default");
01526 else if (StorageGroup::kSpecialGroups.contains(storagegroup))
01527 progMap["storagegroup"] = QObject::tr(storagegroup.toUtf8().constData());
01528 else
01529 progMap["storagegroup"] = storagegroup;
01530
01531 progMap["programflags"] = programflags;
01532
01533 progMap["audioproperties"] = GetAudioProperties();
01534 progMap["videoproperties"] = GetVideoProperties();
01535 progMap["subtitleType"] = GetSubtitleType();
01536
01537 progMap["recstatus"] = ::toString(GetRecordingStatus(),
01538 GetRecordingRuleType());
01539 progMap["recstatuslong"] = ::toDescription(GetRecordingStatus(),
01540 GetRecordingRuleType(),
01541 GetRecordingStartTime());
01542
01543 if (IsRepeat())
01544 {
01545 progMap["repeat"] = QString("(%1) ").arg(QObject::tr("Repeat"));
01546 progMap["longrepeat"] = progMap["repeat"];
01547 if (originalAirDate.isValid())
01548 {
01549 progMap["longrepeat"] = QString("(%1 %2) ")
01550 .arg(QObject::tr("Repeat"))
01551 .arg(MythDateToString(originalAirDate, kDateFull | kAddYear));
01552 }
01553 }
01554 else
01555 {
01556 progMap["repeat"] = "";
01557 progMap["longrepeat"] = "";
01558 }
01559
01560 progMap["seriesid"] = seriesid;
01561 progMap["programid"] = programid;
01562 progMap["inetref"] = inetref;
01563 progMap["catType"] = catType;
01564
01565 progMap["year"] = year ? QString::number(year) : "";
01566
01567 QString star_str = (stars != 0.0f) ?
01568 QObject::tr("%n star(s)", "", GetStars(star_range)) : "";
01569 progMap["stars"] = star_str;
01570 progMap["numstars"] = QString().number(GetStars(star_range));
01571
01572 if (stars != 0.0f && year)
01573 progMap["yearstars"] = QString("(%1, %2)").arg(year).arg(star_str);
01574 else if (stars != 0.0f)
01575 progMap["yearstars"] = QString("(%1)").arg(star_str);
01576 else if (year)
01577 progMap["yearstars"] = QString("(%1)").arg(year);
01578 else
01579 progMap["yearstars"] = "";
01580
01581 if (!originalAirDate.isValid() ||
01582 (!programid.isEmpty() && (programid.left(2) == "MV")))
01583 {
01584 progMap["originalairdate"] = "";
01585 progMap["shortoriginalairdate"] = "";
01586 }
01587 else
01588 {
01589 progMap["originalairdate"] = MythDateToString(originalAirDate, kDateFull);
01590 progMap["shortoriginalairdate"] = MythDateToString(originalAirDate, kDateShort);
01591 }
01592
01593
01594
01595
01596 QString mediaType;
01597 QString mediaTypeString;
01598 switch (GetProgramInfoType())
01599 {
01600 case kProgramInfoTypeVideoFile :
01601 mediaType = "video";
01602 mediaTypeString = QObject::tr("Video");
01603 break;
01604 case kProgramInfoTypeVideoDVD :
01605 mediaType = "dvd";
01606 mediaTypeString = QObject::tr("DVD");
01607 break;
01608 case kProgramInfoTypeVideoStreamingHTML :
01609 mediaType = "httpstream";
01610 mediaTypeString = QObject::tr("HTTP Streaming");
01611 break;
01612 case kProgramInfoTypeVideoStreamingRTSP :
01613 mediaType = "rtspstream";
01614 mediaTypeString = QObject::tr("RTSP Streaming");
01615 break;
01616 case kProgramInfoTypeVideoBD :
01617 mediaType = "bluraydisc";
01618 mediaTypeString = QObject::tr("Blu-ray Disc");
01619 break;
01620 case kProgramInfoTypeRecording :
01621 default :
01622 mediaType = "recording";
01623 mediaTypeString = QObject::tr("Recording",
01624 "Recorded file, object not action");
01625 }
01626 progMap["mediatype"] = mediaType;
01627 progMap["mediatypestring"] = mediaTypeString;
01628 }
01629
01631 uint ProgramInfo::GetSecondsInRecording(void) const
01632 {
01633 int recsecs = recstartts.secsTo(endts);
01634 return (uint) ((recsecs>0) ? recsecs : max(startts.secsTo(endts),0));
01635 }
01636
01638 uint64_t ProgramInfo::QueryLastFrameInPosMap(void) const
01639 {
01640 uint64_t last_frame = 0;
01641 frm_pos_map_t posMap;
01642 QueryPositionMap(posMap, MARK_GOP_BYFRAME);
01643 if (posMap.empty())
01644 {
01645 QueryPositionMap(posMap, MARK_GOP_START);
01646 if (posMap.empty())
01647 QueryPositionMap(posMap, MARK_KEYFRAME);
01648 }
01649 if (!posMap.empty())
01650 {
01651 frm_pos_map_t::const_iterator it = posMap.constEnd();
01652 --it;
01653 last_frame = it.key();
01654 }
01655 return last_frame;
01656 }
01657
01658 QString ProgramInfo::toString(const Verbosity v, QString sep, QString grp)
01659 const
01660 {
01661 QString str;
01662 switch (v)
01663 {
01664 case kLongDescription:
01665 str = LOC + "channame(" + channame + ") startts(" +
01666 startts.toString() + ") endts(" + endts.toString() + ")\n";
01667 str += " recstartts(" + recstartts.toString() +
01668 ") recendts(" + recendts.toString() + ")\n";
01669 str += " title(" + title + ")";
01670 break;
01671 case kTitleSubtitle:
01672 str = title.contains(' ') ?
01673 QString("%1%2%3").arg(grp).arg(title).arg(grp) : title;
01674 if (!subtitle.isEmpty())
01675 {
01676 str += subtitle.contains(' ') ?
01677 QString("%1%2%3%4").arg(sep)
01678 .arg(grp).arg(subtitle).arg(grp) :
01679 QString("%1%2").arg(sep).arg(subtitle);
01680 }
01681 break;
01682 case kRecordingKey:
01683 str = QString("%1 at %2")
01684 .arg(GetChanID()).arg(GetRecordingStartTime(ISODate));
01685 break;
01686 case kSchedulingKey:
01687 str = QString("%1 @ %2")
01688 .arg(GetChanID()).arg(GetScheduledStartTime(ISODate));
01689 break;
01690 }
01691
01692 return str;
01693 }
01694
01695 bool ProgramInfo::Reload(void)
01696 {
01697 ProgramInfo test(chanid, recstartts);
01698 if (test.GetChanID())
01699 {
01700 clone(test, true);
01701 return true;
01702 }
01703 return false;
01704 }
01705
01709 bool ProgramInfo::LoadProgramFromRecorded(
01710 const uint _chanid, const QDateTime &_recstartts)
01711 {
01712 if (!_chanid || !_recstartts.isValid())
01713 {
01714 clear();
01715 return false;
01716 }
01717
01718 MSqlQuery query(MSqlQuery::InitCon());
01719 query.prepare(
01720 kFromRecordedQuery +
01721 "WHERE r.chanid = :CHANID AND "
01722 " r.starttime = :RECSTARTTS");
01723 query.bindValue(":CHANID", _chanid);
01724 query.bindValue(":RECSTARTTS", _recstartts);
01725
01726 if (!query.exec())
01727 {
01728 MythDB::DBError("LoadProgramFromRecorded", query);
01729 clear();
01730 return false;
01731 }
01732
01733 if (!query.next())
01734 {
01735 clear();
01736 return false;
01737 }
01738
01739 bool is_reload = (chanid == _chanid) && (recstartts == _recstartts);
01740 if (!is_reload)
01741 {
01742
01743
01744 catType.clear();
01745 lastInUseTime = QDateTime::currentDateTime().addSecs(-4 * 60 * 60);
01746 rectype = kNotRecording;
01747 oldrecstatus = rsUnknown;
01748 prefinput = 0;
01749 recpriority2 = 0;
01750 parentid = 0;
01751 sourceid = 0;
01752 inputid = 0;
01753 cardid = 0;
01754
01755
01756 spread = startCol = -1;
01757 sortTitle.clear();
01758 availableStatus = asAvailable;
01759 inUseForWhat.clear();
01760 positionMapDBReplacement = NULL;
01761 }
01762
01763 title = query.value(0).toString();
01764 subtitle = query.value(1).toString();
01765 description = query.value(2).toString();
01766 season = query.value(3).toUInt();
01767 episode = query.value(4).toUInt();
01768 category = query.value(5).toString();
01769
01770 chanid = _chanid;
01771 chanstr = QString("#%1").arg(chanid);
01772 chansign = chanstr;
01773 channame = chanstr;
01774 chanplaybackfilters.clear();
01775 if (!query.value(7).toString().isEmpty())
01776 {
01777 chanstr = query.value(7).toString();
01778 chansign = query.value(8).toString();
01779 channame = query.value(9).toString();
01780 chanplaybackfilters = query.value(10).toString();
01781 }
01782
01783 recgroup = query.value(11).toString();
01784 playgroup = query.value(12).toString();
01785
01786
01787
01788
01789 QString new_basename = query.value(14).toString();
01790 if ((GetBasename() != new_basename) || !is_reload)
01791 {
01792 if (is_reload)
01793 {
01794 LOG(VB_FILE, LOG_INFO, LOC +
01795 QString("Updated pathname '%1':'%2' -> '%3'")
01796 .arg(pathname).arg(GetBasename()).arg(new_basename));
01797 }
01798 SetPathname(new_basename);
01799 }
01800
01801 hostname = query.value(15).toString();
01802 storagegroup = query.value(13).toString();
01803
01804 seriesid = query.value(17).toString();
01805 programid = query.value(18).toString();
01806 inetref = query.value(19).toString();
01807
01808
01809 recpriority = query.value(16).toInt();
01810
01811 filesize = query.value(20).toULongLong();
01812
01813 startts = query.value(21).toDateTime();
01814 endts = query.value(22).toDateTime();
01815 recstartts = query.value(24).toDateTime();
01816 recendts = query.value(25).toDateTime();
01817
01818 stars = clamp((float)query.value(23).toDouble(), 0.0f, 1.0f);
01819
01820 year = query.value(26).toUInt();
01821 originalAirDate = query.value(27).toDate();
01822 lastmodified = query.value(28).toDateTime();
01823
01824
01825 recstatus = rsRecorded;
01826
01827
01828
01829
01830
01831 recordid = query.value(29).toUInt();
01832
01833
01834
01835
01836
01837 findid = query.value(45).toUInt();
01838
01839
01840 dupin = RecordingDupInType(query.value(46).toInt());
01841 dupmethod = RecordingDupMethodType(query.value(47).toInt());
01842
01843
01844 set_flag(programflags, FL_CHANCOMMFREE,
01845 query.value(30).toInt() == COMM_DETECT_COMMFREE);
01846 set_flag(programflags, FL_COMMFLAG,
01847 query.value(31).toInt() == COMM_FLAG_DONE);
01848 set_flag(programflags, FL_COMMPROCESSING ,
01849 query.value(31).toInt() == COMM_FLAG_PROCESSING);
01850 set_flag(programflags, FL_REPEAT, query.value(29).toBool());
01851 set_flag(programflags, FL_TRANSCODED,
01852 query.value(34).toInt() == TRANSCODING_COMPLETE);
01853 set_flag(programflags, FL_DELETEPENDING, query.value(35).toBool());
01854 set_flag(programflags, FL_PRESERVED, query.value(36).toBool());
01855 set_flag(programflags, FL_CUTLIST, query.value(37).toBool());
01856 set_flag(programflags, FL_AUTOEXP, query.value(38).toBool());
01857 set_flag(programflags, FL_REALLYEDITING, query.value(39).toBool());
01858 set_flag(programflags, FL_BOOKMARK, query.value(40).toBool());
01859 set_flag(programflags, FL_WATCHED, query.value(41).toBool());
01860 set_flag(programflags, FL_EDITING,
01861 (programflags & FL_REALLYEDITING) ||
01862 (programflags & FL_COMMPROCESSING));
01863
01864 properties = ((query.value(44).toUInt() << kSubtitlePropertyOffset) |
01865 (query.value(43).toUInt() << kVideoPropertyOffset) |
01866 (query.value(42).toUInt() << kAudioPropertyOffset));
01867
01868
01869 if (originalAirDate.isValid() && originalAirDate < QDate(1940, 1, 1))
01870 originalAirDate = QDate();
01871
01872
01873
01874
01875
01876
01877
01878
01879
01880 return true;
01881 }
01882
01886 int ProgramInfo::GetRecordingTypeRecPriority(RecordingType type)
01887 {
01888 switch (type)
01889 {
01890 case kSingleRecord:
01891 return gCoreContext->GetNumSetting("SingleRecordRecPriority", 1);
01892 case kTimeslotRecord:
01893 return gCoreContext->GetNumSetting("TimeslotRecordRecPriority", 0);
01894 case kWeekslotRecord:
01895 return gCoreContext->GetNumSetting("WeekslotRecordRecPriority", 0);
01896 case kChannelRecord:
01897 return gCoreContext->GetNumSetting("ChannelRecordRecPriority", 0);
01898 case kAllRecord:
01899 return gCoreContext->GetNumSetting("AllRecordRecPriority", 0);
01900 case kFindOneRecord:
01901 case kFindDailyRecord:
01902 case kFindWeeklyRecord:
01903 return gCoreContext->GetNumSetting("FindOneRecordRecPriority", -1);
01904 case kOverrideRecord:
01905 case kDontRecord:
01906 return gCoreContext->GetNumSetting("OverrideRecordRecPriority", 0);
01907 default:
01908 return 0;
01909 }
01910 }
01911
01916 bool ProgramInfo::IsSameProgramWeakCheck(const ProgramInfo &other) const
01917 {
01918 return (title == other.title &&
01919 chanid == other.chanid &&
01920 startts == other.startts);
01921 }
01922
01927 bool ProgramInfo::IsSameProgram(const ProgramInfo& other) const
01928 {
01929 if (GetRecordingRuleType() == kFindOneRecord)
01930 return recordid == other.recordid;
01931
01932 if (findid && findid == other.findid &&
01933 (recordid == other.recordid || recordid == other.parentid))
01934 return true;
01935
01936 if (dupmethod & kDupCheckNone)
01937 return false;
01938
01939 if (title.compare(other.title, Qt::CaseInsensitive) != 0)
01940 return false;
01941
01942 if (catType == "series")
01943 {
01944 if (programid.endsWith("0000"))
01945 return false;
01946 }
01947
01948 if (!programid.isEmpty() && !other.programid.isEmpty())
01949 {
01950 if (usingProgIDAuth)
01951 {
01952 int index = programid.indexOf('/');
01953 int oindex = other.programid.indexOf('/');
01954 if (index == oindex && (index < 0 ||
01955 programid.leftRef(index) == other.programid.leftRef(oindex)))
01956 return programid == other.programid;
01957 }
01958 else
01959 {
01960 return programid == other.programid;
01961 }
01962 }
01963
01964 if ((dupmethod & kDupCheckSub) &&
01965 ((subtitle.isEmpty()) ||
01966 (subtitle.compare(other.subtitle, Qt::CaseInsensitive) != 0)))
01967 return false;
01968
01969 if ((dupmethod & kDupCheckDesc) &&
01970 ((description.isEmpty()) ||
01971 (description.compare(other.description, Qt::CaseInsensitive) != 0)))
01972 return false;
01973
01974 if ((dupmethod & kDupCheckSubThenDesc) &&
01975 ((subtitle.isEmpty() &&
01976 ((!other.subtitle.isEmpty() &&
01977 description.compare(other.subtitle, Qt::CaseInsensitive) != 0) ||
01978 (other.subtitle.isEmpty() &&
01979 description.compare(other.description, Qt::CaseInsensitive) != 0))) ||
01980 (!subtitle.isEmpty() &&
01981 ((other.subtitle.isEmpty() &&
01982 subtitle.compare(other.description, Qt::CaseInsensitive) != 0) ||
01983 (!other.subtitle.isEmpty() &&
01984 subtitle.compare(other.subtitle, Qt::CaseInsensitive) != 0)))))
01985 return false;
01986
01987 return true;
01988 }
01989
01995 bool ProgramInfo::IsSameTimeslot(const ProgramInfo& other) const
01996 {
01997 if (title.compare(other.title, Qt::CaseInsensitive) != 0)
01998 return false;
01999 if (startts == other.startts &&
02000 (chanid == other.chanid ||
02001 (!chansign.isEmpty() &&
02002 chansign.compare(other.chansign, Qt::CaseInsensitive) == 0)))
02003 return true;
02004
02005 return false;
02006 }
02007
02014 bool ProgramInfo::IsSameProgramTimeslot(const ProgramInfo &other) const
02015 {
02016 if (title.compare(other.title, Qt::CaseInsensitive) != 0)
02017 return false;
02018 if ((chanid == other.chanid ||
02019 (!chansign.isEmpty() &&
02020 chansign.compare(other.chansign, Qt::CaseInsensitive) == 0)) &&
02021 startts < other.endts &&
02022 endts > other.startts)
02023 return true;
02024
02025 return false;
02026 }
02027
02028 void ProgramInfo::CheckProgramIDAuthorities(void)
02029 {
02030 QMap<QString, int> authMap;
02031 QString tables[] = { "program", "recorded", "oldrecorded", "" };
02032 MSqlQuery query(MSqlQuery::InitCon());
02033
02034 int tableIndex = 0;
02035 QString table = tables[tableIndex];
02036 while (!table.isEmpty())
02037 {
02038 query.prepare(QString(
02039 "SELECT DISTINCT LEFT(programid, LOCATE('/', programid)) "
02040 "FROM %1 WHERE programid <> ''").arg(table));
02041 if (!query.exec())
02042 MythDB::DBError("CheckProgramIDAuthorities", query);
02043 else
02044 {
02045 while (query.next())
02046 authMap[query.value(0).toString()] = 1;
02047 }
02048 ++tableIndex;
02049 table = tables[tableIndex];
02050 }
02051
02052 int numAuths = authMap.count();
02053 LOG(VB_GENERAL, LOG_INFO,
02054 QString("Found %1 distinct programid authorities").arg(numAuths));
02055
02056 usingProgIDAuth = (numAuths > 1);
02057 }
02058
02063 QString ProgramInfo::CreateRecordBasename(const QString &ext) const
02064 {
02065 QString starts = recstartts.toString("yyyyMMddhhmmss");
02066
02067 QString retval = QString("%1_%2.%3").arg(chanid)
02068 .arg(starts).arg(ext);
02069
02070 return retval;
02071 }
02072
02073 static ProgramInfoType discover_program_info_type(
02074 uint chanid, const QString &pathname, bool use_remote)
02075 {
02076 QString fn_lower = pathname.toLower();
02077 ProgramInfoType pit = kProgramInfoTypeVideoFile;
02078 if (chanid)
02079 pit = kProgramInfoTypeRecording;
02080 else if (fn_lower.startsWith("http:"))
02081 pit = kProgramInfoTypeVideoStreamingHTML;
02082 else if (fn_lower.startsWith("rtsp:"))
02083 pit = kProgramInfoTypeVideoStreamingRTSP;
02084 else
02085 {
02086 if (fn_lower.startsWith("dvd:") ||
02087 fn_lower.endsWith(".iso") ||
02088 fn_lower.endsWith(".img") ||
02089 ((pathname.left(1) == "/") &&
02090 QDir(pathname + "/VIDEO_TS").exists()))
02091 {
02092 pit = kProgramInfoTypeVideoDVD;
02093 }
02094 else if (fn_lower.startsWith("bd:") ||
02095 ((pathname.left(1) == "/") &&
02096 QDir(pathname + "/BDMV").exists()))
02097 {
02098 pit = kProgramInfoTypeVideoBD;
02099 }
02100 else if (use_remote && fn_lower.startsWith("myth://"))
02101 {
02102 QString tmpFileDVD = pathname + "/VIDEO_TS";
02103 QString tmpFileBD = pathname + "/BDMV";
02104 if (RemoteFile::Exists(tmpFileDVD))
02105 pit = kProgramInfoTypeVideoDVD;
02106 else if (RemoteFile::Exists(tmpFileBD))
02107 pit = kProgramInfoTypeVideoBD;
02108 }
02109 }
02110 return pit;
02111 }
02112
02113 void ProgramInfo::SetPathname(const QString &pn) const
02114 {
02115 pathname = pn;
02116 pathname.detach();
02117
02118 ProgramInfoType pit = discover_program_info_type(chanid, pathname, false);
02119 const_cast<ProgramInfo*>(this)->SetProgramInfoType(pit);
02120 }
02121
02122 ProgramInfoType ProgramInfo::DiscoverProgramInfoType(void) const
02123 {
02124 return discover_program_info_type(chanid, pathname, true);
02125 }
02126
02127 void ProgramInfo::SetAvailableStatus(
02128 AvailableStatusType status, const QString &where)
02129 {
02130 if (status != availableStatus)
02131 {
02132 LOG(VB_GUI, LOG_INFO,
02133 toString(kTitleSubtitle) + QString(": %1 -> %2")
02134 .arg(::toString((AvailableStatusType)availableStatus))
02135 .arg(::toString(status)));
02136 }
02137 availableStatus = status;
02138 }
02139
02143 bool ProgramInfo::SaveBasename(const QString &basename)
02144 {
02145 MSqlQuery query(MSqlQuery::InitCon());
02146 query.prepare("UPDATE recorded "
02147 "SET basename = :BASENAME "
02148 "WHERE chanid = :CHANID AND "
02149 " starttime = :STARTTIME;");
02150 query.bindValue(":CHANID", chanid);
02151 query.bindValue(":STARTTIME", recstartts);
02152 query.bindValue(":BASENAME", basename);
02153
02154 if (!query.exec())
02155 {
02156 MythDB::DBError("SetRecordBasename", query);
02157 return false;
02158 }
02159
02160 SetPathname(basename);
02161
02162 SendUpdateEvent();
02163 return true;
02164 }
02165
02173 QString ProgramInfo::QueryBasename(void) const
02174 {
02175 QString bn = GetBasename();
02176 if (!bn.isEmpty())
02177 return bn;
02178
02179 MSqlQuery query(MSqlQuery::InitCon());
02180 query.prepare(
02181 "SELECT basename "
02182 "FROM recorded "
02183 "WHERE chanid = :CHANID AND "
02184 " starttime = :STARTTIME");
02185 query.bindValue(":CHANID", chanid);
02186 query.bindValue(":STARTTIME", recstartts);
02187
02188 if (!query.exec())
02189 {
02190 MythDB::DBError("QueryBasename", query);
02191 }
02192 else if (query.next())
02193 {
02194 return query.value(0).toString();
02195 }
02196 else
02197 {
02198 LOG(VB_GENERAL, LOG_INFO,
02199 QString("QueryBasename found no entry for %1 @ %2")
02200 .arg(chanid).arg(recstartts.toString(Qt::ISODate)));
02201 }
02202
02203 return QString();
02204 }
02205
02213 QString ProgramInfo::GetPlaybackURL(
02214 bool checkMaster, bool forceCheckLocal) const
02215 {
02216
02217 if (IsVideoBD() || IsVideoDVD())
02218 return GetPathname();
02219
02220 QString basename = QueryBasename();
02221 if (basename.isEmpty())
02222 return "";
02223
02224 bool checklocal = !gCoreContext->GetNumSetting("AlwaysStreamFiles", 0) ||
02225 forceCheckLocal;
02226
02227 if (IsVideo())
02228 {
02229 QString fullpath = GetPathname();
02230 if (!fullpath.startsWith("myth://", Qt::CaseInsensitive) || !checklocal)
02231 return fullpath;
02232
02233 QUrl url = QUrl(fullpath);
02234 QString path = url.path();
02235 QString host = url.toString(QUrl::RemovePath).mid(7);
02236 QStringList list = host.split(":", QString::SkipEmptyParts);
02237 if (list.size())
02238 {
02239 host = list[0];
02240 list = host.split("@", QString::SkipEmptyParts);
02241 QString group;
02242 if (list.size() > 0 && list.size() < 3)
02243 {
02244 host = list.size() == 1 ? list[0] : list[1];
02245 group = list.size() == 1 ? QString() : list[0];
02246 StorageGroup sg = StorageGroup(group, host);
02247 QString local = sg.FindFile(path);
02248 if (!local.isEmpty() && sg.FileExists(local))
02249 return local;
02250 }
02251 }
02252 return fullpath;
02253 }
02254
02255 QString tmpURL;
02256 if (checklocal)
02257 {
02258
02259 StorageGroup sgroup(storagegroup);
02260 #if 0
02261 LOG(VB_FILE, LOG_DEBUG, LOC +
02262 QString("GetPlaybackURL: CHECKING SG : %1 : ").arg(tmpURL));
02263 #endif
02264 tmpURL = sgroup.FindFile(basename);
02265
02266 if (!tmpURL.isEmpty())
02267 {
02268 LOG(VB_FILE, LOG_INFO, LOC +
02269 QString("GetPlaybackURL: File is local: '%1'") .arg(tmpURL));
02270 return tmpURL;
02271 }
02272 else if (hostname == gCoreContext->GetHostName())
02273 {
02274 LOG(VB_GENERAL, LOG_ERR, LOC +
02275 QString("GetPlaybackURL: '%1' should be local, but it can "
02276 "not be found.").arg(basename));
02277
02278
02279 return QString("GetPlaybackURL/UNABLE/TO/FIND/LOCAL/FILE/ON/%1/%2")
02280 .arg(hostname).arg(basename);
02281 }
02282 }
02283
02284
02285 if ((checkMaster) &&
02286 (gCoreContext->GetNumSetting("MasterBackendOverride", 0)) &&
02287 (RemoteCheckFile(this, false)))
02288 {
02289 tmpURL = gCoreContext->GenMythURL(gCoreContext->GetSetting("MasterServerIP"),
02290 gCoreContext->GetSetting("MasterServerPort").toInt(),
02291 basename);
02292
02293 LOG(VB_FILE, LOG_INFO, LOC +
02294 QString("GetPlaybackURL: Found @ '%1'").arg(tmpURL));
02295 return tmpURL;
02296 }
02297
02298
02299 tmpURL = gCoreContext->GenMythURL(gCoreContext->GetBackendServerIP(hostname),
02300 gCoreContext->GetSettingOnHost("BackendServerPort", hostname).toInt(),
02301 basename);
02302
02303 LOG(VB_FILE, LOG_INFO, LOC +
02304 QString("GetPlaybackURL: Using default of: '%1'") .arg(tmpURL));
02305
02306 return tmpURL;
02307 }
02308
02312 void ProgramInfo::SaveFilesize(uint64_t fsize)
02313 {
02314 SetFilesize(fsize);
02315
02316 MSqlQuery query(MSqlQuery::InitCon());
02317 query.prepare(
02318 "UPDATE recorded "
02319 "SET filesize = :FILESIZE "
02320 "WHERE chanid = :CHANID AND "
02321 " starttime = :STARTTIME");
02322 query.bindValue(":FILESIZE", (quint64)fsize);
02323 query.bindValue(":CHANID", chanid);
02324 query.bindValue(":STARTTIME", recstartts);
02325
02326 if (!query.exec())
02327 MythDB::DBError("File size update", query);
02328
02329 updater->insert(chanid, recstartts, kPIUpdateFileSize, fsize);
02330 }
02331
02333 uint64_t ProgramInfo::QueryFilesize(void) const
02334 {
02335 MSqlQuery query(MSqlQuery::InitCon());
02336
02337 query.prepare(
02338 "SELECT filesize "
02339 "FROM recorded "
02340 "WHERE chanid = :CHANID AND "
02341 " starttime = :STARTTIME");
02342 query.bindValue(":CHANID", chanid);
02343 query.bindValue(":STARTTIME", recstartts);
02344 if (query.exec() && query.next())
02345 return query.value(0).toULongLong();
02346
02347 return filesize;
02348 }
02349
02352 uint ProgramInfo::QueryMplexID(void) const
02353 {
02354 uint ret = 0U;
02355 if (chanid)
02356 {
02357 MSqlQuery query(MSqlQuery::InitCon());
02358
02359 query.prepare("SELECT mplexid FROM channel "
02360 "WHERE chanid = :CHANID");
02361 query.bindValue(":CHANID", chanid);
02362
02363 if (!query.exec())
02364 MythDB::DBError("QueryMplexID", query);
02365 else if (query.next())
02366 ret = query.value(0).toUInt();
02367
02368
02369 ret = (32767 == ret) ? 0 : ret;
02370 }
02371
02372 return ret;
02373 }
02374
02377 void ProgramInfo::SaveBookmark(uint64_t frame)
02378 {
02379 ClearMarkupMap(MARK_BOOKMARK);
02380
02381 bool is_valid = (frame > 0);
02382 if (is_valid)
02383 {
02384 frm_dir_map_t bookmarkmap;
02385 bookmarkmap[frame] = MARK_BOOKMARK;
02386 SaveMarkupMap(bookmarkmap);
02387 }
02388
02389 if (IsRecording())
02390 {
02391 MSqlQuery query(MSqlQuery::InitCon());
02392 query.prepare(
02393 "UPDATE recorded "
02394 "SET bookmarkupdate = CURRENT_TIMESTAMP, "
02395 " bookmark = :BOOKMARKFLAG "
02396 "WHERE chanid = :CHANID AND "
02397 " starttime = :STARTTIME");
02398
02399 query.bindValue(":BOOKMARKFLAG", is_valid);
02400 query.bindValue(":CHANID", chanid);
02401 query.bindValue(":STARTTIME", recstartts);
02402
02403 if (!query.exec())
02404 MythDB::DBError("bookmark flag update", query);
02405 }
02406
02407 set_flag(programflags, FL_BOOKMARK, is_valid);
02408
02409 SendUpdateEvent();
02410 }
02411
02412 void ProgramInfo::SendUpdateEvent(void)
02413 {
02414 updater->insert(chanid, recstartts, kPIUpdate);
02415 }
02416
02417 void ProgramInfo::SendAddedEvent(void) const
02418 {
02419 updater->insert(chanid, recstartts, kPIAdd);
02420 }
02421
02422 void ProgramInfo::SendDeletedEvent(void) const
02423 {
02424 updater->insert(chanid, recstartts, kPIDelete);
02425 }
02426
02430 QDateTime ProgramInfo::QueryBookmarkTimeStamp(void) const
02431 {
02432 MSqlQuery query(MSqlQuery::InitCon());
02433 query.prepare(
02434 "SELECT bookmarkupdate "
02435 "FROM recorded "
02436 "WHERE chanid = :CHANID AND"
02437 " starttime = :STARTTIME");
02438 query.bindValue(":CHANID", chanid);
02439 query.bindValue(":STARTTIME", recstartts);
02440
02441 QDateTime ts;
02442
02443 if (!query.exec())
02444 MythDB::DBError("ProgramInfo::GetBookmarkTimeStamp()", query);
02445 else if (query.next())
02446 ts = query.value(0).toDateTime();
02447
02448 return ts;
02449 }
02450
02457 uint64_t ProgramInfo::QueryBookmark(void) const
02458 {
02459 if (programflags & FL_IGNOREBOOKMARK)
02460 return 0;
02461
02462 frm_dir_map_t bookmarkmap;
02463 QueryMarkupMap(bookmarkmap, MARK_BOOKMARK);
02464
02465 return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
02466 }
02467
02468 uint64_t ProgramInfo::QueryBookmark(uint chanid, const QDateTime &recstartts)
02469 {
02470 frm_dir_map_t bookmarkmap;
02471 QueryMarkupMap(
02472 chanid, recstartts,
02473 bookmarkmap, MARK_BOOKMARK);
02474
02475 return (bookmarkmap.isEmpty()) ? 0 : bookmarkmap.begin().key();
02476 }
02477
02483 QStringList ProgramInfo::QueryDVDBookmark(
02484 const QString &serialid) const
02485 {
02486 QStringList fields = QStringList();
02487 MSqlQuery query(MSqlQuery::InitCon());
02488
02489 if (!(programflags & FL_IGNOREBOOKMARK))
02490 {
02491 query.prepare(" SELECT title, framenum, audionum, subtitlenum "
02492 " FROM dvdbookmark "
02493 " WHERE serialid = :SERIALID ");
02494 query.bindValue(":SERIALID", serialid);
02495
02496 if (query.exec() && query.next())
02497 {
02498 for(int i = 0; i < 4; i++)
02499 fields.append(query.value(i).toString());
02500 }
02501 }
02502
02503 return fields;
02504 }
02505
02506 void ProgramInfo::SaveDVDBookmark(const QStringList &fields) const
02507 {
02508 QStringList::const_iterator it = fields.begin();
02509 MSqlQuery query(MSqlQuery::InitCon());
02510
02511 QString serialid = *(it);
02512 QString name = *(++it);
02513 QString title = *(++it);
02514 QString audionum = *(++it);
02515 QString subtitlenum = *(++it);
02516 QString frame = *(++it);
02517
02518 query.prepare("INSERT IGNORE INTO dvdbookmark "
02519 " (serialid, name)"
02520 " VALUES ( :SERIALID, :NAME );");
02521 query.bindValue(":SERIALID", serialid);
02522 query.bindValue(":NAME", name);
02523
02524 if (!query.exec())
02525 MythDB::DBError("SetDVDBookmark inserting", query);
02526
02527 query.prepare(" UPDATE dvdbookmark "
02528 " SET title = :TITLE , "
02529 " audionum = :AUDIONUM , "
02530 " subtitlenum = :SUBTITLENUM , "
02531 " framenum = :FRAMENUM , "
02532 " timestamp = NOW() "
02533 " WHERE serialid = :SERIALID");
02534 query.bindValue(":TITLE",title);
02535 query.bindValue(":AUDIONUM",audionum);
02536 query.bindValue(":SUBTITLENUM",subtitlenum);
02537 query.bindValue(":FRAMENUM",frame);
02538 query.bindValue(":SERIALID",serialid);
02539
02540 if (!query.exec())
02541 MythDB::DBError("SetDVDBookmark updating", query);
02542 }
02543
02549 QString ProgramInfo::QueryCategoryType(void) const
02550 {
02551 QString ret;
02552
02553 MSqlQuery query(MSqlQuery::InitCon());
02554
02555 query.prepare(" SELECT category_type "
02556 " FROM recordedprogram "
02557 " WHERE chanid = :CHANID "
02558 " AND starttime = :STARTTIME;");
02559
02560 query.bindValue(":CHANID", chanid);
02561 query.bindValue(":STARTTIME", startts);
02562
02563 if (query.exec() && query.next())
02564 {
02565 ret = query.value(0).toString();
02566 }
02567
02568 return ret;
02569 }
02570
02572 void ProgramInfo::SaveWatched(bool watched)
02573 {
02574 if (IsRecording())
02575 {
02576 MSqlQuery query(MSqlQuery::InitCon());
02577
02578 query.prepare("UPDATE recorded"
02579 " SET watched = :WATCHEDFLAG"
02580 " WHERE chanid = :CHANID"
02581 " AND starttime = :STARTTIME ;");
02582 query.bindValue(":CHANID", chanid);
02583 query.bindValue(":STARTTIME", recstartts);
02584 query.bindValue(":WATCHEDFLAG", watched);
02585
02586 if (!query.exec())
02587 MythDB::DBError("Set watched flag", query);
02588 else
02589 UpdateLastDelete(watched);
02590 }
02591 else if (IsVideoFile())
02592 {
02593 QString url = pathname;
02594 if (url.startsWith("myth://"))
02595 {
02596 url = QUrl(url).path();
02597 url.remove(0,1);
02598 }
02599
02600 MSqlQuery query(MSqlQuery::InitCon());
02601 query.prepare("UPDATE videometadata"
02602 " SET watched = :WATCHEDFLAG"
02603 " WHERE title = :TITLE"
02604 " AND subtitle = :SUBTITLE"
02605 " AND filename = :FILENAME ;");
02606 query.bindValue(":TITLE", title);
02607 query.bindValue(":SUBTITLE", subtitle);
02608 query.bindValue(":FILENAME", url);
02609 query.bindValue(":WATCHEDFLAG", watched);
02610
02611 if (!query.exec())
02612 MythDB::DBError("Set watched flag", query);
02613 }
02614
02615 set_flag(programflags, FL_WATCHED, watched);
02616 SendUpdateEvent();
02617 }
02618
02623 bool ProgramInfo::QueryIsEditing(void) const
02624 {
02625 bool editing = programflags & FL_REALLYEDITING;
02626
02627 MSqlQuery query(MSqlQuery::InitCon());
02628
02629 query.prepare("SELECT editing FROM recorded"
02630 " WHERE chanid = :CHANID"
02631 " AND starttime = :STARTTIME ;");
02632 query.bindValue(":CHANID", chanid);
02633 query.bindValue(":STARTTIME", recstartts);
02634
02635 if (query.exec() && query.next())
02636 editing = query.value(0).toBool();
02637
02638
02639
02640
02641
02642
02643 return editing;
02644 }
02645
02649 void ProgramInfo::SaveEditing(bool edit)
02650 {
02651 MSqlQuery query(MSqlQuery::InitCon());
02652
02653 query.prepare("UPDATE recorded"
02654 " SET editing = :EDIT"
02655 " WHERE chanid = :CHANID"
02656 " AND starttime = :STARTTIME ;");
02657 query.bindValue(":EDIT", edit);
02658 query.bindValue(":CHANID", chanid);
02659 query.bindValue(":STARTTIME", recstartts);
02660
02661 if (!query.exec())
02662 MythDB::DBError("Edit status update", query);
02663
02664 set_flag(programflags, FL_REALLYEDITING, edit);
02665 set_flag(programflags, FL_EDITING, ((programflags & FL_REALLYEDITING) ||
02666 (programflags & COMM_FLAG_PROCESSING)));
02667
02668 SendUpdateEvent();
02669 }
02670
02674 void ProgramInfo::SaveDeletePendingFlag(bool deleteFlag)
02675 {
02676 MSqlQuery query(MSqlQuery::InitCon());
02677
02678 query.prepare("UPDATE recorded"
02679 " SET deletepending = :DELETEFLAG, "
02680 " duplicate = 0 "
02681 " WHERE chanid = :CHANID"
02682 " AND starttime = :STARTTIME ;");
02683 query.bindValue(":CHANID", chanid);
02684 query.bindValue(":STARTTIME", recstartts);
02685 query.bindValue(":DELETEFLAG", deleteFlag);
02686
02687 if (!query.exec())
02688 MythDB::DBError("SaveDeletePendingFlag", query);
02689
02690 set_flag(programflags, FL_DELETEPENDING, deleteFlag);
02691
02692 if (!deleteFlag)
02693 SendAddedEvent();
02694
02695 SendUpdateEvent();
02696 }
02697
02702 bool ProgramInfo::QueryIsInUse(QStringList &byWho) const
02703 {
02704 if (!IsRecording())
02705 return false;
02706
02707 QDateTime oneHourAgo = QDateTime::currentDateTime().addSecs(-61 * 60);
02708 MSqlQuery query(MSqlQuery::InitCon());
02709
02710 query.prepare("SELECT hostname, recusage FROM inuseprograms "
02711 " WHERE chanid = :CHANID"
02712 " AND starttime = :STARTTIME "
02713 " AND lastupdatetime > :ONEHOURAGO ;");
02714 query.bindValue(":CHANID", chanid);
02715 query.bindValue(":STARTTIME", recstartts);
02716 query.bindValue(":ONEHOURAGO", oneHourAgo);
02717
02718 byWho.clear();
02719 if (query.exec() && query.size() > 0)
02720 {
02721 QString usageStr, recusage;
02722 while (query.next())
02723 {
02724 usageStr = QObject::tr("Unknown");
02725 recusage = query.value(1).toString();
02726
02727 if (recusage == kPlayerInUseID)
02728 usageStr = QObject::tr("Playing");
02729 else if (recusage == kPIPPlayerInUseID)
02730 usageStr = QObject::tr("PIP");
02731 else if (recusage == kPBPPlayerInUseID)
02732 usageStr = QObject::tr("PBP");
02733 else if ((recusage == kRecorderInUseID) ||
02734 (recusage == kImportRecorderInUseID))
02735 usageStr = QObject::tr("Recording");
02736 else if (recusage == kFileTransferInUseID)
02737 usageStr = QObject::tr("File transfer");
02738 else if (recusage == kTruncatingDeleteInUseID)
02739 usageStr = QObject::tr("Delete");
02740 else if (recusage == kFlaggerInUseID)
02741 usageStr = QObject::tr("Commercial Detection");
02742 else if (recusage == kTranscoderInUseID)
02743 usageStr = QObject::tr("Transcoding");
02744 else if (recusage == kPreviewGeneratorInUseID)
02745 usageStr = QObject::tr("Preview Generation");
02746 else if (recusage == kJobQueueInUseID)
02747 usageStr = QObject::tr("User Job");
02748
02749 byWho.push_back(recusage);
02750 byWho.push_back(query.value(0).toString());
02751 byWho.push_back(query.value(0).toString() + " (" + usageStr + ")");
02752 }
02753
02754 return true;
02755 }
02756
02757 return false;
02758 }
02759
02764 bool ProgramInfo::QueryIsInUse(QString &byWho) const
02765 {
02766 QStringList users;
02767 bool inuse = QueryIsInUse(users);
02768 byWho.clear();
02769 for (uint i = 0; i+2 < (uint)users.size(); i+=3)
02770 byWho += users[i+2] + "\n";
02771 return inuse;
02772 }
02773
02774
02781 bool ProgramInfo::QueryIsDeleteCandidate(bool one_playback_allowed) const
02782 {
02783 if (!IsRecording())
02784 return false;
02785
02786
02787 if (GetRecordingGroup() != "Deleted" && GetRecordingGroup() != "LiveTV")
02788 return true;
02789
02790 bool ok = true;
02791 QStringList byWho;
02792 if (QueryIsInUse(byWho) && !byWho.isEmpty())
02793 {
02794 uint play_cnt = 0, ft_cnt = 0, jq_cnt = 0;
02795 for (uint i = 0; (i+2 < (uint)byWho.size()) && ok; i+=3)
02796 {
02797 play_cnt += byWho[i].contains(kPlayerInUseID) ? 1 : 0;
02798 ft_cnt += (byWho[i].contains(kFlaggerInUseID) ||
02799 byWho[i].contains(kTranscoderInUseID)) ? 1 : 0;
02800 jq_cnt += (byWho[i].contains(kJobQueueInUseID)) ? 1 : 0;
02801 ok = ok && (byWho[i].contains(kRecorderInUseID) ||
02802 byWho[i].contains(kFlaggerInUseID) ||
02803 byWho[i].contains(kTranscoderInUseID) ||
02804 byWho[i].contains(kJobQueueInUseID) ||
02805 (one_playback_allowed && (play_cnt <= 1)));
02806 }
02807 ok = ok && (ft_cnt == jq_cnt);
02808 }
02809
02810 return ok;
02811 }
02812
02814 TranscodingStatus ProgramInfo::QueryTranscodeStatus(void) const
02815 {
02816 MSqlQuery query(MSqlQuery::InitCon());
02817
02818 query.prepare("SELECT transcoded FROM recorded"
02819 " WHERE chanid = :CHANID"
02820 " AND starttime = :STARTTIME ;");
02821 query.bindValue(":CHANID", chanid);
02822 query.bindValue(":STARTTIME", recstartts);
02823
02824 if (query.exec() && query.next())
02825 return (TranscodingStatus) query.value(0).toUInt();
02826 return TRANSCODING_NOT_TRANSCODED;
02827 }
02828
02834 void ProgramInfo::SaveTranscodeStatus(TranscodingStatus trans)
02835 {
02836 MSqlQuery query(MSqlQuery::InitCon());
02837
02838 query.prepare(
02839 "UPDATE recorded "
02840 "SET transcoded = :VALUE "
02841 "WHERE chanid = :CHANID AND"
02842 " starttime = :STARTTIME");
02843 query.bindValue(":VALUE", (uint)trans);
02844 query.bindValue(":CHANID", chanid);
02845 query.bindValue(":STARTTIME", recstartts);
02846
02847 if (!query.exec())
02848 MythDB::DBError("Transcoded status update", query);
02849
02850 set_flag(programflags, FL_TRANSCODED, TRANSCODING_COMPLETE == trans);
02851 SendUpdateEvent();
02852 }
02853
02857 void ProgramInfo::SaveCommFlagged(CommFlagStatus flag)
02858 {
02859 MSqlQuery query(MSqlQuery::InitCon());
02860
02861 query.prepare("UPDATE recorded"
02862 " SET commflagged = :FLAG"
02863 " WHERE chanid = :CHANID"
02864 " AND starttime = :STARTTIME ;");
02865 query.bindValue(":FLAG", (int)flag);
02866 query.bindValue(":CHANID", chanid);
02867 query.bindValue(":STARTTIME", recstartts);
02868
02869 if (!query.exec())
02870 MythDB::DBError("Commercial Flagged status update", query);
02871
02872 set_flag(programflags, FL_COMMFLAG, COMM_FLAG_DONE == flag);
02873 set_flag(programflags, FL_COMMPROCESSING, COMM_FLAG_PROCESSING == flag);
02874 set_flag(programflags, FL_EDITING, ((programflags & FL_REALLYEDITING) ||
02875 (programflags & COMM_FLAG_PROCESSING)));
02876 SendUpdateEvent();
02877 }
02878
02879
02883 void ProgramInfo::SavePreserve(bool preserveEpisode)
02884 {
02885 MSqlQuery query(MSqlQuery::InitCon());
02886
02887 query.prepare("UPDATE recorded"
02888 " SET preserve = :PRESERVE"
02889 " WHERE chanid = :CHANID"
02890 " AND starttime = :STARTTIME ;");
02891 query.bindValue(":PRESERVE", preserveEpisode);
02892 query.bindValue(":CHANID", chanid);
02893 query.bindValue(":STARTTIME", recstartts);
02894
02895 if (!query.exec())
02896 MythDB::DBError("PreserveEpisode update", query);
02897 else
02898 UpdateLastDelete(false);
02899
02900 set_flag(programflags, FL_PRESERVED, preserveEpisode);
02901
02902 SendUpdateEvent();
02903 }
02904
02910 void ProgramInfo::SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete)
02911 {
02912 MSqlQuery query(MSqlQuery::InitCon());
02913
02914 query.prepare("UPDATE recorded"
02915 " SET autoexpire = :AUTOEXPIRE"
02916 " WHERE chanid = :CHANID"
02917 " AND starttime = :STARTTIME ;");
02918 query.bindValue(":AUTOEXPIRE", (uint)autoExpire);
02919 query.bindValue(":CHANID", chanid);
02920 query.bindValue(":STARTTIME", recstartts);
02921
02922 if (!query.exec())
02923 MythDB::DBError("AutoExpire update", query);
02924 else if (updateDelete)
02925 UpdateLastDelete(true);
02926
02927 set_flag(programflags, FL_AUTOEXP, (uint)autoExpire);
02928
02929 SendUpdateEvent();
02930 }
02931
02936 void ProgramInfo::UpdateLastDelete(bool setTime) const
02937 {
02938 MSqlQuery query(MSqlQuery::InitCon());
02939
02940 if (setTime)
02941 {
02942 QDateTime timeNow = QDateTime::currentDateTime();
02943 int delay = recstartts.secsTo(timeNow) / 3600;
02944
02945 if (delay > 200)
02946 delay = 200;
02947 else if (delay < 1)
02948 delay = 1;
02949
02950 query.prepare("UPDATE record SET last_delete = :TIME, "
02951 "avg_delay = (avg_delay * 3 + :DELAY) / 4 "
02952 "WHERE recordid = :RECORDID");
02953 query.bindValue(":TIME", timeNow);
02954 query.bindValue(":DELAY", delay);
02955 }
02956 else
02957 {
02958 query.prepare("UPDATE record SET last_delete = '0000-00-00 00:00:00' "
02959 "WHERE recordid = :RECORDID");
02960 }
02961 query.bindValue(":RECORDID", recordid);
02962
02963 if (!query.exec())
02964 MythDB::DBError("Update last_delete", query);
02965 }
02966
02968 AutoExpireType ProgramInfo::QueryAutoExpire(void) const
02969 {
02970 MSqlQuery query(MSqlQuery::InitCon());
02971
02972 query.prepare("SELECT autoexpire FROM recorded"
02973 " WHERE chanid = :CHANID"
02974 " AND starttime = :STARTTIME ;");
02975 query.bindValue(":CHANID", chanid);
02976 query.bindValue(":STARTTIME", recstartts);
02977
02978 if (query.exec() && query.next())
02979 return (AutoExpireType) query.value(0).toInt();
02980
02981 return kDisableAutoExpire;
02982 }
02983
02984 bool ProgramInfo::QueryCutList(frm_dir_map_t &delMap, bool loadAutoSave) const
02985 {
02986 frm_dir_map_t autosaveMap;
02987 QueryMarkupMap(autosaveMap, MARK_TMP_CUT_START);
02988 QueryMarkupMap(autosaveMap, MARK_TMP_CUT_END, true);
02989 QueryMarkupMap(autosaveMap, MARK_PLACEHOLDER, true);
02990 bool result = !autosaveMap.isEmpty();
02991
02992 if (loadAutoSave)
02993 {
02994
02995 delMap.clear();
02996 frm_dir_map_t::const_iterator i = autosaveMap.constBegin();
02997 for (; i != autosaveMap.constEnd(); ++i)
02998 {
02999 uint64_t frame = i.key();
03000 MarkTypes mark = i.value();
03001 if (mark == MARK_TMP_CUT_START)
03002 mark = MARK_CUT_START;
03003 else if (mark == MARK_TMP_CUT_END)
03004 mark = MARK_CUT_END;
03005 delMap[frame] = mark;
03006 }
03007 }
03008 else
03009 {
03010 QueryMarkupMap(delMap, MARK_CUT_START);
03011 QueryMarkupMap(delMap, MARK_CUT_END, true);
03012 QueryMarkupMap(delMap, MARK_PLACEHOLDER, true);
03013 }
03014
03015 return result;
03016 }
03017
03018 void ProgramInfo::SaveCutList(frm_dir_map_t &delMap, bool isAutoSave) const
03019 {
03020 if (!isAutoSave)
03021 {
03022 ClearMarkupMap(MARK_CUT_START);
03023 ClearMarkupMap(MARK_CUT_END);
03024 }
03025 ClearMarkupMap(MARK_PLACEHOLDER);
03026 ClearMarkupMap(MARK_TMP_CUT_START);
03027 ClearMarkupMap(MARK_TMP_CUT_END);
03028
03029 frm_dir_map_t tmpDelMap;
03030 frm_dir_map_t::const_iterator i = delMap.constBegin();
03031 for (; i != delMap.constEnd(); ++i)
03032 {
03033 uint64_t frame = i.key();
03034 MarkTypes mark = i.value();
03035 if (isAutoSave)
03036 {
03037 if (mark == MARK_CUT_START)
03038 mark = MARK_TMP_CUT_START;
03039 else if (mark == MARK_CUT_END)
03040 mark = MARK_TMP_CUT_END;
03041 }
03042 tmpDelMap[frame] = mark;
03043 }
03044 SaveMarkupMap(tmpDelMap);
03045
03046 if (IsRecording())
03047 {
03048 MSqlQuery query(MSqlQuery::InitCon());
03049
03050
03051 query.prepare("UPDATE recorded"
03052 " SET cutlist = :CUTLIST"
03053 " WHERE chanid = :CHANID"
03054 " AND starttime = :STARTTIME ;");
03055
03056 query.bindValue(":CUTLIST", delMap.isEmpty() ? 0 : 1);
03057 query.bindValue(":CHANID", chanid);
03058 query.bindValue(":STARTTIME", recstartts);
03059
03060 if (!query.exec())
03061 MythDB::DBError("cutlist flag update", query);
03062 }
03063 }
03064
03065 void ProgramInfo::SaveCommBreakList(frm_dir_map_t &frames) const
03066 {
03067 ClearMarkupMap(MARK_COMM_START);
03068 ClearMarkupMap(MARK_COMM_END);
03069 SaveMarkupMap(frames);
03070 }
03071
03072 void ProgramInfo::QueryCommBreakList(frm_dir_map_t &frames) const
03073 {
03074 QueryMarkupMap(frames, MARK_COMM_START);
03075 QueryMarkupMap(frames, MARK_COMM_END, true);
03076 }
03077
03078 void ProgramInfo::ClearMarkupMap(
03079 MarkTypes type, int64_t min_frame, int64_t max_frame) const
03080 {
03081 MSqlQuery query(MSqlQuery::InitCon());
03082 QString comp;
03083
03084 if (min_frame >= 0)
03085 comp += QString(" AND mark >= %1 ").arg(min_frame);
03086
03087 if (max_frame >= 0)
03088 comp += QString(" AND mark <= %1 ").arg(max_frame);
03089
03090 if (type != MARK_ALL)
03091 comp += QString(" AND type = :TYPE ");
03092
03093 if (IsVideo())
03094 {
03095 query.prepare("DELETE FROM filemarkup"
03096 " WHERE filename = :PATH "
03097 + comp + ";");
03098 query.bindValue(":PATH", StorageGroup::GetRelativePathname(pathname));
03099 }
03100 else if (IsRecording())
03101 {
03102 query.prepare("DELETE FROM recordedmarkup"
03103 " WHERE chanid = :CHANID"
03104 " AND STARTTIME = :STARTTIME"
03105 + comp + ';');
03106 query.bindValue(":CHANID", chanid);
03107 query.bindValue(":STARTTIME", recstartts);
03108 }
03109 else
03110 {
03111 return;
03112 }
03113 query.bindValue(":TYPE", type);
03114
03115 if (!query.exec())
03116 MythDB::DBError("ClearMarkupMap deleting", query);
03117 }
03118
03119 void ProgramInfo::SaveMarkupMap(
03120 const frm_dir_map_t &marks, MarkTypes type,
03121 int64_t min_frame, int64_t max_frame) const
03122 {
03123 MSqlQuery query(MSqlQuery::InitCon());
03124 QString videoPath;
03125
03126 if (IsVideo())
03127 {
03128 videoPath = StorageGroup::GetRelativePathname(pathname);
03129 }
03130 else if (IsRecording())
03131 {
03132
03133 query.prepare("SELECT starttime FROM recorded"
03134 " WHERE chanid = :CHANID"
03135 " AND starttime = :STARTTIME ;");
03136 query.bindValue(":CHANID", chanid);
03137 query.bindValue(":STARTTIME", recstartts);
03138
03139 if (!query.exec())
03140 MythDB::DBError("SaveMarkupMap checking record table", query);
03141
03142 if (!query.next())
03143 return;
03144 }
03145 else
03146 {
03147 return;
03148 }
03149
03150 frm_dir_map_t::const_iterator it;
03151 for (it = marks.begin(); it != marks.end(); ++it)
03152 {
03153 uint64_t frame = it.key();
03154 int mark_type;
03155 QString querystr;
03156
03157 if ((min_frame >= 0) && (frame < (uint64_t)min_frame))
03158 continue;
03159
03160 if ((max_frame >= 0) && (frame > (uint64_t)max_frame))
03161 continue;
03162
03163 mark_type = (type != MARK_ALL) ? type : *it;
03164
03165 if (IsVideo())
03166 {
03167 query.prepare("INSERT INTO filemarkup (filename, mark, type)"
03168 " VALUES ( :PATH , :MARK , :TYPE );");
03169 query.bindValue(":PATH", videoPath);
03170 }
03171 else
03172 {
03173 query.prepare("INSERT INTO recordedmarkup"
03174 " (chanid, starttime, mark, type)"
03175 " VALUES ( :CHANID , :STARTTIME , :MARK , :TYPE );");
03176 query.bindValue(":CHANID", chanid);
03177 query.bindValue(":STARTTIME", recstartts);
03178 }
03179 query.bindValue(":MARK", (quint64)frame);
03180 query.bindValue(":TYPE", mark_type);
03181
03182 if (!query.exec())
03183 MythDB::DBError("SaveMarkupMap inserting", query);
03184 }
03185 }
03186
03187 void ProgramInfo::QueryMarkupMap(
03188 frm_dir_map_t &marks, MarkTypes type, bool merge) const
03189 {
03190 if (!merge)
03191 marks.clear();
03192
03193 if (IsVideo())
03194 {
03195 QueryMarkupMap(StorageGroup::GetRelativePathname(pathname),
03196 marks, type, merge);
03197 }
03198 else if (IsRecording())
03199 {
03200 QueryMarkupMap(chanid, recstartts, marks, type, merge);
03201 }
03202 }
03203
03204 void ProgramInfo::QueryMarkupMap(
03205 const QString &video_pathname,
03206 frm_dir_map_t &marks,
03207 MarkTypes type, bool mergeIntoMap)
03208 {
03209 if (!mergeIntoMap)
03210 marks.clear();
03211
03212 MSqlQuery query(MSqlQuery::InitCon());
03213
03214 query.prepare("SELECT mark, type "
03215 "FROM filemarkup "
03216 "WHERE filename = :PATH AND "
03217 " type = :TYPE "
03218 "ORDER BY mark");
03219 query.bindValue(":PATH", video_pathname);
03220 query.bindValue(":TYPE", type);
03221
03222 if (!query.exec())
03223 {
03224 MythDB::DBError("QueryMarkupMap", query);
03225 return;
03226 }
03227
03228 while (query.next())
03229 {
03230 marks[query.value(0).toLongLong()] =
03231 (MarkTypes) query.value(1).toInt();
03232 }
03233 }
03234
03235 void ProgramInfo::QueryMarkupMap(
03236 uint chanid, const QDateTime &recstartts,
03237 frm_dir_map_t &marks,
03238 MarkTypes type, bool mergeIntoMap)
03239 {
03240 if (!mergeIntoMap)
03241 marks.clear();
03242
03243 MSqlQuery query(MSqlQuery::InitCon());
03244 query.prepare("SELECT mark, type "
03245 "FROM recordedmarkup "
03246 "WHERE chanid = :CHANID AND "
03247 " starttime = :STARTTIME AND"
03248 " type = :TYPE "
03249 "ORDER BY mark");
03250 query.bindValue(":CHANID", chanid);
03251 query.bindValue(":STARTTIME", recstartts);
03252 query.bindValue(":TYPE", type);
03253
03254 if (!query.exec())
03255 {
03256 MythDB::DBError("QueryMarkupMap", query);
03257 return;
03258 }
03259
03260 while (query.next())
03261 {
03262 marks[query.value(0).toULongLong()] =
03263 (MarkTypes) query.value(1).toInt();
03264 }
03265 }
03266
03268 bool ProgramInfo::QueryMarkupFlag(MarkTypes type) const
03269 {
03270 frm_dir_map_t flagMap;
03271
03272 QueryMarkupMap(flagMap, type);
03273
03274 return flagMap.contains(0);
03275 }
03276
03278 void ProgramInfo::SaveMarkupFlag(MarkTypes type) const
03279 {
03280 ClearMarkupMap(type);
03281 frm_dir_map_t flagMap;
03282 flagMap[0] = type;
03283 SaveMarkupMap(flagMap, type);
03284 }
03285
03286 void ProgramInfo::QueryPositionMap(
03287 frm_pos_map_t &posMap, MarkTypes type) const
03288 {
03289 if (positionMapDBReplacement)
03290 {
03291 QMutexLocker locker(positionMapDBReplacement->lock);
03292 posMap = positionMapDBReplacement->map[(MarkTypes)type];
03293
03294 return;
03295 }
03296
03297 posMap.clear();
03298 MSqlQuery query(MSqlQuery::InitCon());
03299
03300 if (IsVideo())
03301 {
03302 query.prepare("SELECT mark, offset FROM filemarkup"
03303 " WHERE filename = :PATH"
03304 " AND type = :TYPE ;");
03305 query.bindValue(":PATH", StorageGroup::GetRelativePathname(pathname));
03306 }
03307 else if (IsRecording())
03308 {
03309 query.prepare("SELECT mark, offset FROM recordedseek"
03310 " WHERE chanid = :CHANID"
03311 " AND starttime = :STARTTIME"
03312 " AND type = :TYPE ;");
03313 query.bindValue(":CHANID", chanid);
03314 query.bindValue(":STARTTIME", recstartts);
03315 }
03316 else
03317 {
03318 return;
03319 }
03320 query.bindValue(":TYPE", type);
03321
03322 if (!query.exec())
03323 {
03324 MythDB::DBError("QueryPositionMap", query);
03325 return;
03326 }
03327
03328 while (query.next())
03329 posMap[query.value(0).toULongLong()] = query.value(1).toULongLong();
03330 }
03331
03332 void ProgramInfo::ClearPositionMap(MarkTypes type) const
03333 {
03334 if (positionMapDBReplacement)
03335 {
03336 QMutexLocker locker(positionMapDBReplacement->lock);
03337 positionMapDBReplacement->map.clear();
03338 return;
03339 }
03340
03341 MSqlQuery query(MSqlQuery::InitCon());
03342
03343 if (IsVideo())
03344 {
03345 query.prepare("DELETE FROM filemarkup"
03346 " WHERE filename = :PATH"
03347 " AND type = :TYPE ;");
03348 query.bindValue(":PATH", StorageGroup::GetRelativePathname(pathname));
03349 }
03350 else if (IsRecording())
03351 {
03352 query.prepare("DELETE FROM recordedseek"
03353 " WHERE chanid = :CHANID"
03354 " AND starttime = :STARTTIME"
03355 " AND type = :TYPE ;");
03356 query.bindValue(":CHANID", chanid);
03357 query.bindValue(":STARTTIME", recstartts);
03358 }
03359 else
03360 {
03361 return;
03362 }
03363
03364 query.bindValue(":TYPE", type);
03365
03366 if (!query.exec())
03367 MythDB::DBError("clear position map", query);
03368 }
03369
03370 void ProgramInfo::SavePositionMap(
03371 frm_pos_map_t &posMap, MarkTypes type,
03372 int64_t min_frame, int64_t max_frame) const
03373 {
03374 if (positionMapDBReplacement)
03375 {
03376 QMutexLocker locker(positionMapDBReplacement->lock);
03377
03378 if ((min_frame >= 0) || (max_frame >= 0))
03379 {
03380 frm_pos_map_t::const_iterator it, it_end;
03381 it = positionMapDBReplacement->map[(MarkTypes)type].begin();
03382 it_end = positionMapDBReplacement->map[(MarkTypes)type].end();
03383
03384 frm_pos_map_t new_map;
03385 for (; it != it_end; ++it)
03386 {
03387 uint64_t frame = it.key();
03388 if ((min_frame >= 0) && (frame >= (uint64_t)min_frame))
03389 continue;
03390 if ((min_frame >= 0) && (frame <= (uint64_t)max_frame))
03391 continue;
03392 new_map.insert(it.key(), *it);
03393 }
03394 positionMapDBReplacement->map[(MarkTypes)type] = new_map;
03395 }
03396 else
03397 {
03398 positionMapDBReplacement->map[(MarkTypes)type].clear();
03399 }
03400
03401 frm_pos_map_t::const_iterator it = posMap.begin();
03402 frm_pos_map_t::const_iterator it_end = posMap.end();
03403 for (; it != it_end; ++it)
03404 {
03405 uint64_t frame = it.key();
03406 if ((min_frame >= 0) && (frame >= (uint64_t)min_frame))
03407 continue;
03408 if ((min_frame >= 0) && (frame <= (uint64_t)max_frame))
03409 continue;
03410
03411 positionMapDBReplacement->map[(MarkTypes)type]
03412 .insert(frame, *it);
03413 }
03414
03415 return;
03416 }
03417
03418 MSqlQuery query(MSqlQuery::InitCon());
03419 QString comp;
03420
03421 if (min_frame >= 0)
03422 comp += " AND mark >= :MIN_FRAME ";
03423 if (max_frame >= 0)
03424 comp += " AND mark <= :MAX_FRAME ";
03425
03426 QString videoPath;
03427 if (IsVideo())
03428 {
03429 videoPath = StorageGroup::GetRelativePathname(pathname);
03430
03431 query.prepare("DELETE FROM filemarkup"
03432 " WHERE filename = :PATH"
03433 " AND type = :TYPE"
03434 + comp + ';');
03435 query.bindValue(":PATH", videoPath);
03436 }
03437 else if (IsRecording())
03438 {
03439 query.prepare("DELETE FROM recordedseek"
03440 " WHERE chanid = :CHANID"
03441 " AND starttime = :STARTTIME"
03442 " AND type = :TYPE"
03443 + comp + ';');
03444 query.bindValue(":CHANID", chanid);
03445 query.bindValue(":STARTTIME", recstartts);
03446 }
03447 else
03448 {
03449 return;
03450 }
03451
03452 query.bindValue(":TYPE", type);
03453 if (min_frame >= 0)
03454 query.bindValue(":MIN_FRAME", (quint64)min_frame);
03455 if (max_frame >= 0)
03456 query.bindValue(":MAX_FRAME", (quint64)max_frame);
03457
03458 if (!query.exec())
03459 MythDB::DBError("position map clear", query);
03460
03461 if (IsVideo())
03462 {
03463 query.prepare(
03464 "INSERT INTO "
03465 "filemarkup (filename, mark, type, offset) "
03466 "VALUES ( :PATH , :MARK , :TYPE , :OFFSET )");
03467 query.bindValue(":PATH", videoPath);
03468 }
03469 else
03470 {
03471 query.prepare(
03472 "INSERT INTO "
03473 "recordedseek (chanid, starttime, mark, type, offset) "
03474 " VALUES ( :CHANID , :STARTTIME , :MARK , :TYPE , :OFFSET )");
03475 query.bindValue(":CHANID", chanid);
03476 query.bindValue(":STARTTIME", recstartts);
03477 }
03478 query.bindValue(":TYPE", type);
03479
03480 frm_pos_map_t::iterator it;
03481 for (it = posMap.begin(); it != posMap.end(); ++it)
03482 {
03483 uint64_t frame = it.key();
03484
03485 if ((min_frame >= 0) && (frame < (uint64_t)min_frame))
03486 continue;
03487
03488 if ((max_frame >= 0) && (frame > (uint64_t)max_frame))
03489 continue;
03490
03491 uint64_t offset = *it;
03492
03493 query.bindValue(":MARK", (quint64)frame);
03494 query.bindValue(":OFFSET", (quint64)offset);
03495
03496 if (!query.exec())
03497 {
03498 MythDB::DBError("position map insert", query);
03499 break;
03500 }
03501 }
03502 }
03503
03504 void ProgramInfo::SavePositionMapDelta(
03505 frm_pos_map_t &posMap, MarkTypes type) const
03506 {
03507 if (positionMapDBReplacement)
03508 {
03509 QMutexLocker locker(positionMapDBReplacement->lock);
03510
03511 frm_pos_map_t::const_iterator it = posMap.begin();
03512 frm_pos_map_t::const_iterator it_end = posMap.end();
03513 for (; it != it_end; ++it)
03514 positionMapDBReplacement->map[type].insert(it.key(), *it);
03515
03516 return;
03517 }
03518
03519 MSqlQuery query(MSqlQuery::InitCon());
03520
03521 if (IsVideo())
03522 {
03523 query.prepare(
03524 "INSERT INTO "
03525 "filemarkup (filename, mark, type, offset) "
03526 "VALUES ( :PATH , :MARK , :TYPE , :OFFSET )");
03527 query.bindValue(":PATH", StorageGroup::GetRelativePathname(pathname));
03528 }
03529 else if (IsRecording())
03530 {
03531 query.prepare(
03532 "INSERT INTO "
03533 "recordedseek (chanid, starttime, mark, type, offset) "
03534 " VALUES ( :CHANID , :STARTTIME , :MARK , :TYPE , :OFFSET )");
03535 query.bindValue(":CHANID", chanid);
03536 query.bindValue(":STARTTIME", recstartts);
03537 }
03538 else
03539 {
03540 return;
03541 }
03542 query.bindValue(":TYPE", type);
03543
03544 frm_pos_map_t::iterator it;
03545 for (it = posMap.begin(); it != posMap.end(); ++it)
03546 {
03547 uint64_t frame = it.key();
03548 uint64_t offset = *it;
03549
03550 query.bindValue(":MARK", (quint64)frame);
03551 query.bindValue(":OFFSET", (quint64)offset);
03552
03553 if (!query.exec())
03554 {
03555 MythDB::DBError("delta position map insert", query);
03556 break;
03557 }
03558 }
03559 }
03560
03564 void ProgramInfo::SaveAspect(
03565 uint64_t frame, MarkTypes type, uint customAspect)
03566 {
03567 if (!IsRecording())
03568 return;
03569
03570 MSqlQuery query(MSqlQuery::InitCon());
03571
03572 query.prepare("INSERT INTO recordedmarkup"
03573 " (chanid, starttime, mark, type, data)"
03574 " VALUES"
03575 " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
03576 query.bindValue(":CHANID", chanid);
03577 query.bindValue(":STARTTIME", recstartts);
03578
03579 query.bindValue(":MARK", (quint64)frame);
03580 query.bindValue(":TYPE", type);
03581
03582 if (type == MARK_ASPECT_CUSTOM)
03583 query.bindValue(":DATA", customAspect);
03584 else
03585 query.bindValue(":DATA", QVariant::UInt);
03586
03587 if (!query.exec())
03588 MythDB::DBError("aspect ratio change insert", query);
03589 }
03590
03594 void ProgramInfo::SaveFrameRate(uint64_t frame, uint framerate)
03595 {
03596 if (!IsRecording())
03597 return;
03598
03599 MSqlQuery query(MSqlQuery::InitCon());
03600
03601 query.prepare("INSERT INTO recordedmarkup"
03602 " (chanid, starttime, mark, type, data)"
03603 " VALUES"
03604 " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
03605 query.bindValue(":CHANID", chanid);
03606 query.bindValue(":STARTTIME", recstartts);
03607 query.bindValue(":MARK", (quint64)frame);
03608 query.bindValue(":TYPE", MARK_VIDEO_RATE);
03609 query.bindValue(":DATA", framerate);
03610
03611 if (!query.exec())
03612 MythDB::DBError("Frame rate insert", query);
03613 }
03614
03615
03617 void ProgramInfo::SaveTotalDuration(int64_t duration)
03618 {
03619 if (!IsRecording())
03620 return;
03621
03622 MSqlQuery query(MSqlQuery::InitCon());
03623
03624 query.prepare("DELETE FROM recordedmarkup "
03625 " WHERE chanid=:CHANID "
03626 " AND starttime=:STARTTIME "
03627 " AND type=:TYPE");
03628 query.bindValue(":CHANID", chanid);
03629 query.bindValue(":STARTTIME", recstartts);
03630 query.bindValue(":TYPE", MARK_DURATION_MS);
03631
03632 if (!query.exec())
03633 MythDB::DBError("Duration delete", query);
03634
03635 query.prepare("INSERT INTO recordedmarkup"
03636 " (chanid, starttime, mark, type, data)"
03637 " VALUES"
03638 " ( :CHANID, :STARTTIME, 0, :TYPE, :DATA);");
03639 query.bindValue(":CHANID", chanid);
03640 query.bindValue(":STARTTIME", recstartts);
03641 query.bindValue(":TYPE", MARK_DURATION_MS);
03642 query.bindValue(":DATA", (uint)(duration / 1000));
03643
03644 if (!query.exec())
03645 MythDB::DBError("Duration insert", query);
03646 }
03647
03649 void ProgramInfo::SaveTotalFrames(int64_t frames)
03650 {
03651 if (!IsRecording())
03652 return;
03653
03654 MSqlQuery query(MSqlQuery::InitCon());
03655
03656 query.prepare("DELETE FROM recordedmarkup "
03657 " WHERE chanid=:CHANID "
03658 " AND starttime=:STARTTIME "
03659 " AND type=:TYPE");
03660 query.bindValue(":CHANID", chanid);
03661 query.bindValue(":STARTTIME", recstartts);
03662 query.bindValue(":TYPE", MARK_TOTAL_FRAMES);
03663
03664 if (!query.exec())
03665 MythDB::DBError("Frames delete", query);
03666
03667 query.prepare("INSERT INTO recordedmarkup"
03668 " (chanid, starttime, mark, type, data)"
03669 " VALUES"
03670 " ( :CHANID, :STARTTIME, 0, :TYPE, :DATA);");
03671 query.bindValue(":CHANID", chanid);
03672 query.bindValue(":STARTTIME", recstartts);
03673 query.bindValue(":TYPE", MARK_TOTAL_FRAMES);
03674 query.bindValue(":DATA", (uint)(frames));
03675
03676 if (!query.exec())
03677 MythDB::DBError("Total Frames insert", query);
03678 }
03679
03683 void ProgramInfo::SaveResolution(uint64_t frame, uint width, uint height)
03684 {
03685 if (!IsRecording())
03686 return;
03687
03688 MSqlQuery query(MSqlQuery::InitCon());
03689
03690 query.prepare("INSERT INTO recordedmarkup"
03691 " (chanid, starttime, mark, type, data)"
03692 " VALUES"
03693 " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
03694 query.bindValue(":CHANID", chanid);
03695 query.bindValue(":STARTTIME", recstartts);
03696 query.bindValue(":MARK", (quint64)frame);
03697 query.bindValue(":TYPE", MARK_VIDEO_WIDTH);
03698 query.bindValue(":DATA", width);
03699
03700 if (!query.exec())
03701 MythDB::DBError("Resolution insert", query);
03702
03703 query.prepare("INSERT INTO recordedmarkup"
03704 " (chanid, starttime, mark, type, data)"
03705 " VALUES"
03706 " ( :CHANID, :STARTTIME, :MARK, :TYPE, :DATA);");
03707 query.bindValue(":CHANID", chanid);
03708 query.bindValue(":STARTTIME", recstartts);
03709 query.bindValue(":MARK", (quint64)frame);
03710 query.bindValue(":TYPE", MARK_VIDEO_HEIGHT);
03711 query.bindValue(":DATA", height);
03712
03713 if (!query.exec())
03714 MythDB::DBError("Resolution insert", query);
03715 }
03716
03717 static uint load_markup_datum(
03718 MarkTypes type, uint chanid, const QDateTime &recstartts)
03719 {
03720 QString qstr = QString(
03721 "SELECT recordedmarkup.data "
03722 "FROM recordedmarkup "
03723 "WHERE recordedmarkup.chanid = :CHANID AND "
03724 " recordedmarkup.starttime = :STARTTIME AND "
03725 " recordedmarkup.type = %1 "
03726 "GROUP BY recordedmarkup.data "
03727 "ORDER BY SUM( ( SELECT IFNULL(rm.mark, recordedmarkup.mark)"
03728 " FROM recordedmarkup AS rm "
03729 " WHERE rm.chanid = recordedmarkup.chanid AND "
03730 " rm.starttime = recordedmarkup.starttime AND "
03731 " rm.type = recordedmarkup.type AND "
03732 " rm.mark > recordedmarkup.mark "
03733 " ORDER BY rm.mark ASC LIMIT 1 "
03734 " ) - recordedmarkup.mark "
03735 " ) DESC "
03736 "LIMIT 1").arg((int)type);
03737
03738 MSqlQuery query(MSqlQuery::InitCon());
03739 query.prepare(qstr);
03740 query.bindValue(":CHANID", chanid);
03741 query.bindValue(":STARTTIME", recstartts);
03742
03743 if (!query.exec())
03744 {
03745 MythDB::DBError("load_markup_datum", query);
03746 return 0;
03747 }
03748
03749 return (query.next()) ? query.value(0).toUInt() : 0;
03750 }
03751
03756 uint ProgramInfo::QueryAverageHeight(void) const
03757 {
03758 return load_markup_datum(MARK_VIDEO_HEIGHT, chanid, recstartts);
03759 }
03760
03765 uint ProgramInfo::QueryAverageWidth(void) const
03766 {
03767 return load_markup_datum(MARK_VIDEO_WIDTH, chanid, recstartts);
03768 }
03769
03774 uint ProgramInfo::QueryAverageFrameRate(void) const
03775 {
03776 return load_markup_datum(MARK_VIDEO_RATE, chanid, recstartts);
03777 }
03778
03782 int64_t ProgramInfo::QueryTotalDuration(void) const
03783 {
03784 if (gCoreContext->IsDatabaseIgnored())
03785 return 0LL;
03786 int64_t msec = load_markup_datum(MARK_DURATION_MS, chanid, recstartts);
03787 return msec * 1000;
03788 }
03789
03793 int64_t ProgramInfo::QueryTotalFrames(void) const
03794 {
03795 int64_t frames = load_markup_datum(MARK_TOTAL_FRAMES, chanid, recstartts);
03796 return frames;
03797 }
03798
03799 void ProgramInfo::SaveVideoProperties(uint mask, uint vid_flags)
03800 {
03801 MSqlQuery query(MSqlQuery::InitCon());
03802
03803 LOG(VB_RECORD, LOG_INFO,
03804 QString("SaveVideoProperties(0x%1, 0x%2)")
03805 .arg(mask,2,16,QChar('0')).arg(vid_flags,2,16,QChar('0')));
03806
03807 query.prepare(
03808 "UPDATE recordedprogram "
03809 "SET videoprop = ((videoprop+0) & :OTHERFLAGS) | :FLAGS "
03810 "WHERE chanid = :CHANID AND starttime = :STARTTIME");
03811
03812 query.bindValue(":OTHERFLAGS", ~mask);
03813 query.bindValue(":FLAGS", vid_flags);
03814 query.bindValue(":CHANID", chanid);
03815 query.bindValue(":STARTTIME", startts);
03816 query.exec();
03817
03818 uint videoproperties = GetVideoProperties();
03819 videoproperties &= ~mask;
03820 videoproperties |= vid_flags;
03821 properties &= ~kVideoPropertyMask;
03822 properties |= videoproperties << kVideoPropertyOffset;
03823
03824 SendUpdateEvent();
03825 }
03826
03837 QString ProgramInfo::ChannelText(const QString &format) const
03838 {
03839 QString chan(format);
03840 chan.replace("<num>", chanstr)
03841 .replace("<sign>", chansign)
03842 .replace("<name>", channame);
03843 return chan;
03844 }
03845
03846 void ProgramInfo::UpdateInUseMark(bool force)
03847 {
03848 #ifdef DEBUG_IN_USE
03849 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("UpdateInUseMark(%1) '%2'")
03850 .arg(force?"force":"no force").arg(inUseForWhat));
03851 #endif
03852
03853 if (!IsRecording())
03854 return;
03855
03856 if (inUseForWhat.isEmpty())
03857 return;
03858
03859 if (force || lastInUseTime.secsTo(QDateTime::currentDateTime()) > 15 * 60)
03860 MarkAsInUse(true);
03861 }
03862
03863 void ProgramInfo::SaveSeasonEpisode(uint seas, uint ep)
03864 {
03865 MSqlQuery query(MSqlQuery::InitCon());
03866
03867 query.prepare(
03868 "UPDATE recorded "
03869 "SET season = :SEASON, episode = :EPISODE "
03870 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
03871 "AND recordid = :RECORDID");
03872
03873 query.bindValue(":SEASON", seas);
03874 query.bindValue(":EPISODE", ep);
03875 query.bindValue(":CHANID", chanid);
03876 query.bindValue(":STARTTIME", recstartts);
03877 query.bindValue(":RECORDID", recordid);
03878 query.exec();
03879
03880 SendUpdateEvent();
03881 }
03882
03883 void ProgramInfo::SaveInetRef(const QString &inet)
03884 {
03885 MSqlQuery query(MSqlQuery::InitCon());
03886
03887 query.prepare(
03888 "UPDATE recorded "
03889 "SET inetref = :INETREF "
03890 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
03891 "AND recordid = :RECORDID");
03892
03893 query.bindValue(":INETREF", inet);
03894 query.bindValue(":CHANID", chanid);
03895 query.bindValue(":STARTTIME", recstartts);
03896 query.bindValue(":RECORDID", recordid);
03897 query.exec();
03898
03899 SendUpdateEvent();
03900 }
03901
03908 bool ProgramInfo::IsFileReadable(void) const
03909 {
03910 if (IsLocal() && QFileInfo(pathname).isReadable())
03911 return true;
03912
03913 if (!IsMythStream())
03914 pathname = GetPlaybackURL(true, false);
03915
03916 if (IsMythStream())
03917 return RemoteCheckFile(this);
03918
03919 if (IsLocal())
03920 return QFileInfo(pathname).isReadable();
03921
03922 return false;
03923 }
03924
03925 QString ProgramInfo::QueryRecordingGroupPassword(const QString &group)
03926 {
03927 QString result;
03928
03929 MSqlQuery query(MSqlQuery::InitCon());
03930 query.prepare("SELECT password FROM recgrouppassword "
03931 "WHERE recgroup = :GROUP");
03932 query.bindValue(":GROUP", group);
03933
03934 if (query.exec() && query.next())
03935 result = query.value(0).toString();
03936
03937 return(result);
03938 }
03939
03942 QString ProgramInfo::QueryRecordingGroup(void) const
03943 {
03944 MSqlQuery query(MSqlQuery::InitCon());
03945 query.prepare("SELECT recgroup FROM recorded "
03946 "WHERE chanid = :CHANID AND "
03947 " starttime = :START");
03948 query.bindValue(":START", recstartts);
03949 query.bindValue(":CHANID", chanid);
03950
03951 QString grp = recgroup;
03952 if (query.exec() && query.next())
03953 grp = query.value(0).toString();
03954
03955 return grp;
03956 }
03957
03958 uint ProgramInfo::QueryTranscoderID(void) const
03959 {
03960 MSqlQuery query(MSqlQuery::InitCon());
03961 query.prepare("SELECT transcoder FROM recorded "
03962 "WHERE chanid = :CHANID AND "
03963 " starttime = :START");
03964 query.bindValue(":CHANID", chanid);
03965 query.bindValue(":START", recstartts);
03966
03967 if (query.exec() && query.next())
03968 return query.value(0).toUInt();
03969
03970 return 0;
03971 }
03972
03978 QString ProgramInfo::DiscoverRecordingDirectory(void) const
03979 {
03980 if (!IsLocal())
03981 {
03982 if (!gCoreContext->IsBackend())
03983 return "";
03984
03985 QString path = GetPlaybackURL(false, true);
03986 if (path.left(1) == "/")
03987 {
03988 QFileInfo testFile(path);
03989 return testFile.path();
03990 }
03991
03992 return "";
03993 }
03994
03995 QFileInfo testFile(pathname);
03996 if (testFile.exists() || (gCoreContext->GetHostName() == hostname))
03997 {
03998
03999
04000 if (testFile.exists())
04001 {
04002 if (testFile.isSymLink())
04003 testFile.setFile(getSymlinkTarget(pathname));
04004
04005 if (testFile.isFile())
04006 return testFile.path();
04007 else if (testFile.isDir())
04008 return testFile.filePath();
04009 }
04010 else
04011 {
04012 testFile.setFile(testFile.absolutePath());
04013 if (testFile.exists())
04014 {
04015 if (testFile.isSymLink())
04016 testFile.setFile(getSymlinkTarget(testFile.path()));
04017
04018 if (testFile.isDir())
04019 return testFile.filePath();
04020 }
04021 }
04022 }
04023
04024 return "";
04025 }
04026
04027 #include <cassert>
04034 void ProgramInfo::MarkAsInUse(bool inuse, QString usedFor)
04035 {
04036 if (!IsRecording())
04037 return;
04038
04039 bool notifyOfChange = false;
04040
04041 if (inuse &&
04042 (inUseForWhat.isEmpty() ||
04043 (!usedFor.isEmpty() && usedFor != inUseForWhat)))
04044 {
04045 if (!usedFor.isEmpty())
04046 {
04047
04048 #ifdef DEBUG_IN_USE
04049 if (!inUseForWhat.isEmpty())
04050 {
04051 LOG(VB_GENERAL, LOG_INFO, LOC +
04052 QString("MarkAsInUse(true, '%1'->'%2')")
04053 .arg(inUseForWhat).arg(usedFor) +
04054 " -- use has changed");
04055 }
04056 else
04057 {
04058 LOG(VB_GENERAL, LOG_INFO, LOC +
04059 QString("MarkAsInUse(true, ''->'%1')").arg(usedFor));
04060 }
04061 #endif // DEBUG_IN_USE
04062
04063 inUseForWhat = usedFor;
04064 }
04065 else if (inUseForWhat.isEmpty())
04066 {
04067 QString oldInUseForWhat = inUseForWhat;
04068 inUseForWhat = QString("%1 [%2]")
04069 .arg(QObject::tr("Unknown")).arg(getpid());
04070 LOG(VB_GENERAL, LOG_WARNING, LOC +
04071 QString("MarkAsInUse(true, ''->'%1')").arg(inUseForWhat) +
04072 " -- use was not explicitly set");
04073 }
04074
04075 notifyOfChange = true;
04076 }
04077
04078 if (!inuse && !inUseForWhat.isEmpty() && usedFor != inUseForWhat)
04079 {
04080 LOG(VB_GENERAL, LOG_WARNING, LOC +
04081 QString("MarkAsInUse(false, '%1'->'%2')")
04082 .arg(inUseForWhat).arg(usedFor) +
04083 " -- use has changed since first setting as in use.");
04084 }
04085 #ifdef DEBUG_IN_USE
04086 else if (!inuse)
04087 {
04088 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("MarkAsInUse(false, '%1')")
04089 .arg(inUseForWhat));
04090 }
04091 #endif // DEBUG_IN_USE
04092
04093 if (!inuse && inUseForWhat.isEmpty())
04094 inUseForWhat = usedFor;
04095
04096 if (!inuse && inUseForWhat.isEmpty())
04097 {
04098 LOG(VB_GENERAL, LOG_WARNING, LOC +
04099 "MarkAsInUse requires a key to delete in use mark");
04100 return;
04101 }
04102
04103 if (!inuse)
04104 {
04105 MSqlQuery query(MSqlQuery::InitCon());
04106 query.prepare(
04107 "DELETE FROM inuseprograms "
04108 "WHERE chanid = :CHANID AND starttime = :STARTTIME AND "
04109 " hostname = :HOSTNAME AND recusage = :RECUSAGE");
04110 query.bindValue(":CHANID", chanid);
04111 query.bindValue(":STARTTIME", recstartts);
04112 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
04113 query.bindValue(":RECUSAGE", inUseForWhat);
04114
04115 if (!query.exec())
04116 MythDB::DBError("MarkAsInUse -- delete", query);
04117
04118 inUseForWhat.clear();
04119 lastInUseTime = mythCurrentDateTime().addSecs(-4 * 60 * 60);
04120 SendUpdateEvent();
04121 return;
04122 }
04123
04124 if (pathname == GetBasename())
04125 pathname = GetPlaybackURL(false, true);
04126
04127 QString recDir = DiscoverRecordingDirectory();
04128
04129 QDateTime inUseTime = mythCurrentDateTime();
04130
04131 MSqlQuery query(MSqlQuery::InitCon());
04132 query.prepare(
04133 "SELECT count(*) "
04134 "FROM inuseprograms "
04135 "WHERE chanid = :CHANID AND starttime = :STARTTIME AND "
04136 " hostname = :HOSTNAME AND recusage = :RECUSAGE");
04137 query.bindValue(":CHANID", chanid);
04138 query.bindValue(":STARTTIME", recstartts);
04139 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
04140 query.bindValue(":RECUSAGE", inUseForWhat);
04141
04142 if (!query.exec())
04143 {
04144 MythDB::DBError("MarkAsInUse -- select", query);
04145 }
04146 else if (!query.next())
04147 {
04148 LOG(VB_GENERAL, LOG_ERR, LOC + "MarkAsInUse -- select query failed");
04149 }
04150 else if (query.value(0).toUInt())
04151 {
04152 query.prepare(
04153 "UPDATE inuseprograms "
04154 "SET lastupdatetime = :UPDATETIME "
04155 "WHERE chanid = :CHANID AND starttime = :STARTTIME AND "
04156 " hostname = :HOSTNAME AND recusage = :RECUSAGE");
04157 query.bindValue(":CHANID", chanid);
04158 query.bindValue(":STARTTIME", recstartts);
04159 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
04160 query.bindValue(":RECUSAGE", inUseForWhat);
04161 query.bindValue(":UPDATETIME", inUseTime);
04162
04163 if (!query.exec())
04164 MythDB::DBError("MarkAsInUse -- update failed", query);
04165 else
04166 lastInUseTime = inUseTime;
04167 }
04168 else
04169 {
04170 query.prepare(
04171 "INSERT INTO inuseprograms "
04172 " (chanid, starttime, recusage, hostname, "
04173 " lastupdatetime, rechost, recdir) "
04174 "VALUES "
04175 " (:CHANID, :STARTTIME, :RECUSAGE, :HOSTNAME, "
04176 " :UPDATETIME, :RECHOST, :RECDIR)");
04177 query.bindValue(":CHANID", chanid);
04178 query.bindValue(":STARTTIME", recstartts);
04179 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
04180 query.bindValue(":RECUSAGE", inUseForWhat);
04181 query.bindValue(":UPDATETIME", inUseTime);
04182 query.bindValue(":RECHOST", hostname);
04183 query.bindValue(":RECDIR", recDir);
04184
04185 if (!query.exec())
04186 MythDB::DBError("MarkAsInUse -- insert failed", query);
04187 else
04188 lastInUseTime = inUseTime;
04189 }
04190
04191 if (!notifyOfChange)
04192 return;
04193
04194
04195 QDateTime oneHourAgo = QDateTime::currentDateTime().addSecs(-61 * 60);
04196 query.prepare("SELECT DISTINCT recusage "
04197 "FROM inuseprograms "
04198 "WHERE lastupdatetime >= :ONEHOURAGO AND "
04199 " chanid = :CHANID AND "
04200 " starttime = :STARTTIME");
04201 query.bindValue(":CHANID", chanid);
04202 query.bindValue(":STARTTIME", recstartts);
04203 query.bindValue(":ONEHOURAGO", oneHourAgo);
04204 if (!query.exec())
04205 return;
04206
04207 programflags &= ~(FL_INUSEPLAYING | FL_INUSERECORDING | FL_INUSEOTHER);
04208 while (query.next())
04209 {
04210 QString inUseForWhat = query.value(0).toString();
04211 if (inUseForWhat.contains(kPlayerInUseID))
04212 programflags |= FL_INUSEPLAYING;
04213 else if (inUseForWhat == kRecorderInUseID)
04214 programflags |= FL_INUSERECORDING;
04215 else
04216 programflags |= FL_INUSEOTHER;
04217 }
04218 SendUpdateEvent();
04219 }
04220
04226 bool ProgramInfo::QueryTuningInfo(QString &channum, QString &input) const
04227 {
04228 channum.clear();
04229 input.clear();
04230 MSqlQuery query(MSqlQuery::InitCon());
04231
04232 query.prepare("SELECT channel.channum, cardinput.inputname "
04233 "FROM channel, capturecard, cardinput "
04234 "WHERE channel.chanid = :CHANID AND "
04235 " cardinput.cardid = capturecard.cardid AND "
04236 " cardinput.sourceid = :SOURCEID AND "
04237 " capturecard.cardid = :CARDID");
04238 query.bindValue(":CHANID", chanid);
04239 query.bindValue(":SOURCEID", sourceid);
04240 query.bindValue(":CARDID", cardid);
04241
04242 if (query.exec() && query.next())
04243 {
04244 channum = query.value(0).toString();
04245 input = query.value(1).toString();
04246 return true;
04247 }
04248 else
04249 {
04250 MythDB::DBError("GetChannel(ProgInfo...)", query);
04251 return false;
04252 }
04253 }
04254
04260 QString ProgramInfo::QueryInputDisplayName(void) const
04261 {
04262 if (!inputid)
04263 return QString::null;
04264
04265 MSqlQuery query(MSqlQuery::InitCon());
04266 query.prepare("SELECT displayname, cardid, inputname "
04267 "FROM cardinput "
04268 "WHERE cardinputid = :INPUTID");
04269 query.bindValue(":INPUTID", inputid);
04270
04271 if (!query.exec())
04272 MythDB::DBError("ProgramInfo::GetInputDisplayName(uint)", query);
04273 else if (query.next())
04274 {
04275 QString result = query.value(0).toString();
04276 if (result.isEmpty())
04277 result = QString("%1: %2").arg(query.value(1).toInt())
04278 .arg(query.value(2).toString());
04279 return result;
04280 }
04281
04282 return QString::null;
04283 }
04284
04285 static int init_tr(void)
04286 {
04287 static bool done = false;
04288 static QMutex init_tr_lock;
04289 QMutexLocker locker(&init_tr_lock);
04290 if (done)
04291 return 1;
04292
04293 QString rec_profile_names =
04294 QObject::tr("Default", "Recording Profile Default") +
04295 QObject::tr("High Quality", "Recording Profile High Quality") +
04296 QObject::tr("Live TV", "Recording Profile Live TV") +
04297 QObject::tr("Low Quality", "Recording Profile Low Quality") +
04298 QObject::tr("Medium Quality", "Recording Profile Medium Quality") +
04299 QObject::tr("MPEG-2", "Recording Profile MPEG-2") +
04300 QObject::tr("RTjpeg/MPEG-4", "Recording Profile RTjpeg/MPEG-4");
04301
04302
04303 QString rec_profile_groups =
04304 QObject::tr("CRC IP Recorders",
04305 "Recording Profile Group Name") +
04306 QObject::tr("FireWire Input",
04307 "Recording Profile Group Name") +
04308 QObject::tr("Freebox Input",
04309 "Recording Profile Group Name") +
04310 QObject::tr("Hardware DVB Encoders",
04311 "Recording Profile Group Name") +
04312 QObject::tr("Hardware HDTV",
04313 "Recording Profile Group Name") +
04314 QObject::tr("Hardware MJPEG Encoders (Matrox G200-TV, Miro DC10, etc)",
04315 "Recording Profile Group Name") +
04316 QObject::tr("HD-PVR Recorders",
04317 "Recording Profile Group Name") +
04318 QObject::tr("HDHomeRun Recorders",
04319 "Recording Profile Group Name") +
04320 QObject::tr("MPEG-2 Encoders (PVR-x50, PVR-500)",
04321 "Recording Profile Group Name") +
04322 QObject::tr("Software Encoders (V4L based)",
04323 "Recording Profile Group Name") +
04324 QObject::tr("Transcoders",
04325 "Recording Profile Group Name") +
04326 QObject::tr("USB MPEG-4 Encoder (Plextor ConvertX, etc)",
04327 "Recording Profile Group Name");
04328
04329 QString display_rec_groups =
04330 QObject::tr("All Programs", "Recording Group All Programs") +
04331 QObject::tr("All", "Recording Group All Programs -- short form") +
04332 QObject::tr("Live TV", "Recording Group Live TV") +
04333 QObject::tr("Default", "Recording Group Default") +
04334 QObject::tr("Deleted", "Recording Group Deleted");
04335
04336 QString storage_groups =
04337 QObject::tr("Default", "Storage Group Name") +
04338 QObject::tr("Live TV", "Storage Group Name") +
04339 QObject::tr("Thumbnails", "Storage Group Name") +
04340 QObject::tr("DB Backups", "Storage Group Name");
04341
04342 QString play_groups =
04343 QObject::tr("Default", "Playback Group Name");
04344
04345 done = true;
04346 return (rec_profile_names.length() +
04347 rec_profile_groups.length() +
04348 display_rec_groups.length() +
04349 storage_groups.length() +
04350 play_groups.length());
04351 }
04352
04353 int ProgramInfo::InitStatics(void)
04354 {
04355 QMutexLocker locker(&staticDataLock);
04356 if (!updater)
04357 updater = new ProgramInfoUpdater();
04358 return 1;
04359 }
04360
04362 QString ProgramInfo::i18n(const QString &msg)
04363 {
04364 init_tr();
04365 QByteArray msg_arr = msg.toLatin1();
04366 QString msg_i18n = QObject::tr(msg_arr.constData());
04367 QByteArray msg_i18n_arr = msg_i18n.toLatin1();
04368 return (msg_arr == msg_i18n_arr) ? msg : msg_i18n;
04369 }
04370
04377 void ProgramInfo::SubstituteMatches(QString &str)
04378 {
04379 QString pburl = GetPlaybackURL(false, true);
04380 if (pburl.left(7) == "myth://")
04381 {
04382 str.replace(QString("%DIR%"), pburl);
04383 }
04384 else
04385 {
04386 QFileInfo dirInfo(pburl);
04387 str.replace(QString("%DIR%"), dirInfo.path());
04388 }
04389
04390 str.replace(QString("%FILE%"), GetBasename());
04391 str.replace(QString("%TITLE%"), title);
04392 str.replace(QString("%SUBTITLE%"), subtitle);
04393 str.replace(QString("%SEASON%"), QString::number(season));
04394 str.replace(QString("%EPISODE%"), QString::number(episode));
04395 str.replace(QString("%DESCRIPTION%"), description);
04396 str.replace(QString("%HOSTNAME%"), hostname);
04397 str.replace(QString("%CATEGORY%"), category);
04398 str.replace(QString("%RECGROUP%"), recgroup);
04399 str.replace(QString("%PLAYGROUP%"), playgroup);
04400 str.replace(QString("%CHANID%"), QString::number(chanid));
04401 str.replace(QString("%INETREF%"), inetref);
04402 str.replace(QString("%ORIGINALAIRDATE%"), originalAirDate.toString(Qt::ISODate));
04403 static const char *time_str[] =
04404 { "STARTTIME", "ENDTIME", "PROGSTART", "PROGEND", };
04405 const QDateTime *time_dtr[] =
04406 { &recstartts, &recendts, &startts, &endts, };
04407 for (uint i = 0; i < sizeof(time_str)/sizeof(char*); i++)
04408 {
04409 str.replace(QString("%%1%").arg(time_str[i]),
04410 time_dtr[i]->toString("yyyyMMddhhmmss"));
04411 str.replace(QString("%%1ISO%").arg(time_str[i]),
04412 time_dtr[i]->toString(Qt::ISODate));
04413 str.replace(QString("%%1UTC%").arg(time_str[i]),
04414 (time_dtr[i]->toUTC()).toString("yyyyMMddhhmmss"));
04415 str.replace(QString("%%1ISOUTC%").arg(time_str[i]),
04416 (time_dtr[i]->toUTC()).toString(Qt::ISODate));
04417 }
04418 }
04419
04420 QMap<QString,uint32_t> ProgramInfo::QueryInUseMap(void)
04421 {
04422 QMap<QString, uint32_t> inUseMap;
04423 QDateTime oneHourAgo = QDateTime::currentDateTime().addSecs(-61 * 60);
04424
04425 MSqlQuery query(MSqlQuery::InitCon());
04426
04427 query.prepare("SELECT DISTINCT chanid, starttime, recusage "
04428 "FROM inuseprograms WHERE lastupdatetime >= :ONEHOURAGO");
04429 query.bindValue(":ONEHOURAGO", oneHourAgo);
04430
04431 if (!query.exec())
04432 return inUseMap;
04433
04434 while (query.next())
04435 {
04436 QString inUseKey = ProgramInfo::MakeUniqueKey(
04437 query.value(0).toUInt(), query.value(1).toDateTime());
04438
04439 QString inUseForWhat = query.value(2).toString();
04440
04441 if (!inUseMap.contains(inUseKey))
04442 inUseMap[inUseKey] = 0;
04443
04444 if (inUseForWhat.contains(kPlayerInUseID))
04445 inUseMap[inUseKey] |= FL_INUSEPLAYING;
04446 else if (inUseForWhat == kRecorderInUseID)
04447 inUseMap[inUseKey] |= FL_INUSERECORDING;
04448 else
04449 inUseMap[inUseKey] |= FL_INUSEOTHER;
04450 }
04451
04452 return inUseMap;
04453 }
04454
04455 QMap<QString,bool> ProgramInfo::QueryJobsRunning(int type)
04456 {
04457 QMap<QString,bool> is_job_running;
04458
04459 MSqlQuery query(MSqlQuery::InitCon());
04460 query.prepare("SELECT chanid, starttime, status FROM jobqueue "
04461 "WHERE type = :TYPE");
04462 query.bindValue(":TYPE", type);
04463 if (!query.exec())
04464 return is_job_running;
04465
04466 while (query.next())
04467 {
04468 uint chanid = query.value(0).toUInt();
04469 QDateTime recstartts = query.value(1).toDateTime();
04470 int tmpStatus = query.value(2).toInt();
04471 if ((tmpStatus != 0x0000) &&
04472 (tmpStatus != 0x0001) &&
04473 (!(tmpStatus & 0x0100)))
04474 {
04475 is_job_running[
04476 ProgramInfo::MakeUniqueKey(chanid,recstartts)] = true;
04477 }
04478 }
04479
04480 return is_job_running;
04481 }
04482
04483 QStringList ProgramInfo::LoadFromScheduler(
04484 const QString &tmptable, int recordid)
04485 {
04486 QStringList slist;
04487
04488 MythScheduler *sched = gCoreContext->GetScheduler();
04489 if (sched && tmptable.isEmpty())
04490 {
04491 sched->GetAllPending(slist);
04492 return slist;
04493 }
04494
04495 if (sched)
04496 {
04497 LOG(VB_GENERAL, LOG_ERR,
04498 "Called from master backend\n\t\t\t"
04499 "with recordid or tmptable, this is not currently supported");
04500 return slist;
04501 }
04502
04503 slist.push_back(
04504 (tmptable.isEmpty()) ?
04505 QString("QUERY_GETALLPENDING") :
04506 QString("QUERY_GETALLPENDING %1 %2").arg(tmptable).arg(recordid));
04507
04508 if (!gCoreContext->SendReceiveStringList(slist) || slist.size() < 2)
04509 {
04510 LOG(VB_GENERAL, LOG_ALERT,
04511 "LoadFromScheduler(): Error querying master.");
04512 slist.clear();
04513 }
04514
04515 return slist;
04516 }
04517
04518 static bool FromProgramQuery(
04519 const QString &sql, const MSqlBindings &bindings, MSqlQuery &query)
04520 {
04521 QString querystr = QString(
04522 "SELECT DISTINCT program.chanid, program.starttime, program.endtime, "
04523 " program.title, program.subtitle, program.description, "
04524 " program.category, channel.channum, channel.callsign, "
04525 " channel.name, program.previouslyshown, channel.commmethod, "
04526 " channel.outputfilters, program.seriesid, program.programid, "
04527 " program.airdate, program.stars, program.originalairdate, "
04528 " program.category_type, oldrecstatus.recordid, "
04529 " oldrecstatus.rectype, oldrecstatus.recstatus, "
04530 " oldrecstatus.findid, program.videoprop+0, program.audioprop+0, "
04531 " program.subtitletypes+0 "
04532 "FROM program "
04533 "LEFT JOIN channel ON program.chanid = channel.chanid "
04534 "LEFT JOIN oldrecorded AS oldrecstatus ON "
04535 " oldrecstatus.future = 0 AND "
04536 " program.title = oldrecstatus.title AND "
04537 " channel.callsign = oldrecstatus.station AND "
04538 " program.starttime = oldrecstatus.starttime "
04539 ) + sql;
04540
04541 if (!sql.contains(" GROUP BY "))
04542 querystr += " GROUP BY program.starttime, channel.channum, "
04543 " channel.callsign, program.title ";
04544 if (!sql.contains(" ORDER BY "))
04545 {
04546 querystr += " ORDER BY program.starttime, ";
04547 QString chanorder =
04548 gCoreContext->GetSetting("ChannelOrdering", "channum");
04549 if (chanorder != "channum")
04550 querystr += chanorder + " ";
04551 else
04552 querystr += "atsc_major_chan,atsc_minor_chan,channum,callsign ";
04553 }
04554 if (!sql.contains(" LIMIT "))
04555 querystr += " LIMIT 20000 ";
04556
04557 query.prepare(querystr);
04558 MSqlBindings::const_iterator it;
04559 for (it = bindings.begin(); it != bindings.end(); ++it)
04560 {
04561 if (querystr.contains(it.key()))
04562 query.bindValue(it.key(), it.value());
04563 }
04564
04565 if (!query.exec())
04566 {
04567 MythDB::DBError("LoadFromProgramQuery", query);
04568 return false;
04569 }
04570
04571 return true;
04572 }
04573
04574 bool LoadFromProgram(
04575 ProgramList &destination,
04576 const QString &sql, const MSqlBindings &bindings,
04577 const ProgramList &schedList)
04578 {
04579 destination.clear();
04580
04581 MSqlQuery query(MSqlQuery::InitCon());
04582 if (!FromProgramQuery(sql, bindings, query))
04583 return false;
04584
04585 while (query.next())
04586 {
04587 destination.push_back(
04588 new ProgramInfo(
04589 query.value(3).toString(),
04590 query.value(4).toString(),
04591 query.value(5).toString(),
04592 query.value(6).toString(),
04593
04594 query.value(0).toUInt(),
04595 query.value(7).toString(),
04596 query.value(8).toString(),
04597 query.value(9).toString(),
04598 query.value(12).toString(),
04599
04600 query.value(1).toDateTime(),
04601 query.value(2).toDateTime(),
04602 query.value(1).toDateTime(),
04603 query.value(2).toDateTime(),
04604
04605 query.value(13).toString(),
04606 query.value(14).toString(),
04607 query.value(18).toString(),
04608
04609 query.value(16).toDouble(),
04610 query.value(15).toUInt(),
04611 query.value(17).toDate(),
04612 RecStatusType(query.value(21).toInt()),
04613 query.value(19).toUInt(),
04614 RecordingType(query.value(20).toInt()),
04615 query.value(22).toUInt(),
04616
04617 query.value(11).toInt() == COMM_DETECT_COMMFREE,
04618 query.value(10).toInt(),
04619 query.value(23).toInt(),
04620 query.value(24).toInt(),
04621 query.value(25).toInt(),
04622
04623 schedList));
04624 }
04625
04626 return true;
04627 }
04628
04629 bool LoadFromOldRecorded(
04630 ProgramList &destination, const QString &sql, const MSqlBindings &bindings)
04631 {
04632 destination.clear();
04633
04634 MSqlQuery query(MSqlQuery::InitCon());
04635
04636 QString querystr =
04637 "SELECT oldrecorded.chanid, starttime, endtime, "
04638 " title, subtitle, description, season, episode, category, seriesid, "
04639 " programid, inetref, channel.channum, channel.callsign, "
04640 " channel.name, findid, rectype, recstatus, recordid, "
04641 " duplicate "
04642 " FROM oldrecorded "
04643 " LEFT JOIN channel ON oldrecorded.chanid = channel.chanid "
04644 " WHERE oldrecorded.future = 0 "
04645 + sql;
04646
04647 query.prepare(querystr);
04648 MSqlBindings::const_iterator it;
04649 for (it = bindings.begin(); it != bindings.end(); ++it)
04650 {
04651 if (querystr.contains(it.key()))
04652 query.bindValue(it.key(), it.value());
04653 }
04654
04655 if (!query.exec())
04656 {
04657 MythDB::DBError("LoadFromOldRecorded", query);
04658 return false;
04659 }
04660
04661 while (query.next())
04662 {
04663 uint chanid = query.value(0).toUInt();
04664 QString channum = QString("#%1").arg(chanid);
04665 QString chansign = channum;
04666 QString channame = channum;
04667 if (!query.value(12).toString().isEmpty())
04668 {
04669 channum = query.value(12).toString();
04670 chansign = query.value(13).toString();
04671 channame = query.value(14).toString();
04672 }
04673
04674 destination.push_back(new ProgramInfo(
04675 query.value(3).toString(),
04676 query.value(4).toString(),
04677 query.value(5).toString(),
04678 query.value(6).toUInt(),
04679 query.value(7).toUInt(),
04680 query.value(8).toString(),
04681
04682 chanid, channum, chansign, channame,
04683
04684 query.value(9).toString(), query.value(10).toString(),
04685 query.value(11).toString(),
04686
04687 query.value(1).toDateTime(), query.value(2).toDateTime(),
04688 query.value(1).toDateTime(), query.value(2).toDateTime(),
04689
04690 RecStatusType(query.value(17).toInt()),
04691 query.value(18).toUInt(),
04692 RecordingType(query.value(16).toInt()),
04693 query.value(15).toUInt(),
04694
04695 query.value(19).toInt()));
04696 }
04697
04698 return true;
04699 }
04700
04716 bool LoadFromRecorded(
04717 ProgramList &destination,
04718 bool possiblyInProgressRecordingsOnly,
04719 const QMap<QString,uint32_t> &inUseMap,
04720 const QMap<QString,bool> &isJobRunning,
04721 const QMap<QString, ProgramInfo*> &recMap,
04722 int sort)
04723 {
04724 destination.clear();
04725
04726 QString fs_db_name = "";
04727 QDateTime rectime = QDateTime::currentDateTime().addSecs(
04728 -gCoreContext->GetNumSetting("RecordOverTime"));
04729
04730
04731
04732 QString thequery = ProgramInfo::kFromRecordedQuery;
04733 if (possiblyInProgressRecordingsOnly)
04734 thequery += "WHERE r.endtime >= NOW() AND r.starttime <= NOW() ";
04735
04736 if (sort)
04737 thequery += "ORDER BY r.starttime ";
04738 if (sort < 0)
04739 thequery += "DESC ";
04740
04741 MSqlQuery query(MSqlQuery::InitCon());
04742 query.prepare(thequery);
04743
04744 if (!query.exec())
04745 {
04746 MythDB::DBError("ProgramList::FromRecorded", query);
04747 return true;
04748 }
04749
04750 while (query.next())
04751 {
04752 const uint chanid = query.value(6).toUInt();
04753 QString channum = QString("#%1").arg(chanid);
04754 QString chansign = channum;
04755 QString channame = channum;
04756 QString chanfilt;
04757 if (!query.value(7).toString().isEmpty())
04758 {
04759 channum = query.value(7).toString();
04760 chansign = query.value(8).toString();
04761 channame = query.value(9).toString();
04762 chanfilt = query.value(10).toString();
04763 }
04764
04765 QString hostname = query.value(15).toString();
04766 if (hostname.isEmpty())
04767 hostname = gCoreContext->GetHostName();
04768
04769 RecStatusType recstatus = rsRecorded;
04770 QDateTime recstartts = query.value(24).toDateTime();
04771
04772 QString key = ProgramInfo::MakeUniqueKey(chanid, recstartts);
04773 if (query.value(25).toDateTime() > rectime && recMap.contains(key))
04774 recstatus = rsRecording;
04775
04776 bool save_not_commflagged = false;
04777 uint flags = 0;
04778
04779 set_flag(flags, FL_CHANCOMMFREE,
04780 query.value(30).toInt() == COMM_DETECT_COMMFREE);
04781 set_flag(flags, FL_COMMFLAG,
04782 query.value(31).toInt() == COMM_FLAG_DONE);
04783 set_flag(flags, FL_COMMPROCESSING ,
04784 query.value(31).toInt() == COMM_FLAG_PROCESSING);
04785 set_flag(flags, FL_REPEAT, query.value(32).toBool());
04786 set_flag(flags, FL_TRANSCODED,
04787 query.value(34).toInt() == TRANSCODING_COMPLETE);
04788 set_flag(flags, FL_DELETEPENDING, query.value(35).toBool());
04789 set_flag(flags, FL_PRESERVED, query.value(36).toBool());
04790 set_flag(flags, FL_CUTLIST, query.value(37).toBool());
04791 set_flag(flags, FL_AUTOEXP, query.value(38).toBool());
04792 set_flag(flags, FL_REALLYEDITING, query.value(39).toBool());
04793 set_flag(flags, FL_BOOKMARK, query.value(40).toBool());
04794 set_flag(flags, FL_WATCHED, query.value(41).toBool());
04795
04796 if (inUseMap.contains(key))
04797 flags |= inUseMap[key];
04798
04799 if (flags & FL_COMMPROCESSING &&
04800 (isJobRunning.find(key) == isJobRunning.end()))
04801 {
04802 flags &= ~FL_COMMPROCESSING;
04803 save_not_commflagged = true;
04804 }
04805
04806 set_flag(flags, FL_EDITING,
04807 (flags & FL_REALLYEDITING) ||
04808 (flags & COMM_FLAG_PROCESSING));
04809
04810 destination.push_back(
04811 new ProgramInfo(
04812 query.value(0).toString(),
04813 query.value(1).toString(),
04814 query.value(2).toString(),
04815 query.value(3).toUInt(),
04816 query.value(4).toUInt(),
04817 query.value(5).toString(),
04818
04819 chanid, channum, chansign, channame, chanfilt,
04820
04821 query.value(11).toString(), query.value(12).toString(),
04822
04823 query.value(14).toString(),
04824
04825 hostname, query.value(13).toString(),
04826
04827 query.value(17).toString(), query.value(18).toString(),
04828 query.value(19).toString(),
04829
04830 query.value(16).toInt(),
04831
04832 query.value(20).toULongLong(),
04833
04834 query.value(21).toDateTime(), query.value(22).toDateTime(),
04835 query.value(24).toDateTime(), query.value(25).toDateTime(),
04836
04837 query.value(23).toDouble(),
04838
04839 query.value(26).toUInt(),
04840 query.value(27).toDate(),
04841 query.value(28).toDateTime(),
04842
04843 recstatus,
04844
04845 query.value(29).toUInt(),
04846
04847 RecordingDupInType(query.value(46).toInt()),
04848 RecordingDupMethodType(query.value(47).toInt()),
04849
04850 query.value(45).toUInt(),
04851
04852 flags,
04853 query.value(42).toUInt(),
04854 query.value(43).toUInt(),
04855 query.value(44).toUInt()));
04856
04857 if (save_not_commflagged)
04858 destination.back()->SaveCommFlagged(COMM_FLAG_NOT_FLAGGED);
04859 }
04860
04861 return true;
04862 }
04863
04864 QString SkipTypeToString(int flags)
04865 {
04866 if (COMM_DETECT_COMMFREE == flags)
04867 return QObject::tr("Commercial Free");
04868 if (COMM_DETECT_UNINIT == flags)
04869 return QObject::tr("Use Global Setting");
04870
04871 QChar chr = '0';
04872 QString ret = QString("0x%1").arg(flags,3,16,chr);
04873 bool blank = COMM_DETECT_BLANK & flags;
04874 bool scene = COMM_DETECT_SCENE & flags;
04875 bool logo = COMM_DETECT_LOGO & flags;
04876 bool exp = COMM_DETECT_2 & flags;
04877 bool prePst= COMM_DETECT_PREPOSTROLL & flags;
04878
04879 if (blank && scene && logo)
04880 ret = QObject::tr("All Available Methods");
04881 else if (blank && scene && !logo)
04882 ret = QObject::tr("Blank Frame + Scene Change");
04883 else if (blank && !scene && logo)
04884 ret = QObject::tr("Blank Frame + Logo Detection");
04885 else if (!blank && scene && logo)
04886 ret = QObject::tr("Scene Change + Logo Detection");
04887 else if (blank && !scene && !logo)
04888 ret = QObject::tr("Blank Frame Detection");
04889 else if (!blank && scene && !logo)
04890 ret = QObject::tr("Scene Change Detection");
04891 else if (!blank && !scene && logo)
04892 ret = QObject::tr("Logo Detection");
04893
04894 if (exp)
04895 ret = QObject::tr("Experimental") + ": " + ret;
04896 else if(prePst)
04897 ret = QObject::tr("Pre & Post Roll") + ": " + ret;
04898
04899 return ret;
04900 }
04901
04902 deque<int> GetPreferredSkipTypeCombinations(void)
04903 {
04904 deque<int> tmp;
04905 tmp.push_back(COMM_DETECT_BLANK | COMM_DETECT_SCENE | COMM_DETECT_LOGO);
04906 tmp.push_back(COMM_DETECT_BLANK);
04907 tmp.push_back(COMM_DETECT_BLANK | COMM_DETECT_SCENE);
04908 tmp.push_back(COMM_DETECT_SCENE);
04909 tmp.push_back(COMM_DETECT_LOGO);
04910 tmp.push_back(COMM_DETECT_2 | COMM_DETECT_BLANK | COMM_DETECT_LOGO);
04911 tmp.push_back(COMM_DETECT_PREPOSTROLL | COMM_DETECT_BLANK |
04912 COMM_DETECT_SCENE);
04913 return tmp;
04914 }
04915
04916 bool GetNextRecordingList(QDateTime &nextRecordingStart,
04917 bool *hasConflicts,
04918 vector<ProgramInfo> *list)
04919 {
04920 nextRecordingStart = QDateTime();
04921
04922 bool dummy;
04923 bool *conflicts = (hasConflicts) ? hasConflicts : &dummy;
04924
04925 ProgramList progList;
04926 if (!LoadFromScheduler(progList, *conflicts))
04927 return false;
04928
04929
04930 ProgramList::const_iterator it = progList.begin();
04931 for (; it != progList.end(); ++it)
04932 {
04933 if (((*it)->GetRecordingStatus() == rsWillRecord) &&
04934 (nextRecordingStart.isNull() ||
04935 nextRecordingStart > (*it)->GetRecordingStartTime()))
04936 {
04937 nextRecordingStart = (*it)->GetRecordingStartTime();
04938 }
04939 }
04940
04941 if (!list)
04942 return true;
04943
04944
04945 for (it = progList.begin(); it != progList.end(); ++it)
04946 {
04947 if (((*it)->GetRecordingStatus() == rsWillRecord) &&
04948 ((*it)->GetRecordingStartTime() == nextRecordingStart))
04949 {
04950 list->push_back(ProgramInfo(**it));
04951 }
04952 }
04953
04954 return true;
04955 }
04956
04957 PMapDBReplacement::PMapDBReplacement() : lock(new QMutex())
04958 {
04959 }
04960
04961 PMapDBReplacement::~PMapDBReplacement()
04962 {
04963 delete lock;
04964 }
04965
04966