5#include <QCoreApplication>
9#include <QRegularExpression>
24 (QEvent::Type) QEvent::registerEventType();
27 (QEvent::Type) QEvent::registerEventType();
106 else if (!nfo.isEmpty())
155 if (list.count() == 1 && list[0]->GetStep() ==
kLookupSearch)
172 if (list[0]->GetAutomatic() && list.count() > 1
197 if (qEnvironmentVariableIsSet(
"EXPERIMENTAL_METADATA_GRAB"))
215 QCoreApplication::postEvent(
m_parent,
220 LOG(VB_GENERAL, LOG_INFO,
221 QString(
"Returning Metadata Results: %1 %2 %3")
224 QCoreApplication::postEvent(
m_parent,
231 LOG(VB_GENERAL, LOG_INFO,
232 QString(
"Metadata Lookup Failed: No Results %1 %2 %3")
240 QCoreApplication::postEvent(
m_parent,
250 const QString &originaltitle,
253 unsigned int exactMatches = 0;
254 unsigned int exactMatchesWithArt = 0;
255 static const QRegularExpression year { R
"( \(\d{4}\)$)" };
257 for (
const auto& lkup : std::as_const(list))
260 QString titlewoyear = originaltitle;
261 auto match = year.match(titlewoyear);
262 if (match.hasMatch())
264 titlewoyear.remove(match.capturedStart(), match.capturedLength());
267 if ((QString::compare(lkup->GetTitle(), originaltitle, Qt::CaseInsensitive) == 0) ||
268 (QString::compare(lkup->GetTitle(), titlewoyear, Qt::CaseInsensitive) == 0))
276 exactMatchesWithArt++;
283 return exactMatchesWithArt;
288 const QString &originaltitle)
292 QDate exactTitleDate;
293 float exactTitlePopularity = 0.0F;
294 int exactMatches = 0;
295 int exactMatchesWithArt = 0;
296 bool foundMatchWithArt =
false;
297 bool foundMatchWithYear =
false;
300 QString titlewoyear = originaltitle;
302 static const QRegularExpression regexyear { R
"( \(\d{4}\)$)" };
304 auto match = regexyear.match(titlewoyear);
305 if (match.hasMatch())
307 titlewoyear.remove(match.capturedStart(), match.capturedLength());
308 year = match.captured(0).replace(
" (",
"").replace(
")",
"").toUInt();
309 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Looking for: '%1' with release year: '%2'")
310 .arg(titlewoyear, QString::number(year)));
314 titles.reserve(list.size());
315 for (
const auto& lkup : std::as_const(list))
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)));
325 if ((QString::compare(title, originaltitle, Qt::CaseInsensitive) == 0) ||
326 (QString::compare(title, titlewoyear, Qt::CaseInsensitive) == 0))
328 bool hasArtwork = ((!(lkup->GetArtwork(
kArtworkFanart)).empty()) ||
332 if ((lkup->GetYear() != 0) && (year == lkup->GetYear()))
334 exactTitleDate = lkup->GetReleaseDate();
335 exactTitlePopularity = lkup->GetPopularity();
336 foundMatchWithYear =
true;
340 LOG(VB_GENERAL, LOG_INFO, QString(
"'%1', popularity = %2, ReleaseDate = %3, Year = %4")
342 .arg(lkup->GetPopularity())
343 .arg(lkup->GetReleaseDate().toString())
344 .arg(lkup->GetYear()));
353 if ((ret ==
nullptr) ||
354 (hasArtwork && !foundMatchWithYear &&
355 ((!foundMatchWithArt) ||
356 (lkup->GetPopularity() > exactTitlePopularity) ||
357 ((exactTitlePopularity == 0.0F) && (lkup->GetReleaseDate() > exactTitleDate)))))
359 exactTitleDate = lkup->GetReleaseDate();
360 exactTitlePopularity = lkup->GetPopularity();
367 foundMatchWithArt =
true;
368 exactMatchesWithArt++;
372 titles.append(title);
375 LOG(VB_GENERAL, LOG_DEBUG, QString(
"exactMatches = %1, exactMatchesWithArt = %2")
377 .arg(exactMatchesWithArt));
381 if (exactMatches > 0)
383 if (exactMatches == 1)
385 LOG(VB_GENERAL, LOG_INFO, QString(
"Single exact title match for '%1'")
386 .arg(originaltitle));
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]")
394 (year == 0) ?
"N/A" : QString::number(year),
395 exactTitleDate.toString()));
401 QString bestTitle =
nearestName(originaltitle, titles);
404 if (bestTitle.isEmpty())
406 LOG(VB_GENERAL, LOG_ERR,
407 QString(
"No adequate match or multiple "
408 "matches found for %1. Update manually.")
409 .arg(originaltitle));
413 LOG(VB_GENERAL, LOG_INFO, QString(
"Best Title Match For %1: %2")
414 .arg(originaltitle, bestTitle));
417 for (
const auto& item : std::as_const(list))
419 if (item->GetTitle() == bestTitle)
436 LOG(VB_GENERAL, LOG_INFO, QString(
"Running Grabber: %1 %2")
437 .arg(cmd,
args.join(
" ")));
441 QByteArray result = grabber.
ReadAll();
442 if (!result.isEmpty())
445#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
446 doc.setContent(result,
true);
448 doc.setContent(result, QDomDocument::ParseOption::UseNamespaceProcessing);
450 QDomElement root = doc.documentElement();
451 QDomElement item = root.firstChildElement(
"item");
453 while (!item.isNull())
459 item = item.nextSiblingElement(
"item");
489 LOG(VB_GENERAL, LOG_INFO,
490 QString(
"Movie grabber not functional. Aborting this run."));
501 LOG(VB_GENERAL, LOG_INFO,
502 QString(
"Television grabber not functional. Aborting this run."));
515 LOG(VB_GENERAL, LOG_INFO,
516 QString(
"Matching MXML file found. Parsing %1 for metadata...")
527 bool loaded = rf->SaveAs(mxmlraw);
531#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
532 bool success = doc.setContent(mxmlraw,
true);
534 auto parseResult = doc.setContent(mxmlraw, QDomDocument::ParseOption::UseNamespaceProcessing);
535 bool success { parseResult };
540 QDomElement root = doc.documentElement();
541 item = root.firstChildElement(
"item");
545 LOG(VB_GENERAL, LOG_ERR,
546 QString(
"Corrupt or invalid MXML file."));
568 LOG(VB_GENERAL, LOG_INFO,
569 QString(
"Matching NFO file found. Parsing %1 for metadata...")
582 bool loaded = rf->SaveAs(nforaw);
588#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
589 bool success = doc.setContent(nforaw,
true);
591 auto parseResult = doc.setContent(nforaw, QDomDocument::ParseOption::UseNamespaceProcessing);
592 bool success { parseResult };
597 item = doc.documentElement();
601 LOG(VB_GENERAL, LOG_ERR,
602 QString(
"Invalid NFO file found."));
718 bool searchcollection =
false;
732 if (list.isEmpty() && (!lookup->
GetSubtitle().isEmpty()))
742 searchcollection =
true;
744 else if (list.isEmpty())
750 searchcollection =
true;
778 if (!searchcollection && list.isEmpty() &&
791 for (
auto it = list.begin(); it != list.end(); ++it)
793 (*it)->SetIsCollection(searchcollection);
815 if (list.count() == 1)
846 if (origseason == 0 && origepisode == 0)
854 if (list.count() == 1)
874 QString ext = QFileInfo(qurl.path()).suffix();
879 newname =
filename +
"/" + QFileInfo(qurl.path()).fileName() +
"." +
type;
bool isRunning(void) const
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
uint Wait(std::chrono::seconds timeout=0s)
void Run(std::chrono::seconds timeout=0s)
Runs a command inside the /bin/sh shell. Returns immediately.
RefCountHandler< T > takeFirstAndDecr(void)
Removes the first item in the list and returns it.
T * takeFirst(void)
Removes the first item in the list and returns it.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
static bool Exists(const QString &url, struct stat *fileinfo)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
@ kMSStdOut
allow access to stdout