diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 3d2456a8d..369f7633f 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -271,17 +271,6 @@ Tomahawk.Resolver = { getStreamUrl: function (params) { return params; }, - - _convertUrls: function (results) { - var that = this; - return results.map(function (r) { - if (r && r.url) { - r.url = that._urlProtocol + '://' + r.url; - } - return r; - }); - }, - _adapter_resolve: function (params) { var that = this; var collectionPromises = []; @@ -297,26 +286,12 @@ Tomahawk.Resolver = { }).then(function (collectionResults) { return RSVP.Promise.resolve(that.resolve(params)).then(function (results) { return { - 'results': that._convertUrls(results.concat(collectionResults)) + 'results': results.concat(collectionResults) }; }); }); }, - _adapter_init: function () { - this._urlProtocol = this.settings.name.replace(/[^a-zA-Z]/g, '').toLowerCase(); - Tomahawk.addCustomUrlHandler(this._urlProtocol, '_adapter_getStreamUrl', true); - Tomahawk.log('Registered custom url handler for protocol "' + this._urlProtocol + '"'); - this.init(); - }, - - _adapter_getStreamUrl: function (params) { - params.url = params.url.slice(this._urlProtocol.length + 3); - RSVP.Promise.resolve(this.getStreamUrl(params)).then(function (result) { - Tomahawk.reportStreamUrl(params.qid, result.url, result.headers); - }); - }, - _adapter_search: function (params) { var that = this; var collectionPromises = []; @@ -331,7 +306,7 @@ Tomahawk.Resolver = { }).then(function (collectionResults) { return RSVP.Promise.resolve(that.search(params)).then(function (results) { return { - 'results': that._convertUrls(results.concat(collectionResults)) + 'results': results.concat(collectionResults) }; }); }); @@ -1713,7 +1688,7 @@ Tomahawk.Collection = { ); return t.execDeferredStatements(); }).then(function (results) { - return {results: Tomahawk.resolver.instance._convertUrls(results[0])}; + return {results: results[0]}; }); }, @@ -1887,6 +1862,14 @@ Tomahawk.Collection = { Tomahawk.Collection.BrowseCapability.Albums, Tomahawk.Collection.BrowseCapability.Tracks]; return this.settings; + }, + + getStreamUrl: function(params) { + if(this.resolver) { + return this.resolver.getStreamUrl(params); + } + + return params; } }; @@ -1910,14 +1893,6 @@ Tomahawk.addTrackResults = function (result) { delete Tomahawk.PluginManager.resolve[result.qid]; }; -Tomahawk.reportStreamUrl = function (qid, streamUrl, headers) { - Tomahawk.PluginManager.resolve[qid]({ - url: streamUrl, - headers: headers - }); - delete Tomahawk.PluginManager.resolve[qid]; -}; - Tomahawk.addUrlResult = function (url, result) { /* Merge the whole mess into one consistent result which is independent of type var cleanResult = { diff --git a/src/libtomahawk/ResultProvider.cpp b/src/libtomahawk/ResultProvider.cpp index 77ae2b046..5aa85623f 100644 --- a/src/libtomahawk/ResultProvider.cpp +++ b/src/libtomahawk/ResultProvider.cpp @@ -30,11 +30,8 @@ ResultProvider::~ResultProvider() ScriptJob* ResultProvider::getStreamUrl( const result_ptr& result ) { - QUrl url = result->url(); - QVariantMap data; - data[ "result" ] = QVariant::fromValue( result ); - data[ "url" ] = url; + data[ "url" ] = result->url(); return new SyncScriptJob( data ); } diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index a0bcd2006..f5c2a92c8 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -32,6 +32,8 @@ #include "playlist/SingleTrackPlaylistInterface.h" #include "utils/Closure.h" #include "utils/Logger.h" +#include "utils/NetworkReply.h" +#include "utils/NetworkAccessManager.h" #include "Album.h" #include "Artist.h" @@ -577,27 +579,62 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) ScriptJob* job = result->resolvedBy()->getStreamUrl( result ); connect( job, SIGNAL( done( QVariantMap ) ), SLOT( gotStreamUrl( QVariantMap ) ) ); + job->setProperty( "result", QVariant::fromValue( result ) ); job->start(); } void AudioEngine::gotStreamUrl( const QVariantMap& data ) { - QString url = data[ "url" ].toString(); - result_ptr result = data[ "result" ].value(); + QString streamUrl = data[ "url" ].toString(); + QVariantMap headers = data[ "headers" ].toMap(); + Tomahawk::result_ptr result = sender()->property( "result" ).value(); - if ( !TomahawkUtils::isLocalResult( url ) && !TomahawkUtils::isHttpResult( url ) - && !TomahawkUtils::isRtmpResult( url ) ) + if ( streamUrl.isEmpty() || !( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) || TomahawkUtils::isRtmpResult( streamUrl ) ) ) { - performLoadIODevice( result, url ); + // Not an http(s) or RTMP URL, get IO device + QSharedPointer< QIODevice > sp; + performLoadIODevice( result, streamUrl ); } else { - QSharedPointer< QIODevice > io; - performLoadTrack( result, result->url(), io ); + // TODO: just make this part of the http(s) IoDeviceFactory (?) + QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() ); + QNetworkRequest req( url ); + + QMap parsedHeaders; + foreach ( const QString& key, headers.keys() ) + { + Q_ASSERT_X( headers[key].canConvert( QVariant::String ), Q_FUNC_INFO, "Expected a Map of string for additional headers" ); + if ( headers[key].canConvert( QVariant::String ) ) + { + parsedHeaders.insert( key, headers[key].toString() ); + } + } + + foreach ( const QString& key, parsedHeaders.keys() ) + { + req.setRawHeader( key.toLatin1(), parsedHeaders[key].toLatin1() ); + } + + tDebug() << "Creating a QNetworkReply with url:" << req.url().toString(); + NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) ); + NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotRedirectedStreamUrl( Tomahawk::result_ptr, NetworkReply* )), result, reply ); } } +void +AudioEngine::gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply ) +{ + // std::functions cannot accept temporaries as parameters + QSharedPointer< QIODevice > sp ( reply->reply(), &QObject::deleteLater ); + QString url = reply->reply()->url().toString(); + reply->disconnectFromReply(); + reply->deleteLater(); + + performLoadTrack( result, url, sp ); +} + void AudioEngine::onPositionChanged( float new_position ) diff --git a/src/libtomahawk/audio/AudioEngine.h b/src/libtomahawk/audio/AudioEngine.h index 81e29b086..674b50099 100644 --- a/src/libtomahawk/audio/AudioEngine.h +++ b/src/libtomahawk/audio/AudioEngine.h @@ -28,6 +28,7 @@ #include "DllMacro.h" +class NetworkReply; class AudioEnginePrivate; class DLLEXPORT AudioEngine : public QObject @@ -181,6 +182,9 @@ signals: private slots: void loadTrack( const Tomahawk::result_ptr& result ); //async! void gotStreamUrl( const QVariantMap& data ); + void gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply ); + + void performLoadIODevice( const Tomahawk::result_ptr& result, const QString& url ); //only call from loadTrack kthxbi void performLoadTrack( const Tomahawk::result_ptr result, const QString url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi void loadPreviousTrack(); diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index e2ec13b20..3f48a19ee 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -538,12 +538,6 @@ JSResolver::error() const void JSResolver::resolve( const Tomahawk::query_ptr& query ) { - if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) ); - return; - } - ScriptJob* job = nullptr; if ( !query->isFullTextQuery() ) { @@ -746,3 +740,13 @@ JSResolver::instanceUUID() { return Tomahawk::Database::instance()->impl()->dbid(); } + + +ScriptJob* +JSResolver::getStreamUrl( const result_ptr& result ) +{ + QVariantMap arguments; + arguments["url"] = result->url(); + + return scriptObject()->invoke( "getStreamUrl", arguments ); +} diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index da3c51cb8..4fc063fd7 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -75,6 +75,8 @@ public: ScriptAccount* scriptAccount() const; + ScriptJob* getStreamUrl( const result_ptr& result ) override; + public slots: void resolve( const Tomahawk::query_ptr& query ) override; void stop() override; diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index 1a67d46d4..8bc7bbd89 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -72,7 +72,6 @@ JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* paren : QObject( parent ) , m_resolver( parent ) , m_scriptPath( scriptPath ) - , m_urlCallbackIsAsync( false ) { } @@ -480,31 +479,6 @@ JSResolverHelper::accountId() } -void -JSResolverHelper::addCustomUrlHandler( const QString& protocol, - const QString& callbackFuncName, - const QString& isAsynchronous ) -{ - m_urlCallbackIsAsync = ( isAsynchronous.toLower() == "true" ); - - std::function< void( const Tomahawk::result_ptr&, const QString&, - std::function< void( const QString&, QSharedPointer< QIODevice >& ) > )> fac = - std::bind( &JSResolverHelper::customIODeviceFactory, this, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3 ); - Tomahawk::UrlHandler::registerIODeviceFactory( protocol, fac ); - - m_urlCallback = callbackFuncName; -} - - -void -JSResolverHelper::reportStreamUrl( const QString& qid, const QString& streamUrl ) -{ - reportStreamUrl( qid, streamUrl, QVariantMap() ); -} - - void JSResolverHelper::nativeAssert( bool assertion, const QString& message ) { if ( !assertion ) @@ -515,61 +489,6 @@ void JSResolverHelper::nativeAssert( bool assertion, const QString& message ) } -void -JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr&, const QString& url, - std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback ) -{ - //can be sync or async - if ( m_urlCallbackIsAsync ) - { - QString qid = uuid(); - QString getUrl = QString( - "if(Tomahawk.resolver.instance['_adapter_%1']) {" - " Tomahawk.resolver.instance._adapter_%1( {qid: '%2', url: '%3'} );" - "} else {" - " Tomahawk.resolver.instance.%1( {qid: '%2', url: '%3'} );" - "}" - ).arg( m_urlCallback ) - .arg( qid ) - .arg( url ); - - m_streamCallbacks.insert( qid, callback ); - m_resolver->d_func()->scriptAccount->evaluateJavaScript( getUrl ); - } - else - { - QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback ) - .arg( url ); - - QString urlStr = m_resolver->d_func()->scriptAccount->evaluateJavaScriptWithResult( getUrl ).toString(); - - returnStreamUrl( urlStr, QMap(), callback ); - } -} - - -void -JSResolverHelper::reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers ) -{ - if ( !m_streamCallbacks.contains( qid ) ) - return; - - std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback = m_streamCallbacks.take( qid ); - - QMap parsedHeaders; - foreach ( const QString& key, headers.keys() ) - { - Q_ASSERT_X( headers[key].canConvert( QVariant::String ), Q_FUNC_INFO, "Expected a Map of string for additional headers" ); - if ( headers[key].canConvert( QVariant::String ) ) - { - parsedHeaders.insert( key, headers[key].toString() ); - } - } - - returnStreamUrl( streamUrl, parsedHeaders, callback ); -} - - void JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url, const QString& mime_type, int sizehint, @@ -955,43 +874,3 @@ JSResolverHelper::readdResolver() Pipeline::instance()->addResolver( m_resolver ); } - -void -JSResolverHelper::returnStreamUrl( const QString& streamUrl, const QMap& headers, - std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback ) -{ - if ( streamUrl.isEmpty() || !( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) ) ) - { - // Not an https? URL, so let Phonon handle it - QSharedPointer< QIODevice > sp; - callback( streamUrl, sp ); - } - else - { - QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() ); - QNetworkRequest req( url ); - foreach ( const QString& key, headers.keys() ) - { - req.setRawHeader( key.toLatin1(), headers[key].toLatin1() ); - } - tDebug() << "Creating a QNetworkReply with url:" << req.url().toString(); - NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) ); - - NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotStreamUrl( IODeviceCallback, NetworkReply* )), callback, reply ); - } -} - - -Q_DECLARE_METATYPE( IODeviceCallback ) - -void -JSResolverHelper::gotStreamUrl( std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback, NetworkReply* reply ) -{ - // std::functions cannot accept temporaries as parameters - QSharedPointer< QIODevice > sp ( reply->reply(), &QObject::deleteLater ); - QString url = reply->reply()->url().toString(); - reply->disconnectFromReply(); - reply->deleteLater(); - - callback( url, sp ); -} diff --git a/src/libtomahawk/resolvers/JSResolverHelper.h b/src/libtomahawk/resolvers/JSResolverHelper.h index 08fedb09f..57b963e39 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.h +++ b/src/libtomahawk/resolvers/JSResolverHelper.h @@ -61,9 +61,6 @@ public: */ Q_INVOKABLE QString accountId(); - Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName, const QString& isAsynchronous = "false" ); - Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl ); - Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers ); /** * Make Tomahawk assert the assertion is true, probably not to be used by resolvers directly @@ -122,13 +119,6 @@ public: Q_INVOKABLE void readdResolver(); - /** - * INTERNAL USE ONLY! - */ - void customIODeviceFactory( const Tomahawk::result_ptr&, const QString& url, - std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback ); // async - - public slots: QByteArray readRaw( const QString& fileName ); QString readBase64( const QString& fileName ); @@ -149,22 +139,15 @@ public slots: void unregisterScriptPlugin( const QString& type, const QString& objectId ); private slots: - void gotStreamUrl( IODeviceCallback callback, NetworkReply* reply ); void nativeAsyncRequestDone( int requestId, NetworkReply* reply ); private: - void returnStreamUrl( const QString& streamUrl, const QMap& headers, - std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback ); - bool indexDataFromVariant( const QVariantMap& map, struct Tomahawk::IndexData& indexData ); QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query ); QVariantMap m_resolverConfig; JSResolver* m_resolver; - QString m_scriptPath, m_urlCallback, m_urlTranslator; - QHash< QString, std::function< void( const QString&, QSharedPointer< QIODevice >& ) > > m_streamCallbacks; - QHash< QString, std::function< void( const QString& ) > > m_translatorCallbacks; - bool m_urlCallbackIsAsync; + QString m_scriptPath; }; } // ns: Tomahawk diff --git a/src/libtomahawk/resolvers/ScriptCollection.cpp b/src/libtomahawk/resolvers/ScriptCollection.cpp index 57a89bd12..b6c8d07aa 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.cpp +++ b/src/libtomahawk/resolvers/ScriptCollection.cpp @@ -27,6 +27,7 @@ #include "resolvers/ScriptCommand_AllAlbums.h" #include "resolvers/ScriptCommand_AllTracks.h" #include "ScriptAccount.h" +#include "Result.h" #include #include @@ -223,6 +224,15 @@ void ScriptCollection::parseMetaData() return parseMetaData( readMetaData() ); } +ScriptJob* +ScriptCollection::getStreamUrl( const result_ptr& result ) +{ + QVariantMap arguments; + arguments["url"] = result->url(); + + return scriptObject()->invoke( "getStreamUrl", arguments ); +} + void ScriptCollection::parseMetaData( const QVariantMap& metadata ) diff --git a/src/libtomahawk/resolvers/ScriptCollection.h b/src/libtomahawk/resolvers/ScriptCollection.h index 670a5d464..6d3d3944c 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.h +++ b/src/libtomahawk/resolvers/ScriptCollection.h @@ -91,6 +91,8 @@ public: void parseMetaData(); void parseMetaData( const QVariantMap& metadata ); + ScriptJob* getStreamUrl( const result_ptr& result ) override; + private slots: void onIconFetched();