MythTV master
channelutil.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3#include <algorithm>
4#include <cstdint>
5#include <set>
6#include <utility>
7
8#include <QFile>
9#include <QHash>
10#include <QImage>
11#include <QReadWriteLock>
12#include <QRegularExpression>
13
14#include "libmythbase/mythdb.h"
17
18#include "channelutil.h"
19#include "mpeg/dvbtables.h"
21#include "sourceutil.h"
22
23#define LOC QString("ChanUtil: ")
24
25const QString ChannelUtil::kATSCSeparators = "(_|-|#|\\.)";
26
27static uint get_dtv_multiplex(uint db_source_id, const QString& sistandard,
28 uint64_t frequency,
29 // DVB specific
30 uint transport_id,
31 // tsid exists with other sistandards,
32 // but we only trust it in dvb-land.
33 uint network_id,
34 // must check polarity for dvb-s
35 signed char polarity)
36{
37 QString qstr =
38 "SELECT mplexid "
39 "FROM dtv_multiplex "
40 "WHERE sourceid = :SOURCEID "
41 " AND sistandard = :SISTANDARD ";
42
43 if (sistandard.toLower() != "dvb")
44 {
45 qstr += "AND frequency = :FREQUENCY ";
46 }
47 else
48 {
49 qstr += "AND transportid = :TRANSPORTID ";
50 qstr += "AND networkid = :NETWORKID ";
51 qstr += "AND polarity = :POLARITY ";
52 }
53
54
56 query.prepare(qstr);
57
58 query.bindValue(":SOURCEID", db_source_id);
59 query.bindValue(":SISTANDARD", sistandard);
60
61 if (sistandard.toLower() != "dvb")
62 {
63 query.bindValue(":FREQUENCY", QString::number(frequency));
64 }
65 else
66 {
67 query.bindValue(":TRANSPORTID", transport_id);
68 query.bindValue(":NETWORKID", network_id);
69 query.bindValue(":POLARITY", QChar(static_cast<uint16_t>(polarity)));
70 }
71
72 if (!query.exec() || !query.isActive())
73 {
74 MythDB::DBError("get_dtv_multiplex", query);
75 return 0;
76 }
77
78 if (query.next())
79 return query.value(0).toUInt();
80
81 return 0;
82}
83
85 int db_source_id, const QString& sistandard,
86 uint64_t frequency, const QString& modulation,
87 // DVB specific
88 int transport_id, int network_id,
89 int symbol_rate, signed char bandwidth,
90 signed char polarity, signed char inversion,
91 signed char trans_mode,
92 const QString& inner_FEC, const QString& constellation,
93 signed char hierarchy, const QString& hp_code_rate,
94 const QString& lp_code_rate, const QString& guard_interval,
95 const QString& mod_sys, const QString& rolloff)
96{
98
99 // If transport is already present, skip insert
100 uint mplex = get_dtv_multiplex(
101 db_source_id, sistandard, frequency,
102 // DVB specific
103 transport_id, network_id, polarity);
104
105 LOG(VB_CHANSCAN, LOG_INFO, "insert_dtv_multiplex(" +
106 QString("dbid:%1 std:'%2' ").arg(db_source_id).arg(sistandard) +
107 QString("freq:%1 mod:%2 ").arg(frequency).arg(modulation) +
108 QString("tid:%1 nid:%2 ").arg(transport_id).arg(network_id) +
109 QString("pol:%1 msys:%2 ...)").arg(QChar(static_cast<uint16_t>(polarity))).arg(mod_sys) +
110 QString("mplexid:%1").arg(mplex));
111
112 bool isDVB = (sistandard.toLower() == "dvb");
113
114 QString updateStr =
115 "UPDATE dtv_multiplex "
116 "SET frequency = :FREQUENCY1, ";
117
118 updateStr += (!modulation.isNull()) ?
119 "modulation = :MODULATION, " : "";
120 updateStr += (symbol_rate >= 0) ?
121 "symbolrate = :SYMBOLRATE, " : "";
122 updateStr += (bandwidth >= 0) ?
123 "bandwidth = :BANDWIDTH, " : "";
124 updateStr += (polarity >= 0) ?
125 "polarity = :POLARITY, " : "";
126 updateStr += (inversion >= 0) ?
127 "inversion = :INVERSION, " : "";
128 updateStr += (trans_mode >= 0) ?
129 "transmission_mode= :TRANS_MODE, " : "";
130 updateStr += (!inner_FEC.isNull()) ?
131 "fec = :INNER_FEC, " : "";
132 updateStr += (!constellation.isNull()) ?
133 "constellation = :CONSTELLATION, " : "";
134 updateStr += (hierarchy >= 0) ?
135 "hierarchy = :HIERARCHY, " : "";
136 updateStr += (!hp_code_rate.isNull()) ?
137 "hp_code_rate = :HP_CODE_RATE, " : "";
138 updateStr += (!lp_code_rate.isNull()) ?
139 "lp_code_rate = :LP_CODE_RATE, " : "";
140 updateStr += (!guard_interval.isNull()) ?
141 "guard_interval = :GUARD_INTERVAL, " : "";
142 updateStr += (!mod_sys.isNull()) ?
143 "mod_sys = :MOD_SYS, " : "";
144 updateStr += (symbol_rate >= 0) ?
145 "rolloff = :ROLLOFF, " : "";
146 updateStr += (transport_id && !isDVB) ?
147 "transportid = :TRANSPORTID, " : "";
148
149 updateStr = updateStr.left(updateStr.length()-2) + " ";
150
151 updateStr +=
152 "WHERE sourceid = :SOURCEID AND "
153 " sistandard = :SISTANDARD AND ";
154
155 updateStr += isDVB ?
156 " polarity = :WHEREPOLARITY AND "
157 " transportid = :TRANSPORTID AND networkid = :NETWORKID " :
158 " frequency = :FREQUENCY2 ";
159
160 QString insertStr =
161 "INSERT INTO dtv_multiplex "
162 " (sourceid, sistandard, frequency, ";
163
164 insertStr += (!modulation.isNull()) ? "modulation, " : "";
165 insertStr += (transport_id || isDVB) ? "transportid, " : "";
166 insertStr += isDVB ? "networkid, " : "";
167 insertStr += (symbol_rate >= 0) ? "symbolrate, " : "";
168 insertStr += (bandwidth >= 0) ? "bandwidth, " : "";
169 insertStr += (polarity >= 0) ? "polarity, " : "";
170 insertStr += (inversion >= 0) ? "inversion, " : "";
171 insertStr += (trans_mode >= 0) ? "transmission_mode, " : "";
172 insertStr += (!inner_FEC.isNull()) ? "fec, " : "";
173 insertStr += (!constellation.isNull()) ? "constellation, " : "";
174 insertStr += (hierarchy >= 0) ? "hierarchy, " : "";
175 insertStr += (!hp_code_rate.isNull()) ? "hp_code_rate, " : "";
176 insertStr += (!lp_code_rate.isNull()) ? "lp_code_rate, " : "";
177 insertStr += (!guard_interval.isNull()) ? "guard_interval, " : "";
178 insertStr += (!mod_sys.isNull()) ? "mod_sys, " : "";
179 insertStr += (!rolloff.isNull()) ? "rolloff, " : "";
180 insertStr = insertStr.left(insertStr.length()-2) + ") ";
181
182 insertStr +=
183 "VALUES "
184 " (:SOURCEID, :SISTANDARD, :FREQUENCY1, ";
185 insertStr += (!modulation.isNull()) ? ":MODULATION, " : "";
186 insertStr += (transport_id || isDVB) ? ":TRANSPORTID, " : "";
187 insertStr += isDVB ? ":NETWORKID, " : "";
188 insertStr += (symbol_rate >= 0) ? ":SYMBOLRATE, " : "";
189 insertStr += (bandwidth >= 0) ? ":BANDWIDTH, " : "";
190 insertStr += (polarity >= 0) ? ":POLARITY, " : "";
191 insertStr += (inversion >= 0) ? ":INVERSION, " : "";
192 insertStr += (trans_mode >= 0) ? ":TRANS_MODE, " : "";
193 insertStr += (!inner_FEC.isNull()) ? ":INNER_FEC, " : "";
194 insertStr += (!constellation.isNull()) ? ":CONSTELLATION, " : "";
195 insertStr += (hierarchy >= 0) ? ":HIERARCHY, " : "";
196 insertStr += (!hp_code_rate.isNull()) ? ":HP_CODE_RATE, " : "";
197 insertStr += (!lp_code_rate.isNull()) ? ":LP_CODE_RATE, " : "";
198 insertStr += (!guard_interval.isNull()) ? ":GUARD_INTERVAL, " : "";
199 insertStr += (!mod_sys.isNull()) ? ":MOD_SYS, " : "";
200 insertStr += (!rolloff.isNull()) ? ":ROLLOFF, " : "";
201 insertStr = insertStr.left(insertStr.length()-2) + ");";
202
203 query.prepare(mplex ? updateStr : insertStr);
204
205 query.bindValue(":SOURCEID", db_source_id);
206 query.bindValue(":SISTANDARD", sistandard);
207 query.bindValue(":FREQUENCY1", QString::number(frequency));
208
209 if (mplex)
210 {
211 if (isDVB)
212 {
213 query.bindValue(":TRANSPORTID", transport_id);
214 query.bindValue(":NETWORKID", network_id);
215 query.bindValue(":WHEREPOLARITY", QChar(static_cast<uint16_t>(polarity)));
216 }
217 else
218 {
219 query.bindValue(":FREQUENCY2", QString::number(frequency));
220 if (transport_id)
221 query.bindValue(":TRANSPORTID", transport_id);
222 }
223 }
224 else
225 {
226 if (transport_id || isDVB)
227 query.bindValue(":TRANSPORTID", transport_id);
228 if (isDVB)
229 query.bindValue(":NETWORKID", network_id);
230 }
231
232 if (!modulation.isNull())
233 query.bindValue(":MODULATION", modulation);
234
235 if (symbol_rate >= 0)
236 query.bindValue(":SYMBOLRATE", symbol_rate);
237 if (bandwidth >= 0)
238 query.bindValue(":BANDWIDTH", QString("%1").arg((char)bandwidth));
239 if (polarity >= 0)
240 query.bindValue(":POLARITY", QString("%1").arg((char)polarity));
241 if (inversion >= 0)
242 query.bindValue(":INVERSION", QString("%1").arg((char)inversion));
243 if (trans_mode >= 0)
244 query.bindValue(":TRANS_MODE", QString("%1").arg((char)trans_mode));
245
246 if (!inner_FEC.isNull())
247 query.bindValue(":INNER_FEC", inner_FEC);
248 if (!constellation.isNull())
249 query.bindValue(":CONSTELLATION", constellation);
250 if (hierarchy >= 0)
251 query.bindValue(":HIERARCHY", QString("%1").arg((char)hierarchy));
252 if (!hp_code_rate.isNull())
253 query.bindValue(":HP_CODE_RATE", hp_code_rate);
254 if (!lp_code_rate.isNull())
255 query.bindValue(":LP_CODE_RATE", lp_code_rate);
256 if (!guard_interval.isNull())
257 query.bindValue(":GUARD_INTERVAL",guard_interval);
258 if (!mod_sys.isNull())
259 query.bindValue(":MOD_SYS", mod_sys);
260 if (!rolloff.isNull())
261 query.bindValue(":ROLLOFF", rolloff);
262
263 if (!query.exec() || !query.isActive())
264 {
265 MythDB::DBError("Adding transport to Database.", query);
266 return 0;
267 }
268
269 if (mplex)
270 return mplex;
271
272 mplex = get_dtv_multiplex(
273 db_source_id, sistandard, frequency,
274 // DVB specific
275 transport_id, network_id, polarity);
276
277 LOG(VB_CHANSCAN, LOG_INFO, QString("insert_dtv_multiplex -- ") +
278 QString("inserted mplexid %1").arg(mplex));
279
280 return mplex;
281}
282
283static void handle_transport_desc(std::vector<uint> &muxes,
284 const MPEGDescriptor &desc,
285 uint sourceid, uint tsid, uint netid)
286{
287 uint tag = desc.DescriptorTag();
288
290 {
292 uint64_t freq = cd.FrequencyHz();
293
294 // Use the frequency we already have for this mplex
295 // as it may be one of the other_frequencies for this mplex
296 int mux = ChannelUtil::GetMplexID(sourceid, tsid, netid);
297 if (mux > 0)
298 {
299 QString dummy_mod;
300 QString dummy_sistd;
301 uint dummy_tsid = 0;
302 uint dummy_netid = 0;
303 ChannelUtil::GetTuningParams(mux, dummy_mod, freq,
304 dummy_tsid, dummy_netid, dummy_sistd);
305 }
306
308 (int)sourceid, "dvb",
309 freq, QString(),
310 // DVB specific
311 (int)tsid, (int)netid,
312 -1, cd.BandwidthString().at(0).toLatin1(),
313 -1, 'a',
314 cd.TransmissionModeString().at(0).toLatin1(),
315 QString(), cd.ConstellationString(),
316 cd.HierarchyString().at(0).toLatin1(), cd.CodeRateHPString(),
318 QString(), QString());
319
320 if (mux)
321 muxes.push_back(mux);
322
323 /* unused
324 HighPriority()
325 IsTimeSlicingIndicatorUsed()
326 IsMPE_FECUsed()
327 NativeInterleaver()
328 Alpha()
329 */
330 }
332 {
334
336 sourceid, "dvb",
338 // DVB specific
339 tsid, netid,
340 cd.SymbolRateHz(), -1,
341 cd.PolarizationString().at(0).toLatin1(), 'a',
342 -1,
343 cd.FECInnerString(), QString(),
344 -1, QString(),
345 QString(), QString(),
347
348 if (mux)
349 muxes.push_back(mux);
350
351 /* unused
352 OrbitalPositionString() == OrbitalLocation
353 */
354 }
356 {
357 const CableDeliverySystemDescriptor cd(desc);
358
360 sourceid, "dvb",
361 cd.FrequencyHz(), cd.ModulationString(),
362 // DVB specific
363 tsid, netid,
364 cd.SymbolRateHz(), -1,
365 -1, 'a',
366 -1,
367 cd.FECInnerString(), QString(),
368 -1, QString(),
369 QString(), QString(),
370 QString(), QString());
371
372 if (mux)
373 muxes.push_back(mux);
374 }
375}
376
377uint ChannelUtil::CreateMultiplex(int sourceid, const QString& sistandard,
378 uint64_t frequency, const QString& modulation,
379 int transport_id, int network_id)
380{
381 return CreateMultiplex(
382 sourceid, sistandard,
383 frequency, modulation,
384 transport_id, network_id,
385 -1, -1,
386 -1, -1,
387 -1,
388 QString(), QString(),
389 -1, QString(),
390 QString(), QString(),
391 QString(), QString());
392}
393
395 int sourceid, const QString& sistandard,
396 uint64_t freq, const QString& modulation,
397 // DVB specific
398 int transport_id, int network_id,
399 int symbol_rate, signed char bandwidth,
400 signed char polarity, signed char inversion,
401 signed char trans_mode,
402 const QString& inner_FEC, const QString& constellation,
403 signed char hierarchy, const QString& hp_code_rate,
404 const QString& lp_code_rate, const QString& guard_interval,
405 const QString& mod_sys, const QString& rolloff)
406{
408 sourceid, sistandard,
409 freq, modulation,
410 // DVB specific
411 transport_id, network_id,
412 symbol_rate, bandwidth,
413 polarity, inversion,
414 trans_mode,
415 inner_FEC, constellation,
416 hierarchy, hp_code_rate,
417 lp_code_rate, guard_interval,
418 mod_sys, rolloff);
419}
420
422 int transport_id, int network_id)
423{
425 sourceid, mux.m_sistandard,
427 // DVB specific
428 transport_id, network_id,
429 mux.m_symbolRate, mux.m_bandwidth.toChar().toLatin1(),
430 mux.m_polarity.toChar().toLatin1(), mux.m_inversion.toChar().toLatin1(),
431 mux.m_transMode.toChar().toLatin1(),
432 mux.m_fec.toString(), mux.m_modulation.toString(),
433 mux.m_hierarchy.toChar().toLatin1(), mux.m_hpCodeRate.toString(),
435 mux.m_modSys.toString(), mux.m_rolloff.toString());
436}
437
438
443 int sourceid, const NetworkInformationTable *nit)
444{
445 std::vector<uint> muxes;
446
447 if (sourceid <= 0)
448 return muxes;
449
450 for (uint i = 0; i < nit->TransportStreamCount(); ++i)
451 {
452 const desc_list_t& list =
455
456 uint tsid = nit->TSID(i);
457 uint netid = nit->OriginalNetworkID(i);
458 for (const auto *j : list)
459 {
460 const MPEGDescriptor desc(j);
461 handle_transport_desc(muxes, desc, sourceid, tsid, netid);
462 }
463 }
464 return muxes;
465}
466
467uint ChannelUtil::GetMplexID(uint sourceid, const QString &channum)
468{
470 /* See if mplexid is already in the database */
471 query.prepare(
472 "SELECT mplexid "
473 "FROM channel "
474 "WHERE deleted IS NULL AND "
475 " sourceid = :SOURCEID AND "
476 " channum = :CHANNUM");
477
478 query.bindValue(":SOURCEID", sourceid);
479 query.bindValue(":CHANNUM", channum);
480
481 if (!query.exec() || !query.isActive())
482 MythDB::DBError("GetMplexID 0", query);
483 else if (query.next())
484 return query.value(0).toInt();
485
486 return 0;
487}
488
489int ChannelUtil::GetMplexID(uint sourceid, uint64_t frequency)
490{
492 /* See if mplexid is already in the database */
493 query.prepare(
494 "SELECT mplexid "
495 "FROM dtv_multiplex "
496 "WHERE sourceid = :SOURCEID AND "
497 " frequency = :FREQUENCY");
498
499 query.bindValue(":SOURCEID", sourceid);
500 query.bindValue(":FREQUENCY", QString::number(frequency));
501
502 if (!query.exec() || !query.isActive())
503 {
504 MythDB::DBError("GetMplexID 1", query);
505 return -1;
506 }
507
508 if (query.next())
509 return query.value(0).toInt();
510
511 return -1;
512}
513
514int ChannelUtil::GetMplexID(uint sourceid, uint64_t frequency,
515 uint transport_id, uint network_id)
516{
518 // See if transport already in database
519 query.prepare(
520 "SELECT mplexid "
521 "FROM dtv_multiplex "
522 "WHERE networkid = :NETWORKID AND "
523 " transportid = :TRANSPORTID AND "
524 " frequency = :FREQUENCY AND "
525 " sourceid = :SOURCEID");
526
527 query.bindValue(":SOURCEID", sourceid);
528 query.bindValue(":NETWORKID", network_id);
529 query.bindValue(":TRANSPORTID", transport_id);
530 query.bindValue(":FREQUENCY", QString::number(frequency));
531
532 if (!query.exec() || !query.isActive())
533 {
534 MythDB::DBError("GetMplexID 2", query);
535 return -1;
536 }
537
538 if (query.next())
539 return query.value(0).toInt();
540
541 return -1;
542}
543
545 uint transport_id, uint network_id)
546{
548 // See if transport already in database
549 query.prepare(
550 "SELECT mplexid "
551 "FROM dtv_multiplex "
552 "WHERE networkid = :NETWORKID AND "
553 " transportid = :TRANSPORTID AND "
554 " sourceid = :SOURCEID");
555
556 query.bindValue(":SOURCEID", sourceid);
557 query.bindValue(":NETWORKID", network_id);
558 query.bindValue(":TRANSPORTID", transport_id);
559
560 if (!query.exec() || !query.isActive())
561 {
562 MythDB::DBError("GetMplexID 3", query);
563 return -1;
564 }
565
566 if (query.next())
567 return query.value(0).toInt();
568
569 return -1;
570}
571
573{
575 /* See if mplexid is already in the database */
576 query.prepare(
577 "SELECT mplexid "
578 "FROM channel "
579 "WHERE chanid = :CHANID");
580
581 query.bindValue(":CHANID", chanid);
582
583 if (!query.exec())
584 MythDB::DBError("GetMplexID 4", query);
585 else if (query.next())
586 return query.value(0).toInt();
587
588 return 0;
589}
590
613// current_mplexid always exists in scanner, see ScanTransport()
614//
615int ChannelUtil::GetBetterMplexID(int current_mplexid,
616 int transport_id,
617 int network_id)
618{
619 LOG(VB_CHANSCAN, LOG_INFO,
620 QString("GetBetterMplexID(mplexId %1, tId %2, netId %3)")
621 .arg(current_mplexid).arg(transport_id).arg(network_id));
622
623 int q_networkid = 0;
624 int q_transportid = 0;
626
627 query.prepare("SELECT networkid, transportid "
628 "FROM dtv_multiplex "
629 "WHERE mplexid = :MPLEX_ID");
630
631 query.bindValue(":MPLEX_ID", current_mplexid);
632
633 if (!query.exec())
634 {
635 MythDB::DBError("Getting mplexid global search", query);
636 }
637 else if (query.next())
638 {
639 q_networkid = query.value(0).toInt();
640 q_transportid = query.value(1).toInt();
641 }
642
643 // Got a match, return it.
644 if ((q_networkid == network_id) && (q_transportid == transport_id))
645 {
646 LOG(VB_CHANSCAN, LOG_INFO,
647 QString("GetBetterMplexID(): Returning perfect match %1")
648 .arg(current_mplexid));
649 return current_mplexid;
650 }
651
652 // Not in DB at all, insert it
653 if (!q_networkid && !q_transportid)
654 {
655 int qsize = query.size();
656 query.prepare("UPDATE dtv_multiplex "
657 "SET networkid = :NETWORK_ID, "
658 " transportid = :TRANSPORT_ID "
659 "WHERE mplexid = :MPLEX_ID");
660
661 query.bindValue(":NETWORK_ID", network_id);
662 query.bindValue(":TRANSPORT_ID", transport_id);
663 query.bindValue(":MPLEX_ID", current_mplexid);
664
665 if (!query.exec())
666 MythDB::DBError("Getting mplexid global search", query);
667
668 LOG(VB_CHANSCAN, LOG_INFO,
669 QString("GetBetterMplexID(): net id and transport id "
670 "are null, qsize(%1), Returning %2")
671 .arg(qsize).arg(current_mplexid));
672 return current_mplexid;
673 }
674
675 // We have a partial match, so we try to do better...
676 std::array<QString,2> theQueries
677 {
678 QString("SELECT a.mplexid "
679 "FROM dtv_multiplex a, dtv_multiplex b "
680 "WHERE a.networkid = :NETWORK_ID AND "
681 " a.transportid = :TRANSPORT_ID AND "
682 " a.sourceid = b.sourceid AND "
683 " b.mplexid = :MPLEX_ID"),
684
685 QString("SELECT mplexid "
686 "FROM dtv_multiplex "
687 "WHERE networkid = :NETWORK_ID AND "
688 " transportid = :TRANSPORT_ID"),
689 };
690
691 for (uint i=0; i<2; i++)
692 {
693 query.prepare(theQueries[i]);
694
695 query.bindValue(":NETWORK_ID", network_id);
696 query.bindValue(":TRANSPORT_ID", transport_id);
697 if (i == 0)
698 query.bindValue(":MPLEX_ID", current_mplexid);
699
700 if (!query.exec() || !query.isActive())
701 MythDB::DBError("Finding matching mplexid", query);
702
703 if (query.size() == 1 && query.next())
704 {
705 LOG(VB_CHANSCAN, LOG_INFO,
706 QString("GetBetterMplexID(): query#%1 qsize(%2) "
707 "Returning %3")
708 .arg(i).arg(query.size()).arg(current_mplexid));
709 return query.value(0).toInt();
710 }
711
712 if (query.next())
713 {
714 int ret = (i==0) ? current_mplexid : query.value(0).toInt();
715 LOG(VB_CHANSCAN, LOG_INFO,
716 QString("GetBetterMplexID(): query#%1 qsize(%2) "
717 "Returning %3")
718 .arg(i).arg(query.size()).arg(ret));
719 return ret;
720 }
721 }
722
723 // If you still didn't find this combo return -1 (failure)
724 LOG(VB_CHANSCAN, LOG_INFO, "GetBetterMplexID(): Returning -1");
725 return -1;
726}
727
729 QString &modulation,
730 uint64_t &frequency,
731 uint &dvb_transportid,
732 uint &dvb_networkid,
733 QString &si_std)
734{
735 if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
736 return false;
737
739 query.prepare(
740 "SELECT transportid, networkid, frequency, modulation, sistandard "
741 "FROM dtv_multiplex "
742 "WHERE mplexid = :MPLEXID");
743 query.bindValue(":MPLEXID", mplexid);
744
745 if (!query.exec())
746 {
747 MythDB::DBError("GetTuningParams failed ", query);
748 return false;
749 }
750
751 if (!query.next())
752 return false;
753
754 dvb_transportid = query.value(0).toUInt();
755 dvb_networkid = query.value(1).toUInt();
756 frequency = query.value(2).toULongLong();
757 modulation = query.value(3).toString();
758 si_std = query.value(4).toString();
759
760 return true;
761}
762
763QString ChannelUtil::GetChannelStringField(int chan_id, const QString &field)
764{
765 if (chan_id < 0)
766 return {};
767
769 query.prepare(QString("SELECT %1 FROM channel "
770 "WHERE chanid = :CHANID").arg(field));
771 query.bindValue(":CHANID", chan_id);
772 if (!query.exec())
773 {
774 MythDB::DBError("Selecting channel/dtv_multiplex 1", query);
775 return {};
776 }
777
778 if (!query.next())
779 return {};
780
781 return query.value(0).toString();
782}
783
784QString ChannelUtil::GetChanNum(int chan_id)
785{
786 return GetChannelStringField(chan_id, QString("channum"));
787}
788
789std::chrono::minutes ChannelUtil::GetTimeOffset(int chan_id)
790{
791 return std::chrono::minutes(GetChannelStringField(chan_id, QString("tmoffset")).toInt());
792}
793
794int ChannelUtil::GetSourceID(int db_mplexid)
795{
797
798 query.prepare("SELECT sourceid "
799 "FROM dtv_multiplex "
800 "WHERE mplexid = :MPLEXID");
801 query.bindValue(":MPLEXID", db_mplexid);
802 if (!query.exec())
803 {
804 MythDB::DBError("Selecting channel/dtv_multiplex", query);
805 return -1;
806 }
807
808 if (query.next())
809 return query.value(0).toInt();
810
811 return -1;
812}
813
815{
817
818 query.prepare(
819 "SELECT sourceid "
820 "FROM channel "
821 "WHERE chanid = :CHANID");
822 query.bindValue(":CHANID", chanid);
823
824 if (!query.exec())
825 MythDB::DBError("Selecting channel/dtv_multiplex", query);
826 else if (query.next())
827 return query.value(0).toUInt();
828
829 return 0;
830}
831
833{
835 query.prepare("SELECT cardtype "
836 "FROM capturecard, channel "
837 "WHERE channel.chanid = :CHANID AND "
838 " channel.sourceid = capturecard.sourceid "
839 "GROUP BY cardtype");
840 query.bindValue(":CHANID", chanid);
841
842 QStringList list;
843 if (!query.exec())
844 {
845 MythDB::DBError("ChannelUtil::GetInputTypes", query);
846 return list;
847 }
848 while (query.next())
849 list.push_back(query.value(0).toString());
850 return list;
851}
852
853static bool lt_pidcache(
854 const pid_cache_item_t a, const pid_cache_item_t b)
855{
856 return a.GetPID() < b.GetPID();
857}
858
866 pid_cache_t &pid_cache)
867{
869 query.prepare("SELECT pid, tableid FROM pidcache "
870 "WHERE chanid = :CHANID");
871
872 query.bindValue(":CHANID", chanid);
873
874 if (!query.exec())
875 {
876 MythDB::DBError("GetCachedPids: fetching pids", query);
877 return false;
878 }
879
880 while (query.next())
881 {
882 int pid = query.value(0).toInt();
883 int tid = query.value(1).toInt();
884 if ((pid >= 0) && (tid >= 0))
885 pid_cache.emplace_back(pid, tid);
886 }
887 std::ranges::stable_sort(pid_cache, lt_pidcache);
888
889 return true;
890}
891
899 const pid_cache_t &_pid_cache,
900 bool delete_all)
901{
903
905 if (delete_all)
906 {
907 query.prepare("DELETE FROM pidcache WHERE chanid = :CHANID");
908 }
909 else
910 {
911 query.prepare(
912 "DELETE FROM pidcache "
913 "WHERE chanid = :CHANID AND tableid < 65536");
914 }
915
916 query.bindValue(":CHANID", chanid);
917
918 if (!query.exec())
919 {
920 MythDB::DBError("GetCachedPids -- delete", query);
921 return false;
922 }
923
924 pid_cache_t old_cache;
925 GetCachedPids(chanid, old_cache);
926 pid_cache_t pid_cache = _pid_cache;
927 std::ranges::stable_sort(pid_cache, lt_pidcache);
928
930 query.prepare(
931 "INSERT INTO pidcache "
932 "SET chanid = :CHANID, pid = :PID, tableid = :TABLEID");
933 query.bindValue(":CHANID", chanid);
934
935 bool ok = true;
936 auto ito = old_cache.begin();
937 for (const auto& itn : pid_cache)
938 {
939 // if old pid smaller than current new pid, skip this old pid
940 for (; ito != old_cache.end() && ito->GetPID() < itn.GetPID(); ++ito);
941
942 // if already in DB, skip DB insert
943 if (ito != old_cache.end() && ito->GetPID() == itn.GetPID())
944 continue;
945
946 query.bindValue(":PID", itn.GetPID());
947 query.bindValue(":TABLEID", itn.GetComposite());
948
949 if (!query.exec())
950 {
951 MythDB::DBError("GetCachedPids -- insert", query);
952 ok = false;
953 }
954 }
955
956 return ok;
957}
958
959QString ChannelUtil::GetChannelValueStr(const QString &channel_field,
960 uint sourceid,
961 const QString &channum)
962{
963 QString retval;
964
966
967 query.prepare(
968 QString(
969 "SELECT channel.%1 "
970 "FROM channel "
971 "WHERE deleted IS NULL AND "
972 " channum = :CHANNUM AND "
973 " sourceid = :SOURCEID")
974 .arg(channel_field));
975
976 query.bindValue(":SOURCEID", sourceid);
977 query.bindValue(":CHANNUM", channum);
978
979 if (!query.exec() || !query.isActive())
980 MythDB::DBError("getchannelvalue", query);
981 else if (query.next())
982 retval = query.value(0).toString();
983
984 return retval;
985}
986
987int ChannelUtil::GetChannelValueInt(const QString &channel_field,
988 uint sourceid,
989 const QString &channum)
990{
991 QString val = GetChannelValueStr(channel_field, sourceid, channum);
992
993 int retval = 0;
994 if (!val.isEmpty())
995 retval = val.toInt();
996
997 return retval ? retval : -1;
998}
999
1000QString ChannelUtil::GetChannelNumber(uint sourceid, const QString &channel_name)
1001{
1002 if (channel_name.isEmpty())
1003 return {};
1004
1006 query.prepare("SELECT channum FROM channel WHERE sourceid = :SOURCEID "
1007 "AND name = :NAME "
1008 "AND deleted IS NULL;" );
1009 query.bindValue(":SOURCEID", sourceid);
1010 query.bindValue(":NAME", channel_name.left(64)); // Field channel.name is 64 characters
1011 if (!query.exec())
1012 {
1013 MythDB::DBError("GetChannelNumber", query);
1014 return {};
1015 }
1016
1017 if (!query.next())
1018 return {};
1019
1020 return query.value(0).toString();
1021}
1022
1024 const QString &new_channum,
1025 const QString &old_channum)
1026{
1027 if (new_channum.isEmpty() || old_channum.isEmpty())
1028 return false;
1029
1030 if (new_channum == old_channum)
1031 return true;
1032
1033 uint old_mplexid = GetMplexID(srcid, old_channum);
1034 if (!old_mplexid)
1035 return false;
1036
1037 uint new_mplexid = GetMplexID(srcid, new_channum);
1038 if (!new_mplexid)
1039 return false;
1040
1041 LOG(VB_CHANNEL, LOG_INFO, QString("IsOnSameMultiplex? %1==%2 -> %3")
1042 .arg(old_mplexid).arg(new_mplexid)
1043 .arg(old_mplexid == new_mplexid));
1044
1045 return old_mplexid == new_mplexid;
1046}
1047
1053static QStringList get_valid_recorder_list(uint chanid)
1054{
1055 QStringList reclist;
1056
1057 // Query the database to determine which source is being used currently.
1058 // set the EPG so that it only displays the channels of the current source
1060 // We want to get the current source id for this recorder
1061 query.prepare(
1062 "SELECT capturecard.cardid "
1063 "FROM channel "
1064 "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid "
1065 "WHERE channel.chanid = :CHANID AND "
1066 " capturecard.livetvorder > 0 "
1067 "ORDER BY capturecard.livetvorder, capturecard.cardid");
1068 query.bindValue(":CHANID", chanid);
1069
1070 if (!query.exec() || !query.isActive())
1071 {
1072 MythDB::DBError("get_valid_recorder_list ChanID", query);
1073 return reclist;
1074 }
1075
1076 while (query.next())
1077 reclist << query.value(0).toString();
1078
1079 return reclist;
1080}
1081
1087static QStringList get_valid_recorder_list(const QString &channum)
1088{
1089 QStringList reclist;
1090
1091 // Query the database to determine which source is being used currently.
1092 // set the EPG so that it only displays the channels of the current source
1094 // We want to get the current source id for this recorder
1095 query.prepare(
1096 "SELECT capturecard.cardid "
1097 "FROM channel "
1098 "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid "
1099 "WHERE channel.deleted IS NULL AND "
1100 " channel.channum = :CHANNUM AND "
1101 " capturecard.livetvorder > 0 "
1102 "ORDER BY capturecard.livetvorder, capturecard.cardid");
1103 query.bindValue(":CHANNUM", channum);
1104
1105 if (!query.exec() || !query.isActive())
1106 {
1107 MythDB::DBError("get_valid_recorder_list ChanNum", query);
1108 return reclist;
1109 }
1110
1111 while (query.next())
1112 reclist << query.value(0).toString();
1113
1114 return reclist;
1115}
1116
1125 uint chanid, const QString &channum)
1126{
1127 if (chanid)
1128 return get_valid_recorder_list(chanid);
1129 if (!channum.isEmpty())
1130 return get_valid_recorder_list(channum);
1131 return {};
1132}
1133
1134
1135std::vector<uint> ChannelUtil::GetConflicting(const QString &channum, uint sourceid)
1136{
1138 std::vector<uint> conflicting;
1139
1140 if (sourceid)
1141 {
1142 query.prepare(
1143 "SELECT chanid from channel "
1144 "WHERE deleted IS NULL AND "
1145 " sourceid = :SOURCEID AND "
1146 " channum = :CHANNUM");
1147 query.bindValue(":SOURCEID", sourceid);
1148 }
1149 else
1150 {
1151 query.prepare(
1152 "SELECT chanid from channel "
1153 "WHERE deleted IS NULL AND "
1154 " channum = :CHANNUM");
1155 }
1156
1157 query.bindValue(":CHANNUM", channum);
1158 if (!query.exec())
1159 {
1160 MythDB::DBError("IsConflicting", query);
1161 conflicting.push_back(0);
1162 return conflicting;
1163 }
1164
1165 while (query.next())
1166 conflicting.push_back(query.value(0).toUInt());
1167
1168 return conflicting;
1169}
1170
1171bool ChannelUtil::SetChannelValue(const QString &field_name,
1172 const QString& value,
1173 uint sourceid,
1174 const QString &channum)
1175{
1177
1178 query.prepare(
1179 QString("UPDATE channel SET channel.%1=:VALUE "
1180 "WHERE channel.channum = :CHANNUM AND "
1181 " channel.sourceid = :SOURCEID").arg(field_name));
1182
1183 query.bindValue(":VALUE", value);
1184 query.bindValue(":CHANNUM", channum);
1185 query.bindValue(":SOURCEID", sourceid);
1186
1187 return query.exec();
1188}
1189
1190bool ChannelUtil::SetChannelValue(const QString &field_name,
1191 const QString& value,
1192 int chanid)
1193{
1195
1196 query.prepare(
1197 QString("UPDATE channel SET channel.%1=:VALUE "
1198 "WHERE channel.chanid = :CHANID").arg(field_name));
1199
1200 query.bindValue(":VALUE", value);
1201 query.bindValue(":CHANID", chanid);
1202
1203 return query.exec();
1204}
1205
1209
1212{
1214
1216 {
1218 s_channelDefaultAuthorityMapLock.lockForWrite();
1219 // cppcheck-suppress knownConditionTrueFalse
1221 {
1223 query.prepare(
1224 "SELECT chanid, m.default_authority "
1225 "FROM channel c "
1226 "LEFT JOIN dtv_multiplex m "
1227 "ON (c.mplexid = m.mplexid) "
1228 "WHERE deleted IS NULL");
1229 if (query.exec())
1230 {
1231 while (query.next())
1232 {
1233 if (!query.value(1).toString().isEmpty())
1234 {
1235 s_channelDefaultAuthorityMap[query.value(0).toUInt()] =
1236 query.value(1).toString();
1237 }
1238 }
1240 }
1241 else
1242 {
1243 MythDB::DBError("GetDefaultAuthority 1", query);
1244 }
1245
1246 query.prepare(
1247 "SELECT chanid, default_authority "
1248 "FROM channel "
1249 "WHERE deleted IS NULL");
1250 if (query.exec())
1251 {
1252 while (query.next())
1253 {
1254 if (!query.value(1).toString().isEmpty())
1255 {
1256 s_channelDefaultAuthorityMap[query.value(0).toUInt()] =
1257 query.value(1).toString();
1258 }
1259 }
1261 }
1262 else
1263 {
1264 MythDB::DBError("GetDefaultAuthority 2", query);
1265 }
1266
1267 }
1268 }
1269
1270 QMap<uint,QString>::iterator it = s_channelDefaultAuthorityMap.find(chanid);
1271 QString ret;
1272 if (it != s_channelDefaultAuthorityMap.end())
1273 ret = *it;
1275
1276 return ret;
1277}
1278
1280{
1281 static QReadWriteLock s_channelIconMapLock;
1282 static QHash<uint,QString> s_channelIconMap;
1283 static bool s_runInit = true;
1284
1285 s_channelIconMapLock.lockForRead();
1286
1287 QString ret(s_channelIconMap.value(chanid, "_cold_"));
1288
1289 s_channelIconMapLock.unlock();
1290
1291 if (ret != "_cold_")
1292 return ret;
1293
1294 s_channelIconMapLock.lockForWrite();
1295
1297 QString iconquery = "SELECT chanid, icon FROM channel";
1298
1299 if (s_runInit)
1300 iconquery += " WHERE visible > 0";
1301 else
1302 iconquery += " WHERE chanid = :CHANID";
1303
1304 query.prepare(iconquery);
1305
1306 if (!s_runInit)
1307 query.bindValue(":CHANID", chanid);
1308
1309 if (query.exec())
1310 {
1311 if (s_runInit)
1312 {
1313 s_channelIconMap.reserve(query.size());
1314 while (query.next())
1315 {
1316 s_channelIconMap[query.value(0).toUInt()] =
1317 query.value(1).toString();
1318 }
1319 s_runInit = false;
1320 }
1321 else
1322 {
1323 s_channelIconMap[chanid] = (query.next()) ?
1324 query.value(1).toString() : "";
1325 }
1326 }
1327 else
1328 {
1329 MythDB::DBError("GetIcon", query);
1330 }
1331
1332 ret = s_channelIconMap.value(chanid, "");
1333
1334 s_channelIconMapLock.unlock();
1335
1336 return ret;
1337}
1338
1340{
1341 return tr("UNKNOWN", "Synthesized callsign");
1342}
1343
1344int ChannelUtil::GetChanID(int mplexid, int service_transport_id,
1345 int major_channel, int minor_channel,
1346 int program_number)
1347{
1349
1350 // find source id, so we can find manually inserted ATSC channels
1351 query.prepare("SELECT sourceid "
1352 "FROM dtv_multiplex "
1353 "WHERE mplexid = :MPLEXID");
1354 query.bindValue(":MPLEXID", mplexid);
1355 if (!query.exec())
1356 {
1357 MythDB::DBError("Selecting channel/dtv_multiplex 2", query);
1358 return -1;
1359 }
1360 if (!query.next())
1361 return -1;
1362
1363 int source_id = query.value(0).toInt();
1364
1365 // find a proper ATSC channel
1366 query.prepare("SELECT chanid FROM channel,dtv_multiplex "
1367 "WHERE channel.deleted IS NULL AND "
1368 " channel.sourceid = :SOURCEID AND "
1369 " atsc_major_chan = :MAJORCHAN AND "
1370 " atsc_minor_chan = :MINORCHAN AND "
1371 " dtv_multiplex.transportid = :TRANSPORTID AND "
1372 " dtv_multiplex.mplexid = :MPLEXID AND "
1373 " dtv_multiplex.sourceid = channel.sourceid AND "
1374 " dtv_multiplex.mplexid = channel.mplexid");
1375
1376 query.bindValue(":SOURCEID", source_id);
1377 query.bindValue(":MAJORCHAN", major_channel);
1378 query.bindValue(":MINORCHAN", minor_channel);
1379 query.bindValue(":TRANSPORTID", service_transport_id);
1380 query.bindValue(":MPLEXID", mplexid);
1381
1382 if (query.exec() && query.next())
1383 return query.value(0).toInt();
1384
1385 // Find manually inserted/edited channels in order of scariness.
1386 // find renamed channel, where atsc is valid
1387 query.prepare("SELECT chanid FROM channel "
1388 "WHERE deleted IS NULL AND "
1389 "sourceid = :SOURCEID AND "
1390 "atsc_major_chan = :MAJORCHAN AND "
1391 "atsc_minor_chan = :MINORCHAN");
1392
1393 query.bindValue(":SOURCEID", source_id);
1394 query.bindValue(":MAJORCHAN", major_channel);
1395 query.bindValue(":MINORCHAN", minor_channel);
1396
1397 if (query.exec() && query.next())
1398 return query.value(0).toInt();
1399
1400 // find based on mpeg program number and mplexid alone
1401 query.prepare("SELECT chanid FROM channel "
1402 "WHERE deleted IS NULL AND "
1403 "sourceid = :SOURCEID AND "
1404 "serviceID = :SERVICEID AND "
1405 "mplexid = :MPLEXID");
1406
1407 query.bindValue(":SOURCEID", source_id);
1408 query.bindValue(":SERVICEID", program_number);
1409 query.bindValue(":MPLEXID", mplexid);
1410
1411 if (query.exec() && query.next())
1412 return query.value(0).toInt();
1413
1414 return -1;
1415}
1416
1417uint ChannelUtil::FindChannel(uint sourceid, const QString &freqid)
1418{
1420 query.prepare("SELECT chanid "
1421 "FROM channel "
1422 "WHERE deleted IS NULL AND "
1423 " sourceid = :SOURCEID AND "
1424 " freqid = :FREQID");
1425
1426 query.bindValue(":SOURCEID", sourceid);
1427 query.bindValue(":FREQID", freqid);
1428
1429 if (!query.exec() || !query.isActive())
1430 MythDB::DBError("FindChannel", query);
1431 else if (query.next())
1432 return query.value(0).toUInt();
1433
1434 return 0;
1435}
1436
1437
1438static uint get_max_chanid(uint sourceid)
1439{
1440 QString qstr = "SELECT MAX(chanid) FROM channel ";
1441 qstr += sourceid ? "WHERE sourceid = :SOURCEID" : "";
1442
1444 query.prepare(qstr);
1445
1446 if (sourceid)
1447 query.bindValue(":SOURCEID", sourceid);
1448
1449 if (!query.exec() || !query.isActive())
1450 MythDB::DBError("Getting chanid for new channel (2)", query);
1451 else if (!query.next())
1452 LOG(VB_GENERAL, LOG_ERR, "Error getting chanid for new channel.");
1453 else
1454 return query.value(0).toUInt();
1455
1456 return 0;
1457}
1458
1459static bool chanid_available(uint chanid)
1460{
1462 query.prepare(
1463 "SELECT chanid "
1464 "FROM channel "
1465 "WHERE chanid = :CHANID");
1466 query.bindValue(":CHANID", chanid);
1467
1468 if (!query.exec() || !query.isActive())
1469 MythDB::DBError("is_chan_id_available", query);
1470 else if (query.size() == 0)
1471 return true;
1472
1473 return false;
1474}
1475
1480int ChannelUtil::CreateChanID(uint sourceid, const QString &chan_num)
1481{
1482 // first try to base it on the channel number for human readability
1483 static const QRegularExpression kNonDigitRE { R"(\D)" };
1484 uint chanid = 0;
1485 int chansep = chan_num.indexOf(kNonDigitRE);
1486 if (chansep > 0)
1487 {
1488#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1489 chanid =
1490 (sourceid * 10000) +
1491 (chan_num.leftRef(chansep).toInt() * 100) +
1492 chan_num.rightRef(chan_num.length() - chansep - 1).toInt();
1493#else
1494 chanid =
1495 (sourceid * 10000) +
1496 (QStringView(chan_num).left(chansep).toInt() * 100) +
1497 QStringView(chan_num).right(chan_num.length() - chansep - 1).toInt();
1498#endif
1499 }
1500 else
1501 {
1502 chanid = (sourceid * 10000) + chan_num.toInt();
1503 }
1504
1505 if ((chanid > sourceid * 10000) && (chanid_available(chanid)))
1506 return chanid;
1507
1508 // try to at least base it on the sourceid for human readability
1509 chanid = std::max(get_max_chanid(sourceid) + 1, sourceid * 10000);
1510
1511 if (chanid_available(chanid))
1512 return chanid;
1513
1514 // just get a chanid we know should work
1515 chanid = get_max_chanid(0) + 1;
1516
1517 if (chanid_available(chanid))
1518 return chanid;
1519
1520 // failure
1521 return -1;
1522}
1523
1525 uint db_sourceid,
1526 uint new_channel_id,
1527 const QString &callsign,
1528 const QString &service_name,
1529 const QString &chan_num,
1530 uint service_id,
1531 uint atsc_major_channel,
1532 uint atsc_minor_channel,
1533 bool use_on_air_guide,
1534 ChannelVisibleType visible,
1535 const QString &freqid,
1536 const QString& icon,
1537 QString format,
1538 const QString& xmltvid,
1539 const QString& default_authority,
1540 uint service_type,
1541 int recpriority,
1542 int tmOffset,
1543 int commMethod )
1544{
1546
1547 QString chanNum = (chan_num == "-1") ?
1548 QString::number(service_id) : chan_num;
1549
1550 QString qstr =
1551 "INSERT INTO channel "
1552 " (chanid, channum, sourceid, "
1553 " callsign, name, serviceid, ";
1554 qstr += (db_mplexid > 0) ? "mplexid, " : "";
1555 qstr += (!freqid.isEmpty()) ? "freqid, " : "";
1556 qstr +=
1557 " atsc_major_chan, atsc_minor_chan, "
1558 " useonairguide, visible, tvformat, "
1559 " icon, xmltvid, default_authority, "
1560 " service_type, recpriority, tmoffset, "
1561 " commmethod ) "
1562 "VALUES "
1563 " (:CHANID, :CHANNUM, :SOURCEID, "
1564 " :CALLSIGN, :NAME, :SERVICEID, ";
1565 qstr += (db_mplexid > 0) ? ":MPLEXID, " : "";
1566 qstr += (!freqid.isEmpty()) ? ":FREQID, " : "";
1567 qstr +=
1568 " :MAJORCHAN, :MINORCHAN, "
1569 " :USEOAG, :VISIBLE, :TVFORMAT, "
1570 " :ICON, :XMLTVID, :AUTHORITY, "
1571 " :SERVICETYPE, :RECPRIORITY, :TMOFFSET, "
1572 " :COMMMETHOD ) ";
1573
1574 query.prepare(qstr);
1575
1576 query.bindValue (":CHANID", new_channel_id);
1577 query.bindValueNoNull(":CHANNUM", chanNum);
1578 query.bindValue (":SOURCEID", db_sourceid);
1579 query.bindValueNoNull(":CALLSIGN", callsign);
1580 query.bindValueNoNull(":NAME", service_name);
1581
1582 if (db_mplexid > 0)
1583 query.bindValue(":MPLEXID", db_mplexid);
1584
1585 query.bindValue(":SERVICEID", service_id);
1586 query.bindValue(":MAJORCHAN", atsc_major_channel);
1587 query.bindValue(":MINORCHAN", atsc_minor_channel);
1588 query.bindValue(":USEOAG", use_on_air_guide);
1589 query.bindValue(":VISIBLE", visible);
1590
1591 if (!freqid.isEmpty())
1592 query.bindValue(":FREQID", freqid);
1593
1594 QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : std::move(format);
1595 query.bindValueNoNull(":TVFORMAT", tvformat);
1596 query.bindValueNoNull(":ICON", icon);
1597 query.bindValueNoNull(":XMLTVID", xmltvid);
1598 query.bindValueNoNull(":AUTHORITY", default_authority);
1599 query.bindValue (":SERVICETYPE", service_type);
1600 query.bindValue (":RECPRIORITY", recpriority);
1601 query.bindValue (":TMOFFSET", tmOffset);
1602 query.bindValue (":COMMMETHOD", commMethod);
1603
1604 if (!query.exec() || !query.isActive())
1605 {
1606 MythDB::DBError("Adding Service", query);
1607 return false;
1608 }
1609 return true;
1610}
1611
1613 uint source_id,
1614 uint channel_id,
1615 const QString &callsign,
1616 const QString &service_name,
1617 const QString &chan_num,
1618 uint service_id,
1619 uint atsc_major_channel,
1620 uint atsc_minor_channel,
1621 bool use_on_air_guide,
1622 ChannelVisibleType visible,
1623 const QString& freqid,
1624 const QString& icon,
1625 QString format,
1626 const QString& xmltvid,
1627 const QString& default_authority,
1628 uint service_type,
1629 int recpriority,
1630 int tmOffset,
1631 int commMethod )
1632{
1633 if (!channel_id)
1634 return false;
1635
1636 QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : std::move(format);
1637 bool set_channum = !chan_num.isEmpty() && chan_num != "-1";
1638 QString qstr = QString(
1639 "UPDATE channel "
1640 "SET %1 %2 %3 %4 %5 %6 %7 %8 %9 "
1641 " mplexid = :MPLEXID, serviceid = :SERVICEID, "
1642 " atsc_major_chan = :MAJORCHAN, atsc_minor_chan = :MINORCHAN, "
1643 " callsign = :CALLSIGN, name = :NAME, "
1644 " sourceid = :SOURCEID, useonairguide = :USEOAG, "
1645 " visible = :VISIBLE, service_type = :SERVICETYPE "
1646 "WHERE chanid=:CHANID")
1647 .arg((!set_channum) ? "" : "channum = :CHANNUM, ",
1648 (freqid.isEmpty()) ? "" : "freqid = :FREQID, ",
1649 (icon.isEmpty()) ? "" : "icon = :ICON, ",
1650 (tvformat.isEmpty()) ? "" : "tvformat = :TVFORMAT, ",
1651 (xmltvid.isEmpty()) ? "" : "xmltvid = :XMLTVID, ",
1652 (default_authority.isEmpty()) ?
1653 "" : "default_authority = :AUTHORITY,",
1654 (recpriority == INT_MIN) ? "" : "recpriority = :RECPRIORITY, ",
1655 (tmOffset == INT_MIN) ? "" : "tmOffset = :TMOFFSET, ",
1656 (commMethod == INT_MIN) ? "" : "commmethod = :COMMMETHOD, ");
1657
1659 query.prepare(qstr);
1660
1661 query.bindValue(":CHANID", channel_id);
1662
1663 if (set_channum)
1664 query.bindValue(":CHANNUM", chan_num);
1665
1666 query.bindValue (":SOURCEID", source_id);
1667 query.bindValueNoNull(":CALLSIGN", callsign);
1668 query.bindValueNoNull(":NAME", service_name);
1669
1670 query.bindValue(":MPLEXID", db_mplexid);
1671 query.bindValue(":SERVICEID", service_id);
1672 query.bindValue(":MAJORCHAN", atsc_major_channel);
1673 query.bindValue(":MINORCHAN", atsc_minor_channel);
1674 query.bindValue(":USEOAG", use_on_air_guide);
1675 query.bindValue(":VISIBLE", visible);
1676 query.bindValue(":SERVICETYPE", service_type);
1677
1678 if (!freqid.isNull())
1679 query.bindValue(":FREQID", freqid);
1680 if (!tvformat.isNull())
1681 query.bindValue(":TVFORMAT", tvformat);
1682 if (!icon.isNull())
1683 query.bindValue(":ICON", icon);
1684 if (!xmltvid.isNull())
1685 query.bindValue(":XMLTVID", xmltvid);
1686 if (!default_authority.isNull())
1687 query.bindValue(":AUTHORITY", default_authority);
1688 if (recpriority != INT_MIN)
1689 query.bindValue(":RECPRIORITY", recpriority);
1690 if (tmOffset != INT_MIN)
1691 query.bindValue(":TMOFFSET", tmOffset);
1692 if (commMethod != INT_MIN)
1693 query.bindValue(":COMMMETHOD", commMethod);
1694
1695 if (!query.exec())
1696 {
1697 MythDB::DBError("Updating Service", query);
1698 return false;
1699 }
1700 return true;
1701}
1702
1704{
1706 query.prepare(
1707 "SELECT channum "
1708 "FROM channel "
1709 "WHERE chanid = :ID");
1710 query.bindValue(":ID", chan.m_channelId);
1711
1712 if (!query.exec())
1713 {
1714 MythDB::DBError("UpdateChannelNumberFromDB", query);
1715 return;
1716 }
1717
1718 if (query.next())
1719 {
1720 QString channum = query.value(0).toString();
1721
1722 if (!channum.isEmpty())
1723 {
1724 chan.m_chanNum = channum;
1725 }
1726 }
1727}
1728
1730{
1732 query.prepare(
1733 "SELECT xmltvid, useonairguide, visible "
1734 "FROM channel "
1735 "WHERE chanid = :ID");
1736 query.bindValue(":ID", chan.m_channelId);
1737
1738 if (!query.exec())
1739 {
1740 MythDB::DBError("UpdateInsertInfoFromDB", query);
1741 return;
1742 }
1743
1744 if (query.next())
1745 {
1746 QString xmltvid = query.value(0).toString();
1747 bool useeit = query.value(1).toBool();
1748 ChannelVisibleType visible =
1749 static_cast<ChannelVisibleType>(query.value(2).toInt());
1750
1751 if (!xmltvid.isEmpty())
1752 {
1753 if (useeit)
1754 {
1755 LOG(VB_GENERAL, LOG_ERR,
1756 "Using EIT and xmltv for the same channel "
1757 "is an unsupported configuration.");
1758 }
1759 chan.m_xmltvId = xmltvid;
1760 }
1761 chan.m_useOnAirGuide = useeit;
1762 chan.m_hidden = (visible == kChannelNotVisible ||
1763 visible == kChannelNeverVisible);
1764 chan.m_visible = visible;
1765 }
1766}
1767
1769 uint channel_id, const IPTVTuningData &tuning)
1770{
1772
1773 query.prepare(
1774 "DELETE FROM iptv_channel "
1775 "WHERE chanid=:CHANID");
1776 query.bindValue(":CHANID", channel_id);
1777
1778 if (!query.exec())
1779 {
1780 MythDB::DBError("UpdateIPTVTuningData -- delete", query);
1781 return false;
1782 }
1783
1784 query.prepare(
1785 "INSERT INTO iptv_channel (chanid, url, type, bitrate) "
1786 "VALUES (:CHANID, :URL, :TYPE, :BITRATE)");
1787 query.bindValue(":CHANID", channel_id);
1788
1789 query.bindValue(":URL", tuning.GetDataURL().toString());
1790 query.bindValue(":TYPE", tuning.GetFECTypeString(0));
1791 query.bindValue(":BITRATE", tuning.GetBitrate(0));
1792
1793 if (!query.exec())
1794 {
1795 MythDB::DBError("UpdateIPTVTuningData -- data", query);
1796 return false;
1797 }
1798
1799 if (tuning.GetFECURL0().port() >= 0)
1800 {
1801 query.bindValue(":URL", tuning.GetFECURL0().toString());
1802 query.bindValue(":TYPE", tuning.GetFECTypeString(1));
1803 query.bindValue(":BITRATE", tuning.GetBitrate(1));
1804 if (!query.exec())
1805 {
1806 MythDB::DBError("UpdateIPTVTuningData -- fec 0", query);
1807 return false;
1808 }
1809 }
1810
1811 if (tuning.GetFECURL1().port() >= 0)
1812 {
1813 query.bindValue(":URL", tuning.GetFECURL1().toString());
1814 query.bindValue(":TYPE", tuning.GetFECTypeString(2));
1815 query.bindValue(":BITRATE", tuning.GetBitrate(2));
1816 if (!query.exec())
1817 {
1818 MythDB::DBError("UpdateIPTVTuningData -- fec 1", query);
1819 return false;
1820 }
1821 }
1822
1823 return true;
1824}
1825
1827{
1829 query.prepare(
1830 "UPDATE channel "
1831 "SET deleted = NOW() "
1832 "WHERE chanid = :ID");
1833 query.bindValue(":ID", channel_id);
1834
1835 if (!query.exec())
1836 {
1837 MythDB::DBError("Delete Channel", query);
1838 return false;
1839 }
1840
1841 return true;
1842}
1843
1845{
1847 query.prepare(
1848 "UPDATE channel "
1849 "SET visible = :VISIBLE "
1850 "WHERE chanid = :ID");
1851 query.bindValue(":ID", channel_id);
1852 query.bindValue(":VISIBLE", visible);
1853
1854 if (!query.exec())
1855 {
1856 MythDB::DBError("ChannelUtil::SetVisible", query);
1857 return false;
1858 }
1859
1860 return true;
1861}
1862
1864{
1866
1867 query.prepare("UPDATE dtv_multiplex "
1868 "SET serviceversion = :VERSION "
1869 "WHERE mplexid = :MPLEXID");
1870
1871 query.bindValue(":VERSION", version);
1872 query.bindValue(":MPLEXID", mplexid);
1873
1874 if (!query.exec())
1875 {
1876 MythDB::DBError("Selecting channel/dtv_multiplex", query);
1877 return false;
1878 }
1879 return true;
1880}
1881
1883{
1885
1886 query.prepare("SELECT serviceversion "
1887 "FROM dtv_multiplex "
1888 "WHERE mplexid = :MPLEXID");
1889
1890 query.bindValue(":MPLEXID", mplexid);
1891
1892 if (!query.exec())
1893 {
1894 MythDB::DBError("Selecting channel/dtv_multiplex", query);
1895 return 0;
1896 }
1897
1898 if (query.next())
1899 return query.value(0).toInt();
1900
1901 return -1;
1902}
1903
1904bool ChannelUtil::GetATSCChannel(uint sourceid, const QString &channum,
1905 uint &major, uint &minor)
1906{
1907 major = minor = 0;
1908
1910 query.prepare(
1911 "SELECT atsc_major_chan, atsc_minor_chan "
1912 "FROM channel "
1913 "WHERE deleted IS NULL AND "
1914 " channum = :CHANNUM AND "
1915 " sourceid = :SOURCEID");
1916
1917 query.bindValue(":SOURCEID", sourceid);
1918 query.bindValue(":CHANNUM", channum);
1919
1920 if (!query.exec() || !query.isActive())
1921 {
1922 MythDB::DBError("getatscchannel", query);
1923 }
1924 else if (query.next())
1925 {
1926 major = query.value(0).toUInt();
1927 minor = query.value(1).toUInt();
1928 return true;
1929 }
1930
1931 return false;
1932}
1933
1935 uint sourceid,
1936 uint &chanid, const QString &channum,
1937 QString &name, QString &callsign,
1938 QString &tvformat, QString &modulation,
1939 QString &freqtable, QString &freqid,
1940 int &finetune, uint64_t &frequency,
1941 QString &dtv_si_std, int &mpeg_prog_num,
1942 uint &atsc_major, uint &atsc_minor,
1943 uint &dvb_transportid, uint &dvb_networkid,
1944 uint &mplexid,
1945 bool &commfree)
1946{
1947 chanid = 0;
1948 tvformat.clear();
1949 modulation.clear();
1950 freqtable.clear();;
1951 freqid.clear();
1952 dtv_si_std.clear();
1953 finetune = 0;
1954 frequency = 0;
1955 mpeg_prog_num = -1;
1956 atsc_major = atsc_minor = mplexid = 0;
1957 dvb_networkid = dvb_transportid = 0;
1958 commfree = false;
1959
1960 int found = 0;
1962 query.prepare(
1963 "SELECT finetune, freqid, tvformat, freqtable, "
1964 " commmethod, mplexid, "
1965 " atsc_major_chan, atsc_minor_chan, serviceid, "
1966 " chanid, channel.name, callsign, visible "
1967 "FROM channel, videosource "
1968 "WHERE channel.deleted IS NULL AND "
1969 " videosource.sourceid = channel.sourceid AND "
1970 " channum = :CHANNUM AND "
1971 " channel.sourceid = :SOURCEID "
1972 "ORDER BY channel.visible > 0 DESC, channel.chanid ");
1973 query.bindValue(":CHANNUM", channum);
1974 query.bindValue(":SOURCEID", sourceid);
1975
1976 if (!query.exec() || !query.isActive())
1977 {
1978 MythDB::DBError("GetChannelData", query);
1979 return false;
1980 }
1981
1982 if (query.next())
1983 {
1984 finetune = query.value(0).toInt();
1985 freqid = query.value(1).toString();
1986 tvformat = query.value(2).toString();
1987 freqtable = query.value(3).toString();
1988 commfree = (query.value(4).toInt() == -2);
1989 mplexid = query.value(5).toUInt();
1990 atsc_major = query.value(6).toUInt();
1991 atsc_minor = query.value(7).toUInt();
1992 mpeg_prog_num = (query.value(8).isNull()) ? -1
1993 : query.value(8).toInt();
1994 chanid = query.value(9).toUInt();
1995 name = query.value(10).toString();
1996 callsign = query.value(11).toString();
1997
1998 if (query.value(12).toInt() > kChannelNotVisible)
1999 ++found;
2000 }
2001
2002 while (query.next())
2003 if (query.value(12).toInt() > kChannelNotVisible)
2004 ++found;
2005
2006 if (found == 0 && chanid)
2007 {
2008 LOG(VB_GENERAL, LOG_WARNING,
2009 QString("No visible channels for %1, using invisble chanid %2")
2010 .arg(channum).arg(chanid));
2011 }
2012
2013 if (found > 1)
2014 {
2015 LOG(VB_GENERAL, LOG_WARNING,
2016 QString("Found multiple visible channels for %1, using chanid %2")
2017 .arg(channum).arg(chanid));
2018 }
2019
2020 if (!chanid)
2021 {
2022 LOG(VB_GENERAL, LOG_ERR,
2023 QString("Could not find channel '%1' in DB for source %2 '%3'.")
2024 .arg(channum).arg(sourceid).arg(SourceUtil::GetSourceName(sourceid)));
2025 return false;
2026 }
2027
2028 if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
2029 return true;
2030
2031 return GetTuningParams(mplexid, modulation, frequency,
2032 dvb_transportid, dvb_networkid, dtv_si_std);
2033}
2034
2036{
2038 query.prepare(
2039 "SELECT type+0, url, bitrate "
2040 "FROM iptv_channel "
2041 "WHERE chanid = :CHANID "
2042 "ORDER BY type+0");
2043 query.bindValue(":CHANID", chanid);
2044
2045 if (!query.exec())
2046 {
2047 MythDB::DBError("GetChannelData -- iptv", query);
2048 return {};
2049 }
2050
2051 QString data_url;
2052 QString fec_url0;
2053 QString fec_url1;
2055 std::array<uint,3> bitrate { 0, 0, 0, };
2056 while (query.next())
2057 {
2059 query.value(0).toUInt();
2060 switch (type)
2061 {
2063 data_url = query.value(1).toString();
2064 bitrate[0] = query.value(2).toUInt();
2065 break;
2069 fec_url0 = query.value(1).toString();
2070 bitrate[1] = query.value(2).toUInt();
2071 break;
2075 fec_url1 = query.value(1).toString();
2076 bitrate[2] = query.value(2).toUInt();
2077 break;
2078 }
2079 switch (type)
2080 {
2082 break;
2084 fec_type = IPTVTuningData::kRFC2733;
2085 break;
2087 fec_type = IPTVTuningData::kRFC5109;
2088 break;
2090 fec_type = IPTVTuningData::kSMPTE2022;
2091 break;
2095 break; // will be handled by type of first FEC stream
2096 }
2097 }
2098
2099 IPTVTuningData tuning(data_url, bitrate[0], fec_type,
2100 fec_url0, bitrate[1], fec_url1, bitrate[2]);
2101 LOG(VB_GENERAL, LOG_INFO, QString("Loaded %1 for %2")
2102 .arg(tuning.GetDeviceName()).arg(chanid));
2103 return tuning;
2104}
2105
2106// TODO This should be modified to load a complete channelinfo object including
2107// all fields from the database
2112 uint sourceid, bool visible_only, bool include_disconnected,
2113 const QString &group_by, uint channel_groupid)
2114{
2115 ChannelInfoList list;
2116
2118
2119 QString qstr = QString(
2120 "SELECT videosource.sourceid, GROUP_CONCAT(capturecard.cardid) "
2121 "FROM videosource "
2122 "%1 JOIN capturecard ON capturecard.sourceid = videosource.sourceid "
2123 "GROUP BY videosource.sourceid")
2124 .arg(include_disconnected ? "LEFT" : "");
2125
2126 query.prepare(qstr);
2127 if (!query.exec())
2128 {
2129 MythDB::DBError("ChannelUtil::GetChannels()", query);
2130 return list;
2131 }
2132
2133 QMap<uint, QList<uint>> inputIdLists;
2134 while (query.next())
2135 {
2136 uint qSourceId = query.value(0).toUInt();
2137 QList<uint> &inputIdList = inputIdLists[qSourceId];
2138 QStringList inputIds = query.value(1).toString().split(",");
2139 while (!inputIds.isEmpty())
2140 inputIdList.append(inputIds.takeFirst().toUInt());
2141 }
2142
2143 qstr = QString(
2144 "SELECT channum, callsign, channel.chanid, "
2145 " atsc_major_chan, atsc_minor_chan, "
2146 " name, icon, mplexid, visible, "
2147 " channel.sourceid, "
2148 " GROUP_CONCAT(DISTINCT channelgroup.grpid), "
2149 " xmltvid "
2150 "FROM channel "
2151 "LEFT JOIN channelgroup ON channel.chanid = channelgroup.chanid ");
2152
2153 qstr += "WHERE deleted IS NULL ";
2154
2155 if (sourceid)
2156 qstr += QString("AND channel.sourceid='%1' ").arg(sourceid);
2157
2158 // Select only channels from the specified channel group
2159 if (channel_groupid > 0)
2160 qstr += QString("AND channelgroup.grpid = '%1' ").arg(channel_groupid);
2161
2162 if (visible_only)
2163 qstr += QString("AND visible > 0 ");
2164
2165 qstr += " GROUP BY chanid";
2166
2167 if (!group_by.isEmpty())
2168 qstr += QString(", %1").arg(group_by);
2169
2170 query.prepare(qstr);
2171 if (!query.exec())
2172 {
2173 MythDB::DBError("ChannelUtil::GetChannels()", query);
2174 return list;
2175 }
2176
2177 while (query.next())
2178 {
2179 if (query.value(0).toString().isEmpty() || !query.value(2).toBool())
2180 continue; // skip if channum blank, or chanid empty
2181
2182 uint qSourceID = query.value(9).toUInt();
2183 ChannelInfo chan(
2184 query.value(0).toString(), /* channum */
2185 query.value(1).toString(), /* callsign */
2186 query.value(2).toUInt(), /* chanid */
2187 query.value(3).toUInt(), /* ATSC major */
2188 query.value(4).toUInt(), /* ATSC minor */
2189 query.value(7).toUInt(), /* mplexid */
2190 static_cast<ChannelVisibleType>(query.value(8).toInt()),
2191 /* visible */
2192 query.value(5).toString(), /* name */
2193 query.value(6).toString(), /* icon */
2194 qSourceID); /* sourceid */
2195
2196 chan.m_xmltvId = query.value(11).toString(); /* xmltvid */
2197
2198 for (auto inputId : std::as_const(inputIdLists[qSourceID]))
2199 chan.AddInputId(inputId);
2200
2201 QStringList groupIDs = query.value(10).toString().split(",");
2202 while (!groupIDs.isEmpty())
2203 chan.AddGroupId(groupIDs.takeFirst().toUInt());
2204
2205 list.push_back(chan);
2206
2207 }
2208
2209 return list;
2210}
2211
2212std::vector<uint> ChannelUtil::GetChanIDs(int sourceid, bool onlyVisible)
2213{
2215
2216 QString select = "SELECT chanid FROM channel WHERE deleted IS NULL ";
2217 // Yes, this a little ugly
2218 if (onlyVisible || sourceid > 0)
2219 {
2220 if (onlyVisible)
2221 select += "AND visible > 0 ";
2222 if (sourceid > 0)
2223 select += "AND sourceid=" + QString::number(sourceid);
2224 }
2225
2226 std::vector<uint> list;
2227 query.prepare(select);
2228 if (!query.exec())
2229 {
2230 MythDB::DBError("SourceUtil::GetChanIDs()", query);
2231 return list;
2232 }
2233
2234 while (query.next())
2235 list.push_back(query.value(0).toUInt());
2236
2237 return list;
2238}
2239
2240inline bool lt_callsign(const ChannelInfo &a, const ChannelInfo &b)
2241{
2242 // For the spaceship operator, the c++ standard library explicitly
2243 // requires '0' and not nullptr.
2244 // NOLINTNEXTLINE(modernize-use-nullptr)
2246}
2247
2248inline bool lt_smart(const ChannelInfo &a, const ChannelInfo &b)
2249{
2250 static QMutex s_sepExprLock;
2251 static const QRegularExpression kSepExpr(ChannelUtil::kATSCSeparators);
2252
2253 bool isIntA = false;
2254 bool isIntB = false;
2255 int a_int = a.m_chanNum.toUInt(&isIntA);
2256 int b_int = b.m_chanNum.toUInt(&isIntB);
2257 int a_major = a.m_atscMajorChan;
2258 int b_major = b.m_atscMajorChan;
2259 int a_minor = a.m_atscMinorChan;
2260 int b_minor = b.m_atscMinorChan;
2261
2262 // Extract minor and major numbers from channum..
2263 int idxA = 0;
2264 int idxB = 0;
2265 {
2266 QMutexLocker locker(&s_sepExprLock);
2267 idxA = a.m_chanNum.indexOf(kSepExpr);
2268 idxB = b.m_chanNum.indexOf(kSepExpr);
2269 }
2270 if (idxA >= 0)
2271 {
2272 bool tmp1 = false;
2273 bool tmp2 = false;
2274#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2275 int major = a.m_chanNum.leftRef(idxA).toUInt(&tmp1);
2276 int minor = a.m_chanNum.midRef(idxA+1).toUInt(&tmp2);
2277#else
2278 int major = QStringView(a.m_chanNum).left(idxA).toUInt(&tmp1);
2279 int minor = QStringView(a.m_chanNum).mid(idxA+1).toUInt(&tmp2);
2280#endif
2281 if (tmp1 && tmp2)
2282 (a_major = major), (a_minor = minor), (isIntA = false);
2283 }
2284
2285 if (idxB >= 0)
2286 {
2287 bool tmp1 = false;
2288 bool tmp2 = false;
2289#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2290 int major = b.m_chanNum.leftRef(idxB).toUInt(&tmp1);
2291 int minor = b.m_chanNum.midRef(idxB+1).toUInt(&tmp2);
2292#else
2293 int major = QStringView(b.m_chanNum).left(idxB).toUInt(&tmp1);
2294 int minor = QStringView(b.m_chanNum).mid(idxB+1).toUInt(&tmp2);
2295#endif
2296 if (tmp1 && tmp2)
2297 (b_major = major), (b_minor = minor), (isIntB = false);
2298 }
2299
2300 // If ATSC channel has been renumbered, sort by new channel number
2301 if ((a_minor > 0) && isIntA)
2302 {
2303 int atsc_int = (QString("%1%2").arg(a_major).arg(a_minor)).toInt();
2304 a_minor = (atsc_int == a_int) ? a_minor : 0;
2305 }
2306
2307 if ((b_minor > 0) && isIntB)
2308 {
2309 int atsc_int = (QString("%1%2").arg(b_major).arg(b_minor)).toInt();
2310 b_minor = (atsc_int == b_int) ? b_minor : 0;
2311 }
2312
2313 // one of the channels is an ATSC channel, and the other
2314 // is either ATSC or is numeric.
2315 if ((a_minor || b_minor) &&
2316 (a_minor || isIntA) && (b_minor || isIntB))
2317 {
2318 int a_maj = (!a_minor && isIntA) ? a_int : a_major;
2319 int b_maj = (!b_minor && isIntB) ? b_int : b_major;
2320 int cmp = a_maj - b_maj;
2321 if (cmp != 0)
2322 return cmp < 0;
2323
2324 cmp = a_minor - b_minor;
2325 if (cmp != 0)
2326 return cmp < 0;
2327 }
2328
2329 if (isIntA && isIntB)
2330 {
2331 // both channels have a numeric channum
2332 int cmp = a_int - b_int;
2333 if (cmp)
2334 return cmp < 0;
2335 }
2336 else if (isIntA ^ isIntB)
2337 {
2338 // if only one is channel numeric always consider it less than
2339 return isIntA;
2340 }
2341 else
2342 {
2343 // neither of channels have a numeric channum
2344 // For the spaceship operator, the c++ standard library explicitly
2345 // requires '0' and not nullptr.
2346 // NOLINTBEGIN(modernize-use-nullptr)
2348 if (cmp != 0)
2349 return cmp < 0;
2350 // NOLINTEND(modernize-use-nullptr)
2351 }
2352
2353 return lt_callsign(a,b);
2354}
2355
2357{
2359 QString select;
2360
2361
2362 select = "SELECT chanid FROM channel WHERE deleted IS NULL ";
2363 if (sourceid >= 0)
2364 select += "AND sourceid=" + QString::number(sourceid);
2365 select += ';';
2366
2367 query.prepare(select);
2368
2369 if (!query.exec() || !query.isActive())
2370 return 0;
2371
2372 return query.size();
2373}
2374
2375void ChannelUtil::SortChannels(ChannelInfoList &list, const QString &order,
2376 bool eliminate_duplicates)
2377{
2378 bool cs = order.toLower() == "callsign";
2379 if (cs)
2380 std::ranges::stable_sort(list, lt_callsign);
2381 else /* if (sortorder == "channum") */
2382 std::ranges::stable_sort(list, lt_smart);
2383
2384 if (eliminate_duplicates && !list.empty())
2385 {
2386 ChannelInfoList tmp;
2387 tmp.push_back(list[0]);
2388 for (size_t i = 1; i < list.size(); i++)
2389 {
2390 if ((cs && lt_callsign(tmp.back(), list[i])) ||
2391 (!cs && lt_smart(tmp.back(), list[i])))
2392 {
2393 tmp.push_back(list[i]);
2394 }
2395 }
2396
2397 list = tmp;
2398 }
2399}
2400
2401// Return the array index of the best matching channel. An exact
2402// match is the best match. Otherwise, find the closest numerical
2403// value greater than channum. E.g., if the channel list is {2_1,
2404// 2_2, 4_1, 4_2, 300} then input 3 returns 2_2, input 4 returns 2_2,
2405// and input 5 returns 4_2.
2406//
2407// The list does not need to be sorted.
2409 const QString &channum)
2410{
2411 ChannelInfo target;
2412 target.m_chanNum = channum;
2413 int b = -1; // index of best seen so far
2414 for (int i = 0; i < (int)list.size(); ++i)
2415 {
2416 // Index i is a better result if any of the following hold:
2417 // i is the first element seen
2418 // i < target < best (i.e., i is the first one less than the target)
2419 // best < i < target
2420 // target < i < best
2421 if ((b < 0) ||
2422 (lt_smart(list[i], target) && lt_smart(target, list[b])) ||
2423 (lt_smart(list[b], list[i]) && lt_smart(list[i], target)) ||
2424 (lt_smart(target, list[i]) && lt_smart(list[i], list[b])))
2425 {
2426 b = i;
2427 }
2428 }
2429 return b;
2430}
2431
2432namespace {
2436 {
2438 {
2439 if (it != l.begin())
2440 return --it;
2441 it = std::ranges::find(l, l.rbegin()->m_chanId,
2443 if (it == l.end())
2444 return --it;
2445 return it;
2446 }
2447
2448 // UP or FAVORITE
2449 ++it;
2450 if (it == l.end())
2451 return l.begin();
2452 return it;
2453 }
2454}
2455
2457 const ChannelInfoList &sorted,
2458 uint old_chanid,
2459 uint mplexid_restriction,
2460 uint chanid_restriction,
2461 ChannelChangeDirection direction,
2462 bool skip_non_visible,
2463 bool skip_same_channum_and_callsign,
2464 bool skip_other_sources)
2465{
2466 if (sorted.empty())
2467 return 0; // no channels..
2468
2469 auto it = std::ranges::find(sorted, old_chanid, &ChannelInfo::m_chanId);
2470 if (it == sorted.end())
2471 it = sorted.begin(); // not in list, pretend we are on first channel
2472
2473 if (CHANNEL_DIRECTION_SAME == direction)
2474 return it->m_chanId;
2475
2476 auto start = it;
2477
2478 for (it = next_w_wrap(sorted, it, direction);
2479 it != start;
2480 it = next_w_wrap(sorted, it, direction))
2481 {
2482 if (skip_non_visible && (it->m_visible < kChannelVisible))
2483 continue;
2484 if (skip_other_sources && (it->m_sourceId != start->m_sourceId))
2485 continue;
2486 if (skip_same_channum_and_callsign && (it->m_chanNum == start->m_chanNum &&
2487 it->m_callSign == start->m_callSign))
2488 continue;
2489 if ((mplexid_restriction != 0U) && (mplexid_restriction != it->m_mplexId))
2490 continue;
2491 if ((chanid_restriction != 0U) && (chanid_restriction != it->m_chanId))
2492 continue;
2493 break;
2494 }
2495
2496 return it->m_chanId;
2497}
2498
2500 uint &totalAvailable,
2501 bool ignoreHidden,
2502 ChannelUtil::OrderBy orderBy,
2503 ChannelUtil::GroupBy groupBy,
2504 uint sourceID,
2505 uint channelGroupID,
2506 bool liveTVOnly,
2507 const QString& callsign,
2508 const QString& channum,
2509 bool ignoreUntunable)
2510{
2511 ChannelInfoList channelList;
2512
2514
2515 QString sql = QString(
2516 "SELECT parentid, GROUP_CONCAT(cardid ORDER BY cardid) "
2517 "FROM capturecard "
2518 "WHERE parentid <> 0 "
2519 "GROUP BY parentid ");
2520
2521 query.prepare(sql);
2522 if (!query.exec())
2523 {
2524 MythDB::DBError("ChannelUtil::GetChannels()", query);
2525 return channelList;
2526 }
2527
2528 QMap<uint, QList<uint>> childIdLists;
2529 while (query.next())
2530 {
2531 auto parentId = query.value(0).toUInt();
2532 auto &childIdList = childIdLists[parentId];
2533 auto childIds = query.value(1).toString().split(",");
2534 while (!childIds.isEmpty())
2535 childIdList.append(childIds.takeFirst().toUInt());
2536 }
2537
2538 sql = "SELECT %1 channum, freqid, channel.sourceid, "
2539 "callsign, name, icon, finetune, videofilters, xmltvid, "
2540 "channel.recpriority, channel.contrast, channel.brightness, "
2541 "channel.colour, channel.hue, tvformat, "
2542 "visible, outputfilters, useonairguide, mplexid, "
2543 "serviceid, atsc_major_chan, atsc_minor_chan, last_record, "
2544 "default_authority, commmethod, tmoffset, iptvid, "
2545 "channel.chanid, "
2546 "GROUP_CONCAT(DISTINCT `groups`.`groupids`), " // Creates a CSV list of channel groupids for this channel
2547 "GROUP_CONCAT(DISTINCT capturecard.cardid "
2548 " ORDER BY livetvorder), " // Creates a CSV list of inputids for this channel
2549 "MIN(livetvorder) livetvorder "
2550 "FROM channel ";
2551 if (!channelGroupID)
2552 sql += "LEFT ";
2553 sql += "JOIN ( "
2554 " SELECT chanid ,"
2555 " GROUP_CONCAT(grpid ORDER BY grpid) groupids "
2556 " FROM channelgroup ";
2557 if (channelGroupID)
2558 sql += " WHERE grpid = :CHANGROUPID ";
2559 sql += " GROUP BY chanid "
2560 ") `groups` "
2561 " ON channel.chanid = `groups`.`chanid` ";
2562 if (!ignoreUntunable && !liveTVOnly)
2563 sql += "LEFT ";
2564 sql += "JOIN capturecard "
2565 " ON capturecard.sourceid = channel.sourceid "
2566 " AND capturecard.parentid = 0 ";
2567 if (liveTVOnly)
2568 sql += " AND capturecard.livetvorder > 0 ";
2569
2570 sql += "WHERE channel.deleted IS NULL ";
2571 if (ignoreHidden)
2572 sql += "AND channel.visible > 0 ";
2573
2574 if (sourceID > 0)
2575 sql += "AND channel.sourceid = :SOURCEID ";
2576
2577 if (groupBy == kChanGroupByCallsign)
2578 sql += "GROUP BY channel.callsign ";
2579 else if (groupBy == kChanGroupByCallsignAndChannum)
2580 sql += "GROUP BY channel.callsign, channel.channum ";
2581 else
2582 sql += "GROUP BY channel.chanid "; // We must always group for this query
2583
2584 if (orderBy == kChanOrderByName)
2585 {
2586 sql += "ORDER BY channel.name ";
2587 }
2588 else if (orderBy == kChanOrderByChanNum)
2589 {
2590 // Natural sorting including subchannels e.g. 2_4, 1.3
2591 sql += "ORDER BY LPAD(CAST(channel.channum AS UNSIGNED), 10, 0), "
2592 " LPAD(channel.channum, 10, 0) ";
2593 }
2594 else // kChanOrderByLiveTV
2595 {
2596 sql += "ORDER BY callsign = :CALLSIGN1 AND channum = :CHANNUM DESC, "
2597 " callsign = :CALLSIGN2 DESC, "
2598 " livetvorder, "
2599 " channel.recpriority DESC, "
2600 " chanid ";
2601 }
2602
2603 if (count > 0)
2604 sql += "LIMIT :LIMIT ";
2605
2606 if (startIndex > 0)
2607 sql += "OFFSET :STARTINDEX ";
2608
2609
2610 if (startIndex > 0 || count > 0)
2611 sql = sql.arg("SQL_CALC_FOUND_ROWS");
2612 else
2613 sql = sql.arg(""); // remove place holder
2614
2615 query.prepare(sql);
2616
2617 if (channelGroupID > 0)
2618 query.bindValue(":CHANGROUPID", channelGroupID);
2619
2620 if (sourceID > 0)
2621 query.bindValue(":SOURCEID", sourceID);
2622
2623 if (count > 0)
2624 query.bindValue(":LIMIT", count);
2625
2626 if (startIndex > 0)
2627 query.bindValue(":STARTINDEX", startIndex);
2628
2629 if (orderBy == kChanOrderByLiveTV)
2630 {
2631 query.bindValue(":CALLSIGN1", callsign);
2632 query.bindValue(":CHANNUM", channum);
2633 query.bindValue(":CALLSIGN2", callsign);
2634 }
2635
2636 if (!query.exec())
2637 {
2638 MythDB::DBError("ChannelInfo::Load()", query);
2639 return channelList;
2640 }
2641
2642 std::vector<uint> groupIdList;
2643 while (query.next())
2644 {
2645 ChannelInfo channelInfo;
2646 channelInfo.m_chanNum = query.value(0).toString();
2647 channelInfo.m_freqId = query.value(1).toString();
2648 channelInfo.m_sourceId = query.value(2).toUInt();
2649 channelInfo.m_callSign = query.value(3).toString();
2650 channelInfo.m_name = query.value(4).toString();
2651 channelInfo.m_icon = query.value(5).toString();
2652 channelInfo.m_fineTune = query.value(6).toInt();
2653 channelInfo.m_videoFilters = query.value(7).toString();
2654 channelInfo.m_xmltvId = query.value(8).toString();
2655 channelInfo.m_recPriority = query.value(9).toInt();
2656 channelInfo.m_contrast = query.value(10).toUInt();
2657 channelInfo.m_brightness = query.value(11).toUInt();
2658 channelInfo.m_colour = query.value(12).toUInt();
2659 channelInfo.m_hue = query.value(13).toUInt();
2660 channelInfo.m_tvFormat = query.value(14).toString();
2661 channelInfo.m_visible =
2662 static_cast<ChannelVisibleType>(query.value(15).toInt());
2663 channelInfo.m_outputFilters = query.value(16).toString();
2664 channelInfo.m_useOnAirGuide = query.value(17).toBool();
2665 channelInfo.m_mplexId = query.value(18).toUInt();
2666 channelInfo.m_serviceId = query.value(19).toUInt();
2667 channelInfo.m_atscMajorChan = query.value(20).toUInt();
2668 channelInfo.m_atscMinorChan = query.value(21).toUInt();
2669 channelInfo.m_lastRecord = query.value(22).toDateTime();
2670 channelInfo.m_defaultAuthority = query.value(23).toString();
2671 channelInfo.m_commMethod = query.value(24).toUInt();
2672 channelInfo.m_tmOffset = query.value(25).toUInt();
2673 channelInfo.m_iptvId = query.value(26).toUInt();
2674 channelInfo.m_chanId = query.value(27).toUInt();
2675
2676 QStringList groupIDs = query.value(28).toString().split(",");
2677 groupIdList.clear();
2678 while (!groupIDs.isEmpty())
2679 groupIdList.push_back(groupIDs.takeFirst().toUInt());
2680 std::ranges::sort(groupIdList);
2681 for (auto groupId : groupIdList)
2682 channelInfo.AddGroupId(groupId);
2683
2684 QStringList parentIDs = query.value(29).toString().split(",");
2685 while (!parentIDs.isEmpty())
2686 {
2687 auto parentId = parentIDs.takeFirst().toUInt();
2688 channelInfo.AddInputId(parentId);
2689 auto childIdList = childIdLists[parentId];
2690 for (auto childId : childIdList)
2691 channelInfo.AddInputId(childId);
2692 }
2693
2694 channelList.push_back(channelInfo);
2695 }
2696
2697 if ((startIndex > 0 || count > 0) &&
2698 query.exec("SELECT FOUND_ROWS()") && query.next())
2699 totalAvailable = query.value(0).toUInt();
2700 else
2701 totalAvailable = query.size();
2702
2703 return channelList;
2704}
2705
2706/* vim: set expandtab tabstop=4 shiftwidth=4: */
std::vector< ChannelInfo >::const_iterator ChannelInfoList_ci
Definition: channelinfo.h:131
ChannelVisibleType
Definition: channelinfo.h:20
@ kChannelNeverVisible
Definition: channelinfo.h:24
@ kChannelNotVisible
Definition: channelinfo.h:23
@ kChannelVisible
Definition: channelinfo.h:22
std::vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:130
static bool lt_pidcache(const pid_cache_item_t a, const pid_cache_item_t b)
static uint get_dtv_multiplex(uint db_source_id, const QString &sistandard, uint64_t frequency, uint transport_id, uint network_id, signed char polarity)
Definition: channelutil.cpp:27
static void handle_transport_desc(std::vector< uint > &muxes, const MPEGDescriptor &desc, uint sourceid, uint tsid, uint netid)
static bool chanid_available(uint chanid)
bool lt_callsign(const ChannelInfo &a, const ChannelInfo &b)
bool lt_smart(const ChannelInfo &a, const ChannelInfo &b)
static QStringList get_valid_recorder_list(uint chanid)
Returns list of the recorders that have chanid in their sources.
static uint insert_dtv_multiplex(int db_source_id, const QString &sistandard, uint64_t frequency, const QString &modulation, int transport_id, int network_id, int symbol_rate, signed char bandwidth, signed char polarity, signed char inversion, signed char trans_mode, const QString &inner_FEC, const QString &constellation, signed char hierarchy, const QString &hp_code_rate, const QString &lp_code_rate, const QString &guard_interval, const QString &mod_sys, const QString &rolloff)
Definition: channelutil.cpp:84
static uint get_max_chanid(uint sourceid)
std::vector< pid_cache_item_t > pid_cache_t
Definition: channelutil.h:43
unsigned long long FrequencyHz(void) const
QString ModulationString(void) const
QString FECInnerString(void) const
QString m_outputFilters
Definition: channelinfo.h:106
QString m_chanNum
Definition: channelinfo.h:85
uint m_chanId
Definition: channelinfo.h:84
int m_fineTune
Definition: channelinfo.h:94
QString m_tvFormat
Definition: channelinfo.h:104
QDateTime m_lastRecord
Definition: channelinfo.h:115
QString m_name
Definition: channelinfo.h:91
QString m_icon
Definition: channelinfo.h:92
uint m_atscMinorChan
Definition: channelinfo.h:113
QString m_freqId
Definition: channelinfo.h:86
bool m_useOnAirGuide
Definition: channelinfo.h:107
int m_commMethod
Definition: channelinfo.h:118
ChannelVisibleType m_visible
Definition: channelinfo.h:105
void AddGroupId(uint lgroupid)
Definition: channelinfo.h:58
void AddInputId(uint linputid)
Definition: channelinfo.h:70
uint m_atscMajorChan
Definition: channelinfo.h:112
uint m_contrast
Definition: channelinfo.h:99
uint m_brightness
Definition: channelinfo.h:100
uint m_serviceId
Definition: channelinfo.h:110
int m_recPriority
Definition: channelinfo.h:97
QString m_defaultAuthority
Definition: channelinfo.h:117
QString m_callSign
Definition: channelinfo.h:90
QString m_xmltvId
Definition: channelinfo.h:96
uint m_mplexId
Definition: channelinfo.h:109
uint m_sourceId
Definition: channelinfo.h:88
QString m_videoFilters
Definition: channelinfo.h:95
ChannelVisibleType m_visible
Definition: channelinfo.h:229
static std::vector< uint > GetChanIDs(int sourceid=-1, bool onlyVisible=false)
static QStringList GetValidRecorderList(uint chanid, const QString &channum)
Returns list of the recorders that have chanid or channum in their sources.
static bool s_channelDefaultAuthority_runInit
Definition: channelutil.h:359
static uint GetMplexID(uint sourceid, const QString &channum)
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
static QMap< uint, QString > s_channelDefaultAuthorityMap
Definition: channelutil.h:358
static QStringList GetInputTypes(uint chanid)
static QString GetChannelNumber(uint sourceid, const QString &channel_name)
static QReadWriteLock s_channelDefaultAuthorityMapLock
Definition: channelutil.h:357
static bool DeleteChannel(uint channel_id)
static bool GetCachedPids(uint chanid, pid_cache_t &pid_cache)
Returns cached MPEG PIDs when given a Channel ID.
static QString GetIcon(uint chanid)
static int GetBetterMplexID(int current_mplexid, int transport_id, int network_id)
Returns best match multiplex ID, creating one if needed.
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
static int GetServiceVersion(int mplexid)
static int GetSourceID(int mplexid)
static QString GetChannelValueStr(const QString &channel_field, uint sourceid, const QString &channum)
static const QString kATSCSeparators
Definition: channelutil.h:346
static bool UpdateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
static bool SaveCachedPids(uint chanid, const pid_cache_t &_pid_cache, bool delete_all=false)
Saves PIDs for PSIP tables to database.
static bool UpdateChannel(uint db_mplexid, uint source_id, uint channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, ChannelVisibleType visible, const QString &freqid=QString(), const QString &icon=QString(), QString format=QString(), const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0, int recpriority=INT_MIN, int tmOffset=INT_MIN, int commMethod=INT_MIN)
static uint CreateMultiplex(int sourceid, const QString &sistandard, uint64_t frequency, const QString &modulation, int transport_id=-1, int network_id=-1)
static bool IsOnSameMultiplex(uint srcid, const QString &new_channum, const QString &old_channum)
static std::vector< uint > CreateMultiplexes(int sourceid, const NetworkInformationTable *nit)
static std::chrono::minutes GetTimeOffset(int chan_id)
Returns the listings time offset in minutes for given channel.
static bool SetVisible(uint channel_id, ChannelVisibleType visible)
static bool SetChannelValue(const QString &field_name, const QString &value, uint sourceid, const QString &channum)
static int GetChannelValueInt(const QString &channel_field, uint sourceid, const QString &channum)
static int GetNearestChannel(const ChannelInfoList &list, const QString &channum)
static QString GetDefaultAuthority(uint chanid)
Returns the DVB default authority for the chanid given.
static QString GetChannelStringField(int chan_id, const QString &field)
static bool GetChannelData(uint sourceid, uint &chanid, const QString &channum, QString &name, QString &callsign, QString &tvformat, QString &modulation, QString &freqtable, QString &freqid, int &finetune, uint64_t &frequency, QString &dtv_si_std, int &mpeg_prog_num, uint &atsc_major, uint &atsc_minor, uint &dvb_transportid, uint &dvb_networkid, uint &mplexid, bool &commfree)
@ kChanGroupByCallsignAndChannum
Definition: channelutil.h:217
@ kChanGroupByCallsign
Definition: channelutil.h:216
static std::vector< uint > GetConflicting(const QString &channum, uint sourceid=0)
static uint GetNextChannel(const ChannelInfoList &sorted, uint old_chanid, uint mplexid_restriction, uint chanid_restriction, ChannelChangeDirection direction, bool skip_non_visible=true, bool skip_same_channum_and_callsign=false, bool skip_other_sources=false)
static uint GetChannelCount(int sourceid=-1)
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
static bool CreateChannel(uint db_mplexid, uint db_sourceid, uint new_channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, ChannelVisibleType visible, const QString &freqid, const QString &icon=QString(), QString format="Default", const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0, int recpriority=0, int tmOffset=0, int commMethod=-1)
static ChannelInfoList GetChannelsInternal(uint sourceid, bool visible_only, bool include_disconnected, const QString &group_by, uint channel_groupid)
static bool GetTuningParams(uint mplexid, QString &modulation, uint64_t &frequency, uint &dvb_transportid, uint &dvb_networkid, QString &si_std)
static ChannelInfoList LoadChannels(uint startIndex, uint count, uint &totalAvailable, bool ignoreHidden=true, OrderBy orderBy=kChanOrderByChanNum, GroupBy groupBy=kChanGroupByChanid, uint sourceID=0, uint channelGroupID=0, bool liveTVOnly=false, const QString &callsign="", const QString &channum="", bool ignoreUntunable=true)
Load channels from database into a list of ChannelInfo objects.
static IPTVTuningData GetIPTVTuningData(uint chanid)
static void UpdateChannelNumberFromDB(ChannelInsertInfo &chan)
static QString GetUnknownCallsign(void)
static void UpdateInsertInfoFromDB(ChannelInsertInfo &chan)
static bool SetServiceVersion(int mplexid, int version)
@ kChanOrderByChanNum
Definition: channelutil.h:209
@ kChanOrderByLiveTV
Definition: channelutil.h:211
static uint FindChannel(uint sourceid, const QString &freqid)
static uint GetSourceIDForChannel(uint chanid)
static bool GetATSCChannel(uint sourceid, const QString &channum, uint &major, uint &minor)
QChar toChar() const
QString toString() const
QString toString() const
QChar toChar() const
QChar toChar() const
QString toString() const
DTVHierarchy m_hierarchy
Definition: dtvmultiplex.h:103
DTVCodeRate m_fec
Definition: dtvmultiplex.h:105
DTVInversion m_inversion
Definition: dtvmultiplex.h:96
DTVCodeRate m_hpCodeRate
Definition: dtvmultiplex.h:98
uint64_t m_symbolRate
Definition: dtvmultiplex.h:95
DTVTransmitMode m_transMode
Definition: dtvmultiplex.h:101
DTVModulation m_modulation
Definition: dtvmultiplex.h:100
DTVModulationSystem m_modSys
Definition: dtvmultiplex.h:106
DTVRollOff m_rolloff
Definition: dtvmultiplex.h:107
DTVGuardInterval m_guardInterval
Definition: dtvmultiplex.h:102
DTVBandwidth m_bandwidth
Definition: dtvmultiplex.h:97
uint64_t m_frequency
Definition: dtvmultiplex.h:94
DTVCodeRate m_lpCodeRate
Definition: dtvmultiplex.h:99
QString m_sistandard
Definition: dtvmultiplex.h:111
DTVPolarity m_polarity
Definition: dtvmultiplex.h:104
QChar toChar() const
QString toString() const
@ terrestrial_delivery_system
QString GetFECTypeString(uint i) const
QUrl GetDataURL(void) const
QUrl GetFECURL0(void) const
QUrl GetFECURL1(void) const
uint GetBitrate(uint i) const
QString GetDeviceName(void) const
static desc_list_t Parse(const unsigned char *data, uint len)
uint DescriptorTag(void) const
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:129
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:839
QVariant value(int i) const
Definition: mythdbcon.h:205
int size(void) const
Definition: mythdbcon.h:215
bool isActive(void) const
Definition: mythdbcon.h:216
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:904
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:620
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:890
static MSqlQueryInfo ChannelCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:601
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:814
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:552
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
This table tells the decoder on which PIDs to find other tables.
Definition: dvbtables.h:34
uint OriginalNetworkID(uint i) const
original_network_id 16 2.0+p
Definition: dvbtables.h:84
uint TransportDescriptorsLength(uint i) const
trans_desc_length 12 4.4+p
Definition: dvbtables.h:88
const unsigned char * TransportDescriptors(uint i) const
for(j=0;j<N;j++) x 6.0+p { descriptor() }
Definition: dvbtables.h:92
uint TSID(uint i) const
transport_stream_id 16 0.0+p
Definition: dvbtables.h:82
uint TransportStreamCount(void) const
Definition: dvbtables.h:78
uint64_t FrequencykHz(void) const
QString FECInnerString(void) const
QString ModulationString(void) const
QString ModulationSystemString(void) const
static QString GetSourceName(uint sourceid)
Definition: sourceutil.cpp:50
QString ConstellationString(void) const
QString TransmissionModeString(void) const
uint GetPID(void) const
Definition: channelutil.h:30
unsigned int uint
Definition: compat.h:60
#define minor(X)
Definition: compat.h:58
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
static const std::array< const uint32_t, 4 > freq
Definition: element.cpp:45
static const iso6937table * d
unsigned short uint16_t
Definition: iso6937tables.h:3
std::vector< const unsigned char * > desc_list_t
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MBASE_PUBLIC std::strong_ordering naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity=Qt::CaseSensitive)
This method chops the input a and b into pieces of digits and non-digits (a1.05 becomes a | 1 | .
Definition: stringutil.cpp:122
ChannelInfoList_ci next_w_wrap(const ChannelInfoList &l, ChannelInfoList_ci it, ChannelChangeDirection d)
string version
Definition: giantbomb.py:185
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:32
@ CHANNEL_DIRECTION_SAME
Definition: tv.h:36
@ CHANNEL_DIRECTION_DOWN
Definition: tv.h:34