|
MythTV
0.26-pre
|
00001 // -*- Mode: c++ -*- 00002 00003 // POSIX headers 00004 #include <sys/types.h> 00005 #include <unistd.h> 00006 00007 // C headers 00008 #include <cstdlib> 00009 00010 // C++ headers 00011 #include <iostream> 00012 #include <algorithm> 00013 using namespace std; 00014 00015 // Qt headers 00016 #include <QRegExp> 00017 #include <QMap> 00018 #include <QUrl> 00019 #include <QFile> 00020 #include <QFileInfo> 00021 #include <QDir> 00022 00023 // MythTV headers 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 //#define DEBUG_IN_USE 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, "// 0-2 00052 " r.season, r.episode, r.category, "// 3-5 00053 " r.chanid, c.channum, c.callsign, "// 6-8 00054 " c.name, c.outputfilters,r.recgroup, "// 9-11 00055 " r.playgroup, r.storagegroup, r.basename, "//12-14 00056 " r.hostname, r.recpriority, r.seriesid, "//15-17 00057 " r.programid, r.inetref, r.filesize, "//18-20 00058 " r.progstart, r.progend, r.stars, "//21-23 00059 " r.starttime, r.endtime, p.airdate+0, "//24-26 00060 " r.originalairdate, r.lastmodified, r.recordid, "//27-29 00061 " c.commmethod, r.commflagged, r.previouslyshown, "//30-32 00062 " r.transcoder, r.transcoded, r.deletepending, "//33-35 00063 " r.preserve, r.cutlist, r.autoexpire, "//36-38 00064 " r.editing, r.bookmark, r.watched, "//39-41 00065 " p.audioprop+0, p.videoprop+0, p.subtitletypes+0, "//42-44 00066 " r.findid, rec.dupin, rec.dupmethod "//45-47 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 // everything below this line is not serialized 00151 availableStatus(asAvailable), 00152 spread(-1), 00153 startCol(-1), 00154 sortTitle(), 00155 00156 // Private 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 // everything below this line is not serialized 00228 availableStatus(other.availableStatus), 00229 spread(other.spread), 00230 startCol(other.startCol), 00231 sortTitle(other.sortTitle), 00232 00233 // Private 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 // everything below this line is not serialized 00365 availableStatus(asAvailable), 00366 spread(-1), 00367 startCol(-1), 00368 sortTitle(), 00369 00370 // Private 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 // everything below this line is not serialized 00474 availableStatus(asAvailable), 00475 spread(-1), 00476 startCol(-1), 00477 sortTitle(), 00478 00479 // Private 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 // everything below this line is not serialized 00587 availableStatus(asAvailable), 00588 spread(-1), 00589 startCol(-1), 00590 sortTitle(), 00591 00592 // Private 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 // Stop recording keys on chanid (and recstarts). If the 00632 // recording is running, override the chanid, so we can stop 00633 // it from any matching program. Remove this hack when we 00634 // replace chanid/recstartts as the primary key for the 00635 // recorded and related tables with something better. 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 // everything below this line is not serialized 00732 availableStatus(asAvailable), 00733 spread(-1), 00734 startCol(-1), 00735 sortTitle(), 00736 00737 // Private 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 // everything below this line is not serialized 01073 spread = -1; 01074 startCol = -1; 01075 availableStatus = asAvailable; 01076 01077 // Private 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); // 0 01175 STR_TO_LIST(subtitle); // 1 01176 STR_TO_LIST(description); // 2 01177 INT_TO_LIST(season); // 3 01178 INT_TO_LIST(episode); // 4 01179 STR_TO_LIST(category); // 5 01180 INT_TO_LIST(chanid); // 6 01181 STR_TO_LIST(chanstr); // 7 01182 STR_TO_LIST(chansign); // 8 01183 STR_TO_LIST(channame); // 9 01184 STR_TO_LIST(pathname); // 10 01185 INT_TO_LIST(filesize); // 11 01186 01187 DATETIME_TO_LIST(startts); // 12 01188 DATETIME_TO_LIST(endts); // 13 01189 INT_TO_LIST(findid); // 14 01190 STR_TO_LIST(hostname); // 15 01191 INT_TO_LIST(sourceid); // 16 01192 INT_TO_LIST(cardid); // 17 01193 INT_TO_LIST(inputid); // 18 01194 INT_TO_LIST(recpriority); // 19 01195 INT_TO_LIST(recstatus); // 20 01196 INT_TO_LIST(recordid); // 21 01197 01198 INT_TO_LIST(rectype); // 22 01199 INT_TO_LIST(dupin); // 23 01200 INT_TO_LIST(dupmethod); // 24 01201 DATETIME_TO_LIST(recstartts);//25 01202 DATETIME_TO_LIST(recendts);// 26 01203 INT_TO_LIST(programflags); // 27 01204 STR_TO_LIST((!recgroup.isEmpty()) ? recgroup : "Default"); // 28 01205 STR_TO_LIST(chanplaybackfilters); // 29 01206 STR_TO_LIST(seriesid); // 30 01207 STR_TO_LIST(programid); // 31 01208 STR_TO_LIST(inetref); // 32 01209 01210 DATETIME_TO_LIST(lastmodified); // 33 01211 FLOAT_TO_LIST(stars); // 34 01212 DATE_TO_LIST(originalAirDate); // 35 01213 STR_TO_LIST((!playgroup.isEmpty()) ? playgroup : "Default"); // 36 01214 INT_TO_LIST(recpriority2); // 37 01215 INT_TO_LIST(parentid); // 38 01216 STR_TO_LIST((!storagegroup.isEmpty()) ? storagegroup : "Default"); // 39 01217 INT_TO_LIST(GetAudioProperties()); // 40 01218 INT_TO_LIST(GetVideoProperties()); // 41 01219 INT_TO_LIST(GetSubtitleType()); // 42 01220 01221 INT_TO_LIST(year); // 43 01222 /* do not forget to update the NUMPROGRAMLINES defines! */ 01223 } 01224 01225 // QStringList::const_iterator it = list.begin()+offset; 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); // 0 01270 STR_FROM_LIST(subtitle); // 1 01271 STR_FROM_LIST(description); // 2 01272 INT_FROM_LIST(season); // 3 01273 INT_FROM_LIST(episode); // 4 01274 STR_FROM_LIST(category); // 5 01275 INT_FROM_LIST(chanid); // 6 01276 STR_FROM_LIST(chanstr); // 7 01277 STR_FROM_LIST(chansign); // 8 01278 STR_FROM_LIST(channame); // 9 01279 STR_FROM_LIST(pathname); // 10 01280 INT_FROM_LIST(filesize); // 11 01281 01282 DATETIME_FROM_LIST(startts); // 12 01283 DATETIME_FROM_LIST(endts); // 13 01284 INT_FROM_LIST(findid); // 14 01285 STR_FROM_LIST(hostname); // 15 01286 INT_FROM_LIST(sourceid); // 16 01287 INT_FROM_LIST(cardid); // 17 01288 INT_FROM_LIST(inputid); // 18 01289 INT_FROM_LIST(recpriority); // 19 01290 ENUM_FROM_LIST(recstatus, RecStatusType); // 20 01291 INT_FROM_LIST(recordid); // 21 01292 01293 ENUM_FROM_LIST(rectype, RecordingType); // 22 01294 ENUM_FROM_LIST(dupin, RecordingDupInType); // 23 01295 ENUM_FROM_LIST(dupmethod, RecordingDupMethodType); // 24 01296 DATETIME_FROM_LIST(recstartts); // 25 01297 DATETIME_FROM_LIST(recendts); // 26 01298 INT_FROM_LIST(programflags); // 27 01299 STR_FROM_LIST(recgroup); // 28 01300 STR_FROM_LIST(chanplaybackfilters);//29 01301 STR_FROM_LIST(seriesid); // 30 01302 STR_FROM_LIST(programid); // 31 01303 STR_FROM_LIST(inetref); // 32 01304 01305 DATETIME_FROM_LIST(lastmodified); // 33 01306 FLOAT_FROM_LIST(stars); // 34 01307 DATE_FROM_LIST(originalAirDate);; // 35 01308 STR_FROM_LIST(playgroup); // 36 01309 INT_FROM_LIST(recpriority2); // 37 01310 INT_FROM_LIST(parentid); // 38 01311 STR_FROM_LIST(storagegroup); // 39 01312 uint audioproperties, videoproperties, subtitleType; 01313 INT_FROM_LIST(audioproperties); // 40 01314 INT_FROM_LIST(videoproperties); // 41 01315 INT_FROM_LIST(subtitleType); // 42 01316 properties = ((subtitleType << kSubtitlePropertyOffset) | 01317 (videoproperties << kVideoPropertyOffset) | 01318 (audioproperties << kAudioPropertyOffset)); 01319 01320 INT_FROM_LIST(year); // 43 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 // NOTE: Format changes and relevant additions made here should be 01345 // reflected in RecordingRule 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 // if (IsRecording()) 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 // 'mediatype' for a statetype, so untranslated 01594 // 'mediatypestring' for textarea, so translated 01595 // TODO Move to a dedicated ToState() method? 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 : // Fall through 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 // These items are not initialized below so they need to be cleared 01743 // if we're loading in a different program into this ProgramInfo 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 // everything below this line (in context) is not serialized 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 // We don't want to update the pathname if the basename is 01787 // the same as we may have already expanded pathname from 01788 // a simple basename to a localized path. 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 //catType; 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 //lastInUseTime; 01824 01825 recstatus = rsRecorded; 01826 //oldrecstatus; 01827 01828 //prefinput; 01829 //recpriority2; 01830 01831 recordid = query.value(29).toUInt(); 01832 //parentid; 01833 01834 //sourcid; 01835 //inputid; 01836 //cardid; 01837 findid = query.value(45).toUInt(); 01838 01839 //rectype; 01840 dupin = RecordingDupInType(query.value(46).toInt()); 01841 dupmethod = RecordingDupMethodType(query.value(47).toInt()); 01842 01843 // ancillary data -- begin 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 // ancillary data -- end 01868 01869 if (originalAirDate.isValid() && originalAirDate < QDate(1940, 1, 1)) 01870 originalAirDate = QDate(); 01871 01872 // Extra stuff which is not serialized and may get lost. 01873 // spread 01874 // startCol 01875 // sortTitle 01876 // availableStatus 01877 // inUseForWhat 01878 // postitionMapDBReplacement 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 // return the original path if BD or DVD URI 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 // Check to see if the file exists locally 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 // Note do not preceed with "/" that will cause existing code 02278 // to look for a local file with this name... 02279 return QString("GetPlaybackURL/UNABLE/TO/FIND/LOCAL/FILE/ON/%1/%2") 02280 .arg(hostname).arg(basename); 02281 } 02282 } 02283 02284 // Check to see if we should stream from the master backend 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 // Fallback to streaming from the backend the recording was created on 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 // clear out bogus mplexid's 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 set_flag(programflags, FL_REALLYEDITING, editing); 02640 set_flag(programflags, FL_EDITING, ((programflags & FL_REALLYEDITING) || 02641 (programflags & COMM_FLAG_PROCESSING))); 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 // gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0) && 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 // Convert the temporary marks into regular marks. 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 // Flag the existence of a cutlist 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 // check to make sure the show still exists before saving markups 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 // if (IsRecording()) 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 // if (IsRecording()) 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 // we may be recording this file and it may not exist yet so we need 03999 // to do some checking to see what is in pathname 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; // can't delete if we don't have a key 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 // if (!query.value(0).toUInt()) 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 // Let others know we changed status 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; // not safe to send update event... 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 != /*JOB_UNKNOWN*/ 0x0000) && 04472 (tmpStatus != /*JOB_QUEUED*/ 0x0001) && 04473 (!(tmpStatus & /*JOB_DONE*/ 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 // approximation which the DB can handle 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(), // title 04590 query.value(4).toString(), // subtitle 04591 query.value(5).toString(), // description 04592 query.value(6).toString(), // category 04593 04594 query.value(0).toUInt(), // chanid 04595 query.value(7).toString(), // channum 04596 query.value(8).toString(), // chansign 04597 query.value(9).toString(), // channame 04598 query.value(12).toString(), // chanplaybackfilters 04599 04600 query.value(1).toDateTime(), // startts 04601 query.value(2).toDateTime(), // endts 04602 query.value(1).toDateTime(), // recstartts 04603 query.value(2).toDateTime(), // recendts 04604 04605 query.value(13).toString(), // seriesid 04606 query.value(14).toString(), // programid 04607 query.value(18).toString(), // catType 04608 04609 query.value(16).toDouble(), // stars 04610 query.value(15).toUInt(), // year 04611 query.value(17).toDate(), // originalAirDate 04612 RecStatusType(query.value(21).toInt()), // recstatus 04613 query.value(19).toUInt(), // recordid 04614 RecordingType(query.value(20).toInt()), // rectype 04615 query.value(22).toUInt(), // findid 04616 04617 query.value(11).toInt() == COMM_DETECT_COMMFREE, // commfree 04618 query.value(10).toInt(), // repeat 04619 query.value(23).toInt(), // videoprop 04620 query.value(24).toInt(), // audioprop 04621 query.value(25).toInt(), // subtitletypes 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 // find the earliest scheduled recording 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 // save the details of the earliest recording(s) 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 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1