00001
00002 #include <sys/types.h>
00003 #include <unistd.h>
00004
00005
00006 #include <cstdlib>
00007
00008
00009 #include <iostream>
00010 #include <algorithm>
00011 using namespace std;
00012
00013
00014 #include <QFileInfo>
00015 #include <QRegExp>
00016 #include <QFile>
00017 #include <QMap>
00018
00019
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
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
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
00324 endts.setTime(QTime(endts.time().hour(),
00325 endts.time().minute() / kUnknownProgramLength
00326 * kUnknownProgramLength));
00327 endts = endts.addSecs(kUnknownProgramLength * 60);
00328
00329
00330 if (startts.secsTo(endts) < 60)
00331 endts = endts.addSecs(kUnknownProgramLength * 60);
00332
00333 recendts = endts;
00334
00335
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
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
00665
00666
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")
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
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
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
01224
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 ( 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
01259
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
01323
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