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