11 #include <QReadWriteLock>
12 #include <QRegularExpression>
23 #define LOC QString("ChanUtil: ")
40 "WHERE sourceid = :SOURCEID "
41 " AND sistandard = :SISTANDARD ";
43 if (sistandard.toLower() !=
"dvb")
44 qstr +=
"AND frequency = :FREQUENCY ";
47 qstr +=
"AND transportid = :TRANSPORTID ";
48 qstr +=
"AND networkid = :NETWORKID ";
49 qstr +=
"AND polarity = :POLARITY ";
56 query.
bindValue(
":SOURCEID", db_source_id);
57 query.
bindValue(
":SISTANDARD", sistandard);
59 if (sistandard.toLower() !=
"dvb")
60 query.
bindValue(
":FREQUENCY", QString::number(frequency));
63 query.
bindValue(
":TRANSPORTID", transport_id);
64 query.
bindValue(
":NETWORKID", network_id);
65 query.
bindValue(
":POLARITY", QChar(polarity));
75 return query.
value(0).toUInt();
81 int db_source_id,
const QString& sistandard,
82 uint64_t frequency,
const QString& modulation,
84 int transport_id,
int network_id,
85 int symbol_rate,
signed char bandwidth,
86 signed char polarity,
signed char inversion,
87 signed char trans_mode,
88 const QString& inner_FEC,
const QString& constellation,
89 signed char hierarchy,
const QString& hp_code_rate,
90 const QString& lp_code_rate,
const QString& guard_interval,
91 const QString& mod_sys,
const QString& rolloff)
97 db_source_id, sistandard, frequency,
99 transport_id, network_id, polarity);
101 LOG(VB_CHANSCAN, LOG_INFO,
"insert_dtv_multiplex(" +
102 QString(
"dbid:%1 std:'%2' ").arg(db_source_id).arg(sistandard) +
103 QString(
"freq:%1 mod:%2 ").arg(frequency).arg(modulation) +
104 QString(
"tid:%1 nid:%2 ").arg(transport_id).arg(network_id) +
105 QString(
"pol:%1 msys:%2 ...)").arg(QChar(polarity)).arg(mod_sys) +
106 QString(
"mplexid:%1").arg(mplex));
108 bool isDVB = (sistandard.toLower() ==
"dvb");
111 "UPDATE dtv_multiplex "
112 "SET frequency = :FREQUENCY1, ";
114 updateStr += (!modulation.isNull()) ?
115 "modulation = :MODULATION, " :
"";
116 updateStr += (symbol_rate >= 0) ?
117 "symbolrate = :SYMBOLRATE, " :
"";
118 updateStr += (bandwidth >= 0) ?
119 "bandwidth = :BANDWIDTH, " :
"";
120 updateStr += (polarity >= 0) ?
121 "polarity = :POLARITY, " :
"";
122 updateStr += (inversion >= 0) ?
123 "inversion = :INVERSION, " :
"";
124 updateStr += (trans_mode >= 0) ?
125 "transmission_mode= :TRANS_MODE, " :
"";
126 updateStr += (!inner_FEC.isNull()) ?
127 "fec = :INNER_FEC, " :
"";
128 updateStr += (!constellation.isNull()) ?
129 "constellation = :CONSTELLATION, " :
"";
130 updateStr += (hierarchy >= 0) ?
131 "hierarchy = :HIERARCHY, " :
"";
132 updateStr += (!hp_code_rate.isNull()) ?
133 "hp_code_rate = :HP_CODE_RATE, " :
"";
134 updateStr += (!lp_code_rate.isNull()) ?
135 "lp_code_rate = :LP_CODE_RATE, " :
"";
136 updateStr += (!guard_interval.isNull()) ?
137 "guard_interval = :GUARD_INTERVAL, " :
"";
138 updateStr += (!mod_sys.isNull()) ?
139 "mod_sys = :MOD_SYS, " :
"";
140 updateStr += (symbol_rate >= 0) ?
141 "rolloff = :ROLLOFF, " :
"";
142 updateStr += (transport_id && !isDVB) ?
143 "transportid = :TRANSPORTID, " :
"";
145 updateStr = updateStr.left(updateStr.length()-2) +
" ";
148 "WHERE sourceid = :SOURCEID AND "
149 " sistandard = :SISTANDARD AND ";
151 updateStr += (isDVB) ?
152 " polarity = :WHEREPOLARITY AND "
153 " transportid = :TRANSPORTID AND networkid = :NETWORKID " :
154 " frequency = :FREQUENCY2 ";
157 "INSERT INTO dtv_multiplex "
158 " (sourceid, sistandard, frequency, ";
160 insertStr += (!modulation.isNull()) ?
"modulation, " :
"";
161 insertStr += (transport_id || isDVB) ?
"transportid, " :
"";
162 insertStr += (isDVB) ?
"networkid, " :
"";
163 insertStr += (symbol_rate >= 0) ?
"symbolrate, " :
"";
164 insertStr += (bandwidth >= 0) ?
"bandwidth, " :
"";
165 insertStr += (polarity >= 0) ?
"polarity, " :
"";
166 insertStr += (inversion >= 0) ?
"inversion, " :
"";
167 insertStr += (trans_mode >= 0) ?
"transmission_mode, " :
"";
168 insertStr += (!inner_FEC.isNull()) ?
"fec, " :
"";
169 insertStr += (!constellation.isNull()) ?
"constellation, " :
"";
170 insertStr += (hierarchy >= 0) ?
"hierarchy, " :
"";
171 insertStr += (!hp_code_rate.isNull()) ?
"hp_code_rate, " :
"";
172 insertStr += (!lp_code_rate.isNull()) ?
"lp_code_rate, " :
"";
173 insertStr += (!guard_interval.isNull()) ?
"guard_interval, " :
"";
174 insertStr += (!mod_sys.isNull()) ?
"mod_sys, " :
"";
175 insertStr += (!rolloff.isNull()) ?
"rolloff, " :
"";
176 insertStr = insertStr.left(insertStr.length()-2) +
") ";
180 " (:SOURCEID, :SISTANDARD, :FREQUENCY1, ";
181 insertStr += (!modulation.isNull()) ?
":MODULATION, " :
"";
182 insertStr += (transport_id || isDVB) ?
":TRANSPORTID, " :
"";
183 insertStr += (isDVB) ?
":NETWORKID, " :
"";
184 insertStr += (symbol_rate >= 0) ?
":SYMBOLRATE, " :
"";
185 insertStr += (bandwidth >= 0) ?
":BANDWIDTH, " :
"";
186 insertStr += (polarity >= 0) ?
":POLARITY, " :
"";
187 insertStr += (inversion >= 0) ?
":INVERSION, " :
"";
188 insertStr += (trans_mode >= 0) ?
":TRANS_MODE, " :
"";
189 insertStr += (!inner_FEC.isNull()) ?
":INNER_FEC, " :
"";
190 insertStr += (!constellation.isNull()) ?
":CONSTELLATION, " :
"";
191 insertStr += (hierarchy >= 0) ?
":HIERARCHY, " :
"";
192 insertStr += (!hp_code_rate.isNull()) ?
":HP_CODE_RATE, " :
"";
193 insertStr += (!lp_code_rate.isNull()) ?
":LP_CODE_RATE, " :
"";
194 insertStr += (!guard_interval.isNull()) ?
":GUARD_INTERVAL, " :
"";
195 insertStr += (!mod_sys.isNull()) ?
":MOD_SYS, " :
"";
196 insertStr += (!rolloff.isNull()) ?
":ROLLOFF, " :
"";
197 insertStr = insertStr.left(insertStr.length()-2) +
");";
199 query.
prepare((mplex) ? updateStr : insertStr);
201 query.
bindValue(
":SOURCEID", db_source_id);
202 query.
bindValue(
":SISTANDARD", sistandard);
203 query.
bindValue(
":FREQUENCY1", QString::number(frequency));
209 query.
bindValue(
":TRANSPORTID", transport_id);
210 query.
bindValue(
":NETWORKID", network_id);
211 query.
bindValue(
":WHEREPOLARITY", QChar(polarity));
215 query.
bindValue(
":FREQUENCY2", QString::number(frequency));
217 query.
bindValue(
":TRANSPORTID", transport_id);
222 if (transport_id || isDVB)
223 query.
bindValue(
":TRANSPORTID", transport_id);
225 query.
bindValue(
":NETWORKID", network_id);
228 if (!modulation.isNull())
229 query.
bindValue(
":MODULATION", modulation);
231 if (symbol_rate >= 0)
232 query.
bindValue(
":SYMBOLRATE", symbol_rate);
234 query.
bindValue(
":BANDWIDTH", QString(
"%1").arg((
char)bandwidth));
236 query.
bindValue(
":POLARITY", QString(
"%1").arg((
char)polarity));
238 query.
bindValue(
":INVERSION", QString(
"%1").arg((
char)inversion));
240 query.
bindValue(
":TRANS_MODE", QString(
"%1").arg((
char)trans_mode));
242 if (!inner_FEC.isNull())
243 query.
bindValue(
":INNER_FEC", inner_FEC);
244 if (!constellation.isNull())
245 query.
bindValue(
":CONSTELLATION", constellation);
247 query.
bindValue(
":HIERARCHY", QString(
"%1").arg((
char)hierarchy));
248 if (!hp_code_rate.isNull())
249 query.
bindValue(
":HP_CODE_RATE", hp_code_rate);
250 if (!lp_code_rate.isNull())
251 query.
bindValue(
":LP_CODE_RATE", lp_code_rate);
252 if (!guard_interval.isNull())
253 query.
bindValue(
":GUARD_INTERVAL",guard_interval);
254 if (!mod_sys.isNull())
256 if (!rolloff.isNull())
269 db_source_id, sistandard, frequency,
271 transport_id, network_id, polarity);
273 LOG(VB_CHANSCAN, LOG_INFO, QString(
"insert_dtv_multiplex -- ") +
274 QString(
"inserted mplexid %1").arg(mplex));
298 uint dummy_netid = 0;
300 dummy_tsid, dummy_netid, dummy_sistd);
304 (
int)sourceid,
"dvb",
307 (
int)tsid, (
int)netid,
314 QString(), QString());
317 muxes.push_back(mux);
341 QString(), QString(),
345 muxes.push_back(mux);
365 QString(), QString(),
366 QString(), QString());
369 muxes.push_back(mux);
374 uint64_t frequency,
const QString& modulation,
375 int transport_id,
int network_id)
378 sourceid, sistandard,
379 frequency, modulation,
380 transport_id, network_id,
384 QString(), QString(),
386 QString(), QString(),
387 QString(), QString());
391 int sourceid,
const QString& sistandard,
392 uint64_t
freq,
const QString& modulation,
394 int transport_id,
int network_id,
395 int symbol_rate,
signed char bandwidth,
396 signed char polarity,
signed char inversion,
397 signed char trans_mode,
398 const QString& inner_FEC,
const QString& constellation,
399 signed char hierarchy,
const QString& hp_code_rate,
400 const QString& lp_code_rate,
const QString& guard_interval,
401 const QString& mod_sys,
const QString& rolloff)
404 sourceid, sistandard,
407 transport_id, network_id,
408 symbol_rate, bandwidth,
411 inner_FEC, constellation,
412 hierarchy, hp_code_rate,
413 lp_code_rate, guard_interval,
418 int transport_id,
int network_id)
424 transport_id, network_id,
441 std::vector<uint> muxes;
454 for (
const auto *j : list)
470 "WHERE deleted IS NULL AND "
471 " sourceid = :SOURCEID AND "
472 " channum = :CHANNUM");
479 else if (query.
next())
480 return query.
value(0).toInt();
491 "FROM dtv_multiplex "
492 "WHERE sourceid = :SOURCEID AND "
493 " frequency = :FREQUENCY");
496 query.
bindValue(
":FREQUENCY", QString::number(frequency));
505 return query.
value(0).toInt();
517 "FROM dtv_multiplex "
518 "WHERE networkid = :NETWORKID AND "
519 " transportid = :TRANSPORTID AND "
520 " frequency = :FREQUENCY AND "
521 " sourceid = :SOURCEID");
524 query.
bindValue(
":NETWORKID", network_id);
525 query.
bindValue(
":TRANSPORTID", transport_id);
526 query.
bindValue(
":FREQUENCY", QString::number(frequency));
535 return query.
value(0).toInt();
547 "FROM dtv_multiplex "
548 "WHERE networkid = :NETWORKID AND "
549 " transportid = :TRANSPORTID AND "
550 " sourceid = :SOURCEID");
553 query.
bindValue(
":NETWORKID", network_id);
554 query.
bindValue(
":TRANSPORTID", transport_id);
563 return query.
value(0).toInt();
575 "WHERE chanid = :CHANID");
581 else if (query.
next())
582 return query.
value(0).toInt();
615 LOG(VB_CHANSCAN, LOG_INFO,
616 QString(
"GetBetterMplexID(mplexId %1, tId %2, netId %3)")
617 .arg(current_mplexid).arg(transport_id).arg(network_id));
620 int q_transportid = 0;
623 query.
prepare(
"SELECT networkid, transportid "
624 "FROM dtv_multiplex "
625 "WHERE mplexid = :MPLEX_ID");
627 query.
bindValue(
":MPLEX_ID", current_mplexid);
631 else if (query.
next())
633 q_networkid = query.
value(0).toInt();
634 q_transportid = query.
value(1).toInt();
638 if ((q_networkid == network_id) && (q_transportid == transport_id))
640 LOG(VB_CHANSCAN, LOG_INFO,
641 QString(
"GetBetterMplexID(): Returning perfect match %1")
642 .arg(current_mplexid));
643 return current_mplexid;
647 if (!q_networkid && !q_transportid)
649 int qsize = query.
size();
650 query.
prepare(
"UPDATE dtv_multiplex "
651 "SET networkid = :NETWORK_ID, "
652 " transportid = :TRANSPORT_ID "
653 "WHERE mplexid = :MPLEX_ID");
655 query.
bindValue(
":NETWORK_ID", network_id);
656 query.
bindValue(
":TRANSPORT_ID", transport_id);
657 query.
bindValue(
":MPLEX_ID", current_mplexid);
662 LOG(VB_CHANSCAN, LOG_INFO,
663 QString(
"GetBetterMplexID(): net id and transport id "
664 "are null, qsize(%1), Returning %2")
665 .arg(qsize).arg(current_mplexid));
666 return current_mplexid;
670 std::array<QString,2> theQueries
672 QString(
"SELECT a.mplexid "
673 "FROM dtv_multiplex a, dtv_multiplex b "
674 "WHERE a.networkid = :NETWORK_ID AND "
675 " a.transportid = :TRANSPORT_ID AND "
676 " a.sourceid = b.sourceid AND "
677 " b.mplexid = :MPLEX_ID"),
679 QString(
"SELECT mplexid "
680 "FROM dtv_multiplex "
681 "WHERE networkid = :NETWORK_ID AND "
682 " transportid = :TRANSPORT_ID"),
685 for (
uint i=0; i<2; i++)
689 query.
bindValue(
":NETWORK_ID", network_id);
690 query.
bindValue(
":TRANSPORT_ID", transport_id);
692 query.
bindValue(
":MPLEX_ID", current_mplexid);
697 if (query.
size() == 1 && query.
next())
699 LOG(VB_CHANSCAN, LOG_INFO,
700 QString(
"GetBetterMplexID(): query#%1 qsize(%2) "
702 .arg(i).arg(query.
size()).arg(current_mplexid));
703 return query.
value(0).toInt();
708 int ret = (i==0) ? current_mplexid : query.
value(0).toInt();
709 LOG(VB_CHANSCAN, LOG_INFO,
710 QString(
"GetBetterMplexID(): query#%1 qsize(%2) "
712 .arg(i).arg(query.
size()).arg(ret));
718 LOG(VB_CHANSCAN, LOG_INFO,
"GetBetterMplexID(): Returning -1");
725 uint &dvb_transportid,
729 if (!mplexid || (mplexid == 32767))
734 "SELECT transportid, networkid, frequency, modulation, sistandard "
735 "FROM dtv_multiplex "
736 "WHERE mplexid = :MPLEXID");
748 dvb_transportid = query.
value(0).toUInt();
749 dvb_networkid = query.
value(1).toUInt();
750 frequency = query.
value(2).toULongLong();
751 modulation = query.
value(3).toString();
752 si_std = query.
value(4).toString();
763 query.
prepare(QString(
"SELECT %1 FROM channel "
764 "WHERE chanid = :CHANID").arg(field));
775 return query.
value(0).toString();
792 query.
prepare(
"SELECT sourceid "
793 "FROM dtv_multiplex "
794 "WHERE mplexid = :MPLEXID");
803 return query.
value(0).toInt();
815 "WHERE chanid = :CHANID");
820 else if (query.
next())
821 return query.
value(0).toUInt();
829 query.
prepare(
"SELECT cardtype "
830 "FROM capturecard, channel "
831 "WHERE channel.chanid = :CHANID AND "
832 " channel.sourceid = capturecard.sourceid "
833 "GROUP BY cardtype");
843 list.push_back(query.
value(0).toString());
863 query.
prepare(
"SELECT pid, tableid FROM pidcache "
864 "WHERE chanid = :CHANID");
876 int pid = query.
value(0).toInt();
877 int tid = query.
value(1).toInt();
878 if ((pid >= 0) && (tid >= 0))
879 pid_cache.emplace_back(pid, tid);
881 stable_sort(pid_cache.begin(), pid_cache.end(),
lt_pidcache);
900 query.
prepare(
"DELETE FROM pidcache WHERE chanid = :CHANID");
904 "DELETE FROM pidcache "
905 "WHERE chanid = :CHANID AND tableid < 65536");
919 stable_sort(pid_cache.begin(), pid_cache.end(),
lt_pidcache);
923 "INSERT INTO pidcache "
924 "SET chanid = :CHANID, pid = :PID, tableid = :TABLEID");
928 auto ito = old_cache.begin();
929 for (
const auto& itn : pid_cache)
932 for (; ito != old_cache.end() && ito->GetPID() < itn.GetPID(); ++ito);
935 if (ito != old_cache.end() && ito->GetPID() == itn.GetPID())
939 query.
bindValue(
":TABLEID", itn.GetComposite());
953 const QString &channum)
963 "WHERE deleted IS NULL AND "
964 " channum = :CHANNUM AND "
965 " sourceid = :SOURCEID")
966 .arg(channel_field));
973 else if (query.
next())
974 retval = query.
value(0).toString();
981 const QString &channum)
987 retval = val.toInt();
989 return (retval) ? retval : -1;
994 if (channel_name.isEmpty())
998 query.
prepare(
"SELECT channum FROM channel WHERE sourceid = :SOURCEID "
1000 "AND deleted IS NULL;" );
1002 query.
bindValue(
":NAME", channel_name.left(64));
1012 return query.
value(0).toString();
1016 const QString &new_channum,
1017 const QString &old_channum)
1019 if (new_channum.isEmpty() || old_channum.isEmpty())
1022 if (new_channum == old_channum)
1033 LOG(VB_CHANNEL, LOG_INFO, QString(
"IsOnSameMultiplex? %1==%2 -> %3")
1034 .arg(old_mplexid).arg(new_mplexid)
1035 .arg(old_mplexid == new_mplexid));
1037 return old_mplexid == new_mplexid;
1047 QStringList reclist;
1054 "SELECT capturecard.cardid "
1056 "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid "
1057 "WHERE channel.chanid = :CHANID AND "
1058 " capturecard.livetvorder > 0 "
1059 "ORDER BY capturecard.livetvorder, capturecard.cardid");
1068 while (query.
next())
1069 reclist << query.
value(0).toString();
1081 QStringList reclist;
1088 "SELECT capturecard.cardid "
1090 "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid "
1091 "WHERE channel.deleted IS NULL AND "
1092 " channel.channum = :CHANNUM AND "
1093 " capturecard.livetvorder > 0 "
1094 "ORDER BY capturecard.livetvorder, capturecard.cardid");
1103 while (query.
next())
1104 reclist << query.
value(0).toString();
1117 uint chanid,
const QString &channum)
1121 if (!channum.isEmpty())
1130 std::vector<uint> conflicting;
1135 "SELECT chanid from channel "
1136 "WHERE deleted IS NULL AND "
1137 " sourceid = :SOURCEID AND "
1138 " channum = :CHANNUM");
1144 "SELECT chanid from channel "
1145 "WHERE deleted IS NULL AND "
1146 " channum = :CHANNUM");
1153 conflicting.push_back(0);
1157 while (query.
next())
1158 conflicting.push_back(query.
value(0).toUInt());
1164 const QString& value,
1166 const QString &channum)
1171 QString(
"UPDATE channel SET channel.%1=:VALUE "
1172 "WHERE channel.channum = :CHANNUM AND "
1173 " channel.sourceid = :SOURCEID").arg(field_name));
1179 return query.
exec();
1183 const QString& value,
1189 QString(
"UPDATE channel SET channel.%1=:VALUE "
1190 "WHERE channel.chanid = :CHANID").arg(field_name));
1195 return query.
exec();
1216 "SELECT chanid, m.default_authority "
1218 "LEFT JOIN dtv_multiplex m "
1219 "ON (c.mplexid = m.mplexid) "
1220 "WHERE deleted IS NULL");
1223 while (query.
next())
1225 if (!query.
value(1).toString().isEmpty())
1228 query.
value(1).toString();
1239 "SELECT chanid, default_authority "
1241 "WHERE deleted IS NULL");
1244 while (query.
next())
1246 if (!query.
value(1).toString().isEmpty())
1249 query.
value(1).toString();
1273 static QReadWriteLock s_channelIconMapLock;
1274 static QHash<uint,QString> s_channelIconMap;
1275 static bool s_runInit =
true;
1277 s_channelIconMapLock.lockForRead();
1279 QString ret(s_channelIconMap.value(chanid,
"_cold_"));
1281 s_channelIconMapLock.unlock();
1283 if (ret !=
"_cold_")
1286 s_channelIconMapLock.lockForWrite();
1289 QString iconquery =
"SELECT chanid, icon FROM channel";
1292 iconquery +=
" WHERE visible > 0";
1294 iconquery +=
" WHERE chanid = :CHANID";
1305 s_channelIconMap.reserve(query.
size());
1306 while (query.
next())
1308 s_channelIconMap[query.
value(0).toUInt()] =
1309 query.
value(1).toString();
1315 s_channelIconMap[chanid] = (query.
next()) ?
1316 query.
value(1).toString() :
"";
1324 ret = s_channelIconMap.value(chanid,
"");
1326 s_channelIconMapLock.unlock();
1333 return tr(
"UNKNOWN",
"Synthesized callsign");
1337 int major_channel,
int minor_channel,
1343 query.
prepare(
"SELECT sourceid "
1344 "FROM dtv_multiplex "
1345 "WHERE mplexid = :MPLEXID");
1355 int source_id = query.
value(0).toInt();
1358 query.
prepare(
"SELECT chanid FROM channel,dtv_multiplex "
1359 "WHERE channel.deleted IS NULL AND "
1360 " channel.sourceid = :SOURCEID AND "
1361 " atsc_major_chan = :MAJORCHAN AND "
1362 " atsc_minor_chan = :MINORCHAN AND "
1363 " dtv_multiplex.transportid = :TRANSPORTID AND "
1364 " dtv_multiplex.mplexid = :MPLEXID AND "
1365 " dtv_multiplex.sourceid = channel.sourceid AND "
1366 " dtv_multiplex.mplexid = channel.mplexid");
1368 query.
bindValue(
":SOURCEID", source_id);
1369 query.
bindValue(
":MAJORCHAN", major_channel);
1370 query.
bindValue(
":MINORCHAN", minor_channel);
1371 query.
bindValue(
":TRANSPORTID", service_transport_id);
1375 return query.
value(0).toInt();
1379 query.
prepare(
"SELECT chanid FROM channel "
1380 "WHERE deleted IS NULL AND "
1381 "sourceid = :SOURCEID AND "
1382 "atsc_major_chan = :MAJORCHAN AND "
1383 "atsc_minor_chan = :MINORCHAN");
1385 query.
bindValue(
":SOURCEID", source_id);
1386 query.
bindValue(
":MAJORCHAN", major_channel);
1387 query.
bindValue(
":MINORCHAN", minor_channel);
1390 return query.
value(0).toInt();
1393 query.
prepare(
"SELECT chanid FROM channel "
1394 "WHERE deleted IS NULL AND "
1395 "sourceid = :SOURCEID AND "
1396 "serviceID = :SERVICEID AND "
1397 "mplexid = :MPLEXID");
1399 query.
bindValue(
":SOURCEID", source_id);
1400 query.
bindValue(
":SERVICEID", program_number);
1404 return query.
value(0).toInt();
1412 query.
prepare(
"SELECT chanid "
1414 "WHERE deleted IS NULL AND "
1415 " sourceid = :SOURCEID AND "
1416 " freqid = :FREQID");
1423 else if (query.
next())
1424 return query.
value(0).toUInt();
1432 QString qstr =
"SELECT MAX(chanid) FROM channel ";
1433 qstr += (sourceid) ?
"WHERE sourceid = :SOURCEID" :
"";
1443 else if (!query.
next())
1444 LOG(VB_GENERAL, LOG_ERR,
"Error getting chanid for new channel.");
1446 return query.
value(0).toUInt();
1457 "WHERE chanid = :CHANID");
1462 else if (query.
size() == 0)
1475 static const QRegularExpression kNonDigitRE { R
"(\D)" };
1477 int chansep = chan_num.indexOf(kNonDigitRE);
1482 chan_num.left(chansep).toInt() * 100 +
1483 chan_num.right(chan_num.length() - chansep - 1).toInt();
1487 chanid = sourceid * 10000 + chan_num.toInt();
1494 chanid = std::max(
get_max_chanid(sourceid) + 1, sourceid * 10000);
1511 uint new_channel_id,
1512 const QString &callsign,
1513 const QString &service_name,
1514 const QString &chan_num,
1516 uint atsc_major_channel,
1517 uint atsc_minor_channel,
1518 bool use_on_air_guide,
1520 const QString &freqid,
1521 const QString& icon,
1523 const QString& xmltvid,
1524 const QString& default_authority,
1532 QString chanNum = (chan_num ==
"-1") ?
1533 QString::number(service_id) : chan_num;
1536 "INSERT INTO channel "
1537 " (chanid, channum, sourceid, "
1538 " callsign, name, serviceid, ";
1539 qstr += (db_mplexid > 0) ?
"mplexid, " :
"";
1540 qstr += (!freqid.isEmpty()) ?
"freqid, " :
"";
1542 " atsc_major_chan, atsc_minor_chan, "
1543 " useonairguide, visible, tvformat, "
1544 " icon, xmltvid, default_authority, "
1545 " service_type, recpriority, tmoffset, "
1548 " (:CHANID, :CHANNUM, :SOURCEID, "
1549 " :CALLSIGN, :NAME, :SERVICEID, ";
1550 qstr += (db_mplexid > 0) ?
":MPLEXID, " :
"";
1551 qstr += (!freqid.isEmpty()) ?
":FREQID, " :
"";
1553 " :MAJORCHAN, :MINORCHAN, "
1554 " :USEOAG, :VISIBLE, :TVFORMAT, "
1555 " :ICON, :XMLTVID, :AUTHORITY, "
1556 " :SERVICETYPE, :RECPRIORITY, :TMOFFSET, "
1561 query.
bindValue (
":CHANID", new_channel_id);
1563 query.
bindValue (
":SOURCEID", db_sourceid);
1568 query.
bindValue(
":MPLEXID", db_mplexid);
1570 query.
bindValue(
":SERVICEID", service_id);
1571 query.
bindValue(
":MAJORCHAN", atsc_major_channel);
1572 query.
bindValue(
":MINORCHAN", atsc_minor_channel);
1573 query.
bindValue(
":USEOAG", use_on_air_guide);
1576 if (!freqid.isEmpty())
1579 QString tvformat = (atsc_minor_channel > 0) ?
"ATSC" : std::move(format);
1584 query.
bindValue (
":SERVICETYPE", service_type);
1585 query.
bindValue (
":RECPRIORITY", recpriority);
1586 query.
bindValue (
":TMOFFSET", tmOffset);
1587 query.
bindValue (
":COMMMETHOD", commMethod);
1600 const QString &callsign,
1601 const QString &service_name,
1602 const QString &chan_num,
1604 uint atsc_major_channel,
1605 uint atsc_minor_channel,
1606 bool use_on_air_guide,
1608 const QString& freqid,
1609 const QString& icon,
1611 const QString& xmltvid,
1612 const QString& default_authority,
1621 QString tvformat = (atsc_minor_channel > 0) ?
"ATSC" : std::move(format);
1622 bool set_channum = !chan_num.isEmpty() && chan_num !=
"-1";
1623 QString qstr = QString(
1625 "SET %1 %2 %3 %4 %5 %6 %7 %8 %9 "
1626 " mplexid = :MPLEXID, serviceid = :SERVICEID, "
1627 " atsc_major_chan = :MAJORCHAN, atsc_minor_chan = :MINORCHAN, "
1628 " callsign = :CALLSIGN, name = :NAME, "
1629 " sourceid = :SOURCEID, useonairguide = :USEOAG, "
1630 " visible = :VISIBLE, service_type = :SERVICETYPE "
1631 "WHERE chanid=:CHANID")
1632 .arg((!set_channum) ?
"" :
"channum = :CHANNUM, ",
1633 (freqid.isEmpty()) ?
"" :
"freqid = :FREQID, ",
1634 (icon.isEmpty()) ?
"" :
"icon = :ICON, ",
1635 (tvformat.isEmpty()) ?
"" :
"tvformat = :TVFORMAT, ",
1636 (xmltvid.isEmpty()) ?
"" :
"xmltvid = :XMLTVID, ",
1637 (default_authority.isEmpty()) ?
1638 "" :
"default_authority = :AUTHORITY,",
1639 (recpriority == INT_MIN) ?
"" :
"recpriority = :RECPRIORITY, ",
1640 (tmOffset == INT_MIN) ?
"" :
"tmOffset = :TMOFFSET, ",
1641 (commMethod == INT_MIN) ?
"" :
"commmethod = :COMMMETHOD, ");
1651 query.
bindValue (
":SOURCEID", source_id);
1655 query.
bindValue(
":MPLEXID", db_mplexid);
1656 query.
bindValue(
":SERVICEID", service_id);
1657 query.
bindValue(
":MAJORCHAN", atsc_major_channel);
1658 query.
bindValue(
":MINORCHAN", atsc_minor_channel);
1659 query.
bindValue(
":USEOAG", use_on_air_guide);
1661 query.
bindValue(
":SERVICETYPE", service_type);
1663 if (!freqid.isNull())
1665 if (!tvformat.isNull())
1669 if (!xmltvid.isNull())
1671 if (!default_authority.isNull())
1672 query.
bindValue(
":AUTHORITY", default_authority);
1673 if (recpriority != INT_MIN)
1674 query.
bindValue(
":RECPRIORITY", recpriority);
1675 if (tmOffset != INT_MIN)
1677 if (commMethod != INT_MIN)
1678 query.
bindValue(
":COMMMETHOD", commMethod);
1694 "WHERE chanid = :ID");
1705 QString channum = query.
value(0).toString();
1707 if (!channum.isEmpty())
1718 "SELECT xmltvid, useonairguide, visible "
1720 "WHERE chanid = :ID");
1731 QString xmltvid = query.
value(0).toString();
1732 bool useeit = query.
value(1).toBool();
1736 if (!xmltvid.isEmpty())
1740 LOG(VB_GENERAL, LOG_ERR,
1741 "Using EIT and xmltv for the same channel "
1742 "is an unsupported configuration.");
1759 "DELETE FROM iptv_channel "
1760 "WHERE chanid=:CHANID");
1770 "INSERT INTO iptv_channel (chanid, url, type, bitrate) "
1771 "VALUES (:CHANID, :URL, :TYPE, :BITRATE)");
1816 "SET deleted = NOW() "
1817 "WHERE chanid = :ID");
1834 "SET visible = :VISIBLE "
1835 "WHERE chanid = :ID");
1852 query.
prepare(
"UPDATE dtv_multiplex "
1853 "SET serviceversion = :VERSION "
1854 "WHERE mplexid = :MPLEXID");
1871 query.
prepare(
"SELECT serviceversion "
1872 "FROM dtv_multiplex "
1873 "WHERE mplexid = :MPLEXID");
1884 return query.
value(0).toInt();
1896 "SELECT atsc_major_chan, atsc_minor_chan "
1898 "WHERE deleted IS NULL AND "
1899 " channum = :CHANNUM AND "
1900 " sourceid = :SOURCEID");
1907 else if (query.
next())
1909 major = query.
value(0).toUInt();
1919 uint &chanid,
const QString &channum,
1920 QString &tvformat, QString &modulation,
1921 QString &freqtable, QString &freqid,
1922 int &finetune, uint64_t &frequency,
1923 QString &dtv_si_std,
int &mpeg_prog_num,
1924 uint &atsc_major,
uint &atsc_minor,
1925 uint &dvb_transportid,
uint &dvb_networkid,
1938 atsc_major = atsc_minor = mplexid = 0;
1939 dvb_networkid = dvb_transportid = 0;
1945 "SELECT finetune, freqid, tvformat, freqtable, "
1946 " commmethod, mplexid, "
1947 " atsc_major_chan, atsc_minor_chan, serviceid, "
1949 "FROM channel, videosource "
1950 "WHERE channel.deleted IS NULL AND "
1951 " videosource.sourceid = channel.sourceid AND "
1952 " channum = :CHANNUM AND "
1953 " channel.sourceid = :SOURCEID "
1954 "ORDER BY channel.visible > 0 DESC, channel.chanid ");
1966 finetune = query.
value(0).toInt();
1967 freqid = query.
value(1).toString();
1968 tvformat = query.
value(2).toString();
1969 freqtable = query.
value(3).toString();
1970 commfree = (query.
value(4).toInt() == -2);
1971 mplexid = query.
value(5).toUInt();
1972 atsc_major = query.
value(6).toUInt();
1973 atsc_minor = query.
value(7).toUInt();
1974 mpeg_prog_num = (query.
value(8).isNull()) ? -1
1975 : query.
value(8).toInt();
1976 chanid = query.
value(9).toUInt();
1982 while (query.
next())
1986 if (found == 0 && chanid)
1988 LOG(VB_GENERAL, LOG_WARNING,
1989 QString(
"No visible channels for %1, using invisble chanid %2")
1990 .arg(channum).arg(chanid));
1995 LOG(VB_GENERAL, LOG_WARNING,
1996 QString(
"Found multiple visible channels for %1, using chanid %2")
1997 .arg(channum).arg(chanid));
2002 LOG(VB_GENERAL, LOG_ERR,
2003 QString(
"Could not find channel '%1' in DB for source %2 '%3'.")
2008 if (!mplexid || (mplexid == 32767))
2012 dvb_transportid, dvb_networkid, dtv_si_std);
2019 "SELECT type+0, url, bitrate "
2020 "FROM iptv_channel "
2021 "WHERE chanid = :CHANID "
2035 std::array<uint,3> bitrate { 0, 0, 0, };
2036 while (query.
next())
2039 query.
value(0).toUInt();
2043 data_url = query.
value(1).toString();
2044 bitrate[0] = query.
value(2).toUInt();
2049 fec_url0 = query.
value(1).toString();
2050 bitrate[1] = query.
value(2).toUInt();
2055 fec_url1 = query.
value(1).toString();
2056 bitrate[2] = query.
value(2).toUInt();
2080 fec_url0, bitrate[1], fec_url1, bitrate[2]);
2081 LOG(VB_GENERAL, LOG_INFO, QString(
"Loaded %1 for %2")
2092 uint sourceid,
bool visible_only,
bool include_disconnected,
2093 const QString &group_by,
uint channel_groupid)
2099 QString qstr = QString(
2100 "SELECT videosource.sourceid, GROUP_CONCAT(capturecard.cardid) "
2102 "%1 JOIN capturecard ON capturecard.sourceid = videosource.sourceid "
2103 "GROUP BY videosource.sourceid")
2104 .arg((include_disconnected) ?
"LEFT" :
"");
2113 QMap<uint, QList<uint>> inputIdLists;
2114 while (query.
next())
2116 uint qSourceId = query.
value(0).toUInt();
2117 QList<uint> &inputIdList = inputIdLists[qSourceId];
2118 QStringList inputIds = query.
value(1).toString().split(
",");
2119 while (!inputIds.isEmpty())
2120 inputIdList.append(inputIds.takeFirst().toUInt());
2124 "SELECT channum, callsign, channel.chanid, "
2125 " atsc_major_chan, atsc_minor_chan, "
2126 " name, icon, mplexid, visible, "
2127 " channel.sourceid, "
2128 " GROUP_CONCAT(DISTINCT channelgroup.grpid), "
2131 "LEFT JOIN channelgroup ON channel.chanid = channelgroup.chanid ");
2133 qstr +=
"WHERE deleted IS NULL ";
2136 qstr += QString(
"AND channel.sourceid='%1' ").arg(sourceid);
2139 if (channel_groupid > 0)
2140 qstr += QString(
"AND channelgroup.grpid = '%1' ").arg(channel_groupid);
2143 qstr += QString(
"AND visible > 0 ");
2145 qstr +=
" GROUP BY chanid";
2147 if (!group_by.isEmpty())
2148 qstr += QString(
", %1").arg(group_by);
2157 while (query.
next())
2159 if (query.
value(0).toString().isEmpty() || !query.
value(2).toBool())
2162 uint qSourceID = query.
value(9).toUInt();
2164 query.
value(0).toString(),
2165 query.
value(1).toString(),
2166 query.
value(2).toUInt(),
2167 query.
value(3).toUInt(),
2168 query.
value(4).toUInt(),
2169 query.
value(7).toUInt(),
2172 query.
value(5).toString(),
2173 query.
value(6).toString(),
2178 for (
auto inputId : std::as_const(inputIdLists[qSourceID]))
2181 QStringList groupIDs = query.
value(10).toString().split(
",");
2182 while (!groupIDs.isEmpty())
2183 chan.
AddGroupId(groupIDs.takeFirst().toUInt());
2185 list.push_back(chan);
2196 QString select =
"SELECT chanid FROM channel WHERE deleted IS NULL ";
2198 if (onlyVisible || sourceid > 0)
2201 select +=
"AND visible > 0 ";
2203 select +=
"AND sourceid=" + QString::number(sourceid);
2206 std::vector<uint> list;
2214 while (query.
next())
2215 list.push_back(query.
value(0).toUInt());
2227 static QMutex s_sepExprLock;
2230 bool isIntA =
false;
2231 bool isIntB =
false;
2232 int a_int = a.
m_chanNum.toUInt(&isIntA);
2233 int b_int = b.
m_chanNum.toUInt(&isIntB);
2243 QMutexLocker locker(&s_sepExprLock);
2251 int major = a.
m_chanNum.left(idxA).toUInt(&tmp1);
2254 (a_major = major), (a_minor =
minor), (isIntA =
false);
2261 int major = b.
m_chanNum.left(idxB).toUInt(&tmp1);
2264 (b_major = major), (b_minor =
minor), (isIntB =
false);
2268 if ((a_minor > 0) && isIntA)
2270 int atsc_int = (QString(
"%1%2").arg(a_major).arg(a_minor)).toInt();
2271 a_minor = (atsc_int == a_int) ? a_minor : 0;
2274 if ((b_minor > 0) && isIntB)
2276 int atsc_int = (QString(
"%1%2").arg(b_major).arg(b_minor)).toInt();
2277 b_minor = (atsc_int == b_int) ? b_minor : 0;
2282 if ((a_minor || b_minor) &&
2283 (a_minor || isIntA) && (b_minor || isIntB))
2285 int a_maj = (!a_minor && isIntA) ? a_int : a_major;
2286 int b_maj = (!b_minor && isIntB) ? b_int : b_major;
2287 int cmp = a_maj - b_maj;
2291 cmp = a_minor - b_minor;
2296 if (isIntA && isIntB)
2299 int cmp = a_int - b_int;
2303 else if (isIntA ^ isIntB)
2325 select =
"SELECT chanid FROM channel WHERE deleted IS NULL ";
2327 select +=
"AND sourceid=" + QString::number(sourceid);
2335 return query.
size();
2339 bool eliminate_duplicates)
2341 bool cs = order.toLower() ==
"callsign";
2343 stable_sort(list.begin(), list.end(),
lt_callsign);
2345 stable_sort(list.begin(), list.end(),
lt_smart);
2347 if (eliminate_duplicates && !list.empty())
2350 tmp.push_back(list[0]);
2351 for (
size_t i = 1; i < list.size(); i++)
2356 tmp.push_back(list[i]);
2372 const QString &channum)
2377 for (
int i = 0; i < (int)list.size(); ++i)
2398 uint mplexid_restriction,
2399 uint chanid_restriction,
2401 bool skip_non_visible,
2402 bool skip_same_channum_and_callsign,
2403 bool skip_other_sources)
2405 auto it =
find(sorted.cbegin(), sorted.cend(), old_chanid);
2407 if (it == sorted.end())
2408 it = sorted.begin();
2410 if (it == sorted.end())
2419 if (it == sorted.begin())
2421 it =
find(sorted.begin(), sorted.end(),
2422 sorted.rbegin()->m_chanId);
2423 if (it == sorted.end())
2433 while ((it != start) &&
2435 (skip_other_sources &&
2436 it->m_sourceId != start->m_sourceId) ||
2437 (skip_same_channum_and_callsign &&
2438 it->m_chanNum == start->m_chanNum &&
2439 it->m_callSign == start->m_callSign) ||
2440 ((mplexid_restriction != 0U) &&
2441 (mplexid_restriction != it->m_mplexId)) ||
2442 ((chanid_restriction != 0U) &&
2443 (chanid_restriction != it->m_chanId))));
2451 if (it == sorted.end())
2452 it = sorted.begin();
2454 while ((it != start) &&
2456 (skip_other_sources &&
2457 it->m_sourceId != start->m_sourceId) ||
2458 (skip_same_channum_and_callsign &&
2459 it->m_chanNum == start->m_chanNum &&
2460 it->m_callSign == start->m_callSign) ||
2461 ((mplexid_restriction != 0U) &&
2462 (mplexid_restriction != it->m_mplexId)) ||
2463 ((chanid_restriction != 0U) &&
2464 (chanid_restriction != it->m_chanId))));
2467 return it->m_chanId;
2471 uint &totalAvailable,
2476 uint channelGroupID,
2478 const QString& callsign,
2479 const QString& channum,
2480 bool ignoreUntunable)
2486 QString sql = QString(
2487 "SELECT parentid, GROUP_CONCAT(cardid ORDER BY cardid) "
2489 "WHERE parentid <> 0 "
2490 "GROUP BY parentid ");
2499 QMap<uint, QList<uint>> childIdLists;
2500 while (query.
next())
2502 auto parentId = query.
value(0).toUInt();
2503 auto &childIdList = childIdLists[parentId];
2504 auto childIds = query.
value(1).toString().split(
",");
2505 while (!childIds.isEmpty())
2506 childIdList.append(childIds.takeFirst().toUInt());
2509 sql =
"SELECT %1 channum, freqid, channel.sourceid, "
2510 "callsign, name, icon, finetune, videofilters, xmltvid, "
2511 "channel.recpriority, channel.contrast, channel.brightness, "
2512 "channel.colour, channel.hue, tvformat, "
2513 "visible, outputfilters, useonairguide, mplexid, "
2514 "serviceid, atsc_major_chan, atsc_minor_chan, last_record, "
2515 "default_authority, commmethod, tmoffset, iptvid, "
2517 "GROUP_CONCAT(DISTINCT `groups`.`groupids`), "
2518 "GROUP_CONCAT(DISTINCT capturecard.cardid "
2519 " ORDER BY livetvorder), "
2520 "MIN(livetvorder) livetvorder "
2522 if (!channelGroupID)
2526 " GROUP_CONCAT(grpid ORDER BY grpid) groupids "
2527 " FROM channelgroup ";
2529 sql +=
" WHERE grpid = :CHANGROUPID ";
2530 sql +=
" GROUP BY chanid "
2532 " ON channel.chanid = `groups`.`chanid` ";
2533 if (!ignoreUntunable && !liveTVOnly)
2535 sql +=
"JOIN capturecard "
2536 " ON capturecard.sourceid = channel.sourceid "
2537 " AND capturecard.parentid = 0 ";
2539 sql +=
" AND capturecard.livetvorder > 0 ";
2541 sql +=
"WHERE channel.deleted IS NULL ";
2543 sql +=
"AND channel.visible > 0 ";
2546 sql +=
"AND channel.sourceid = :SOURCEID ";
2549 sql +=
"GROUP BY channel.callsign ";
2551 sql +=
"GROUP BY channel.callsign, channel.channum ";
2553 sql +=
"GROUP BY channel.chanid ";
2556 sql +=
"ORDER BY channel.name ";
2560 sql +=
"ORDER BY LPAD(CAST(channel.channum AS UNSIGNED), 10, 0), "
2561 " LPAD(channel.channum, 10, 0) ";
2565 sql +=
"ORDER BY callsign = :CALLSIGN1 AND channum = :CHANNUM DESC, "
2566 " callsign = :CALLSIGN2 DESC, "
2568 " channel.recpriority DESC, "
2573 sql +=
"LIMIT :LIMIT ";
2576 sql +=
"OFFSET :STARTINDEX ";
2579 if (startIndex > 0 || count > 0)
2580 sql = sql.arg(
"SQL_CALC_FOUND_ROWS");
2586 if (channelGroupID > 0)
2587 query.
bindValue(
":CHANGROUPID", channelGroupID);
2596 query.
bindValue(
":STARTINDEX", startIndex);
2600 query.
bindValue(
":CALLSIGN1", callsign);
2602 query.
bindValue(
":CALLSIGN2", callsign);
2611 QList<uint> groupIdList;
2612 while (query.
next())
2628 channelInfo.
m_hue = query.
value(13).toUInt();
2645 QStringList groupIDs = query.
value(28).toString().split(
",");
2646 groupIdList.clear();
2647 while (!groupIDs.isEmpty())
2648 groupIdList.push_back(groupIDs.takeFirst().toUInt());
2649 std::sort(groupIdList.begin(), groupIdList.end());
2650 for (
auto groupId : groupIdList)
2653 QStringList parentIDs = query.
value(29).toString().split(
",");
2654 while (!parentIDs.isEmpty())
2656 auto parentId = parentIDs.takeFirst().toUInt();
2658 auto childIdList = childIdLists[parentId];
2659 for (
auto childId : childIdList)
2663 channelList.push_back(channelInfo);
2666 if ((startIndex > 0 || count > 0) &&
2667 query.
exec(
"SELECT FOUND_ROWS()") && query.
next())
2668 totalAvailable = query.
value(0).toUInt();
2670 totalAvailable = query.
size();