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