PageRenderTime 182ms CodeModel.GetById 40ms app.highlight 80ms RepoModel.GetById 21ms app.codeStats 1ms

/src/libtomahawk/Album.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 468 lines | 348 code | 102 blank | 18 comment | 41 complexity | 391548d24f081ad8fa37bbd64d4ca08c MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2015, 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 "Album_p.h"
 21
 22#include "database/Database.h"
 23#include "database/DatabaseImpl.h"
 24#include "database/IdThreadWorker.h"
 25#include "utils/TomahawkUtilsGui.h"
 26#include "utils/Logger.h"
 27
 28#include "Artist.h"
 29#include "AlbumPlaylistInterface.h"
 30#include "PlaylistEntry.h"
 31#include "Query.h"
 32#include "Source.h"
 33
 34#include <QReadWriteLock>
 35#include <QPixmapCache>
 36#include <QCoreApplication>
 37
 38using namespace Tomahawk;
 39
 40QHash< QString, album_wptr > Album::s_albumsByName = QHash< QString, album_wptr >();
 41QHash< unsigned int, album_wptr > Album::s_albumsById = QHash< unsigned int, album_wptr >();
 42
 43static QMutex s_nameCacheMutex;
 44static QReadWriteLock s_idMutex;
 45
 46
 47Album::~Album()
 48{
 49    Q_D( Album );
 50    d->ownRef.clear();
 51
 52    delete d->cover;
 53}
 54
 55
 56inline QString
 57albumCacheKey( const Tomahawk::artist_ptr& artist, const QString& albumName )
 58{
 59    return QString( "%1\t\t%2" ).arg( artist->name().toLower() ).arg( albumName.toLower() );
 60}
 61
 62
 63album_ptr
 64Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate )
 65{
 66    if ( !Database::instance() || !Database::instance()->impl() )
 67        return album_ptr();
 68
 69    QMutexLocker lock( &s_nameCacheMutex );
 70    const QString key = albumCacheKey( artist, name );
 71    if ( s_albumsByName.contains( key ) )
 72    {
 73        album_wptr album = s_albumsByName.value( key );
 74        if ( album )
 75            return album.toStrongRef();
 76    }
 77
 78    album_ptr album = album_ptr( new Album( name, artist ), &Album::deleteLater );
 79    album->moveToThread( QCoreApplication::instance()->thread() );
 80    album->setWeakRef( album.toWeakRef() );
 81    album->loadId( autoCreate );
 82    s_albumsByName.insert( key, album );
 83
 84    return album;
 85}
 86
 87
 88album_ptr
 89Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist )
 90{
 91    s_idMutex.lockForRead();
 92    if ( s_albumsById.contains( id ) )
 93    {
 94        album_wptr album = s_albumsById.value( id );
 95        s_idMutex.unlock();
 96
 97        if ( album )
 98            return album;
 99    }
100    s_idMutex.unlock();
101
102    QMutexLocker lock( &s_nameCacheMutex );
103    const QString key = albumCacheKey( artist, name );
104    if ( s_albumsByName.contains( key ) )
105    {
106        album_wptr album = s_albumsByName.value( key );
107        if ( album )
108            return album;
109    }
110
111    album_ptr a = album_ptr( new Album( id, name, artist ), &Album::deleteLater );
112    a->setWeakRef( a.toWeakRef() );
113    s_albumsByName.insert( key, a );
114
115    if ( id > 0 )
116    {
117        s_idMutex.lockForWrite();
118        s_albumsById.insert( id, a );
119        s_idMutex.unlock();
120    }
121
122    return a;
123}
124
125
126Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist )
127    : d_ptr( new AlbumPrivate( this, id, name, artist ) )
128{
129    Q_D( Album );
130    d->sortname = DatabaseImpl::sortname( name );
131}
132
133
134Album::Album( const QString& name, const Tomahawk::artist_ptr& artist )
135    : d_ptr( new AlbumPrivate( this, name, artist ) )
136{
137    Q_D( Album );
138    d->sortname = DatabaseImpl::sortname( name );
139}
140
141
142void
143Album::deleteLater()
144{
145    Q_D( Album );
146    QMutexLocker lock( &s_nameCacheMutex );
147
148    const QString key = albumCacheKey( d->artist, d->name );
149    if ( s_albumsByName.contains( key ) )
150    {
151        s_albumsByName.remove( key );
152    }
153
154    if ( d->id > 0 )
155    {
156        s_idMutex.lockForWrite();
157        if ( s_albumsById.contains( d->id ) )
158        {
159            s_albumsById.remove( d->id );
160        }
161        s_idMutex.unlock();
162    }
163
164    QObject::deleteLater();
165}
166
167
168void
169Album::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection )
170{
171    emit tracksAdded( playlistInterface( mode, collection )->tracks(), mode, collection );
172}
173
174
175artist_ptr
176Album::artist() const
177{
178    Q_D( const Album );
179    return d->artist;
180}
181
182
183void
184Album::loadId( bool autoCreate )
185{
186    Q_D( Album );
187    Q_ASSERT( d->waitingForId );
188    IdThreadWorker::getAlbumId( d->ownRef.toStrongRef(), autoCreate );
189}
190
191
192void
193Album::setIdFuture( QFuture<unsigned int> future )
194{
195    Q_D( Album );
196    d->idFuture = future;
197}
198
199
200unsigned int
201Album::id() const
202{
203    Q_D( const Album );
204    s_idMutex.lockForRead();
205    const bool waiting = d->waitingForId;
206    s_idMutex.unlock();
207
208    if ( waiting )
209    {
210        d->idFuture.waitForFinished();
211
212        s_idMutex.lockForWrite();
213        d->id = d->idFuture.result();
214        d->waitingForId = false;
215
216        if ( d->id > 0 )
217            s_albumsById.insert( d->id, d->ownRef.toStrongRef() );
218
219        s_idMutex.unlock();
220    }
221
222    return d->id;
223}
224
225
226QString
227Album::name() const
228{
229    Q_D( const Album );
230    return d->name;
231}
232
233
234QString
235Album::sortname() const
236{
237    Q_D( const Album );
238    return d->sortname;
239}
240
241
242QString
243Album::purchaseUrl() const
244{
245    Q_D( const Album );
246    if ( !d->purchaseUrlLoaded )
247    {
248        Tomahawk::InfoSystem::InfoStringHash albumInfo;
249        albumInfo["artist"] = d->artist->name();
250        albumInfo["album"] = d->name;
251
252        Tomahawk::InfoSystem::InfoRequestData requestData;
253        requestData.caller = infoid();
254        requestData.type = Tomahawk::InfoSystem::InfoAlbumPurchaseUrl;
255        requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( albumInfo );
256        requestData.customData = QVariantMap();
257        requestData.allSources = true;
258
259        connect( Tomahawk::InfoSystem::InfoSystem::instance(),
260                SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
261                SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
262
263        connect( Tomahawk::InfoSystem::InfoSystem::instance(),
264                SIGNAL( finished( QString ) ),
265                SLOT( infoSystemFinished( QString ) ) );
266
267        Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
268    }
269
270    return d->purchaseUrl;
271}
272
273
274bool
275Album::purchased() const
276{
277    Q_D( const Album );
278    return d->purchased;
279}
280
281
282QPixmap
283Album::cover( const QSize& size, bool forceLoad ) const
284{
285    Q_D( const Album );
286    if ( d->name.isEmpty() )
287    {
288        d->coverLoaded = true;
289        return QPixmap();
290    }
291
292    if ( !d->coverLoaded && !d->coverLoading )
293    {
294        if ( !forceLoad )
295            return QPixmap();
296
297        Tomahawk::InfoSystem::InfoStringHash trackInfo;
298        trackInfo["artist"] = d->artist->name();
299        trackInfo["album"] = d->name;
300
301        Tomahawk::InfoSystem::InfoRequestData requestData;
302        requestData.caller = infoid();
303        requestData.type = Tomahawk::InfoSystem::InfoAlbumCoverArt;
304        requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo );
305        requestData.customData = QVariantMap();
306        requestData.allSources = true;
307
308        connect( Tomahawk::InfoSystem::InfoSystem::instance(),
309                SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
310                SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
311
312        connect( Tomahawk::InfoSystem::InfoSystem::instance(),
313                SIGNAL( finished( QString ) ),
314                SLOT( infoSystemFinished( QString ) ) );
315
316        Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
317
318        d->coverLoading = true;
319    }
320
321    if ( !d->cover && !d->coverBuffer.isEmpty() )
322    {
323        QPixmap cover;
324        cover.loadFromData( d->coverBuffer );
325        d->coverBuffer.clear();
326
327        d->cover = new QPixmap( TomahawkUtils::squareCenterPixmap( cover ) );
328    }
329
330    if ( d->cover && !d->cover->isNull() && !size.isEmpty() )
331    {
332        const QString cacheKey = QString( "%1_%2_%3" ).arg( infoid() ).arg( size.width() ).arg( size.height() );
333        QPixmap cover;
334
335        if ( !QPixmapCache::find( cacheKey, &cover ) )
336        {
337            cover = d->cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
338            QPixmapCache::insert( cacheKey, cover );
339            return cover;
340        }
341        return cover;
342    }
343
344    if ( d->cover )
345        return *d->cover;
346    else
347        return QPixmap();
348}
349
350
351bool
352Album::coverLoaded() const
353{
354    Q_D( const Album );
355    return d->coverLoaded;
356}
357
358
359void
360Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData, const QVariant& output )
361{
362    Q_D( Album );
363    if ( requestData.caller != infoid() )
364        return;
365
366    if ( requestData.type == Tomahawk::InfoSystem::InfoAlbumPurchaseUrl && output.isValid() )
367    {
368        QVariantMap returnedData = output.value< QVariantMap >();
369        d->purchaseUrlLoaded = true;
370        d->purchaseUrl = returnedData["url"].toString();
371        d->purchased = returnedData["purchased"].toBool();
372        emit updated();
373        return;
374    }
375
376    if ( requestData.type != Tomahawk::InfoSystem::InfoAlbumCoverArt )
377    {
378        return;
379    }
380
381    if ( output.isNull() )
382    {
383        d->coverLoaded = true;
384    }
385    else if ( output.isValid() )
386    {
387        QVariantMap returnedData = output.value< QVariantMap >();
388        const QByteArray ba = returnedData["imgbytes"].toByteArray();
389        if ( !ba.isEmpty() )
390        {
391            d->coverBuffer = ba;
392        }
393
394        d->coverLoaded = true;
395        emit coverChanged();
396    }
397}
398
399
400void
401Album::infoSystemFinished( const QString& target )
402{
403    Q_D( Album );
404    if ( target != infoid() )
405        return;
406
407    disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
408                this, SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
409
410    disconnect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ),
411                this, SLOT( infoSystemFinished( QString ) ) );
412
413    d->coverLoading = false;
414    emit updated();
415}
416
417
418Tomahawk::playlistinterface_ptr
419Album::playlistInterface( ModelMode mode, const Tomahawk::collection_ptr& collection )
420{
421    Q_D( Album );
422    playlistinterface_ptr pli = d->playlistInterface[ mode ][ collection ];
423
424    if ( pli.isNull() )
425    {
426        pli = Tomahawk::playlistinterface_ptr( new Tomahawk::AlbumPlaylistInterface( this, mode, collection ) );
427        connect( pli.data(), SIGNAL( tracksLoaded( Tomahawk::ModelMode, Tomahawk::collection_ptr ) ),
428                               SLOT( onTracksLoaded( Tomahawk::ModelMode, Tomahawk::collection_ptr ) ) );
429
430        d->playlistInterface[ mode ][ collection ] = pli;
431    }
432
433    return pli;
434}
435
436
437QWeakPointer<Album>
438Album::weakRef()
439{
440    Q_D( Album );
441    return d->ownRef;
442}
443
444
445void
446Album::setWeakRef( QWeakPointer<Album> weakRef )
447{
448    Q_D( Album );
449    d->ownRef = weakRef;
450}
451
452
453QList<Tomahawk::query_ptr>
454Album::tracks( ModelMode mode, const Tomahawk::collection_ptr& collection )
455{
456    return playlistInterface( mode, collection )->tracks();
457}
458
459
460QString
461Album::infoid() const
462{
463    Q_D( const Album );
464    if ( d->uuid.isEmpty() )
465        d->uuid = uuid();
466
467    return d->uuid;
468}