MythTV  master
channeldata.cpp
Go to the documentation of this file.
1 // C++ headers
2 #include <iostream>
3 #include <cstdlib>
4 
5 // Qt headers
6 #include <QRegExp>
7 #include <QDir>
8 #include <QFile>
9 
10 // libmythbase headers
11 #include "mythdownloadmanager.h"
12 
13 // libmyth headers
14 #include "mythlogging.h"
15 #include "mythdb.h"
16 #include "mythmiscutil.h"
17 
18 // libmythtv headers
19 #include "channelutil.h"
20 #include "frequencytables.h"
21 #include "cardutil.h"
22 #include "sourceutil.h"
23 
24 // filldata headers
25 #include "channeldata.h"
26 #include "fillutil.h"
27 
28 static void get_atsc_stuff(const QString& channum, int sourceid, int freqid,
29  int &major, int &minor, long long &freq)
30 {
31  major = freqid;
32  minor = 0;
33 
34  int chansep = channum.indexOf(QRegExp("\\D"));
35  if (chansep < 0)
36  return;
37 
38  major = channum.left(chansep).toInt();
39  minor = channum.right(channum.length() - (chansep + 1)).toInt();
40 
41  freq = get_center_frequency("atsc", "vsb8", "us", freqid);
42 
43  // Check if this is connected to an HDTV card.
45  query.prepare(
46  "SELECT cardtype "
47  "FROM capturecard "
48  "WHERE sourceid = :SOURCEID");
49  query.bindValue(":SOURCEID", sourceid);
50 
51  if (query.exec() && query.isActive() && query.next() &&
52  query.value(0).toString() == "HDTV")
53  {
54  freq -= 1750000; // convert to visual carrier freq.
55  }
56 }
57 
59 {
60  bool insert_channels = m_channelUpdates;
61  if (!insert_channels)
62  {
63  bool isEncoder = false;
64  bool isUnscanable = false;
65  bool isCableCard = SourceUtil::IsCableCardPresent(sourceid);
66  if (m_cardType.isEmpty())
67  {
68  isEncoder = SourceUtil::IsEncoder(sourceid);
69  isUnscanable = SourceUtil::IsUnscanable(sourceid);
70  }
71  else
72  {
73  isEncoder = CardUtil::IsEncoder(m_cardType);
74  isUnscanable = CardUtil::IsUnscanable(m_cardType);
75  }
76  insert_channels = (isCableCard || isEncoder || isUnscanable);
77  }
78 
79  return insert_channels;
80 }
81 
82 
84  ChannelInfoList::iterator chaninfo, unsigned int chanid)
85 {
86  (*chaninfo).m_name = getResponse(QObject::tr("Choose a channel name (any string, "
87  "long version) "),(*chaninfo).m_name);
88  (*chaninfo).m_callsign = getResponse(QObject::tr("Choose a channel callsign (any string, "
89  "short version) "),(*chaninfo).m_callsign);
90 
91  if (m_channelPreset)
92  {
93  (*chaninfo).m_channum = getResponse(QObject::tr("Choose a channel preset (0..999) "),
94  (*chaninfo).m_channum);
95  (*chaninfo).m_freqid = getResponse(QObject::tr("Choose a frequency id "),
96  (*chaninfo).m_freqid);
97  }
98  else
99  {
100  (*chaninfo).m_channum = getResponse(QObject::tr("Choose a channel number "),
101  (*chaninfo).m_channum);
102  (*chaninfo).m_freqid = (*chaninfo).m_channum;
103  }
104 
105  (*chaninfo).m_finetune = getResponse(QObject::tr("Choose a channel fine tune offset "),
106  QString::number((*chaninfo).m_finetune)).toInt();
107 
108  (*chaninfo).m_tvformat = getResponse(QObject::tr("Choose a TV format "
109  "(PAL/SECAM/NTSC/ATSC/Default) "),
110  (*chaninfo).m_tvformat);
111 
112  (*chaninfo).m_icon = getResponse(QObject::tr("Choose a channel icon image "
113  "(relative path to icon storage group) "),
114  (*chaninfo).m_icon);
115 
116  return(chanid);
117 }
118 
119 QString ChannelData::normalizeChannelKey(const QString &chanName)
120 {
121  QString result = chanName;
122 
123  // Lowercase
124  result = result.toLower();
125  // Strip all whitespace
126  result = result.replace(" ", "");
127 
128  return result;
129 }
130 
132 {
133  ChannelList retList;
134 
135  uint avail = 0;
139  sourceId);
140 
141  auto it = channelList.begin();
142  for ( ; it != channelList.end(); ++it)
143  {
144  QString chanName = (*it).m_name;
145  QString key = normalizeChannelKey(chanName);
146  retList.insert(key, *it);
147  }
148 
149  return retList;
150 }
151 
153  ChannelList existingChannels)
154 {
155  ChannelList::iterator it;
156  for (it = existingChannels.begin(); it != existingChannels.end(); ++it)
157  {
158  if ((*it).m_xmltvid == chanInfo.m_xmltvid)
159  return (*it);
160  }
161 
162  QString searchKey = normalizeChannelKey(chanInfo.m_name);
163  ChannelInfo existChan = existingChannels.value(searchKey);
164 
165  if (existChan.m_chanid < 1)
166  {
167  // Check if it is ATSC
168  int chansep = chanInfo.m_channum.indexOf(QRegExp("\\D"));
169  if (chansep > 0)
170  {
171  // Populate xmltvid for scanned ATSC channels
172  uint major = chanInfo.m_channum.left(chansep).toInt();
173  uint minor = chanInfo.m_channum.right
174  (chanInfo.m_channum.length() - (chansep + 1)).toInt();
175 
176  for (it = existingChannels.begin();
177  it != existingChannels.end(); ++it)
178  {
179  if ((*it).m_atsc_major_chan == major &&
180  (*it).m_atsc_minor_chan == minor)
181  return (*it);
182  }
183  }
184  }
185 
186  return existChan;
187 }
188 
190 {
191  if (m_guideDataOnly)
192  {
193  LOG(VB_GENERAL, LOG_NOTICE, "Skipping Channel Updates");
194  return;
195  }
196 
197  ChannelList existingChannels = channelList(id);
198  QString fileprefix = SetupIconCacheDirectory();
199 
200  QDir::setCurrent(fileprefix);
201 
202  fileprefix += "/";
203 
204  bool insertChan = insert_chan(id); // unscannable source
205 
206  auto i = chanlist->begin();
207  for (; i != chanlist->end(); ++i)
208  {
209  if ((*i).m_xmltvid.isEmpty())
210  continue;
211 
212  QString localfile;
213 
214  if (!(*i).m_icon.isEmpty())
215  {
216  QDir remotefile = QDir((*i).m_icon);
217  QString filename = remotefile.dirName();
218 
219  localfile = fileprefix + filename;
220  QFile actualfile(localfile);
221  if (!actualfile.exists() &&
222  !GetMythDownloadManager()->download((*i).m_icon, localfile))
223  {
224  LOG(VB_GENERAL, LOG_ERR,
225  QString("Failed to fetch icon from '%1'")
226  .arg((*i).m_icon));
227  }
228 
229  localfile = filename;
230  }
231 
232  MSqlQuery query(MSqlQuery::InitCon());
233 
234  if (!(*i).m_old_xmltvid.isEmpty())
235  {
236  query.prepare(
237  "SELECT xmltvid "
238  "FROM channel "
239  "WHERE xmltvid = :XMLTVID");
240  query.bindValue(":XMLTVID", (*i).m_old_xmltvid);
241 
242  if (!query.exec())
243  {
244  MythDB::DBError("xmltvid conversion 1", query);
245  }
246  else if (query.next())
247  {
248  LOG(VB_GENERAL, LOG_INFO,
249  QString("Converting old xmltvid (%1) to new (%2)")
250  .arg((*i).m_old_xmltvid).arg((*i).m_xmltvid));
251 
252  query.prepare("UPDATE channel "
253  "SET xmltvid = :NEWXMLTVID"
254  "WHERE xmltvid = :OLDXMLTVID");
255  query.bindValue(":NEWXMLTVID", (*i).m_xmltvid);
256  query.bindValue(":OLDXMLTVID", (*i).m_old_xmltvid);
257 
258  if (!query.exec())
259  {
260  MythDB::DBError("xmltvid conversion 2", query);
261  }
262  }
263  }
264 
265  ChannelInfo dbChan = FindMatchingChannel(*i, existingChannels);
266  if (dbChan.m_chanid > 0) // Channel exists, updating
267  {
268  LOG(VB_XMLTV, LOG_NOTICE,
269  QString("Match found for xmltvid %1 to channel %2 (%3)")
270  .arg((*i).m_xmltvid).arg(dbChan.m_name).arg(dbChan.m_chanid));
271  if (m_interactive)
272  {
273 
274  cout << "### " << endl;
275  cout << "### Existing channel found" << endl;
276  cout << "### " << endl;
277  cout << "### xmltvid = "
278  << (*i).m_xmltvid.toLocal8Bit().constData() << endl;
279  cout << "### chanid = "
280  << dbChan.m_chanid << endl;
281  cout << "### name = "
282  << dbChan.m_name.toLocal8Bit().constData() << endl;
283  cout << "### callsign = "
284  << dbChan.m_callsign.toLocal8Bit().constData() << endl;
285  cout << "### channum = "
286  << dbChan.m_channum.toLocal8Bit().constData() << endl;
287  if (m_channelPreset)
288  {
289  cout << "### freqid = "
290  << dbChan.m_freqid.toLocal8Bit().constData() << endl;
291  }
292  cout << "### finetune = "
293  << dbChan.m_finetune << endl;
294  cout << "### tvformat = "
295  << dbChan.m_tvformat.toLocal8Bit().constData() << endl;
296  cout << "### icon = "
297  << dbChan.m_icon.toLocal8Bit().constData() << endl;
298  cout << "### " << endl;
299 
300  // The only thing the xmltv data supplies here is the icon
301  (*i).m_name = dbChan.m_name;
302  (*i).m_callsign = dbChan.m_callsign;
303  (*i).m_channum = dbChan.m_channum;
304  (*i).m_finetune = dbChan.m_finetune;
305  (*i).m_freqid = dbChan.m_freqid;
306  (*i).m_tvformat = dbChan.m_tvformat;
307 
309 
310  if ((*i).m_callsign.isEmpty())
311  (*i).m_callsign = dbChan.m_name;
312 
313  if (dbChan.m_name != (*i).m_name ||
314  dbChan.m_callsign != (*i).m_callsign ||
315  dbChan.m_channum != (*i).m_channum ||
316  dbChan.m_finetune != (*i).m_finetune ||
317  dbChan.m_freqid != (*i).m_freqid ||
318  dbChan.m_icon != localfile ||
319  dbChan.m_tvformat != (*i).m_tvformat)
320  {
321  MSqlQuery subquery(MSqlQuery::InitCon());
322  subquery.prepare("UPDATE channel SET chanid = :CHANID, "
323  "name = :NAME, callsign = :CALLSIGN, "
324  "channum = :CHANNUM, finetune = :FINE, "
325  "icon = :ICON, freqid = :FREQID, "
326  "tvformat = :TVFORMAT "
327  " WHERE xmltvid = :XMLTVID "
328  "AND sourceid = :SOURCEID;");
329  subquery.bindValue(":CHANID", dbChan.m_chanid);
330  subquery.bindValue(":NAME", (*i).m_name);
331  subquery.bindValue(":CALLSIGN", (*i).m_callsign);
332  subquery.bindValue(":CHANNUM", (*i).m_channum);
333  subquery.bindValue(":FINE", (*i).m_finetune);
334  subquery.bindValue(":ICON", localfile);
335  subquery.bindValue(":FREQID", (*i).m_freqid);
336  subquery.bindValue(":TVFORMAT", (*i).m_tvformat);
337  subquery.bindValue(":XMLTVID", (*i).m_xmltvid);
338  subquery.bindValue(":SOURCEID", id);
339 
340  if (!subquery.exec())
341  {
342  MythDB::DBError("update failed", subquery);
343  }
344  else
345  {
346  cout << "### " << endl;
347  cout << "### Change performed" << endl;
348  cout << "### " << endl;
349  }
350  }
351  else
352  {
353  cout << "### " << endl;
354  cout << "### Nothing changed" << endl;
355  cout << "### " << endl;
356  }
357  }
358  else if ((dbChan.m_icon != localfile) ||
359  (dbChan.m_xmltvid != (*i).m_xmltvid))
360  {
361  LOG(VB_XMLTV, LOG_NOTICE, QString("Updating channel %1 (%2)")
362  .arg(dbChan.m_name).arg(dbChan.m_chanid));
363 
364  if (localfile.isEmpty())
365  localfile = dbChan.m_icon;
366 
367  if (dbChan.m_xmltvid != (*i).m_xmltvid)
368  {
369  MSqlQuery subquery(MSqlQuery::InitCon());
370 
371  subquery.prepare("UPDATE channel SET icon = :ICON "
372  ", xmltvid:= :XMLTVID WHERE "
373  "chanid = :CHANID;");
374  subquery.bindValue(":ICON", localfile);
375  subquery.bindValue(":XMLTVID", (*i).m_xmltvid);
376  subquery.bindValue(":CHANID", dbChan.m_chanid);
377 
378  if (!subquery.exec())
379  MythDB::DBError("Channel icon change", subquery);
380  }
381  else
382  {
383  MSqlQuery subquery(MSqlQuery::InitCon());
384  subquery.prepare("UPDATE channel SET icon = :ICON WHERE "
385  "chanid = :CHANID;");
386  subquery.bindValue(":ICON", localfile);
387  subquery.bindValue(":CHANID", dbChan.m_chanid);
388 
389  if (!subquery.exec())
390  MythDB::DBError("Channel icon change", subquery);
391  }
392 
393  }
394  }
395  else if (insertChan) // Only insert channels for non-scannable sources
396  {
397  int major = 0;
398  int minor = 0;
399  long long freq = 0;
400  get_atsc_stuff((*i).m_channum, id, (*i).m_freqid.toInt(), major, minor, freq);
401 
402  if (m_interactive && ((minor == 0) || (freq > 0)))
403  {
404  cout << "### " << endl;
405  cout << "### New channel found" << endl;
406  cout << "### " << endl;
407  cout << "### name = "
408  << (*i).m_name.toLocal8Bit().constData() << endl;
409  cout << "### callsign = "
410  << (*i).m_callsign.toLocal8Bit().constData() << endl;
411  cout << "### channum = "
412  << (*i).m_channum.toLocal8Bit().constData() << endl;
413  if (m_channelPreset)
414  {
415  cout << "### freqid = "
416  << (*i).m_freqid.toLocal8Bit().constData() << endl;
417  }
418  cout << "### finetune = "
419  << (*i).m_finetune << endl;
420  cout << "### tvformat = "
421  << (*i).m_tvformat.toLocal8Bit().constData() << endl;
422  cout << "### icon = "
423  << localfile.toLocal8Bit().constData() << endl;
424  cout << "### " << endl;
425 
426  uint chanid = promptForChannelUpdates(i,0);
427 
428  if ((*i).m_callsign.isEmpty())
429  (*i).m_callsign = QString::number(chanid);
430 
431  int mplexid = 0;
432  if ((chanid > 0) && (minor > 0))
433  mplexid = ChannelUtil::CreateMultiplex(id, "atsc",
434  freq, "8vsb");
435 
436  if (((mplexid > 0) || ((minor == 0) && (chanid > 0))) &&
438  mplexid, id, chanid,
439  (*i).m_callsign, (*i).m_name, (*i).m_channum,
440  0 /*service id*/, major, minor,
441  false /*use on air guide*/, false /*hidden*/,
442  false /*hidden in guide*/,
443  (*i).m_freqid, localfile, (*i).m_tvformat,
444  (*i).m_xmltvid))
445  {
446  cout << "### " << endl;
447  cout << "### Channel inserted" << endl;
448  cout << "### " << endl;
449  }
450  else
451  {
452  cout << "### " << endl;
453  cout << "### Channel skipped" << endl;
454  cout << "### " << endl;
455  }
456  }
457  else if ((minor == 0) || (freq > 0))
458  {
459  // We only do this if we are not asked to skip it with the
460  // --update-guide-only (formerly --update) flag.
461  int mplexid = 0;
462  int chanid = 0;
463  if (minor > 0)
464  {
466  id, "atsc", freq, "8vsb");
467  }
468 
469  if ((mplexid > 0) || (minor == 0))
470  chanid = ChannelUtil::CreateChanID(id, (*i).m_channum);
471 
472  if ((*i).m_callsign.isEmpty())
473  {
474  QStringList words = (*i).m_name.simplified().toUpper()
475  .split(" ");
476  QString callsign = "";
477  QString w1 = !words.empty() ? words[0] : QString();
478  QString w2 = words.size() > 1 ? words[1] : QString();
479  if (w1.isEmpty())
480  callsign = QString::number(chanid);
481  else if (w2.isEmpty())
482  callsign = words[0].left(5);
483  else
484  {
485  callsign = w1.left(w2.length() == 1 ? 4:3);
486  callsign += w2.left(5 - callsign.length());
487  }
488  (*i).m_callsign = callsign;
489  }
490 
491  if (chanid > 0)
492  {
493  QString cstr = (*i).m_channum;
494  if(m_channelPreset && cstr.isEmpty())
495  cstr = QString::number(chanid % 1000);
496 
497  bool retval = ChannelUtil::CreateChannel(
498  mplexid, id,
499  chanid,
500  (*i).m_callsign,
501  (*i).m_name, cstr,
502  0 /*service id*/,
503  major, minor,
504  false /*use on air guide*/,
505  false /*hidden*/,
506  false /*hidden in guide*/,
507  (*i).m_freqid,
508  localfile,
509  (*i).m_tvformat,
510  (*i).m_xmltvid
511  );
512  if (!retval)
513  cout << "Channel " << chanid << " creation failed"
514  << endl;
515  }
516  }
517  }
518  }
519 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
static MSqlQueryInfo ChannelCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:584
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
static ChannelInfoList LoadChannels(uint startIndex, uint count, uint &totalAvailable, bool ignoreHidden=true, OrderBy orderBy=kChanOrderByChanNum, GroupBy groupBy=kChanGroupByChanid, uint sourceID=0, uint channelGroupID=0, bool liveTVOnly=false, const QString &callsign="", const QString &channum="", bool ignoreUntunable=true)
Load channels from database into a list of ChannelInfo objects.
static bool IsCableCardPresent(uint sourceid)
Definition: sourceutil.cpp:332
QString m_name
Definition: channelinfo.h:82
static bool CreateChannel(uint db_mplexid, uint db_sourceid, uint new_channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, bool hidden, bool hidden_in_guide, const QString &freqid, const QString &icon=QString(), QString format="Default", const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
QString m_cardType
Definition: channeldata.h:33
static bool IsUnscanable(const QString &rawtype)
Definition: cardutil.h:144
uint32_t freq[4]
Definition: element.c:44
QString m_xmltvid
Definition: channelinfo.h:87
bool m_channelUpdates
Definition: channeldata.h:31
static bool IsUnscanable(uint sourceid)
Definition: sourceutil.cpp:321
static ChannelList channelList(int sourceId)
QString m_tvformat
Definition: channelinfo.h:95
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:121
QString m_icon
Definition: channelinfo.h:83
QVariant value(int i) const
Definition: mythdbcon.h:198
QString SetupIconCacheDirectory(void)
Definition: fillutil.cpp:35
unsigned int promptForChannelUpdates(ChannelInfoList::iterator chaninfo, unsigned int chanid)
Definition: channeldata.cpp:83
static void get_atsc_stuff(const QString &channum, int sourceid, int freqid, int &major, int &minor, long long &freq)
Definition: channeldata.cpp:28
bool m_guideDataOnly
Definition: channeldata.h:29
long long get_center_frequency(const QString &format, const QString &modulation, const QString &country, int freqid)
QString m_freqid
Definition: channelinfo.h:77
int m_finetune
Definition: channelinfo.h:85
#define minor(X)
Definition: compat.h:138
bool isActive(void) const
Definition: mythdbcon.h:204
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
bool m_channelPreset
Definition: channeldata.h:30
bool download(const QString &url, const QString &dest, const bool reload=false)
Downloads a URL to a file in blocking mode.
bool m_interactive
Definition: channeldata.h:28
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
QString getResponse(const QString &query, const QString &def)
In an interactive shell, prompt the user to input a string.
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
QString m_channum
Definition: channelinfo.h:76
void handleChannels(int id, ChannelInfoList *chanlist)
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
bool insert_chan(uint sourceid)
Definition: channeldata.cpp:58
static ChannelInfo FindMatchingChannel(const ChannelInfo &chanInfo, ChannelList existingChannels)
QList< ChannelListItem > ChannelList
QString m_callsign
Definition: channelinfo.h:81
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 void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
static QString normalizeChannelKey(const QString &chanName)
static uint CreateMultiplex(int sourceid, const QString &sistandard, uint64_t frequency, const QString &modulation, int transport_id=-1, int network_id=-1)
vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:121
uint m_chanid
Definition: channelinfo.h:75