From 35d89459758cad1f1e5c6b83810e9823382f6b5b Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 13 Nov 2015 13:20:52 +0100 Subject: [PATCH 01/26] Port JSResolver::resolve() --- data/js/tomahawk.js | 39 +++++++------ src/libtomahawk/resolvers/JSResolver.cpp | 58 +++++++++++++++---- src/libtomahawk/resolvers/JSResolver.h | 3 + .../resolvers/JSResolverHelper.cpp | 19 ------ src/libtomahawk/resolvers/JSResolverHelper.h | 2 - 5 files changed, 70 insertions(+), 51 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 40fd2e57d..b88ac8cda 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -281,27 +281,23 @@ Tomahawk.Resolver = { }); }, - _adapter_resolve: function (qid, artist, album, title) { + _adapter_resolve: function (params) { var that = this; var collectionPromises = []; Tomahawk.collections.forEach(function (col) { if (col.resolve) { - collectionPromises.push(col.resolve({artist: artist, album: album, track: title})); + collectionPromises.push(col.resolve(params)); } }); - RSVP.Promise.all(collectionPromises).then(function (collectionResults) { + + return RSVP.Promise.all(collectionPromises).then(function (collectionResults) { var merged = []; return merged.concat.apply(merged, collectionResults); }).then(function (collectionResults) { - RSVP.Promise.resolve(that.resolve({ - artist: artist, - album: album, - track: title - })).then(function (results) { - Tomahawk.addTrackResults({ - 'qid': qid, + return RSVP.Promise.resolve(that.resolve(params)).then(function (results) { + return { 'results': that._convertUrls(results.concat(collectionResults)) - }); + }; }); }); }, @@ -320,23 +316,22 @@ Tomahawk.Resolver = { }); }, - _adapter_search: function (qid, query) { + _adapter_search: function (params) { var that = this; var collectionPromises = []; Tomahawk.collections.forEach(function (col) { if (col.search) { - collectionPromises.push(col.search({query: query})); + collectionPromises.push(col.search(params)); } }); - RSVP.Promise.all(collectionPromises).then(function (collectionResults) { + return RSVP.Promise.all(collectionPromises).then(function (collectionResults) { var merged = []; return merged.concat.apply(merged, collectionResults); }).then(function (collectionResults) { - RSVP.Promise.resolve(that.search({query: query})).then(function (results) { - Tomahawk.addTrackResults({ - 'qid': qid, + return RSVP.Promise.resolve(that.search(params)).then(function (results) { + return { 'results': that._convertUrls(results.concat(collectionResults)) - }); + }; }); }); }, @@ -815,7 +810,9 @@ Tomahawk.base64Encode = function (b) { return window.btoa(b); }; + Tomahawk.PluginManager = { + wrapperPrefix: '_adapter_', objects: {}, objectCounter: 0, identifyObject: function (object) { @@ -852,6 +849,12 @@ Tomahawk.PluginManager = { } } + + if (this.objects[objectId][this.wrapperPrefix + methodName]) { + methodName = this.wrapperPrefix + methodName; + } + + var pluginManager = this; if (!this.objects[objectId]) { Tomahawk.log("Object not found! objectId: " + objectId + " methodName: " + methodName); diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 816580621..b1c84379e 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -44,6 +44,7 @@ #include "Track.h" #include "ScriptInfoPlugin.h" #include "JSAccount.h" +#include "ScriptJob.h" #include #include @@ -250,8 +251,7 @@ JSResolver::init() d->scriptAccount->loadScript( filePath() ); // HACK: register resolver object - d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" ) -; + d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" ); // init resolver resolverInit(); @@ -394,23 +394,28 @@ JSResolver::resolve( const Tomahawk::query_ptr& query ) return; } - QString eval; + ScriptJob* job = nullptr; if ( !query->isFullTextQuery() ) { - eval = QString( "resolve( '%1', '%2', '%3', '%4' )" ) - .arg( JSAccount::escape( query->id() ) ) - .arg( JSAccount::escape( query->queryTrack()->artist() ) ) - .arg( JSAccount::escape( query->queryTrack()->album() ) ) - .arg( JSAccount::escape( query->queryTrack()->track() ) ); + QVariantMap arguments; + arguments["artist"] = query->queryTrack()->artist(); + arguments["album"] = query->queryTrack()->album(); + arguments["track"] = query->queryTrack()->track(); + + job = scriptObject()->invoke( "resolve", arguments ); } else { - eval = QString( "search( '%1', '%2' )" ) - .arg( JSAccount::escape( query->id() ) ) - .arg( JSAccount::escape( query->fullTextQuery() ) ); + QVariantMap arguments; + arguments["query"] = query->fullTextQuery(); + job = scriptObject()->invoke( "search", arguments ); } - QVariantMap m = callOnResolver( eval ).toMap(); + + job->setProperty( "qid", query->id() ); + connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) ); + + job->start(); } @@ -552,3 +557,32 @@ JSResolver::callOnResolver( const QString& scriptSource ) "}" ).arg( propertyName ).arg( scriptSource ) ); } + + +void +JSResolver::onResolveRequestDone( const QVariantMap& data ) +{ + Q_ASSERT( QThread::currentThread() == thread() ); + Q_D( JSResolver ); + + ScriptJob* job = qobject_cast< ScriptJob* >( sender() ); + if ( job->error() ) + { + // what do here?! + } + else + { + + QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "results" ).toList() ); + + foreach( const result_ptr& result, results ) + { + result->setResolvedByResolver( this ); + result->setFriendlySource( name() ); + } + + Tomahawk::Pipeline::instance()->reportResults( job->property( "qid" ).toString(), this, results ); + } + + sender()->deleteLater(); +} diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index 7bcd9b2fc..ab6b5a4a0 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -89,6 +89,9 @@ signals: protected: QVariant callOnResolver( const QString& scriptSource ); +private slots: + void onResolveRequestDone(const QVariantMap& data); + private: void init(); diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index 4b9ea8ec1..881cfd023 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -140,25 +140,6 @@ JSResolverHelper::log( const QString& message ) } -void -JSResolverHelper::addTrackResults( const QVariantMap& results ) -{ - Q_ASSERT( results["results"].toMap().isEmpty() ); - - QList< Tomahawk::result_ptr > tracks = m_resolver->scriptAccount()->parseResultVariantList( results.value( "results" ).toList() ); - - foreach( const result_ptr& track, tracks ) - { - track->setResolvedByResolver( m_resolver ); - track->setFriendlySource( m_resolver->name() ); - } - - QString qid = results.value("qid").toString(); - - Tomahawk::Pipeline::instance()->reportResults( qid, m_resolver, tracks ); -} - - query_ptr JSResolverHelper::parseTrack( const QVariantMap& track ) { diff --git a/src/libtomahawk/resolvers/JSResolverHelper.h b/src/libtomahawk/resolvers/JSResolverHelper.h index ddb52938a..b69c92135 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.h +++ b/src/libtomahawk/resolvers/JSResolverHelper.h @@ -142,8 +142,6 @@ public slots: void log( const QString& message ); bool fakeEnv() { return false; } - void addTrackResults( const QVariantMap& results ); - void addUrlResult( const QString& url, const QVariantMap& result ); void nativeReportCapabilities( const QVariant& capabilities ); From 846699c03c84ce43be5840b325954fe16dc21a36 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 13 Nov 2015 14:12:12 +0100 Subject: [PATCH 02/26] Don't wait for timeouts on resolver errors --- src/libtomahawk/Pipeline.cpp | 7 +++++++ src/libtomahawk/Pipeline.h | 1 + src/libtomahawk/resolvers/JSResolver.cpp | 7 +++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index ee9634a44..dfac3b2bd 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -323,6 +323,13 @@ Pipeline::resolve( QID qid, bool prioritized, bool temporaryQuery ) } +void +Pipeline::reportError( QID qid, Tomahawk::Resolver* r ) +{ + reportResults( qid, r, QList< result_ptr>() ); +} + + void Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results ) { diff --git a/src/libtomahawk/Pipeline.h b/src/libtomahawk/Pipeline.h index 83ec38756..caceafe24 100644 --- a/src/libtomahawk/Pipeline.h +++ b/src/libtomahawk/Pipeline.h @@ -54,6 +54,7 @@ public: unsigned int pendingQueryCount() const; unsigned int activeQueryCount() const; + void reportError( QID qid, Tomahawk::Resolver* r ); void reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr >& results ); void reportAlbums( QID qid, const QList< album_ptr >& albums ); void reportArtists( QID qid, const QList< artist_ptr >& artists ); diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index b1c84379e..118f34cca 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -566,9 +566,12 @@ JSResolver::onResolveRequestDone( const QVariantMap& data ) Q_D( JSResolver ); ScriptJob* job = qobject_cast< ScriptJob* >( sender() ); + + QID qid = job->property( "qid" ).toString(); + if ( job->error() ) { - // what do here?! + Tomahawk::Pipeline::instance()->reportError( qid, this ); } else { @@ -581,7 +584,7 @@ JSResolver::onResolveRequestDone( const QVariantMap& data ) result->setFriendlySource( name() ); } - Tomahawk::Pipeline::instance()->reportResults( job->property( "qid" ).toString(), this, results ); + Tomahawk::Pipeline::instance()->reportResults( qid, this, results ); } sender()->deleteLater(); From c1eadce3742ce9439044a32b87da784c95735014 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Mon, 16 Nov 2015 18:19:51 +0100 Subject: [PATCH 03/26] Move lookupUrl stuff from JSResolverHelper to JSResolver, to be moved to own plugin type --- data/js/tomahawk.js | 3 +- src/libtomahawk/DropJob.cpp | 12 +- src/libtomahawk/GlobalActionManager.cpp | 2 +- src/libtomahawk/resolvers/ExternalResolver.h | 11 +- src/libtomahawk/resolvers/JSResolver.cpp | 213 +++++++++++++++--- src/libtomahawk/resolvers/JSResolver.h | 11 + .../resolvers/JSResolverHelper.cpp | 169 -------------- src/libtomahawk/resolvers/JSResolverHelper.h | 8 - 8 files changed, 211 insertions(+), 218 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index b88ac8cda..3d2456a8d 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -173,7 +173,8 @@ var TomahawkUrlType = { Playlist: 1, Track: 2, Album: 4, - Artist: 8 + Artist: 8, + Xspf: 16 }; //Deprecated for 0.9 resolvers. Use Tomahawk.ConfigTestResultType instead. diff --git a/src/libtomahawk/DropJob.cpp b/src/libtomahawk/DropJob.cpp index 13421b011..c301bb610 100644 --- a/src/libtomahawk/DropJob.cpp +++ b/src/libtomahawk/DropJob.cpp @@ -175,7 +175,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType // Check Scriptresolvers foreach ( QPointer resolver, Pipeline::instance()->scriptResolvers() ) { - if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) ) + if ( resolver->canParseUrl( url, ExternalResolver::UrlTypePlaylist ) ) return true; } } @@ -201,7 +201,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType // Check Scriptresolvers foreach ( QPointer resolver, Pipeline::instance()->scriptResolvers() ) { - if ( resolver->canParseUrl( url, ExternalResolver::Track ) ) + if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeTrack ) ) return true; } } @@ -218,7 +218,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType // Check Scriptresolvers foreach ( QPointer resolver, Pipeline::instance()->scriptResolvers() ) { - if ( resolver->canParseUrl( url, ExternalResolver::Album ) ) + if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAlbum ) ) return true; } } @@ -235,7 +235,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType // Check Scriptresolvers foreach ( QPointer resolver, Pipeline::instance()->scriptResolvers() ) { - if ( resolver->canParseUrl( url, ExternalResolver::Artist ) ) + if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeArtist ) ) return true; } } @@ -306,7 +306,7 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data ) // Check Scriptresolvers foreach ( QPointer resolver, Pipeline::instance()->scriptResolvers() ) { - if ( resolver->canParseUrl( url, ExternalResolver::Playlist ) ) + if ( resolver->canParseUrl( url, ExternalResolver::UrlTypePlaylist ) ) { tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Accepting current drop as a playlist" << resolver->name(); return true; @@ -763,7 +763,7 @@ DropJob::handleTrackUrls( const QString& urls ) { foreach ( QPointer resolver, Pipeline::instance()->scriptResolvers() ) { - if ( resolver->canParseUrl( track, ExternalResolver::Any ) ) + if ( resolver->canParseUrl( track, ExternalResolver::UrlTypeAny ) ) { ScriptCommand_LookupUrl* cmd = new ScriptCommand_LookupUrl( resolver, track ); connect( cmd, SIGNAL( information( QString, QSharedPointer ) ), this, SLOT( informationForUrl( QString, QSharedPointer ) ) ); diff --git a/src/libtomahawk/GlobalActionManager.cpp b/src/libtomahawk/GlobalActionManager.cpp index c341bb8f6..d8131397e 100644 --- a/src/libtomahawk/GlobalActionManager.cpp +++ b/src/libtomahawk/GlobalActionManager.cpp @@ -167,7 +167,7 @@ GlobalActionManager::openUrl( const QString& url ) QList< QPointer< ExternalResolver > > possibleResolvers; foreach ( QPointer resolver, Pipeline::instance()->scriptResolvers() ) { - if ( resolver->canParseUrl( url, ExternalResolver::Any ) ) + if ( resolver->canParseUrl( url, ExternalResolver::UrlTypeAny ) ) { canParse = true; possibleResolvers << resolver; diff --git a/src/libtomahawk/resolvers/ExternalResolver.h b/src/libtomahawk/resolvers/ExternalResolver.h index 57a1eeb74..71e786946 100644 --- a/src/libtomahawk/resolvers/ExternalResolver.h +++ b/src/libtomahawk/resolvers/ExternalResolver.h @@ -68,11 +68,12 @@ public: enum UrlType { - Any = 0x00, - Playlist = 0x01, - Track = 0x02, - Album = 0x04, - Artist = 0x08 + UrlTypeAny = 0x00, + UrlTypePlaylist = 0x01, + UrlTypeTrack = 0x02, + UrlTypeAlbum = 0x04, + UrlTypeArtist = 0x08, + UrlTypeXspf = 0x10 }; Q_DECLARE_FLAGS( UrlTypes, UrlType ) Q_FLAGS( UrlTypes ) diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 118f34cca..e2ec13b20 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -46,6 +46,12 @@ #include "JSAccount.h" #include "ScriptJob.h" +// lookupUrl stuff +#include "playlist/PlaylistTemplate.h" +#include "playlist/XspfPlaylistTemplate.h" +#include "database/Database.h" +#include "database/DatabaseImpl.h" + #include #include #include @@ -319,20 +325,13 @@ JSResolver::canParseUrl( const QString& url, UrlType type ) { Q_D( const JSResolver ); - // FIXME: How can we do this? - /*if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, "canParseUrl", Qt::QueuedConnection, - Q_ARG( QString, url ) ); - return; - }*/ - if ( d->capabilities.testFlag( UrlLookup ) ) { - QString eval = QString( "canParseUrl( '%1', %2 )" ) - .arg( JSAccount::escape( QString( url ) ) ) - .arg( (int) type ); - return callOnResolver( eval ).toBool(); + QVariantMap arguments; + arguments["url"] = url; + arguments["type"] = (int) type; + + return scriptObject()->syncInvoke( "canParseUrl", arguments ).toBool(); } else { @@ -345,34 +344,185 @@ JSResolver::canParseUrl( const QString& url, UrlType type ) void JSResolver::lookupUrl( const QString& url ) { - if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, "lookupUrl", Qt::QueuedConnection, - Q_ARG( QString, url ) ); - return; - } - Q_D( const JSResolver ); + if ( !d->capabilities.testFlag( UrlLookup ) ) { emit informationFound( url, QSharedPointer() ); return; } - QString eval = QString( "lookupUrl( '%1' )" ) - .arg( JSAccount::escape( QString( url ) ) ); + QVariantMap arguments; + arguments["url"] = url; + Tomahawk::ScriptJob* job = scriptObject()->invoke( "lookupUrl", arguments ); + connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onLookupUrlRequestDone( QVariantMap ) ) ); + job->setProperty( "url", url ); + job->start(); +} - QVariantMap m = callOnResolver( eval ).toMap(); - if ( m.isEmpty() ) + +void +JSResolver::onLookupUrlRequestDone( const QVariantMap& result ) +{ + sender()->deleteLater(); + + QString url = sender()->property( "url" ).toString(); + + tLog() << "ON LOOKUP URL REQUEST DONE" << url << result; + + // It may seem a bit weird, but currently no slot should do anything + // more as we starting on a new URL and not task are waiting for it yet. + m_pendingUrl = QString(); + m_pendingAlbum = album_ptr(); + + UrlTypes type = (UrlTypes) result.value( "type" ).toInt(); + if ( type == UrlTypeArtist ) { - // if the resolver doesn't return anything, async api is used - return; + QString name = result.value( "name" ).toString(); + Q_ASSERT( !name.isEmpty() ); + emit informationFound( url, Artist::get( name, true ).objectCast() ); + } + else if ( type == UrlTypeAlbum ) + { + QString name = result.value( "name" ).toString(); + QString artist = result.value( "artist" ).toString(); + album_ptr album = Album::get( Artist::get( artist, true ), name ); + m_pendingUrl = url; + m_pendingAlbum = album; + connect( album.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), + SLOT( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) ); + if ( !album->tracks().isEmpty() ) + { + emit informationFound( url, album.objectCast() ); + } + } + else if ( type == UrlTypeTrack ) + { + Tomahawk::query_ptr query = parseTrack( result ); + if ( query.isNull() ) + { + // A valid track result shoud have non-empty title and artist. + tLog() << Q_FUNC_INFO << name() << "Got empty track information for " << url; + emit informationFound( url, QSharedPointer() ); + } + else + { + emit informationFound( url, query.objectCast() ); + } + } + else if ( type == UrlTypePlaylist ) + { + QString guid = result.value( "guid" ).toString(); + Q_ASSERT( !guid.isEmpty() ); + // Append nodeid to guid to make it globally unique. + guid += instanceUUID(); + + // Do we already have this playlist loaded? + { + playlist_ptr playlist = Playlist::get( guid ); + if ( !playlist.isNull() ) + { + emit informationFound( url, playlist.objectCast() ); + return; + } + } + + // Get all information to build a new playlist but do not build it until we know, + // if it is really handled as a playlist and not as a set of tracks. + Tomahawk::source_ptr source = SourceList::instance()->getLocal(); + const QString title = result.value( "title" ).toString(); + const QString info = result.value( "info" ).toString(); + const QString creator = result.value( "creator" ).toString(); + QList queries; + foreach( QVariant track, result.value( "tracks" ).toList() ) + { + query_ptr query = parseTrack( track.toMap() ); + if ( !query.isNull() ) + { + queries << query; + } + } + tLog( LOGVERBOSE ) << Q_FUNC_INFO << name() << "Got playlist for " << url; + playlisttemplate_ptr pltemplate( new PlaylistTemplate( source, guid, title, info, creator, false, queries ) ); + emit informationFound( url, pltemplate.objectCast() ); + } + else if ( type == UrlTypeXspf ) + { + QString xspfUrl = result.value( "url" ).toString(); + Q_ASSERT( !xspfUrl.isEmpty() ); + QString guid = QString( "xspf-%1-%2" ).arg( xspfUrl.toUtf8().toBase64().constData() ).arg( instanceUUID() ); + + // Do we already have this playlist loaded? + { + playlist_ptr playlist = Playlist::get( guid ); + if ( !playlist.isNull() ) + { + emit informationFound( url, playlist.objectCast() ); + return; + } + } + + + // Get all information to build a new playlist but do not build it until we know, + // if it is really handled as a playlist and not as a set of tracks. + Tomahawk::source_ptr source = SourceList::instance()->getLocal(); + QSharedPointer pltemplate( new XspfPlaylistTemplate( xspfUrl, source, guid ) ); + NewClosure( pltemplate, SIGNAL( tracksLoaded( QList< Tomahawk::query_ptr > ) ), + this, SLOT( pltemplateTracksLoadedForUrl( QString, Tomahawk::playlisttemplate_ptr ) ), + url, pltemplate.objectCast() ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << name() << "Got playlist for " << url; + pltemplate->load(); + } + else + { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << name() << "No usable information found for " << url; + emit informationFound( url, QSharedPointer() ); + } +} + + +query_ptr +JSResolver::parseTrack( const QVariantMap& track ) +{ + QString title = track.value( "track" ).toString(); + QString artist = track.value( "artist" ).toString(); + QString album = track.value( "album" ).toString(); + if ( title.isEmpty() || artist.isEmpty() ) + { + return query_ptr(); } - QString errorMessage = tr( "Script Resolver Warning: API call %1 returned data synchronously." ).arg( eval ); - JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) ); - tDebug() << errorMessage << m; + Tomahawk::query_ptr query = Tomahawk::Query::get( artist, title, album ); + QString resultHint = track.value( "hint" ).toString(); + if ( !resultHint.isEmpty() ) + { + query->setResultHint( resultHint ); + query->setSaveHTTPResultHint( true ); + } + + return query; +} + + +void +JSResolver::tracksAdded( const QList&, const ModelMode, const collection_ptr&) +{ + // Check if we still are actively waiting + if ( m_pendingAlbum.isNull() || m_pendingUrl.isNull() ) + return; + + emit informationFound( m_pendingUrl, m_pendingAlbum.objectCast() ); + m_pendingAlbum = album_ptr(); + m_pendingUrl = QString(); +} + + +void +JSResolver::pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate ) +{ + tLog() << Q_FUNC_INFO; + emit informationFound( url, pltemplate.objectCast() ); } @@ -589,3 +739,10 @@ JSResolver::onResolveRequestDone( const QVariantMap& data ) sender()->deleteLater(); } + + +QString +JSResolver::instanceUUID() +{ + return Tomahawk::Database::instance()->impl()->dbid(); +} diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index ab6b5a4a0..da3c51cb8 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -91,6 +91,7 @@ protected: private slots: void onResolveRequestDone(const QVariantMap& data); + void onLookupUrlRequestDone(const QVariantMap& data); private: void init(); @@ -105,6 +106,16 @@ private: Q_DECLARE_PRIVATE( JSResolver ) QScopedPointer d_ptr; + + +// TODO: move lookupUrl stuff to its own plugin type + QString instanceUUID(); + static Tomahawk::query_ptr parseTrack( const QVariantMap& track ); + QString m_pendingUrl; + Tomahawk::album_ptr m_pendingAlbum; +private slots: + void tracksAdded( const QList& tracks, const Tomahawk::ModelMode, const Tomahawk::collection_ptr& collection ); + void pltemplateTracksLoadedForUrl( const QString& url, const Tomahawk::playlisttemplate_ptr& pltemplate ); }; } // ns: Tomahawk diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index 881cfd023..1a67d46d4 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -21,10 +21,6 @@ #include "JSResolverHelper.h" -#include "database/Database.h" -#include "database/DatabaseImpl.h" -#include "playlist/PlaylistTemplate.h" -#include "playlist/XspfPlaylistTemplate.h" #include "resolvers/ScriptEngine.h" #include "network/Servent.h" #include "utils/Closure.h" @@ -140,36 +136,6 @@ JSResolverHelper::log( const QString& message ) } -query_ptr -JSResolverHelper::parseTrack( const QVariantMap& track ) -{ - QString title = track.value( "title" ).toString(); - QString artist = track.value( "artist" ).toString(); - QString album = track.value( "album" ).toString(); - if ( title.isEmpty() || artist.isEmpty() ) - { - return query_ptr(); - } - - Tomahawk::query_ptr query = Tomahawk::Query::get( artist, title, album ); - QString resultHint = track.value( "hint" ).toString(); - if ( !resultHint.isEmpty() ) - { - query->setResultHint( resultHint ); - query->setSaveHTTPResultHint( true ); - } - - return query; -} - - -QString -JSResolverHelper::instanceUUID() -{ - return Tomahawk::Database::instance()->impl()->dbid(); -} - - QString JSResolverHelper::uuid() const { @@ -464,120 +430,6 @@ JSResolverHelper::currentCountry() const } -void -JSResolverHelper::addUrlResult( const QString& url, const QVariantMap& result ) -{ - // It may seem a bit weird, but currently no slot should do anything - // more as we starting on a new URL and not task are waiting for it yet. - m_pendingUrl = QString(); - m_pendingAlbum = album_ptr(); - - QString type = result.value( "type" ).toString(); - if ( type == "artist" ) - { - QString name = result.value( "name" ).toString(); - Q_ASSERT( !name.isEmpty() ); - emit m_resolver->informationFound( url, Artist::get( name, true ).objectCast() ); - } - else if ( type == "album" ) - { - QString name = result.value( "name" ).toString(); - QString artist = result.value( "artist" ).toString(); - album_ptr album = Album::get( Artist::get( artist, true ), name ); - m_pendingUrl = url; - m_pendingAlbum = album; - connect( album.data(), SIGNAL( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ), - SLOT( tracksAdded( QList, Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) ); - if ( !album->tracks().isEmpty() ) - { - emit m_resolver->informationFound( url, album.objectCast() ); - } - } - else if ( type == "track" ) - { - Tomahawk::query_ptr query = parseTrack( result ); - if ( query.isNull() ) - { - // A valid track result shoud have non-empty title and artist. - tLog() << Q_FUNC_INFO << m_resolver->name() << "Got empty track information for " << url; - emit m_resolver->informationFound( url, QSharedPointer() ); - } - else - { - emit m_resolver->informationFound( url, query.objectCast() ); - } - } - else if ( type == "playlist" ) - { - QString guid = result.value( "guid" ).toString(); - Q_ASSERT( !guid.isEmpty() ); - // Append nodeid to guid to make it globally unique. - guid += instanceUUID(); - - // Do we already have this playlist loaded? - { - playlist_ptr playlist = Playlist::get( guid ); - if ( !playlist.isNull() ) - { - emit m_resolver->informationFound( url, playlist.objectCast() ); - return; - } - } - - // Get all information to build a new playlist but do not build it until we know, - // if it is really handled as a playlist and not as a set of tracks. - Tomahawk::source_ptr source = SourceList::instance()->getLocal(); - const QString title = result.value( "title" ).toString(); - const QString info = result.value( "info" ).toString(); - const QString creator = result.value( "creator" ).toString(); - QList queries; - foreach( QVariant track, result.value( "tracks" ).toList() ) - { - query_ptr query = parseTrack( track.toMap() ); - if ( !query.isNull() ) - { - queries << query; - } - } - tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "Got playlist for " << url; - playlisttemplate_ptr pltemplate( new PlaylistTemplate( source, guid, title, info, creator, false, queries ) ); - emit m_resolver->informationFound( url, pltemplate.objectCast() ); - } - else if ( type == "xspf-url" ) - { - QString xspfUrl = result.value( "url" ).toString(); - Q_ASSERT( !xspfUrl.isEmpty() ); - QString guid = QString( "xspf-%1-%2" ).arg( xspfUrl.toUtf8().toBase64().constData() ).arg( instanceUUID() ); - - // Do we already have this playlist loaded? - { - playlist_ptr playlist = Playlist::get( guid ); - if ( !playlist.isNull() ) - { - emit m_resolver->informationFound( url, playlist.objectCast() ); - return; - } - } - - - // Get all information to build a new playlist but do not build it until we know, - // if it is really handled as a playlist and not as a set of tracks. - Tomahawk::source_ptr source = SourceList::instance()->getLocal(); - QSharedPointer pltemplate( new XspfPlaylistTemplate( xspfUrl, source, guid ) ); - NewClosure( pltemplate, SIGNAL( tracksLoaded( QList< Tomahawk::query_ptr > ) ), - this, SLOT( pltemplateTracksLoadedForUrl( QString, Tomahawk::playlisttemplate_ptr ) ), - url, pltemplate.objectCast() ); - tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "Got playlist for " << url; - pltemplate->load(); - } - else - { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << m_resolver->name() << "No usable information found for " << url; - emit m_resolver->informationFound( url, QSharedPointer() ); - } -} - - void JSResolverHelper::nativeReportCapabilities( const QVariant& v ) { @@ -614,27 +466,6 @@ JSResolverHelper::unregisterScriptPlugin( const QString& type, const QString& ob } -void -JSResolverHelper::tracksAdded( const QList&, const ModelMode, const collection_ptr&) -{ - // Check if we still are actively waiting - if ( m_pendingAlbum.isNull() || m_pendingUrl.isNull() ) - return; - - emit m_resolver->informationFound( m_pendingUrl, m_pendingAlbum.objectCast() ); - m_pendingAlbum = album_ptr(); - m_pendingUrl = QString(); -} - - -void -JSResolverHelper::pltemplateTracksLoadedForUrl( const QString& url, const playlisttemplate_ptr& pltemplate ) -{ - tLog() << Q_FUNC_INFO; - emit m_resolver->informationFound( url, pltemplate.objectCast() ); -} - - void JSResolverHelper::setResolverConfig( const QVariantMap& config ) { diff --git a/src/libtomahawk/resolvers/JSResolverHelper.h b/src/libtomahawk/resolvers/JSResolverHelper.h index b69c92135..08fedb09f 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.h +++ b/src/libtomahawk/resolvers/JSResolverHelper.h @@ -133,7 +133,6 @@ public slots: QByteArray readRaw( const QString& fileName ); QString readBase64( const QString& fileName ); QString readCompressed( const QString& fileName ); - QString instanceUUID(); QString uuid() const; int currentCountry() const; QString compress( const QString& data ); @@ -142,8 +141,6 @@ public slots: void log( const QString& message ); bool fakeEnv() { return false; } - void addUrlResult( const QString& url, const QVariantMap& result ); - void nativeReportCapabilities( const QVariant& capabilities ); void reportScriptJobResults( const QVariantMap& result ); @@ -153,12 +150,9 @@ public slots: private slots: void gotStreamUrl( IODeviceCallback callback, NetworkReply* reply ); - void tracksAdded( const QList& tracks, const Tomahawk::ModelMode, const Tomahawk::collection_ptr& collection ); - void pltemplateTracksLoadedForUrl( const QString& url, const Tomahawk::playlisttemplate_ptr& pltemplate ); void nativeAsyncRequestDone( int requestId, NetworkReply* reply ); private: - Tomahawk::query_ptr parseTrack( const QVariantMap& track ); void returnStreamUrl( const QString& streamUrl, const QMap& headers, std::function< void( const QString&, QSharedPointer< QIODevice >& ) > callback ); @@ -171,8 +165,6 @@ private: 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_pendingUrl; - Tomahawk::album_ptr m_pendingAlbum; }; } // ns: Tomahawk From 64f71fe453346a69072cfac2e3a9c054d2422cdd Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Tue, 17 Nov 2015 02:50:00 +0100 Subject: [PATCH 04/26] Forward declare ResultProvider in Result --- src/libtomahawk/Result.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/Result.h b/src/libtomahawk/Result.h index 8810fcc2c..fcf69a4bf 100644 --- a/src/libtomahawk/Result.h +++ b/src/libtomahawk/Result.h @@ -21,7 +21,6 @@ #ifndef RESULT_H #define RESULT_H -#include "ResultProvider.h" #include "DownloadJob.h" #include "utils/TomahawkUtils.h" #include "Typedefs.h" @@ -38,6 +37,7 @@ class MetadataEditor; namespace Tomahawk { +class ResultProvider; class Resolver; class DLLEXPORT Result : public QObject From 30789bcb9bd7d0d7f177f051e256032ecb32705d Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Tue, 17 Nov 2015 03:12:12 +0100 Subject: [PATCH 05/26] Add getStreamUrl translation step in AudioEngine --- src/libtomahawk/ResultProvider.cpp | 16 ++++++++++++++++ src/libtomahawk/ResultProvider.h | 4 ++++ src/libtomahawk/audio/AudioEngine.cpp | 18 +++++++++++++++--- src/libtomahawk/audio/AudioEngine.h | 1 + 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/ResultProvider.cpp b/src/libtomahawk/ResultProvider.cpp index 55c1ae08f..77ae2b046 100644 --- a/src/libtomahawk/ResultProvider.cpp +++ b/src/libtomahawk/ResultProvider.cpp @@ -17,8 +17,24 @@ */ #include "ResultProvider.h" +#include "Result.h" +#include "resolvers/SyncScriptJob.h" + using namespace Tomahawk; ResultProvider::~ResultProvider() { } + + +ScriptJob* +ResultProvider::getStreamUrl( const result_ptr& result ) +{ + QUrl url = result->url(); + + QVariantMap data; + data[ "result" ] = QVariant::fromValue( result ); + data[ "url" ] = url; + + return new SyncScriptJob( data ); +} diff --git a/src/libtomahawk/ResultProvider.h b/src/libtomahawk/ResultProvider.h index 3b7b7e79c..3e68d99fe 100644 --- a/src/libtomahawk/ResultProvider.h +++ b/src/libtomahawk/ResultProvider.h @@ -20,6 +20,7 @@ #define TOMAHAWK_RESULTPROVIDER_H #include "DllMacro.h" +#include "Typedefs.h" class QPixmap; class QString; @@ -27,6 +28,7 @@ class QSize; namespace Tomahawk { +class ScriptJob; class DLLEXPORT ResultProvider { @@ -35,6 +37,8 @@ public: virtual QString name() const = 0; virtual QPixmap icon( const QSize& size ) const = 0; + + virtual ScriptJob* getStreamUrl( const result_ptr& result ); }; } diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index 067851269..a0bcd2006 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -40,6 +40,7 @@ #include "SourceList.h" #include "TomahawkSettings.h" #include "UrlHandler.h" +#include "resolvers/ScriptJob.h" #include @@ -574,10 +575,21 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) setCurrentTrack( result ); - if ( !TomahawkUtils::isLocalResult( d->currentTrack->url() ) && !TomahawkUtils::isHttpResult( d->currentTrack->url() ) - && !TomahawkUtils::isRtmpResult( d->currentTrack->url() ) ) + ScriptJob* job = result->resolvedBy()->getStreamUrl( result ); + connect( job, SIGNAL( done( QVariantMap ) ), SLOT( gotStreamUrl( QVariantMap ) ) ); + job->start(); +} + +void +AudioEngine::gotStreamUrl( const QVariantMap& data ) +{ + QString url = data[ "url" ].toString(); + result_ptr result = data[ "result" ].value(); + + if ( !TomahawkUtils::isLocalResult( url ) && !TomahawkUtils::isHttpResult( url ) + && !TomahawkUtils::isRtmpResult( url ) ) { - performLoadIODevice( d->currentTrack, d->currentTrack->url() ); + performLoadIODevice( result, url ); } else { diff --git a/src/libtomahawk/audio/AudioEngine.h b/src/libtomahawk/audio/AudioEngine.h index 44da5f480..81e29b086 100644 --- a/src/libtomahawk/audio/AudioEngine.h +++ b/src/libtomahawk/audio/AudioEngine.h @@ -180,6 +180,7 @@ signals: private slots: void loadTrack( const Tomahawk::result_ptr& result ); //async! + void gotStreamUrl( const QVariantMap& data ); 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(); From dde7db616a6aa37dd421d0d5e427d3179d6d626e Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 01:23:38 +0100 Subject: [PATCH 06/26] Use getStreamUrl on ResultProviders instead of awkward custom iodevicefactories --- data/js/tomahawk.js | 47 ++----- src/libtomahawk/ResultProvider.cpp | 5 +- src/libtomahawk/audio/AudioEngine.cpp | 51 +++++++- src/libtomahawk/audio/AudioEngine.h | 4 + src/libtomahawk/resolvers/JSResolver.cpp | 16 ++- src/libtomahawk/resolvers/JSResolver.h | 2 + .../resolvers/JSResolverHelper.cpp | 121 ------------------ src/libtomahawk/resolvers/JSResolverHelper.h | 19 +-- .../resolvers/ScriptCollection.cpp | 10 ++ src/libtomahawk/resolvers/ScriptCollection.h | 2 + 10 files changed, 85 insertions(+), 192 deletions(-) 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(); From 90d6f0d4e5ef0922d419a282bf28f37edc7110ab Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 01:24:07 +0100 Subject: [PATCH 07/26] Set collectionId on results from Tomahawk.Collection --- data/js/tomahawk.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 369f7633f..e6eb59ea0 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -1587,6 +1587,7 @@ Tomahawk.Collection = { }, _fuzzyIndexIdsToTracks: function (resultIds, id) { + var collection = this; if (typeof id === 'undefined') { id = this.settings.id; } @@ -1603,7 +1604,8 @@ Tomahawk.Collection = { linkUrl: row.linkUrl, releaseyear: row.releaseyear, bitrate: row.bitrate, - albumpos: row.albumPos + albumpos: row.albumPos, + collectionId: collection.id }; }; for (var idx = 0; resultIds && idx < resultIds.length; idx++) { @@ -1648,6 +1650,8 @@ Tomahawk.Collection = { }, tracks: function (params, where) { + var collection = this; + //TODO filter/where support var id = params.id; if (typeof id === 'undefined') { @@ -1667,7 +1671,8 @@ Tomahawk.Collection = { linkUrl: row.linkUrl, releaseyear: row.releaseyear, bitrate: row.bitrate, - albumpos: row.albumPos + albumpos: row.albumPos, + collectionId: collection.id }; }; t.sqlSelect("tracks", mapFn, From 1b4efa8f4a229d3cc601e46cf18aa591c0f23718 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 01:44:54 +0100 Subject: [PATCH 08/26] Bye bye callOnResolver --- src/libtomahawk/resolvers/JSResolver.cpp | 41 +++--------------------- src/libtomahawk/resolvers/JSResolver.h | 2 -- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 3f48a19ee..92923fc57 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -259,9 +259,9 @@ JSResolver::init() // HACK: register resolver object d->scriptAccount->evaluateJavaScript( "Tomahawk.PluginManager.registerPlugin('resolver', Tomahawk.resolver.instance);" ); // init resolver - resolverInit(); + scriptObject()->syncInvoke( "init" ); - QVariantMap m = resolverSettings(); + QVariantMap m = scriptObject()->syncInvoke( "settings" ).toMap(); d->name = m.value( "name" ).toString(); d->weight = m.value( "weight", 0 ).toUInt(); d->timeout = m.value( "timeout", 25 ).toUInt() * 1000; @@ -582,7 +582,7 @@ JSResolver::loadUi() { Q_D( JSResolver ); - QVariantMap m = callOnResolver( "getConfigUi()" ).toMap(); + QVariantMap m = scriptObject()->syncInvoke( "getConfigUi" ).toMap(); bool compressed = m.value( "compressed", "false" ).toBool(); qDebug() << "Resolver has a preferences widget! compressed?" << compressed; @@ -634,7 +634,7 @@ JSResolver::saveConfig() // qDebug() << Q_FUNC_INFO << saveData; d->resolverHelper->setResolverConfig( saveData.toMap() ); - callOnResolver( "saveUserConfig()" ); + scriptObject()->syncInvoke( "saveUserConfig" ); } @@ -665,41 +665,10 @@ JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capa } -QVariantMap -JSResolver::resolverSettings() -{ - return callOnResolver( "settings" ).toMap(); -} - - QVariantMap JSResolver::resolverUserConfig() { - return callOnResolver( "getUserConfig()" ).toMap(); -} - - -QVariantMap -JSResolver::resolverInit() -{ - return callOnResolver( "init()" ).toMap(); -} - - -QVariant -JSResolver::callOnResolver( const QString& scriptSource ) -{ - Q_D( JSResolver ); - - QString propertyName = scriptSource.split('(').first(); - - return d->scriptAccount->evaluateJavaScriptWithResult( QString( - "if(Tomahawk.resolver.instance['_adapter_%1']) {" - " Tomahawk.resolver.instance._adapter_%2;" - "} else {" - " Tomahawk.resolver.instance.%2" - "}" - ).arg( propertyName ).arg( scriptSource ) ); + return scriptObject()->syncInvoke( "getUserConfig" ).toMap(); } diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index 4fc063fd7..df6ea97e1 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -102,9 +102,7 @@ private: void onCapabilitiesChanged( Capabilities capabilities ); // encapsulate javascript calls - QVariantMap resolverSettings(); QVariantMap resolverUserConfig(); - QVariantMap resolverInit(); Q_DECLARE_PRIVATE( JSResolver ) QScopedPointer d_ptr; From 3db8e91b5174b581c873189fe2edee4d57a9a601 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 05:36:05 +0100 Subject: [PATCH 09/26] Bye bye syncRequest --- data/js/tomahawk.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index e6eb59ea0..34cbefa90 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -335,34 +335,6 @@ Tomahawk.valueForSubNode = function (node, tag) { return element.textContent; }; -/** - * Do a synchronous HTTP(S) request. For further options see - * Tomahawk.asyncRequest - */ -Tomahawk.syncRequest = function (url, extraHeaders, options) { - // unpack options - var opt = options || {}; - var method = opt.method || 'GET'; - - var xmlHttpRequest = new XMLHttpRequest(); - xmlHttpRequest.open(method, url, false, opt.username, opt.password); - if (extraHeaders) { - for (var headerName in extraHeaders) { - xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]); - } - } - xmlHttpRequest.send(null); - if (httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) { - return xmlHttpRequest.responseText; - } else { - Tomahawk.log("Failed to do GET request: to: " + url); - Tomahawk.log("Status Code was: " + xmlHttpRequest.status); - if (opt.hasOwnProperty('errorHandler')) { - opt.errorHandler.call(window, xmlHttpRequest); - } - } -}; - /** * Internal counter used to identify retrievedMetadata call back from native * code. From 17d71b413f24dcbc232f672383e6461e08cd4e4f Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 05:39:07 +0100 Subject: [PATCH 10/26] Implement Tomahawk.ajax native implementation on behalf of NativeScriptJobs --- data/js/tomahawk.js | 106 +++++++++--------- src/libtomahawk/resolvers/JSAccount.cpp | 16 +++ src/libtomahawk/resolvers/JSAccount.h | 2 + .../resolvers/JSResolverHelper.cpp | 37 ++++-- src/libtomahawk/resolvers/JSResolverHelper.h | 21 ++-- src/libtomahawk/resolvers/ScriptAccount.h | 3 +- 6 files changed, 108 insertions(+), 77 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 34cbefa90..7149b5dae 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -430,6 +430,23 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) { delete Tomahawk.asyncRequestCallbacks[reqId]; }; + + +/** + * This method is externalized from Tomahawk.asyncRequest, so that other clients + * (like tomahawk-android) can inject their own logic that determines whether or not to do a request + * natively. + * + * @returns boolean indicating whether or not to do a request with the given parameters natively + */ +var shouldDoNativeRequest = function (options) { + var extraHeaders = options.headers; + return (extraHeaders && (extraHeaders.hasOwnProperty("Referer") + || extraHeaders.hasOwnProperty("referer") + || extraHeaders.hasOwnProperty("User-Agent"))); +}; + + /** * Possible options: * - method: The HTTP request method (default: GET) @@ -439,59 +456,45 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) { * - data: body data included in POST requests * - needCookieHeader: boolean indicating whether or not the request needs to be able to get the * "Set-Cookie" response header + * - headers: headers set on the request */ -Tomahawk.asyncRequest = function (url, callback, extraHeaders, options) { - // unpack options - var opt = options || {}; - var method = opt.method || 'GET'; +var doRequest = function(options) { + if (shouldDoNativeRequest(options)) { + return Tomahawk.NativeScriptJobManager.invoke('httpRequest', options).then(function(xhr) { + xhr.responseHeaders = xhr.responseHeaders || {}; + xhr.getAllResponseHeaders = function() { + return this.responseHeaders; + }; + xhr.getResponseHeader = function (header) { + return this.responseHeaders[header]; + }; - if (shouldDoNativeRequest(url, callback, extraHeaders, options)) { - // Assign a request Id to the callback so we can use it when we are - // returning from the native call. - var reqId = Tomahawk.asyncRequestIdCounter; - Tomahawk.asyncRequestIdCounter++; - Tomahawk.asyncRequestCallbacks[reqId] = { - callback: callback, - errorHandler: opt.errorHandler - }; - Tomahawk.nativeAsyncRequest(reqId, url, extraHeaders, options); + return xhr; + }); } else { - var xmlHttpRequest = new XMLHttpRequest(); - xmlHttpRequest.open(method, url, true, opt.username, opt.password); - if (extraHeaders) { - for (var headerName in extraHeaders) { - xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]); - } - } - xmlHttpRequest.onreadystatechange = function () { - if (xmlHttpRequest.readyState == 4 - && httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) { - callback.call(window, xmlHttpRequest); - } else if (xmlHttpRequest.readyState === 4) { - Tomahawk.log("Failed to do " + method + " request: to: " + url); - Tomahawk.log("Status Code was: " + xmlHttpRequest.status); - if (opt.hasOwnProperty('errorHandler')) { - opt.errorHandler.call(window, xmlHttpRequest); + return new RSVP.Promise(function(resolve, reject) { + var xmlHttpRequest = new XMLHttpRequest(); + xmlHttpRequest.open(options.method, options.url, true, options.username, options.password); + if (options.headers) { + for (var headerName in options.headers) { + xmlHttpRequest.setRequestHeader(headerName, options.headers[headerName]); } } - }; - xmlHttpRequest.send(opt.data || null); + xmlHttpRequest.onreadystatechange = function () { + if (xmlHttpRequest.readyState == 4 + && httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) { + resolve(xmlHttpRequest); + } else if (xmlHttpRequest.readyState === 4) { + Tomahawk.log("Failed to do " + options.method + " request: to: " + options.url); + Tomahawk.log("Status Code was: " + xmlHttpRequest.status); + reject(xmlHttpRequest); + } + }; + xmlHttpRequest.send(options.data || null); + }); } }; -/** - * This method is externalized from Tomahawk.asyncRequest, so that other clients - * (like tomahawk-android) can inject their own logic that determines whether or not to do a request - * natively. - * - * @returns boolean indicating whether or not to do a request with the given parameters natively - */ -var shouldDoNativeRequest = function (url, callback, extraHeaders, options) { - return (extraHeaders && (extraHeaders.hasOwnProperty("Referer") - || extraHeaders.hasOwnProperty("referer") - || extraHeaders.hasOwnProperty("User-Agent"))); -}; - Tomahawk.ajax = function (url, settings) { if (typeof url === "object") { settings = url; @@ -549,10 +552,7 @@ Tomahawk.ajax = function (url, settings) { } } - return new RSVP.Promise(function (resolve, reject) { - settings.errorHandler = reject; - Tomahawk.asyncRequest(settings.url, resolve, settings.headers, settings); - }).then(function (xhr) { + return doRequest(settings).then(function (xhr) { if (settings.rawResponse) { return xhr; } @@ -876,12 +876,17 @@ Tomahawk.PluginManager = { } }; + +var encodeParamsToNativeFunctions = function(param) { + return param; +}; + Tomahawk.NativeScriptJobManager = { idCounter: 0, deferreds: {}, invoke: function (methodName, params) { var requestId = this.idCounter++; - Tomahawk.invokeNativeScriptJob(requestId, methodName, JSON.stringify(params)); + Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params)); this.deferreds[requestId] = RSVP.defer(); return this.deferreds[requestId].promise; }, @@ -891,6 +896,7 @@ Tomahawk.NativeScriptJobManager = { Tomahawk.log("Deferred object with the given requestId is not present!"); } deferred.resolve(result); + delete this.deferreds[requestId]; } }; diff --git a/src/libtomahawk/resolvers/JSAccount.cpp b/src/libtomahawk/resolvers/JSAccount.cpp index 772f0cf9e..5f666c568 100644 --- a/src/libtomahawk/resolvers/JSAccount.cpp +++ b/src/libtomahawk/resolvers/JSAccount.cpp @@ -170,6 +170,22 @@ JSAccount::syncInvoke( const scriptobject_ptr& scriptObject, const QString& meth return evaluateJavaScriptWithResult( eval ); } +void +JSAccount::reportNativeScriptJobResult( int resultId, const QVariantMap& result ) +{ + QString eval = QString( + "Tomahawk.NativeScriptJobManager.reportNativeScriptJobResult(" + "%1," // requestId + "%2" // results + ");" + ).arg( resultId ) + .arg( serializeQVariantMap( result ) ); + + // Remove when new scripting api turned out to work reliably + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval; + + evaluateJavaScript( eval ); +} QVariant JSAccount::evaluateJavaScriptInternal( const QString& scriptSource ) diff --git a/src/libtomahawk/resolvers/JSAccount.h b/src/libtomahawk/resolvers/JSAccount.h index 9bff73b38..6965de07d 100644 --- a/src/libtomahawk/resolvers/JSAccount.h +++ b/src/libtomahawk/resolvers/JSAccount.h @@ -71,6 +71,8 @@ public: static QString serializeQVariantMap(const QVariantMap& map); + void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) override; + private: /** * Wrap the pure evaluateJavaScript call in here, while the threadings guards are in public methods diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index 8bc7bbd89..773ae0a69 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -47,6 +47,8 @@ #include #include #include +#include + #include #include #include @@ -627,12 +629,24 @@ JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url, } } +void +JSResolverHelper::invokeNativeScriptJob( int requestId, const QString& methodName, const QVariantMap& params ) +{ + if ( methodName == "httpRequest" ) { + nativeAsyncRequest( requestId, params ); + } else { + // TODO: make promise reject instead + Q_ASSERT_X(false, "invokeNativeScriptJob", "NativeScriptJob methodName was not found"); + } +} + void -JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url, - const QVariantMap& headers, - const QVariantMap& options ) +JSResolverHelper::nativeAsyncRequest( const int requestId, const QVariantMap& options ) { + QString url = options[ "url" ].toString(); + QVariantMap headers = options[ "headers" ].toMap(); + QNetworkRequest req( url ); foreach ( const QString& key, headers.keys() ) { @@ -688,17 +702,16 @@ JSResolverHelper::nativeAsyncRequestDone( int requestId, NetworkReply* reply ) map["status"] = reply->reply()->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); map["statusText"] = QString("%1 %2").arg( map["status"].toString() ) .arg( reply->reply()->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString() ); - if (reply->reply()->hasRawHeader( "Content-Type" )) - map["contentType"] = reply->reply()->rawHeader( "Content-Type" ); - bool ok = false; - QString json = QString::fromUtf8( TomahawkUtils::toJson( map, &ok ) ); - Q_ASSERT( ok ); - QString javascript = QString( "Tomahawk.nativeAsyncRequestDone( %1, %2 );" ) - .arg( QString::number( requestId ) ) - .arg( json ); - m_resolver->d_func()->scriptAccount->evaluateJavaScript( javascript ); + QVariantMap responseHeaders; + foreach( const QNetworkReply::RawHeaderPair& pair, reply->reply()->rawHeaderPairs() ) + { + responseHeaders[ pair.first ] = pair.second; + } + map["responseHeaders"] = responseHeaders; + + m_resolver->d_func()->scriptAccount->reportNativeScriptJobResult( requestId, map ); } diff --git a/src/libtomahawk/resolvers/JSResolverHelper.h b/src/libtomahawk/resolvers/JSResolverHelper.h index 57b963e39..8790cb49b 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.h +++ b/src/libtomahawk/resolvers/JSResolverHelper.h @@ -86,20 +86,9 @@ public: int sizehint, const QVariantMap& options ); - /** - * Native handler for asynchronous HTTP requests. - * - * This handler shall only be used if we cannot achieve the request with - * XMLHttpRequest as that would be more efficient. - * Use cases are: - * * Referer header: Stripped on MacOS and the specification says it - * should be stripped - * - * INTERNAL USE ONLY! - */ - Q_INVOKABLE void nativeAsyncRequest( int requestId, const QString& url, - const QVariantMap& headers, - const QVariantMap& options ); + Q_INVOKABLE void invokeNativeScriptJob( int requestId, + const QString& methodName, + const QVariantMap& params ); /** * Lucene++ indices for JS resolvers @@ -145,6 +134,10 @@ private: bool indexDataFromVariant( const QVariantMap& map, struct Tomahawk::IndexData& indexData ); QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query ); + // native script jobs + void nativeAsyncRequest( int requestId, const QVariantMap& options ); + + QVariantMap m_resolverConfig; JSResolver* m_resolver; QString m_scriptPath; diff --git a/src/libtomahawk/resolvers/ScriptAccount.h b/src/libtomahawk/resolvers/ScriptAccount.h index 1c9890d9e..c773fe0fe 100644 --- a/src/libtomahawk/resolvers/ScriptAccount.h +++ b/src/libtomahawk/resolvers/ScriptAccount.h @@ -65,13 +65,14 @@ public: ScriptJob* invoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ); virtual QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0; - virtual void startJob( ScriptJob* scriptJob ) = 0; void reportScriptJobResult( const QVariantMap& result ); void registerScriptPlugin( const QString& type, const QString& objectId ); void unregisterScriptPlugin( const QString& type, const QString& objectId ); + virtual void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) = 0; + virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object ); QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist ); From 29aa9546a819cd0a2f9f19e834b25566d2f29a1b Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 06:19:53 +0100 Subject: [PATCH 11/26] Allow instantaneous returns for nativeScriptJobs --- data/js/tomahawk.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 7149b5dae..5423e6856 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -886,9 +886,10 @@ Tomahawk.NativeScriptJobManager = { deferreds: {}, invoke: function (methodName, params) { var requestId = this.idCounter++; - Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params)); - this.deferreds[requestId] = RSVP.defer(); - return this.deferreds[requestId].promise; + var deferred = RSVP.defer(); + this.deferreds[requestId] = deferred; + Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));; + return deferred.promise; }, reportNativeScriptJobResult: function (requestId, result) { var deferred = this.deferreds[requestId]; From e8aa2e6de9d7d2c748c2a6ad598380cd4812d589 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 06:20:09 +0100 Subject: [PATCH 12/26] Make params for nativeScriptJobs optional --- data/js/tomahawk.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 5423e6856..b22c00fce 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -885,6 +885,8 @@ Tomahawk.NativeScriptJobManager = { idCounter: 0, deferreds: {}, invoke: function (methodName, params) { + params = params || {}; + var requestId = this.idCounter++; var deferred = RSVP.defer(); this.deferreds[requestId] = deferred; From e997a194a404b01804f44443654e265511244ac3 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 06:20:28 +0100 Subject: [PATCH 13/26] Add nativeScriptJob errors --- data/js/tomahawk.js | 10 +++++++++- src/libtomahawk/resolvers/JSAccount.cpp | 19 +++++++++++++++++++ src/libtomahawk/resolvers/JSAccount.h | 1 + .../resolvers/JSResolverHelper.cpp | 7 +++++-- src/libtomahawk/resolvers/ScriptAccount.h | 1 + 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index b22c00fce..bac76c878 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -893,13 +893,21 @@ Tomahawk.NativeScriptJobManager = { Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));; return deferred.promise; }, - reportNativeScriptJobResult: function (requestId, result) { + reportNativeScriptJobResult: function(requestId, result) { var deferred = this.deferreds[requestId]; if (!deferred) { Tomahawk.log("Deferred object with the given requestId is not present!"); } deferred.resolve(result); delete this.deferreds[requestId]; + }, + reportNativeScriptJobError: function(requestId, error) { + var deferred = this.deferreds[requestId]; + if (!deferred) { + console.log("Deferred object with the given requestId is not present!"); + } + deferred.reject(error); + delete this.deferreds[requestId]; } }; diff --git a/src/libtomahawk/resolvers/JSAccount.cpp b/src/libtomahawk/resolvers/JSAccount.cpp index 5f666c568..e7a4ba90f 100644 --- a/src/libtomahawk/resolvers/JSAccount.cpp +++ b/src/libtomahawk/resolvers/JSAccount.cpp @@ -187,6 +187,25 @@ JSAccount::reportNativeScriptJobResult( int resultId, const QVariantMap& result evaluateJavaScript( eval ); } + +void +JSAccount::reportNativeScriptJobError( int resultId, const QVariantMap& error ) +{ + QString eval = QString( + "Tomahawk.NativeScriptJobManager.reportNativeScriptJobError(" + "%1," // requestId + "%2" // results + ");" + ).arg( resultId ) + .arg( serializeQVariantMap( error ) ); + + // Remove when new scripting api turned out to work reliably + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval; + + evaluateJavaScript( eval ); +} + + QVariant JSAccount::evaluateJavaScriptInternal( const QString& scriptSource ) { diff --git a/src/libtomahawk/resolvers/JSAccount.h b/src/libtomahawk/resolvers/JSAccount.h index 6965de07d..fe924e064 100644 --- a/src/libtomahawk/resolvers/JSAccount.h +++ b/src/libtomahawk/resolvers/JSAccount.h @@ -72,6 +72,7 @@ public: static QString serializeQVariantMap(const QVariantMap& map); void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) override; + void reportNativeScriptJobError( int resultId, const QVariantMap& error ) override; private: /** diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index 773ae0a69..6ae6a639c 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -635,8 +635,11 @@ JSResolverHelper::invokeNativeScriptJob( int requestId, const QString& methodNam if ( methodName == "httpRequest" ) { nativeAsyncRequest( requestId, params ); } else { - // TODO: make promise reject instead - Q_ASSERT_X(false, "invokeNativeScriptJob", "NativeScriptJob methodName was not found"); + QVariantMap error; + error["message"] = "NativeScriptJob methodName was not found"; + error["name"] = "method_was_not_found"; + + m_resolver->d_func()->scriptAccount->reportNativeScriptJobError( requestId, error ); } } diff --git a/src/libtomahawk/resolvers/ScriptAccount.h b/src/libtomahawk/resolvers/ScriptAccount.h index c773fe0fe..dfbfd78c9 100644 --- a/src/libtomahawk/resolvers/ScriptAccount.h +++ b/src/libtomahawk/resolvers/ScriptAccount.h @@ -72,6 +72,7 @@ public: void unregisterScriptPlugin( const QString& type, const QString& objectId ); virtual void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) = 0; + virtual void reportNativeScriptJobError( int resultId, const QVariantMap& error ) = 0; virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object ); From dc32f7eeb133b4854c01659bf4fb155de8a34767 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 19 Nov 2015 13:38:23 +0100 Subject: [PATCH 14/26] Remove legacy hacks and add useful error --- data/js/tomahawk.js | 47 +++------------------------------------------ 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index bac76c878..0f15d54fc 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -812,52 +812,11 @@ Tomahawk.PluginManager = { } } - if (typeof this.objects[objectId][methodName] === 'function') { - if (!Tomahawk.resolver.instance.apiVersion - || Tomahawk.resolver.instance.apiVersion < 0.9) { - if (methodName == 'artists') { - return new RSVP.Promise(function (resolve, reject) { - pluginManager.resolve[requestId] = resolve; - Tomahawk.resolver.instance.artists(requestId); - }); - } else if (methodName == 'albums') { - return new RSVP.Promise(function (resolve, reject) { - pluginManager.resolve[requestId] = resolve; - Tomahawk.resolver.instance.albums(requestId, params.artist); - }); - } else if (methodName == 'tracks') { - return new RSVP.Promise(function (resolve, reject) { - pluginManager.resolve[requestId] = resolve; - Tomahawk.resolver.instance.tracks(requestId, params.artist, params.album); - }); - } else if (methodName == 'lookupUrl') { - return new RSVP.Promise(function (resolve, reject) { - pluginManager.resolve[params.url] = resolve; - Tomahawk.resolver.instance.lookupUrl(params.url); - }); - } else if (methodName == 'getStreamUrl') { - return new RSVP.Promise(function (resolve, reject) { - pluginManager.resolve[requestId] = resolve; - Tomahawk.resolver.instance.getStreamUrl(requestId, params.url); - }); - } else if (methodName == 'resolve') { - return new RSVP.Promise(function (resolve, reject) { - pluginManager.resolve[requestId] = resolve; - Tomahawk.resolver.instance.resolve(requestId, params.artist, - params.album, params.track); - }); - } else if (methodName == 'search') { - return new RSVP.Promise(function (resolve, reject) { - pluginManager.resolve[requestId] = resolve; - Tomahawk.resolver.instance.search(requestId, params.query); - }); - } - } - - return this.objects[objectId][methodName](params); + if (typeof this.objects[objectId][methodName] !== 'function') { + throw new Error('\'' + methodName + '\' on ScriptObject ' + objectId + ' is not a function', typeof this.objects[objectId][methodName]); } - return this.objects[objectId][methodName]; + return this.objects[objectId][methodName](params); }, invoke: function (requestId, objectId, methodName, params) { From 4637f3ed23cd281a04ab41d68e8a2f734481e95a Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 22:11:44 +0100 Subject: [PATCH 15/26] Make collections able to resolve queries --- data/js/tomahawk.js | 57 ++++++------- src/libtomahawk/CMakeLists.txt | 1 - src/libtomahawk/Pipeline.cpp | 2 +- src/libtomahawk/Result.cpp | 2 +- src/libtomahawk/Result.h | 5 +- src/libtomahawk/ResultProvider.cpp | 37 --------- src/libtomahawk/ResultProvider.h | 46 ----------- src/libtomahawk/collection/Collection.cpp | 2 +- src/libtomahawk/collection/Collection.h | 4 +- .../database/DatabaseCollection.cpp | 29 +++++++ src/libtomahawk/database/DatabaseCollection.h | 5 ++ src/libtomahawk/resolvers/JSResolver.cpp | 81 +++++++------------ src/libtomahawk/resolvers/Resolver.cpp | 21 +++++ src/libtomahawk/resolvers/Resolver.h | 13 +-- src/libtomahawk/resolvers/ScriptAccount.cpp | 26 ++++++ src/libtomahawk/resolvers/ScriptAccount.h | 7 +- .../resolvers/ScriptCollection.cpp | 57 +++++++++++++ src/libtomahawk/resolvers/ScriptCollection.h | 5 ++ .../plugins/ScriptCollectionFactory.cpp | 3 + 19 files changed, 220 insertions(+), 183 deletions(-) delete mode 100644 src/libtomahawk/ResultProvider.cpp delete mode 100644 src/libtomahawk/ResultProvider.h diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 0f15d54fc..34254ad77 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -272,43 +272,18 @@ Tomahawk.Resolver = { return params; }, _adapter_resolve: function (params) { - var that = this; - var collectionPromises = []; - Tomahawk.collections.forEach(function (col) { - if (col.resolve) { - collectionPromises.push(col.resolve(params)); - } - }); - - return RSVP.Promise.all(collectionPromises).then(function (collectionResults) { - var merged = []; - return merged.concat.apply(merged, collectionResults); - }).then(function (collectionResults) { - return RSVP.Promise.resolve(that.resolve(params)).then(function (results) { - return { - 'results': results.concat(collectionResults) - }; - }); + return RSVP.Promise.resolve(this.resolve(params)).then(function (results) { + return { + 'results': results + }; }); }, _adapter_search: function (params) { - var that = this; - var collectionPromises = []; - Tomahawk.collections.forEach(function (col) { - if (col.search) { - collectionPromises.push(col.search(params)); - } - }); - return RSVP.Promise.all(collectionPromises).then(function (collectionResults) { - var merged = []; - return merged.concat.apply(merged, collectionResults); - }).then(function (collectionResults) { - return RSVP.Promise.resolve(that.search(params)).then(function (results) { - return { - 'results': results.concat(collectionResults) - }; - }); + return RSVP.Promise.resolve(this.search(params)).then(function (results) { + return { + 'results': results + }; }); }, @@ -1587,11 +1562,27 @@ Tomahawk.Collection = { }); }, + _adapter_resolve: function (params) { + return RSVP.Promise.resolve(this.resolve(params)).then(function (results) { + return { + 'results': results + }; + }); + }, + resolve: function (params) { var resultIds = Tomahawk.resolveFromFuzzyIndex(params.artist, params.album, params.track); return this._fuzzyIndexIdsToTracks(resultIds); }, + _adapter_search: function (params) { + return RSVP.Promise.resolve(this.search(params)).then(function (results) { + return { + 'results': results + }; + }); + }, + search: function (params) { var resultIds = Tomahawk.searchFuzzyIndex(params.query); return this._fuzzyIndexIdsToTracks(resultIds); diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 5da56cce6..5d5e47118 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -199,7 +199,6 @@ list(APPEND libSources MetaPlaylistInterface.cpp Query.cpp Result.cpp - ResultProvider.cpp Source.cpp Track.cpp TrackData.cpp diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index dfac3b2bd..5a0a59284 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -340,7 +340,7 @@ Pipeline::reportResults( QID qid, Tomahawk::Resolver* r, const QList< result_ptr { if ( !results.isEmpty() ) { - ResultProvider* resolvedBy = results[0]->resolvedBy(); + Resolver* resolvedBy = results[0]->resolvedBy(); if ( resolvedBy ) { tDebug() << "Result arrived too late for:" << qid << "by" << resolvedBy->name(); diff --git a/src/libtomahawk/Result.cpp b/src/libtomahawk/Result.cpp index 7686011a9..9afd75fba 100644 --- a/src/libtomahawk/Result.cpp +++ b/src/libtomahawk/Result.cpp @@ -491,7 +491,7 @@ Result::setFileId( unsigned int id ) } -Tomahawk::ResultProvider* +Tomahawk::Resolver* Result::resolvedBy() const { if ( !m_collection.isNull() ) diff --git a/src/libtomahawk/Result.h b/src/libtomahawk/Result.h index fcf69a4bf..c989ecb85 100644 --- a/src/libtomahawk/Result.h +++ b/src/libtomahawk/Result.h @@ -37,7 +37,6 @@ class MetadataEditor; namespace Tomahawk { -class ResultProvider; class Resolver; class DLLEXPORT Result : public QObject @@ -86,9 +85,9 @@ public: void setResolvedByResolver( Tomahawk::Resolver* resolver ); /** - * This is very bad. ResultProvider is not a QObject and thus can not be tracked by a qt smart pointer ... :-( + * TODO: Make this a smart pointer */ - ResultProvider* resolvedBy() const; + Resolver* resolvedBy() const; RID id() const; bool isOnline() const; diff --git a/src/libtomahawk/ResultProvider.cpp b/src/libtomahawk/ResultProvider.cpp deleted file mode 100644 index 5aa85623f..000000000 --- a/src/libtomahawk/ResultProvider.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright (C) 2015 Dominik Schmidt - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ -#include "ResultProvider.h" - -#include "Result.h" -#include "resolvers/SyncScriptJob.h" - -using namespace Tomahawk; - -ResultProvider::~ResultProvider() -{ -} - - -ScriptJob* -ResultProvider::getStreamUrl( const result_ptr& result ) -{ - QVariantMap data; - data[ "url" ] = result->url(); - - return new SyncScriptJob( data ); -} diff --git a/src/libtomahawk/ResultProvider.h b/src/libtomahawk/ResultProvider.h deleted file mode 100644 index 3e68d99fe..000000000 --- a/src/libtomahawk/ResultProvider.h +++ /dev/null @@ -1,46 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright (C) 2015 Dominik Schmidt - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ -#pragma once -#ifndef TOMAHAWK_RESULTPROVIDER_H -#define TOMAHAWK_RESULTPROVIDER_H - -#include "DllMacro.h" -#include "Typedefs.h" - -class QPixmap; -class QString; -class QSize; - -namespace Tomahawk -{ -class ScriptJob; - -class DLLEXPORT ResultProvider -{ -public: - virtual ~ResultProvider(); - - virtual QString name() const = 0; - virtual QPixmap icon( const QSize& size ) const = 0; - - virtual ScriptJob* getStreamUrl( const result_ptr& result ); -}; - -} - -#endif // TOMAHAWK_RESULTPROVIDER_H diff --git a/src/libtomahawk/collection/Collection.cpp b/src/libtomahawk/collection/Collection.cpp index 804012b60..b468470fd 100644 --- a/src/libtomahawk/collection/Collection.cpp +++ b/src/libtomahawk/collection/Collection.cpp @@ -35,7 +35,7 @@ using namespace Tomahawk; Collection::Collection( const source_ptr& source, const QString& name, QObject* parent ) - : QObject( parent ) + : Resolver( parent ) , m_name( name ) , m_lastmodified( 0 ) , m_changed( false ) diff --git a/src/libtomahawk/collection/Collection.h b/src/libtomahawk/collection/Collection.h index efa0700b3..594de4a4d 100644 --- a/src/libtomahawk/collection/Collection.h +++ b/src/libtomahawk/collection/Collection.h @@ -33,7 +33,7 @@ #include "collection/ArtistsRequest.h" #include "collection/AlbumsRequest.h" #include "collection/TracksRequest.h" -#include "../ResultProvider.h" +#include "../resolvers/Resolver.h" #include "DllMacro.h" @@ -46,7 +46,7 @@ namespace Tomahawk { -class DLLEXPORT Collection : public QObject, public ResultProvider +class DLLEXPORT Collection : public Resolver { Q_OBJECT diff --git a/src/libtomahawk/database/DatabaseCollection.cpp b/src/libtomahawk/database/DatabaseCollection.cpp index a713d86e5..0181dc0c9 100644 --- a/src/libtomahawk/database/DatabaseCollection.cpp +++ b/src/libtomahawk/database/DatabaseCollection.cpp @@ -251,3 +251,32 @@ DatabaseCollection::stationCreated( const source_ptr& source, const QVariantList } + +/* + * Resolver interface + * + * We implement searching the database in the DatabaseResolver which avoids a n+1 query here. + * We can't simply let ScriptCollection inherit Collection and Resolver because both are QObjects, + * although Resolver doesn't need to be a QObject atm, blocking adding signals/slots to Resolver + * in future seems to me worse than violating Liskov's law here. ~ domme + */ +unsigned int +DatabaseCollection::timeout() const +{ + return 0; +} + + +unsigned int +DatabaseCollection::weight() const +{ + return 0; +} + + +void +DatabaseCollection::resolve( const Tomahawk::query_ptr& query ) +{ + Q_UNUSED( query ); + Q_ASSERT(false); +} diff --git a/src/libtomahawk/database/DatabaseCollection.h b/src/libtomahawk/database/DatabaseCollection.h index 1f0b11777..621b42755 100644 --- a/src/libtomahawk/database/DatabaseCollection.h +++ b/src/libtomahawk/database/DatabaseCollection.h @@ -65,6 +65,11 @@ public: int trackCount() const override; QPixmap icon( const QSize& size ) const override; + // Resolver interface + unsigned int weight() const override; + unsigned int timeout() const override; + void resolve( const Tomahawk::query_ptr& query ) override; + public slots: virtual void addTracks( const QList& newitems ); virtual void removeTracks( const QDir& dir ); diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 92923fc57..1e51bc9d3 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -538,30 +538,43 @@ JSResolver::error() const void JSResolver::resolve( const Tomahawk::query_ptr& query ) { - ScriptJob* job = nullptr; - if ( !query->isFullTextQuery() ) - { - QVariantMap arguments; - arguments["artist"] = query->queryTrack()->artist(); - arguments["album"] = query->queryTrack()->album(); - arguments["track"] = query->queryTrack()->track(); + ScriptJob* job = scriptAccount()->resolve( scriptObject(), query ); - job = scriptObject()->invoke( "resolve", arguments ); - } - else - { - QVariantMap arguments; - arguments["query"] = query->fullTextQuery(); - job = scriptObject()->invoke( "search", arguments ); - } - - - job->setProperty( "qid", query->id() ); connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) ); job->start(); } +void +JSResolver::onResolveRequestDone( const QVariantMap& data ) +{ + Q_ASSERT( QThread::currentThread() == thread() ); + Q_D( JSResolver ); + + ScriptJob* job = qobject_cast< ScriptJob* >( sender() ); + + QID qid = job->property( "qid" ).toString(); + + if ( job->error() ) + { + Tomahawk::Pipeline::instance()->reportError( qid, this ); + } + else + { + + QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "results" ).toList() ); + + foreach( const result_ptr& result, results ) + { + result->setResolvedByResolver( this ); + result->setFriendlySource( name() ); + } + + Tomahawk::Pipeline::instance()->reportResults( qid, this, results ); + } + + sender()->deleteLater(); +} void JSResolver::stop() @@ -672,38 +685,6 @@ JSResolver::resolverUserConfig() } -void -JSResolver::onResolveRequestDone( const QVariantMap& data ) -{ - Q_ASSERT( QThread::currentThread() == thread() ); - Q_D( JSResolver ); - - ScriptJob* job = qobject_cast< ScriptJob* >( sender() ); - - QID qid = job->property( "qid" ).toString(); - - if ( job->error() ) - { - Tomahawk::Pipeline::instance()->reportError( qid, this ); - } - else - { - - QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "results" ).toList() ); - - foreach( const result_ptr& result, results ) - { - result->setResolvedByResolver( this ); - result->setFriendlySource( name() ); - } - - Tomahawk::Pipeline::instance()->reportResults( qid, this, results ); - } - - sender()->deleteLater(); -} - - QString JSResolver::instanceUUID() { diff --git a/src/libtomahawk/resolvers/Resolver.cpp b/src/libtomahawk/resolvers/Resolver.cpp index d31fb12a3..15202e514 100644 --- a/src/libtomahawk/resolvers/Resolver.cpp +++ b/src/libtomahawk/resolvers/Resolver.cpp @@ -17,10 +17,31 @@ */ #include "Resolver.h" +#include "../resolvers/SyncScriptJob.h" +#include "../Result.h" + #include + +Tomahawk::Resolver::Resolver( QObject* parent ) + : QObject( parent ) +{ +} + + QPixmap Tomahawk::Resolver::icon( const QSize& ) const { + Q_ASSERT(false); return QPixmap(); } + + +Tomahawk::ScriptJob* +Tomahawk::Resolver::getStreamUrl( const result_ptr& result ) +{ + QVariantMap data; + data[ "url" ] = result->url(); + + return new SyncScriptJob( data ); +} diff --git a/src/libtomahawk/resolvers/Resolver.h b/src/libtomahawk/resolvers/Resolver.h index 29eb7fc9c..d2165e2c8 100644 --- a/src/libtomahawk/resolvers/Resolver.h +++ b/src/libtomahawk/resolvers/Resolver.h @@ -19,7 +19,6 @@ #ifndef RESOLVER_H #define RESOLVER_H -#include "../ResultProvider.h" #include "Typedefs.h" #include "DllMacro.h" @@ -36,21 +35,23 @@ namespace Tomahawk { +class ScriptJob; -class DLLEXPORT Resolver : public QObject, public ResultProvider +class DLLEXPORT Resolver : public QObject { Q_OBJECT public: - Resolver() {} + Resolver( QObject* parent = nullptr ); + + virtual QString name() const = 0; + virtual QPixmap icon( const QSize& size ) const; virtual unsigned int weight() const = 0; virtual unsigned int timeout() const = 0; - virtual QPixmap icon( const QSize& size ) const override; - -public slots: virtual void resolve( const Tomahawk::query_ptr& query ) = 0; + virtual ScriptJob* getStreamUrl( const result_ptr& result ); }; } //ns diff --git a/src/libtomahawk/resolvers/ScriptAccount.cpp b/src/libtomahawk/resolvers/ScriptAccount.cpp index 13dae9ada..bc23d05c2 100644 --- a/src/libtomahawk/resolvers/ScriptAccount.cpp +++ b/src/libtomahawk/resolvers/ScriptAccount.cpp @@ -353,3 +353,29 @@ ScriptAccount::scriptCollection( const QString& id ) const { return m_collectionFactory->scriptPlugins().value( id ); } + + +ScriptJob* +ScriptAccount::resolve( const scriptobject_ptr& scriptObject, const query_ptr& query ) +{ + ScriptJob* job = nullptr; + if ( !query->isFullTextQuery() ) + { + QVariantMap arguments; + arguments["artist"] = query->queryTrack()->artist(); + arguments["album"] = query->queryTrack()->album(); + arguments["track"] = query->queryTrack()->track(); + + job = scriptObject->invoke( "resolve", arguments ); + } + else + { + QVariantMap arguments; + arguments["query"] = query->fullTextQuery(); + job = scriptObject->invoke( "search", arguments ); + } + + job->setProperty( "qid", query->id() ); + + return job; +} diff --git a/src/libtomahawk/resolvers/ScriptAccount.h b/src/libtomahawk/resolvers/ScriptAccount.h index dfbfd78c9..18e4609c6 100644 --- a/src/libtomahawk/resolvers/ScriptAccount.h +++ b/src/libtomahawk/resolvers/ScriptAccount.h @@ -76,10 +76,13 @@ public: virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object ); - QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist ); - QSharedPointer< ScriptCollection > scriptCollection( const QString& id ) const; + + // helpers + QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist ); + ScriptJob* resolve( const scriptobject_ptr& scriptObject, const query_ptr& query ); + private slots: void onJobDeleted( const QString& jobId ); diff --git a/src/libtomahawk/resolvers/ScriptCollection.cpp b/src/libtomahawk/resolvers/ScriptCollection.cpp index b6c8d07aa..38036a8c4 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.cpp +++ b/src/libtomahawk/resolvers/ScriptCollection.cpp @@ -26,8 +26,10 @@ #include "resolvers/ScriptCommand_AllArtists.h" #include "resolvers/ScriptCommand_AllAlbums.h" #include "resolvers/ScriptCommand_AllTracks.h" +#include "resolvers/ScriptJob.h" #include "ScriptAccount.h" #include "Result.h" +#include "Pipeline.h" #include #include @@ -336,3 +338,58 @@ ScriptCollection::onIconFetched() reply->deleteLater(); } } + + +unsigned int +ScriptCollection::timeout() const +{ + return 0; +} + + +unsigned int +ScriptCollection::weight() const +{ + return 0; +} + + +void +ScriptCollection::resolve( const Tomahawk::query_ptr& query ) +{ + ScriptJob* job = scriptAccount()->resolve( scriptObject(), query ); + + connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onResolveRequestDone( QVariantMap ) ) ); + + job->start(); +} + + +void +ScriptCollection::onResolveRequestDone( const QVariantMap& data ) +{ + Q_ASSERT( QThread::currentThread() == thread() ); + + ScriptJob* job = qobject_cast< ScriptJob* >( sender() ); + + QID qid = job->property( "qid" ).toString(); + + if ( job->error() ) + { + Tomahawk::Pipeline::instance()->reportError( qid, this ); + } + else + { + QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "results" ).toList() ); + + foreach( const result_ptr& result, results ) + { + result->setResolvedByCollection( weakRef().toStrongRef() ); + result->setFriendlySource( name() ); + } + + Tomahawk::Pipeline::instance()->reportResults( qid, this, results ); + } + + sender()->deleteLater(); +} diff --git a/src/libtomahawk/resolvers/ScriptCollection.h b/src/libtomahawk/resolvers/ScriptCollection.h index 6d3d3944c..eb9d0ecb3 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.h +++ b/src/libtomahawk/resolvers/ScriptCollection.h @@ -91,10 +91,15 @@ public: void parseMetaData(); void parseMetaData( const QVariantMap& metadata ); + // Resolver interface + unsigned int weight() const override; + unsigned int timeout() const override; + void resolve( const Tomahawk::query_ptr& query ) override; ScriptJob* getStreamUrl( const result_ptr& result ) override; private slots: void onIconFetched(); + void onResolveRequestDone( const QVariantMap& data ); private: ScriptAccount* m_scriptAccount; diff --git a/src/libtomahawk/resolvers/plugins/ScriptCollectionFactory.cpp b/src/libtomahawk/resolvers/plugins/ScriptCollectionFactory.cpp index 164f36293..2c5116119 100644 --- a/src/libtomahawk/resolvers/plugins/ScriptCollectionFactory.cpp +++ b/src/libtomahawk/resolvers/plugins/ScriptCollectionFactory.cpp @@ -19,6 +19,7 @@ #include "SourceList.h" #include "../ScriptAccount.h" +#include "../../Pipeline.h" using namespace Tomahawk; @@ -29,12 +30,14 @@ void ScriptCollectionFactory::addPlugin( const QSharedPointer& collection->setOnline( true ); SourceList::instance()->addScriptCollection( collection ); + Pipeline::instance()->addResolver( collection.data() ); } void ScriptCollectionFactory::removePlugin( const QSharedPointer& collection ) const { collection->setOnline( false ); SourceList::instance()->removeScriptCollection( collection ); + Pipeline::instance()->removeResolver( collection.data() ); } QSharedPointer< ScriptCollection > ScriptCollectionFactory::createPlugin( const scriptobject_ptr& object, ScriptAccount* scriptAccount ) From f3c8038c42acef1ed609797a521c9b8440a7bb48 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 22:11:55 +0100 Subject: [PATCH 16/26] Remove legacy hack --- data/js/tomahawk.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 34254ad77..3be31d831 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -764,15 +764,6 @@ Tomahawk.PluginManager = { resolve: [], invokeSync: function (requestId, objectId, methodName, params) { - if (!Tomahawk.resolver.instance.apiVersion || Tomahawk.resolver.instance.apiVersion < 0.9) { - if (methodName === 'artistAlbums') { - methodName = 'albums'; - } else if (methodName === 'albumTracks') { - methodName = 'tracks'; - } - } - - if (this.objects[objectId][this.wrapperPrefix + methodName]) { methodName = this.wrapperPrefix + methodName; } From 16946592b7c16e95748b2731909330de34a7ad8e Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 22:12:16 +0100 Subject: [PATCH 17/26] Allow script jobs to return object members directly if they are not functions --- data/js/tomahawk.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 3be31d831..ad77cb8e9 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -778,7 +778,9 @@ Tomahawk.PluginManager = { } } - if (typeof this.objects[objectId][methodName] !== 'function') { + if (typeof this.objects[objectId][methodName] !== 'function' && this.objects[objectId][methodName]) { + return this.objects[objectId][methodName]; + } else if (typeof this.objects[objectId][methodName] !== 'function') { throw new Error('\'' + methodName + '\' on ScriptObject ' + objectId + ' is not a function', typeof this.objects[objectId][methodName]); } From a589e4f6889a9379234f2c2cb4607d468b71f54a Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 23:55:14 +0100 Subject: [PATCH 18/26] Expect javascript resolvers to return "tracks" instead of "results" --- data/js/tomahawk.js | 23 ++++++++----------- src/libtomahawk/resolvers/JSResolver.cpp | 2 +- .../resolvers/ScriptCollection.cpp | 2 +- .../resolvers/ScriptCommand_AllTracks.cpp | 2 +- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index ad77cb8e9..99de6ac3d 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -274,7 +274,7 @@ Tomahawk.Resolver = { _adapter_resolve: function (params) { return RSVP.Promise.resolve(this.resolve(params)).then(function (results) { return { - 'results': results + 'tracks': results }; }); }, @@ -282,7 +282,7 @@ Tomahawk.Resolver = { _adapter_search: function (params) { return RSVP.Promise.resolve(this.search(params)).then(function (results) { return { - 'results': results + 'tracks': results }; }); }, @@ -1558,7 +1558,7 @@ Tomahawk.Collection = { _adapter_resolve: function (params) { return RSVP.Promise.resolve(this.resolve(params)).then(function (results) { return { - 'results': results + 'tracks': results }; }); }, @@ -1568,17 +1568,14 @@ Tomahawk.Collection = { return this._fuzzyIndexIdsToTracks(resultIds); }, - _adapter_search: function (params) { - return RSVP.Promise.resolve(this.search(params)).then(function (results) { - return { - 'results': results - }; - }); - }, - search: function (params) { var resultIds = Tomahawk.searchFuzzyIndex(params.query); - return this._fuzzyIndexIdsToTracks(resultIds); + + return this._fuzzyIndexIdsToTracks(resultIds).then(function(tracks) { + return { + tracks: tracks + }; + }); }, tracks: function (params, where) { @@ -1625,7 +1622,7 @@ Tomahawk.Collection = { ); return t.execDeferredStatements(); }).then(function (results) { - return {results: results[0]}; + return {tracks: results[0]}; }); }, diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 1e51bc9d3..bb0507918 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -562,7 +562,7 @@ JSResolver::onResolveRequestDone( const QVariantMap& data ) else { - QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "results" ).toList() ); + QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() ); foreach( const result_ptr& result, results ) { diff --git a/src/libtomahawk/resolvers/ScriptCollection.cpp b/src/libtomahawk/resolvers/ScriptCollection.cpp index 38036a8c4..67c14409e 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.cpp +++ b/src/libtomahawk/resolvers/ScriptCollection.cpp @@ -380,7 +380,7 @@ ScriptCollection::onResolveRequestDone( const QVariantMap& data ) } else { - QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "results" ).toList() ); + QList< Tomahawk::result_ptr > results = scriptAccount()->parseResultVariantList( data.value( "tracks" ).toList() ); foreach( const result_ptr& result, results ) { diff --git a/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp b/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp index 1251cebbd..2164c66a1 100644 --- a/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp +++ b/src/libtomahawk/resolvers/ScriptCommand_AllTracks.cpp @@ -117,7 +117,7 @@ ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result ) QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >(); Q_ASSERT( !collection.isNull() ); - QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "results"].toList() ); + QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "tracks" ].toList() ); QList< Tomahawk::query_ptr > queries; From 8679713dea6d8afc36e8fe28467b44de0832f157 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 23:56:12 +0100 Subject: [PATCH 19/26] Remove more leftovers from the old collection hack --- data/js/tomahawk.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index 99de6ac3d..c635c3d92 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -747,10 +747,6 @@ Tomahawk.PluginManager = { }, registerPlugin: function (type, object) { this.objects[this.identifyObject(object)] = object; - if (type === 'collection') { - Tomahawk.collections.push(object); - } - Tomahawk.log("registerPlugin: " + type + " id: " + object.id); Tomahawk.registerScriptPlugin(type, object.id); }, @@ -1108,8 +1104,6 @@ Tomahawk.Country = { LatinAmericaAndTheCaribbean: 246 }; -Tomahawk.collections = []; - Tomahawk.Collection = { BrowseCapability: { Artists: 1, From 88099eae0eaf0d466175bd0ba8c23a108ca5c93d Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 23:57:08 +0100 Subject: [PATCH 20/26] Don't use collectionId from resolvers anymore --- data/js/tomahawk.js | 9 ++------- src/libtomahawk/resolvers/ScriptAccount.cpp | 22 --------------------- src/libtomahawk/resolvers/ScriptAccount.h | 3 --- 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index c635c3d92..ad6d2b0f9 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -1497,7 +1497,6 @@ Tomahawk.Collection = { }, _fuzzyIndexIdsToTracks: function (resultIds, id) { - var collection = this; if (typeof id === 'undefined') { id = this.settings.id; } @@ -1514,8 +1513,7 @@ Tomahawk.Collection = { linkUrl: row.linkUrl, releaseyear: row.releaseyear, bitrate: row.bitrate, - albumpos: row.albumPos, - collectionId: collection.id + albumpos: row.albumPos }; }; for (var idx = 0; resultIds && idx < resultIds.length; idx++) { @@ -1573,8 +1571,6 @@ Tomahawk.Collection = { }, tracks: function (params, where) { - var collection = this; - //TODO filter/where support var id = params.id; if (typeof id === 'undefined') { @@ -1594,8 +1590,7 @@ Tomahawk.Collection = { linkUrl: row.linkUrl, releaseyear: row.releaseyear, bitrate: row.bitrate, - albumpos: row.albumPos, - collectionId: collection.id + albumpos: row.albumPos }; }; t.sqlSelect("tracks", mapFn, diff --git a/src/libtomahawk/resolvers/ScriptAccount.cpp b/src/libtomahawk/resolvers/ScriptAccount.cpp index bc23d05c2..c798267ca 100644 --- a/src/libtomahawk/resolvers/ScriptAccount.cpp +++ b/src/libtomahawk/resolvers/ScriptAccount.cpp @@ -326,21 +326,6 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist ) } rp->setDownloadFormats( fl ); - // find collection - const QString collectionId = m.value( "collectionId" ).toString(); - if ( !collectionId.isEmpty() ) - { - if ( scriptCollection( collectionId ).isNull() ) - { - tLog() << "Resolver returned invalid collection id"; - Q_ASSERT( false ); - } - else - { - rp->setResolvedByCollection( scriptCollection( collectionId ) ); - } - } - results << rp; } @@ -348,13 +333,6 @@ ScriptAccount::parseResultVariantList( const QVariantList& reslist ) } -QSharedPointer< ScriptCollection > -ScriptAccount::scriptCollection( const QString& id ) const -{ - return m_collectionFactory->scriptPlugins().value( id ); -} - - ScriptJob* ScriptAccount::resolve( const scriptobject_ptr& scriptObject, const query_ptr& query ) { diff --git a/src/libtomahawk/resolvers/ScriptAccount.h b/src/libtomahawk/resolvers/ScriptAccount.h index 18e4609c6..aebca5a1c 100644 --- a/src/libtomahawk/resolvers/ScriptAccount.h +++ b/src/libtomahawk/resolvers/ScriptAccount.h @@ -76,9 +76,6 @@ public: virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object ); - QSharedPointer< ScriptCollection > scriptCollection( const QString& id ) const; - - // helpers QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist ); ScriptJob* resolve( const scriptobject_ptr& scriptObject, const query_ptr& query ); From 6823b578230abd40ece1586962ddef41128bf7d8 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 23:58:10 +0100 Subject: [PATCH 21/26] Show friendlySource in alternate sources box tooltips --- src/libtomahawk/playlist/TrackDetailView.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/TrackDetailView.cpp b/src/libtomahawk/playlist/TrackDetailView.cpp index 30c5f4b8f..aeae73f38 100644 --- a/src/libtomahawk/playlist/TrackDetailView.cpp +++ b/src/libtomahawk/playlist/TrackDetailView.cpp @@ -262,8 +262,15 @@ TrackDetailView::onResultsChanged() resolverLabel->setFont( f ); resolverLabel->setStyleSheet( "QLabel { color: rgba( 0, 0, 0, 50% ) }" ); resolverLabel->setText( QString( "%1 - %2" ).arg( result->track()->track() ).arg( result->track()->artist() ) ); - resolverLabel->setToolTip( QString( "%1 by %2%3" ).arg( result->track()->track() ).arg( result->track()->artist() ) - .arg( !result->track()->album().isEmpty() ? QString( " " ) + tr( "on %1" ).arg( result->track()->album() ) : QString() ) ); + resolverLabel->setToolTip( + QString( "%1 by %2%3 (%4)" ) + .arg( result->track()->track() ) + .arg( result->track()->artist() ) + .arg( !result->track()->album().isEmpty() ? QString( " " ) + tr( "on %1" ).arg( result->track()->album() ) : QString() ) + .arg( result->friendlySource() ) + ); + + ; resolverLabel->setFixedWidth( width() - 32 - 4 ); NewClosure( resolverLabel, SIGNAL( clicked() ), const_cast< AudioEngine* >( AudioEngine::instance() ), From 576c91eb19be2e2c6d4a61835a59a1780edcf62f Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 20 Nov 2015 23:58:47 +0100 Subject: [PATCH 22/26] Set *pretty* collection name as result friendly source --- src/libtomahawk/resolvers/ScriptCollection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/resolvers/ScriptCollection.cpp b/src/libtomahawk/resolvers/ScriptCollection.cpp index 67c14409e..c45572e8a 100644 --- a/src/libtomahawk/resolvers/ScriptCollection.cpp +++ b/src/libtomahawk/resolvers/ScriptCollection.cpp @@ -385,7 +385,7 @@ ScriptCollection::onResolveRequestDone( const QVariantMap& data ) foreach( const result_ptr& result, results ) { result->setResolvedByCollection( weakRef().toStrongRef() ); - result->setFriendlySource( name() ); + result->setFriendlySource( prettyName() ); } Tomahawk::Pipeline::instance()->reportResults( qid, this, results ); From e697303743144a06e6af04dd6f186a1f997c91ed Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sat, 21 Nov 2015 00:45:03 +0100 Subject: [PATCH 23/26] Properly log errors to javascript console --- data/js/tomahawk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index ad6d2b0f9..c51503185 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -49,7 +49,7 @@ RSVP.on('error', function (reason) { resolverName = Tomahawk.resolver.instance.settings.name + " - "; } if (reason) { - console.error(resolverName + 'Uncaught error:' + JSON.stringify(reason)); + console.error(resolverName + 'Uncaught error:', reason); } else { console.error(resolverName + 'Uncaught error: error thrown from RSVP but it was empty'); } From 03935f26a9d17ed6350f1061efa919c6733f38a2 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sat, 21 Nov 2015 00:45:17 +0100 Subject: [PATCH 24/26] Use ordinary wrapper function for testConfig --- src/libtomahawk/accounts/ResolverAccount.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/accounts/ResolverAccount.cpp b/src/libtomahawk/accounts/ResolverAccount.cpp index 27e83285e..0ed4e1bd7 100644 --- a/src/libtomahawk/accounts/ResolverAccount.cpp +++ b/src/libtomahawk/accounts/ResolverAccount.cpp @@ -524,7 +524,7 @@ void ResolverAccount::testConfig() if ( resolver ) { QVariantMap data = resolver->loadDataFromWidgets(); - ScriptJob* job = resolver->scriptObject()->invoke( "_testConfig", data ); + ScriptJob* job = resolver->scriptObject()->invoke( "testConfig", data ); connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onTestConfig( QVariantMap ) ) ); job->start(); } From d0c1d83f903a00416c80a6b8dc6c6a7a775b2f32 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sat, 21 Nov 2015 16:36:10 +0100 Subject: [PATCH 25/26] Remove more comp hacks --- data/js/tomahawk.js | 58 --------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/data/js/tomahawk.js b/data/js/tomahawk.js index c51503185..bd382746a 100644 --- a/data/js/tomahawk.js +++ b/data/js/tomahawk.js @@ -1795,61 +1795,3 @@ Tomahawk.Collection = { return params; } }; - -// Legacy compability for 0.8 and before -Tomahawk.reportCapabilities = function (capabilities) { - if (capabilities & TomahawkResolverCapability.Browsable) { - Tomahawk.PluginManager.registerPlugin("collection", Tomahawk.resolver.instance); - } - - Tomahawk.nativeReportCapabilities(capabilities); -}; - -Tomahawk.addArtistResults = Tomahawk.addAlbumResults = Tomahawk.addAlbumTrackResults - = function (result) { - Tomahawk.PluginManager.resolve[result.qid](result); - delete Tomahawk.PluginManager.resolve[result.qid]; -}; - -Tomahawk.addTrackResults = function (result) { - Tomahawk.PluginManager.resolve[result.qid](result.results); - delete Tomahawk.PluginManager.resolve[result.qid]; -}; - -Tomahawk.addUrlResult = function (url, result) { - /* Merge the whole mess into one consistent result which is independent of type - var cleanResult = { - type: result.type, - guid: result.guid, - info: result.info, - creator: result.creator, - linkUrl: result.url - }; - if (cleanResult.type == "track") { - cleanResult.track = result.title; - cleanResult.artist = result.artist; - } else if (cleanResult.type == "artist") { - cleanResult.artist = result.name; - } else if (cleanResult.type == "album") { - cleanResult.album = result.name; - cleanResult.artist = result.artist; - } else if (cleanResult.type == "playlist") { - cleanResult.title = result.title; - } else if (cleanResult.type == "xspf-url") { - cleanResult.url = result.url; - } - if (result.tracks) { - cleanResult.tracks = []; - var i; - for (i=0;i Date: Mon, 14 Dec 2015 21:49:45 +0100 Subject: [PATCH 26/26] Don't leak script jobs --- src/libtomahawk/audio/AudioEngine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index f5c2a92c8..008353642 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -621,6 +621,8 @@ AudioEngine::gotStreamUrl( const QVariantMap& data ) NetworkReply* reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) ); NewClosure( reply, SIGNAL( finalUrlReached() ), this, SLOT( gotRedirectedStreamUrl( Tomahawk::result_ptr, NetworkReply* )), result, reply ); } + + sender()->deleteLater(); } void