|
MythTV
0.26-pre
|
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: */
1.7.6.1