PageRenderTime 235ms CodeModel.GetById 60ms app.highlight 88ms RepoModel.GetById 83ms app.codeStats 1ms

/thirdparty/liblastfm2/src/scrobble/Audioscrobbler.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 246 lines | 177 code | 44 blank | 25 comment | 32 complexity | a2c7c3200f27ed3ae42fa6ca852d98c9 MD5 | raw file
  1/*
  2   Copyright 2009 Last.fm Ltd. 
  3      - Primarily authored by Max Howell, Jono Cole and Doug Mansell
  4
  5   This file is part of liblastfm.
  6
  7   liblastfm is free software: you can redistribute it and/or modify
  8   it under the terms of the GNU General Public License as published by
  9   the Free Software Foundation, either version 3 of the License, or
 10   (at your option) any later version.
 11
 12   liblastfm is distributed in the hope that it will be useful,
 13   but WITHOUT ANY WARRANTY; without even the implied warranty of
 14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15   GNU General Public License for more details.
 16
 17   You should have received a copy of the GNU General Public License
 18   along with liblastfm.  If not, see <http://www.gnu.org/licenses/>.
 19*/
 20
 21#include <QPointer>
 22
 23#include "Audioscrobbler.h"
 24#include "ScrobbleCache.h"
 25
 26#include "../types/User.h"
 27#include "../types/Track.h"
 28#include "../ws/ws.h"
 29#include "../core/XmlQuery.h"
 30
 31
 32namespace lastfm
 33{
 34    class AudioscrobblerPrivate
 35    {
 36    public:
 37        AudioscrobblerPrivate(const QString& id)
 38                : m_id( id )
 39                , m_cache( ws::Username )
 40        {}
 41        
 42        ~AudioscrobblerPrivate()
 43        {
 44        }
 45
 46        const QString m_id;
 47        ScrobbleCache m_cache;
 48        QList<Track> m_batch;
 49        QPointer<QNetworkReply> m_nowPlayingReply;
 50        QPointer<QNetworkReply> m_scrobbleReply;
 51        Track m_nowPlayingTrack;
 52    };
 53}
 54
 55
 56lastfm::Audioscrobbler::Audioscrobbler( const QString& id )
 57        : d( new AudioscrobblerPrivate(id) )
 58{
 59    submit();
 60}
 61
 62
 63lastfm::Audioscrobbler::~Audioscrobbler()
 64{
 65    delete d;
 66}
 67
 68
 69void
 70lastfm::Audioscrobbler::nowPlaying( const Track& track )
 71{
 72    if ( d->m_nowPlayingReply.isNull())
 73    {
 74        d->m_nowPlayingTrack = track;
 75        d->m_nowPlayingReply = track.updateNowPlaying();
 76        connect( d->m_nowPlayingReply, SIGNAL(finished()), SLOT(onNowPlayingReturn()));
 77    }
 78}
 79
 80
 81void
 82lastfm::Audioscrobbler::cache( const Track& track )
 83{
 84    QList<Track> tracks;
 85    tracks.append( track );
 86    cacheBatch( tracks );
 87}
 88
 89
 90void
 91lastfm::Audioscrobbler::cacheBatch( const QList<lastfm::Track>& tracks )
 92{
 93    d->m_cache.add( tracks );
 94
 95    foreach ( const Track& track, d->m_cache.tracks() )
 96        MutableTrack( track ).setScrobbleStatus( Track::Cached );
 97
 98    emit scrobblesCached( tracks );
 99    submit();
100}
101
102
103void
104lastfm::Audioscrobbler::submit()
105{
106    if (d->m_cache.tracks().isEmpty() // there are no tracks to submit
107            || !d->m_scrobbleReply.isNull() ) // we are already submitting scrobbles
108        return;
109
110    // copy tracks to be submitted to a temporary list
111    d->m_batch = d->m_cache.tracks().mid( 0, 50 );
112
113    // if there is only one track use track.scrobble, otherwise use track.scrobbleBatch
114    if (d->m_batch.count() == 1)
115        d->m_scrobbleReply = d->m_batch[0].scrobble();
116    else
117        d->m_scrobbleReply = lastfm::Track::scrobble( d->m_batch );
118
119    connect( d->m_scrobbleReply, SIGNAL(finished()), SLOT(onTrackScrobbleReturn()));
120}
121
122void
123lastfm::Audioscrobbler::parseTrack( const XmlQuery& trackXml, const Track& track )
124{
125    MutableTrack mTrack = MutableTrack( track );
126    bool isScrobble = QDomElement(trackXml).tagName() == "scrobble";
127
128    if ( trackXml["ignoredMessage"].attribute("code") == "0" )
129    {
130        if ( isScrobble ) mTrack.setScrobbleStatus( Track::Submitted );
131
132        // corrections!
133        if ( trackXml["track"].attribute("corrected") == "1"
134             || trackXml["artist"].attribute("corrected") == "1"
135             || trackXml["album"].attribute("corrected") == "1"
136             || trackXml["albumArtist"].attribute("corrected") == "1")
137        {
138            mTrack.setCorrections(trackXml["track"].text(),
139                                           trackXml["album"].text(),
140                                           trackXml["artist"].text(),
141                                           trackXml["albumArtist"].text());
142        }
143    }
144    else if ( isScrobble )
145    {
146        mTrack.setScrobbleError( static_cast<Track::ScrobbleError>(trackXml["ignoredMessage"].attribute("code").toInt()) );
147        mTrack.setScrobbleStatus( Track::Error );
148    }
149}
150
151void
152lastfm::Audioscrobbler::onNowPlayingReturn()
153{
154    try
155    {
156        lastfm::XmlQuery lfm = static_cast<QNetworkReply*>(sender())->readAll();
157
158        qDebug() << lfm;
159
160        if ( lfm.attribute("status") == "ok" )
161            parseTrack( lfm["nowplaying"], d->m_nowPlayingTrack );
162        else
163            emit nowPlayingError( lfm["error"].attribute("code").toInt(), lfm["error"].text() );
164
165        d->m_nowPlayingTrack = Track();
166        d->m_nowPlayingReply = 0;
167    }
168    catch ( lastfm::ws::ParseError p )
169    {
170        qDebug() << p.message() << p.enumValue();
171    }
172    catch ( lastfm::ws::Error p )
173    {
174        qDebug() << p;
175    }
176
177    d->m_nowPlayingTrack = Track();
178    d->m_nowPlayingReply = 0;
179}
180
181
182void
183lastfm::Audioscrobbler::onTrackScrobbleReturn()
184{
185    try
186    {
187        lastfm::XmlQuery lfm = d->m_scrobbleReply->readAll();
188
189        qDebug() << lfm;
190
191        if (lfm.attribute("status") == "ok")
192        {
193            int index = 0;
194
195            foreach ( const XmlQuery& scrobble, lfm["scrobbles"].children("scrobble") )
196                parseTrack( scrobble, d->m_batch.at( index++ ) );
197
198            emit scrobblesSubmitted( d->m_batch );
199
200            d->m_cache.remove( d->m_batch );
201            d->m_batch.clear();
202        }
203        else if ( d->m_scrobbleReply->error() == QNetworkReply::NoError )
204        {
205            // The scrobble submission failed, but the http request was sucessful
206
207            if ( !(lfm["error"].attribute("code") == "9" // Bad session
208                || lfm["error"].attribute("code") == "11" // Service offline
209                || lfm["error"].attribute("code") == "16") ) // Service temporarily unavailable
210            {
211                foreach ( const Track& track, d->m_batch )
212                {
213                    MutableTrack mTrack = MutableTrack( track );
214                    mTrack.setScrobbleError( static_cast<Track::ScrobbleError>(lfm["error"].attribute("code").toInt()) );
215                    mTrack.setScrobbleErrorText( lfm["error"].text() );
216                    mTrack.setScrobbleStatus( Track::Error );
217                }
218
219                emit scrobblesSubmitted( d->m_batch );
220
221                // clear the cache if it was not one of these error codes
222                d->m_cache.remove( d->m_batch );
223                d->m_batch.clear();
224            }
225            else
226            {
227                Q_ASSERT(false);
228            }
229        }
230
231        d->m_scrobbleReply = 0;
232
233        // check is there are anymore scrobbles to submit
234        submit();
235    }
236    catch ( lastfm::ws::ParseError p )
237    {
238        qDebug() << p.message() << p.enumValue();
239        d->m_scrobbleReply = 0;
240    }
241    catch ( lastfm::ws::Error p )
242    {
243        qDebug() << p;
244        d->m_scrobbleReply = 0;
245    }
246}