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(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.
44  MSqlQuery query(MSqlQuery::DDCon());
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, isUnscanable;
64  bool isCableCard = SourceUtil::IsCableCardPresent(sourceid);
65  if (m_cardType.isEmpty())
66  {
67  isEncoder = SourceUtil::IsEncoder(sourceid);
68  isUnscanable = SourceUtil::IsUnscanable(sourceid);
69  }
70  else
71  {
72  isEncoder = CardUtil::IsEncoder(m_cardType);
73  isUnscanable = CardUtil::IsUnscanable(m_cardType);
74  }
75  insert_channels = (isCableCard || isEncoder || isUnscanable) &&
77  }
78 
79  return insert_channels;
80 }
81 
82 
84  ChannelInfoList::iterator chaninfo, unsigned int chanid)
85 {
86  (*chaninfo).name = getResponse(QObject::tr("Choose a channel name (any string, "
87  "long version) "),(*chaninfo).name);
88  (*chaninfo).callsign = getResponse(QObject::tr("Choose a channel callsign (any string, "
89  "short version) "),(*chaninfo).callsign);
90 
91  if (m_channelPreset)
92  {
93  (*chaninfo).channum = getResponse(QObject::tr("Choose a channel preset (0..999) "),
94  (*chaninfo).channum);
95  (*chaninfo).freqid = getResponse(QObject::tr("Choose a frequency id "),
96  (*chaninfo).freqid);
97  }
98  else
99  {
100  (*chaninfo).channum = getResponse(QObject::tr("Choose a channel number "),
101  (*chaninfo).channum);
102  (*chaninfo).freqid = (*chaninfo).channum;
103  }
104 
105  (*chaninfo).finetune = getResponse(QObject::tr("Choose a channel fine tune offset "),
106  QString::number((*chaninfo).finetune)).toInt();
107 
108  (*chaninfo).tvformat = getResponse(QObject::tr("Choose a TV format "
109  "(PAL/SECAM/NTSC/ATSC/Default) "),
110  (*chaninfo).tvformat);
111 
112  (*chaninfo).icon = getResponse(QObject::tr("Choose a channel icon image "
113  "(relative path to icon storage group) "),
114  (*chaninfo).icon);
115 
116  return(chanid);
117 }
118 
119 QString ChannelData::normalizeChannelKey(const QString &chanName) const
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  ChannelInfoList::iterator it = channelList.begin();
142  for ( ; it != channelList.end(); ++it)
143  {
144  QString chanName = (*it).name;
145  QString key = normalizeChannelKey(chanName);
146  retList.insert(key, *it);
147  }
148 
149  return retList;
150 }
151 
153  ChannelList existingChannels) const
154 {
155  ChannelList::iterator it;
156  for (it = existingChannels.begin(); it != existingChannels.end(); ++it)
157  {
158  if ((*it).xmltvid == chanInfo.xmltvid)
159  return (*it);
160  }
161 
162  QString searchKey = normalizeChannelKey(chanInfo.name);
163  ChannelInfo existChan = existingChannels.value(searchKey);
164 
165  if (existChan.chanid < 1)
166  {
167  // Check if it is ATSC
168  int chansep = chanInfo.channum.indexOf(QRegExp("\\D"));
169  if (chansep > 0)
170  {
171  // Populate xmltvid for scanned ATSC channels
172  uint major = chanInfo.channum.left(chansep).toInt();
173  uint minor = chanInfo.channum.right
174  (chanInfo.channum.length() - (chansep + 1)).toInt();
175 
176  for (it = existingChannels.begin();
177  it != existingChannels.end(); ++it)
178  {
179  if ((*it).atsc_major_chan == major &&
180  (*it).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  ChannelInfoList::iterator i = chanlist->begin();
207  for (; i != chanlist->end(); ++i)
208  {
209  if ((*i).xmltvid.isEmpty())
210  continue;
211 
212  QString localfile;
213 
214  if (!(*i).icon.isEmpty())
215  {
216  QDir remotefile = QDir((*i).icon);
217  QString filename = remotefile.dirName();
218 
219  localfile = fileprefix + filename;
220  QFile actualfile(localfile);
221  if (!actualfile.exists() &&
222  !GetMythDownloadManager()->download((*i).icon, localfile))
223  {
224  LOG(VB_GENERAL, LOG_ERR,
225  QString("Failed to fetch icon from '%1'")
226  .arg((*i).icon));
227  }
228 
229  localfile = filename;
230  }
231 
232  MSqlQuery query(MSqlQuery::InitCon());
233 
234  if (!(*i).old_xmltvid.isEmpty())
235  {
236  query.prepare(
237  "SELECT xmltvid "
238  "FROM channel "
239  "WHERE xmltvid = :XMLTVID");
240  query.bindValue(":XMLTVID", (*i).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).old_xmltvid).arg((*i).xmltvid));
251 
252  query.prepare("UPDATE channel "
253  "SET xmltvid = :NEWXMLTVID"
254  "WHERE xmltvid = :OLDXMLTVID");
255  query.bindValue(":NEWXMLTVID", (*i).xmltvid);
256  query.bindValue(":OLDXMLTVID", (*i).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.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).xmltvid).arg(dbChan.name).arg(dbChan.chanid));
271  if (m_interactive)
272  {
273 
274  cout << "### " << endl;
275  cout << "### Existing channel found" << endl;
276  cout << "### " << endl;
277  cout << "### xmltvid = "
278  << (*i).xmltvid.toLocal8Bit().constData() << endl;
279  cout << "### chanid = "
280  << dbChan.chanid << endl;
281  cout << "### name = "
282  << dbChan.name.toLocal8Bit().constData() << endl;
283  cout << "### callsign = "
284  << dbChan.callsign.toLocal8Bit().constData() << endl;
285  cout << "### channum = "
286  << dbChan.channum.toLocal8Bit().constData() << endl;
287  if (m_channelPreset)
288  {
289  cout << "### freqid = "
290  << dbChan.freqid.toLocal8Bit().constData() << endl;
291  }
292  cout << "### finetune = "
293  << dbChan.finetune << endl;
294  cout << "### tvformat = "
295  << dbChan.tvformat.toLocal8Bit().constData() << endl;
296  cout << "### icon = "
297  << dbChan.icon.toLocal8Bit().constData() << endl;
298  cout << "### " << endl;
299 
300  // The only thing the xmltv data supplies here is the icon
301  (*i).name = dbChan.name;
302  (*i).callsign = dbChan.callsign;
303  (*i).channum = dbChan.channum;
304  (*i).finetune = dbChan.finetune;
305  (*i).freqid = dbChan.freqid;
306  (*i).tvformat = dbChan.tvformat;
307 
308  promptForChannelUpdates(i, dbChan.chanid);
309 
310  if ((*i).callsign.isEmpty())
311  (*i).callsign = dbChan.name;
312 
313  if (dbChan.name != (*i).name ||
314  dbChan.callsign != (*i).callsign ||
315  dbChan.channum != (*i).channum ||
316  dbChan.finetune != (*i).finetune ||
317  dbChan.freqid != (*i).freqid ||
318  dbChan.icon != localfile ||
319  dbChan.tvformat != (*i).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.chanid);
330  subquery.bindValue(":NAME", (*i).name);
331  subquery.bindValue(":CALLSIGN", (*i).callsign);
332  subquery.bindValue(":CHANNUM", (*i).channum);
333  subquery.bindValue(":FINE", (*i).finetune);
334  subquery.bindValue(":ICON", localfile);
335  subquery.bindValue(":FREQID", (*i).freqid);
336  subquery.bindValue(":TVFORMAT", (*i).tvformat);
337  subquery.bindValue(":XMLTVID", (*i).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.icon != localfile) ||
359  (dbChan.xmltvid != (*i).xmltvid))
360  {
361  LOG(VB_XMLTV, LOG_NOTICE, QString("Updating channel %1 (%2)")
362  .arg(dbChan.name).arg(dbChan.chanid));
363 
364  if (localfile.isEmpty())
365  localfile = dbChan.icon;
366 
367  if (dbChan.xmltvid != (*i).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).xmltvid);
376  subquery.bindValue(":CHANID", dbChan.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.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, minor = 0;
398  long long freq = 0;
399  get_atsc_stuff((*i).channum, id, (*i).freqid.toInt(), major, minor, freq);
400 
401  if (m_interactive && ((minor == 0) || (freq > 0)))
402  {
403  cout << "### " << endl;
404  cout << "### New channel found" << endl;
405  cout << "### " << endl;
406  cout << "### name = "
407  << (*i).name.toLocal8Bit().constData() << endl;
408  cout << "### callsign = "
409  << (*i).callsign.toLocal8Bit().constData() << endl;
410  cout << "### channum = "
411  << (*i).channum.toLocal8Bit().constData() << endl;
412  if (m_channelPreset)
413  {
414  cout << "### freqid = "
415  << (*i).freqid.toLocal8Bit().constData() << endl;
416  }
417  cout << "### finetune = "
418  << (*i).finetune << endl;
419  cout << "### tvformat = "
420  << (*i).tvformat.toLocal8Bit().constData() << endl;
421  cout << "### icon = "
422  << localfile.toLocal8Bit().constData() << endl;
423  cout << "### " << endl;
424 
425  uint chanid = promptForChannelUpdates(i,0);
426 
427  if ((*i).callsign.isEmpty())
428  (*i).callsign = QString::number(chanid);
429 
430  int mplexid = 0;
431  if ((chanid > 0) && (minor > 0))
432  mplexid = ChannelUtil::CreateMultiplex(id, "atsc",
433  freq, "8vsb");
434 
435  if (((mplexid > 0) || ((minor == 0) && (chanid > 0))) &&
437  mplexid, id, chanid,
438  (*i).callsign, (*i).name, (*i).channum,
439  0 /*service id*/, major, minor,
440  false /*use on air guide*/, false /*hidden*/,
441  false /*hidden in guide*/,
442  (*i).freqid, localfile, (*i).tvformat,
443  (*i).xmltvid))
444  {
445  cout << "### " << endl;
446  cout << "### Channel inserted" << endl;
447  cout << "### " << endl;
448  }
449  else
450  {
451  cout << "### " << endl;
452  cout << "### Channel skipped" << endl;
453  cout << "### " << endl;
454  }
455  }
456  else if ((minor == 0) || (freq > 0))
457  {
458  // We only do this if we are not asked to skip it with the
459  // --update-guide-only (formerly --update) flag.
460  int mplexid = 0, chanid = 0;
461  if (minor > 0)
462  {
464  id, "atsc", freq, "8vsb");
465  }
466 
467  if ((mplexid > 0) || (minor == 0))
468  chanid = ChannelUtil::CreateChanID(id, (*i).channum);
469 
470  if ((*i).callsign.isEmpty())
471  {
472  QStringList words = (*i).name.simplified().toUpper()
473  .split(" ");
474  QString callsign = "";
475  QString w1 = words.size() > 0 ? words[0] : QString();
476  QString w2 = words.size() > 1 ? words[1] : QString();
477  if (w1.isEmpty())
478  callsign = QString::number(chanid);
479  else if (w2.isEmpty())
480  callsign = words[0].left(5);
481  else
482  {
483  callsign = w1.left(w2.length() == 1 ? 4:3);
484  callsign += w2.left(5 - callsign.length());
485  }
486  (*i).callsign = callsign;
487  }
488 
489  if (chanid > 0)
490  {
491  QString cstr = (*i).channum;
492  if(m_channelPreset && cstr.isEmpty())
493  cstr = QString::number(chanid % 1000);
494 
495  bool retval = ChannelUtil::CreateChannel(
496  mplexid, id,
497  chanid,
498  (*i).callsign,
499  (*i).name, cstr,
500  0 /*service id*/,
501  major, minor,
502  false /*use on air guide*/,
503  false /*hidden*/,
504  false /*hidden in guide*/,
505  (*i).freqid,
506  localfile,
507  (*i).tvformat,
508  (*i).xmltvid
509  );
510  if (!retval)
511  cout << "Channel " << chanid << " creation failed"
512  << endl;
513  }
514  }
515  }
516  }
517 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:794
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, QString icon=QString(), QString format="Default", QString xmltvid=QString(), QString default_authority=QString())
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:875
static bool IsCableCardPresent(uint sourceid)
Definition: sourceutil.cpp:328
QString channum
Definition: channelinfo.h:76
bool m_removeNewChannels
Definition: channeldata.h:35
QString tvformat
Definition: channelinfo.h:94
QString freqid
Definition: channelinfo.h:77
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
QString m_cardType
Definition: channeldata.h:37
static bool IsUnscanable(const QString &rawtype)
Definition: cardutil.h:143
unsigned int uint
Definition: compat.h:140
uint32_t freq[4]
Definition: element.c:44
bool m_channelUpdates
Definition: channeldata.h:34
static bool IsUnscanable(uint sourceid)
Definition: sourceutil.cpp:317
QString normalizeChannelKey(const QString &chanName) const
ChannelList channelList(int sourceId)
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:120
QVariant value(int i) const
Definition: mythdbcon.h:182
QString SetupIconCacheDirectory(void)
Definition: fillutil.cpp:35
QString callsign
Definition: channelinfo.h:80
unsigned int promptForChannelUpdates(ChannelInfoList::iterator chaninfo, unsigned int chanid)
Definition: channeldata.cpp:83
bool m_guideDataOnly
Definition: channeldata.h:32
#define minor(X)
Definition: compat.h:138
bool isActive(void) const
Definition: mythdbcon.h:188
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
bool m_channelPreset
Definition: channeldata.h:33
static MSqlQueryInfo DDCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:596
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:31
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, QString callsign="", QString channum="")
Load channels from database into a list of ChannelInfo objects.
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:547
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.
void handleChannels(int id, ChannelInfoList *chanlist)
static void get_atsc_stuff(QString channum, int sourceid, int freqid, int &major, int &minor, long long &freq)
Definition: channeldata.cpp:28
ChannelInfo FindMatchingChannel(const ChannelInfo &chanInfo, ChannelList existingChannels) const
vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:119
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:819
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
bool insert_chan(uint sourceid)
Definition: channeldata.cpp:58
QString icon
Definition: channelinfo.h:82
static uint CreateMultiplex(int sourceid, QString sistandard, uint64_t frequency, QString modulation, int transport_id=-1, int network_id=-1)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:615
static bool IsEncoder(uint sourceid, bool strict=false)
Definition: sourceutil.cpp:281
long long get_center_frequency(QString format, QString modulation, QString country, int freqid)
QString name
Definition: channelinfo.h:81
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
QString xmltvid
Definition: channelinfo.h:86
QList< ChannelListItem > ChannelList