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 for (
const auto& lkup : std::as_const(list))
316 QString title = lkup->GetTitle();
317 LOG(VB_GENERAL, LOG_INFO,
318 QString(
"Comparing metadata title '%1' [%2] to recording title '%3' [%4]")
319 .arg(title, lkup->GetReleaseDate().toString(), titlewoyear,
320 (year == 0) ?
"N/A" : QString::number(year)));
324 if ((QString::compare(title, originaltitle, Qt::CaseInsensitive) == 0) ||
325 (QString::compare(title, titlewoyear, Qt::CaseInsensitive) == 0))
327 bool hasArtwork = ((!(lkup->GetArtwork(
kArtworkFanart)).empty()) ||
331 if ((lkup->GetYear() != 0) && (year == lkup->GetYear()))
333 exactTitleDate = lkup->GetReleaseDate();
334 exactTitlePopularity = lkup->GetPopularity();
335 foundMatchWithYear =
true;
339 LOG(VB_GENERAL, LOG_INFO, QString(
"'%1', popularity = %2, ReleaseDate = %3, Year = %4")
341 .arg(lkup->GetPopularity())
342 .arg(lkup->GetReleaseDate().toString())
343 .arg(lkup->GetYear()));
352 if ((ret ==
nullptr) ||
353 (hasArtwork && !foundMatchWithYear &&
354 ((!foundMatchWithArt) ||
355 ((lkup->GetPopularity() > exactTitlePopularity)) ||
356 ((exactTitlePopularity == 0.0F) && (lkup->GetReleaseDate() > exactTitleDate)))))
358 exactTitleDate = lkup->GetReleaseDate();
359 exactTitlePopularity = lkup->GetPopularity();
366 foundMatchWithArt =
true;
367 exactMatchesWithArt++;
371 titles.append(title);
374 LOG(VB_GENERAL, LOG_DEBUG, QString(
"exactMatches = %1, exactMatchesWithArt = %2")
376 .arg(exactMatchesWithArt));
380 if (exactMatches > 0)
382 if (exactMatches == 1)
384 LOG(VB_GENERAL, LOG_INFO, QString(
"Single exact title match for '%1'")
385 .arg(originaltitle));
389 LOG(VB_GENERAL, LOG_INFO,
390 QString(
"Multiple exact title matches found for '%1'. "
391 "Selecting by exact year [%2] or most popular or most recent [%3]")
393 (year == 0) ?
"N/A" : QString::number(year),
394 exactTitleDate.toString()));
400 QString bestTitle =
nearestName(originaltitle, titles);
403 if (bestTitle.isEmpty())
405 LOG(VB_GENERAL, LOG_ERR,
406 QString(
"No adequate match or multiple "
407 "matches found for %1. Update manually.")
408 .arg(originaltitle));
412 LOG(VB_GENERAL, LOG_INFO, QString(
"Best Title Match For %1: %2")
413 .arg(originaltitle, bestTitle));
416 for (
const auto& item : std::as_const(list))
418 if (item->GetTitle() == bestTitle)
435 LOG(VB_GENERAL, LOG_INFO, QString(
"Running Grabber: %1 %2")
436 .arg(cmd,
args.join(
" ")));
440 QByteArray result = grabber.
ReadAll();
441 if (!result.isEmpty())
444#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
445 doc.setContent(result,
true);
447 doc.setContent(result, QDomDocument::ParseOption::UseNamespaceProcessing);
449 QDomElement root = doc.documentElement();
450 QDomElement item = root.firstChildElement(
"item");
452 while (!item.isNull())
458 item = item.nextSiblingElement(
"item");
488 LOG(VB_GENERAL, LOG_INFO,
489 QString(
"Movie grabber not functional. Aborting this run."));
500 LOG(VB_GENERAL, LOG_INFO,
501 QString(
"Television grabber not functional. Aborting this run."));
514 LOG(VB_GENERAL, LOG_INFO,
515 QString(
"Matching MXML file found. Parsing %1 for metadata...")
526 bool loaded = rf->SaveAs(mxmlraw);
530#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
531 bool success = doc.setContent(mxmlraw,
true);
533 auto parseResult = doc.setContent(mxmlraw, QDomDocument::ParseOption::UseNamespaceProcessing);
534 bool success { parseResult };
539 QDomElement root = doc.documentElement();
540 item = root.firstChildElement(
"item");
544 LOG(VB_GENERAL, LOG_ERR,
545 QString(
"Corrupt or invalid MXML file."));
567 LOG(VB_GENERAL, LOG_INFO,
568 QString(
"Matching NFO file found. Parsing %1 for metadata...")
581 bool loaded = rf->SaveAs(nforaw);
587#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
588 bool success = doc.setContent(nforaw,
true);
590 auto parseResult = doc.setContent(nforaw, QDomDocument::ParseOption::UseNamespaceProcessing);
591 bool success { parseResult };
596 item = doc.documentElement();
600 LOG(VB_GENERAL, LOG_ERR,
601 QString(
"Invalid NFO file found."));
717 bool searchcollection =
false;
731 if (list.isEmpty() && (!lookup->
GetSubtitle().isEmpty()))
741 searchcollection =
true;
743 else if (list.isEmpty())
749 searchcollection =
true;
777 if (!searchcollection && list.isEmpty() &&
790 for (
auto it = list.begin(); it != list.end(); ++it)
792 (*it)->SetIsCollection(searchcollection);
814 if (list.count() == 1)
845 if (origseason == 0 && origepisode == 0)
853 if (list.count() == 1)
873 QString ext = QFileInfo(qurl.path()).suffix();
878 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