1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-04 13:17:34 +02:00

Add basic metadata scanning for JS Resolvers

This commit is contained in:
Uwe L. Korn
2014-06-10 16:01:49 +01:00
parent 50370f7b53
commit fff437d7a3
3 changed files with 216 additions and 1 deletions

View File

@@ -4,7 +4,7 @@
* Copyright 2011-2012, Leo Franchi <lfranchi@kde.org> * Copyright 2011-2012, Leo Franchi <lfranchi@kde.org>
* Copyright 2011, Thierry Goeckel * Copyright 2011, Thierry Goeckel
* Copyright 2013, Teo Mrnjavac <teo@kde.org> * Copyright 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com> * Copyright 2013-2014, Uwe L. Korn <uwelk@xhochy.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -277,6 +277,59 @@ Tomahawk.syncRequest = function (url, extraHeaders, options) {
} }
}; };
/**
* Internal counter used to identify retrievedMetadata call back from native
* code.
*/
Tomahawk.retrieveMetadataIdCounter = 0;
/**
* Internal map used to map metadataIds to the respective JavaScript callbacks.
*/
Tomahawk.retrieveMetadataCallbacks = {};
/**
* Retrieve metadata for a media stream.
*
* @param url String The URL which should be scanned for metadata.
* @param mimetype String The mimetype of the stream, e.g. application/ogg
* @param sizehint Size in bytes if not supplied possibly the whole file needs
* to be downloaded
* @param options Object Map to specify various parameters related to the media
* URL. This includes:
* * headers: Object of HTTP(S) headers that should be set on doing the
* request.
* * method: String HTTP verb to be used (default: GET)
* * username: Username when using authentication
* * password: Password when using authentication
* @param callback Function(Object,String) This function is called on completeion.
* If an error occured, error is set to the corresponding message else
* null.
*/
Tomahawk.retrieveMetadata = function (url, mimetype, sizehint, options, callback) {
var metadataId = Tomahawk.retrieveMetadataIdCounter;
Tomahawk.retrieveMetadataIdCounter++;
Tomahawk.retrieveMetadataCallbacks[metadataId] = callback;
Tomahawk.nativeRetrieveMetadata(metadataId, url, mimetype, sizehint, options);
};
/**
* Pass the natively retrieved metadata back to the JavaScript callback.
*
* Internal use only!
*/
Tomahawk.retrievedMetadata = function(metadataId, metadata, error) {
// Check we have a matching callback stored.
if (!Tomahawk.retrieveMetadataCallbacks.hasOwnProperty(metadataId)) {
return;
}
// Call the real callback
Tomahawk.retrieveMetadataCallbacks[metadataId](metadata, error);
// Callback are only used once.
delete Tomahawk.retrieveMetadataCallbacks[metadataId];
};
/** /**
* Internal counter used to identify asyncRequest callback from native code. * Internal counter used to identify asyncRequest callback from native code.
*/ */

View File

@@ -28,6 +28,7 @@
#include "resolvers/ScriptEngine.h" #include "resolvers/ScriptEngine.h"
#include "network/Servent.h" #include "network/Servent.h"
#include "utils/Closure.h" #include "utils/Closure.h"
#include "utils/Cloudstream.h"
#include "utils/Json.h" #include "utils/Json.h"
#include "utils/NetworkAccessManager.h" #include "utils/NetworkAccessManager.h"
#include "utils/NetworkReply.h" #include "utils/NetworkReply.h"
@@ -45,6 +46,19 @@
#include <QFileInfo> #include <QFileInfo>
#include <QMap> #include <QMap>
#include <QWebFrame> #include <QWebFrame>
#include <taglib/asffile.h>
#include <taglib/flacfile.h>
#include <taglib/id3v2framefactory.h>
#include <taglib/mp4file.h>
#include <taglib/mpegfile.h>
#include <taglib/oggfile.h>
#include <taglib/vorbisfile.h>
#if defined(TAGLIB_MAJOR_VERSION) && defined(TAGLIB_MINOR_VERSION)
#if TAGLIB_MAJOR_VERSION >= 1 && TAGLIB_MINOR_VERSION >= 9
#include <taglib/opusfile.h>
#endif
#endif
using namespace Tomahawk; using namespace Tomahawk;
@@ -488,6 +502,137 @@ JSResolverHelper::reportStreamUrl( const QString& qid,
} }
void
JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
const QString& mime_type, int sizehint,
const QVariantMap& options )
{
if ( sizehint <= 0 )
{
QString javascript = QString( "Tomahawk.retrievedMetadata( %1, null, 'Supplied size is not (yet) supported');" )
.arg( metadataId );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
return;
}
if ( TomahawkUtils::isHttpResult( url ) || TomahawkUtils::isHttpsResult( url ) )
{
// TODO: Add heuristic if size is not defined
// TOOD: Support pushing multiple headers
CloudStream stream( url, sizehint, QString("insert headers here"),
Tomahawk::Utils::nam() );
stream.Precache();
QScopedPointer<TagLib::File> tag;
if ( mime_type == "audio/mpeg" )
{
tag.reset( new TagLib::MPEG::File( &stream,
TagLib::ID3v2::FrameFactory::instance(),
TagLib::AudioProperties::Accurate
));
}
else if ( mime_type == "audio/mp4" )
{
tag.reset( new TagLib::MP4::File( &stream,
true, TagLib::AudioProperties::Accurate
));
}
#if defined(TAGLIB_MAJOR_VERSION) && defined(TAGLIB_MINOR_VERSION)
#if TAGLIB_MAJOR_VERSION >= 1 && TAGLIB_MINOR_VERSION >= 9
else if ( mime_type == "application/opus" || mime_type == "audio/opus" )
{
tag.reset( new TagLib::Ogg::Opus::File( &stream, true,
TagLib::AudioProperties::Accurate
));
}
#endif
#endif
else if ( mime_type == "application/ogg" || mime_type == "audio/ogg" )
{
tag.reset( new TagLib::Ogg::Vorbis::File( &stream, true,
TagLib::AudioProperties::Accurate
));
}
else if ( mime_type == "application/x-flac" || mime_type == "audio/flac" ||
mime_type == "audio/x-flac" )
{
tag.reset( new TagLib::FLAC::File( &stream,
TagLib::ID3v2::FrameFactory::instance(),
true, TagLib::AudioProperties::Accurate
));
}
else if ( mime_type == "audio/x-ms-wma" )
{
tag.reset( new TagLib::ASF::File( &stream, true,
TagLib::AudioProperties::Accurate
));
}
else
{
QString javascript = QString( "Tomahawk.retrievedMetadata( %1, null, 'Unknown mime type for tagging: %2');" )
.arg( metadataId ).arg( mime_type );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
return;
}
if ( stream.num_requests() > 2)
{
// Warn if pre-caching failed.
tLog() << "Total requests for file:" << url
<< stream.num_requests() << stream.cached_bytes();
}
if ( !tag->tag() || tag->tag()->isEmpty() )
{
QString javascript = QString( "Tomahawk.retrievedMetadata( %1, null, 'Could not read tag information.');" )
.arg( metadataId );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
return;
}
QVariantMap m;
m["url"] = url;
m["track"] = QString( tag->tag()->title().toCString() ).trimmed();
m["album"] = QString( tag->tag()->album().toCString() ).trimmed();
m["artist"] = QString( tag->tag()->artist().toCString() ).trimmed();
if ( m["track"].toString().isEmpty() )
{
QString javascript = QString( "Tomahawk.retrievedMetadata( %1, null, 'Empty track returnd');" )
.arg( metadataId );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
return;
}
if ( m["artist"].toString().isEmpty() )
{
QString javascript = QString( "Tomahawk.retrievedMetadata( %1, null, 'Empty artist returnd');" )
.arg( metadataId );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
return;
}
if ( tag->audioProperties() )
{
m["bitrate"] = tag->audioProperties()->bitrate();
m["channels"] = tag->audioProperties()->channels();
m["duration"] = tag->audioProperties()->length();
m["samplerate"] = tag->audioProperties()->sampleRate();
}
QString javascript = QString( "Tomahawk.retrievedMetadata( %1, %2 );" )
.arg( metadataId )
.arg( QString::fromLatin1( TomahawkUtils::toJson( m ) ) );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
}
else
{
QString javascript = QString( "Tomahawk.retrievedMetadata( %1, null, 'Protocol not supported');" )
.arg( metadataId );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
}
}
void void
JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url, JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url,
const QVariantMap& headers, const QVariantMap& headers,

View File

@@ -49,6 +49,23 @@ public:
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl ); Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl );
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers ); Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers );
/**
* Retrieve metadata for a media stream.
*
* Current suported transport protocols are:
* * HTTP
* * HTTPS
*
* This method is asynchronous and will call
* Tomahawk.retrievedMetadata(metadataId, metadata, error)
* on completion. This method is an internal variant, JavaScript resolvers
* are advised to use Tomahawk.retrieveMetadata(url, options, callback).
*/
Q_INVOKABLE void nativeRetrieveMetadata( int metadataId, const QString& url,
const QString& mimetype,
int sizehint,
const QVariantMap& options );
/** /**
* Native handler for asynchronous HTTP requests. * Native handler for asynchronous HTTP requests.
* *