PageRenderTime 186ms CodeModel.GetById 81ms app.highlight 60ms RepoModel.GetById 40ms app.codeStats 0ms

/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 397 lines | 293 code | 78 blank | 26 comment | 45 complexity | 06a3b3e3606f11072973754a1a9d19e3 MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2011, Hugo Lindstr??m <hugolm84@gmail.com>
  4 *
  5 *   Tomahawk is free software: you can redistribute it and/or modify
  6 *   it under the terms of the GNU General Public License as published by
  7 *   the Free Software Foundation, either version 3 of the License, or
  8 *   (at your option) any later version.
  9 *
 10 *   Tomahawk is distributed in the hope that it will be useful,
 11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 13 *   GNU General Public License for more details.
 14 *
 15 *   You should have received a copy of the GNU General Public License
 16 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 17 */
 18
 19#include "spotifyPlugin.h"
 20
 21#include <QDir>
 22#include <QSettings>
 23#include <QCryptographicHash>
 24#include <QNetworkConfiguration>
 25#include <QNetworkReply>
 26
 27#include "album.h"
 28#include "typedefs.h"
 29#include "audio/audioengine.h"
 30#include "tomahawksettings.h"
 31#include "utils/tomahawkutils.h"
 32#include "utils/logger.h"
 33#include "chartsplugin_data_p.h"
 34
 35#define SPOTIFY_API_URL "http://spotikea.tomahawk-player.org/"
 36#include <qjson/parser.h>
 37#include <qjson/serializer.h>
 38
 39using namespace Tomahawk::InfoSystem;
 40
 41
 42SpotifyPlugin::SpotifyPlugin()
 43    : InfoPlugin()
 44    , m_chartsFetchJobs( 0 )
 45{
 46
 47    m_supportedGetTypes << InfoChart << InfoChartCapabilities;
 48
 49}
 50
 51
 52SpotifyPlugin::~SpotifyPlugin()
 53{
 54    qDebug() << Q_FUNC_INFO;
 55}
 56
 57
 58void
 59SpotifyPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData )
 60{
 61    emit info( requestData, QVariant() );
 62    return;
 63}
 64
 65
 66void
 67SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 68{
 69    qDebug() << Q_FUNC_INFO << requestData.caller;
 70    qDebug() << Q_FUNC_INFO << requestData.customData;
 71
 72    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
 73
 74
 75    switch ( requestData.type )
 76    {
 77        case InfoChart:
 78            if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "spotify" )
 79            {
 80                dataError( requestData );
 81                break;
 82            }
 83            qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"];
 84            fetchChart( requestData );
 85            break;
 86
 87        case InfoChartCapabilities:
 88            fetchChartCapabilities( requestData );
 89            break;
 90        default:
 91            dataError( requestData );
 92    }
 93}
 94
 95
 96void
 97SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
 98{
 99    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
100    {
101        dataError( requestData );
102        return;
103    }
104
105    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
106    Tomahawk::InfoSystem::InfoStringHash criteria;
107    /// Each request needs to contain both a id and source
108    if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) )
109    {
110        tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!";
111        dataError( requestData );
112        return;
113
114    }
115    /// Set the criterias for current chart
116    criteria["chart_id"] = hash["chart_id"];
117    criteria["chart_source"] = hash["chart_source"];
118
119    emit getCachedInfo( criteria, 86400000 /* Expire chart cache in 1 day */, requestData );
120}
121void
122SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
123{
124    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
125    {
126        dataError( requestData );
127        return;
128    }
129
130    Tomahawk::InfoSystem::InfoStringHash criteria;
131    criteria[ "InfoChartCapabilities" ] = "spotifyplugin";
132    emit getCachedInfo( criteria, 604800000, requestData );
133}
134
135void
136SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
137{
138    switch ( requestData.type )
139    {
140
141        case InfoChart:
142        {
143            /// Fetch the chart, we need source and id
144            tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart not in cache! Fetching...";
145            QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/%1/" ).arg( criteria["chart_id"] ) );
146            qDebug() << Q_FUNC_INFO << "Getting chart url" << url;
147
148            QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
149            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
150            connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
151            return;
152
153
154        }
155        case InfoChartCapabilities:
156        {
157            tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities not in cache! Fetching...";
158
159            // we never need to re-fetch
160            if ( !m_allChartsMap.isEmpty() )
161                return;
162
163            /// We need to fetch possible types before they are asked for
164            tDebug() << "SpotifyPlugin: InfoChart fetching possible resources";
165
166            QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/charts" )  );
167            QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
168            tDebug() << Q_FUNC_INFO << "fetching:" << url;
169            connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) );
170            m_chartsFetchJobs++;
171
172            if ( m_chartsFetchJobs > 0 )
173            {
174                qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
175                m_cachedRequests.append( requestData );
176                return;
177            }
178
179            emit info( requestData, m_allChartsMap );
180            return;
181        }
182
183        default:
184        {
185            tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss";
186            emit info( requestData, QVariant() );
187            return;
188        }
189    }
190}
191
192
193void
194SpotifyPlugin::chartTypes()
195{
196    /// Get possible chart type for specificSpotifyPlugin: InfoChart types returned chart source
197    tDebug() << Q_FUNC_INFO << "Got spotifychart type result";
198    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
199
200    if ( reply->error() == QNetworkReply::NoError )
201    {
202        QJson::Parser p;
203        bool ok;
204        const QVariantMap res = p.parse( reply, &ok ).toMap();
205        const QVariantMap chartObj = res;
206
207        if ( !ok )
208        {
209            tLog() << Q_FUNC_INFO << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine();
210
211            return;
212        }
213
214        QVariantMap charts;
215        foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() )
216        {
217
218           const QString geo = geos.toMap().value( "name" ).toString();
219           const QString geoId = geos.toMap().value( "id" ).toString();
220           QString country;
221
222           if( geo == "For me" )
223              continue; /// country = geo; Lets use this later, when we can get the spotify username from tomahawk
224           else if( geo == "Everywhere" )
225               country = geo;
226           else
227           {
228
229               QLocale l( QString( "en_%1" ).arg( geo ) );
230               country = Tomahawk::CountryUtils::fullCountryFromCode( geo );
231
232               for ( int i = 1; i < country.size(); i++ )
233               {
234                   if ( country.at( i ).isUpper() )
235                   {
236                       country.insert( i, " " );
237                       i++;
238                   }
239               }
240           }
241
242           QList< InfoStringHash > chart_types;
243           foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() )
244           {
245               QString type = types.toMap().value( "id" ).toString();
246               QString label = types.toMap().value( "name" ).toString();
247
248               InfoStringHash c;
249               c[ "id" ] = type + "/" + geoId;
250               c[ "label" ] = label;
251               c[ "type" ] = type;
252
253               chart_types.append( c );
254
255           }
256
257           charts.insert( country.toUtf8(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) );
258
259        }
260
261        QVariantMap defaultMap;
262        defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums";
263        m_allChartsMap[ "defaults" ] = defaultMap;
264        m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) );
265
266    }
267    else
268    {
269        tLog() << Q_FUNC_INFO << "Error fetching charts:" << reply->errorString();
270    }
271
272    m_chartsFetchJobs--;
273    if ( !m_cachedRequests.isEmpty() && m_chartsFetchJobs == 0 )
274    {
275        foreach ( InfoRequestData request, m_cachedRequests )
276        {
277            emit info( request, m_allChartsMap );
278            Tomahawk::InfoSystem::InfoStringHash criteria;
279            criteria[ "InfoChartCapabilities" ] = "spotifyplugin";
280            emit updateCache( criteria,604800000, request.type, m_allChartsMap );
281        }
282        m_cachedRequests.clear();
283    }
284
285}
286
287void
288SpotifyPlugin::chartReturned()
289{
290
291    /// Chart request returned something! Woho
292    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
293    QString url = reply->url().toString();
294    QVariantMap returnedData;
295    if ( reply->error() == QNetworkReply::NoError )
296    {
297        QJson::Parser p;
298        bool ok;
299        QVariantMap res = p.parse( reply, &ok ).toMap();
300
301        if ( !ok )
302        {
303            tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine();
304            return;
305        }
306
307        /// SO we have a result, parse it!
308        QList< InfoStringHash > top_tracks;
309        QList< InfoStringHash > top_albums;
310        QStringList top_artists;
311
312        if( url.contains( "albums" ) )
313            setChartType( Album );
314        else if( url.contains( "tracks" ) )
315            setChartType( Track );
316        else if( url.contains( "artists" ) )
317            setChartType( Artist );
318        else
319            setChartType( None );
320
321        foreach(QVariant result, res.value("toplist").toMap().value("result").toList() )
322        {
323            QString title, artist;
324            QVariantMap chartMap = result.toMap();
325
326            if ( !chartMap.isEmpty() )
327            {
328
329                title = chartMap.value( "title" ).toString();
330                artist = chartMap.value( "artist" ).toString();
331
332                if( chartType() == Track )
333                {
334                    InfoStringHash pair;
335                    pair["artist"] = artist;
336                    pair["track"] = title;
337                    top_tracks << pair;
338
339                    qDebug() << "SpotifyChart type is track";
340                }
341
342                if( chartType() == Album )
343                {
344
345                    InfoStringHash pair;
346                    pair["artist"] = artist;
347                    pair["album"] = title;
348                    top_albums << pair;
349                    qDebug() << "SpotifyChart type is album";
350                }
351
352                if( chartType() == Artist )
353                {
354
355                    top_artists << chartMap.value( "name" ).toString();
356                    qDebug() << "SpotifyChart type is artist";
357
358                }
359            }
360        }
361
362        if( chartType() == Track )
363        {
364            tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks";
365            returnedData["tracks"] = QVariant::fromValue( top_tracks );
366            returnedData["type"] = "tracks";
367        }
368
369        if( chartType() == Album )
370        {
371            tDebug() << "ChartsPlugin:" << "\tgot " << top_albums.size() << " albums";
372            returnedData["albums"] = QVariant::fromValue( top_albums );
373            returnedData["type"] = "albums";
374        }
375
376        if( chartType() == Artist )
377        {
378            tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists";
379            returnedData["artists"] = top_artists;
380            returnedData["type"] = "artists";
381        }
382
383        Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
384
385        emit info( requestData, returnedData );
386
387        // update cache
388        Tomahawk::InfoSystem::InfoStringHash criteria;
389        Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
390        criteria[ "chart_id" ] = origData[ "chart_id" ];
391        criteria[ "chart_source" ] = origData[ "chart_source" ];
392        emit updateCache( criteria, 86400000, requestData.type, returnedData );
393    }
394    else
395        qDebug() << "Network error in fetching chart:" << reply->url().toString();
396
397}