From 5263b21138cb3a6c63116ae7e6250abe99cfcd47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= <hugolm84@gmail.com>
Date: Mon, 24 Oct 2011 12:45:54 +0200
Subject: [PATCH] Spotify InfoSystemPlugin

---
 src/libtomahawk/CMakeLists.txt                |   2 +
 .../infoplugins/generic/spotifyPlugin.cpp     | 395 ++++++++++++++++++
 .../infoplugins/generic/spotifyPlugin.h       |  80 ++++
 .../infosystem/infosystemworker.cpp           |   4 +
 4 files changed, 481 insertions(+)
 create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp
 create mode 100644 src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h

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 - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
+ *   Copyright 2011, Leo Franchi <lfranchi@kde.org>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "spotifyPlugin.h"
+
+#include <QDir>
+#include <QSettings>
+#include <QCryptographicHash>
+#include <QNetworkConfiguration>
+#include <QNetworkReply>
+#include <QDomElement>
+
+#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 <qjson/parser.h>
+#include <qjson/serializer.h>
+
+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<QString, QString> 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<QNetworkReply*>( 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<QList< InfoStringHash > >( chart_types ) );
+
+        }
+
+        m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) );
+
+    }
+    else
+    {
+        tLog() << Q_FUNC_INFO << "Error fetching charts:" << reply->errorString();
+    }
+
+}
+
+void
+SpotifyPlugin::chartReturned()
+{
+
+    /// Chart request returned something! Woho
+    QNetworkReply* reply = qobject_cast<QNetworkReply*>( 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 - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SpotifyPlugin_H
+#define SpotifyPlugin_H
+
+#include "infosystem/infosystem.h"
+#include "infosystem/infosystemworker.h"
+#include <QNetworkReply>
+#include <QObject>
+
+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() );