6#include <QDomImplementation>
19 QString subtitle, QString sortSubtitle,
20 QString desc, QString URL,
21 QString thumbnail, QString mediaURL,
22 QString author,
const QDateTime& date,
23 const QString& time,
const QString&
rating,
24 const off_t filesize,
const QString& player,
25 const QStringList& playerargs,
const QString& download,
26 const QStringList& downloadargs,
const uint width,
27 const uint height,
const QString& language,
28 const bool downloadable,
const QStringList& countries,
29 const uint season,
const uint episode,
30 const bool customhtml)
31 : m_title(std::move(title)), m_sorttitle(std::move(sortTitle)),
32 m_subtitle(std::move(subtitle)), m_sortsubtitle(std::move(sortSubtitle)),
33 m_desc(std::move(desc)), m_url(std::move(URL)),
34 m_thumbnail(std::move(thumbnail)), m_mediaURL(std::move(mediaURL)),
35 m_author(std::move(author))
76 metadataMap[
"description"] =
m_desc;
77 metadataMap[
"url"] =
m_url;
83 metadataMap[
"date"] = QString();
89 metadataMap[
"length"] = QString();
95 QTime fin = time.addSecs(secs);
105 metadataMap[
"length"] = fin.toString(format);
109 metadataMap[
"rating"] = QString();
114 metadataMap[
"filesize"] = QString();
116 metadataMap[
"filesize"] = QObject::tr(
"Web Only");
118 metadataMap[
"filesize"] = QObject::tr(
"Downloadable");
120 metadataMap[
"filesize"] = QString::number(
m_filesize);
122 QString tmpSize = QString(
"%1 ")
124 tmpSize += QObject::tr(
"MB",
"Megabytes");
126 metadataMap[
"filesize_str"] = QString();
128 metadataMap[
"filesize_str"] = QObject::tr(
"Web Only");
130 metadataMap[
"filesize_str"] = QObject::tr(
"Downloadable");
132 metadataMap[
"filesize"] = tmpSize;
139 metadataMap[
"width"] = QString();
141 metadataMap[
"width"] = QString::number(
m_width);
143 metadataMap[
"height"] = QString();
145 metadataMap[
"height"] = QString::number(
m_height);
147 metadataMap[
"resolution"] = QString();
149 metadataMap[
"resolution"] = QString(
"%1x%2").arg(
m_width).arg(
m_height);
158 metadataMap[
"s##e##"] = metadataMap[
"s00e00"] = QString(
"s%1e%2")
161 metadataMap[
"##x##"] = metadataMap[
"00x00"] = QString(
"%1x%2")
167 metadataMap[
"season"] = QString();
168 metadataMap[
"episode"] = QString();
169 metadataMap[
"s##e##"] = metadataMap[
"s00e00"] = QString();
170 metadataMap[
"##x##"] = metadataMap[
"00x00"] = QString();
177 const QString& ns,
const QString& name)
179 QList<QDomNode> result;
180 QDomNodeList unf = elem.elementsByTagNameNS(ns, name);
181 result.reserve(unf.size());
182 for (
int i = 0, size = unf.size(); i < size; ++i)
183 if (unf.at(i).parentNode() == elem)
221 if (!child.
m_url.isEmpty())
249 if (!child.
m_tags.isEmpty())
267 QList<MRSSEntry> result;
272 for (
int i = 0; i < groups.size(); ++i)
284 QList<MRSSEntry> result;
288 result.reserve(entries.size());
289 for (
int i = 0; i < entries.size(); ++i)
293 QDomElement en = entries.at(i).toElement();
296 if (en.hasAttribute(
"url"))
297 entry.
URL = en.attribute(
"url");
301 entry.
Size = en.attribute(
"fileSize").toInt();
302 entry.
Type = en.attribute(
"type");
303 entry.
Medium = en.attribute(
"medium");
304 entry.
IsDefault = (en.attribute(
"isDefault") ==
"true");
305 entry.
Expression = en.attribute(
"expression");
308 entry.
Bitrate = en.attribute(
"bitrate").toInt();
309 entry.
Framerate = en.attribute(
"framerate").toDouble();
310 entry.
SamplingRate = en.attribute(
"samplingrate").toDouble();
311 entry.
Channels = en.attribute(
"channels").toInt();
312 if (!en.attribute(
"duration").isNull())
313 entry.
Duration = en.attribute(
"duration").toInt();
316 if (!en.attribute(
"width").isNull())
317 entry.
Width = en.attribute(
"width").toInt();
320 if (!en.attribute(
"height").isNull())
321 entry.
Height = en.attribute(
"height").toInt();
324 if (!en.attribute(
"lang").isNull())
325 entry.
Lang = en.attribute(
"lang");
327 entry.
Lang = QString();
329 if (!en.attribute(
"rating").isNull())
339 if (
d.m_ratingAverage != 0)
347 entry.
Favs =
d.m_favs;
348 entry.
Tags =
d.m_tags;
364 QList<QDomElement> parents;
365 QDomElement parent = holder;
366 while (!parent.isNull())
368 parents.prepend(parent);
369 parent = parent.parentNode().toElement();
372 for (
const auto&
p : std::as_const(parents))
378 static QString
GetURL(
const QDomElement& element)
385 return elems.at(0).toElement().attribute(
"url");
388 static QString
GetTitle(
const QDomElement& element)
396 QDomElement telem = elems.at(0).toElement();
408 QDomElement telem = elems.at(0).toElement();
420 QDomElement telem = elems.at(0).toElement();
424 static int GetInt(
const QDomElement& elem,
const QString& attrname)
426 if (elem.hasAttribute(attrname))
429 int result = elem.attribute(attrname).toInt(&ok);
438 QList<MRSSThumbnail> result;
441 result.reserve(thumbs.size());
442 for (
const auto& dom : std::as_const(thumbs))
444 QDomElement thumbNode = dom.toElement();
445 int widthOpt =
GetInt(thumbNode,
"width");
446 int width = widthOpt ? widthOpt : 0;
447 int heightOpt =
GetInt(thumbNode,
"height");
448 int height = heightOpt ? heightOpt : 0;
451 .
URL=thumbNode.attribute(
"url"),
454 .Time=thumbNode.attribute(
"time")
461 static QList<MRSSCredit>
GetCredits(
const QDomElement& element)
463 QList<MRSSCredit> result;
467 result.reserve(credits.size());
468 for (
const auto& dom : std::as_const(credits))
470 QDomElement creditNode = dom.toElement();
471 if (!creditNode.hasAttribute(
"role"))
475 .
Role=creditNode.attribute(
"role"),
476 .Who=creditNode.text()
485 QList<MRSSComment> result;
489 if (!commParents.empty())
491 QDomNodeList comments = commParents.at(0).toElement()
494 result.reserve(comments.size());
495 for (
int i = 0; i < comments.size(); ++i)
499 .
Type=QObject::tr(
"Comments"),
500 .Comment=comments.at(i).toElement().text()
509 if (!respParents.empty())
511 QDomNodeList responses = respParents.at(0).toElement()
514#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
515 result.reserve(result.capacity() + responses.size());
517 for (
int i = 0; i < responses.size(); ++i)
521 .
Type=QObject::tr(
"Responses"),
522 .Comment=responses.at(i).toElement().text()
531 if (!backParents.empty())
533 QDomNodeList backlinks = backParents.at(0).toElement()
536#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
537 result.reserve(result.capacity() + backlinks.size());
539 for (
int i = 0; i < backlinks.size(); ++i)
543 .
Type=QObject::tr(
"Backlinks"),
544 .Comment=backlinks.at(i).toElement().text()
554 QList<MRSSPeerLink> result;
558 result.reserve(links.size());
559 for (
const auto& dom : std::as_const(links))
561 QDomElement linkNode = dom.toElement();
564 .
Type=linkNode.attribute(
"type"),
565 .Link=linkNode.attribute(
"href")
572 static QList<MRSSScene>
GetScenes(
const QDomElement& element)
574 QList<MRSSScene> result;
578 if (!scenesNode.empty())
580 QDomNodeList scenesNodes = scenesNode.at(0).toElement()
583 result.reserve(scenesNodes.size());
584 for (
int i = 0; i < scenesNodes.size(); ++i)
586 QDomElement sceneNode = scenesNodes.at(i).toElement();
589 .
Title=sceneNode.firstChildElement(
"sceneTitle").text(),
590 .Description=sceneNode.firstChildElement(
"sceneDescription").text(),
591 .StartTime=sceneNode.firstChildElement(
"sceneStartTime").text(),
592 .EndTime=sceneNode.firstChildElement(
"sceneEndTime").text()
611 QDomElement relem = elems.at(0).toElement();
613 if (relem.hasAttribute(
"scheme"))
614 rscheme = relem.attribute(
"scheme");
616 rscheme =
"urn:simple";
628 QDomElement celem = elems.at(0).toElement();
629 ctext = celem.text();
630 if (celem.hasAttribute(
"url"))
631 curl = celem.attribute(
"url");
647 QDomElement comm = comms.at(0).toElement();
652 QDomElement ratingDom = stars.at(0).toElement();
653 raverage =
GetInt(ratingDom,
"average");
654 rcount =
GetInt(ratingDom,
"count");
655 rmin =
GetInt(ratingDom,
"min");
656 rmax =
GetInt(ratingDom,
"max");
663 QDomElement stat = stats.at(0).toElement();
664 views =
GetInt(stat,
"views");
665 favs =
GetInt(stat,
"favorites");
672 QDomElement tag = tagsNode.at(0).toElement();
709const QString
Parse::kDC =
"http://purl.org/dc/elements/1.1/";
710const QString
Parse::kWFW =
"http://wellformedweb.org/CommentAPI/";
711const QString
Parse::kAtom =
"http://www.w3.org/2005/Atom";
712const QString
Parse::kRDF =
"http://www.w3.org/1999/02/22-rdf-syntax-ns#";
713const QString
Parse::kSlash =
"http://purl.org/rss/1.0/modules/slash/";
714const QString
Parse::kEnc =
"http://purl.oclc.org/net/rss_2.0/enc#";
715const QString
Parse::kITunes =
"http://www.itunes.com/dtds/podcast-1.0.dtd";
719const QString
Parse::kMythRSS =
"http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format";
730 QString document = domDoc.toString();
731 LOG(VB_GENERAL, LOG_DEBUG,
"Will Be Parsing: " + document);
733 QDomElement root = domDoc.documentElement();
734 QDomElement channel = root.firstChildElement(
"channel");
735 while (!channel.isNull())
737 QDomElement item = channel.firstChildElement(
"item");
738 while (!item.isNull())
741 item = item.nextSiblingElement(
"item");
743 channel = channel.nextSiblingElement(
"channel");
752 QString subtitle(
"");
753 QString description(
"");
756 QString duration(
"");
758 QString thumbnail(
"");
759 QString mediaURL(
"");
761 QString language(
"");
762 QString download(
"");
769 QStringList playerargs;
770 QStringList downloadargs;
771 QStringList countries;
772 bool downloadable =
true;
773 bool customhtml =
false;
776 title = item.firstChildElement(
"title").text();
782 QDomNodeList subt = item.elementsByTagNameNS(
kMythRSS,
"subtitle");
785 subtitle = subt.at(0).toElement().text();
789 QDomElement descriptiontemp = item.firstChildElement(
"description");
790 if (!descriptiontemp.isNull())
791 description = descriptiontemp.text();
792 if (description.isEmpty())
794 QDomNodeList nodes = item.elementsByTagNameNS(
kITunes,
"summary");
796 description = nodes.at(0).toElement().text();
799 if (description.isEmpty())
805 url = item.firstChildElement(
"link").text();
808 QDomElement authortemp = item.firstChildElement(
"author");
809 if (!authortemp.isNull())
810 author = authortemp.text();
811 if (author.isEmpty())
816 if (!date.isValid() || date.isNull())
818 if (!date.isValid() || date.isNull())
822 QDomNodeList dur = item.elementsByTagNameNS(
kITunes,
"duration");
825 QString itunestime = dur.at(0).toElement().text();
828 if (itunestime.size() == 8)
830 else if (itunestime.size() == 7)
832 else if (itunestime.size() == 5)
834 else if (itunestime.size() == 4)
836 else if (itunestime.size() == 2)
846 int seconds = itime.second() + (itime.minute() * 60) + (itime.hour() * 3600);
847 duration = QString::number(seconds);
853 QDomElement ratingtemp = item.firstChildElement(
"rating");
854 if (!ratingtemp.isNull())
855 rating = ratingtemp.text();
858 QDomElement playertemp = item.firstChildElement(
"player");
859 if (!playertemp.isNull())
860 player = playertemp.text();
863 QDomElement playerargstemp = item.firstChildElement(
"playerargs");
864 if (!playerargstemp.isNull())
865 playerargs = playerargstemp.text().split(
" ");
868 QDomElement downloadtemp = item.firstChildElement(
"download");
869 if (!downloadtemp.isNull())
870 download = downloadtemp.text();
873 QDomElement downloadargstemp = item.firstChildElement(
"downloadargs");
874 if (!downloadargstemp.isNull())
875 downloadargs = downloadargstemp.text().split(
" ");
878 QDomNodeList cties = item.elementsByTagNameNS(
kMythRSS,
"country");
882 while (i < cties.size())
884 countries.append(cties.at(i).toElement().text());
890 QDomNodeList seas = item.elementsByTagNameNS(
kMythRSS,
"season");
893 season = seas.at(0).toElement().text().toUInt();
897 QDomNodeList ep = item.elementsByTagNameNS(
kMythRSS,
"episode");
900 episode = ep.at(0).toElement().text().toUInt();
904 QDomNodeList html = item.elementsByTagNameNS(
kMythRSS,
"customhtml");
907 QString htmlstring = html.at(0).toElement().text();
908 if (htmlstring.contains(
"true", Qt::CaseInsensitive) || htmlstring ==
"1" ||
909 htmlstring.contains(
"yes", Qt::CaseInsensitive))
915 if (!enclosures.empty())
919 QList<MRSSThumbnail> thumbs = media.
Thumbnails;
923 thumbnail = thumb.
URL;
926 mediaURL = media.
URL;
930 language = media.
Lang;
932 if (duration.isEmpty())
933 duration = QString::number(media.
Duration);
936 filesize = media.
Size;
941 if (mediaURL.isEmpty())
956 if (mediaURL.isNull() || mediaURL == url)
957 downloadable =
false;
960 return(
new ResultItem(title, sh->doTitle(title),
961 subtitle, sh->doTitle(subtitle), description,
962 url, thumbnail, mediaURL, author, date, duration,
963 rating, filesize, player, playerargs,
964 download, downloadargs, width, height,
965 language, downloadable, countries, season,
966 episode, customhtml));
972 QDomElement link = parent.firstChildElement(
"link");
973 while(!link.isNull())
975 if (!link.hasAttribute(
"rel") || link.attribute(
"rel") ==
"alternate")
977 if (!link.hasAttribute(
"href"))
978 result = link.text();
980 result = link.attribute(
"href");
983 link = link.nextSiblingElement(
"link");
991 QDomNodeList nodes = parent.elementsByTagNameNS(
kITunes,
995 result = nodes.at(0).toElement().text();
999 nodes = parent.elementsByTagNameNS(
kDC,
1003 result = nodes.at(0).toElement().text();
1013 QDomNodeList nodes = parent.elementsByTagNameNS(
kWFW,
1016 result = nodes.at(0).toElement().text();
1023 QDomNodeList nodes = parent.elementsByTagNameNS(
"",
"comments");
1025 result = nodes.at(0).toElement().text();
1031 QDomNodeList dates = parent.elementsByTagNameNS(
kDC,
"date");
1034 return FromRFC3339(dates.at(0).toElement().text());
1039 static const QRegularExpression kNonDigitRE { R
"(\D)" };
1044 QString time =
t.simplified();
1045 short int hoursShift = 0;
1046 short int minutesShift = 0;
1048 QStringList tmp = time.split(
' ');
1051 if (tmp.at(0).contains(kNonDigitRE))
1053 if (tmp.size() != 5)
1055 QString tmpTimezone = tmp.takeAt(tmp.size() -1);
1056 if (tmpTimezone.size() == 5)
1059 int tz = tmpTimezone.toInt(&ok);
1062 hoursShift = tz / 100;
1063 minutesShift = tz % 100;
1071 if (tmp.at(0).size() == 1)
1072 tmp[0].prepend(
"0");
1073 tmp [1].truncate(3);
1075 time = tmp.join(
" ");
1078 if (tmp.at(2).size() == 4)
1079 result = QLocale::c().toDateTime(time,
"dd MMM yyyy hh:mm:ss");
1081 result = QLocale::c().toDateTime(time,
"dd MMM yy hh:mm:ss");
1082 if (result.isNull() || !result.isValid())
1084 result = result.addSecs((hoursShift * 3600 * (-1)) + (minutesShift * 60 * (-1)));
1085#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
1086 result.setTimeSpec(Qt::UTC);
1088 result.setTimeZone(QTimeZone(QTimeZone::UTC));
1098 static const QRegularExpression fractionalSeconds { R
"(\.(\d+))" };
1099 auto match = fractionalSeconds.match(
t);
1100 if (match.hasMatch())
1103 int fractional = match.capturedView(1).toInt(&ok);
1106 if (fractional < 100)
1110 result = result.addMSecs(fractional);
1113 static const QRegularExpression timeZone { R
"((\+|\-)(\d\d):(\d\d)$)" };
1114 match = timeZone.match(t);
1115 if (match.hasMatch())
1117 short int multiplier = -1;
1118 if (match.captured(1) ==
"-")
1120 int hoursShift = match.capturedView(2).toInt();
1121 int minutesShift = match.capturedView(3).toInt();
1122 result = result.addSecs((hoursShift * 3600 * multiplier) + (minutesShift * 60 * multiplier));
1124#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
1125 result.setTimeSpec(Qt::UTC);
1127 result.setTimeZone(QTimeZone(QTimeZone::UTC));
1134 QList<Enclosure> result;
1135 QDomNodeList links = entry.elementsByTagName(
"enclosure");
1136 result.reserve(links.size());
1137 for (
int i = 0; i < links.size(); ++i)
1139 QDomElement link = links.at(i).toElement();
1143 .
URL=link.attribute(
"url"),
1144 .Type=link.attribute(
"type"),
1145 .Length=link.attribute(
"length",
"-1").toLongLong(),
1146 .Lang=link.attribute(
"hreflang")
1161 QString result = escaped;
1162 result.replace(
"&",
"&");
1163 result.replace(
"<",
"<");
1164 result.replace(
">",
">");
1165 result.replace(
"'",
"\'");
1166 result.replace(
"’",
"\'");
1167 result.replace(
"’",
"\'");
1168 result.replace(
""",
"\"");
1169 result.replace(
"…",QChar(8230));
1170 result.replace(
"é",QChar(233));
1171 result.replace(
"—", QChar(8212));
1172 result.replace(
" ",
" ");
1173 result.replace(
" ", QChar(160));
1174 result.replace(
"á", QChar(225));
1175 result.replace(
"‘", QChar(8216));
1176 result.replace(
"’", QChar(8217));
1177 result.replace(
"'",
"\'");
1178 result.replace(
"–", QChar(8211));
1179 result.replace(
"ä", QChar(0x00e4));
1180 result.replace(
"ö", QChar(0x00f6));
1181 result.replace(
"ü", QChar(0x00fc));
1182 result.replace(
"Ä", QChar(0x00c4));
1183 result.replace(
"Ö", QChar(0x00d6));
1184 result.replace(
"Ü", QChar(0x00dc));
1185 result.replace(
"ß", QChar(0x00df));
1186 result.replace(
"€",
"€");
1187 result.replace(
"…",
"...");
1188 result.replace(
"®", QChar(0x00ae));
1189 result.replace(
"“", QChar(0x201c));
1190 result.replace(
"”", QChar(0x201d));
1191 result.replace(
"<p>",
"\n");
1193 static const QRegularExpression kStripHtmlRE {
"<.*?>"};
1194 result.remove(kStripHtmlRE);
1199#include "moc_rssparse.cpp"
static const QString kAtom
static const QString kITunes
static const QString kGeoRSSW3
static QString UnescapeHTML(const QString &escaped)
static QString GetAuthor(const QDomElement &parent)
static const QString kGeoRSSSimple
static QList< Enclosure > GetEnclosures(const QDomElement &entry)
static const QString kWFW
static const QString kSlash
static const QString kMediaRSS
static QString GetCommentsRSS(const QDomElement &parent)
static ResultItem * ParseItem(const QDomElement &item)
static QString GetCommentsLink(const QDomElement &parent)
static QDateTime FromRFC3339(const QString &t)
static ResultItem::resultList parseRSS(const QDomDocument &domDoc)
static QMap< QString, int > m_timezoneOffsets
static QString GetLink(const QDomElement &parent)
static const QString kRDF
static QDateTime RFC822TimeToQDateTime(const QString &parent)
static QList< MRSSEntry > GetMediaRSS(const QDomElement &item)
static QDateTime GetDCDateTime(const QDomElement &parent)
static const QString kMythRSS
static const QString kEnc
QStringList m_downloadargs
void toMap(InfoMap &metadataMap)
void ensureSortFields(void)
QList< ResultItem * > resultList
static const iso6937table * d
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
QHash< QString, QString > InfoMap
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
@ kDateFull
Default local time.
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
QString intToPaddedString(int n, int width=2)
Creates a zero padded string representation of an integer.
def rating(profile, smoonURL, gate)
Describes an enclosure associated with an item.