MythTV  master
rssparse.cpp
Go to the documentation of this file.
1 #include <QFile>
2 #include <QDataStream>
3 #include <QDomDocument>
4 #include <QDomImplementation>
5 #include <QHash>
6 #include <QLocale>
7 #include <QUrl>
8 #include <QFileInfo>
9 #include <QRegExp>
10 
11 #include "rssparse.h"
12 #include "mythcontext.h"
13 #include "mythdirs.h"
14 #include "mythdate.h"
15 #include "programinfo.h" // for format_season_and_episode()
16 #include "mythsorthelper.h"
17 
18 using namespace std;
19 
20 ResultItem::ResultItem(const QString& title, const QString& sortTitle,
21  const QString& subtitle, const QString& sortSubtitle,
22  const QString& desc, const QString& URL,
23  const QString& thumbnail, const QString& mediaURL,
24  const QString& author, const QDateTime& date,
25  const QString& time, const QString& rating,
26  const off_t& filesize, const QString& player,
27  const QStringList& playerargs, const QString& download,
28  const QStringList& downloadargs, const uint& width,
29  const uint& height, const QString& language,
30  const bool& downloadable, const QStringList& countries,
31  const uint& season, const uint& episode,
32  const bool& customhtml)
33 {
34  m_title = title;
35  m_sorttitle = sortTitle;
36  m_subtitle = subtitle;
37  m_sortsubtitle = sortSubtitle;
38  m_desc = desc;
39  m_URL = URL;
40  m_thumbnail = thumbnail;
41  m_mediaURL = mediaURL;
42  m_author = author;
43  if (!date.isNull())
44  m_date = date;
45  else
46  m_date = QDateTime();
47  m_time = time;
48  m_rating = rating;
49  m_filesize = filesize;
50  m_player = player;
51  m_playerargs = playerargs;
52  m_download = download;
53  m_downloadargs = downloadargs;
54  m_width = width;
55  m_height = height;
56  m_language = language;
57  m_downloadable = downloadable;
58  m_countries = countries;
59  m_season = season;
60  m_episode = episode;
61  m_customhtml = customhtml;
62 
63  ensureSortFields();
64 }
65 
67 {
68  std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
69 
70  if (m_sorttitle.isEmpty() and not m_title.isEmpty())
71  m_sorttitle = sh->doTitle(m_title);
72  if (m_sortsubtitle.isEmpty() and not m_subtitle.isEmpty())
73  m_sortsubtitle = sh->doTitle(m_subtitle);
74 }
75 
76 void ResultItem::toMap(InfoMap &metadataMap)
77 {
78  metadataMap["title"] = m_title;
79  metadataMap["sorttitle"] = m_sorttitle;
80  metadataMap["subtitle"] = m_subtitle;
81  metadataMap["sortsubtitle"] = m_sortsubtitle;
82  metadataMap["description"] = m_desc;
83  metadataMap["url"] = m_URL;
84  metadataMap["thumbnail"] = m_thumbnail;
85  metadataMap["mediaurl"] = m_mediaURL;
86  metadataMap["author"] = m_author;
87 
88  if (m_date.isNull())
89  metadataMap["date"] = QString();
90  else
91  metadataMap["date"] = MythDate::toString(m_date, MythDate::kDateFull);
92 
93  if (m_time.toInt() == 0)
94  metadataMap["length"] = QString();
95  else
96  {
97  QTime time(0,0,0,0);
98  int secs = m_time.toInt();
99  QTime fin = time.addSecs(secs);
100  QString format;
101  if (secs >= 3600)
102  format = "H:mm:ss";
103  else if (secs >= 600)
104  format = "mm:ss";
105  else if (secs >= 60)
106  format = "m:ss";
107  else
108  format = ":ss";
109  metadataMap["length"] = fin.toString(format);
110  }
111 
112  if (m_rating == nullptr || m_rating.isNull())
113  metadataMap["rating"] = QString();
114  else
115  metadataMap["rating"] = m_rating;
116 
117  if (m_filesize == -1)
118  metadataMap["filesize"] = QString();
119  else if (m_filesize == 0 && !m_downloadable)
120  metadataMap["filesize"] = QObject::tr("Web Only");
121  else if (m_filesize == 0 && m_downloadable)
122  metadataMap["filesize"] = QObject::tr("Downloadable");
123  else
124  metadataMap["filesize"] = QString::number(m_filesize);
125 
126  QString tmpSize;
127  tmpSize.sprintf("%0.2f ", m_filesize / 1024.0 / 1024.0);
128  tmpSize += QObject::tr("MB", "Megabytes");
129  if (m_filesize == -1)
130  metadataMap["filesize_str"] = QString();
131  else if (m_filesize == 0 && !m_downloadable)
132  metadataMap["filesize_str"] = QObject::tr("Web Only");
133  else if (m_filesize == 0 && m_downloadable)
134  metadataMap["filesize_str"] = QObject::tr("Downloadable");
135  else
136  metadataMap["filesize"] = tmpSize;
137 
138  metadataMap["player"] = m_player;
139  metadataMap["playerargs"] = m_playerargs.join(", ");
140  metadataMap["downloader"] = m_download;
141  metadataMap["downloadargs"] = m_downloadargs.join(", ");
142  if (m_width == 0)
143  metadataMap["width"] = QString();
144  else
145  metadataMap["width"] = QString::number(m_width);
146  if (m_height == 0)
147  metadataMap["height"] = QString();
148  else
149  metadataMap["height"] = QString::number(m_height);
150  if (m_width == 0 || m_height == 0)
151  metadataMap["resolution"] = QString();
152  else
153  metadataMap["resolution"] = QString("%1x%2").arg(m_width).arg(m_height);
154  metadataMap["language"] = m_language;
155  metadataMap["countries"] = m_countries.join(", ");
156 
157 
158  if (m_season > 0 || m_episode > 0)
159  {
160  metadataMap["season"] = format_season_and_episode(m_season, 1);
161  metadataMap["episode"] = format_season_and_episode(m_episode, 1);
162  metadataMap["s##e##"] = metadataMap["s00e00"] = QString("s%1e%2")
163  .arg(format_season_and_episode(m_season, 2))
164  .arg(format_season_and_episode(m_episode, 2));
165  metadataMap["##x##"] = metadataMap["00x00"] = QString("%1x%2")
166  .arg(format_season_and_episode(m_season, 1))
167  .arg(format_season_and_episode(m_episode, 2));
168  }
169  else
170  {
171  metadataMap["season"] = QString();
172  metadataMap["episode"] = QString();
173  metadataMap["s##e##"] = metadataMap["s00e00"] = QString();
174  metadataMap["##x##"] = metadataMap["00x00"] = QString();
175  }
176 }
177 
178 namespace
179 {
180  QList<QDomNode> GetDirectChildrenNS(const QDomElement& elem,
181  const QString& ns, const QString& name)
182  {
183  QList<QDomNode> result;
184  QDomNodeList unf = elem.elementsByTagNameNS(ns, name);
185  for (int i = 0, size = unf.size(); i < size; ++i)
186  if (unf.at(i).parentNode() == elem)
187  result << unf.at(i);
188  return result;
189  }
190 }
191 
193 {
195  {
196  QString URL;
197  QString Rating;
198  QString RatingScheme;
199  QString Title;
200  QString Description;
201  QString Keywords;
202  QString CopyrightURL;
203  QString CopyrightText;
204  int RatingAverage {0};
205  int RatingCount {0};
206  int RatingMin {0};
207  int RatingMax {0};
208  int Views {0};
209  int Favs {0};
210  QString Tags;
211  QList<MRSSThumbnail> Thumbnails;
212  QList<MRSSCredit> Credits;
213  QList<MRSSComment> Comments;
214  QList<MRSSPeerLink> PeerLinks;
215  QList<MRSSScene> Scenes;
216 
217  ArbitraryLocatedData() = default;
218 
222  ArbitraryLocatedData& operator+= (const ArbitraryLocatedData& child)
223  {
224  if (!child.URL.isEmpty())
225  URL = child.URL;
226  if (!child.Rating.isEmpty())
227  Rating = child.Rating;
228  if (!child.RatingScheme.isEmpty())
229  RatingScheme = child.RatingScheme;
230  if (!child.Title.isEmpty())
231  Title = child.Title;
232  if (!child.Description.isEmpty())
233  Description = child.Description;
234  if (!child.Keywords.isEmpty())
235  Keywords = child.Keywords;
236  if (!child.CopyrightURL.isEmpty())
237  CopyrightURL = child.CopyrightURL;
238  if (!child.CopyrightText.isEmpty())
239  CopyrightText = child.CopyrightText;
240  if (child.RatingAverage != 0)
241  RatingAverage = child.RatingAverage;
242  if (child.RatingCount != 0)
243  RatingCount = child.RatingCount;
244  if (child.RatingMin != 0)
245  RatingMin = child.RatingMin;
246  if (child.RatingMax != 0)
247  RatingMax = child.RatingMax;
248  if (child.Views != 0)
249  Views = child.Views;
250  if (child.Favs != 0)
251  Favs = child.Favs;
252  if (!child.Tags.isEmpty())
253  Tags = child.Tags;
254 
255  Thumbnails += child.Thumbnails;
256  Credits += child.Credits;
257  Comments += child.Comments;
258  PeerLinks += child.PeerLinks;
259  Scenes += child.Scenes;
260  return *this;
261  }
262  };
263 
264 
265 public:
266  MRSSParser() = default;
267 
268  QList<MRSSEntry> operator() (const QDomElement& item)
269  {
270  QList<MRSSEntry> result;
271 
272  QDomNodeList groups = item.elementsByTagNameNS(Parse::s_MediaRSS,
273  "group");
274 
275  for (int i = 0; i < groups.size(); ++i)
276  result += CollectChildren(groups.at(i).toElement());
277 
278  result += CollectChildren(item);
279 
280  return result;
281  }
282 
283 private:
284 
285  QList<MRSSEntry> CollectChildren(const QDomElement& holder)
286  {
287  QList<MRSSEntry> result;
288  QDomNodeList entries = holder.elementsByTagNameNS(Parse::s_MediaRSS,
289  "content");
290 
291  for (int i = 0; i < entries.size(); ++i)
292  {
293  MRSSEntry entry;
294 
295  QDomElement en = entries.at(i).toElement();
296  ArbitraryLocatedData d = GetArbitraryLocatedDataFor(en);
297 
298  if (en.hasAttribute("url"))
299  entry.URL = en.attribute("url");
300  else
301  entry.URL = d.URL;
302 
303  entry.Size = en.attribute("fileSize").toInt();
304  entry.Type = en.attribute("type");
305  entry.Medium = en.attribute("medium");
306  entry.IsDefault = (en.attribute("isDefault") == "true");
307  entry.Expression = en.attribute("expression");
308  if (entry.Expression.isEmpty())
309  entry.Expression = "full";
310  entry.Bitrate = en.attribute("bitrate").toInt();
311  entry.Framerate = en.attribute("framerate").toDouble();
312  entry.SamplingRate = en.attribute("samplingrate").toDouble();
313  entry.Channels = en.attribute("channels").toInt();
314  if (!en.attribute("duration").isNull())
315  entry.Duration = en.attribute("duration").toInt();
316  else
317  entry.Duration = 0;
318  if (!en.attribute("width").isNull())
319  entry.Width = en.attribute("width").toInt();
320  else
321  entry.Width = 0;
322  if (!en.attribute("height").isNull())
323  entry.Height = en.attribute("height").toInt();
324  else
325  entry.Height = 0;
326  if (!en.attribute("lang").isNull())
327  entry.Lang = en.attribute("lang");
328  else
329  entry.Lang = QString();
330 
331  if (!en.attribute("rating").isNull())
332  entry.Rating = d.Rating;
333  else
334  entry.Rating = QString();
335  entry.RatingScheme = d.RatingScheme;
336  entry.Title = d.Title;
337  entry.Description = d.Description;
338  entry.Keywords = d.Keywords;
339  entry.CopyrightURL = d.CopyrightURL;
340  entry.CopyrightText = d.CopyrightText;
341  if (d.RatingAverage != 0)
342  entry.RatingAverage = d.RatingAverage;
343  else
344  entry.RatingAverage = 0;
345  entry.RatingCount = d.RatingCount;
346  entry.RatingMin = d.RatingMin;
347  entry.RatingMax = d.RatingMax;
348  entry.Views = d.Views;
349  entry.Favs = d.Favs;
350  entry.Tags = d.Tags;
351  entry.Thumbnails = d.Thumbnails;
352  entry.Credits = d.Credits;
353  entry.Comments = d.Comments;
354  entry.PeerLinks = d.PeerLinks;
355  entry.Scenes = d.Scenes;
356 
357  result << entry;
358  }
359  return result;
360  }
361 
363  {
364  ArbitraryLocatedData result;
365 
366  QList<QDomElement> parents;
367  QDomElement parent = holder;
368  while (!parent.isNull())
369  {
370  parents.prepend(parent);
371  parent = parent.parentNode().toElement();
372  }
373 
374  Q_FOREACH(QDomElement p, parents)
375  result += CollectArbitraryLocatedData(p);
376 
377  return result;
378  }
379 
380  QString GetURL(const QDomElement& element)
381  {
382  QList<QDomNode> elems = GetDirectChildrenNS(element, Parse::s_MediaRSS,
383  "player");
384  if (elems.empty())
385  return QString();
386 
387  return elems.at(0).toElement().attribute("url");
388  }
389 
390  QString GetTitle(const QDomElement& element)
391  {
392  QList<QDomNode> elems = GetDirectChildrenNS(element, Parse::s_MediaRSS,
393  "title");
394 
395  if (elems.empty())
396  return QString();
397 
398  QDomElement telem = elems.at(0).toElement();
399  return Parse::UnescapeHTML(telem.text());
400  }
401 
402  QString GetDescription(const QDomElement& element)
403  {
404  QList<QDomNode> elems = GetDirectChildrenNS(element, Parse::s_MediaRSS,
405  "description");
406 
407  if (elems.empty())
408  return QString();
409 
410  QDomElement telem = elems.at(0).toElement();
411  return Parse::UnescapeHTML(telem.text());
412  }
413 
414  QString GetKeywords(const QDomElement& element)
415  {
416  QList<QDomNode> elems = GetDirectChildrenNS(element, Parse::s_MediaRSS,
417  "keywords");
418 
419  if (elems.empty())
420  return QString();
421 
422  QDomElement telem = elems.at(0).toElement();
423  return telem.text();
424  }
425 
426  int GetInt(const QDomElement& elem, const QString& attrname)
427  {
428  if (elem.hasAttribute(attrname))
429  {
430  bool ok = false;
431  int result = elem.attribute(attrname).toInt(&ok);
432  if (ok)
433  return result;
434  }
435  return int();
436  }
437 
438  QList<MRSSThumbnail> GetThumbnails(const QDomElement& element)
439  {
440  QList<MRSSThumbnail> result;
441  QList<QDomNode> thumbs = GetDirectChildrenNS(element, Parse::s_MediaRSS,
442  "thumbnail");
443  for (int i = 0; i < thumbs.size(); ++i)
444  {
445  QDomElement thumbNode = thumbs.at(i).toElement();
446  int widthOpt = GetInt(thumbNode, "width");
447  int width = widthOpt ? widthOpt : 0;
448  int heightOpt = GetInt(thumbNode, "height");
449  int height = heightOpt ? heightOpt : 0;
450  MRSSThumbnail thumb =
451  {
452  thumbNode.attribute("url"),
453  width,
454  height,
455  thumbNode.attribute("time")
456  };
457  result << thumb;
458  }
459  return result;
460  }
461 
462  QList<MRSSCredit> GetCredits(const QDomElement& element)
463  {
464  QList<MRSSCredit> result;
465  QList<QDomNode> credits = GetDirectChildrenNS(element, Parse::s_MediaRSS,
466  "credit");
467 
468  for (int i = 0; i < credits.size(); ++i)
469  {
470  QDomElement creditNode = credits.at(i).toElement();
471  if (!creditNode.hasAttribute("role"))
472  continue;
473  MRSSCredit credit =
474  {
475  creditNode.attribute("role"),
476  creditNode.text()
477  };
478  result << credit;
479  }
480  return result;
481  }
482 
483  QList<MRSSComment> GetComments(const QDomElement& element)
484  {
485  QList<MRSSComment> result;
486  QList<QDomNode> commParents = GetDirectChildrenNS(element, Parse::s_MediaRSS,
487  "comments");
488 
489  if (!commParents.empty())
490  {
491  QDomNodeList comments = commParents.at(0).toElement()
492  .elementsByTagNameNS(Parse::s_MediaRSS,
493  "comment");
494  for (int i = 0; i < comments.size(); ++i)
495  {
496  MRSSComment comment =
497  {
498  QObject::tr("Comments"),
499  comments.at(i).toElement().text()
500  };
501  result << comment;
502  }
503  }
504 
505  QList<QDomNode> respParents = GetDirectChildrenNS(element, Parse::s_MediaRSS,
506  "responses");
507 
508  if (!respParents.empty())
509  {
510  QDomNodeList responses = respParents.at(0).toElement()
511  .elementsByTagNameNS(Parse::s_MediaRSS,
512  "response");
513  for (int i = 0; i < responses.size(); ++i)
514  {
515  MRSSComment comment =
516  {
517  QObject::tr("Responses"),
518  responses.at(i).toElement().text()
519  };
520  result << comment;
521  }
522  }
523 
524  QList<QDomNode> backParents = GetDirectChildrenNS(element, Parse::s_MediaRSS,
525  "backLinks");
526 
527  if (!backParents.empty())
528  {
529  QDomNodeList backlinks = backParents.at(0).toElement()
530  .elementsByTagNameNS(Parse::s_MediaRSS,
531  "backLink");
532  for (int i = 0; i < backlinks.size(); ++i)
533  {
534  MRSSComment comment =
535  {
536  QObject::tr("Backlinks"),
537  backlinks.at(i).toElement().text()
538  };
539  result << comment;
540  }
541  }
542  return result;
543  }
544 
545  QList<MRSSPeerLink> GetPeerLinks(const QDomElement& element)
546  {
547  QList<MRSSPeerLink> result;
548  QList<QDomNode> links = GetDirectChildrenNS(element, Parse::s_MediaRSS,
549  "peerLink");
550 
551  for (int i = 0; i < links.size(); ++i)
552  {
553  QDomElement linkNode = links.at(i).toElement();
554  MRSSPeerLink pl =
555  {
556  linkNode.attribute("type"),
557  linkNode.attribute("href")
558  };
559  result << pl;
560  }
561  return result;
562  }
563 
564  QList<MRSSScene> GetScenes(const QDomElement& element)
565  {
566  QList<MRSSScene> result;
567  QList<QDomNode> scenesNode = GetDirectChildrenNS(element, Parse::s_MediaRSS,
568  "scenes");
569 
570  if (!scenesNode.empty())
571  {
572  QDomNodeList scenesNodes = scenesNode.at(0).toElement()
573  .elementsByTagNameNS(Parse::s_MediaRSS, "scene");
574 
575  for (int i = 0; i < scenesNodes.size(); ++i)
576  {
577  QDomElement sceneNode = scenesNodes.at(i).toElement();
578  MRSSScene scene =
579  {
580  sceneNode.firstChildElement("sceneTitle").text(),
581  sceneNode.firstChildElement("sceneDescription").text(),
582  sceneNode.firstChildElement("sceneStartTime").text(),
583  sceneNode.firstChildElement("sceneEndTime").text()
584  };
585  result << scene;
586  }
587  }
588  return result;
589  }
590 
592  {
593 
594  QString rating;
595  QString rscheme;
596  {
597  QList<QDomNode> elems = GetDirectChildrenNS(element, Parse::s_MediaRSS,
598  "rating");
599 
600  if (!elems.empty())
601  {
602  QDomElement relem = elems.at(0).toElement();
603  rating = relem.text();
604  if (relem.hasAttribute("scheme"))
605  rscheme = relem.attribute("scheme");
606  else
607  rscheme = "urn:simple";
608  }
609  }
610 
611  QString curl;
612  QString ctext;
613  {
614  QList<QDomNode> elems = GetDirectChildrenNS(element, Parse::s_MediaRSS,
615  "copyright");
616 
617  if (!elems.empty())
618  {
619  QDomElement celem = elems.at(0).toElement();
620  ctext = celem.text();
621  if (celem.hasAttribute("url"))
622  curl = celem.attribute("url");
623  }
624  }
625 
626  int raverage = 0;
627  int rcount = 0;
628  int rmin = 0;
629  int rmax = 0;
630  int views = 0;
631  int favs = 0;
632  QString tags;
633  {
634  QList<QDomNode> comms = GetDirectChildrenNS(element, Parse::s_MediaRSS,
635  "community");
636  if (!comms.empty())
637  {
638  QDomElement comm = comms.at(0).toElement();
639  QDomNodeList stars = comm.elementsByTagNameNS(Parse::s_MediaRSS,
640  "starRating");
641  if (stars.size())
642  {
643  QDomElement ratingDom = stars.at(0).toElement();
644  raverage = GetInt(ratingDom, "average");
645  rcount = GetInt(ratingDom, "count");
646  rmin = GetInt(ratingDom, "min");
647  rmax = GetInt(ratingDom, "max");
648  }
649 
650  QDomNodeList stats = comm.elementsByTagNameNS(Parse::s_MediaRSS,
651  "statistics");
652  if (stats.size())
653  {
654  QDomElement stat = stats.at(0).toElement();
655  views = GetInt(stat, "views");
656  favs = GetInt(stat, "favorites");
657  }
658 
659  QDomNodeList tagsNode = comm.elementsByTagNameNS(Parse::s_MediaRSS,
660  "tags");
661  if (tagsNode.size())
662  {
663  QDomElement tag = tagsNode.at(0).toElement();
664  tags = tag.text();
665  }
666  }
667  }
668 
669  ArbitraryLocatedData result;
670  result.URL = GetURL(element);
671  result.Rating = rating;
672  result.RatingScheme = rscheme;
673  result.Title = GetTitle(element);
674  result.Description = GetDescription(element);
675  result.Keywords = GetKeywords(element);
676  result.CopyrightURL = curl;
677  result.CopyrightText = ctext;
678  result.RatingAverage = raverage;
679  result.RatingCount = rcount;
680  result.RatingMin = rmin;
681  result.RatingMax = rmax;
682  result.Views = views;
683  result.Favs = favs;
684  result.Tags = tags;
685  result.Thumbnails = GetThumbnails(element);
686  result.Credits = GetCredits(element);
687  result.Comments = GetComments(element);
688  result.PeerLinks = GetPeerLinks(element);
689  result.Scenes = GetScenes(element);
690 
691  return result;
692  }
693 };
694 
695 
696 //========================================================================================
697 // Search Construction, Destruction
698 //========================================================================================
699 
700 const QString Parse::s_DC = "http://purl.org/dc/elements/1.1/";
701 const QString Parse::s_WFW = "http://wellformedweb.org/CommentAPI/";
702 const QString Parse::s_Atom = "http://www.w3.org/2005/Atom";
703 const QString Parse::s_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
704 const QString Parse::s_Slash = "http://purl.org/rss/1.0/modules/slash/";
705 const QString Parse::s_Enc = "http://purl.oclc.org/net/rss_2.0/enc#";
706 const QString Parse::s_ITunes = "http://www.itunes.com/dtds/podcast-1.0.dtd";
707 const QString Parse::s_GeoRSSSimple = "http://www.georss.org/georss";
708 const QString Parse::s_GeoRSSW3 = "http://www.w3.org/2003/01/geo/wgs84_pos#";
709 const QString Parse::s_MediaRSS = "http://search.yahoo.com/mrss/";
710 const QString Parse::s_MythRSS = "http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format";
711 
712 ResultItem::resultList Parse::parseRSS(const QDomDocument& domDoc)
713 {
715 
716  QString document = domDoc.toString();
717  LOG(VB_GENERAL, LOG_DEBUG, "Will Be Parsing: " + document);
718 
719  QDomElement root = domDoc.documentElement();
720  QDomElement channel = root.firstChildElement("channel");
721  while (!channel.isNull())
722  {
723  QDomElement item = channel.firstChildElement("item");
724  while (!item.isNull())
725  {
726  vList.append(ParseItem(item));
727  item = item.nextSiblingElement("item");
728  }
729  channel = channel.nextSiblingElement("channel");
730  }
731 
732  return vList;
733 }
734 
735 ResultItem* Parse::ParseItem(const QDomElement& item) const
736 {
737  QString title(""), subtitle(""), description(""), url(""), author(""),
738  duration(""), rating(""), thumbnail(""), mediaURL(""), player(""),
739  language(""), download("");
740  off_t filesize = 0;
741  uint width = 0, height = 0, season = 0, episode = 0;
742  QDateTime date;
743  QStringList playerargs, downloadargs, countries;
744  bool downloadable = true;
745  bool customhtml = false;
746 
747  // Get the title of the article/video
748  title = item.firstChildElement("title").text();
749  title = UnescapeHTML(title);
750  if (title.isEmpty())
751  title = "";
752 
753  // Get the subtitle of this item.
754  QDomNodeList subt = item.elementsByTagNameNS(s_MythRSS, "subtitle");
755  if (subt.size())
756  {
757  subtitle = subt.at(0).toElement().text();
758  }
759 
760  // Parse the description of the article/video
761  QDomElement descriptiontemp = item.firstChildElement("description");
762  if (!descriptiontemp.isNull())
763  description = descriptiontemp.text();
764  if (description.isEmpty())
765  {
766  QDomNodeList nodes = item.elementsByTagNameNS(s_ITunes, "summary");
767  if (nodes.size())
768  description = nodes.at(0).toElement().text();
769  }
770  // Unescape and remove HTML tags from the description.
771  if (description.isEmpty())
772  description = "";
773  else
774  description = UnescapeHTML(description);
775 
776  // Get the link (web playable)
777  url = item.firstChildElement("link").text();
778 
779  // Parse the item author
780  QDomElement authortemp = item.firstChildElement("author");
781  if (!authortemp.isNull())
782  author = authortemp.text();
783  if (author.isEmpty())
784  author = GetAuthor(item);
785 
786  // Turn the RFC-822 pubdate into a QDateTime
787  date = RFC822TimeToQDateTime(item.firstChildElement("pubDate").text());
788  if (!date.isValid() || date.isNull())
789  date = GetDCDateTime(item);
790  if (!date.isValid() || date.isNull())
791  date = MythDate::current();
792 
793  // Parse the insane iTunes duration (HH:MM:SS or H:MM:SS or MM:SS or M:SS or SS)
794  QDomNodeList dur = item.elementsByTagNameNS(s_ITunes, "duration");
795  if (dur.size())
796  {
797  QString itunestime = dur.at(0).toElement().text();
798  QString dateformat;
799 
800  if (itunestime.count() == 8)
801  dateformat = "hh:mm:ss";
802  else if (itunestime.count() == 7)
803  dateformat = "h:mm:ss";
804  else if (itunestime.count() == 5)
805  dateformat = "mm:ss";
806  else if (itunestime.count() == 4)
807  dateformat = "m:ss";
808  else if (itunestime.count() == 2)
809  dateformat = "ss";
810  else
811  duration = "0";
812 
813  if (!dateformat.isNull())
814  {
815  QTime itime = QTime::fromString(itunestime, dateformat);
816  if (itime.isValid())
817  {
818  int seconds = itime.second() + (itime.minute() * 60) + (itime.hour() * 3600);
819  duration = QString::number(seconds);
820  }
821  }
822  }
823 
824  // Get the rating
825  QDomElement ratingtemp = item.firstChildElement("rating");
826  if (!ratingtemp.isNull())
827  rating = ratingtemp.text();
828 
829  // Get the external player binary
830  QDomElement playertemp = item.firstChildElement("player");
831  if (!playertemp.isNull())
832  player = playertemp.text();
833 
834  // Get the arguments to pass to the external player
835  QDomElement playerargstemp = item.firstChildElement("playerargs");
836  if (!playerargstemp.isNull())
837  playerargs = playerargstemp.text().split(" ");
838 
839  // Get the external downloader binary/script
840  QDomElement downloadtemp = item.firstChildElement("download");
841  if (!downloadtemp.isNull())
842  download = downloadtemp.text();
843 
844  // Get the arguments to pass to the external downloader
845  QDomElement downloadargstemp = item.firstChildElement("downloadargs");
846  if (!downloadargstemp.isNull())
847  downloadargs = downloadargstemp.text().split(" ");
848 
849  // Get the countries in which this item is playable
850  QDomNodeList cties = item.elementsByTagNameNS(s_MythRSS, "country");
851  if (cties.size())
852  {
853  int i = 0;
854  while (i < cties.size())
855  {
856  countries.append(cties.at(i).toElement().text());
857  i++;
858  }
859  }
860 
861  // Get the season number of this item.
862  QDomNodeList seas = item.elementsByTagNameNS(s_MythRSS, "season");
863  if (seas.size())
864  {
865  season = seas.at(0).toElement().text().toUInt();
866  }
867 
868  // Get the Episode number of this item.
869  QDomNodeList ep = item.elementsByTagNameNS(s_MythRSS, "episode");
870  if (ep.size())
871  {
872  episode = ep.at(0).toElement().text().toUInt();
873  }
874 
875  // Does this grabber return custom HTML?
876  QDomNodeList html = item.elementsByTagNameNS(s_MythRSS, "customhtml");
877  if (html.size())
878  {
879  QString htmlstring = html.at(0).toElement().text();
880  if (htmlstring.toLower().contains("true") || htmlstring == "1" ||
881  htmlstring.toLower().contains("yes"))
882  customhtml = true;
883  }
884 
885  QList<MRSSEntry> enclosures = GetMediaRSS(item);
886 
887  if (!enclosures.empty())
888  {
889  MRSSEntry media = enclosures.takeAt(0);
890 
891  QList<MRSSThumbnail> thumbs = media.Thumbnails;
892  if (!thumbs.empty())
893  {
894  MRSSThumbnail thumb = thumbs.takeAt(0);
895  thumbnail = thumb.URL;
896  }
897 
898  mediaURL = media.URL;
899 
900  width = media.Width;
901  height = media.Height;
902  language = media.Lang;
903 
904  if (duration.isEmpty())
905  duration = QString::number(media.Duration);
906 
907  if (filesize == 0)
908  filesize = media.Size;
909 
910  if (rating.isEmpty())
911  rating = QString::number(media.RatingAverage);
912  }
913  if (mediaURL.isEmpty())
914  {
915  QList<Enclosure> stdEnc = GetEnclosures(item);
916 
917  if (!stdEnc.empty())
918  {
919  Enclosure e = stdEnc.takeAt(0);
920 
921  mediaURL = e.URL;
922 
923  if (filesize == 0)
924  filesize = e.Length;
925  }
926  }
927 
928  if (mediaURL.isNull() || mediaURL == url)
929  downloadable = false;
930 
931  std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
932  return(new ResultItem(title, sh->doTitle(title),
933  subtitle, sh->doTitle(subtitle), description,
934  url, thumbnail, mediaURL, author, date, duration,
935  rating, filesize, player, playerargs,
936  download, downloadargs, width, height,
937  language, downloadable, countries, season,
938  episode, customhtml));
939 }
940 
941 QString Parse::GetLink(const QDomElement& parent) const
942 {
943  QString result;
944  QDomElement link = parent.firstChildElement("link");
945  while(!link.isNull())
946  {
947  if (!link.hasAttribute("rel") || link.attribute("rel") == "alternate")
948  {
949  if (!link.hasAttribute("href"))
950  result = link.text();
951  else
952  result = link.attribute("href");
953  break;
954  }
955  link = link.nextSiblingElement("link");
956  }
957  return result;
958 }
959 
960 QString Parse::GetAuthor(const QDomElement& parent) const
961 {
962  QString result("");
963  QDomNodeList nodes = parent.elementsByTagNameNS(s_ITunes,
964  "author");
965  if (nodes.size())
966  {
967  result = nodes.at(0).toElement().text();
968  return result;
969  }
970 
971  nodes = parent.elementsByTagNameNS(s_DC,
972  "creator");
973  if (nodes.size())
974  {
975  result = nodes.at(0).toElement().text();
976  return result;
977  }
978 
979  return result;
980 }
981 
982 QString Parse::GetCommentsRSS(const QDomElement& parent) const
983 {
984  QString result;
985  QDomNodeList nodes = parent.elementsByTagNameNS(s_WFW,
986  "commentRss");
987  if (nodes.size())
988  result = nodes.at(0).toElement().text();
989  return result;
990 }
991 
992 QString Parse::GetCommentsLink(const QDomElement& parent) const
993 {
994  QString result;
995  QDomNodeList nodes = parent.elementsByTagNameNS("", "comments");
996  if (nodes.size())
997  result = nodes.at(0).toElement().text();
998  return result;
999 }
1000 
1001 QDateTime Parse::GetDCDateTime(const QDomElement& parent) const
1002 {
1003  QDomNodeList dates = parent.elementsByTagNameNS(s_DC, "date");
1004  if (!dates.size())
1005  return QDateTime();
1006  return FromRFC3339(dates.at(0).toElement().text());
1007 }
1008 
1009 QDateTime Parse::RFC822TimeToQDateTime(const QString& t) const
1010 {
1011  if (t.size() < 20)
1012  return QDateTime();
1013 
1014  QString time = t.simplified();
1015  short int hoursShift = 0, minutesShift = 0;
1016 
1017  QStringList tmp = time.split(' ');
1018  if (tmp.isEmpty())
1019  return QDateTime();
1020  if (tmp. at(0).contains(QRegExp("\\D")))
1021  tmp.removeFirst();
1022  if (tmp.size() != 5)
1023  return QDateTime();
1024  QString tmpTimezone = tmp.takeAt(tmp.size() -1);
1025  if (tmpTimezone.size() == 5)
1026  {
1027  bool ok;
1028  int tz = tmpTimezone.toInt(&ok);
1029  if(ok)
1030  {
1031  hoursShift = tz / 100;
1032  minutesShift = tz % 100;
1033  }
1034  }
1035  else
1036  hoursShift = TimezoneOffsets.value(tmpTimezone, 0);
1037 
1038  if (tmp.at(0).size() == 1)
1039  tmp[0].prepend("0");
1040  tmp [1].truncate(3);
1041 
1042  time = tmp.join(" ");
1043 
1044  QDateTime result;
1045  if (tmp.at(2).size() == 4)
1046  result = QLocale::c().toDateTime(time, "dd MMM yyyy hh:mm:ss");
1047  else
1048  result = QLocale::c().toDateTime(time, "dd MMM yy hh:mm:ss");
1049  if (result.isNull() || !result.isValid())
1050  return QDateTime();
1051  result = result.addSecs(hoursShift * 3600 * (-1) + minutesShift *60 * (-1));
1052  result.setTimeSpec(Qt::UTC);
1053  return result;
1054 }
1055 
1056 QDateTime Parse::FromRFC3339(const QString& t) const
1057 {
1058  int hoursShift = 0, minutesShift = 0;
1059  if (t.size() < 19)
1060  return QDateTime();
1061  QDateTime result = MythDate::fromString(t.left(19).toUpper());
1062  QRegExp fractionalSeconds("(\\.)(\\d+)");
1063  if (fractionalSeconds.indexIn(t) > -1)
1064  {
1065  bool ok;
1066  int fractional = fractionalSeconds.cap(2).toInt(&ok);
1067  if (ok)
1068  {
1069  if (fractional < 100)
1070  fractional *= 10;
1071  if (fractional <10)
1072  fractional *= 100;
1073  result = result.addMSecs(fractional);
1074  }
1075  }
1076  QRegExp timeZone("(\\+|\\-)(\\d\\d)(:)(\\d\\d)$");
1077  if (timeZone.indexIn(t) > -1)
1078  {
1079  short int multiplier = -1;
1080  if (timeZone.cap(1) == "-")
1081  multiplier = 1;
1082  hoursShift = timeZone.cap(2).toInt();
1083  minutesShift = timeZone.cap(4).toInt();
1084  result = result.addSecs(hoursShift * 3600 * multiplier + minutesShift * 60 * multiplier);
1085  }
1086  result.setTimeSpec(Qt::UTC);
1087  return result;
1088 }
1089 
1090 QList<Enclosure> Parse::GetEnclosures(const QDomElement& entry) const
1091 {
1092  QList<Enclosure> result;
1093  QDomNodeList links = entry.elementsByTagName("enclosure");
1094  for (int i = 0; i < links.size(); ++i)
1095  {
1096  QDomElement link = links.at(i).toElement();
1097 
1098  Enclosure e =
1099  {
1100  link.attribute("url"),
1101  link.attribute("type"),
1102  link.attribute("length", "-1").toLongLong(),
1103  link.attribute("hreflang")
1104  };
1105 
1106  result << e;
1107  }
1108  return result;
1109 }
1110 
1111 QList<MRSSEntry> Parse::GetMediaRSS(const QDomElement& item) const
1112 {
1113  return MRSSParser() (item);
1114 }
1115 
1116 QString Parse::UnescapeHTML(const QString& escaped)
1117 {
1118  QString result = escaped;
1119  result.replace("&amp;", "&");
1120  result.replace("&lt;", "<");
1121  result.replace("&gt;", ">");
1122  result.replace("&apos;", "\'");
1123  result.replace("&rsquo;", "\'");
1124  result.replace("&#x2019;", "\'");
1125  result.replace("&quot;", "\"");
1126  result.replace("&#8230;",QChar(8230));
1127  result.replace("&#233;",QChar(233));
1128  result.replace("&mdash;", QChar(8212));
1129  result.replace("&nbsp;", " ");
1130  result.replace("&#160;", QChar(160));
1131  result.replace("&#225;", QChar(225));
1132  result.replace("&#8216;", QChar(8216));
1133  result.replace("&#8217;", QChar(8217));
1134  result.replace("&#039;", "\'");
1135  result.replace("&ndash;", QChar(8211));
1136  result.replace("&auml;", QChar(0x00e4));
1137  result.replace("&ouml;", QChar(0x00f6));
1138  result.replace("&uuml;", QChar(0x00fc));
1139  result.replace("&Auml;", QChar(0x00c4));
1140  result.replace("&Ouml;", QChar(0x00d6));
1141  result.replace("&Uuml;", QChar(0x00dc));
1142  result.replace("&szlig;", QChar(0x00df));
1143  result.replace("&euro;", "€");
1144  result.replace("&#8230;", "...");
1145  result.replace("&#x00AE;", QChar(0x00ae));
1146  result.replace("&#x201C;", QChar(0x201c));
1147  result.replace("&#x201D;", QChar(0x201d));
1148  result.replace("<p>", "\n");
1149 
1150  QRegExp stripHTML(QLatin1String("<.*>"));
1151  stripHTML.setMinimal(true);
1152  result.remove(stripHTML);
1153 
1154  return result;
1155 }
double Framerate
Definition: rssparse.h:80
int GetInt(const QDomElement &elem, const QString &attrname)
Definition: rssparse.cpp:426
QList< MRSSThumbnail > GetThumbnails(const QDomElement &element)
Definition: rssparse.cpp:438
QString GetKeywords(const QDomElement &element)
Definition: rssparse.cpp:414
QList< MRSSScene > Scenes
Definition: rssparse.h:106
QString Medium
Definition: rssparse.h:76
static const QString s_ITunes
Definition: rssparse.h:224
static const QString s_RDF
Definition: rssparse.h:221
QString Description
Definition: rssparse.h:91
Describes an enclosure associated with an item.
Definition: rssparse.h:29
QString GetTitle(const QDomElement &element)
Definition: rssparse.cpp:390
QList< MRSSEntry > GetMediaRSS(const QDomElement &) const
Definition: rssparse.cpp:1111
QString URL
Definition: rssparse.h:31
static const QString s_DC
Definition: rssparse.h:218
QString GetCommentsLink(const QDomElement &) const
Definition: rssparse.cpp:992
string dateformat
Definition: mythburn.py:141
QString GetURL(const QDomElement &element)
Definition: rssparse.cpp:380
QString Lang
Definition: rssparse.h:86
QDateTime FromRFC3339(const QString &) const
Definition: rssparse.cpp:1056
MPUBLIC QString format_season_and_episode(int seasEp, int digits)
QList< MRSSComment > GetComments(const QDomElement &element)
Definition: rssparse.cpp:483
static const char URL[]
Definition: cddb.cpp:29
qint64 Size
Definition: rssparse.h:74
unsigned int uint
Definition: compat.h:140
ResultItem * ParseItem(const QDomElement &item) const
Definition: rssparse.cpp:735
int RatingAverage
Definition: rssparse.h:95
int Views
Definition: rssparse.h:99
static const QString s_MythRSS
Definition: rssparse.h:228
QList< MRSSScene > GetScenes(const QDomElement &element)
Definition: rssparse.cpp:564
static guint32 * tmp
Definition: goom_core.c:35
int RatingMax
Definition: rssparse.h:98
QString URL
Definition: rssparse.h:73
#define off_t
QString GetCommentsRSS(const QDomElement &) const
Definition: rssparse.cpp:982
QString Tags
Definition: rssparse.h:101
int Favs
Definition: rssparse.h:100
QString GetLink(const QDomElement &) const
Definition: rssparse.cpp:941
QString CopyrightText
Definition: rssparse.h:94
qint64 Length
Definition: rssparse.h:33
QList< MRSSThumbnail > Thumbnails
Definition: rssparse.h:102
QString CopyrightURL
Definition: rssparse.h:93
int Height
Definition: rssparse.h:85
QList< MRSSCredit > Credits
Definition: rssparse.h:103
def rating(profile, smoonURL, gate)
Definition: scan.py:25
static const QString s_GeoRSSW3
Definition: rssparse.h:226
int Channels
Definition: rssparse.h:82
int RatingCount
Definition: rssparse.h:96
QDateTime RFC822TimeToQDateTime(const QString &) const
Definition: rssparse.cpp:1009
QList< MRSSComment > Comments
Definition: rssparse.cpp:213
int Width
Definition: rssparse.h:84
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
static const uint16_t * d
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
unsigned char t
Definition: ParseText.cpp:329
QList< MRSSPeerLink > GetPeerLinks(const QDomElement &element)
Definition: rssparse.cpp:545
ArbitraryLocatedData CollectArbitraryLocatedData(const QDomElement &element)
Definition: rssparse.cpp:591
Default local time.
Definition: mythdate.h:16
QList< MRSSCredit > GetCredits(const QDomElement &element)
Definition: rssparse.cpp:462
QList< Enclosure > GetEnclosures(const QDomElement &entry) const
Definition: rssparse.cpp:1090
const char * name
Definition: ParseText.cpp:328
static const QString s_Enc
Definition: rssparse.h:223
void ensureSortFields(void)
Definition: rssparse.cpp:66
QList< MRSSThumbnail > Thumbnails
Definition: rssparse.cpp:211
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
static const QString s_WFW
Definition: rssparse.h:219
static int GetInt(MHParameter *parm, MHEngine *engine)
Definition: Programs.cpp:132
bool IsDefault
Definition: rssparse.h:77
QString Title
Definition: rssparse.h:90
static QString UnescapeHTML(const QString &)
Definition: rssparse.cpp:1116
QList< MRSSPeerLink > PeerLinks
Definition: rssparse.cpp:214
QString RatingScheme
Definition: rssparse.h:89
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
ResultItem::resultList parseRSS(const QDomDocument &domDoc)
Definition: rssparse.cpp:712
int Bitrate
Definition: rssparse.h:79
int Duration
Definition: rssparse.h:83
ResultItem()=default
QString Keywords
Definition: rssparse.h:92
QString Rating
Definition: rssparse.h:88
static const QString s_GeoRSSSimple
Definition: rssparse.h:225
static const QString s_Slash
Definition: rssparse.h:222
QString GetDescription(const QDomElement &element)
Definition: rssparse.cpp:402
QList< MRSSPeerLink > PeerLinks
Definition: rssparse.h:105
QList< MRSSCredit > Credits
Definition: rssparse.cpp:212
double SamplingRate
Definition: rssparse.h:81
QDateTime GetDCDateTime(const QDomElement &) const
Definition: rssparse.cpp:1001
QString Type
Definition: rssparse.h:75
QList< MRSSComment > Comments
Definition: rssparse.h:104
QList< MRSSEntry > CollectChildren(const QDomElement &holder)
Definition: rssparse.cpp:285
QList< ResultItem * > resultList
Definition: rssparse.h:114
static const QString s_Atom
Definition: rssparse.h:220
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
int RatingMin
Definition: rssparse.h:97
ArbitraryLocatedData GetArbitraryLocatedDataFor(const QDomElement &holder)
Definition: rssparse.cpp:362
QString GetAuthor(const QDomElement &) const
Definition: rssparse.cpp:960
static const QString s_MediaRSS
Definition: rssparse.h:227
QDateTime RFC822TimeToQDateTime(const QString &t)
QString URL
Definition: rssparse.h:39
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
QString Expression
Definition: rssparse.h:78
void toMap(InfoMap &metadataMap)
Definition: rssparse.cpp:76