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