diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 9ab439ce9..fc153c608 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -111,6 +111,7 @@ set( libSources infosystem/infoplugins/generic/echonestplugin.cpp infosystem/infoplugins/generic/lastfmplugin.cpp infosystem/infoplugins/generic/chartsplugin.cpp + infosystem/infoplugins/generic/spotifyPlugin.cpp infosystem/infoplugins/generic/musixmatchplugin.cpp infosystem/infoplugins/generic/musicbrainzPlugin.cpp infosystem/infoplugins/generic/RoviPlugin.cpp @@ -341,6 +342,7 @@ set( libHeaders infosystem/infoplugins/generic/echonestplugin.h infosystem/infoplugins/generic/lastfmplugin.h infosystem/infoplugins/generic/chartsplugin.h + infosystem/infoplugins/generic/spotifyPlugin.h infosystem/infoplugins/generic/musixmatchplugin.h infosystem/infoplugins/generic/musicbrainzPlugin.h infosystem/infoplugins/generic/RoviPlugin.h diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp new file mode 100644 index 000000000..f6c77eef0 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp @@ -0,0 +1,395 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * Copyright 2011, Leo Franchi + * + * 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 3 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 "spotifyPlugin.h" + +#include +#include +#include +#include +#include +#include + +#include "album.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" +#include "chartsplugin_data_p.h" + +#define CHART_URL "http://localhost:5112/" +#include +#include + +using namespace Tomahawk::InfoSystem; + + +SpotifyPlugin::SpotifyPlugin() + : InfoPlugin() +{ + + m_supportedGetTypes << InfoChart << InfoChartCapabilities; + +} + + +SpotifyPlugin::~SpotifyPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + + +void +SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam ) +{ + tDebug() << "SpotifyPlugin: namChangedSLot"; + qDebug() << Q_FUNC_INFO; + if( !nam ) + return; + + m_nam = QWeakPointer< QNetworkAccessManager >( nam ); + + /// We need to fetch possible types before they are asked for + tDebug() << "SpotifyPlugin: InfoChart fetching possible resources"; + + QUrl url = QUrl( QString( CHART_URL "toplist/charts" ) ); + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + tDebug() << Q_FUNC_INFO << "fetching:" << url; + connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) ); + +} + + +void +SpotifyPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + emit info( requestId, requestData, QVariant() ); + return; +} + + +void +SpotifyPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + qDebug() << Q_FUNC_INFO << requestData.caller; + qDebug() << Q_FUNC_INFO << requestData.customData; + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + + + switch ( requestData.type ) + { + case InfoChart: + if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "spotify" ) + { + dataError( requestId, requestData ); + break; + } + qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"]; + fetchChart( requestId, requestData ); + break; + + case InfoChartCapabilities: + fetchChartCapabilities( requestId, requestData ); + break; + default: + dataError( requestId, requestData ); + } +} + + +void +SpotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input ) +{ + Q_UNUSED( caller ) + Q_UNUSED( type) + Q_UNUSED( input ) +} + +void +SpotifyPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestId, requestData ); + return; + } + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash criteria; + if ( !hash.contains( "chart_id" ) ) + { + dataError( requestId, requestData ); + return; + } else { + criteria["chart_id"] = hash["chart_id"]; + } + + emit getCachedInfo( requestId, criteria, 0, requestData ); +} +void +SpotifyPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + dataError( requestId, requestData ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + emit getCachedInfo( requestId, criteria, 0, requestData ); +} + +void +SpotifyPlugin::notInCacheSlot( uint requestId, QHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) +{ + if ( !m_nam.data() ) + { + tLog() << Q_FUNC_INFO << "Have a null QNAM, uh oh"; + emit info( requestId, requestData, QVariant() ); + return; + } + + + switch ( requestData.type ) + { + + case InfoChart: + { + /// Fetch the chart, we need source and id + QUrl url = QUrl( QString( CHART_URL "toplist/%1/" ).arg( criteria["chart_id"] ) ); + qDebug() << Q_FUNC_INFO << "Getting chart url" << url; + + QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestId", requestId ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); + return; + + + } + case InfoChartCapabilities: + { + qDebug() << Q_FUNC_INFO << "EMITTING CHART" << m_allChartsMap; + emit info( + requestId, + requestData, + m_allChartsMap + ); + return; + } + + default: + { + tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; + emit info( requestId, requestData, QVariant() ); + return; + } + } +} + + +void +SpotifyPlugin::chartTypes() +{ + /// Get possible chart type for specificSpotifyPlugin: InfoChart types returned chart source + tDebug() << Q_FUNC_INFO << "Got spotifychart type result"; + QNetworkReply* reply = qobject_cast( sender() ); + + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + const QVariantMap res = p.parse( reply, &ok ).toMap(); + const QVariantMap chartObj = res; + + if ( !ok ) + { + tLog() << Q_FUNC_INFO << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine(); + + return; + } + + QVariantMap charts; + foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() ) + { + + const QString geo = geos.toMap().value( "name" ).toString(); + const QString geoId = geos.toMap().value( "id" ).toString(); + QString country; + + if( geo == "For me" ) + continue; /// country = geo; Lets use this later, when we can get the spotify username from tomahawk + else if( geo == "Everywhere" ) + country = geo; + else + { + + QLocale l( QString( "en_%1" ).arg( geo ) ); + country = Tomahawk::CountryUtils::fullCountryFromCode( geo ); + + for ( int i = 1; i < country.size(); i++ ) + { + if ( country.at( i ).isUpper() ) + { + country.insert( i, " " ); + i++; + } + } + } + + QList< InfoStringHash > chart_types; + foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() ) + { + QString type = types.toMap().value( "id" ).toString(); + QString label = types.toMap().value( "name" ).toString(); + + InfoStringHash c; + c[ "id" ] = type + "/" + geoId; + c[ "label" ] = label; + c[ "type" ] = type; + + chart_types.append( c ); + + } + + charts.insert( country.toUtf8(), QVariant::fromValue >( chart_types ) ); + + } + + m_allChartsMap.insert( "Spotify", QVariant::fromValue( charts ) ); + + } + else + { + tLog() << Q_FUNC_INFO << "Error fetching charts:" << reply->errorString(); + } + +} + +void +SpotifyPlugin::chartReturned() +{ + + /// Chart request returned something! Woho + QNetworkReply* reply = qobject_cast( sender() ); + QString url = reply->url().toString(); + QVariantMap returnedData; + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine(); + return; + } + + /// SO we have a result, parse it! + QList< InfoStringHash > top_tracks; + QList< InfoStringHash > top_albums; + QStringList top_artists; + + if( url.contains( "albums" ) ) + setChartType( Album ); + else if( url.contains( "tracks" ) ) + setChartType( Track ); + else if( url.contains( "artists" ) ) + setChartType( Artist ); + else + setChartType( None ); + + foreach(QVariant result, res.value("toplist").toMap().value("result").toList() ) + { + QString title, artist; + QVariantMap chartMap = result.toMap(); + + if ( !chartMap.isEmpty() ) + { + + title = chartMap.value( "title" ).toString(); + artist = chartMap.value( "artist" ).toString(); + + if( chartType() == Track ) + { + InfoStringHash pair; + pair["artist"] = artist; + pair["track"] = title; + top_tracks << pair; + + qDebug() << "SpotifyChart type is track"; + } + + if( chartType() == Album ) + { + + InfoStringHash pair; + pair["artist"] = artist; + pair["album"] = title; + top_albums << pair; + qDebug() << "SpotifyChart type is album"; + } + + if( chartType() == Artist ) + { + + top_artists << chartMap.value( "name" ).toString(); + qDebug() << "SpotifyChart type is artist"; + + } + } + } + + if( chartType() == Track ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks"; + returnedData["tracks"] = QVariant::fromValue( top_tracks ); + returnedData["type"] = "tracks"; + } + + if( chartType() == Album ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_albums.size() << " albums"; + returnedData["albums"] = QVariant::fromValue( top_albums ); + returnedData["type"] = "albums"; + } + + if( chartType() == Artist ) + { + tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists"; + returnedData["artists"] = top_artists; + returnedData["type"] = "artists"; + } + + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + + emit info( + reply->property( "requestId" ).toUInt(), + requestData, + returnedData + ); + + } + else + qDebug() << "Network error in fetching chart:" << reply->url().toString(); + +} diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h new file mode 100644 index 000000000..b27e0cd26 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h @@ -0,0 +1,80 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Hugo Lindström + * + * 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 3 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 . + */ + +#ifndef SpotifyPlugin_H +#define SpotifyPlugin_H + +#include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" +#include +#include + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class SpotifyPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + SpotifyPlugin(); + virtual ~SpotifyPlugin(); + + enum ChartType { + None = 0x00, + Track = 0x01, + Album = 0x02, + Artist = 0x04 + + }; + void setChartType( ChartType type ) { m_chartType = type; } + ChartType chartType() const { return m_chartType; } + +public slots: + void chartReturned(); + void chartTypes(); + void namChangedSlot( QNetworkAccessManager *nam ); + +protected slots: + virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ); + +private: + void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ); + + + ChartType m_chartType; + QVariantMap m_allChartsMap; + + + QWeakPointer< QNetworkAccessManager > m_nam; +}; + +} + +} + +#endif // SpotifyPlugin_H diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index 965a21581..3f9478df6 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -26,6 +26,7 @@ #include "infoplugins/generic/echonestplugin.h" #include "infoplugins/generic/musixmatchplugin.h" #include "infoplugins/generic/chartsplugin.h" +#include "infoplugins/generic/spotifyPlugin.h" #include "infoplugins/generic/lastfmplugin.h" #include "infoplugins/generic/musicbrainzPlugin.h" #include "utils/tomahawkutils.h" @@ -96,6 +97,9 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac InfoPluginPtr roviptr( new RoviPlugin() ); m_plugins.append( roviptr ); registerInfoTypes( roviptr, roviptr.data()->supportedGetTypes(), roviptr.data()->supportedPushTypes() ); + InfoPluginPtr spotptr( new SpotifyPlugin() ); + m_plugins.append( spotptr ); + registerInfoTypes( spotptr, spotptr.data()->supportedGetTypes(), spotptr.data()->supportedPushTypes() ); #ifdef Q_WS_MAC InfoPluginPtr admptr( new AdiumPlugin() );