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, 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);
76  }
77 
78  return insert_channels;
79 }
80 
81 
83  ChannelInfoList::iterator chaninfo, unsigned int chanid)
84 {
85  (*chaninfo).m_name = getResponse(QObject::tr("Choose a channel name (any string, "
86  "long version) "),(*chaninfo).m_name);
87  (*chaninfo).m_callsign = getResponse(QObject::tr("Choose a channel callsign (any string, "
88  "short version) "),(*chaninfo).m_callsign);
89 
90  if (m_channelPreset)
91  {
92  (*chaninfo).m_channum = getResponse(QObject::tr("Choose a channel preset (0..999) "),
93  (*chaninfo).m_channum);
94  (*chaninfo).m_freqid = getResponse(QObject::tr("Choose a frequency id "),
95  (*chaninfo).m_freqid);
96  }
97  else
98  {
99  (*chaninfo).m_channum = getResponse(QObject::tr("Choose a channel number "),
100  (*chaninfo).m_channum);
101  (*chaninfo).m_freqid = (*chaninfo).m_channum;
102  }
103 
104  (*chaninfo).m_finetune = getResponse(QObject::tr("Choose a channel fine tune offset "),
105  QString::number((*chaninfo).m_finetune)).toInt();
106 
107  (*chaninfo).m_tvformat = getResponse(QObject::tr("Choose a TV format "
108  "(PAL/SECAM/NTSC/ATSC/Default) "),
109  (*chaninfo).m_tvformat);
110 
111  (*chaninfo).m_icon = getResponse(QObject::tr("Choose a channel icon image "
112  "(relative path to icon storage group) "),
113  (*chaninfo).m_icon);
114 
115  return(chanid);
116 }
117 
118 QString ChannelData::normalizeChannelKey(const QString &chanName) const
119 {
120  QString result = chanName;
121 
122  // Lowercase
123  result = result.toLower();
124  // Strip all whitespace
125  result = result.replace(" ", "");
126 
127  return result;
128 }
129 
131 {
132  ChannelList retList;
133 
134  uint avail = 0;
138  sourceId);
139 
140  ChannelInfoList::iterator it = channelList.begin();
141  for ( ; it != channelList.end(); ++it)
142  {
143  QString chanName = (*it).m_name;
144  QString key = normalizeChannelKey(chanName);
145  retList.insert(key, *it);
146  }
147 
148  return retList;
149 }
150 
152  ChannelList existingChannels) const
153 {
154  ChannelList::iterator it;
155  for (it = existingChannels.begin(); it != existingChannels.end(); ++it)
156  {
157  if ((*it).m_xmltvid == chanInfo.m_xmltvid)
158  return (*it);
159  }
160 
161  QString searchKey = normalizeChannelKey(chanInfo.m_name);
162  ChannelInfo existChan = existingChannels.value(searchKey);
163 
164  if (existChan.m_chanid < 1)
165  {
166  // Check if it is ATSC
167  int chansep = chanInfo.m_channum.indexOf(QRegExp("\\D"));
168  if (chansep > 0)
169  {
170  // Populate xmltvid for scanned ATSC channels
171  uint major = chanInfo.m_channum.left(chansep).toInt();
172  uint minor = chanInfo.m_channum.right
173  (chanInfo.m_channum.length() - (chansep + 1)).toInt();
174 
175  for (it = existingChannels.begin();
176  it != existingChannels.end(); ++it)
177  {
178  if ((*it).m_atsc_major_chan == major &&
179  (*it).m_atsc_minor_chan == minor)
180  return (*it);
181  }
182  }
183  }
184 
185  return existChan;
186 }
187 
189 {
190  if (m_guideDataOnly)
191  {
192  LOG(VB_GENERAL, LOG_NOTICE, "Skipping Channel Updates");
193  return;
194  }
195 
196  ChannelList existingChannels = channelList(id);
197  QString fileprefix = SetupIconCacheDirectory();
198 
199  QDir::setCurrent(fileprefix);
200 
201  fileprefix += "/";
202 
203  bool insertChan = insert_chan(id); // unscannable source
204 
205  ChannelInfoList::iterator i = chanlist->begin();
206  for (; i != chanlist->end(); ++i)
207  {
208  if ((*i).m_xmltvid.isEmpty())
209  continue;
210 
211  QString localfile;
212 
213  if (!(*i).m_icon.isEmpty())
214  {
215  QDir remotefile = QDir((*i).m_icon);
216  QString filename = remotefile.dirName();
217 
218  localfile = fileprefix + filename;
219  QFile actualfile(localfile);
220  if (!actualfile.exists() &&
221  !GetMythDownloadManager()->download((*i).m_icon, localfile))
222  {
223  LOG(VB_GENERAL, LOG_ERR,
224  QString("Failed to fetch icon from '%1'")
225  .arg((*i).m_icon));
226  }
227 
228  localfile = filename;
229  }
230 
231  MSqlQuery query(MSqlQuery::InitCon());
232 
233  if (!(*i).m_old_xmltvid.isEmpty())
234  {
235  query.prepare(
236  "SELECT xmltvid "
237  "FROM channel "
238  "WHERE xmltvid = :XMLTVID");
239  query.bindValue(":XMLTVID", (*i).m_old_xmltvid);
240 
241  if (!query.exec())
242  {
243  MythDB::DBError("xmltvid conversion 1", query);
244  }
245  else if (query.next())
246  {
247  LOG(VB_GENERAL, LOG_INFO,
248  QString("Converting old xmltvid (%1) to new (%2)")
249  .arg((*i).m_old_xmltvid).arg((*i).m_xmltvid));
250 
251  query.prepare("UPDATE channel "
252  "SET xmltvid = :NEWXMLTVID"
253  "WHERE xmltvid = :OLDXMLTVID");
254  query.bindValue(":NEWXMLTVID", (*i).m_xmltvid);
255  query.bindValue(":OLDXMLTVID", (*i).m_old_xmltvid);
256 
257  if (!query.exec())
258  {
259  MythDB::DBError("xmltvid conversion 2", query);
260  }
261  }
262  }
263 
264  ChannelInfo dbChan = FindMatchingChannel(*i, existingChannels);
265  if (dbChan.m_chanid > 0) // Channel exists, updating
266  {
267  LOG(VB_XMLTV, LOG_NOTICE,
268  QString("Match found for xmltvid %1 to channel %2 (%3)")
269  .arg((*i).m_xmltvid).arg(dbChan.m_name).arg(dbChan.m_chanid));
270  if (m_interactive)
271  {
272 
273  cout << "### " << endl;
274  cout << "### Existing channel found" << endl;
275  cout << "### " << endl;
276  cout << "### xmltvid = "
277  << (*i).m_xmltvid.toLocal8Bit().constData() << endl;
278  cout << "### chanid = "
279  << dbChan.m_chanid << endl;
280  cout << "### name = "
281  << dbChan.m_name.toLocal8Bit().constData() << endl;
282  cout << "### callsign = "
283  << dbChan.m_callsign.toLocal8Bit().constData() << endl;
284  cout << "### channum = "
285  << dbChan.m_channum.toLocal8Bit().constData() << endl;
286  if (m_channelPreset)
287  {
288  cout << "### freqid = "
289  << dbChan.m_freqid.toLocal8Bit().constData() << endl;
290  }
291  cout << "### finetune = "
292  << dbChan.m_finetune << endl;
293  cout << "### tvformat = "
294  << dbChan.m_tvformat.toLocal8Bit().constData() << endl;
295  cout << "### icon = "
296  << dbChan.m_icon.toLocal8Bit().constData() << endl;
297  cout << "### " << endl;
298 
299  // The only thing the xmltv data supplies here is the icon
300  (*i).m_name = dbChan.m_name;
301  (*i).m_callsign = dbChan.m_callsign;
302  (*i).m_channum = dbChan.m_channum;
303  (*i).m_finetune = dbChan.m_finetune;
304  (*i).m_freqid = dbChan.m_freqid;
305  (*i).m_tvformat = dbChan.m_tvformat;
306 
308 
309  if ((*i).m_callsign.isEmpty())
310  (*i).m_callsign = dbChan.m_name;
311 
312  if (dbChan.m_name != (*i).m_name ||
313  dbChan.m_callsign != (*i).m_callsign ||
314  dbChan.m_channum != (*i).m_channum ||
315  dbChan.m_finetune != (*i).m_finetune ||
316  dbChan.m_freqid != (*i).m_freqid ||
317  dbChan.m_icon != localfile ||
318  dbChan.m_tvformat != (*i).m_tvformat)
319  {
320  MSqlQuery subquery(MSqlQuery::InitCon());
321  subquery.prepare("UPDATE channel SET chanid = :CHANID, "
322  "name = :NAME, callsign = :CALLSIGN, "
323  "channum = :CHANNUM, finetune = :FINE, "
324  "icon = :ICON, freqid = :FREQID, "
325  "tvformat = :TVFORMAT "
326  " WHERE xmltvid = :XMLTVID "
327  "AND sourceid = :SOURCEID;");
328  subquery.bindValue(":CHANID", dbChan.m_chanid);
329  subquery.bindValue(":NAME", (*i).m_name);
330  subquery.bindValue(":CALLSIGN", (*i).m_callsign);
331  subquery.bindValue(":CHANNUM", (*i).m_channum);
332  subquery.bindValue(":FINE", (*i).m_finetune);
333  subquery.bindValue(":ICON", localfile);
334  subquery.bindValue(":FREQID", (*i).m_freqid);
335  subquery.bindValue(":TVFORMAT", (*i).m_tvformat);
336  subquery.bindValue(":XMLTVID", (*i).m_xmltvid);
337  subquery.bindValue(":SOURCEID", id);
338 
339  if (!subquery.exec())
340  {
341  MythDB::DBError("update failed", subquery);
342  }
343  else
344  {
345  cout << "### " << endl;
346  cout << "### Change performed" << endl;
347  cout << "### " << endl;
348  }
349  }
350  else
351  {
352  cout << "### " << endl;
353  cout << "### Nothing changed" << endl;
354  cout << "### " << endl;
355  }
356  }
357  else if ((dbChan.m_icon != localfile) ||
358  (dbChan.m_xmltvid != (*i).m_xmltvid))
359  {
360  LOG(VB_XMLTV, LOG_NOTICE, QString("Updating channel %1 (%2)")
361  .arg(dbChan.m_name).arg(dbChan.m_chanid));
362 
363  if (localfile.isEmpty())
364  localfile = dbChan.m_icon;
365 
366  if (dbChan.m_xmltvid != (*i).m_xmltvid)
367  {
368  MSqlQuery subquery(MSqlQuery::InitCon());
369 
370  subquery.prepare("UPDATE channel SET icon = :ICON "
371  ", xmltvid:= :XMLTVID WHERE "
372  "chanid = :CHANID;");
373  subquery.bindValue(":ICON", localfile);
374  subquery.bindValue(":XMLTVID", (*i).m_xmltvid);
375  subquery.bindValue(":CHANID", dbChan.m_chanid);
376 
377  if (!subquery.exec())
378  MythDB::DBError("Channel icon change", subquery);
379  }
380  else
381  {
382  MSqlQuery subquery(MSqlQuery::InitCon());
383  subquery.prepare("UPDATE channel SET icon = :ICON WHERE "
384  "chanid = :CHANID;");
385  subquery.bindValue(":ICON", localfile);
386  subquery.bindValue(":CHANID", dbChan.m_chanid);
387 
388  if (!subquery.exec())
389  MythDB::DBError("Channel icon change", subquery);
390  }
391 
392  }
393  }
394  else if (insertChan) // Only insert channels for non-scannable sources
395  {
396  int major, minor = 0;
397  long long freq = 0;
398  get_atsc_stuff((*i).m_channum, id, (*i).m_freqid.toInt(), major, minor, freq);
399 
400  if (m_interactive && ((minor == 0) || (freq > 0)))
401  {
402  cout << "### " << endl;
403  cout << "### New channel found" << endl;
404  cout << "### " << endl;
405  cout << "### name = "
406  << (*i).m_name.toLocal8Bit().constData() << endl;
407  cout << "### callsign = "
408  << (*i).m_callsign.toLocal8Bit().constData() << endl;
409  cout << "### channum = "
410  << (*i).m_channum.toLocal8Bit().constData() << endl;
411  if (m_channelPreset)
412  {
413  cout << "### freqid = "
414  << (*i).m_freqid.toLocal8Bit().constData() << endl;
415  }
416  cout << "### finetune = "
417  << (*i).m_finetune << endl;
418  cout << "### tvformat = "
419  << (*i).m_tvformat.toLocal8Bit().constData() << endl;
420  cout << "### icon = "
421  << localfile.toLocal8Bit().constData() << endl;
422  cout << "### " << endl;
423 
424  uint chanid = promptForChannelUpdates(i,0);
425 
426  if ((*i).m_callsign.isEmpty())
427  (*i).m_callsign = QString::number(chanid);
428 
429  int mplexid = 0;
430  if ((chanid > 0) && (minor > 0))
431  mplexid = ChannelUtil::CreateMultiplex(id, "atsc",
432  freq, "8vsb");
433 
434  if (((mplexid > 0) || ((minor == 0) && (chanid > 0))) &&
436  mplexid, id, chanid,
437  (*i).m_callsign, (*i).m_name, (*i).m_channum,
438  0 /*service id*/, major, minor,
439  false /*use on air guide*/, false /*hidden*/,
440  false /*hidden in guide*/,
441  (*i).m_freqid, localfile, (*i).m_tvformat,
442  (*i).m_xmltvid))
443  {
444  cout << "### " << endl;
445  cout << "### Channel inserted" << endl;
446  cout << "### " << endl;
447  }
448  else
449  {
450  cout << "### " << endl;
451  cout << "### Channel skipped" << endl;
452  cout << "### " << endl;
453  }
454  }
455  else if ((minor == 0) || (freq > 0))
456  {
457  // We only do this if we are not asked to skip it with the
458  // --update-guide-only (formerly --update) flag.
459  int mplexid = 0, chanid = 0;
460  if (minor > 0)
461  {
463  id, "atsc", freq, "8vsb");
464  }
465 
466  if ((mplexid > 0) || (minor == 0))
467  chanid = ChannelUtil::CreateChanID(id, (*i).m_channum);
468 
469  if ((*i).m_callsign.isEmpty())
470  {
471  QStringList words = (*i).m_name.simplified().toUpper()
472  .split(" ");
473  QString callsign = "";
474  QString w1 = !words.empty() ? words[0] : QString();
475  QString w2 = words.size() > 1 ? words[1] : QString();
476  if (w1.isEmpty())
477  callsign = QString::number(chanid);
478  else if (w2.isEmpty())
479  callsign = words[0].left(5);
480  else
481  {
482  callsign = w1.left(w2.length() == 1 ? 4:3);
483  callsign += w2.left(5 - callsign.length());
484  }
485  (*i).m_callsign = callsign;
486  }
487 
488  if (chanid > 0)
489  {
490  QString cstr = (*i).m_channum;
491  if(m_channelPreset && cstr.isEmpty())
492  cstr = QString::number(chanid % 1000);
493 
494  bool retval = ChannelUtil::CreateChannel(
495  mplexid, id,
496  chanid,
497  (*i).m_callsign,
498  (*i).m_name, cstr,
499  0 /*service id*/,
500  major, minor,
501  false /*use on air guide*/,
502  false /*hidden*/,
503  false /*hidden in guide*/,
504  (*i).m_freqid,
505  localfile,
506  (*i).m_tvformat,
507  (*i).m_xmltvid
508  );
509  if (!retval)
510  cout << "Channel " << chanid << " creation failed"
511  << endl;
512  }
513  }
514  }
515  }
516 }
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
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)
Add a single binding.
Definition: mythdbcon.cpp:863
static bool IsCableCardPresent(uint sourceid)
Definition: sourceutil.cpp:328
QString m_name
Definition: channelinfo.h:82
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="")
Load channels from database into a list of ChannelInfo objects.
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
unsigned int uint
Definition: compat.h:140
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:317
QString normalizeChannelKey(const QString &chanName) const
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:82
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
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)
ChannelInfo FindMatchingChannel(const ChannelInfo &chanInfo, ChannelList existingChannels) const
vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:120
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
QString m_callsign
Definition: channelinfo.h:81
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:603
static bool IsEncoder(uint sourceid, bool strict=false)
Definition: sourceutil.cpp:281
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
QList< ChannelListItem > ChannelList
uint m_chanid
Definition: channelinfo.h:75