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