|
MythTV
0.26-pre
|
00001 // POSIX headers 00002 #include <sys/types.h> 00003 #include <unistd.h> 00004 00005 // C headers 00006 #include <cstdlib> 00007 00008 // C++ headers 00009 #include <iostream> 00010 #include <algorithm> 00011 using namespace std; 00012 00013 // Qt headers 00014 #include <QFileInfo> 00015 #include <QRegExp> 00016 #include <QFile> 00017 #include <QMap> 00018 00019 // MythTV headers 00020 #include "recordinginfo.h" 00021 #include "recordingrule.h" 00022 #include "scheduledrecording.h" 00023 #include "mythmiscutil.h" 00024 #include "mythcorecontext.h" 00025 #include "dialogbox.h" 00026 #include "remoteutil.h" 00027 #include "tvremoteutil.h" 00028 #include "jobqueue.h" 00029 #include "mythdb.h" 00030 #include "mythlogging.h" 00031 #include "previewgenerator.h" 00032 #include "channelutil.h" 00033 00034 #define LOC QString("RecordingInfo(%1): ").arg(GetBasename()) 00035 00036 static inline QString null_to_empty(const QString &str) 00037 { 00038 return str.isEmpty() ? "" : str; 00039 } 00040 00041 QString RecordingInfo::unknownTitle; 00042 // works only for integer divisors of 60 00043 static const uint kUnknownProgramLength = 30; 00044 00045 RecordingInfo::RecordingInfo( 00046 const QString &_title, 00047 const QString &_subtitle, 00048 const QString &_description, 00049 uint _season, 00050 uint _episode, 00051 const QString &_category, 00052 00053 uint _chanid, 00054 const QString &_chanstr, 00055 const QString &_chansign, 00056 const QString &_channame, 00057 00058 const QString &_recgroup, 00059 const QString &_playgroup, 00060 00061 const QString &_hostname, 00062 const QString &_storagegroup, 00063 00064 uint _year, 00065 00066 const QString &_seriesid, 00067 const QString &_programid, 00068 const QString &_inetref, 00069 const QString &_catType, 00070 00071 int _recpriority, 00072 00073 const QDateTime &_startts, 00074 const QDateTime &_endts, 00075 const QDateTime &_recstartts, 00076 const QDateTime &_recendts, 00077 00078 float _stars, 00079 const QDate &_originalAirDate, 00080 00081 bool _repeat, 00082 00083 RecStatusType _oldrecstatus, 00084 bool _reactivate, 00085 00086 uint _recordid, 00087 uint _parentid, 00088 RecordingType _rectype, 00089 RecordingDupInType _dupin, 00090 RecordingDupMethodType _dupmethod, 00091 00092 uint _sourceid, 00093 uint _inputid, 00094 uint _cardid, 00095 00096 uint _findid, 00097 00098 bool _commfree, 00099 uint _subtitleType, 00100 uint _videoproperties, 00101 uint _audioproperties, 00102 bool _future) : 00103 ProgramInfo( 00104 _title, _subtitle, _description, _season, _episode, 00105 _category, _chanid, _chanstr, _chansign, _channame, 00106 QString(), _recgroup, _playgroup, 00107 _startts, _endts, _recstartts, _recendts, 00108 _seriesid, _programid, _inetref), 00109 oldrecstatus(_oldrecstatus), 00110 savedrecstatus(rsUnknown), 00111 future(_future), 00112 record(NULL) 00113 { 00114 hostname = _hostname; 00115 storagegroup = _storagegroup; 00116 00117 year = _year; 00118 00119 catType = _catType; 00120 00121 recpriority = _recpriority; 00122 00123 stars = clamp(_stars, 0.0f, 1.0f); 00124 originalAirDate = _originalAirDate; 00125 if (originalAirDate.isValid() && originalAirDate < QDate(1940, 1, 1)) 00126 originalAirDate = QDate(); 00127 00128 programflags &= ~FL_REPEAT; 00129 programflags |= _repeat ? FL_REPEAT : 0; 00130 programflags &= ~FL_REACTIVATE; 00131 programflags |= _reactivate ? FL_REACTIVATE : 0; 00132 programflags &= ~FL_CHANCOMMFREE; 00133 programflags |= _commfree ? FL_CHANCOMMFREE : 0; 00134 00135 recordid = _recordid; 00136 parentid = _parentid; 00137 rectype = _rectype; 00138 dupin = _dupin; 00139 dupmethod = _dupmethod; 00140 00141 sourceid = _sourceid; 00142 inputid = _inputid; 00143 cardid = _cardid; 00144 00145 findid = _findid; 00146 00147 properties = ((_subtitleType << 11) | 00148 (_videoproperties << 6) | 00149 _audioproperties); 00150 00151 if (recstartts >= recendts) 00152 { 00153 // start/end-offsets are invalid so ignore 00154 recstartts = startts; 00155 recendts = endts; 00156 } 00157 } 00158 00159 RecordingInfo::RecordingInfo( 00160 const QString &_title, 00161 const QString &_subtitle, 00162 const QString &_description, 00163 uint _season, 00164 uint _episode, 00165 const QString &_category, 00166 00167 uint _chanid, 00168 const QString &_chanstr, 00169 const QString &_chansign, 00170 const QString &_channame, 00171 00172 const QString &_recgroup, 00173 const QString &_playgroup, 00174 00175 const QString &_seriesid, 00176 const QString &_programid, 00177 const QString &_inetref, 00178 00179 int _recpriority, 00180 00181 const QDateTime &_startts, 00182 const QDateTime &_endts, 00183 const QDateTime &_recstartts, 00184 const QDateTime &_recendts, 00185 00186 RecStatusType _recstatus, 00187 00188 uint _recordid, 00189 RecordingType _rectype, 00190 RecordingDupInType _dupin, 00191 RecordingDupMethodType _dupmethod, 00192 00193 uint _findid, 00194 00195 bool _commfree) : 00196 ProgramInfo( 00197 _title, _subtitle, _description, _season, _episode, 00198 _category, _chanid, _chanstr, _chansign, _channame, 00199 QString(), _recgroup, _playgroup, 00200 _startts, _endts, _recstartts, _recendts, 00201 _seriesid, _programid, _inetref), 00202 oldrecstatus(rsUnknown), 00203 savedrecstatus(rsUnknown), 00204 future(false), 00205 record(NULL) 00206 { 00207 recpriority = _recpriority; 00208 00209 recstatus = _recstatus, 00210 00211 recordid = _recordid; 00212 rectype = _rectype; 00213 dupin = _dupin; 00214 dupmethod = _dupmethod; 00215 00216 findid = _findid; 00217 00218 programflags &= ~FL_CHANCOMMFREE; 00219 programflags |= _commfree ? FL_CHANCOMMFREE : 0; 00220 } 00221 00230 RecordingInfo::RecordingInfo( 00231 uint _chanid, const QDateTime &desiredts, 00232 bool genUnknown, uint maxHours, LoadStatus *status) : 00233 oldrecstatus(rsUnknown), 00234 savedrecstatus(rsUnknown), 00235 future(false), 00236 record(NULL) 00237 { 00238 ProgramList schedList; 00239 ProgramList progList; 00240 00241 MSqlBindings bindings; 00242 QString querystr = "WHERE program.chanid = :CHANID AND " 00243 " program.starttime < :STARTTS1 AND " 00244 " program.endtime > :STARTTS2 "; 00245 bindings[":CHANID"] = QString::number(_chanid); 00246 QDateTime query_startts = desiredts.addSecs(50 - desiredts.time().second()); 00247 bindings[":STARTTS1"] = query_startts; 00248 bindings[":STARTTS2"] = query_startts; 00249 00250 ::LoadFromScheduler(schedList); 00251 LoadFromProgram(progList, querystr, bindings, schedList); 00252 00253 if (!progList.empty()) 00254 { 00255 ProgramInfo *pginfo = progList[0]; 00256 00257 if (maxHours > 0) 00258 { 00259 if (desiredts.secsTo( 00260 pginfo->GetScheduledEndTime()) > (int)maxHours * 3600) 00261 { 00262 pginfo->SetScheduledEndTime(desiredts.addSecs(maxHours * 3600)); 00263 pginfo->SetRecordingEndTime(pginfo->GetScheduledEndTime()); 00264 } 00265 } 00266 00267 *this = *pginfo; 00268 if (status) 00269 *status = kFoundProgram; 00270 return; 00271 } 00272 00273 recstartts = startts = desiredts; 00274 recendts = endts = desiredts; 00275 lastmodified = desiredts; 00276 00277 MSqlQuery query(MSqlQuery::InitCon()); 00278 query.prepare("SELECT chanid, channum, callsign, name, " 00279 "commmethod, outputfilters " 00280 "FROM channel " 00281 "WHERE chanid = :CHANID"); 00282 query.bindValue(":CHANID", _chanid); 00283 00284 if (!query.exec()) 00285 { 00286 MythDB::DBError("Loading Program overlapping a datetime", query); 00287 if (status) 00288 *status = kNoProgram; 00289 return; 00290 } 00291 00292 if (!query.next()) 00293 { 00294 if (status) 00295 *status = kNoProgram; 00296 return; 00297 } 00298 00299 chanid = query.value(0).toUInt(); 00300 chanstr = query.value(1).toString(); 00301 chansign = query.value(2).toString(); 00302 channame = query.value(3).toString(); 00303 programflags &= ~FL_CHANCOMMFREE; 00304 programflags |= (query.value(4).toInt() == COMM_DETECT_COMMFREE) ? 00305 FL_CHANCOMMFREE : 0; 00306 chanplaybackfilters = query.value(5).toString(); 00307 00308 { 00309 QMutexLocker locker(&staticDataLock); 00310 if (unknownTitle.isEmpty()) 00311 unknownTitle = gCoreContext->GetSetting("UnknownTitle"); 00312 title = unknownTitle; 00313 title.detach(); 00314 } 00315 00316 if (!genUnknown) 00317 { 00318 if (status) 00319 *status = kFakedZeroMinProgram; 00320 return; 00321 } 00322 00323 // Round endtime up to the next half-hour. 00324 endts.setTime(QTime(endts.time().hour(), 00325 endts.time().minute() / kUnknownProgramLength 00326 * kUnknownProgramLength)); 00327 endts = endts.addSecs(kUnknownProgramLength * 60); 00328 00329 // if under a minute, bump it up to the next half hour 00330 if (startts.secsTo(endts) < 60) 00331 endts = endts.addSecs(kUnknownProgramLength * 60); 00332 00333 recendts = endts; 00334 00335 // Find next program starttime 00336 bindings.clear(); 00337 QDateTime nextstart = startts; 00338 querystr = "WHERE program.chanid = :CHANID AND " 00339 " program.starttime > :STARTTS " 00340 "GROUP BY program.starttime ORDER BY program.starttime LIMIT 1 "; 00341 bindings[":CHANID"] = QString::number(_chanid); 00342 bindings[":STARTTS"] = desiredts.addSecs(50 - desiredts.time().second()); 00343 00344 LoadFromProgram(progList, querystr, bindings, schedList); 00345 00346 if (!progList.empty()) 00347 nextstart = (*progList.begin())->GetScheduledStartTime(); 00348 00349 if (nextstart > startts && nextstart < recendts) 00350 recendts = endts = nextstart; 00351 00352 if (status) 00353 *status = kFakedLiveTVProgram; 00354 } 00355 00357 void RecordingInfo::clone(const RecordingInfo &other, 00358 bool ignore_non_serialized_data) 00359 { 00360 bool is_same = 00361 (chanid && recstartts.isValid() && startts.isValid() && 00362 chanid == other.GetChanID() && 00363 recstartts == other.GetRecordingStartTime() && 00364 startts == other.GetScheduledStartTime()); 00365 00366 ProgramInfo::clone(other, ignore_non_serialized_data); 00367 00368 if (!is_same) 00369 { 00370 delete record; 00371 record = NULL; 00372 } 00373 00374 if (!ignore_non_serialized_data) 00375 { 00376 oldrecstatus = other.oldrecstatus; 00377 savedrecstatus = other.savedrecstatus; 00378 future = other.future; 00379 } 00380 } 00381 00383 void RecordingInfo::clone(const ProgramInfo &other, 00384 bool ignore_non_serialized_data) 00385 { 00386 bool is_same = 00387 (chanid && recstartts.isValid() && startts.isValid() && 00388 chanid == other.GetChanID() && 00389 recstartts == other.GetRecordingStartTime() && 00390 startts == other.GetScheduledStartTime()); 00391 00392 ProgramInfo::clone(other, ignore_non_serialized_data); 00393 00394 if (!is_same) 00395 { 00396 delete record; 00397 record = NULL; 00398 } 00399 00400 oldrecstatus = rsUnknown; 00401 savedrecstatus = rsUnknown; 00402 future = false; 00403 } 00404 00405 void RecordingInfo::clear(void) 00406 { 00407 ProgramInfo::clear(); 00408 00409 delete record; 00410 record = NULL; 00411 00412 oldrecstatus = rsUnknown; 00413 savedrecstatus = rsUnknown; 00414 future = false; 00415 } 00416 00417 00421 RecordingInfo::~RecordingInfo() 00422 { 00423 delete record; 00424 record = NULL; 00425 } 00426 00432 RecordingType RecordingInfo::GetProgramRecordingStatus(void) 00433 { 00434 if (record == NULL) 00435 { 00436 record = new RecordingRule(); 00437 record->LoadByProgram(this); 00438 } 00439 00440 return record->m_type; 00441 } 00442 00448 QString RecordingInfo::GetProgramRecordingProfile(void) const 00449 { 00450 if (record == NULL) 00451 { 00452 record = new RecordingRule(); 00453 record->LoadByProgram(this); 00454 } 00455 00456 return record->m_recProfile; 00457 } 00458 00462 int RecordingInfo::GetAutoRunJobs(void) const 00463 { 00464 if (record == NULL) 00465 { 00466 record = new RecordingRule(); 00467 record->LoadByProgram(this); 00468 } 00469 00470 int result = 0; 00471 00472 if (record->m_autoTranscode) 00473 result |= JOB_TRANSCODE; 00474 if (record->m_autoCommFlag) 00475 result |= JOB_COMMFLAG; 00476 if (record->m_autoMetadataLookup) 00477 result |= JOB_METADATA; 00478 if (record->m_autoUserJob1) 00479 result |= JOB_USERJOB1; 00480 if (record->m_autoUserJob2) 00481 result |= JOB_USERJOB2; 00482 if (record->m_autoUserJob3) 00483 result |= JOB_USERJOB3; 00484 if (record->m_autoUserJob4) 00485 result |= JOB_USERJOB4; 00486 00487 00488 return result; 00489 } 00490 00494 void RecordingInfo::ApplyRecordRecID(void) 00495 { 00496 MSqlQuery query(MSqlQuery::InitCon()); 00497 00498 if (getRecordID() < 0) 00499 { 00500 LOG(VB_GENERAL, LOG_ERR, 00501 "ProgInfo Error: ApplyRecordRecID(void) needs recordid"); 00502 return; 00503 } 00504 00505 query.prepare("UPDATE recorded " 00506 "SET recordid = :RECID " 00507 "WHERE chanid = :CHANID AND starttime = :START"); 00508 00509 if (rectype == kOverrideRecord && parentid > 0) 00510 query.bindValue(":RECID", parentid); 00511 else 00512 query.bindValue(":RECID", getRecordID()); 00513 query.bindValue(":CHANID", chanid); 00514 query.bindValue(":START", recstartts); 00515 00516 if (!query.exec()) 00517 MythDB::DBError(LOC + "RecordID update", query); 00518 } 00519 00525 // newstate uses same values as return of GetProgramRecordingState 00526 void RecordingInfo::ApplyRecordStateChange(RecordingType newstate, bool save) 00527 { 00528 GetProgramRecordingStatus(); 00529 if (newstate == kOverrideRecord || newstate == kDontRecord) 00530 record->MakeOverride(); 00531 record->m_type = newstate; 00532 00533 if (save) 00534 { 00535 if (newstate == kNotRecording) 00536 record->Delete(); 00537 else 00538 record->Save(); 00539 } 00540 } 00541 00547 void RecordingInfo::ApplyRecordRecPriorityChange(int newrecpriority) 00548 { 00549 GetProgramRecordingStatus(); 00550 record->m_recPriority = newrecpriority; 00551 record->Save(); 00552 } 00553 00559 void RecordingInfo::ApplyRecordRecGroupChange(const QString &newrecgroup) 00560 { 00561 MSqlQuery query(MSqlQuery::InitCon()); 00562 00563 query.prepare("UPDATE recorded" 00564 " SET recgroup = :RECGROUP" 00565 " WHERE chanid = :CHANID" 00566 " AND starttime = :START ;"); 00567 query.bindValue(":RECGROUP", null_to_empty(newrecgroup)); 00568 query.bindValue(":START", recstartts); 00569 query.bindValue(":CHANID", chanid); 00570 00571 if (!query.exec()) 00572 MythDB::DBError("RecGroup update", query); 00573 00574 recgroup = newrecgroup; 00575 00576 SendUpdateEvent(); 00577 } 00578 00584 void RecordingInfo::ApplyRecordPlayGroupChange(const QString &newplaygroup) 00585 { 00586 MSqlQuery query(MSqlQuery::InitCon()); 00587 00588 query.prepare("UPDATE recorded" 00589 " SET playgroup = :PLAYGROUP" 00590 " WHERE chanid = :CHANID" 00591 " AND starttime = :START ;"); 00592 query.bindValue(":PLAYGROUP", null_to_empty(newplaygroup)); 00593 query.bindValue(":START", recstartts); 00594 query.bindValue(":CHANID", chanid); 00595 00596 if (!query.exec()) 00597 MythDB::DBError("PlayGroup update", query); 00598 00599 playgroup = newplaygroup; 00600 00601 SendUpdateEvent(); 00602 } 00603 00609 void RecordingInfo::ApplyStorageGroupChange(const QString &newstoragegroup) 00610 { 00611 MSqlQuery query(MSqlQuery::InitCon()); 00612 00613 query.prepare("UPDATE recorded" 00614 " SET storagegroup = :STORAGEGROUP" 00615 " WHERE chanid = :CHANID" 00616 " AND starttime = :START ;"); 00617 query.bindValue(":STORAGEGROUP", null_to_empty(newstoragegroup)); 00618 query.bindValue(":START", recstartts); 00619 query.bindValue(":CHANID", chanid); 00620 00621 if (!query.exec()) 00622 MythDB::DBError("StorageGroup update", query); 00623 00624 storagegroup = newstoragegroup; 00625 00626 SendUpdateEvent(); 00627 } 00628 00636 void RecordingInfo::ApplyRecordRecTitleChange(const QString &newTitle, 00637 const QString &newSubtitle, const QString &newDescription) 00638 { 00639 MSqlQuery query(MSqlQuery::InitCon()); 00640 QString sql = "UPDATE recorded SET title = :TITLE, subtitle = :SUBTITLE "; 00641 if (!newDescription.isNull()) 00642 sql += ", description = :DESCRIPTION "; 00643 sql += " WHERE chanid = :CHANID AND starttime = :START ;"; 00644 00645 query.prepare(sql); 00646 query.bindValue(":TITLE", newTitle); 00647 query.bindValue(":SUBTITLE", null_to_empty(newSubtitle)); 00648 if (!newDescription.isNull()) 00649 query.bindValue(":DESCRIPTION", newDescription); 00650 query.bindValue(":CHANID", chanid); 00651 query.bindValue(":START", recstartts); 00652 00653 if (!query.exec()) 00654 MythDB::DBError("RecTitle update", query); 00655 00656 title = newTitle; 00657 subtitle = newSubtitle; 00658 if (!newDescription.isNull()) 00659 description = newDescription; 00660 00661 SendUpdateEvent(); 00662 } 00663 00664 /* \fn RecordingInfo::ApplyTranscoderProfileChangeById(int id) 00665 * \brief Sets the transcoder profile for a recording 00666 * \param profileid is the 'id' field from recordingprofiles table. 00667 */ 00668 void RecordingInfo::ApplyTranscoderProfileChangeById(int id) 00669 { 00670 MSqlQuery query(MSqlQuery::InitCon()); 00671 00672 query.prepare("UPDATE recorded " 00673 "SET transcoder = :PROFILEID " 00674 "WHERE chanid = :CHANID " 00675 "AND starttime = :START"); 00676 query.bindValue(":PROFILEID", id); 00677 query.bindValue(":CHANID", chanid); 00678 query.bindValue(":START", recstartts); 00679 00680 if (!query.exec()) 00681 MythDB::DBError(LOC + "unable to update transcoder " 00682 "in recorded table", query); 00683 } 00684 00688 void RecordingInfo::ApplyTranscoderProfileChange(const QString &profile) const 00689 { 00690 if (profile == "Default") // use whatever is already in the transcoder 00691 return; 00692 00693 MSqlQuery query(MSqlQuery::InitCon()); 00694 00695 if (profile == "Autodetect") 00696 { 00697 query.prepare("UPDATE recorded " 00698 "SET transcoder = 0 " 00699 "WHERE chanid = :CHANID " 00700 "AND starttime = :START"); 00701 query.bindValue(":CHANID", chanid); 00702 query.bindValue(":START", recstartts); 00703 00704 if (!query.exec()) 00705 MythDB::DBError(LOC + "unable to update transcoder " 00706 "in recorded table", query); 00707 } 00708 else 00709 { 00710 MSqlQuery pidquery(MSqlQuery::InitCon()); 00711 pidquery.prepare("SELECT r.id " 00712 "FROM recordingprofiles r, profilegroups p " 00713 "WHERE r.profilegroup = p.id " 00714 "AND p.name = 'Transcoders' " 00715 "AND r.name = :PROFILE "); 00716 pidquery.bindValue(":PROFILE", profile); 00717 00718 if (!pidquery.exec()) 00719 { 00720 MythDB::DBError("ProgramInfo: unable to query transcoder " 00721 "profile ID", query); 00722 } 00723 else if (pidquery.next()) 00724 { 00725 query.prepare("UPDATE recorded " 00726 "SET transcoder = :TRANSCODER " 00727 "WHERE chanid = :CHANID " 00728 "AND starttime = :START"); 00729 query.bindValue(":TRANSCODER", pidquery.value(0).toInt()); 00730 query.bindValue(":CHANID", chanid); 00731 query.bindValue(":START", recstartts); 00732 00733 if (!query.exec()) 00734 MythDB::DBError(LOC + "unable to update transcoder " 00735 "in recorded table", query); 00736 } 00737 else 00738 { 00739 LOG(VB_GENERAL, LOG_ERR, 00740 "ProgramInfo: unable to query transcoder profile ID"); 00741 } 00742 } 00743 } 00744 00763 void RecordingInfo::ToggleRecord(void) 00764 { 00765 RecordingType curType = GetProgramRecordingStatus(); 00766 00767 switch (curType) 00768 { 00769 case kNotRecording: 00770 ApplyRecordStateChange(kSingleRecord); 00771 break; 00772 case kSingleRecord: 00773 ApplyRecordStateChange(kFindOneRecord); 00774 break; 00775 case kFindOneRecord: 00776 ApplyRecordStateChange(kAllRecord); 00777 break; 00778 case kAllRecord: 00779 ApplyRecordStateChange(kSingleRecord); 00780 break; 00781 00782 case kOverrideRecord: 00783 ApplyRecordStateChange(kDontRecord); 00784 break; 00785 case kDontRecord: 00786 ApplyRecordStateChange(kOverrideRecord); 00787 break; 00788 00789 default: 00790 ApplyRecordStateChange(kAllRecord); 00791 break; 00792 /* 00793 case kNotRecording: 00794 ApplyRecordStateChange(kSingleRecord); 00795 break; 00796 case kSingleRecord: 00797 ApplyRecordStateChange(kFindOneRecord); 00798 break; 00799 case kFindOneRecord: 00800 ApplyRecordStateChange(kWeekslotRecord); 00801 break; 00802 case kWeekslotRecord: 00803 ApplyRecordStateChange(kFindWeeklyRecord); 00804 break; 00805 case kFindWeeklyRecord: 00806 ApplyRecordStateChange(kTimeslotRecord); 00807 break; 00808 case kTimeslotRecord: 00809 ApplyRecordStateChange(kFindDailyRecord); 00810 break; 00811 case kFindDailyRecord: 00812 ApplyRecordStateChange(kChannelRecord); 00813 break; 00814 case kChannelRecord: 00815 ApplyRecordStateChange(kAllRecord); 00816 break; 00817 case kAllRecord: 00818 default: 00819 ApplyRecordStateChange(kNotRecording); 00820 break; 00821 case kOverrideRecord: 00822 ApplyRecordStateChange(kDontRecord); 00823 break; 00824 case kDontRecord: 00825 ApplyRecordStateChange(kOverrideRecord); 00826 break; 00827 */ 00828 } 00829 } 00830 00834 RecordingRule* RecordingInfo::GetRecordingRule(void) 00835 { 00836 GetProgramRecordingStatus(); 00837 return record; 00838 } 00839 00843 int RecordingInfo::getRecordID(void) 00844 { 00845 GetProgramRecordingStatus(); 00846 recordid = record->m_recordID; 00847 return recordid; 00848 } 00849 00858 void RecordingInfo::StartedRecording(QString ext) 00859 { 00860 QString dirname = pathname; 00861 00862 if (!record) 00863 { 00864 record = new RecordingRule(); 00865 record->LoadByProgram(this); 00866 } 00867 00868 hostname = gCoreContext->GetHostName(); 00869 pathname = CreateRecordBasename(ext); 00870 00871 int count = 0; 00872 while (!InsertProgram(this, record) && count < 50) 00873 { 00874 recstartts = recstartts.addSecs(1); 00875 pathname = CreateRecordBasename(ext); 00876 count++; 00877 } 00878 00879 if (count >= 50) 00880 { 00881 LOG(VB_GENERAL, LOG_ERR, "Couldn't insert program"); 00882 return; 00883 } 00884 00885 pathname = dirname + "/" + pathname; 00886 00887 LOG(VB_FILE, LOG_INFO, QString(LOC + "StartedRecording: Recording to '%1'") 00888 .arg(pathname)); 00889 00890 00891 MSqlQuery query(MSqlQuery::InitCon()); 00892 00893 query.prepare("DELETE FROM recordedseek WHERE chanid = :CHANID" 00894 " AND starttime = :START;"); 00895 query.bindValue(":CHANID", chanid); 00896 query.bindValue(":START", recstartts); 00897 00898 if (!query.exec() || !query.isActive()) 00899 MythDB::DBError("Clear seek info on record", query); 00900 00901 query.prepare("DELETE FROM recordedmarkup WHERE chanid = :CHANID" 00902 " AND starttime = :START;"); 00903 query.bindValue(":CHANID", chanid); 00904 query.bindValue(":START", recstartts); 00905 00906 if (!query.exec() || !query.isActive()) 00907 MythDB::DBError("Clear markup on record", query); 00908 00909 query.prepare("REPLACE INTO recordedcredits" 00910 " SELECT * FROM credits" 00911 " WHERE chanid = :CHANID AND starttime = :START;"); 00912 query.bindValue(":CHANID", chanid); 00913 query.bindValue(":START", startts); 00914 if (!query.exec() || !query.isActive()) 00915 MythDB::DBError("Copy program credits on record", query); 00916 00917 query.prepare("REPLACE INTO recordedprogram" 00918 " SELECT * from program" 00919 " WHERE chanid = :CHANID AND starttime = :START" 00920 " AND title = :TITLE;"); 00921 query.bindValue(":CHANID", chanid); 00922 query.bindValue(":START", startts); 00923 query.bindValue(":TITLE", title); 00924 if (!query.exec() || !query.isActive()) 00925 MythDB::DBError("Copy program data on record", query); 00926 00927 query.prepare("REPLACE INTO recordedrating" 00928 " SELECT * from programrating" 00929 " WHERE chanid = :CHANID AND starttime = :START;"); 00930 query.bindValue(":CHANID", chanid); 00931 query.bindValue(":START", startts); 00932 if (!query.exec() || !query.isActive()) 00933 MythDB::DBError("Copy program ratings on record", query); 00934 00935 SendAddedEvent(); 00936 } 00937 00938 bool RecordingInfo::InsertProgram(const RecordingInfo *pg, 00939 const RecordingRule *rule) 00940 { 00941 MSqlQuery query(MSqlQuery::InitCon()); 00942 00943 if (!query.exec("LOCK TABLES recorded WRITE")) 00944 { 00945 MythDB::DBError("InsertProgram -- lock", query); 00946 return false; 00947 } 00948 00949 query.prepare( 00950 "SELECT recordid " 00951 " FROM recorded " 00952 " WHERE chanid = :CHANID AND " 00953 " starttime = :STARTS"); 00954 query.bindValue(":CHANID", pg->chanid); 00955 query.bindValue(":STARTS", pg->recstartts); 00956 00957 bool err = true; 00958 if (!query.exec()) 00959 { 00960 MythDB::DBError("InsertProgram -- select", query); 00961 } 00962 else if (query.next()) 00963 { 00964 LOG(VB_GENERAL, LOG_ERR, 00965 QString("RecordingInfo::InsertProgram(%1): ") 00966 .arg(pg->toString()) + "recording already exists..."); 00967 } 00968 else 00969 { 00970 err = false; 00971 } 00972 00973 if (err) 00974 { 00975 if (!query.exec("UNLOCK TABLES")) 00976 MythDB::DBError("InsertProgram -- unlock tables", query); 00977 return false; 00978 } 00979 00980 query.prepare( 00981 "INSERT INTO recorded " 00982 " (chanid, starttime, endtime, title, " 00983 " subtitle, description, season, episode, " 00984 " hostname, category, recgroup, autoexpire, " 00985 " recordid, seriesid, programid, inetref, " 00986 " stars, previouslyshown, originalairdate, " 00987 " findid, transcoder, playgroup, recpriority, " 00988 " basename, progstart, progend, profile, " 00989 " duplicate, storagegroup) " 00990 "VALUES" 00991 " (:CHANID, :STARTS, :ENDS, :TITLE, " 00992 " :SUBTITLE, :DESC, :SEASON, :EPISODE, " 00993 " :HOSTNAME, :CATEGORY, :RECGROUP, :AUTOEXP, " 00994 " :RECORDID, :SERIESID, :PROGRAMID, :INETREF, " 00995 " :STARS, :REPEAT, :ORIGAIRDATE, " 00996 " :FINDID, :TRANSCODER, :PLAYGROUP, :RECPRIORITY, " 00997 " :BASENAME, :PROGSTART, :PROGEND, :PROFILE, " 00998 " 0, :STORGROUP) " 00999 ); 01000 01001 if (pg->rectype == kOverrideRecord) 01002 query.bindValue(":RECORDID", pg->parentid); 01003 else 01004 query.bindValue(":RECORDID", pg->recordid); 01005 01006 if (pg->originalAirDate.isValid()) 01007 query.bindValue(":ORIGAIRDATE", pg->originalAirDate); 01008 else 01009 query.bindValue(":ORIGAIRDATE", "0000-00-00"); 01010 01011 query.bindValue(":CHANID", pg->chanid); 01012 query.bindValue(":STARTS", pg->recstartts); 01013 query.bindValue(":ENDS", pg->recendts); 01014 query.bindValue(":TITLE", pg->title); 01015 query.bindValue(":SUBTITLE", null_to_empty(pg->subtitle)); 01016 query.bindValue(":DESC", null_to_empty(pg->description)); 01017 query.bindValue(":SEASON", pg->season); 01018 query.bindValue(":EPISODE", pg->episode); 01019 query.bindValue(":HOSTNAME", pg->hostname); 01020 query.bindValue(":CATEGORY", null_to_empty(pg->category)); 01021 query.bindValue(":RECGROUP", null_to_empty(pg->recgroup)); 01022 query.bindValue(":AUTOEXP", rule->m_autoExpire); 01023 query.bindValue(":SERIESID", null_to_empty(pg->seriesid)); 01024 query.bindValue(":PROGRAMID", null_to_empty(pg->programid)); 01025 query.bindValue(":INETREF", null_to_empty(pg->inetref)); 01026 query.bindValue(":FINDID", pg->findid); 01027 query.bindValue(":STARS", pg->stars); 01028 query.bindValue(":REPEAT", pg->IsRepeat()); 01029 query.bindValue(":TRANSCODER", rule->m_transcoder); 01030 query.bindValue(":PLAYGROUP", pg->playgroup); 01031 query.bindValue(":RECPRIORITY", rule->m_recPriority); 01032 query.bindValue(":BASENAME", pg->pathname); 01033 query.bindValue(":STORGROUP", null_to_empty(pg->storagegroup)); 01034 query.bindValue(":PROGSTART", pg->startts); 01035 query.bindValue(":PROGEND", pg->endts); 01036 query.bindValue(":PROFILE", null_to_empty(rule->m_recProfile)); 01037 01038 bool ok = query.exec() && (query.numRowsAffected() > 0); 01039 bool active = query.isActive(); 01040 01041 if (!query.exec("UNLOCK TABLES")) 01042 MythDB::DBError("InsertProgram -- unlock tables", query); 01043 01044 if (!ok && !active) 01045 MythDB::DBError("InsertProgram -- insert", query); 01046 01047 else if (pg->recordid > 0) 01048 { 01049 query.prepare("UPDATE channel SET last_record = NOW() " 01050 "WHERE chanid = :CHANID"); 01051 query.bindValue(":CHANID", pg->GetChanID()); 01052 if (!query.exec()) 01053 MythDB::DBError("InsertProgram -- channel last_record", query); 01054 01055 query.prepare("UPDATE record SET last_record = NOW() " 01056 "WHERE recordid = :RECORDID"); 01057 query.bindValue(":RECORDID", pg->recordid); 01058 if (!query.exec()) 01059 MythDB::DBError("InsertProgram -- record last_record", query); 01060 01061 if (pg->rectype == kOverrideRecord && pg->parentid > 0) 01062 { 01063 query.prepare("UPDATE record SET last_record = NOW() " 01064 "WHERE recordid = :PARENTID"); 01065 query.bindValue(":PARENTID", pg->parentid); 01066 if (!query.exec()) 01067 MythDB::DBError("InsertProgram -- record last_record override", 01068 query); 01069 } 01070 } 01071 01072 return ok; 01073 } 01074 01080 void RecordingInfo::FinishedRecording(bool allowReRecord) 01081 { 01082 MSqlQuery query(MSqlQuery::InitCon()); 01083 query.prepare("UPDATE recorded SET endtime = :ENDTIME, " 01084 " duplicate = :DUPLICATE " 01085 "WHERE chanid = :CHANID AND " 01086 " starttime = :STARTTIME "); 01087 query.bindValue(":ENDTIME", recendts); 01088 query.bindValue(":CHANID", chanid); 01089 query.bindValue(":STARTTIME", recstartts); 01090 query.bindValue(":DUPLICATE", !allowReRecord); 01091 01092 if (!query.exec()) 01093 MythDB::DBError("FinishedRecording update", query); 01094 01095 GetProgramRecordingStatus(); 01096 if (!allowReRecord) 01097 { 01098 recstatus = rsRecorded; 01099 01100 uint starttime = recstartts.toTime_t(); 01101 uint endtime = recendts.toTime_t(); 01102 int64_t duration = ((int64_t)endtime - (int64_t)starttime) * 1000000; 01103 SaveTotalDuration(duration); 01104 01105 QString msg = "Finished recording"; 01106 QString msg_subtitle = subtitle.isEmpty() ? "" : 01107 QString(" \"%1\"").arg(subtitle); 01108 QString details = QString("%1%2: channel %3") 01109 .arg(title) 01110 .arg(msg_subtitle) 01111 .arg(chanid); 01112 01113 LOG(VB_GENERAL, LOG_INFO, QString("%1 %2").arg(msg).arg(details)); 01114 } 01115 01116 SendUpdateEvent(); 01117 } 01118 01123 void RecordingInfo::UpdateRecordingEnd(void) 01124 { 01125 MSqlQuery query(MSqlQuery::InitCon()); 01126 query.prepare("UPDATE recorded SET endtime = :ENDTIME " 01127 "WHERE chanid = :CHANID AND " 01128 " starttime = :STARTTIME "); 01129 query.bindValue(":ENDTIME", recendts); 01130 01131 query.bindValue(":CHANID", chanid); 01132 query.bindValue(":STARTTIME", recstartts); 01133 01134 if (!query.exec()) 01135 MythDB::DBError("UpdateRecordingEnd update", query); 01136 01137 SendUpdateEvent(); 01138 } 01139 01143 void RecordingInfo::ReactivateRecording(void) 01144 { 01145 MSqlQuery result(MSqlQuery::InitCon()); 01146 01147 result.prepare("UPDATE oldrecorded SET reactivate = 1 " 01148 "WHERE station = :STATION AND " 01149 " starttime = :STARTTIME AND " 01150 " title = :TITLE;"); 01151 result.bindValue(":STARTTIME", startts); 01152 result.bindValue(":TITLE", title); 01153 result.bindValue(":STATION", chansign); 01154 01155 if (!result.exec()) 01156 MythDB::DBError("ReactivateRecording", result); 01157 01158 ScheduledRecording::ReschedulePlace("Reactivate"); 01159 } 01160 01164 void RecordingInfo::AddHistory(bool resched, bool forcedup, bool future) 01165 { 01166 bool dup = (GetRecordingStatus() == rsRecorded || forcedup); 01167 RecStatusType rs = (GetRecordingStatus() == rsCurrentRecording && 01168 !future) ? rsPreviousRecording : GetRecordingStatus(); 01169 LOG(VB_SCHEDULE, LOG_INFO, QString("AddHistory: %1/%2, %3, %4, %5/%6") 01170 .arg(int(rs)).arg(int(oldrecstatus)).arg(future).arg(dup) 01171 .arg(GetScheduledStartTime().toString()).arg(GetTitle())); 01172 if (!future) 01173 oldrecstatus = GetRecordingStatus(); 01174 if (dup) 01175 SetReactivated(false); 01176 uint erecid = parentid ? parentid : recordid; 01177 01178 MSqlQuery result(MSqlQuery::InitCon()); 01179 01180 result.prepare("REPLACE INTO oldrecorded (chanid,starttime," 01181 "endtime,title,subtitle,description,season,episode," 01182 "category,seriesid,programid,inetref,findid,recordid," 01183 "station,rectype,recstatus,duplicate,reactivate,future) " 01184 "VALUES(:CHANID,:START,:END,:TITLE,:SUBTITLE,:DESC,:SEASON," 01185 ":EPISODE,:CATEGORY,:SERIESID,:PROGRAMID,:INETREF," 01186 ":FINDID,:RECORDID,:STATION,:RECTYPE,:RECSTATUS,:DUPLICATE," 01187 ":REACTIVATE,:FUTURE);"); 01188 result.bindValue(":CHANID", chanid); 01189 result.bindValue(":START", startts); 01190 result.bindValue(":END", endts); 01191 result.bindValue(":TITLE", title); 01192 result.bindValue(":SUBTITLE", null_to_empty(subtitle)); 01193 result.bindValue(":DESC", null_to_empty(description)); 01194 result.bindValue(":SEASON", season); 01195 result.bindValue(":EPISODE", episode); 01196 result.bindValue(":CATEGORY", null_to_empty(category)); 01197 result.bindValue(":SERIESID", null_to_empty(seriesid)); 01198 result.bindValue(":PROGRAMID", null_to_empty(programid)); 01199 result.bindValue(":INETREF", null_to_empty(inetref)); 01200 result.bindValue(":FINDID", findid); 01201 result.bindValue(":RECORDID", erecid); 01202 result.bindValue(":STATION", null_to_empty(chansign)); 01203 result.bindValue(":RECTYPE", rectype); 01204 result.bindValue(":RECSTATUS", rs); 01205 result.bindValue(":DUPLICATE", dup); 01206 result.bindValue(":REACTIVATE", IsReactivated()); 01207 result.bindValue(":FUTURE", future); 01208 01209 if (!result.exec()) 01210 MythDB::DBError("addHistory", result); 01211 01212 if (dup && findid) 01213 { 01214 result.prepare("REPLACE INTO oldfind (recordid, findid) " 01215 "VALUES(:RECORDID,:FINDID);"); 01216 result.bindValue(":RECORDID", erecid); 01217 result.bindValue(":FINDID", findid); 01218 01219 if (!result.exec()) 01220 MythDB::DBError("addFindHistory", result); 01221 } 01222 01223 // The adding of an entry to oldrecorded may affect near-future 01224 // scheduling decisions, so recalculate if told 01225 if (resched) 01226 ScheduledRecording::RescheduleCheck(*this, "AddHistory"); 01227 } 01228 01232 void RecordingInfo::DeleteHistory(void) 01233 { 01234 uint erecid = parentid ? parentid : recordid; 01235 01236 MSqlQuery result(MSqlQuery::InitCon()); 01237 01238 result.prepare("DELETE FROM oldrecorded WHERE title = :TITLE AND " 01239 "starttime = :START AND station = :STATION"); 01240 result.bindValue(":TITLE", title); 01241 result.bindValue(":START", recstartts); 01242 result.bindValue(":STATION", chansign); 01243 01244 if (!result.exec()) 01245 MythDB::DBError("deleteHistory", result); 01246 01247 if (/*duplicate &&*/ findid) 01248 { 01249 result.prepare("DELETE FROM oldfind WHERE " 01250 "recordid = :RECORDID AND findid = :FINDID"); 01251 result.bindValue(":RECORDID", erecid); 01252 result.bindValue(":FINDID", findid); 01253 01254 if (!result.exec()) 01255 MythDB::DBError("deleteFindHistory", result); 01256 } 01257 01258 // The removal of an entry from oldrecorded may affect near-future 01259 // scheduling decisions, so recalculate 01260 ScheduledRecording::RescheduleCheck(*this, "DeleteHistory"); 01261 } 01262 01271 void RecordingInfo::ForgetHistory(void) 01272 { 01273 uint erecid = parentid ? parentid : recordid; 01274 01275 MSqlQuery result(MSqlQuery::InitCon()); 01276 01277 result.prepare("UPDATE recorded SET duplicate = 0 " 01278 "WHERE chanid = :CHANID " 01279 "AND starttime = :STARTTIME " 01280 "AND title = :TITLE;"); 01281 result.bindValue(":STARTTIME", recstartts); 01282 result.bindValue(":TITLE", title); 01283 result.bindValue(":CHANID", chanid); 01284 01285 if (!result.exec()) 01286 MythDB::DBError("forgetRecorded", result); 01287 01288 result.prepare("UPDATE oldrecorded SET duplicate = 0 " 01289 "WHERE duplicate = 1 " 01290 "AND title = :TITLE AND " 01291 "((programid = '' AND subtitle = :SUBTITLE" 01292 " AND description = :DESC) OR " 01293 " (programid <> '' AND programid = :PROGRAMID) OR " 01294 " (findid <> 0 AND findid = :FINDID))"); 01295 result.bindValue(":TITLE", title); 01296 result.bindValue(":SUBTITLE", null_to_empty(subtitle)); 01297 result.bindValue(":DESC", null_to_empty(description)); 01298 result.bindValue(":PROGRAMID", null_to_empty(programid)); 01299 result.bindValue(":FINDID", findid); 01300 01301 if (!result.exec()) 01302 MythDB::DBError("forgetHistory", result); 01303 01304 result.prepare("DELETE FROM oldrecorded " 01305 "WHERE recstatus = :NEVER AND duplicate = 0"); 01306 result.bindValue(":NEVER", rsNeverRecord); 01307 01308 if (!result.exec()) 01309 MythDB::DBError("forgetNeverHisttory", result); 01310 01311 if (findid) 01312 { 01313 result.prepare("DELETE FROM oldfind WHERE " 01314 "recordid = :RECORDID AND findid = :FINDID"); 01315 result.bindValue(":RECORDID", erecid); 01316 result.bindValue(":FINDID", findid); 01317 01318 if (!result.exec()) 01319 MythDB::DBError("forgetFindHistory", result); 01320 } 01321 01322 // The removal of an entry from oldrecorded may affect near-future 01323 // scheduling decisions, so recalculate 01324 ScheduledRecording::RescheduleCheck(*this, "ForgetHistory"); 01325 } 01326 01330 void RecordingInfo::SetDupHistory(void) 01331 { 01332 MSqlQuery result(MSqlQuery::InitCon()); 01333 01334 result.prepare("UPDATE oldrecorded SET duplicate = 1 " 01335 "WHERE future = 0 AND duplicate = 0 " 01336 "AND title = :TITLE AND " 01337 "((programid = '' AND subtitle = :SUBTITLE" 01338 " AND description = :DESC) OR " 01339 " (programid <> '' AND programid = :PROGRAMID) OR " 01340 " (findid <> 0 AND findid = :FINDID))"); 01341 result.bindValue(":TITLE", title); 01342 result.bindValue(":SUBTITLE", null_to_empty(subtitle)); 01343 result.bindValue(":DESC", null_to_empty(description)); 01344 result.bindValue(":PROGRAMID", null_to_empty(programid)); 01345 result.bindValue(":FINDID", findid); 01346 01347 if (!result.exec()) 01348 MythDB::DBError("setDupHistory", result); 01349 01350 ScheduledRecording::RescheduleCheck(*this, "SetHistory"); 01351 } 01352 01357 void RecordingInfo::SubstituteMatches(QString &str) 01358 { 01359 str.replace("%RECID%", QString::number(getRecordID())); 01360 str.replace("%PARENTID%", QString::number(parentid)); 01361 str.replace("%FINDID%", QString::number(findid)); 01362 str.replace("%RECSTATUS%", QString::number(recstatus)); 01363 str.replace("%RECTYPE%", QString::number(rectype)); 01364 str.replace("%REACTIVATE%", IsReactivated() ? "1" : "0"); 01365 01366 ProgramInfo::SubstituteMatches(str); 01367 } 01368 01369 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1