PageRenderTime 230ms CodeModel.GetById 60ms app.highlight 140ms RepoModel.GetById 21ms app.codeStats 1ms

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

http://github.com/tomahawk-player/tomahawk
C++ | 808 lines | 639 code | 142 blank | 27 comment | 69 complexity | b7c8b4bedfeaff8a459ed7164ed94942 MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  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 "lastfmplugin.h"
 20
 21#include <QDir>
 22#include <QSettings>
 23#include <QNetworkConfiguration>
 24#include <QDomElement>
 25
 26#include "album.h"
 27#include "typedefs.h"
 28#include "audio/audioengine.h"
 29#include "tomahawksettings.h"
 30#include "utils/tomahawkutils.h"
 31#include "utils/logger.h"
 32
 33#include <lastfm/ws.h>
 34#include <lastfm/XmlQuery>
 35
 36#include <qjson/parser.h>
 37
 38using namespace Tomahawk::InfoSystem;
 39
 40
 41LastFmPlugin::LastFmPlugin()
 42    : InfoPlugin()
 43    , m_scrobbler( 0 )
 44{
 45    m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoArtistSimilars << InfoArtistSongs << InfoChart << InfoChartCapabilities;
 46    m_supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying << InfoLove << InfoUnLove;
 47
 48    // Flush session key cache
 49    TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() );
 50
 51    lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a";
 52    lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f";
 53    lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername();
 54    lastfm::setNetworkAccessManager( TomahawkUtils::nam() );
 55
 56    m_pw = TomahawkSettings::instance()->lastFmPassword();
 57
 58    //HACK work around a bug in liblastfm---it doesn't create its config dir, so when it
 59    // tries to write the track cache, it fails silently. until we have a fixed version, do this
 60    // code taken from Amarok (src/services/lastfm/ScrobblerAdapter.cpp)
 61#ifdef Q_WS_X11
 62    QString lpath = QDir::home().filePath( ".local/share/Last.fm" );
 63    QDir ldir = QDir( lpath );
 64    if( !ldir.exists() )
 65    {
 66        ldir.mkpath( lpath );
 67    }
 68#endif
 69
 70    m_badUrls << QUrl( "http://cdn.last.fm/flatness/catalogue/noimage" );
 71
 72    connect( TomahawkSettings::instance(), SIGNAL( changed() ),
 73                                             SLOT( settingsChanged() ), Qt::QueuedConnection );
 74
 75    QTimer::singleShot( 0, this, SLOT( settingsChanged() ) );
 76}
 77
 78
 79LastFmPlugin::~LastFmPlugin()
 80{
 81    qDebug() << Q_FUNC_INFO;
 82    delete m_scrobbler;
 83    m_scrobbler = 0;
 84}
 85
 86
 87void
 88LastFmPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData )
 89{
 90    emit info( requestData, QVariant() );
 91    return;
 92}
 93
 94
 95void
 96LastFmPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 97{
 98    switch ( requestData.type )
 99    {
100        case InfoArtistImages:
101            fetchArtistImages( requestData );
102            break;
103
104        case InfoAlbumCoverArt:
105            fetchCoverArt( requestData );
106            break;
107
108        case InfoArtistSimilars:
109            fetchSimilarArtists( requestData );
110            break;
111
112        case InfoArtistSongs:
113            fetchTopTracks( requestData );
114            break;
115
116        case InfoChart:
117            fetchChart( requestData );
118            break;
119
120        case InfoChartCapabilities:
121            fetchChartCapabilities( requestData );
122            break;
123        default:
124            dataError( requestData );
125    }
126}
127
128
129void
130LastFmPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
131{
132    Q_UNUSED( caller )
133    switch ( type )
134    {
135        case InfoSubmitNowPlaying:
136            nowPlaying( input );
137            break;
138
139        case InfoSubmitScrobble:
140            scrobble();
141            break;
142
143        case InfoLove:
144        case InfoUnLove:
145            sendLoveSong( type, input );
146            break;
147
148        default:
149            return;
150    }
151}
152
153
154void
155LastFmPlugin::nowPlaying( const QVariant &input )
156{
157    if ( !input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() || !m_scrobbler )
158    {
159        tLog() << "LastFmPlugin::nowPlaying no m_scrobbler, or cannot convert input!";
160        if ( !m_scrobbler )
161            tLog() << "No scrobbler!";
162        return;
163    }
164
165    InfoStringHash hash = input.value< Tomahawk::InfoSystem::InfoStringHash >();
166    if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) || !hash.contains( "duration" ) )
167        return;
168
169    m_track = lastfm::MutableTrack();
170    m_track.stamp();
171
172    m_track.setTitle( hash["title"] );
173    m_track.setArtist( hash["artist"] );
174    m_track.setAlbum( hash["album"] );
175    bool ok;
176    m_track.setDuration( hash["duration"].toUInt( &ok ) );
177    m_track.setSource( lastfm::Track::Player );
178
179    m_scrobbler->nowPlaying( m_track );
180}
181
182
183void
184LastFmPlugin::scrobble()
185{
186    if ( !m_scrobbler || m_track.isNull() )
187        return;
188
189    tLog() << Q_FUNC_INFO << "Scrobbling now:" << m_track.toString();
190    m_scrobbler->cache( m_track );
191    m_scrobbler->submit();
192}
193
194
195void
196LastFmPlugin::sendLoveSong( const InfoType type, QVariant input )
197{
198    qDebug() << Q_FUNC_INFO;
199
200    if ( !input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
201    {
202        tLog() << "LastFmPlugin::nowPlaying cannot convert input!";
203        return;
204    }
205
206    InfoStringHash hash = input.value< Tomahawk::InfoSystem::InfoStringHash >();
207    if ( !hash.contains( "title" ) || !hash.contains( "artist" ) || !hash.contains( "album" ) )
208        return;
209
210    lastfm::MutableTrack track;
211    track.stamp();
212
213    track.setTitle( hash["title"] );
214    track.setArtist( hash["artist"] );
215    track.setAlbum( hash["album"] );
216    bool ok;
217    track.setDuration( hash["duration"].toUInt( &ok ) );
218    track.setSource( lastfm::Track::Player );
219
220    if ( type == Tomahawk::InfoSystem::InfoLove )
221    {
222        track.love();
223    }
224    else if ( type == Tomahawk::InfoSystem::InfoUnLove )
225    {
226        track.unlove();
227    }
228}
229
230
231void
232LastFmPlugin::fetchSimilarArtists( Tomahawk::InfoSystem::InfoRequestData requestData )
233{
234    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
235    {
236        dataError( requestData );
237        return;
238    }
239    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
240    if ( !hash.contains( "artist" ) )
241    {
242        dataError( requestData );
243        return;
244    }
245
246    Tomahawk::InfoSystem::InfoStringHash criteria;
247    criteria["artist"] = hash["artist"];
248
249    emit getCachedInfo( criteria, 2419200000, requestData );
250}
251
252
253void
254LastFmPlugin::fetchTopTracks( Tomahawk::InfoSystem::InfoRequestData requestData )
255{
256    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
257    {
258        dataError( requestData );
259        return;
260    }
261    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
262    if ( !hash.contains( "artist" ) )
263    {
264        dataError( requestData );
265        return;
266    }
267
268    Tomahawk::InfoSystem::InfoStringHash criteria;
269    criteria["artist"] = hash["artist"];
270
271    emit getCachedInfo( criteria, 2419200000, requestData );
272}
273
274void
275LastFmPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
276{
277    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
278    {
279        dataError( requestData );
280        return;
281    }
282    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
283    Tomahawk::InfoSystem::InfoStringHash criteria;
284    if ( !hash.contains( "chart_id" ) )
285    {
286        dataError( requestData );
287        return;
288    } else {
289        criteria["chart_id"] = hash["chart_id"];
290    }
291
292    emit getCachedInfo( criteria, 0, requestData );
293}
294
295void
296LastFmPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
297{
298    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
299    {
300        dataError( requestData );
301        return;
302    }
303    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
304    Tomahawk::InfoSystem::InfoStringHash criteria;
305
306    emit getCachedInfo( criteria, 0, requestData );
307}
308
309void
310LastFmPlugin::fetchCoverArt( Tomahawk::InfoSystem::InfoRequestData requestData )
311{
312    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
313    {
314        dataError( requestData );
315        return;
316    }
317    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
318    if ( !hash.contains( "artist" ) || !hash.contains( "album" ) )
319    {
320        dataError( requestData );
321        return;
322    }
323
324    Tomahawk::InfoSystem::InfoStringHash criteria;
325    criteria["artist"] = hash["artist"];
326    criteria["album"] = hash["album"];
327
328    emit getCachedInfo( criteria, 2419200000, requestData );
329}
330
331
332void
333LastFmPlugin::fetchArtistImages( Tomahawk::InfoSystem::InfoRequestData requestData )
334{
335    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
336    {
337        dataError( requestData );
338        return;
339    }
340    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
341    if ( !hash.contains( "artist" ) )
342    {
343        dataError( requestData );
344        return;
345    }
346
347    Tomahawk::InfoSystem::InfoStringHash criteria;
348    criteria["artist"] = hash["artist"];
349
350    emit getCachedInfo( criteria, 2419200000, requestData );
351}
352
353
354void
355LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
356{
357    if ( !TomahawkUtils::nam() )
358    {
359        tLog() << "Have a null QNAM, uh oh";
360        emit info( requestData, QVariant() );
361        return;
362    }
363
364    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
365    switch ( requestData.type )
366    {
367        case InfoChart:
368        {
369             /// We need something to check if the request is actually ment to go to this plugin
370            if ( !hash.contains( "chart_source" ) )
371            {
372                dataError( requestData );
373                break;
374            }
375            else
376            {
377                if( "last.fm" != hash["chart_source"] )
378                {
379                    dataError( requestData );
380                    break;
381                }
382
383            }
384            QMap<QString, QString> args;
385            args["method"] = criteria["chart_id"];
386            args["limit"] = "100";
387            QNetworkReply* reply = lastfm::ws::get(args);
388            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
389
390            connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
391            return;
392        }
393
394        case InfoChartCapabilities:
395        {
396            QList< InfoStringHash > track_charts;
397            InfoStringHash c;
398            c[ "type" ] = "tracks";
399            c[ "id" ] = "chart.getTopTracks";
400            c[ "label" ] = tr( "Top Tracks" );
401            track_charts.append( c );
402            c[ "id" ] = "chart.getLovedTracks";
403            c[ "label" ] = tr( "Loved Tracks" );
404            track_charts.append( c );
405            c[ "id" ] = "chart.getHypedTracks";
406            c[ "label" ] = tr( "Hyped Tracks" );
407            track_charts.append( c );
408
409            QList< InfoStringHash > artist_charts;
410            c[ "type" ] = "artists";
411            c[ "id" ] = "chart.getTopArtists";
412            c[ "label" ] = tr( "Top Artists" );
413            artist_charts.append( c );
414            c[ "id" ] = "chart.getHypedArtists";
415            c[ "label" ] = tr( "Hyped Artists" );
416            artist_charts.append( c );
417
418            QVariantMap charts;
419            charts.insert( "Tracks", QVariant::fromValue< QList< InfoStringHash > >( track_charts ) );
420            charts.insert( "Artists", QVariant::fromValue< QList< InfoStringHash > >( artist_charts ) );
421
422            QVariantMap result;
423            result.insert( "Last.fm", QVariant::fromValue<QVariantMap>( charts ) );
424
425            emit info( requestData, result );
426            return;
427        }
428
429        case InfoArtistSimilars:
430        {
431            lastfm::Artist a( criteria["artist"] );
432            QNetworkReply* reply = a.getSimilar();
433            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
434
435            connect( reply, SIGNAL( finished() ), SLOT( similarArtistsReturned() ) );
436            return;
437        }
438
439        case InfoArtistSongs:
440        {
441            lastfm::Artist a( criteria["artist"] );
442            QNetworkReply* reply = a.getTopTracks();
443            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
444
445            connect( reply, SIGNAL( finished() ), SLOT( topTracksReturned() ) );
446            return;
447        }
448
449        case InfoAlbumCoverArt:
450        {
451            QString artistName = criteria["artist"];
452            QString albumName = criteria["album"];
453
454            QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b";
455            QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) );
456            QNetworkReply* reply = TomahawkUtils::nam()->get( req );
457            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
458
459            connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) );
460            return;
461        }
462
463        case InfoArtistImages:
464        {
465            QString artistName = criteria["artist"];
466
467            QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b";
468            QNetworkRequest req( imgurl.arg( artistName ) );
469            QNetworkReply* reply = TomahawkUtils::nam()->get( req );
470            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
471
472            connect( reply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) );
473            return;
474        }
475
476        default:
477        {
478            tLog() << "Couldn't figure out what to do with this type of request after cache miss";
479            emit info( requestData, QVariant() );
480            return;
481        }
482    }
483}
484
485
486void
487LastFmPlugin::similarArtistsReturned()
488{
489    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
490
491    QMap< int, QString > similarArtists = lastfm::Artist::getSimilar( reply );
492
493    QStringList sortedArtists;
494    QStringList sortedScores;
495    QStringList al;
496    QStringList sl;
497
498    foreach ( const QString& artist, similarArtists.values() )
499        al << artist;
500    foreach ( int score, similarArtists.keys() )
501        sl << QString::number( score );
502
503    for ( int i = al.count() - 1; i >= 0; i-- )
504    {
505        sortedArtists << al.at( i );
506        sortedScores << sl.at( i );
507    }
508
509    QVariantMap returnedData;
510    returnedData["artists"] = sortedArtists;
511    returnedData["score"] = sortedScores;
512
513    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
514
515    emit info( requestData, returnedData );
516
517    Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
518    Tomahawk::InfoSystem::InfoStringHash criteria;
519    criteria["artist"] = origData["artist"];
520    emit updateCache( criteria, 2419200000, requestData.type, returnedData );
521}
522
523
524void
525LastFmPlugin::chartReturned()
526{
527    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
528
529    QVariantMap returnedData;
530    const QRegExp tracks_rx( "chart\\.\\S+tracks\\S*", Qt::CaseInsensitive );
531    const QRegExp artists_rx( "chart\\.\\S+artists\\S*", Qt::CaseInsensitive );
532    const QString url = reply->url().toString();
533
534    if ( url.contains( tracks_rx ) )
535    {
536        QList<lastfm::Track> tracks = parseTrackList( reply );
537        QList<InfoStringHash> top_tracks;
538        foreach( const lastfm::Track &t, tracks ) {
539            InfoStringHash pair;
540            pair[ "artist" ] = t.artist().toString();
541            pair[ "track" ] = t.title();
542            top_tracks << pair;
543        }
544        returnedData["tracks"] = QVariant::fromValue( top_tracks );
545        returnedData["type"] = "tracks";
546
547    }
548    else if ( url.contains( artists_rx ) )
549    {
550        QList<lastfm::Artist> list = lastfm::Artist::list( reply );
551        QStringList al;
552        foreach ( const lastfm::Artist& a, list )
553            al << a.toString();
554        returnedData["artists"] = al;
555        returnedData["type"] = "artists";
556    }
557    else
558    {
559        tDebug() << Q_FUNC_INFO << "got non tracks and non artists";
560    }
561
562    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
563
564    emit info( requestData, returnedData );
565    // TODO update cache
566}
567
568
569void
570LastFmPlugin::topTracksReturned()
571{
572    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
573
574    QStringList topTracks = lastfm::Artist::getTopTracks( reply );
575    QVariantMap returnedData;
576    returnedData["tracks"] = topTracks;
577
578    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
579
580    emit info( requestData, returnedData );
581
582    Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
583    Tomahawk::InfoSystem::InfoStringHash criteria;
584    criteria["artist"] = origData["artist"];
585    emit updateCache( criteria, 0, requestData.type, returnedData );
586}
587
588
589void
590LastFmPlugin::coverArtReturned()
591{
592    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
593    QUrl redir = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ).toUrl();
594    if ( redir.isEmpty() )
595    {
596        QByteArray ba = reply->readAll();
597        if ( ba.isNull() || !ba.length() )
598        {
599            tLog() << Q_FUNC_INFO << "Uh oh, null byte array";
600            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
601            return;
602        }
603        foreach ( const QUrl& url, m_badUrls )
604        {
605            if ( reply->url().toString().startsWith( url.toString() ) )
606                ba = QByteArray();
607        }
608
609        QVariantMap returnedData;
610        returnedData["imgbytes"] = ba;
611        returnedData["url"] = reply->url().toString();
612
613        Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
614
615        emit info( requestData, returnedData );
616
617        Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
618        Tomahawk::InfoSystem::InfoStringHash criteria;
619        criteria["artist"] = origData["artist"];
620        criteria["album"] = origData["album"];
621        emit updateCache( criteria, 2419200000, requestData.type, returnedData );
622    }
623    else
624    {
625        if ( !TomahawkUtils::nam() )
626        {
627            tLog() << Q_FUNC_INFO << "Uh oh, nam is null";
628            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
629            return;
630        }
631        // Follow HTTP redirect
632        QNetworkRequest req( redir );
633        QNetworkReply* newReply = TomahawkUtils::nam()->get( req );
634        newReply->setProperty( "requestData", reply->property( "requestData" ) );
635        connect( newReply, SIGNAL( finished() ), SLOT( coverArtReturned() ) );
636    }
637
638    reply->deleteLater();
639}
640
641
642void
643LastFmPlugin::artistImagesReturned()
644{
645    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
646    QUrl redir = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ).toUrl();
647    if ( redir.isEmpty() )
648    {
649        QByteArray ba = reply->readAll();
650        if ( ba.isNull() || !ba.length() )
651        {
652            tLog() << Q_FUNC_INFO << "Uh oh, null byte array";
653            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
654            return;
655        }
656        foreach ( const QUrl& url, m_badUrls )
657        {
658            if ( reply->url().toString().startsWith( url.toString() ) )
659                ba = QByteArray();
660        }
661        QVariantMap returnedData;
662        returnedData["imgbytes"] = ba;
663        returnedData["url"] = reply->url().toString();
664
665        Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
666
667        emit info( requestData, returnedData );
668
669        Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
670        Tomahawk::InfoSystem::InfoStringHash criteria;
671        criteria["artist"] = origData["artist"];
672        emit updateCache( criteria, 2419200000, requestData.type, returnedData );
673    }
674    else
675    {
676        if ( !TomahawkUtils::nam() )
677        {
678            tLog() << Q_FUNC_INFO << "Uh oh, nam is null";
679            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
680            return;
681        }
682        // Follow HTTP redirect
683        QNetworkRequest req( redir );
684        QNetworkReply* newReply = TomahawkUtils::nam()->get( req );
685        newReply->setProperty( "requestData", reply->property( "requestData" ) );
686        connect( newReply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) );
687    }
688
689    reply->deleteLater();
690}
691
692
693void
694LastFmPlugin::settingsChanged()
695{
696    if ( !m_scrobbler && TomahawkSettings::instance()->scrobblingEnabled() )
697    { // can simply create the scrobbler
698        lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername();
699        m_pw = TomahawkSettings::instance()->lastFmPassword();
700
701        createScrobbler();
702    }
703    else if ( m_scrobbler && !TomahawkSettings::instance()->scrobblingEnabled() )
704    {
705        delete m_scrobbler;
706        m_scrobbler = 0;
707    }
708    else if ( TomahawkSettings::instance()->lastFmUsername() != lastfm::ws::Username ||
709               TomahawkSettings::instance()->lastFmPassword() != m_pw )
710    {
711        lastfm::ws::Username = TomahawkSettings::instance()->lastFmUsername();
712        m_pw = TomahawkSettings::instance()->lastFmPassword();
713        // credentials have changed, have to re-create scrobbler for them to take effect
714        if ( m_scrobbler )
715        {
716            delete m_scrobbler;
717            m_scrobbler = 0;
718        }
719
720        createScrobbler();
721    }
722}
723
724
725void
726LastFmPlugin::onAuthenticated()
727{
728    QNetworkReply* authJob = dynamic_cast<QNetworkReply*>( sender() );
729    if ( !authJob )
730    {
731        tLog() << Q_FUNC_INFO << "Help! No longer got a last.fm auth job!";
732        return;
733    }
734
735    if ( authJob->error() == QNetworkReply::NoError )
736    {
737        lastfm::XmlQuery lfm = lastfm::XmlQuery( authJob->readAll() );
738
739        if ( lfm.children( "error" ).size() > 0 )
740        {
741            tLog() << "Error from authenticating with Last.fm service:" << lfm.text();
742            TomahawkSettings::instance()->setLastFmSessionKey( QByteArray() );
743        }
744        else
745        {
746            lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text();
747
748            TomahawkSettings::instance()->setLastFmSessionKey( lastfm::ws::SessionKey.toLatin1() );
749
750//            qDebug() << "Got session key from last.fm";
751            if ( TomahawkSettings::instance()->scrobblingEnabled() )
752                m_scrobbler = new lastfm::Audioscrobbler( "thk" );
753        }
754    }
755    else
756    {
757        tLog() << "Got error in Last.fm authentication job:" << authJob->errorString();
758    }
759
760    authJob->deleteLater();
761}
762
763
764void
765LastFmPlugin::createScrobbler()
766{
767    if ( TomahawkSettings::instance()->lastFmSessionKey().isEmpty() ) // no session key, so get one
768    {
769        qDebug() << "LastFmPlugin::createScrobbler Session key is empty";
770        QString authToken = TomahawkUtils::md5( ( lastfm::ws::Username.toLower() + TomahawkUtils::md5( m_pw.toUtf8() ) ).toUtf8() );
771
772        QMap<QString, QString> query;
773        query[ "method" ] = "auth.getMobileSession";
774        query[ "username" ] = lastfm::ws::Username;
775        query[ "authToken" ] = authToken;
776        QNetworkReply* authJob = lastfm::ws::post( query );
777
778        connect( authJob, SIGNAL( finished() ), SLOT( onAuthenticated() ) );
779    }
780    else
781    {
782        qDebug() << "LastFmPlugin::createScrobbler Already have session key";
783        lastfm::ws::SessionKey = TomahawkSettings::instance()->lastFmSessionKey();
784
785        m_scrobbler = new lastfm::Audioscrobbler( "thk" );
786    }
787}
788
789
790QList<lastfm::Track>
791LastFmPlugin::parseTrackList( QNetworkReply* reply )
792{
793    QList<lastfm::Track> tracks;
794    try
795    {
796        lastfm::XmlQuery lfm = reply->readAll();
797        foreach ( lastfm::XmlQuery xq, lfm.children( "track" ) )
798        {
799            tracks.append( lastfm::Track( xq ) );
800        }
801    }
802    catch ( lastfm::ws::ParseError& e )
803    {
804        qWarning() << e.what();
805    }
806
807    return tracks;
808}