10 #include <QTextStream>
22 #define LOC QString("IPTVChanFetch: ")
36 uint cardid, QString inputname,
uint sourceid,
38 m_scanMonitor(monitor),
39 m_cardId(cardid), m_inputName(
std::move(inputname)),
40 m_sourceId(sourceid), m_isMpts(is_mpts),
41 m_thread(new
MThread(
"IPTVChannelFetcher", this))
43 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Has ScanMonitor %1")
44 .arg(monitor?
"true":
"false"));
79 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Found %1 channels")
103 LOG(VB_CHANNEL, LOG_INFO,
LOC +
"Playlist URL was empty");
104 QMutexLocker locker(&
m_lock);
110 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Playlist URL: %1").arg(url));
126 QCoreApplication::translate(
"(Common)",
"Error"));
130 QMutexLocker locker(&
m_lock);
151 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Found %1 channels")
156 fbox_chan_map_t::const_iterator it =
m_channels.cbegin();
159 const QString& channum = it.key();
160 QString name = (*it).m_name;
161 QString xmltvid = (*it).m_xmltvid.isEmpty() ?
"" : (*it).m_xmltvid;
162 uint programnumber = (*it).m_programNumber;
164 QString msg = tr(
"Channel #%1 : %2").arg(channum, name);
166 LOG(VB_CHANNEL, LOG_INFO, QString(
"Handling channel %1 %2")
167 .arg(channum, name));
178 channum, programnumber, 0, 0,
180 QString(),
"Default", xmltvid);
188 tr(
"Updating %1").arg(msg));
191 channum, programnumber, 0, 0,
193 QString(),
"Default", xmltvid);
209 QMutexLocker locker(&
m_lock);
217 uint range = 70 - minval;
226 uint range = 100 - minval;
241 if (url.startsWith(
"file", Qt::CaseInsensitive))
245 QFile
file(qurl.toLocalFile());
246 if (!
file.open(QIODevice::ReadOnly))
248 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Opening '%1'")
249 .arg(qurl.toLocalFile()) +
ENO);
253 QTextStream stream(&
file);
254 while (!stream.atEnd())
255 ret += stream.readLine() +
"\n";
267 LOG(VB_GENERAL, LOG_INFO,
LOC +
268 QString(
"DownloadPlaylist failed to "
269 "download from %1").arg(url));
274 return tmp.isNull() ?
tmp : QString::fromUtf8(
tmp.toLatin1().constData());
283 QString url = rawdata.section(
"\n", numLine, numLine, QString::SectionSkipEmpty);
288 if (!url.startsWith(
"#"))
299 QString rawdata = reallyrawdata;
300 rawdata.replace(
"\r\n",
"\n");
303 QString header = rawdata.section(
"\n", 0, 0);
304 if (!header.startsWith(
"#EXTM3U"))
306 LOG(VB_GENERAL, LOG_ERR,
LOC +
307 QString(
"Invalid channel list header (%1)").arg(header));
312 tr(
"ERROR: M3U channel list is malformed"));
324 LOG(VB_CHANNEL, LOG_INFO,
LOC +
325 QString(
"Estimating there are %1 channels in playlist")
333 QString sql =
"select MAX(CONVERT(channum, UNSIGNED INTEGER)) from channel where sourceid = :SOURCEID;";
346 nextChanNum = query.
value(0).toInt() + 1;
347 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Next available channel number from DB is: %1").arg(nextChanNum));
352 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"No channels found for this source, using default channel number: %1").arg(nextChanNum));
359 for (
uint i = 1;
true; ++i)
367 QString msg = tr(
"Encountered malformed channel");
368 if (!channum.isEmpty())
370 chanmap[channum] = info;
372 msg = QString(
"Parsing Channel #%1 : %2 : %3")
373 .arg(channum, info.
m_name,
375 LOG(VB_CHANNEL, LOG_INFO,
LOC + msg);
411 QMap<QString,QString> values;
415 QString line = rawdata.section(
"\n", lineNum, lineNum, QString::SectionSkipEmpty);
420 if (line.startsWith(
"#"))
422 if (line.startsWith(
"#EXTINF:"))
424 parse_extinf(line.mid(line.indexOf(
':')+1), channum, name, nextChanNum);
426 else if (line.startsWith(
"#EXTMYTHTV:"))
428 QString data = line.mid(line.indexOf(
':')+1);
429 QString key = data.left(data.indexOf(
'='));
433 name = data.mid(data.indexOf(
'=')+1);
434 else if (key ==
"channum")
435 channum = data.mid(data.indexOf(
'=')+1);
437 values[key] = data.mid(data.indexOf(
'=')+1);
440 else if (line.startsWith(
"#EXTVLCOPT:program="))
442 values[
"programnumber"] = line.mid(line.indexOf(
'=')+1);
450 LOG(VB_GENERAL, LOG_INFO,
LOC +
451 QString(
"parse_chan_info name='%2'").arg(name));
452 LOG(VB_GENERAL, LOG_INFO,
LOC +
453 QString(
"parse_chan_info channum='%2'").arg(channum));
454 for (
auto it = values.cbegin(); it != values.cend(); ++it)
456 LOG(VB_GENERAL, LOG_INFO,
LOC +
457 QString(
"parse_chan_info [%1]='%2'")
458 .arg(it.key(), *it));
461 name, values[
"xmltvid"],
462 line, values[
"bitrate"].toUInt(),
464 values[
"fecurl0"], values[
"fecbitrate0"].toUInt(),
465 values[
"fecurl1"], values[
"fecbitrate1"].toUInt(),
466 values[
"programnumber"].toUInt());
477 static const QRegularExpression chanNumName1
478 { R
"(^-?\d+,(\d+)(?:\.\s|\s-\s)(.*)$)" };
479 auto match = chanNumName1.match(line);
480 if (match.hasMatch())
482 channum = match.captured(1);
483 name = match.captured(2);
488 static const QRegularExpression chanNumName2
489 {
"^-?\\d+\\s+[^,]*tvg-num=\"(\\d+)\"[^,]*,(.*)$" };
490 match = chanNumName2.match(line);
491 if (match.hasMatch())
493 channum = match.captured(1);
494 name = match.captured(2);
499 static const QRegularExpression chanNumName3
500 { R
"(^-?\d+,\[(\d+)\]\s+(.*)$)" };
501 match = chanNumName3.match(line);
502 if (match.hasMatch())
504 channum = match.captured(1);
505 name = match.captured(2);
510 static const QRegularExpression chanNumName4
511 { R
"(^-?\d+,(.*)\s+\[(\d+)\]$)" };
512 match = chanNumName4.match(line);
513 if (match.hasMatch())
515 channum = match.captured(2);
516 name = match.captured(1);
521 static const QRegularExpression chanNumName5
522 { R
"(^(-?\d+)\s+[^,]*,\s*(.*)$)" };
523 match = chanNumName5.match(line);
524 if (match.hasMatch())
526 channum = match.captured(1).simplified();
527 name = match.captured(2).simplified();
529 int channel_number = channum.toInt (&ok);
530 if (ok && (channel_number > 0))
538 static const QRegularExpression chanNumName6
539 { R
"(^-?\d+\s+channel-id=\"([^\"]+)\"\s+channel-number=\"([^\"]+)\"\s+tvg-name=\"([^\"]+)\".*$)" };
540 match = chanNumName6.match(line);
541 if (match.hasMatch())
543 channum = match.captured(2).simplified();
544 name = match.captured(3).simplified();
546 int channel_number = channum.toInt (&ok);
547 if (ok && (channel_number > 0))
555 static const QRegularExpression chanNumName7
556 {
"(^-?\\d+)\\s+[^,]*[^,]*,(.*)$" };
557 match = chanNumName7.match(line);
558 if (match.hasMatch())
560 channum = match.captured(1).simplified();
561 name = match.captured(2).simplified();
564 int channel_number = channum.toInt(&ok);
565 if (ok && channel_number > 0)
567 if (channel_number >= nextChanNum)
568 nextChanNum = channel_number + 1;
573 LOG(VB_GENERAL, LOG_ERR, QString(
"No channel number found, using next available: %1 for channel: %2").arg(nextChanNum).arg(name));
574 channum = QString::number(nextChanNum);
580 QString msg =
LOC + QString(
"Invalid header in channel list line \n\t\t\tEXTINF:%1").arg(line);
581 LOG(VB_GENERAL, LOG_ERR, msg);