~ubuntu-branches/ubuntu/trusty/tomahawk/trusty-proposed

« back to all changes in this revision

Viewing changes to src/libtomahawk/accounts/lastfm/LastFmConfig.cpp

  • Committer: Package Import Robot
  • Author(s): Harald Sitter
  • Date: 2013-03-07 21:50:13 UTC
  • Revision ID: package-import@ubuntu.com-20130307215013-6gdjkdds7i9uenvs
Tags: upstream-0.6.0+dfsg
ImportĀ upstreamĀ versionĀ 0.6.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
 
2
 *
 
3
 *   Copyright 2010-2012, Leo Franchi <lfranchi@kde.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 "LastFmConfig.h"
 
20
#include "ui_LastFmConfig.h"
 
21
 
 
22
#include <boost/bind.hpp>
 
23
 
 
24
#include "LastFmAccount.h"
 
25
#include "database/Database.h"
 
26
#include "database/DatabaseCommand_LogPlayback.h"
 
27
#include <database/DatabaseCommand_LoadSocialActions.h>
 
28
#include <database/DatabaseCommand_SocialAction.h>
 
29
#include "utils/TomahawkUtils.h"
 
30
#include "utils/Logger.h"
 
31
#include "utils/Closure.h"
 
32
 
 
33
#include <lastfm/ws.h>
 
34
#include <lastfm/User.h>
 
35
#include <lastfm/XmlQuery.h>
 
36
#include <lastfm/Track.h>
 
37
 
 
38
using namespace Tomahawk::Accounts;
 
39
 
 
40
 
 
41
LastFmConfig::LastFmConfig( LastFmAccount* account )
 
42
    : QWidget( 0 )
 
43
    , m_account( account )
 
44
    , m_page( 1 )
 
45
    , m_lastTimeStamp( 0 )
 
46
    , m_totalLovedPages( -1 )
 
47
    , m_doneFetchingLoved( false )
 
48
    , m_doneFetchingLocal( false )
 
49
{
 
50
    m_ui = new Ui_LastFmConfig;
 
51
    m_ui->setupUi( this );
 
52
 
 
53
    m_ui->progressBar->hide();
 
54
 
 
55
    m_ui->username->setText( m_account->username() );
 
56
    m_ui->password->setText( m_account->password() );
 
57
    m_ui->enable->setChecked( m_account->scrobble() );
 
58
 
 
59
    connect( m_ui->testLogin, SIGNAL( clicked( bool ) ), SLOT( testLogin() ) );
 
60
    connect( m_ui->importHistory, SIGNAL( clicked( bool ) ), SLOT( loadHistory() ) );
 
61
    connect( m_ui->syncLovedTracks, SIGNAL( clicked( bool ) ), SLOT( syncLovedTracks() ) );
 
62
 
 
63
    connect( m_ui->username, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
 
64
    connect( m_ui->password, SIGNAL( textChanged( QString ) ), SLOT( enableButton() ) );
 
65
}
 
66
 
 
67
 
 
68
QString
 
69
LastFmConfig::password() const
 
70
{
 
71
    return m_ui->password->text();
 
72
}
 
73
 
 
74
 
 
75
bool
 
76
LastFmConfig::scrobble() const
 
77
{
 
78
    return m_ui->enable->isChecked();
 
79
}
 
80
 
 
81
 
 
82
QString
 
83
LastFmConfig::username() const
 
84
{
 
85
    return m_ui->username->text().trimmed();
 
86
}
 
87
 
 
88
 
 
89
void
 
90
LastFmConfig::testLogin()
 
91
{
 
92
    m_ui->testLogin->setEnabled( false );
 
93
    m_ui->testLogin->setText( tr( "Testing..." ) );
 
94
 
 
95
    QString authToken = TomahawkUtils::md5( ( m_ui->username->text().toLower() + TomahawkUtils::md5( m_ui->password->text().toUtf8() ) ).toUtf8() );
 
96
 
 
97
    // now authenticate w/ last.fm and get our session key
 
98
    QMap<QString, QString> query;
 
99
    query[ "method" ] = "auth.getMobileSession";
 
100
    query[ "username" ] =  m_ui->username->text().toLower();
 
101
    query[ "authToken" ] = authToken;
 
102
 
 
103
    // ensure they have up-to-date settings
 
104
    lastfm::setNetworkAccessManager( TomahawkUtils::nam() );
 
105
 
 
106
    QNetworkReply* authJob = lastfm::ws::post( query );
 
107
 
 
108
    connect( authJob, SIGNAL( finished() ), SLOT( onLastFmFinished() ) );
 
109
}
 
110
 
 
111
 
 
112
void
 
113
LastFmConfig::enableButton()
 
114
{
 
115
    m_ui->testLogin->setText( tr( "Test Login" ) );
 
116
    m_ui->testLogin->setEnabled( true );
 
117
}
 
118
 
 
119
 
 
120
void
 
121
LastFmConfig::loadHistory()
 
122
{
 
123
    if ( m_lastTimeStamp )
 
124
    {
 
125
        m_ui->importHistory->setText( tr( "Importing %1", "e.g. Importing 2012/01/01" ).arg( QDateTime::fromTime_t( m_lastTimeStamp ).toString( "MMMM d yyyy" ) ) );
 
126
    }
 
127
    else
 
128
        m_ui->importHistory->setText( tr( "Importing History..." ) );
 
129
 
 
130
    m_ui->importHistory->setEnabled( false );
 
131
    m_ui->progressBar->show();
 
132
 
 
133
    emit sizeHintChanged();
 
134
 
 
135
    QNetworkReply* reply = lastfm::User( m_ui->username->text().toLower() ).getRecentTracks( 200, m_page );
 
136
    connect( reply, SIGNAL( finished() ), SLOT( onHistoryLoaded() ) );
 
137
}
 
138
 
 
139
 
 
140
void
 
141
LastFmConfig::onHistoryLoaded()
 
142
{
 
143
    uint total = 0;
 
144
    bool finished = false;
 
145
    QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
 
146
 
 
147
    try
 
148
    {
 
149
        lastfm::XmlQuery lfm;
 
150
        lfm.parse( reply->readAll() );
 
151
 
 
152
        foreach ( lastfm::XmlQuery e, lfm.children( "track" ) )
 
153
        {
 
154
//            tDebug() << "Found:" << e.children( "artist" ).first()["name"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt();
 
155
            Tomahawk::query_ptr query = Tomahawk::Query::get( e.children( "artist" ).first()["name"].text(), e["name"].text(), QString(), QString(), false );
 
156
            if ( query.isNull() )
 
157
                continue;
 
158
 
 
159
            m_lastTimeStamp = e["date"].attribute( "uts" ).toUInt();
 
160
 
 
161
            DatabaseCommand_LogPlayback* cmd = new DatabaseCommand_LogPlayback( query, DatabaseCommand_LogPlayback::Finished, m_lastTimeStamp );
 
162
            Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
 
163
        }
 
164
 
 
165
        if ( !lfm.children( "recenttracks" ).isEmpty() )
 
166
        {
 
167
            lastfm::XmlQuery stats = lfm.children( "recenttracks" ).first();
 
168
 
 
169
            uint page = stats.attribute( "page" ).toUInt();
 
170
            total = stats.attribute( "totalPages" ).toUInt();
 
171
 
 
172
            m_ui->progressBar->setMaximum( total );
 
173
            m_ui->progressBar->setValue( page );
 
174
 
 
175
            if ( page < total )
 
176
            {
 
177
                m_page = page + 1;
 
178
                loadHistory();
 
179
            }
 
180
            else
 
181
                finished = true;
 
182
        }
 
183
        else
 
184
            finished = true;
 
185
    }
 
186
    catch( lastfm::ws::ParseError e )
 
187
    {
 
188
        tDebug() << "XmlQuery error:" << e.message();
 
189
        finished = true;
 
190
    }
 
191
 
 
192
    if ( finished )
 
193
    {
 
194
        if ( m_page != total )
 
195
        {
 
196
            //: Text on a button that resumes import
 
197
            m_ui->importHistory->setText( tr( "History Incomplete. Resume" ) );
 
198
            m_ui->importHistory->setEnabled( true );
 
199
        }
 
200
        else
 
201
        {
 
202
            m_ui->importHistory->setText( tr( "Playback History Imported" ) );
 
203
        }
 
204
    }
 
205
}
 
206
 
 
207
 
 
208
void
 
209
LastFmConfig::onLastFmFinished()
 
210
{
 
211
    QNetworkReply* authJob = dynamic_cast<QNetworkReply*>( sender() );
 
212
    if( !authJob )
 
213
    {
 
214
        qDebug() << Q_FUNC_INFO << "No auth job returned!";
 
215
        return;
 
216
    }
 
217
    if( authJob->error() == QNetworkReply::NoError )
 
218
    {
 
219
        lastfm::XmlQuery lfm;
 
220
        lfm.parse( authJob->readAll() );
 
221
 
 
222
        if( lfm.children( "error" ).size() > 0 )
 
223
        {
 
224
            qDebug() << "ERROR from last.fm:" << lfm.text();
 
225
            m_ui->testLogin->setText( tr( "Failed" ) );
 
226
            m_ui->testLogin->setEnabled( true );
 
227
        }
 
228
        else
 
229
        {
 
230
            m_ui->testLogin->setText( tr( "Success" ) );
 
231
            m_ui->testLogin->setEnabled( false );
 
232
            m_ui->syncLovedTracks->setEnabled( true );
 
233
        }
 
234
    }
 
235
    else
 
236
    {
 
237
        switch( authJob->error() )
 
238
        {
 
239
            case QNetworkReply::ContentOperationNotPermittedError:
 
240
            case QNetworkReply::AuthenticationRequiredError:
 
241
                m_ui->testLogin->setText( tr( "Failed" ) );
 
242
                m_ui->testLogin->setEnabled( true );
 
243
                break;
 
244
 
 
245
            default:
 
246
                qDebug() << "Couldn't get last.fm auth result";
 
247
                m_ui->testLogin->setText( tr( "Could not contact server" ) );
 
248
                m_ui->testLogin->setEnabled( true );
 
249
                return;
 
250
        }
 
251
    }
 
252
}
 
253
 
 
254
 
 
255
void
 
256
LastFmConfig::syncLovedTracks( uint page )
 
257
{
 
258
    QNetworkReply* reply = lastfm::User( username() ).getLovedTracks( 200, page );
 
259
 
 
260
    m_ui->syncLovedTracks->setEnabled( false );
 
261
    m_ui->syncLovedTracks->setText( tr( "Synchronizing..." ) );
 
262
    m_ui->progressBar->show();
 
263
    emit sizeHintChanged();
 
264
 
 
265
    NewClosure( reply, SIGNAL( finished() ), this, SLOT( onLovedFinished( QNetworkReply* ) ), reply );
 
266
 
 
267
    DatabaseCommand_LoadSocialActions* cmd = new DatabaseCommand_LoadSocialActions( "Love", SourceList::instance()->getLocal() );
 
268
    connect( cmd, SIGNAL( done( DatabaseCommand_LoadSocialActions::TrackActions ) ), this, SLOT( localLovedLoaded( DatabaseCommand_LoadSocialActions::TrackActions ) ) );
 
269
 
 
270
    Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
 
271
}
 
272
 
 
273
 
 
274
void
 
275
LastFmConfig::onLovedFinished( QNetworkReply* reply )
 
276
{
 
277
    Q_ASSERT( reply );
 
278
 
 
279
    try
 
280
    {
 
281
        lastfm::XmlQuery lfm;
 
282
        lfm.parse( reply->readAll() );
 
283
 
 
284
        if ( !lfm.children( "lovedtracks" ).isEmpty() )
 
285
        {
 
286
            lastfm::XmlQuery loved = lfm.children( "lovedtracks" ).first();
 
287
 
 
288
            const int thisPage = loved.attribute( "page" ).toInt();
 
289
 
 
290
            if ( m_totalLovedPages < 0 )
 
291
            {
 
292
                m_totalLovedPages = loved.attribute( "totalPages" ).toInt();
 
293
                m_ui->progressBar->setMaximum( m_totalLovedPages + 2 );
 
294
            }
 
295
 
 
296
            m_ui->progressBar->setValue( thisPage );
 
297
            foreach ( lastfm::XmlQuery e, loved.children( "track" ) )
 
298
            {
 
299
                 tDebug() << "Found:" << e.children( "artist" ).first()["name"].text() << e["name"].text() << e["date"].attribute( "uts" ).toUInt();
 
300
                Tomahawk::query_ptr query = Tomahawk::Query::get( e.children( "artist" ).first()["name"].text(), e["name"].text(), QString(), QString(), false );
 
301
                if ( query.isNull() )
 
302
                    continue;
 
303
 
 
304
                m_lastfmLoved.insert( query );
 
305
            }
 
306
 
 
307
            if ( thisPage == m_totalLovedPages )
 
308
            {
 
309
                m_doneFetchingLoved = true;
 
310
 
 
311
                if ( m_doneFetchingLocal )
 
312
                    syncLoved();
 
313
 
 
314
                return;
 
315
            }
 
316
            else
 
317
            {
 
318
                syncLovedTracks( thisPage + 1 );
 
319
            }
 
320
        }
 
321
        else
 
322
        {
 
323
            m_ui->syncLovedTracks->setText( "Failed" );
 
324
            m_ui->progressBar->hide();
 
325
            emit sizeHintChanged();
 
326
        }
 
327
    }
 
328
    catch( lastfm::ws::ParseError e )
 
329
    {
 
330
        m_ui->syncLovedTracks->setText( "Failed" );
 
331
        m_ui->progressBar->hide();
 
332
        emit sizeHintChanged();
 
333
        tDebug() << "XmlQuery error:" << e.message();
 
334
    }
 
335
}
 
336
 
 
337
 
 
338
bool trackEquality( const Tomahawk::query_ptr& first, const Tomahawk::query_ptr& second )
 
339
{
 
340
    qDebug() << "Comparing:" << first->track() << second->track();
 
341
    qDebug() << "==========" << first->artist() << second->artist();
 
342
    return first->equals( second, true );
 
343
}
 
344
 
 
345
 
 
346
void
 
347
LastFmConfig::localLovedLoaded( DatabaseCommand_LoadSocialActions::TrackActions tracks )
 
348
{
 
349
    m_localLoved = tracks;
 
350
    m_doneFetchingLocal = true;
 
351
 
 
352
    if ( m_doneFetchingLoved )
 
353
        syncLoved();
 
354
}
 
355
 
 
356
 
 
357
void
 
358
LastFmConfig::syncLoved()
 
359
{
 
360
    QSet< Tomahawk::query_ptr > localToLove, lastFmToLove, lastFmToUnlove;
 
361
 
 
362
    const QSet< Tomahawk::query_ptr > myLoved = m_localLoved.keys().toSet();
 
363
 
 
364
    m_ui->progressBar->setValue( m_ui->progressBar->value() + 1 );
 
365
 
 
366
    foreach ( const Tomahawk::query_ptr& lastfmLoved, m_lastfmLoved )
 
367
    {
 
368
        QSet< Tomahawk::query_ptr >::const_iterator iter = std::find_if( myLoved.begin(), myLoved.end(), boost::bind( &trackEquality, _1, boost::ref( lastfmLoved ) ) );
 
369
        if ( iter == myLoved.constEnd() )
 
370
        {
 
371
//             qDebug() << "Found last.fm loved track that we didn't have loved locally:" << lastfmLoved->track() << lastfmLoved->artist();
 
372
            localToLove << lastfmLoved;
 
373
        }
 
374
    }
 
375
 
 
376
    foreach ( const Tomahawk::query_ptr& localLoved, myLoved )
 
377
    {
 
378
        qDebug() << "CHECKING FOR LOCAL LOVED ON LAST.FM TOO:" << m_localLoved[ localLoved ].value.toString() << localLoved->track() << localLoved->artist();
 
379
        QSet< Tomahawk::query_ptr >::const_iterator iter = std::find_if( m_lastfmLoved.begin(), m_lastfmLoved.end(), boost::bind( &trackEquality, _1, boost::ref( localLoved ) ) );
 
380
 
 
381
        qDebug() << "Result:" << (iter == m_lastfmLoved.constEnd());
 
382
        // If we unloved it locally, but it's still loved on last.fm, unlove it
 
383
        if ( m_localLoved[ localLoved ].value.toString() == "false" && iter != m_lastfmLoved.constEnd() )
 
384
            lastFmToUnlove << localLoved;
 
385
 
 
386
        // If we loved it locally but not loved on last.fm, love it
 
387
        if ( m_localLoved[ localLoved ].value.toString() == "true" && iter == m_lastfmLoved.constEnd() )
 
388
        {
 
389
            qDebug() << "Found Local loved track but not on last.fm!:" << localLoved->track() << localLoved->artist();
 
390
            lastFmToLove << localLoved;
 
391
        }
 
392
    }
 
393
 
 
394
    foreach ( const Tomahawk::query_ptr& track, localToLove )
 
395
    {
 
396
        // Don't use the infosystem as we don't want to tweet a few hundred times :)
 
397
        DatabaseCommand_SocialAction* cmd = new DatabaseCommand_SocialAction( track, QString( "Love" ), QString( "true" ) );
 
398
        Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
 
399
    }
 
400
 
 
401
    lastFmToLove.unite( lastFmToUnlove );
 
402
 
 
403
    foreach ( const Tomahawk::query_ptr& track, lastFmToLove )
 
404
    {
 
405
        lastfm::MutableTrack lfmTrack;
 
406
        lfmTrack.stamp();
 
407
 
 
408
        lfmTrack.setTitle( track->track() );
 
409
        lfmTrack.setArtist( track->artist() );
 
410
        lfmTrack.setSource( lastfm::Track::Player );
 
411
 
 
412
        if ( lastFmToUnlove.contains( track ) )
 
413
            lfmTrack.unlove();
 
414
        else
 
415
            lfmTrack.love();
 
416
    }
 
417
 
 
418
    m_ui->progressBar->setValue( m_ui->progressBar->value() + 1 );
 
419
    m_ui->syncLovedTracks->setText( tr( "Synchronization Finished" ) );
 
420
}
 
421