1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-07-31 19:30:21 +02:00

Port script collections to new script system

This commit is contained in:
Dominik Schmidt
2015-01-09 20:20:32 +01:00
parent 2455d5f12d
commit 25ba94f183
20 changed files with 501 additions and 437 deletions

View File

@@ -615,6 +615,12 @@ Tomahawk.PluginManager = {
Tomahawk.registerScriptPlugin(type, object.id);
},
unregisterPlugin: function(type, object) {
this.objects[this.identifyObject(object)] = object;
Tomahawk.log("unregisterPlugin: " + type + " id: " + object.id);
Tomahawk.unregisterScriptPlugin(type, object.id);
},
invokeSync: function (objectId, methodName, params) {
if (!this.objects[objectId]) {

View File

@@ -303,6 +303,8 @@ Result::setResolvedByCollection( const Tomahawk::collection_ptr& collection , bo
m_collection = collection;
if ( emitOnlineEvents )
{
Q_ASSERT( !collection.isNull() );
connect( collection.data(), SIGNAL( destroyed( QObject * ) ), SLOT( onOffline() ), Qt::QueuedConnection );
connect( collection->source().data(), SIGNAL( online() ), SLOT( onOnline() ), Qt::QueuedConnection );
connect( collection->source().data(), SIGNAL( offline() ), SLOT( onOffline() ), Qt::QueuedConnection );
}

View File

@@ -52,6 +52,8 @@ public:
QList<Tomahawk::source_ptr> sources( bool onlyOnline = false ) const;
unsigned int count() const;
void addScriptCollection( const Tomahawk::collection_ptr& collection );
void removeScriptCollection( const Tomahawk::collection_ptr& collection );
QList<Tomahawk::collection_ptr> scriptCollections() const;
Tomahawk::source_ptr get( const QString& username, const QString& friendlyName = QString(), bool autoCreate = false );
@@ -81,9 +83,6 @@ private slots:
void latchedOn( const Tomahawk::source_ptr& );
void latchedOff( const Tomahawk::source_ptr& );
void addScriptCollection( const Tomahawk::collection_ptr& collection );
void removeScriptCollection( const Tomahawk::collection_ptr& collection );
private:
void add( const Tomahawk::source_ptr& source );

View File

@@ -25,9 +25,6 @@
#include "DllMacro.h"
#include "Resolver.h"
#include "ScriptCommandQueue.h"
#include "ScriptCommand_AllArtists.h"
#include "ScriptCommand_AllAlbums.h"
#include "ScriptCommand_AllTracks.h"
#include "ScriptCommand_LookupUrl.h"
#include "Typedefs.h"
@@ -49,9 +46,6 @@ class DLLEXPORT ExternalResolver : public Resolver
Q_OBJECT
friend class ScriptCommandQueue;
friend class ScriptCommand_AllArtists;
friend class ScriptCommand_AllAlbums;
friend class ScriptCommand_AllTracks;
friend class ScriptCommand_LookupUrl;
public:
@@ -120,10 +114,6 @@ protected:
ScriptCommandQueue* m_commandQueue;
// Should only be called by ScriptCommands
// ScriptCollection
virtual void artists( const Tomahawk::collection_ptr& collection ) = 0;
virtual void albums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist ) = 0;
virtual void tracks( const Tomahawk::collection_ptr& collection, const Tomahawk::album_ptr& album ) = 0;
// UrlLookup
virtual void lookupUrl( const QString& url ) = 0;

View File

@@ -48,11 +48,9 @@
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QImageReader>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QMetaProperty>
#include <QTime>
#include <QWebFrame>
using namespace Tomahawk;
@@ -68,6 +66,8 @@ JSResolver::JSResolver( const QString& accountId, const QString& scriptPath, con
d->name = QFileInfo( filePath() ).baseName();
d->scriptAccount.reset( new JSAccount( d->name ) );
d->scriptAccount->setResolver( this );
d->scriptAccount->setFilePath( filePath() );
d->scriptAccount->setIcon( icon( QSize( 0, 0 ) ) );
// set the icon, if we launch properly we'll get the icon the resolver reports
d->icon = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultResolver, TomahawkUtils::Original, QSize( 128, 128 ) );
@@ -312,115 +312,6 @@ JSResolver::start()
}
void
JSResolver::artists( const Tomahawk::collection_ptr& collection )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "artists", Qt::QueuedConnection, Q_ARG( Tomahawk::collection_ptr, collection ) );
return;
}
Q_D( const JSResolver );
if ( /* !m_collections.contains( collection->name() ) || */ //if the collection doesn't belong to this resolver
!d->capabilities.testFlag( Browsable ) ) //or this resolver doesn't even support collections
{
emit artistsFound( QList< Tomahawk::artist_ptr >() );
return;
}
QString eval = QString( "artists( '%1' )" )
.arg( JSAccount::escape( collection->name() ) );
QVariantMap m = callOnResolver( eval ).toMap();
if ( m.isEmpty() )
{
// if the resolver doesn't return anything, async api is used
return;
}
QString errorMessage = tr( "Script Resolver Warning: API call %1 returned data synchronously." ).arg( eval );
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) );
tDebug() << errorMessage << m;
}
void
JSResolver::albums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "albums", Qt::QueuedConnection,
Q_ARG( Tomahawk::collection_ptr, collection ),
Q_ARG( Tomahawk::artist_ptr, artist ) );
return;
}
Q_D( const JSResolver );
if ( /* !m_collections.contains( collection->name() ) || */ //if the collection doesn't belong to this resolver
!d->capabilities.testFlag( Browsable ) ) //or this resolver doesn't even support collections
{
emit albumsFound( QList< Tomahawk::album_ptr >() );
return;
}
QString eval = QString( "albums( '%1', '%2' )" )
.arg( JSAccount::escape( collection->name() ) )
.arg( JSAccount::escape( artist->name() ) );
QVariantMap m = callOnResolver( eval ).toMap();
if ( m.isEmpty() )
{
// if the resolver doesn't return anything, async api is used
return;
}
QString errorMessage = tr( "Script Resolver Warning: API call %1 returned data synchronously." ).arg( eval );
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) );
tDebug() << errorMessage << m;
}
void
JSResolver::tracks( const Tomahawk::collection_ptr& collection, const Tomahawk::album_ptr& album )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "tracks", Qt::QueuedConnection,
Q_ARG( Tomahawk::collection_ptr, collection ),
Q_ARG( Tomahawk::album_ptr, album ) );
return;
}
Q_D( const JSResolver );
if ( /* !m_collections.contains( collection->name() ) || */ //if the collection doesn't belong to this resolver
!d->capabilities.testFlag( Browsable ) ) //or this resolver doesn't even support collections
{
emit tracksFound( QList< Tomahawk::query_ptr >() );
return;
}
QString eval = QString( "tracks( '%1', '%2', '%3' )" )
.arg( JSAccount::escape( collection->name() ) )
.arg( JSAccount::escape( album->artist()->name() ) )
.arg( JSAccount::escape( album->name() ) );
QVariantMap m = callOnResolver( eval ).toMap();
if ( m.isEmpty() )
{
// if the resolver doesn't return anything, async api is used
return;
}
QString errorMessage = tr( "Script Resolver Warning: API call %1 returned data synchronously." ).arg( eval );
JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorMessage ) );
tDebug() << errorMessage << m;
}
bool
JSResolver::canParseUrl( const QString& url, UrlType type )
{
@@ -518,140 +409,6 @@ JSResolver::resolve( const Tomahawk::query_ptr& query )
}
QVariantMap m = callOnResolver( eval ).toMap();
if ( m.isEmpty() )
{
// if the resolver doesn't return anything, async api is used
return;
}
qDebug() << "JavaScript Result:" << m;
const QString qid = query->id();
const QVariantList reslist = m.value( "results" ).toList();
QList< Tomahawk::result_ptr > results = parseResultVariantList( reslist );
Tomahawk::Pipeline::instance()->reportResults( qid, results );
}
QList< Tomahawk::result_ptr >
JSResolver::parseResultVariantList( const QVariantList& reslist )
{
QList< Tomahawk::result_ptr > results;
foreach( const QVariant& rv, reslist )
{
QVariantMap m = rv.toMap();
// TODO we need to handle preview urls separately. they should never trump a real url, and we need to display
// the purchaseUrl for the user to upgrade to a full stream.
if ( m.value( "preview" ).toBool() == true )
continue;
int duration = m.value( "duration", 0 ).toInt();
if ( duration <= 0 && m.contains( "durationString" ) )
{
QTime time = QTime::fromString( m.value( "durationString" ).toString(), "hh:mm:ss" );
duration = time.secsTo( QTime( 0, 0 ) ) * -1;
}
Tomahawk::track_ptr track = Tomahawk::Track::get( m.value( "artist" ).toString(),
m.value( "track" ).toString(),
m.value( "album" ).toString(),
m.value( "albumArtist" ).toString(),
duration,
QString(),
m.value( "albumpos" ).toUInt(),
m.value( "discnumber" ).toUInt() );
if ( !track )
continue;
Tomahawk::result_ptr rp = Tomahawk::Result::get( m.value( "url" ).toString(), track );
if ( !rp )
continue;
rp->setBitrate( m.value( "bitrate" ).toUInt() );
rp->setSize( m.value( "size" ).toUInt() );
rp->setRID( uuid() );
rp->setFriendlySource( name() );
rp->setPurchaseUrl( m.value( "purchaseUrl" ).toString() );
rp->setLinkUrl( m.value( "linkUrl" ).toString() );
rp->setScore( m.value( "score" ).toFloat() );
rp->setChecked( m.value( "checked" ).toBool() );
//FIXME
if ( m.contains( "year" ) )
{
QVariantMap attr;
attr[ "releaseyear" ] = m.value( "year" );
// rp->track()->setAttributes( attr );
}
rp->setMimetype( m.value( "mimetype" ).toString() );
if ( rp->mimetype().isEmpty() )
{
rp->setMimetype( TomahawkUtils::extensionToMimetype( m.value( "extension" ).toString() ) );
Q_ASSERT( !rp->mimetype().isEmpty() );
}
rp->setResolvedByResolver( this );
// find collection
const QString collectionId = m.value( "collectionId" ).toString();
if ( !collectionId.isEmpty() )
{
Tomahawk::collection_ptr collection = Tomahawk::collection_ptr();
if ( !collection.isNull() )
{
rp->setResolvedByCollection( collection );
}
}
results << rp;
}
return results;
}
QList< Tomahawk::artist_ptr >
JSResolver::parseArtistVariantList( const QVariantList& reslist )
{
QList< Tomahawk::artist_ptr > results;
foreach( const QVariant& rv, reslist )
{
const QString val = rv.toString();
if ( val.trimmed().isEmpty() )
continue;
Tomahawk::artist_ptr ap = Tomahawk::Artist::get( val, false );
results << ap;
}
return results;
}
QList< Tomahawk::album_ptr >
JSResolver::parseAlbumVariantList( const Tomahawk::artist_ptr& artist, const QVariantList& reslist )
{
QList< Tomahawk::album_ptr > results;
foreach( const QVariant& rv, reslist )
{
const QString val = rv.toString();
if ( val.trimmed().isEmpty() )
continue;
Tomahawk::album_ptr ap = Tomahawk::Album::get( artist, val, false );
results << ap;
}
return results;
}
@@ -662,6 +419,7 @@ JSResolver::stop()
d->stopped = true;
scriptAccount()->stop();
Tomahawk::Pipeline::instance()->removeResolver( this );
emit stopped();
@@ -738,6 +496,15 @@ JSResolver::loadDataFromWidgets()
}
ScriptAccount*
JSResolver::scriptAccount() const
{
Q_D( const JSResolver );
return d->scriptAccount.get();
}
void
JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capabilities )
{
@@ -747,30 +514,6 @@ JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capa
}
void
JSResolver::onCollectionIconFetched()
{
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
if ( reply != 0 )
{
Tomahawk::collection_ptr collection;
/* collection = m_collections.value( reply->property( "collectionName" ).toString() ); */
if ( !collection.isNull() )
{
if ( reply->error() == QNetworkReply::NoError )
{
QImageReader imageReader( reply );
QPixmap collectionIcon = QPixmap::fromImageReader( &imageReader );
if ( !collectionIcon.isNull() )
qobject_cast< Tomahawk::ScriptCollection* >( collection.data() )->setIcon( collectionIcon );
}
}
reply->deleteLater();
}
}
QVariantMap
JSResolver::resolverSettings()
{

View File

@@ -73,15 +73,13 @@ public:
QVariantMap loadDataFromWidgets();
ScriptAccount* scriptAccount() const;
public slots:
void resolve( const Tomahawk::query_ptr& query ) override;
void stop() override;
void start() override;
// For ScriptCollection
void artists( const Tomahawk::collection_ptr& collection ) override;
void albums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist ) override;
void tracks( const Tomahawk::collection_ptr& collection, const Tomahawk::album_ptr& album ) override;
// For UrlLookup
void lookupUrl( const QString& url ) override;
@@ -91,9 +89,6 @@ signals:
protected:
QVariant callOnResolver( const QString& scriptSource );
private slots:
void onCollectionIconFetched();
private:
void init();
@@ -105,11 +100,6 @@ private:
QVariantMap resolverUserConfig();
QVariantMap resolverInit();
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
QList< Tomahawk::artist_ptr > parseArtistVariantList( const QVariantList& reslist );
QList< Tomahawk::album_ptr > parseAlbumVariantList( const Tomahawk::artist_ptr& artist,
const QVariantList& reslist );
Q_DECLARE_PRIVATE( JSResolver )
QScopedPointer<JSResolverPrivate> d_ptr;
};

View File

@@ -41,6 +41,10 @@
#include "SourceList.h"
#include "UrlHandler.h"
#include "JSAccount.h"
#include "../Album.h"
#include "../Artist.h"
#include "../Result.h"
#include "../Track.h"
#include <QFile>
#include <QFileInfo>
@@ -133,8 +137,18 @@ JSResolverHelper::log( const QString& message )
void
JSResolverHelper::addTrackResults( const QVariantMap& results )
{
qDebug() << "Resolver reporting results:" << results;
QList< Tomahawk::result_ptr > tracks = m_resolver->parseResultVariantList( results.value("results").toList() );
tLog() << "Resolver reporting results:" << m_resolver->name() << 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 )
{
tLog() << "Found result: " << track->track()->track() << "by" << track->track()->artist();
track->setResolvedByResolver( m_resolver );
track->setFriendlySource( "FOOBAR" );
}
QString qid = results.value("qid").toString();
@@ -142,86 +156,6 @@ JSResolverHelper::addTrackResults( const QVariantMap& results )
}
void
JSResolverHelper::addArtistResults( const QVariantMap& results )
{
qDebug() << "Resolver reporting artists:" << results;
QList< Tomahawk::artist_ptr > artists = m_resolver->parseArtistVariantList( results.value( "artists" ).toList() );
QString qid = results.value("qid").toString();
Tomahawk::collection_ptr collection = Tomahawk::collection_ptr();
if ( collection.isNull() )
return;
tDebug() << Q_FUNC_INFO << "about to push" << artists.count() << "artists";
foreach( const Tomahawk::artist_ptr& artist, artists)
tDebug() << artist->name();
emit m_resolver->artistsFound( artists );
}
void
JSResolverHelper::addAlbumResults( const QVariantMap& results )
{
qDebug() << "Resolver reporting albums:" << results;
QString artistName = results.value( "artist" ).toString();
if ( artistName.trimmed().isEmpty() )
return;
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistName, false );
QList< Tomahawk::album_ptr > albums = m_resolver->parseAlbumVariantList( artist, results.value( "albums" ).toList() );
QString qid = results.value("qid").toString();
Tomahawk::collection_ptr collection = Tomahawk::collection_ptr();
if ( collection.isNull() )
return;
tDebug() << Q_FUNC_INFO << "about to push" << albums.count() << "albums";
foreach( const Tomahawk::album_ptr& album, albums)
tDebug() << album->name();
emit m_resolver->albumsFound( albums );
}
void
JSResolverHelper::addAlbumTrackResults( const QVariantMap& results )
{
qDebug() << "Resolver reporting album tracks:" << results;
QString artistName = results.value( "artist" ).toString();
if ( artistName.trimmed().isEmpty() )
return;
QString albumName = results.value( "album" ).toString();
if ( albumName.trimmed().isEmpty() )
return;
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistName, false );
Tomahawk::album_ptr album = Tomahawk::Album::get( artist, albumName, false );
QList< Tomahawk::result_ptr > tracks = m_resolver->parseResultVariantList( results.value("results").toList() );
QString qid = results.value("qid").toString();
Tomahawk::collection_ptr collection = Tomahawk::collection_ptr();
if ( collection.isNull() )
return;
QList< Tomahawk::query_ptr > queries;
foreach ( const Tomahawk::result_ptr& result, tracks )
{
result->setScore( 1.0 );
result->setResolvedByCollection( collection );
queries.append( result->toQuery() );
}
tDebug() << Q_FUNC_INFO << "about to push" << tracks.count() << "tracks";
emit m_resolver->tracksFound( queries );
}
query_ptr
JSResolverHelper::parseTrack( const QVariantMap& track )
{
@@ -396,12 +330,19 @@ JSResolverHelper::reportScriptJobResults( const QVariantMap& result )
void
JSResolverHelper::registerScriptPlugin(const QString& type, const QString& objectId)
JSResolverHelper::registerScriptPlugin( const QString& type, const QString& objectId )
{
m_resolver->d_func()->scriptAccount->registerScriptPlugin( type, objectId );
}
void
JSResolverHelper::unregisterScriptPlugin( const QString& type, const QString& objectId )
{
m_resolver->d_func()->scriptAccount->unregisterScriptPlugin( type, objectId );
}
void
JSResolverHelper::tracksAdded( const QList<query_ptr>&, const ModelMode, const collection_ptr&)
{

View File

@@ -143,10 +143,6 @@ public slots:
void addTrackResults( const QVariantMap& results );
void addArtistResults( const QVariantMap& results );
void addAlbumResults( const QVariantMap& results );
void addAlbumTrackResults( const QVariantMap& results );
void addUrlResult( const QString& url, const QVariantMap& result );
void reportCapabilities( const QVariant& capabilities );
@@ -154,6 +150,7 @@ public slots:
void reportScriptJobResults( const QVariantMap& result );
void registerScriptPlugin( const QString& type, const QString& objectId );
void unregisterScriptPlugin( const QString& type, const QString& objectId );
private slots:
void gotStreamUrl( IODeviceCallback callback, NetworkReply* reply );

View File

@@ -26,6 +26,15 @@
#include "../utils/LinkGenerator.h"
#include "ScriptLinkGeneratorPlugin.h"
#include "ScriptInfoPlugin.h"
#include "SourceList.h"
#include "ScriptCollection.h"
// TODO:
#include "../Result.h"
#include "../Track.h"
#include <QTime>
#include <QFileInfo>
using namespace Tomahawk;
@@ -37,6 +46,51 @@ ScriptAccount::ScriptAccount( const QString& name )
}
void
ScriptAccount::stop()
{
foreach( const QWeakPointer< ScriptCollection >& collection, m_collections.hash().values() )
{
unregisterScriptPlugin( "collection", collection.data()->scriptObject()->id() );
}
}
const QString
ScriptAccount::name() const
{
return m_name;
}
void
ScriptAccount::setIcon(const QPixmap& icon)
{
m_icon = icon;
}
const QPixmap
ScriptAccount::icon() const
{
return m_icon;
}
void
ScriptAccount::setFilePath( const QString& filePath )
{
m_filePath = filePath;
}
const QString
ScriptAccount::filePath() const
{
return m_filePath;
}
static QString
requestIdGenerator()
{
@@ -102,6 +156,33 @@ ScriptAccount::registerScriptPlugin( const QString& type, const QString& objectI
}
void
ScriptAccount::unregisterScriptPlugin( const QString& type, const QString& objectId )
{
scriptobject_ptr object = m_objects.value( objectId );
if( !object )
{
tLog() << "ScriptAccount" << name() << "tried to unregister plugin that was not registered";
return;
}
if ( type == "collection" )
{
collection_ptr collection = scriptCollection( objectId );
if ( !collection.isNull() )
{
SourceList::instance()->removeScriptCollection( collection );
}
}
else
{
tLog() << "This plugin type is not handled by Tomahawk or simply cannot be removed yet";
Q_ASSERT( false );
}
}
void
ScriptAccount::onScriptObjectDeleted()
{
@@ -137,6 +218,54 @@ ScriptAccount::scriptPluginFactory( const QString& type, const scriptobject_ptr&
// add it to infosystem
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin );
}
else if( type == "collection" )
{
if ( !scriptCollection( object->id() ).isNull() )
return;
const QVariantMap collectionInfo = object->syncInvoke( "collection" ).toMap();
if ( collectionInfo.isEmpty() ||
!collectionInfo.contains( "prettyname" ) ||
!collectionInfo.contains( "description" ) )
return;
const QString prettyname = collectionInfo.value( "prettyname" ).toString();
const QString desc = collectionInfo.value( "description" ).toString();
// at this point we assume that all the tracks browsable through a resolver belong to the local source
Tomahawk::ScriptCollection* sc = new Tomahawk::ScriptCollection( object, SourceList::instance()->getLocal(), this );
QSharedPointer<ScriptCollection> collection( sc );
collection->setWeakRef( collection.toWeakRef() );
sc->setServiceName( prettyname );
sc->setDescription( desc );
if ( collectionInfo.contains( "trackcount" ) ) //a resolver might not expose this
{
bool ok = false;
int trackCount = collectionInfo.value( "trackcount" ).toInt( &ok );
if ( ok )
sc->setTrackCount( trackCount );
}
if ( collectionInfo.contains( "iconfile" ) )
{
QString iconPath = QFileInfo( filePath() ).path() + "/"
+ collectionInfo.value( "iconfile" ).toString();
QPixmap iconPixmap;
bool ok = iconPixmap.load( iconPath );
if ( ok && !iconPixmap.isNull() )
sc->setIcon( iconPixmap );
}
SourceList::instance()->addScriptCollection( collection );
sc->fetchIcon( collectionInfo.value( "iconurl" ).toString() );
m_collections.insert( object->id(), collection );
}
else
{
tLog() << "This plugin type is not handled by Tomahawk";
@@ -150,3 +279,92 @@ ScriptAccount::onJobDeleted( const QString& jobId )
{
m_jobs.remove( jobId );
}
QList< Tomahawk::result_ptr >
ScriptAccount::parseResultVariantList( const QVariantList& reslist )
{
QList< Tomahawk::result_ptr > results;
foreach( const QVariant& rv, reslist )
{
QVariantMap m = rv.toMap();
// TODO we need to handle preview urls separately. they should never trump a real url, and we need to display
// the purchaseUrl for the user to upgrade to a full stream.
if ( m.value( "preview" ).toBool() == true )
continue;
int duration = m.value( "duration", 0 ).toInt();
if ( duration <= 0 && m.contains( "durationString" ) )
{
QTime time = QTime::fromString( m.value( "durationString" ).toString(), "hh:mm:ss" );
duration = time.secsTo( QTime( 0, 0 ) ) * -1;
}
Tomahawk::track_ptr track = Tomahawk::Track::get( m.value( "artist" ).toString(),
m.value( "track" ).toString(),
m.value( "album" ).toString(),
m.value( "albumArtist" ).toString(),
duration,
QString(),
m.value( "albumpos" ).toUInt(),
m.value( "discnumber" ).toUInt() );
if ( !track )
continue;
Tomahawk::result_ptr rp = Tomahawk::Result::get( m.value( "url" ).toString(), track );
if ( !rp )
continue;
rp->setBitrate( m.value( "bitrate" ).toUInt() );
rp->setSize( m.value( "size" ).toUInt() );
rp->setRID( uuid() );
rp->setPurchaseUrl( m.value( "purchaseUrl" ).toString() );
rp->setLinkUrl( m.value( "linkUrl" ).toString() );
rp->setScore( m.value( "score" ).toFloat() );
rp->setChecked( m.value( "checked" ).toBool() );
//FIXME
if ( m.contains( "year" ) )
{
QVariantMap attr;
attr[ "releaseyear" ] = m.value( "year" );
// rp->track()->setAttributes( attr );
}
rp->setMimetype( m.value( "mimetype" ).toString() );
if ( rp->mimetype().isEmpty() )
{
rp->setMimetype( TomahawkUtils::extensionToMimetype( m.value( "extension" ).toString() ) );
Q_ASSERT( !rp->mimetype().isEmpty() );
}
rp->setFriendlySource( name() );
// 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;
}
return results;
}
const QSharedPointer< ScriptCollection >
ScriptAccount::scriptCollection( const QString& id ) const
{
return m_collections.hash().value( id );
}

View File

@@ -27,7 +27,11 @@
#include <QVariantMap>
//TODO: pimple
#include "../utils/WeakObjectHash.h"
#include "ScriptCollection.h"
#include <QHash>
#include <QPixmap>
#include "../DllMacro.h"
@@ -44,6 +48,16 @@ public:
ScriptAccount( const QString& name );
virtual ~ScriptAccount() {}
void stop();
const QString name() const;
void setIcon( const QPixmap& icon );
const QPixmap icon() const;
void setFilePath( const QString& filePath );
const QString filePath() const;
ScriptJob* invoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments );
virtual const QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0;
@@ -51,9 +65,14 @@ public:
void reportScriptJobResult( const QVariantMap& result );
void registerScriptPlugin( const QString& type, const QString& objectId );
void unregisterScriptPlugin( const QString& type, const QString& objectId );
virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object );
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
const QSharedPointer< ScriptCollection > scriptCollection( const QString& id ) const;
private slots:
void onJobDeleted( const QString& jobId );
@@ -61,8 +80,11 @@ private slots:
private: // TODO: pimple, might be renamed before tho
QString m_name;
QPixmap m_icon;
QString m_filePath;
QHash< QString, ScriptJob* > m_jobs;
QHash< QString, scriptobject_ptr > m_objects;
Utils::WeakObjectHash< ScriptCollection > m_collections;
};
} // ns: Tomahawk

View File

@@ -20,45 +20,45 @@
#include "ScriptCollection.h"
#include "Source.h"
#include "ExternalResolverGui.h"
#include "utils/TomahawkUtilsGui.h"
#include "utils/NetworkAccessManager.h"
#include "utils/Logger.h"
#include "resolvers/ScriptCommand_AllArtists.h"
#include "resolvers/ScriptCommand_AllAlbums.h"
#include "resolvers/ScriptCommand_AllTracks.h"
#include "ScriptAccount.h"
#include <QImageReader>
#include <QPainter>
using namespace Tomahawk;
ScriptCollection::ScriptCollection( const QString& id,
ScriptCollection::ScriptCollection( const scriptobject_ptr& scriptObject,
const source_ptr& source,
ExternalResolver* resolver,
ScriptAccount* scriptAccount,
QObject* parent )
: Collection( source, QString( "scriptcollection:" + resolver->name() + ":" + uuid() ), parent )
, m_id( id )
: Collection( source, QString( "scriptcollection:" + scriptAccount->name() + ":" + uuid() ), parent )
, ScriptPlugin( scriptObject )
, m_scriptAccount( scriptAccount )
, m_trackCount( -1 ) //null value
{
Q_ASSERT( resolver );
qDebug() << Q_FUNC_INFO << resolver->name() << Collection::name();
Q_ASSERT( scriptAccount );
qDebug() << Q_FUNC_INFO << scriptAccount->name() << Collection::name();
m_resolver = resolver;
m_servicePrettyName = m_resolver->name();
m_servicePrettyName = scriptAccount->name();
}
ScriptCollection::~ScriptCollection()
{
}
const QString
ScriptCollection::id() const
ScriptAccount*
ScriptCollection::scriptAccount() const
{
return m_id;
return m_scriptAccount;
}
@@ -73,7 +73,7 @@ QString
ScriptCollection::prettyName() const
{
return tr( "%1 Collection",
"Name of a collection based on a resolver, e.g. Subsonic Collection" )
"Name of a collection based on a script pluginsc, e.g. Subsonic Collection" )
.arg( m_servicePrettyName );
}
@@ -181,3 +181,39 @@ ScriptCollection::trackCount() const
{
return m_trackCount;
}
void
ScriptCollection::fetchIcon( const QString& iconUrlString )
{
if ( !iconUrlString.isEmpty() )
{
QUrl iconUrl = QUrl::fromEncoded( iconUrlString.toLatin1() );
if ( iconUrl.isValid() )
{
QNetworkRequest req( iconUrl );
tDebug() << "Creating a QNetworkReply with url:" << req.url().toString();
QNetworkReply* reply = Tomahawk::Utils::nam()->get( req );
connect( reply, SIGNAL( finished() ),
this, SLOT( onIconFetched() ) );
}
}
}
void
ScriptCollection::onIconFetched()
{
QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
if ( reply != 0 )
{
if( reply->error() == QNetworkReply::NoError )
{
QImageReader imageReader( reply );
setIcon( QPixmap::fromImageReader( &imageReader ) );
}
reply->deleteLater();
}
}

View File

@@ -21,7 +21,7 @@
#ifndef SCRIPTCOLLECTION_H
#define SCRIPTCOLLECTION_H
#include "ExternalResolver.h"
#include "ScriptPlugin.h"
#include "collection/Collection.h"
#include "collection/ArtistsRequest.h"
#include "collection/AlbumsRequest.h"
@@ -34,19 +34,27 @@
namespace Tomahawk
{
class ScriptAccount;
class DLLEXPORT ScriptCollection : public Collection
class DLLEXPORT ScriptCollection : public Collection, public ScriptPlugin
{
Q_OBJECT
Q_OBJECT
// access to ScriptObject
friend class ScriptCommand_AllArtists;
friend class ScriptCommand_AllAlbums;
friend class ScriptCommand_AllTracks;
friend class JSResolver;
public:
explicit ScriptCollection( const QString& id,
explicit ScriptCollection( const scriptobject_ptr& scriptObject,
const source_ptr& source,
ExternalResolver* resolver,
ScriptAccount* scriptAccount,
QObject* parent = nullptr );
virtual ~ScriptCollection();
const QString id() const;
ScriptAccount* scriptAccount() const;
/**
* @brief setServiceName sets the name of the service that provides the ScriptCollection.
@@ -62,6 +70,7 @@ public:
QString itemName() const override;
BackendType backendType() const override { return ScriptCollectionType; }
void fetchIcon( const QString& iconUrl );
void setIcon( const QPixmap& icon );
const QPixmap icon( const QSize& size ) const override;
QPixmap bigIcon() const override;
@@ -69,8 +78,6 @@ public:
void setDescription( const QString& text );
QString description() const override;
virtual ExternalResolver* resolver() { return m_resolver; }
Tomahawk::ArtistsRequest* requestArtists() override;
Tomahawk::AlbumsRequest* requestAlbums( const Tomahawk::artist_ptr& artist ) override;
Tomahawk::TracksRequest* requestTracks( const Tomahawk::album_ptr& album ) override;
@@ -78,9 +85,11 @@ public:
void setTrackCount( int count );
int trackCount() const override;
private slots:
void onIconFetched();
private:
QString m_id;
ExternalResolver* m_resolver;
ScriptAccount* m_scriptAccount;
QString m_servicePrettyName;
QString m_description;
int m_trackCount;

View File

@@ -22,9 +22,10 @@
#include "Album.h"
#include "Artist.h"
#include "ExternalResolver.h"
#include "ScriptAccount.h"
#include "PlaylistEntry.h"
#include "ScriptCollection.h"
#include "ScriptJob.h"
using namespace Tomahawk;
@@ -75,10 +76,12 @@ ScriptCommand_AllAlbums::exec()
return;
}
connect( collection->resolver(), SIGNAL( albumsFound( QList< Tomahawk::album_ptr > ) ),
this, SLOT( onResolverDone( QList< Tomahawk::album_ptr > ) ) );
QVariantMap arguments;
arguments[ "artist" ] = m_artist->name();
collection->resolver()->albums( m_collection, m_artist );
ScriptJob* job = collection->scriptObject()->invoke( "albums", arguments );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onAlbumsJobDone( QVariantMap ) ), Qt::QueuedConnection );
job->start();
}
@@ -94,8 +97,19 @@ ScriptCommand_AllAlbums::reportFailure()
void
ScriptCommand_AllAlbums::onResolverDone( const QList< Tomahawk::album_ptr >& a )
ScriptCommand_AllAlbums::onAlbumsJobDone(const QVariantMap& result)
{
ScriptJob* job = qobject_cast< ScriptJob* >( sender() );
Q_ASSERT( job );
if ( job->error() )
{
reportFailure();
return;
}
QList< Tomahawk::album_ptr > a = parseAlbumVariantList( m_artist, result[ "albums" ].toList() );
if ( m_filter.isEmpty() )
emit albums( a );
else
@@ -110,4 +124,26 @@ ScriptCommand_AllAlbums::onResolverDone( const QList< Tomahawk::album_ptr >& a )
emit albums( filtered );
}
emit done();
job->deleteLater();
}
QList< Tomahawk::album_ptr >
ScriptCommand_AllAlbums::parseAlbumVariantList( const Tomahawk::artist_ptr& artist, const QVariantList& reslist )
{
QList< Tomahawk::album_ptr > results;
foreach( const QVariant& rv, reslist )
{
const QString val = rv.toString();
if ( val.trimmed().isEmpty() )
continue;
Tomahawk::album_ptr ap = Tomahawk::Album::get( artist, val, false );
results << ap;
}
return results;
}

View File

@@ -49,9 +49,11 @@ protected:
virtual void reportFailure();
private slots:
void onResolverDone( const QList< Tomahawk::album_ptr >& );
void onAlbumsJobDone( const QVariantMap& result );
private:
static QList< Tomahawk::album_ptr > parseAlbumVariantList( const Tomahawk::artist_ptr& artist,
const QVariantList& reslist );
Tomahawk::collection_ptr m_collection;
Tomahawk::artist_ptr m_artist;
QString m_filter;

View File

@@ -19,10 +19,13 @@
#include "ScriptCommand_AllArtists.h"
#include "Artist.h"
#include "ExternalResolver.h"
#include "ScriptAccount.h"
#include "ScriptCollection.h"
#include "ScriptObject.h"
#include "ScriptJob.h"
#include "utils/Logger.h"
#include "../Typedefs.h"
using namespace Tomahawk;
@@ -59,16 +62,11 @@ void
ScriptCommand_AllArtists::exec()
{
Tomahawk::ScriptCollection* collection = qobject_cast< Tomahawk::ScriptCollection* >( m_collection.data() );
if ( collection == 0 )
{
emit artists( QList< Tomahawk::artist_ptr >() );
return;
}
Q_ASSERT( collection );
connect( collection->resolver(), SIGNAL( artistsFound( QList< Tomahawk::artist_ptr > ) ),
this, SLOT( onResolverDone( QList< Tomahawk::artist_ptr > ) ) );
collection->resolver()->artists( m_collection );
ScriptJob* job = collection->scriptObject()->invoke( "artists" );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onArtistsJobDone( QVariantMap ) ), Qt::QueuedConnection );
job->start();
}
@@ -81,10 +79,24 @@ ScriptCommand_AllArtists::reportFailure()
}
void ScriptCommand_AllArtists::onResolverDone( const QList< Tomahawk::artist_ptr >& a )
void
ScriptCommand_AllArtists::onArtistsJobDone( const QVariantMap& result )
{
ScriptJob* job = qobject_cast< ScriptJob* >( sender() );
Q_ASSERT( job );
if ( job->error() )
{
reportFailure();
return;
}
QList< Tomahawk::artist_ptr > a = parseArtistVariantList( result[ "artists" ].toList() );
if ( m_filter.isEmpty() )
{
emit artists( a );
}
else
{
QList< Tomahawk::artist_ptr > filtered;
@@ -93,7 +105,30 @@ void ScriptCommand_AllArtists::onResolverDone( const QList< Tomahawk::artist_ptr
if ( artist->name().contains( m_filter ) )
filtered.append( artist );
}
emit artists( filtered );
emit artists( a );
}
emit done();
job->deleteLater();
}
QList< Tomahawk::artist_ptr >
ScriptCommand_AllArtists::parseArtistVariantList( const QVariantList& reslist )
{
QList< Tomahawk::artist_ptr > results;
foreach( const QVariant& rv, reslist )
{
const QString val = rv.toString();
if ( val.trimmed().isEmpty() )
continue;
Tomahawk::artist_ptr ap = Tomahawk::Artist::get( val, false );
results << ap;
}
return results;
}

View File

@@ -48,9 +48,11 @@ protected:
void reportFailure() override;
private slots:
void onResolverDone( const QList< Tomahawk::artist_ptr >& );
void onArtistsJobDone( const QVariantMap& result );
private:
static QList< Tomahawk::artist_ptr > parseArtistVariantList( const QVariantList& reslist );
Tomahawk::collection_ptr m_collection;
QString m_filter;
};

View File

@@ -18,11 +18,15 @@
#include "ScriptCommand_AllTracks.h"
#include "ExternalResolver.h"
#include "ScriptAccount.h"
#include "PlaylistEntry.h"
#include "ScriptCollection.h"
#include "Artist.h"
#include "Album.h"
#include "ScriptJob.h"
#include "utils/Logger.h"
#include "../Result.h"
using namespace Tomahawk;
@@ -66,10 +70,13 @@ ScriptCommand_AllTracks::exec()
return;
}
connect( collection->resolver(), SIGNAL( tracksFound( QList< Tomahawk::query_ptr > ) ),
this, SLOT( onResolverDone( QList< Tomahawk::query_ptr > ) ) );
QVariantMap arguments;
arguments[ "artist" ] = m_album->artist()->name();
arguments[ "album" ] = m_album->name();
collection->resolver()->tracks( m_collection, m_album );
ScriptJob* job = collection->scriptObject()->invoke( "tracks", arguments );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( onTracksJobDone( QVariantMap ) ), Qt::QueuedConnection );
job->start();
}
@@ -89,8 +96,37 @@ ScriptCommand_AllTracks::reportFailure()
void
ScriptCommand_AllTracks::onResolverDone( const QList< Tomahawk::query_ptr >& q )
ScriptCommand_AllTracks::onTracksJobDone( const QVariantMap& result )
{
emit tracks( q );
ScriptJob* job = qobject_cast< ScriptJob* >( sender() );
Q_ASSERT( job );
qDebug() << "Resolver reporting album tracks:" << result;
if ( job->error() )
{
reportFailure();
return;
}
QSharedPointer< ScriptCollection > collection = m_collection.objectCast< ScriptCollection >();
Q_ASSERT( !collection.isNull() );
QList< Tomahawk::result_ptr > t = collection->scriptAccount()->parseResultVariantList( result[ "tracks"].toList() );
QList< Tomahawk::query_ptr > queries;
foreach ( const Tomahawk::result_ptr& result, t )
{
result->setScore( 1.0 );
result->setResolvedByCollection( m_collection );
queries.append( result->toQuery() );
}
tDebug() << Q_FUNC_INFO << "about to push" << queries.count() << "tracks";
emit tracks( queries );
emit done();
job->deleteLater();
}

View File

@@ -43,11 +43,11 @@ signals:
void done();
protected:
void exec() override;
Q_INVOKABLE void exec() override;
void reportFailure() override;
private slots:
void onResolverDone( const QList< Tomahawk::query_ptr >& );
void onTracksJobDone( const QVariantMap& result );
private:
Tomahawk::collection_ptr m_collection;

View File

@@ -42,6 +42,8 @@ public:
ScriptObject( const QString& id, ScriptAccount* parent );
virtual ~ScriptObject();
QString id() const;
void setWeakRef( const scriptobject_wptr& weakRef );
const scriptobject_wptr weakRef() const;
@@ -53,8 +55,6 @@ public:
const QVariant syncInvoke( const QString& methodName, const QVariantMap& arguments = QVariantMap() );
protected:
QString id() const;
void startJob( ScriptJob* scriptJob );
private:

View File

@@ -72,7 +72,7 @@ public:
m_hash.insert( key, value.toWeakRef() );
}
const QHash< QString, QWeakPointer<T> >& hash() { return m_hash; }
const QHash< QString, QWeakPointer<T> >& hash() const { return m_hash; }
virtual void remove( const QString& key ) { m_hash.remove( key ); }
private: