Skip to content

Commit

Permalink
Add a Metadata Lookup API Service.
Browse files Browse the repository at this point in the history
This adds a synchronous metadata lookup method, a new data contract and method to the video API, and some other random tweaks to make it work.

I am not completely certain that the way I've crammed the asynchronous lookups into a synchronous wrapper is the best way to handle it, but until there's some way to handle Qt Events and asynchronous code in the API, I can't see another way to do it.

This API is pretty technical, and is not for the faint of heart.  You really, really ought to be a developer to wrap it, it has little utility for the average user.  It will, however, be useful for anyone who wants to add lookups to MythWeb's scheduling or similar.

In extreme short, here are some uses:

**** How to Query ****

Arguments:

* Title  -  The title of the item being looked up.
* Subtitle  -  The subtitle of the item being looked up.
* Inetref  -  The inetref of the item being looked up.
* Season  -  The Season Number of the item being looked up.
* Episode  - The Episode Number of the item being looked up.
* GrabberType  -  A way of forcing a particular grabber to be used.  Valid arguments are "tv" and "movie."

Most people should *not* specify a GrabberType, but should instead fill in as much of the other data as they can.  However, since the decision making process *can* be imperfect, the application developer may wish to offer manual triggers for one or the other grabber to see what results are available.

Examples of how grabber detection logic would work:

==

BackendIP:6544/Video/LookupVideo?Title=Star Wars

Will always choose the movie grabber.  There's just not enough data here to do better.  However:

==

BackendIP:6544/Video/LookupVideo?Title=Star Wars&GrabberType=tv

Will force a lookup using the configured TV grabber.

==

BackendIP:6544/Video/LookupVideo?Title=Friends&Subtitle=The One With Rachel's Book

Will choose the TV grabber and attempt a lookup by title and subtitle.

==

BackendIP:6544/Video/LookupVideo?Inetref=73739

Will choose the Movie grabber and look up by Inetref, returning a single result, if any.

==

BackendIP:6544/Video/LookupVideo?InetRef=73739&Season=6&Episode=17

Will choose the Television grabber and look up by Inetref, Season, and Episode, returning a single result, if any.

==

BackendIP:6544/Video/LookupVideo?Title=Lost&Subtitle=The End&Inetref=73739&Season=6&Episode=18

This is a tricky one.  The metadata classes will attempt to use the most specific information when it's available.  This lookup will use Inetref, season, and episode with the TV grabber.

**** Interpreting Results ****

The result is a VideoLookupInfoList.  This contains information about the number of results (Count), and a <VideoLookupInfos>, which contains one or more <VideoInfo>, each of which is a result to your query.  It presently contains most of the textual metadata people care about (but not all the data returned by our grabbers).  I'll flesh this out later.

A basic workflow should work like this:

1) Query using all available information.
2) Result is returned.
   2a) If no results, stop.  Lookup failed or no match.
   2b) If one result, you are done.  Parse result.
   2c) If greater than one result, select the inetref you want from the resulting list, and perform a new query with inetref added to your data.
3) Query should now return one result.  Parse result.

You really want to perform the second query with the inetref after a multi-result.  Multiple results do not necessarily return all metadata.  For example, a search for a movie with multiple results will not return tagline and some other info you may wish to display or use.  Always perform searches, adding in new data as it is selected, until only a single result is returned.  This will be a full metadata record.

Currently the lookup API only implements the following returns:

Title
Subtitle
Season
Episode
Year
Tagline
Certification (Rating)
Inetref
Homepage
Release Date
User Rating
Length

I'll add further stuff (artwork, etc) from the metadata classes in the future, but this allows one to get started if they wanted to work on adding lookups to MythWeb.  I will also add a much easier lookup method that will work better for recordings that instead takes chanid and starttime as arguments.  I should be able to do this in the (very) near future.

The Metadata Lookup API will likely see substantial expansion in the future, and this is just a first run at it.  The basic logic will remain the same, though, so it's probably safe to start working with.
  • Loading branch information
Robert McNamara committed Jul 21, 2011
1 parent 8efd5e5 commit a0c8225
Show file tree
Hide file tree
Showing 10 changed files with 428 additions and 10 deletions.
2 changes: 1 addition & 1 deletion mythtv/libs/libmythbase/mythversion.h
Expand Up @@ -12,7 +12,7 @@
/// Update this whenever the plug-in API changes.
/// Including changes in the libmythbase, libmyth, libmythtv, libmythav* and
/// libmythui class methods used by plug-ins.
#define MYTH_BINARY_VERSION "0.25.20110720-2"
#define MYTH_BINARY_VERSION "0.25.20110720-3"

/** \brief Increment this whenever the MythTV network protocol changes.
*
Expand Down
22 changes: 18 additions & 4 deletions mythtv/libs/libmythmetadata/metadatadownload.cpp
Expand Up @@ -405,8 +405,14 @@ MetadataLookupList MetadataDownload::handleMovie(MetadataLookup* lookup)
{
MetadataLookupList list;

QString mxml = getMXMLPath(lookup->GetFilename());
QString nfo = getNFOPath(lookup->GetFilename());
QString mxml;
QString nfo;

if (!lookup->GetFilename().isEmpty())
{
mxml = getMXMLPath(lookup->GetFilename());
nfo = getNFOPath(lookup->GetFilename());
}

if (mxml.isEmpty() && nfo.isEmpty())
{
Expand Down Expand Up @@ -472,8 +478,16 @@ MetadataLookupList MetadataDownload::handleTelevision(MetadataLookup* lookup)
if (lookup->GetStep() == kLookupSearch)
{
args.append(QString("-M"));
QString title = lookup->GetTitle();
args.append(title);
if (lookup->GetInetref().isEmpty())
{
QString title = lookup->GetTitle();
args.append(title);
}
else
{
QString inetref = lookup->GetInetref();
args.append(inetref);
}
}
else if (lookup->GetStep() == kLookupData)
{
Expand Down
81 changes: 79 additions & 2 deletions mythtv/libs/libmythmetadata/metadatafactory.cpp
Expand Up @@ -34,7 +34,8 @@ QEvent::Type MetadataFactoryVideoChanges::kEventType =
(QEvent::Type) QEvent::registerEventType();

MetadataFactory::MetadataFactory(QObject *parent) :
m_parent(parent), m_scanning(false)
m_parent(parent), m_scanning(false),
m_returnList(), m_sync(false)
{
m_lookupthread = new MetadataDownload(this);
m_imagedownload = new MetadataImageDownload(this);
Expand Down Expand Up @@ -164,6 +165,59 @@ void MetadataFactory::Lookup(MetadataLookup *lookup)
m_lookupthread->addLookup(lookup);
}

MetadataLookupList MetadataFactory::SynchronousLookup(QString title,
QString subtitle,
QString inetref,
int season,
int episode,
QString grabber)
{
MetadataLookup *lookup = new MetadataLookup();
lookup->SetStep(kLookupSearch);
lookup->SetType(kMetadataRecording);
lookup->SetAutomatic(false);
lookup->SetHandleImages(false);
lookup->SetTitle(title);
lookup->SetSubtitle(subtitle);
lookup->SetSeason(season);
lookup->SetEpisode(episode);
lookup->SetInetref(inetref);
if (grabber.toLower() == "movie")
lookup->SetSubtype(kProbableMovie);
else if (grabber.toLower() == "tv" ||
grabber.toLower() == "television")
lookup->SetSubtype(kProbableTelevision);
else
lookup->SetSubtype(GuessLookupType(lookup));

return SynchronousLookup(lookup);
}

MetadataLookupList MetadataFactory::SynchronousLookup(MetadataLookup *lookup)
{
if (!lookup)
return MetadataLookupList();

m_sync = true;

if (m_lookupthread->isRunning())
m_lookupthread->prependLookup(lookup);
else
m_lookupthread->addLookup(lookup);

while (m_returnList.isEmpty() && m_sync)
{
sleep(1);
qApp->processEvents();
}

m_sync = false;

delete lookup;

return m_returnList;
}

void MetadataFactory::VideoScan()
{
if (IsRunning())
Expand Down Expand Up @@ -424,7 +478,11 @@ void MetadataFactory::customEvent(QEvent *levent)
if (lul.isEmpty())
return;

if (lul.count() == 1)
if (m_sync)
{
m_returnList = lul;
}
else if (lul.count() == 1)
{
OnSingleResult(lul.takeFirst());
}
Expand All @@ -442,6 +500,11 @@ void MetadataFactory::customEvent(QEvent *levent)
if (lul.isEmpty())
return;

if (m_sync)
{
m_returnList = MetadataLookupList();
m_sync = false;
}
if (lul.size())
{
OnNoResult(lul.takeFirst());
Expand Down Expand Up @@ -555,6 +618,20 @@ LookupType GuessLookupType(ProgramInfo *pginfo)
return ret;
}

LookupType GuessLookupType(MetadataLookup *lookup)
{
LookupType ret = kUnknownVideo;

if (lookup->GetSeason() > 0 || lookup->GetEpisode() > 0 ||
!lookup->GetSubtitle().isEmpty())
ret = kProbableTelevision;
else
ret = kProbableMovie;

return ret;
}


LookupType GuessLookupType(RecordingRule *recrule)
{
LookupType ret = kUnknownVideo;
Expand Down
13 changes: 13 additions & 0 deletions mythtv/libs/libmythmetadata/metadatafactory.h
Expand Up @@ -83,6 +83,14 @@ class META_PUBLIC MetadataFactory : public QObject
bool getimages = true);
void Lookup(MetadataLookup *lookup);

MetadataLookupList SynchronousLookup(QString title,
QString subtitle,
QString inetref,
int season,
int episode,
QString grabber);
MetadataLookupList SynchronousLookup(MetadataLookup *lookup);

void VideoScan();

bool IsRunning() { return m_lookupthread->isRunning() ||
Expand All @@ -107,9 +115,14 @@ class META_PUBLIC MetadataFactory : public QObject
VideoScannerThread *m_videoscanner;
VideoMetadataListManager *m_mlm;
bool m_scanning;

// Variables used in synchronous mode
MetadataLookupList m_returnList;
bool m_sync;
};

META_PUBLIC LookupType GuessLookupType(ProgramInfo *pginfo);
META_PUBLIC LookupType GuessLookupType(MetadataLookup *lookup);
META_PUBLIC LookupType GuessLookupType(RecordingRule *recrule);

#endif
112 changes: 112 additions & 0 deletions mythtv/libs/libmythservicecontracts/datacontracts/videoLookupInfo.h
@@ -0,0 +1,112 @@
//////////////////////////////////////////////////////////////////////////////
// Program Name: videoLookupInfo.h
// Created : Jul. 19, 2011
//
// Copyright (c) 2011 Robert McNamara <rmcnamara@mythtv.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or at your option any later version of the LGPL.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library. If not, see <http://www.gnu.org/licenses/>.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef VIDEOLOOKUPINFO_H_
#define VIDEOLOOKUPINFO_H_

#include <QString>
#include <QDateTime>

#include "serviceexp.h"
#include "datacontracthelper.h"

namespace DTC
{

/////////////////////////////////////////////////////////////////////////////

class SERVICE_PUBLIC VideoLookupInfo : public QObject
{
Q_OBJECT
Q_CLASSINFO( "version" , "1.0" );

Q_PROPERTY( QString Title READ Title WRITE setTitle )
Q_PROPERTY( QString SubTitle READ SubTitle WRITE setSubTitle )
Q_PROPERTY( int Season READ Season WRITE setSeason )
Q_PROPERTY( int Episode READ Episode WRITE setEpisode )
Q_PROPERTY( int Year READ Year WRITE setYear )
Q_PROPERTY( QString Tagline READ Tagline WRITE setTagline )
Q_PROPERTY( QString Description READ Description WRITE setDescription )
Q_PROPERTY( QString Certification READ Certification WRITE setCertification )
Q_PROPERTY( QString InetRef READ InetRef WRITE setInetRef )
Q_PROPERTY( QString HomePage READ HomePage WRITE setHomePage )
Q_PROPERTY( QDateTime ReleaseDate READ ReleaseDate WRITE setReleaseDate )
Q_PROPERTY( float UserRating READ UserRating WRITE setUserRating )
Q_PROPERTY( int Length READ Length WRITE setLength )

PROPERTYIMP ( QString , Title )
PROPERTYIMP ( QString , SubTitle )
PROPERTYIMP ( int , Season )
PROPERTYIMP ( int , Episode )
PROPERTYIMP ( int , Year )
PROPERTYIMP ( QString , Tagline )
PROPERTYIMP ( QString , Description )
PROPERTYIMP ( QString , Certification )
PROPERTYIMP ( QString , InetRef )
PROPERTYIMP ( QString , HomePage )
PROPERTYIMP ( QDateTime , ReleaseDate )
PROPERTYIMP ( float , UserRating )
PROPERTYIMP ( int , Length )

public:

static void InitializeCustomTypes()
{
qRegisterMetaType< VideoLookupInfo >();
qRegisterMetaType< VideoLookupInfo* >();
}

public:

VideoLookupInfo(QObject *parent = 0)
: QObject ( parent )
{
}

VideoLookupInfo( const VideoLookupInfo &src )
{
Copy( src );
}

void Copy( const VideoLookupInfo &src )
{
m_Title = src.m_Title ;
m_SubTitle = src.m_SubTitle ;
m_Season = src.m_Season ;
m_Episode = src.m_Episode ;
m_Year = src.m_Year ;
m_Tagline = src.m_Tagline ;
m_Description = src.m_Description ;
m_Certification = src.m_Certification ;
m_InetRef = src.m_InetRef ;
m_HomePage = src.m_HomePage ;
m_ReleaseDate = src.m_ReleaseDate ;
m_UserRating = src.m_UserRating ;
m_Length = src.m_Length ;
}
};

} // namespace DTC

Q_DECLARE_METATYPE( DTC::VideoLookupInfo )
Q_DECLARE_METATYPE( DTC::VideoLookupInfo* )

#endif

0 comments on commit a0c8225

Please sign in to comment.