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 (channum.right(2).startsWith("0"))
114 counts["0"]++;
115 else
116 counts[(where < 0) ? "" : QString(channum.at(where))]++;
117 }
118 QString sep = "_";
119 uint max = counts["_"];
120 static const std::array<const QString,5> s_spacers { "", "-", "#", ".", "0" };
121 for (const auto & spacer : s_spacers)
122 {
123 if (counts[spacer] > max)
124 {
125 max = counts[spacer];
126 sep = spacer;
127 }
128 }
129 return sep;
130 }
131 return "_"; // default on failure
132}
133
135{
136 return QString("%1") + GetChannelSeparator(sourceid) + QString("%2");
137}
138
140{
142 query.prepare("SELECT sum(1) "
143 "FROM channel "
144 "WHERE deleted IS NULL AND "
145 " sourceid = :SOURCEID");
146 query.bindValue(":SOURCEID", sourceid);
147 if (query.exec() && query.isActive() && query.next())
148 return query.value(0).toUInt();
149 return 0;
150}
151
152std::vector<uint> SourceUtil::GetMplexIDs(uint sourceid)
153{
155
156 query.prepare(
157 "SELECT mplexid "
158 "FROM dtv_multiplex "
159 "WHERE sourceid = :SOURCEID");
160 query.bindValue(":SOURCEID", sourceid);
161
162 std::vector<uint> list;
163 if (!query.exec())
164 {
165 MythDB::DBError("SourceUtil::GetMplexIDs()", query);
166 return list;
167 }
168
169 while (query.next())
170 list.push_back(query.value(0).toUInt());
171
172 return list;
173}
174
176 QString &grabber, QString &userid,
177 QString &passwd, QString &lineupid)
178{
180 query.prepare(
181 "SELECT xmltvgrabber, userid, password, lineupid "
182 "FROM videosource "
183 "WHERE sourceid = :SOURCEID");
184 query.bindValue(":SOURCEID", sourceid);
185
186 if (!query.exec() || !query.isActive())
187 {
188 MythDB::DBError("SourceUtil::GetListingsLoginData()", query);
189 return false;
190 }
191
192 if (!query.next())
193 return false;
194
195 grabber = query.value(0).toString();
196 userid = query.value(1).toString();
197 passwd = query.value(2).toString();
198 lineupid = query.value(3).toString();
199
200 return true;
201}
202
203static QStringList get_inputtypes(uint sourceid)
204{
205 QStringList list;
206
208 query.prepare(
209 "SELECT cardtype, inputname "
210 "FROM capturecard "
211 "WHERE capturecard.sourceid = :SOURCEID");
212 query.bindValue(":SOURCEID", sourceid);
213
214 if (!query.exec() || !query.isActive())
215 {
216 MythDB::DBError("get_inputtypes()", query);
217 }
218 else
219 {
220 while (query.next())
221 {
223 QString inputtype = query.value(0).toString().toUpper();
224 QString inputname = query.value(1).toString().toUpper();
225 inputtype = ((inputtype == "DVB") && (!inputname.startsWith("DVB"))) ?
226 "V4L" : inputtype;
228 list += inputtype;
229 }
230 }
231
232 return list;
233}
234
236{
237 QStringList types = get_inputtypes(sourceid);
238 return types.size();
239}
240
241bool SourceUtil::IsProperlyConnected(uint sourceid, bool strict)
242{
243 QStringList types = get_inputtypes(sourceid);
244 QMap<QString,uint> counts;
245 for (const auto & type : std::as_const(types))
246 {
247 counts[type]++;
248
249 counts[CardUtil::IsEncoder(type) ? "ENCODER" : "NOT_ENCODER"]++;
250 counts[CardUtil::IsUnscanable(type) ? "NO_SCAN" : "SCAN"]++;
251
253 counts["ANALOG_TUNING"]++;
255 counts["DIGITAL_TUNING"]++;
257 counts["VIRTUAL_TUNING"]++;
258 }
259
260 bool tune_mismatch =
261 ((counts["ANALOG_TUNING"] != 0U) && (counts["DIGITAL_TUNING"] != 0U)) ||
262 ((counts["VIRTUAL_TUNING"] != 0U) && (counts["DIGITAL_TUNING"] != 0U));
263 bool enc_mismatch = (counts["ENCODER"] != 0U) && (counts["NOT_ENCODER"] != 0U);
264 bool scan_mismatch = (counts["SCAN"] != 0U) && (counts["NO_SCAN"] != 0U);
265
266 if (tune_mismatch)
267 {
268 uint a = counts["ANALOG_TUNERS"];
269 uint d = counts["DIGITAL_TUNERS"];
270 uint v = counts["VIRTUAL_TUNERS"];
271 LOG(VB_GENERAL, LOG_NOTICE,
272 QString("SourceUtil::IsProperlyConnected(): ") +
273 QString("Connected to %1 analog, %2 digital and %3 virtual "
274 "tuners\n\t\t\t").arg(a).arg(d).arg(v) +
275 QString("Can not mix digital with other tuning information."));
276 }
277
278 if (enc_mismatch)
279 {
280 uint a = counts["ENCODER"];
281 uint d = counts["NOT_ENCODER"];
282 LOG(VB_GENERAL, LOG_NOTICE,
283 QString("SourceUtil::IsProperlyConnected(): ") +
284 QString("Source ID %1 ").arg(sourceid) +
285 QString("appears to be connected\n\t\t\tto %1 encoder%2, ")
286 .arg(a).arg((1 == a) ? "":"s") +
287 QString("and %1 non-encoder%2. ")
288 .arg(d).arg((1 == d) ? "":"s") +
289 QString("This is probably a bad idea."));
290 }
291
292 if (scan_mismatch)
293 {
294 uint a = counts["SCAN"];
295 uint d = counts["NO_SCAN"];
296 LOG(VB_GENERAL, LOG_NOTICE,
297 QString("SourceUtil::IsProperlyConnected(): ") +
298 QString("Source ID %1 ").arg(sourceid) +
299 QString("appears to be connected\n\t\t\t"
300 "to %1 scanable input%2, ")
301 .arg(a).arg((1 == a) ? "":"s") +
302 QString("and %1 non-scanable input%2. ")
303 .arg(d).arg((1 == d) ? "":"s") +
304 QString("This may be a problem."));
305 }
306
307 if (!strict)
308 return !tune_mismatch;
309
310 return !tune_mismatch && !enc_mismatch && !scan_mismatch;
311}
312
313bool SourceUtil::IsEncoder(uint sourceid, bool strict)
314{
315 QStringList types = get_inputtypes(sourceid);
316 auto isencoder = [](const auto & type){ return CardUtil::IsEncoder(type); };
317 bool encoder = std::ranges::all_of(std::as_const(types), isencoder);
318
319 // Source is connected, go by input types for type determination
320 if (!types.empty())
321 return encoder;
322
323 // Try looking at channels if source is not connected,
325 query.prepare(
326 "SELECT atsc_minor_chan, serviceid "
327 "FROM channel "
328 "WHERE deleted IS NULL AND "
329 " sourceid = :SOURCEID");
330 query.bindValue(":SOURCEID", sourceid);
331
332 bool has_any_chan = false;
333 if (!query.exec() || !query.isActive())
334 {
335 MythDB::DBError("SourceUtil::IsEncoder", query);
336 }
337 else
338 {
339 while (query.next())
340 {
341 encoder &= !query.value(0).toBool() && !query.value(1).toBool();
342 has_any_chan = true;
343 }
344 }
345
346 return (strict && !has_any_chan) ? false: encoder;
347}
348
350{
351 QStringList types = get_inputtypes(sourceid);
352 if (types.empty())
353 return true;
354 auto unscannable = [](const auto & type) { return CardUtil::IsUnscanable(type); };
355 return std::ranges::all_of(std::as_const(types), unscannable);
356}
357
359{
360 std::vector<uint> inputs = CardUtil::GetInputIDs(sourceid);
361 auto ccpresent = [](uint input)
363 CardUtil::GetRawInputType(input) == "HDHOMERUN"; };
364 return std::ranges::any_of(std::as_const(inputs), ccpresent);
365}
366
368{
370 query.prepare("SELECT sourceid FROM videosource");
371
372 if (!query.exec() || !query.isActive())
373 {
374 MythDB::DBError("SourceUtil::IsAnySourceScanable", query);
375 return false;
376 }
377
378 while (query.next())
379 {
380 if (!IsUnscanable(query.value(0).toUInt()))
381 return true;
382 }
383
384 return false;
385}
386
388{
390 query.prepare(
391 "SELECT sourceid FROM videosource WHERE sourceid = :SOURCEID");
392 query.bindValue(":SOURCEID", sourceid);
393
394 if (!query.exec() || !query.isActive())
395 {
396 MythDB::DBError("SourceUtil::IsSourceIDValid", query);
397 return false;
398 }
399
400 return query.next();
401}
402
403bool SourceUtil::UpdateChannelsFromListings(uint sourceid, const QString& inputtype, bool wait)
404{
405 if (wait)
406 {
407 QString cmd = GetAppBinDir() +
408 "mythfilldatabase";
409 QStringList args;
410 args.append("--only-update-channels");
411
412 if (sourceid)
413 {
414 args.append(QString("--sourceid"));
415 args.append(QString::number(sourceid));
416 }
417 if (!inputtype.isEmpty())
418 {
419 args.append(QString("--cardtype"));
420 args.append(inputtype);
421 }
422
424 getchan.Run();
425 getchan.Wait();
426 }
427 else
428 {
429 QString cmd = GetAppBinDir() +
430 "mythfilldatabase --only-update-channels";
431 if (sourceid)
432 cmd += QString(" --sourceid %1").arg(sourceid);
433 if (!inputtype.isEmpty())
434 cmd += QString(" --cardtype %1").arg(inputtype);
435 cmd += logPropagateArgs;
436
437 myth_system(cmd);
438 }
439
440 return true;
441}
442
443bool SourceUtil::UpdateSource( uint sourceid, const QString& sourcename,
444 const QString& grabber, const QString& userid,
445 const QString& freqtable, const QString& lineupid,
446 const QString& password, bool useeit,
447 const QString& configpath, int nitid,
448 uint bouquetid, uint regionid, uint scanfrequency,
449 uint lcnoffset)
450{
452
453 query.prepare("UPDATE videosource SET name = :NAME, xmltvgrabber = :XMLTVGRABBER, "
454 "userid = :USERID, freqtable = :FREQTABLE, lineupid = :LINEUPID,"
455 "password = :PASSWORD, useeit = :USEEIT, configpath = :CONFIGPATH, "
456 "dvb_nit_id = :NITID, bouquet_id = :BOUQUETID, region_id = :REGIONID, "
457 "scanfrequency = :SCANFREQUENCY, lcnoffset = :LCNOFFSET "
458 "WHERE sourceid = :SOURCEID");
459
460 query.bindValue(":NAME", sourcename);
461 query.bindValue(":XMLTVGRABBER", grabber);
462 query.bindValue(":USERID", userid);
463 query.bindValue(":FREQTABLE", freqtable);
464 query.bindValue(":LINEUPID", lineupid);
465 query.bindValue(":PASSWORD", password);
466 query.bindValue(":USEEIT", useeit);
467 query.bindValue(":CONFIGPATH", configpath.isEmpty() ? nullptr : configpath);
468 query.bindValue(":NITID", nitid);
469 query.bindValue(":BOUQUETID", bouquetid);
470 query.bindValue(":REGIONID", regionid);
471 query.bindValue(":SOURCEID", sourceid);
472 query.bindValue(":SCANFREQUENCY", scanfrequency);
473 query.bindValue(":LCNOFFSET", lcnoffset);
474
475 if (!query.exec() || !query.isActive())
476 {
477 MythDB::DBError("Updating Video Source", query);
478 return false;
479 }
480
481 return true;
482}
483
484int SourceUtil::CreateSource( const QString& sourcename,
485 const QString& grabber, const QString& userid,
486 const QString& freqtable, const QString& lineupid,
487 const QString& password, bool useeit,
488 const QString& configpath, int nitid,
489 uint bouquetid, uint regionid, uint scanfrequency,
490 uint lcnoffset)
491{
493
494 query.prepare("INSERT INTO videosource (name,xmltvgrabber,userid,freqtable,lineupid,"
495 "password,useeit,configpath,dvb_nit_id,bouquet_id,region_id, scanfrequency,"
496 "lcnoffset) "
497 "VALUES (:NAME, :XMLTVGRABBER, :USERID, :FREQTABLE, :LINEUPID, :PASSWORD, "
498 ":USEEIT, :CONFIGPATH, :NITID, :BOUQUETID, :REGIONID, :SCANFREQUENCY, "
499 ":LCNOFFSET)");
500
501 query.bindValue(":NAME", sourcename);
502 query.bindValue(":XMLTVGRABBER", grabber);
503 query.bindValue(":USERID", userid);
504 query.bindValue(":FREQTABLE", freqtable);
505 query.bindValue(":LINEUPID", lineupid);
506 query.bindValue(":PASSWORD", password);
507 query.bindValue(":USEEIT", useeit);
508 query.bindValue(":CONFIGPATH", configpath.isEmpty() ? nullptr : configpath);
509 query.bindValue(":NITID", nitid);
510 query.bindValue(":BOUQUETID", bouquetid);
511 query.bindValue(":REGIONID", regionid);
512 query.bindValue(":SCANFREQUENCY", scanfrequency);
513 query.bindValue(":LCNOFFSET", lcnoffset);
514
515 if (!query.exec() || !query.isActive())
516 {
517 MythDB::DBError("Adding Video Source", query);
518 return -1;
519 }
520
521 query.prepare("SELECT MAX(sourceid) FROM videosource");
522
523 if (!query.exec())
524 {
525 MythDB::DBError("CreateSource maxsource", query);
526 return -1;
527 }
528
529 int sourceid = -1; /* must be int not uint because of return type. */
530
531 if (query.next())
532 sourceid = query.value(0).toInt();
533
534 return sourceid;
535}
536
538{
540
541 // Delete the transports associated with the source
542 query.prepare("DELETE FROM dtv_multiplex "
543 "WHERE sourceid = :SOURCEID");
544 query.bindValue(":SOURCEID", sourceid);
545
546 if (!query.exec() || !query.isActive())
547 {
548 MythDB::DBError("Deleting transports", query);
549 return false;
550 }
551
552 // Delete the channels associated with the source
553 query.prepare("UPDATE channel "
554 "SET deleted = NOW(), mplexid = 0, sourceid = 0 "
555 "WHERE deleted IS NULL AND sourceid = :SOURCEID");
556 query.bindValue(":SOURCEID", sourceid);
557
558 if (!query.exec() || !query.isActive())
559 {
560 MythDB::DBError("Deleting Channels", query);
561 return false;
562 }
563
564 // Detach the inputs associated with the source
565 query.prepare("UPDATE capturecard "
566 "SET sourceid = 0 "
567 "WHERE sourceid = :SOURCEID");
568 query.bindValue(":SOURCEID", sourceid);
569
570 if (!query.exec() || !query.isActive())
571 {
572 MythDB::DBError("Disassociate source inputs", query);
573 return false;
574 }
575
576 // Delete all the saved channel scans for this source
578
579 // Delete the source itself
580 query.prepare("DELETE FROM videosource "
581 "WHERE sourceid = :SOURCEID");
582 query.bindValue(":SOURCEID", sourceid);
583
584 if (!query.exec() || !query.isActive())
585 {
586 MythDB::DBError("Deleting VideoSource", query);
587 return false;
588 }
589
590 return true;
591}
592
594{
596
597 // Detach all inputs from any source
598 query.prepare("UPDATE capturecard "
599 "SET sourceid = 0");
600 if (!query.exec() || !query.isActive())
601 {
602 MythDB::DBError("Deleting sources", query);
603 return false;
604 }
605
606 // Delete all channels
607 query.prepare("UPDATE channel "
608 "SET deleted = NOW(), mplexid = 0, sourceid = 0 "
609 "WHERE deleted IS NULL");
610
611 if (!query.exec() || !query.isActive())
612 {
613 MythDB::DBError("Deleting all Channels", query);
614 return false;
615 }
616
617 return (query.exec("TRUNCATE TABLE program") &&
618 query.exec("TRUNCATE TABLE videosource") &&
619 query.exec("TRUNCATE TABLE credits") &&
620 query.exec("TRUNCATE TABLE programrating") &&
621 query.exec("TRUNCATE TABLE programgenres") &&
622 query.exec("TRUNCATE TABLE dtv_multiplex") &&
623 query.exec("TRUNCATE TABLE diseqc_config") &&
624 query.exec("TRUNCATE TABLE diseqc_tree") &&
625 query.exec("TRUNCATE TABLE eit_cache") &&
626 query.exec("TRUNCATE TABLE channelgroup") &&
627 query.exec("TRUNCATE TABLE channelgroupnames"));
628}
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:1311
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:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
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:235
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:175
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:134
static bool DeleteSource(uint sourceid)
Definition: sourceutil.cpp:537
static bool DeleteAllSources(void)
Definition: sourceutil.cpp:593
static std::vector< uint > GetMplexIDs(uint sourceid)
Definition: sourceutil.cpp:152
static bool IsEncoder(uint sourceid, bool strict=false)
Definition: sourceutil.cpp:313
static bool IsSourceIDValid(uint sourceid)
Definition: sourceutil.cpp:387
static bool IsCableCardPresent(uint sourceid)
Definition: sourceutil.cpp:358
static uint GetChannelCount(uint sourceid)
Definition: sourceutil.cpp:139
static bool IsUnscanable(uint sourceid)
Definition: sourceutil.cpp:349
static bool IsAnySourceScanable(void)
Definition: sourceutil.cpp:367
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:484
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:443
static bool UpdateChannelsFromListings(uint sourceid, const QString &inputtype=QString(), bool wait=false)
Definition: sourceutil.cpp:403
static bool IsProperlyConnected(uint sourceid, bool strict=false)
Definition: sourceutil.cpp:241
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:86
QString GetAppBinDir(void)
Definition: mythdirs.cpp:282
#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:203