From 913f0d8436f6bdd6edb7542112d041a943076264 Mon Sep 17 00:00:00 2001 From: Robert McNamara Date: Sun, 3 Jul 2011 09:12:36 -0700 Subject: [PATCH] Add a MythMetdataLookup program. This program should be thought of as a companion job to records like mythcommflag or mythtranscode. It takes programs as an input, performs a lookup online, and then sets various pieces of metadata on a successful return. For now, it can be run in one of two ways: ./mythmetadatalookup Will cycle through your entire list of recordings, look them up online, and if matches are found it will set: * For Movies: Inetref * For Television: Inetref, Season, and Episode This mode will not re-run a lookup on a movie that already has an inetref, or a television show that already has inetref, season, and episode. It can also be run as: ./mythmetadatalookup --chanid 12345 --starttime 07-02-2011T19:30:00 (using any supported date-time format) Which will unequivocally look up a valid program from that chanid and starttime. Though this program will work and set correct values in the recorded table, to really show its worth it relies on: * Themes to be updated to show the season and episode values when relevant * Recording rule editor changes to allow inetref to be set on recording rules, so that they propagate to the recordings (so that you can set the inetref for "Castle" to TVDB's inetref for "Castle (2009)" and have all your lookups work thereafter) I will also possibly add a mode later that is a little more "promiscuous" and overrides existing program info values-- say you get a generic episode that you really needed, you could theoretically change the subtitle in the UI and run a lookup on it to get correct, full metadata. I need to think about how best to accomplish this. --- mythtv/programs/mythmetadatalookup/.gitignore | 1 + mythtv/programs/mythmetadatalookup/lookup.cpp | 136 +++++++++++++++++ mythtv/programs/mythmetadatalookup/lookup.h | 30 ++++ mythtv/programs/mythmetadatalookup/main.cpp | 137 ++++++++++++++++++ .../mythmetadatalookup/mythmetadatalookup.pro | 19 +++ mythtv/programs/programs.pro | 1 + 6 files changed, 324 insertions(+) create mode 100644 mythtv/programs/mythmetadatalookup/.gitignore create mode 100644 mythtv/programs/mythmetadatalookup/lookup.cpp create mode 100644 mythtv/programs/mythmetadatalookup/lookup.h create mode 100644 mythtv/programs/mythmetadatalookup/main.cpp create mode 100644 mythtv/programs/mythmetadatalookup/mythmetadatalookup.pro diff --git a/mythtv/programs/mythmetadatalookup/.gitignore b/mythtv/programs/mythmetadatalookup/.gitignore new file mode 100644 index 00000000000..9e8a4045e92 --- /dev/null +++ b/mythtv/programs/mythmetadatalookup/.gitignore @@ -0,0 +1 @@ +mythfillnetvision diff --git a/mythtv/programs/mythmetadatalookup/lookup.cpp b/mythtv/programs/mythmetadatalookup/lookup.cpp new file mode 100644 index 00000000000..37cc9c5730d --- /dev/null +++ b/mythtv/programs/mythmetadatalookup/lookup.cpp @@ -0,0 +1,136 @@ +#include + +#include + +#include "programinfo.h" +#include "mythlogging.h" +#include "jobqueue.h" + +#include "lookup.h" + +LookerUpper::LookerUpper() : + m_busyRecList(QList()) +{ + m_metadataFactory = new MetadataFactory(this); +} + +LookerUpper::~LookerUpper() +{ +} + +bool LookerUpper::StillWorking() +{ + if (m_metadataFactory->IsRunning() || + m_busyRecList.count()) + { + return true; + } + + return false; +} + +void LookerUpper::HandleSingleRecording(const uint chanid, + const QDateTime starttime) +{ + ProgramInfo *pginfo = new ProgramInfo(chanid, starttime); + + if (!pginfo) + { + VERBOSE(VB_IMPORTANT, "No valid program info for supplied chanid/starttime"); + return; + } + + m_busyRecList.append(pginfo); + m_metadataFactory->Lookup(pginfo, false, false); +} + +void LookerUpper::HandleAllRecordings() +{ + QMap< QString, ProgramInfo* > recMap; + QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap(); + QMap< QString, bool > isJobRunning = ProgramInfo::QueryJobsRunning(JOB_COMMFLAG); + + ProgramList progList; + + LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, -1 ); + + for( int n = 0; n < (int)progList.size(); n++) + { + ProgramInfo *pginfo = new ProgramInfo(*(progList[n])); + if (pginfo->GetInetRef().isEmpty() || + (!pginfo->GetSubtitle().isEmpty() && + (pginfo->GetSeason() == 0) && + (pginfo->GetEpisode() == 0))) + { + QString msg = QString("Looking up: %1 %2").arg(pginfo->GetTitle()) + .arg(pginfo->GetSubtitle()); + VERBOSE(VB_IMPORTANT, msg); + + m_busyRecList.append(pginfo); + m_metadataFactory->Lookup(pginfo, false, false); + } + } +} + +void LookerUpper::customEvent(QEvent *levent) +{ + if (levent->type() == MetadataFactoryMultiResult::kEventType) + { + VERBOSE(VB_IMPORTANT, "Got a multiresult."); + // We shouldn't get any of these. If we do, metadataFactory->Lookup + // was called with the wrong arguments. + } + else if (levent->type() == MetadataFactorySingleResult::kEventType) + { + MetadataFactorySingleResult *mfsr = dynamic_cast(levent); + + if (!mfsr) + return; + + MetadataLookup *lookup = mfsr->result; + + if (!lookup) + return; + + ProgramInfo *pginfo = qVariantValue(lookup->GetData()); + + // This null check could hang us as this pginfo would then never be removed + if (!pginfo) + return; + + VERBOSE(VB_GENERAL|VB_EXTRA, QString("I found the following data:")); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" Input Title: %1").arg(pginfo->GetTitle())); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" Input Sub: %1").arg(pginfo->GetSubtitle())); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" Title: %1").arg(lookup->GetTitle())); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" Subtitle: %1").arg(lookup->GetSubtitle())); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" Season: %1").arg(lookup->GetSeason())); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" Episode: %1").arg(lookup->GetEpisode())); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" Inetref: %1").arg(lookup->GetInetref())); + VERBOSE(VB_GENERAL|VB_EXTRA, QString(" User Rating: %1").arg(lookup->GetUserRating())); + + pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode()); + pginfo->SaveInetRef(lookup->GetInetref()); + + m_busyRecList.removeAll(pginfo); + } + else if (levent->type() == MetadataFactoryNoResult::kEventType) + { + MetadataFactoryNoResult *mfnr = dynamic_cast(levent); + + if (!mfnr) + return; + + MetadataLookup *lookup = mfnr->result; + + if (!lookup) + return; + + ProgramInfo *pginfo = qVariantValue(lookup->GetData()); + + // This null check could hang us as this pginfo would then never be removed + if (!pginfo) + return; + + m_busyRecList.removeAll(pginfo); + } +} diff --git a/mythtv/programs/mythmetadatalookup/lookup.h b/mythtv/programs/mythmetadatalookup/lookup.h new file mode 100644 index 00000000000..3687a67654b --- /dev/null +++ b/mythtv/programs/mythmetadatalookup/lookup.h @@ -0,0 +1,30 @@ +#ifndef LOOKUP_H_ +#define LOOKUP_H_ + +#include +#include + +#include "programinfo.h" +#include "metadatafactory.h" + +class LookerUpper : public QObject +{ + public: + LookerUpper(); + ~LookerUpper(); + + bool StillWorking(); + + void HandleSingleRecording(const uint chanid, + const QDateTime starttime); + void HandleAllRecordings(); + + private: + void customEvent(QEvent *event); + + MetadataFactory *m_metadataFactory; + + QList m_busyRecList; +}; + +#endif //LOOKUP_H_ diff --git a/mythtv/programs/mythmetadatalookup/main.cpp b/mythtv/programs/mythmetadatalookup/main.cpp new file mode 100644 index 00000000000..c7d2f3c40a5 --- /dev/null +++ b/mythtv/programs/mythmetadatalookup/main.cpp @@ -0,0 +1,137 @@ +// C headers +#include + +// C++ headers +#include +using namespace std; + +// Qt headers +#include +#include + +// libmyth headers +#include "exitcodes.h" +#include "mythcontext.h" +#include "mythdb.h" +#include "mythversion.h" +#include "util.h" +#include "mythtranslation.h" +#include "mythconfig.h" +#include "mythcommandlineparser.h" +#include "mythlogging.h" + +#include "lookup.h" + +class MPUBLIC MythMetadataLookupCommandLineParser : public MythCommandLineParser +{ + public: + MythMetadataLookupCommandLineParser(); + void LoadArguments(void); +}; + +MythMetadataLookupCommandLineParser::MythMetadataLookupCommandLineParser() : + MythCommandLineParser("mythmetadatalookup") +{ LoadArguments(); } + +void MythMetadataLookupCommandLineParser::LoadArguments(void) +{ + addHelp(); + addVersion(); + addVerbose(); + addRecording(); + addLogging(); + + add("--refresh-all", "refresh-all", false, + "Refresh all recorded programs and recording rules metadata", ""); +} + +int main(int argc, char *argv[]) +{ + MythMetadataLookupCommandLineParser cmdline; + if (!cmdline.Parse(argc, argv)) + { + cmdline.PrintHelp(); + return GENERIC_EXIT_INVALID_CMDLINE; + } + + if (cmdline.toBool("showhelp")) + { + cmdline.PrintHelp(); + return GENERIC_EXIT_OK; + } + + if (cmdline.toBool("showversion")) + { + cmdline.PrintVersion(); + return GENERIC_EXIT_OK; + } + + QCoreApplication a(argc, argv); + QCoreApplication::setApplicationName("mythmetadatalookup"); + + int retval; + if ((retval = cmdline.ConfigureLogging()) != GENERIC_EXIT_OK) + return retval; + + /////////////////////////////////////////////////////////////////////// + // Don't listen to console input + close(0); + + gContext = new MythContext(MYTH_BINARY_VERSION); + if (!gContext->Init(false)) + { + VERBOSE(VB_IMPORTANT, "Failed to init MythContext, exiting."); + delete gContext; + return GENERIC_EXIT_NO_MYTHCONTEXT; + } + + bool refreshall = cmdline.toBool("refresh-all"); + bool usedchanid = cmdline.toBool("chanid"); + bool usedstarttime = cmdline.toBool("starttime"); + + uint chanid = cmdline.toUInt("chanid"); + QString startstring = cmdline.toString("starttime"); + QDateTime starttime = myth_dt_from_string(startstring); + + if (refreshall && (usedchanid || usedstarttime)) + { + VERBOSE(VB_IMPORTANT, "--refresh-all must not be accompanied by " + "--chanid or --starttime"); + return GENERIC_EXIT_INVALID_CMDLINE; + } + + if (!refreshall && !(usedchanid && usedstarttime)) + { + VERBOSE(VB_IMPORTANT, "--chanid and --starttime must be used together."); + return GENERIC_EXIT_INVALID_CMDLINE; + } + + if (!refreshall && !usedchanid && !usedstarttime) + { + refreshall = true; + } + + myth_nice(19); + + MythTranslation::load("mythfrontend"); + + LookerUpper *lookup = new LookerUpper(); + + if (refreshall) + lookup->HandleAllRecordings(); + else + lookup->HandleSingleRecording(chanid, starttime); + + while (lookup->StillWorking()) + { + sleep(1); + qApp->processEvents(); + } + + delete lookup; + delete gContext; + + VERBOSE(VB_IMPORTANT, "MythMetadataLookup run complete."); + + return GENERIC_EXIT_OK; +} diff --git a/mythtv/programs/mythmetadatalookup/mythmetadatalookup.pro b/mythtv/programs/mythmetadatalookup/mythmetadatalookup.pro new file mode 100644 index 00000000000..90f01a0e49a --- /dev/null +++ b/mythtv/programs/mythmetadatalookup/mythmetadatalookup.pro @@ -0,0 +1,19 @@ +include ( ../../settings.pro ) +include ( ../../version.pro ) +include ( ../programs-libs.pro ) + +QT += network xml sql + +TEMPLATE = app +CONFIG += thread +CONFIG -= moc +TARGET = mythmetadatalookup +target.path = $${PREFIX}/bin +INSTALLS = target + +QMAKE_CLEAN += $(TARGET) + +# Input +HEADERS += lookup.h +SOURCES += main.cpp lookup.cpp + diff --git a/mythtv/programs/programs.pro b/mythtv/programs/programs.pro index 16df7c21dbd..05fd633a85d 100644 --- a/mythtv/programs/programs.pro +++ b/mythtv/programs/programs.pro @@ -13,6 +13,7 @@ using_frontend { using_backend { SUBDIRS += mythbackend mythfilldatabase mythtv-setup scripts + SUBDIRS += mythmetadatalookup } using_mythtranscode: SUBDIRS += mythtranscode