MythTV  0.26-pre
recordinginfo.cpp
Go to the documentation of this file.
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: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends