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

« back to all changes in this revision

Viewing changes to src/libtomahawk/Source.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-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
 
4
 *   Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
 
5
 *
 
6
 *   Tomahawk is free software: you can redistribute it and/or modify
 
7
 *   it under the terms of the GNU General Public License as published by
 
8
 *   the Free Software Foundation, either version 3 of the License, or
 
9
 *   (at your option) any later version.
 
10
 *
 
11
 *   Tomahawk is distributed in the hope that it will be useful,
 
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
14
 *   GNU General Public License for more details.
 
15
 *
 
16
 *   You should have received a copy of the GNU General Public License
 
17
 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include "Source.h"
 
21
 
 
22
#include "Collection.h"
 
23
#include "SourceList.h"
 
24
#include "SourcePlaylistInterface.h"
 
25
 
 
26
#include "accounts/AccountManager.h"
 
27
#include "network/ControlConnection.h"
 
28
#include "database/DatabaseCommand_AddSource.h"
 
29
#include "database/DatabaseCommand_CollectionStats.h"
 
30
#include "database/DatabaseCommand_SourceOffline.h"
 
31
#include "database/DatabaseCommand_UpdateSearchIndex.h"
 
32
#include "database/Database.h"
 
33
 
 
34
#include <QCoreApplication>
 
35
#include <QBuffer>
 
36
 
 
37
#include "utils/TomahawkCache.h"
 
38
#include "database/DatabaseCommand_SocialAction.h"
 
39
 
 
40
#ifndef ENABLE_HEADLESS
 
41
    #include "utils/TomahawkUtilsGui.h"
 
42
#endif
 
43
 
 
44
#include "utils/Logger.h"
 
45
 
 
46
using namespace Tomahawk;
 
47
 
 
48
 
 
49
Source::Source( int id, const QString& username )
 
50
    : QObject()
 
51
    , m_isLocal( false )
 
52
    , m_online( false )
 
53
    , m_username( username )
 
54
    , m_id( id )
 
55
    , m_updateIndexWhenSynced( false )
 
56
    , m_avatarUpdated( true )
 
57
    , m_state( DBSyncConnection::UNKNOWN )
 
58
    , m_cc( 0 )
 
59
    , m_commandCount( 0 )
 
60
    , m_avatar( 0 )
 
61
    , m_fancyAvatar( 0 )
 
62
{
 
63
    m_scrubFriendlyName = qApp->arguments().contains( "--demo" );
 
64
 
 
65
    if ( id == 0 )
 
66
        m_isLocal = true;
 
67
 
 
68
    m_currentTrackTimer.setSingleShot( true );
 
69
    connect( &m_currentTrackTimer, SIGNAL( timeout() ), this, SLOT( trackTimerFired() ) );
 
70
 
 
71
    if ( m_isLocal )
 
72
    {
 
73
        connect( Accounts::AccountManager::instance(), SIGNAL( connected( Tomahawk::Accounts::Account* ) ), SLOT( setOnline() ) );
 
74
        connect( Accounts::AccountManager::instance(), SIGNAL( disconnected( Tomahawk::Accounts::Account* ) ), SLOT( setOffline() ) );
 
75
    }
 
76
}
 
77
 
 
78
 
 
79
Source::~Source()
 
80
{
 
81
    qDebug() << Q_FUNC_INFO << friendlyName();
 
82
    delete m_avatar;
 
83
    delete m_fancyAvatar;
 
84
}
 
85
 
 
86
 
 
87
void
 
88
Source::setControlConnection( ControlConnection* cc )
 
89
{
 
90
    m_cc = cc;
 
91
}
 
92
 
 
93
 
 
94
collection_ptr
 
95
Source::collection() const
 
96
{
 
97
    if( m_collections.length() )
 
98
        return m_collections.first();
 
99
 
 
100
    collection_ptr tmp;
 
101
    return tmp;
 
102
}
 
103
 
 
104
 
 
105
void
 
106
Source::setStats( const QVariantMap& m )
 
107
{
 
108
    m_stats = m;
 
109
    emit stats( m_stats );
 
110
    emit stateChanged();
 
111
}
 
112
 
 
113
 
 
114
QString
 
115
Source::friendlyName() const
 
116
{
 
117
    if ( m_friendlyname.isEmpty() )
 
118
        return m_username;
 
119
 
 
120
    //TODO: this is a terrible assumption, help me clean this up, mighty muesli!
 
121
    if ( m_friendlyname.contains( "@conference." ) )
 
122
        return QString( m_friendlyname ).remove( 0, m_friendlyname.lastIndexOf( "/" ) + 1 ).append( " via MUC" );
 
123
 
 
124
    if ( m_friendlyname.contains( "/" ) )
 
125
        return m_friendlyname.left( m_friendlyname.indexOf( "/" ) );
 
126
 
 
127
    return m_friendlyname;
 
128
}
 
129
 
 
130
 
 
131
#ifndef ENABLE_HEADLESS
 
132
void
 
133
Source::setAvatar( const QPixmap& avatar )
 
134
{
 
135
    QByteArray ba;
 
136
    QBuffer buffer( &ba );
 
137
    buffer.open( QIODevice::WriteOnly );
 
138
    avatar.save( &buffer, "PNG" );
 
139
 
 
140
    // Check if the avatar is different by comparing a hash of the first 4096 bytes
 
141
    const QByteArray hash = QCryptographicHash::hash( ba.left( 4096 ), QCryptographicHash::Sha1 );
 
142
    if ( m_avatarHash == hash )
 
143
        return;
 
144
    else
 
145
        m_avatarHash = hash;
 
146
 
 
147
    delete m_avatar;
 
148
    m_avatar = new QPixmap( avatar );
 
149
    m_fancyAvatar = 0;
 
150
 
 
151
    TomahawkUtils::Cache::instance()->putData( "Sources", 7776000000 /* 90 days */, m_username, ba );
 
152
    m_avatarUpdated = true;
 
153
}
 
154
 
 
155
 
 
156
QPixmap
 
157
Source::avatar( TomahawkUtils::ImageMode style, const QSize& size )
 
158
{
 
159
    if ( !m_avatar && m_avatarUpdated )
 
160
    {
 
161
        m_avatar = new QPixmap();
 
162
        QByteArray ba = TomahawkUtils::Cache::instance()->getData( "Sources", m_username ).toByteArray();
 
163
 
 
164
        if ( ba.count() )
 
165
            m_avatar->loadFromData( ba );
 
166
 
 
167
        if ( m_avatar->isNull() )
 
168
        {
 
169
            delete m_avatar;
 
170
            m_avatar = 0;
 
171
        }
 
172
        m_avatarUpdated = false;
 
173
    }
 
174
 
 
175
    if ( style == TomahawkUtils::RoundedCorners && m_avatar && !m_avatar->isNull() && !m_fancyAvatar )
 
176
        m_fancyAvatar = new QPixmap( TomahawkUtils::createRoundedImage( QPixmap( *m_avatar ), QSize( 0, 0 ) ) );
 
177
 
 
178
    QPixmap pixmap;
 
179
    if ( style == TomahawkUtils::RoundedCorners && m_fancyAvatar )
 
180
    {
 
181
        pixmap = *m_fancyAvatar;
 
182
    }
 
183
    else if ( m_avatar )
 
184
    {
 
185
        pixmap = *m_avatar;
 
186
    }
 
187
 
 
188
    if ( !pixmap.isNull() && !size.isEmpty() )
 
189
    {
 
190
        if ( m_coverCache[ style ].contains( size.width() ) )
 
191
        {
 
192
            return m_coverCache[ style ].value( size.width() );
 
193
        }
 
194
 
 
195
        QPixmap scaledCover;
 
196
        scaledCover = pixmap.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
 
197
 
 
198
        QHash< int, QPixmap > innerCache = m_coverCache[ style ];
 
199
        innerCache.insert( size.width(), scaledCover );
 
200
        m_coverCache[ style ] = innerCache;
 
201
 
 
202
        return scaledCover;
 
203
    }
 
204
 
 
205
    return pixmap;
 
206
}
 
207
#endif
 
208
 
 
209
 
 
210
void
 
211
Source::setFriendlyName( const QString& fname )
 
212
{
 
213
    if ( fname.isEmpty() )
 
214
        return;
 
215
 
 
216
    m_friendlyname = fname;
 
217
    if ( m_scrubFriendlyName )
 
218
    {
 
219
        if ( m_friendlyname.indexOf( "@" ) > 0 )
 
220
            m_friendlyname = m_friendlyname.split( "@" ).first();
 
221
    }
 
222
}
 
223
 
 
224
 
 
225
void
 
226
Source::addCollection( const collection_ptr& c )
 
227
{
 
228
    Q_ASSERT( m_collections.length() == 0 ); // only 1 source supported atm
 
229
    m_collections.append( c );
 
230
    emit collectionAdded( c );
 
231
}
 
232
 
 
233
 
 
234
void
 
235
Source::removeCollection( const collection_ptr& c )
 
236
{
 
237
    Q_ASSERT( m_collections.length() == 1 && m_collections.first() == c ); // only 1 source supported atm
 
238
    m_collections.removeAll( c );
 
239
    emit collectionRemoved( c );
 
240
}
 
241
 
 
242
 
 
243
void
 
244
Source::setOffline()
 
245
{
 
246
    qDebug() << Q_FUNC_INFO << friendlyName();
 
247
    if ( !m_online )
 
248
        return;
 
249
 
 
250
    m_online = false;
 
251
    emit offline();
 
252
 
 
253
    if ( !isLocal() )
 
254
    {
 
255
        m_currentTrack.clear();
 
256
        emit stateChanged();
 
257
 
 
258
        m_cc = 0;
 
259
        DatabaseCommand_SourceOffline* cmd = new DatabaseCommand_SourceOffline( id() );
 
260
        Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
 
261
    }
 
262
}
 
263
 
 
264
 
 
265
void
 
266
Source::setOnline()
 
267
{
 
268
    tDebug( LOGVERBOSE ) << Q_FUNC_INFO << friendlyName();
 
269
    if ( m_online )
 
270
        return;
 
271
 
 
272
    m_online = true;
 
273
    emit online();
 
274
 
 
275
    if ( !isLocal() )
 
276
    {
 
277
        // ensure username is in the database
 
278
        DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_username, friendlyName() );
 
279
        connect( cmd, SIGNAL( done( unsigned int, QString ) ),
 
280
                        SLOT( dbLoaded( unsigned int, const QString& ) ) );
 
281
        Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
 
282
    }
 
283
}
 
284
 
 
285
 
 
286
void
 
287
Source::dbLoaded( unsigned int id, const QString& fname )
 
288
{
 
289
    m_id = id;
 
290
    setFriendlyName( fname );
 
291
 
 
292
    emit syncedWithDatabase();
 
293
}
 
294
 
 
295
 
 
296
void
 
297
Source::scanningProgress( unsigned int files )
 
298
{
 
299
    if ( files )
 
300
        m_textStatus = tr( "Scanning (%L1 tracks)" ).arg( files );
 
301
    else
 
302
        m_textStatus = tr( "Scanning" );
 
303
 
 
304
    emit stateChanged();
 
305
}
 
306
 
 
307
 
 
308
void
 
309
Source::scanningFinished( bool updateGUI )
 
310
{
 
311
    m_textStatus = QString();
 
312
 
 
313
    if ( m_updateIndexWhenSynced )
 
314
    {
 
315
        m_updateIndexWhenSynced = false;
 
316
        updateTracks();
 
317
    }
 
318
 
 
319
    emit stateChanged();
 
320
 
 
321
    if ( updateGUI )
 
322
        emit synced();
 
323
}
 
324
 
 
325
 
 
326
void
 
327
Source::onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info )
 
328
{
 
329
    Q_UNUSED( oldstate );
 
330
 
 
331
    QString msg;
 
332
    switch( newstate )
 
333
    {
 
334
        case DBSyncConnection::CHECKING:
 
335
        {
 
336
            msg = tr( "Checking" );
 
337
            break;
 
338
        }
 
339
        case DBSyncConnection::FETCHING:
 
340
        {
 
341
            msg = tr( "Syncing" );
 
342
            break;
 
343
        }
 
344
        case DBSyncConnection::PARSING:
 
345
        {
 
346
            msg = tr( "Importing" );
 
347
            break;
 
348
        }
 
349
        case DBSyncConnection::SCANNING:
 
350
        {
 
351
            msg = tr( "Scanning (%L1 tracks)" ).arg( info );
 
352
            break;
 
353
        }
 
354
        case DBSyncConnection::SYNCED:
 
355
        {
 
356
            msg = QString();
 
357
            break;
 
358
        }
 
359
 
 
360
        default:
 
361
            msg = QString();
 
362
    }
 
363
 
 
364
    m_state = newstate;
 
365
    m_textStatus = msg;
 
366
    emit stateChanged();
 
367
}
 
368
 
 
369
 
 
370
unsigned int
 
371
Source::trackCount() const
 
372
{
 
373
    return m_stats.value( "numfiles" ).toUInt();
 
374
}
 
375
 
 
376
 
 
377
Tomahawk::playlistinterface_ptr
 
378
Source::playlistInterface()
 
379
{
 
380
    if ( m_playlistInterface.isNull() )
 
381
    {
 
382
        Tomahawk::source_ptr source = SourceList::instance()->get( id() );
 
383
        m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::SourcePlaylistInterface( source.data() ) );
 
384
    }
 
385
 
 
386
    return m_playlistInterface;
 
387
}
 
388
 
 
389
 
 
390
void
 
391
Source::onPlaybackStarted( const Tomahawk::query_ptr& query, unsigned int duration )
 
392
{
 
393
    tLog( LOGVERBOSE ) << Q_FUNC_INFO << query->toString();
 
394
 
 
395
    m_currentTrack = query;
 
396
    m_currentTrackTimer.start( duration * 1000 + 900000 ); // duration comes in seconds
 
397
 
 
398
    if ( m_playlistInterface.isNull() )
 
399
        playlistInterface();
 
400
 
 
401
    emit playbackStarted( query );
 
402
    emit stateChanged();
 
403
}
 
404
 
 
405
 
 
406
void
 
407
Source::onPlaybackFinished( const Tomahawk::query_ptr& query )
 
408
{
 
409
    tDebug() << Q_FUNC_INFO << query->toString();
 
410
    emit playbackFinished( query );
 
411
 
 
412
    m_currentTrack.clear();
 
413
    emit stateChanged();
 
414
}
 
415
 
 
416
 
 
417
void
 
418
Source::trackTimerFired()
 
419
{
 
420
    m_currentTrack.clear();
 
421
    emit stateChanged();
 
422
}
 
423
 
 
424
 
 
425
QString
 
426
Source::lastCmdGuid() const
 
427
{
 
428
    QMutexLocker lock( &m_cmdMutex );
 
429
    return m_lastCmdGuid;
 
430
}
 
431
 
 
432
 
 
433
void
 
434
Source::addCommand( const QSharedPointer<DatabaseCommand>& command )
 
435
{
 
436
    QMutexLocker lock( &m_cmdMutex );
 
437
 
 
438
    m_cmds << command;
 
439
    if ( !command->singletonCmd() )
 
440
        m_lastCmdGuid = command->guid();
 
441
 
 
442
    m_commandCount = m_cmds.count();
 
443
}
 
444
 
 
445
 
 
446
void
 
447
Source::executeCommands()
 
448
{
 
449
    if ( QThread::currentThread() != thread() )
 
450
    {
 
451
        QMetaObject::invokeMethod( this, "executeCommands", Qt::QueuedConnection );
 
452
        return;
 
453
    }
 
454
 
 
455
    bool commandsAvail = false;
 
456
    {
 
457
        QMutexLocker lock( &m_cmdMutex );
 
458
        commandsAvail = !m_cmds.isEmpty();
 
459
    }
 
460
 
 
461
    if ( commandsAvail )
 
462
    {
 
463
        QMutexLocker lock( &m_cmdMutex );
 
464
        QList< QSharedPointer<DatabaseCommand> > cmdGroup;
 
465
        QSharedPointer<DatabaseCommand> cmd = m_cmds.takeFirst();
 
466
        while ( cmd->groupable() )
 
467
        {
 
468
            cmdGroup << cmd;
 
469
            if ( !m_cmds.isEmpty() && m_cmds.first()->groupable() && m_cmds.first()->commandname() == cmd->commandname() )
 
470
                cmd = m_cmds.takeFirst();
 
471
            else
 
472
                break;
 
473
        }
 
474
 
 
475
        // return here when the last command finished
 
476
        connect( cmd.data(), SIGNAL( finished() ), SLOT( executeCommands() ) );
 
477
 
 
478
        if ( cmdGroup.count() )
 
479
        {
 
480
            Database::instance()->enqueue( cmdGroup );
 
481
        }
 
482
        else
 
483
        {
 
484
            Database::instance()->enqueue( cmd );
 
485
        }
 
486
 
 
487
        int percentage = ( float( m_commandCount - m_cmds.count() ) / (float)m_commandCount ) * 100.0;
 
488
        m_textStatus = tr( "Saving (%1%)" ).arg( percentage );
 
489
        emit stateChanged();
 
490
    }
 
491
    else
 
492
    {
 
493
        if ( m_updateIndexWhenSynced )
 
494
        {
 
495
            m_updateIndexWhenSynced = false;
 
496
            updateTracks();
 
497
        }
 
498
 
 
499
        m_textStatus = QString();
 
500
        m_state = DBSyncConnection::SYNCED;
 
501
 
 
502
        emit commandsFinished();
 
503
        emit stateChanged();
 
504
        emit synced();
 
505
    }
 
506
}
 
507
 
 
508
 
 
509
void
 
510
Source::reportSocialAttributesChanged( DatabaseCommand_SocialAction* action )
 
511
{
 
512
    Q_ASSERT( action );
 
513
 
 
514
    emit socialAttributesChanged( action->action() );
 
515
 
 
516
    if ( action->action() == "latchOn" )
 
517
    {
 
518
        const source_ptr to = SourceList::instance()->get( action->comment() );
 
519
        if ( !to.isNull() )
 
520
            emit latchedOn( to );
 
521
    }
 
522
    else if ( action->action() == "latchOff" )
 
523
    {
 
524
        const source_ptr from = SourceList::instance()->get( action->comment() );
 
525
        if ( !from.isNull() )
 
526
            emit latchedOff( from );
 
527
    }
 
528
}
 
529
 
 
530
 
 
531
void
 
532
Source::updateTracks()
 
533
{
 
534
    {
 
535
        DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex();
 
536
        Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
 
537
    }
 
538
 
 
539
    {
 
540
        // Re-calculate local db stats
 
541
        DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->get( id() ) );
 
542
        connect( cmd, SIGNAL( done( QVariantMap ) ), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection );
 
543
        Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
 
544
    }
 
545
}
 
546
 
 
547
 
 
548
void
 
549
Source::updateIndexWhenSynced()
 
550
{
 
551
    m_updateIndexWhenSynced = true;
 
552
}
 
553
 
 
554
 
 
555
QString
 
556
Source::textStatus() const
 
557
{
 
558
    if ( !m_textStatus.isEmpty() )
 
559
        return m_textStatus;
 
560
 
 
561
    if ( !currentTrack().isNull() )
 
562
    {
 
563
        return currentTrack()->artist() + " - " + currentTrack()->track();
 
564
    }
 
565
 
 
566
    // do not use isOnline() here - it will always return true for the local source
 
567
    if ( m_online )
 
568
    {
 
569
        return tr( "Online" );
 
570
    }
 
571
    else
 
572
    {
 
573
        return tr( "Offline" );
 
574
    }
 
575
}