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; |
319 | | QString season; |
320 | | QString episode; |
321 | | QString totalepisodes; |
322 | | auto *pginfo = new ProgInfo(); |
323 | | |
324 | | QString text = element.attribute("start", ""); |
325 | | fromXMLTVDate(text, pginfo->m_starttime); |
326 | | pginfo->m_startts = text; |
327 | | |
328 | | text = element.attribute("stop", ""); |
329 | | fromXMLTVDate(text, pginfo->m_endtime); |
330 | | pginfo->m_endts = text; |
331 | | |
332 | | text = element.attribute("channel", ""); |
333 | | QStringList split = text.split(" "); |
334 | | |
335 | | pginfo->m_channel = split[0]; |
336 | | |
337 | | text = element.attribute("clumpidx", ""); |
338 | | if (!text.isEmpty()) |
339 | | { |
340 | | split = text.split('/'); |
341 | | pginfo->m_clumpidx = split[0]; |
342 | | pginfo->m_clumpmax = split[1]; |
343 | | } |
344 | | |
345 | | for (QDomNode child = element.firstChild(); !child.isNull(); |
346 | | child = child.nextSibling()) |
347 | | { |
348 | | QDomElement info = child.toElement(); |
349 | | if (!info.isNull()) |
350 | | { |
351 | | if (info.tagName() == "title") |
352 | | { |
353 | | if (info.attribute("lang") == "ja_JP") |
354 | | { // NOLINT(bugprone-branch-clone) |
355 | | pginfo->m_title = getFirstText(info); |
356 | | } |
357 | | else if (info.attribute("lang") == "ja_JP@kana") |
358 | | { |
359 | | pginfo->m_title_pronounce = getFirstText(info); |
360 | | } |
361 | | else if (pginfo->m_title.isEmpty()) |
362 | | { |
363 | | pginfo->m_title = getFirstText(info); |
364 | | } |
365 | | } |
366 | | else if (info.tagName() == "sub-title" && |
367 | | pginfo->m_subtitle.isEmpty()) |
368 | | { |
369 | | pginfo->m_subtitle = getFirstText(info); |
370 | | } |
371 | | else if (info.tagName() == "desc" && pginfo->m_description.isEmpty()) |
372 | | { |
373 | | pginfo->m_description = getFirstText(info); |
374 | | } |
375 | | else if (info.tagName() == "category") |
376 | | { |
377 | | const QString cat = getFirstText(info); |
378 | | |
379 | | if (ProgramInfo::kCategoryNone == pginfo->m_categoryType && |
380 | | string_to_myth_category_type(cat) != ProgramInfo::kCategoryNone) |
381 | | { |
382 | | pginfo->m_categoryType = string_to_myth_category_type(cat); |
383 | | } |
384 | | else if (pginfo->m_category.isEmpty()) |
385 | | { |
386 | | pginfo->m_category = cat; |
387 | | } |
388 | | |
389 | | if ((cat.compare(QObject::tr("movie"),Qt::CaseInsensitive) == 0) || |
390 | | (cat.compare(QObject::tr("film"),Qt::CaseInsensitive) == 0)) |
391 | | { |
392 | | // Hack for tv_grab_uk_rt |
393 | | pginfo->m_categoryType = ProgramInfo::kCategoryMovie; |
394 | | } |
395 | | |
396 | | pginfo->m_genres.append(cat); |
397 | | } |
398 | | else if (info.tagName() == "date" && (pginfo->m_airdate == 0U)) |
399 | | { |
400 | | // Movie production year |
401 | | QString date = getFirstText(info); |
402 | | pginfo->m_airdate = date.left(4).toUInt(); |
403 | | } |
404 | | else if (info.tagName() == "star-rating" && pginfo->m_stars == 0.0F) |
405 | | { |
406 | | QDomNodeList values = info.elementsByTagName("value"); |
407 | | QDomElement item; |
408 | | QString stars; |
409 | | float rating = 0.0; |
410 | | |
411 | | // Use the first rating to appear in the xml, this should be |
412 | | // the most important one. |
413 | | // |
414 | | // Averaging is not a good idea here, any subsequent ratings |
415 | | // are likely to represent that days recommended programmes |
416 | | // which on a bad night could given to an average programme. |
417 | | // In the case of uk_rt it's not unknown for a recommendation |
418 | | // to be given to programmes which are 'so bad, you have to |
419 | | // watch!' |
420 | | // |
421 | | // XMLTV uses zero based ratings and signals no rating by absence. |
422 | | // A rating from 1 to 5 is encoded as 0/4 to 4/4. |
423 | | // MythTV uses zero to signal no rating! |
424 | | // The same rating is encoded as 0.2 to 1.0 with steps of 0.2, it |
425 | | // is not encoded as 0.0 to 1.0 with steps of 0.25 because |
426 | | // 0 signals no rating! |
427 | | // See http://xmltv.cvs.sourceforge.net/viewvc/xmltv/xmltv/xmltv.dtd?revision=1.47&view=markup#l539 |
428 | | item = values.item(0).toElement(); |
429 | | if (!item.isNull()) |
430 | | { |
431 | | stars = getFirstText(item); |
432 | | float num = stars.section('/', 0, 0).toFloat() + 1; |
433 | | float den = stars.section('/', 1, 1).toFloat() + 1; |
434 | | if (0.0F < den) |
435 | | rating = num/den; |
436 | | } |
437 | | |
438 | | pginfo->m_stars = rating; |
439 | | } |
440 | | else if (info.tagName() == "rating") |
441 | | { |
442 | | // again, the structure of ratings seems poorly represented |
443 | | // in the XML. no idea what we'd do with multiple values. |
444 | | QDomNodeList values = info.elementsByTagName("value"); |
445 | | QDomElement item = values.item(0).toElement(); |
446 | | if (item.isNull()) |
447 | | continue; |
448 | | EventRating rating; |
449 | | rating.m_system = info.attribute("system", ""); |
450 | | rating.m_rating = getFirstText(item); |
451 | | pginfo->m_ratings.append(rating); |
452 | | } |
453 | | else if (info.tagName() == "previously-shown") |
454 | | { |
455 | | pginfo->m_previouslyshown = true; |
456 | | |
457 | | QString prevdate = info.attribute("start"); |
458 | | if (!prevdate.isEmpty()) |
459 | | { |
460 | | QDateTime date; |
461 | | fromXMLTVDate(prevdate, date); |
462 | | pginfo->m_originalairdate = date.date(); |
463 | | } |
464 | | } |
465 | | else if (info.tagName() == "credits") |
466 | | { |
467 | | parseCredits(info, pginfo); |
468 | | } |
469 | | else if (info.tagName() == "subtitles") |
470 | | { |
471 | | if (info.attribute("type") == "teletext") |
472 | | pginfo->m_subtitleType |= SUB_NORMAL; |
473 | | else if (info.attribute("type") == "onscreen") |
474 | | pginfo->m_subtitleType |= SUB_ONSCREEN; |
475 | | else if (info.attribute("type") == "deaf-signed") |
476 | | pginfo->m_subtitleType |= SUB_SIGNED; |
477 | | } |
478 | | else if (info.tagName() == "audio") |
479 | | { |
480 | | parseAudio(info, pginfo); |
481 | | } |
482 | | else if (info.tagName() == "video") |
483 | | { |
484 | | parseVideo(info, pginfo); |
485 | | } |
486 | | else if (info.tagName() == "episode-num") |
487 | | { |
488 | | if (info.attribute("system") == "dd_progid") |
489 | | { |
490 | | QString episodenum(getFirstText(info)); |
491 | | // if this field includes a dot, strip it out |
492 | | int idx = episodenum.indexOf('.'); |
493 | | if (idx != -1) |
494 | | episodenum.remove(idx, 1); |
495 | | programid = episodenum; |
496 | | /* Only EPisodes and SHows are part of a series for SD */ |
497 | | if (programid.startsWith(QString("EP")) || |
498 | | programid.startsWith(QString("SH"))) |
499 | | pginfo->m_seriesId = QString("EP") + programid.mid(2,8); |
500 | | } |
501 | | else if (info.attribute("system") == "xmltv_ns") |
502 | | { |
503 | | QString episodenum(getFirstText(info)); |
504 | | episode = episodenum.section('.',1,1); |
505 | | totalepisodes = episode.section('/',1,1).trimmed(); |
506 | | episode = episode.section('/',0,0).trimmed(); |
507 | | season = episodenum.section('.',0,0).trimmed(); |
508 | | season = season.section('/',0,0).trimmed(); |
509 | | QString part(episodenum.section('.',2,2)); |
510 | | QString partnumber(part.section('/',0,0).trimmed()); |
511 | | QString parttotal(part.section('/',1,1).trimmed()); |
512 | | |
513 | | pginfo->m_categoryType = ProgramInfo::kCategorySeries; |
514 | | |
515 | | if (!season.isEmpty()) |
516 | | { |
517 | | int tmp = season.toUInt() + 1; |
518 | | pginfo->m_season = tmp; |
519 | | season = QString::number(tmp); |
520 | | pginfo->m_syndicatedepisodenumber = 'S' + season; |
521 | | } |
522 | | |
523 | | if (!episode.isEmpty()) |
524 | | { |
525 | | int tmp = episode.toUInt() + 1; |
526 | | pginfo->m_episode = tmp; |
527 | | episode = QString::number(tmp); |
528 | | pginfo->m_syndicatedepisodenumber.append('E' + episode); |
529 | | } |
530 | | |
531 | | if (!totalepisodes.isEmpty()) |
532 | | { |
533 | | pginfo->m_totalepisodes = totalepisodes.toUInt(); |
534 | | } |
535 | | |
536 | | uint partno = 0; |
537 | | if (!partnumber.isEmpty()) |
538 | | { |
539 | | bool ok = false; |
540 | | partno = partnumber.toUInt(&ok) + 1; |
541 | | partno = (ok) ? partno : 0; |
542 | | } |
543 | | |
544 | | if (!parttotal.isEmpty() && partno > 0) |
545 | | { |
546 | | bool ok = false; |
547 | | uint partto = parttotal.toUInt(&ok); |
548 | | if (ok && partnumber <= parttotal) |
549 | | { |
550 | | pginfo->m_parttotal = partto; |
551 | | pginfo->m_partnumber = partno; |
552 | | } |
553 | | } |
554 | | } |
555 | | else if (info.attribute("system") == "onscreen") |
556 | | { |
557 | | pginfo->m_categoryType = ProgramInfo::kCategorySeries; |
558 | | if (pginfo->m_subtitle.isEmpty()) |
559 | | { |
560 | | pginfo->m_subtitle = getFirstText(info); |
561 | | } |
562 | | } |
563 | | else if ((info.attribute("system") == "themoviedb.org") && |
564 | | (m_movieGrabberPath.endsWith(QString("/tmdb3.py")))) |
565 | | { |
566 | | /* text is movie/<inetref> */ |
567 | | QString inetrefRaw(getFirstText(info)); |
568 | | if (inetrefRaw.startsWith(QString("movie/"))) { |
569 | | QString inetref(QString ("tmdb3.py_") + inetrefRaw.section('/',1,1).trimmed()); |
570 | | pginfo->m_inetref = inetref; |
571 | | } |
572 | | } |
573 | | else if ((info.attribute("system") == "thetvdb.com") && |
574 | | (m_tvGrabberPath.endsWith(QString("/ttvdb.py")))) |
575 | | { |
576 | | /* text is series/<inetref> */ |
577 | | QString inetrefRaw(getFirstText(info)); |
578 | | if (inetrefRaw.startsWith(QString("series/"))) { |
579 | | QString inetref(QString ("ttvdb.py_") + inetrefRaw.section('/',1,1).trimmed()); |
580 | | pginfo->m_inetref = inetref; |
581 | | /* ProgInfo does not have a collectionref, so we don't set any */ |
582 | | } |
583 | | } |
584 | | } |
585 | | } |
586 | | } |
587 | | |
588 | | if (pginfo->m_category.isEmpty() && |
589 | | pginfo->m_categoryType != ProgramInfo::kCategoryNone) |
590 | | pginfo->m_category = myth_category_type_to_string(pginfo->m_categoryType); |
591 | | |
592 | | if (!pginfo->m_airdate |
593 | | && ProgramInfo::kCategorySeries != pginfo->m_categoryType) |
594 | | pginfo->m_airdate = m_currentYear; |
595 | | |
596 | | if (programid.isEmpty()) |
597 | | { |
598 | | |
599 | | /* Let's build ourself a programid */ |
600 | | |
601 | | if (ProgramInfo::kCategoryMovie == pginfo->m_categoryType) |
602 | | programid = "MV"; |
603 | | else if (ProgramInfo::kCategorySeries == pginfo->m_categoryType) |
604 | | programid = "EP"; |
605 | | else if (ProgramInfo::kCategorySports == pginfo->m_categoryType) |
606 | | programid = "SP"; |
607 | | else |
608 | | programid = "SH"; |
609 | | |
610 | | QString seriesid = QString::number(ELFHash(pginfo->m_title.toUtf8())); |
611 | | pginfo->m_seriesId = seriesid; |
612 | | programid.append(seriesid); |
613 | | |
614 | | if (!episode.isEmpty() && !season.isEmpty()) |
615 | | { |
616 | | /* Append unpadded episode and season number to the seriesid (to |
617 | | maintain consistency with historical encoding), but limit the |
618 | | season number representation to a single base-36 character to |
619 | | ensure unique programid generation. */ |
620 | | int season_int = season.toInt(); |
621 | | if (season_int > 35) |
622 | | { |
623 | | // Cannot represent season as a single base-36 character, so |
624 | | // remove the programid and fall back to normal dup matching. |
625 | | if (ProgramInfo::kCategoryMovie != pginfo->m_categoryType) |
626 | | programid.clear(); |
627 | | } |
628 | | else |
629 | | { |
630 | | programid.append(episode); |
631 | | programid.append(QString::number(season_int, 36)); |
632 | | if (pginfo->m_partnumber && pginfo->m_parttotal) |
633 | | { |
634 | | programid += QString::number(pginfo->m_partnumber); |
635 | | programid += QString::number(pginfo->m_parttotal); |
636 | | } |
637 | | } |
638 | | } |
639 | | else |
640 | | { |
641 | | /* No ep/season info? Well then remove the programid and rely on |
642 | | normal dupchecking methods instead. */ |
643 | | if (ProgramInfo::kCategoryMovie != pginfo->m_categoryType) |
644 | | programid.clear(); |
645 | | } |
| 179 | xml.readNext(); |
| 180 | if (xml.hasError()) { |
| 181 | LOG(VB_GENERAL, LOG_ERR, QString("Malformed XML file at line %1, %2").arg(xml.lineNumber()).arg(xml.errorString())); |
| 182 | return false; |
| 206 | while (!xml.atEnd() && !xml.hasError()) { |
| 207 | if (xml.readNextStartElement()) { |
| 208 | if (xml.name() == "tv") { |
| 209 | sourceUrl = QUrl(xml.attributes().value("source-info-url").toString()); |
| 210 | baseUrl = QUrl(xml.attributes().value("source-data-url").toString()); |
| 211 | } |
| 212 | if (xml.name() == "channel") { |
| 213 | //get id attribute |
| 214 | QString xmltvid; |
| 215 | xmltvid = xml.attributes().value( "id").toString(); |
| 216 | auto *chaninfo = new ChannelInfo; |
| 217 | chaninfo->m_xmltvId = xmltvid; |
| 218 | chaninfo->m_tvFormat = "Default"; |
| 219 | |
| 220 | //readNextStartElement says it reads for the next start element WITHIN the current element; but it doesnt; so we use readNext() |
| 221 | do { |
| 222 | if (!readNextWithErrorCheck(xml)) |
| 223 | return false; |
| 224 | if (xml.name() == "icon") { |
| 225 | if (chaninfo->m_icon.isEmpty()) { |
| 226 | QString path = xml.attributes().value("src").toString(); |
| 227 | if (!path.isEmpty() && !path.contains("://")) { |
| 228 | QString base = baseUrl.toString(QUrl::StripTrailingSlash); |
| 229 | chaninfo->m_icon = base + |
| 230 | ((path.startsWith("/")) ? path : QString("/") + path); |
| 231 | } |
| 232 | else if (!path.isEmpty()) { |
| 233 | QUrl url(path); |
| 234 | if (url.isValid()) |
| 235 | chaninfo->m_icon = url.toString(); |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | else if (xml.name() == "display-name") { |
| 240 | //now get text |
| 241 | QString text; |
| 242 | text = xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 243 | if (!text.isEmpty()) { |
| 244 | if (chaninfo->m_name.isEmpty()) { |
| 245 | chaninfo->m_name = text; |
| 246 | } |
| 247 | else if (chaninfo->m_callSign.isEmpty()) { |
| 248 | chaninfo->m_callSign = text; |
| 249 | } |
| 250 | else if (chaninfo->m_chanNum.isEmpty()) { |
| 251 | chaninfo->m_chanNum = text; |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | while (! (xml.isEndElement() && xml.name() == "channel")); |
| 257 | chaninfo->m_freqId = chaninfo->m_chanNum; |
| 258 | //TODO optimize this, no use to do al this parsing if xmltvid is empty; but make sure you will read until the next channel!! |
| 259 | if (!chaninfo->m_xmltvId.isEmpty()) |
| 260 | chanlist->push_back(*chaninfo); |
| 261 | delete chaninfo; |
| 262 | }//channel |
| 263 | else if (xml.name() == "programme") { |
| 264 | QString programid, season, episode, totalepisodes; |
| 265 | auto *pginfo = new ProgInfo(); |
| 266 | |
| 267 | QString text = xml.attributes().value("start").toString(); |
| 268 | fromXMLTVDate(text, pginfo->m_starttime); |
| 269 | pginfo->m_startts = text; |
| 270 | |
| 271 | text = xml.attributes().value("stop").toString(); |
| 272 | fromXMLTVDate(text, pginfo->m_endtime); |
| 273 | pginfo->m_endts = text; |
| 274 | |
| 275 | text = xml.attributes().value("channel").toString(); |
| 276 | QStringList split = text.split(" "); |
| 277 | pginfo->m_channel = split[0]; |
| 278 | |
| 279 | text = xml.attributes().value("clumpidx").toString(); |
| 280 | if (!text.isEmpty()) { |
| 281 | split = text.split('/'); |
| 282 | pginfo->m_clumpidx = split[0]; |
| 283 | pginfo->m_clumpmax = split[1]; |
| 284 | } |
| 285 | |
| 286 | do { |
| 287 | if (!readNextWithErrorCheck(xml)) |
| 288 | return false; |
| 289 | if (xml.name() == "title") { |
| 290 | QString text2=xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 291 | if (xml.attributes().value("lang").toString() == "ja_JP") { |
| 292 | pginfo->m_title = text2; |
| 293 | } |
| 294 | else if (xml.attributes().value("lang").toString() == "ja_JP@kana") { |
| 295 | pginfo->m_title_pronounce = text2; |
| 296 | } |
| 297 | else if (pginfo->m_title.isEmpty()) { |
| 298 | pginfo->m_title = text2; |
| 299 | } |
| 300 | } |
| 301 | else if (xml.name() == "sub-title" && pginfo->m_subtitle.isEmpty()) { |
| 302 | pginfo->m_subtitle = xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 303 | } |
| 304 | else if (xml.name() == "subtitles") { |
| 305 | if (xml.attributes().value("type").toString() == "teletext") |
| 306 | pginfo->m_subtitleType |= SUB_NORMAL; |
| 307 | else if (xml.attributes().value("type").toString() == "onscreen") |
| 308 | pginfo->m_subtitleType |= SUB_ONSCREEN; |
| 309 | else if (xml.attributes().value("type").toString() == "deaf-signed") |
| 310 | pginfo->m_subtitleType |= SUB_SIGNED; |
| 311 | } |
| 312 | else if (xml.name() == "desc" && pginfo->m_description.isEmpty()) { |
| 313 | pginfo->m_description = xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 314 | } |
| 315 | else if (xml.name() == "category") { |
| 316 | const QString cat = xml.readElementText(QXmlStreamReader::SkipChildElements); |
690 | | QDomNode n = docElem.firstChild(); |
691 | | while (!n.isNull()) |
692 | | { |
693 | | QDomElement e = n.toElement(); |
694 | | if (!e.isNull()) |
695 | | { |
696 | | if (e.tagName() == "channel") |
697 | | { |
698 | | ChannelInfo *chinfo = parseChannel(e, baseUrl); |
699 | | if (!chinfo->m_xmltvId.isEmpty()) |
700 | | chanlist->push_back(*chinfo); |
701 | | delete chinfo; |
702 | | } |
703 | | else if (e.tagName() == "programme") |
704 | | { |
705 | | ProgInfo *pginfo = parseProgram(e); |
| 318 | if (ProgramInfo::kCategoryNone == pginfo->m_categoryType && string_to_myth_category_type(cat) != ProgramInfo::kCategoryNone) { |
| 319 | pginfo->m_categoryType = string_to_myth_category_type(cat); |
| 320 | } |
| 321 | else if (pginfo->m_category.isEmpty()) { |
| 322 | pginfo->m_category = cat; |
| 323 | } |
| 324 | if ((cat.compare(QObject::tr("movie"),Qt::CaseInsensitive) == 0) || (cat.compare(QObject::tr("film"),Qt::CaseInsensitive) == 0)) { |
| 325 | // Hack for tv_grab_uk_rt |
| 326 | pginfo->m_categoryType = ProgramInfo::kCategoryMovie; |
| 327 | } |
| 328 | pginfo->m_genres.append(cat); |
| 329 | } |
| 330 | else if (xml.name() == "date" && (pginfo->m_airdate == 0U)) { |
| 331 | // Movie production year |
| 332 | QString date = xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 333 | pginfo->m_airdate = date.left(4).toUInt(); |
| 334 | } |
| 335 | else if (xml.name() == "star-rating") { |
| 336 | QString stars; |
| 337 | float rating = 0.0; |
| 338 | |
| 339 | // Use the first rating to appear in the xml, this should be |
| 340 | // the most important one. |
| 341 | // |
| 342 | // Averaging is not a good idea here, any subsequent ratings |
| 343 | // are likely to represent that days recommended programmes |
| 344 | // which on a bad night could given to an average programme. |
| 345 | // In the case of uk_rt it's not unknown for a recommendation |
| 346 | // to be given to programmes which are 'so bad, you have to |
| 347 | // watch!' |
| 348 | // |
| 349 | // XMLTV uses zero based ratings and signals no rating by absence. |
| 350 | // A rating from 1 to 5 is encoded as 0/4 to 4/4. |
| 351 | // MythTV uses zero to signal no rating! |
| 352 | // The same rating is encoded as 0.2 to 1.0 with steps of 0.2, it |
| 353 | // is not encoded as 0.0 to 1.0 with steps of 0.25 because |
| 354 | // 0 signals no rating! |
| 355 | // See http://xmltv.cvs.sourceforge.net/viewvc/xmltv/xmltv/xmltv.dtd?revision=1.47&view=markup#l539 |
| 356 | stars = "0"; //no rating |
| 357 | do { |
| 358 | if (!readNextWithErrorCheck(xml)) |
| 359 | return false; |
| 360 | if (xml.isStartElement()) { |
| 361 | if (xml.name() == "value") { |
| 362 | stars=xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 363 | } |
| 364 | } |
| 365 | } |
| 366 | while (! (xml.isEndElement() && xml.name() == "star-rating")); |
| 367 | if (pginfo->m_stars == 0.0F) { |
| 368 | float num = stars.section('/', 0, 0).toFloat() + 1; |
| 369 | float den = stars.section('/', 1, 1).toFloat() + 1; |
| 370 | if (0.0F < den) |
| 371 | rating = num/den; |
| 372 | } |
| 373 | pginfo->m_stars = rating; |
| 374 | } |
| 375 | else if (xml.name() == "rating") { |
| 376 | // again, the structure of ratings seems poorly represented |
| 377 | // in the XML. no idea what we'd do with multiple values. |
| 378 | QString rat; |
| 379 | QString rating_system = xml.attributes().value("system").toString(); |
| 380 | if (rating_system == NULL) |
| 381 | rating_system = ""; |
| 382 | |
| 383 | do { |
| 384 | if (!readNextWithErrorCheck(xml)) |
| 385 | return false; |
| 386 | if (xml.isStartElement()) { |
| 387 | if (xml.name() == "value") { |
| 388 | rat=xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | while (! (xml.isEndElement() && xml.name() == "rating")); |
| 393 | |
| 394 | if (!rat.isEmpty()) { |
| 395 | EventRating rating; |
| 396 | rating.m_system = rating_system; |
| 397 | rating.m_rating = rat; |
| 398 | pginfo->m_ratings.append(rating); |
| 399 | } |
| 400 | } |
| 401 | else if (xml.name() == "previously-shown") { |
| 402 | pginfo->m_previouslyshown = true; |
| 403 | QString prevdate = xml.attributes().value( "start").toString(); |
| 404 | if (!prevdate.isEmpty()) { |
| 405 | QDateTime date; |
| 406 | fromXMLTVDate(prevdate, date); |
| 407 | pginfo->m_originalairdate = date.date(); |
| 408 | } |
| 409 | } |
| 410 | else if (xml.name() == "credits") { |
| 411 | do { |
| 412 | if (!readNextWithErrorCheck(xml)) |
| 413 | return false; |
| 414 | if (xml.isStartElement()) { |
| 415 | QString tagname=xml.name().toString(); |
| 416 | QString text2=xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 417 | pginfo->AddPerson(tagname, text2); |
| 418 | } |
| 419 | } |
| 420 | while (! (xml.isEndElement() && xml.name() == "credits")); |
| 421 | } |
| 422 | else if (xml.name() == "audio") { |
| 423 | do { |
| 424 | if (!readNextWithErrorCheck(xml)) |
| 425 | return false; |
| 426 | if (xml.isStartElement()) { |
| 427 | if (xml.name() == "stereo") { |
| 428 | QString text2=xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 429 | if (text2 == "mono") { |
| 430 | pginfo->m_audioProps |= AUD_MONO; |
| 431 | } |
| 432 | else if (text2 == "stereo") { |
| 433 | pginfo->m_audioProps |= AUD_STEREO; |
| 434 | } |
| 435 | else if (text2 == "dolby" || text2 == "dolby digital") { |
| 436 | pginfo->m_audioProps |= AUD_DOLBY; |
| 437 | } |
| 438 | else if (text2 == "surround") { |
| 439 | pginfo->m_audioProps |= AUD_SURROUND; |
| 440 | } |
| 441 | } |
| 442 | } |
| 443 | } |
| 444 | while (! (xml.isEndElement() && xml.name() == "audio")); |
| 445 | } |
| 446 | else if (xml.name() == "video") { |
| 447 | do { |
| 448 | if (!readNextWithErrorCheck(xml)) |
| 449 | return false; |
| 450 | if (xml.isStartElement()) { |
| 451 | if (xml.name() == "quality") { |
| 452 | if (xml.readElementText(QXmlStreamReader::SkipChildElements) == "HDTV") |
| 453 | pginfo->m_videoProps |= VID_HDTV; |
| 454 | } |
| 455 | else if (xml.name() == "aspect") { |
| 456 | if (xml.readElementText(QXmlStreamReader::SkipChildElements) == "16:9") |
| 457 | pginfo->m_videoProps |= VID_WIDESCREEN; |
| 458 | } |
| 459 | } |
| 460 | } |
| 461 | while (! (xml.isEndElement() && xml.name() == "video")); |
| 462 | } |
| 463 | else if (xml.name() == "episode-num") { |
| 464 | QString system = xml.attributes().value( "system").toString(); |
| 465 | if (system == "dd_progid") { |
| 466 | QString episodenum(xml.readElementText(QXmlStreamReader::SkipChildElements)); |
| 467 | // if this field includes a dot, strip it out |
| 468 | int idx = episodenum.indexOf('.'); |
| 469 | if (idx != -1) |
| 470 | episodenum.remove(idx, 1); |
| 471 | programid = episodenum; |
| 472 | // Only EPisodes and SHows are part of a series for SD |
| 473 | if (programid.startsWith(QString("EP")) || |
| 474 | programid.startsWith(QString("SH"))) |
| 475 | pginfo->m_seriesId = QString("EP") + programid.mid(2,8); |
| 476 | } |
| 477 | else if (system == "xmltv_ns") { |
| 478 | QString episodenum(xml.readElementText(QXmlStreamReader::SkipChildElements)); |
| 479 | episode = episodenum.section('.',1,1); |
| 480 | totalepisodes = episode.section('/',1,1).trimmed(); |
| 481 | episode = episode.section('/',0,0).trimmed(); |
| 482 | season = episodenum.section('.',0,0).trimmed(); |
| 483 | season = season.section('/',0,0).trimmed(); |
| 484 | QString part(episodenum.section('.',2,2)); |
| 485 | QString partnumber(part.section('/',0,0).trimmed()); |
| 486 | QString parttotal(part.section('/',1,1).trimmed()); |
| 487 | pginfo->m_categoryType = ProgramInfo::kCategorySeries; |
| 488 | if (!season.isEmpty()) { |
| 489 | int tmp = season.toUInt() + 1; |
| 490 | pginfo->m_season = tmp; |
| 491 | season = QString::number(tmp); |
| 492 | pginfo->m_syndicatedepisodenumber = 'S' + season; |
| 493 | } |
| 494 | if (!episode.isEmpty()) { |
| 495 | int tmp = episode.toUInt() + 1; |
| 496 | pginfo->m_episode = tmp; |
| 497 | episode = QString::number(tmp); |
| 498 | pginfo->m_syndicatedepisodenumber.append('E' + episode); |
| 499 | } |
| 500 | if (!totalepisodes.isEmpty()) { |
| 501 | pginfo->m_totalepisodes = totalepisodes.toUInt(); |
| 502 | } |
| 503 | uint partno = 0; |
| 504 | if (!partnumber.isEmpty()) { |
| 505 | bool ok = false; |
| 506 | partno = partnumber.toUInt(&ok) + 1; |
| 507 | partno = (ok) ? partno : 0; |
| 508 | } |
| 509 | if (!parttotal.isEmpty() && partno > 0) { |
| 510 | bool ok = false; |
| 511 | uint partto = parttotal.toUInt(&ok); |
| 512 | if (ok && partnumber <= parttotal) { |
| 513 | pginfo->m_parttotal = partto; |
| 514 | pginfo->m_partnumber = partno; |
| 515 | } |
| 516 | } |
| 517 | } |
| 518 | else if (system == "onscreen") { |
| 519 | pginfo->m_categoryType = ProgramInfo::kCategorySeries; |
| 520 | if (pginfo->m_subtitle.isEmpty()) { |
| 521 | pginfo->m_subtitle = xml.readElementText(QXmlStreamReader::SkipChildElements); |
| 522 | } |
| 523 | } |
| 524 | else if ((system == "themoviedb.org") && (m_movieGrabberPath.endsWith(QString("/tmdb3.py")))) { |
| 525 | // text is movie/<inetref> |
| 526 | QString inetrefRaw(xml.readElementText(QXmlStreamReader::SkipChildElements)); |
| 527 | if (inetrefRaw.startsWith(QString("movie/"))) { |
| 528 | QString inetref(QString ("tmdb3.py_") + inetrefRaw.section('/',1,1).trimmed()); |
| 529 | pginfo->m_inetref = inetref; |
| 530 | } |
| 531 | } |
| 532 | else if ((system == "thetvdb.com") && (m_tvGrabberPath.endsWith(QString("/ttvdb.py")))) { |
| 533 | // text is series/<inetref> |
| 534 | QString inetrefRaw(xml.readElementText(QXmlStreamReader::SkipChildElements)); |
| 535 | if (inetrefRaw.startsWith(QString("series/"))) { |
| 536 | QString inetref(QString ("ttvdb.py_") + inetrefRaw.section('/',1,1).trimmed()); |
| 537 | pginfo->m_inetref = inetref; |
| 538 | // ProgInfo does not have a collectionref, so we don't set any |
| 539 | } |
| 540 | } |
| 541 | }//episode-num |
| 542 | } |
| 543 | while (! (xml.isEndElement() && xml.name() == "programme")); |
| 544 | |
| 545 | if (pginfo->m_category.isEmpty() && pginfo->m_categoryType != ProgramInfo::kCategoryNone) |
| 546 | pginfo->m_category = myth_category_type_to_string(pginfo->m_categoryType); |
| 547 | |
| 548 | if (!pginfo->m_airdate && ProgramInfo::kCategorySeries != pginfo->m_categoryType) |
| 549 | pginfo->m_airdate = m_currentYear; |
| 550 | |
| 551 | if (programid.isEmpty()) { |
| 552 | //Let's build ourself a programid |
| 553 | if (ProgramInfo::kCategoryMovie == pginfo->m_categoryType) |
| 554 | programid = "MV"; |
| 555 | else if (ProgramInfo::kCategorySeries == pginfo->m_categoryType) |
| 556 | programid = "EP"; |
| 557 | else if (ProgramInfo::kCategorySports == pginfo->m_categoryType) |
| 558 | programid = "SP"; |
| 559 | else |
| 560 | programid = "SH"; |