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