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 chanid =
1489 (sourceid * 10000) +
1490 (chan_num.left(chansep).toInt() * 100) +
1491 chan_num.right(chan_num.length() - chansep - 1).toInt();
1492 }
1493 else
1494 {
1495 chanid = (sourceid * 10000) + chan_num.toInt();
1496 }
1497
1498 if ((chanid > sourceid * 10000) && (chanid_available(chanid)))
1499 return chanid;
1500
1501 // try to at least base it on the sourceid for human readability
1502 chanid = std::max(get_max_chanid(sourceid) + 1, sourceid * 10000);
1503
1504 if (chanid_available(chanid))
1505 return chanid;
1506
1507 // just get a chanid we know should work
1508 chanid = get_max_chanid(0) + 1;
1509
1510 if (chanid_available(chanid))
1511 return chanid;
1512
1513 // failure
1514 return -1;
1515}
1516
1518 uint db_sourceid,
1519 uint new_channel_id,
1520 const QString &callsign,
1521 const QString &service_name,
1522 const QString &chan_num,
1523 uint service_id,
1524 uint atsc_major_channel,
1525 uint atsc_minor_channel,
1526 bool use_on_air_guide,
1527 ChannelVisibleType visible,
1528 const QString &freqid,
1529 const QString& icon,
1530 QString format,
1531 const QString& xmltvid,
1532 const QString& default_authority,
1533 uint service_type,
1534 int recpriority,
1535 int tmOffset,
1536 int commMethod )
1537{
1539
1540 QString chanNum = (chan_num == "-1") ?
1541 QString::number(service_id) : chan_num;
1542
1543 QString qstr =
1544 "INSERT INTO channel "
1545 " (chanid, channum, sourceid, "
1546 " callsign, name, serviceid, ";
1547 qstr += (db_mplexid > 0) ? "mplexid, " : "";
1548 qstr += (!freqid.isEmpty()) ? "freqid, " : "";
1549 qstr +=
1550 " atsc_major_chan, atsc_minor_chan, "
1551 " useonairguide, visible, tvformat, "
1552 " icon, xmltvid, default_authority, "
1553 " service_type, recpriority, tmoffset, "
1554 " commmethod ) "
1555 "VALUES "
1556 " (:CHANID, :CHANNUM, :SOURCEID, "
1557 " :CALLSIGN, :NAME, :SERVICEID, ";
1558 qstr += (db_mplexid > 0) ? ":MPLEXID, " : "";
1559 qstr += (!freqid.isEmpty()) ? ":FREQID, " : "";
1560 qstr +=
1561 " :MAJORCHAN, :MINORCHAN, "
1562 " :USEOAG, :VISIBLE, :TVFORMAT, "
1563 " :ICON, :XMLTVID, :AUTHORITY, "
1564 " :SERVICETYPE, :RECPRIORITY, :TMOFFSET, "
1565 " :COMMMETHOD ) ";
1566
1567 query.prepare(qstr);
1568
1569 query.bindValue (":CHANID", new_channel_id);
1570 query.bindValueNoNull(":CHANNUM", chanNum);
1571 query.bindValue (":SOURCEID", db_sourceid);
1572 query.bindValueNoNull(":CALLSIGN", callsign);
1573 query.bindValueNoNull(":NAME", service_name);
1574
1575 if (db_mplexid > 0)
1576 query.bindValue(":MPLEXID", db_mplexid);
1577
1578 query.bindValue(":SERVICEID", service_id);
1579 query.bindValue(":MAJORCHAN", atsc_major_channel);
1580 query.bindValue(":MINORCHAN", atsc_minor_channel);
1581 query.bindValue(":USEOAG", use_on_air_guide);
1582 query.bindValue(":VISIBLE", visible);
1583
1584 if (!freqid.isEmpty())
1585 query.bindValue(":FREQID", freqid);
1586
1587 QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : std::move(format);
1588 query.bindValueNoNull(":TVFORMAT", tvformat);
1589 query.bindValueNoNull(":ICON", icon);
1590 query.bindValueNoNull(":XMLTVID", xmltvid);
1591 query.bindValueNoNull(":AUTHORITY", default_authority);
1592 query.bindValue (":SERVICETYPE", service_type);
1593 query.bindValue (":RECPRIORITY", recpriority);
1594 query.bindValue (":TMOFFSET", tmOffset);
1595 query.bindValue (":COMMMETHOD", commMethod);
1596
1597 if (!query.exec() || !query.isActive())
1598 {
1599 MythDB::DBError("Adding Service", query);
1600 return false;
1601 }
1602 return true;
1603}
1604
1606 uint source_id,
1607 uint channel_id,
1608 const QString &callsign,
1609 const QString &service_name,
1610 const QString &chan_num,
1611 uint service_id,
1612 uint atsc_major_channel,
1613 uint atsc_minor_channel,
1614 bool use_on_air_guide,
1615 ChannelVisibleType visible,
1616 const QString& freqid,
1617 const QString& icon,
1618 QString format,
1619 const QString& xmltvid,
1620 const QString& default_authority,
1621 uint service_type,
1622 int recpriority,
1623 int tmOffset,
1624 int commMethod )
1625{
1626 if (!channel_id)
1627 return false;
1628
1629 QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : std::move(format);
1630 bool set_channum = !chan_num.isEmpty() && chan_num != "-1";
1631 QString qstr = QString(
1632 "UPDATE channel "
1633 "SET %1 %2 %3 %4 %5 %6 %7 %8 %9 "
1634 " mplexid = :MPLEXID, serviceid = :SERVICEID, "
1635 " atsc_major_chan = :MAJORCHAN, atsc_minor_chan = :MINORCHAN, "
1636 " callsign = :CALLSIGN, name = :NAME, "
1637 " sourceid = :SOURCEID, useonairguide = :USEOAG, "
1638 " visible = :VISIBLE, service_type = :SERVICETYPE "
1639 "WHERE chanid=:CHANID")
1640 .arg((!set_channum) ? "" : "channum = :CHANNUM, ",
1641 (freqid.isEmpty()) ? "" : "freqid = :FREQID, ",
1642 (icon.isEmpty()) ? "" : "icon = :ICON, ",
1643 (tvformat.isEmpty()) ? "" : "tvformat = :TVFORMAT, ",
1644 (xmltvid.isEmpty()) ? "" : "xmltvid = :XMLTVID, ",
1645 (default_authority.isEmpty()) ?
1646 "" : "default_authority = :AUTHORITY,",
1647 (recpriority == INT_MIN) ? "" : "recpriority = :RECPRIORITY, ",
1648 (tmOffset == INT_MIN) ? "" : "tmOffset = :TMOFFSET, ",
1649 (commMethod == INT_MIN) ? "" : "commmethod = :COMMMETHOD, ");
1650
1652 query.prepare(qstr);
1653
1654 query.bindValue(":CHANID", channel_id);
1655
1656 if (set_channum)
1657 query.bindValue(":CHANNUM", chan_num);
1658
1659 query.bindValue (":SOURCEID", source_id);
1660 query.bindValueNoNull(":CALLSIGN", callsign);
1661 query.bindValueNoNull(":NAME", service_name);
1662
1663 query.bindValue(":MPLEXID", db_mplexid);
1664 query.bindValue(":SERVICEID", service_id);
1665 query.bindValue(":MAJORCHAN", atsc_major_channel);
1666 query.bindValue(":MINORCHAN", atsc_minor_channel);
1667 query.bindValue(":USEOAG", use_on_air_guide);
1668 query.bindValue(":VISIBLE", visible);
1669 query.bindValue(":SERVICETYPE", service_type);
1670
1671 if (!freqid.isNull())
1672 query.bindValue(":FREQID", freqid);
1673 if (!tvformat.isNull())
1674 query.bindValue(":TVFORMAT", tvformat);
1675 if (!icon.isNull())
1676 query.bindValue(":ICON", icon);
1677 if (!xmltvid.isNull())
1678 query.bindValue(":XMLTVID", xmltvid);
1679 if (!default_authority.isNull())
1680 query.bindValue(":AUTHORITY", default_authority);
1681 if (recpriority != INT_MIN)
1682 query.bindValue(":RECPRIORITY", recpriority);
1683 if (tmOffset != INT_MIN)
1684 query.bindValue(":TMOFFSET", tmOffset);
1685 if (commMethod != INT_MIN)
1686 query.bindValue(":COMMMETHOD", commMethod);
1687
1688 if (!query.exec())
1689 {
1690 MythDB::DBError("Updating Service", query);
1691 return false;
1692 }
1693 return true;
1694}
1695
1697{
1699 query.prepare(
1700 "SELECT channum "
1701 "FROM channel "
1702 "WHERE chanid = :ID");
1703 query.bindValue(":ID", chan.m_channelId);
1704
1705 if (!query.exec())
1706 {
1707 MythDB::DBError("UpdateChannelNumberFromDB", query);
1708 return;
1709 }
1710
1711 if (query.next())
1712 {
1713 QString channum = query.value(0).toString();
1714
1715 if (!channum.isEmpty())
1716 {
1717 chan.m_chanNum = channum;
1718 }
1719 }
1720}
1721
1723{
1725 query.prepare(
1726 "SELECT xmltvid, useonairguide, visible "
1727 "FROM channel "
1728 "WHERE chanid = :ID");
1729 query.bindValue(":ID", chan.m_channelId);
1730
1731 if (!query.exec())
1732 {
1733 MythDB::DBError("UpdateInsertInfoFromDB", query);
1734 return;
1735 }
1736
1737 if (query.next())
1738 {
1739 QString xmltvid = query.value(0).toString();
1740 bool useeit = query.value(1).toBool();
1741 ChannelVisibleType visible =
1742 static_cast<ChannelVisibleType>(query.value(2).toInt());
1743
1744 if (!xmltvid.isEmpty())
1745 {
1746 if (useeit)
1747 {
1748 LOG(VB_GENERAL, LOG_ERR,
1749 "Using EIT and xmltv for the same channel "
1750 "is an unsupported configuration.");
1751 }
1752 chan.m_xmltvId = xmltvid;
1753 }
1754 chan.m_useOnAirGuide = useeit;
1755 chan.m_hidden = (visible == kChannelNotVisible ||
1756 visible == kChannelNeverVisible);
1757 chan.m_visible = visible;
1758 }
1759}
1760
1762 uint channel_id, const IPTVTuningData &tuning)
1763{
1765
1766 query.prepare(
1767 "DELETE FROM iptv_channel "
1768 "WHERE chanid=:CHANID");
1769 query.bindValue(":CHANID", channel_id);
1770
1771 if (!query.exec())
1772 {
1773 MythDB::DBError("UpdateIPTVTuningData -- delete", query);
1774 return false;
1775 }
1776
1777 query.prepare(
1778 "INSERT INTO iptv_channel (chanid, url, type, bitrate) "
1779 "VALUES (:CHANID, :URL, :TYPE, :BITRATE)");
1780 query.bindValue(":CHANID", channel_id);
1781
1782 query.bindValue(":URL", tuning.GetDataURL().toString());
1783 query.bindValue(":TYPE", tuning.GetFECTypeString(0));
1784 query.bindValue(":BITRATE", tuning.GetBitrate(0));
1785
1786 if (!query.exec())
1787 {
1788 MythDB::DBError("UpdateIPTVTuningData -- data", query);
1789 return false;
1790 }
1791
1792 if (tuning.GetFECURL0().port() >= 0)
1793 {
1794 query.bindValue(":URL", tuning.GetFECURL0().toString());
1795 query.bindValue(":TYPE", tuning.GetFECTypeString(1));
1796 query.bindValue(":BITRATE", tuning.GetBitrate(1));
1797 if (!query.exec())
1798 {
1799 MythDB::DBError("UpdateIPTVTuningData -- fec 0", query);
1800 return false;
1801 }
1802 }
1803
1804 if (tuning.GetFECURL1().port() >= 0)
1805 {
1806 query.bindValue(":URL", tuning.GetFECURL1().toString());
1807 query.bindValue(":TYPE", tuning.GetFECTypeString(2));
1808 query.bindValue(":BITRATE", tuning.GetBitrate(2));
1809 if (!query.exec())
1810 {
1811 MythDB::DBError("UpdateIPTVTuningData -- fec 1", query);
1812 return false;
1813 }
1814 }
1815
1816 return true;
1817}
1818
1820{
1822 query.prepare(
1823 "UPDATE channel "
1824 "SET deleted = NOW() "
1825 "WHERE chanid = :ID");
1826 query.bindValue(":ID", channel_id);
1827
1828 if (!query.exec())
1829 {
1830 MythDB::DBError("Delete Channel", query);
1831 return false;
1832 }
1833
1834 return true;
1835}
1836
1838{
1840 query.prepare(
1841 "UPDATE channel "
1842 "SET visible = :VISIBLE "
1843 "WHERE chanid = :ID");
1844 query.bindValue(":ID", channel_id);
1845 query.bindValue(":VISIBLE", visible);
1846
1847 if (!query.exec())
1848 {
1849 MythDB::DBError("ChannelUtil::SetVisible", query);
1850 return false;
1851 }
1852
1853 return true;
1854}
1855
1857{
1859
1860 query.prepare("UPDATE dtv_multiplex "
1861 "SET serviceversion = :VERSION "
1862 "WHERE mplexid = :MPLEXID");
1863
1864 query.bindValue(":VERSION", version);
1865 query.bindValue(":MPLEXID", mplexid);
1866
1867 if (!query.exec())
1868 {
1869 MythDB::DBError("Selecting channel/dtv_multiplex", query);
1870 return false;
1871 }
1872 return true;
1873}
1874
1876{
1878
1879 query.prepare("SELECT serviceversion "
1880 "FROM dtv_multiplex "
1881 "WHERE mplexid = :MPLEXID");
1882
1883 query.bindValue(":MPLEXID", mplexid);
1884
1885 if (!query.exec())
1886 {
1887 MythDB::DBError("Selecting channel/dtv_multiplex", query);
1888 return 0;
1889 }
1890
1891 if (query.next())
1892 return query.value(0).toInt();
1893
1894 return -1;
1895}
1896
1897bool ChannelUtil::GetATSCChannel(uint sourceid, const QString &channum,
1898 uint &major, uint &minor)
1899{
1900 major = minor = 0;
1901
1903 query.prepare(
1904 "SELECT atsc_major_chan, atsc_minor_chan "
1905 "FROM channel "
1906 "WHERE deleted IS NULL AND "
1907 " channum = :CHANNUM AND "
1908 " sourceid = :SOURCEID");
1909
1910 query.bindValue(":SOURCEID", sourceid);
1911 query.bindValue(":CHANNUM", channum);
1912
1913 if (!query.exec() || !query.isActive())
1914 {
1915 MythDB::DBError("getatscchannel", query);
1916 }
1917 else if (query.next())
1918 {
1919 major = query.value(0).toUInt();
1920 minor = query.value(1).toUInt();
1921 return true;
1922 }
1923
1924 return false;
1925}
1926
1928 uint sourceid,
1929 uint &chanid, const QString &channum,
1930 QString &name, QString &callsign,
1931 QString &tvformat, QString &modulation,
1932 QString &freqtable, QString &freqid,
1933 int &finetune, uint64_t &frequency,
1934 QString &dtv_si_std, int &mpeg_prog_num,
1935 uint &atsc_major, uint &atsc_minor,
1936 uint &dvb_transportid, uint &dvb_networkid,
1937 uint &mplexid,
1938 bool &commfree)
1939{
1940 chanid = 0;
1941 tvformat.clear();
1942 modulation.clear();
1943 freqtable.clear();;
1944 freqid.clear();
1945 dtv_si_std.clear();
1946 finetune = 0;
1947 frequency = 0;
1948 mpeg_prog_num = -1;
1949 atsc_major = atsc_minor = mplexid = 0;
1950 dvb_networkid = dvb_transportid = 0;
1951 commfree = false;
1952
1953 int found = 0;
1955 query.prepare(
1956 "SELECT finetune, freqid, tvformat, freqtable, "
1957 " commmethod, mplexid, "
1958 " atsc_major_chan, atsc_minor_chan, serviceid, "
1959 " chanid, channel.name, callsign, visible "
1960 "FROM channel, videosource "
1961 "WHERE channel.deleted IS NULL AND "
1962 " videosource.sourceid = channel.sourceid AND "
1963 " channum = :CHANNUM AND "
1964 " channel.sourceid = :SOURCEID "
1965 "ORDER BY channel.visible > 0 DESC, channel.chanid ");
1966 query.bindValue(":CHANNUM", channum);
1967 query.bindValue(":SOURCEID", sourceid);
1968
1969 if (!query.exec() || !query.isActive())
1970 {
1971 MythDB::DBError("GetChannelData", query);
1972 return false;
1973 }
1974
1975 if (query.next())
1976 {
1977 finetune = query.value(0).toInt();
1978 freqid = query.value(1).toString();
1979 tvformat = query.value(2).toString();
1980 freqtable = query.value(3).toString();
1981 commfree = (query.value(4).toInt() == -2);
1982 mplexid = query.value(5).toUInt();
1983 atsc_major = query.value(6).toUInt();
1984 atsc_minor = query.value(7).toUInt();
1985 mpeg_prog_num = (query.value(8).isNull()) ? -1
1986 : query.value(8).toInt();
1987 chanid = query.value(9).toUInt();
1988 name = query.value(10).toString();
1989 callsign = query.value(11).toString();
1990
1991 if (query.value(12).toInt() > kChannelNotVisible)
1992 ++found;
1993 }
1994
1995 while (query.next())
1996 if (query.value(12).toInt() > kChannelNotVisible)
1997 ++found;
1998
1999 if (found == 0 && chanid)
2000 {
2001 LOG(VB_GENERAL, LOG_WARNING,
2002 QString("No visible channels for %1, using invisble chanid %2")
2003 .arg(channum).arg(chanid));
2004 }
2005
2006 if (found > 1)
2007 {
2008 LOG(VB_GENERAL, LOG_WARNING,
2009 QString("Found multiple visible channels for %1, using chanid %2")
2010 .arg(channum).arg(chanid));
2011 }
2012
2013 if (!chanid)
2014 {
2015 LOG(VB_GENERAL, LOG_ERR,
2016 QString("Could not find channel '%1' in DB for source %2 '%3'.")
2017 .arg(channum).arg(sourceid).arg(SourceUtil::GetSourceName(sourceid)));
2018 return false;
2019 }
2020
2021 if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
2022 return true;
2023
2024 return GetTuningParams(mplexid, modulation, frequency,
2025 dvb_transportid, dvb_networkid, dtv_si_std);
2026}
2027
2029{
2031 query.prepare(
2032 "SELECT type+0, url, bitrate "
2033 "FROM iptv_channel "
2034 "WHERE chanid = :CHANID "
2035 "ORDER BY type+0");
2036 query.bindValue(":CHANID", chanid);
2037
2038 if (!query.exec())
2039 {
2040 MythDB::DBError("GetChannelData -- iptv", query);
2041 return {};
2042 }
2043
2044 QString data_url;
2045 QString fec_url0;
2046 QString fec_url1;
2048 std::array<uint,3> bitrate { 0, 0, 0, };
2049 while (query.next())
2050 {
2052 query.value(0).toUInt();
2053 switch (type)
2054 {
2056 data_url = query.value(1).toString();
2057 bitrate[0] = query.value(2).toUInt();
2058 break;
2062 fec_url0 = query.value(1).toString();
2063 bitrate[1] = query.value(2).toUInt();
2064 break;
2068 fec_url1 = query.value(1).toString();
2069 bitrate[2] = query.value(2).toUInt();
2070 break;
2071 }
2072 switch (type)
2073 {
2075 break;
2077 fec_type = IPTVTuningData::kRFC2733;
2078 break;
2080 fec_type = IPTVTuningData::kRFC5109;
2081 break;
2083 fec_type = IPTVTuningData::kSMPTE2022;
2084 break;
2088 break; // will be handled by type of first FEC stream
2089 }
2090 }
2091
2092 IPTVTuningData tuning(data_url, bitrate[0], fec_type,
2093 fec_url0, bitrate[1], fec_url1, bitrate[2]);
2094 LOG(VB_GENERAL, LOG_INFO, QString("Loaded %1 for %2")
2095 .arg(tuning.GetDeviceName()).arg(chanid));
2096 return tuning;
2097}
2098
2099// TODO This should be modified to load a complete channelinfo object including
2100// all fields from the database
2105 uint sourceid, bool visible_only, bool include_disconnected,
2106 const QString &group_by, uint channel_groupid)
2107{
2108 ChannelInfoList list;
2109
2111
2112 QString qstr = QString(
2113 "SELECT videosource.sourceid, GROUP_CONCAT(capturecard.cardid) "
2114 "FROM videosource "
2115 "%1 JOIN capturecard ON capturecard.sourceid = videosource.sourceid "
2116 "GROUP BY videosource.sourceid")
2117 .arg(include_disconnected ? "LEFT" : "");
2118
2119 query.prepare(qstr);
2120 if (!query.exec())
2121 {
2122 MythDB::DBError("ChannelUtil::GetChannels()", query);
2123 return list;
2124 }
2125
2126 QMap<uint, QList<uint>> inputIdLists;
2127 while (query.next())
2128 {
2129 uint qSourceId = query.value(0).toUInt();
2130 QList<uint> &inputIdList = inputIdLists[qSourceId];
2131 QStringList inputIds = query.value(1).toString().split(",");
2132 while (!inputIds.isEmpty())
2133 inputIdList.append(inputIds.takeFirst().toUInt());
2134 }
2135
2136 qstr = QString(
2137 "SELECT channum, callsign, channel.chanid, "
2138 " atsc_major_chan, atsc_minor_chan, "
2139 " name, icon, mplexid, visible, "
2140 " channel.sourceid, "
2141 " GROUP_CONCAT(DISTINCT channelgroup.grpid), "
2142 " xmltvid "
2143 "FROM channel "
2144 "LEFT JOIN channelgroup ON channel.chanid = channelgroup.chanid ");
2145
2146 qstr += "WHERE deleted IS NULL ";
2147
2148 if (sourceid)
2149 qstr += QString("AND channel.sourceid='%1' ").arg(sourceid);
2150
2151 // Select only channels from the specified channel group
2152 if (channel_groupid > 0)
2153 qstr += QString("AND channelgroup.grpid = '%1' ").arg(channel_groupid);
2154
2155 if (visible_only)
2156 qstr += QString("AND visible > 0 ");
2157
2158 qstr += " GROUP BY chanid";
2159
2160 if (!group_by.isEmpty())
2161 qstr += QString(", %1").arg(group_by);
2162
2163 query.prepare(qstr);
2164 if (!query.exec())
2165 {
2166 MythDB::DBError("ChannelUtil::GetChannels()", query);
2167 return list;
2168 }
2169
2170 while (query.next())
2171 {
2172 if (query.value(0).toString().isEmpty() || !query.value(2).toBool())
2173 continue; // skip if channum blank, or chanid empty
2174
2175 uint qSourceID = query.value(9).toUInt();
2176 ChannelInfo chan(
2177 query.value(0).toString(), /* channum */
2178 query.value(1).toString(), /* callsign */
2179 query.value(2).toUInt(), /* chanid */
2180 query.value(3).toUInt(), /* ATSC major */
2181 query.value(4).toUInt(), /* ATSC minor */
2182 query.value(7).toUInt(), /* mplexid */
2183 static_cast<ChannelVisibleType>(query.value(8).toInt()),
2184 /* visible */
2185 query.value(5).toString(), /* name */
2186 query.value(6).toString(), /* icon */
2187 qSourceID); /* sourceid */
2188
2189 chan.m_xmltvId = query.value(11).toString(); /* xmltvid */
2190
2191 for (auto inputId : std::as_const(inputIdLists[qSourceID]))
2192 chan.AddInputId(inputId);
2193
2194 QStringList groupIDs = query.value(10).toString().split(",");
2195 while (!groupIDs.isEmpty())
2196 chan.AddGroupId(groupIDs.takeFirst().toUInt());
2197
2198 list.push_back(chan);
2199
2200 }
2201
2202 return list;
2203}
2204
2205std::vector<uint> ChannelUtil::GetChanIDs(int sourceid, bool onlyVisible)
2206{
2208
2209 QString select = "SELECT chanid FROM channel WHERE deleted IS NULL ";
2210 // Yes, this a little ugly
2211 if (onlyVisible || sourceid > 0)
2212 {
2213 if (onlyVisible)
2214 select += "AND visible > 0 ";
2215 if (sourceid > 0)
2216 select += "AND sourceid=" + QString::number(sourceid);
2217 }
2218
2219 std::vector<uint> list;
2220 query.prepare(select);
2221 if (!query.exec())
2222 {
2223 MythDB::DBError("SourceUtil::GetChanIDs()", query);
2224 return list;
2225 }
2226
2227 while (query.next())
2228 list.push_back(query.value(0).toUInt());
2229
2230 return list;
2231}
2232
2233inline bool lt_callsign(const ChannelInfo &a, const ChannelInfo &b)
2234{
2235 // For the spaceship operator, the c++ standard library explicitly
2236 // requires '0' and not nullptr.
2237 // NOLINTNEXTLINE(modernize-use-nullptr)
2239}
2240
2241inline bool lt_smart(const ChannelInfo &a, const ChannelInfo &b)
2242{
2243 static QMutex s_sepExprLock;
2244 static const QRegularExpression kSepExpr(ChannelUtil::kATSCSeparators);
2245
2246 bool isIntA = false;
2247 bool isIntB = false;
2248 int a_int = a.m_chanNum.toUInt(&isIntA);
2249 int b_int = b.m_chanNum.toUInt(&isIntB);
2250 int a_major = a.m_atscMajorChan;
2251 int b_major = b.m_atscMajorChan;
2252 int a_minor = a.m_atscMinorChan;
2253 int b_minor = b.m_atscMinorChan;
2254
2255 // Extract minor and major numbers from channum..
2256 int idxA = 0;
2257 int idxB = 0;
2258 {
2259 QMutexLocker locker(&s_sepExprLock);
2260 idxA = a.m_chanNum.indexOf(kSepExpr);
2261 idxB = b.m_chanNum.indexOf(kSepExpr);
2262 }
2263 if (idxA >= 0)
2264 {
2265 bool tmp1 = false;
2266 bool tmp2 = false;
2267 int major = a.m_chanNum.left(idxA).toUInt(&tmp1);
2268 int minor = a.m_chanNum.mid(idxA+1).toUInt(&tmp2);
2269 if (tmp1 && tmp2)
2270 (a_major = major), (a_minor = minor), (isIntA = false);
2271 }
2272
2273 if (idxB >= 0)
2274 {
2275 bool tmp1 = false;
2276 bool tmp2 = false;
2277 int major = b.m_chanNum.left(idxB).toUInt(&tmp1);
2278 int minor = b.m_chanNum.mid(idxB+1).toUInt(&tmp2);
2279 if (tmp1 && tmp2)
2280 (b_major = major), (b_minor = minor), (isIntB = false);
2281 }
2282
2283 // If ATSC channel has been renumbered, sort by new channel number
2284 if ((a_minor > 0) && isIntA)
2285 {
2286 int atsc_int = (QString("%1%2").arg(a_major).arg(a_minor)).toInt();
2287 a_minor = (atsc_int == a_int) ? a_minor : 0;
2288 }
2289
2290 if ((b_minor > 0) && isIntB)
2291 {
2292 int atsc_int = (QString("%1%2").arg(b_major).arg(b_minor)).toInt();
2293 b_minor = (atsc_int == b_int) ? b_minor : 0;
2294 }
2295
2296 // one of the channels is an ATSC channel, and the other
2297 // is either ATSC or is numeric.
2298 if ((a_minor || b_minor) &&
2299 (a_minor || isIntA) && (b_minor || isIntB))
2300 {
2301 int a_maj = (!a_minor && isIntA) ? a_int : a_major;
2302 int b_maj = (!b_minor && isIntB) ? b_int : b_major;
2303 int cmp = a_maj - b_maj;
2304 if (cmp != 0)
2305 return cmp < 0;
2306
2307 cmp = a_minor - b_minor;
2308 if (cmp != 0)
2309 return cmp < 0;
2310 }
2311
2312 if (isIntA && isIntB)
2313 {
2314 // both channels have a numeric channum
2315 int cmp = a_int - b_int;
2316 if (cmp)
2317 return cmp < 0;
2318 }
2319 else if (isIntA ^ isIntB)
2320 {
2321 // if only one is channel numeric always consider it less than
2322 return isIntA;
2323 }
2324 else
2325 {
2326 // neither of channels have a numeric channum
2327 // For the spaceship operator, the c++ standard library explicitly
2328 // requires '0' and not nullptr.
2329 // NOLINTBEGIN(modernize-use-nullptr)
2331 if (cmp != 0)
2332 return cmp < 0;
2333 // NOLINTEND(modernize-use-nullptr)
2334 }
2335
2336 return lt_callsign(a,b);
2337}
2338
2340{
2342 QString select;
2343
2344
2345 select = "SELECT chanid FROM channel WHERE deleted IS NULL ";
2346 if (sourceid >= 0)
2347 select += "AND sourceid=" + QString::number(sourceid);
2348 select += ';';
2349
2350 query.prepare(select);
2351
2352 if (!query.exec() || !query.isActive())
2353 return 0;
2354
2355 return query.size();
2356}
2357
2358void ChannelUtil::SortChannels(ChannelInfoList &list, const QString &order,
2359 bool eliminate_duplicates)
2360{
2361 bool cs = order.toLower() == "callsign";
2362 if (cs)
2363 std::ranges::stable_sort(list, lt_callsign);
2364 else /* if (sortorder == "channum") */
2365 std::ranges::stable_sort(list, lt_smart);
2366
2367 if (eliminate_duplicates && !list.empty())
2368 {
2369 ChannelInfoList tmp;
2370 tmp.push_back(list[0]);
2371 for (size_t i = 1; i < list.size(); i++)
2372 {
2373 if ((cs && lt_callsign(tmp.back(), list[i])) ||
2374 (!cs && lt_smart(tmp.back(), list[i])))
2375 {
2376 tmp.push_back(list[i]);
2377 }
2378 }
2379
2380 list = tmp;
2381 }
2382}
2383
2384// Return the array index of the best matching channel. An exact
2385// match is the best match. Otherwise, find the closest numerical
2386// value greater than channum. E.g., if the channel list is {2_1,
2387// 2_2, 4_1, 4_2, 300} then input 3 returns 2_2, input 4 returns 2_2,
2388// and input 5 returns 4_2.
2389//
2390// The list does not need to be sorted.
2392 const QString &channum)
2393{
2394 ChannelInfo target;
2395 target.m_chanNum = channum;
2396 int b = -1; // index of best seen so far
2397 for (int i = 0; i < (int)list.size(); ++i)
2398 {
2399 // Index i is a better result if any of the following hold:
2400 // i is the first element seen
2401 // i < target < best (i.e., i is the first one less than the target)
2402 // best < i < target
2403 // target < i < best
2404 if ((b < 0) ||
2405 (lt_smart(list[i], target) && lt_smart(target, list[b])) ||
2406 (lt_smart(list[b], list[i]) && lt_smart(list[i], target)) ||
2407 (lt_smart(target, list[i]) && lt_smart(list[i], list[b])))
2408 {
2409 b = i;
2410 }
2411 }
2412 return b;
2413}
2414
2415namespace {
2419 {
2421 {
2422 if (it != l.begin())
2423 return --it;
2424 it = std::ranges::find(l, l.rbegin()->m_chanId,
2426 if (it == l.end())
2427 return --it;
2428 return it;
2429 }
2430
2431 // UP or FAVORITE
2432 ++it;
2433 if (it == l.end())
2434 return l.begin();
2435 return it;
2436 }
2437}
2438
2440 const ChannelInfoList &sorted,
2441 uint old_chanid,
2442 uint mplexid_restriction,
2443 uint chanid_restriction,
2444 ChannelChangeDirection direction,
2445 bool skip_non_visible,
2446 bool skip_same_channum_and_callsign,
2447 bool skip_other_sources)
2448{
2449 if (sorted.empty())
2450 return 0; // no channels..
2451
2452 auto it = std::ranges::find(sorted, old_chanid, &ChannelInfo::m_chanId);
2453 if (it == sorted.end())
2454 it = sorted.begin(); // not in list, pretend we are on first channel
2455
2456 if (CHANNEL_DIRECTION_SAME == direction)
2457 return it->m_chanId;
2458
2459 auto start = it;
2460
2461 for (it = next_w_wrap(sorted, it, direction);
2462 it != start;
2463 it = next_w_wrap(sorted, it, direction))
2464 {
2465 if (skip_non_visible && (it->m_visible < kChannelVisible))
2466 continue;
2467 if (skip_other_sources && (it->m_sourceId != start->m_sourceId))
2468 continue;
2469 if (skip_same_channum_and_callsign && (it->m_chanNum == start->m_chanNum &&
2470 it->m_callSign == start->m_callSign))
2471 continue;
2472 if ((mplexid_restriction != 0U) && (mplexid_restriction != it->m_mplexId))
2473 continue;
2474 if ((chanid_restriction != 0U) && (chanid_restriction != it->m_chanId))
2475 continue;
2476 break;
2477 }
2478
2479 return it->m_chanId;
2480}
2481
2483 uint &totalAvailable,
2484 bool ignoreHidden,
2485 ChannelUtil::OrderBy orderBy,
2486 ChannelUtil::GroupBy groupBy,
2487 uint sourceID,
2488 uint channelGroupID,
2489 bool liveTVOnly,
2490 const QString& callsign,
2491 const QString& channum,
2492 bool ignoreUntunable)
2493{
2494 ChannelInfoList channelList;
2495
2497
2498 QString sql = QString(
2499 "SELECT parentid, GROUP_CONCAT(cardid ORDER BY cardid) "
2500 "FROM capturecard "
2501 "WHERE parentid <> 0 "
2502 "GROUP BY parentid ");
2503
2504 query.prepare(sql);
2505 if (!query.exec())
2506 {
2507 MythDB::DBError("ChannelUtil::GetChannels()", query);
2508 return channelList;
2509 }
2510
2511 QMap<uint, QList<uint>> childIdLists;
2512 while (query.next())
2513 {
2514 auto parentId = query.value(0).toUInt();
2515 auto &childIdList = childIdLists[parentId];
2516 auto childIds = query.value(1).toString().split(",");
2517 while (!childIds.isEmpty())
2518 childIdList.append(childIds.takeFirst().toUInt());
2519 }
2520
2521 sql = "SELECT %1 channum, freqid, channel.sourceid, "
2522 "callsign, name, icon, finetune, videofilters, xmltvid, "
2523 "channel.recpriority, channel.contrast, channel.brightness, "
2524 "channel.colour, channel.hue, tvformat, "
2525 "visible, outputfilters, useonairguide, mplexid, "
2526 "serviceid, atsc_major_chan, atsc_minor_chan, last_record, "
2527 "default_authority, commmethod, tmoffset, iptvid, "
2528 "channel.chanid, "
2529 "GROUP_CONCAT(DISTINCT `groups`.`groupids`), " // Creates a CSV list of channel groupids for this channel
2530 "GROUP_CONCAT(DISTINCT capturecard.cardid "
2531 " ORDER BY livetvorder), " // Creates a CSV list of inputids for this channel
2532 "MIN(livetvorder) livetvorder "
2533 "FROM channel ";
2534 if (!channelGroupID)
2535 sql += "LEFT ";
2536 sql += "JOIN ( "
2537 " SELECT chanid ,"
2538 " GROUP_CONCAT(grpid ORDER BY grpid) groupids "
2539 " FROM channelgroup ";
2540 if (channelGroupID)
2541 sql += " WHERE grpid = :CHANGROUPID ";
2542 sql += " GROUP BY chanid "
2543 ") `groups` "
2544 " ON channel.chanid = `groups`.`chanid` ";
2545 if (!ignoreUntunable && !liveTVOnly)
2546 sql += "LEFT ";
2547 sql += "JOIN capturecard "
2548 " ON capturecard.sourceid = channel.sourceid "
2549 " AND capturecard.parentid = 0 ";
2550 if (liveTVOnly)
2551 sql += " AND capturecard.livetvorder > 0 ";
2552
2553 sql += "WHERE channel.deleted IS NULL ";
2554 if (ignoreHidden)
2555 sql += "AND channel.visible > 0 ";
2556
2557 if (sourceID > 0)
2558 sql += "AND channel.sourceid = :SOURCEID ";
2559
2560 if (groupBy == kChanGroupByCallsign)
2561 sql += "GROUP BY channel.callsign ";
2562 else if (groupBy == kChanGroupByCallsignAndChannum)
2563 sql += "GROUP BY channel.callsign, channel.channum ";
2564 else
2565 sql += "GROUP BY channel.chanid "; // We must always group for this query
2566
2567 if (orderBy == kChanOrderByName)
2568 {
2569 sql += "ORDER BY channel.name ";
2570 }
2571 else if (orderBy == kChanOrderByChanNum)
2572 {
2573 // Natural sorting including subchannels e.g. 2_4, 1.3
2574 sql += "ORDER BY LPAD(CAST(channel.channum AS UNSIGNED), 10, 0), "
2575 " LPAD(channel.channum, 10, 0) ";
2576 }
2577 else // kChanOrderByLiveTV
2578 {
2579 sql += "ORDER BY callsign = :CALLSIGN1 AND channum = :CHANNUM DESC, "
2580 " callsign = :CALLSIGN2 DESC, "
2581 " livetvorder, "
2582 " channel.recpriority DESC, "
2583 " chanid ";
2584 }
2585
2586 if (count > 0)
2587 sql += "LIMIT :LIMIT ";
2588
2589 if (startIndex > 0)
2590 sql += "OFFSET :STARTINDEX ";
2591
2592
2593 if (startIndex > 0 || count > 0)
2594 sql = sql.arg("SQL_CALC_FOUND_ROWS");
2595 else
2596 sql = sql.arg(""); // remove place holder
2597
2598 query.prepare(sql);
2599
2600 if (channelGroupID > 0)
2601 query.bindValue(":CHANGROUPID", channelGroupID);
2602
2603 if (sourceID > 0)
2604 query.bindValue(":SOURCEID", sourceID);
2605
2606 if (count > 0)
2607 query.bindValue(":LIMIT", count);
2608
2609 if (startIndex > 0)
2610 query.bindValue(":STARTINDEX", startIndex);
2611
2612 if (orderBy == kChanOrderByLiveTV)
2613 {
2614 query.bindValue(":CALLSIGN1", callsign);
2615 query.bindValue(":CHANNUM", channum);
2616 query.bindValue(":CALLSIGN2", callsign);
2617 }
2618
2619 if (!query.exec())
2620 {
2621 MythDB::DBError("ChannelInfo::Load()", query);
2622 return channelList;
2623 }
2624
2625 std::vector<uint> groupIdList;
2626 while (query.next())
2627 {
2628 ChannelInfo channelInfo;
2629 channelInfo.m_chanNum = query.value(0).toString();
2630 channelInfo.m_freqId = query.value(1).toString();
2631 channelInfo.m_sourceId = query.value(2).toUInt();
2632 channelInfo.m_callSign = query.value(3).toString();
2633 channelInfo.m_name = query.value(4).toString();
2634 channelInfo.m_icon = query.value(5).toString();
2635 channelInfo.m_fineTune = query.value(6).toInt();
2636 channelInfo.m_videoFilters = query.value(7).toString();
2637 channelInfo.m_xmltvId = query.value(8).toString();
2638 channelInfo.m_recPriority = query.value(9).toInt();
2639 channelInfo.m_contrast = query.value(10).toUInt();
2640 channelInfo.m_brightness = query.value(11).toUInt();
2641 channelInfo.m_colour = query.value(12).toUInt();
2642 channelInfo.m_hue = query.value(13).toUInt();
2643 channelInfo.m_tvFormat = query.value(14).toString();
2644 channelInfo.m_visible =
2645 static_cast<ChannelVisibleType>(query.value(15).toInt());
2646 channelInfo.m_outputFilters = query.value(16).toString();
2647 channelInfo.m_useOnAirGuide = query.value(17).toBool();
2648 channelInfo.m_mplexId = query.value(18).toUInt();
2649 channelInfo.m_serviceId = query.value(19).toUInt();
2650 channelInfo.m_atscMajorChan = query.value(20).toUInt();
2651 channelInfo.m_atscMinorChan = query.value(21).toUInt();
2652 channelInfo.m_lastRecord = query.value(22).toDateTime();
2653 channelInfo.m_defaultAuthority = query.value(23).toString();
2654 channelInfo.m_commMethod = query.value(24).toUInt();
2655 channelInfo.m_tmOffset = query.value(25).toUInt();
2656 channelInfo.m_iptvId = query.value(26).toUInt();
2657 channelInfo.m_chanId = query.value(27).toUInt();
2658
2659 QStringList groupIDs = query.value(28).toString().split(",");
2660 groupIdList.clear();
2661 while (!groupIDs.isEmpty())
2662 groupIdList.push_back(groupIDs.takeFirst().toUInt());
2663 std::ranges::sort(groupIdList);
2664 for (auto groupId : groupIdList)
2665 channelInfo.AddGroupId(groupId);
2666
2667 QStringList parentIDs = query.value(29).toString().split(",");
2668 while (!parentIDs.isEmpty())
2669 {
2670 auto parentId = parentIDs.takeFirst().toUInt();
2671 channelInfo.AddInputId(parentId);
2672 auto childIdList = childIdLists[parentId];
2673 for (auto childId : childIdList)
2674 channelInfo.AddInputId(childId);
2675 }
2676
2677 channelList.push_back(channelInfo);
2678 }
2679
2680 if ((startIndex > 0 || count > 0) &&
2681 query.exec("SELECT FOUND_ROWS()") && query.next())
2682 totalAvailable = query.value(0).toUInt();
2683 else
2684 totalAvailable = query.size();
2685
2686 return channelList;
2687}
2688
2689/* 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:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:903
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
static MSqlQueryInfo ChannelCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:600
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
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