00001 #include <iostream>
00002 #include <qsocket.h>
00003 #include <qregexp.h>
00004 #include <qmap.h>
00005 #include <qlayout.h>
00006 #include <qlabel.h>
00007 #include <qapplication.h>
00008 #include <qfile.h>
00009 #include <qfileinfo.h>
00010
00011 #include <sys/types.h>
00012 #include <unistd.h>
00013 #include <stdlib.h>
00014
00015 #include "programinfo.h"
00016 #include "progdetails.h"
00017 #include "scheduledrecording.h"
00018 #include "util.h"
00019 #include "mythcontext.h"
00020 #include "dialogbox.h"
00021 #include "remoteutil.h"
00022 #include "jobqueue.h"
00023 #include "mythdbcon.h"
00024 #include "storagegroup.h"
00025 #include "previewgenerator.h"
00026
00027 #define LOC QString("ProgramInfo: ")
00028 #define LOC_ERR QString("ProgramInfo, Error: ")
00029
00030 using namespace std;
00031
00032
00033 static const uint kUnknownProgramLength = 30;
00034
00035 static bool insert_program(const ProgramInfo*,
00036 const ScheduledRecording*);
00037
00046 static QString StripHTMLTags(const QString& src)
00047 {
00048 QString dst(src);
00049
00050
00051 dst.replace( QRegExp("<br[^>]*>"), "\n" );
00052 dst.replace( QRegExp("<p[^>]*>"), "\n" );
00053 dst.replace( QRegExp("<li[^>]*>"), "\n- " );
00054
00055 dst.replace( QRegExp("<[^>]*>"), "" );
00056
00057 return dst;
00058 }
00059
00073 ProgramInfo::ProgramInfo(void) :
00074 regExpLock(false), regExpSeries("0000$")
00075 {
00076 spread = -1;
00077 startCol = -1;
00078 isVideo = false;
00079 lenMins = 0;
00080
00081 title = "";
00082 subtitle = "";
00083 description = "";
00084 category = "";
00085 chanstr = "";
00086 chansign = "";
00087 channame = "";
00088 chancommfree = 0;
00089 chanOutputFilters = "";
00090 year = "";
00091 stars = 0;
00092 availableStatus = asAvailable;
00093
00094 pathname = "";
00095 storagegroup = QString("Default");
00096 filesize = 0;
00097 hostname = "";
00098 programflags = 0;
00099 transcoder = 0;
00100 audioproperties = 0;
00101 videoproperties = 0;
00102 subtitleType = 0;
00103
00104 startts = mythCurrentDateTime();
00105 endts = startts;
00106 recstartts = startts;
00107 recendts = startts;
00108 originalAirDate = QDate::QDate (0, 1, 1);
00109 lastmodified = startts;
00110 lastInUseTime = startts.addSecs(-4 * 60 * 60);
00111
00112 recstatus = rsUnknown;
00113 oldrecstatus = rsUnknown;
00114 savedrecstatus = rsUnknown;
00115 recpriority2 = 0;
00116 reactivate = false;
00117 recordid = 0;
00118 parentid = 0;
00119 rectype = kNotRecording;
00120 dupin = kDupsInAll;
00121 dupmethod = kDupCheckSubDesc;
00122
00123 sourceid = 0;
00124 inputid = 0;
00125 cardid = 0;
00126 shareable = false;
00127 duplicate = false;
00128 schedulerid = "";
00129 findid = 0;
00130 recpriority = 0;
00131 recgroup = QString("Default");
00132 playgroup = QString("Default");
00133
00134 hasAirDate = false;
00135 repeat = false;
00136
00137 seriesid = "";
00138 programid = "";
00139 ignoreBookmark = false;
00140 catType = "";
00141
00142 sortTitle = "";
00143
00144 inUseForWhat = "";
00145
00146 record = NULL;
00147 }
00148
00152 ProgramInfo::ProgramInfo(const ProgramInfo &other) :
00153 record(NULL), regExpLock(false), regExpSeries("0000$")
00154 {
00155 clone(other);
00156 }
00157
00161 ProgramInfo &ProgramInfo::operator=(const ProgramInfo &other)
00162 {
00163 return clone(other);
00164 }
00165
00169 ProgramInfo &ProgramInfo::clone(const ProgramInfo &other)
00170 {
00171 if (record)
00172 {
00173 record->deleteLater();
00174 record = NULL;
00175 }
00176
00177 isVideo = other.isVideo;
00178 lenMins = other.lenMins;
00179
00180 title = QDeepCopy<QString>(other.title);
00181 subtitle = QDeepCopy<QString>(other.subtitle);
00182 description = QDeepCopy<QString>(other.description);
00183 category = QDeepCopy<QString>(other.category);
00184 chanid = QDeepCopy<QString>(other.chanid);
00185 chanstr = QDeepCopy<QString>(other.chanstr);
00186 chansign = QDeepCopy<QString>(other.chansign);
00187 channame = QDeepCopy<QString>(other.channame);
00188 chancommfree = other.chancommfree;
00189 chanOutputFilters = QDeepCopy<QString>(other.chanOutputFilters);
00190
00191 pathname = QDeepCopy<QString>(other.pathname);
00192 storagegroup = QDeepCopy<QString>(other.storagegroup);
00193 filesize = other.filesize;
00194 hostname = QDeepCopy<QString>(other.hostname);
00195
00196 startts = other.startts;
00197 endts = other.endts;
00198 recstartts = other.recstartts;
00199 recendts = other.recendts;
00200 lastmodified = other.lastmodified;
00201 spread = other.spread;
00202 startCol = other.startCol;
00203
00204 availableStatus = other.availableStatus;
00205
00206 recstatus = other.recstatus;
00207 oldrecstatus = other.oldrecstatus;
00208 savedrecstatus = other.savedrecstatus;
00209 recpriority2 = other.recpriority2;
00210 reactivate = other.reactivate;
00211 recordid = other.recordid;
00212 parentid = other.parentid;
00213 rectype = other.rectype;
00214 dupin = other.dupin;
00215 dupmethod = other.dupmethod;
00216
00217 sourceid = other.sourceid;
00218 inputid = other.inputid;
00219 cardid = other.cardid;
00220 shareable = other.shareable;
00221 duplicate = other.duplicate;
00222 schedulerid = QDeepCopy<QString>(other.schedulerid);
00223 findid = other.findid;
00224 recpriority = other.recpriority;
00225 recgroup = QDeepCopy<QString>(other.recgroup);
00226 playgroup = QDeepCopy<QString>(other.playgroup);
00227 programflags = other.programflags;
00228 transcoder = other.transcoder;
00229 audioproperties = other.audioproperties;
00230 videoproperties = other.videoproperties;
00231 subtitleType = other.subtitleType;
00232
00233 hasAirDate = other.hasAirDate;
00234 repeat = other.repeat;
00235
00236 seriesid = QDeepCopy<QString>(other.seriesid);
00237 programid = QDeepCopy<QString>(other.programid);
00238 catType = QDeepCopy<QString>(other.catType);
00239
00240 sortTitle = QDeepCopy<QString>(other.sortTitle);
00241
00242 originalAirDate = other.originalAirDate;
00243 stars = other.stars;
00244 year = QDeepCopy<QString>(other.year);
00245 ignoreBookmark = other.ignoreBookmark;
00246
00247 inUseForWhat = QDeepCopy<QString>(other.inUseForWhat);
00248 lastInUseTime = other.lastInUseTime;
00249 record = NULL;
00250
00251 return *this;
00252 }
00253
00257 ProgramInfo::~ProgramInfo()
00258 {
00259 if (record)
00260 {
00261 record->deleteLater();
00262 record = NULL;
00263 }
00264 }
00265
00269 QString ProgramInfo::MakeUniqueKey(void) const
00270 {
00271 return chanid + "_" + recstartts.toString(Qt::ISODate);
00272 }
00273
00274 #define INT_TO_LIST(x) sprintf(tmp, "%i", (x)); list << tmp;
00275
00276 #define DATETIME_TO_LIST(x) INT_TO_LIST((x).toTime_t())
00277
00278 #define LONGLONG_TO_LIST(x) INT_TO_LIST((int)((x) >> 32)) \
00279 INT_TO_LIST((int)((x) & 0xffffffffLL))
00280
00281 #define STR_TO_LIST(x) if ((x).isNull()) list << ""; else list << (x);
00282 #define DATE_TO_LIST(x) STR_TO_LIST((x).toString(Qt::ISODate))
00283
00284 #define FLOAT_TO_LIST(x) sprintf(tmp, "%f", (x)); list << tmp;
00285
00292 void ProgramInfo::ToStringList(QStringList &list) const
00293 {
00294 char tmp[64];
00295
00296 STR_TO_LIST(title)
00297 STR_TO_LIST(subtitle)
00298 STR_TO_LIST(description)
00299 STR_TO_LIST(category)
00300 STR_TO_LIST(chanid)
00301 STR_TO_LIST(chanstr)
00302 STR_TO_LIST(chansign)
00303 STR_TO_LIST(channame)
00304 STR_TO_LIST(pathname)
00305 LONGLONG_TO_LIST(filesize)
00306
00307 DATETIME_TO_LIST(startts)
00308 DATETIME_TO_LIST(endts)
00309 INT_TO_LIST(duplicate)
00310 INT_TO_LIST(shareable)
00311 INT_TO_LIST(findid);
00312 STR_TO_LIST(hostname)
00313 INT_TO_LIST(sourceid)
00314 INT_TO_LIST(cardid)
00315 INT_TO_LIST(inputid)
00316 INT_TO_LIST(recpriority)
00317 INT_TO_LIST(recstatus)
00318 INT_TO_LIST(recordid)
00319 INT_TO_LIST(rectype)
00320 INT_TO_LIST(dupin)
00321 INT_TO_LIST(dupmethod)
00322 DATETIME_TO_LIST(recstartts)
00323 DATETIME_TO_LIST(recendts)
00324 INT_TO_LIST(repeat)
00325 INT_TO_LIST(programflags)
00326 STR_TO_LIST((recgroup != "") ? recgroup : "Default")
00327 INT_TO_LIST(chancommfree)
00328 STR_TO_LIST(chanOutputFilters)
00329 STR_TO_LIST(seriesid)
00330 STR_TO_LIST(programid)
00331 DATETIME_TO_LIST(lastmodified)
00332 FLOAT_TO_LIST(stars)
00333 DATE_TO_LIST(originalAirDate)
00334 INT_TO_LIST(hasAirDate)
00335 STR_TO_LIST((playgroup != "") ? playgroup : "Default")
00336 INT_TO_LIST(recpriority2)
00337 INT_TO_LIST(parentid)
00338 STR_TO_LIST((storagegroup != "") ? storagegroup : "Default")
00339 INT_TO_LIST(audioproperties)
00340 INT_TO_LIST(videoproperties)
00341 INT_TO_LIST(subtitleType)
00342
00343 }
00344
00360 bool ProgramInfo::FromStringList(const QStringList &list, uint offset)
00361 {
00362 QStringList::const_iterator it = list.at(offset);
00363 return FromStringList(it, list.end());
00364 }
00365
00366 #define NEXT_STR() if (it == listend) \
00367 { \
00368 VERBOSE(VB_IMPORTANT, listerror); \
00369 return false; \
00370 } \
00371 ts = *it++; \
00372 if (ts.isNull()) \
00373 ts = "";
00374
00375 #define INT_FROM_LIST(x) NEXT_STR() (x) = atoi(ts.ascii());
00376 #define ENUM_FROM_LIST(x, y) NEXT_STR() (x) = (y)atoi(ts.ascii());
00377
00378 #define DATETIME_FROM_LIST(x) NEXT_STR() (x).setTime_t((uint)atoi(ts.ascii()));
00379 #define DATE_FROM_LIST(x) NEXT_STR() (x) = \
00380 ((ts.isEmpty()) || (ts == "0000-00-00")) ?\
00381 QDate() : QDate::fromString(ts, Qt::ISODate)
00382
00383 #define LONGLONG_FROM_LIST(x) INT_FROM_LIST(ti); NEXT_STR() \
00384 (x) = ((long long)(ti) << 32) | \
00385 ((long long)(atoi(ts.ascii())) & 0xffffffffLL);
00386
00387 #define STR_FROM_LIST(x) NEXT_STR() (x) = ts;
00388
00389 #define FLOAT_FROM_LIST(x) NEXT_STR() (x) = atof(ts.ascii());
00390
00402 bool ProgramInfo::FromStringList(QStringList::const_iterator &it,
00403 QStringList::const_iterator listend)
00404 {
00405 QString listerror = LOC + "FromStringList, not enough items in list.";
00406 QString ts;
00407 int ti;
00408
00409 STR_FROM_LIST(title)
00410 STR_FROM_LIST(subtitle)
00411 STR_FROM_LIST(description)
00412 STR_FROM_LIST(category)
00413 STR_FROM_LIST(chanid)
00414 STR_FROM_LIST(chanstr)
00415 STR_FROM_LIST(chansign)
00416 STR_FROM_LIST(channame)
00417 STR_FROM_LIST(pathname)
00418 LONGLONG_FROM_LIST(filesize)
00419
00420 DATETIME_FROM_LIST(startts)
00421 DATETIME_FROM_LIST(endts)
00422 NEXT_STR()
00423 INT_FROM_LIST(shareable)
00424 INT_FROM_LIST(findid)
00425 STR_FROM_LIST(hostname)
00426 INT_FROM_LIST(sourceid)
00427 INT_FROM_LIST(cardid)
00428 INT_FROM_LIST(inputid)
00429 INT_FROM_LIST(recpriority)
00430 ENUM_FROM_LIST(recstatus, RecStatusType)
00431 INT_FROM_LIST(recordid)
00432 ENUM_FROM_LIST(rectype, RecordingType)
00433 ENUM_FROM_LIST(dupin, RecordingDupInType)
00434 ENUM_FROM_LIST(dupmethod, RecordingDupMethodType)
00435 DATETIME_FROM_LIST(recstartts)
00436 DATETIME_FROM_LIST(recendts)
00437 INT_FROM_LIST(repeat)
00438 INT_FROM_LIST(programflags)
00439 STR_FROM_LIST(recgroup)
00440 INT_FROM_LIST(chancommfree)
00441 STR_FROM_LIST(chanOutputFilters)
00442 STR_FROM_LIST(seriesid)
00443 STR_FROM_LIST(programid)
00444 DATETIME_FROM_LIST(lastmodified)
00445 FLOAT_FROM_LIST(stars)
00446 DATE_FROM_LIST(originalAirDate);
00447 INT_FROM_LIST(hasAirDate);
00448 STR_FROM_LIST(playgroup)
00449 INT_FROM_LIST(recpriority2)
00450 INT_FROM_LIST(parentid)
00451 STR_FROM_LIST(storagegroup)
00452 INT_FROM_LIST(audioproperties)
00453 INT_FROM_LIST(videoproperties)
00454 INT_FROM_LIST(subtitleType)
00455
00456 return true;
00457 }
00458
00463 void ProgramInfo::ToMap(QMap<QString, QString> &progMap,
00464 bool showrerecord) const
00465 {
00466 QString timeFormat = gContext->GetSetting("TimeFormat", "h:mm AP");
00467 QString dateFormat = gContext->GetSetting("DateFormat", "ddd MMMM d");
00468 QString fullDateFormat = dateFormat;
00469 if (fullDateFormat.find(QRegExp("yyyy")) < 0)
00470 fullDateFormat += " yyyy";
00471 QString shortDateFormat = gContext->GetSetting("ShortDateFormat", "M/d");
00472 QString channelFormat =
00473 gContext->GetSetting("ChannelFormat", "<num> <sign>");
00474 QString longChannelFormat =
00475 gContext->GetSetting("LongChannelFormat", "<num> <name>");
00476
00477 QDateTime timeNow = QDateTime::currentDateTime();
00478
00479 QString length;
00480 int hours, minutes, seconds;
00481
00482 progMap["title"] = title;
00483 progMap["subtitle"] = subtitle;
00484 progMap["description"] = StripHTMLTags(description);
00485 progMap["category"] = category;
00486 progMap["callsign"] = chansign;
00487 progMap["commfree"] = chancommfree;
00488 progMap["outputfilters"] = chanOutputFilters;
00489 if (isVideo)
00490 {
00491 progMap["starttime"] = "";
00492 progMap["startdate"] = "";
00493 progMap["endtime"] = "";
00494 progMap["enddate"] = "";
00495 progMap["recstarttime"] = "";
00496 progMap["recstartdate"] = "";
00497 progMap["recendtime"] = "";
00498 progMap["recenddate"] = "";
00499
00500 if (startts.date().year() == 1895)
00501 {
00502 progMap["startdate"] = "?";
00503 progMap["recstartdate"] = "?";
00504 }
00505 else
00506 {
00507 progMap["startdate"] = startts.toString("yyyy");
00508 progMap["recstartdate"] = startts.toString("yyyy");
00509 }
00510 }
00511 else
00512 {
00513 progMap["starttime"] = startts.toString(timeFormat);
00514 progMap["startdate"] = startts.toString(shortDateFormat);
00515 progMap["endtime"] = endts.toString(timeFormat);
00516 progMap["enddate"] = endts.toString(shortDateFormat);
00517 progMap["recstarttime"] = recstartts.toString(timeFormat);
00518 progMap["recstartdate"] = recstartts.toString(shortDateFormat);
00519 progMap["recendtime"] = recendts.toString(timeFormat);
00520 progMap["recenddate"] = recendts.toString(shortDateFormat);
00521 }
00522
00523 progMap["lastmodifiedtime"] = lastmodified.toString(timeFormat);
00524 progMap["lastmodifieddate"] = lastmodified.toString(dateFormat);
00525 progMap["lastmodified"] = lastmodified.toString(dateFormat) + " " +
00526 lastmodified.toString(timeFormat);
00527
00528 progMap["channum"] = chanstr;
00529 progMap["chanid"] = chanid;
00530 progMap["channel"] = ChannelText(channelFormat);
00531 progMap["longchannel"] = ChannelText(longChannelFormat);
00532 progMap["iconpath"] = "";
00533
00534 QString tmpSize;
00535
00536 tmpSize.sprintf("%0.2f ", filesize / 1024.0 / 1024.0 / 1024.0);
00537 tmpSize += QObject::tr("GB", "GigaBytes");
00538 progMap["filesize_str"] = tmpSize;
00539
00540 progMap["filesize"] = longLongToString(filesize);
00541
00542 if (isVideo)
00543 {
00544 minutes = lenMins;
00545 seconds = lenMins * 60;
00546 }
00547 else
00548 {
00549 seconds = recstartts.secsTo(recendts);
00550 minutes = seconds / 60;
00551 }
00552
00553 progMap["lenmins"] = QString("%1 %2").
00554 arg(minutes).arg(QObject::tr("minutes"));
00555 hours = minutes / 60;
00556 minutes = minutes % 60;
00557 length.sprintf("%d:%02d", hours, minutes);
00558 progMap["lentime"] = length;
00559
00560 progMap["rec_type"] = RecTypeChar();
00561 progMap["rec_str"] = RecTypeText();
00562 if (rectype != kNotRecording)
00563 {
00564 QString tmp_rec;
00565 if (recendts > timeNow && recstatus <= rsWillRecord ||
00566 recstatus == rsConflict || recstatus == rsLaterShowing)
00567 {
00568 tmp_rec += QString().sprintf(" %+d", recpriority);
00569 if (recpriority2)
00570 tmp_rec += QString().sprintf("/%+d", recpriority2);
00571 tmp_rec += " ";
00572 }
00573 else
00574 {
00575 tmp_rec += " -- ";
00576 }
00577 if (showrerecord && recstatus == rsRecorded && !duplicate)
00578 tmp_rec += QObject::tr("Re-Record");
00579 else
00580 tmp_rec += RecStatusText();
00581 progMap["rec_str"] += tmp_rec;
00582 }
00583 progMap["recordingstatus"] = progMap["rec_str"];
00584 progMap["type"] = progMap["rec_str"];
00585
00586 progMap["recpriority"] = recpriority;
00587 progMap["recpriority2"] = recpriority2;
00588 progMap["recgroup"] = recgroup;
00589 progMap["playgroup"] = playgroup;
00590 progMap["programflags"] = programflags;
00591
00592 progMap["audioproperties"] = audioproperties;
00593 progMap["videoproperties"] = videoproperties;
00594 progMap["subtitleType"] = subtitleType;
00595
00596 progMap["timedate"] = recstartts.date().toString(dateFormat) + ", " +
00597 recstartts.time().toString(timeFormat) + " - " +
00598 recendts.time().toString(timeFormat);
00599
00600 progMap["shorttimedate"] =
00601 recstartts.date().toString(shortDateFormat) + ", " +
00602 recstartts.time().toString(timeFormat) + " - " +
00603 recendts.time().toString(timeFormat);
00604
00605 progMap["time"] = timeNow.time().toString(timeFormat);
00606
00607 MSqlQuery query(MSqlQuery::InitCon());
00608
00609 query.prepare("SELECT icon FROM channel WHERE chanid = :CHANID ;");
00610 query.bindValue(":CHANID", chanid);
00611
00612 if (query.exec() && query.isActive() && query.size() > 0)
00613 if (query.next())
00614 progMap["iconpath"] = query.value(0).toString();
00615
00616 progMap["RECSTATUS"] = RecStatusText();
00617
00618 if (repeat)
00619 {
00620 progMap["REPEAT"] = QString("(%1) ").arg(QObject::tr("Repeat"));
00621 progMap["LONGREPEAT"] = progMap["REPEAT"];
00622 if (hasAirDate)
00623 progMap["LONGREPEAT"] = QString("(%1 %2) ")
00624 .arg(QObject::tr("Repeat"))
00625 .arg(originalAirDate.toString(fullDateFormat));
00626 }
00627 else
00628 {
00629 progMap["REPEAT"] = "";
00630 progMap["LONGREPEAT"] = "";
00631 }
00632
00633 progMap["seriesid"] = seriesid;
00634 progMap["programid"] = programid;
00635 progMap["catType"] = catType;
00636
00637 progMap["year"] = year;
00638
00639 if (stars)
00640 {
00641 QString str = QObject::tr("stars");
00642 if (stars > 0 && stars <= 0.25)
00643 str = QObject::tr("star");
00644
00645 if (year != "")
00646 progMap["stars"] = QString("(%1, %2 %3) ")
00647 .arg(year).arg(4.0 * stars).arg(str);
00648 else
00649 progMap["stars"] = QString("(%1 %2) ").arg(4.0 * stars).arg(str);
00650 }
00651 else
00652 progMap["stars"] = "";
00653
00654 if (hasAirDate)
00655 {
00656 progMap["originalairdate"] = originalAirDate.toString(dateFormat);
00657 progMap["shortoriginalairdate"] =
00658 originalAirDate.toString(shortDateFormat);
00659 }
00660 else
00661 {
00662 progMap["originalairdate"] = "";
00663 progMap["shortoriginalairdate"] = "";
00664 }
00665 }
00666
00670 int ProgramInfo::CalculateLength(void) const
00671 {
00672 if (isVideo)
00673 return lenMins * 60;
00674 else
00675 return startts.secsTo(endts);
00676 }
00677
00682 int ProgramInfo::SecsTillStart(void) const
00683 {
00684 return QDateTime::currentDateTime().secsTo(startts);
00685 }
00686
00698 ProgramInfo *ProgramInfo::GetProgramAtDateTime(const QString &channel,
00699 const QDateTime &dtime,
00700 bool genUnknown,
00701 int clampHoursMax)
00702 {
00703 ProgramList schedList;
00704 ProgramList progList;
00705
00706 MSqlBindings bindings;
00707 QString querystr = "WHERE program.chanid = :CHANID "
00708 " AND program.starttime < :STARTTS "
00709 " AND program.endtime > :STARTTS ";
00710 bindings[":CHANID"] = channel;
00711 bindings[":STARTTS"] = dtime.toString("yyyy-MM-ddThh:mm:50");
00712
00713 schedList.FromScheduler();
00714 progList.FromProgram(querystr, bindings, schedList);
00715
00716 if (!progList.isEmpty())
00717 {
00718 ProgramInfo *pginfo = progList.take(0);
00719
00720 if (clampHoursMax > 0)
00721 {
00722 if (dtime.secsTo(pginfo->endts) > clampHoursMax * 3600)
00723 {
00724 pginfo->endts = dtime.addSecs(clampHoursMax * 3600);
00725 pginfo->recendts = pginfo->endts;
00726 }
00727 }
00728
00729 return pginfo;
00730 }
00731 ProgramInfo *p = new ProgramInfo;
00732
00733 MSqlQuery query(MSqlQuery::InitCon());
00734 query.prepare("SELECT chanid, channum, callsign, name, "
00735 "commmethod, outputfilters "
00736 "FROM channel "
00737 "WHERE chanid = :CHANID ;");
00738 query.bindValue(":CHANID", channel);
00739
00740 if (!query.exec() || !query.isActive())
00741 {
00742 MythContext::DBError(LOC + "GetProgramAtDateTime",
00743 query);
00744 return p;
00745 }
00746
00747 if (!query.next())
00748 return p;
00749
00750 p->chanid = query.value(0).toString();
00751 p->startts = dtime;
00752 p->endts = dtime;
00753 p->recstartts = p->startts;
00754 p->recendts = p->endts;
00755 p->lastmodified = p->startts;
00756 p->title = gContext->GetSetting("UnknownTitle");
00757 p->subtitle = "";
00758 p->description = "";
00759 p->category = "";
00760 p->chanstr = query.value(1).toString();
00761 p->chansign = QString::fromUtf8(query.value(2).toString());
00762 p->channame = QString::fromUtf8(query.value(3).toString());
00763 p->repeat = 0;
00764 p->chancommfree = (query.value(4).toInt() == -2);
00765 p->chanOutputFilters = query.value(5).toString();
00766 p->seriesid = "";
00767 p->programid = "";
00768 p->year = "";
00769 p->stars = 0.0f;
00770
00771 if (!genUnknown)
00772 return p;
00773
00774
00775 p->endts.setTime(QTime(p->endts.time().hour(),
00776 p->endts.time().minute() / kUnknownProgramLength
00777 * kUnknownProgramLength));
00778 p->endts = p->endts.addSecs(kUnknownProgramLength * 60);
00779
00780
00781 if (p->startts.secsTo(p->endts) < 60)
00782 p->endts = p->endts.addSecs(kUnknownProgramLength * 60);
00783
00784 p->recendts = p->endts;
00785
00786
00787 QDateTime nextstart = p->startts;
00788 querystr = "WHERE program.chanid = :CHANID AND "
00789 " program.starttime > :STARTTS "
00790 "GROUP BY program.starttime ORDER BY program.starttime LIMIT 1 ";
00791 bindings[":CHANID"] = channel;
00792 bindings[":STARTTS"] = dtime.toString("yyyy-MM-ddThh:mm:50");
00793
00794 progList.FromProgram(querystr, bindings, schedList);
00795
00796 if (!progList.isEmpty())
00797 nextstart = progList.at(0)->startts;
00798
00799 if (nextstart > p->startts && nextstart < p->recendts)
00800 {
00801 p->recendts = p->endts = nextstart;
00802 }
00803
00804 return p;
00805 }
00806
00807 QString ProgramInfo::toString(void) const
00808 {
00809 QString str("");
00810 str += LOC + "channame(" + channame + ") startts(" +
00811 startts.toString() + ") endts(" + endts.toString() + ")\n";
00812 str += " recstartts(" + recstartts.toString() +
00813 ") recendts(" + recendts.toString() + ")\n";
00814 str += " title(" + title + ")";
00815 return str;
00816 }
00817
00822 ProgramInfo *ProgramInfo::GetProgramFromBasename(const QString filename)
00823 {
00824 QFileInfo inf(filename);
00825
00826 MSqlQuery query(MSqlQuery::InitCon());
00827
00828 query.prepare("SELECT chanid, starttime FROM recorded "
00829 "WHERE basename = :BASENAME;");
00830 query.bindValue(":BASENAME", inf.fileName());
00831
00832 if (query.exec() && query.isActive() && query.size() > 0)
00833 {
00834 query.next();
00835
00836 return GetProgramFromRecorded(query.value(0).toString(),
00837 query.value(1).toDateTime());
00838 }
00839
00840 return NULL;
00841 }
00842
00847 ProgramInfo *ProgramInfo::GetProgramFromRecorded(const QString &channel,
00848 const QDateTime &dtime)
00849 {
00850 return GetProgramFromRecorded(channel, dtime.toString(Qt::ISODate));
00851 }
00852
00857 ProgramInfo *ProgramInfo::GetProgramFromRecorded(const QString &channel,
00858 const QString &starttime)
00859 {
00860 MSqlQuery query(MSqlQuery::InitCon());
00861 query.prepare("SELECT recorded.chanid,starttime,endtime,title, "
00862 "subtitle,description,channel.channum, "
00863 "channel.callsign,channel.name,channel.commmethod, "
00864 "channel.outputfilters,seriesid,programid,filesize, "
00865 "lastmodified,stars,previouslyshown,originalairdate, "
00866 "hostname,recordid,transcoder,playgroup, "
00867 "recorded.recpriority,progstart,progend,basename,recgroup, "
00868 "storagegroup "
00869 "FROM recorded "
00870 "LEFT JOIN channel "
00871 "ON recorded.chanid = channel.chanid "
00872 "WHERE recorded.chanid = :CHANNEL "
00873 "AND starttime = :STARTTIME ;");
00874 query.bindValue(":CHANNEL", channel);
00875 query.bindValue(":STARTTIME", starttime);
00876
00877 if (query.exec() && query.isActive() && query.size() > 0)
00878 {
00879 query.next();
00880
00881 ProgramInfo *proginfo = new ProgramInfo;
00882 proginfo->chanid = query.value(0).toString();
00883 proginfo->startts = query.value(23).toDateTime();
00884 proginfo->endts = query.value(24).toDateTime();
00885 proginfo->recstartts = query.value(1).toDateTime();
00886 proginfo->recendts = query.value(2).toDateTime();
00887 proginfo->title = QString::fromUtf8(query.value(3).toString());
00888 proginfo->subtitle = QString::fromUtf8(query.value(4).toString());
00889 proginfo->description = QString::fromUtf8(query.value(5).toString());
00890
00891 proginfo->chanstr = query.value(6).toString();
00892 proginfo->chansign = QString::fromUtf8(query.value(7).toString());
00893 proginfo->channame = QString::fromUtf8(query.value(8).toString());
00894 proginfo->chancommfree = (query.value(9).toInt() == -2);
00895 proginfo->chanOutputFilters = query.value(10).toString();
00896 proginfo->seriesid = query.value(11).toString();
00897 proginfo->programid = query.value(12).toString();
00898 proginfo->filesize = stringToLongLong(query.value(13).toString());
00899
00900 proginfo->lastmodified =
00901 QDateTime::fromString(query.value(14).toString(),
00902 Qt::ISODate);
00903
00904 proginfo->stars = query.value(15).toDouble();
00905 proginfo->repeat = query.value(16).toInt();
00906
00907 if (query.value(17).isNull() || query.value(17).toString().isEmpty())
00908 {
00909 proginfo->originalAirDate = QDate::QDate (0, 1, 1);
00910 proginfo->hasAirDate = false;
00911 }
00912 else
00913 {
00914 proginfo->originalAirDate =
00915 QDate::fromString(query.value(17).toString(),Qt::ISODate);
00916
00917 if (proginfo->originalAirDate > QDate(1940, 1, 1))
00918 proginfo->hasAirDate = true;
00919 else
00920 proginfo->hasAirDate = false;
00921 }
00922 proginfo->hostname = query.value(18).toString();
00923 proginfo->recstatus = rsRecorded;
00924 proginfo->recordid = query.value(19).toInt();
00925 proginfo->transcoder = query.value(20).toInt();
00926
00927 proginfo->spread = -1;
00928
00929 proginfo->programflags = proginfo->getProgramFlags();
00930
00931 proginfo->getProgramProperties();
00932
00933 proginfo->recgroup = QString::fromUtf8(query.value(26).toString());
00934 proginfo->storagegroup = QString::fromUtf8(query.value(27).toString());
00935 proginfo->playgroup = QString::fromUtf8(query.value(21).toString());
00936 proginfo->recpriority = query.value(22).toInt();
00937
00938 proginfo->pathname = QString::fromUtf8(query.value(25).toString());
00939
00940 return proginfo;
00941 }
00942
00943 return NULL;
00944 }
00945
00949 bool ProgramInfo::IsFindApplicable(void) const
00950 {
00951 return rectype == kFindDailyRecord ||
00952 rectype == kFindWeeklyRecord;
00953 }
00954
00959 int ProgramInfo::IsProgramRecurring(void) const
00960 {
00961 QDateTime dtime = startts;
00962
00963 int weekday = dtime.date().dayOfWeek();
00964 if (weekday < 6)
00965 {
00966
00967 int daysadd = 1;
00968 if (weekday == 5)
00969 daysadd = 3;
00970
00971 QDateTime checktime = dtime.addDays(daysadd);
00972
00973 ProgramInfo *nextday = GetProgramAtDateTime(chanid, checktime);
00974
00975 if (NULL == nextday)
00976 return -1;
00977
00978 if (nextday && nextday->title == title)
00979 {
00980 delete nextday;
00981 return 1;
00982 }
00983 if (nextday)
00984 delete nextday;
00985 }
00986
00987 QDateTime checktime = dtime.addDays(7);
00988 ProgramInfo *nextweek = GetProgramAtDateTime(chanid, checktime);
00989
00990 if (NULL == nextweek)
00991 return -1;
00992
00993 if (nextweek && nextweek->title == title)
00994 {
00995 delete nextweek;
00996 return 2;
00997 }
00998
00999 if (nextweek)
01000 delete nextweek;
01001 return 0;
01002 }
01003
01009 RecordingType ProgramInfo::GetProgramRecordingStatus(void)
01010 {
01011 if (record == NULL)
01012 {
01013 record = new ScheduledRecording();
01014 record->loadByProgram(this);
01015 }
01016
01017 return record->getRecordingType();
01018 }
01019
01025 QString ProgramInfo::GetProgramRecordingProfile(void)
01026 {
01027 if (record == NULL)
01028 {
01029 record = new ScheduledRecording();
01030 record->loadByProgram(this);
01031 }
01032
01033 return record->getProfileName();
01034 }
01035
01040 int ProgramInfo::GetAutoRunJobs(void) const
01041 {
01042 if (record == NULL)
01043 {
01044 record = new ScheduledRecording();
01045 record->loadByProgram(this);
01046 }
01047
01048 return record->GetAutoRunJobs();
01049 }
01050
01055 int ProgramInfo::GetChannelRecPriority(const QString &channel)
01056 {
01057 MSqlQuery query(MSqlQuery::InitCon());
01058 query.prepare("SELECT recpriority FROM channel WHERE chanid = :CHANID ;");
01059 query.bindValue(":CHANID", channel);
01060
01061 if (query.exec() && query.isActive() && query.size() > 0)
01062 {
01063 query.next();
01064 return query.value(0).toInt();
01065 }
01066
01067 return 0;
01068 }
01069
01073 int ProgramInfo::GetRecordingTypeRecPriority(RecordingType type)
01074 {
01075 switch (type)
01076 {
01077 case kSingleRecord:
01078 return gContext->GetNumSetting("SingleRecordRecPriority", 1);
01079 case kTimeslotRecord:
01080 return gContext->GetNumSetting("TimeslotRecordRecPriority", 0);
01081 case kWeekslotRecord:
01082 return gContext->GetNumSetting("WeekslotRecordRecPriority", 0);
01083 case kChannelRecord:
01084 return gContext->GetNumSetting("ChannelRecordRecPriority", 0);
01085 case kAllRecord:
01086 return gContext->GetNumSetting("AllRecordRecPriority", 0);
01087 case kFindOneRecord:
01088 case kFindDailyRecord:
01089 case kFindWeeklyRecord:
01090 return gContext->GetNumSetting("FindOneRecordRecPriority", -1);
01091 case kOverrideRecord:
01092 case kDontRecord:
01093 return gContext->GetNumSetting("OverrideRecordRecPriority", 0);
01094 default:
01095 return 0;
01096 }
01097 }
01098
01102 void ProgramInfo::ApplyRecordRecID(void)
01103 {
01104 MSqlQuery query(MSqlQuery::InitCon());
01105
01106 if (getRecordID() < 0)
01107 {
01108 VERBOSE(VB_IMPORTANT,
01109 "ProgInfo Error: ApplyRecordRecID(void) needs recordid");
01110 return;
01111 }
01112
01113 query.prepare("UPDATE recorded "
01114 "SET recordid = :RECID "
01115 "WHERE chanid = :CHANID AND starttime = :START");
01116
01117 if (rectype == kOverrideRecord && parentid > 0)
01118 query.bindValue(":RECID", parentid);
01119 else
01120 query.bindValue(":RECID", getRecordID());
01121 query.bindValue(":CHANID", chanid);
01122 query.bindValue(":START", recstartts);
01123
01124 if (!query.exec())
01125 MythContext::DBError(LOC + "RecordID update", query);
01126 }
01127
01133
01134 void ProgramInfo::ApplyRecordStateChange(RecordingType newstate)
01135 {
01136 GetProgramRecordingStatus();
01137 if (newstate == kOverrideRecord || newstate == kDontRecord)
01138 record->makeOverride();
01139 record->setRecordingType(newstate);
01140 record->save();
01141 }
01142
01148 void ProgramInfo::ApplyRecordRecPriorityChange(int newrecpriority)
01149 {
01150 GetProgramRecordingStatus();
01151 record->setRecPriority(newrecpriority);
01152 record->save();
01153 }
01154
01160 void ProgramInfo::ApplyRecordRecGroupChange(const QString &newrecgroup)
01161 {
01162 MSqlQuery query(MSqlQuery::InitCon());
01163
01164 query.prepare("UPDATE recorded"
01165 " SET recgroup = :RECGROUP"
01166 " WHERE chanid = :CHANID"
01167 " AND starttime = :START ;");
01168 query.bindValue(":RECGROUP", newrecgroup.utf8());
01169 query.bindValue(":START", recstartts);
01170 query.bindValue(":CHANID", chanid);
01171
01172 if (!query.exec())
01173 MythContext::DBError("RecGroup update", query);
01174
01175 recgroup = newrecgroup;
01176 }
01177
01183 void ProgramInfo::ApplyRecordPlayGroupChange(const QString &newplaygroup)
01184 {
01185 MSqlQuery query(MSqlQuery::InitCon());
01186
01187 query.prepare("UPDATE recorded"
01188 " SET playgroup = :PLAYGROUP"
01189 " WHERE chanid = :CHANID"
01190 " AND starttime = :START ;");
01191 query.bindValue(":PLAYGROUP", newplaygroup.utf8());
01192 query.bindValue(":START", recstartts);
01193 query.bindValue(":CHANID", chanid);
01194
01195 if (!query.exec())
01196 MythContext::DBError("PlayGroup update", query);
01197
01198 playgroup = newplaygroup;
01199 }
01200
01207 void ProgramInfo::ApplyRecordRecTitleChange(const QString &newTitle, const QString &newSubtitle)
01208 {
01209 MSqlQuery query(MSqlQuery::InitCon());
01210
01211 query.prepare("UPDATE recorded"
01212 " SET title = :TITLE, subtitle = :SUBTITLE"
01213 " WHERE chanid = :CHANID"
01214 " AND starttime = :START ;");
01215 query.bindValue(":TITLE", newTitle.utf8());
01216 query.bindValue(":SUBTITLE", newSubtitle.utf8());
01217 query.bindValue(":CHANID", chanid);
01218 query.bindValue(":START", recstartts.toString("yyyyMMddhhmmss"));
01219
01220 if (!query.exec())
01221 MythContext::DBError("RecTitle update", query);
01222
01223 title = newTitle;
01224 subtitle = newSubtitle;
01225 }
01226
01227
01228
01229
01230
01231 void ProgramInfo::ApplyTranscoderProfileChange(QString profile)
01232 {
01233 if(profile == "Default")
01234 return;
01235
01236 MSqlQuery query(MSqlQuery::InitCon());
01237
01238 if(profile == "Autodetect")
01239 {
01240 query.prepare("UPDATE recorded "
01241 "SET transcoder = 0 "
01242 "WHERE chanid = :CHANID "
01243 "AND starttime = :START");
01244 query.bindValue(":CHANID", chanid);
01245 query.bindValue(":START", recstartts);
01246
01247 if (!query.exec())
01248 MythContext::DBError(LOC + "unable to update transcoder "
01249 "in recorded table", query);
01250 }
01251 else
01252 {
01253 MSqlQuery pidquery(MSqlQuery::InitCon());
01254 pidquery.prepare("SELECT r.id "
01255 "FROM recordingprofiles r, profilegroups p "
01256 "WHERE r.profilegroup = p.id "
01257 "AND p.name = 'Transcoders' "
01258 "AND r.name = :PROFILE ");
01259 pidquery.bindValue(":PROFILE", profile);
01260
01261 if (pidquery.exec() && pidquery.isActive() && pidquery.next())
01262 {
01263 query.prepare("UPDATE recorded "
01264 "SET transcoder = :TRANSCODER "
01265 "WHERE chanid = :CHANID "
01266 "AND starttime = :START");
01267 query.bindValue(":TRANSCODER", pidquery.value(0).toInt());
01268 query.bindValue(":CHANID", chanid);
01269 query.bindValue(":START", recstartts);
01270
01271 if (!query.exec())
01272 MythContext::DBError(LOC + "unable to update transcoder "
01273 "in recorded table", query);
01274 }
01275 else
01276 MythContext::DBError("PlaybackBox: unable to query transcoder "
01277 "profile ID", query);
01278 }
01279 }
01280
01299 void ProgramInfo::ToggleRecord(void)
01300 {
01301 RecordingType curType = GetProgramRecordingStatus();
01302
01303 switch (curType)
01304 {
01305 case kNotRecording:
01306 ApplyRecordStateChange(kSingleRecord);
01307 break;
01308 case kSingleRecord:
01309 ApplyRecordStateChange(kFindOneRecord);
01310 break;
01311 case kFindOneRecord:
01312 ApplyRecordStateChange(kAllRecord);
01313 break;
01314 case kAllRecord:
01315 ApplyRecordStateChange(kSingleRecord);
01316 break;
01317
01318 case kOverrideRecord:
01319 ApplyRecordStateChange(kDontRecord);
01320 break;
01321 case kDontRecord:
01322 ApplyRecordStateChange(kOverrideRecord);
01323 break;
01324
01325 default:
01326 ApplyRecordStateChange(kAllRecord);
01327 break;
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364 }
01365 }
01366
01370 ScheduledRecording* ProgramInfo::GetScheduledRecording(void)
01371 {
01372 GetProgramRecordingStatus();
01373 return record;
01374 }
01375
01379 int ProgramInfo::getRecordID(void)
01380 {
01381 GetProgramRecordingStatus();
01382 recordid = record->getRecordID();
01383 return recordid;
01384 }
01385
01390 bool ProgramInfo::IsSameProgram(const ProgramInfo& other) const
01391 {
01392 if (rectype == kFindOneRecord)
01393 return recordid == other.recordid;
01394
01395 if (findid && findid == other.findid &&
01396 (recordid == other.recordid || recordid == other.parentid))
01397 return true;
01398
01399 if (title.lower() != other.title.lower())
01400 return false;
01401
01402 if (findid && findid == other.findid)
01403 return true;
01404
01405 if (dupmethod & kDupCheckNone)
01406 return false;
01407
01408 if (catType == "series")
01409 {
01410 QMutexLocker locker(®ExpLock);
01411 if (programid.contains(regExpSeries))
01412 return false;
01413 }
01414
01415 if (!programid.isEmpty() && !other.programid.isEmpty())
01416 return programid == other.programid;
01417
01418 if ((dupmethod & kDupCheckSub) &&
01419 ((subtitle.isEmpty()) ||
01420 (subtitle.lower() != other.subtitle.lower())))
01421 return false;
01422
01423 if ((dupmethod & kDupCheckDesc) &&
01424 ((description.isEmpty()) ||
01425 (description.lower() != other.description.lower())))
01426 return false;
01427
01428 if ((dupmethod & kDupCheckSubThenDesc) &&
01429 ((subtitle.isEmpty() && other.subtitle.isEmpty() &&
01430 description.lower() != other.description.lower()) ||
01431 (subtitle.lower() != other.subtitle.lower()) ||
01432 (description.isEmpty() && subtitle.isEmpty())))
01433 return false;
01434
01435 return true;
01436 }
01437
01443 bool ProgramInfo::IsSameTimeslot(const ProgramInfo& other) const
01444 {
01445 if (title != other.title)
01446 return false;
01447 if (startts == other.startts && endts == other.endts &&
01448 (chanid == other.chanid ||
01449 (chansign != "" && chansign == other.chansign)))
01450 return true;
01451
01452 return false;
01453 }
01454
01461 bool ProgramInfo::IsSameProgramTimeslot(const ProgramInfo &other) const
01462 {
01463 if (title != other.title)
01464 return false;
01465 if ((chanid == other.chanid ||
01466 (chansign != "" && chansign == other.chansign)) &&
01467 startts < other.endts &&
01468 endts > other.startts)
01469 return true;
01470
01471 return false;
01472 }
01473
01478 QString ProgramInfo::CreateRecordBasename(const QString &ext) const
01479 {
01480 QString starts = recstartts.toString("yyyyMMddhhmmss");
01481
01482 QString retval = QString("%1_%2.%3").arg(chanid)
01483 .arg(starts).arg(ext);
01484
01485 return retval;
01486 }
01487
01491 bool ProgramInfo::SetRecordBasename(QString basename)
01492 {
01493 MSqlQuery query(MSqlQuery::InitCon());
01494 query.prepare("UPDATE recorded "
01495 "SET basename = :BASENAME "
01496 "WHERE chanid = :CHANID AND "
01497 " starttime = :STARTTIME;");
01498 query.bindValue(":CHANID", chanid);
01499 query.bindValue(":STARTTIME", recstartts);
01500 query.bindValue(":BASENAME", basename);
01501
01502 if (!query.exec() || !query.isActive())
01503 {
01504 MythContext::DBError("SetRecordBasename", query);
01505 return false;
01506 }
01507
01508 return true;
01509 }
01510
01515 QString ProgramInfo::GetRecordBasename(bool fromDB) const
01516 {
01517 QString retval = "";
01518
01519 if (!fromDB && !pathname.isEmpty())
01520 retval = pathname.section('/', -1);
01521 else
01522 {
01523 MSqlQuery query(MSqlQuery::InitCon());
01524 query.prepare("SELECT basename FROM recorded "
01525 "WHERE chanid = :CHANID AND "
01526 " starttime = :STARTTIME;");
01527 query.bindValue(":CHANID", chanid);
01528 query.bindValue(":STARTTIME", recstartts);
01529
01530 if (!query.exec() || !query.isActive())
01531 MythContext::DBError("GetRecordBasename", query);
01532 else if (query.size() < 1)
01533 VERBOSE(VB_IMPORTANT, QString("GetRecordBasename found no entry "
01534 "for %1 @ %2")
01535 .arg(chanid).arg(recstartts.toString(Qt::ISODate)));
01536 else
01537 {
01538 query.next();
01539 retval = query.value(0).toString();
01540 }
01541 }
01542
01543 return retval;
01544 }
01545
01551 QString ProgramInfo::GetPlaybackURL(bool checkMaster, bool forceCheckLocal)
01552 {
01553 QString tmpURL;
01554 QString basename = GetRecordBasename(true);
01555
01556 if (basename == "")
01557 return "";
01558
01559 bool alwaysStream = gContext->GetNumSetting("AlwaysStreamFiles", 0);
01560
01561 if ((!alwaysStream) ||
01562 (forceCheckLocal) ||
01563 (hostname == gContext->GetHostName()))
01564 {
01565
01566 StorageGroup sgroup(storagegroup);
01567 tmpURL = sgroup.FindRecordingFile(basename);
01568
01569 if (tmpURL != "")
01570 {
01571 VERBOSE(VB_FILE, LOC +
01572 QString("GetPlaybackURL: File is local: '%1'").arg(tmpURL));
01573 return tmpURL;
01574 }
01575 else if (hostname == gContext->GetHostName())
01576 {
01577 VERBOSE(VB_IMPORTANT, LOC_ERR +
01578 QString("GetPlaybackURL: '%1' should be local, but it can "
01579 "not be found.").arg(basename));
01580 return QString("/GetPlaybackURL/UNABLE/TO/FIND/LOCAL/FILE/ON/%1/%2")
01581 .arg(hostname).arg(basename);
01582 }
01583 }
01584
01585
01586 if ((checkMaster) &&
01587 (gContext->GetNumSetting("MasterBackendOverride", 0)) &&
01588 (RemoteCheckFile(this, false)))
01589 {
01590 tmpURL = QString("myth://") +
01591 gContext->GetSetting("MasterServerIP") + ":" +
01592 gContext->GetSetting("MasterServerPort") + "/" + basename;
01593 VERBOSE(VB_FILE, LOC +
01594 QString("GetPlaybackURL: Found @ '%1'").arg(tmpURL));
01595 return tmpURL;
01596 }
01597
01598
01599 tmpURL = QString("myth://") +
01600 gContext->GetSettingOnHost("BackendServerIP", hostname) + ":" +
01601 gContext->GetSettingOnHost("BackendServerPort", hostname) + "/" +
01602 basename;
01603
01604 VERBOSE(VB_FILE, LOC + QString("GetPlaybackURL: Using default of: '%1'")
01605 .arg(tmpURL));
01606
01607 return tmpURL;
01608 }
01609
01618 void ProgramInfo::StartedRecording(QString ext)
01619 {
01620 QString dirname = pathname;
01621
01622 if (!record)
01623 {
01624 record = new ScheduledRecording();
01625 record->loadByProgram(this);
01626 }
01627
01628 hostname = gContext->GetHostName();
01629 pathname = CreateRecordBasename(ext);
01630
01631 int count = 0;
01632 while (!insert_program(this, record) && count < 50)
01633 {
01634 recstartts = recstartts.addSecs(1);
01635 pathname = CreateRecordBasename(ext);
01636 count++;
01637 }
01638
01639 if (count >= 50)
01640 {
01641 VERBOSE(VB_IMPORTANT, "Couldn't insert program");
01642 return;
01643 }
01644
01645 pathname = dirname + "/" + pathname;
01646
01647 VERBOSE(VB_FILE, QString(LOC + "StartedRecording: Recording to '%1'")
01648 .arg(pathname));
01649
01650
01651 MSqlQuery query(MSqlQuery::InitCon());
01652
01653 query.prepare("DELETE FROM recordedseek WHERE chanid = :CHANID"
01654 " AND starttime = :START;");
01655 query.bindValue(":CHANID", chanid);
01656 query.bindValue(":START", recstartts);
01657
01658 if (!query.exec() || !query.isActive())
01659 MythContext::DBError("Clear seek info on record", query);
01660
01661 query.prepare("DELETE FROM recordedmarkup WHERE chanid = :CHANID"
01662 " AND starttime = :START;");
01663 query.bindValue(":CHANID", chanid);
01664 query.bindValue(":START", recstartts);
01665
01666 if (!query.exec() || !query.isActive())
01667 MythContext::DBError("Clear markup on record", query);
01668
01669 query.prepare("REPLACE INTO recordedcredits"
01670 " SELECT * FROM credits"
01671 " WHERE chanid = :CHANID AND starttime = :START;");
01672 query.bindValue(":CHANID", chanid);
01673 query.bindValue(":START", startts);
01674 if (!query.exec() || !query.isActive())
01675 MythContext::DBError("Copy program credits on record", query);
01676
01677 query.prepare("REPLACE INTO recordedprogram"
01678 " SELECT * from program"
01679 " WHERE chanid = :CHANID AND starttime = :START"
01680 " AND title = :TITLE;");
01681 query.bindValue(":CHANID", chanid);
01682 query.bindValue(":START", startts);
01683 query.bindValue(":TITLE", title.utf8());
01684 if (!query.exec() || !query.isActive())
01685 MythContext::DBError("Copy program data on record", query);
01686
01687 query.prepare("REPLACE INTO recordedrating"
01688 " SELECT * from programrating"
01689 " WHERE chanid = :CHANID AND starttime = :START;");
01690 query.bindValue(":CHANID", chanid);
01691 query.bindValue(":START", startts);
01692 if (!query.exec() || !query.isActive())
01693 MythContext::DBError("Copy program ratings on record", query);
01694 }
01695
01696 static bool insert_program(const ProgramInfo *pg,
01697 const ScheduledRecording *schd)
01698 {
01699 MSqlQuery query(MSqlQuery::InitCon());
01700
01701 query.prepare("LOCK TABLES recorded WRITE");
01702 if (!query.exec())
01703 {
01704 MythContext::DBError("insert_program -- lock", query);
01705 return false;
01706 }
01707
01708 query.prepare(
01709 "SELECT recordid "
01710 " FROM recorded "
01711 " WHERE chanid = :CHANID AND "
01712 " starttime = :STARTS");
01713 query.bindValue(":CHANID", pg->chanid);
01714 query.bindValue(":STARTS", pg->recstartts);
01715
01716 if (!query.exec() || query.size())
01717 {
01718 if (!query.isActive())
01719 MythContext::DBError("insert_program -- select", query);
01720 else
01721 VERBOSE(VB_IMPORTANT, "recording already exists...");
01722
01723 query.prepare("UNLOCK TABLES");
01724 query.exec();
01725 return false;
01726 }
01727
01728 query.prepare(
01729 "INSERT INTO recorded "
01730 " (chanid, starttime, endtime, title, "
01731 " subtitle, description, hostname, category, "
01732 " recgroup, autoexpire, recordid, seriesid, "
01733 " programid, stars, previouslyshown, originalairdate, "
01734 " findid, transcoder, playgroup, recpriority, "
01735 " basename, progstart, progend, profile, "
01736 " duplicate, storagegroup) "
01737 "VALUES"
01738 " (:CHANID, :STARTS, :ENDS, :TITLE, "
01739 " :SUBTITLE, :DESC, :HOSTNAME, :CATEGORY, "
01740 " :RECGROUP, :AUTOEXP, :RECORDID, :SERIESID, "
01741 " :PROGRAMID,:STARS, :REPEAT, :ORIGAIRDATE, "
01742 " :FINDID, :TRANSCODER, :PLAYGROUP, :RECPRIORITY, "
01743 " :BASENAME, :PROGSTART, :PROGEND, :PROFILE, "
01744 " 0, :STORGROUP) "
01745 );
01746
01747 if (pg->rectype == kOverrideRecord)
01748 query.bindValue(":RECORDID", pg->parentid);
01749 else
01750 query.bindValue(":RECORDID", pg->recordid);
01751
01752 if (pg->hasAirDate)
01753 query.bindValue(":ORIGAIRDATE", pg->originalAirDate);
01754 else
01755 query.bindValue(":ORIGAIRDATE", "0000-00-00");
01756
01757 query.bindValue(":CHANID", pg->chanid);
01758 query.bindValue(":STARTS", pg->recstartts);
01759 query.bindValue(":ENDS", pg->recendts);
01760 query.bindValue(":TITLE", pg->title.utf8());
01761 query.bindValue(":SUBTITLE", pg->subtitle.utf8());
01762 query.bindValue(":DESC", pg->description.utf8());
01763 query.bindValue(":HOSTNAME", pg->hostname);
01764 query.bindValue(":CATEGORY", pg->category.utf8());
01765 query.bindValue(":RECGROUP", pg->recgroup.utf8());
01766 query.bindValue(":AUTOEXP", schd->GetAutoExpire());
01767 query.bindValue(":SERIESID", pg->seriesid.utf8());
01768 query.bindValue(":PROGRAMID", pg->programid.utf8());
01769 query.bindValue(":FINDID", pg->findid);
01770 query.bindValue(":STARS", pg->stars);
01771 query.bindValue(":REPEAT", pg->repeat);
01772 query.bindValue(":TRANSCODER", schd->GetTranscoder());
01773 query.bindValue(":PLAYGROUP", pg->playgroup.utf8());
01774 query.bindValue(":RECPRIORITY", schd->getRecPriority());
01775 query.bindValue(":BASENAME", pg->pathname);
01776 query.bindValue(":STORGROUP", pg->storagegroup.utf8());
01777 query.bindValue(":PROGSTART", pg->startts);
01778 query.bindValue(":PROGEND", pg->endts);
01779 query.bindValue(":PROFILE", schd->getProfileName());
01780
01781 bool ok = query.exec() && (query.numRowsAffected() > 0);
01782 bool active = query.isActive();
01783
01784 query.prepare("UNLOCK TABLES");
01785 query.exec();
01786
01787 if (!ok && !active)
01788 MythContext::DBError("insert_program -- insert", query);
01789
01790 else if (pg->recordid > 0)
01791 {
01792 query.prepare("UPDATE channel SET last_record = NOW() "
01793 "WHERE chanid = :CHANID");
01794 query.bindValue(":CHANID", pg->chanid);
01795 query.exec();
01796
01797 query.prepare("UPDATE record SET last_record = NOW() "
01798 "WHERE recordid = :RECORDID");
01799 query.bindValue(":RECORDID", pg->recordid);
01800 query.exec();
01801
01802 if (pg->rectype == kOverrideRecord && pg->parentid > 0)
01803 {
01804 query.prepare("UPDATE record SET last_record = NOW() "
01805 "WHERE recordid = :PARENTID");
01806 query.bindValue(":PARENTID", pg->parentid);
01807 query.exec();
01808 }
01809 }
01810
01811 return ok;
01812 }
01813
01819 void ProgramInfo::FinishedRecording(bool prematurestop)
01820 {
01821 MSqlQuery query(MSqlQuery::InitCon());
01822 query.prepare("UPDATE recorded SET endtime = :ENDTIME, "
01823 " duplicate = 1 "
01824 "WHERE chanid = :CHANID AND "
01825 " starttime = :STARTTIME ");
01826 query.bindValue(":ENDTIME", recendts);
01827 query.bindValue(":CHANID", chanid);
01828 query.bindValue(":STARTTIME", recstartts);
01829
01830 query.exec();
01831
01832 if (!query.isActive())
01833 MythContext::DBError("FinishedRecording update", query);
01834
01835 GetProgramRecordingStatus();
01836 if (!prematurestop)
01837 record->doneRecording(*this);
01838 }
01839
01844 void ProgramInfo::UpdateRecordingEnd(void)
01845 {
01846 MSqlQuery query(MSqlQuery::InitCon());
01847 query.prepare("UPDATE recorded SET endtime = :ENDTIME "
01848 "WHERE chanid = :CHANID AND "
01849 " starttime = :STARTTIME ");
01850 query.bindValue(":ENDTIME", recendts);
01851
01852 query.bindValue(":CHANID", chanid);
01853 query.bindValue(":STARTTIME", recstartts);
01854
01855 query.exec();
01856
01857 if (!query.isActive())
01858 MythContext::DBError("FinishedRecording update", query);
01859 }
01860
01861
01865 void ProgramInfo::SetFilesize(long long fsize)
01866 {
01867 filesize = fsize;
01868
01869 MSqlQuery query(MSqlQuery::InitCon());
01870 query.prepare("UPDATE recorded SET filesize = :FILESIZE"
01871 " WHERE chanid = :CHANID"
01872 " AND starttime = :STARTTIME ;");
01873 query.bindValue(":FILESIZE", longLongToString(fsize));
01874 query.bindValue(":CHANID", chanid);
01875 query.bindValue(":STARTTIME", recstartts);
01876
01877 if (!query.exec() || !query.isActive())
01878 MythContext::DBError("File size update",
01879 query);
01880 }
01881
01885 long long ProgramInfo::GetFilesize(void)
01886 {
01887 MSqlQuery query(MSqlQuery::InitCon());
01888
01889 query.prepare("SELECT filesize FROM recorded"
01890 " WHERE chanid = :CHANID"
01891 " AND starttime = :STARTTIME ;");
01892 query.bindValue(":CHANID", chanid);
01893 query.bindValue(":STARTTIME", recstartts);
01894
01895 if (query.exec() && query.isActive() && query.size() > 0)
01896 {
01897 query.next();
01898 filesize = stringToLongLong(query.value(0).toString());
01899 }
01900 else
01901 filesize = 0;
01902
01903 return filesize;
01904 }
01905
01909 int ProgramInfo::GetMplexID(void) const
01910 {
01911 int ret = 0;
01912 if (chanid)
01913 {
01914 MSqlQuery query(MSqlQuery::InitCon());
01915
01916 query.prepare("SELECT mplexid FROM channel "
01917 "WHERE chanid = :CHANID");
01918 query.bindValue(":CHANID", chanid);
01919
01920 if (!query.exec())
01921 MythContext::DBError("GetMplexID", query);
01922 else if (query.next())
01923 ret = query.value(0).toUInt();
01924
01925
01926 ret = (32767 == ret) ? 0 : ret;
01927 }
01928
01929 return ret;
01930 }
01931
01936 void ProgramInfo::SetBookmark(long long pos) const
01937 {
01938 ClearMarkupMap(MARK_BOOKMARK);
01939 frm_dir_map_t bookmarkmap;
01940 bookmarkmap[pos] = MARK_BOOKMARK;
01941 SetMarkupMap(bookmarkmap);
01942
01943 if (!isVideo)
01944 {
01945 MSqlQuery query(MSqlQuery::InitCon());
01946
01947
01948
01949 query.prepare("UPDATE recorded"
01950 " SET bookmark = :BOOKMARKFLAG"
01951 " WHERE chanid = :CHANID"
01952 " AND starttime = :STARTTIME ;");
01953
01954 query.bindValue(":BOOKMARKFLAG", pos == 0 ? 0 : 1);
01955 query.bindValue(":CHANID", chanid);
01956 query.bindValue(":STARTTIME", recstartts);
01957
01958 if (!query.exec() || !query.isActive())
01959 MythContext::DBError("bookmark flag update", query);
01960 }
01961 }
01962
01970 long long ProgramInfo::GetBookmark(void) const
01971 {
01972 QMap<long long, int>::Iterator i;
01973 long long pos = 0;
01974
01975 if (ignoreBookmark)
01976 return pos;
01977
01978 frm_dir_map_t bookmarkmap;
01979 GetMarkupMap(bookmarkmap, MARK_BOOKMARK);
01980
01981 if (bookmarkmap.isEmpty())
01982 return pos;
01983
01984 i = bookmarkmap.begin();
01985 pos = i.key();
01986
01987 return pos;
01988 }
01989
01995 QStringList ProgramInfo::GetDVDBookmark(QString serialid, bool delbookmark) const
01996 {
01997 QStringList fields = QStringList();
01998 MSqlQuery query(MSqlQuery::InitCon());
01999
02000 if (!ignoreBookmark)
02001 {
02002 query.prepare(" SELECT title, framenum, audionum, subtitlenum "
02003 " FROM dvdbookmark "
02004 " WHERE serialid = ? ");
02005 query.addBindValue(serialid.utf8());
02006
02007 if (query.exec() && query.isActive() && query.size() > 0)
02008 {
02009 query.next();
02010 for(int i = 0; i < 4; i++)
02011 fields.append(query.value(i).toString());
02012 }
02013 }
02014
02015 if (delbookmark)
02016 {
02017 int days = -(gContext->GetNumSetting("DVDBookmarkDays", 10));
02018 QDateTime removedate = mythCurrentDateTime().addDays(days);
02019 query.prepare(" DELETE from dvdbookmark "
02020 " WHERE timestamp < ? ");
02021 query.addBindValue(removedate.toString(Qt::ISODate));
02022
02023 if (!query.exec() || !query.isActive())
02024 MythContext::DBError("GetDVDBookmark deleting old entries", query);
02025 }
02026
02027 return fields;
02028 }
02029
02030 void ProgramInfo::SetDVDBookmark(QStringList fields) const
02031 {
02032 QStringList::Iterator it = fields.begin();
02033 MSqlQuery query(MSqlQuery::InitCon());
02034
02035 QString serialid = *(it);
02036 QString name = *(++it);
02037 QString title = *(++it);
02038 QString audionum = *(++it);
02039 QString subtitlenum = *(++it);
02040 QString frame = *(++it);
02041
02042 query.prepare("INSERT IGNORE INTO dvdbookmark "
02043 " (serialid, name)"
02044 " VALUES ( :SERIALID, :NAME );");
02045 query.bindValue(":SERIALID", serialid.utf8());
02046 query.bindValue(":NAME", name.utf8());
02047
02048 if (!query.exec() || !query.isActive())
02049 MythContext::DBError("SetDVDBookmark inserting", query);
02050
02051 query.prepare(" UPDATE dvdbookmark "
02052 " SET title = ? , "
02053 " audionum = ? , "
02054 " subtitlenum = ? , "
02055 " framenum = ? , "
02056 " timestamp = NOW() "
02057 " WHERE serialid = ? ;");
02058 query.addBindValue(title.utf8());
02059 query.addBindValue(audionum.utf8());
02060 query.addBindValue(subtitlenum.utf8());
02061 query.addBindValue(frame.utf8());
02062 query.addBindValue(serialid.utf8());
02063
02064 if (!query.exec() || !query.isActive())
02065 MythContext::DBError("SetDVDBookmark updating", query);
02066 }
02071 void ProgramInfo::SetWatchedFlag(bool watchedFlag) const
02072 {
02073
02074 if (!isVideo)
02075 {
02076 MSqlQuery query(MSqlQuery::InitCon());
02077
02078 query.prepare("UPDATE recorded"
02079 " SET watched = :WATCHEDFLAG"
02080 " WHERE chanid = :CHANID"
02081 " AND starttime = :STARTTIME ;");
02082 query.bindValue(":CHANID", chanid);
02083 query.bindValue(":STARTTIME", recstartts);
02084
02085 if (watchedFlag)
02086 query.bindValue(":WATCHEDFLAG", 1);
02087 else
02088 query.bindValue(":WATCHEDFLAG", 0);
02089
02090 if (!query.exec() || !query.isActive())
02091 MythContext::DBError("Set watched flag", query);
02092 else
02093 UpdateLastDelete(watchedFlag);
02094 }
02095 }
02096
02102 bool ProgramInfo::IsEditing(void) const
02103 {
02104 MSqlQuery query(MSqlQuery::InitCon());
02105
02106 query.prepare("SELECT editing FROM recorded"
02107 " WHERE chanid = :CHANID"
02108 " AND starttime = :STARTTIME ;");
02109 query.bindValue(":CHANID", chanid);
02110 query.bindValue(":STARTTIME", recstartts);
02111
02112 if (query.exec() && query.isActive() && query.size() > 0)
02113 {
02114 query.next();
02115 return query.value(0).toBool();
02116 }
02117
02118 return false;
02119 }
02120
02125 void ProgramInfo::SetEditing(bool edit) const
02126 {
02127 MSqlQuery query(MSqlQuery::InitCon());
02128
02129 query.prepare("UPDATE recorded"
02130 " SET editing = :EDIT"
02131 " WHERE chanid = :CHANID"
02132 " AND starttime = :STARTTIME ;");
02133 query.bindValue(":EDIT", edit);
02134 query.bindValue(":CHANID", chanid);
02135 query.bindValue(":STARTTIME", recstartts);
02136
02137 if (!query.exec() || !query.isActive())
02138 MythContext::DBError("Edit status update",
02139 query);
02140 }
02141
02146 void ProgramInfo::SetDeleteFlag(bool deleteFlag) const
02147 {
02148 MSqlQuery query(MSqlQuery::InitCon());
02149
02150 query.prepare("UPDATE recorded"
02151 " SET deletepending = :DELETEFLAG"
02152 " WHERE chanid = :CHANID"
02153 " AND starttime = :STARTTIME ;");
02154 query.bindValue(":CHANID", chanid);
02155 query.bindValue(":STARTTIME", recstartts);
02156
02157 if (deleteFlag)
02158 query.bindValue(":DELETEFLAG", 1);
02159 else
02160 query.bindValue(":DELETEFLAG", 0);
02161
02162 if (!query.exec() || !query.isActive())
02163 MythContext::DBError("Set delete flag", query);
02164 }
02165
02170 bool ProgramInfo::IsCommFlagged(void) const
02171 {
02172 MSqlQuery query(MSqlQuery::InitCon());
02173
02174 query.prepare("SELECT commflagged FROM recorded"
02175 " WHERE chanid = :CHANID"
02176 " AND starttime = :STARTTIME ;");
02177 query.bindValue(":CHANID", chanid);
02178 query.bindValue(":STARTTIME", recstartts);
02179
02180 if (query.exec() && query.isActive() && query.size() > 0)
02181 {
02182 query.next();
02183 return query.value(0).toBool();
02184 }
02185
02186 return false;
02187 }
02188
02194 bool ProgramInfo::IsInUse(QString &byWho) const
02195 {
02196 if (isVideo)
02197 return false;
02198
02199 QDateTime oneHourAgo = QDateTime::currentDateTime().addSecs(-61 * 60);
02200 MSqlQuery query(MSqlQuery::InitCon());
02201
02202 query.prepare("SELECT hostname, recusage FROM inuseprograms "
02203 " WHERE chanid = :CHANID"
02204 " AND starttime = :STARTTIME "
02205 " AND lastupdatetime > :ONEHOURAGO ;");
02206 query.bindValue(":CHANID", chanid);
02207 query.bindValue(":STARTTIME", recstartts);
02208 query.bindValue(":ONEHOURAGO", oneHourAgo);
02209
02210 byWho = "";
02211 if (query.exec() && query.isActive() && query.size() > 0)
02212 {
02213 QString usageStr, recusage;
02214 while(query.next())
02215 {
02216 usageStr = QObject::tr("Unknown");
02217 recusage = query.value(1).toString();
02218
02219 if (recusage == "player")
02220 usageStr = QObject::tr("Playing");
02221 else if (recusage == "recorder")
02222 usageStr = QObject::tr("Recording");
02223 else if (recusage == "flagger")
02224 usageStr = QObject::tr("Commercial Flagging");
02225 else if (recusage == "transcoder")
02226 usageStr = QObject::tr("Transcoding");
02227 else if (recusage == "PIP player")
02228 usageStr = QObject::tr("PIP");
02229
02230 byWho += query.value(0).toString() + " (" + usageStr + ")\n";
02231 }
02232
02233 return true;
02234 }
02235
02236 return false;
02237 }
02238
02242 int ProgramInfo::GetTranscodedStatus(void) const
02243 {
02244 MSqlQuery query(MSqlQuery::InitCon());
02245
02246 query.prepare("SELECT transcoded FROM recorded"
02247 " WHERE chanid = :CHANID"
02248 " AND starttime = :STARTTIME ;");
02249 query.bindValue(":CHANID", chanid);
02250 query.bindValue(":STARTTIME", recstartts);
02251
02252 if (query.exec() && query.isActive() && query.size() > 0)
02253 {
02254 query.next();
02255 return query.value(0).toInt();
02256 }
02257
02258 return false;
02259 }
02260
02265 void ProgramInfo::SetTranscoded(int transFlag) const
02266 {
02267 MSqlQuery query(MSqlQuery::InitCon());
02268
02269 query.prepare("UPDATE recorded"
02270 " SET transcoded = :FLAG"
02271 " WHERE chanid = :CHANID"
02272 " AND starttime = :STARTTIME ;");
02273 query.bindValue(":FLAG", transFlag);
02274 query.bindValue(":CHANID", chanid);
02275 query.bindValue(":STARTTIME", recstartts);
02276
02277 if(!query.exec() || !query.isActive())
02278 MythContext::DBError("Transcoded status update",
02279 query);
02280 }
02281
02286 void ProgramInfo::SetCommFlagged(int flag) const
02287 {
02288 MSqlQuery query(MSqlQuery::InitCon());
02289
02290 query.prepare("UPDATE recorded"
02291 " SET commflagged = :FLAG"
02292 " WHERE chanid = :CHANID"
02293 " AND starttime = :STARTTIME ;");
02294 query.bindValue(":FLAG", flag);
02295 query.bindValue(":CHANID", chanid);
02296 query.bindValue(":STARTTIME", recstartts);
02297
02298 if (!query.exec() || !query.isActive())
02299 MythContext::DBError("Commercial Flagged status update",
02300 query);
02301 }
02302
02307 void ProgramInfo::SetPreserveEpisode(bool preserveEpisode) const
02308 {
02309 MSqlQuery query(MSqlQuery::InitCon());
02310
02311 query.prepare("UPDATE recorded"
02312 " SET preserve = :PRESERVE"
02313 " WHERE chanid = :CHANID"
02314 " AND starttime = :STARTTIME ;");
02315 query.bindValue(":PRESERVE", preserveEpisode);
02316 query.bindValue(":CHANID", chanid);
02317 query.bindValue(":STARTTIME", recstartts);
02318
02319 if (!query.exec() || !query.isActive())
02320 MythContext::DBError("PreserveEpisode update", query);
02321 else
02322 UpdateLastDelete(false);
02323 }
02324
02329 void ProgramInfo::SetAutoExpire(int autoExpire, bool updateDelete) const
02330 {
02331 MSqlQuery query(MSqlQuery::InitCon());
02332
02333 query.prepare("UPDATE recorded"
02334 " SET autoexpire = :AUTOEXPIRE"
02335 " WHERE chanid = :CHANID"
02336 " AND starttime = :STARTTIME ;");
02337 query.bindValue(":AUTOEXPIRE", autoExpire);
02338 query.bindValue(":CHANID", chanid);
02339 query.bindValue(":STARTTIME", recstartts);
02340
02341 if (!query.exec() || !query.isActive())
02342 MythContext::DBError("AutoExpire update", query);
02343 else if (updateDelete)
02344 UpdateLastDelete(true);
02345 }
02346
02351 void ProgramInfo::UpdateLastDelete(bool setTime) const
02352 {
02353 MSqlQuery query(MSqlQuery::InitCon());
02354
02355 if (setTime)
02356 {
02357 QDateTime timeNow = QDateTime::currentDateTime();
02358 int delay = recstartts.secsTo(timeNow) / 3600;
02359
02360 if (delay > 200)
02361 delay = 200;
02362 else if (delay < 1)
02363 delay = 1;
02364
02365 query.prepare("UPDATE record SET last_delete = :TIME, "
02366 "avg_delay = (avg_delay * 3 + :DELAY) / 4 "
02367 "WHERE recordid = :RECORDID");
02368 query.bindValue(":TIME", timeNow);
02369 query.bindValue(":DELAY", delay);
02370 query.bindValue(":RECORDID", recordid);
02371 }
02372 else
02373 query.prepare("UPDATE record SET last_delete = '0000-00-00T00:00:00' "
02374 "WHERE recordid = :RECORDID");
02375 query.bindValue(":RECORDID", recordid);
02376
02377 if (!query.exec() || !query.isActive())
02378 MythContext::DBError("Update last_delete", query);
02379 }
02380
02384 int ProgramInfo::GetAutoExpireFromRecorded(void) const
02385 {
02386 MSqlQuery query(MSqlQuery::InitCon());
02387
02388 query.prepare("SELECT autoexpire FROM recorded"
02389 " WHERE chanid = :CHANID"
02390 " AND starttime = :STARTTIME ;");
02391 query.bindValue(":CHANID", chanid);
02392 query.bindValue(":STARTTIME", recstartts);
02393
02394 if (query.exec() && query.isActive() && query.size() > 0)
02395 {
02396 query.next();
02397 return query.value(0).toInt();
02398 }
02399
02400 return false;
02401 }
02402
02406 bool ProgramInfo::GetPreserveEpisodeFromRecorded(void) const
02407 {
02408 MSqlQuery query(MSqlQuery::InitCon());
02409
02410 query.prepare("SELECT preserve FROM recorded"
02411 " WHERE chanid = :CHANID"
02412 " AND starttime = :STARTTIME ;");
02413 query.bindValue(":CHANID", chanid);
02414 query.bindValue(":STARTTIME", recstartts);
02415
02416 if (query.exec() && query.isActive() && query.size() > 0)
02417 {
02418 query.next();
02419 return query.value(0).toBool();
02420 }
02421
02422 return false;
02423 }
02424
02428 bool ProgramInfo::UsesMaxEpisodes(void) const
02429 {
02430 MSqlQuery query(MSqlQuery::InitCon());
02431
02432 query.prepare("SELECT maxepisodes FROM record WHERE "
02433 "recordid = :RECID ;");
02434 query.bindValue(":RECID", recordid);
02435
02436 if (query.exec() && query.isActive() && query.size() > 0)
02437 {
02438 query.next();
02439 return query.value(0).toInt();
02440 }
02441
02442 return false;
02443 }
02444
02445 void ProgramInfo::GetCutList(frm_dir_map_t &delMap) const
02446 {
02447 GetMarkupMap(delMap, MARK_CUT_START);
02448 GetMarkupMap(delMap, MARK_CUT_END, true);
02449 }
02450
02451 void ProgramInfo::SetCutList(frm_dir_map_t &delMap) const
02452 {
02453 ClearMarkupMap(MARK_CUT_START);
02454 ClearMarkupMap(MARK_CUT_END);
02455 SetMarkupMap(delMap);
02456
02457 if (!isVideo)
02458 {
02459 MSqlQuery query(MSqlQuery::InitCon());
02460
02461
02462 query.prepare("UPDATE recorded"
02463 " SET cutlist = :CUTLIST"
02464 " WHERE chanid = :CHANID"
02465 " AND starttime = :STARTTIME ;");
02466
02467 query.bindValue(":CUTLIST", delMap.isEmpty() ? 0 : 1);
02468 query.bindValue(":CHANID", chanid);
02469 query.bindValue(":STARTTIME", recstartts);
02470
02471 if (!query.exec() || !query.isActive())
02472 MythContext::DBError("cutlist flag update", query);
02473 }
02474 }
02475
02476 void ProgramInfo::SetCommBreakList(frm_dir_map_t &frames) const
02477 {
02478 ClearMarkupMap(MARK_COMM_START);
02479 ClearMarkupMap(MARK_COMM_END);
02480 SetMarkupMap(frames);
02481 }
02482
02483 void ProgramInfo::GetCommBreakList(frm_dir_map_t &frames) const
02484 {
02485 GetMarkupMap(frames, MARK_COMM_START);
02486 GetMarkupMap(frames, MARK_COMM_END, true);
02487 }
02488
02489 void ProgramInfo::ClearMarkupMap(int type, long long min_frame,
02490 long long max_frame) const
02491 {
02492 MSqlQuery query(MSqlQuery::InitCon());
02493 QString comp = "";
02494
02495 if (min_frame >= 0)
02496 {
02497 char tempc[128];
02498 sprintf(tempc, " AND mark >= %lld ", min_frame);
02499 comp += tempc;
02500 }
02501
02502 if (max_frame >= 0)
02503 {
02504 char tempc[128];
02505 sprintf(tempc, " AND mark <= %lld ", max_frame);
02506 comp += tempc;
02507 }
02508
02509 if (type != -100)
02510 comp += QString(" AND type = :TYPE ");
02511
02512 if (isVideo)
02513 {
02514 query.prepare("DELETE FROM filemarkup"
02515 " WHERE filename = :PATH "
02516 + comp + ";");
02517 query.bindValue(":PATH", pathname);
02518 }
02519 else
02520 {
02521 query.prepare("DELETE FROM recordedmarkup"
02522 " WHERE chanid = :CHANID"
02523 " AND STARTTIME = :STARTTIME"
02524 + comp + ";");
02525 query.bindValue(":CHANID", chanid);
02526 query.bindValue(":STARTTIME", recstartts);
02527 }
02528 query.bindValue(":TYPE", type);
02529
02530 if (!query.exec() || !query.isActive())
02531 MythContext::DBError("ClearMarkupMap deleting", query);
02532 }
02533
02534 void ProgramInfo::SetMarkupMap(frm_dir_map_t &marks,
02535 int type, long long min_frame,
02536 long long max_frame) const
02537 {
02538 QMap<long long, int>::Iterator i;
02539 MSqlQuery query(MSqlQuery::InitCon());
02540
02541 if (!isVideo)
02542 {
02543
02544 query.prepare("SELECT starttime FROM recorded"
02545 " WHERE chanid = :CHANID"
02546 " AND starttime = :STARTTIME ;");
02547 query.bindValue(":CHANID", chanid);
02548 query.bindValue(":STARTTIME", recstartts);
02549
02550 if (!query.exec() || !query.isActive())
02551 MythContext::DBError("SetMarkupMap checking record table", query);
02552
02553 if (query.size() < 1 || !query.next())
02554 return;
02555 }
02556
02557 for (i = marks.begin(); i != marks.end(); ++i)
02558 {
02559 long long frame = i.key();
02560 int mark_type;
02561 QString querystr;
02562
02563 if ((min_frame >= 0) && (frame < min_frame))
02564 continue;
02565
02566 if ((max_frame >= 0) && (frame > max_frame))
02567 continue;
02568
02569 if (type != -100)
02570 mark_type = type;
02571 else
02572 mark_type = i.data();
02573
02574 if (isVideo)
02575 {
02576 query.prepare("INSERT INTO filemarkup (filename, mark, type)"
02577 " VALUES ( :PATH , :MARK , :TYPE );");
02578 query.bindValue(":PATH", pathname);
02579 }
02580 else
02581 {
02582 query.prepare("INSERT INTO recordedmarkup"
02583 " (chanid, starttime, mark, type)"
02584 " VALUES ( :CHANID , :STARTTIME , :MARK , :TYPE );");
02585 query.bindValue(":CHANID", chanid);
02586 query.bindValue(":STARTTIME", recstartts);
02587 }
02588 query.bindValue(":MARK", frame);
02589 query.bindValue(":TYPE", mark_type);
02590
02591 if (!query.exec() || !query.isActive())
02592 MythContext::DBError("SetMarkupMap inserting", query);
02593 }
02594 }
02595
02596 void ProgramInfo::GetMarkupMap(frm_dir_map_t &marks,
02597 int type, bool mergeIntoMap) const
02598 {
02599 if (!mergeIntoMap)
02600 marks.clear();
02601
02602 MSqlQuery query(MSqlQuery::InitCon());
02603
02604 if (isVideo)
02605 {
02606 query.prepare("SELECT mark, type FROM filemarkup"
02607 " WHERE filename = :PATH"
02608 " AND type = :TYPE"
02609 " ORDER BY mark;");
02610 query.bindValue(":PATH", pathname);
02611 }
02612 else
02613 {
02614 query.prepare("SELECT mark, type FROM recordedmarkup"
02615 " WHERE chanid = :CHANID"
02616 " AND starttime = :STARTTIME"
02617 " AND type = :TYPE"
02618 " ORDER BY mark;");
02619 query.bindValue(":CHANID", chanid);
02620 query.bindValue(":STARTTIME", recstartts);
02621 }
02622 query.bindValue(":TYPE", type);
02623
02624 if (query.exec() && query.isActive() && query.size() > 0)
02625 {
02626 while(query.next())
02627 marks[query.value(0).toLongLong()] = query.value(1).toInt();
02628 }
02629 }
02630
02631 bool ProgramInfo::CheckMarkupFlag(int type) const
02632 {
02633 QMap<long long, int> flagMap;
02634
02635 GetMarkupMap(flagMap, type);
02636
02637 return(flagMap.contains(0));
02638 }
02639
02640 void ProgramInfo::SetMarkupFlag(int type, bool flag) const
02641 {
02642 ClearMarkupMap(type);
02643
02644 if (flag)
02645 {
02646 QMap<long long, int> flagMap;
02647
02648 flagMap[0] = type;
02649 SetMarkupMap(flagMap, type);
02650 }
02651 }
02652
02653 void ProgramInfo::GetPositionMap(frm_pos_map_t &posMap,
02654 int type) const
02655 {
02656 posMap.clear();
02657 MSqlQuery query(MSqlQuery::InitCon());
02658
02659 if (isVideo)
02660 {
02661 query.prepare("SELECT mark, offset FROM filemarkup"
02662 " WHERE filename = :PATH"
02663 " AND type = :TYPE ;");
02664 query.bindValue(":PATH", pathname);
02665 }
02666 else
02667 {
02668 query.prepare("SELECT mark, offset FROM recordedseek"
02669 " WHERE chanid = :CHANID"
02670 " AND starttime = :STARTTIME"
02671 " AND type = :TYPE ;");
02672 query.bindValue(":CHANID", chanid);
02673 query.bindValue(":STARTTIME", recstartts);
02674 }
02675 query.bindValue(":TYPE", type);
02676
02677 if (query.exec() && query.isActive() && query.size() > 0)
02678 {
02679 while (query.next())
02680 posMap[query.value(0).toLongLong()] = query.value(1).toLongLong();
02681 }
02682 }
02683
02684 void ProgramInfo::ClearPositionMap(int type) const
02685 {
02686 MSqlQuery query(MSqlQuery::InitCon());
02687
02688 if (isVideo)
02689 {
02690 query.prepare("DELETE FROM filemarkup"
02691 " WHERE filename = :PATH"
02692 " AND type = :TYPE ;");
02693 query.bindValue(":PATH", pathname);
02694 }
02695 else
02696 {
02697 query.prepare("DELETE FROM recordedseek"
02698 " WHERE chanid = :CHANID"
02699 " AND starttime = :STARTTIME"
02700 " AND type = :TYPE ;");
02701 query.bindValue(":CHANID", chanid);
02702 query.bindValue(":STARTTIME", recstartts);
02703 }
02704 query.bindValue(":TYPE", type);
02705
02706 if (!query.exec() || !query.isActive())
02707 MythContext::DBError("clear position map",
02708 query);
02709 }
02710
02711 void ProgramInfo::SetPositionMap(frm_pos_map_t &posMap, int type,
02712 long long min_frame, long long max_frame) const
02713 {
02714 QMap<long long, long long>::Iterator i;
02715 MSqlQuery query(MSqlQuery::InitCon());
02716 QString comp = "";
02717
02718 if (min_frame >= 0)
02719 comp += " AND mark >= :MIN_FRAME ";
02720 if (max_frame >= 0)
02721 comp += " AND mark <= :MAX_FRAME ";
02722
02723 if (isVideo)
02724 {
02725 query.prepare("DELETE FROM filemarkup"
02726 " WHERE filename = :PATH"
02727 " AND type = :TYPE"
02728 + comp + ";");
02729 query.bindValue(":PATH", pathname);
02730 }
02731 else
02732 {
02733 query.prepare("DELETE FROM recordedseek"
02734 " WHERE chanid = :CHANID"
02735 " AND starttime = :STARTTIME"
02736 " AND type = :TYPE"
02737 + comp + ";");
02738 query.bindValue(":CHANID", chanid);
02739 query.bindValue(":STARTTIME", recstartts);
02740 }
02741 query.bindValue(":TYPE", type);
02742 if (min_frame >= 0)
02743 query.bindValue(":MIN_FRAME", min_frame);
02744 if (max_frame >= 0)
02745 query.bindValue(":MAX_FRAME", max_frame);
02746
02747 if (!query.exec() || !query.isActive())
02748 MythContext::DBError("position map clear",
02749 query);
02750
02751 for (i = posMap.begin(); i != posMap.end(); ++i)
02752 {
02753 long long frame = i.key();
02754
02755 if ((min_frame >= 0) && (frame < min_frame))
02756 continue;
02757
02758 if ((max_frame >= 0) && (frame > max_frame))
02759 continue;
02760
02761 long long offset = i.data();
02762
02763 if (isVideo)
02764 {
02765 query.prepare("INSERT INTO filemarkup"
02766 " (filename, mark, type, offset)"
02767 " VALUES"
02768 " ( :PATH , :MARK , :TYPE , :OFFSET );");
02769 query.bindValue(":PATH", pathname);
02770 }
02771 else
02772 {
02773 query.prepare("INSERT INTO recordedseek"
02774 " (chanid, starttime, mark, type, offset)"
02775 " VALUES"
02776 " ( :CHANID , :STARTTIME , :MARK , :TYPE , :OFFSET );");
02777 query.bindValue(":CHANID", chanid);
02778 query.bindValue(":STARTTIME", recstartts);
02779 }
02780 query.bindValue(":MARK", frame);
02781 query.bindValue(":TYPE", type);
02782 query.bindValue(":OFFSET", offset);
02783
02784 if (!query.exec() || !query.isActive())
02785 MythContext::DBError("position map insert",
02786 query);
02787 }
02788 }
02789
02790 void ProgramInfo::SetPositionMapDelta(frm_pos_map_t &posMap,
02791 int type) const
02792 {
02793 QMap<long long, long long>::Iterator i;
02794 MSqlQuery query(MSqlQuery::InitCon());
02795
02796 for (i = posMap.begin(); i != posMap.end(); ++i)
02797 {
02798 long long frame = i.key();
02799 long long offset = i.data();
02800
02801 if (isVideo)
02802 {
02803 query.prepare("INSERT INTO filemarkup"
02804 " (filename, mark, type, offset)"
02805 " VALUES"
02806 " ( :PATH , :MARK , :TYPE , :OFFSET );");
02807 query.bindValue(":PATH", pathname);
02808 }
02809 else
02810 {
02811 query.prepare("INSERT INTO recordedseek"
02812 " (chanid, starttime, mark, type, offset)"
02813 " VALUES"
02814 " ( :CHANID , :STARTTIME , :MARK , :TYPE , :OFFSET );");
02815 query.bindValue(":CHANID", chanid);
02816 query.bindValue(":STARTTIME", recstartts);
02817 }
02818 query.bindValue(":MARK", frame);
02819 query.bindValue(":TYPE", type);
02820 query.bindValue(":OFFSET", offset);
02821
02822 if (!query.exec() || !query.isActive())
02823 MythContext::DBError("delta position map insert",
02824 query);
02825 }
02826 }
02827
02831 void ProgramInfo::ReactivateRecording(void)
02832 {
02833 MSqlQuery result(MSqlQuery::InitCon());
02834
02835 result.prepare("UPDATE oldrecorded SET reactivate = 1 "
02836 "WHERE station = :STATION AND "
02837 " starttime = :STARTTIME AND "
02838 " title = :TITLE;");
02839 result.bindValue(":STARTTIME", startts);
02840 result.bindValue(":TITLE", title.utf8());
02841 result.bindValue(":STATION", chansign);
02842
02843 result.exec();
02844 if (!result.isActive())
02845 MythContext::DBError("ReactivateRecording", result);
02846
02847 ScheduledRecording::signalChange(0);
02848 }
02849
02853 void ProgramInfo::AddHistory(bool resched, bool forcedup)
02854 {
02855 bool dup = (recstatus == rsRecorded || forcedup);
02856 RecStatusType rs = (recstatus == rsCurrentRecording) ?
02857 rsPreviousRecording : recstatus;
02858 oldrecstatus = recstatus;
02859 if (dup)
02860 reactivate = false;
02861
02862 MSqlQuery result(MSqlQuery::InitCon());
02863
02864 result.prepare("REPLACE INTO oldrecorded (chanid,starttime,"
02865 "endtime,title,subtitle,description,category,"
02866 "seriesid,programid,findid,recordid,station,"
02867 "rectype,recstatus,duplicate,reactivate) "
02868 "VALUES(:CHANID,:START,:END,:TITLE,:SUBTITLE,:DESC,"
02869 ":CATEGORY,:SERIESID,:PROGRAMID,:FINDID,:RECORDID,"
02870 ":STATION,:RECTYPE,:RECSTATUS,:DUPLICATE,:REACTIVATE);");
02871 result.bindValue(":CHANID", chanid);
02872 result.bindValue(":START", startts.toString(Qt::ISODate));
02873 result.bindValue(":END", endts.toString(Qt::ISODate));
02874 result.bindValue(":TITLE", title.utf8());
02875 result.bindValue(":SUBTITLE", subtitle.utf8());
02876 result.bindValue(":DESC", description.utf8());
02877 result.bindValue(":CATEGORY", category.utf8());
02878 result.bindValue(":SERIESID", seriesid.utf8());
02879 result.bindValue(":PROGRAMID", programid.utf8());
02880 result.bindValue(":FINDID", findid);
02881 result.bindValue(":RECORDID", recordid);
02882 result.bindValue(":STATION", chansign);
02883 result.bindValue(":RECTYPE", rectype);
02884 result.bindValue(":RECSTATUS", rs);
02885 result.bindValue(":DUPLICATE", dup);
02886 result.bindValue(":REACTIVATE", reactivate);
02887
02888 result.exec();
02889 if (!result.isActive())
02890 MythContext::DBError("addHistory", result);
02891
02892 if (dup && findid)
02893 {
02894 result.prepare("REPLACE INTO oldfind (recordid, findid) "
02895 "VALUES(:RECORDID,:FINDID);");
02896 result.bindValue(":RECORDID", recordid);
02897 result.bindValue(":FINDID", findid);
02898
02899 result.exec();
02900 if (!result.isActive())
02901 MythContext::DBError("addFindHistory", result);
02902 }
02903
02904
02905
02906 if (resched)
02907 ScheduledRecording::signalChange(0);
02908 }
02909
02913 void ProgramInfo::DeleteHistory(void)
02914 {
02915 MSqlQuery result(MSqlQuery::InitCon());
02916
02917 result.prepare("DELETE FROM oldrecorded WHERE title = :TITLE AND "
02918 "starttime = :START AND station = :STATION");
02919 result.bindValue(":TITLE", title.utf8());
02920 result.bindValue(":START", recstartts);
02921 result.bindValue(":STATION", chansign);
02922
02923 result.exec();
02924 if (!result.isActive())
02925 MythContext::DBError("deleteHistory", result);
02926
02927 if ( findid)
02928 {
02929 result.prepare("DELETE FROM oldfind WHERE "
02930 "recordid = :RECORDID AND findid = :FINDID");
02931 result.bindValue(":RECORDID", recordid);
02932 result.bindValue(":FINDID", findid);
02933
02934 result.exec();
02935 if (!result.isActive())
02936 MythContext::DBError("deleteFindHistory", result);
02937 }
02938
02939
02940
02941 ScheduledRecording::signalChange(0);
02942 }
02943
02952 void ProgramInfo::ForgetHistory(void)
02953 {
02954 MSqlQuery result(MSqlQuery::InitCon());
02955
02956 result.prepare("UPDATE recorded SET duplicate = 0 "
02957 "WHERE chanid = :CHANID "
02958 "AND starttime = :STARTTIME "
02959 "AND title = :TITLE;");
02960 result.bindValue(":STARTTIME", recstartts);
02961 result.bindValue(":TITLE", title.utf8());
02962 result.bindValue(":CHANID", chanid);
02963
02964 result.exec();
02965 if (!result.isActive())
02966 MythContext::DBError("forgetRecorded", result);
02967
02968 result.prepare("UPDATE oldrecorded SET duplicate = 0 "
02969 "WHERE duplicate = 1 "
02970 "AND title = :TITLE AND "
02971 "((programid = '' AND subtitle = :SUBTITLE"
02972 " AND description = :DESC) OR "
02973 " (programid <> '' AND programid = :PROGRAMID) OR "
02974 " (findid <> 0 AND findid = :FINDID))");
02975 result.bindValue(":TITLE", title.utf8());
02976 result.bindValue(":SUBTITLE", subtitle.utf8());
02977 result.bindValue(":DESC", description.utf8());
02978 result.bindValue(":PROGRAMID", programid);
02979 result.bindValue(":FINDID", findid);
02980
02981 result.exec();
02982 if (!result.isActive())
02983 MythContext::DBError("forgetHistory", result);
02984
02985 result.prepare("DELETE FROM oldrecorded "
02986 "WHERE recstatus = :NEVER AND duplicate = 0");
02987 result.bindValue(":NEVER", rsNeverRecord);
02988
02989 result.exec();
02990 if (!result.isActive())
02991 MythContext::DBError("forgetNeverHisttory", result);
02992
02993 if (findid)
02994 {
02995 result.prepare("DELETE FROM oldfind WHERE "
02996 "recordid = :RECORDID AND findid = :FINDID");
02997 result.bindValue(":RECORDID", recordid);
02998 result.bindValue(":FINDID", findid);
02999
03000 result.exec();
03001 if (!result.isActive())
03002 MythContext::DBError("forgetFindHistory", result);
03003 }
03004
03005
03006
03007 ScheduledRecording::signalChange(0);
03008 }
03009
03013 void ProgramInfo::SetDupHistory(void)
03014 {
03015 MSqlQuery result(MSqlQuery::InitCon());
03016
03017 result.prepare("UPDATE oldrecorded SET duplicate = 1 "
03018 "WHERE duplicate = 0 "
03019 "AND title = :TITLE AND "
03020 "((programid = '' AND subtitle = :SUBTITLE"
03021 " AND description = :DESC) OR "
03022 " (programid <> '' AND programid = :PROGRAMID) OR "
03023 " (findid <> 0 AND findid = :FINDID))");
03024 result.bindValue(":TITLE", title.utf8());
03025 result.bindValue(":SUBTITLE", subtitle.utf8());
03026 result.bindValue(":DESC", description.utf8());
03027 result.bindValue(":PROGRAMID", programid);
03028 result.bindValue(":FINDID", findid);
03029
03030 result.exec();
03031 if (!result.isActive())
03032 MythContext::DBError("setDupHistory", result);
03033
03034 ScheduledRecording::signalChange(0);
03035 }
03036
03040 QString ProgramInfo::RecTypeChar(void) const
03041 {
03042 switch (rectype)
03043 {
03044 case kSingleRecord:
03045 return QObject::tr("S", "RecTypeChar kSingleRecord");
03046 case kTimeslotRecord:
03047 return QObject::tr("T", "RecTypeChar kTimeslotRecord");
03048 case kWeekslotRecord:
03049 return QObject::tr("W", "RecTypeChar kWeekslotRecord");
03050 case kChannelRecord:
03051 return QObject::tr("C", "RecTypeChar kChannelRecord");
03052 case kAllRecord:
03053 return QObject::tr("A", "RecTypeChar kAllRecord");
03054 case kFindOneRecord:
03055 return QObject::tr("F", "RecTypeChar kFindOneRecord");
03056 case kFindDailyRecord:
03057 return QObject::tr("d", "RecTypeChar kFindDailyRecord");
03058 case kFindWeeklyRecord:
03059 return QObject::tr("w", "RecTypeChar kFindWeeklyRecord");
03060 case kOverrideRecord:
03061 case kDontRecord:
03062 return QObject::tr("O", "RecTypeChar kOverrideRecord/kDontRecord");
03063 case kNotRecording:
03064 default:
03065 return " ";
03066 }
03067 }
03068
03072 QString ProgramInfo::RecTypeText(void) const
03073 {
03074 switch (rectype)
03075 {
03076 case kSingleRecord:
03077 return QObject::tr("Single Record");
03078 case kTimeslotRecord:
03079 return QObject::tr("Record Daily");
03080 case kWeekslotRecord:
03081 return QObject::tr("Record Weekly");
03082 case kChannelRecord:
03083 return QObject::tr("Channel Record");
03084 case kAllRecord:
03085 return QObject::tr("Record All");
03086 case kFindOneRecord:
03087 return QObject::tr("Find One");
03088 case kFindDailyRecord:
03089 return QObject::tr("Find Daily");
03090 case kFindWeeklyRecord:
03091 return QObject::tr("Find Weekly");
03092 case kOverrideRecord:
03093 case kDontRecord:
03094 return QObject::tr("Override Recording");
03095 default:
03096 return QObject::tr("Not Recording");
03097 }
03098 }
03099
03103 QString ProgramInfo::RecStatusChar(void) const
03104 {
03105 switch (recstatus)
03106 {
03107 case rsAborted:
03108 return QObject::tr("A", "RecStatusChar rsAborted");
03109 case rsRecorded:
03110 return QObject::tr("R", "RecStatusChar rsRecorded");
03111 case rsRecording:
03112 if (cardid > 0)
03113 return QString::number(cardid);
03114 else
03115 return QObject::tr("R", "RecStatusChar rsCurrentRecording");
03116 case rsWillRecord:
03117 return QString::number(cardid);
03118 case rsDontRecord:
03119 return QObject::tr("X", "RecStatusChar rsDontRecord");
03120 case rsPreviousRecording:
03121 return QObject::tr("P", "RecStatusChar rsPreviousRecording");
03122 case rsCurrentRecording:
03123 return QObject::tr("R", "RecStatusChar rsCurrentRecording");
03124 case rsEarlierShowing:
03125 return QObject::tr("E", "RecStatusChar rsEarlierShowing");
03126 case rsTooManyRecordings:
03127 return QObject::tr("T", "RecStatusChar rsTooManyRecordings");
03128 case rsCancelled:
03129 return QObject::tr("c", "RecStatusChar rsCancelled");
03130 case rsMissed:
03131 return QObject::tr("M", "RecStatusChar rsMissed");
03132 case rsConflict:
03133 return QObject::tr("C", "RecStatusChar rsConflict");
03134 case rsLaterShowing:
03135 return QObject::tr("L", "RecStatusChar rsLaterShowing");
03136 case rsRepeat:
03137 return QObject::tr("r", "RecStatusChar rsRepeat");
03138 case rsInactive:
03139 return QObject::tr("x", "RecStatusChar rsInactive");
03140 case rsLowDiskSpace:
03141 return QObject::tr("K", "RecStatusChar rsLowDiskSpace");
03142 case rsTunerBusy:
03143 return QObject::tr("B", "RecStatusChar rsTunerBusy");
03144 case rsFailed:
03145 return QObject::tr("f", "RecStatusChar rsFailed");
03146 case rsNotListed:
03147 return QObject::tr("N", "RecStatusChar rsNotListed");
03148 case rsNeverRecord:
03149 return QObject::tr("V", "RecStatusChar rsNeverRecord");
03150 case rsOffLine:
03151 return QObject::tr("F", "RecStatusChar rsOffLine");
03152 case rsOtherShowing:
03153 return QObject::tr("O", "RecStatusChar rsOtherShowing");
03154 default:
03155 return "-";
03156 }
03157 }
03158
03162 QString ProgramInfo::RecStatusText(void) const
03163 {
03164 if (rectype == kNotRecording)
03165 return QObject::tr("Not Recording");
03166 else
03167 {
03168 switch (recstatus)
03169 {
03170 case rsAborted:
03171 return QObject::tr("Aborted");
03172 case rsRecorded:
03173 return QObject::tr("Recorded");
03174 case rsRecording:
03175 return QObject::tr("Recording");
03176 case rsWillRecord:
03177 return QObject::tr("Will Record");
03178 case rsDontRecord:
03179 return QObject::tr("Don't Record");
03180 case rsPreviousRecording:
03181 return QObject::tr("Previously Recorded");
03182 case rsCurrentRecording:
03183 return QObject::tr("Currently Recorded");
03184 case rsEarlierShowing:
03185 return QObject::tr("Earlier Showing");
03186 case rsTooManyRecordings:
03187 return QObject::tr("Max Recordings");
03188 case rsCancelled:
03189 return QObject::tr("Manual Cancel");
03190 case rsMissed:
03191 return QObject::tr("Missed");
03192 case rsConflict:
03193 return QObject::tr("Conflicting");
03194 case rsLaterShowing:
03195 return QObject::tr("Later Showing");
03196 case rsRepeat:
03197 return QObject::tr("Repeat");
03198 case rsInactive:
03199 return QObject::tr("Inactive");
03200 case rsLowDiskSpace:
03201 return QObject::tr("Low Disk Space");
03202 case rsTunerBusy:
03203 return QObject::tr("Tuner Busy");
03204 case rsFailed:
03205 return QObject::tr("Recorder Failed");
03206 case rsNotListed:
03207 return QObject::tr("Not Listed");
03208 case rsNeverRecord:
03209 return QObject::tr("Never Record");
03210 case rsOffLine:
03211 return QObject::tr("Recorder Off-Line");
03212 case rsOtherShowing:
03213 return QObject::tr("Other Showing");
03214 default:
03215 return QObject::tr("Unknown");
03216 }
03217 }
03218
03219 return QObject::tr("Unknown");
03220 }
03221
03225 QString ProgramInfo::RecStatusDesc(void) const
03226 {
03227 QString message;
03228 QDateTime now = QDateTime::currentDateTime();
03229
03230 if (recstatus <= rsWillRecord)
03231 {
03232 switch (recstatus)
03233 {
03234 case rsWillRecord:
03235 message = QObject::tr("This showing will be recorded.");
03236 break;
03237 case rsRecording:
03238 message = QObject::tr("This showing is being recorded.");
03239 break;
03240 case rsRecorded:
03241 message = QObject::tr("This showing was recorded.");
03242 break;
03243 case rsAborted:
03244 message = QObject::tr("This showing was recorded but was aborted "
03245 "before recording was completed.");
03246 break;
03247 case rsMissed:
03248 message += QObject::tr("This showing was not recorded because it "
03249 "was scheduled after it would have ended.");
03250 break;
03251 case rsCancelled:
03252 message += QObject::tr("This showing was not recorded because it "
03253 "was manually cancelled.");
03254 break;
03255 case rsLowDiskSpace:
03256 message += QObject::tr("there wasn't enough disk space available.");
03257 break;
03258 case rsTunerBusy:
03259 message += QObject::tr("the tuner card was already being used.");
03260 break;
03261 case rsFailed:
03262 message += QObject::tr("the recorder failed to record.");
03263 break;
03264 default:
03265 message = QObject::tr("The status of this showing is unknown.");
03266 break;
03267 }
03268 }
03269 else
03270 {
03271 if (recstartts > now)
03272 message = QObject::tr("This showing will not be recorded because ");
03273 else
03274 message = QObject::tr("This showing was not recorded because ");
03275
03276 switch (recstatus)
03277 {
03278 case rsDontRecord:
03279 message += QObject::tr("it was manually set to not record.");
03280 break;
03281 case rsPreviousRecording:
03282 message += QObject::tr("this episode was previously recorded "
03283 "according to the duplicate policy chosen "
03284 "for this title.");
03285 break;
03286 case rsCurrentRecording:
03287 message += QObject::tr("this episode was previously recorded and "
03288 "is still available in the list of "
03289 "recordings.");
03290 break;
03291 case rsEarlierShowing:
03292 message += QObject::tr("this episode will be recorded at an "
03293 "earlier time instead.");
03294 break;
03295 case rsTooManyRecordings:
03296 message += QObject::tr("too many recordings of this program have "
03297 "already been recorded.");
03298 break;
03299 case rsConflict:
03300 message += QObject::tr("another program with a higher priority "
03301 "will be recorded.");
03302 break;
03303 case rsLaterShowing:
03304 message += QObject::tr("this episode will be recorded at a "
03305 "later time.");
03306 break;
03307 case rsRepeat:
03308 message += QObject::tr("this episode is a repeat.");
03309 break;
03310 case rsInactive:
03311 message += QObject::tr("this recording rule is inactive.");
03312 break;
03313 case rsNotListed:
03314 message += QObject::tr("this rule does not match any showings in "
03315 "the current program listings.");
03316 break;
03317 case rsNeverRecord:
03318 message += QObject::tr("it was marked to never be recorded.");
03319 break;
03320 case rsOffLine:
03321 message += QObject::tr("the backend recorder is off-line.");
03322 break;
03323 case rsOtherShowing:
03324 message += QObject::tr("this episode will be recorded on a "
03325 "different channel in this time slot.");
03326 break;
03327 default:
03328 message += QObject::tr("you should never see this.");
03329 break;
03330 }
03331 }
03332
03333 return message;
03334 }
03335
03346 QString ProgramInfo::ChannelText(const QString &format) const
03347 {
03348 QString chan(format);
03349 chan.replace("<num>", chanstr)
03350 .replace("<sign>", chansign)
03351 .replace("<name>", channame);
03352 return chan;
03353 }
03354
03360 bool ProgramInfo::FillInRecordInfo(const vector<ProgramInfo *> &reclist)
03361 {
03362 vector<ProgramInfo *>::const_iterator i;
03363 ProgramInfo *found = NULL;
03364 int pfound = 0;
03365
03366 for (i = reclist.begin(); i != reclist.end(); i++)
03367 {
03368 ProgramInfo *p = *i;
03369 if (IsSameTimeslot(*p))
03370 {
03371 int pp = RecTypePriority(p->rectype);
03372 if (!found || pp < pfound ||
03373 (pp == pfound && p->recordid < found->recordid))
03374 {
03375 found = p;
03376 pfound = pp;
03377 }
03378 }
03379 }
03380
03381 if (found)
03382 {
03383 recstatus = found->recstatus;
03384 recordid = found->recordid;
03385 rectype = found->rectype;
03386 dupin = found->dupin;
03387 dupmethod = found->dupmethod;
03388 recstartts = found->recstartts;
03389 recendts = found->recendts;
03390 cardid = found->cardid;
03391 inputid = found->inputid;
03392 }
03393 return found;
03394 }
03395
03400 void ProgramInfo::Save(void) const
03401 {
03402 MSqlQuery query(MSqlQuery::InitCon());
03403
03404
03405
03406 query.prepare("DELETE FROM program"
03407 " WHERE chanid = :CHANID"
03408 " AND starttime = :STARTTIME ;");
03409 query.bindValue(":CHANID", chanid.toInt());
03410 query.bindValue(":STARTTIME", startts);
03411 if (!query.exec())
03412 MythContext::DBError("Saving program",
03413 query);
03414
03415 query.prepare("INSERT INTO program (chanid,starttime,endtime,"
03416 " title,subtitle,description,category,airdate,"
03417 " stars) VALUES (:CHANID,:STARTTIME,:ENDTIME,:TITLE,"
03418 " :SUBTITLE,:DESCRIPTION,:CATEGORY,:AIRDATE,:STARS);");
03419 query.bindValue(":CHANID", chanid.toInt());
03420 query.bindValue(":STARTTIME", startts);
03421 query.bindValue(":ENDTIME", endts);
03422 query.bindValue(":TITLE", title.utf8());
03423 query.bindValue(":SUBTITLE", subtitle.utf8());
03424 query.bindValue(":DESCRIPTION", description.utf8());
03425 query.bindValue(":CATEGORY", category.utf8());
03426 query.bindValue(":AIRDATE", "0");
03427 query.bindValue(":STARS", "0");
03428
03429 if (!query.exec())
03430 MythContext::DBError("Saving program",
03431 query);
03432 }
03433
03434
03439 void ProgramInfo::EditRecording(void)
03440 {
03441 if (recordid == 0)
03442 EditScheduled();
03443 else if (recstatus <= rsWillRecord)
03444 ShowRecordingDialog();
03445 else
03446 ShowNotRecordingDialog();
03447 }
03448
03453 void ProgramInfo::EditScheduled(void)
03454 {
03455 GetProgramRecordingStatus();
03456 record->exec();
03457 }
03458
03459 static QString get_ratings(bool recorded, uint chanid, QDateTime startts)
03460 {
03461 QString table = (recorded) ? "recordedrating" : "programrating";
03462 QString sel = QString(
03463 "SELECT system, rating FROM %1 "
03464 "WHERE chanid = :CHANID "
03465 "AND starttime = :STARTTIME").arg(table);
03466
03467 MSqlQuery query(MSqlQuery::InitCon());
03468 query.prepare(sel);
03469 query.bindValue(":CHANID", chanid);
03470 query.bindValue(":STARTTIME", startts);
03471
03472 if (!query.exec() || !query.isActive())
03473 {
03474 MythContext::DBError("programinfo.cpp: get_ratings", query);
03475 return "";
03476 }
03477
03478 QMap<QString,QString> main_ratings;
03479 QString advisory = "";
03480 while (query.next())
03481 {
03482 if (query.value(0).toString().lower() == "advisory")
03483 {
03484 advisory += query.value(1).toString() + ", ";
03485 continue;
03486 }
03487 main_ratings[query.value(0).toString()] = query.value(1).toString();
03488 }
03489
03490 if (!advisory.length() > 2)
03491 advisory.left(advisory.length() - 2);
03492
03493 if (main_ratings.empty())
03494 return advisory;
03495
03496 if (!advisory.isEmpty())
03497 advisory = ": " + advisory;
03498
03499 if (main_ratings.size() == 1)
03500 {
03501 return *main_ratings.begin() + advisory;
03502 }
03503
03504 QString ratings = "";
03505 QMap<QString,QString>::const_iterator it;
03506 for (it = main_ratings.begin(); it != main_ratings.end(); ++it)
03507 {
03508 ratings += it.key() + ": " + *it + ", ";
03509 }
03510
03511 return ratings + "Advisory" + advisory;
03512 }
03513
03514 #define ADD_PAR(title,text,result) \
03515 result += details_dialog->themeText("heading", title + ": ", 3) \
03516 + details_dialog->themeText("body", text, 3) + "<br>";
03517
03522 void ProgramInfo::showDetails(void) const
03523 {
03524 MSqlQuery query(MSqlQuery::InitCon());
03525 QString fullDateFormat = gContext->GetSetting("DateFormat", "M/d/yyyy");
03526 if (fullDateFormat.find(QRegExp("yyyy")) < 0)
03527 fullDateFormat += " yyyy";
03528 QString category_type, showtype, year, epinum, rating, colorcode,
03529 title_pronounce;
03530 float stars = 0.0;
03531 int partnumber = 0, parttotal = 0;
03532 int audioprop = 0, videoprop = 0, subtype = 0, generic = 0;
03533 bool recorded = false;
03534
03535 if (record == NULL && recordid)
03536 {
03537 record = new ScheduledRecording();
03538 record->loadByProgram(this);
03539 }
03540
03541 if (filesize > 0)
03542 recorded = true;
03543
03544 if (endts != startts)
03545 {
03546 QString ptable = "program";
03547 if (recorded)
03548 ptable = "recordedprogram";
03549
03550 query.prepare(QString("SELECT category_type, airdate, stars,"
03551 " partnumber, parttotal, audioprop+0, videoprop+0,"
03552 " subtitletypes+0, syndicatedepisodenumber, generic,"
03553 " showtype, colorcode, title_pronounce"
03554 " FROM %1 WHERE chanid = :CHANID AND"
03555 " starttime = :STARTTIME ;").arg(ptable));
03556
03557 query.bindValue(":CHANID", chanid);
03558 query.bindValue(":STARTTIME", startts);
03559
03560 if (query.exec() && query.isActive() && query.size() > 0)
03561 {
03562 query.next();
03563 category_type = query.value(0).toString();
03564 year = query.value(1).toString();
03565 stars = query.value(2).toDouble();
03566 partnumber = query.value(3).toInt();
03567 parttotal = query.value(4).toInt();
03568 audioprop = query.value(5).toInt();
03569 videoprop = query.value(6).toInt();
03570 subtype = query.value(7).toInt();
03571 epinum = query.value(8).toString();
03572 generic = query.value(9).toInt();
03573 showtype = query.value(10).toString();
03574 colorcode = query.value(11).toString();
03575 title_pronounce = QString::fromUtf8(query.value(12).toString());
03576 }
03577 else if (!query.isActive())
03578 MythContext::DBError(LOC + "showDetails", query);
03579
03580 rating = get_ratings(recorded, chanid.toUInt(), startts);
03581 }
03582
03583 if (category_type == "" && programid != "")
03584 {
03585 QString prefix = programid.left(2);
03586
03587 if (prefix == "MV")
03588 category_type = "movie";
03589 else if (prefix == "EP")
03590 category_type = "series";
03591 else if (prefix == "SP")
03592 category_type = "sports";
03593 else if (prefix == "SH")
03594 category_type = "tvshow";
03595 }
03596
03597 ProgDetails *details_dialog = new ProgDetails(gContext->GetMainWindow(),
03598 "progdetails");
03599
03600 QString msg = "";
03601 QString s = "";
03602
03603 s = title;
03604 if (subtitle != "")
03605 s += " - \"" + subtitle + "\"";
03606 ADD_PAR(QObject::tr("Title"), s, msg)
03607
03608 if (title_pronounce != "")
03609 ADD_PAR(QObject::tr("Title Pronounce"), title_pronounce, msg)
03610
03611 s = description;
03612
03613 QString attr = "";
03614
03615 if (partnumber > 0)
03616 attr += QString(QObject::tr("Part %1 of %2, ")).arg(partnumber).arg(parttotal);
03617
03618 if (rating != "" && rating != "NR")
03619 attr += rating + ", ";
03620 if (category_type == "movie")
03621 {
03622 if (year != "")
03623 attr += year + ", ";
03624
03625 if (stars > 0.0)
03626 {
03627 QString str = QObject::tr("stars");
03628 if (stars > 0 && stars <= 0.25)
03629 str = QObject::tr("star");
03630
03631 attr += QString("%1 %2, ").arg(4.0 * stars).arg(str);
03632 }
03633 }
03634 if (colorcode != "")
03635 attr += colorcode + ", ";
03636
03637 if (audioprop & AUD_MONO)
03638 attr += QObject::tr("Mono") + ", ";
03639 if (audioprop & AUD_STEREO)
03640 attr += QObject::tr("Stereo") + ", ";
03641 if (audioprop & AUD_SURROUND)
03642 attr += QObject::tr("Surround Sound") + ", ";
03643 if (audioprop & AUD_DOLBY)
03644 attr += QObject::tr("Dolby Sound") + ", ";
03645 if (audioprop & AUD_HARDHEAR)
03646 attr += QObject::tr("Audio for Hearing Impaired") + ", ";
03647 if (audioprop & AUD_VISUALIMPAIR)
03648 attr += QObject::tr("Audio for Visually Impaired") + ", ";
03649
03650 if (videoprop & VID_HDTV)
03651 attr += QObject::tr("HDTV") + ", ";
03652 if (videoprop & VID_WIDESCREEN)
03653 attr += QObject::tr("Widescreen") + ", ";
03654 if (videoprop & VID_AVC)
03655 attr += QObject::tr("AVC/H.264") + ", ";
03656
03657 if (subtype & SUB_HARDHEAR)
03658 attr += QObject::tr("CC","Closed Captioned") + ", ";
03659 if (subtype & SUB_NORMAL)
03660 attr += QObject::tr("Subtitles Available") + ", ";
03661 if (subtype & SUB_ONSCREEN)
03662 attr += QObject::tr("Subtitled") + ", ";
03663 if (subtype & SUB_SIGNED)
03664 attr += QObject::tr("Deaf Signing") + ", ";
03665
03666 if (generic && category_type == "series")
03667 attr += QObject::tr("Unidentified Episode") + ", ";
03668 else if (repeat)
03669 attr += QObject::tr("Repeat") + ", ";
03670
03671 if (attr != "")
03672 {
03673 attr.truncate(attr.findRev(','));
03674 s += " (" + attr + ")";
03675 }
03676
03677 if (s != "")
03678 ADD_PAR(QObject::tr("Description"), s, msg)
03679
03680 if (category != "")
03681 {
03682 s = category;
03683
03684 query.prepare("SELECT genre FROM programgenres "
03685 "WHERE chanid = :CHANID AND starttime = :STARTTIME "
03686 "AND relevance > 0 ORDER BY relevance;");
03687
03688 query.bindValue(":CHANID", chanid);
03689 query.bindValue(":STARTTIME", startts);
03690
03691 if (query.exec() && query.isActive() && query.size() > 0)
03692 {
03693 while (query.next())
03694 s += ", " + query.value(0).toString();
03695 }
03696 ADD_PAR(QObject::tr("Category"), s, msg)
03697 }
03698
03699 if (category_type != "")
03700 {
03701 s = category_type;
03702 if (seriesid != "")
03703 s += " (" + seriesid + ")";
03704 if (showtype != "")
03705 s += " " + showtype;
03706 ADD_PAR(QObject::tr("Type","category_type"), s, msg)
03707 }
03708
03709 if (epinum != "")
03710 ADD_PAR(QObject::tr("Episode Number"), epinum, msg)
03711
03712 if (hasAirDate && category_type != "movie")
03713 {
03714 ADD_PAR(QObject::tr("Original Airdate"),
03715 originalAirDate.toString(fullDateFormat), msg)
03716 }
03717 if (programid != "")
03718 ADD_PAR(QObject::tr("Program ID"), programid, msg)
03719
03720 QString role = "", pname = "";
03721
03722 if (endts != startts)
03723 {
03724 if (recorded)
03725 query.prepare("SELECT role,people.name FROM recordedcredits"
03726 " AS credits"
03727 " LEFT JOIN people ON credits.person = people.person"
03728 " WHERE credits.chanid = :CHANID"
03729 " AND credits.starttime = :STARTTIME"
03730 " ORDER BY role;");
03731 else
03732 query.prepare("SELECT role,people.name FROM credits"
03733 " LEFT JOIN people ON credits.person = people.person"
03734 " WHERE credits.chanid = :CHANID"
03735 " AND credits.starttime = :STARTTIME"
03736 " ORDER BY role;");
03737 query.bindValue(":CHANID", chanid);
03738 query.bindValue(":STARTTIME", startts);
03739
03740 if (query.exec() && query.isActive() && query.size() > 0)
03741 {
03742 QString rstr = "", plist = "";
03743
03744 while(query.next())
03745 {
03746 role = QString::fromUtf8(query.value(0).toString());
03747 pname = QString::fromUtf8(query.value(1).toString());
03748
03749 if (rstr == role)
03750 plist += ", " + pname;
03751 else
03752 {
03753 if (rstr == "actor")
03754 ADD_PAR(QObject::tr("Actors"), plist, msg)
03755 else if (rstr == "director")
03756 ADD_PAR(QObject::tr("Director"), plist, msg)
03757 else if (rstr == "producer")
03758 ADD_PAR(QObject::tr("Producer"), plist, msg)
03759 else if (rstr == "executive_producer")
03760 ADD_PAR(QObject::tr("Executive Producer"), plist, msg)
03761 else if (rstr == "writer")
03762 ADD_PAR(QObject::tr("Writer"), plist, msg)
03763 else if (rstr == "guest_star")
03764 ADD_PAR(QObject::tr("Guest Star"), plist, msg)
03765 else if (rstr == "host")
03766 ADD_PAR(QObject::tr("Host"), plist, msg)
03767 else if (rstr == "adapter")
03768 ADD_PAR(QObject::tr("Adapter"), plist, msg)
03769 else if (rstr == "presenter")
03770 ADD_PAR(QObject::tr("Presenter"), plist, msg)
03771 else if (rstr == "commentator")
03772 ADD_PAR(QObject::tr("Commentator"), plist, msg)
03773 else if (rstr == "guest")
03774 ADD_PAR(QObject::tr("Guest"), plist, msg)
03775
03776 rstr = role;
03777 plist = pname;
03778 }
03779 }
03780 if (rstr == "actor")
03781 ADD_PAR(QObject::tr("Actors"), plist, msg)
03782 else if (rstr == "director")
03783 ADD_PAR(QObject::tr("Director"), plist, msg)
03784 else if (rstr == "producer")
03785 ADD_PAR(QObject::tr("Producer"), plist, msg)
03786 else if (rstr == "executive_producer")
03787 ADD_PAR(QObject::tr("Executive Producer"), plist, msg)
03788 else if (rstr == "writer")
03789 ADD_PAR(QObject::tr("Writer"), plist, msg)
03790 else if (rstr == "guest_star")
03791 ADD_PAR(QObject::tr("Guest Star"), plist, msg)
03792 else if (rstr == "host")
03793 ADD_PAR(QObject::tr("Host"), plist, msg)
03794 else if (rstr == "adapter")
03795 ADD_PAR(QObject::tr("Adapter"), plist, msg)
03796 else if (rstr == "presenter")
03797 ADD_PAR(QObject::tr("Presenter"), plist, msg)
03798 else if (rstr == "commentator")
03799 ADD_PAR(QObject::tr("Commentator"), plist, msg)
03800 else if (rstr == "guest")
03801 ADD_PAR(QObject::tr("Guest"), plist, msg)
03802 }
03803 }
03804
03805
03806 msg += "<p>";
03807 QDateTime statusDate;
03808 if (recstatus == rsWillRecord)
03809 statusDate = startts;
03810
03811 ProgramInfo *p = new ProgramInfo;
03812 p->rectype = kSingleRecord;
03813 p->recstatus = recstatus;
03814
03815 if (p->recstatus == rsPreviousRecording ||
03816 p->recstatus == rsNeverRecord || p->recstatus == rsUnknown)
03817 {
03818 query.prepare("SELECT recstatus, starttime "
03819 "FROM oldrecorded WHERE duplicate > 0 AND "
03820 "((programid <> '' AND programid = :PROGRAMID) OR "
03821 " (title <> '' AND title = :TITLE AND "
03822 " subtitle <> '' AND subtitle = :SUBTITLE AND "
03823 " description <> '' AND description = :DECRIPTION));");
03824
03825 query.bindValue(":PROGRAMID", programid);
03826 query.bindValue(":TITLE", title);
03827 query.bindValue(":SUBTITLE", subtitle);
03828 query.bindValue(":DECRIPTION", description);
03829
03830 if (!query.exec() || !query.isActive())
03831 MythContext::DBError("showDetails", query);
03832
03833 if (query.isActive() && query.size() > 0)
03834 {
03835 query.next();
03836 if (p->recstatus == rsUnknown)
03837 p->recstatus = RecStatusType(query.value(0).toInt());
03838 if (p->recstatus == rsPreviousRecording ||
03839 p->recstatus == rsNeverRecord || p->recstatus == rsRecorded)
03840 statusDate = QDateTime::fromString(query.value(1).toString(),
03841 Qt::ISODate);
03842 }
03843 }
03844 if (p->recstatus == rsUnknown)
03845 {
03846 if (recorded)
03847 {
03848 p->recstatus = rsRecorded;
03849 statusDate = startts;
03850 }
03851 else
03852 {
03853 p->rectype = rectype;
03854 }
03855 }
03856 s = p->RecStatusText();
03857 if (statusDate.isValid())
03858 s += " " + statusDate.toString(fullDateFormat);
03859 ADD_PAR(QString("MythTV " + QObject::tr("Status")), s, msg)
03860 delete p;
03861
03862 if (recordid)
03863 {
03864 s = QString("%1, ").arg(recordid);
03865 if (rectype != kNotRecording)
03866 s += RecTypeText();
03867 if (record->getRecordTitle())
03868 s += QString(" \"%2\"").arg(record->getRecordTitle());
03869 ADD_PAR(QObject::tr("Recording Rule"), s, msg)
03870
03871 query.prepare("SELECT last_record, next_record, avg_delay "
03872 "FROM record WHERE recordid = :RECORDID");
03873 query.bindValue(":RECORDID", recordid);
03874
03875 if (query.exec() && query.isActive() && query.size() > 0)
03876 {
03877 query.next();
03878 if (query.value(0).toDateTime().isValid())
03879 ADD_PAR(QObject::tr("Last Recorded"),
03880 QObject::tr(query.value(0).toDateTime()
03881 .toString(fullDateFormat)), msg)
03882 if (query.value(1).toDateTime().isValid())
03883 ADD_PAR(QObject::tr("Next Recording"),
03884 QObject::tr(query.value(1).toDateTime()
03885 .toString(fullDateFormat)), msg)
03886 if (query.value(2).toInt() > 0)
03887 ADD_PAR(QObject::tr("Average Time Shift"),
03888 QString("%1 %2").arg(query.value(2).toInt())
03889 .arg(QObject::tr("hours")), msg)
03890 }
03891 if (recorded)
03892 {
03893 if (recpriority2 > 0)
03894 ADD_PAR(QObject::tr("Watch List Score"),
03895 QString("%1").arg(recpriority2), msg)
03896
03897 if (recpriority2 < 0)
03898 {
03899 QString st = "";
03900
03901 switch(recpriority2)
03902 {
03903 case wlExpireOff:
03904 st = QObject::tr("Auto-expire off");
03905 break;
03906 case wlWatched:
03907 st = QObject::tr("Marked as 'watched'");
03908 break;
03909 case wlEarlier:
03910 st = QObject::tr("Not the earliest episode");
03911 break;
03912 case wlDeleted:
03913 st = QObject::tr("Recently deleted episode");
03914 break;
03915 }
03916 ADD_PAR(QObject::tr("Watch List Status"), st, msg)
03917 }
03918 }
03919 if (record->getSearchType() &&
03920 record->getSearchType() != kManualSearch &&
03921 record->getRecordDescription() != description)
03922 ADD_PAR(QObject::tr("Search Phrase"),
03923 record->getRecordDescription().replace("<", "<")
03924 .replace(">", ">").replace("\n", " "), msg)
03925 }
03926 if (findid > 0)
03927 {
03928 QDate fdate = QDate::QDate (1970, 1, 1);
03929 fdate = fdate.addDays(findid - 719528);
03930 ADD_PAR(QObject::tr("Find ID"), QString("%1 (%2)").arg(findid)
03931 .arg(fdate.toString(fullDateFormat)), msg)
03932 }
03933 if (recorded)
03934 {
03935 ADD_PAR(QObject::tr("Recording Host"), hostname, msg)
03936 ADD_PAR(QObject::tr("Recorded File Name"), GetRecordBasename(), msg)
03937
03938 QString tmpSize;
03939 tmpSize.sprintf("%0.2f ", filesize / 1024.0 / 1024.0 / 1024.0);
03940 tmpSize += QObject::tr("GB", "GigaBytes");
03941 ADD_PAR(QObject::tr("Recorded File Size"), tmpSize, msg)
03942
03943 query.prepare("SELECT profile FROM recorded"
03944 " WHERE chanid = :CHANID"
03945 " AND starttime = :STARTTIME;");
03946 query.bindValue(":CHANID", chanid);
03947 query.bindValue(":STARTTIME", recstartts);
03948
03949 if (query.exec() && query.isActive() && query.size() > 0)
03950 {
03951 query.next();
03952 if (query.value(0).toString() > "")
03953 ADD_PAR(QObject::tr("Recording Profile"),
03954 QObject::tr(query.value(0).toString()), msg)
03955 }
03956 ADD_PAR(QObject::tr("Recording Group"), QObject::tr(recgroup), msg)
03957 ADD_PAR(QObject::tr("Storage Group"), QObject::tr(storagegroup), msg)
03958 ADD_PAR(QObject::tr("Playback Group"), QObject::tr(playgroup), msg)
03959 }
03960 else if (recordid)
03961 {
03962 ADD_PAR(QObject::tr("Recording Profile"), record->getProfileName(),msg)
03963 }
03964 msg.remove(QRegExp("<br>$"));
03965 details_dialog->setDetails(msg);
03966 details_dialog->exec();
03967
03968 delete details_dialog;
03969 }
03970
03975 int ProgramInfo::getProgramFlags(void) const
03976 {
03977 int flags = 0;
03978 MSqlQuery query(MSqlQuery::InitCon());
03979
03980 query.prepare("SELECT commflagged, cutlist, autoexpire, "
03981 "editing, bookmark, watched, preserve "
03982 "FROM recorded LEFT JOIN recordedprogram ON "
03983 "(recorded.chanid = recordedprogram.chanid AND "
03984 "recorded.progstart = recordedprogram.starttime) "
03985 "WHERE recorded.chanid = :CHANID AND recorded.starttime = :STARTTIME ;");
03986 query.bindValue(":CHANID", chanid);
03987 query.bindValue(":STARTTIME", recstartts);
03988
03989 if (query.exec() && query.isActive() && query.size() > 0)
03990 {
03991 query.next();
03992
03993 flags |= (query.value(0).toInt() == COMM_FLAG_DONE) ? FL_COMMFLAG : 0;
03994 flags |= (query.value(1).toInt() == 1) ? FL_CUTLIST : 0;
03995 flags |= query.value(2).toInt() ? FL_AUTOEXP : 0;
03996 if ((query.value(3).toInt()) ||
03997 (query.value(0).toInt() == COMM_FLAG_PROCESSING))
03998 flags |= FL_EDITING;
03999 flags |= (query.value(4).toInt() == 1) ? FL_BOOKMARK : 0;
04000 flags |= (query.value(5).toInt() == 1) ? FL_WATCHED : 0;
04001 flags |= (query.value(6).toInt() == 1) ? FL_PRESERVED : 0;
04002 }
04003
04004 return flags;
04005 }
04006
04007 void ProgramInfo::getProgramProperties(void)
04008 {
04009
04010 MSqlQuery query(MSqlQuery::InitCon());
04011
04012 query.prepare("SELECT audioprop+0, videoprop+0, subtitletypes+0 "
04013 "FROM recorded LEFT JOIN recordedprogram ON "
04014 "(recorded.chanid = recordedprogram.chanid AND "
04015 "recorded.progstart = recordedprogram.starttime) "
04016 "WHERE recorded.chanid = :CHANID AND recorded.starttime = :STARTTIME ;");
04017 query.bindValue(":CHANID", chanid);
04018 query.bindValue(":STARTTIME", recstartts);
04019
04020 if (query.exec() && query.isActive() && query.size() > 0)
04021 {
04022 query.next();
04023
04024 audioproperties = query.value(0).toInt();
04025 videoproperties = query.value(1).toInt();
04026 subtitleType = query.value(2).toInt();
04027 }
04028
04029 }
04030
04031 void ProgramInfo::UpdateInUseMark(bool force)
04032 {
04033 if (isVideo)
04034 return;
04035
04036 if (inUseForWhat == "")
04037 return;
04038
04039 if (force || lastInUseTime.secsTo(QDateTime::currentDateTime()) > 15 * 60)
04040 MarkAsInUse(true);
04041 }
04042
04043 bool ProgramInfo::PathnameExists(void)
04044 {
04045 if (pathname.left(7) == "myth://")
04046 return RemoteCheckFile(this);
04047
04048 QFile checkFile(pathname);
04049
04050 return checkFile.exists();
04051 }
04052
04053 QString ProgramInfo::GetRecGroupPassword(QString group)
04054 {
04055 QString result = QString("");
04056
04057 if (group == "All Programs")
04058 {
04059 result = gContext->GetSetting("AllRecGroupPassword");
04060 }
04061 else
04062 {
04063 MSqlQuery query(MSqlQuery::InitCon());
04064 query.prepare("SELECT password FROM recgrouppassword "
04065 "WHERE recgroup = :GROUP ;");
04066 query.bindValue(":GROUP", group.utf8());
04067
04068 if (query.exec() && query.isActive() && query.size() > 0)
04069 if (query.next())
04070 result = query.value(0).toString();
04071 }
04072
04073 if (result == QString::null)
04074 result = QString("");
04075
04076 return(result);
04077 }
04078
04081 void ProgramInfo::UpdateRecGroup(void)
04082 {
04083 MSqlQuery query(MSqlQuery::InitCon());
04084 query.prepare("SELECT recgroup FROM recorded"
04085 "WHERE chanid = :CHANID"
04086 "AND starttime = :START ;");
04087 query.bindValue(":START", recstartts);
04088 query.bindValue(":CHANID", chanid);
04089 if (query.exec() && query.next())
04090 {
04091 recgroup = QString::fromUtf8(query.value(0).toString());
04092 }
04093 }
04094 void ProgramInfo::MarkAsInUse(bool inuse, QString usedFor)
04095 {
04096 if (isVideo)
04097 return;
04098
04099 bool notifyOfChange = false;
04100
04101 if (inuse && inUseForWhat.length() < 2)
04102 {
04103 if (usedFor != "")
04104 inUseForWhat = usedFor;
04105 else
04106 inUseForWhat = QObject::tr("Unknown") + " [" +
04107 QString::number(getpid()) + "]";
04108
04109 notifyOfChange = true;
04110 }
04111
04112 if (!inuse && inUseForWhat.length() < 2)
04113 return;
04114
04115 MSqlQuery query(MSqlQuery::InitCon());
04116
04117 query.prepare("DELETE FROM inuseprograms WHERE "
04118 "chanid = :CHANID AND starttime = :STARTTIME AND "
04119 "hostname = :HOSTNAME AND recusage = :RECUSAGE ;");
04120 query.bindValue(":CHANID", chanid);
04121 query.bindValue(":STARTTIME", recstartts);
04122 query.bindValue(":HOSTNAME", gContext->GetHostName());
04123 query.bindValue(":RECUSAGE", inUseForWhat);
04124
04125 query.exec();
04126
04127 if (!inuse)
04128 {
04129 if (!gContext->IsBackend())
04130 RemoteSendMessage("RECORDING_LIST_CHANGE");
04131 inUseForWhat = "";
04132 return;
04133 }
04134
04135 if (pathname.left(7) == "myth://")
04136 pathname = GetPlaybackURL();
04137
04138 if (pathname.right(1) == "/")
04139 pathname.remove(pathname.length() - 1, 1);
04140
04141 QString recDir = "";
04142 if (hostname == gContext->GetHostName())
04143 {
04144
04145
04146 QFileInfo testFile(pathname);
04147 if (testFile.exists())
04148 {
04149 while (testFile.isSymLink())
04150 testFile.setFile(testFile.readLink());
04151
04152 if (testFile.isFile())
04153 recDir = testFile.dirPath();
04154 else if (testFile.isDir())
04155 recDir = testFile.filePath();
04156 }
04157 else
04158 {
04159 testFile.setFile(testFile.dirPath());
04160 if (testFile.exists())
04161 {
04162 while(testFile.isSymLink())
04163 testFile.setFile(testFile.readLink());
04164
04165 if (testFile.isDir())
04166 recDir = testFile.filePath();
04167 }
04168 }
04169 }
04170 else if (inUseForWhat == PreviewGenerator::kInUseID)
04171 {
04172 recDir = "";
04173 }
04174 else if (!gContext->IsBackend() && RemoteCheckFile(this))
04175 {
04176 recDir = pathname.section("/", 0, -2);
04177 }
04178
04179 lastInUseTime = mythCurrentDateTime();
04180
04181 query.prepare("INSERT INTO inuseprograms "
04182 " (chanid, starttime, recusage, hostname, lastupdatetime, "
04183 " rechost, recdir ) "
04184 " VALUES "
04185 " (:CHANID, :STARTTIME, :RECUSAGE, :HOSTNAME, :UPDATETIME, "
04186 " :RECHOST, :RECDIR);");
04187 query.bindValue(":CHANID", chanid);
04188 query.bindValue(":STARTTIME", recstartts);
04189 query.bindValue(":HOSTNAME", gContext->GetHostName());
04190 query.bindValue(":RECUSAGE", inUseForWhat);
04191 query.bindValue(":UPDATETIME", lastInUseTime);
04192 query.bindValue(":RECHOST", hostname);
04193 query.bindValue(":RECDIR", recDir);
04194
04195 if (!query.exec() || !query.isActive())
04196 MythContext::DBError("SetInUse", query);
04197
04198