MythTV  master
xmltvparser.cpp
Go to the documentation of this file.
1 #include "xmltvparser.h"
2 
3 // Qt headers
4 #include <QFile>
5 #include <QStringList>
6 #include <QDateTime>
7 #include <QDomDocument>
8 #include <QUrl>
9 
10 // C++ headers
11 #include <iostream>
12 #include <cstdlib>
13 
14 // libmyth headers
15 #include "exitcodes.h"
16 #include "mythcorecontext.h"
17 #include "mythdate.h"
18 
19 // libmythtv headers
20 #include "programinfo.h"
21 #include "programdata.h"
22 #include "dvbdescriptors.h"
23 #include "channelinfo.h"
24 
25 // libmythmetadata headers
26 #include "metadatadownload.h"
27 
28 // filldata headers
29 #include "channeldata.h"
30 #include "fillutil.h"
31 
33 {
34  m_current_year = MythDate::current().date().toString("yyyy").toUInt();
35 }
36 
38 {
41 }
42 
43 static uint ELFHash(const QByteArray &ba)
44 {
45  const uchar *k = (const uchar *)ba.data();
46  uint h = 0;
47 
48  if (k)
49  {
50  while (*k)
51  {
52  uint g;
53  h = (h << 4) + *k++;
54  if ((g = (h & 0xf0000000)) != 0)
55  h ^= g >> 24;
56  h &= ~g;
57  }
58  }
59 
60  return h;
61 }
62 
63 static QString getFirstText(const QDomElement& element)
64 {
65  for (QDomNode dname = element.firstChild(); !dname.isNull();
66  dname = dname.nextSibling())
67  {
68  QDomText t = dname.toText();
69  if (!t.isNull())
70  return t.data();
71  }
72  return QString();
73 }
74 
75 ChannelInfo *XMLTVParser::parseChannel(QDomElement &element, QUrl &baseUrl)
76 {
77  ChannelInfo *chaninfo = new ChannelInfo;
78 
79  QString xmltvid = element.attribute("id", "");
80 
81  chaninfo->m_xmltvid = xmltvid;
82  chaninfo->m_tvformat = "Default";
83 
84  for (QDomNode child = element.firstChild(); !child.isNull();
85  child = child.nextSibling())
86  {
87  QDomElement info = child.toElement();
88  if (!info.isNull())
89  {
90  if (info.tagName() == "icon")
91  {
92  if (chaninfo->m_icon.isEmpty())
93  {
94  QString path = info.attribute("src", "");
95  if (!path.isEmpty() && !path.contains("://"))
96  {
97  QString base = baseUrl.toString(QUrl::StripTrailingSlash);
98  chaninfo->m_icon = base +
99  ((path.startsWith("/")) ? path : QString("/") + path);
100  }
101  else if (!path.isEmpty())
102  {
103  QUrl url(path);
104  if (url.isValid())
105  chaninfo->m_icon = url.toString();
106  }
107  }
108  }
109  else if (info.tagName() == "display-name")
110  {
111  if (chaninfo->m_name.isEmpty())
112  {
113  chaninfo->m_name = info.text();
114  }
115  else if (chaninfo->m_callsign.isEmpty())
116  {
117  chaninfo->m_callsign = info.text();
118  }
119  else if (chaninfo->m_channum.isEmpty())
120  {
121  chaninfo->m_channum = info.text();
122  }
123  }
124  }
125  }
126 
127  chaninfo->m_freqid = chaninfo->m_channum;
128  return chaninfo;
129 }
130 
131 static void fromXMLTVDate(QString &timestr, QDateTime &dt)
132 {
133  // The XMLTV spec requires dates to either be in UTC/GMT or to specify a
134  // valid timezone. We are sticking to the spec and require all grabbers
135  // to comply.
136 
137  if (timestr.isEmpty())
138  {
139  LOG(VB_XMLTV, LOG_ERR, "Found empty Date/Time in XMLTV data, ignoring");
140  return;
141  }
142 
143  QStringList split = timestr.split(" ", QString::SkipEmptyParts);
144  QString ts = split[0];
145  QDate tmpDate;
146  QTime tmpTime;
147  QString tzoffset;
148 
149  // Process the TZ offset (if any)
150  if (split.size() > 1)
151  {
152  tzoffset = split[1];
153  // These shouldn't be required and they aren't ISO 8601 but the
154  // xmltv spec mentions these and just these so handle them just in
155  // case
156  if (tzoffset == "GMT" || tzoffset == "UTC")
157  tzoffset = "+0000";
158  else if (tzoffset == "BST")
159  tzoffset = "+0100";
160  }
161  else
162  {
163  // We will accept a datetime with a trailing Z as being explicit
164  if (ts.endsWith('Z'))
165  {
166  tzoffset = "+0000";
167  ts.truncate(ts.length()-1);
168  }
169  else
170  {
171  tzoffset = "+0000";
172  static bool warned_once_on_implicit_utc = false;
173  if (!warned_once_on_implicit_utc)
174  {
175  LOG(VB_XMLTV, LOG_WARNING, "No explicit time zone found, "
176  "guessing implicit UTC! Please consider enhancing "
177  "the guide source to provide explicit UTC or local "
178  "time instead.");
179  warned_once_on_implicit_utc = true;
180  }
181  }
182  }
183 
184  // Process the date part
185  QString tsDate = ts.left(8);
186  if (tsDate.length() == 8)
187  tmpDate = QDate::fromString(tsDate, "yyyyMMdd");
188  else if (tsDate.length() == 6)
189  tmpDate = QDate::fromString(tsDate, "yyyyMM");
190  else if (tsDate.length() == 4)
191  tmpDate = QDate::fromString(tsDate, "yyyy");
192  if (!tmpDate.isValid())
193  {
194  LOG(VB_XMLTV, LOG_ERR,
195  QString("Invalid datetime (date) in XMLTV data, ignoring: %1")
196  .arg(timestr));
197  return;
198  }
199 
200  // Process the time part (if any)
201  if (ts.length() > 8)
202  {
203  QString tsTime = ts.mid(8);
204  if (tsTime.length() == 6)
205  {
206  if (tsTime == "235960")
207  tsTime = "235959";
208  tmpTime = QTime::fromString(tsTime, "HHmmss");
209  }
210  else if (tsTime.length() == 4)
211  tmpTime = QTime::fromString(tsTime, "HHmm");
212  else if (tsTime.length() == 2)
213  tmpTime = QTime::fromString(tsTime, "HH");
214  if (!tmpTime.isValid())
215  {
216  // Time part exists, but is (somehow) invalid
217  LOG(VB_XMLTV, LOG_ERR,
218  QString("Invalid datetime (time) in XMLTV data, ignoring: %1")
219  .arg(timestr));
220  return;
221  }
222  }
223 
224  QDateTime tmpDT = QDateTime(tmpDate, tmpTime, Qt::UTC);
225  if (!tmpDT.isValid())
226  {
227  LOG(VB_XMLTV, LOG_ERR,
228  QString("Invalid datetime (combination of date/time) "
229  "in XMLTV data, ignoring: %1").arg(timestr));
230  return;
231  }
232 
233  // While this seems like a hack, it's better than what was done before
234  QString isoDateString = tmpDT.toString(Qt::ISODate);
235  if (isoDateString.endsWith('Z')) // Should always be Z, but ...
236  isoDateString.truncate(isoDateString.length()-1);
237  isoDateString += tzoffset;
238  dt = QDateTime::fromString(isoDateString, Qt::ISODate).toUTC();
239 
240  if (!dt.isValid())
241  {
242  LOG(VB_XMLTV, LOG_ERR,
243  QString("Invalid datetime (zone offset) in XMLTV data, "
244  "ignoring: %1").arg(timestr));
245  return;
246  }
247 
249 }
250 
251 static void parseCredits(QDomElement &element, ProgInfo *pginfo)
252 {
253  for (QDomNode child = element.firstChild(); !child.isNull();
254  child = child.nextSibling())
255  {
256  QDomElement info = child.toElement();
257  if (!info.isNull())
258  pginfo->AddPerson(info.tagName(), getFirstText(info));
259  }
260 }
261 
262 static void parseVideo(QDomElement &element, ProgInfo *pginfo)
263 {
264  for (QDomNode child = element.firstChild(); !child.isNull();
265  child = child.nextSibling())
266  {
267  QDomElement info = child.toElement();
268  if (!info.isNull())
269  {
270  if (info.tagName() == "quality")
271  {
272  if (getFirstText(info) == "HDTV")
273  pginfo->m_videoProps |= VID_HDTV;
274  }
275  else if (info.tagName() == "aspect")
276  {
277  if (getFirstText(info) == "16:9")
278  pginfo->m_videoProps |= VID_WIDESCREEN;
279  }
280  }
281  }
282 }
283 
284 static void parseAudio(QDomElement &element, ProgInfo *pginfo)
285 {
286  for (QDomNode child = element.firstChild(); !child.isNull();
287  child = child.nextSibling())
288  {
289  QDomElement info = child.toElement();
290  if (!info.isNull())
291  {
292  if (info.tagName() == "stereo")
293  {
294  if (getFirstText(info) == "mono")
295  {
296  pginfo->m_audioProps |= AUD_MONO;
297  }
298  else if (getFirstText(info) == "stereo")
299  {
300  pginfo->m_audioProps |= AUD_STEREO;
301  }
302  else if (getFirstText(info) == "dolby" ||
303  getFirstText(info) == "dolby digital")
304  {
305  pginfo->m_audioProps |= AUD_DOLBY;
306  }
307  else if (getFirstText(info) == "surround")
308  {
309  pginfo->m_audioProps |= AUD_SURROUND;
310  }
311  }
312  }
313  }
314 }
315 
316 ProgInfo *XMLTVParser::parseProgram(QDomElement &element)
317 {
318  QString programid, season, episode, totalepisodes;
319  ProgInfo *pginfo = new ProgInfo();
320 
321  QString text = element.attribute("start", "");
322  fromXMLTVDate(text, pginfo->m_starttime);
323  pginfo->m_startts = text;
324 
325  text = element.attribute("stop", "");
326  fromXMLTVDate(text, pginfo->m_endtime);
327  pginfo->m_endts = text;
328 
329  text = element.attribute("channel", "");
330  QStringList split = text.split(" ");
331 
332  pginfo->m_channel = split[0];
333 
334  text = element.attribute("clumpidx", "");
335  if (!text.isEmpty())
336  {
337  split = text.split('/');
338  pginfo->m_clumpidx = split[0];
339  pginfo->m_clumpmax = split[1];
340  }
341 
342  for (QDomNode child = element.firstChild(); !child.isNull();
343  child = child.nextSibling())
344  {
345  QDomElement info = child.toElement();
346  if (!info.isNull())
347  {
348  if (info.tagName() == "title")
349  {
350  if (info.attribute("lang") == "ja_JP")
351  {
352  pginfo->m_title = getFirstText(info);
353  }
354  else if (info.attribute("lang") == "ja_JP@kana")
355  {
356  pginfo->m_title_pronounce = getFirstText(info);
357  }
358  else if (pginfo->m_title.isEmpty())
359  {
360  pginfo->m_title = getFirstText(info);
361  }
362  }
363  else if (info.tagName() == "sub-title" &&
364  pginfo->m_subtitle.isEmpty())
365  {
366  pginfo->m_subtitle = getFirstText(info);
367  }
368  else if (info.tagName() == "desc" && pginfo->m_description.isEmpty())
369  {
370  pginfo->m_description = getFirstText(info);
371  }
372  else if (info.tagName() == "category")
373  {
374  const QString cat = getFirstText(info);
375 
378  {
380  }
381  else if (pginfo->m_category.isEmpty())
382  {
383  pginfo->m_category = cat;
384  }
385 
386  if ((cat.compare(QObject::tr("movie"),Qt::CaseInsensitive) == 0) ||
387  (cat.compare(QObject::tr("film"),Qt::CaseInsensitive) == 0))
388  {
389  // Hack for tv_grab_uk_rt
391  }
392 
393  pginfo->m_genres.append(cat);
394  }
395  else if (info.tagName() == "date" && (pginfo->m_airdate == 0U))
396  {
397  // Movie production year
398  QString date = getFirstText(info);
399  pginfo->m_airdate = date.left(4).toUInt();
400  }
401  else if (info.tagName() == "star-rating" && pginfo->m_stars == 0.0F)
402  {
403  QDomNodeList values = info.elementsByTagName("value");
404  QDomElement item;
405  QString stars;
406  float rating = 0.0;
407 
408  // Use the first rating to appear in the xml, this should be
409  // the most important one.
410  //
411  // Averaging is not a good idea here, any subsequent ratings
412  // are likely to represent that days recommended programmes
413  // which on a bad night could given to an average programme.
414  // In the case of uk_rt it's not unknown for a recommendation
415  // to be given to programmes which are 'so bad, you have to
416  // watch!'
417  //
418  // XMLTV uses zero based ratings and signals no rating by absence.
419  // A rating from 1 to 5 is encoded as 0/4 to 4/4.
420  // MythTV uses zero to signal no rating!
421  // The same rating is encoded as 0.2 to 1.0 with steps of 0.2, it
422  // is not encoded as 0.0 to 1.0 with steps of 0.25 because
423  // 0 signals no rating!
424  // See http://xmltv.cvs.sourceforge.net/viewvc/xmltv/xmltv/xmltv.dtd?revision=1.47&view=markup#l539
425  item = values.item(0).toElement();
426  if (!item.isNull())
427  {
428  stars = getFirstText(item);
429  float num = stars.section('/', 0, 0).toFloat() + 1;
430  float den = stars.section('/', 1, 1).toFloat() + 1;
431  if (0.0F < den)
432  rating = num/den;
433  }
434 
435  pginfo->m_stars = rating;
436  }
437  else if (info.tagName() == "rating")
438  {
439  // again, the structure of ratings seems poorly represented
440  // in the XML. no idea what we'd do with multiple values.
441  QDomNodeList values = info.elementsByTagName("value");
442  QDomElement item = values.item(0).toElement();
443  if (item.isNull())
444  continue;
446  rating.m_system = info.attribute("system", "");
447  rating.m_rating = getFirstText(item);
448  pginfo->m_ratings.append(rating);
449  }
450  else if (info.tagName() == "previously-shown")
451  {
452  pginfo->m_previouslyshown = true;
453 
454  QString prevdate = info.attribute("start");
455  if (!prevdate.isEmpty())
456  {
457  QDateTime date;
458  fromXMLTVDate(prevdate, date);
459  pginfo->m_originalairdate = date.date();
460  }
461  }
462  else if (info.tagName() == "credits")
463  {
464  parseCredits(info, pginfo);
465  }
466  else if (info.tagName() == "subtitles")
467  {
468  if (info.attribute("type") == "teletext")
469  pginfo->m_subtitleType |= SUB_NORMAL;
470  else if (info.attribute("type") == "onscreen")
471  pginfo->m_subtitleType |= SUB_ONSCREEN;
472  else if (info.attribute("type") == "deaf-signed")
473  pginfo->m_subtitleType |= SUB_SIGNED;
474  }
475  else if (info.tagName() == "audio")
476  {
477  parseAudio(info, pginfo);
478  }
479  else if (info.tagName() == "video")
480  {
481  parseVideo(info, pginfo);
482  }
483  else if (info.tagName() == "episode-num")
484  {
485  if (info.attribute("system") == "dd_progid")
486  {
487  QString episodenum(getFirstText(info));
488  // if this field includes a dot, strip it out
489  int idx = episodenum.indexOf('.');
490  if (idx != -1)
491  episodenum.remove(idx, 1);
492  programid = episodenum;
493  /* Only EPisodes and SHows are part of a series for SD */
494  if (programid.startsWith(QString("EP")) ||
495  programid.startsWith(QString("SH")))
496  pginfo->m_seriesId = QString("EP") + programid.mid(2,8);
497  }
498  else if (info.attribute("system") == "xmltv_ns")
499  {
500  int tmp;
501  QString episodenum(getFirstText(info));
502  episode = episodenum.section('.',1,1);
503  totalepisodes = episode.section('/',1,1).trimmed();
504  episode = episode.section('/',0,0).trimmed();
505  season = episodenum.section('.',0,0).trimmed();
506  season = season.section('/',0,0).trimmed();
507  QString part(episodenum.section('.',2,2));
508  QString partnumber(part.section('/',0,0).trimmed());
509  QString parttotal(part.section('/',1,1).trimmed());
510 
512 
513  if (!season.isEmpty())
514  {
515  tmp = season.toUInt() + 1;
516  pginfo->m_season = tmp;
517  season = QString::number(tmp);
518  pginfo->m_syndicatedepisodenumber = 'S' + season;
519  }
520 
521  if (!episode.isEmpty())
522  {
523  tmp = episode.toUInt() + 1;
524  pginfo->m_episode = tmp;
525  episode = QString::number(tmp);
526  pginfo->m_syndicatedepisodenumber.append('E' + episode);
527  }
528 
529  if (!totalepisodes.isEmpty())
530  {
531  pginfo->m_totalepisodes = totalepisodes.toUInt();
532  }
533 
534  uint partno = 0;
535  if (!partnumber.isEmpty())
536  {
537  bool ok;
538  partno = partnumber.toUInt(&ok) + 1;
539  partno = (ok) ? partno : 0;
540  }
541 
542  if (!parttotal.isEmpty() && partno > 0)
543  {
544  bool ok;
545  uint partto = parttotal.toUInt(&ok);
546  if (ok && partnumber <= parttotal)
547  {
548  pginfo->m_parttotal = partto;
549  pginfo->m_partnumber = partno;
550  }
551  }
552  }
553  else if (info.attribute("system") == "onscreen")
554  {
556  if (pginfo->m_subtitle.isEmpty())
557  {
558  pginfo->m_subtitle = getFirstText(info);
559  }
560  }
561  else if ((info.attribute("system") == "themoviedb.org") &&
562  (m_movieGrabberPath.endsWith(QString("/tmdb3.py"))))
563  {
564  /* text is movie/<inetref> */
565  QString inetrefRaw(getFirstText(info));
566  if (inetrefRaw.startsWith(QString("movie/"))) {
567  QString inetref(QString ("tmdb3.py_") + inetrefRaw.section('/',1,1).trimmed());
568  pginfo->m_inetref = inetref;
569  }
570  }
571  else if ((info.attribute("system") == "thetvdb.com") &&
572  (m_tvGrabberPath.endsWith(QString("/ttvdb.py"))))
573  {
574  /* text is series/<inetref> */
575  QString inetrefRaw(getFirstText(info));
576  if (inetrefRaw.startsWith(QString("series/"))) {
577  QString inetref(QString ("ttvdb.py_") + inetrefRaw.section('/',1,1).trimmed());
578  pginfo->m_inetref = inetref;
579  /* ProgInfo does not have a collectionref, so we don't set any */
580  }
581  }
582  }
583  }
584  }
585 
586  if (pginfo->m_category.isEmpty() &&
589 
590  if (!pginfo->m_airdate
592  pginfo->m_airdate = m_current_year;
593 
594  if (programid.isEmpty())
595  {
596 
597  /* Let's build ourself a programid */
598 
600  programid = "MV";
601  else if (ProgramInfo::kCategorySeries == pginfo->m_categoryType)
602  programid = "EP";
603  else if (ProgramInfo::kCategorySports == pginfo->m_categoryType)
604  programid = "SP";
605  else
606  programid = "SH";
607 
608  QString seriesid = QString::number(ELFHash(pginfo->m_title.toUtf8()));
609  pginfo->m_seriesId = seriesid;
610  programid.append(seriesid);
611 
612  if (!episode.isEmpty() && !season.isEmpty())
613  {
614  /* Append unpadded episode and season number to the seriesid (to
615  maintain consistency with historical encoding), but limit the
616  season number representation to a single base-36 character to
617  ensure unique programid generation. */
618  int season_int = season.toInt();
619  if (season_int > 35)
620  {
621  // Cannot represent season as a single base-36 character, so
622  // remove the programid and fall back to normal dup matching.
624  programid.clear();
625  }
626  else
627  {
628  programid.append(episode);
629  programid.append(QString::number(season_int, 36));
630  if (pginfo->m_partnumber && pginfo->m_parttotal)
631  {
632  programid += QString::number(pginfo->m_partnumber);
633  programid += QString::number(pginfo->m_parttotal);
634  }
635  }
636  }
637  else
638  {
639  /* No ep/season info? Well then remove the programid and rely on
640  normal dupchecking methods instead. */
642  programid.clear();
643  }
644  }
645 
646  pginfo->m_programId = programid;
647 
648  return pginfo;
649 }
650 
652  const QString& filename, ChannelInfoList *chanlist,
653  QMap<QString, QList<ProgInfo> > *proglist)
654 {
655  QDomDocument doc;
656  QFile f;
657 
658  if (!dash_open(f, filename, QIODevice::ReadOnly))
659  {
660  LOG(VB_GENERAL, LOG_ERR,
661  QString("Error unable to open '%1' for reading.") .arg(filename));
662  return false;
663  }
664 
665  QString errorMsg = "unknown";
666  int errorLine = 0;
667  int errorColumn = 0;
668 
669  if (!doc.setContent(&f, &errorMsg, &errorLine, &errorColumn))
670  {
671  LOG(VB_GENERAL, LOG_ERR, QString("Error in %1:%2: %3")
672  .arg(errorLine).arg(errorColumn).arg(errorMsg));
673 
674  f.close();
675  return true;
676  }
677 
678  f.close();
679 
680  QDomElement docElem = doc.documentElement();
681 
682  QUrl baseUrl(docElem.attribute("source-data-url", ""));
683  //QUrl sourceUrl(docElem.attribute("source-info-url", ""));
684 
685  QString aggregatedTitle;
686  QString aggregatedDesc;
687 
688  QDomNode n = docElem.firstChild();
689  while (!n.isNull())
690  {
691  QDomElement e = n.toElement();
692  if (!e.isNull())
693  {
694  if (e.tagName() == "channel")
695  {
696  ChannelInfo *chinfo = parseChannel(e, baseUrl);
697  if (!chinfo->m_xmltvid.isEmpty())
698  chanlist->push_back(*chinfo);
699  delete chinfo;
700  }
701  else if (e.tagName() == "programme")
702  {
703  ProgInfo *pginfo = parseProgram(e);
704 
705  if (!(pginfo->m_starttime.isValid()))
706  {
707  LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), "
708  "invalid start time, "
709  "skipping")
710  .arg(pginfo->m_title));
711  }
712  else if (pginfo->m_channel.isEmpty())
713  {
714  LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), "
715  "missing channel, "
716  "skipping")
717  .arg(pginfo->m_title));
718  }
719  else if (pginfo->m_startts == pginfo->m_endts)
720  {
721  LOG(VB_GENERAL, LOG_WARNING, QString("Invalid programme (%1), "
722  "identical start and end "
723  "times, skipping")
724  .arg(pginfo->m_title));
725  }
726  else
727  {
728  if (pginfo->m_clumpidx.isEmpty())
729  (*proglist)[pginfo->m_channel].push_back(*pginfo);
730  else
731  {
732  /* append all titles/descriptions from one clump */
733  if (pginfo->m_clumpidx.toInt() == 0)
734  {
735  aggregatedTitle.clear();
736  aggregatedDesc.clear();
737  }
738 
739  if (!pginfo->m_title.isEmpty())
740  {
741  if (!aggregatedTitle.isEmpty())
742  aggregatedTitle.append(" | ");
743  aggregatedTitle.append(pginfo->m_title);
744  }
745 
746  if (!pginfo->m_description.isEmpty())
747  {
748  if (!aggregatedDesc.isEmpty())
749  aggregatedDesc.append(" | ");
750  aggregatedDesc.append(pginfo->m_description);
751  }
752  if (pginfo->m_clumpidx.toInt() ==
753  pginfo->m_clumpmax.toInt() - 1)
754  {
755  pginfo->m_title = aggregatedTitle;
756  pginfo->m_description = aggregatedDesc;
757  (*proglist)[pginfo->m_channel].push_back(*pginfo);
758  }
759  }
760  }
761  delete pginfo;
762  }
763  }
764  n = n.nextSibling();
765  }
766 
767  return true;
768 }
QDateTime m_endtime
Definition: programdata.h:138
QString m_description
Definition: programdata.h:135
bool dash_open(QFile &file, const QString &filename, int m, FILE *handle)
Definition: fillutil.cpp:11
QString m_clumpidx
Definition: programdata.h:233
static void parseVideo(QDomElement &element, ProgInfo *pginfo)
QString m_name
Definition: channelinfo.h:82
uint16_t m_airdate
movie year / production year
Definition: programdata.h:139
QString m_category
Definition: programdata.h:136
QString m_title_pronounce
Definition: programdata.h:230
QString m_title
Definition: programdata.h:133
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:15
QString m_syndicatedepisodenumber
Definition: programdata.h:144
QString m_channel
Definition: programdata.h:227
unsigned int uint
Definition: compat.h:140
static void parseCredits(QDomElement &element, ProgInfo *pginfo)
QString m_xmltvid
Definition: channelinfo.h:87
void lateInit()
Definition: xmltvparser.cpp:37
static guint32 * tmp
Definition: goom_core.c:35
QString m_subtitle
Definition: programdata.h:134
uint m_season
Definition: programdata.h:157
ProgramInfo::CategoryType string_to_myth_category_type(const QString &category_type)
QString m_tvformat
Definition: channelinfo.h:95
bool m_previouslyshown
Definition: programdata.h:153
QString m_icon
Definition: channelinfo.h:83
static void parseAudio(QDomElement &element, ProgInfo *pginfo)
QDate m_originalairdate
origial broadcast date
Definition: programdata.h:140
QStringList m_genres
Definition: programdata.h:156
static QString getFirstText(const QDomElement &element)
Definition: xmltvparser.cpp:63
def rating(profile, smoonURL, gate)
Definition: scan.py:25
unsigned char m_audioProps
Definition: programdata.h:146
QString m_inetref
Definition: programdata.h:152
unsigned char m_subtitleType
Definition: programdata.h:145
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString m_freqid
Definition: channelinfo.h:77
unsigned char t
Definition: ParseText.cpp:329
QDateTime m_starttime
Definition: programdata.h:137
QList< EventRating > m_ratings
Definition: programdata.h:155
QString m_tvGrabberPath
Definition: xmltvparser.h:30
uint m_episode
Definition: programdata.h:158
ProgInfo * parseProgram(QDomElement &element)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
static QString GetTelevisionGrabber()
QString m_channum
Definition: channelinfo.h:76
static uint ELFHash(const QByteArray &ba)
Definition: xmltvparser.cpp:43
QString myth_category_type_to_string(ProgramInfo::CategoryType category_type)
QString m_seriesId
Definition: programdata.h:150
QString m_startts
Definition: programdata.h:228
vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:120
QString m_movieGrabberPath
Definition: xmltvparser.h:29
uint m_totalepisodes
Definition: programdata.h:159
unsigned char m_videoProps
Definition: programdata.h:147
float m_stars
Definition: programdata.h:148
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
ProgramInfo::CategoryType m_categoryType
Definition: programdata.h:149
ChannelInfo * parseChannel(QDomElement &element, QUrl &baseUrl)
Definition: xmltvparser.cpp:75
static void fromXMLTVDate(QString &timestr, QDateTime &dt)
QString m_clumpmax
Definition: programdata.h:234
QString m_callsign
Definition: channelinfo.h:81
QString m_endts
Definition: programdata.h:229
bool parseFile(const QString &filename, ChannelInfoList *chanlist, QMap< QString, QList< ProgInfo > > *proglist)
uint16_t m_partnumber
Definition: programdata.h:142
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
static QString GetMovieGrabber()
unsigned int m_current_year
Definition: xmltvparser.h:28
Default UTC.
Definition: mythdate.h:14
uint16_t m_parttotal
Definition: programdata.h:143
void AddPerson(DBPerson::Role, const QString &name)
QString m_programId
Definition: programdata.h:151
unsigned char g
Definition: ParseText.cpp:329