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