1
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
3
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
4
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
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.
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.
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/>.
22
#include "Collection.h"
23
#include "SourceList.h"
24
#include "SourcePlaylistInterface.h"
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"
34
#include <QCoreApplication>
37
#include "utils/TomahawkCache.h"
38
#include "database/DatabaseCommand_SocialAction.h"
40
#ifndef ENABLE_HEADLESS
41
#include "utils/TomahawkUtilsGui.h"
44
#include "utils/Logger.h"
46
using namespace Tomahawk;
49
Source::Source( int id, const QString& username )
53
, m_username( username )
55
, m_updateIndexWhenSynced( false )
56
, m_avatarUpdated( true )
57
, m_state( DBSyncConnection::UNKNOWN )
63
m_scrubFriendlyName = qApp->arguments().contains( "--demo" );
68
m_currentTrackTimer.setSingleShot( true );
69
connect( &m_currentTrackTimer, SIGNAL( timeout() ), this, SLOT( trackTimerFired() ) );
73
connect( Accounts::AccountManager::instance(), SIGNAL( connected( Tomahawk::Accounts::Account* ) ), SLOT( setOnline() ) );
74
connect( Accounts::AccountManager::instance(), SIGNAL( disconnected( Tomahawk::Accounts::Account* ) ), SLOT( setOffline() ) );
81
qDebug() << Q_FUNC_INFO << friendlyName();
88
Source::setControlConnection( ControlConnection* cc )
95
Source::collection() const
97
if( m_collections.length() )
98
return m_collections.first();
106
Source::setStats( const QVariantMap& m )
109
emit stats( m_stats );
115
Source::friendlyName() const
117
if ( m_friendlyname.isEmpty() )
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" );
124
if ( m_friendlyname.contains( "/" ) )
125
return m_friendlyname.left( m_friendlyname.indexOf( "/" ) );
127
return m_friendlyname;
131
#ifndef ENABLE_HEADLESS
133
Source::setAvatar( const QPixmap& avatar )
136
QBuffer buffer( &ba );
137
buffer.open( QIODevice::WriteOnly );
138
avatar.save( &buffer, "PNG" );
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 )
148
m_avatar = new QPixmap( avatar );
151
TomahawkUtils::Cache::instance()->putData( "Sources", 7776000000 /* 90 days */, m_username, ba );
152
m_avatarUpdated = true;
157
Source::avatar( TomahawkUtils::ImageMode style, const QSize& size )
159
if ( !m_avatar && m_avatarUpdated )
161
m_avatar = new QPixmap();
162
QByteArray ba = TomahawkUtils::Cache::instance()->getData( "Sources", m_username ).toByteArray();
165
m_avatar->loadFromData( ba );
167
if ( m_avatar->isNull() )
172
m_avatarUpdated = false;
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 ) ) );
179
if ( style == TomahawkUtils::RoundedCorners && m_fancyAvatar )
181
pixmap = *m_fancyAvatar;
188
if ( !pixmap.isNull() && !size.isEmpty() )
190
if ( m_coverCache[ style ].contains( size.width() ) )
192
return m_coverCache[ style ].value( size.width() );
196
scaledCover = pixmap.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
198
QHash< int, QPixmap > innerCache = m_coverCache[ style ];
199
innerCache.insert( size.width(), scaledCover );
200
m_coverCache[ style ] = innerCache;
211
Source::setFriendlyName( const QString& fname )
213
if ( fname.isEmpty() )
216
m_friendlyname = fname;
217
if ( m_scrubFriendlyName )
219
if ( m_friendlyname.indexOf( "@" ) > 0 )
220
m_friendlyname = m_friendlyname.split( "@" ).first();
226
Source::addCollection( const collection_ptr& c )
228
Q_ASSERT( m_collections.length() == 0 ); // only 1 source supported atm
229
m_collections.append( c );
230
emit collectionAdded( c );
235
Source::removeCollection( const collection_ptr& c )
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 );
246
qDebug() << Q_FUNC_INFO << friendlyName();
255
m_currentTrack.clear();
259
DatabaseCommand_SourceOffline* cmd = new DatabaseCommand_SourceOffline( id() );
260
Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
268
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << friendlyName();
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) );
287
Source::dbLoaded( unsigned int id, const QString& fname )
290
setFriendlyName( fname );
292
emit syncedWithDatabase();
297
Source::scanningProgress( unsigned int files )
300
m_textStatus = tr( "Scanning (%L1 tracks)" ).arg( files );
302
m_textStatus = tr( "Scanning" );
309
Source::scanningFinished( bool updateGUI )
311
m_textStatus = QString();
313
if ( m_updateIndexWhenSynced )
315
m_updateIndexWhenSynced = false;
327
Source::onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info )
329
Q_UNUSED( oldstate );
334
case DBSyncConnection::CHECKING:
336
msg = tr( "Checking" );
339
case DBSyncConnection::FETCHING:
341
msg = tr( "Syncing" );
344
case DBSyncConnection::PARSING:
346
msg = tr( "Importing" );
349
case DBSyncConnection::SCANNING:
351
msg = tr( "Scanning (%L1 tracks)" ).arg( info );
354
case DBSyncConnection::SYNCED:
371
Source::trackCount() const
373
return m_stats.value( "numfiles" ).toUInt();
377
Tomahawk::playlistinterface_ptr
378
Source::playlistInterface()
380
if ( m_playlistInterface.isNull() )
382
Tomahawk::source_ptr source = SourceList::instance()->get( id() );
383
m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::SourcePlaylistInterface( source.data() ) );
386
return m_playlistInterface;
391
Source::onPlaybackStarted( const Tomahawk::query_ptr& query, unsigned int duration )
393
tLog( LOGVERBOSE ) << Q_FUNC_INFO << query->toString();
395
m_currentTrack = query;
396
m_currentTrackTimer.start( duration * 1000 + 900000 ); // duration comes in seconds
398
if ( m_playlistInterface.isNull() )
401
emit playbackStarted( query );
407
Source::onPlaybackFinished( const Tomahawk::query_ptr& query )
409
tDebug() << Q_FUNC_INFO << query->toString();
410
emit playbackFinished( query );
412
m_currentTrack.clear();
418
Source::trackTimerFired()
420
m_currentTrack.clear();
426
Source::lastCmdGuid() const
428
QMutexLocker lock( &m_cmdMutex );
429
return m_lastCmdGuid;
434
Source::addCommand( const QSharedPointer<DatabaseCommand>& command )
436
QMutexLocker lock( &m_cmdMutex );
439
if ( !command->singletonCmd() )
440
m_lastCmdGuid = command->guid();
442
m_commandCount = m_cmds.count();
447
Source::executeCommands()
449
if ( QThread::currentThread() != thread() )
451
QMetaObject::invokeMethod( this, "executeCommands", Qt::QueuedConnection );
455
bool commandsAvail = false;
457
QMutexLocker lock( &m_cmdMutex );
458
commandsAvail = !m_cmds.isEmpty();
463
QMutexLocker lock( &m_cmdMutex );
464
QList< QSharedPointer<DatabaseCommand> > cmdGroup;
465
QSharedPointer<DatabaseCommand> cmd = m_cmds.takeFirst();
466
while ( cmd->groupable() )
469
if ( !m_cmds.isEmpty() && m_cmds.first()->groupable() && m_cmds.first()->commandname() == cmd->commandname() )
470
cmd = m_cmds.takeFirst();
475
// return here when the last command finished
476
connect( cmd.data(), SIGNAL( finished() ), SLOT( executeCommands() ) );
478
if ( cmdGroup.count() )
480
Database::instance()->enqueue( cmdGroup );
484
Database::instance()->enqueue( cmd );
487
int percentage = ( float( m_commandCount - m_cmds.count() ) / (float)m_commandCount ) * 100.0;
488
m_textStatus = tr( "Saving (%1%)" ).arg( percentage );
493
if ( m_updateIndexWhenSynced )
495
m_updateIndexWhenSynced = false;
499
m_textStatus = QString();
500
m_state = DBSyncConnection::SYNCED;
502
emit commandsFinished();
510
Source::reportSocialAttributesChanged( DatabaseCommand_SocialAction* action )
514
emit socialAttributesChanged( action->action() );
516
if ( action->action() == "latchOn" )
518
const source_ptr to = SourceList::instance()->get( action->comment() );
520
emit latchedOn( to );
522
else if ( action->action() == "latchOff" )
524
const source_ptr from = SourceList::instance()->get( action->comment() );
525
if ( !from.isNull() )
526
emit latchedOff( from );
532
Source::updateTracks()
535
DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex();
536
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
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 ) );
549
Source::updateIndexWhenSynced()
551
m_updateIndexWhenSynced = true;
556
Source::textStatus() const
558
if ( !m_textStatus.isEmpty() )
561
if ( !currentTrack().isNull() )
563
return currentTrack()->artist() + " - " + currentTrack()->track();
566
// do not use isOnline() here - it will always return true for the local source
569
return tr( "Online" );
573
return tr( "Offline" );