5 #include <QDomDocument>
6 #include <QDomImplementation>
18 #if QT_VERSION < QT_VERSION_CHECK(5,15,2)
19 #define capturedView capturedRef
23 QString subtitle, QString sortSubtitle,
24 QString desc, QString URL,
25 QString thumbnail, QString mediaURL,
26 QString author,
const QDateTime& date,
27 const QString& time,
const QString&
rating,
28 const off_t filesize,
const QString& player,
29 const QStringList& playerargs,
const QString& download,
30 const QStringList& downloadargs,
const uint width,
31 const uint height,
const QString& language,
32 const bool downloadable,
const QStringList& countries,
33 const uint season,
const uint episode,
34 const bool customhtml)
35 : m_title(
std::move(title)), m_sorttitle(
std::move(sortTitle)),
36 m_subtitle(
std::move(subtitle)), m_sortsubtitle(
std::move(sortSubtitle)),
37 m_desc(
std::move(desc)), m_url(
std::move(URL)),
38 m_thumbnail(
std::move(thumbnail)), m_mediaURL(
std::move(mediaURL)),
39 m_author(
std::move(author))
80 metadataMap[
"description"] =
m_desc;
81 metadataMap[
"url"] =
m_url;
87 metadataMap[
"date"] = QString();
92 metadataMap[
"length"] = QString();
97 QTime fin = time.addSecs(secs);
101 else if (secs >= 600)
107 metadataMap[
"length"] = fin.toString(format);
111 metadataMap[
"rating"] = QString();
116 metadataMap[
"filesize"] = QString();
118 metadataMap[
"filesize"] = QObject::tr(
"Web Only");
120 metadataMap[
"filesize"] = QObject::tr(
"Downloadable");
122 metadataMap[
"filesize"] = QString::number(
m_filesize);
124 QString tmpSize = QString(
"%1 ")
126 tmpSize += QObject::tr(
"MB",
"Megabytes");
128 metadataMap[
"filesize_str"] = QString();
130 metadataMap[
"filesize_str"] = QObject::tr(
"Web Only");
132 metadataMap[
"filesize_str"] = QObject::tr(
"Downloadable");
134 metadataMap[
"filesize"] = tmpSize;
141 metadataMap[
"width"] = QString();
143 metadataMap[
"width"] = QString::number(
m_width);
145 metadataMap[
"height"] = QString();
147 metadataMap[
"height"] = QString::number(
m_height);
149 metadataMap[
"resolution"] = QString();
151 metadataMap[
"resolution"] = QString(
"%1x%2").arg(
m_width).arg(
m_height);
160 metadataMap[
"s##e##"] = metadataMap[
"s00e00"] = QString(
"s%1e%2")
163 metadataMap[
"##x##"] = metadataMap[
"00x00"] = QString(
"%1x%2")
169 metadataMap[
"season"] = QString();
170 metadataMap[
"episode"] = QString();
171 metadataMap[
"s##e##"] = metadataMap[
"s00e00"] = QString();
172 metadataMap[
"##x##"] = metadataMap[
"00x00"] = QString();
178 QList<QDomNode> GetDirectChildrenNS(
const QDomElement& elem,
179 const QString& ns,
const QString& name)
181 QList<QDomNode> result;
182 QDomNodeList unf = elem.elementsByTagNameNS(ns, name);
183 for (
int i = 0, size = unf.size(); i < size; ++i)
184 if (unf.at(i).parentNode() == elem)
222 if (!child.
m_url.isEmpty())
250 if (!child.
m_tags.isEmpty())
268 QList<MRSSEntry> result;
273 for (
int i = 0; i < groups.size(); ++i)
285 QList<MRSSEntry> result;
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 : qAsConst(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 for (
const auto& dom : qAsConst(thumbs))
443 QDomElement thumbNode = dom.toElement();
444 int widthOpt =
GetInt(thumbNode,
"width");
445 int width = widthOpt ? widthOpt : 0;
446 int heightOpt =
GetInt(thumbNode,
"height");
447 int height = heightOpt ? heightOpt : 0;
450 thumbNode.attribute(
"url"),
453 thumbNode.attribute(
"time")
460 static QList<MRSSCredit>
GetCredits(
const QDomElement& element)
462 QList<MRSSCredit> result;
466 for (
const auto& dom : qAsConst(credits))
468 QDomElement creditNode = dom.toElement();
469 if (!creditNode.hasAttribute(
"role"))
473 creditNode.attribute(
"role"),
483 QList<MRSSComment> result;
484 QList<QDomNode> commParents = GetDirectChildrenNS(element,
Parse::kMediaRSS,
487 if (!commParents.empty())
489 QDomNodeList comments = commParents.at(0).toElement()
492 for (
int i = 0; i < comments.size(); ++i)
496 QObject::tr(
"Comments"),
497 comments.at(i).toElement().text()
503 QList<QDomNode> respParents = GetDirectChildrenNS(element,
Parse::kMediaRSS,
506 if (!respParents.empty())
508 QDomNodeList responses = respParents.at(0).toElement()
511 for (
int i = 0; i < responses.size(); ++i)
515 QObject::tr(
"Responses"),
516 responses.at(i).toElement().text()
522 QList<QDomNode> backParents = GetDirectChildrenNS(element,
Parse::kMediaRSS,
525 if (!backParents.empty())
527 QDomNodeList backlinks = backParents.at(0).toElement()
530 for (
int i = 0; i < backlinks.size(); ++i)
534 QObject::tr(
"Backlinks"),
535 backlinks.at(i).toElement().text()
545 QList<MRSSPeerLink> result;
549 for (
const auto& dom : qAsConst(links))
551 QDomElement linkNode = dom.toElement();
554 linkNode.attribute(
"type"),
555 linkNode.attribute(
"href")
562 static QList<MRSSScene>
GetScenes(
const QDomElement& element)
564 QList<MRSSScene> result;
568 if (!scenesNode.empty())
570 QDomNodeList scenesNodes = scenesNode.at(0).toElement()
573 for (
int i = 0; i < scenesNodes.size(); ++i)
575 QDomElement sceneNode = scenesNodes.at(i).toElement();
578 sceneNode.firstChildElement(
"sceneTitle").text(),
579 sceneNode.firstChildElement(
"sceneDescription").text(),
580 sceneNode.firstChildElement(
"sceneStartTime").text(),
581 sceneNode.firstChildElement(
"sceneEndTime").text()
600 QDomElement relem = elems.at(0).toElement();
602 if (relem.hasAttribute(
"scheme"))
603 rscheme = relem.attribute(
"scheme");
605 rscheme =
"urn:simple";
617 QDomElement celem = elems.at(0).toElement();
618 ctext = celem.text();
619 if (celem.hasAttribute(
"url"))
620 curl = celem.attribute(
"url");
636 QDomElement comm = comms.at(0).toElement();
641 QDomElement ratingDom = stars.at(0).toElement();
642 raverage =
GetInt(ratingDom,
"average");
643 rcount =
GetInt(ratingDom,
"count");
644 rmin =
GetInt(ratingDom,
"min");
645 rmax =
GetInt(ratingDom,
"max");
652 QDomElement stat = stats.at(0).toElement();
653 views =
GetInt(stat,
"views");
654 favs =
GetInt(stat,
"favorites");
661 QDomElement tag = tagsNode.at(0).toElement();
698 const QString
Parse::kDC =
"http://purl.org/dc/elements/1.1/";
699 const QString
Parse::kWFW =
"http://wellformedweb.org/CommentAPI/";
700 const QString
Parse::kAtom =
"http://www.w3.org/2005/Atom";
701 const QString
Parse::kRDF =
"http://www.w3.org/1999/02/22-rdf-syntax-ns#";
702 const QString
Parse::kSlash =
"http://purl.org/rss/1.0/modules/slash/";
703 const QString
Parse::kEnc =
"http://purl.oclc.org/net/rss_2.0/enc#";
704 const QString
Parse::kITunes =
"http://www.itunes.com/dtds/podcast-1.0.dtd";
706 const QString
Parse::kGeoRSSW3 =
"http://www.w3.org/2003/01/geo/wgs84_pos#";
708 const QString
Parse::kMythRSS =
"http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format";
719 QString document = domDoc.toString();
720 LOG(VB_GENERAL, LOG_DEBUG,
"Will Be Parsing: " + document);
722 QDomElement root = domDoc.documentElement();
723 QDomElement channel = root.firstChildElement(
"channel");
724 while (!channel.isNull())
726 QDomElement item = channel.firstChildElement(
"item");
727 while (!item.isNull())
730 item = item.nextSiblingElement(
"item");
732 channel = channel.nextSiblingElement(
"channel");
741 QString subtitle(
"");
742 QString description(
"");
745 QString duration(
"");
747 QString thumbnail(
"");
748 QString mediaURL(
"");
750 QString language(
"");
751 QString download(
"");
758 QStringList playerargs;
759 QStringList downloadargs;
760 QStringList countries;
761 bool downloadable =
true;
762 bool customhtml =
false;
765 title = item.firstChildElement(
"title").text();
771 QDomNodeList subt = item.elementsByTagNameNS(
kMythRSS,
"subtitle");
774 subtitle = subt.at(0).toElement().text();
778 QDomElement descriptiontemp = item.firstChildElement(
"description");
779 if (!descriptiontemp.isNull())
780 description = descriptiontemp.text();
781 if (description.isEmpty())
783 QDomNodeList nodes = item.elementsByTagNameNS(
kITunes,
"summary");
785 description = nodes.at(0).toElement().text();
788 if (description.isEmpty())
794 url = item.firstChildElement(
"link").text();
797 QDomElement authortemp = item.firstChildElement(
"author");
798 if (!authortemp.isNull())
799 author = authortemp.text();
800 if (author.isEmpty())
805 if (!date.isValid() || date.isNull())
807 if (!date.isValid() || date.isNull())
811 QDomNodeList dur = item.elementsByTagNameNS(
kITunes,
"duration");
814 QString itunestime = dur.at(0).toElement().text();
817 if (itunestime.count() == 8)
819 else if (itunestime.count() == 7)
821 else if (itunestime.count() == 5)
823 else if (itunestime.count() == 4)
825 else if (itunestime.count() == 2)
835 int seconds = itime.second() + (itime.minute() * 60) + (itime.hour() * 3600);
836 duration = QString::number(seconds);
842 QDomElement ratingtemp = item.firstChildElement(
"rating");
843 if (!ratingtemp.isNull())
844 rating = ratingtemp.text();
847 QDomElement playertemp = item.firstChildElement(
"player");
848 if (!playertemp.isNull())
849 player = playertemp.text();
852 QDomElement playerargstemp = item.firstChildElement(
"playerargs");
853 if (!playerargstemp.isNull())
854 playerargs = playerargstemp.text().split(
" ");
857 QDomElement downloadtemp = item.firstChildElement(
"download");
858 if (!downloadtemp.isNull())
859 download = downloadtemp.text();
862 QDomElement downloadargstemp = item.firstChildElement(
"downloadargs");
863 if (!downloadargstemp.isNull())
864 downloadargs = downloadargstemp.text().split(
" ");
867 QDomNodeList cties = item.elementsByTagNameNS(
kMythRSS,
"country");
871 while (i < cties.size())
873 countries.append(cties.at(i).toElement().text());
879 QDomNodeList seas = item.elementsByTagNameNS(
kMythRSS,
"season");
882 season = seas.at(0).toElement().text().toUInt();
886 QDomNodeList ep = item.elementsByTagNameNS(
kMythRSS,
"episode");
889 episode = ep.at(0).toElement().text().toUInt();
893 QDomNodeList html = item.elementsByTagNameNS(
kMythRSS,
"customhtml");
896 QString htmlstring = html.at(0).toElement().text();
897 if (htmlstring.contains(
"true", Qt::CaseInsensitive) || htmlstring ==
"1" ||
898 htmlstring.contains(
"yes", Qt::CaseInsensitive))
904 if (!enclosures.empty())
908 QList<MRSSThumbnail> thumbs = media.
Thumbnails;
912 thumbnail = thumb.
URL;
915 mediaURL = media.
URL;
919 language = media.
Lang;
921 if (duration.isEmpty())
922 duration = QString::number(media.
Duration);
925 filesize = media.
Size;
930 if (mediaURL.isEmpty())
945 if (mediaURL.isNull() || mediaURL == url)
946 downloadable =
false;
949 return(
new ResultItem(title, sh->doTitle(title),
950 subtitle, sh->doTitle(subtitle), description,
951 url, thumbnail, mediaURL, author, date, duration,
952 rating, filesize, player, playerargs,
953 download, downloadargs, width, height,
954 language, downloadable, countries, season,
955 episode, customhtml));
961 QDomElement link = parent.firstChildElement(
"link");
962 while(!link.isNull())
964 if (!link.hasAttribute(
"rel") || link.attribute(
"rel") ==
"alternate")
966 if (!link.hasAttribute(
"href"))
967 result = link.text();
969 result = link.attribute(
"href");
972 link = link.nextSiblingElement(
"link");
980 QDomNodeList nodes = parent.elementsByTagNameNS(
kITunes,
984 result = nodes.at(0).toElement().text();
988 nodes = parent.elementsByTagNameNS(
kDC,
992 result = nodes.at(0).toElement().text();
1002 QDomNodeList nodes = parent.elementsByTagNameNS(
kWFW,
1005 result = nodes.at(0).toElement().text();
1012 QDomNodeList nodes = parent.elementsByTagNameNS(
"",
"comments");
1014 result = nodes.at(0).toElement().text();
1020 QDomNodeList dates = parent.elementsByTagNameNS(
kDC,
"date");
1023 return FromRFC3339(dates.at(0).toElement().text());
1028 static const QRegularExpression kNonDigitRE { R
"(\D)" };
1033 QString time =
t.simplified();
1034 short int hoursShift = 0;
1035 short int minutesShift = 0;
1037 QStringList
tmp = time.split(
' ');
1040 if (
tmp.at(0).contains(kNonDigitRE))
1042 if (
tmp.size() != 5)
1044 QString tmpTimezone =
tmp.takeAt(
tmp.size() -1);
1045 if (tmpTimezone.size() == 5)
1048 int tz = tmpTimezone.toInt(&ok);
1051 hoursShift = tz / 100;
1052 minutesShift = tz % 100;
1058 if (
tmp.at(0).size() == 1)
1059 tmp[0].prepend(
"0");
1060 tmp [1].truncate(3);
1062 time =
tmp.join(
" ");
1065 if (
tmp.at(2).size() == 4)
1066 result = QLocale::c().toDateTime(time,
"dd MMM yyyy hh:mm:ss");
1068 result = QLocale::c().toDateTime(time,
"dd MMM yy hh:mm:ss");
1069 if (result.isNull() || !result.isValid())
1071 result = result.addSecs(hoursShift * 3600 * (-1) + minutesShift *60 * (-1));
1072 result.setTimeSpec(Qt::UTC);
1081 static const QRegularExpression fractionalSeconds { R
"(\.(\d+))" };
1082 auto match = fractionalSeconds.match(
t);
1083 if (match.hasMatch())
1086 int fractional = match.capturedView(1).toInt(&ok);
1089 if (fractional < 100)
1093 result = result.addMSecs(fractional);
1096 static const QRegularExpression timeZone { R
"((\+|\-)(\d\d):(\d\d)$)" };
1097 match = timeZone.match(t);
1098 if (match.hasMatch())
1100 short int multiplier = -1;
1101 if (match.captured(1) ==
"-")
1103 int hoursShift = match.capturedView(2).toInt();
1104 int minutesShift = match.capturedView(3).toInt();
1105 result = result.addSecs(hoursShift * 3600 * multiplier + minutesShift * 60 * multiplier);
1107 result.setTimeSpec(Qt::UTC);
1113 QList<Enclosure> result;
1114 QDomNodeList links = entry.elementsByTagName(
"enclosure");
1115 for (
int i = 0; i < links.size(); ++i)
1117 QDomElement link = links.at(i).toElement();
1121 link.attribute(
"url"),
1122 link.attribute(
"type"),
1123 link.attribute(
"length",
"-1").toLongLong(),
1124 link.attribute(
"hreflang")
1139 QString result = escaped;
1140 result.replace(
"&",
"&");
1141 result.replace(
"<",
"<");
1142 result.replace(
">",
">");
1143 result.replace(
"'",
"\'");
1144 result.replace(
"’",
"\'");
1145 result.replace(
"’",
"\'");
1146 result.replace(
""",
"\"");
1147 result.replace(
"…",QChar(8230));
1148 result.replace(
"é",QChar(233));
1149 result.replace(
"—", QChar(8212));
1150 result.replace(
" ",
" ");
1151 result.replace(
" ", QChar(160));
1152 result.replace(
"á", QChar(225));
1153 result.replace(
"‘", QChar(8216));
1154 result.replace(
"’", QChar(8217));
1155 result.replace(
"'",
"\'");
1156 result.replace(
"–", QChar(8211));
1157 result.replace(
"ä", QChar(0x00e4));
1158 result.replace(
"ö", QChar(0x00f6));
1159 result.replace(
"ü", QChar(0x00fc));
1160 result.replace(
"Ä", QChar(0x00c4));
1161 result.replace(
"Ö", QChar(0x00d6));
1162 result.replace(
"Ü", QChar(0x00dc));
1163 result.replace(
"ß", QChar(0x00df));
1164 result.replace(
"€",
"€");
1165 result.replace(
"…",
"...");
1166 result.replace(
"®", QChar(0x00ae));
1167 result.replace(
"“", QChar(0x201c));
1168 result.replace(
"”", QChar(0x201d));
1169 result.replace(
"<p>",
"\n");
1171 static const QRegularExpression kStripHtmlRE {
"<.*?>"};
1172 result.remove(kStripHtmlRE);