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  QStringList::const_iterator it = types.begin();
216  for (; it != types.end(); ++it)
217  {
218  counts[*it]++;
219 
220  counts[CardUtil::IsEncoder(*it) ? "ENCODER" : "NOT_ENCODER"]++;
221  counts[CardUtil::IsUnscanable(*it) ? "NO_SCAN" : "SCAN"]++;
222 
223  if (CardUtil::IsTuningAnalog(*it))
224  counts["ANALOG_TUNING"]++;
225  else if (CardUtil::IsTuningDigital(*it))
226  counts["DIGITAL_TUNING"]++;
227  else if (CardUtil::IsTuningVirtual(*it))
228  counts["VIRTUAL_TUNING"]++;
229  }
230 
231  bool tune_mismatch =
232  (counts["ANALOG_TUNING"] && counts["DIGITAL_TUNING"]) ||
233  (counts["VIRTUAL_TUNING"] && counts["DIGITAL_TUNING"]);
234  bool enc_mismatch = counts["ENCODER"] && counts["NOT_ENCODER"];
235  bool scan_mismatch = counts["SCAN"] && counts["NO_SCAN"];
236 
237  if (tune_mismatch)
238  {
239  uint a = counts["ANALOG_TUNERS"];
240  uint d = counts["DIGITAL_TUNERS"];
241  uint v = counts["VIRTUAL_TUNERS"];
242  LOG(VB_GENERAL, LOG_NOTICE,
243  QString("SourceUtil::IsProperlyConnected(): ") +
244  QString("Connected to %1 analog, %2 digital and %3 virtual "
245  "tuners\n\t\t\t").arg(a).arg(d).arg(v) +
246  QString("Can not mix digital with other tuning information."));
247  }
248 
249  if (enc_mismatch)
250  {
251  uint a = counts["ENCODER"];
252  uint d = counts["NOT_ENCODER"];
253  LOG(VB_GENERAL, LOG_NOTICE,
254  QString("SourceUtil::IsProperlyConnected(): ") +
255  QString("Source ID %1 ").arg(sourceid) +
256  QString("appears to be connected\n\t\t\tto %1 encoder%2, ")
257  .arg(a).arg((1 == a) ? "":"s") +
258  QString("and %1 non-encoder%2. ")
259  .arg(d).arg((1 == d) ? "":"s") +
260  QString("This is probably a bad idea."));
261  }
262 
263  if (scan_mismatch)
264  {
265  uint a = counts["SCAN"];
266  uint d = counts["NO_SCAN"];
267  LOG(VB_GENERAL, LOG_NOTICE,
268  QString("SourceUtil::IsProperlyConnected(): ") +
269  QString("Source ID %1 ").arg(sourceid) +
270  QString("appears to be connected\n\t\t\t"
271  "to %1 scanable input%2, ")
272  .arg(a).arg((1 == a) ? "":"s") +
273  QString("and %1 non-scanable input%2. ")
274  .arg(d).arg((1 == d) ? "":"s") +
275  QString("This may be a problem."));
276  }
277 
278  if (!strict)
279  return !tune_mismatch;
280 
281  return !tune_mismatch && !enc_mismatch && !scan_mismatch;
282 }
283 
284 bool SourceUtil::IsEncoder(uint sourceid, bool strict)
285 {
286  bool encoder = true;
287 
288  QStringList types = get_inputtypes(sourceid);
289  QStringList::const_iterator it = types.begin();
290  for (; it != types.end(); ++it)
291  encoder &= CardUtil::IsEncoder(*it);
292 
293  // Source is connected, go by input types for type determination
294  if (!types.empty())
295  return encoder;
296 
297  // Try looking at channels if source is not connected,
298  MSqlQuery query(MSqlQuery::InitCon());
299  query.prepare(
300  "SELECT atsc_minor_chan, serviceid "
301  "FROM channel "
302  "WHERE deleted IS NULL AND "
303  " sourceid = :SOURCEID");
304  query.bindValue(":SOURCEID", sourceid);
305 
306  bool has_any_chan = false;
307  if (!query.exec() || !query.isActive())
308  MythDB::DBError("SourceUtil::IsEncoder", query);
309  else
310  {
311  while (query.next())
312  {
313  encoder &= !query.value(0).toBool() && !query.value(1).toBool();
314  has_any_chan = true;
315  }
316  }
317 
318  return (strict && !has_any_chan) ? false: encoder;
319 }
320 
322 {
323  bool unscanable = true;
324  QStringList types = get_inputtypes(sourceid);
325  QStringList::const_iterator it = types.begin();
326  for (; it != types.end(); ++it)
327  unscanable &= CardUtil::IsUnscanable(*it);
328 
329  return types.empty() || unscanable;
330 }
331 
333 {
334  bool ccpresent = false;
335  vector<uint> inputs = CardUtil::GetInputIDs(sourceid);
336  auto it = inputs.begin();
337  for (; it != inputs.end(); ++it)
338  {
340  || CardUtil::GetRawInputType(*it) == "HDHOMERUN")
341  ccpresent = true;
342  }
343 
344  return ccpresent;
345 }
346 
348 {
349  MSqlQuery query(MSqlQuery::InitCon());
350  query.prepare("SELECT sourceid FROM videosource");
351 
352  if (!query.exec() || !query.isActive())
353  {
354  MythDB::DBError("SourceUtil::IsAnySourceScanable", query);
355  return false;
356  }
357 
358  while (query.next())
359  {
360  if (!IsUnscanable(query.value(0).toUInt()))
361  return true;
362  }
363 
364  return false;
365 }
366 
368 {
369  MSqlQuery query(MSqlQuery::InitCon());
370  query.prepare(
371  "SELECT sourceid FROM videosource WHERE sourceid = :SOURCEID");
372  query.bindValue(":SOURCEID", sourceid);
373 
374  if (!query.exec() || !query.isActive())
375  {
376  MythDB::DBError("SourceUtil::IsSourceIDValid", query);
377  return false;
378  }
379 
380  return query.next();
381 }
382 
383 bool SourceUtil::UpdateChannelsFromListings(uint sourceid, const QString& inputtype, bool wait)
384 {
385  if (wait)
386  {
387  QString cmd = GetAppBinDir() +
388  "mythfilldatabase";
389  QStringList args;
390  args.append("--only-update-channels");
391 
392  if (sourceid)
393  {
394  args.append(QString("--sourceid"));
395  args.append(QString::number(sourceid));
396  }
397  if (!inputtype.isEmpty())
398  {
399  args.append(QString("--cardtype"));
400  args.append(inputtype);
401  }
402 
404  getchan.Run();
405  getchan.Wait();
406  }
407  else
408  {
409  QString cmd = GetAppBinDir() +
410  "mythfilldatabase --only-update-channels";
411  if (sourceid)
412  cmd += QString(" --sourceid %1").arg(sourceid);
413  if (!inputtype.isEmpty())
414  cmd += QString(" --cardtype %1").arg(inputtype);
415  cmd += logPropagateArgs;
416 
417  myth_system(cmd);
418  }
419 
420  return true;
421 }
422 
423 bool SourceUtil::UpdateSource( uint sourceid, const QString& sourcename,
424  const QString& grabber, const QString& userid,
425  const QString& freqtable, const QString& lineupid,
426  const QString& password, bool useeit,
427  const QString& configpath, int nitid,
428  uint bouquetid, uint regionid, uint scanfrequency)
429 {
430  MSqlQuery query(MSqlQuery::InitCon());
431 
432  query.prepare("UPDATE videosource SET name = :NAME, xmltvgrabber = :XMLTVGRABBER, "
433  "userid = :USERID, freqtable = :FREQTABLE, lineupid = :LINEUPID,"
434  "password = :PASSWORD, useeit = :USEEIT, configpath = :CONFIGPATH, "
435  "dvb_nit_id = :NITID, bouquet_id = :BOUQUETID, region_id = :REGIONID, "
436  "scanfrequency = :SCANFREQUENCY "
437  "WHERE sourceid = :SOURCEID");
438 
439  query.bindValue(":NAME", sourcename);
440  query.bindValue(":XMLTVGRABBER", grabber);
441  query.bindValue(":USERID", userid);
442  query.bindValue(":FREQTABLE", freqtable);
443  query.bindValue(":LINEUPID", lineupid);
444  query.bindValue(":PASSWORD", password);
445  query.bindValue(":USEEIT", useeit);
446  query.bindValue(":CONFIGPATH", configpath.isEmpty() ? nullptr : configpath);
447  query.bindValue(":NITID", nitid);
448  query.bindValue(":BOUQUETID", bouquetid);
449  query.bindValue(":REGIONID", regionid);
450  query.bindValue(":SOURCEID", sourceid);
451  query.bindValue(":SCANFREQUENCY", scanfrequency);
452 
453  if (!query.exec() || !query.isActive())
454  {
455  MythDB::DBError("Updating Video Source", query);
456  return false;
457  }
458 
459  return true;
460 }
461 
462 int SourceUtil::CreateSource( const QString& sourcename,
463  const QString& grabber, const QString& userid,
464  const QString& freqtable, const QString& lineupid,
465  const QString& password, bool useeit,
466  const QString& configpath, int nitid,
467  uint bouquetid, uint regionid, uint scanfrequency)
468 {
469  MSqlQuery query(MSqlQuery::InitCon());
470 
471  query.prepare("INSERT INTO videosource (name,xmltvgrabber,userid,freqtable,lineupid,"
472  "password,useeit,configpath,dvb_nit_id,bouquet_id,region_id, scanfrequency) "
473  "VALUES (:NAME, :XMLTVGRABBER, :USERID, :FREQTABLE, :LINEUPID, :PASSWORD, "
474  ":USEEIT, :CONFIGPATH, :NITID, :BOUQUETID, :REGIONID, :SCANFREQUENCY)");
475 
476  query.bindValue(":NAME", sourcename);
477  query.bindValue(":XMLTVGRABBER", grabber);
478  query.bindValue(":USERID", userid);
479  query.bindValue(":FREQTABLE", freqtable);
480  query.bindValue(":LINEUPID", lineupid);
481  query.bindValue(":PASSWORD", password);
482  query.bindValue(":USEEIT", useeit);
483  query.bindValue(":CONFIGPATH", configpath.isEmpty() ? nullptr : configpath);
484  query.bindValue(":NITID", nitid);
485  query.bindValue(":BOUQUETID", bouquetid);
486  query.bindValue(":REGIONID", regionid);
487  query.bindValue(":SCANFREQUENCY", scanfrequency);
488 
489  if (!query.exec() || !query.isActive())
490  {
491  MythDB::DBError("Adding Video Source", query);
492  return -1;
493  }
494 
495  query.prepare("SELECT MAX(sourceid) FROM videosource");
496 
497  if (!query.exec())
498  {
499  MythDB::DBError("CreateSource maxsource", query);
500  return -1;
501  }
502 
503  int sourceid = -1; /* must be int not uint because of return type. */
504 
505  if (query.next())
506  sourceid = query.value(0).toInt();
507 
508  return sourceid;
509 }
510 
512 {
513  MSqlQuery query(MSqlQuery::InitCon());
514 
515  // Delete the channels associated with the source
516  query.prepare("UPDATE channel "
517  "SET deleted = NOW() "
518  "WHERE deleted IS NULL AND "
519  " sourceid = :SOURCEID");
520  query.bindValue(":SOURCEID", sourceid);
521 
522  if (!query.exec() || !query.isActive())
523  {
524  MythDB::DBError("Deleting Channels", query);
525  return false;
526  }
527 
528  // Detach the inputs associated with the source
529  query.prepare("UPDATE capturecard "
530  "SET sourceid = 0 "
531  "WHERE sourceid = :SOURCEID");
532  query.bindValue(":SOURCEID", sourceid);
533 
534  if (!query.exec() || !query.isActive())
535  {
536  MythDB::DBError("Deleting inputs", query);
537  return false;
538  }
539 
540  // Delete the source itself
541  query.prepare("DELETE FROM videosource "
542  "WHERE sourceid = :SOURCEID");
543  query.bindValue(":SOURCEID", sourceid);
544 
545  if (!query.exec() || !query.isActive())
546  {
547  MythDB::DBError("Deleting VideoSource", query);
548  return false;
549  }
550 
551  return true;
552 }
553 
555 {
556  MSqlQuery query(MSqlQuery::InitCon());
557 
558  // Detach all inputs from any source
559  query.prepare("UPDATE capturecard "
560  "SET sourceid = 0");
561  if (!query.exec() || !query.isActive())
562  {
563  MythDB::DBError("Deleting sources", query);
564  return false;
565  }
566 
567  return (query.exec("TRUNCATE TABLE channel") &&
568  query.exec("TRUNCATE TABLE program") &&
569  query.exec("TRUNCATE TABLE videosource") &&
570  query.exec("TRUNCATE TABLE credits") &&
571  query.exec("TRUNCATE TABLE programrating") &&
572  query.exec("TRUNCATE TABLE programgenres") &&
573  query.exec("TRUNCATE TABLE dtv_multiplex") &&
574  query.exec("TRUNCATE TABLE diseqc_config") &&
575  query.exec("TRUNCATE TABLE diseqc_tree") &&
576  query.exec("TRUNCATE TABLE eit_cache") &&
577  query.exec("TRUNCATE TABLE channelgroup") &&
578  query.exec("TRUNCATE TABLE channelgroupnames"));
579 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
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:863
static bool IsCableCardPresent(uint sourceid)
Definition: sourceutil.cpp:332
static uint GetChannelCount(uint sourceid)
Definition: sourceutil.cpp:111
static bool DeleteSource(uint sourceid)
Definition: sourceutil.cpp:511
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:347
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:321
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:367
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:383
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:1277
static bool IsTuningDigital(const QString &rawtype)
Definition: cardutil.h:174
static MSqlQueryInfo InitCon(ConnectionReuse=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:807
#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:423
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:462
static bool DeleteAllSources(void)
Definition: sourceutil.cpp:554
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:284
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