5 #include <QDomDocument>
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();
88 metadataMap[
"length"] = QString();
93 QTime fin = time.addSecs(secs);
103 metadataMap[
"length"] = fin.toString(format);
107 metadataMap[
"rating"] = QString();
112 metadataMap[
"filesize"] = QString();
114 metadataMap[
"filesize"] = QObject::tr(
"Web Only");
116 metadataMap[
"filesize"] = QObject::tr(
"Downloadable");
118 metadataMap[
"filesize"] = QString::number(
m_filesize);
120 QString tmpSize = QString(
"%1 ")
122 tmpSize += QObject::tr(
"MB",
"Megabytes");
124 metadataMap[
"filesize_str"] = QString();
126 metadataMap[
"filesize_str"] = QObject::tr(
"Web Only");
128 metadataMap[
"filesize_str"] = QObject::tr(
"Downloadable");
130 metadataMap[
"filesize"] = tmpSize;
137 metadataMap[
"width"] = QString();
139 metadataMap[
"width"] = QString::number(
m_width);
141 metadataMap[
"height"] = QString();
143 metadataMap[
"height"] = QString::number(
m_height);
145 metadataMap[
"resolution"] = QString();
147 metadataMap[
"resolution"] = QString(
"%1x%2").arg(
m_width).arg(
m_height);
156 metadataMap[
"s##e##"] = metadataMap[
"s00e00"] = QString(
"s%1e%2")
159 metadataMap[
"##x##"] = metadataMap[
"00x00"] = QString(
"%1x%2")
165 metadataMap[
"season"] = QString();
166 metadataMap[
"episode"] = QString();
167 metadataMap[
"s##e##"] = metadataMap[
"s00e00"] = QString();
168 metadataMap[
"##x##"] = metadataMap[
"00x00"] = QString();
174 QList<QDomNode> GetDirectChildrenNS(
const QDomElement& elem,
175 const QString& ns,
const QString& name)
177 QList<QDomNode> result;
178 QDomNodeList unf = elem.elementsByTagNameNS(ns, name);
179 for (
int i = 0, size = unf.size(); i < size; ++i)
180 if (unf.at(i).parentNode() == elem)
218 if (!child.
m_url.isEmpty())
246 if (!child.
m_tags.isEmpty())
264 QList<MRSSEntry> result;
269 for (
int i = 0; i < groups.size(); ++i)
281 QList<MRSSEntry> result;
285 for (
int i = 0; i < entries.size(); ++i)
289 QDomElement en = entries.at(i).toElement();
292 if (en.hasAttribute(
"url"))
293 entry.
URL = en.attribute(
"url");
297 entry.
Size = en.attribute(
"fileSize").toInt();
298 entry.
Type = en.attribute(
"type");
299 entry.
Medium = en.attribute(
"medium");
300 entry.
IsDefault = (en.attribute(
"isDefault") ==
"true");
301 entry.
Expression = en.attribute(
"expression");
304 entry.
Bitrate = en.attribute(
"bitrate").toInt();
305 entry.
Framerate = en.attribute(
"framerate").toDouble();
306 entry.
SamplingRate = en.attribute(
"samplingrate").toDouble();
307 entry.
Channels = en.attribute(
"channels").toInt();
308 if (!en.attribute(
"duration").isNull())
309 entry.
Duration = en.attribute(
"duration").toInt();
312 if (!en.attribute(
"width").isNull())
313 entry.
Width = en.attribute(
"width").toInt();
316 if (!en.attribute(
"height").isNull())
317 entry.
Height = en.attribute(
"height").toInt();
320 if (!en.attribute(
"lang").isNull())
321 entry.
Lang = en.attribute(
"lang");
323 entry.
Lang = QString();
325 if (!en.attribute(
"rating").isNull())
335 if (
d.m_ratingAverage != 0)
343 entry.
Favs =
d.m_favs;
344 entry.
Tags =
d.m_tags;
360 QList<QDomElement> parents;
361 QDomElement parent = holder;
362 while (!parent.isNull())
364 parents.prepend(parent);
365 parent = parent.parentNode().toElement();
368 for (
const auto&
p : std::as_const(parents))
374 static QString
GetURL(
const QDomElement& element)
381 return elems.at(0).toElement().attribute(
"url");
384 static QString
GetTitle(
const QDomElement& element)
392 QDomElement telem = elems.at(0).toElement();
404 QDomElement telem = elems.at(0).toElement();
416 QDomElement telem = elems.at(0).toElement();
420 static int GetInt(
const QDomElement& elem,
const QString& attrname)
422 if (elem.hasAttribute(attrname))
425 int result = elem.attribute(attrname).toInt(&ok);
434 QList<MRSSThumbnail> result;
437 for (
const auto& dom : std::as_const(thumbs))
439 QDomElement thumbNode = dom.toElement();
440 int widthOpt =
GetInt(thumbNode,
"width");
441 int width = widthOpt ? widthOpt : 0;
442 int heightOpt =
GetInt(thumbNode,
"height");
443 int height = heightOpt ? heightOpt : 0;
446 thumbNode.attribute(
"url"),
449 thumbNode.attribute(
"time")
456 static QList<MRSSCredit>
GetCredits(
const QDomElement& element)
458 QList<MRSSCredit> result;
462 for (
const auto& dom : std::as_const(credits))
464 QDomElement creditNode = dom.toElement();
465 if (!creditNode.hasAttribute(
"role"))
469 creditNode.attribute(
"role"),
479 QList<MRSSComment> result;
480 QList<QDomNode> commParents = GetDirectChildrenNS(element,
Parse::kMediaRSS,
483 if (!commParents.empty())
485 QDomNodeList comments = commParents.at(0).toElement()
488 for (
int i = 0; i < comments.size(); ++i)
492 QObject::tr(
"Comments"),
493 comments.at(i).toElement().text()
499 QList<QDomNode> respParents = GetDirectChildrenNS(element,
Parse::kMediaRSS,
502 if (!respParents.empty())
504 QDomNodeList responses = respParents.at(0).toElement()
507 for (
int i = 0; i < responses.size(); ++i)
511 QObject::tr(
"Responses"),
512 responses.at(i).toElement().text()
518 QList<QDomNode> backParents = GetDirectChildrenNS(element,
Parse::kMediaRSS,
521 if (!backParents.empty())
523 QDomNodeList backlinks = backParents.at(0).toElement()
526 for (
int i = 0; i < backlinks.size(); ++i)
530 QObject::tr(
"Backlinks"),
531 backlinks.at(i).toElement().text()
541 QList<MRSSPeerLink> result;
545 for (
const auto& dom : std::as_const(links))
547 QDomElement linkNode = dom.toElement();
550 linkNode.attribute(
"type"),
551 linkNode.attribute(
"href")
558 static QList<MRSSScene>
GetScenes(
const QDomElement& element)
560 QList<MRSSScene> result;
564 if (!scenesNode.empty())
566 QDomNodeList scenesNodes = scenesNode.at(0).toElement()
569 for (
int i = 0; i < scenesNodes.size(); ++i)
571 QDomElement sceneNode = scenesNodes.at(i).toElement();
574 sceneNode.firstChildElement(
"sceneTitle").text(),
575 sceneNode.firstChildElement(
"sceneDescription").text(),
576 sceneNode.firstChildElement(
"sceneStartTime").text(),
577 sceneNode.firstChildElement(
"sceneEndTime").text()
596 QDomElement relem = elems.at(0).toElement();
598 if (relem.hasAttribute(
"scheme"))
599 rscheme = relem.attribute(
"scheme");
601 rscheme =
"urn:simple";
613 QDomElement celem = elems.at(0).toElement();
614 ctext = celem.text();
615 if (celem.hasAttribute(
"url"))
616 curl = celem.attribute(
"url");
632 QDomElement comm = comms.at(0).toElement();
637 QDomElement ratingDom = stars.at(0).toElement();
638 raverage =
GetInt(ratingDom,
"average");
639 rcount =
GetInt(ratingDom,
"count");
640 rmin =
GetInt(ratingDom,
"min");
641 rmax =
GetInt(ratingDom,
"max");
648 QDomElement stat = stats.at(0).toElement();
649 views =
GetInt(stat,
"views");
650 favs =
GetInt(stat,
"favorites");
657 QDomElement tag = tagsNode.at(0).toElement();
694 const QString
Parse::kDC =
"http://purl.org/dc/elements/1.1/";
695 const QString
Parse::kWFW =
"http://wellformedweb.org/CommentAPI/";
696 const QString
Parse::kAtom =
"http://www.w3.org/2005/Atom";
697 const QString
Parse::kRDF =
"http://www.w3.org/1999/02/22-rdf-syntax-ns#";
698 const QString
Parse::kSlash =
"http://purl.org/rss/1.0/modules/slash/";
699 const QString
Parse::kEnc =
"http://purl.oclc.org/net/rss_2.0/enc#";
700 const QString
Parse::kITunes =
"http://www.itunes.com/dtds/podcast-1.0.dtd";
702 const QString
Parse::kGeoRSSW3 =
"http://www.w3.org/2003/01/geo/wgs84_pos#";
704 const QString
Parse::kMythRSS =
"http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format";
715 QString document = domDoc.toString();
716 LOG(VB_GENERAL, LOG_DEBUG,
"Will Be Parsing: " + document);
718 QDomElement root = domDoc.documentElement();
719 QDomElement channel = root.firstChildElement(
"channel");
720 while (!channel.isNull())
722 QDomElement item = channel.firstChildElement(
"item");
723 while (!item.isNull())
726 item = item.nextSiblingElement(
"item");
728 channel = channel.nextSiblingElement(
"channel");
737 QString subtitle(
"");
738 QString description(
"");
741 QString duration(
"");
743 QString thumbnail(
"");
744 QString mediaURL(
"");
746 QString language(
"");
747 QString download(
"");
754 QStringList playerargs;
755 QStringList downloadargs;
756 QStringList countries;
757 bool downloadable =
true;
758 bool customhtml =
false;
761 title = item.firstChildElement(
"title").text();
767 QDomNodeList subt = item.elementsByTagNameNS(
kMythRSS,
"subtitle");
770 subtitle = subt.at(0).toElement().text();
774 QDomElement descriptiontemp = item.firstChildElement(
"description");
775 if (!descriptiontemp.isNull())
776 description = descriptiontemp.text();
777 if (description.isEmpty())
779 QDomNodeList nodes = item.elementsByTagNameNS(
kITunes,
"summary");
781 description = nodes.at(0).toElement().text();
784 if (description.isEmpty())
790 url = item.firstChildElement(
"link").text();
793 QDomElement authortemp = item.firstChildElement(
"author");
794 if (!authortemp.isNull())
795 author = authortemp.text();
796 if (author.isEmpty())
801 if (!date.isValid() || date.isNull())
803 if (!date.isValid() || date.isNull())
807 QDomNodeList dur = item.elementsByTagNameNS(
kITunes,
"duration");
810 QString itunestime = dur.at(0).toElement().text();
813 if (itunestime.size() == 8)
815 else if (itunestime.size() == 7)
817 else if (itunestime.size() == 5)
819 else if (itunestime.size() == 4)
821 else if (itunestime.size() == 2)
831 int seconds = itime.second() + (itime.minute() * 60) + (itime.hour() * 3600);
832 duration = QString::number(seconds);
838 QDomElement ratingtemp = item.firstChildElement(
"rating");
839 if (!ratingtemp.isNull())
840 rating = ratingtemp.text();
843 QDomElement playertemp = item.firstChildElement(
"player");
844 if (!playertemp.isNull())
845 player = playertemp.text();
848 QDomElement playerargstemp = item.firstChildElement(
"playerargs");
849 if (!playerargstemp.isNull())
850 playerargs = playerargstemp.text().split(
" ");
853 QDomElement downloadtemp = item.firstChildElement(
"download");
854 if (!downloadtemp.isNull())
855 download = downloadtemp.text();
858 QDomElement downloadargstemp = item.firstChildElement(
"downloadargs");
859 if (!downloadargstemp.isNull())
860 downloadargs = downloadargstemp.text().split(
" ");
863 QDomNodeList cties = item.elementsByTagNameNS(
kMythRSS,
"country");
867 while (i < cties.size())
869 countries.append(cties.at(i).toElement().text());
875 QDomNodeList seas = item.elementsByTagNameNS(
kMythRSS,
"season");
878 season = seas.at(0).toElement().text().toUInt();
882 QDomNodeList ep = item.elementsByTagNameNS(
kMythRSS,
"episode");
885 episode = ep.at(0).toElement().text().toUInt();
889 QDomNodeList html = item.elementsByTagNameNS(
kMythRSS,
"customhtml");
892 QString htmlstring = html.at(0).toElement().text();
893 if (htmlstring.contains(
"true", Qt::CaseInsensitive) || htmlstring ==
"1" ||
894 htmlstring.contains(
"yes", Qt::CaseInsensitive))
900 if (!enclosures.empty())
904 QList<MRSSThumbnail> thumbs = media.
Thumbnails;
908 thumbnail = thumb.
URL;
911 mediaURL = media.
URL;
915 language = media.
Lang;
917 if (duration.isEmpty())
918 duration = QString::number(media.
Duration);
921 filesize = media.
Size;
926 if (mediaURL.isEmpty())
941 if (mediaURL.isNull() || mediaURL == url)
942 downloadable =
false;
945 return(
new ResultItem(title, sh->doTitle(title),
946 subtitle, sh->doTitle(subtitle), description,
947 url, thumbnail, mediaURL, author, date, duration,
948 rating, filesize, player, playerargs,
949 download, downloadargs, width, height,
950 language, downloadable, countries, season,
951 episode, customhtml));
957 QDomElement link = parent.firstChildElement(
"link");
958 while(!link.isNull())
960 if (!link.hasAttribute(
"rel") || link.attribute(
"rel") ==
"alternate")
962 if (!link.hasAttribute(
"href"))
963 result = link.text();
965 result = link.attribute(
"href");
968 link = link.nextSiblingElement(
"link");
976 QDomNodeList nodes = parent.elementsByTagNameNS(
kITunes,
980 result = nodes.at(0).toElement().text();
984 nodes = parent.elementsByTagNameNS(
kDC,
988 result = nodes.at(0).toElement().text();
998 QDomNodeList nodes = parent.elementsByTagNameNS(
kWFW,
1001 result = nodes.at(0).toElement().text();
1008 QDomNodeList nodes = parent.elementsByTagNameNS(
"",
"comments");
1010 result = nodes.at(0).toElement().text();
1016 QDomNodeList dates = parent.elementsByTagNameNS(
kDC,
"date");
1019 return FromRFC3339(dates.at(0).toElement().text());
1024 static const QRegularExpression kNonDigitRE { R
"(\D)" };
1029 QString time =
t.simplified();
1030 short int hoursShift = 0;
1031 short int minutesShift = 0;
1033 QStringList
tmp = time.split(
' ');
1036 if (
tmp.at(0).contains(kNonDigitRE))
1038 if (
tmp.size() != 5)
1040 QString tmpTimezone =
tmp.takeAt(
tmp.size() -1);
1041 if (tmpTimezone.size() == 5)
1044 int tz = tmpTimezone.toInt(&ok);
1047 hoursShift = tz / 100;
1048 minutesShift = tz % 100;
1054 if (
tmp.at(0).size() == 1)
1055 tmp[0].prepend(
"0");
1056 tmp [1].truncate(3);
1058 time =
tmp.join(
" ");
1061 if (
tmp.at(2).size() == 4)
1062 result = QLocale::c().toDateTime(time,
"dd MMM yyyy hh:mm:ss");
1064 result = QLocale::c().toDateTime(time,
"dd MMM yy hh:mm:ss");
1065 if (result.isNull() || !result.isValid())
1067 result = result.addSecs(hoursShift * 3600 * (-1) + minutesShift *60 * (-1));
1068 result.setTimeSpec(Qt::UTC);
1077 static const QRegularExpression fractionalSeconds { R
"(\.(\d+))" };
1078 auto match = fractionalSeconds.match(
t);
1079 if (match.hasMatch())
1082 int fractional = match.capturedView(1).toInt(&ok);
1085 if (fractional < 100)
1089 result = result.addMSecs(fractional);
1092 static const QRegularExpression timeZone { R
"((\+|\-)(\d\d):(\d\d)$)" };
1093 match = timeZone.match(t);
1094 if (match.hasMatch())
1096 short int multiplier = -1;
1097 if (match.captured(1) ==
"-")
1099 int hoursShift = match.capturedView(2).toInt();
1100 int minutesShift = match.capturedView(3).toInt();
1101 result = result.addSecs(hoursShift * 3600 * multiplier + minutesShift * 60 * multiplier);
1103 result.setTimeSpec(Qt::UTC);
1109 QList<Enclosure> result;
1110 QDomNodeList links = entry.elementsByTagName(
"enclosure");
1111 for (
int i = 0; i < links.size(); ++i)
1113 QDomElement link = links.at(i).toElement();
1117 link.attribute(
"url"),
1118 link.attribute(
"type"),
1119 link.attribute(
"length",
"-1").toLongLong(),
1120 link.attribute(
"hreflang")
1135 QString result = escaped;
1136 result.replace(
"&",
"&");
1137 result.replace(
"<",
"<");
1138 result.replace(
">",
">");
1139 result.replace(
"'",
"\'");
1140 result.replace(
"’",
"\'");
1141 result.replace(
"’",
"\'");
1142 result.replace(
""",
"\"");
1143 result.replace(
"…",QChar(8230));
1144 result.replace(
"é",QChar(233));
1145 result.replace(
"—", QChar(8212));
1146 result.replace(
" ",
" ");
1147 result.replace(
" ", QChar(160));
1148 result.replace(
"á", QChar(225));
1149 result.replace(
"‘", QChar(8216));
1150 result.replace(
"’", QChar(8217));
1151 result.replace(
"'",
"\'");
1152 result.replace(
"–", QChar(8211));
1153 result.replace(
"ä", QChar(0x00e4));
1154 result.replace(
"ö", QChar(0x00f6));
1155 result.replace(
"ü", QChar(0x00fc));
1156 result.replace(
"Ä", QChar(0x00c4));
1157 result.replace(
"Ö", QChar(0x00d6));
1158 result.replace(
"Ü", QChar(0x00dc));
1159 result.replace(
"ß", QChar(0x00df));
1160 result.replace(
"€",
"€");
1161 result.replace(
"…",
"...");
1162 result.replace(
"®", QChar(0x00ae));
1163 result.replace(
"“", QChar(0x201c));
1164 result.replace(
"”", QChar(0x201d));
1165 result.replace(
"<p>",
"\n");
1167 static const QRegularExpression kStripHtmlRE {
"<.*?>"};
1168 result.remove(kStripHtmlRE);