10 #include <QRegularExpression>
11 #include <QTextStream>
23 #define LOC QString("IPTVChanFetch: ")
37 uint cardid, QString inputname,
uint sourceid,
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))
44 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Has ScanMonitor %1")
45 .arg(monitor?
"true":
"false"));
80 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Found %1 channels")
104 LOG(VB_CHANNEL, LOG_INFO,
LOC +
"Playlist URL was empty");
105 QMutexLocker locker(&
m_lock);
111 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Playlist URL: %1").arg(url));
127 QCoreApplication::translate(
"(Common)",
"Error"));
131 QMutexLocker locker(&
m_lock);
152 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"Found %1 channels")
157 fbox_chan_map_t::const_iterator it =
m_channels.cbegin();
160 const QString& channum = it.key();
161 QString name = (*it).m_name;
162 QString xmltvid = (*it).m_xmltvid.isEmpty() ?
"" : (*it).m_xmltvid;
163 uint programnumber = (*it).m_programNumber;
165 QString msg = tr(
"Channel #%1 : %2").arg(channum, name);
167 LOG(VB_CHANNEL, LOG_INFO, QString(
"Handling channel %1 %2")
168 .arg(channum, name));
179 channum, programnumber, 0, 0,
181 QString(),
"Default", xmltvid);
189 tr(
"Updating %1").arg(msg));
192 channum, programnumber, 0, 0,
194 QString(),
"Default", xmltvid);
210 QMutexLocker locker(&
m_lock);
218 uint range = 70 - minval;
227 uint range = 100 - minval;
242 if (url.startsWith(
"file", Qt::CaseInsensitive))
246 QFile
file(qurl.toLocalFile());
247 if (!
file.open(QIODevice::ReadOnly))
249 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Opening '%1'")
250 .arg(qurl.toLocalFile()) +
ENO);
254 QTextStream stream(&
file);
255 while (!stream.atEnd())
256 ret += stream.readLine() +
"\n";
268 LOG(VB_GENERAL, LOG_INFO,
LOC +
269 QString(
"DownloadPlaylist failed to "
270 "download from %1").arg(url));
275 return tmp.isNull() ?
tmp : QString::fromUtf8(
tmp.toLatin1().constData());
284 QString url = rawdata.section(
"\n", numLine, numLine, QString::SectionSkipEmpty);
289 if (!url.startsWith(
"#"))
300 QString rawdata = reallyrawdata;
301 rawdata.replace(
"\r\n",
"\n");
304 QString header = rawdata.section(
"\n", 0, 0);
305 if (!header.startsWith(
"#EXTM3U"))
307 LOG(VB_GENERAL, LOG_ERR,
LOC +
308 QString(
"Invalid channel list header (%1)").arg(header));
313 tr(
"ERROR: M3U channel list is malformed"));
325 LOG(VB_CHANNEL, LOG_INFO,
LOC +
326 QString(
"Estimating there are %1 channels in playlist")
334 QString sql =
"select MAX(CONVERT(channum, UNSIGNED INTEGER)) from channel where sourceid = :SOURCEID;";
347 nextChanNum = query.
value(0).toInt() + 1;
348 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Next available channel number from DB is: %1").arg(nextChanNum));
353 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"No channels found for this source, using default channel number: %1").arg(nextChanNum));
360 for (
uint i = 1;
true; ++i)
368 QString msg = tr(
"Encountered malformed channel");
369 if (!channum.isEmpty())
371 chanmap[channum] = info;
373 msg = QString(
"Parsing Channel #%1 : %2 : %3")
374 .arg(channum, info.
m_name,
376 LOG(VB_CHANNEL, LOG_INFO,
LOC + msg);
412 QMap<QString,QString> values;
416 QString line = rawdata.section(
"\n", lineNum, lineNum, QString::SectionSkipEmpty);
421 if (line.startsWith(
"#"))
423 if (line.startsWith(
"#EXTINF:"))
425 parse_extinf(line.mid(line.indexOf(
':')+1), channum, name, nextChanNum);
427 else if (line.startsWith(
"#EXTMYTHTV:"))
429 QString data = line.mid(line.indexOf(
':')+1);
430 QString key = data.left(data.indexOf(
'='));
434 name = data.mid(data.indexOf(
'=')+1);
435 else if (key ==
"channum")
436 channum = data.mid(data.indexOf(
'=')+1);
438 values[key] = data.mid(data.indexOf(
'=')+1);
441 else if (line.startsWith(
"#EXTVLCOPT:program="))
443 values[
"programnumber"] = line.mid(line.indexOf(
'=')+1);
451 LOG(VB_GENERAL, LOG_INFO,
LOC +
452 QString(
"parse_chan_info name='%2'").arg(name));
453 LOG(VB_GENERAL, LOG_INFO,
LOC +
454 QString(
"parse_chan_info channum='%2'").arg(channum));
455 for (
auto it = values.cbegin(); it != values.cend(); ++it)
457 LOG(VB_GENERAL, LOG_INFO,
LOC +
458 QString(
"parse_chan_info [%1]='%2'")
459 .arg(it.key(), *it));
462 name, values[
"xmltvid"],
463 line, values[
"bitrate"].toUInt(),
465 values[
"fecurl0"], values[
"fecbitrate0"].toUInt(),
466 values[
"fecurl1"], values[
"fecbitrate1"].toUInt(),
467 values[
"programnumber"].toUInt());
478 static const QRegularExpression chanNumName1
479 { R
"(^-?\d+,(\d+)(?:\.\s|\s-\s)(.*)$)" };
480 auto match = chanNumName1.match(line);
481 if (match.hasMatch())
483 channum = match.captured(1);
484 name = match.captured(2);
489 static const QRegularExpression chanNumName2
490 {
"^-?\\d+\\s+[^,]*tvg-num=\"(\\d+)\"[^,]*,(.*)$" };
491 match = chanNumName2.match(line);
492 if (match.hasMatch())
494 channum = match.captured(1);
495 name = match.captured(2);
500 static const QRegularExpression chanNumName3
501 { R
"(^-?\d+,\[(\d+)\]\s+(.*)$)" };
502 match = chanNumName3.match(line);
503 if (match.hasMatch())
505 channum = match.captured(1);
506 name = match.captured(2);
511 static const QRegularExpression chanNumName4
512 { R
"(^-?\d+,(.*)\s+\[(\d+)\]$)" };
513 match = chanNumName4.match(line);
514 if (match.hasMatch())
516 channum = match.captured(2);
517 name = match.captured(1);
522 static const QRegularExpression chanNumName5
523 { R
"(^(-?\d+)\s+[^,]*,\s*(.*)$)" };
524 match = chanNumName5.match(line);
525 if (match.hasMatch())
527 channum = match.captured(1).simplified();
528 name = match.captured(2).simplified();
530 int channel_number = channum.toInt (&ok);
531 if (ok && (channel_number > 0))
540 static const QRegularExpression chanNumName6
541 { R
"(^-?\d+\s+channel-id=\"([^\"]+)\"\s+channel-number=\"([^\"]+)\"\s+tvg-name=\"([^\"]+)\".*$)" };
542 match = chanNumName6.match(line);
543 if (match.hasMatch())
545 channum = match.captured(2).simplified();
546 name = match.captured(3).simplified();
547 if (!channum.isEmpty() && !name.isEmpty())
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);