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