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