File indexing completed on 2024-05-19 04:49:17
0001 /**************************************************************************************** 0002 * Copyright (c) 2011 Bart Cerneels <bart.cerneels@kde.org> * 0003 * Copyright (c) 2012 Matěj Laitl <matej@laitl.cz> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify it under * 0006 * the terms of the GNU General Public License as published by the Free Software * 0007 * Foundation; either version 2 of the License, or (at your option) any later * 0008 * version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License along with * 0015 * this program. If not, see <http://www.gnu.org/licenses/>. * 0016 ****************************************************************************************/ 0017 0018 #include "MemoryMeta.h" 0019 0020 #include "core/meta/Statistics.h" 0021 #include "core/meta/TrackEditor.h" 0022 #include "core-impl/capabilities/AlbumActionsCapability.h" 0023 #include "covermanager/CoverCache.h" 0024 0025 0026 using namespace MemoryMeta; 0027 0028 Meta::TrackList 0029 Base::tracks() 0030 { 0031 // construct AmarokSharedPointers on demand, see m_track comment 0032 QReadLocker locker( &m_tracksLock ); 0033 Meta::TrackList list; 0034 foreach( Track *track, m_tracks ) 0035 { 0036 list << Meta::TrackPtr( track ); 0037 } 0038 return list; 0039 } 0040 0041 void 0042 Base::addTrack( Track *track ) 0043 { 0044 QWriteLocker locker( &m_tracksLock ); 0045 m_tracks.append( track ); 0046 } 0047 0048 void 0049 Base::removeTrack( Track *track ) 0050 { 0051 QWriteLocker locker( &m_tracksLock ); 0052 m_tracks.removeOne( track ); 0053 } 0054 0055 Album::Album( const Meta::AlbumPtr &other ) 0056 : MemoryMeta::Base( other->name() ) 0057 , m_isCompilation( other->isCompilation() ) 0058 , m_canUpdateCompilation( other->canUpdateCompilation() ) 0059 , m_image( other->image() ) 0060 , m_canUpdateImage( other->canUpdateImage() ) 0061 { 0062 if( other->hasAlbumArtist() && other->albumArtist() ) 0063 m_albumArtist = Meta::ArtistPtr( new Artist( other->albumArtist()->name() ) ); 0064 } 0065 0066 bool 0067 Album::hasCapabilityInterface( Capabilities::Capability::Type type ) const 0068 { 0069 switch( type ) 0070 { 0071 case Capabilities::Capability::Actions: 0072 return true; 0073 default: 0074 return false; 0075 } 0076 } 0077 0078 Capabilities::Capability* 0079 Album::createCapabilityInterface( Capabilities::Capability::Type type ) 0080 { 0081 switch( type ) 0082 { 0083 case Capabilities::Capability::Actions: 0084 return new Capabilities::AlbumActionsCapability( Meta::AlbumPtr( this ) ); 0085 default: 0086 return nullptr; 0087 } 0088 } 0089 0090 void 0091 Album::setCompilation( bool isCompilation ) 0092 { 0093 // we re-create the albums for each track - that's how MemoryMeta works - by 0094 // aggregating multiple entities of same name into one. 0095 foreach( Meta::TrackPtr track, tracks() ) 0096 { 0097 Track *memoryTrack = static_cast<Track *>( track.data() ); 0098 Meta::AlbumPtr album = memoryTrack->originalTrack()->album(); 0099 if( album && album->canUpdateCompilation() ) 0100 album->setCompilation( isCompilation ); 0101 } 0102 // no notifyObservers() etc needed - this is done from down-up by proxied tracks 0103 } 0104 0105 QImage 0106 Album::image( int size ) const 0107 { 0108 if( size > 1 && size <= 1000 && !m_image.isNull() ) 0109 return m_image.scaled( size, size, Qt::KeepAspectRatio, Qt::FastTransformation ); 0110 return m_image; 0111 } 0112 0113 void 0114 Album::setImage( const QImage &image ) 0115 { 0116 foreach( Meta::TrackPtr track, tracks() ) 0117 { 0118 Track *memoryTrack = static_cast<Track *>( track.data() ); 0119 Meta::AlbumPtr album = memoryTrack->originalTrack()->album(); 0120 if( album && album->canUpdateImage() ) 0121 album->setImage( image ); 0122 } 0123 // no notifyObservers() etc needed - this is done from down-up by proxied tracks 0124 } 0125 0126 void 0127 Album::removeImage() 0128 { 0129 foreach( Meta::TrackPtr track, tracks() ) 0130 { 0131 Track *memoryTrack = static_cast<Track *>( track.data() ); 0132 Meta::AlbumPtr album = memoryTrack->originalTrack()->album(); 0133 if( album && album->canUpdateImage() ) 0134 album->removeImage(); 0135 } 0136 // no notifyObservers() etc needed - this is done from down-up by proxied tracks 0137 } 0138 0139 void 0140 Album::updateCachedValues() 0141 { 0142 m_isCompilation = false; 0143 m_canUpdateCompilation = false; 0144 m_image = QImage(); 0145 m_canUpdateImage = false; 0146 foreach( Meta::TrackPtr track, tracks() ) 0147 { 0148 Track *memoryTrack = static_cast<Track *>( track.data() ); 0149 Meta::AlbumPtr album = memoryTrack->originalTrack()->album(); 0150 0151 if( !album ) 0152 continue; 0153 0154 if( !m_isCompilation ) 0155 m_isCompilation = album->isCompilation(); 0156 if( !m_canUpdateCompilation ) 0157 m_canUpdateCompilation = album->canUpdateCompilation(); 0158 if( m_image.isNull() && album->hasImage() ) 0159 m_image = album->image(); 0160 if( !m_canUpdateImage ) 0161 m_canUpdateImage = album->canUpdateImage(); 0162 } 0163 } 0164 0165 Track::Track(const Meta::TrackPtr& originalTrack) 0166 : m_track( originalTrack ) 0167 , m_album( nullptr ) 0168 , m_artist( nullptr ) 0169 , m_composer( nullptr ) 0170 , m_genre( nullptr ) 0171 , m_year( nullptr ) 0172 { 0173 Q_ASSERT( originalTrack ); 0174 } 0175 0176 Track::~Track() 0177 { 0178 // all following static casts are valid - there is no way attributes could have been 0179 // set to different Meta::* subclasses 0180 if( m_album ) 0181 static_cast<Album *>( m_album.data() )->removeTrack( this ); 0182 if( m_artist ) 0183 static_cast<Artist *>( m_artist.data() )->removeTrack( this ); 0184 if( m_composer ) 0185 static_cast<Composer *>( m_composer.data() )->removeTrack( this ); 0186 if( m_genre ) 0187 static_cast<Genre *>( m_genre.data() )->removeTrack( this ); 0188 if( m_year ) 0189 static_cast<Year *>( m_year.data() )->removeTrack( this ); 0190 } 0191 0192 Meta::TrackEditorPtr 0193 Track::editor() 0194 { 0195 return m_track->editor(); 0196 } 0197 0198 Meta::StatisticsPtr 0199 Track::statistics() 0200 { 0201 return m_track->statistics(); 0202 } 0203 0204 void 0205 Track::setAlbum( Album *album ) 0206 { 0207 if( m_album ) 0208 static_cast<Album *>( m_album.data() )->removeTrack( this ); 0209 if( album ) 0210 album->addTrack( this ); 0211 m_album = Meta::AlbumPtr( album ); 0212 } 0213 0214 void 0215 Track::setArtist( Artist *artist ) 0216 { 0217 if( m_artist ) 0218 static_cast<Artist *>( m_artist.data() )->removeTrack( this ); 0219 if( artist ) 0220 artist->addTrack( this ); 0221 m_artist = Meta::ArtistPtr( artist ); 0222 } 0223 0224 void 0225 Track::setComposer( Composer *composer ) 0226 { 0227 if( m_composer ) 0228 static_cast<Composer *>( m_composer.data() )->removeTrack( this ); 0229 if( composer ) 0230 composer->addTrack( this ); 0231 m_composer = Meta::ComposerPtr( composer ); 0232 } 0233 0234 void 0235 Track::setGenre( Genre *genre ) 0236 { 0237 if( m_genre ) 0238 static_cast<Genre *>( m_genre.data() )->removeTrack( this ); 0239 if( genre ) 0240 genre->addTrack( this ); 0241 m_genre = Meta::GenrePtr( genre ); 0242 } 0243 0244 void 0245 Track::setYear( Year *year ) 0246 { 0247 if( m_year ) 0248 static_cast<Year *>( m_year.data() )->removeTrack( this ); 0249 if( year ) 0250 year->addTrack( this ); 0251 m_year = Meta::YearPtr( year ); 0252 } 0253 0254 MapChanger::MapChanger( MemoryCollection *memoryCollection ) 0255 : m_mc( memoryCollection ) 0256 { 0257 m_mc->acquireWriteLock(); 0258 } 0259 0260 MapChanger::~MapChanger() 0261 { 0262 m_mc->releaseLock(); 0263 } 0264 0265 Meta::TrackPtr 0266 MapChanger::addTrack( Meta::TrackPtr track ) 0267 { 0268 if( !track ) 0269 return Meta::TrackPtr(); // nothing to do 0270 if( m_mc->trackMap().contains( track->uidUrl() ) ) 0271 return Meta::TrackPtr(); 0272 0273 Track *memoryTrack = new Track( track ); 0274 return addExistingTrack( track, memoryTrack ); 0275 } 0276 0277 Meta::TrackPtr 0278 MapChanger::addExistingTrack( Meta::TrackPtr track, Track *memoryTrack ) 0279 { 0280 Meta::TrackPtr metaTrackPtr = Meta::TrackPtr( memoryTrack ); 0281 m_mc->addTrack( metaTrackPtr ); 0282 0283 QString artistName = track->artist().isNull() ? QString() : track->artist()->name(); 0284 Meta::ArtistPtr artist = m_mc->artistMap().value( artistName ); 0285 if( artist.isNull() ) 0286 { 0287 artist = Meta::ArtistPtr( new Artist( artistName ) ); 0288 m_mc->addArtist( artist ); 0289 } 0290 memoryTrack->setArtist( static_cast<Artist *>( artist.data() ) ); 0291 0292 Meta::AlbumPtr trackAlbum = track->album(); 0293 QString albumName = trackAlbum ? trackAlbum->name() : QString(); 0294 QString albumArtistName; 0295 if( trackAlbum && trackAlbum->hasAlbumArtist() && trackAlbum->albumArtist() ) 0296 albumArtistName = trackAlbum->albumArtist()->name(); 0297 Meta::AlbumPtr album = m_mc->albumMap().value( albumName, albumArtistName ); 0298 if( !album ) 0299 { 0300 Meta::ArtistPtr albumArtist; 0301 const bool isCompilation = trackAlbum ? trackAlbum->isCompilation() : false; 0302 // we create even empty-named album artists if the album is not compilation 0303 // because Collection Browser wouldn't show these otherwise 0304 if( !albumArtistName.isEmpty() || !isCompilation ) 0305 { 0306 /* Even if MemoryQueryMaker doesn't need albumArtists to be in MemoryCollection 0307 * maps, we add her into artist map so that album artist has the same instance as 0308 * identically-named artist (of potentially different tracks) */ 0309 albumArtist = m_mc->artistMap().value( albumArtistName ); 0310 if( !albumArtist ) 0311 { 0312 albumArtist = Meta::ArtistPtr( new Artist( albumArtistName ) ); 0313 m_mc->addArtist( albumArtist ); 0314 } 0315 } 0316 0317 album = Meta::AlbumPtr( new Album( albumName, albumArtist ) ); 0318 m_mc->addAlbum( album ); 0319 } 0320 Album *memoryAlbum = static_cast<Album *>( album.data() ); 0321 memoryTrack->setAlbum( memoryAlbum ); 0322 memoryAlbum->updateCachedValues(); 0323 0324 QString genreName = track->genre().isNull() ? QString() : track->genre()->name(); 0325 Meta::GenrePtr genre = m_mc->genreMap().value( genreName ); 0326 if( genre.isNull() ) 0327 { 0328 genre = Meta::GenrePtr( new Genre( genreName ) ); 0329 m_mc->addGenre( genre ); 0330 } 0331 memoryTrack->setGenre( static_cast<Genre *>( genre.data() ) ); 0332 0333 QString composerName = track->composer().isNull() ? QString() : track->composer()->name(); 0334 Meta::ComposerPtr composer = m_mc->composerMap().value( composerName ); 0335 if( composer.isNull() ) 0336 { 0337 composer = Meta::ComposerPtr( new Composer( composerName ) ); 0338 m_mc->addComposer( composer ); 0339 } 0340 memoryTrack->setComposer( static_cast<Composer *>( composer.data() ) ); 0341 0342 int year = track->year().isNull() ? 0 : track->year()->year(); 0343 Meta::YearPtr yearPtr = m_mc->yearMap().value( year ); 0344 if( yearPtr.isNull() ) 0345 { 0346 yearPtr = Meta::YearPtr( new Year( year ? QString::number( year ) : QString() ) ); 0347 m_mc->addYear( yearPtr ); 0348 } 0349 memoryTrack->setYear( static_cast<Year *>( yearPtr.data() ) ); 0350 0351 //TODO: labels (when doing this, don't forget to tweak removeTrack too) 0352 0353 return metaTrackPtr; 0354 } 0355 0356 Meta::TrackPtr 0357 MapChanger::removeTrack( Meta::TrackPtr track ) 0358 { 0359 if( !track ) 0360 return Meta::TrackPtr(); // nothing to do 0361 0362 TrackMap trackMap = m_mc->trackMap(); 0363 ArtistMap artistMap = m_mc->artistMap(); 0364 AlbumMap albumMap = m_mc->albumMap(); 0365 GenreMap genreMap = m_mc->genreMap(); 0366 ComposerMap composerMap = m_mc->composerMap(); 0367 YearMap yearMap = m_mc->yearMap(); 0368 0369 /* Ensure that we have the memory track (not the underlying one) and that it is 0370 * actually present in MemoryCollection */ 0371 track = trackMap.value( track->uidUrl() ); 0372 if( !track ) 0373 return Meta::TrackPtr(); // was not in collection, nothing to do 0374 0375 /* Track added using mapadder are MemoryMeta::Tracks, but cope with different too: */ 0376 Track *memoryTrack = dynamic_cast<Track *>( track.data() ); 0377 0378 trackMap.remove( track->uidUrl() ); 0379 0380 /* Remove potentially dangling entities from memory collection. We cannot simply use 0381 * if( entity && entity->tracks().count() == 1 ) because it would be racy: entity could 0382 * have referenced a track that is no longer in MemoryCollection, but not yet destroyed. 0383 * 0384 * When track to remove is MemoryMeta::Track, copy and reassign Meta:: entities so 0385 * that the track is detached from MemoryCollection completely. */ 0386 0387 Meta::ArtistPtr artist = track->artist(); 0388 if( artist && !hasTrackInMap( artist->tracks(), trackMap ) 0389 && !referencedAsAlbumArtist( artist, albumMap ) ) 0390 artistMap.remove( artist->name() ); 0391 if( artist && memoryTrack ) 0392 memoryTrack->setArtist( new Artist( artist->name() ) ); 0393 0394 Meta::AlbumPtr album = track->album(); 0395 if( album && !hasTrackInMap( album->tracks(), trackMap ) ) 0396 { 0397 albumMap.remove( album ); 0398 Meta::ArtistPtr albumArtist = album->hasAlbumArtist() ? album->albumArtist() : Meta::ArtistPtr(); 0399 if( albumArtist && !hasTrackInMap( albumArtist->tracks(), trackMap ) 0400 && !referencedAsAlbumArtist( albumArtist, albumMap ) ) 0401 artistMap.remove( albumArtist->name() ); 0402 } 0403 if( album && memoryTrack ) 0404 memoryTrack->setAlbum( new Album( album ) ); // copy-like constructor 0405 0406 Meta::GenrePtr genre = track->genre(); 0407 if( genre && !hasTrackInMap( genre->tracks(), trackMap ) ) 0408 genreMap.remove( genre->name() ); 0409 if( genre && memoryTrack ) 0410 memoryTrack->setGenre( new Genre( genre->name() ) ); 0411 0412 Meta::ComposerPtr composer = track->composer(); 0413 if( composer && !hasTrackInMap( composer->tracks(), trackMap ) ) 0414 composerMap.remove( composer->name() ); 0415 if( composer && memoryTrack ) 0416 memoryTrack->setComposer( new Composer( composer->name() ) ); 0417 0418 Meta::YearPtr year = track->year(); 0419 if( year && !hasTrackInMap( year->tracks(), trackMap ) ) 0420 yearMap.remove( year->year() ); 0421 if( year && memoryTrack ) 0422 memoryTrack->setYear( new Year( year->name() ) ); 0423 0424 m_mc->setTrackMap( trackMap ); 0425 m_mc->setArtistMap( artistMap ); 0426 m_mc->setAlbumMap( albumMap ); 0427 m_mc->setGenreMap( genreMap ); 0428 m_mc->setComposerMap( composerMap ); 0429 m_mc->setYearMap( yearMap ); 0430 0431 if( memoryTrack ) 0432 return memoryTrack->originalTrack(); 0433 return Meta::TrackPtr(); 0434 } 0435 0436 bool 0437 MapChanger::trackChanged( Meta::TrackPtr track ) 0438 { 0439 if( !track ) 0440 return false; 0441 // make sure we have a track from memory collection: 0442 track = m_mc->trackMap().value( track->uidUrl() ); 0443 if( !track ) 0444 return false; 0445 Track *memoryTrack = dynamic_cast<Track *>( track.data() ); 0446 if( !memoryTrack ) 0447 return false; 0448 0449 Meta::TrackPtr originalTrack = memoryTrack->originalTrack(); 0450 if( !originalTrack ) 0451 return false; // paranoia 0452 0453 bool mapsNeedUpdating = false; 0454 // make album first so that invalidateAlbum is called every time it is needed 0455 if( entitiesDiffer( originalTrack->album().data(), memoryTrack->album().data() ) ) 0456 { 0457 CoverCache::invalidateAlbum( memoryTrack->album().data() ); 0458 mapsNeedUpdating = true; 0459 } 0460 else if( entitiesDiffer( originalTrack->artist().data(), memoryTrack->artist().data() ) ) 0461 mapsNeedUpdating = true; 0462 else if( entitiesDiffer( originalTrack->genre().data(), memoryTrack->genre().data() ) ) 0463 mapsNeedUpdating = true; 0464 else if( entitiesDiffer( originalTrack->composer().data(), memoryTrack->composer().data() ) ) 0465 mapsNeedUpdating = true; 0466 else if( entitiesDiffer( originalTrack->year().data(), memoryTrack->year().data() ) ) 0467 mapsNeedUpdating = true; 0468 0469 if( mapsNeedUpdating ) 0470 { 0471 // we hold write lock so we can do the following trick: 0472 removeTrack( track ); 0473 addExistingTrack( originalTrack, memoryTrack ); 0474 } 0475 0476 memoryTrack->notifyObservers(); 0477 return mapsNeedUpdating; 0478 } 0479 0480 bool 0481 MapChanger::hasTrackInMap( const Meta::TrackList &needles, const TrackMap &haystack ) 0482 { 0483 foreach( Meta::TrackPtr track, needles ) 0484 { 0485 if( track && haystack.contains( track->uidUrl() ) ) 0486 return true; 0487 } 0488 return false; 0489 } 0490 0491 bool 0492 MapChanger::referencedAsAlbumArtist( const Meta::ArtistPtr &artist, const AlbumMap &haystack ) 0493 { 0494 foreach( Meta::AlbumPtr album, haystack ) 0495 { 0496 if( album && album->hasAlbumArtist() && album->albumArtist() == artist ) 0497 return true; 0498 } 0499 return false; 0500 } 0501 0502 bool MapChanger::entitiesDiffer( const Meta::Base *first, const Meta::Base *second ) 0503 { 0504 if( !first && !second ) 0505 return false; // both null -> do not differ 0506 if( !first || !second ) 0507 return true; // one of them null -> do differ 0508 0509 return first->name() != second->name(); 0510 } 0511 0512 bool MapChanger::entitiesDiffer( const Meta::Album *first, const Meta::Album *second ) 0513 { 0514 if( !first && !second ) 0515 return false; // both null -> do not differ 0516 if( !first || !second ) 0517 return true; // one of them null -> do differ 0518 0519 if( first->name() != second->name() ) 0520 return true; 0521 if( first->isCompilation() != second->isCompilation() ) 0522 return true; 0523 if( first->hasImage() != second->hasImage() ) 0524 return true; 0525 if( entitiesDiffer( first->albumArtist().data(), second->albumArtist().data() ) ) 0526 return true; 0527 0528 // we only compare images using dimension, it would be too costly otherwise 0529 if( first->image().width() != second->image().width() ) 0530 return true; 0531 if( first->image().height() != second->image().height() ) 0532 return true; 0533 0534 return false; 0535 }