MythTV  0.26-pre
channelutil.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 
00003 #include <stdint.h>
00004 
00005 #include <algorithm>
00006 #include <set>
00007 using namespace std;
00008 
00009 #include <QRegExp>
00010 #include <QImage>
00011 #include <QFile>
00012 #include <QReadWriteLock>
00013 #include <QHash>
00014 
00015 #include "channelutil.h"
00016 #include "mythdb.h"
00017 #include "dvbtables.h"
00018 #include "tv.h" // for CHANNEL_DIRECTION
00019 
00020 #define LOC QString("ChanUtil: ")
00021 
00022 const QString ChannelUtil::kATSCSeparators = "(_|-|#|\\.)";
00023 
00024 static uint get_dtv_multiplex(uint     db_source_id,  QString sistandard,
00025                               uint64_t frequency,
00026                               // DVB specific
00027                               uint     transport_id,
00028                               // tsid exists with other sistandards,
00029                               // but we only trust it in dvb-land.
00030                               uint     network_id,
00031                               // must check polarity for dvb-s
00032                               signed char polarity)
00033 {
00034     QString qstr =
00035         "SELECT mplexid "
00036         "FROM dtv_multiplex "
00037         "WHERE sourceid     = :SOURCEID   "
00038         "  AND sistandard   = :SISTANDARD ";
00039 
00040     if (sistandard.toLower() != "dvb")
00041         qstr += "AND frequency    = :FREQUENCY   ";
00042     else
00043     {
00044         qstr += "AND transportid  = :TRANSPORTID ";
00045         qstr += "AND networkid    = :NETWORKID   ";
00046         qstr += "AND polarity     = :POLARITY    ";
00047     }
00048 
00049 
00050     MSqlQuery query(MSqlQuery::InitCon());
00051     query.prepare(qstr);
00052 
00053     query.bindValue(":SOURCEID",          db_source_id);
00054     query.bindValue(":SISTANDARD",        sistandard);
00055 
00056     if (sistandard.toLower() != "dvb")
00057         query.bindValue(":FREQUENCY",   QString::number(frequency));
00058     else
00059     {
00060         query.bindValue(":TRANSPORTID", transport_id);
00061         query.bindValue(":NETWORKID",   network_id);
00062         query.bindValue(":POLARITY",    QString(polarity));
00063     }
00064 
00065     if (!query.exec() || !query.isActive())
00066     {
00067         MythDB::DBError("get_dtv_multiplex", query);
00068         return 0;
00069     }
00070 
00071     if (query.next())
00072         return query.value(0).toUInt();
00073 
00074     return 0;
00075 }
00076 
00077 static uint insert_dtv_multiplex(
00078     int         db_source_id,  QString     sistandard,
00079     uint64_t    frequency,     QString     modulation,
00080     // DVB specific
00081     int         transport_id,  int         network_id,
00082     int         symbol_rate,   signed char bandwidth,
00083     signed char polarity,      signed char inversion,
00084     signed char trans_mode,
00085     QString     inner_FEC,     QString      constellation,
00086     signed char hierarchy,     QString      hp_code_rate,
00087     QString     lp_code_rate,  QString      guard_interval,
00088     QString     mod_sys,       QString      rolloff)
00089 {
00090     MSqlQuery query(MSqlQuery::InitCon());
00091 
00092     // If transport is already present, skip insert
00093     uint mplex = get_dtv_multiplex(
00094         db_source_id,  sistandard,    frequency,
00095         // DVB specific
00096         transport_id,  network_id, polarity);
00097 
00098     LOG(VB_CHANSCAN, LOG_INFO, QString(
00099                 "insert_dtv_multiplex(db_source_id: %1, sistandard: '%2', "
00100                 "frequency: %3, modulation: %4, transport_id: %5, "
00101                 "network_id: %6, polarity: %7...) mplexid:%8")
00102             .arg(db_source_id).arg(sistandard)
00103             .arg(frequency).arg(modulation)
00104             .arg(transport_id).arg(network_id)
00105             .arg(polarity).arg(mplex));
00106 
00107     bool isDVB = (sistandard.toLower() == "dvb");
00108 
00109     QString updateStr =
00110         "UPDATE dtv_multiplex "
00111         "SET frequency    = :FREQUENCY1, ";
00112 
00113     updateStr += (!modulation.isNull()) ?
00114         "modulation       = :MODULATION, " : "";
00115     updateStr += (symbol_rate >= 0) ?
00116         "symbolrate       = :SYMBOLRATE, " : "";
00117     updateStr += (bandwidth   >= 0) ?
00118         "bandwidth        = :BANDWIDTH, " : "";
00119     updateStr += (polarity    >= 0) ?
00120         "polarity         = :POLARITY, " : "";
00121     updateStr += (inversion   >= 0) ?
00122         "inversion        = :INVERSION, " : "";
00123     updateStr += (trans_mode  >= 0) ?
00124         "transmission_mode= :TRANS_MODE, " : "";
00125     updateStr += (!inner_FEC.isNull()) ?
00126         "fec              = :INNER_FEC, " : "";
00127     updateStr += (!constellation.isNull()) ?
00128         "constellation    = :CONSTELLATION, " : "";
00129     updateStr += (hierarchy  >= 0) ?
00130         "hierarchy        = :HIERARCHY, " : "";
00131     updateStr += (!hp_code_rate.isNull()) ?
00132         "hp_code_rate     = :HP_CODE_RATE, " : "";
00133     updateStr += (!lp_code_rate.isNull()) ?
00134         "lp_code_rate     = :LP_CODE_RATE, " : "";
00135     updateStr += (!guard_interval.isNull()) ?
00136         "guard_interval   = :GUARD_INTERVAL, " : "";
00137     updateStr += (!mod_sys.isNull()) ?
00138         "mod_sys          = :MOD_SYS, " : "";
00139     updateStr += (symbol_rate >= 0) ?
00140         "rolloff          = :ROLLOFF, " : "";
00141     updateStr += (transport_id && !isDVB) ?
00142         "transportid      = :TRANSPORTID, " : "";
00143 
00144     updateStr = updateStr.left(updateStr.length()-2) + " ";
00145 
00146     updateStr +=
00147         "WHERE sourceid    = :SOURCEID      AND "
00148         "      sistandard  = :SISTANDARD    AND ";
00149 
00150     updateStr += (isDVB) ?
00151         " polarity     = :WHEREPOLARITY      AND "
00152         " transportid = :TRANSPORTID AND networkid = :NETWORKID " :
00153         " frequency = :FREQUENCY2 ";
00154 
00155     QString insertStr =
00156         "INSERT INTO dtv_multiplex "
00157         "  (sourceid,        sistandard,        frequency,  ";
00158 
00159     insertStr += (!modulation.isNull())     ? "modulation, "        : "";
00160     insertStr += (transport_id || isDVB)    ? "transportid, "       : "";
00161     insertStr += (isDVB)                    ? "networkid, "         : "";
00162     insertStr += (symbol_rate >= 0)         ? "symbolrate, "        : "";
00163     insertStr += (bandwidth   >= 0)         ? "bandwidth, "         : "";
00164     insertStr += (polarity    >= 0)         ? "polarity, "          : "";
00165     insertStr += (inversion   >= 0)         ? "inversion, "         : "";
00166     insertStr += (trans_mode  >= 0)         ? "transmission_mode, " : "";
00167     insertStr += (!inner_FEC.isNull())      ? "fec, "               : "";
00168     insertStr += (!constellation.isNull())  ? "constellation, "     : "";
00169     insertStr += (hierarchy  >= 0)          ? "hierarchy, "         : "";
00170     insertStr += (!hp_code_rate.isNull())   ? "hp_code_rate, "      : "";
00171     insertStr += (!lp_code_rate.isNull())   ? "lp_code_rate, "      : "";
00172     insertStr += (!guard_interval.isNull()) ? "guard_interval, "    : "";
00173     insertStr += (!mod_sys.isNull())        ? "mod_sys, "           : "";
00174     insertStr += (!rolloff.isNull())        ? "rolloff, "           : "";
00175     insertStr = insertStr.left(insertStr.length()-2) + ") ";
00176 
00177     insertStr +=
00178         "VALUES "
00179         "  (:SOURCEID,      :SISTANDARD,       :FREQUENCY1, ";
00180     insertStr += (!modulation.isNull())     ? ":MODULATION, "       : "";
00181     insertStr += (transport_id || isDVB)    ? ":TRANSPORTID, "      : "";
00182     insertStr += (isDVB)                    ? ":NETWORKID, "        : "";
00183     insertStr += (symbol_rate >= 0)         ? ":SYMBOLRATE, "       : "";
00184     insertStr += (bandwidth   >= 0)         ? ":BANDWIDTH, "        : "";
00185     insertStr += (polarity    >= 0)         ? ":POLARITY, "         : "";
00186     insertStr += (inversion   >= 0)         ? ":INVERSION, "        : "";
00187     insertStr += (trans_mode  >= 0)         ? ":TRANS_MODE, "       : "";
00188     insertStr += (!inner_FEC.isNull())      ? ":INNER_FEC, "        : "";
00189     insertStr += (!constellation.isNull())  ? ":CONSTELLATION, "    : "";
00190     insertStr += (hierarchy   >= 0)         ? ":HIERARCHY, "        : "";
00191     insertStr += (!hp_code_rate.isNull())   ? ":HP_CODE_RATE, "     : "";
00192     insertStr += (!lp_code_rate.isNull())   ? ":LP_CODE_RATE, "     : "";
00193     insertStr += (!guard_interval.isNull()) ? ":GUARD_INTERVAL, "   : "";
00194     insertStr += (!mod_sys.isNull())        ? ":MOD_SYS, "          : "";
00195     insertStr += (!rolloff.isNull())        ? ":ROLLOFF, "          : "";
00196     insertStr = insertStr.left(insertStr.length()-2) + ");";
00197 
00198     query.prepare((mplex) ? updateStr : insertStr);
00199 
00200     query.bindValue(":SOURCEID",          db_source_id);
00201     query.bindValue(":SISTANDARD",        sistandard);
00202     query.bindValue(":FREQUENCY1",        QString::number(frequency));
00203 
00204     if (mplex)
00205     {
00206         if (isDVB)
00207         {
00208             query.bindValue(":TRANSPORTID",   transport_id);
00209             query.bindValue(":NETWORKID",    network_id);
00210             query.bindValue(":WHEREPOLARITY", QString(polarity));
00211         }
00212         else
00213         {
00214             query.bindValue(":FREQUENCY2",    QString::number(frequency));
00215             if (transport_id)
00216                 query.bindValue(":TRANSPORTID", transport_id);
00217         }
00218     }
00219     else
00220     {
00221         if (transport_id || isDVB)
00222             query.bindValue(":TRANSPORTID",   transport_id);
00223         if (isDVB)
00224             query.bindValue(":NETWORKID",    network_id);
00225     }
00226 
00227     if (!modulation.isNull())
00228         query.bindValue(":MODULATION",    modulation);
00229 
00230     if (symbol_rate >= 0)
00231         query.bindValue(":SYMBOLRATE",    symbol_rate);
00232     if (bandwidth >= 0)
00233         query.bindValue(":BANDWIDTH",     QString("%1").arg((char)bandwidth));
00234     if (polarity >= 0)
00235         query.bindValue(":POLARITY",      QString("%1").arg((char)polarity));
00236     if (inversion >= 0)
00237         query.bindValue(":INVERSION",     QString("%1").arg((char)inversion));
00238     if (trans_mode >= 0)
00239         query.bindValue(":TRANS_MODE",    QString("%1").arg((char)trans_mode));
00240 
00241     if (!inner_FEC.isNull())
00242         query.bindValue(":INNER_FEC",     inner_FEC);
00243     if (!constellation.isNull())
00244         query.bindValue(":CONSTELLATION", constellation);
00245     if (hierarchy >= 0)
00246         query.bindValue(":HIERARCHY",     QString("%1").arg((char)hierarchy));
00247     if (!hp_code_rate.isNull())
00248         query.bindValue(":HP_CODE_RATE",  hp_code_rate);
00249     if (!lp_code_rate.isNull())
00250         query.bindValue(":LP_CODE_RATE",  lp_code_rate);
00251     if (!guard_interval.isNull())
00252         query.bindValue(":GUARD_INTERVAL",guard_interval);
00253     if (!mod_sys.isNull())
00254         query.bindValue(":MOD_SYS",       mod_sys);
00255     if (!rolloff.isNull())
00256         query.bindValue(":ROLLOFF",       rolloff);
00257 
00258     if (!query.exec() || !query.isActive())
00259     {
00260         MythDB::DBError("Adding transport to Database.", query);
00261         return 0;
00262     }
00263 
00264     if (mplex)
00265         return mplex;
00266 
00267     mplex = get_dtv_multiplex(
00268         db_source_id,  sistandard,    frequency,
00269         // DVB specific
00270         transport_id,  network_id, polarity);
00271 
00272     LOG(VB_CHANSCAN, LOG_INFO, QString("insert_dtv_multiplex -- ") +
00273             QString("inserted %1").arg(mplex));
00274 
00275     return mplex;
00276 }
00277 
00278 static void handle_transport_desc(vector<uint> &muxes,
00279                                   const MPEGDescriptor &desc,
00280                                   uint sourceid, uint tsid, uint netid)
00281 {
00282     uint tag = desc.DescriptorTag();
00283 
00284     if (tag == DescriptorID::terrestrial_delivery_system)
00285     {
00286         const TerrestrialDeliverySystemDescriptor cd(desc);
00287         uint64_t freq = cd.FrequencyHz();
00288 
00289         // Use the frequency we already have for this mplex
00290         // as it may be one of the other_frequencies for this mplex
00291         int mux = ChannelUtil::GetMplexID(sourceid, tsid, netid);
00292         if (mux > 0)
00293         {
00294             QString dummy_mod;
00295             QString dummy_sistd;
00296             uint dummy_tsid, dummy_netid;
00297             ChannelUtil::GetTuningParams(mux, dummy_mod, freq,
00298                                          dummy_tsid, dummy_netid, dummy_sistd);
00299         }
00300 
00301         mux = ChannelUtil::CreateMultiplex(
00302             (int)sourceid,        "dvb",
00303             freq,                  QString(),
00304             // DVB specific
00305             (int)tsid,            (int)netid,
00306             -1,                   cd.BandwidthString()[0].toAscii(),
00307             -1,                   'a',
00308             cd.TransmissionModeString()[0].toAscii(),
00309             QString(),                         cd.ConstellationString(),
00310             cd.HierarchyString()[0].toAscii(), cd.CodeRateHPString(),
00311             cd.CodeRateLPString(),             cd.GuardIntervalString(),
00312             QString(),                         QString());
00313 
00314         if (mux)
00315             muxes.push_back(mux);
00316 
00317         /* unused
00318            HighPriority()
00319            IsTimeSlicingIndicatorUsed()
00320            IsMPE_FECUsed()
00321            NativeInterleaver()
00322            Alpha()
00323         */
00324     }
00325     else if (tag == DescriptorID::satellite_delivery_system)
00326     {
00327         const SatelliteDeliverySystemDescriptor cd(desc);
00328 
00329         uint mux = ChannelUtil::CreateMultiplex(
00330             sourceid,             "dvb",
00331             cd.FrequencyHz(),     cd.ModulationString(),
00332             // DVB specific
00333             tsid,                 netid,
00334             cd.SymbolRateHz(),    -1,
00335             cd.PolarizationString()[0].toAscii(), 'a',
00336             -1,
00337             cd.FECInnerString(),  QString(),
00338             -1,                   QString(),
00339             QString(),            QString(),
00340             cd.ModulationSystemString(), cd.RollOffString());
00341 
00342         if (mux)
00343             muxes.push_back(mux);
00344 
00345         /* unused
00346            OrbitalPositionString() == OrbitalLocation
00347         */
00348     }
00349     else if (tag == DescriptorID::cable_delivery_system)
00350     {
00351         const CableDeliverySystemDescriptor cd(desc);
00352 
00353         uint mux = ChannelUtil::CreateMultiplex(
00354             sourceid,             "dvb",
00355             cd.FrequencyHz(),     cd.ModulationString(),
00356             // DVB specific
00357             tsid,                 netid,
00358             cd.SymbolRateHz(),    -1,
00359             -1,                   'a',
00360             -1,
00361             cd.FECInnerString(),  QString::null,
00362             -1,                   QString::null,
00363             QString::null,        QString::null,
00364             QString::null,        QString::null);
00365 
00366         if (mux)
00367             muxes.push_back(mux);
00368     }
00369 }
00370 
00371 uint ChannelUtil::CreateMultiplex(int  sourceid,      QString sistandard,
00372                                   uint64_t frequency, QString modulation,
00373                                   int  transport_id,  int     network_id)
00374 {
00375     return CreateMultiplex(
00376         sourceid,           sistandard,
00377         frequency,          modulation,
00378         transport_id,       network_id,
00379         -1,                 -1,
00380         -1,                 -1,
00381         -1,
00382         QString::null,      QString::null,
00383         -1,                 QString::null,
00384         QString::null,      QString::null,
00385         QString::null,      QString::null);
00386 }
00387 
00388 uint ChannelUtil::CreateMultiplex(
00389     int         sourceid,     QString     sistandard,
00390     uint64_t    freq,         QString     modulation,
00391     // DVB specific
00392     int         transport_id, int         network_id,
00393     int         symbol_rate,  signed char bandwidth,
00394     signed char polarity,     signed char inversion,
00395     signed char trans_mode,
00396     QString     inner_FEC,    QString     constellation,
00397     signed char hierarchy,    QString     hp_code_rate,
00398     QString     lp_code_rate, QString     guard_interval,
00399     QString     mod_sys,      QString     rolloff)
00400 {
00401     return insert_dtv_multiplex(
00402         sourceid,           sistandard,
00403         freq,               modulation,
00404         // DVB specific
00405         transport_id,       network_id,
00406         symbol_rate,        bandwidth,
00407         polarity,           inversion,
00408         trans_mode,
00409         inner_FEC,          constellation,
00410         hierarchy,          hp_code_rate,
00411         lp_code_rate,       guard_interval,
00412         mod_sys,            rolloff);
00413 }
00414 
00415 uint ChannelUtil::CreateMultiplex(uint sourceid, const DTVMultiplex &mux,
00416                                   int transport_id, int network_id)
00417 {
00418     return insert_dtv_multiplex(
00419         sourceid,                         mux.sistandard,
00420         mux.frequency,                    mux.modulation.toString(),
00421         // DVB specific
00422         transport_id,                     network_id,
00423         mux.symbolrate,                   mux.bandwidth.toChar().toAscii(),
00424         mux.polarity.toChar().toAscii(),  mux.inversion.toChar().toAscii(),
00425         mux.trans_mode.toChar().toAscii(),
00426         mux.fec.toString(),               mux.modulation.toString(),
00427         mux.hierarchy.toChar().toAscii(), mux.hp_code_rate.toString(),
00428         mux.lp_code_rate.toString(),      mux.guard_interval.toString(),
00429         mux.mod_sys.toString(),           mux.rolloff.toString());
00430 }
00431 
00432 
00436 vector<uint> ChannelUtil::CreateMultiplexes(
00437     int sourceid, const NetworkInformationTable *nit)
00438 {
00439     vector<uint> muxes;
00440 
00441     if (sourceid <= 0)
00442         return muxes;
00443 
00444     for (uint i = 0; i < nit->TransportStreamCount(); ++i)
00445     {
00446         const desc_list_t& list =
00447             MPEGDescriptor::Parse(nit->TransportDescriptors(i),
00448                                   nit->TransportDescriptorsLength(i));
00449 
00450         uint tsid  = nit->TSID(i);
00451         uint netid = nit->OriginalNetworkID(i);
00452         for (uint j = 0; j < list.size(); ++j)
00453         {
00454             const MPEGDescriptor desc(list[j]);
00455             handle_transport_desc(muxes, desc, sourceid, tsid, netid);
00456         }
00457     }
00458     return muxes;
00459 }
00460 
00461 uint ChannelUtil::GetMplexID(uint sourceid, const QString &channum)
00462 {
00463     MSqlQuery query(MSqlQuery::InitCon());
00464     /* See if mplexid is already in the database */
00465     query.prepare(
00466         "SELECT mplexid "
00467         "FROM channel "
00468         "WHERE sourceid  = :SOURCEID  AND "
00469         "      channum   = :CHANNUM");
00470 
00471     query.bindValue(":SOURCEID",  sourceid);
00472     query.bindValue(":CHANNUM",   channum);
00473 
00474     if (!query.exec() || !query.isActive())
00475         MythDB::DBError("GetMplexID 0", query);
00476     else if (query.next())
00477         return query.value(0).toInt();
00478 
00479     return 0;
00480 }
00481 
00482 int ChannelUtil::GetMplexID(uint sourceid, uint64_t frequency)
00483 {
00484     MSqlQuery query(MSqlQuery::InitCon());
00485     /* See if mplexid is already in the database */
00486     query.prepare(
00487         "SELECT mplexid "
00488         "FROM dtv_multiplex "
00489         "WHERE sourceid  = :SOURCEID  AND "
00490         "      frequency = :FREQUENCY");
00491 
00492     query.bindValue(":SOURCEID",  sourceid);
00493     query.bindValue(":FREQUENCY", QString::number(frequency));
00494 
00495     if (!query.exec() || !query.isActive())
00496     {
00497         MythDB::DBError("GetMplexID 1", query);
00498         return -1;
00499     }
00500 
00501     if (query.next())
00502         return query.value(0).toInt();
00503 
00504     return -1;
00505 }
00506 
00507 int ChannelUtil::GetMplexID(uint sourceid,     uint64_t frequency,
00508                             uint transport_id, uint     network_id)
00509 {
00510     MSqlQuery query(MSqlQuery::InitCon());
00511     // See if transport already in database
00512     query.prepare(
00513         "SELECT mplexid "
00514         "FROM dtv_multiplex "
00515         "WHERE networkid   = :NETWORKID   AND "
00516         "      transportid = :TRANSPORTID AND "
00517         "      frequency   = :FREQUENCY   AND "
00518         "      sourceid    = :SOURCEID");
00519 
00520     query.bindValue(":SOURCEID",    sourceid);
00521     query.bindValue(":NETWORKID",   network_id);
00522     query.bindValue(":TRANSPORTID", transport_id);
00523     query.bindValue(":FREQUENCY",   QString::number(frequency));
00524 
00525     if (!query.exec() || !query.isActive())
00526     {
00527         MythDB::DBError("GetMplexID 2", query);
00528         return -1;
00529     }
00530 
00531     if (query.next())
00532         return query.value(0).toInt();
00533 
00534     return -1;
00535 }
00536 
00537 int ChannelUtil::GetMplexID(uint sourceid,
00538                             uint transport_id, uint network_id)
00539 {
00540     MSqlQuery query(MSqlQuery::InitCon());
00541     // See if transport already in database
00542     query.prepare(
00543         "SELECT mplexid "
00544         "FROM dtv_multiplex "
00545         "WHERE networkid   = :NETWORKID   AND "
00546         "      transportid = :TRANSPORTID AND "
00547         "      sourceid    = :SOURCEID");
00548 
00549     query.bindValue(":SOURCEID",    sourceid);
00550     query.bindValue(":NETWORKID",   network_id);
00551     query.bindValue(":TRANSPORTID", transport_id);
00552 
00553     if (!query.exec() || !query.isActive())
00554     {
00555         MythDB::DBError("GetMplexID 3", query);
00556         return -1;
00557     }
00558 
00559     if (query.next())
00560         return query.value(0).toInt();
00561 
00562     return -1;
00563 }
00564 
00565 uint ChannelUtil::GetMplexID(uint chanid)
00566 {
00567     MSqlQuery query(MSqlQuery::InitCon());
00568     /* See if mplexid is already in the database */
00569     query.prepare(
00570         "SELECT mplexid "
00571         "FROM channel "
00572         "WHERE chanid = :CHANID");
00573 
00574     query.bindValue(":CHANID", chanid);
00575 
00576     if (!query.exec())
00577         MythDB::DBError("GetMplexID 4", query);
00578     else if (query.next())
00579         return query.value(0).toInt();
00580 
00581     return 0;
00582 }
00583 
00606 // current_mplexid always exists in scanner, see ScanTranport()
00607 //
00608 int ChannelUtil::GetBetterMplexID(int current_mplexid,
00609                                   int transport_id,
00610                                   int network_id)
00611 {
00612     LOG(VB_CHANSCAN, LOG_INFO,
00613         QString("GetBetterMplexID(mplexId %1, tId %2, netId %3)")
00614             .arg(current_mplexid).arg(transport_id).arg(network_id));
00615 
00616     int q_networkid = 0, q_transportid = 0;
00617     MSqlQuery query(MSqlQuery::InitCon());
00618 
00619     query.prepare(QString("SELECT networkid, transportid "
00620                           "FROM dtv_multiplex "
00621                           "WHERE mplexid = %1").arg(current_mplexid));
00622 
00623     if (!query.exec() || !query.isActive())
00624         MythDB::DBError("Getting mplexid global search", query);
00625     else if (query.size())
00626     {
00627         query.next();
00628         q_networkid   = query.value(0).toInt();
00629         q_transportid = query.value(1).toInt();
00630     }
00631 
00632     // Got a match, return it.
00633     if ((q_networkid == network_id) && (q_transportid == transport_id))
00634     {
00635         LOG(VB_CHANSCAN, LOG_INFO,
00636             QString("GetBetterMplexID(): Returning perfect match %1")
00637                 .arg(current_mplexid));
00638         return current_mplexid;
00639     }
00640 
00641     // Not in DB at all, insert it
00642     if (!q_networkid && !q_transportid)
00643     {
00644         int qsize = query.size();
00645         query.prepare(QString("UPDATE dtv_multiplex "
00646                               "SET networkid = %1, transportid = %2 "
00647                               "WHERE mplexid = %3")
00648                       .arg(network_id).arg(transport_id).arg(current_mplexid));
00649 
00650         if (!query.exec() || !query.isActive())
00651             MythDB::DBError("Getting mplexid global search", query);
00652 
00653         LOG(VB_CHANSCAN, LOG_INFO,
00654             QString("GetBetterMplexID(): net id and transport id "
00655                     "are null, qsize(%1), Returning %2")
00656                 .arg(qsize).arg(current_mplexid));
00657         return current_mplexid;
00658     }
00659 
00660     // We have a partial match, so we try to do better...
00661     QString theQueries[2] =
00662     {
00663         QString("SELECT a.mplexid "
00664                 "FROM dtv_multiplex a, dtv_multiplex b "
00665                 "WHERE a.networkid   = %1 AND "
00666                 "      a.transportid = %2 AND "
00667                 "      a.sourceid    = b.sourceid AND "
00668                 "      b.mplexid     = %3")
00669         .arg(network_id).arg(transport_id).arg(current_mplexid),
00670 
00671         QString("SELECT mplexid "
00672                 "FROM dtv_multiplex "
00673                 "WHERE networkid = %1 AND "
00674                 "      transportid = %2")
00675         .arg(network_id).arg(transport_id),
00676     };
00677 
00678     for (uint i=0; i<2; i++)
00679     {
00680         query.prepare(theQueries[i]);
00681 
00682         if (!query.exec() || !query.isActive())
00683             MythDB::DBError("Finding matching mplexid", query);
00684 
00685         if (query.size() == 1)
00686         {
00687             LOG(VB_CHANSCAN, LOG_INFO,
00688                 QString("GetBetterMplexID(): query#%1 qsize(%2) "
00689                         "Returning %3")
00690                     .arg(i).arg(query.size()).arg(current_mplexid));
00691             query.next();
00692             return query.value(0).toInt();
00693         }
00694 
00695         if (query.size() > 1)
00696         {
00697             query.next();
00698             int ret = (i==0) ? current_mplexid : query.value(0).toInt();
00699             LOG(VB_CHANSCAN, LOG_INFO,
00700                 QString("GetBetterMplexID(): query#%1 qsize(%2) "
00701                         "Returning %3")
00702                     .arg(i).arg(query.size()).arg(ret));
00703             return ret;
00704         }
00705     }
00706 
00707     // If you still didn't find this combo return -1 (failure)
00708     LOG(VB_CHANSCAN, LOG_INFO, "GetBetterMplexID(): Returning -1");
00709     return -1;
00710 }
00711 
00712 bool ChannelUtil::GetTuningParams(uint      mplexid,
00713                                   QString  &modulation,
00714                                   uint64_t &frequency,
00715                                   uint     &dvb_transportid,
00716                                   uint     &dvb_networkid,
00717                                   QString  &si_std)
00718 {
00719     if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
00720         return false;
00721 
00722     MSqlQuery query(MSqlQuery::InitCon());
00723     query.prepare(
00724         "SELECT transportid, networkid, frequency, modulation, sistandard "
00725         "FROM dtv_multiplex "
00726         "WHERE mplexid = :MPLEXID");
00727     query.bindValue(":MPLEXID", mplexid);
00728 
00729     if (!query.exec() || !query.isActive())
00730     {
00731         MythDB::DBError("GetTuningParams failed ", query);
00732         return false;
00733     }
00734 
00735     if (!query.next())
00736         return false;
00737 
00738     dvb_transportid = query.value(0).toUInt();
00739     dvb_networkid   = query.value(1).toUInt();
00740     frequency       = query.value(2).toULongLong();
00741     modulation      = query.value(3).toString();
00742     si_std          = query.value(4).toString();
00743 
00744     return true;
00745 }
00746 
00747 QString ChannelUtil::GetChannelStringField(int chan_id, const QString &field)
00748 {
00749     if (chan_id < 0)
00750         return QString::null;
00751 
00752     MSqlQuery query(MSqlQuery::InitCon());
00753     query.prepare(QString("SELECT %1 FROM channel "
00754             "WHERE chanid=%2").arg(field).arg(chan_id));
00755     if (!query.exec() || !query.isActive())
00756     {
00757         MythDB::DBError("Selecting channel/dtv_multiplex 1", query);
00758         return QString::null;
00759     }
00760     if (!query.size())
00761         return QString::null;
00762 
00763     query.next();
00764     return query.value(0).toString();
00765 }
00766 
00767 QString ChannelUtil::GetChanNum(int chan_id)
00768 {
00769     return GetChannelStringField(chan_id, QString("channum"));
00770 }
00771 
00772 QString ChannelUtil::GetCallsign(int chan_id)
00773 {
00774     return GetChannelStringField(chan_id, QString("callsign"));
00775 }
00776 
00777 QString ChannelUtil::GetServiceName(int chan_id)
00778 {
00779     return GetChannelStringField(chan_id, QString("name"));
00780 }
00781 
00782 int ChannelUtil::GetTimeOffset(int chan_id)
00783 {
00784     return GetChannelStringField(chan_id, QString("tmoffset")).toInt();
00785 }
00786 
00787 int ChannelUtil::GetSourceID(int db_mplexid)
00788 {
00789     MSqlQuery query(MSqlQuery::InitCon());
00790 
00791     query.prepare(QString("SELECT sourceid "
00792                           "FROM dtv_multiplex "
00793                           "WHERE mplexid = %1").arg(db_mplexid));
00794 
00795     if (!query.exec() || !query.isActive())
00796     {
00797         MythDB::DBError("Selecting channel/dtv_multiplex", query);
00798         return -1;
00799     }
00800 
00801     if (query.size() > 0)
00802     {
00803         query.next();
00804         return query.value(0).toInt();
00805     }
00806     return -1;
00807 }
00808 
00809 uint ChannelUtil::GetSourceIDForChannel(uint chanid)
00810 {
00811     MSqlQuery query(MSqlQuery::InitCon());
00812 
00813     query.prepare(
00814         "SELECT sourceid "
00815         "FROM channel "
00816         "WHERE chanid = :CHANID");
00817     query.bindValue(":CHANID", chanid);
00818 
00819     if (!query.exec())
00820         MythDB::DBError("Selecting channel/dtv_multiplex", query);
00821     else if (query.next())
00822         return query.value(0).toUInt();
00823 
00824     return 0;
00825 }
00826 
00827 int ChannelUtil::GetInputID(int source_id, int card_id)
00828 {
00829     int input_id = -1;
00830 
00831     MSqlQuery query(MSqlQuery::InitCon());
00832     query.prepare("SELECT cardinputid"
00833                   " FROM cardinput"
00834                   " WHERE sourceid = :SOURCEID"
00835                   " AND cardid = :CARDID");
00836     query.bindValue(":SOURCEID", source_id);
00837     query.bindValue(":CARDID", card_id);
00838 
00839     if (query.exec() && query.isActive() && query.next())
00840         input_id = query.value(0).toInt();
00841 
00842     return input_id;
00843 }
00844 
00845 QStringList ChannelUtil::GetCardTypes(uint chanid)
00846 {
00847     MSqlQuery query(MSqlQuery::InitCon());
00848     query.prepare("SELECT cardtype "
00849                   "FROM capturecard, cardinput, channel "
00850                   "WHERE channel.chanid   = :CHANID            AND "
00851                   "      channel.sourceid = cardinput.sourceid AND "
00852                   "      cardinput.cardid = capturecard.cardid "
00853                   "GROUP BY cardtype");
00854     query.bindValue(":CHANID", chanid);
00855 
00856     QStringList list;
00857     if (!query.exec())
00858     {
00859         MythDB::DBError("ChannelUtil::GetCardTypes", query);
00860         return list;
00861     }
00862     while (query.next())
00863         list.push_back(query.value(0).toString());
00864     return list;
00865 }
00866 
00867 static bool lt_pidcache(
00868     const pid_cache_item_t &a, const pid_cache_item_t &b)
00869 {
00870     return a.GetPID() < b.GetPID();
00871 }
00872 
00879 bool ChannelUtil::GetCachedPids(uint chanid,
00880                                 pid_cache_t &pid_cache)
00881 {
00882     MSqlQuery query(MSqlQuery::InitCon());
00883     QString thequery = QString("SELECT pid, tableid FROM pidcache "
00884                                "WHERE chanid='%1'").arg(chanid);
00885     query.prepare(thequery);
00886 
00887     if (!query.exec() || !query.isActive())
00888     {
00889         MythDB::DBError("GetCachedPids: fetching pids", query);
00890         return false;
00891     }
00892 
00893     while (query.next())
00894     {
00895         int pid = query.value(0).toInt(), tid = query.value(1).toInt();
00896         if ((pid >= 0) && (tid >= 0))
00897             pid_cache.push_back(pid_cache_item_t(pid, tid));
00898     }
00899     stable_sort(pid_cache.begin(), pid_cache.end(), lt_pidcache);
00900 
00901     return true;
00902 }
00903 
00910 bool ChannelUtil::SaveCachedPids(uint chanid,
00911                                  const pid_cache_t &_pid_cache,
00912                                  bool delete_all)
00913 {
00914     MSqlQuery query(MSqlQuery::InitCon());
00915 
00917     if (delete_all)
00918         query.prepare("DELETE FROM pidcache WHERE chanid = :CHANID");
00919     else
00920         query.prepare(
00921             "DELETE FROM pidcache "
00922             "WHERE chanid = :CHANID AND tableid < 65536");
00923 
00924     query.bindValue(":CHANID", chanid);
00925 
00926     if (!query.exec())
00927     {
00928         MythDB::DBError("GetCachedPids -- delete", query);
00929         return false;
00930     }
00931 
00932     pid_cache_t old_cache;
00933     GetCachedPids(chanid, old_cache);
00934     pid_cache_t pid_cache = _pid_cache;
00935     stable_sort(pid_cache.begin(), pid_cache.end(), lt_pidcache);
00936 
00938     query.prepare(
00939         "INSERT INTO pidcache "
00940         "SET chanid = :CHANID, pid = :PID, tableid = :TABLEID");
00941     query.bindValue(":CHANID", chanid);
00942 
00943     bool ok = true;
00944     pid_cache_t::const_iterator ito = old_cache.begin();
00945     pid_cache_t::const_iterator itn = pid_cache.begin();
00946     for (; itn != pid_cache.end(); ++itn)
00947     {
00948         // if old pid smaller than current new pid, skip this old pid
00949         for (; ito != old_cache.end() && ito->GetPID() < itn->GetPID(); ++ito);
00950 
00951         // if already in DB, skip DB insert
00952         if (ito != old_cache.end() && ito->GetPID() == itn->GetPID())
00953             continue;
00954 
00955         query.bindValue(":PID",     itn->GetPID());
00956         query.bindValue(":TABLEID", itn->GetComposite());
00957 
00958         if (!query.exec())
00959         {
00960             MythDB::DBError("GetCachedPids -- insert", query);
00961             ok = false;
00962         }
00963     }
00964 
00965     return ok;
00966 }
00967 
00968 QString ChannelUtil::GetChannelValueStr(const QString &channel_field,
00969                                         uint           cardid,
00970                                         const QString &input,
00971                                         const QString &channum)
00972 {
00973     QString retval = QString::null;
00974 
00975     MSqlQuery query(MSqlQuery::InitCon());
00976 
00977     query.prepare(
00978         QString(
00979             "SELECT channel.%1 "
00980             "FROM channel, capturecard, cardinput "
00981             "WHERE channel.channum      = :CHANNUM           AND "
00982             "      channel.sourceid     = cardinput.sourceid AND "
00983             "      cardinput.inputname  = :INPUT             AND "
00984             "      cardinput.cardid     = capturecard.cardid AND "
00985             "      capturecard.cardid   = :CARDID ")
00986         .arg(channel_field));
00987 
00988     query.bindValue(":CARDID",   cardid);
00989     query.bindValue(":INPUT",    input);
00990     query.bindValue(":CHANNUM",  channum);
00991 
00992     if (!query.exec() || !query.isActive())
00993         MythDB::DBError("getchannelvalue", query);
00994     else if (query.next())
00995         retval = query.value(0).toString();
00996 
00997     return retval;
00998 }
00999 
01000 QString ChannelUtil::GetChannelValueStr(const QString &channel_field,
01001                                         uint           sourceid,
01002                                         const QString &channum)
01003 {
01004     QString retval = QString::null;
01005 
01006     MSqlQuery query(MSqlQuery::InitCon());
01007 
01008     query.prepare(
01009         QString(
01010             "SELECT channel.%1 "
01011             "FROM channel "
01012             "WHERE channum  = :CHANNUM AND "
01013             "      sourceid = :SOURCEID")
01014         .arg(channel_field));
01015 
01016     query.bindValue(":SOURCEID", sourceid);
01017     query.bindValue(":CHANNUM",  channum);
01018 
01019     if (!query.exec() || !query.isActive())
01020         MythDB::DBError("getchannelvalue", query);
01021     else if (query.next())
01022         retval = query.value(0).toString();
01023 
01024     return retval;
01025 }
01026 
01027 int ChannelUtil::GetChannelValueInt(const QString &channel_field,
01028                                     uint           cardid,
01029                                     const QString &input,
01030                                     const QString &channum)
01031 {
01032     QString val = GetChannelValueStr(channel_field, cardid, input, channum);
01033 
01034     int retval = 0;
01035     if (!val.isEmpty())
01036         retval = val.toInt();
01037 
01038     return (retval) ? retval : -1;
01039 }
01040 
01041 int ChannelUtil::GetChannelValueInt(const QString &channel_field,
01042                                     uint           sourceid,
01043                                     const QString &channum)
01044 {
01045     QString val = GetChannelValueStr(channel_field, sourceid, channum);
01046 
01047     int retval = 0;
01048     if (!val.isEmpty())
01049         retval = val.toInt();
01050 
01051     return (retval) ? retval : -1;
01052 }
01053 
01054 bool ChannelUtil::IsOnSameMultiplex(uint srcid,
01055                                     const QString &new_channum,
01056                                     const QString &old_channum)
01057 {
01058     if (new_channum.isEmpty() || old_channum.isEmpty())
01059         return false;
01060 
01061     if (new_channum == old_channum)
01062         return true;
01063 
01064     uint old_mplexid = GetMplexID(srcid, old_channum);
01065     if (!old_mplexid)
01066         return false;
01067 
01068     uint new_mplexid = GetMplexID(srcid, new_channum);
01069     if (!new_mplexid)
01070         return false;
01071 
01072     LOG(VB_CHANNEL, LOG_INFO, QString("IsOnSameMultiplex? %1==%2 -> %3")
01073             .arg(old_mplexid).arg(new_mplexid)
01074             .arg(old_mplexid == new_mplexid));
01075 
01076     return old_mplexid == new_mplexid;
01077 }
01078 
01084 static QStringList get_valid_recorder_list(uint chanid)
01085 {
01086     QStringList reclist;
01087 
01088     // Query the database to determine which source is being used currently.
01089     // set the EPG so that it only displays the channels of the current source
01090     MSqlQuery query(MSqlQuery::InitCon());
01091     // We want to get the current source id for this recorder
01092     query.prepare(
01093             "SELECT cardinput.cardid "
01094             "FROM channel "
01095             "LEFT JOIN cardinput ON channel.sourceid = cardinput.sourceid "
01096             "WHERE channel.chanid = :CHANID AND "
01097             "      cardinput.livetvorder > 0 "
01098             "ORDER BY cardinput.livetvorder, cardinput.cardinputid");
01099     query.bindValue(":CHANID", chanid);
01100 
01101     if (!query.exec() || !query.isActive())
01102     {
01103         MythDB::DBError("get_valid_recorder_list ChanID", query);
01104         return reclist;
01105     }
01106 
01107     while (query.next())
01108         reclist << query.value(0).toString();
01109 
01110     return reclist;
01111 }
01112 
01118 static QStringList get_valid_recorder_list(const QString &channum)
01119 {
01120     QStringList reclist;
01121 
01122     // Query the database to determine which source is being used currently.
01123     // set the EPG so that it only displays the channels of the current source
01124     MSqlQuery query(MSqlQuery::InitCon());
01125     // We want to get the current source id for this recorder
01126     query.prepare(
01127         "SELECT cardinput.cardid "
01128         "FROM channel "
01129         "LEFT JOIN cardinput ON channel.sourceid = cardinput.sourceid "
01130         "WHERE channel.channum = :CHANNUM AND "
01131         "      cardinput.livetvorder > 0 "
01132         "ORDER BY cardinput.livetvorder, cardinput.cardinputid");
01133     query.bindValue(":CHANNUM", channum);
01134 
01135     if (!query.exec() || !query.isActive())
01136     {
01137         MythDB::DBError("get_valid_recorder_list ChanNum", query);
01138         return reclist;
01139     }
01140 
01141     while (query.next())
01142         reclist << query.value(0).toString();
01143 
01144     return reclist;
01145 }
01146 
01154 QStringList ChannelUtil::GetValidRecorderList(
01155     uint chanid, const QString &channum)
01156 {
01157     if (chanid)
01158         return get_valid_recorder_list(chanid);
01159     else if (!channum.isEmpty())
01160         return get_valid_recorder_list(channum);
01161     return QStringList();
01162 }
01163 
01164 
01165 vector<uint> ChannelUtil::GetConflicting(const QString &channum, uint sourceid)
01166 {
01167     MSqlQuery query(MSqlQuery::InitCon());
01168     vector<uint> conflicting;
01169 
01170     if (sourceid)
01171     {
01172         query.prepare(
01173             "SELECT chanid from channel "
01174             "WHERE sourceid = :SOURCEID AND "
01175             "      channum  = :CHANNUM");
01176         query.bindValue(":SOURCEID", sourceid);
01177     }
01178     else
01179     {
01180         query.prepare(
01181             "SELECT chanid from channel "
01182             "WHERE channum = :CHANNUM");
01183     }
01184 
01185     query.bindValue(":CHANNUM",  channum);
01186     if (!query.exec())
01187     {
01188         MythDB::DBError("IsConflicting", query);
01189         conflicting.push_back(0);
01190         return conflicting;
01191     }
01192 
01193     while (query.next())
01194         conflicting.push_back(query.value(0).toUInt());
01195 
01196     return conflicting;
01197 }
01198 
01199 bool ChannelUtil::SetChannelValue(const QString &field_name,
01200                                   QString        value,
01201                                   uint           sourceid,
01202                                   const QString &channum)
01203 {
01204     MSqlQuery query(MSqlQuery::InitCon());
01205 
01206     query.prepare(
01207         QString("UPDATE channel SET channel.%1=:VALUE "
01208                 "WHERE channel.channum  = :CHANNUM AND "
01209                 "      channel.sourceid = :SOURCEID").arg(field_name));
01210 
01211     query.bindValue(":VALUE",    value);
01212     query.bindValue(":CHANNUM",  channum);
01213     query.bindValue(":SOURCEID", sourceid);
01214 
01215     return query.exec();
01216 }
01217 
01218 bool ChannelUtil::SetChannelValue(const QString &field_name,
01219                                   QString        value,
01220                                   int            chanid)
01221 {
01222     MSqlQuery query(MSqlQuery::InitCon());
01223 
01224     query.prepare(
01225         QString("UPDATE channel SET channel.%1=:VALUE "
01226                 "WHERE channel.chanid = :CHANID").arg(field_name));
01227 
01228     query.bindValue(":VALUE",  value);
01229     query.bindValue(":CHANID", chanid);
01230 
01231     return query.exec();
01232 }
01233 
01235 QString ChannelUtil::GetDefaultAuthority(uint chanid)
01236 {
01237     static QReadWriteLock channel_default_authority_map_lock;
01238     static QMap<uint,QString> channel_default_authority_map;
01239     static bool run_init = true;
01240 
01241     channel_default_authority_map_lock.lockForRead();
01242 
01243     if (run_init)
01244     {
01245         channel_default_authority_map_lock.unlock();
01246         channel_default_authority_map_lock.lockForWrite();
01247         if (run_init)
01248         {
01249             MSqlQuery query(MSqlQuery::InitCon());
01250             query.prepare(
01251                 "SELECT chanid, m.default_authority "
01252                 "FROM channel c "
01253                 "LEFT JOIN dtv_multiplex m "
01254                 "ON (c.mplexid = m.mplexid)");
01255             if (query.exec())
01256             {
01257                 while (query.next())
01258                 {
01259                     if (!query.value(1).toString().isEmpty())
01260                     {
01261                         channel_default_authority_map[query.value(0).toUInt()] =
01262                             query.value(1).toString();
01263                     }
01264                 }
01265                 run_init = false;
01266             }
01267             else
01268             {
01269                 MythDB::DBError("GetDefaultAuthority 1", query);
01270             }
01271 
01272             query.prepare(
01273                 "SELECT chanid, default_authority "
01274                 "FROM channel");
01275             if (query.exec())
01276             {
01277                 while (query.next())
01278                 {
01279                     if (!query.value(1).toString().isEmpty())
01280                     {
01281                         channel_default_authority_map[query.value(0).toUInt()] =
01282                             query.value(1).toString();
01283                     }
01284                 }
01285                 run_init = false;
01286             }
01287             else
01288             {
01289                 MythDB::DBError("GetDefaultAuthority 2", query);
01290             }
01291 
01292         }
01293     }
01294 
01295     QMap<uint,QString>::iterator it = channel_default_authority_map.find(chanid);
01296     QString ret = QString::null;
01297     if (it != channel_default_authority_map.end())
01298     {
01299         ret = *it;
01300         ret.detach();
01301     }
01302     channel_default_authority_map_lock.unlock();
01303 
01304     return ret;
01305 }
01306 
01307 QString ChannelUtil::GetIcon(uint chanid)
01308 {
01309     static QReadWriteLock channel_icon_map_lock;
01310     static QHash<uint,QString> channel_icon_map;
01311     static bool run_init = true;
01312 
01313     channel_icon_map_lock.lockForRead();
01314 
01315     QString ret(channel_icon_map.value(chanid, "_cold_"));
01316 
01317     channel_icon_map_lock.unlock();
01318 
01319     if (ret != "_cold_")
01320         return ret;
01321 
01322     channel_icon_map_lock.lockForWrite();
01323 
01324     MSqlQuery query(MSqlQuery::InitCon());
01325     QString iconquery = "SELECT chanid, icon FROM channel";
01326 
01327     if (run_init)
01328         iconquery += " WHERE visible = 1";
01329     else
01330         iconquery += " WHERE chanid = :CHANID";
01331 
01332     query.prepare(iconquery);
01333 
01334     if (!run_init)
01335         query.bindValue(":CHANID", chanid);
01336 
01337     if (query.exec())
01338     {
01339         if (run_init)
01340         {
01341             channel_icon_map.reserve(query.size());
01342             while (query.next())
01343             {
01344                 channel_icon_map[query.value(0).toUInt()] =
01345                     query.value(1).toString();
01346             }
01347             run_init = false;
01348         }
01349         else
01350         {
01351             channel_icon_map[chanid] = (query.next()) ?
01352                 query.value(1).toString() : "";
01353         }
01354     }
01355     else
01356     {
01357         MythDB::DBError("GetIcon", query);
01358     }
01359 
01360     ret = channel_icon_map.value(chanid, "");
01361 
01362     channel_icon_map_lock.unlock();
01363 
01364     return ret;
01365 }
01366 
01367 QString ChannelUtil::GetUnknownCallsign(void)
01368 {
01369     QString tmp = QObject::tr("UNKNOWN", "Synthesized callsign");
01370     tmp.detach();
01371     return tmp;
01372 }
01373 
01374 int ChannelUtil::GetChanID(int mplexid,       int service_transport_id,
01375                            int major_channel, int minor_channel,
01376                            int program_number)
01377 {
01378     MSqlQuery query(MSqlQuery::InitCon());
01379 
01380     // find source id, so we can find manually inserted ATSC channels
01381     query.prepare("SELECT sourceid "
01382                   "FROM dtv_multiplex "
01383                   "WHERE mplexid = :MPLEXID");
01384     query.bindValue(":MPLEXID", mplexid);
01385     if (!query.exec())
01386     {
01387         MythDB::DBError("Selecting channel/dtv_multiplex 2", query);
01388         return -1;
01389     }
01390     if (!query.next())
01391         return -1;
01392 
01393     int source_id = query.value(0).toInt();
01394 
01395     QStringList qstr;
01396 
01397     // find a proper ATSC channel
01398     qstr.push_back(
01399         QString("SELECT chanid FROM channel,dtv_multiplex "
01400                 "WHERE channel.sourceid          = %1 AND "
01401                 "      atsc_major_chan           = %2 AND "
01402                 "      atsc_minor_chan           = %3 AND "
01403                 "      dtv_multiplex.transportid = %4 AND "
01404                 "      dtv_multiplex.mplexid     = %5 AND "
01405                 "      dtv_multiplex.sourceid    = channel.sourceid AND "
01406                 "      dtv_multiplex.mplexid     = channel.mplexid")
01407         .arg(source_id).arg(major_channel).arg(minor_channel)
01408         .arg(service_transport_id).arg(mplexid));
01409 
01410     // Find manually inserted/edited channels in order of scariness.
01411     // find renamed channel, where atsc is valid
01412     qstr.push_back(
01413         QString("SELECT chanid FROM channel "
01414                 "WHERE sourceid=%1 AND "
01415                 "atsc_major_chan=%2 AND "
01416                 "atsc_minor_chan=%3")
01417         .arg(source_id).arg(major_channel).arg(minor_channel));
01418 
01419         // find based on mpeg program number and mplexid alone
01420     qstr.push_back(
01421         QString("SELECT chanid FROM channel "
01422                 "WHERE sourceid=%1 AND serviceID=%2 AND mplexid=%3")
01423         .arg(source_id).arg(program_number).arg(mplexid));
01424 
01425     for (int i = 0; i < qstr.size(); i++)
01426     {
01427         query.prepare(qstr[i]);
01428         if (!query.exec())
01429             MythDB::DBError("Selecting channel/dtv_multiplex 3", query);
01430         else if (query.next())
01431             return query.value(0).toInt();
01432     }
01433 
01434     return -1;
01435 }
01436 
01437 uint ChannelUtil::FindChannel(uint sourceid, const QString &freqid)
01438 {
01439     MSqlQuery query(MSqlQuery::InitCon());
01440     query.prepare("SELECT chanid "
01441                   "FROM channel "
01442                   "WHERE sourceid = :SOURCEID AND "
01443                   "      freqid   = :FREQID");
01444 
01445     query.bindValue(":SOURCEID", sourceid);
01446     query.bindValue(":FREQID",   freqid);
01447 
01448     if (!query.exec() || !query.isActive())
01449         MythDB::DBError("FindChannel", query);
01450     else if (query.next())
01451         return query.value(0).toUInt();
01452 
01453     return 0;
01454 }
01455 
01456 
01457 static uint get_max_chanid(uint sourceid)
01458 {
01459     QString qstr = "SELECT MAX(chanid) FROM channel ";
01460     qstr += (sourceid) ? "WHERE sourceid = :SOURCEID" : "";
01461 
01462     MSqlQuery query(MSqlQuery::DDCon());
01463     query.prepare(qstr);
01464 
01465     if (sourceid)
01466         query.bindValue(":SOURCEID", sourceid);
01467 
01468     if (!query.exec() || !query.isActive())
01469         MythDB::DBError("Getting chanid for new channel (2)", query);
01470     else if (!query.next())
01471         LOG(VB_GENERAL, LOG_ERR, "Error getting chanid for new channel.");
01472     else
01473         return query.value(0).toUInt();
01474 
01475     return 0;
01476 }
01477 
01478 static bool chanid_available(uint chanid)
01479 {
01480     MSqlQuery query(MSqlQuery::DDCon());
01481     query.prepare(
01482         "SELECT chanid "
01483         "FROM channel "
01484         "WHERE chanid = :CHANID");
01485     query.bindValue(":CHANID", chanid);
01486 
01487     if (!query.exec() || !query.isActive())
01488         MythDB::DBError("is_chan_id_available", query);
01489     else if (query.size() == 0)
01490         return true;
01491 
01492     return false;
01493 }
01494 
01499 int ChannelUtil::CreateChanID(uint sourceid, const QString &chan_num)
01500 {
01501     // first try to base it on the channel number for human readability
01502     uint chanid = 0;
01503     int chansep = chan_num.indexOf(QRegExp("\\D"));
01504     if (chansep > 0)
01505     {
01506         chanid =
01507             sourceid * 1000 +
01508             chan_num.left(chansep).toInt() * 10 +
01509             chan_num.right(chan_num.length()-chansep-1).toInt();
01510     }
01511     else
01512     {
01513         chanid = sourceid * 1000 + chan_num.toInt();
01514     }
01515 
01516     if ((chanid > sourceid * 1000) && (chanid_available(chanid)))
01517         return chanid;
01518 
01519     // try to at least base it on the sourceid for human readability
01520     chanid = max(get_max_chanid(sourceid) + 1, sourceid * 1000);
01521 
01522     if (chanid_available(chanid))
01523         return chanid;
01524 
01525     // just get a chanid we know should work
01526     chanid = get_max_chanid(0) + 1;
01527 
01528     if (chanid_available(chanid))
01529         return chanid;
01530 
01531     // failure
01532     return -1;
01533 }
01534 
01535 bool ChannelUtil::CreateChannel(uint db_mplexid,
01536                                 uint db_sourceid,
01537                                 uint new_channel_id,
01538                                 const QString &callsign,
01539                                 const QString &service_name,
01540                                 const QString &chan_num,
01541                                 uint service_id,
01542                                 uint atsc_major_channel,
01543                                 uint atsc_minor_channel,
01544                                 bool use_on_air_guide,
01545                                 bool hidden,
01546                                 bool hidden_in_guide,
01547                                 const QString &freqid,
01548                                 QString icon,
01549                                 QString format,
01550                                 QString xmltvid,
01551                                 QString default_authority)
01552 {
01553     MSqlQuery query(MSqlQuery::InitCon());
01554 
01555     QString chanNum = (chan_num == "-1") ?
01556         QString::number(service_id) : chan_num;
01557 
01558     QString qstr =
01559         "INSERT INTO channel "
01560         "  (chanid,        channum,    sourceid,          "
01561         "   callsign,      name,       serviceid,         ";
01562     qstr += (db_mplexid > 0)    ? "mplexid, " : "";
01563     qstr += (!freqid.isEmpty()) ? "freqid, "  : "";
01564     qstr +=
01565         "   atsc_major_chan,           atsc_minor_chan,   "
01566         "   useonairguide, visible,    tvformat,          "
01567         "   icon,          xmltvid,    default_authority) "
01568         "VALUES "
01569         "  (:CHANID,       :CHANNUM,   :SOURCEID,         "
01570         "   :CALLSIGN,     :NAME,      :SERVICEID,        ";
01571     qstr += (db_mplexid > 0)    ? ":MPLEXID, " : "";
01572     qstr += (!freqid.isEmpty()) ? ":FREQID, "  : "";
01573     qstr +=
01574         "   :MAJORCHAN,                :MINORCHAN,        "
01575         "   :USEOAG,       :VISIBLE,   :TVFORMAT,         "
01576         "   :ICON,         :XMLTVID,   :AUTHORITY)        ";
01577 
01578     query.prepare(qstr);
01579 
01580     query.bindValue(":CHANID",    new_channel_id);
01581     query.bindValue(":CHANNUM",   chanNum);
01582     query.bindValue(":SOURCEID",  db_sourceid);
01583     query.bindValue(":CALLSIGN",  callsign);
01584     query.bindValue(":NAME",      service_name);
01585 
01586     if (db_mplexid > 0)
01587         query.bindValue(":MPLEXID",   db_mplexid);
01588 
01589     query.bindValue(":SERVICEID", service_id);
01590     query.bindValue(":MAJORCHAN", atsc_major_channel);
01591     query.bindValue(":MINORCHAN", atsc_minor_channel);
01592     query.bindValue(":USEOAG",    use_on_air_guide);
01593     query.bindValue(":VISIBLE",   !hidden);
01594     (void) hidden_in_guide; // MythTV can't hide the channel in just the guide.
01595 
01596     if (!freqid.isEmpty())
01597         query.bindValue(":FREQID",    freqid);
01598 
01599     QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : format;
01600     tvformat = tvformat.isNull() ? "" : tvformat;
01601     query.bindValue(":TVFORMAT", tvformat);
01602 
01603     icon = (icon.isNull()) ? "" : icon;
01604     query.bindValue(":ICON", icon);
01605 
01606     xmltvid = (xmltvid.isNull()) ? "" : xmltvid;
01607     query.bindValue(":XMLTVID", xmltvid);
01608 
01609     default_authority = (default_authority.isNull()) ? "" : default_authority;
01610     query.bindValue(":AUTHORITY", default_authority);
01611 
01612     if (!query.exec() || !query.isActive())
01613     {
01614         MythDB::DBError("Adding Service", query);
01615         return false;
01616     }
01617     return true;
01618 }
01619 
01620 bool ChannelUtil::UpdateChannel(uint db_mplexid,
01621                                 uint source_id,
01622                                 uint channel_id,
01623                                 const QString &callsign,
01624                                 const QString &service_name,
01625                                 const QString &chan_num,
01626                                 uint service_id,
01627                                 uint atsc_major_channel,
01628                                 uint atsc_minor_channel,
01629                                 bool use_on_air_guide,
01630                                 bool hidden,
01631                                 bool hidden_in_guide,
01632                                 QString freqid,
01633                                 QString icon,
01634                                 QString format,
01635                                 QString xmltvid,
01636                                 QString default_authority)
01637 {
01638     if (!channel_id)
01639         return false;
01640 
01641     QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : format;
01642     bool set_channum = !chan_num.isEmpty() && chan_num != "-1";
01643     QString qstr = QString(
01644         "UPDATE channel "
01645         "SET %1 %2 %3 %4 %5 %6"
01646         "    mplexid         = :MPLEXID,   serviceid       = :SERVICEID, "
01647         "    atsc_major_chan = :MAJORCHAN, atsc_minor_chan = :MINORCHAN, "
01648         "    callsign        = :CALLSIGN,  name            = :NAME,      "
01649         "    sourceid        = :SOURCEID,  useonairguide   = :USEOAG,    "
01650         "    visible         = :VISIBLE "
01651         "WHERE chanid=:CHANID")
01652         .arg((!set_channum)       ? "" : "channum  = :CHANNUM,  ")
01653         .arg((freqid.isEmpty())   ? "" : "freqid   = :FREQID,   ")
01654         .arg((icon.isEmpty())     ? "" : "icon     = :ICON,     ")
01655         .arg((tvformat.isEmpty()) ? "" : "tvformat = :TVFORMAT, ")
01656         .arg((xmltvid.isEmpty())  ? "" : "xmltvid  = :XMLTVID,  ")
01657         .arg((default_authority.isEmpty()) ?
01658              "" : "default_authority = :AUTHORITY,");
01659 
01660     MSqlQuery query(MSqlQuery::InitCon());
01661     query.prepare(qstr);
01662 
01663     query.bindValue(":CHANID", channel_id);
01664 
01665     if (set_channum)
01666         query.bindValue(":CHANNUM", chan_num);
01667 
01668     query.bindValue(":SOURCEID",  source_id);
01669     query.bindValue(":CALLSIGN",  callsign);
01670     query.bindValue(":NAME",      service_name);
01671 
01672     query.bindValue(":MPLEXID",   db_mplexid);
01673 
01674     query.bindValue(":SERVICEID", service_id);
01675     query.bindValue(":MAJORCHAN", atsc_major_channel);
01676     query.bindValue(":MINORCHAN", atsc_minor_channel);
01677     query.bindValue(":USEOAG",    use_on_air_guide);
01678     query.bindValue(":VISIBLE",   !hidden);
01679     (void) hidden_in_guide; // MythTV can't hide the channel in just the guide.
01680 
01681     if (!freqid.isEmpty())
01682         query.bindValue(":FREQID",    freqid);
01683 
01684     if (!tvformat.isEmpty())
01685         query.bindValue(":TVFORMAT",  tvformat);
01686 
01687     if (!icon.isEmpty())
01688         query.bindValue(":ICON",      icon);
01689     if (!xmltvid.isEmpty())
01690         query.bindValue(":XMLTVID",   xmltvid);
01691     if (!default_authority.isEmpty())
01692         query.bindValue(":AUTHORITY",   default_authority);
01693 
01694     if (!query.exec())
01695     {
01696         MythDB::DBError("Updating Service", query);
01697         return false;
01698     }
01699     return true;
01700 }
01701 
01702 void ChannelUtil::UpdateInsertInfoFromDB(ChannelInsertInfo &chan)
01703 {
01704     MSqlQuery query(MSqlQuery::InitCon());
01705     query.prepare(
01706         "SELECT xmltvid, useonairguide "
01707         "FROM channel "
01708         "WHERE chanid = :ID");
01709     query.bindValue(":ID", chan.channel_id);
01710 
01711     if (!query.exec())
01712     {
01713         MythDB::DBError("UpdateInsertInfoFromDB", query);
01714         return;
01715     }
01716 
01717     if (query.next())
01718     {
01719         QString xmltvid = query.value(0).toString();
01720         bool useeit     = query.value(1).toInt();
01721 
01722         if (!xmltvid.isEmpty())
01723         {
01724             if (useeit)
01725                 LOG(VB_GENERAL, LOG_ERR,
01726                     "Using EIT and xmltv for the same channel "
01727                     "is a unsupported configuration.");
01728             chan.xmltvid = xmltvid;
01729             chan.use_on_air_guide = useeit;
01730         }
01731     }
01732 }
01733 
01734 bool ChannelUtil::DeleteChannel(uint channel_id)
01735 {
01736     MSqlQuery query(MSqlQuery::InitCon());
01737     query.prepare(
01738         "DELETE FROM channel "
01739         "WHERE chanid = :ID");
01740     query.bindValue(":ID", channel_id);
01741 
01742     if (!query.exec())
01743     {
01744         MythDB::DBError("Delete Channel", query);
01745         return false;
01746     }
01747 
01748     return true;
01749 }
01750 
01751 bool ChannelUtil::SetVisible(uint channel_id, bool visible)
01752 {
01753     MSqlQuery query(MSqlQuery::InitCon());
01754     query.prepare(
01755         "UPDATE channel "
01756         "SET   visible = :VISIBLE "
01757         "WHERE chanid  = :ID");
01758     query.bindValue(":ID", channel_id);
01759     query.bindValue(":VISIBLE", visible);
01760 
01761     if (!query.exec())
01762     {
01763         MythDB::DBError("ChannelUtil::SetVisible", query);
01764         return false;
01765     }
01766 
01767     return true;
01768 }
01769 
01770 bool ChannelUtil::SetServiceVersion(int mplexid, int version)
01771 {
01772     MSqlQuery query(MSqlQuery::InitCon());
01773 
01774     query.prepare(
01775         QString("UPDATE dtv_multiplex "
01776                 "SET serviceversion = %1 "
01777                 "WHERE mplexid = %2").arg(version).arg(mplexid));
01778 
01779     if (!query.exec() || !query.isActive())
01780     {
01781         MythDB::DBError("Selecting channel/dtv_multiplex", query);
01782         return false;
01783     }
01784     return true;
01785 }
01786 
01787 int ChannelUtil::GetServiceVersion(int mplexid)
01788 {
01789     MSqlQuery query(MSqlQuery::InitCon());
01790 
01791     query.prepare(QString("SELECT serviceversion "
01792                           "FROM dtv_multiplex "
01793                           "WHERE mplexid = %1").arg(mplexid));
01794 
01795     if (!query.exec() || !query.isActive())
01796     {
01797         MythDB::DBError("Selecting channel/dtv_multiplex", query);
01798         return false;
01799     }
01800 
01801     if (query.size() > 0)
01802     {
01803         query.next();
01804         return query.value(0).toInt();
01805     }
01806     return -1;
01807 }
01808 
01809 bool ChannelUtil::GetATSCChannel(uint sourceid, const QString &channum,
01810                                  uint &major,   uint          &minor)
01811 {
01812     major = minor = 0;
01813 
01814     MSqlQuery query(MSqlQuery::InitCon());
01815     query.prepare(
01816         "SELECT atsc_major_chan, atsc_minor_chan "
01817         "FROM channel "
01818         "WHERE channum  = :CHANNUM AND "
01819         "      sourceid = :SOURCEID");
01820 
01821     query.bindValue(":SOURCEID", sourceid);
01822     query.bindValue(":CHANNUM",  channum);
01823 
01824     if (!query.exec() || !query.isActive())
01825         MythDB::DBError("getatscchannel", query);
01826     else if (query.next())
01827     {
01828         major = query.value(0).toUInt();
01829         minor = query.value(1).toUInt();
01830         return true;
01831     }
01832 
01833     return false;
01834 }
01835 
01836 bool ChannelUtil::GetChannelData(
01837     uint    sourceid,         const QString &channum,
01838     QString &tvformat,        QString       &modulation,
01839     QString &freqtable,       QString       &freqid,
01840     int     &finetune,        uint64_t      &frequency,
01841     QString &dtv_si_std,      int           &mpeg_prog_num,
01842     uint    &atsc_major,      uint          &atsc_minor,
01843     uint    &dvb_transportid, uint          &dvb_networkid,
01844     uint    &mplexid,
01845     bool    &commfree)
01846 {
01847     tvformat      = modulation = freqtable = QString::null;
01848     freqid        = dtv_si_std = QString::null;
01849     finetune      = 0;
01850     frequency     = 0;
01851     mpeg_prog_num = -1;
01852     atsc_major    = atsc_minor = mplexid = 0;
01853     dvb_networkid = dvb_transportid = 0;
01854     commfree      = false;
01855 
01856     MSqlQuery query(MSqlQuery::InitCon());
01857     query.prepare(
01858         "SELECT finetune, freqid, tvformat, freqtable, "
01859         "       commmethod, mplexid, "
01860         "       atsc_major_chan, atsc_minor_chan, serviceid "
01861         "FROM channel, videosource "
01862         "WHERE videosource.sourceid = channel.sourceid AND "
01863         "      channum              = :CHANNUM         AND "
01864         "      channel.sourceid     = :SOURCEID");
01865     query.bindValue(":CHANNUM",  channum);
01866     query.bindValue(":SOURCEID", sourceid);
01867 
01868     if (!query.exec() || !query.isActive())
01869     {
01870         MythDB::DBError("GetChannelData", query);
01871         return false;
01872     }
01873     else if (!query.next())
01874     {
01875         LOG(VB_GENERAL, LOG_ERR,
01876             QString("GetChannelData() failed because it could not\n"
01877                     "\t\t\tfind channel number '%1' in DB for source '%2'.")
01878                 .arg(channum).arg(sourceid));
01879         return false;
01880     }
01881 
01882     finetune      = query.value(0).toInt();
01883     freqid        = query.value(1).toString();
01884     tvformat      = query.value(2).toString();
01885     freqtable     = query.value(3).toString();
01886     commfree      = (query.value(4).toInt() == -2);
01887     mplexid       = query.value(5).toUInt();
01888     atsc_major    = query.value(6).toUInt();
01889     atsc_minor    = query.value(7).toUInt();
01890     mpeg_prog_num = (query.value(8).isNull()) ? -1 : query.value(8).toInt();
01891 
01892     if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
01893         return true;
01894 
01895     return GetTuningParams(mplexid, modulation, frequency,
01896                            dvb_transportid, dvb_networkid, dtv_si_std);
01897 }
01898 
01899 bool ChannelUtil::GetExtendedChannelData(
01900     uint    sourceid,         const QString &channum,
01901     QString &tvformat,        QString       &modulation,
01902     QString &freqtable,       QString       &freqid,
01903     int     &finetune,        uint64_t      &frequency,
01904     QString &dtv_si_std,      int           &mpeg_prog_num,
01905     uint    &atsc_major,      uint          &atsc_minor,
01906     uint    &dvb_transportid, uint          &dvb_networkid,
01907     uint    &mplexid,         bool          &commfree,
01908     bool    &use_on_air_guide,bool          &visible,
01909     QString &xmltvid,         QString       &default_authority,
01910     QString &icon)
01911 {
01912     tvformat          = modulation = freqtable = QString::null;
01913     freqid            = dtv_si_std = xmltvid = QString::null;
01914     default_authority = icon       = QString::null;
01915     finetune         = 0;
01916     frequency        = 0;
01917     mpeg_prog_num    = -1;
01918     atsc_major       = atsc_minor = mplexid = 0;
01919     dvb_networkid    = dvb_transportid = 0;
01920     commfree         = false;
01921     use_on_air_guide = false;
01922     visible          = true;
01923 
01924     MSqlQuery query(MSqlQuery::InitCon());
01925     query.prepare(
01926         "SELECT finetune, freqid, tvformat, freqtable, "
01927         "       commmethod, mplexid, "
01928         "       atsc_major_chan, atsc_minor_chan, serviceid, "
01929         "       useonairguide, visible, xmltvid, default_authority, icon "
01930         "FROM channel, videosource "
01931         "WHERE videosource.sourceid = channel.sourceid AND "
01932         "      channum              = :CHANNUM         AND "
01933         "      channel.sourceid     = :SOURCEID");
01934     query.bindValue(":CHANNUM",  channum);
01935     query.bindValue(":SOURCEID", sourceid);
01936 
01937     if (!query.exec() || !query.isActive())
01938     {
01939         MythDB::DBError("GetChannelData", query);
01940         return false;
01941     }
01942     else if (!query.next())
01943     {
01944         LOG(VB_GENERAL, LOG_ERR,
01945             QString("GetChannelData() failed because it could not\n"
01946                     "\t\t\tfind channel number '%1' in DB for source '%2'.")
01947                 .arg(channum).arg(sourceid));
01948         return false;
01949     }
01950 
01951     finetune          = query.value(0).toInt();
01952     freqid            = query.value(1).toString();
01953     tvformat          = query.value(2).toString();
01954     freqtable         = query.value(3).toString();
01955     commfree          = (query.value(4).toInt() == -2);
01956     mplexid           = query.value(5).toUInt();
01957     atsc_major        = query.value(6).toUInt();
01958     atsc_minor        = query.value(7).toUInt();
01959     mpeg_prog_num     = query.value(8).toUInt();
01960     use_on_air_guide  = query.value(9).toBool();
01961     visible           = query.value(10).toBool();
01962     xmltvid           = query.value(11).toString();
01963     default_authority = query.value(12).toString();
01964     icon              = query.value(13).toString();
01965 
01966     if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
01967         return true;
01968 
01969     return GetTuningParams(mplexid, modulation, frequency,
01970                            dvb_transportid, dvb_networkid, dtv_si_std);
01971 }
01972 
01973 DBChanList ChannelUtil::GetChannelsInternal(
01974     uint sourceid, bool vis_only, bool include_disconnected,
01975     const QString &grp, uint changrpid)
01976 {
01977     DBChanList list;
01978 
01979     MSqlQuery query(MSqlQuery::InitCon());
01980 
01981     QString qstr = QString(
01982         "SELECT channum, callsign, channel.chanid, "
01983         "       atsc_major_chan, atsc_minor_chan, "
01984         "       name, icon, mplexid, visible, "
01985         "       channel.sourceid, cardinput.cardid, channelgroup.grpid "
01986         "FROM channel "
01987         "LEFT JOIN channelgroup ON channel.chanid     = channelgroup.chanid "
01988         " %1  JOIN cardinput    ON cardinput.sourceid = channel.sourceid "
01989         " %2  JOIN capturecard  ON cardinput.cardid   = capturecard.cardid ")
01990         .arg((include_disconnected) ? "LEFT" : "")
01991         .arg((include_disconnected) ? "LEFT" : "");
01992 
01993     QString cond = " WHERE ";
01994 
01995     if (sourceid)
01996     {
01997         qstr += QString("WHERE channel.sourceid='%1' ").arg(sourceid);
01998         cond = " AND ";
01999     }
02000 
02001     // Select only channels from the specified channel group
02002     if (changrpid > 0)
02003     {
02004         qstr += QString("%1 channelgroup.grpid = '%2' ")
02005             .arg(cond).arg(changrpid);
02006         cond = " AND ";
02007     }
02008 
02009     if (vis_only)
02010     {
02011         qstr += QString("%1 visible=1 ").arg(cond);
02012         cond = " AND ";
02013     }
02014 
02015     if (!grp.isEmpty())
02016         qstr += QString(" GROUP BY %1 ").arg(grp);
02017 
02018     query.prepare(qstr);
02019     if (!query.exec())
02020     {
02021         MythDB::DBError("ChannelUtil::GetChannels()", query);
02022         return list;
02023     }
02024 
02025     while (query.next())
02026     {
02027         if (query.value(0).toString().isEmpty() || !query.value(2).toUInt())
02028             continue; // skip if channum blank, or chanid empty
02029 
02030         DBChannel chan(
02031             query.value(0).toString(),                    /* channum    */
02032             query.value(1).toString(),                    /* callsign   */
02033             query.value(2).toUInt(),                      /* chanid     */
02034             query.value(3).toUInt(),                      /* ATSC major */
02035             query.value(4).toUInt(),                      /* ATSC minor */
02036             query.value(7).toUInt(),                      /* mplexid    */
02037             query.value(8).toBool(),                      /* visible    */
02038             query.value(5).toString(),                    /* name       */
02039             query.value(6).toString(),                    /* icon       */
02040             query.value(9).toUInt(),                      /* sourceid   */
02041             query.value(11).toUInt(),                     /* cardid     */
02042             query.value(10).toUInt());                    /* grpid      */
02043 
02044         list.push_back(chan);
02045     }
02046 
02047     return list;
02048 }
02049 
02050 vector<uint> ChannelUtil::GetChanIDs(int sourceid)
02051 {
02052     MSqlQuery query(MSqlQuery::InitCon());
02053 
02054     QString select = "SELECT chanid FROM channel";
02055     if (sourceid > 0)
02056         select += " WHERE sourceid=" + QString::number(sourceid);
02057 
02058     vector<uint> list;
02059     if (!query.exec(select))
02060     {
02061         MythDB::DBError("SourceUtil::GetChanIDs()", query);
02062         return list;
02063     }
02064 
02065     while (query.next())
02066         list.push_back(query.value(0).toUInt());
02067 
02068     return list;
02069 }
02070 
02071 inline bool lt_callsign(const DBChannel &a, const DBChannel &b)
02072 {
02073     return QString::localeAwareCompare(a.callsign, b.callsign) < 0;
02074 }
02075 
02076 inline bool lt_smart(const DBChannel &a, const DBChannel &b)
02077 {
02078     static QMutex sepExprLock;
02079     static const QRegExp sepExpr(ChannelUtil::kATSCSeparators);
02080 
02081     int cmp = 0;
02082 
02083     bool isIntA, isIntB;
02084     int a_int = a.channum.toUInt(&isIntA);
02085     int b_int = b.channum.toUInt(&isIntB);
02086     int a_major = a.major_chan;
02087     int b_major = b.major_chan;
02088     int a_minor = a.minor_chan;
02089     int b_minor = b.minor_chan;
02090 
02091     // Extract minor and major numbers from channum..
02092     bool tmp1, tmp2;
02093     int idxA, idxB;
02094     {
02095         QMutexLocker locker(&sepExprLock);
02096         idxA = a.channum.indexOf(sepExpr);
02097         idxB = b.channum.indexOf(sepExpr);
02098     }
02099     if (idxA >= 0)
02100     {
02101         int major = a.channum.left(idxA).toUInt(&tmp1);
02102         int minor = a.channum.mid(idxA+1).toUInt(&tmp2);
02103         if (tmp1 && tmp2)
02104             (a_major = major), (a_minor = minor), (isIntA = false);
02105     }
02106 
02107     if (idxB >= 0)
02108     {
02109         int major = b.channum.left(idxB).toUInt(&tmp1);
02110         int minor = b.channum.mid(idxB+1).toUInt(&tmp2);
02111         if (tmp1 && tmp2)
02112             (b_major = major), (b_minor = minor), (isIntB = false);
02113     }
02114 
02115     // If ATSC channel has been renumbered, sort by new channel number
02116     if ((a_minor > 0) && isIntA)
02117     {
02118         int atsc_int = (QString("%1%2").arg(a_major).arg(a_minor)).toInt();
02119         a_minor = (atsc_int == a_int) ? a_minor : 0;
02120     }
02121 
02122     if ((b_minor > 0) && isIntB)
02123     {
02124         int atsc_int = (QString("%1%2").arg(b_major).arg(b_minor)).toInt();
02125         b_minor = (atsc_int == b_int) ? b_minor : 0;
02126     }
02127 
02128     // one of the channels is an ATSC channel, and the other
02129     // is either ATSC or is numeric.
02130     if ((a_minor || b_minor) &&
02131         (a_minor || isIntA) && (b_minor || isIntB))
02132     {
02133         int a_maj = (!a_minor && isIntA) ? a_int : a_major;
02134         int b_maj = (!b_minor && isIntB) ? b_int : b_major;
02135         if ((cmp = a_maj - b_maj))
02136             return cmp < 0;
02137 
02138         if ((cmp = a_minor - b_minor))
02139             return cmp < 0;
02140     }
02141 
02142     if (isIntA && isIntB)
02143     {
02144         // both channels have a numeric channum
02145         cmp = a_int - b_int;
02146         if (cmp)
02147             return cmp < 0;
02148     }
02149     else if (isIntA ^ isIntB)
02150     {
02151         // if only one is channel numeric always consider it less than
02152         return isIntA;
02153     }
02154     else
02155     {
02156         // neither of channels have a numeric channum
02157         cmp = QString::localeAwareCompare(a.channum, b.channum);
02158         if (cmp)
02159             return cmp < 0;
02160     }
02161 
02162     return lt_callsign(a,b);
02163 }
02164 
02165 uint ChannelUtil::GetChannelCount(int sourceid)
02166 {
02167     MSqlQuery query(MSqlQuery::InitCon());
02168     QString   select;
02169 
02170 
02171     select = "SELECT chanid FROM channel";
02172     if (sourceid >= 0)
02173         select += " WHERE sourceid=" + QString::number(sourceid);
02174     select += ';';
02175 
02176     query.prepare(select);
02177 
02178     if (!query.exec() || !query.isActive())
02179         return 0;
02180 
02181     return query.size();
02182 }
02183 
02184 void ChannelUtil::SortChannels(DBChanList &list, const QString &order,
02185                                bool eliminate_duplicates)
02186 {
02187     bool cs = order.toLower() == "callsign";
02188     if (cs)
02189         stable_sort(list.begin(), list.end(), lt_callsign);
02190     else /* if (sortorder == "channum") */
02191         stable_sort(list.begin(), list.end(), lt_smart);
02192 
02193     if (eliminate_duplicates && !list.empty())
02194     {
02195         DBChanList tmp;
02196         tmp.push_back(list[0]);
02197         for (uint i = 1; i < list.size(); i++)
02198         {
02199             if ((cs && lt_callsign(tmp.back(), list[i])) ||
02200                 (!cs && lt_smart(tmp.back(), list[i])))
02201             {
02202                 tmp.push_back(list[i]);
02203             }
02204         }
02205 
02206         list = tmp;
02207     }
02208 }
02209 
02210 uint ChannelUtil::GetNextChannel(
02211     const DBChanList &sorted,
02212     uint              old_chanid,
02213     uint              mplexid_restriction,
02214     int               direction,
02215     bool              skip_non_visible,
02216     bool              skip_same_channum_and_callsign)
02217 {
02218     DBChanList::const_iterator it =
02219         find(sorted.begin(), sorted.end(), old_chanid);
02220 
02221     if (it == sorted.end())
02222         it = sorted.begin(); // not in list, pretend we are on first channel
02223 
02224     if (it == sorted.end())
02225         return 0; // no channels..
02226 
02227     DBChanList::const_iterator start = it;
02228 
02229     if (CHANNEL_DIRECTION_DOWN == direction)
02230     {
02231         do
02232         {
02233             if (it == sorted.begin())
02234                 it = find(sorted.begin(), sorted.end(),
02235                           sorted.rbegin()->chanid);
02236             else
02237                 --it;
02238         }
02239         while ((it != start) &&
02240                ((skip_non_visible && !it->visible) ||
02241                 (skip_same_channum_and_callsign &&
02242                  it->channum  == start->channum &&
02243                  it->callsign == start->callsign) ||
02244                 (mplexid_restriction &&
02245                  (mplexid_restriction != it->mplexid))));
02246     }
02247     else if ((CHANNEL_DIRECTION_UP == direction) ||
02248              (CHANNEL_DIRECTION_FAVORITE == direction))
02249     {
02250         do
02251         {
02252             ++it;
02253             if (it == sorted.end())
02254                 it = sorted.begin();
02255         }
02256         while ((it != start) &&
02257                ((skip_non_visible && !it->visible) ||
02258                 (skip_same_channum_and_callsign &&
02259                  it->channum  == start->channum &&
02260                  it->callsign == start->callsign) ||
02261                 (mplexid_restriction &&
02262                  (mplexid_restriction != it->mplexid))));
02263     }
02264 
02265     return it->chanid;
02266 }
02267 
02268 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends