MythTV master
sourceutil.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3// C++ headers
4#include <algorithm>
5
6// Qt Headers
7#include <QRegularExpression>
8
9// MythTV headers
10#include "libmythbase/mythdb.h"
14
15#include "cardutil.h"
17#include "sourceutil.h"
18
20{
22
23 query.prepare(
24 "SELECT mplexid, atsc_minor_chan, serviceid "
25 "FROM channel "
26 "WHERE deleted IS NULL AND "
27 " sourceid = :SOURCEID");
28 query.bindValue(":SOURCEID", sourceid);
29
30 if (!query.exec())
31 {
32 MythDB::DBError("SourceUtil::HasDigitalChannel()", query);
33 return false;
34 }
35
36 while (query.next())
37 {
38 uint mplexid = query.value(0).toUInt();
39 uint minor = query.value(1).toUInt();
40 uint prognum = query.value(2).toUInt();
41 mplexid = (32767 == mplexid) ? 0 : mplexid;
42
43 if (mplexid && (minor || prognum))
44 return true;
45 }
46
47 return false;
48}
49
51{
53
54 query.prepare(
55 "SELECT name "
56 "FROM videosource "
57 "WHERE sourceid = :SOURCEID");
58 query.bindValue(":SOURCEID", sourceid);
59
60 if (!query.exec())
61 {
62 MythDB::DBError("SourceUtil::GetSourceName()", query);
63 return {};
64 }
65 if (!query.next())
66 {
67 return {};
68 }
69
70 return query.value(0).toString();
71}
72
73uint SourceUtil::GetSourceID(const QString &name)
74{
76
77 query.prepare(
78 "SELECT sourceid "
79 "FROM videosource "
80 "WHERE name = :NAME");
81 query.bindValue(":NAME", name);
82
83 if (!query.exec())
84 {
85 MythDB::DBError("SourceUtil::GetSourceID()", query);
86 return 0;
87 }
88 if (!query.next())
89 {
90 return 0;
91 }
92
93 return query.value(0).toUInt();
94}
95
97{
99 query.prepare("SELECT channum "
100 "FROM channel "
101 "WHERE deleted IS NULL AND "
102 " sourceid = :SOURCEID");
103 query.bindValue(":SOURCEID", sourceid);
104
105 if (query.exec() && query.isActive() && query.size() > 0)
106 {
107 QMap<QString,uint> counts;
108 static const QRegularExpression kSeparatorRE { R"((_|-|#|\.))" };
109 while (query.next())
110 {
111 const QString channum = query.value(0).toString();
112 const int where = channum.indexOf(kSeparatorRE);
113 if (
114#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
115 channum.rightRef(2).startsWith("0")
116#else
117 QStringView(channum).right(2).startsWith(QStringLiteral("0"))
118#endif
119 )
120 counts["0"]++;
121 else
122 counts[(where < 0) ? "" : QString(channum.at(where))]++;
123 }
124 QString sep = "_";
125 uint max = counts["_"];
126 static const std::array<const QString,5> s_spacers { "", "-", "#", ".", "0" };
127 for (const auto & spacer : s_spacers)
128 {
129 if (counts[spacer] > max)
130 {
131 max = counts[spacer];
132 sep = spacer;
133 }
134 }
135 return sep;
136 }
137 return "_"; // default on failure
138}
139
141{
142 return QString("%1") + GetChannelSeparator(sourceid) + QString("%2");
143}
144
146{
148 query.prepare("SELECT sum(1) "
149 "FROM channel "
150 "WHERE deleted IS NULL AND "
151 " sourceid = :SOURCEID");
152 query.bindValue(":SOURCEID", sourceid);
153 if (query.exec() && query.isActive() && query.next())
154 return query.value(0).toUInt();
155 return 0;
156}
157
158std::vector<uint> SourceUtil::GetMplexIDs(uint sourceid)
159{
161
162 query.prepare(
163 "SELECT mplexid "
164 "FROM dtv_multiplex "
165 "WHERE sourceid = :SOURCEID");
166 query.bindValue(":SOURCEID", sourceid);
167
168 std::vector<uint> list;
169 if (!query.exec())
170 {
171 MythDB::DBError("SourceUtil::GetMplexIDs()", query);
172 return list;
173 }
174
175 while (query.next())
176 list.push_back(query.value(0).toUInt());
177
178 return list;
179}
180
182 QString &grabber, QString &userid,
183 QString &passwd, QString &lineupid)
184{
186 query.prepare(
187 "SELECT xmltvgrabber, userid, password, lineupid "
188 "FROM videosource "
189 "WHERE sourceid = :SOURCEID");
190 query.bindValue(":SOURCEID", sourceid);
191
192 if (!query.exec() || !query.isActive())
193 {
194 MythDB::DBError("SourceUtil::GetListingsLoginData()", query);
195 return false;
196 }
197
198 if (!query.next())
199 return false;
200
201 grabber = query.value(0).toString();
202 userid = query.value(1).toString();
203 passwd = query.value(2).toString();
204 lineupid = query.value(3).toString();
205
206 return true;
207}
208
209static QStringList get_inputtypes(uint sourceid)
210{
211 QStringList list;
212
214 query.prepare(
215 "SELECT cardtype, inputname "
216 "FROM capturecard "
217 "WHERE capturecard.sourceid = :SOURCEID");
218 query.bindValue(":SOURCEID", sourceid);
219
220 if (!query.exec() || !query.isActive())
221 {
222 MythDB::DBError("get_inputtypes()", query);
223 }
224 else
225 {
226 while (query.next())
227 {
229 QString inputtype = query.value(0).toString().toUpper();
230 QString inputname = query.value(1).toString().toUpper();
231 inputtype = ((inputtype == "DVB") && (!inputname.startsWith("DVB"))) ?
232 "V4L" : inputtype;
234 list += inputtype;
235 }
236 }
237
238 return list;
239}
240
242{
243 QStringList types = get_inputtypes(sourceid);
244 return types.size();
245}
246
247bool SourceUtil::IsProperlyConnected(uint sourceid, bool strict)
248{
249 QStringList types = get_inputtypes(sourceid);
250 QMap<QString,uint> counts;
251 for (const auto & type : std::as_const(types))
252 {
253 counts[type]++;
254
255 counts[CardUtil::IsEncoder(type) ? "ENCODER" : "NOT_ENCODER"]++;
256 counts[CardUtil::IsUnscanable(type) ? "NO_SCAN" : "SCAN"]++;
257
259 counts["ANALOG_TUNING"]++;
261 counts["DIGITAL_TUNING"]++;
263 counts["VIRTUAL_TUNING"]++;
264 }
265
266 bool tune_mismatch =
267 ((counts["ANALOG_TUNING"] != 0U) && (counts["DIGITAL_TUNING"] != 0U)) ||
268 ((counts["VIRTUAL_TUNING"] != 0U) && (counts["DIGITAL_TUNING"] != 0U));
269 bool enc_mismatch = (counts["ENCODER"] != 0U) && (counts["NOT_ENCODER"] != 0U);
270 bool scan_mismatch = (counts["SCAN"] != 0U) && (counts["NO_SCAN"] != 0U);
271
272 if (tune_mismatch)
273 {
274 uint a = counts["ANALOG_TUNERS"];
275 uint d = counts["DIGITAL_TUNERS"];
276 uint v = counts["VIRTUAL_TUNERS"];
277 LOG(VB_GENERAL, LOG_NOTICE,
278 QString("SourceUtil::IsProperlyConnected(): ") +
279 QString("Connected to %1 analog, %2 digital and %3 virtual "
280 "tuners\n\t\t\t").arg(a).arg(d).arg(v) +
281 QString("Can not mix digital with other tuning information."));
282 }
283
284 if (enc_mismatch)
285 {
286 uint a = counts["ENCODER"];
287 uint d = counts["NOT_ENCODER"];
288 LOG(VB_GENERAL, LOG_NOTICE,
289 QString("SourceUtil::IsProperlyConnected(): ") +
290 QString("Source ID %1 ").arg(sourceid) +
291 QString("appears to be connected\n\t\t\tto %1 encoder%2, ")
292 .arg(a).arg((1 == a) ? "":"s") +
293 QString("and %1 non-encoder%2. ")
294 .arg(d).arg((1 == d) ? "":"s") +
295 QString("This is probably a bad idea."));
296 }
297
298 if (scan_mismatch)
299 {
300 uint a = counts["SCAN"];
301 uint d = counts["NO_SCAN"];
302 LOG(VB_GENERAL, LOG_NOTICE,
303 QString("SourceUtil::IsProperlyConnected(): ") +
304 QString("Source ID %1 ").arg(sourceid) +
305 QString("appears to be connected\n\t\t\t"
306 "to %1 scanable input%2, ")
307 .arg(a).arg((1 == a) ? "":"s") +
308 QString("and %1 non-scanable input%2. ")
309 .arg(d).arg((1 == d) ? "":"s") +
310 QString("This may be a problem."));
311 }
312
313 if (!strict)
314 return !tune_mismatch;
315
316 return !tune_mismatch && !enc_mismatch && !scan_mismatch;
317}
318
319bool SourceUtil::IsEncoder(uint sourceid, bool strict)
320{
321 QStringList types = get_inputtypes(sourceid);
322 auto isencoder = [](const auto & type){ return CardUtil::IsEncoder(type); };
323 bool encoder = std::ranges::all_of(std::as_const(types), isencoder);
324
325 // Source is connected, go by input types for type determination
326 if (!types.empty())
327 return encoder;
328
329 // Try looking at channels if source is not connected,
331 query.prepare(
332 "SELECT atsc_minor_chan, serviceid "
333 "FROM channel "
334 "WHERE deleted IS NULL AND "
335 " sourceid = :SOURCEID");
336 query.bindValue(":SOURCEID", sourceid);
337
338 bool has_any_chan = false;
339 if (!query.exec() || !query.isActive())
340 {
341 MythDB::DBError("SourceUtil::IsEncoder", query);
342 }
343 else
344 {
345 while (query.next())
346 {
347 encoder &= !query.value(0).toBool() && !query.value(1).toBool();
348 has_any_chan = true;
349 }
350 }
351
352 return (strict && !has_any_chan) ? false: encoder;
353}
354
356{
357 QStringList types = get_inputtypes(sourceid);
358 if (types.empty())
359 return true;
360 auto unscannable = [](const auto & type) { return CardUtil::IsUnscanable(type); };
361 return std::ranges::all_of(std::as_const(types), unscannable);
362}
363
365{
366 std::vector<uint> inputs = CardUtil::GetInputIDs(sourceid);
367 auto ccpresent = [](uint input)
369 CardUtil::GetRawInputType(input) == "HDHOMERUN"; };
370 return std::ranges::any_of(std::as_const(inputs), ccpresent);
371}
372
374{
376 query.prepare("SELECT sourceid FROM videosource");
377
378 if (!query.exec() || !query.isActive())
379 {
380 MythDB::DBError("SourceUtil::IsAnySourceScanable", query);
381 return false;
382 }
383
384 while (query.next())
385 {
386 if (!IsUnscanable(query.value(0).toUInt()))
387 return true;
388 }
389
390 return false;
391}
392
394{
396 query.prepare(
397 "SELECT sourceid FROM videosource WHERE sourceid = :SOURCEID");
398 query.bindValue(":SOURCEID", sourceid);
399
400 if (!query.exec() || !query.isActive())
401 {
402 MythDB::DBError("SourceUtil::IsSourceIDValid", query);
403 return false;
404 }
405
406 return query.next();
407}
408
409bool SourceUtil::UpdateChannelsFromListings(uint sourceid, const QString& inputtype, bool wait)
410{
411 if (wait)
412 {
413 QString cmd = GetAppBinDir() +
414 "mythfilldatabase";
415 QStringList args;
416 args.append("--only-update-channels");
417
418 if (sourceid)
419 {
420 args.append(QString("--sourceid"));
421 args.append(QString::number(sourceid));
422 }
423 if (!inputtype.isEmpty())
424 {
425 args.append(QString("--cardtype"));
426 args.append(inputtype);
427 }
428
430 getchan.Run();
431 getchan.Wait();
432 }
433 else
434 {
435 QString cmd = GetAppBinDir() +
436 "mythfilldatabase --only-update-channels";
437 if (sourceid)
438 cmd += QString(" --sourceid %1").arg(sourceid);
439 if (!inputtype.isEmpty())
440 cmd += QString(" --cardtype %1").arg(inputtype);
441 cmd += logPropagateArgs;
442
443 myth_system(cmd);
444 }
445
446 return true;
447}
448
449bool SourceUtil::UpdateSource( uint sourceid, const QString& sourcename,
450 const QString& grabber, const QString& userid,
451 const QString& freqtable, const QString& lineupid,
452 const QString& password, bool useeit,
453 const QString& configpath, int nitid,
454 uint bouquetid, uint regionid, uint scanfrequency,
455 uint lcnoffset)
456{
458
459 query.prepare("UPDATE videosource SET name = :NAME, xmltvgrabber = :XMLTVGRABBER, "
460 "userid = :USERID, freqtable = :FREQTABLE, lineupid = :LINEUPID,"
461 "password = :PASSWORD, useeit = :USEEIT, configpath = :CONFIGPATH, "
462 "dvb_nit_id = :NITID, bouquet_id = :BOUQUETID, region_id = :REGIONID, "
463 "scanfrequency = :SCANFREQUENCY, lcnoffset = :LCNOFFSET "
464 "WHERE sourceid = :SOURCEID");
465
466 query.bindValue(":NAME", sourcename);
467 query.bindValue(":XMLTVGRABBER", grabber);
468 query.bindValue(":USERID", userid);
469 query.bindValue(":FREQTABLE", freqtable);
470 query.bindValue(":LINEUPID", lineupid);
471 query.bindValue(":PASSWORD", password);
472 query.bindValue(":USEEIT", useeit);
473 query.bindValue(":CONFIGPATH", configpath.isEmpty() ? nullptr : configpath);
474 query.bindValue(":NITID", nitid);
475 query.bindValue(":BOUQUETID", bouquetid);
476 query.bindValue(":REGIONID", regionid);
477 query.bindValue(":SOURCEID", sourceid);
478 query.bindValue(":SCANFREQUENCY", scanfrequency);
479 query.bindValue(":LCNOFFSET", lcnoffset);
480
481 if (!query.exec() || !query.isActive())
482 {
483 MythDB::DBError("Updating Video Source", query);
484 return false;
485 }
486
487 return true;
488}
489
490int SourceUtil::CreateSource( const QString& sourcename,
491 const QString& grabber, const QString& userid,
492 const QString& freqtable, const QString& lineupid,
493 const QString& password, bool useeit,
494 const QString& configpath, int nitid,
495 uint bouquetid, uint regionid, uint scanfrequency,
496 uint lcnoffset)
497{
499
500 query.prepare("INSERT INTO videosource (name,xmltvgrabber,userid,freqtable,lineupid,"
501 "password,useeit,configpath,dvb_nit_id,bouquet_id,region_id, scanfrequency,"
502 "lcnoffset) "
503 "VALUES (:NAME, :XMLTVGRABBER, :USERID, :FREQTABLE, :LINEUPID, :PASSWORD, "
504 ":USEEIT, :CONFIGPATH, :NITID, :BOUQUETID, :REGIONID, :SCANFREQUENCY, "
505 ":LCNOFFSET)");
506
507 query.bindValue(":NAME", sourcename);
508 query.bindValue(":XMLTVGRABBER", grabber);
509 query.bindValue(":USERID", userid);
510 query.bindValue(":FREQTABLE", freqtable);
511 query.bindValue(":LINEUPID", lineupid);
512 query.bindValue(":PASSWORD", password);
513 query.bindValue(":USEEIT", useeit);
514 query.bindValue(":CONFIGPATH", configpath.isEmpty() ? nullptr : configpath);
515 query.bindValue(":NITID", nitid);
516 query.bindValue(":BOUQUETID", bouquetid);
517 query.bindValue(":REGIONID", regionid);
518 query.bindValue(":SCANFREQUENCY", scanfrequency);
519 query.bindValue(":LCNOFFSET", lcnoffset);
520
521 if (!query.exec() || !query.isActive())
522 {
523 MythDB::DBError("Adding Video Source", query);
524 return -1;
525 }
526
527 query.prepare("SELECT MAX(sourceid) FROM videosource");
528
529 if (!query.exec())
530 {
531 MythDB::DBError("CreateSource maxsource", query);
532 return -1;
533 }
534
535 int sourceid = -1; /* must be int not uint because of return type. */
536
537 if (query.next())
538 sourceid = query.value(0).toInt();
539
540 return sourceid;
541}
542
544{
546
547 // Delete the transports associated with the source
548 query.prepare("DELETE FROM dtv_multiplex "
549 "WHERE sourceid = :SOURCEID");
550 query.bindValue(":SOURCEID", sourceid);
551
552 if (!query.exec() || !query.isActive())
553 {
554 MythDB::DBError("Deleting transports", query);
555 return false;
556 }
557
558 // Delete the channels associated with the source
559 query.prepare("UPDATE channel "
560 "SET deleted = NOW(), mplexid = 0, sourceid = 0 "
561 "WHERE deleted IS NULL AND sourceid = :SOURCEID");
562 query.bindValue(":SOURCEID", sourceid);
563
564 if (!query.exec() || !query.isActive())
565 {
566 MythDB::DBError("Deleting Channels", query);
567 return false;
568 }
569
570 // Detach the inputs associated with the source
571 query.prepare("UPDATE capturecard "
572 "SET sourceid = 0 "
573 "WHERE sourceid = :SOURCEID");
574 query.bindValue(":SOURCEID", sourceid);
575
576 if (!query.exec() || !query.isActive())
577 {
578 MythDB::DBError("Disassociate source inputs", query);
579 return false;
580 }
581
582 // Delete all the saved channel scans for this source
584
585 // Delete the source itself
586 query.prepare("DELETE FROM videosource "
587 "WHERE sourceid = :SOURCEID");
588 query.bindValue(":SOURCEID", sourceid);
589
590 if (!query.exec() || !query.isActive())
591 {
592 MythDB::DBError("Deleting VideoSource", query);
593 return false;
594 }
595
596 return true;
597}
598
600{
602
603 // Detach all inputs from any source
604 query.prepare("UPDATE capturecard "
605 "SET sourceid = 0");
606 if (!query.exec() || !query.isActive())
607 {
608 MythDB::DBError("Deleting sources", query);
609 return false;
610 }
611
612 // Delete all channels
613 query.prepare("UPDATE channel "
614 "SET deleted = NOW(), mplexid = 0, sourceid = 0 "
615 "WHERE deleted IS NULL");
616
617 if (!query.exec() || !query.isActive())
618 {
619 MythDB::DBError("Deleting all Channels", query);
620 return false;
621 }
622
623 return (query.exec("TRUNCATE TABLE program") &&
624 query.exec("TRUNCATE TABLE videosource") &&
625 query.exec("TRUNCATE TABLE credits") &&
626 query.exec("TRUNCATE TABLE programrating") &&
627 query.exec("TRUNCATE TABLE programgenres") &&
628 query.exec("TRUNCATE TABLE dtv_multiplex") &&
629 query.exec("TRUNCATE TABLE diseqc_config") &&
630 query.exec("TRUNCATE TABLE diseqc_tree") &&
631 query.exec("TRUNCATE TABLE eit_cache") &&
632 query.exec("TRUNCATE TABLE channelgroup") &&
633 query.exec("TRUNCATE TABLE channelgroupnames"));
634}
static bool IsUnscanable(const QString &rawtype)
Definition: cardutil.h:160
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:294
static bool IsTuningAnalog(const QString &rawtype)
Definition: cardutil.h:200
static bool IsTuningVirtual(const QString &rawtype)
Definition: cardutil.h:207
static std::vector< uint > GetInputIDs(const QString &videodevice=QString(), const QString &rawtype=QString(), const QString &inputname=QString(), QString hostname=QString())
Returns all inputids of inputs that uses the specified videodevice if specified, and optionally rawty...
Definition: cardutil.cpp:1312
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
static bool IsTuningDigital(const QString &rawtype)
Definition: cardutil.h:192
static bool IsCableCardPresent(uint inputid, const QString &inputType)
Definition: cardutil.cpp:113
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:129
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:839
QVariant value(int i) const
Definition: mythdbcon.h:205
int size(void) const
Definition: mythdbcon.h:215
bool isActive(void) const
Definition: mythdbcon.h:216
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:620
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:890
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:814
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:552
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
uint Wait(std::chrono::seconds timeout=0s)
void Run(std::chrono::seconds timeout=0s)
Runs a command inside the /bin/sh shell. Returns immediately.
static void DeleteScansFromSource(uint sourceid)
Definition: scaninfo.cpp:259
static uint GetConnectionCount(uint sourceid)
Definition: sourceutil.cpp:241
static bool HasDigitalChannel(uint sourceid)
Definition: sourceutil.cpp:19
static bool GetListingsLoginData(uint sourceid, QString &grabber, QString &userid, QString &passwd, QString &lineupid)
Definition: sourceutil.cpp:181
static QString GetChannelSeparator(uint sourceid)
Definition: sourceutil.cpp:96
static QString GetSourceName(uint sourceid)
Definition: sourceutil.cpp:50
static uint GetSourceID(const QString &name)
Definition: sourceutil.cpp:73
static QString GetChannelFormat(uint sourceid)
Definition: sourceutil.cpp:140
static bool DeleteSource(uint sourceid)
Definition: sourceutil.cpp:543
static bool DeleteAllSources(void)
Definition: sourceutil.cpp:599
static std::vector< uint > GetMplexIDs(uint sourceid)
Definition: sourceutil.cpp:158
static bool IsEncoder(uint sourceid, bool strict=false)
Definition: sourceutil.cpp:319
static bool IsSourceIDValid(uint sourceid)
Definition: sourceutil.cpp:393
static bool IsCableCardPresent(uint sourceid)
Definition: sourceutil.cpp:364
static uint GetChannelCount(uint sourceid)
Definition: sourceutil.cpp:145
static bool IsUnscanable(uint sourceid)
Definition: sourceutil.cpp:355
static bool IsAnySourceScanable(void)
Definition: sourceutil.cpp:373
static int CreateSource(const QString &sourcename, const QString &grabber, const QString &userid, const QString &freqtable, const QString &lineupid, const QString &password, bool useeit, const QString &configpath, int nitid, uint bouquetid, uint regionid, uint scanfrequency, uint lcnoffset)
Definition: sourceutil.cpp:490
static bool UpdateSource(uint sourceid, const QString &sourcename, const QString &grabber, const QString &userid, const QString &freqtable, const QString &lineupid, const QString &password, bool useeit, const QString &configpath, int nitid, uint bouquetid, uint regionid, uint scanfrequency, uint lcnoffset)
Definition: sourceutil.cpp:449
static bool UpdateChannelsFromListings(uint sourceid, const QString &inputtype=QString(), bool wait=false)
Definition: sourceutil.cpp:409
static bool IsProperlyConnected(uint sourceid, bool strict=false)
Definition: sourceutil.cpp:247
unsigned int uint
Definition: compat.h:60
#define minor(X)
Definition: compat.h:58
static const struct wl_interface * types[]
static const iso6937table * d
QString logPropagateArgs
Definition: logging.cpp:87
QString GetAppBinDir(void)
Definition: mythdirs.cpp:279
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
@ kMSRunShell
run process through shell
Definition: mythsystem.h:43
@ kMSAutoCleanup
automatically delete if backgrounded
Definition: mythsystem.h:45
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
static QStringList get_inputtypes(uint sourceid)
Definition: sourceutil.cpp:209