MythTV  master
iptvchannelfetcher.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 // Std C headers
4 #include <cmath>
5 #include <unistd.h>
6 #include <utility>
7 
8 // Qt headers
9 #include <QFile>
10 #include <QRegularExpression>
11 #include <QTextStream>
12 
13 // MythTV headers
14 #include "libmyth/mythcontext.h"
15 #include "libmythbase/mythdirs.h"
18 
19 #include "cardutil.h"
20 #include "channelutil.h"
21 #include "iptvchannelfetcher.h"
22 #include "scanmonitor.h"
23 
24 #define LOC QString("IPTVChanFetch: ")
25 
26 static bool parse_chan_info(const QString &rawdata,
28  QString &channum,
29  uint &lineNum);
30 
31 static bool parse_extinf(const QString &line,
32  QString &channum,
33  QString &name,
34  QString &logo);
35 
37  uint cardid, QString inputname, uint sourceid,
38  bool is_mpts, ScanMonitor *monitor) :
39  m_scanMonitor(monitor),
40  m_cardId(cardid), m_inputName(std::move(inputname)),
41  m_sourceId(sourceid), m_isMpts(is_mpts),
42  m_thread(new MThread("IPTVChannelFetcher", this))
43 {
44  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Has ScanMonitor %1")
45  .arg(monitor?"true":"false"));
46 }
47 
49 {
50  Stop();
51  delete m_thread;
52  m_thread = nullptr;
53 }
54 
59 {
60  m_lock.lock();
61 
62  while (m_threadRunning)
63  {
64  m_stopNow = true;
65  m_lock.unlock();
66  m_thread->wait(5ms);
67  m_lock.lock();
68  }
69 
70  m_lock.unlock();
71 
72  m_thread->wait();
73 }
74 
76 {
77  while (!m_thread->isFinished())
78  m_thread->wait(500ms);
79 
80  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Found %1 channels")
81  .arg(m_channels.size()));
82  return m_channels;
83 }
84 
87 {
88  Stop();
89  m_stopNow = false;
90  m_thread->start();
91 }
92 
93 // Download the IPTV channel logo and write to configured location.
94 static bool download_logo(QString logoUrl, QString filename)
95 {
96  bool ret = false;
97 
98  if (!logoUrl.isEmpty())
99  {
100  QByteArray data;
101  if (GetMythDownloadManager()->download(logoUrl, &data))
102  {
103  QString iconDir = GetConfDir() + "/channels/";
104  QString filepath = iconDir + filename;
105  QFile file(filepath);
106  if (file.open(QIODevice::WriteOnly))
107  {
108  if (file.write(data) > 0)
109  {
110  ret = true;
111  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
112  QString("DownloadLogo to file %1").arg(filepath));
113  }
114  else
115  {
116  LOG(VB_CHANNEL, LOG_ERR, LOC +
117  QString("DownloadLogo failed to write to file %1").arg(filepath));
118  }
119  file.close();
120  }
121  else
122  {
123  LOG(VB_CHANNEL, LOG_ERR, LOC +
124  QString("DownloadLogo failed to open file %1").arg(filepath));
125  }
126  }
127  else
128  {
129  LOG(VB_CHANNEL, LOG_ERR, LOC +
130  QString("DownloadLogo failed to download %1").arg(logoUrl));
131  }
132  }
133  else
134  {
135  LOG(VB_CHANNEL, LOG_DEBUG, LOC + "DownloadLogo empty logoUrl");
136  }
137  return ret;
138 }
139 
141 {
142  m_lock.lock();
143  m_threadRunning = true;
144  m_lock.unlock();
145 
146  // Step 1/4 : Get info from DB
147  QString url = CardUtil::GetVideoDevice(m_cardId);
148 
149  if (m_stopNow || url.isEmpty())
150  {
151  LOG(VB_CHANNEL, LOG_INFO, LOC + "Playlist URL is empty");
152  QMutexLocker locker(&m_lock);
153  m_threadRunning = false;
154  m_stopNow = true;
155  return;
156  }
157 
158  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Playlist URL: %1").arg(url));
159 
160  // Step 2/4 : Download
161  if (m_scanMonitor)
162  {
163  m_scanMonitor->ScanAppendTextToLog(tr("Downloading Playlist"));
165  }
166 
167  QString playlist = DownloadPlaylist(url);
168 
169  if (m_stopNow || playlist.isEmpty())
170  {
171  if (playlist.isNull() && m_scanMonitor)
172  {
174  QCoreApplication::translate("(Common)", "Error"));
176  m_scanMonitor->ScanErrored(tr("Downloading Playlist Failed"));
177  }
178  QMutexLocker locker(&m_lock);
179  m_threadRunning = false;
180  m_stopNow = true;
181  return;
182  }
183 
184  LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("Playlist file size: %1").arg(playlist.length()));
185 
186  // Step 3/4 : Process
187  if (m_scanMonitor)
188  {
189  m_scanMonitor->ScanAppendTextToLog(tr("Processing Playlist"));
191  }
192 
193  m_channels.clear();
194  m_channels = ParsePlaylist(playlist, this);
195 
196  // Step 4/4 : Finish up
197  if (m_scanMonitor)
198  m_scanMonitor->ScanAppendTextToLog(tr("Adding Channels"));
200 
201  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Found %1 channels")
202  .arg(m_channels.size()));
203 
204  if (!m_isMpts)
205  {
206  // Sort channels in ascending channel number order
207  std::vector<fbox_chan_map_t::const_iterator> acno (m_channels.size());
208  {
209  fbox_chan_map_t::const_iterator it = m_channels.cbegin();
210  for (uint i = 0; it != m_channels.cend(); ++it, ++i)
211  {
212  acno[i] = it;
213  }
214  std::sort(acno.begin(), acno.end(),
215  [] (fbox_chan_map_t::const_iterator s1, fbox_chan_map_t::const_iterator s2) -> bool
216  {
217  return s1.key().toInt() < s2.key().toInt();
218  }
219  );
220  }
221 
222  // Insert channels in ascending channel number order
223  for (uint i = 0; i < acno.size(); ++i)
224  {
225  fbox_chan_map_t::const_iterator it = acno[i];
226  const QString& channum = it.key();
227  QString name = (*it).m_name;
228  QString xmltvid = (*it).m_xmltvid.isEmpty() ? "" : (*it).m_xmltvid;
229  QString logoUrl = (*it).m_logo;
230  QString logo;
231 
232  // Download channel icon (logo) when there is an logo URL in the EXTINF
233  if (!logoUrl.isEmpty())
234  {
235  QString filename = QString("IPTV_%1_%2_logo").arg(m_sourceId).arg(channum);
236  if (download_logo(logoUrl, filename))
237  {
238  logo = filename;
239  }
240  }
241 
242  uint programnumber = (*it).m_programNumber;
243  //: %1 is the channel number, %2 is the channel name
244  QString msg = tr("Channel #%1 : %2").arg(channum, name);
245 
246  LOG(VB_CHANNEL, LOG_INFO, QString("Handling channel %1 %2")
247  .arg(channum, name));
248 
249  int chanid = ChannelUtil::GetChanID(m_sourceId, channum);
250  if (chanid <= 0)
251  {
252  if (m_scanMonitor)
253  {
254  m_scanMonitor->ScanAppendTextToLog(tr("Adding %1").arg(msg));
255  }
256  chanid = ChannelUtil::CreateChanID(m_sourceId, channum);
257  ChannelUtil::CreateChannel(0, m_sourceId, chanid, name, name,
258  channum, programnumber, 0, 0,
259  false, kChannelVisible, QString(),
260  logo, "Default", xmltvid);
261  ChannelUtil::CreateIPTVTuningData(chanid, (*it).m_tuning);
262  }
263  else
264  {
265  if (m_scanMonitor)
266  {
268  tr("Updating %1").arg(msg));
269  }
270  ChannelUtil::UpdateChannel(0, m_sourceId, chanid, name, name,
271  channum, programnumber, 0, 0,
272  false, kChannelVisible, QString(),
273  logo, "Default", xmltvid);
274  ChannelUtil::UpdateIPTVTuningData(chanid, (*it).m_tuning);
275  }
276 
278  }
279 
280  if (m_scanMonitor)
281  {
282  m_scanMonitor->ScanAppendTextToLog(tr("Done"));
286  }
287  }
288 
289  QMutexLocker locker(&m_lock);
290  m_threadRunning = false;
291  m_stopNow = true;
292 }
293 
295 {
296  uint minval = 35;
297  uint range = 70 - minval;
298  uint pct = minval + (uint) truncf((((float)val) / m_chanCnt) * range);
299  if (m_scanMonitor)
301 }
302 
304 {
305  uint minval = 70;
306  uint range = 100 - minval;
307  uint pct = minval + (uint) truncf((((float)val) / m_chanCnt) * range);
308  if (m_scanMonitor)
310 }
311 
312 void IPTVChannelFetcher::SetMessage(const QString &status)
313 {
314  if (m_scanMonitor)
316 }
317 
318 // This function is always called from a thread context.
319 QString IPTVChannelFetcher::DownloadPlaylist(const QString &url)
320 {
321  if (url.startsWith("file", Qt::CaseInsensitive))
322  {
323  QString ret;
324  QUrl qurl(url);
325  QFile file(qurl.toLocalFile());
326  if (!file.open(QIODevice::ReadOnly))
327  {
328  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Opening '%1'")
329  .arg(qurl.toLocalFile()) + ENO);
330  return ret;
331  }
332 
333  while (!file.atEnd())
334  {
335  QByteArray data = file.readLine();
336  ret += QString(data);
337  }
338 
339  file.close();
340  return ret;
341  }
342 
343  // Use MythDownloadManager for http URLs
344  QByteArray data;
345  QString ret;
346 
347  if (GetMythDownloadManager()->download(url, &data))
348  {
349  ret = QString(data);
350  }
351  else
352  {
353  LOG(VB_GENERAL, LOG_ERR, LOC +
354  QString("DownloadPlaylist failed to "
355  "download from %1").arg(url));
356  }
357  return ret;
358 }
359 
360 static uint estimate_number_of_channels(const QString &rawdata)
361 {
362  uint result = 0;
363  uint numLine = 1;
364  while (true)
365  {
366  QString url = rawdata.section("\n", numLine, numLine, QString::SectionSkipEmpty);
367  if (url.isEmpty())
368  return result;
369 
370  ++numLine;
371  if (!url.startsWith("#")) // ignore comments
372  ++result;
373  }
374 }
375 
377  const QString &reallyrawdata, IPTVChannelFetcher *fetcher)
378 {
379  fbox_chan_map_t chanmap;
380  int nextChanNum = 1;
381 
382  QString rawdata = reallyrawdata;
383  rawdata.replace("\r\n", "\n");
384 
385  // Verify header is ok
386  QString header = rawdata.section("\n", 0, 0);
387  if (!header.startsWith("#EXTM3U"))
388  {
389  LOG(VB_GENERAL, LOG_ERR, LOC +
390  QString("Invalid channel list header (%1)").arg(header));
391 
392  if (fetcher)
393  {
394  fetcher->SetMessage(
395  tr("ERROR: M3U channel list is malformed"));
396  }
397 
398  return chanmap;
399  }
400 
401  // Estimate number of channels
402  if (fetcher)
403  {
404  uint num_channels = estimate_number_of_channels(rawdata);
405  fetcher->SetTotalNumChannels(num_channels);
406 
407  LOG(VB_CHANNEL, LOG_INFO, LOC +
408  QString("Estimating there are %1 channels in playlist")
409  .arg(num_channels));
410  }
411 
412  // Get the next available channel number for the source (only used if we can't find one in the playlist)
413  if (fetcher)
414  {
415  MSqlQuery query(MSqlQuery::InitCon());
416  QString sql = "SELECT MAX(CONVERT(channum, UNSIGNED INTEGER)) FROM channel "
417  "WHERE sourceid = :SOURCEID AND deleted IS NULL";
418 
419  query.prepare(sql);
420  query.bindValue(":SOURCEID", fetcher->m_sourceId);
421 
422  if (!query.exec())
423  {
424  MythDB::DBError("Get next max channel number", query);
425  }
426  else
427  {
428  if (query.first())
429  {
430  nextChanNum = query.value(0).toInt() + 1;
431  LOG(VB_CHANNEL, LOG_INFO, LOC +
432  QString("Next available channel number from DB is: %1").arg(nextChanNum));
433  }
434  else
435  {
436  nextChanNum = 1;
437  LOG(VB_CHANNEL, LOG_INFO, LOC +
438  QString("No channels found for this source, using default channel number: %1").arg(nextChanNum));
439  }
440  }
441  }
442 
443  // Parse each channel
444  uint lineNum = 1;
445  for (uint i = 1; true; ++i)
446  {
448  QString channum;
449 
450  if (!parse_chan_info(rawdata, info, channum, lineNum))
451  break;
452 
453  // Check if parsed channel number is valid
454  if (!channum.isEmpty())
455  {
456  bool ok = false;
457  int channel_number = channum.toInt(&ok);
458  if (ok && channel_number > 0)
459  {
460  // Positive integer channel number found
461  }
462  else
463  {
464  // Ignore invalid or negative channel numbers
465  channum.clear();
466  }
467  }
468 
469  // No channel number found, try to find channel in database and use that channel number
470  if (fetcher)
471  {
472  if (channum.isEmpty())
473  {
474  uint sourceId = fetcher->m_sourceId;
475  QString channum_db = ChannelUtil::GetChannelNumber(sourceId, info.m_name);
476  if (!channum_db.isEmpty())
477  {
478  LOG(VB_RECORD, LOG_INFO, LOC +
479  QString("Found channel in database, channel number: %1 for channel: %2")
480  .arg(channum_db, info.m_name));
481 
482  channum = channum_db;
483  }
484  }
485  }
486 
487  // Update highest channel number found so far
488  if (!channum.isEmpty())
489  {
490  bool ok = false;
491  int channel_number = channum.toInt(&ok);
492  if (ok && channel_number > 0)
493  {
494  if (channel_number >= nextChanNum)
495  {
496  nextChanNum = channel_number + 1;
497  }
498  }
499  }
500 
501  // No channel number found, use the default next one
502  if (channum.isEmpty())
503  {
504  LOG(VB_CHANNEL, LOG_INFO, QString("No channel number found, using next available: %1 for channel: %2")
505  .arg(nextChanNum).arg(info.m_name));
506  channum = QString::number(nextChanNum);
507  nextChanNum++;
508  }
509 
510  QString msg = tr("Encountered malformed channel");
511  if (!channum.isEmpty())
512  {
513  chanmap[channum] = info;
514 
515  msg = QString("Parsing Channel #%1: '%2' %3")
516  .arg(channum, info.m_name,
517  info.m_tuning.GetDataURL().toString());
518  LOG(VB_CHANNEL, LOG_INFO, LOC + msg);
519 
520  msg.clear(); // don't tell fetcher
521  }
522 
523  if (fetcher)
524  {
525  if (!msg.isEmpty())
526  fetcher->SetMessage(msg);
527  fetcher->SetNumChannelsParsed(i);
528  }
529  }
530 
531  return chanmap;
532 }
533 
534 static bool parse_chan_info(const QString &rawdata,
536  QString &channum,
537  uint &lineNum)
538 {
539  // #EXTINF:0,2 - France 2 <-- duration,channum - channame
540  // #EXTMYTHTV:xmltvid=C2.telepoche.com <-- optional line (myth specific)
541  // #EXTMYTHTV:bitrate=BITRATE <-- optional line (myth specific)
542  // #EXTMYTHTV:fectype=FECTYPE <-- optional line (myth specific)
543  // The FECTYPE can be rfc2733, rfc5109, or smpte2022
544  // #EXTMYTHTV:fecurl0=URL <-- optional line (myth specific)
545  // #EXTMYTHTV:fecurl1=URL <-- optional line (myth specific)
546  // #EXTMYTHTV:fecbitrate0=BITRATE <-- optional line (myth specific)
547  // #EXTMYTHTV:fecbitrate1=BITRATE <-- optional line (myth specific)
548  // #EXTVLCOPT:program=program_number <-- optional line (used by MythTV and VLC)
549  // #... <-- ignored comments
550  // rtsp://maiptv.iptv.fr/iptvtv/201 <-- url
551 
552  QString name;
553  QString logo;
554  QMap<QString,QString> values;
555 
556  while (true)
557  {
558  QString line = rawdata.section("\n", lineNum, lineNum, QString::SectionSkipEmpty);
559  if (line.isEmpty())
560  return false;
561 
562  LOG(VB_CHANNEL, LOG_INFO, LOC + line);
563 
564  ++lineNum;
565  if (line.startsWith("#"))
566  {
567  if (line.startsWith("#EXTINF:"))
568  {
569  parse_extinf(line.mid(line.indexOf(':')+1), channum, name, logo);
570  }
571  else if (line.startsWith("#EXTMYTHTV:"))
572  {
573  QString data = line.mid(line.indexOf(':')+1);
574  QString key = data.left(data.indexOf('='));
575  if (!key.isEmpty())
576  {
577  if (key == "name")
578  name = data.mid(data.indexOf('=')+1);
579  else if (key == "channum")
580  channum = data.mid(data.indexOf('=')+1);
581  else
582  values[key] = data.mid(data.indexOf('=')+1);
583  }
584  }
585  else if (line.startsWith("#EXTVLCOPT:program="))
586  {
587  values["programnumber"] = line.mid(line.indexOf('=')+1);
588  }
589  continue;
590  }
591 
592  if (name.isEmpty())
593  return false;
594 
595  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
596  QString("parse_chan_info name='%2'").arg(name));
597  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
598  QString("parse_chan_info channum='%2'").arg(channum));
599  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
600  QString("parse_chan_info logo='%2'").arg(logo));
601  for (auto it = values.cbegin(); it != values.cend(); ++it)
602  {
603  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
604  QString("parse_chan_info [%1]='%2'")
605  .arg(it.key(), *it));
606  }
608  name, values["xmltvid"], logo,
609  line, values["bitrate"].toUInt(),
610  values["fectype"],
611  values["fecurl0"], values["fecbitrate0"].toUInt(),
612  values["fecurl1"], values["fecbitrate1"].toUInt(),
613  values["programnumber"].toUInt());
614  return true;
615  }
616 }
617 
618 // Search for channel name in last part of the EXTINF line
619 static QString parse_extinf_name_trailing(const QString& line)
620 {
621  QString result;
622  static const QRegularExpression re_name { R"([^\,+],(.*)$)" };
623  auto match = re_name.match(line);
624  if (match.hasMatch())
625  {
626  result = match.captured(1).simplified();
627  }
628  return result;
629 }
630 
631 // Search for field value, e.g. field="value", in EXTINF line
632 static QString parse_extinf_field(QString line, const QString& field)
633 {
634  QString result;
635  auto pos = line.indexOf(field, 0, Qt::CaseInsensitive);
636  if (pos > 0)
637  {
638  line.remove(0, pos);
639  static const QRegularExpression re { R"(\"([^\"]*)\"(.*)$)" };
640  auto match = re.match(line);
641  if (match.hasMatch())
642  {
643  result = match.captured(1).simplified();
644  }
645  }
646 
647  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("line:%1 field:%2 result:%3")
648  .arg(line, field, result));
649 
650  return result;
651 }
652 
653 // Search for channel number, channel name and channel logo in EXTINF line
654 static bool parse_extinf(const QString &line,
655  QString &channum,
656  QString &name,
657  QString &logo)
658 {
659 
660  // Parse EXTINF line with TVG fields, Zatto style
661  // EG. #EXTINF:0001 tvg-id="ITV1London.uk" tvg-chno="90001" group-title="General Interest" tvg-logo="https://images.zattic.com/logos/ee3c9d2ac083eb2154b5/black/210x120.png", ITV 1 HD
662 
663  // Parse EXTINF line, HDHomeRun style
664  // EG. #EXTINF:-1 channel-id="22" channel-number="22" tvg-name="Omroep Brabant",22 Omroep Brabant
665  // #EXTINF:-1 channel-id="2.1" channel-number="2.1" tvg-name="CBS2-HD",2.1 CBS2-HD
666 
667  // Parse EXTINF line, https://github.com/iptv-org/iptv/blob/master/channels/ style
668  // EG. #EXTINF:-1 tvg-id="" tvg-name="" tvg-logo="https://i.imgur.com/VejnhiB.png" group-title="News",BBC News
669  {
670  channum = parse_extinf_field(line, "tvg-chno");
671  if (channum.isEmpty())
672  {
673  channum = parse_extinf_field(line, "channel-number");
674  }
675  logo = parse_extinf_field(line, "tvg-logo");
676  name = parse_extinf_field(line, "tvg-name");
677  if (name.isEmpty())
678  {
679  name = parse_extinf_name_trailing(line);
680  }
681 
682  // If we only have the name then it might be another format
683  if (!name.isEmpty() && !channum.isEmpty())
684  {
685  return true;
686  }
687  }
688 
689  // Freebox or SAT>IP format
690  {
691  static const QRegularExpression re
692  { R"(^-?\d+,(\d+)(?:\.\s|\s-\s)(.*)$)" };
693  auto match = re.match(line);
694  if (match.hasMatch())
695  {
696  channum = match.captured(1);
697  name = match.captured(2);
698  if (!channum.isEmpty() && !name.isEmpty())
699  {
700  return true;
701  }
702  }
703  }
704 
705  // Moviestar TV number then name
706  {
707  static const QRegularExpression re
708  { R"(^-?\d+,\[(\d+)\]\s+(.*)$)" };
709  auto match = re.match(line);
710  if (match.hasMatch())
711  {
712  channum = match.captured(1);
713  name = match.captured(2);
714  if (!channum.isEmpty() && !name.isEmpty())
715  {
716  return true;
717  }
718  }
719  }
720 
721  // Moviestar TV name then number
722  {
723  static const QRegularExpression re
724  { R"(^-?\d+,(.*)\s+\[(\d+)\]$)" };
725  auto match = re.match(line);
726  if (match.hasMatch())
727  {
728  channum = match.captured(2);
729  name = match.captured(1);
730  if (!channum.isEmpty() && !name.isEmpty())
731  {
732  return true;
733  }
734  }
735  }
736 
737  // Parse russion iptv plugin style
738  {
739  static const QRegularExpression re
740  { R"(^(-?\d+)\s+[^,]*,\s*(.*)$)" };
741  auto match = re.match(line);
742  if (match.hasMatch())
743  {
744  channum = match.captured(1).simplified();
745  name = match.captured(2).simplified();
746  bool ok = false;
747  int channel_number = channum.toInt (&ok);
748  if (ok && (channel_number > 0) && !name.isEmpty())
749  {
750  return true;
751  }
752  }
753  }
754 
755  // Almost a catchall: just get the name from the end of the line
756  // EG. #EXTINF:-1,Channel Title
757  name = parse_extinf_name_trailing(line);
758  if (!name.isEmpty())
759  {
760  return true;
761  }
762 
763  // Not one of the formats we support
764  QString msg = LOC + QString("Invalid header in channel list line \n\t\t\tEXTINF:%1").arg(line);
765  LOG(VB_GENERAL, LOG_ERR, msg);
766  return false;
767 }
768 
769 /* vim: set expandtab tabstop=4 shiftwidth=4: */
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
IPTVChannelFetcher::m_sourceId
uint m_sourceId
Definition: iptvchannelfetcher.h:97
IPTVChannelFetcher::m_channels
fbox_chan_map_t m_channels
Definition: iptvchannelfetcher.h:99
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
IPTVChannelFetcher::m_cardId
uint m_cardId
Definition: iptvchannelfetcher.h:95
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
IPTVChannelFetcher::m_thread
MThread * m_thread
Definition: iptvchannelfetcher.h:103
ChannelUtil::GetChannelNumber
static QString GetChannelNumber(uint sourceid, const QString &channel_name)
Definition: channelutil.cpp:991
IPTVChannelFetcher::SetNumChannelsInserted
void SetNumChannelsInserted(uint val)
Definition: iptvchannelfetcher.cpp:303
ScanMonitor::ScanPercentComplete
void ScanPercentComplete(int pct)
Definition: scanmonitor.cpp:103
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
IPTVChannelFetcher::SetMessage
void SetMessage(const QString &status)
Definition: iptvchannelfetcher.cpp:312
IPTVChannelFetcher::m_threadRunning
bool m_threadRunning
Definition: iptvchannelfetcher.h:101
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
IPTVChannelFetcher::Scan
void Scan(void)
Scans the given frequency list.
Definition: iptvchannelfetcher.cpp:86
build_compdb.file
file
Definition: build_compdb.py:55
mythdirs.h
IPTVChannelFetcher::IPTVChannelFetcher
IPTVChannelFetcher(uint cardid, QString inputname, uint sourceid, bool is_mpts, ScanMonitor *monitor=nullptr)
Definition: iptvchannelfetcher.cpp:36
IPTVChannelFetcher::run
void run(void) override
Definition: iptvchannelfetcher.cpp:140
parse_chan_info
static bool parse_chan_info(const QString &rawdata, IPTVChannelInfo &info, QString &channum, uint &lineNum)
Definition: iptvchannelfetcher.cpp:534
ScanMonitor::ScanErrored
void ScanErrored(const QString &error)
Definition: scanmonitor.cpp:128
scanmonitor.h
kChannelVisible
@ kChannelVisible
Definition: channelinfo.h:23
IPTVChannelFetcher::Stop
void Stop(void)
Stops the scanning thread running.
Definition: iptvchannelfetcher.cpp:58
ChannelUtil::GetChanID
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
Definition: channelutil.cpp:1335
mythlogging.h
IPTVChannelFetcher::m_chanCnt
uint m_chanCnt
Definition: iptvchannelfetcher.h:100
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:256
parse_extinf_field
static QString parse_extinf_field(QString line, const QString &field)
Definition: iptvchannelfetcher.cpp:632
IPTVChannelInfo
Definition: iptvchannelfetcher.h:26
MSqlQuery::first
bool first(void)
Wrap QSqlQuery::first() so we can display the query results.
Definition: mythdbcon.cpp:822
LOC
#define LOC
Definition: iptvchannelfetcher.cpp:24
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
ChannelUtil::UpdateChannel
static bool UpdateChannel(uint db_mplexid, uint source_id, uint 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, ChannelVisibleType visible, const QString &freqid=QString(), const QString &icon=QString(), QString format=QString(), const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0, int recpriority=INT_MIN, int tmOffset=INT_MIN, int commMethod=INT_MIN)
Definition: channelutil.cpp:1596
IPTVChannelFetcher
Definition: iptvchannelfetcher.h:67
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
download_logo
static bool download_logo(QString logoUrl, QString filename)
Definition: iptvchannelfetcher.cpp:94
parse_extinf
static bool parse_extinf(const QString &line, QString &channum, QString &name, QString &logo)
Definition: iptvchannelfetcher.cpp:654
ChannelUtil::CreateChanID
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
Definition: channelutil.cpp:1471
MThread::isFinished
bool isFinished(void) const
Definition: mthread.cpp:258
ScanMonitor::ScanAppendTextToLog
void ScanAppendTextToLog(const QString &status)
Definition: scanmonitor.cpp:109
parse_extinf_name_trailing
static QString parse_extinf_name_trailing(const QString &line)
Definition: iptvchannelfetcher.cpp:619
ChannelUtil::CreateChannel
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, ChannelVisibleType visible, const QString &freqid, const QString &icon=QString(), QString format="Default", const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0, int recpriority=0, int tmOffset=0, int commMethod=-1)
Definition: channelutil.cpp:1508
channelutil.h
IPTVChannelFetcher::m_lock
QMutex m_lock
Definition: iptvchannelfetcher.h:104
cardutil.h
ScanMonitor::ScanComplete
void ScanComplete(void)
Definition: scanmonitor.cpp:98
ChannelUtil::CreateIPTVTuningData
static bool CreateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
Definition: channelutil.h:155
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
IPTVChannelFetcher::m_scanMonitor
ScanMonitor * m_scanMonitor
Definition: iptvchannelfetcher.h:94
mythcontext.h
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
IPTVChannelFetcher::m_isMpts
bool m_isMpts
Definition: iptvchannelfetcher.h:98
IPTVChannelFetcher::~IPTVChannelFetcher
~IPTVChannelFetcher() override
Definition: iptvchannelfetcher.cpp:48
IPTVChannelFetcher::GetChannels
fbox_chan_map_t GetChannels(void)
Definition: iptvchannelfetcher.cpp:75
IPTVChannelFetcher::m_stopNow
bool m_stopNow
Definition: iptvchannelfetcher.h:102
azlyrics.info
dictionary info
Definition: azlyrics.py:7
estimate_number_of_channels
static uint estimate_number_of_channels(const QString &rawdata)
Definition: iptvchannelfetcher.cpp:360
IPTVChannelFetcher::DownloadPlaylist
static QString DownloadPlaylist(const QString &url)
Definition: iptvchannelfetcher.cpp:319
mythdownloadmanager.h
build_compdb.filename
filename
Definition: build_compdb.py:21
iptvchannelfetcher.h
fbox_chan_map_t
QMap< QString, IPTVChannelInfo > fbox_chan_map_t
Definition: iptvchannelfetcher.h:65
ScanMonitor
Definition: scanmonitor.h:44
IPTVChannelFetcher::SetTotalNumChannels
void SetTotalNumChannels(uint val)
Definition: iptvchannelfetcher.h:85
IPTVChannelFetcher::SetNumChannelsParsed
void SetNumChannelsParsed(uint val)
Definition: iptvchannelfetcher.cpp:294
CardUtil::GetVideoDevice
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:294
IPTVChannelFetcher::ParsePlaylist
static MTV_PUBLIC fbox_chan_map_t ParsePlaylist(const QString &rawdata, IPTVChannelFetcher *fetcher=nullptr)
Definition: iptvchannelfetcher.cpp:376
uint
unsigned int uint
Definition: freesurround.h:24
ChannelUtil::UpdateIPTVTuningData
static bool UpdateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
Definition: channelutil.cpp:1752
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:146
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837