MythTV  0.26-pre
programdata.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 
00003 #include <limits.h>
00004 
00005 // C++ includes
00006 #include <algorithm>
00007 using namespace std;
00008 
00009 // MythTV headers
00010 #include "channelutil.h"
00011 #include "mythdb.h"
00012 #include "mythlogging.h"
00013 #include "programinfo.h"
00014 #include "programdata.h"
00015 #include "dvbdescriptors.h"
00016 
00017 #define LOC      QString("ProgramData: ")
00018 
00019 static const char *roles[] =
00020 {
00021     "",
00022     "actor",     "director",    "producer", "executive_producer",
00023     "writer",    "guest_star",  "host",     "adapter",
00024     "presenter", "commentator", "guest",
00025 };
00026 
00027 static QString denullify(const QString &str)
00028 {
00029     return str.isNull() ? "" : str;
00030 }
00031 
00032 DBPerson::DBPerson(const DBPerson &other) :
00033     role(other.role), name(other.name)
00034 {
00035     name.squeeze();
00036 }
00037 
00038 DBPerson::DBPerson(Role _role, const QString &_name) :
00039     role(_role), name(_name)
00040 {
00041     name.squeeze();
00042 }
00043 
00044 DBPerson::DBPerson(const QString &_role, const QString &_name) :
00045     role(kUnknown), name(_name)
00046 {
00047     if (!_role.isEmpty())
00048     {
00049         for (uint i = 0; i < sizeof(roles) / sizeof(char *); i++)
00050         {
00051             if (_role == QString(roles[i]))
00052                 role = (Role) i;
00053         }
00054     }
00055     name.squeeze();
00056 }
00057 
00058 QString DBPerson::GetRole(void) const
00059 {
00060     if ((role < kActor) || (role > kGuest))
00061         return "guest";
00062     return roles[role];
00063 }
00064 
00065 uint DBPerson::InsertDB(MSqlQuery &query, uint chanid,
00066                         const QDateTime &starttime) const
00067 {
00068     uint personid = GetPersonDB(query);
00069     if (!personid && InsertPersonDB(query))
00070         personid = GetPersonDB(query);
00071 
00072     return InsertCreditsDB(query, personid, chanid, starttime);
00073 }
00074 
00075 uint DBPerson::GetPersonDB(MSqlQuery &query) const
00076 {
00077     query.prepare(
00078         "SELECT person "
00079         "FROM people "
00080         "WHERE name = :NAME");
00081     query.bindValue(":NAME", name);
00082 
00083     if (!query.exec())
00084         MythDB::DBError("get_person", query);
00085     else if (query.next())
00086         return query.value(0).toUInt();
00087 
00088     return 0;
00089 }
00090 
00091 uint DBPerson::InsertPersonDB(MSqlQuery &query) const
00092 {
00093     query.prepare(
00094         "INSERT IGNORE INTO people (name) "
00095         "VALUES (:NAME);");
00096     query.bindValue(":NAME", name);
00097 
00098     if (query.exec())
00099         return 1;
00100 
00101     MythDB::DBError("insert_person", query);
00102     return 0;
00103 }
00104 
00105 uint DBPerson::InsertCreditsDB(MSqlQuery &query, uint personid, uint chanid,
00106                                const QDateTime &starttime) const
00107 {
00108     if (!personid)
00109         return 0;
00110 
00111     query.prepare(
00112         "REPLACE INTO credits "
00113         "       ( person,  chanid,  starttime,  role) "
00114         "VALUES (:PERSON, :CHANID, :STARTTIME, :ROLE) ");
00115     query.bindValue(":PERSON",    personid);
00116     query.bindValue(":CHANID",    chanid);
00117     query.bindValue(":STARTTIME", starttime);
00118     query.bindValue(":ROLE",      GetRole());
00119 
00120     if (query.exec())
00121         return 1;
00122 
00123     MythDB::DBError("insert_credits", query);
00124     return 0;
00125 }
00126 
00127 DBEvent &DBEvent::operator=(const DBEvent &other)
00128 {
00129     if (this == &other)
00130         return *this;
00131 
00132     title           = other.title;
00133     subtitle        = other.subtitle;
00134     description     = other.description;
00135     category        = other.category;
00136     starttime       = other.starttime;
00137     endtime         = other.endtime;
00138     airdate         = other.airdate;
00139     originalairdate = other.originalairdate;
00140 
00141     if (credits != other.credits)
00142     {
00143         if (credits)
00144         {
00145             delete credits;
00146             credits = NULL;
00147         }
00148 
00149         if (other.credits)
00150         {
00151             credits = new DBCredits;
00152             credits->insert(credits->end(),
00153                             other.credits->begin(),
00154                             other.credits->end());
00155         }
00156     }
00157 
00158     partnumber      = other.partnumber;
00159     parttotal       = other.parttotal;
00160     syndicatedepisodenumber = other.syndicatedepisodenumber;
00161     subtitleType    = other.subtitleType;
00162     audioProps      = other.audioProps;
00163     videoProps      = other.videoProps;
00164     stars           = other.stars;
00165     categoryType    = other.categoryType;
00166     seriesId        = other.seriesId;
00167     programId       = other.programId;
00168     previouslyshown = other.previouslyshown;
00169     ratings         = other.ratings;
00170     listingsource   = other.listingsource;
00171 
00172     Squeeze();
00173 
00174     return *this;
00175 }
00176 
00177 void DBEvent::Squeeze(void)
00178 {
00179     title.squeeze();
00180     subtitle.squeeze();
00181     description.squeeze();
00182     category.squeeze();
00183     syndicatedepisodenumber.squeeze();
00184     seriesId.squeeze();
00185     programId.squeeze();
00186 }
00187 
00188 void DBEvent::AddPerson(DBPerson::Role role, const QString &name)
00189 {
00190     if (!credits)
00191         credits = new DBCredits;
00192 
00193     credits->push_back(DBPerson(role, name));
00194 }
00195 
00196 void DBEvent::AddPerson(const QString &role, const QString &name)
00197 {
00198     if (!credits)
00199         credits = new DBCredits;
00200 
00201     credits->push_back(DBPerson(role, name));
00202 }
00203 
00204 bool DBEvent::HasTimeConflict(const DBEvent &o) const
00205 {
00206     return ((starttime <= o.starttime && o.starttime < endtime) ||
00207             (o.endtime <= endtime     && starttime   < o.endtime));
00208 }
00209 
00210 uint DBEvent::UpdateDB(
00211     MSqlQuery &query, uint chanid, int match_threshold) const
00212 {
00213     vector<DBEvent> programs;
00214     uint count = GetOverlappingPrograms(query, chanid, programs);
00215     int  match = INT_MIN;
00216     int  i     = -1;
00217 
00218     if (!count)
00219         return InsertDB(query, chanid);
00220 
00221     // move overlapping programs out of the way and update existing if possible
00222     match = GetMatch(programs, i);
00223 
00224     if (match >= match_threshold)
00225     {
00226         LOG(VB_EIT, LOG_DEBUG,
00227             QString("EIT: accept match[%1]: %2 '%3' vs. '%4'")
00228                 .arg(i).arg(match).arg(title).arg(programs[i].title));
00229         return UpdateDB(query, chanid, programs, i);
00230     }
00231     else
00232     {
00233         if (i >= 0)
00234         {
00235             LOG(VB_EIT, LOG_DEBUG,
00236                 QString("EIT: reject match[%1]: %2 '%3' vs. '%4'")
00237                     .arg(i).arg(match).arg(title).arg(programs[i].title));
00238         }
00239         return UpdateDB(query, chanid, programs, -1);
00240     }
00241 }
00242 
00243 uint DBEvent::GetOverlappingPrograms(
00244     MSqlQuery &query, uint chanid, vector<DBEvent> &programs) const
00245 {
00246     uint count = 0;
00247     query.prepare(
00248         "SELECT title,          subtitle,      description, "
00249         "       category,       category_type, "
00250         "       starttime,      endtime, "
00251         "       subtitletypes+0,audioprop+0,   videoprop+0, "
00252         "       seriesid,       programid, "
00253         "       partnumber,     parttotal, "
00254         "       syndicatedepisodenumber, "
00255         "       airdate,        originalairdate, "
00256         "       previouslyshown,listingsource, "
00257         "       stars+0 "
00258         "FROM program "
00259         "WHERE chanid   = :CHANID AND "
00260         "      manualid = 0       AND "
00261         "      ( ( starttime >= :STIME1 AND starttime <  :ETIME1 ) OR "
00262         "        ( endtime   >  :STIME2 AND endtime   <= :ETIME2 ) )");
00263     query.bindValue(":CHANID", chanid);
00264     query.bindValue(":STIME1", starttime);
00265     query.bindValue(":ETIME1", endtime);
00266     query.bindValue(":STIME2", starttime);
00267     query.bindValue(":ETIME2", endtime);
00268 
00269     if (!query.exec())
00270     {
00271         MythDB::DBError("GetOverlappingPrograms 1", query);
00272         return 0;
00273     }
00274 
00275     while (query.next())
00276     {
00277         MythCategoryType category_type =
00278             string_to_myth_category_type(query.value(4).toString());
00279 
00280         DBEvent prog(
00281             query.value(0).toString(),
00282             query.value(1).toString(),
00283             query.value(2).toString(),
00284             query.value(3).toString(),
00285             category_type,
00286             query.value(5).toDateTime(), query.value(6).toDateTime(),
00287             query.value(7).toUInt(),
00288             query.value(8).toUInt(),
00289             query.value(9).toUInt(),
00290             query.value(19).toDouble(),
00291             query.value(10).toString(),
00292             query.value(11).toString(),
00293             query.value(18).toUInt());
00294 
00295         prog.partnumber = query.value(12).toUInt();
00296         prog.parttotal  = query.value(13).toUInt();
00297         prog.syndicatedepisodenumber = query.value(14).toString();
00298         prog.airdate    = query.value(15).toUInt();
00299         prog.originalairdate  = query.value(16).toDate();
00300         prog.previouslyshown  = query.value(17).toBool();
00301         ;
00302 
00303         programs.push_back(prog);
00304         count++;
00305     }
00306 
00307     return count;
00308 }
00309 
00310 
00311 static int score_words(const QStringList &al, const QStringList &bl)
00312 {
00313     QStringList::const_iterator ait = al.begin();
00314     QStringList::const_iterator bit = bl.begin();
00315     int score = 0;
00316     for (; (ait != al.end()) && (bit != bl.end()); ++ait)
00317     {
00318         QStringList::const_iterator bit2 = bit;
00319         int dist = 0;
00320         int bscore = 0;
00321         for (; bit2 != bl.end(); ++bit2)
00322         {
00323             if (*ait == *bit)
00324             {
00325                 bscore = max(1000, 2000 - (dist * 500));
00326                 // lower score for short words
00327                 if (ait->length() < 5)
00328                     bscore /= 5 - ait->length();
00329                 break;
00330             }
00331             dist++;
00332         }
00333         if (bscore && dist < 3)
00334         {
00335             for (int i = 0; (i < dist) && bit != bl.end(); i++)
00336                 ++bit;
00337         }
00338         score += bscore;
00339     }
00340 
00341     return score / al.size();
00342 }
00343 
00344 static int score_match(const QString &a, const QString &b)
00345 {
00346     if (a.isEmpty() || b.isEmpty())
00347         return 0;
00348     else if (a == b)
00349         return 1000;
00350 
00351     QString A = a.simplified().toUpper();
00352     QString B = b.simplified().toUpper();
00353     if (A == B)
00354         return 1000;
00355 
00356     QStringList al, bl;
00357     al = A.split(" ", QString::SkipEmptyParts);
00358     if (!al.size())
00359         return 0;
00360 
00361     bl = B.split(" ", QString::SkipEmptyParts);
00362     if (!bl.size())
00363         return 0;
00364 
00365     // score words symmetrically
00366     int score = (score_words(al, bl) + score_words(bl, al)) / 2;
00367 
00368     return min(900, score);
00369 }
00370 
00371 int DBEvent::GetMatch(const vector<DBEvent> &programs, int &bestmatch) const
00372 {
00373     bestmatch = -1;
00374     int match_val = INT_MIN;
00375     int overlap = 0;
00376     int duration = starttime.secsTo(endtime);
00377 
00378     for (uint i = 0; i < programs.size(); i++)
00379     {
00380         int mv = 0;
00381         int duration_loop = programs[i].starttime.secsTo(programs[i].endtime);
00382 
00383         mv -= abs(starttime.secsTo(programs[i].starttime));
00384         mv -= abs(endtime.secsTo(programs[i].endtime));
00385         mv -= abs(duration - duration_loop);
00386         mv += score_match(title, programs[i].title) * 10;
00387         mv += score_match(subtitle, programs[i].subtitle);
00388         mv += score_match(description, programs[i].description);
00389 
00390         /* determine overlap of both programs
00391          * we don't know which one starts first */
00392         if (starttime < programs[i].starttime)
00393             overlap = programs[i].starttime.secsTo(endtime);
00394         else if (starttime > programs[i].starttime)
00395             overlap = starttime.secsTo(programs[i].endtime);
00396         else
00397         {
00398             if (endtime <= programs[i].endtime)
00399                 overlap = starttime.secsTo(endtime);
00400             else
00401                 overlap = starttime.secsTo(programs[i].endtime);
00402         }
00403 
00404         /* scale the score depending on the overlap length
00405          * full score is preserved if the overlap is at least 1/2 of the length
00406          * of the shorter program */
00407         if (overlap > 0)
00408         {
00409             /* crappy providers apparently have events without duration
00410              * ensure that the minimal duration is 2 second to avoid
00411              * muliplying and more importantly dividing by zero */
00412             int min_dur = max(2, min(duration, duration_loop));
00413             overlap = min(overlap, min_dur/2);
00414             mv *= overlap * 2;
00415             mv /= min_dur;
00416         }
00417         else
00418         {
00419             LOG(VB_GENERAL, LOG_ERR,
00420                 QString("Unexpected result: shows don't "
00421                         "overlap\n\t%1: %2 - %3\n\t%4: %5 - %6")
00422                     .arg(title.left(30), 30)
00423                     .arg(starttime.toString(Qt::ISODate))
00424                     .arg(endtime.toString(Qt::ISODate))
00425                     .arg(programs[i].title.left(30), 30)
00426                     .arg(programs[i].starttime.toString(Qt::ISODate))
00427                     .arg(programs[i].endtime.toString(Qt::ISODate))
00428                 );
00429         }
00430 
00431         if (mv > match_val)
00432         {
00433             LOG(VB_EIT, LOG_DEBUG,
00434                 QString("GM : %1 new best match %2 with score %3")
00435                     .arg(title.left(25))
00436                     .arg(programs[i].title.left(25)).arg(mv));
00437             bestmatch = i;
00438             match_val = mv;
00439         }
00440     }
00441 
00442     return match_val;
00443 }
00444 
00445 uint DBEvent::UpdateDB(
00446     MSqlQuery &q, uint chanid, const vector<DBEvent> &p, int match) const
00447 {
00448     // adjust/delete overlaps;
00449     bool ok = true;
00450     for (uint i = 0; i < p.size(); i++)
00451     {
00452         if (i != (uint)match)
00453             ok &= MoveOutOfTheWayDB(q, chanid, p[i]);
00454     }
00455 
00456     // if we failed to move programs out of the way, don't insert new ones..
00457     if (!ok)
00458         return 0;
00459 
00460     // if no match, insert current item
00461     if ((match < 0) || ((uint)match >= p.size()))
00462         return InsertDB(q, chanid);
00463 
00464     // update matched item with current data
00465     return UpdateDB(q, chanid, p[match]);
00466 }
00467 
00468 uint DBEvent::UpdateDB(
00469     MSqlQuery &query, uint chanid, const DBEvent &match) const
00470 {
00471     QString  ltitle     = title;
00472     QString  lsubtitle  = subtitle;
00473     QString  ldesc      = description;
00474     QString  lcategory  = category;
00475     uint16_t lairdate   = airdate;
00476     QString  lprogramId = programId;
00477     QString  lseriesId  = seriesId;
00478     QDate loriginalairdate = originalairdate;
00479 
00480     if (match.title.length() >= ltitle.length())
00481         ltitle = match.title;
00482 
00483     if (match.subtitle.length() >= lsubtitle.length())
00484         lsubtitle = match.subtitle;
00485 
00486     if (match.description.length() >= ldesc.length())
00487         ldesc = match.description;
00488 
00489     if (lcategory.isEmpty() && !match.category.isEmpty())
00490         lcategory = match.category;
00491 
00492     if (!lairdate && !match.airdate)
00493         lairdate = match.airdate;
00494 
00495     if (!loriginalairdate.isValid() && match.originalairdate.isValid())
00496         loriginalairdate = match.originalairdate;
00497 
00498     if (lprogramId.isEmpty() && !match.programId.isEmpty())
00499         lprogramId = match.programId;
00500 
00501     if (lseriesId.isEmpty() && !match.seriesId.isEmpty())
00502         lseriesId = match.seriesId;
00503 
00504     uint tmp = categoryType;
00505     if (!categoryType && match.categoryType)
00506         tmp = match.categoryType;
00507 
00508     QString lcattype = myth_category_type_to_string(tmp);
00509 
00510     unsigned char lsubtype = subtitleType | match.subtitleType;
00511     unsigned char laudio   = audioProps   | match.audioProps;
00512     unsigned char lvideo   = videoProps   | match.videoProps;
00513 
00514     uint lpartnumber =
00515         (!partnumber && match.partnumber) ? match.partnumber : partnumber;
00516     uint lparttotal =
00517         (!parttotal  && match.parttotal ) ? match.parttotal  : parttotal;
00518 
00519     bool lpreviouslyshown = previouslyshown | match.previouslyshown;
00520 
00521     uint32_t llistingsource = listingsource | match.listingsource;
00522 
00523     QString lsyndicatedepisodenumber = syndicatedepisodenumber;
00524     if (lsyndicatedepisodenumber.isEmpty() &&
00525         !match.syndicatedepisodenumber.isEmpty())
00526         lsyndicatedepisodenumber = match.syndicatedepisodenumber;
00527 
00528     query.prepare(
00529         "UPDATE program "
00530         "SET title          = :TITLE,     subtitle      = :SUBTITLE, "
00531         "    description    = :DESC, "
00532         "    category       = :CATEGORY,  category_type = :CATTYPE, "
00533         "    starttime      = :STARTTIME, endtime       = :ENDTIME, "
00534         "    closecaptioned = :CC,        subtitled     = :HASSUBTITLES, "
00535         "    stereo         = :STEREO,    hdtv          = :HDTV, "
00536         "    subtitletypes  = :SUBTYPE, "
00537         "    audioprop      = :AUDIOPROP, videoprop     = :VIDEOPROP, "
00538         "    partnumber     = :PARTNO,    parttotal     = :PARTTOTAL, "
00539         "    syndicatedepisodenumber = :SYNDICATENO, "
00540         "    airdate        = :AIRDATE,   originalairdate=:ORIGAIRDATE, "
00541         "    listingsource  = :LSOURCE, "
00542         "    seriesid       = :SERIESID,  programid     = :PROGRAMID, "
00543         "    previouslyshown = :PREVSHOWN "
00544         "WHERE chanid    = :CHANID AND "
00545         "      starttime = :OLDSTART ");
00546 
00547     query.bindValue(":CHANID",      chanid);
00548     query.bindValue(":OLDSTART",    match.starttime);
00549     query.bindValue(":TITLE",       denullify(ltitle));
00550     query.bindValue(":SUBTITLE",    denullify(lsubtitle));
00551     query.bindValue(":DESC",        denullify(ldesc));
00552     query.bindValue(":CATEGORY",    denullify(lcategory));
00553     query.bindValue(":CATTYPE",     lcattype);
00554     query.bindValue(":STARTTIME",   starttime);
00555     query.bindValue(":ENDTIME",     endtime);
00556     query.bindValue(":CC",          lsubtype & SUB_HARDHEAR ? true : false);
00557     query.bindValue(":HASSUBTITLES",lsubtype & SUB_NORMAL   ? true : false);
00558     query.bindValue(":STEREO",      laudio   & AUD_STEREO   ? true : false);
00559     query.bindValue(":HDTV",        lvideo   & VID_HDTV     ? true : false);
00560     query.bindValue(":SUBTYPE",     lsubtype);
00561     query.bindValue(":AUDIOPROP",   laudio);
00562     query.bindValue(":VIDEOPROP",   lvideo);
00563     query.bindValue(":PARTNO",      lpartnumber);
00564     query.bindValue(":PARTTOTAL",   lparttotal);
00565     query.bindValue(":SYNDICATENO", denullify(lsyndicatedepisodenumber));
00566     query.bindValue(":AIRDATE",     lairdate?QString::number(lairdate):"0000");
00567     query.bindValue(":ORIGAIRDATE", loriginalairdate);
00568     query.bindValue(":LSOURCE",     llistingsource);
00569     query.bindValue(":SERIESID",    denullify(lseriesId));
00570     query.bindValue(":PROGRAMID",   denullify(lprogramId));
00571     query.bindValue(":PREVSHOWN",   lpreviouslyshown);
00572 
00573     if (!query.exec())
00574     {
00575         MythDB::DBError("InsertDB", query);
00576         return 0;
00577     }
00578 
00579     if (credits)
00580     {
00581         for (uint i = 0; i < credits->size(); i++)
00582             (*credits)[i].InsertDB(query, chanid, starttime);
00583     }
00584 
00585     return 1;
00586 }
00587 
00588 static bool delete_program(MSqlQuery &query, uint chanid, const QDateTime &st)
00589 {
00590     query.prepare(
00591         "DELETE from program "
00592         "WHERE chanid    = :CHANID AND "
00593         "      starttime = :STARTTIME");
00594 
00595     query.bindValue(":CHANID",    chanid);
00596     query.bindValue(":STARTTIME", st);
00597 
00598     if (!query.exec())
00599     {
00600         MythDB::DBError("delete_program", query);
00601         return false;
00602     }
00603 
00604     query.prepare(
00605         "DELETE from credits "
00606         "WHERE chanid    = :CHANID AND "
00607         "      starttime = :STARTTIME");
00608 
00609     query.bindValue(":CHANID",    chanid);
00610     query.bindValue(":STARTTIME", st);
00611 
00612     if (!query.exec())
00613     {
00614         MythDB::DBError("delete_credits", query);
00615         return false;
00616     }
00617 
00618     return true;
00619 }
00620 
00621 static bool change_program(MSqlQuery &query, uint chanid, const QDateTime &st,
00622                            const QDateTime &new_st, const QDateTime &new_end)
00623 {
00624     query.prepare(
00625         "UPDATE program "
00626         "SET starttime = :NEWSTART, "
00627         "    endtime   = :NEWEND "
00628         "WHERE chanid    = :CHANID AND "
00629         "      starttime = :OLDSTART");
00630 
00631     query.bindValue(":CHANID",   chanid);
00632     query.bindValue(":OLDSTART", st);
00633     query.bindValue(":NEWSTART", new_st);
00634     query.bindValue(":NEWEND",   new_end);
00635 
00636     if (!query.exec())
00637     {
00638         MythDB::DBError("change_program", query);
00639         return false;
00640     }
00641 
00642     query.prepare(
00643         "UPDATE credits "
00644         "SET starttime = :NEWSTART "
00645         "WHERE chanid    = :CHANID AND "
00646         "      starttime = :OLDSTART");
00647 
00648     query.bindValue(":CHANID",   chanid);
00649     query.bindValue(":OLDSTART", st);
00650     query.bindValue(":NEWSTART", new_st);
00651 
00652     if (!query.exec())
00653     {
00654         MythDB::DBError("change_credits", query);
00655         return false;
00656     }
00657 
00658     return true;
00659 }
00660 
00661 bool DBEvent::MoveOutOfTheWayDB(
00662     MSqlQuery &query, uint chanid, const DBEvent &prog) const
00663 {
00664     if (prog.starttime >= starttime && prog.endtime <= endtime)
00665     {
00666         // inside current program
00667         return delete_program(query, chanid, prog.starttime);
00668     }
00669     else if (prog.starttime < starttime && prog.endtime > starttime)
00670     {
00671         // starts before, but ends during our program
00672         return change_program(query, chanid, prog.starttime,
00673                               prog.starttime, starttime);
00674     }
00675     else if (prog.starttime < endtime && prog.endtime > endtime)
00676     {
00677         // starts during, but ends after our program
00678         return change_program(query, chanid, prog.starttime,
00679                               endtime, prog.endtime);
00680     }
00681     // must be non-conflicting...
00682     return true;
00683 }
00684 
00685 uint DBEvent::InsertDB(MSqlQuery &query, uint chanid) const
00686 {
00687     query.prepare(
00688         "REPLACE INTO program ("
00689         "  chanid,         title,          subtitle,        description, "
00690         "  category,       category_type, "
00691         "  starttime,      endtime, "
00692         "  closecaptioned, stereo,         hdtv,            subtitled, "
00693         "  subtitletypes,  audioprop,      videoprop, "
00694         "  stars,          partnumber,     parttotal, "
00695         "  syndicatedepisodenumber, "
00696         "  airdate,        originalairdate,listingsource, "
00697         "  seriesid,       programid,      previouslyshown ) "
00698         "VALUES ("
00699         " :CHANID,        :TITLE,         :SUBTITLE,       :DESCRIPTION, "
00700         " :CATEGORY,      :CATTYPE, "
00701         " :STARTTIME,     :ENDTIME, "
00702         " :CC,            :STEREO,        :HDTV,           :HASSUBTITLES, "
00703         " :SUBTYPES,      :AUDIOPROP,     :VIDEOPROP, "
00704         " :STARS,         :PARTNUMBER,    :PARTTOTAL, "
00705         " :SYNDICATENO, "
00706         " :AIRDATE,       :ORIGAIRDATE,   :LSOURCE, "
00707         " :SERIESID,      :PROGRAMID,     :PREVSHOWN) ");
00708 
00709     QString cattype = myth_category_type_to_string(categoryType);
00710     QString empty("");
00711     query.bindValue(":CHANID",      chanid);
00712     query.bindValue(":TITLE",       denullify(title));
00713     query.bindValue(":SUBTITLE",    denullify(subtitle));
00714     query.bindValue(":DESCRIPTION", denullify(description));
00715     query.bindValue(":CATEGORY",    denullify(category));
00716     query.bindValue(":CATTYPE",     cattype);
00717     query.bindValue(":STARTTIME",   starttime);
00718     query.bindValue(":ENDTIME",     endtime);
00719     query.bindValue(":CC",          subtitleType & SUB_HARDHEAR ? true : false);
00720     query.bindValue(":STEREO",      audioProps   & AUD_STEREO   ? true : false);
00721     query.bindValue(":HDTV",        videoProps   & VID_HDTV     ? true : false);
00722     query.bindValue(":HASSUBTITLES",subtitleType & SUB_NORMAL   ? true : false);
00723     query.bindValue(":SUBTYPES",    subtitleType);
00724     query.bindValue(":AUDIOPROP",   audioProps);
00725     query.bindValue(":VIDEOPROP",   videoProps);
00726     query.bindValue(":STARS",       stars);
00727     query.bindValue(":PARTNUMBER",  partnumber);
00728     query.bindValue(":PARTTOTAL",   parttotal);
00729     query.bindValue(":SYNDICATENO", denullify(syndicatedepisodenumber));
00730     query.bindValue(":AIRDATE",     airdate ? QString::number(airdate):"0000");
00731     query.bindValue(":ORIGAIRDATE", originalairdate);
00732     query.bindValue(":LSOURCE",     listingsource);
00733     query.bindValue(":SERIESID",    denullify(seriesId));
00734     query.bindValue(":PROGRAMID",   denullify(programId));
00735     query.bindValue(":PREVSHOWN",   previouslyshown);
00736 
00737     if (!query.exec())
00738     {
00739         MythDB::DBError("InsertDB", query);
00740         return 0;
00741     }
00742 
00743     if (credits)
00744     {
00745         for (uint i = 0; i < credits->size(); i++)
00746             (*credits)[i].InsertDB(query, chanid, starttime);
00747     }
00748 
00749     return 1;
00750 }
00751 
00752 ProgInfo::ProgInfo(const ProgInfo &other) :
00753     DBEvent(other.listingsource)
00754 {
00755     *this = other;
00756 }
00757 
00758 ProgInfo &ProgInfo::operator=(const ProgInfo &other)
00759 {
00760     if (this == &other)
00761         return *this;
00762 
00763     DBEvent::operator=(other);
00764 
00765     channel         = other.channel;
00766     startts         = other.startts;
00767     endts           = other.endts;
00768     stars           = other.stars;
00769     title_pronounce = other.title_pronounce;
00770     showtype        = other.showtype;
00771     colorcode       = other.colorcode;
00772     clumpidx        = other.clumpidx;
00773     clumpmax        = other.clumpmax;
00774 
00775     channel.squeeze();
00776     startts.squeeze();
00777     endts.squeeze();
00778     stars.squeeze();
00779     title_pronounce.squeeze();
00780     showtype.squeeze();
00781     colorcode.squeeze();
00782     clumpidx.squeeze();
00783     clumpmax.squeeze();
00784 
00785     return *this;
00786 }
00787 
00788 void ProgInfo::Squeeze(void)
00789 {
00790     DBEvent::Squeeze();
00791     channel.squeeze();
00792     startts.squeeze();
00793     endts.squeeze();
00794     stars.squeeze();
00795     title_pronounce.squeeze();
00796     showtype.squeeze();
00797     colorcode.squeeze();
00798     clumpidx.squeeze();
00799     clumpmax.squeeze();
00800 }
00801 
00802 uint ProgInfo::InsertDB(MSqlQuery &query, uint chanid) const
00803 {
00804     LOG(VB_XMLTV, LOG_INFO,
00805         QString("Inserting new program    : %1 - %2 %3 %4")
00806             .arg(starttime.toString(Qt::ISODate))
00807             .arg(endtime.toString(Qt::ISODate))
00808             .arg(channel)
00809             .arg(title));
00810 
00811     query.prepare(
00812         "REPLACE INTO program ("
00813         "  chanid,         title,          subtitle,        description, "
00814         "  category,       category_type,  "
00815         "  starttime,      endtime, "
00816         "  closecaptioned, stereo,         hdtv,            subtitled, "
00817         "  subtitletypes,  audioprop,      videoprop, "
00818         "  partnumber,     parttotal, "
00819         "  syndicatedepisodenumber, "
00820         "  airdate,        originalairdate,listingsource, "
00821         "  seriesid,       programid,      previouslyshown, "
00822         "  stars,          showtype,       title_pronounce, colorcode ) "
00823 
00824         "VALUES("
00825         " :CHANID,        :TITLE,         :SUBTITLE,       :DESCRIPTION, "
00826         " :CATEGORY,      :CATTYPE,       "
00827         " :STARTTIME,     :ENDTIME, "
00828         " :CC,            :STEREO,        :HDTV,           :HASSUBTITLES, "
00829         " :SUBTYPES,      :AUDIOPROP,     :VIDEOPROP, "
00830         " :PARTNUMBER,    :PARTTOTAL, "
00831         " :SYNDICATENO, "
00832         " :AIRDATE,       :ORIGAIRDATE,   :LSOURCE, "
00833         " :SERIESID,      :PROGRAMID,     :PREVSHOWN, "
00834         " :STARS,         :SHOWTYPE,      :TITLEPRON,      :COLORCODE)");
00835 
00836     QString cattype = myth_category_type_to_string(categoryType);
00837 
00838     query.bindValue(":CHANID",      chanid);
00839     query.bindValue(":TITLE",       denullify(title));
00840     query.bindValue(":SUBTITLE",    denullify(subtitle));
00841     query.bindValue(":DESCRIPTION", denullify(description));
00842     query.bindValue(":CATEGORY",    denullify(category));
00843     query.bindValue(":CATTYPE",     cattype);
00844     query.bindValue(":STARTTIME",   starttime);
00845     query.bindValue(":ENDTIME",     endtime);
00846     query.bindValue(":CC",
00847                     subtitleType & SUB_HARDHEAR ? true : false);
00848     query.bindValue(":STEREO",
00849                     audioProps   & AUD_STEREO   ? true : false);
00850     query.bindValue(":HDTV",
00851                     videoProps   & VID_HDTV     ? true : false);
00852     query.bindValue(":HASSUBTITLES",
00853                     subtitleType & SUB_NORMAL   ? true : false);
00854     query.bindValue(":SUBTYPES",    subtitleType);
00855     query.bindValue(":AUDIOPROP",   audioProps);
00856     query.bindValue(":VIDEOPROP",   videoProps);
00857     query.bindValue(":PARTNUMBER",  partnumber);
00858     query.bindValue(":PARTTOTAL",   parttotal);
00859     query.bindValue(":SYNDICATENO", denullify(syndicatedepisodenumber));
00860     query.bindValue(":AIRDATE",     airdate ? QString::number(airdate):"0000");
00861     query.bindValue(":ORIGAIRDATE", originalairdate);
00862     query.bindValue(":LSOURCE",     listingsource);
00863     query.bindValue(":SERIESID",    denullify(seriesId));
00864     query.bindValue(":PROGRAMID",   denullify(programId));
00865     query.bindValue(":PREVSHOWN",   previouslyshown);
00866     query.bindValue(":STARS",       stars);
00867     query.bindValue(":SHOWTYPE",    showtype);
00868     query.bindValue(":TITLEPRON",   title_pronounce);
00869     query.bindValue(":COLORCODE",   colorcode);
00870 
00871     if (!query.exec())
00872     {
00873         MythDB::DBError("program insert", query);
00874         return 0;
00875     }
00876 
00877     QList<EventRating>::const_iterator j = ratings.begin();
00878     for (; j != ratings.end(); ++j)
00879     {
00880         query.prepare(
00881             "INSERT INTO programrating "
00882             "       ( chanid, starttime, system, rating) "
00883             "VALUES (:CHANID, :START,    :SYS,  :RATING)");
00884         query.bindValue(":CHANID", chanid);
00885         query.bindValue(":START",  starttime);
00886         query.bindValue(":SYS",    (*j).system);
00887         query.bindValue(":RATING", (*j).rating);
00888 
00889         if (!query.exec())
00890             MythDB::DBError("programrating insert", query);
00891     }
00892 
00893     if (credits)
00894     {
00895         for (uint i = 0; i < credits->size(); ++i)
00896             (*credits)[i].InsertDB(query, chanid, starttime);
00897     }
00898 
00899     return 1;
00900 }
00901 
00902 bool ProgramData::ClearDataByChannel(
00903     uint chanid, const QDateTime &from, const QDateTime &to,
00904     bool use_channel_time_offset)
00905 {
00906     int secs = 0;
00907     if (use_channel_time_offset)
00908         secs = ChannelUtil::GetTimeOffset(chanid) * 60;
00909 
00910     QDateTime newFrom = from.addSecs(secs);
00911     QDateTime newTo   = to.addSecs(secs);
00912 
00913     MSqlQuery query(MSqlQuery::InitCon());
00914     query.prepare("DELETE FROM program "
00915                   "WHERE starttime >= :FROM AND starttime < :TO "
00916                   "AND chanid = :CHANID ;");
00917     query.bindValue(":FROM", newFrom);
00918     query.bindValue(":TO", newTo);
00919     query.bindValue(":CHANID", chanid);
00920     bool ok = query.exec();
00921 
00922     query.prepare("DELETE FROM programrating "
00923                   "WHERE starttime >= :FROM AND starttime < :TO "
00924                   "AND chanid = :CHANID ;");
00925     query.bindValue(":FROM", newFrom);
00926     query.bindValue(":TO", newTo);
00927     query.bindValue(":CHANID", chanid);
00928     ok &= query.exec();
00929 
00930     query.prepare("DELETE FROM credits "
00931                   "WHERE starttime >= :FROM AND starttime < :TO "
00932                   "AND chanid = :CHANID ;");
00933     query.bindValue(":FROM", newFrom);
00934     query.bindValue(":TO", newTo);
00935     query.bindValue(":CHANID", chanid);
00936     ok &= query.exec();
00937 
00938     query.prepare("DELETE FROM programgenres "
00939                   "WHERE starttime >= :FROM AND starttime < :TO "
00940                   "AND chanid = :CHANID ;");
00941     query.bindValue(":FROM", newFrom);
00942     query.bindValue(":TO", newTo);
00943     query.bindValue(":CHANID", chanid);
00944     ok &= query.exec();
00945 
00946     return ok;
00947 }
00948 
00949 bool ProgramData::ClearDataBySource(
00950     uint sourceid, const QDateTime &from, const QDateTime &to,
00951     bool use_channel_time_offset)
00952 {
00953     vector<uint> chanids = ChannelUtil::GetChanIDs(sourceid);
00954 
00955     bool ok = true;
00956     for (uint i = 0; i < chanids.size(); i++)
00957         ok &= ClearDataByChannel(chanids[i], from, to, use_channel_time_offset);
00958 
00959     return ok;
00960 }
00961 
00962 static bool start_time_less_than(const DBEvent *a, const DBEvent *b)
00963 {
00964     return (a->starttime < b->starttime);
00965 }
00966 
00967 void ProgramData::FixProgramList(QList<ProgInfo*> &fixlist)
00968 {
00969     qStableSort(fixlist.begin(), fixlist.end(), start_time_less_than);
00970 
00971     QList<ProgInfo*>::iterator it = fixlist.begin();
00972     while (1)
00973     {
00974         QList<ProgInfo*>::iterator cur = it;
00975         ++it;
00976 
00977         // fill in miss stop times
00978         if ((*cur)->endts.isEmpty() || (*cur)->startts > (*cur)->endts)
00979         {
00980             if (it != fixlist.end())
00981             {
00982                 (*cur)->endts   = (*it)->startts;
00983                 (*cur)->endtime = (*it)->starttime;
00984             }
00985             else
00986             {
00987                 (*cur)->endtime = (*cur)->starttime;
00988                 if ((*cur)->endtime < QDateTime((*cur)->endtime.date(), QTime(6, 0)))
00989                 {
00990                     (*cur)->endtime.setTime(QTime(6, 0));
00991                 }
00992                 else
00993                 {
00994                    (*cur)->endtime.setTime(QTime(0, 0));
00995                    (*cur)->endtime.setDate((*cur)->endtime.date().addDays(1));
00996                 }
00997 
00998                 QString    datestr = (*cur)->endtime.toString("yyyyMMddhhmmss");
00999                 QByteArray datearr = datestr.toAscii();
01000                 (*cur)->endts = QString(datearr.constData());
01001             }
01002         }
01003 
01004         if (it == fixlist.end())
01005             break;
01006 
01007         // remove overlapping programs
01008         if ((*cur)->HasTimeConflict(**it))
01009         {
01010             QList<ProgInfo*>::iterator tokeep, todelete;
01011 
01012             if ((*cur)->endtime <= (*cur)->starttime)
01013                 tokeep = it, todelete = cur;
01014             else if ((*it)->endtime <= (*it)->starttime)
01015                 tokeep = cur, todelete = it;
01016             else if (!(*cur)->subtitle.isEmpty() &&
01017                      (*it)->subtitle.isEmpty())
01018                 tokeep = cur, todelete = it;
01019             else if (!(*it)->subtitle.isEmpty()  &&
01020                      (*cur)->subtitle.isEmpty())
01021                 tokeep = it, todelete = cur;
01022             else if (!(*cur)->description.isEmpty() &&
01023                      (*it)->description.isEmpty())
01024                 tokeep = cur, todelete = it;
01025             else
01026                 tokeep = it, todelete = cur;
01027 
01028 
01029             LOG(VB_XMLTV, LOG_INFO,
01030                 QString("Removing conflicting program: %1 - %2 %3 %4")
01031                     .arg((*todelete)->starttime.toString(Qt::ISODate))
01032                     .arg((*todelete)->endtime.toString(Qt::ISODate))
01033                     .arg((*todelete)->channel)
01034                     .arg((*todelete)->title));
01035 
01036             LOG(VB_XMLTV, LOG_INFO,
01037                 QString("Conflicted with            : %1 - %2 %3 %4")
01038                     .arg((*tokeep)->starttime.toString(Qt::ISODate))
01039                     .arg((*tokeep)->endtime.toString(Qt::ISODate))
01040                     .arg((*tokeep)->channel)
01041                     .arg((*tokeep)->title));
01042 
01043             bool step_back = todelete == it;
01044             it = fixlist.erase(todelete);
01045             if (step_back)
01046                 --it;
01047         }
01048     }
01049 }
01050 
01051 void ProgramData::HandlePrograms(
01052     uint sourceid, QMap<QString, QList<ProgInfo> > &proglist)
01053 {
01054     uint unchanged = 0, updated = 0;
01055 
01056     MSqlQuery query(MSqlQuery::InitCon());
01057 
01058     QMap<QString, QList<ProgInfo> >::const_iterator mapiter;
01059     for (mapiter = proglist.begin(); mapiter != proglist.end(); ++mapiter)
01060     {
01061         if (mapiter.key().isEmpty())
01062             continue;
01063 
01064         query.prepare(
01065             "SELECT chanid "
01066             "FROM channel "
01067             "WHERE sourceid = :ID AND "
01068             "      xmltvid  = :XMLTVID");
01069         query.bindValue(":ID",      sourceid);
01070         query.bindValue(":XMLTVID", mapiter.key());
01071 
01072         if (!query.exec())
01073         {
01074             MythDB::DBError("ProgramData::HandlePrograms", query);
01075             continue;
01076         }
01077 
01078         vector<uint> chanids;
01079         while (query.next())
01080             chanids.push_back(query.value(0).toUInt());
01081 
01082         if (chanids.empty())
01083         {
01084             LOG(VB_GENERAL, LOG_NOTICE,
01085                 QString("Unknown xmltv channel identifier: %1"
01086                         " - Skipping channel.").arg(mapiter.key()));
01087             continue;
01088         }
01089 
01090         QList<ProgInfo> &list = proglist[mapiter.key()];
01091         QList<ProgInfo*> sortlist;
01092         QList<ProgInfo>::iterator it = list.begin();
01093         for (; it != list.end(); ++it)
01094             sortlist.push_back(&(*it));
01095 
01096         FixProgramList(sortlist);
01097 
01098         for (uint i = 0; i < chanids.size(); ++i)
01099         {
01100             HandlePrograms(query, chanids[i], sortlist, unchanged, updated);
01101         }
01102     }
01103 
01104     LOG(VB_GENERAL, LOG_INFO,
01105         QString("Updated programs: %1 Unchanged programs: %2")
01106                 .arg(updated) .arg(unchanged));
01107 }
01108 
01109 void ProgramData::HandlePrograms(MSqlQuery             &query,
01110                                  uint                   chanid,
01111                                  const QList<ProgInfo*> &sortlist,
01112                                  uint &unchanged,
01113                                  uint &updated)
01114 {
01115     QList<ProgInfo*>::const_iterator it = sortlist.begin();
01116     for (; it != sortlist.end(); ++it)
01117     {
01118         if (IsUnchanged(query, chanid, **it))
01119         {
01120             unchanged++;
01121             continue;
01122         }
01123 
01124         if (!DeleteOverlaps(query, chanid, **it))
01125             continue;
01126 
01127         updated += (*it)->InsertDB(query, chanid);
01128     }
01129 }
01130 
01131 int ProgramData::fix_end_times(void)
01132 {
01133     int count = 0;
01134     QString chanid, starttime, endtime, querystr;
01135     MSqlQuery query1(MSqlQuery::InitCon()), query2(MSqlQuery::InitCon());
01136 
01137     querystr = "SELECT chanid, starttime, endtime FROM program "
01138                "WHERE (DATE_FORMAT(endtime,'%H%i') = '0000') "
01139                "ORDER BY chanid, starttime;";
01140 
01141     if (!query1.exec(querystr))
01142     {
01143         LOG(VB_GENERAL, LOG_ERR,
01144             QString("fix_end_times query failed: %1").arg(querystr));
01145         return -1;
01146     }
01147 
01148     while (query1.next())
01149     {
01150         starttime = query1.value(1).toString();
01151         chanid = query1.value(0).toString();
01152         endtime = query1.value(2).toString();
01153 
01154         querystr = QString("SELECT chanid, starttime, endtime FROM program "
01155                            "WHERE starttime BETWEEN '%1 00:00:00'"
01156                            "AND '%2 23:59:59' AND chanid = '%3' "
01157                            "ORDER BY starttime LIMIT 1;")
01158                            .arg(endtime.left(10))
01159                            .arg(endtime.left(10))
01160                            .arg(chanid);
01161 
01162         if (!query2.exec(querystr))
01163         {
01164             LOG(VB_GENERAL, LOG_ERR,
01165                 QString("fix_end_times query failed: %1").arg(querystr));
01166             return -1;
01167         }
01168 
01169         if (query2.next() && (endtime != query2.value(1).toString()))
01170         {
01171             count++;
01172             endtime = query2.value(1).toString();
01173             querystr = QString("UPDATE program SET starttime = '%1', "
01174                                "endtime = '%2' WHERE (chanid = '%3' AND "
01175                                "starttime = '%4');")
01176                                .arg(starttime)
01177                                .arg(endtime)
01178                                .arg(chanid)
01179                                .arg(starttime);
01180 
01181             if (!query2.exec(querystr))
01182             {
01183                 LOG(VB_GENERAL, LOG_ERR,
01184                     QString("fix_end_times query failed: %1").arg(querystr));
01185                 return -1;
01186             }
01187         }
01188     }
01189 
01190     return count;
01191 }
01192 
01193 bool ProgramData::IsUnchanged(
01194     MSqlQuery &query, uint chanid, const ProgInfo &pi)
01195 {
01196     query.prepare(
01197         "SELECT count(*) "
01198         "FROM program "
01199         "WHERE chanid          = :CHANID     AND "
01200         "      starttime       = :START      AND "
01201         "      endtime         = :END        AND "
01202         "      title           = :TITLE      AND "
01203         "      subtitle        = :SUBTITLE   AND "
01204         "      description     = :DESC       AND "
01205         "      category        = :CATEGORY   AND "
01206         "      category_type   = :CATEGORY_TYPE AND "
01207         "      airdate         = :AIRDATE    AND "
01208         "      stars >= (:STARS1 - 0.001)    AND "
01209         "      stars <= (:STARS2 + 0.001)    AND "
01210         "      previouslyshown = :PREVIOUSLYSHOWN AND "
01211         "      title_pronounce = :TITLE_PRONOUNCE AND "
01212         "      audioprop       = :AUDIOPROP  AND "
01213         "      videoprop       = :VIDEOPROP  AND "
01214         "      subtitletypes   = :SUBTYPES   AND "
01215         "      partnumber      = :PARTNUMBER AND "
01216         "      parttotal       = :PARTTOTAL  AND "
01217         "      seriesid        = :SERIESID   AND "
01218         "      showtype        = :SHOWTYPE   AND "
01219         "      colorcode       = :COLORCODE  AND "
01220         "      syndicatedepisodenumber = :SYNDICATEDEPISODENUMBER AND "
01221         "      programid       = :PROGRAMID");
01222 
01223     QString cattype = myth_category_type_to_string(pi.categoryType);
01224 
01225     query.bindValue(":CHANID",     chanid);
01226     query.bindValue(":START",      pi.starttime);
01227     query.bindValue(":END",        pi.endtime);
01228     query.bindValue(":TITLE",      denullify(pi.title));
01229     query.bindValue(":SUBTITLE",   denullify(pi.subtitle));
01230     query.bindValue(":DESC",       denullify(pi.description));
01231     query.bindValue(":CATEGORY",   denullify(pi.category));
01232     query.bindValue(":CATEGORY_TYPE", cattype);
01233     query.bindValue(":AIRDATE",    pi.airdate);
01234     query.bindValue(":STARS1",     pi.stars);
01235     query.bindValue(":STARS2",     pi.stars);
01236     query.bindValue(":PREVIOUSLYSHOWN", pi.previouslyshown);
01237     query.bindValue(":TITLE_PRONOUNCE", pi.title_pronounce);
01238     query.bindValue(":AUDIOPROP",  pi.audioProps);
01239     query.bindValue(":VIDEOPROP",  pi.videoProps);
01240     query.bindValue(":SUBTYPES",   pi.subtitleType);
01241     query.bindValue(":PARTNUMBER", pi.partnumber);
01242     query.bindValue(":PARTTOTAL",  pi.parttotal);
01243     query.bindValue(":SERIESID",   denullify(pi.seriesId));
01244     query.bindValue(":SHOWTYPE",   pi.showtype);
01245     query.bindValue(":COLORCODE",  pi.colorcode);
01246     query.bindValue(":SYNDICATEDEPISODENUMBER",
01247                     denullify(pi.syndicatedepisodenumber));
01248     query.bindValue(":PROGRAMID",  denullify(pi.programId));
01249 
01250     if (query.exec() && query.next())
01251         return query.value(0).toUInt() > 0;
01252 
01253     return false;
01254 }
01255 
01256 bool ProgramData::DeleteOverlaps(
01257     MSqlQuery &query, uint chanid, const ProgInfo &pi)
01258 {
01259     if (VERBOSE_LEVEL_CHECK(VB_XMLTV, LOG_INFO))
01260     {
01261         // Get overlaps..
01262         query.prepare(
01263             "SELECT title,starttime,endtime "
01264             "FROM program "
01265             "WHERE chanid     = :CHANID AND "
01266             "      starttime >= :START AND "
01267             "      starttime <  :END;");
01268         query.bindValue(":CHANID", chanid);
01269         query.bindValue(":START",  pi.starttime);
01270         query.bindValue(":END",    pi.endtime);
01271 
01272         if (!query.exec())
01273             return false;
01274 
01275         if (!query.next())
01276             return true;
01277 
01278         do
01279         {
01280             LOG(VB_XMLTV, LOG_INFO,
01281                 QString("Removing existing program: %1 - %2 %3 %4")
01282                     .arg(query.value(1).toDateTime().toString(Qt::ISODate))
01283                     .arg(query.value(2).toDateTime().toString(Qt::ISODate))
01284                     .arg(pi.channel)
01285                     .arg(query.value(0).toString()));
01286         } while (query.next());
01287     }
01288 
01289     if (!ClearDataByChannel(chanid, pi.starttime, pi.endtime, false))
01290     {
01291         LOG(VB_XMLTV, LOG_ERR,
01292             QString("Program delete failed    : %1 - %2 %3 %4")
01293                 .arg(pi.starttime.toString(Qt::ISODate))
01294                 .arg(pi.endtime.toString(Qt::ISODate))
01295                 .arg(pi.channel)
01296                 .arg(pi.title));
01297         return false;
01298     }
01299 
01300     return true;
01301 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends