MythTV  master
metadatadownload.cpp
Go to the documentation of this file.
1 // C/C++
2 #include <cstdlib>
3 
4 // qt
5 #include <QCoreApplication>
6 #include <QEvent>
7 #include <QDir>
8 #include <QUrl>
9 #include <QRegularExpression>
10 
11 // myth
13 #include "libmythbase/mythdirs.h"
17 #include "libmythbase/remotefile.h"
19 #include "libmythui/mythuihelper.h"
20 
21 #include "metadatadownload.h"
22 #include "metadatafactory.h"
23 
24 const QEvent::Type MetadataLookupEvent::kEventType =
25  (QEvent::Type) QEvent::registerEventType();
26 
27 const QEvent::Type MetadataLookupFailure::kEventType =
28  (QEvent::Type) QEvent::registerEventType();
29 
31 {
32  cancel();
33  wait();
34 }
35 
41 {
42  // Add a lookup to the queue
43  QMutexLocker lock(&m_mutex);
44 
45  m_lookupList.append(lookup);
46  lookup->DecrRef();
47  if (!isRunning())
48  start();
49 }
50 
56 {
57  // Add a lookup to the queue
58  QMutexLocker lock(&m_mutex);
59 
60  m_lookupList.prepend(lookup);
61  lookup->DecrRef();
62  if (!isRunning())
63  start();
64 }
65 
67 {
68  QMutexLocker lock(&m_mutex);
69 
70  m_lookupList.clear();
71  m_parent = nullptr;
72 }
73 
75 {
76  RunProlog();
77 
78  while (true)
79  {
80  m_mutex.lock();
81  if (m_lookupList.isEmpty())
82  {
83  // no more to process, we're done
84  m_mutex.unlock();
85  break;
86  }
87  // Ref owns the MetadataLookup object for the duration of the loop
88  // and it will be deleted automatically when the loop completes
90  m_mutex.unlock();
91  MetadataLookup *lookup = ref;
92  MetadataLookupList list;
93 
94  // Go go gadget Metadata Lookup
95  if (lookup->GetType() == kMetadataVideo ||
96  lookup->GetType() == kMetadataRecording)
97  {
98  // First, look for mxml and nfo files in video storage groups
99  if (lookup->GetType() == kMetadataVideo &&
100  !lookup->GetFilename().isEmpty())
101  {
102  QString mxml = getMXMLPath(lookup->GetFilename());
103  QString nfo = getNFOPath(lookup->GetFilename());
104 
105  if (!mxml.isEmpty())
106  list = readMXML(mxml, lookup);
107  else if (!nfo.isEmpty())
108  list = readNFO(nfo, lookup);
109  }
110 
111  // If nothing found, create lookups based on filename
112  if (list.isEmpty())
113  {
114  if (lookup->GetSubtype() == kProbableTelevision)
115  {
116  list = handleTelevision(lookup);
117  if ((findExactMatchCount(list, lookup->GetBaseTitle(), true) == 0) ||
118  (list.size() > 1 && !lookup->GetAutomatic()))
119  {
120  // There are no exact match prospects with artwork from TV search,
121  // so add in movies, where we might find a better match.
122  // In case of manual mode and ambiguous result, add it as well.
123  list.append(handleMovie(lookup));
124  }
125  }
126  else if (lookup->GetSubtype() == kProbableMovie)
127  {
128  list = handleMovie(lookup);
129  if ((findExactMatchCount(list, lookup->GetBaseTitle(), true) == 0) ||
130  (list.size() > 1 && !lookup->GetAutomatic()))
131  {
132  // There are no exact match prospects with artwork from Movie search
133  // so add in television, where we might find a better match.
134  // In case of manual mode and ambiguous result, add it as well.
135  list.append(handleTelevision(lookup));
136  }
137  }
138  else
139  {
140  // will try both movie and TV
141  list = handleVideoUndetermined(lookup);
142  }
143  }
144  }
145  else if (lookup->GetType() == kMetadataGame)
146  {
147  list = handleGame(lookup);
148  }
149 
150  // inform parent we have lookup ready for it
151  if (m_parent && !list.isEmpty())
152  {
153  // If there's only one result, don't bother asking
154  // our parent about it, just add it to the back of
155  // the queue in kLookupData mode.
156  if (list.count() == 1 && list[0]->GetStep() == kLookupSearch)
157  {
158  MetadataLookup *newlookup = list.takeFirst();
159 
160  newlookup->SetStep(kLookupData);
161  prependLookup(newlookup);
162  // Type may have changed
163  LookupType ret = GuessLookupType(newlookup);
164  if (ret != kUnknownVideo)
165  {
166  newlookup->SetSubtype(ret);
167  }
168  continue;
169  }
170 
171  // If we're in automatic mode, we need to make
172  // these decisions on our own. Pass to title match.
173  if (list[0]->GetAutomatic() && list.count() > 1
174  && list[0]->GetStep() == kLookupSearch)
175  {
176  MetadataLookup *bestLookup = findBestMatch(list, lookup->GetBaseTitle());
177  if (bestLookup)
178  {
179  MetadataLookup *newlookup = bestLookup;
180 
181  // pass through automatic type
182  newlookup->SetAutomatic(true);
183  // bestlookup is owned by list, we need an extra reference
184  newlookup->IncrRef();
185  newlookup->SetStep(kLookupData);
186  // Type may have changed
187  LookupType ret = GuessLookupType(newlookup);
188  if (ret != kUnknownVideo)
189  {
190  newlookup->SetSubtype(ret);
191  }
192  prependLookup(newlookup);
193  continue;
194  }
195 
196  // Experimental:
197  // If nothing matches, always return the first found item
198  if (qEnvironmentVariableIsSet("EXPERIMENTAL_METADATA_GRAB"))
199  {
200  MetadataLookup *newlookup = list.takeFirst();
201 
202  // pass through automatic type
203  newlookup->SetAutomatic(true);
204  newlookup->SetStep(kLookupData);
205  // Type may have changed
206  LookupType ret = GuessLookupType(newlookup);
207  if (ret != kUnknownVideo)
208  {
209  newlookup->SetSubtype(ret);
210  }
211  prependLookup(newlookup);
212  continue;
213  }
214 
215  // nothing more we can do in automatic mode
216  QCoreApplication::postEvent(m_parent,
217  new MetadataLookupFailure(MetadataLookupList() << lookup));
218  continue;
219  }
220 
221  LOG(VB_GENERAL, LOG_INFO,
222  QString("Returning Metadata Results: %1 %2 %3")
223  .arg(lookup->GetBaseTitle()).arg(lookup->GetSeason())
224  .arg(lookup->GetEpisode()));
225  QCoreApplication::postEvent(m_parent,
226  new MetadataLookupEvent(list));
227  }
228  else
229  {
230  if (list.isEmpty())
231  {
232  LOG(VB_GENERAL, LOG_INFO,
233  QString("Metadata Lookup Failed: No Results %1 %2 %3")
234  .arg(lookup->GetBaseTitle()).arg(lookup->GetSeason())
235  .arg(lookup->GetEpisode()));
236  }
237  if (m_parent)
238  {
239  // list is always empty here
240  list.append(lookup);
241  QCoreApplication::postEvent(m_parent,
242  new MetadataLookupFailure(list));
243  }
244  }
245  }
246 
247  RunEpilog();
248 }
249 
251  const QString &originaltitle,
252  bool withArt)
253 {
254  unsigned int exactMatches = 0;
255  unsigned int exactMatchesWithArt = 0;
256  static const QRegularExpression year { R"( \(\d{4}\)$)" };
257 
258  for (const auto& lkup : std::as_const(list))
259  {
260  // Consider exact title matches with or without trailing '(year)' (ignoring case)
261  QString titlewoyear = originaltitle;
262  auto match = year.match(titlewoyear);
263  if (match.hasMatch())
264  {
265  titlewoyear.remove(match.capturedStart(), match.capturedLength());
266  }
267 
268  if ((QString::compare(lkup->GetTitle(), originaltitle, Qt::CaseInsensitive) == 0) ||
269  (QString::compare(lkup->GetTitle(), titlewoyear, Qt::CaseInsensitive) == 0))
270  {
271  // In lookup by name, the television database tends to only include Banner artwork.
272  // In lookup by name, the movie database tends to include only Fan and Cover artwork.
273  if ((!(lkup->GetArtwork(kArtworkFanart)).empty()) ||
274  (!(lkup->GetArtwork(kArtworkCoverart)).empty()) ||
275  (!(lkup->GetArtwork(kArtworkBanner)).empty()))
276  {
277  exactMatchesWithArt++;
278  }
279  exactMatches++;
280  }
281  }
282 
283  if (withArt)
284  return exactMatchesWithArt;
285  return exactMatches;
286 }
287 
289  const QString &originaltitle)
290 {
291  QStringList titles;
292  MetadataLookup *ret = nullptr;
293  QDate exactTitleDate;
294  float exactTitlePopularity = 0.0F;
295  int exactMatches = 0;
296  int exactMatchesWithArt = 0;
297  bool foundMatchWithArt = false;
298  bool foundMatchWithYear = false;
299  uint year = 0;
300 
301  QString titlewoyear = originaltitle;
302 
303  static const QRegularExpression regexyear { R"( \(\d{4}\)$)" };
304 
305  auto match = regexyear.match(titlewoyear);
306  if (match.hasMatch())
307  {
308  titlewoyear.remove(match.capturedStart(), match.capturedLength());
309  year = match.captured(0).replace(" (","").replace(")","").toUInt();
310  LOG(VB_GENERAL, LOG_DEBUG, QString("Looking for: '%1' with release year: '%2'")
311  .arg(titlewoyear, QString::number(year)));
312  }
313 
314  // Build a list of all the titles
315  for (const auto& lkup : std::as_const(list))
316  {
317  QString title = lkup->GetTitle();
318  LOG(VB_GENERAL, LOG_INFO,
319  QString("Comparing metadata title '%1' [%2] to recording title '%3' [%4]")
320  .arg(title, lkup->GetReleaseDate().toString(), titlewoyear,
321  (year == 0) ? "N/A" : QString::number(year)));
322 
323  // Consider exact title matches with or without trailing '(year)' (ignoring case),
324  // which have some artwork available.
325  if ((QString::compare(title, originaltitle, Qt::CaseInsensitive) == 0) ||
326  (QString::compare(title, titlewoyear, Qt::CaseInsensitive) == 0))
327  {
328  bool hasArtwork = ((!(lkup->GetArtwork(kArtworkFanart)).empty()) ||
329  (!(lkup->GetArtwork(kArtworkCoverart)).empty()) ||
330  (!(lkup->GetArtwork(kArtworkBanner)).empty()));
331 
332  if ((lkup->GetYear() != 0) && (year == lkup->GetYear()))
333  {
334  exactTitleDate = lkup->GetReleaseDate();
335  exactTitlePopularity = lkup->GetPopularity();
336  foundMatchWithYear = true;
337  ret = lkup;
338  }
339 
340  LOG(VB_GENERAL, LOG_INFO, QString("'%1', popularity = %2, ReleaseDate = %3, Year = %4")
341  .arg(title)
342  .arg(lkup->GetPopularity())
343  .arg(lkup->GetReleaseDate().toString())
344  .arg(lkup->GetYear()));
345 
346  // After the first exact match, prefer any more popular one.
347  // Most of the Movie database entries have Popularity fields.
348  // The TV series database generally has no Popularity values specified,
349  // so if none are found so far in the search, pick the most recently
350  // released entry with artwork. Also, if the first exact match had
351  // no artwork, prefer any later exact match with artwork.
352  // Stop searching if we have already found a match with correct year.
353  if ((ret == nullptr) ||
354  (hasArtwork && !foundMatchWithYear &&
355  ((!foundMatchWithArt) ||
356  ((lkup->GetPopularity() > exactTitlePopularity)) ||
357  ((exactTitlePopularity == 0.0F) && (lkup->GetReleaseDate() > exactTitleDate)))))
358  {
359  exactTitleDate = lkup->GetReleaseDate();
360  exactTitlePopularity = lkup->GetPopularity();
361  ret = lkup;
362  }
363 
364  exactMatches++;
365  if (hasArtwork)
366  {
367  foundMatchWithArt = true;
368  exactMatchesWithArt++;
369  }
370  }
371 
372  titles.append(title);
373  }
374 
375  LOG(VB_GENERAL, LOG_DEBUG, QString("exactMatches = %1, exactMatchesWithArt = %2")
376  .arg(exactMatches)
377  .arg(exactMatchesWithArt));
378 
379  // If there was one or more exact matches then we can skip a more intensive
380  // and time consuming search
381  if (exactMatches > 0)
382  {
383  if (exactMatches == 1)
384  {
385  LOG(VB_GENERAL, LOG_INFO, QString("Single exact title match for '%1'")
386  .arg(originaltitle));
387  }
388  else
389  {
390  LOG(VB_GENERAL, LOG_INFO,
391  QString("Multiple exact title matches found for '%1'. "
392  "Selecting by exact year [%2] or most popular or most recent [%3]")
393  .arg(originaltitle,
394  (year == 0) ? "N/A" : QString::number(year),
395  exactTitleDate.toString()));
396  }
397  return ret;
398  }
399 
400  // Apply Levenshtein distance algorithm to determine closest match
401  QString bestTitle = nearestName(originaltitle, titles);
402 
403  // If no "best" was chosen, give up.
404  if (bestTitle.isEmpty())
405  {
406  LOG(VB_GENERAL, LOG_ERR,
407  QString("No adequate match or multiple "
408  "matches found for %1. Update manually.")
409  .arg(originaltitle));
410  return nullptr;
411  }
412 
413  LOG(VB_GENERAL, LOG_INFO, QString("Best Title Match For %1: %2")
414  .arg(originaltitle, bestTitle));
415 
416  // Grab the one item that matches the besttitle (IMPERFECT)
417  for (const auto& item : std::as_const(list))
418  {
419  if (item->GetTitle() == bestTitle)
420  {
421  ret = item;
422  break;
423  }
424  }
425 
426  return ret;
427 }
428 
429 MetadataLookupList MetadataDownload::runGrabber(const QString& cmd, const QStringList& args,
430  MetadataLookup *lookup,
431  bool passseas)
432 {
433  MythSystemLegacy grabber(cmd, args, kMSStdOut);
434  MetadataLookupList list;
435 
436  LOG(VB_GENERAL, LOG_INFO, QString("Running Grabber: %1 %2")
437  .arg(cmd, args.join(" ")));
438 
439  grabber.Run();
440  grabber.Wait();
441  QByteArray result = grabber.ReadAll();
442  if (!result.isEmpty())
443  {
444  QDomDocument doc;
445 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
446  doc.setContent(result, true);
447 #else
448  doc.setContent(result, QDomDocument::ParseOption::UseNamespaceProcessing);
449 #endif
450  QDomElement root = doc.documentElement();
451  QDomElement item = root.firstChildElement("item");
452 
453  while (!item.isNull())
454  {
455  MetadataLookup *tmp = ParseMetadataItem(item, lookup, passseas);
456  list.append(tmp);
457  // MetadataLookup is to be owned by list
458  tmp->DecrRef();
459  item = item.nextSiblingElement("item");
460  }
461  }
462  return list;
463 }
464 
466 {
467  return MetaGrabberScript::GetType(kGrabberMovie).GetPath();
468 }
469 
471 {
473 }
474 
476 {
477  return MetaGrabberScript::GetType(kGrabberGame).GetPath();
478 }
479 
480 bool MetadataDownload::runGrabberTest(const QString &grabberpath)
481 {
482  return MetaGrabberScript(grabberpath).Test();
483 }
484 
486 {
488  {
489  LOG(VB_GENERAL, LOG_INFO,
490  QString("Movie grabber not functional. Aborting this run."));
491  return false;
492  }
493 
494  return true;
495 }
496 
498 {
500  {
501  LOG(VB_GENERAL, LOG_INFO,
502  QString("Television grabber not functional. Aborting this run."));
503  return false;
504  }
505 
506  return true;
507 }
508 
510  MetadataLookup *lookup,
511  bool passseas)
512 {
513  MetadataLookupList list;
514 
515  LOG(VB_GENERAL, LOG_INFO,
516  QString("Matching MXML file found. Parsing %1 for metadata...")
517  .arg(MXMLpath));
518 
519  if (lookup->GetType() == kMetadataVideo)
520  {
521  QByteArray mxmlraw;
522  QDomElement item;
523  auto *rf = new RemoteFile(MXMLpath);
524 
525  if (rf->isOpen())
526  {
527  bool loaded = rf->SaveAs(mxmlraw);
528  if (loaded)
529  {
530  QDomDocument doc;
531 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
532  bool success = doc.setContent(mxmlraw, true);
533 #else
534  auto parseResult = doc.setContent(mxmlraw, QDomDocument::ParseOption::UseNamespaceProcessing);
535  bool success { parseResult };
536 #endif
537  if (!success)
538  {
539  lookup->SetStep(kLookupData);
540  QDomElement root = doc.documentElement();
541  item = root.firstChildElement("item");
542  }
543  else
544  {
545  LOG(VB_GENERAL, LOG_ERR,
546  QString("Corrupt or invalid MXML file."));
547  }
548  }
549  }
550 
551  delete rf;
552  rf = nullptr;
553 
554  MetadataLookup *tmp = ParseMetadataItem(item, lookup, passseas);
555  list.append(tmp);
556  // MetadataLookup is owned by the MetadataLookupList returned
557  tmp->DecrRef();
558  }
559 
560  return list;
561 }
562 
564  MetadataLookup *lookup)
565 {
566  MetadataLookupList list;
567 
568  LOG(VB_GENERAL, LOG_INFO,
569  QString("Matching NFO file found. Parsing %1 for metadata...")
570  .arg(NFOpath));
571 
572  bool error = false;
573 
574  if (lookup->GetType() == kMetadataVideo)
575  {
576  QByteArray nforaw;
577  QDomElement item;
578  auto *rf = new RemoteFile(NFOpath);
579 
580  if (rf->isOpen())
581  {
582  bool loaded = rf->SaveAs(nforaw);
583 
584  if (loaded)
585  {
586  QDomDocument doc;
587 
588 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
589  bool success = doc.setContent(nforaw, true);
590 #else
591  auto parseResult = doc.setContent(nforaw, QDomDocument::ParseOption::UseNamespaceProcessing);
592  bool success { parseResult };
593 #endif
594  if (success)
595  {
596  lookup->SetStep(kLookupData);
597  item = doc.documentElement();
598  }
599  else
600  {
601  LOG(VB_GENERAL, LOG_ERR,
602  QString("Invalid NFO file found."));
603  error = true;
604  }
605  }
606  }
607 
608  delete rf;
609  rf = nullptr;
610 
611  if (!error)
612  {
613  MetadataLookup *tmp = ParseMetadataMovieNFO(item, lookup);
614 
615  list.append(tmp);
616  // MetadataLookup is owned by the MetadataLookupList returned
617  tmp->DecrRef();
618  }
619  }
620 
621  return list;
622 }
623 
625 {
626  MetadataLookupList list;
627  MetaGrabberScript grabber =
629  if (!grabber.IsValid())
630  return {};
631 
632  // If the inetref is populated, even in kLookupSearch mode,
633  // become a kLookupData grab and use that.
634  if (lookup->GetStep() == kLookupSearch &&
635  (!lookup->GetInetref().isEmpty() &&
636  lookup->GetInetref() != "00000000"))
637  {
638  lookup->SetStep(kLookupData);
639  }
640 
641  if (lookup->GetStep() == kLookupSearch)
642  {
643  if (lookup->GetTitle().isEmpty())
644  {
645  // no point searching on nothing...
646  return list;
647  }
648  // we're searching
649  list = grabber.Search(lookup->GetTitle(), lookup);
650  }
651  else if (lookup->GetStep() == kLookupData)
652  {
653  // we're just grabbing data
654  list = grabber.LookupData(lookup->GetInetref(), lookup);
655  }
656 
657  return list;
658 }
659 
669 {
670  MetadataLookupList list;
671 
672  MetaGrabberScript grabber =
674  if (!grabber.IsValid())
675  return {};
676 
677  // initial search mode
678  if (!lookup->GetInetref().isEmpty() && lookup->GetInetref() != "00000000" &&
679  (lookup->GetStep() == kLookupSearch || lookup->GetStep() == kLookupData))
680  {
681  // with inetref
682  lookup->SetStep(kLookupData);
683  // we're just grabbing data
684  list = grabber.LookupData(lookup->GetInetref(), lookup);
685  }
686  else if (lookup->GetStep() == kLookupSearch)
687  {
688  if (lookup->GetBaseTitle().isEmpty())
689  {
690  // no point searching on nothing...
691  return list;
692  }
693  list = grabber.Search(lookup->GetBaseTitle(), lookup);
694  }
695 
696  return list;
697 }
698 
711 {
712  MetadataLookupList list;
713 
714  MetaGrabberScript grabber =
716  if (!grabber.IsValid())
717  return {};
718  bool searchcollection = false;
719 
720  // initial search mode
721  if (!lookup->GetInetref().isEmpty() && lookup->GetInetref() != "00000000" &&
722  (lookup->GetStep() == kLookupSearch || lookup->GetStep() == kLookupData))
723  {
724  // with inetref
725  lookup->SetStep(kLookupData);
726  if (lookup->GetSeason() || lookup->GetEpisode())
727  {
728  list = grabber.LookupData(lookup->GetInetref(), lookup->GetSeason(),
729  lookup->GetEpisode(), lookup);
730  }
731 
732  if (list.isEmpty() && (!lookup->GetSubtitle().isEmpty()))
733  {
734  list = grabber.SearchSubtitle(lookup->GetInetref(),
735  lookup->GetBaseTitle() /* unused */,
736  lookup->GetSubtitle(), lookup, false);
737  }
738 
739  if (list.isEmpty() && !lookup->GetCollectionref().isEmpty())
740  {
741  list = grabber.LookupCollection(lookup->GetCollectionref(), lookup);
742  searchcollection = true;
743  }
744  else if (list.isEmpty())
745  {
746  // We do not store CollectionRef in our database
747  // so try with the inetref, for all purposes with TVDB, they are
748  // always identical
749  list = grabber.LookupCollection(lookup->GetInetref(), lookup);
750  searchcollection = true;
751  }
752  }
753  else if (lookup->GetStep() == kLookupSearch)
754  {
755  if (lookup->GetBaseTitle().isEmpty())
756  {
757  // no point searching on nothing...
758  return list;
759  }
760  if (!lookup->GetSubtitle().isEmpty())
761  {
762  list = grabber.SearchSubtitle(lookup->GetBaseTitle(),
763  lookup->GetSubtitle(), lookup, false);
764  }
765  if (list.isEmpty())
766  {
767  list = grabber.Search(lookup->GetBaseTitle(), lookup);
768  }
769  }
770  else if (lookup->GetStep() == kLookupCollection)
771  {
772  list = grabber.LookupCollection(lookup->GetCollectionref(), lookup);
773  }
774 
775  // Collection Fallback
776  // If the lookup allows generic metadata, and the specific
777  // season and episode are not available, try for series metadata.
778  if (!searchcollection && list.isEmpty() &&
779  !lookup->GetCollectionref().isEmpty() &&
780  lookup->GetAllowGeneric() && lookup->GetStep() == kLookupData)
781  {
782  lookup->SetStep(kLookupCollection);
783  list = grabber.LookupCollection(lookup->GetCollectionref(), lookup);
784  }
785 
786  if (!list.isEmpty())
787  {
788  // mark all results so that search collection is properly handled later
789  lookup->SetIsCollection(searchcollection);
790  // NOLINTNEXTLINE(modernize-loop-convert)
791  for (auto it = list.begin(); it != list.end(); ++it)
792  {
793  (*it)->SetIsCollection(searchcollection);
794  }
795  }
796 
797  return list;
798 }
799 
801 {
802  MetadataLookupList list;
803 
804  if (lookup->GetSubtype() != kProbableMovie &&
805  !lookup->GetSubtitle().isEmpty())
806  {
807  list.append(handleTelevision(lookup));
808  }
809 
810  if (lookup->GetSubtype() != kProbableTelevision)
811  {
812  list.append(handleMovie(lookup));
813  }
814 
815  if (list.count() == 1)
816  {
817  list[0]->SetStep(kLookupData);
818  }
819 
820  return list;
821 }
822 
824 {
825  // We only enter this mode if we are pretty darn sure this is a TV show,
826  // but we're for some reason looking up a generic, or the title didn't
827  // exactly match in one of the earlier lookups. This is a total
828  // hail mary to try to get at least *series* level info and art/inetref.
829 
830  MetadataLookupList list;
831 
832  if (lookup->GetBaseTitle().isEmpty())
833  {
834  // no point searching on nothing...
835  return list;
836  }
837 
838  // no inetref known, just pull the default grabber
840 
841  // cache some initial values so we can change them in the lookup later
842  LookupType origtype = lookup->GetSubtype();
843  int origseason = lookup->GetSeason();
844  int origepisode = lookup->GetEpisode();
845 
846  if (origseason == 0 && origepisode == 0)
847  {
848  lookup->SetSeason(1);
849  lookup->SetEpisode(1);
850  }
851 
852  list = grabber.Search(lookup->GetBaseTitle(), lookup);
853 
854  if (list.count() == 1)
855  {
856  // search was successful, rerun as normal television mode
857  lookup->SetInetref(list[0]->GetInetref());
858  lookup->SetCollectionref(list[0]->GetCollectionref());
859  list = handleTelevision(lookup);
860  }
861 
862  lookup->SetSeason(origseason);
863  lookup->SetEpisode(origepisode);
864  lookup->SetSubtype(origtype);
865 
866  return list;
867 }
868 
869 static QString getNameWithExtension(const QString &filename, const QString &type)
870 {
871  QString ret;
872  QString newname;
873  QUrl qurl(filename);
874  QString ext = QFileInfo(qurl.path()).suffix();
875 
876  if (ext.isEmpty())
877  {
878  // no extension, assume it is a directory
879  newname = filename + "/" + QFileInfo(qurl.path()).fileName() + "." + type;
880  }
881  else
882  {
883  newname = filename.left(filename.size() - ext.size()) + type;
884  }
885 
886  if (RemoteFile::Exists(newname))
887  ret = newname;
888 
889  return ret;
890 }
891 
893 {
894  return getNameWithExtension(filename, "mxml");
895 }
896 
897 QString MetadataDownload::getNFOPath(const QString& filename)
898 {
899  return getNameWithExtension(filename, "nfo");
900 }
MetadataDownload::readMXML
static MetadataLookupList readMXML(const QString &MXMLpath, MetadataLookup *lookup, bool passseas=true)
Definition: metadatadownload.cpp:509
MetadataDownload::runGrabberTest
static bool runGrabberTest(const QString &grabberpath)
Definition: metadatadownload.cpp:480
MetadataDownload::m_lookupList
MetadataLookupList m_lookupList
Definition: metadatadownload.h:87
build_compdb.args
args
Definition: build_compdb.py:11
MetaGrabberScript
Definition: metadatagrabber.h:29
MetadataDownload::run
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: metadatadownload.cpp:74
MetaGrabberScript::LookupCollection
MetadataLookupList LookupCollection(const QString &collectionref, MetadataLookup *lookup, bool passseas=true)
Definition: metadatagrabber.cpp:530
MetadataDownload::runGrabber
static MetadataLookupList runGrabber(const QString &cmd, const QStringList &args, MetadataLookup *lookup, bool passseas=true)
Definition: metadatadownload.cpp:429
RefCountHandler
Definition: referencecounterlist.h:17
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
MetadataDownload::findBestMatch
static MetadataLookup * findBestMatch(MetadataLookupList list, const QString &originaltitle)
Definition: metadatadownload.cpp:288
MetadataLookup::SetSubtype
void SetSubtype(LookupType subtype)
Definition: metadatacommon.h:240
kGrabberGame
@ kGrabberGame
Definition: metadatagrabber.h:25
error
static void error(const char *str,...)
Definition: vbi.cpp:37
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:125
kGrabberMovie
@ kGrabberMovie
Definition: metadatagrabber.h:22
MetadataLookup::SetIsCollection
void SetIsCollection(bool collection)
Definition: metadatacommon.h:273
MythSystemLegacy
Definition: mythsystemlegacy.h:67
MetadataLookup::GetTitle
QString GetTitle() const
Definition: metadatacommon.h:299
MetaGrabberScript::Test
bool Test(void)
Definition: metadatagrabber.cpp:374
MetaGrabberScript::SearchSubtitle
MetadataLookupList SearchSubtitle(const QString &title, const QString &subtitle, MetadataLookup *lookup, bool passseas=true)
Definition: metadatagrabber.cpp:474
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:460
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
MetadataLookup::GetSubtype
LookupType GetSubtype() const
Definition: metadatacommon.h:287
kLookupData
@ kLookupData
Definition: metadatacommon.h:29
kProbableMovie
@ kProbableMovie
Definition: metadatacommon.h:53
RefCountedList::takeFirstAndDecr
RefCountHandler< T > takeFirstAndDecr(void)
Removes the first item in the list and returns it.
Definition: referencecounterlist.h:112
MetadataDownload::prependLookup
void prependLookup(MetadataLookup *lookup)
prependLookup: Add lookup to top of the queue MetadataDownload::m_lookupList takes ownership of the g...
Definition: metadatadownload.cpp:55
RemoteFile
Definition: remotefile.h:17
GuessLookupType
LookupType GuessLookupType(ProgramInfo *pginfo)
Definition: metadatafactory.cpp:645
MetaGrabberScript::IsValid
bool IsValid(void) const
Definition: metadatagrabber.h:55
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
MythSystemLegacy::ReadAll
QByteArray & ReadAll()
Definition: mythsystemlegacy.cpp:402
kMetadataVideo
@ kMetadataVideo
Definition: metadatacommon.h:43
MetadataDownload::GetTelevisionGrabber
static QString GetTelevisionGrabber()
Definition: metadatadownload.cpp:470
mythdirs.h
MetadataDownload::~MetadataDownload
~MetadataDownload() override
Definition: metadatadownload.cpp:30
MetadataLookup::SetCollectionref
void SetCollectionref(const QString &collectionref)
Definition: metadatacommon.h:270
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
MetadataLookup::GetStep
LookupStep GetStep() const
Definition: metadatacommon.h:289
nearestName
QString nearestName(const QString &actual, const QStringList &candidates)
Definition: metadatacommon.cpp:1387
mythsystemlegacy.h
MetadataDownload::handleMovie
static MetadataLookupList handleMovie(MetadataLookup *lookup)
handleMovie: attempt to find movie data via the following (in order) 1- Local MXML: already done befo...
Definition: metadatadownload.cpp:668
MetadataLookup
Definition: metadatacommon.h:87
MetadataDownload::getNFOPath
static QString getNFOPath(const QString &filename)
Definition: metadatadownload.cpp:897
MetadataLookup::SetEpisode
void SetEpisode(uint episode)
Definition: metadatacommon.h:268
kArtworkCoverart
@ kArtworkCoverart
Definition: metadataimagehelper.h:11
kLookupSearch
@ kLookupSearch
Definition: metadatacommon.h:28
MetaGrabberScript::Search
MetadataLookupList Search(const QString &title, MetadataLookup *lookup, bool passseas=true)
Definition: metadatagrabber.cpp:462
kGrabberTelevision
@ kGrabberTelevision
Definition: metadatagrabber.h:23
kArtworkFanart
@ kArtworkFanart
Definition: metadataimagehelper.h:12
mythlogging.h
MetadataLookup::GetEpisode
uint GetEpisode() const
Definition: metadatacommon.h:315
MetadataLookupFailure
Definition: metadatadownload.h:23
remotefile.h
metadatadownload.h
RefCountedList< MetadataLookup >
MetadataDownload::m_parent
QObject * m_parent
Definition: metadatadownload.h:86
kArtworkBanner
@ kArtworkBanner
Definition: metadataimagehelper.h:13
kMetadataRecording
@ kMetadataRecording
Definition: metadatacommon.h:44
getNameWithExtension
static QString getNameWithExtension(const QString &filename, const QString &type)
Definition: metadatadownload.cpp:869
MetadataLookup::GetType
MetadataType GetType() const
Definition: metadatacommon.h:286
MythSystemLegacy::Wait
uint Wait(std::chrono::seconds timeout=0s)
Definition: mythsystemlegacy.cpp:243
MetadataLookup::GetSubtitle
QString GetSubtitle() const
Definition: metadatacommon.h:310
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
MetadataDownload::readNFO
static MetadataLookupList readNFO(const QString &NFOpath, MetadataLookup *lookup)
Definition: metadatadownload.cpp:563
storagegroup.h
MetadataDownload::addLookup
void addLookup(MetadataLookup *lookup)
addLookup: Add lookup to bottom of the queue MetadataDownload::m_lookupList takes ownership of the gi...
Definition: metadatadownload.cpp:40
MetadataDownload::handleRecordingGeneric
static MetadataLookupList handleRecordingGeneric(MetadataLookup *lookup)
Definition: metadatadownload.cpp:823
MetadataLookup::GetCollectionref
QString GetCollectionref() const
Definition: metadatacommon.h:357
kLookupCollection
@ kLookupCollection
Definition: metadatacommon.h:30
RefCountedList::takeFirst
T * takeFirst(void)
Removes the first item in the list and returns it.
Definition: referencecounterlist.h:78
MetadataLookup::SetSeason
void SetSeason(uint season)
Definition: metadatacommon.h:267
MetadataDownload::handleTelevision
static MetadataLookupList handleTelevision(MetadataLookup *lookup)
handleTelevision attempt to find television data via the following (in order) 1- Local MXML: already ...
Definition: metadatadownload.cpp:710
MetaGrabberScript::GetGrabber
static MetaGrabberScript GetGrabber(GrabberType defaultType, const MetadataLookup *lookup=nullptr)
Definition: metadatagrabber.cpp:143
MetadataLookup::GetAllowGeneric
bool GetAllowGeneric() const
Definition: metadatacommon.h:295
MetadataLookupFailure::kEventType
static const Type kEventType
Definition: metadatadownload.h:32
MetadataLookup::GetSeason
uint GetSeason() const
Definition: metadatacommon.h:314
MetadataLookup::GetFilename
QString GetFilename() const
Definition: metadatacommon.h:298
MetadataLookup::GetBaseTitle
QString GetBaseTitle() const
Definition: metadatacommon.h:300
mythuihelper.h
MetadataDownload::handleGame
static MetadataLookupList handleGame(MetadataLookup *lookup)
Definition: metadatadownload.cpp:624
kMetadataGame
@ kMetadataGame
Definition: metadatacommon.h:46
MetadataDownload::getMXMLPath
static QString getMXMLPath(const QString &filename)
Definition: metadatadownload.cpp:892
kUnknownVideo
@ kUnknownVideo
Definition: metadatacommon.h:54
mythmiscutil.h
LookupType
LookupType
Definition: metadatacommon.h:50
MetadataDownload::MovieGrabberWorks
static bool MovieGrabberWorks()
Definition: metadatadownload.cpp:485
mythcorecontext.h
MetadataLookup::SetAutomatic
void SetAutomatic(bool autom)
Definition: metadatacommon.h:246
MetadataLookup::GetInetref
QString GetInetref() const
Definition: metadatacommon.h:356
MetadataLookup::SetInetref
void SetInetref(const QString &inetref)
Definition: metadatacommon.h:269
MetaGrabberScript::LookupData
MetadataLookupList LookupData(const QString &inetref, MetadataLookup *lookup, bool passseas=true)
Definition: metadatagrabber.cpp:503
MetadataDownload::handleVideoUndetermined
static MetadataLookupList handleVideoUndetermined(MetadataLookup *lookup)
Definition: metadatadownload.cpp:800
MetaGrabberScript::GetType
GrabberType GetType(void) const
Definition: metadatagrabber.h:64
metadatafactory.h
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:263
MetadataDownload::m_mutex
QMutex m_mutex
Definition: metadatadownload.h:88
kProbableTelevision
@ kProbableTelevision
Definition: metadatacommon.h:51
ParseMetadataItem
MetadataLookup * ParseMetadataItem(const QDomElement &item, MetadataLookup *lookup, bool passseas)
Definition: metadatacommon.cpp:894
MetadataLookupEvent::kEventType
static const Type kEventType
Definition: metadatadownload.h:20
MythSystemLegacy::Run
void Run(std::chrono::seconds timeout=0s)
Runs a command inside the /bin/sh shell. Returns immediately.
Definition: mythsystemlegacy.cpp:213
MetadataLookup::GetAutomatic
bool GetAutomatic() const
Definition: metadatacommon.h:290
MetadataDownload::GetGameGrabber
static QString GetGameGrabber()
Definition: metadatadownload.cpp:475
ParseMetadataMovieNFO
MetadataLookup * ParseMetadataMovieNFO(const QDomElement &item, MetadataLookup *lookup)
Definition: metadatacommon.cpp:1153
ReferenceCounter::IncrRef
virtual int IncrRef(void)
Increments reference count.
Definition: referencecounter.cpp:101
build_compdb.filename
filename
Definition: build_compdb.py:21
MetadataLookupEvent
Definition: metadatadownload.h:11
kMSStdOut
@ kMSStdOut
allow access to stdout
Definition: mythsystem.h:41
MetadataDownload::GetMovieGrabber
static QString GetMovieGrabber()
Definition: metadatadownload.cpp:465
MetadataDownload::TelevisionGrabberWorks
static bool TelevisionGrabberWorks()
Definition: metadatadownload.cpp:497
MetadataLookup::SetStep
void SetStep(LookupStep step)
Definition: metadatacommon.h:244
MetadataLookupList
RefCountedList< MetadataLookup > MetadataLookupList
Definition: metadatacommon.h:462
uint
unsigned int uint
Definition: freesurround.h:24
MetadataDownload::cancel
void cancel()
Definition: metadatadownload.cpp:66
MetadataDownload::findExactMatchCount
static unsigned int findExactMatchCount(MetadataLookupList list, const QString &originaltitle, bool withArt)
Definition: metadatadownload.cpp:250