MythTV master
metadatacommon.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <utility>
3
4// Qt headers
5#include <QCoreApplication>
6#include <QLocale>
7#include <QMetaType>
8#include <QRegularExpression>
9
16
17#include "metadatacommon.h"
18
19static int x0 = qRegisterMetaType< RefCountHandler<MetadataLookup> >();
20
21// full constructor
24 LookupType subtype,
25 QVariant data,
26 LookupStep step,
27 bool automatic,
28 bool handleimages,
29 bool allowoverwrites,
30 bool allowgeneric,
31 bool preferdvdorder,
32 QString host,
33 QString filename,
34 const QString &title,
35 QString network,
36 QString status,
37 QStringList categories,
38 const float userrating,
39 uint ratingcount,
40 QString language,
41 QString subtitle,
42 QString tagline,
43 QString description,
44 uint season,
45 uint episode,
46 uint chanid,
47 QString channum,
48 QString chansign,
49 QString channame,
50 QString chanplaybackfilters,
51 QString recgroup,
52 QString playgroup,
53 QString seriesid,
54 QString programid,
55 QString storagegroup,
56 QDateTime startts,
57 QDateTime endts,
58 QDateTime recstartts,
59 QDateTime recendts,
60 uint programflags,
61 uint audioproperties,
62 uint videoproperties,
63 uint subtitletype,
64 QString certification,
65 QStringList countries,
66 const float popularity,
67 const uint budget,
68 const uint revenue,
69 QString album,
70 uint tracknum,
71 QString system,
72 const uint year,
73 const QDate releasedate,
74 QDateTime lastupdated,
75 std::chrono::minutes runtime,
76 std::chrono::seconds runtimesecs,
77 QString inetref,
78 QString collectionref,
79 QString tmsref,
80 QString imdb,
81 PeopleMap people,
82 QStringList studios,
83 QString homepage,
84 QString trailerURL,
85 ArtworkMap artwork,
86 DownloadMap downloads) :
87 ReferenceCounter("MetadataLookup"),
88
89 m_type(type),
90 m_subtype(subtype),
91 m_data(std::move(data)),
92 m_step(step),
93 m_automatic(automatic),
94 m_handleImages(handleimages),
95 m_allowOverwrites(allowoverwrites),
96 m_allowGeneric(allowgeneric),
97 m_dvdOrder(preferdvdorder),
98 m_host(std::move(host)),
99 m_filename(std::move(filename)),
100 m_title(title),
101 m_baseTitle(title),
102 m_network(std::move(network)),
103 m_status(std::move(status)),
104 m_categories(std::move(categories)),
105 m_userRating(userrating),
106 m_ratingCount(ratingcount),
107 m_language(std::move(language)),
108 m_subtitle(std::move(subtitle)),
109 m_tagline(std::move(tagline)),
110 m_description(std::move(description)),
111 m_season(season),
112 m_episode(episode),
113 m_chanId(chanid),
114 m_chanNum(std::move(channum)),
115 m_chanSign(std::move(chansign)),
116 m_chanName(std::move(channame)),
117 m_chanPlaybackFilters(std::move(chanplaybackfilters)),
118 m_recGroup(std::move(recgroup)),
119 m_playGroup(std::move(playgroup)),
120 m_seriesId(std::move(seriesid)),
121 m_programid(std::move(programid)),
122 m_storageGroup(std::move(storagegroup)),
123 m_startTs(std::move(startts)),
124 m_endTs(std::move(endts)),
125 m_recStartTs(std::move(recstartts)),
126 m_recEndTs(std::move(recendts)),
127 m_programFlags(programflags),
128 m_audioProperties(audioproperties),
129 m_videoProperties(videoproperties),
130 m_subtitleType(subtitletype),
131 m_certification(std::move(certification)),
132 m_countries(std::move(countries)),
133 m_popularity(popularity),
134 m_budget(budget),
135 m_revenue(revenue),
136 m_album(std::move(album)),
137 m_trackNum(tracknum),
138 m_system(std::move(system)),
139 m_year(year),
140 m_releaseDate(releasedate),
141 m_lastUpdated(std::move(lastupdated)),
142 m_runtime(runtime),
143 m_runtimeSecs(runtimesecs),
144 m_inetRef(std::move(inetref)),
145 m_collectionRef(std::move(collectionref)),
146 m_tmsRef(std::move(tmsref)),
147 m_imdb(std::move(imdb)),
148 m_people(std::move(people)),
149 m_studios(std::move(studios)),
150 m_homepage(std::move(homepage)),
151 m_trailerURL(std::move(trailerURL)),
152 m_artwork(std::move(artwork)),
153 m_downloads(std::move(downloads))
154{
155 QString manRecSuffix = QString(" (%1)").arg(QObject::tr("Manual Record"));
156 m_baseTitle.replace(manRecSuffix,"");
157}
158
159// ProgramInfo-style constructor
162 LookupType subtype,
163 QVariant data,
164 LookupStep step,
165 bool automatic,
166 bool handleimages,
167 bool allowoverwrites,
168 bool allowgeneric,
169 bool preferdvdorder,
170 QString host,
171 QString filename,
172 const QString &title,
173 QStringList categories,
174 const float userrating,
175 QString subtitle,
176 QString description,
177 uint chanid,
178 QString channum,
179 QString chansign,
180 QString channame,
181 QString chanplaybackfilters,
182 QString recgroup,
183 QString playgroup,
184 QString seriesid,
185 QString programid,
186 QString storagegroup,
187 QDateTime startts,
188 QDateTime endts,
189 QDateTime recstartts,
190 QDateTime recendts,
191 uint programflags,
192 uint audioproperties,
193 uint videoproperties,
194 uint subtitletype,
195 const uint year,
196 const QDate releasedate,
197 QDateTime lastupdated,
198 std::chrono::minutes runtime,
199 std::chrono::seconds runtimesecs) :
200 ReferenceCounter("MetadataLookup"),
201
202 m_type(type),
203 m_subtype(subtype),
204 m_data(std::move(data)),
205 m_step(step),
206 m_automatic(automatic),
207 m_handleImages(handleimages),
208 m_allowOverwrites(allowoverwrites),
209 m_allowGeneric(allowgeneric),
210 m_dvdOrder(preferdvdorder),
211 m_host(std::move(host)),
212 m_filename(std::move(filename)),
213 m_title(title),
214 m_baseTitle(title),
215 m_categories(std::move(categories)),
216 m_userRating(userrating),
217 m_subtitle(std::move(subtitle)),
218 m_description(std::move(description)),
219 m_chanId(chanid),
220 m_chanNum(std::move(channum)),
221 m_chanSign(std::move(chansign)),
222 m_chanName(std::move(channame)),
223 m_chanPlaybackFilters(std::move(chanplaybackfilters)),
224 m_recGroup(std::move(recgroup)),
225 m_playGroup(std::move(playgroup)),
226 m_seriesId(std::move(seriesid)),
227 m_programid(std::move(programid)),
228 m_storageGroup(std::move(storagegroup)),
229 m_startTs(std::move(startts)),
230 m_endTs(std::move(endts)),
231 m_recStartTs(std::move(recstartts)),
232 m_recEndTs(std::move(recendts)),
233 m_programFlags(programflags),
234 m_audioProperties(audioproperties),
235 m_videoProperties(videoproperties),
236 m_subtitleType(subtitletype),
237 m_year(year),
238 m_releaseDate(releasedate),
239 m_lastUpdated(std::move(lastupdated)),
240 m_runtime(runtime),
241 m_runtimeSecs(runtimesecs)
242{
243 QString manRecSuffix = QString(" (%1)").arg(QObject::tr("Manual Record"));
244 m_baseTitle.replace(manRecSuffix,"");
245}
246
247// XBMC NFO-style constructor
250 LookupType subtype,
251 QVariant data,
252 LookupStep step,
253 bool automatic,
254 bool handleimages,
255 bool allowoverwrites,
256 bool allowgeneric,
257 bool preferdvdorder,
258 QString host,
259 QString filename,
260 const QString &title,
261 QStringList categories,
262 const float userrating,
263 QString subtitle,
264 QString tagline,
265 QString description,
266 uint season,
267 uint episode,
268 QString certification,
269 const uint year,
270 const QDate releasedate,
271 std::chrono::minutes runtime,
272 std::chrono::seconds runtimesecs,
273 QString inetref,
274 PeopleMap people,
275 QString trailerURL,
276 ArtworkMap artwork,
277 DownloadMap downloads) :
278 ReferenceCounter("MetadataLookup"),
279
280 m_type(type),
281 m_subtype(subtype),
282 m_data(std::move(data)),
283 m_step(step),
284 m_automatic(automatic),
285 m_handleImages(handleimages),
286 m_allowOverwrites(allowoverwrites),
287 m_allowGeneric(allowgeneric),
288 m_dvdOrder(preferdvdorder),
289 m_host(std::move(host)),
290 m_filename(std::move(filename)),
291 m_title(title),
292 m_baseTitle(title),
293 m_categories(std::move(categories)),
294 m_userRating(userrating),
295 m_subtitle(std::move(subtitle)),
296 m_tagline(std::move(tagline)),
297 m_description(std::move(description)),
298 m_season(season),
299 m_episode(episode),
300 m_certification(std::move(certification)),
301 m_year(year),
302 m_releaseDate(releasedate),
303 m_runtime(runtime),
304 m_runtimeSecs(runtimesecs),
305 m_inetRef(std::move(inetref)),
306 m_people(std::move(people)),
307 m_trailerURL(std::move(trailerURL)),
308 m_artwork(std::move(artwork)),
309 m_downloads(std::move(downloads))
310{
311 QString manRecSuffix = QString(" (%1)").arg(QObject::tr("Manual Record"));
312 m_baseTitle.replace(manRecSuffix,"");
313}
314
316{
317 QList<PersonInfo> ret;
318 // QMultiMap::values() returns items in reverse order which we need to
319 // correct by iterating back over the list
320 // See http://qt-project.org/doc/qt-4.8/qmultimap.html#details
321 // Specifically "The items that share the same key are available from "
322 // "most recently to least recently inserted."
323 QListIterator<PersonInfo> it(m_people.values(type));
324 it.toBack();
325 while (it.hasPrevious())
326 ret.append(it.previous());
327
328 return ret;
329}
330
332{
333 ArtworkList ret;
334
335 // QMultiMap::values() returns items in reverse order which we need to
336 // correct by iterating back over the list
337 // See http://qt-project.org/doc/qt-4.8/qmultimap.html#details
338 // Specifically "The items that share the same key are available from "
339 // "most recently to least recently inserted."
340 QListIterator<ArtworkInfo> it(m_artwork.values(type));
341 it.toBack();
342 while (it.hasPrevious())
343 ret.append(it.previous());
344
345 return ret;
346}
347
349{
350 metadataMap["filename"] = m_filename;
351 metadataMap["title"] = m_title;
352 metadataMap["network"] = m_network;
353 metadataMap["status"] = m_status;
354 metadataMap["category"] = m_categories.join(", ");
355 metadataMap["userrating"] = QString::number(m_userRating);
356 metadataMap["ratingcount"] = QString::number(m_ratingCount);
357 metadataMap["language"] = m_language;
358 metadataMap["subtitle"] = m_subtitle;
359 metadataMap["tagline"] = m_tagline;
360 metadataMap["description0"] = metadataMap["description"] = m_description;
361 metadataMap["season"] = QString::number(m_season);
362 metadataMap["episode"] = QString::number(m_episode);
363 metadataMap["chanid"] = QString::number(m_chanId);
364 metadataMap["channum"] = m_chanNum;
365 metadataMap["callsign"] = m_chanSign;
366 metadataMap["channame"] = m_chanName;
367 metadataMap["playbackfilters"] = m_chanPlaybackFilters;
368 metadataMap["recgroup"] = m_recGroup;
369 metadataMap["playgroup"] = m_playGroup;
370 metadataMap["seriesid"] = m_seriesId;
371 metadataMap["programid"] = m_programid;
372 metadataMap["storagegroup"] = m_storageGroup;
373 metadataMap["startts"] = MythDate::toString(m_startTs, MythDate::kDateFull);
374 metadataMap["endts"] = MythDate::toString(m_endTs, MythDate::kDateFull);
375 metadataMap["recstartts"] = MythDate::toString(m_recStartTs, MythDate::kDateFull);
376 metadataMap["recendts"] = MythDate::toString(m_recEndTs, MythDate::kDateFull);
377 metadataMap["certification"] = m_certification;
378 metadataMap["countries"] = m_countries.join(", ");
379 metadataMap["popularity"] = QString::number(m_popularity);
380 metadataMap["budget"] = QString::number(m_budget);
381 metadataMap["revenue"] = QString::number(m_revenue);
382 metadataMap["album"] = m_album;
383 metadataMap["tracknum"] = QString::number(m_trackNum);
384 metadataMap["system"] = m_system;
385 metadataMap["year"] = QString::number(m_year);
386
387 metadataMap["releasedate"] = MythDate::toString(
389 metadataMap["lastupdated"] = MythDate::toString(m_lastUpdated, MythDate::kDateFull);
390
391 metadataMap["runtime"] = QCoreApplication::translate("(Common)",
392 "%n minute(s)",
393 "",
394 m_runtime.count());
395
396
397 metadataMap["runtimesecs"] = QCoreApplication::translate("(Common)",
398 "%n second(s)",
399 "",
400 m_runtimeSecs.count());
401 metadataMap["inetref"] = m_inetRef;
402 metadataMap["collectionref"] = m_collectionRef;
403 metadataMap["tmsref"] = m_tmsRef;
404 metadataMap["imdb"] = m_imdb;
405 metadataMap["studios"] = m_studios.join(", ");
406 metadataMap["homepage"] = m_homepage;
407 metadataMap["trailer"] = m_trailerURL;
408}
409
411{
412 auto runtimesecs =
413 std::chrono::seconds(pginfo->GetRecordingStartTime()
414 .secsTo(pginfo->GetRecordingEndTime()));
415 auto runtime = duration_cast<std::chrono::minutes>(runtimesecs);
416
418 QVariant::fromValue(pginfo), kLookupData, false, false, false, false, false,
419 pginfo->GetHostname(),pginfo->GetBasename(),pginfo->GetTitle(),
420 QStringList() << pginfo->GetCategory(), pginfo->GetStars() * 10,
421 pginfo->GetSubtitle(), pginfo->GetDescription(), pginfo->GetChanID(),
422 pginfo->GetChanNum(), pginfo->GetChannelSchedulingID(),
423 pginfo->GetChannelName(), pginfo->GetChannelPlaybackFilters(),
424 pginfo->GetRecordingGroup(), pginfo->GetPlaybackGroup(),
425 pginfo->GetSeriesID(), pginfo->GetProgramID(), pginfo->GetStorageGroup(),
426 pginfo->GetScheduledStartTime(), pginfo->GetScheduledEndTime(),
427 pginfo->GetRecordingStartTime(), pginfo->GetRecordingEndTime(),
428 pginfo->GetProgramFlags(), pginfo->GetAudioProperties(),
429 pginfo->GetVideoProperties(), pginfo->GetSubtitleType(),
430 pginfo->GetYearOfInitialRelease(), pginfo->GetOriginalAirDate(),
431 pginfo->GetLastModifiedTime(), runtime, runtimesecs);
432
433 ret->SetSeason(pginfo->GetSeason());
434 ret->SetEpisode(pginfo->GetEpisode());
435 ret->SetInetref(pginfo->GetInetRef());
436
437 return ret;
438}
439
441{
442 QDomDocument doc("MythMetadataXML");
443
444 QDomElement root = doc.createElement("metadata");
445 doc.appendChild(root);
446
447 for (const auto & item : std::as_const(list))
448 CreateMetadataXMLItem(item, root, doc);
449
450 return doc;
451}
452
454{
455 QDomDocument doc("MythMetadataXML");
456
457 QDomElement root = doc.createElement("metadata");
458 doc.appendChild(root);
459
460 CreateMetadataXMLItem(lookup, root, doc);
461
462 return doc;
463}
464
465QDomDocument CreateMetadataXML(ProgramInfo *pginfo)
466{
467 QDomDocument doc("MythMetadataXML");
468
469 MetadataLookup *lookup = LookupFromProgramInfo(pginfo);
470 if (lookup)
471 {
472 doc = CreateMetadataXML(lookup);
473 lookup->DecrRef();
474 }
475 lookup = nullptr;
476
477 return doc;
478}
479
481 QDomElement placetoadd,
482 QDomDocument docroot)
483{
484 if (!lookup)
485 return;
486
487 QDomElement item = docroot.createElement("item");
488 placetoadd.appendChild(item);
489 QString RFC822("ddd, d MMMM yyyy hh:mm:ss");
490
491 // Language
492 if (!lookup->GetLanguage().isEmpty())
493 {
494 QDomElement language = docroot.createElement("language");
495 item.appendChild(language);
496 language.appendChild(docroot.createTextNode(lookup->GetLanguage()));
497 }
498 // Title
499 if (!lookup->GetTitle().isEmpty())
500 {
501 QDomElement title = docroot.createElement("title");
502 item.appendChild(title);
503 title.appendChild(docroot.createTextNode(lookup->GetTitle()));
504 }
505 // Subtitle
506 if (!lookup->GetSubtitle().isEmpty())
507 {
508 QDomElement subtitle = docroot.createElement("subtitle");
509 item.appendChild(subtitle);
510 subtitle.appendChild(docroot.createTextNode(lookup->GetSubtitle()));
511 }
512 // Network
513 if (!lookup->GetNetwork().isEmpty())
514 {
515 QDomElement network = docroot.createElement("network");
516 item.appendChild(network);
517 network.appendChild(docroot.createTextNode(lookup->GetNetwork()));
518 }
519 // Status
520 if (!lookup->GetStatus().isEmpty())
521 {
522 QDomElement status = docroot.createElement("status");
523 item.appendChild(status);
524 status.appendChild(docroot.createTextNode(lookup->GetStatus()));
525 }
526 // Season and Episode
527 if (lookup->GetSeason() > 0 || lookup->GetEpisode() > 0)
528 {
529 QDomElement season = docroot.createElement("season");
530 item.appendChild(season);
531 season.appendChild(docroot.createTextNode(
532 QString::number(lookup->GetSeason())));
533
534 QDomElement episode = docroot.createElement("episode");
535 item.appendChild(episode);
536 episode.appendChild(docroot.createTextNode(
537 QString::number(lookup->GetEpisode())));
538 }
539 // Tagline
540 if (!lookup->GetTagline().isEmpty())
541 {
542 QDomElement tagline = docroot.createElement("tagline");
543 item.appendChild(tagline);
544 tagline.appendChild(docroot.createTextNode(lookup->GetTagline()));
545 }
546 // Plot
547 if (!lookup->GetDescription().isEmpty())
548 {
549 QDomElement desc = docroot.createElement("description");
550 item.appendChild(desc);
551 desc.appendChild(docroot.createTextNode(lookup->GetDescription()));
552 }
553 // Album Name
554 if (!lookup->GetAlbumTitle().isEmpty())
555 {
556 QDomElement albumname = docroot.createElement("albumname");
557 item.appendChild(albumname);
558 albumname.appendChild(docroot.createTextNode(lookup->GetAlbumTitle()));
559 }
560 // Inetref
561 if (!lookup->GetInetref().isEmpty())
562 {
563 QDomElement inetref = docroot.createElement("inetref");
564 item.appendChild(inetref);
565 inetref.appendChild(docroot.createTextNode(lookup->GetInetref()));
566 }
567 // Collectionref
568 if (!lookup->GetCollectionref().isEmpty())
569 {
570 QDomElement collectionref = docroot.createElement("collectionref");
571 item.appendChild(collectionref);
572 collectionref.appendChild(docroot.createTextNode(lookup->GetCollectionref()));
573 }
574 // TMSref/SeriesID
575 if (!lookup->GetTMSref().isEmpty())
576 {
577 QDomElement tmsref = docroot.createElement("tmsref");
578 item.appendChild(tmsref);
579 tmsref.appendChild(docroot.createTextNode(lookup->GetTMSref()));
580 }
581 // IMDB
582 if (!lookup->GetIMDB().isEmpty())
583 {
584 QDomElement imdb = docroot.createElement("imdb");
585 item.appendChild(imdb);
586 imdb.appendChild(docroot.createTextNode(lookup->GetIMDB()));
587 }
588 // Home Page
589 if (!lookup->GetHomepage().isEmpty())
590 {
591 QDomElement homepage = docroot.createElement("homepage");
592 item.appendChild(homepage);
593 homepage.appendChild(docroot.createTextNode(lookup->GetHomepage()));
594 }
595 // Trailer
596 if (!lookup->GetTrailerURL().isEmpty())
597 {
598 QDomElement trailer = docroot.createElement("trailer");
599 item.appendChild(trailer);
600 trailer.appendChild(docroot.createTextNode(lookup->GetTrailerURL()));
601 }
602 // Chan ID
603 if (lookup->GetChanId() > 0)
604 {
605 QDomElement chanid = docroot.createElement("chanid");
606 item.appendChild(chanid);
607 chanid.appendChild(docroot.createTextNode(
608 QString::number(lookup->GetChanId())));
609 }
610 // Channel Number
611 if (!lookup->GetChanNum().isEmpty())
612 {
613 QDomElement channum = docroot.createElement("channum");
614 item.appendChild(channum);
615 channum.appendChild(docroot.createTextNode(lookup->GetChanNum()));
616 }
617 // Callsign
618 if (!lookup->GetChanSign().isEmpty())
619 {
620 QDomElement callsign = docroot.createElement("chansign");
621 item.appendChild(callsign);
622 callsign.appendChild(docroot.createTextNode(lookup->GetChanSign()));
623 }
624 // Channel Name
625 if (!lookup->GetChanName().isEmpty())
626 {
627 QDomElement channame = docroot.createElement("channame");
628 item.appendChild(channame);
629 channame.appendChild(docroot.createTextNode(lookup->GetChanName()));
630 }
631 // Playback Filters
632 if (!lookup->GetChanPlaybackFilters().isEmpty())
633 {
634 QDomElement filters = docroot.createElement("filters");
635 item.appendChild(filters);
636 filters.appendChild(docroot.createTextNode(
637 lookup->GetChanPlaybackFilters()));
638 }
639 // Recording Group
640 if (!lookup->GetRecGroup().isEmpty())
641 {
642 QDomElement recgroup = docroot.createElement("recgroup");
643 item.appendChild(recgroup);
644 recgroup.appendChild(docroot.createTextNode(lookup->GetRecGroup()));
645 }
646 // Playback Group
647 if (!lookup->GetPlayGroup().isEmpty())
648 {
649 QDomElement playgroup = docroot.createElement("playgroup");
650 item.appendChild(playgroup);
651 playgroup.appendChild(docroot.createTextNode(lookup->GetPlayGroup()));
652 }
653 // SeriesID
654 if (!lookup->GetSeriesId().isEmpty())
655 {
656 QDomElement seriesid = docroot.createElement("seriesid");
657 item.appendChild(seriesid);
658 seriesid.appendChild(docroot.createTextNode(lookup->GetSeriesId()));
659 }
660 // ProgramID
661 if (!lookup->GetProgramId().isEmpty())
662 {
663 QDomElement programid = docroot.createElement("programid");
664 item.appendChild(programid);
665 programid.appendChild(docroot.createTextNode(lookup->GetProgramId()));
666 }
667 // Storage Group
668 if (!lookup->GetStorageGroup().isEmpty())
669 {
670 QDomElement sgroup = docroot.createElement("storagegroup");
671 item.appendChild(sgroup);
672 sgroup.appendChild(docroot.createTextNode(lookup->GetStorageGroup()));
673 }
674 // Start TS
675 if (!lookup->GetStartTS().isNull())
676 {
677 QDomElement startts = docroot.createElement("startts");
678 item.appendChild(startts);
679 startts.appendChild(docroot.createTextNode(
680 lookup->GetStartTS().toString(RFC822)));
681 }
682 // End TS
683 if (!lookup->GetEndTS().isNull())
684 {
685 QDomElement endts = docroot.createElement("endts");
686 item.appendChild(endts);
687 endts.appendChild(docroot.createTextNode(
688 lookup->GetEndTS().toString(RFC822)));
689 }
690 // Rec Start TS
691 if (!lookup->GetRecStartTS().isNull())
692 {
693 QDomElement recstartts = docroot.createElement("recstartts");
694 item.appendChild(recstartts);
695 recstartts.appendChild(docroot.createTextNode(
696 lookup->GetRecStartTS().toString(RFC822)));
697 }
698 // Rec End TS
699 if (!lookup->GetRecEndTS().isNull())
700 {
701 QDomElement recendts = docroot.createElement("recendts");
702 item.appendChild(recendts);
703 recendts.appendChild(docroot.createTextNode(
704 lookup->GetRecEndTS().toString(RFC822)));
705 }
706 // Program Flags
707 if (lookup->GetProgramFlags() > 0)
708 {
709 QDomElement progflags = docroot.createElement("programflags");
710 item.appendChild(progflags);
711 progflags.appendChild(docroot.createTextNode(
712 QString::number(lookup->GetProgramFlags())));
713 }
714 // Audio Properties
715 if (lookup->GetAudioProperties() > 0)
716 {
717 QDomElement audioprops = docroot.createElement("audioproperties");
718 item.appendChild(audioprops);
719 audioprops.appendChild(docroot.createTextNode(
720 QString::number(lookup->GetAudioProperties())));
721 }
722 // Video Properties
723 if (lookup->GetVideoProperties() > 0)
724 {
725 QDomElement videoprops = docroot.createElement("videoproperties");
726 item.appendChild(videoprops);
727 videoprops.appendChild(docroot.createTextNode(
728 QString::number(lookup->GetVideoProperties())));
729 }
730 // Subtitle Type
731 if (lookup->GetSubtitleType() > 0)
732 {
733 QDomElement subprops = docroot.createElement("subtitletype");
734 item.appendChild(subprops);
735 subprops.appendChild(docroot.createTextNode(
736 QString::number(lookup->GetSubtitleType())));
737 }
738 // Release Date
739 if (!lookup->GetReleaseDate().isNull())
740 {
741 QDomElement releasedate = docroot.createElement("releasedate");
742 item.appendChild(releasedate);
743 releasedate.appendChild(docroot.createTextNode(
744 lookup->GetReleaseDate().toString("yyyy-MM-dd")));
745 }
746 // Last Updated
747 if (!lookup->GetLastUpdated().isNull())
748 {
749 QDomElement lastupdated = docroot.createElement("lastupdated");
750 item.appendChild(lastupdated);
751 lastupdated.appendChild(docroot.createTextNode(
752 lookup->GetLastUpdated().toString(RFC822)));
753 }
754 // User Rating
755 if (lookup->GetUserRating() > 0)
756 {
757 QDomElement userrating = docroot.createElement("userrating");
758 item.appendChild(userrating);
759 userrating.appendChild(docroot.createTextNode(QString::number(
760 lookup->GetUserRating())));
761 }
762 // Rating Count
763 if (lookup->GetRatingCount() > 0)
764 {
765 QDomElement ratingcount = docroot.createElement("ratingcount");
766 item.appendChild(ratingcount);
767 ratingcount.appendChild(docroot.createTextNode(QString::number(
768 lookup->GetRatingCount())));
769 }
770 // Track Number
771 if (lookup->GetTrackNumber() > 0)
772 {
773 QDomElement tracknum = docroot.createElement("tracknum");
774 item.appendChild(tracknum);
775 tracknum.appendChild(docroot.createTextNode(QString::number(
776 lookup->GetTrackNumber())));
777 }
778 // Popularity
779 if (lookup->GetPopularity() > 0)
780 {
781 QDomElement popularity = docroot.createElement("popularity");
782 item.appendChild(popularity);
783 popularity.appendChild(docroot.createTextNode(QString::number(
784 lookup->GetPopularity())));
785 }
786 // Budget
787 if (lookup->GetBudget() > 0)
788 {
789 QDomElement budget = docroot.createElement("budget");
790 item.appendChild(budget);
791 budget.appendChild(docroot.createTextNode(QString::number(
792 lookup->GetBudget())));
793 }
794 // Revenue
795 if (lookup->GetRevenue() > 0)
796 {
797 QDomElement revenue = docroot.createElement("revenue");
798 item.appendChild(revenue);
799 revenue.appendChild(docroot.createTextNode(QString::number(
800 lookup->GetRevenue())));
801 }
802 // Runtime
803 auto minutes = lookup->GetRuntime();
804 if (minutes > 0min)
805 {
806 QDomElement runtime = docroot.createElement("runtime");
807 item.appendChild(runtime);
808 runtime.appendChild(docroot.createTextNode(QString::number(
809 minutes.count())));
810 }
811 // Runtimesecs
812 auto seconds = lookup->GetRuntimeSeconds();
813 if (seconds > 0s)
814 {
815 QDomElement runtimesecs = docroot.createElement("runtimesecs");
816 item.appendChild(runtimesecs);
817 runtimesecs.appendChild(docroot.createTextNode(QString::number(
818 seconds.count())));
819 }
820
821 if (!lookup->GetCertification().isEmpty())
822 AddCertifications(lookup, item, docroot);
823 if (!lookup->GetCategories().empty())
824 AddCategories(lookup, item, docroot);
825 if (!lookup->GetStudios().empty())
826 AddStudios(lookup, item, docroot);
827 if (!lookup->GetCountries().empty())
828 AddCountries(lookup, item, docroot);
829}
830
832 QDomElement placetoadd,
833 QDomDocument docroot)
834{
835 QString certstr = lookup->GetCertification();
836 QDomElement certifications = docroot.createElement("certifications");
837 placetoadd.appendChild(certifications);
838
839 QDomElement cert = docroot.createElement("certification");
840 certifications.appendChild(cert);
841 cert.setAttribute("locale", gCoreContext->GetLocale()->GetCountryCode());
842 cert.setAttribute("name", certstr);
843}
844
846 QDomElement placetoadd,
847 QDomDocument docroot)
848{
849 QStringList cats = lookup->GetCategories();
850 QDomElement categories = docroot.createElement("categories");
851 placetoadd.appendChild(categories);
852
853 for (const auto & str : std::as_const(cats))
854 {
855 QDomElement cat = docroot.createElement("category");
856 categories.appendChild(cat);
857 cat.setAttribute("type", "genre");
858 cat.setAttribute("name", str);
859 }
860}
861
863 QDomElement placetoadd,
864 QDomDocument docroot)
865{
866 QStringList studs = lookup->GetStudios();
867 QDomElement studios = docroot.createElement("studios");
868 placetoadd.appendChild(studios);
869
870 for (const auto & str : std::as_const(studs))
871 {
872 QDomElement stud = docroot.createElement("studio");
873 studios.appendChild(stud);
874 stud.setAttribute("name", str);
875 }
876}
877
879 QDomElement placetoadd,
880 QDomDocument docroot)
881{
882 QStringList counts = lookup->GetCountries();
883 QDomElement countries = docroot.createElement("countries");
884 placetoadd.appendChild(countries);
885
886 for (const auto & str : std::as_const(counts))
887 {
888 QDomElement count = docroot.createElement("country");
889 countries.appendChild(count);
890 count.setAttribute("name", str);
891 }
892}
893
894MetadataLookup* ParseMetadataItem(const QDomElement& item,
895 MetadataLookup *lookup,
896 bool passseas)
897{
898 if (!lookup)
899 return new MetadataLookup();
900
901 uint season = 0;
902 uint episode = 0;
903 uint chanid = 0;
904 uint programflags = 0;
905 uint audioproperties = 0;
906 uint videoproperties = 0;
907 uint subtitletype = 0;
908 uint tracknum = 0;
909 uint budget = 0;
910 uint revenue = 0;
911 uint year = 0;
912 uint ratingcount = 0;
913 QString title;
914 QString network;
915 QString status;
916 QString subtitle;
917 QString tagline;
918 QString description;
919 QString certification;
920 QString channum;
921 QString chansign;
922 QString channame;
923 QString chanplaybackfilters;
924 QString recgroup;
925 QString playgroup;
926 QString seriesid;
927 QString programid;
928 QString storagegroup;
929 QString album;
930 QString system;
931 QString inetref;
932 QString collectionref;
933 QString tmsref;
934 QString imdb;
935 QString homepage;
936 QString trailerURL;
937 QString language;
938 QStringList categories;
939 QStringList countries;
940 QStringList studios;
941 float userrating = 0;
942 float popularity = 0;
943 QDate releasedate;
944 QDateTime lastupdated;
945 QDateTime startts;
946 QDateTime endts;
947 QDateTime recstartts;
948 QDateTime recendts;
949 PeopleMap people;
950 ArtworkMap artwork;
951
952 // Get the easy parses
953 language = item.firstChildElement("language").text();
954 title = Parse::UnescapeHTML(item.firstChildElement("title").text());
955 network = Parse::UnescapeHTML(item.firstChildElement("network").text());
956 status = Parse::UnescapeHTML(item.firstChildElement("status").text());
957 subtitle = Parse::UnescapeHTML(item.firstChildElement("subtitle").text());
958 tagline = Parse::UnescapeHTML(item.firstChildElement("tagline").text());
959 description = Parse::UnescapeHTML(item.firstChildElement("description").text());
960 album = Parse::UnescapeHTML(item.firstChildElement("albumname").text());
961 inetref = item.firstChildElement("inetref").text();
962 collectionref = item.firstChildElement("collectionref").text();
963 tmsref = item.firstChildElement("tmsref").text();
964 imdb = item.firstChildElement("imdb").text();
965 homepage = item.firstChildElement("homepage").text();
966 trailerURL = item.firstChildElement("trailer").text();
967
968 // ProgramInfo specific parsing
969 chanid = item.firstChildElement("chanid").text().toUInt();
970 channum = item.firstChildElement("channum").text();
971 chansign = item.firstChildElement("chansign").text();
972 channame = item.firstChildElement("channame").text();
973 chanplaybackfilters = item.firstChildElement("chanplaybackfilters").text();
974 recgroup = item.firstChildElement("recgroup").text();
975 playgroup = item.firstChildElement("playgroup").text();
976 seriesid = item.firstChildElement("seriesid").text();
977 programid = item.firstChildElement("programid").text();
978 storagegroup = item.firstChildElement("storagegroup").text();
979 startts = RFC822TimeToQDateTime(item.
980 firstChildElement("startts").text());
981 endts = RFC822TimeToQDateTime(item.
982 firstChildElement("endts").text());
983 recstartts = RFC822TimeToQDateTime(item.
984 firstChildElement("recstartts").text());
985 recendts = RFC822TimeToQDateTime(item.
986 firstChildElement("recendts").text());
987 programflags = item.firstChildElement("programflags").text().toUInt();
988 audioproperties = item.firstChildElement("audioproperties").text().toUInt();
989 videoproperties = item.firstChildElement("videoproperties").text().toUInt();
990 subtitletype = item.firstChildElement("subtitletype").text().toUInt();
991
992 QString tmpDate = item.firstChildElement("releasedate").text();
993 if (!tmpDate.isEmpty())
994 releasedate = QDate::fromString(tmpDate, "yyyy-MM-dd");
995 lastupdated = RFC822TimeToQDateTime(item.
996 firstChildElement("lastupdated").text());
997
998 userrating = item.firstChildElement("userrating").text().toFloat();
999 ratingcount = item.firstChildElement("ratingcount").text().toUInt();
1000 tracknum = item.firstChildElement("tracknum").text().toUInt();
1001 popularity = item.firstChildElement("popularity").text().toFloat();
1002 budget = item.firstChildElement("budget").text().toUInt();
1003 revenue = item.firstChildElement("revenue").text().toUInt();
1004 year = item.firstChildElement("year").text().toUInt();
1005 if (!year && !releasedate.isNull())
1006 year = releasedate.toString("yyyy").toUInt();
1007 auto runtime = std::chrono::minutes(item.firstChildElement("runtime").text().toUInt());
1008 auto runtimesecs = std::chrono::seconds(item.firstChildElement("runtimesecs").text().toUInt());
1009
1010 QDomElement systems = item.firstChildElement("systems");
1011 if (!systems.isNull())
1012 {
1013 QDomElement firstSystem = systems.firstChildElement("system");
1014 if (!firstSystem.isNull())
1015 system = firstSystem.text();
1016 }
1017
1018 // TODO: Once TMDB supports certification per-locale, come back and match
1019 // locale of myth to certification locale.
1020 QDomElement certifications = item.firstChildElement("certifications");
1021 QVector< QPair<QString,QString> > ratinglist;
1022 if (!certifications.isNull())
1023 {
1024 QDomElement cert = certifications.firstChildElement("certification");
1025 if (!cert.isNull())
1026 {
1027 while (!cert.isNull())
1028 {
1029 if (cert.hasAttribute("locale") && cert.hasAttribute("name"))
1030 {
1031 QPair<QString,QString> newcert(cert.attribute("locale"),
1032 cert.attribute("name"));
1033 ratinglist.append(newcert);
1034 }
1035 cert = cert.nextSiblingElement("certification");
1036 }
1037 }
1038 }
1039 // HACK: To go away when someone supports ratings by locale.
1040 if (!ratinglist.isEmpty())
1041 certification = ratinglist.takeFirst().second;
1042
1043 // Categories
1044 QDomElement categoriesxml = item.firstChildElement("categories");
1045 if (!categoriesxml.isNull())
1046 {
1047 QDomElement cat = categoriesxml.firstChildElement("category");
1048 if (!cat.isNull())
1049 {
1050 while (!cat.isNull())
1051 {
1052 if (cat.hasAttribute("name"))
1053 categories.append(cat.attribute("name"));
1054 cat = cat.nextSiblingElement("category");
1055 }
1056 }
1057 }
1058
1059 // Countries
1060 QDomElement countriesxml = item.firstChildElement("countries");
1061 if (!countriesxml.isNull())
1062 {
1063 QDomElement cntry = countriesxml.firstChildElement("country");
1064 if (!cntry.isNull())
1065 {
1066 while (!cntry.isNull())
1067 {
1068 if (cntry.hasAttribute("name"))
1069 countries.append(cntry.attribute("name"));
1070 cntry = cntry.nextSiblingElement("country");
1071 }
1072 }
1073 }
1074
1075 // Studios
1076 QDomElement studiosxml = item.firstChildElement("studios");
1077 if (!studiosxml.isNull())
1078 {
1079 QDomElement studio = studiosxml.firstChildElement("studio");
1080 if (!studio.isNull())
1081 {
1082 while (!studio.isNull())
1083 {
1084 if (studio.hasAttribute("name"))
1085 studios.append(studio.attribute("name"));
1086 studio = studio.nextSiblingElement("studio");
1087 }
1088 }
1089 }
1090
1091 // People
1092 QDomElement peoplexml = item.firstChildElement("people");
1093 if (!peoplexml.isNull())
1094 {
1095 people = ParsePeople(peoplexml);
1096 }
1097
1098 // Artwork
1099 QDomElement artworkxml = item.firstChildElement("images");
1100 if (!artworkxml.isNull())
1101 {
1102 artwork = ParseArtwork(artworkxml);
1103 }
1104
1105 // Have to handle season and episode a little differently.
1106 // If the query object comes in with a season or episode number,
1107 // we want to pass that through. However, if we are doing a title/subtitle
1108 // lookup, we need to parse for season and episode.
1109 if (passseas)
1110 {
1111 season = lookup->GetSeason();
1112 episode = lookup->GetEpisode();
1113 }
1114
1115 if (lookup->GetPreferDVDOrdering())
1116 {
1117 if (!season)
1118 {
1119 season = item.firstChildElement("dvdseason").text().toUInt();
1120 }
1121 if (!episode)
1122 {
1123 episode = item.firstChildElement("dvdepisode").text().toUInt();
1124 }
1125 }
1126
1127 if (!season)
1128 {
1129 season = item.firstChildElement("season").text().toUInt();
1130 }
1131 if (!episode)
1132 {
1133 episode = item.firstChildElement("episode").text().toUInt();
1134 }
1135 LOG(VB_GENERAL, LOG_INFO, QString("Result Found, Season %1 Episode %2")
1136 .arg(season).arg(episode));
1137
1138 return new MetadataLookup(lookup->GetType(), lookup->GetSubtype(),
1139 lookup->GetData(), lookup->GetStep(), lookup->GetAutomatic(),
1140 lookup->GetHandleImages(), lookup->GetAllowOverwrites(),
1141 lookup->GetAllowGeneric(), lookup->GetPreferDVDOrdering(), lookup->GetHost(),
1142 lookup->GetFilename(), title, network, status, categories, userrating,
1143 ratingcount, language, subtitle, tagline, description, season,
1144 episode, chanid, channum, chansign, channame,
1145 chanplaybackfilters, recgroup, playgroup, seriesid, programid,
1146 storagegroup, startts, endts, recstartts, recendts, programflags,
1147 audioproperties, videoproperties, subtitletype, certification,
1148 countries, popularity, budget, revenue, album, tracknum, system, year,
1149 releasedate, lastupdated, runtime, runtimesecs, inetref, collectionref,
1150 tmsref, imdb, people, studios, homepage, trailerURL, artwork, DownloadMap());
1151}
1152
1153MetadataLookup* ParseMetadataMovieNFO(const QDomElement& item,
1154 MetadataLookup *lookup)
1155{
1156 if (!lookup)
1157 return new MetadataLookup();
1158
1159 uint year = 0;
1160 uint season = 0;
1161 uint episode = 0;
1162 QString title;
1163 QString subtitle;
1164 QString tagline;
1165 QString description;
1166 QString inetref;
1167 QString trailer;
1168 QString certification;
1169 float userrating = 0;
1170 QDate releasedate;
1171 QStringList categories;
1172 PeopleMap people;
1173 ArtworkMap artwork;
1174
1175 // Get the easy parses
1176 if (item.tagName() == "movie")
1177 title = Parse::UnescapeHTML(item.firstChildElement("title").text());
1178 else if (item.tagName() == "episodedetails")
1179 subtitle = Parse::UnescapeHTML(item.firstChildElement("title").text());
1180 userrating = item.firstChildElement("rating").text().toFloat();
1181 year = item.firstChildElement("year").text().toUInt();
1182 season = item.firstChildElement("season").text().toUInt();
1183 episode = item.firstChildElement("episode").text().toUInt();
1184 description = Parse::UnescapeHTML(item.firstChildElement("plot").text());
1185 tagline = Parse::UnescapeHTML(item.firstChildElement("tagline").text());
1186 inetref = item.firstChildElement("id").text();
1187 trailer = item.firstChildElement("trailer").text();
1188 certification = item.firstChildElement("mpaa").text();
1189 categories.append(item.firstChildElement("genre").text());
1190
1191 QString tmpDate = item.firstChildElement("releasedate").text();
1192 if (!tmpDate.isEmpty())
1193 releasedate = QDate::fromString(tmpDate, "yyyy-MM-dd");
1194 else if (year > 0)
1195 releasedate = QDate::fromString(QString::number(year), "yyyy");
1196
1197 static const QRegularExpression kAlphaRE { "[A-Za-z]" };
1198 auto runtime =
1199 std::chrono::minutes(item.firstChildElement("runtime").text()
1200 .remove(kAlphaRE)
1201 .trimmed().toUInt());
1202
1203 QDomElement actor = item.firstChildElement("actor");
1204 if (!actor.isNull())
1205 {
1206 while (!actor.isNull())
1207 {
1209 info.name = actor.firstChildElement("name").text();
1210 info.role = actor.firstChildElement("role").text();
1211 info.thumbnail = actor.firstChildElement("thumb").text();
1212 people.insert(kPersonActor, info);
1213 actor = actor.nextSiblingElement("actor");
1214 }
1215 }
1216
1217 QString director = item.firstChildElement("director").text();
1218 if (!director.isEmpty())
1219 {
1221 info.name = director;
1222 people.insert(kPersonDirector, info);
1223 }
1224
1225 return new MetadataLookup(lookup->GetType(), lookup->GetSubtype(),
1226 lookup->GetData(), lookup->GetStep(),
1227 lookup->GetAutomatic(), lookup->GetHandleImages(),
1228 lookup->GetAllowOverwrites(), lookup->GetAllowGeneric(),
1229 lookup->GetPreferDVDOrdering(), lookup->GetHost(),
1230 lookup->GetFilename(), title, categories,
1231 userrating, subtitle, tagline, description, season, episode,
1232 certification, year, releasedate, runtime, runtime,
1233 inetref, people, trailer, artwork, DownloadMap());
1234}
1235
1236PeopleMap ParsePeople(const QDomElement& people)
1237{
1238 PeopleMap ret;
1239
1240 QDomElement person = people.firstChildElement("person");
1241 if (!person.isNull())
1242 {
1243 while (!person.isNull())
1244 {
1245 if (person.hasAttribute("job"))
1246 {
1247 QString jobstring = person.attribute("job");
1249 if (jobstring.toLower() == "actor")
1250 type = kPersonActor; // NOLINT(bugprone-branch-clone)
1251 else if (jobstring.toLower() == "author")
1253 else if (jobstring.toLower() == "producer")
1255 else if (jobstring.toLower() == "executive producer")
1257 else if (jobstring.toLower() == "director")
1259 else if (jobstring.toLower() == "cinematographer")
1261 else if (jobstring.toLower() == "composer")
1263 else if (jobstring.toLower() == "editor")
1265 else if (jobstring.toLower() == "casting")
1267 else if (jobstring.toLower() == "artist")
1269 else if (jobstring.toLower() == "album artist")
1271 else if (jobstring.toLower() == "guest star")
1273 else
1275
1277 if (person.hasAttribute("name"))
1278 info.name = person.attribute("name");
1279 if (person.hasAttribute("character"))
1280 info.role = person.attribute("character");
1281 if (person.hasAttribute("thumb"))
1282 info.thumbnail = person.attribute("thumb");
1283 if (person.hasAttribute("url"))
1284 info.url = person.attribute("url");
1285
1286 ret.insert(type,info);
1287 }
1288 person = person.nextSiblingElement("person");
1289 }
1290 }
1291 return ret;
1292}
1293
1294ArtworkMap ParseArtwork(const QDomElement& artwork)
1295{
1296 ArtworkMap ret;
1297
1298 QDomElement image = artwork.firstChildElement("image");
1299 if (!image.isNull())
1300 {
1301 while (!image.isNull())
1302 {
1303 if (image.hasAttribute("type"))
1304 {
1305 QString typestring = image.attribute("type");
1307 if (typestring.toLower() == "coverart")
1308 type = kArtworkCoverart; // NOLINT(bugprone-branch-clone)
1309 else if (typestring.toLower() == "fanart")
1311 else if (typestring.toLower() == "banner")
1313 else if (typestring.toLower() == "screenshot")
1315 else if (typestring.toLower() == "poster")
1317 else if (typestring.toLower() == "back cover")
1319 else if (typestring.toLower() == "inside cover")
1321 else if (typestring.toLower() == "cd image")
1323 else
1325
1327 if (image.hasAttribute("thumb"))
1328 info.thumbnail = image.attribute("thumb");
1329 if (image.hasAttribute("url"))
1330 info.url = image.attribute("url");
1331 if (image.hasAttribute("width"))
1332 info.width = image.attribute("width").toUInt();
1333 else
1334 info.width = 0;
1335 if (image.hasAttribute("height"))
1336 info.height = image.attribute("height").toUInt();
1337 else
1338 info.height = 0;
1339
1340 ret.insert(type,info);
1341 }
1342 image = image.nextSiblingElement("image");
1343 }
1344 }
1345 return ret;
1346}
1347
1348int editDistance( const QString& s, const QString& t )
1349{
1350// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1351#define D( i, j ) d[((i) * n) + (j)]
1352 size_t m = s.length() + 1;
1353 size_t n = t.length() + 1;
1354 int *d = new int[m * n];
1355
1356 for ( size_t i = 0; i < m; i++ )
1357 D( i, 0 ) = i;
1358 for ( size_t j = 0; j < n; j++ )
1359 D( 0, j ) = j;
1360 for ( size_t i = 1; i < m; i++ )
1361 {
1362 for ( size_t j = 1; j < n; j++ )
1363 {
1364 if (
1365#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1366 s[static_cast<int>(i) - 1] == t[static_cast<int>(j) - 1]
1367#else
1368 s[i - 1] == t[j - 1]
1369#endif
1370 )
1371 D( i, j ) = D( i - 1, j - 1 );
1372 else
1373 {
1374 int x = D( i - 1, j );
1375 int y = D( i - 1, j - 1 );
1376 int z = D( i, j - 1 );
1377 D( i, j ) = 1 + std::min({x, y, z});
1378 }
1379 }
1380 }
1381 int result = D( m - 1, n - 1 );
1382 delete[] d;
1383 return result;
1384#undef D
1385}
1386
1387QString nearestName(const QString& actual, const QStringList& candidates)
1388{
1389 int deltaBest = 10000;
1390 int numBest = 0;
1391 int tolerance = gCoreContext->GetNumSetting("MetadataLookupTolerance", 5);
1392 QString best;
1393
1394 QStringList::ConstIterator i = candidates.begin();
1395 while ( i != candidates.end() )
1396 {
1397 QString candidate = *i;
1398 int delta = editDistance( actual.toLower(), candidate.toLower() );
1399 if ( delta < deltaBest )
1400 {
1401 deltaBest = delta;
1402 numBest = 1;
1403 best = *i;
1404 }
1405 else if ( delta == deltaBest )
1406 {
1407 numBest++;
1408 }
1409 ++i;
1410 }
1411
1412 if ( numBest == 1 && deltaBest <= tolerance &&
1413 actual.length() + best.length() >= 5 )
1414 return best;
1415 return {};
1416}
1417
1418QDateTime RFC822TimeToQDateTime(const QString& t)
1419{
1420 QMap<QString, int> TimezoneOffsets;
1421
1422 if (t.size() < 20)
1423 return {};
1424
1425 QString time = t.simplified();
1426 short int hoursShift = 0;
1427 short int minutesShift = 0;
1428
1429 QStringList tmp = time.split(' ');
1430 if (tmp.isEmpty())
1431 return {};
1432 static const QRegularExpression kNonDigitRE { "\\D" };
1433 if (tmp.at(0).contains(kNonDigitRE))
1434 tmp.removeFirst();
1435 if (tmp.size() != 5)
1436 return {};
1437 QString ltimezone = tmp.takeAt(tmp.size() -1);
1438 if (ltimezone.size() == 5)
1439 {
1440 bool ok = false;
1441 int tz = ltimezone.toInt(&ok);
1442 if(ok)
1443 {
1444 hoursShift = tz / 100;
1445 minutesShift = tz % 100;
1446 }
1447 }
1448 else
1449 {
1450 hoursShift = TimezoneOffsets.value(ltimezone, 0);
1451 }
1452
1453 if (tmp.at(0).size() == 1)
1454 tmp[0].prepend("0");
1455 tmp [1].truncate(3);
1456
1457 time = tmp.join(" ");
1458
1459 QDateTime result;
1460 if (tmp.at(2).size() == 4)
1461 result = QLocale::c().toDateTime(time, "dd MMM yyyy hh:mm:ss");
1462 else
1463 result = QLocale::c().toDateTime(time, "dd MMM yy hh:mm:ss");
1464 if (result.isNull() || !result.isValid())
1465 return {};
1466 result = result.addSecs((hoursShift * 3600 * (-1)) + (minutesShift *60 * (-1)));
1467#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
1468 result.setTimeSpec(Qt::UTC);
1469#else
1470 result.setTimeZone(QTimeZone(QTimeZone::UTC));
1471#endif
1472 return result;
1473}
const QString m_description
QString GetStorageGroup() const
uint GetSeason() const
QString GetNetwork() const
const QString m_language
const QString m_recGroup
QString GetHost() const
MetadataType GetType() const
const QString m_storageGroup
QString GetDescription() const
QString GetSeriesId() const
const QString m_playGroup
QString GetCollectionref() const
const QString m_tagline
QDateTime GetRecEndTS() const
bool GetAllowOverwrites() const
float GetUserRating() const
uint GetRevenue() const
QStringList GetStudios() const
QDateTime GetStartTS() const
const QString m_seriesId
uint GetTrackNumber() const
QString GetChanSign() const
QString GetTagline() const
QVariant GetData() const
ArtworkList GetArtwork(VideoArtworkType type) const
QString m_collectionRef
const QString m_homepage
bool GetPreferDVDOrdering() const
QString GetSubtitle() const
bool GetAutomatic() const
QString GetPlayGroup() const
QString GetTrailerURL() const
uint GetRatingCount() const
QString GetCertification() const
QDate GetReleaseDate() const
QString GetProgramId() const
QString GetFilename() const
QString GetAlbumTitle() const
const QDateTime m_recEndTs
QDateTime GetLastUpdated() const
const QDateTime m_lastUpdated
QString GetTitle() const
const QStringList m_categories
const QString m_chanNum
QStringList GetCountries() const
QDateTime GetEndTS() const
const QString m_programid
QString GetTMSref() const
LookupStep GetStep() const
float GetPopularity() const
LookupType GetSubtype() const
const QString m_chanName
const QStringList m_countries
void toMap(InfoMap &map)
const QDateTime m_startTs
QDateTime GetRecStartTS() const
const QString m_trailerURL
QString GetRecGroup() const
const QDateTime m_endTs
const QString m_chanPlaybackFilters
uint GetBudget() const
const QStringList m_studios
const PeopleMap m_people
QString GetIMDB() const
QString GetChanName() const
uint GetSubtitleType() const
std::chrono::seconds GetRuntimeSeconds() const
QString GetChanPlaybackFilters() const
const QString m_chanSign
QString GetInetref() const
QString GetChanNum() const
std::chrono::seconds m_runtimeSecs
QString GetHomepage() const
uint GetVideoProperties() const
std::chrono::minutes m_runtime
QStringList GetCategories() const
const QString m_certification
bool GetHandleImages() const
const QDateTime m_recStartTs
QString GetStatus() const
QString GetLanguage() const
QList< PersonInfo > GetPeople(PeopleType type) const
const ArtworkMap m_artwork
uint GetAudioProperties() const
const QDate m_releaseDate
bool GetAllowGeneric() const
std::chrono::minutes GetRuntime() const
uint GetEpisode() const
uint GetProgramFlags() const
uint GetChanId() const
int GetNumSetting(const QString &key, int defaultval=0)
MythLocale * GetLocale(void) const
QString GetCountryCode() const
Definition: mythlocale.cpp:59
static QString UnescapeHTML(const QString &escaped)
Definition: rssparse.cpp:1143
Holds information on recordings and videos.
Definition: programinfo.h:68
float GetStars(void) const
Definition: programinfo.h:446
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
QString GetBasename(void) const
Definition: programinfo.h:345
QString GetSeriesID(void) const
Definition: programinfo.h:439
uint GetVideoProperties(void) const
Definition: programinfo.h:500
uint GetEpisode(void) const
Definition: programinfo.h:368
uint GetSubtitleType(void) const
Definition: programinfo.h:498
QString GetProgramID(void) const
Definition: programinfo.h:440
QString GetRecordingGroup(void) const
Definition: programinfo.h:420
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:398
QString GetChannelPlaybackFilters(void) const
Definition: programinfo.h:388
QString GetInetRef(void) const
Definition: programinfo.h:441
uint GetAudioProperties(void) const
Definition: programinfo.h:502
QString GetHostname(void) const
Definition: programinfo.h:422
QString GetPlaybackGroup(void) const
Definition: programinfo.h:421
QString GetDescription(void) const
Definition: programinfo.h:366
QDateTime GetLastModifiedTime(void) const
Definition: programinfo.h:433
QString GetStorageGroup(void) const
Definition: programinfo.h:423
QString GetChannelName(void) const
This is the channel name in the local market, i.e.
Definition: programinfo.h:387
QString GetTitle(void) const
Definition: programinfo.h:362
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:405
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:377
uint GetYearOfInitialRelease(void) const
Definition: programinfo.h:424
QDate GetOriginalAirDate(void) const
Definition: programinfo.h:432
uint32_t GetProgramFlags(void) const
Definition: programinfo.h:474
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:413
QString GetSubtitle(void) const
Definition: programinfo.h:364
QString GetCategory(void) const
Definition: programinfo.h:370
uint GetSeason(void) const
Definition: programinfo.h:367
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
Definition: programinfo.h:384
General purpose reference counter.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
unsigned int uint
Definition: freesurround.h:24
static guint32 * tmp
Definition: goom_core.cpp:26
static const iso6937table * d
MetadataLookup * LookupFromProgramInfo(ProgramInfo *pginfo)
PeopleMap ParsePeople(const QDomElement &people)
void AddCategories(MetadataLookup *lookup, QDomElement placetoadd, QDomDocument docroot)
void AddCertifications(MetadataLookup *lookup, QDomElement placetoadd, QDomDocument docroot)
void CreateMetadataXMLItem(MetadataLookup *lookup, QDomElement placetoadd, QDomDocument docroot)
static int x0
MetadataLookup * ParseMetadataItem(const QDomElement &item, MetadataLookup *lookup, bool passseas)
MetadataLookup * ParseMetadataMovieNFO(const QDomElement &item, MetadataLookup *lookup)
QDomDocument CreateMetadataXML(MetadataLookupList list)
#define D(i, j)
void AddCountries(MetadataLookup *lookup, QDomElement placetoadd, QDomDocument docroot)
ArtworkMap ParseArtwork(const QDomElement &artwork)
int editDistance(const QString &s, const QString &t)
void AddStudios(MetadataLookup *lookup, QDomElement placetoadd, QDomDocument docroot)
QDateTime RFC822TimeToQDateTime(const QString &t)
QString nearestName(const QString &actual, const QStringList &candidates)
LookupStep
@ kLookupData
LookupType
@ kUnknownVideo
QMultiMap< PeopleType, PersonInfo > PeopleMap
MetadataType
@ kMetadataRecording
PeopleType
@ kPersonArtist
@ kPersonCastingDirector
@ kPersonEditor
@ kPersonCinematographer
@ kPersonActor
@ kPersonAuthor
@ kPersonExecProducer
@ kPersonComposer
@ kPersonAlbumArtist
@ kPersonDirector
@ kPersonGuestStar
@ kPersonProducer
QMap< VideoArtworkType, ArtworkInfo > DownloadMap
QList< ArtworkInfo > ArtworkList
QMultiMap< VideoArtworkType, ArtworkInfo > ArtworkMap
VideoArtworkType
@ kArtworkBackCover
@ kArtworkScreenshot
@ kArtworkInsideCover
@ kArtworkCDImage
@ kArtworkFanart
@ kArtworkPoster
@ kArtworkBanner
@ kArtworkCoverart
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kDateFull
Default local time.
Definition: mythdate.h:19
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
dictionary info
Definition: azlyrics.py:7
STL namespace.