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 for (
int i = 0, size = unf.size(); i < size; ++i)
182 if (unf.at(i).parentNode() == elem)
220 if (!child.
m_url.isEmpty())
248 if (!child.
m_tags.isEmpty())
266 QList<MRSSEntry> result;
271 for (
int i = 0; i < groups.size(); ++i)
283 QList<MRSSEntry> result;
287 for (
int i = 0; i < entries.size(); ++i)
291 QDomElement en = entries.at(i).toElement();
294 if (en.hasAttribute(
"url"))
295 entry.
URL = en.attribute(
"url");
299 entry.
Size = en.attribute(
"fileSize").toInt();
300 entry.
Type = en.attribute(
"type");
301 entry.
Medium = en.attribute(
"medium");
302 entry.
IsDefault = (en.attribute(
"isDefault") ==
"true");
303 entry.
Expression = en.attribute(
"expression");
306 entry.
Bitrate = en.attribute(
"bitrate").toInt();
307 entry.
Framerate = en.attribute(
"framerate").toDouble();
308 entry.
SamplingRate = en.attribute(
"samplingrate").toDouble();
309 entry.
Channels = en.attribute(
"channels").toInt();
310 if (!en.attribute(
"duration").isNull())
311 entry.
Duration = en.attribute(
"duration").toInt();
314 if (!en.attribute(
"width").isNull())
315 entry.
Width = en.attribute(
"width").toInt();
318 if (!en.attribute(
"height").isNull())
319 entry.
Height = en.attribute(
"height").toInt();
322 if (!en.attribute(
"lang").isNull())
323 entry.
Lang = en.attribute(
"lang");
325 entry.
Lang = QString();
327 if (!en.attribute(
"rating").isNull())
337 if (
d.m_ratingAverage != 0)
345 entry.
Favs =
d.m_favs;
346 entry.
Tags =
d.m_tags;
362 QList<QDomElement> parents;
363 QDomElement parent = holder;
364 while (!parent.isNull())
366 parents.prepend(parent);
367 parent = parent.parentNode().toElement();
370 for (
const auto&
p : std::as_const(parents))
376 static QString
GetURL(
const QDomElement& element)
383 return elems.at(0).toElement().attribute(
"url");
386 static QString
GetTitle(
const QDomElement& element)
394 QDomElement telem = elems.at(0).toElement();
406 QDomElement telem = elems.at(0).toElement();
418 QDomElement telem = elems.at(0).toElement();
422 static int GetInt(
const QDomElement& elem,
const QString& attrname)
424 if (elem.hasAttribute(attrname))
427 int result = elem.attribute(attrname).toInt(&ok);
436 QList<MRSSThumbnail> result;
439 for (
const auto& dom : std::as_const(thumbs))
441 QDomElement thumbNode = dom.toElement();
442 int widthOpt =
GetInt(thumbNode,
"width");
443 int width = widthOpt ? widthOpt : 0;
444 int heightOpt =
GetInt(thumbNode,
"height");
445 int height = heightOpt ? heightOpt : 0;
448 .
URL=thumbNode.attribute(
"url"),
451 .Time=thumbNode.attribute(
"time")
458 static QList<MRSSCredit>
GetCredits(
const QDomElement& element)
460 QList<MRSSCredit> result;
464 for (
const auto& dom : std::as_const(credits))
466 QDomElement creditNode = dom.toElement();
467 if (!creditNode.hasAttribute(
"role"))
471 .
Role=creditNode.attribute(
"role"),
472 .Who=creditNode.text()
481 QList<MRSSComment> result;
485 if (!commParents.empty())
487 QDomNodeList comments = commParents.at(0).toElement()
490 for (
int i = 0; i < comments.size(); ++i)
494 .
Type=QObject::tr(
"Comments"),
495 .Comment=comments.at(i).toElement().text()
504 if (!respParents.empty())
506 QDomNodeList responses = respParents.at(0).toElement()
509 for (
int i = 0; i < responses.size(); ++i)
513 .
Type=QObject::tr(
"Responses"),
514 .Comment=responses.at(i).toElement().text()
523 if (!backParents.empty())
525 QDomNodeList backlinks = backParents.at(0).toElement()
528 for (
int i = 0; i < backlinks.size(); ++i)
532 .
Type=QObject::tr(
"Backlinks"),
533 .Comment=backlinks.at(i).toElement().text()
543 QList<MRSSPeerLink> result;
547 for (
const auto& dom : std::as_const(links))
549 QDomElement linkNode = dom.toElement();
552 .
Type=linkNode.attribute(
"type"),
553 .Link=linkNode.attribute(
"href")
560 static QList<MRSSScene>
GetScenes(
const QDomElement& element)
562 QList<MRSSScene> result;
566 if (!scenesNode.empty())
568 QDomNodeList scenesNodes = scenesNode.at(0).toElement()
571 for (
int i = 0; i < scenesNodes.size(); ++i)
573 QDomElement sceneNode = scenesNodes.at(i).toElement();
576 .
Title=sceneNode.firstChildElement(
"sceneTitle").text(),
577 .Description=sceneNode.firstChildElement(
"sceneDescription").text(),
578 .StartTime=sceneNode.firstChildElement(
"sceneStartTime").text(),
579 .EndTime=sceneNode.firstChildElement(
"sceneEndTime").text()
598 QDomElement relem = elems.at(0).toElement();
600 if (relem.hasAttribute(
"scheme"))
601 rscheme = relem.attribute(
"scheme");
603 rscheme =
"urn:simple";
615 QDomElement celem = elems.at(0).toElement();
616 ctext = celem.text();
617 if (celem.hasAttribute(
"url"))
618 curl = celem.attribute(
"url");
634 QDomElement comm = comms.at(0).toElement();
639 QDomElement ratingDom = stars.at(0).toElement();
640 raverage =
GetInt(ratingDom,
"average");
641 rcount =
GetInt(ratingDom,
"count");
642 rmin =
GetInt(ratingDom,
"min");
643 rmax =
GetInt(ratingDom,
"max");
650 QDomElement stat = stats.at(0).toElement();
651 views =
GetInt(stat,
"views");
652 favs =
GetInt(stat,
"favorites");
659 QDomElement tag = tagsNode.at(0).toElement();
696const QString
Parse::kDC =
"http://purl.org/dc/elements/1.1/";
697const QString
Parse::kWFW =
"http://wellformedweb.org/CommentAPI/";
698const QString
Parse::kAtom =
"http://www.w3.org/2005/Atom";
699const QString
Parse::kRDF =
"http://www.w3.org/1999/02/22-rdf-syntax-ns#";
700const QString
Parse::kSlash =
"http://purl.org/rss/1.0/modules/slash/";
701const QString
Parse::kEnc =
"http://purl.oclc.org/net/rss_2.0/enc#";
702const QString
Parse::kITunes =
"http://www.itunes.com/dtds/podcast-1.0.dtd";
706const QString
Parse::kMythRSS =
"http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format";
717 QString document = domDoc.toString();
718 LOG(VB_GENERAL, LOG_DEBUG,
"Will Be Parsing: " + document);
720 QDomElement root = domDoc.documentElement();
721 QDomElement channel = root.firstChildElement(
"channel");
722 while (!channel.isNull())
724 QDomElement item = channel.firstChildElement(
"item");
725 while (!item.isNull())
728 item = item.nextSiblingElement(
"item");
730 channel = channel.nextSiblingElement(
"channel");
739 QString subtitle(
"");
740 QString description(
"");
743 QString duration(
"");
745 QString thumbnail(
"");
746 QString mediaURL(
"");
748 QString language(
"");
749 QString download(
"");
756 QStringList playerargs;
757 QStringList downloadargs;
758 QStringList countries;
759 bool downloadable =
true;
760 bool customhtml =
false;
763 title = item.firstChildElement(
"title").text();
769 QDomNodeList subt = item.elementsByTagNameNS(
kMythRSS,
"subtitle");
772 subtitle = subt.at(0).toElement().text();
776 QDomElement descriptiontemp = item.firstChildElement(
"description");
777 if (!descriptiontemp.isNull())
778 description = descriptiontemp.text();
779 if (description.isEmpty())
781 QDomNodeList nodes = item.elementsByTagNameNS(
kITunes,
"summary");
783 description = nodes.at(0).toElement().text();
786 if (description.isEmpty())
792 url = item.firstChildElement(
"link").text();
795 QDomElement authortemp = item.firstChildElement(
"author");
796 if (!authortemp.isNull())
797 author = authortemp.text();
798 if (author.isEmpty())
803 if (!date.isValid() || date.isNull())
805 if (!date.isValid() || date.isNull())
809 QDomNodeList dur = item.elementsByTagNameNS(
kITunes,
"duration");
812 QString itunestime = dur.at(0).toElement().text();
815 if (itunestime.size() == 8)
817 else if (itunestime.size() == 7)
819 else if (itunestime.size() == 5)
821 else if (itunestime.size() == 4)
823 else if (itunestime.size() == 2)
833 int seconds = itime.second() + (itime.minute() * 60) + (itime.hour() * 3600);
834 duration = QString::number(seconds);
840 QDomElement ratingtemp = item.firstChildElement(
"rating");
841 if (!ratingtemp.isNull())
842 rating = ratingtemp.text();
845 QDomElement playertemp = item.firstChildElement(
"player");
846 if (!playertemp.isNull())
847 player = playertemp.text();
850 QDomElement playerargstemp = item.firstChildElement(
"playerargs");
851 if (!playerargstemp.isNull())
852 playerargs = playerargstemp.text().split(
" ");
855 QDomElement downloadtemp = item.firstChildElement(
"download");
856 if (!downloadtemp.isNull())
857 download = downloadtemp.text();
860 QDomElement downloadargstemp = item.firstChildElement(
"downloadargs");
861 if (!downloadargstemp.isNull())
862 downloadargs = downloadargstemp.text().split(
" ");
865 QDomNodeList cties = item.elementsByTagNameNS(
kMythRSS,
"country");
869 while (i < cties.size())
871 countries.append(cties.at(i).toElement().text());
877 QDomNodeList seas = item.elementsByTagNameNS(
kMythRSS,
"season");
880 season = seas.at(0).toElement().text().toUInt();
884 QDomNodeList ep = item.elementsByTagNameNS(
kMythRSS,
"episode");
887 episode = ep.at(0).toElement().text().toUInt();
891 QDomNodeList html = item.elementsByTagNameNS(
kMythRSS,
"customhtml");
894 QString htmlstring = html.at(0).toElement().text();
895 if (htmlstring.contains(
"true", Qt::CaseInsensitive) || htmlstring ==
"1" ||
896 htmlstring.contains(
"yes", Qt::CaseInsensitive))
902 if (!enclosures.empty())
906 QList<MRSSThumbnail> thumbs = media.
Thumbnails;
910 thumbnail = thumb.
URL;
913 mediaURL = media.
URL;
917 language = media.
Lang;
919 if (duration.isEmpty())
920 duration = QString::number(media.
Duration);
923 filesize = media.
Size;
928 if (mediaURL.isEmpty())
943 if (mediaURL.isNull() || mediaURL == url)
944 downloadable =
false;
947 return(
new ResultItem(title, sh->doTitle(title),
948 subtitle, sh->doTitle(subtitle), description,
949 url, thumbnail, mediaURL, author, date, duration,
950 rating, filesize, player, playerargs,
951 download, downloadargs, width, height,
952 language, downloadable, countries, season,
953 episode, customhtml));
959 QDomElement link = parent.firstChildElement(
"link");
960 while(!link.isNull())
962 if (!link.hasAttribute(
"rel") || link.attribute(
"rel") ==
"alternate")
964 if (!link.hasAttribute(
"href"))
965 result = link.text();
967 result = link.attribute(
"href");
970 link = link.nextSiblingElement(
"link");
978 QDomNodeList nodes = parent.elementsByTagNameNS(
kITunes,
982 result = nodes.at(0).toElement().text();
986 nodes = parent.elementsByTagNameNS(
kDC,
990 result = nodes.at(0).toElement().text();
1000 QDomNodeList nodes = parent.elementsByTagNameNS(
kWFW,
1003 result = nodes.at(0).toElement().text();
1010 QDomNodeList nodes = parent.elementsByTagNameNS(
"",
"comments");
1012 result = nodes.at(0).toElement().text();
1018 QDomNodeList dates = parent.elementsByTagNameNS(
kDC,
"date");
1021 return FromRFC3339(dates.at(0).toElement().text());
1026 static const QRegularExpression kNonDigitRE { R
"(\D)" };
1031 QString time =
t.simplified();
1032 short int hoursShift = 0;
1033 short int minutesShift = 0;
1035 QStringList tmp = time.split(
' ');
1038 if (tmp.at(0).contains(kNonDigitRE))
1040 if (tmp.size() != 5)
1042 QString tmpTimezone = tmp.takeAt(tmp.size() -1);
1043 if (tmpTimezone.size() == 5)
1046 int tz = tmpTimezone.toInt(&ok);
1049 hoursShift = tz / 100;
1050 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#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
1073 result.setTimeSpec(Qt::UTC);
1075 result.setTimeZone(QTimeZone(QTimeZone::UTC));
1085 static const QRegularExpression fractionalSeconds { R
"(\.(\d+))" };
1086 auto match = fractionalSeconds.match(
t);
1087 if (match.hasMatch())
1090 int fractional = match.capturedView(1).toInt(&ok);
1093 if (fractional < 100)
1097 result = result.addMSecs(fractional);
1100 static const QRegularExpression timeZone { R
"((\+|\-)(\d\d):(\d\d)$)" };
1101 match = timeZone.match(t);
1102 if (match.hasMatch())
1104 short int multiplier = -1;
1105 if (match.captured(1) ==
"-")
1107 int hoursShift = match.capturedView(2).toInt();
1108 int minutesShift = match.capturedView(3).toInt();
1109 result = result.addSecs((hoursShift * 3600 * multiplier) + (minutesShift * 60 * multiplier));
1111#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
1112 result.setTimeSpec(Qt::UTC);
1114 result.setTimeZone(QTimeZone(QTimeZone::UTC));
1121 QList<Enclosure> result;
1122 QDomNodeList links = entry.elementsByTagName(
"enclosure");
1123 for (
int i = 0; i < links.size(); ++i)
1125 QDomElement link = links.at(i).toElement();
1129 .
URL=link.attribute(
"url"),
1130 .Type=link.attribute(
"type"),
1131 .Length=link.attribute(
"length",
"-1").toLongLong(),
1132 .Lang=link.attribute(
"hreflang")
1147 QString result = escaped;
1148 result.replace(
"&",
"&");
1149 result.replace(
"<",
"<");
1150 result.replace(
">",
">");
1151 result.replace(
"'",
"\'");
1152 result.replace(
"’",
"\'");
1153 result.replace(
"’",
"\'");
1154 result.replace(
""",
"\"");
1155 result.replace(
"…",QChar(8230));
1156 result.replace(
"é",QChar(233));
1157 result.replace(
"—", QChar(8212));
1158 result.replace(
" ",
" ");
1159 result.replace(
" ", QChar(160));
1160 result.replace(
"á", QChar(225));
1161 result.replace(
"‘", QChar(8216));
1162 result.replace(
"’", QChar(8217));
1163 result.replace(
"'",
"\'");
1164 result.replace(
"–", QChar(8211));
1165 result.replace(
"ä", QChar(0x00e4));
1166 result.replace(
"ö", QChar(0x00f6));
1167 result.replace(
"ü", QChar(0x00fc));
1168 result.replace(
"Ä", QChar(0x00c4));
1169 result.replace(
"Ö", QChar(0x00d6));
1170 result.replace(
"Ü", QChar(0x00dc));
1171 result.replace(
"ß", QChar(0x00df));
1172 result.replace(
"€",
"€");
1173 result.replace(
"…",
"...");
1174 result.replace(
"®", QChar(0x00ae));
1175 result.replace(
"“", QChar(0x201c));
1176 result.replace(
"”", QChar(0x201d));
1177 result.replace(
"<p>",
"\n");
1179 static const QRegularExpression kStripHtmlRE {
"<.*?>"};
1180 result.remove(kStripHtmlRE);
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.