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 )