File indexing completed on 2025-01-05 04:25:51
0001 /**************************************************************************************** 0002 * Copyright (c) 2009,2010 Maximilian Kossick <maximilian.kossick@googlemail.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "AggregateMeta" 0018 0019 #include "AggregateMeta.h" 0020 0021 #include "SvgHandler.h" 0022 #include "core/meta/TrackEditor.h" 0023 #include "core/meta/support/MetaUtility.h" 0024 #include "core/support/Debug.h" 0025 #include "core-impl/collections/aggregate/AggregateCollection.h" 0026 0027 #include <QDateTime> 0028 #include <QSet> 0029 #include <QTimer> 0030 0031 namespace Meta 0032 { 0033 0034 #define FORWARD( call ) { foreach( TrackEditorPtr e, m_editors ) { e->call; } \ 0035 if( !m_batchMode ) QTimer::singleShot( 0, m_collection, &Collections::AggregateCollection::slotUpdated ); } 0036 0037 class AggregateTrackEditor : public TrackEditor 0038 { 0039 public: 0040 AggregateTrackEditor( Collections::AggregateCollection *coll, const QList<TrackEditorPtr> &editors ) 0041 : TrackEditor() 0042 , m_batchMode( false ) 0043 , m_collection( coll ) 0044 , m_editors( editors ) 0045 {} 0046 0047 void beginUpdate() override 0048 { 0049 m_batchMode = true; 0050 foreach( TrackEditorPtr ec, m_editors ) ec->beginUpdate(); 0051 } 0052 void endUpdate() override 0053 { 0054 foreach( TrackEditorPtr ec, m_editors ) ec->endUpdate(); 0055 m_batchMode = false; 0056 QTimer::singleShot( 0, m_collection, &Collections::AggregateCollection::slotUpdated ); 0057 } 0058 void setComment( const QString &newComment ) override { FORWARD( setComment( newComment ) ) } 0059 void setTrackNumber( int newTrackNumber ) override { FORWARD( setTrackNumber( newTrackNumber ) ) } 0060 void setDiscNumber( int newDiscNumber ) override { FORWARD( setDiscNumber( newDiscNumber ) ) } 0061 void setBpm( const qreal newBpm ) override { FORWARD( setBpm( newBpm ) ) } 0062 void setTitle( const QString &newTitle ) override { FORWARD( setTitle( newTitle ) ) } 0063 void setArtist( const QString &newArtist ) override { FORWARD( setArtist( newArtist ) ) } 0064 void setAlbum( const QString &newAlbum ) override { FORWARD( setAlbum( newAlbum ) ) } 0065 void setAlbumArtist( const QString &newAlbumArtist ) override { FORWARD( setAlbumArtist ( newAlbumArtist ) ) } 0066 void setGenre( const QString &newGenre ) override { FORWARD( setGenre( newGenre ) ) } 0067 void setComposer( const QString &newComposer ) override { FORWARD( setComposer( newComposer ) ) } 0068 void setYear( int newYear ) override { FORWARD( setYear( newYear ) ) } 0069 private: 0070 bool m_batchMode; 0071 Collections::AggregateCollection *m_collection; 0072 QList<TrackEditorPtr> m_editors; 0073 }; 0074 0075 #undef FORWARD 0076 0077 AggregateTrack::AggregateTrack( Collections::AggregateCollection *coll, const TrackPtr &track ) 0078 : Track() 0079 , Observer() 0080 , m_collection( coll ) 0081 , m_name( track->name() ) 0082 , m_album( nullptr ) 0083 , m_artist( nullptr ) 0084 , m_genre( nullptr ) 0085 , m_composer( nullptr ) 0086 , m_year( nullptr ) 0087 { 0088 subscribeTo( track ); 0089 m_tracks.append( track ); 0090 0091 if( track->album() ) 0092 m_album = Meta::AlbumPtr( m_collection->getAlbum( track->album() ) ); 0093 if( track->artist() ) 0094 m_artist = Meta::ArtistPtr( m_collection->getArtist( track->artist() ) ); 0095 if( track->genre() ) 0096 m_genre = Meta::GenrePtr( m_collection->getGenre( track->genre() ) ); 0097 if( track->composer() ) 0098 m_composer = Meta::ComposerPtr( m_collection->getComposer( track->composer() ) ); 0099 if( track->year() ) 0100 m_year = Meta::YearPtr( m_collection->getYear( track->year() ) ); 0101 } 0102 0103 AggregateTrack::~AggregateTrack() 0104 { 0105 } 0106 0107 QString 0108 AggregateTrack::name() const 0109 { 0110 return m_name; 0111 } 0112 0113 QString 0114 AggregateTrack::prettyName() const 0115 { 0116 return m_name; 0117 } 0118 0119 QString 0120 AggregateTrack::sortableName() const 0121 { 0122 if( !m_tracks.isEmpty() ) 0123 return m_tracks.first()->sortableName(); 0124 0125 return m_name; 0126 } 0127 0128 QUrl 0129 AggregateTrack::playableUrl() const 0130 { 0131 Meta::TrackPtr bestPlayableTrack; 0132 foreach( const Meta::TrackPtr &track, m_tracks ) 0133 { 0134 if( track->isPlayable() ) 0135 { 0136 bool local = track->playableUrl().isLocalFile(); 0137 if( local ) 0138 { 0139 bestPlayableTrack = track; 0140 break; 0141 } 0142 else 0143 { 0144 //we might want to add some more sophisticated logic to figure out 0145 //the best remote track to play, but this works for now 0146 bestPlayableTrack = track; 0147 } 0148 } 0149 } 0150 if( bestPlayableTrack ) 0151 return bestPlayableTrack->playableUrl(); 0152 0153 return QUrl(); 0154 } 0155 0156 QString 0157 AggregateTrack::prettyUrl() const 0158 { 0159 if( m_tracks.count() == 1 ) 0160 { 0161 return m_tracks.first()->prettyUrl(); 0162 } 0163 else 0164 { 0165 return QString(); 0166 } 0167 } 0168 0169 QString 0170 AggregateTrack::uidUrl() const 0171 { 0172 // this is where it gets interesting 0173 // a uidUrl for a AggregateTrack probably has to be generated 0174 // from the parts of the key in AggregateCollection 0175 // need to think about this some more 0176 return QString(); 0177 } 0178 0179 QString 0180 AggregateTrack::notPlayableReason() const 0181 { 0182 QStringList reasons; 0183 foreach( const Meta::TrackPtr &track, m_tracks ) 0184 { 0185 if( !track->isPlayable() ) 0186 reasons.append( track->notPlayableReason() ); 0187 else 0188 return QString(); // no reason if at least one playable 0189 } 0190 return reasons.join( QStringLiteral( ", " ) ); 0191 } 0192 0193 Meta::AlbumPtr 0194 AggregateTrack::album() const 0195 { 0196 return m_album; 0197 } 0198 0199 Meta::ArtistPtr 0200 AggregateTrack::artist() const 0201 { 0202 return m_artist; 0203 } 0204 0205 Meta::ComposerPtr 0206 AggregateTrack::composer() const 0207 { 0208 return m_composer; 0209 } 0210 0211 Meta::GenrePtr 0212 AggregateTrack::genre() const 0213 { 0214 return m_genre; 0215 } 0216 0217 Meta::YearPtr 0218 AggregateTrack::year() const 0219 { 0220 return m_year; 0221 } 0222 0223 QString 0224 AggregateTrack::comment() const 0225 { 0226 //try to return something sensible here... 0227 //do not show a comment if the internal tracks disagree about the comment 0228 QString comment; 0229 if( !m_tracks.isEmpty() ) 0230 comment = m_tracks.first()->comment(); 0231 0232 foreach( const Meta::TrackPtr &track, m_tracks ) 0233 { 0234 if( track->comment() != comment ) 0235 { 0236 comment.clear(); 0237 break; 0238 } 0239 } 0240 return comment; 0241 } 0242 0243 qreal 0244 AggregateTrack::bpm() const 0245 { 0246 //Similar to comment(), try to return something sensible here... 0247 //do not show a common bpm value if the internal tracks disagree about the bpm 0248 qreal bpm = -1.0; 0249 if( !m_tracks.isEmpty() ) 0250 bpm = m_tracks.first()->bpm(); 0251 0252 foreach( const Meta::TrackPtr &track, m_tracks ) 0253 { 0254 if( track->bpm() != bpm ) 0255 { 0256 bpm = -1.0; 0257 break; 0258 } 0259 } 0260 return bpm; 0261 } 0262 0263 double 0264 AggregateTrack::score() const 0265 { 0266 //again, multiple ways to implement this method: 0267 //return the maximum score, the minimum score, the average 0268 //the score of the track with the maximum play count, 0269 //or an average weighted by play count. And probably a couple of ways that 0270 //I cannot think of right now... 0271 0272 //implementing the weighted average here... 0273 double weightedSum = 0.0; 0274 int totalCount = 0; 0275 foreach( const Meta::TrackPtr &track, m_tracks ) 0276 { 0277 ConstStatisticsPtr statistics = track->statistics(); 0278 totalCount += statistics->playCount(); 0279 weightedSum += statistics->playCount() * statistics->score(); 0280 } 0281 if( totalCount ) 0282 return weightedSum / totalCount; 0283 0284 return 0.0; 0285 } 0286 0287 void 0288 AggregateTrack::setScore( double newScore ) 0289 { 0290 foreach( Meta::TrackPtr track, m_tracks ) 0291 { 0292 track->statistics()->setScore( newScore ); 0293 } 0294 } 0295 0296 int 0297 AggregateTrack::rating() const 0298 { 0299 //yay, multiple options again. As this has to be defined by the user, let's take 0300 //the maximum here. 0301 int result = 0; 0302 foreach( const Meta::TrackPtr &track, m_tracks ) 0303 { 0304 if( track->statistics()->rating() > result ) 0305 result = track->statistics()->rating(); 0306 } 0307 return result; 0308 } 0309 0310 void 0311 AggregateTrack::setRating( int newRating ) 0312 { 0313 foreach( Meta::TrackPtr track, m_tracks ) 0314 { 0315 track->statistics()->setRating( newRating ); 0316 } 0317 } 0318 0319 QDateTime 0320 AggregateTrack::firstPlayed() const 0321 { 0322 QDateTime result; 0323 foreach( const Meta::TrackPtr &track, m_tracks ) 0324 { 0325 ConstStatisticsPtr statistics = track->statistics(); 0326 //use the track's firstPlayed value if it represents an earlier timestamp than 0327 //the current result, or use it directly if result has not been set yet 0328 //this should result in the earliest timestamp for first play of all internal 0329 //tracks being returned 0330 if( ( statistics->firstPlayed().isValid() && result.isValid() && statistics->firstPlayed() < result ) || 0331 ( statistics->firstPlayed().isValid() && !result.isValid() ) ) 0332 { 0333 result = statistics->firstPlayed(); 0334 } 0335 } 0336 return result; 0337 } 0338 0339 void 0340 AggregateTrack::setFirstPlayed( const QDateTime &date ) 0341 { 0342 foreach( Meta::TrackPtr track, m_tracks ) 0343 { 0344 // only "lower" the first played 0345 Meta::StatisticsPtr trackStats = track->statistics(); 0346 if( !trackStats->firstPlayed().isValid() || 0347 trackStats->firstPlayed() > date ) 0348 { 0349 trackStats->setFirstPlayed( date ); 0350 } 0351 } 0352 } 0353 0354 QDateTime 0355 AggregateTrack::lastPlayed() const 0356 { 0357 QDateTime result; 0358 //return the latest timestamp. Easier than firstPlayed because we do not have to 0359 //care about 0. 0360 //when are we going to perform the refactoring as discussed in Berlin? 0361 foreach( const Meta::TrackPtr &track, m_tracks ) 0362 { 0363 if( track->statistics()->lastPlayed() > result ) 0364 { 0365 result = track->statistics()->lastPlayed(); 0366 } 0367 } 0368 return result; 0369 } 0370 0371 void 0372 AggregateTrack::setLastPlayed(const QDateTime& date) 0373 { 0374 foreach( Meta::TrackPtr track, m_tracks ) 0375 { 0376 // only "raise" the last played 0377 Meta::StatisticsPtr trackStats = track->statistics(); 0378 if( !trackStats->lastPlayed().isValid() || 0379 trackStats->lastPlayed() < date ) 0380 { 0381 trackStats->setLastPlayed( date ); 0382 } 0383 } 0384 } 0385 0386 int 0387 AggregateTrack::playCount() const 0388 { 0389 // show the maximum of all play counts. 0390 int result = 0; 0391 foreach( const Meta::TrackPtr &track, m_tracks ) 0392 { 0393 if( track->statistics()->playCount() > result ) 0394 { 0395 result = track->statistics()->playCount(); 0396 } 0397 } 0398 return result; 0399 } 0400 0401 void 0402 AggregateTrack::setPlayCount( int newPlayCount ) 0403 { 0404 Q_UNUSED( newPlayCount ) 0405 // no safe thing to do here. Notice we override finishedPlaying() 0406 } 0407 0408 void 0409 AggregateTrack::finishedPlaying( double playedFraction ) 0410 { 0411 foreach( Meta::TrackPtr track, m_tracks ) 0412 { 0413 track->finishedPlaying( playedFraction ); 0414 } 0415 } 0416 0417 qint64 0418 AggregateTrack::length() const 0419 { 0420 foreach( const Meta::TrackPtr &track, m_tracks ) 0421 { 0422 if( track->length() ) 0423 return track->length(); 0424 } 0425 return 0; 0426 } 0427 0428 int 0429 AggregateTrack::filesize() const 0430 { 0431 foreach( const Meta::TrackPtr &track, m_tracks ) 0432 { 0433 if( track->filesize() ) 0434 { 0435 return track->filesize(); 0436 } 0437 } 0438 return 0; 0439 } 0440 0441 int 0442 AggregateTrack::sampleRate() const 0443 { 0444 foreach( const Meta::TrackPtr &track, m_tracks ) 0445 { 0446 if( track->sampleRate() ) 0447 return track->sampleRate(); 0448 } 0449 return 0; 0450 } 0451 0452 int 0453 AggregateTrack::bitrate() const 0454 { 0455 foreach( const Meta::TrackPtr &track, m_tracks ) 0456 { 0457 if( track->bitrate() ) 0458 return track->bitrate(); 0459 } 0460 return 0; 0461 } 0462 0463 QDateTime 0464 AggregateTrack::createDate() const 0465 { 0466 QDateTime result; 0467 foreach( const Meta::TrackPtr &track, m_tracks ) 0468 { 0469 //use the track's firstPlayed value if it represents an earlier timestamp than 0470 //the current result, or use it directly if result has not been set yet 0471 //this should result in the earliest timestamp for first play of all internal 0472 //tracks being returned 0473 if( ( track->createDate().isValid() && result.isValid() && track->createDate() < result ) || 0474 ( track->createDate().isValid() && !result.isValid() ) ) 0475 { 0476 result = track->createDate(); 0477 } 0478 } 0479 return result; 0480 } 0481 0482 int 0483 AggregateTrack::trackNumber() const 0484 { 0485 int result = 0; 0486 foreach( const Meta::TrackPtr &track, m_tracks ) 0487 { 0488 if( ( !result && track->trackNumber() ) || ( result && result == track->trackNumber() ) ) 0489 { 0490 result = track->trackNumber(); 0491 } 0492 else if( result && result != track->trackNumber() ) 0493 { 0494 //tracks disagree about the tracknumber 0495 return 0; 0496 } 0497 } 0498 return result; 0499 } 0500 0501 int 0502 AggregateTrack::discNumber() const 0503 { 0504 int result = 0; 0505 foreach( const Meta::TrackPtr &track, m_tracks ) 0506 { 0507 if( ( !result && track->discNumber() ) || ( result && result == track->discNumber() ) ) 0508 { 0509 result = track->discNumber(); 0510 } 0511 else if( result && result != track->discNumber() ) 0512 { 0513 //tracks disagree about the disc number 0514 return 0; 0515 } 0516 } 0517 return result; 0518 } 0519 0520 QString 0521 AggregateTrack::type() const 0522 { 0523 if( m_tracks.size() == 1 ) 0524 { 0525 return m_tracks.first()->type(); 0526 } 0527 else 0528 { 0529 //TODO: figure something out 0530 return QString(); 0531 } 0532 } 0533 0534 Collections::Collection* 0535 AggregateTrack::collection() const 0536 { 0537 return m_collection; 0538 } 0539 0540 bool 0541 AggregateTrack::hasCapabilityInterface( Capabilities::Capability::Type type ) const 0542 { 0543 if( m_tracks.count() == 1 ) 0544 // if we aggregate only one track, simply return the tracks capability directly 0545 return m_tracks.first()->hasCapabilityInterface( type ); 0546 else 0547 return false; 0548 } 0549 0550 Capabilities::Capability* 0551 AggregateTrack::createCapabilityInterface( Capabilities::Capability::Type type ) 0552 { 0553 if( m_tracks.count() == 1 ) 0554 return m_tracks.first()->createCapabilityInterface( type ); 0555 else 0556 return nullptr; 0557 } 0558 0559 TrackEditorPtr 0560 AggregateTrack::editor() 0561 { 0562 if( m_tracks.count() == 1 ) 0563 return m_tracks.first()->editor(); 0564 0565 QList<Meta::TrackEditorPtr> editors; 0566 foreach( Meta::TrackPtr track, m_tracks ) 0567 { 0568 Meta::TrackEditorPtr ec = track->editor(); 0569 if( ec ) 0570 editors << ec; 0571 else 0572 return TrackEditorPtr(); 0573 } 0574 return TrackEditorPtr( new AggregateTrackEditor( m_collection, editors ) ); 0575 } 0576 0577 void 0578 AggregateTrack::addLabel( const QString &label ) 0579 { 0580 foreach( Meta::TrackPtr track, m_tracks ) 0581 { 0582 track->addLabel( label ); 0583 } 0584 } 0585 0586 void 0587 AggregateTrack::addLabel( const Meta::LabelPtr &label ) 0588 { 0589 foreach( Meta::TrackPtr track, m_tracks ) 0590 { 0591 track->addLabel( label ); 0592 } 0593 } 0594 0595 void 0596 AggregateTrack::removeLabel( const Meta::LabelPtr &label ) 0597 { 0598 foreach( Meta::TrackPtr track, m_tracks ) 0599 { 0600 track->removeLabel( label ); 0601 } 0602 } 0603 0604 Meta::LabelList 0605 AggregateTrack::labels() const 0606 { 0607 QSet<AggregateLabel *> aggregateLabels; 0608 foreach( const Meta::TrackPtr &track, m_tracks ) 0609 { 0610 foreach( Meta::LabelPtr label, track->labels() ) 0611 { 0612 aggregateLabels.insert( m_collection->getLabel( label ) ); 0613 } 0614 } 0615 Meta::LabelList result; 0616 foreach( AggregateLabel *label, aggregateLabels ) 0617 { 0618 result << Meta::LabelPtr( label ); 0619 } 0620 return result; 0621 } 0622 0623 StatisticsPtr 0624 AggregateTrack::statistics() 0625 { 0626 return StatisticsPtr( this ); 0627 } 0628 0629 void 0630 AggregateTrack::add( const Meta::TrackPtr &track ) 0631 { 0632 if( !track || m_tracks.contains( track ) ) 0633 return; 0634 0635 m_tracks.append( track ); 0636 subscribeTo( track ); 0637 0638 notifyObservers(); 0639 } 0640 0641 void 0642 AggregateTrack::metadataChanged(const TrackPtr &track ) 0643 { 0644 if( !track ) 0645 return; 0646 0647 if( !m_tracks.contains( track ) ) 0648 { 0649 //why are we subscribed? 0650 unsubscribeFrom( track ); 0651 return; 0652 } 0653 0654 const TrackKey myKey( Meta::TrackPtr( this ) ); 0655 const TrackKey otherKey( track ); 0656 if( myKey == otherKey ) 0657 { 0658 //no key relevant metadata did change 0659 notifyObservers(); 0660 return; 0661 } 0662 else 0663 { 0664 if( m_tracks.size() == 1 ) 0665 { 0666 if( m_collection->hasTrack( otherKey ) ) 0667 { 0668 unsubscribeFrom( track ); 0669 m_collection->getTrack( track ); 0670 m_tracks.removeAll( track ); 0671 m_collection->removeTrack( myKey ); 0672 return; //do not notify observers, this track is not valid anymore! 0673 } 0674 else 0675 { 0676 m_name = track->name(); 0677 if( track->album() ) 0678 m_album = Meta::AlbumPtr( m_collection->getAlbum( track->album() ) ); 0679 if( track->artist() ) 0680 m_artist = Meta::ArtistPtr( m_collection->getArtist( track->artist() ) ); 0681 if( track->genre() ) 0682 m_genre = Meta::GenrePtr( m_collection->getGenre( track->genre() ) ); 0683 if( track->composer() ) 0684 m_composer = Meta::ComposerPtr( m_collection->getComposer( track->composer() ) ); 0685 if( track->year() ) 0686 m_year = Meta::YearPtr( m_collection->getYear( track->year() ) ); 0687 0688 m_collection->setTrack( this ); 0689 m_collection->removeTrack( myKey ); 0690 } 0691 } 0692 else 0693 { 0694 unsubscribeFrom( track ); 0695 m_collection->getTrack( track ); 0696 m_tracks.removeAll( track ); 0697 } 0698 notifyObservers(); 0699 } 0700 } 0701 0702 AggregateAlbum::AggregateAlbum( Collections::AggregateCollection *coll, Meta::AlbumPtr album ) 0703 : Meta::Album() 0704 , Meta::Observer() 0705 , m_collection( coll ) 0706 , m_name( album->name() ) 0707 { 0708 m_albums.append( album ); 0709 if( album->hasAlbumArtist() ) 0710 m_albumArtist = Meta::ArtistPtr( m_collection->getArtist( album->albumArtist() ) ); 0711 } 0712 0713 AggregateAlbum::~AggregateAlbum() 0714 { 0715 } 0716 0717 QString 0718 AggregateAlbum::name() const 0719 { 0720 return m_name; 0721 } 0722 0723 QString 0724 AggregateAlbum::prettyName() const 0725 { 0726 return m_name; 0727 } 0728 0729 QString 0730 AggregateAlbum::sortableName() const 0731 { 0732 if( !m_albums.isEmpty() ) 0733 return m_albums.first()->sortableName(); 0734 0735 return m_name; 0736 } 0737 0738 Meta::TrackList 0739 AggregateAlbum::tracks() 0740 { 0741 QSet<AggregateTrack*> tracks; 0742 foreach( Meta::AlbumPtr album, m_albums ) 0743 { 0744 Meta::TrackList tmp = album->tracks(); 0745 foreach( const Meta::TrackPtr &track, tmp ) 0746 { 0747 tracks.insert( m_collection->getTrack( track ) ); 0748 } 0749 } 0750 0751 Meta::TrackList result; 0752 foreach( AggregateTrack *track, tracks ) 0753 { 0754 result.append( Meta::TrackPtr( track ) ); 0755 } 0756 return result; 0757 } 0758 0759 Meta::ArtistPtr 0760 AggregateAlbum::albumArtist() const 0761 { 0762 return m_albumArtist; 0763 } 0764 0765 bool 0766 AggregateAlbum::isCompilation() const 0767 { 0768 return m_albumArtist.isNull(); 0769 } 0770 0771 bool 0772 AggregateAlbum::hasAlbumArtist() const 0773 { 0774 return !m_albumArtist.isNull(); 0775 } 0776 0777 bool 0778 AggregateAlbum::hasCapabilityInterface(Capabilities::Capability::Type type ) const 0779 { 0780 0781 if( m_albums.count() == 1 ) 0782 { 0783 return m_albums.first()->hasCapabilityInterface( type ); 0784 } 0785 else 0786 { 0787 return false; 0788 } 0789 } 0790 0791 Capabilities::Capability* 0792 AggregateAlbum::createCapabilityInterface( Capabilities::Capability::Type type ) 0793 { 0794 if( m_albums.count() == 1 ) 0795 { 0796 return m_albums.first()->createCapabilityInterface( type ); 0797 } 0798 else 0799 { 0800 return nullptr; 0801 } 0802 } 0803 0804 void 0805 AggregateAlbum::add( const Meta::AlbumPtr &album ) 0806 { 0807 if( !album || m_albums.contains( album ) ) 0808 return; 0809 0810 m_albums.append( album ); 0811 subscribeTo( album ); 0812 0813 notifyObservers(); 0814 } 0815 0816 bool 0817 AggregateAlbum::hasImage( int size ) const 0818 { 0819 foreach( const Meta::AlbumPtr &album, m_albums ) 0820 { 0821 if( album->hasImage( size ) ) 0822 return true; 0823 } 0824 return false; 0825 } 0826 0827 QImage 0828 AggregateAlbum::image( int size ) const 0829 { 0830 foreach( Meta::AlbumPtr album, m_albums ) 0831 { 0832 if( album->hasImage( size ) ) 0833 { 0834 return album->image( size ); 0835 } 0836 } 0837 return Meta::Album::image( size ); 0838 } 0839 0840 QUrl 0841 AggregateAlbum::imageLocation( int size ) 0842 { 0843 foreach( Meta::AlbumPtr album, m_albums ) 0844 { 0845 if( album->hasImage( size ) ) 0846 { 0847 QUrl url = album->imageLocation( size ); 0848 if( url.isValid() ) 0849 { 0850 return url; 0851 } 0852 } 0853 } 0854 return QUrl(); 0855 } 0856 0857 QPixmap 0858 AggregateAlbum::imageWithBorder( int size, int borderWidth ) 0859 { 0860 foreach( Meta::AlbumPtr album, m_albums ) 0861 { 0862 if( album->hasImage( size ) ) 0863 { 0864 return The::svgHandler()->imageWithBorder( album, size, borderWidth ); 0865 } 0866 } 0867 return QPixmap(); 0868 } 0869 0870 bool 0871 AggregateAlbum::canUpdateImage() const 0872 { 0873 if( m_albums.isEmpty() ) 0874 return false; 0875 0876 foreach( const Meta::AlbumPtr &album, m_albums ) 0877 { 0878 //we can only update the image for all albums at the same time 0879 if( !album->canUpdateImage() ) 0880 return false; 0881 } 0882 return true; 0883 } 0884 0885 void 0886 AggregateAlbum::setImage( const QImage &image ) 0887 { 0888 foreach( Meta::AlbumPtr album, m_albums ) 0889 { 0890 album->setImage( image ); 0891 } 0892 } 0893 0894 void 0895 AggregateAlbum::removeImage() 0896 { 0897 foreach( Meta::AlbumPtr album, m_albums ) 0898 { 0899 album->removeImage(); 0900 } 0901 } 0902 0903 void 0904 AggregateAlbum::setSuppressImageAutoFetch( bool suppress ) 0905 { 0906 foreach( Meta::AlbumPtr album, m_albums ) 0907 { 0908 album->setSuppressImageAutoFetch( suppress ); 0909 } 0910 } 0911 0912 bool 0913 AggregateAlbum::suppressImageAutoFetch() const 0914 { 0915 foreach( const Meta::AlbumPtr &album, m_albums ) 0916 { 0917 if( !album->suppressImageAutoFetch() ) 0918 return false; 0919 } 0920 return true; 0921 } 0922 0923 void 0924 AggregateAlbum::metadataChanged(const AlbumPtr &album ) 0925 { 0926 if( !album || !m_albums.contains( album ) ) 0927 return; 0928 0929 if( album->name() != m_name || 0930 hasAlbumArtist() != album->hasAlbumArtist() || 0931 ( hasAlbumArtist() && m_albumArtist->name() != album->albumArtist()->name() ) ) 0932 { 0933 if( m_albums.count() > 1 ) 0934 { 0935 m_collection->getAlbum( album ); 0936 unsubscribeFrom( album ); 0937 m_albums.removeAll( album ); 0938 } 0939 else 0940 { 0941 Meta::ArtistPtr albumartist; 0942 if( album->hasAlbumArtist() ) 0943 albumartist = Meta::ArtistPtr( m_collection->getArtist( album->albumArtist() ) ); 0944 0945 QString artistname = m_albumArtist ? m_albumArtist->name() : QString(); 0946 m_collection->removeAlbum( m_name, artistname ); 0947 m_name = album->name(); 0948 m_albumArtist = albumartist; 0949 m_collection->setAlbum( this ); 0950 } 0951 } 0952 0953 notifyObservers(); 0954 } 0955 0956 AggregateArtist::AggregateArtist( Collections::AggregateCollection *coll, const Meta::ArtistPtr &artist ) 0957 : Meta::Artist() 0958 , Meta::Observer() 0959 , m_collection( coll ) 0960 , m_name( artist->name() ) 0961 { 0962 m_artists.append( artist ); 0963 subscribeTo( artist ); 0964 } 0965 0966 AggregateArtist::~AggregateArtist() 0967 { 0968 } 0969 0970 QString 0971 AggregateArtist::name() const 0972 { 0973 return m_name; 0974 } 0975 0976 QString 0977 AggregateArtist::prettyName() const 0978 { 0979 return m_name; 0980 } 0981 0982 QString 0983 AggregateArtist::sortableName() const 0984 { 0985 if( !m_artists.isEmpty() ) 0986 return m_artists.first()->sortableName(); 0987 0988 return m_name; 0989 } 0990 0991 Meta::TrackList 0992 AggregateArtist::tracks() 0993 { 0994 QSet<AggregateTrack*> tracks; 0995 foreach( Meta::ArtistPtr artist, m_artists ) 0996 { 0997 Meta::TrackList tmp = artist->tracks(); 0998 foreach( const Meta::TrackPtr &track, tmp ) 0999 { 1000 tracks.insert( m_collection->getTrack( track ) ); 1001 } 1002 } 1003 1004 Meta::TrackList result; 1005 foreach( AggregateTrack *track, tracks ) 1006 { 1007 result.append( Meta::TrackPtr( track ) ); 1008 } 1009 return result; 1010 } 1011 1012 bool 1013 AggregateArtist::hasCapabilityInterface(Capabilities::Capability::Type type ) const 1014 { 1015 1016 if( m_artists.count() == 1 ) 1017 { 1018 return m_artists.first()->hasCapabilityInterface( type ); 1019 } 1020 else 1021 { 1022 return false; 1023 } 1024 } 1025 1026 Capabilities::Capability* 1027 AggregateArtist::createCapabilityInterface( Capabilities::Capability::Type type ) 1028 { 1029 if( m_artists.count() == 1 ) 1030 { 1031 return m_artists.first()->createCapabilityInterface( type ); 1032 } 1033 else 1034 { 1035 return nullptr; 1036 } 1037 } 1038 1039 void 1040 AggregateArtist::add( const Meta::ArtistPtr &artist ) 1041 { 1042 if( !artist || m_artists.contains( artist ) ) 1043 return; 1044 1045 m_artists.append( artist ); 1046 subscribeTo( artist ); 1047 1048 notifyObservers(); 1049 } 1050 1051 void 1052 AggregateArtist::metadataChanged(const ArtistPtr &artist ) 1053 { 1054 if( !artist || !m_artists.contains( artist ) ) 1055 return; 1056 1057 if( artist->name() != m_name ) 1058 { 1059 if( m_artists.count() > 1 ) 1060 { 1061 m_collection->getArtist( artist ); 1062 unsubscribeFrom( artist ); 1063 m_artists.removeAll( artist ); 1064 } 1065 else 1066 { 1067 //possible race condition here: 1068 //if another thread creates an Artist with the new name 1069 //we will have two instances that have the same name! 1070 //TODO: figure out a way around that 1071 //the race condition is a problem for all other metadataChanged methods too 1072 m_collection->removeArtist( m_name ); 1073 m_name = artist->name(); 1074 m_collection->setArtist( this ); 1075 1076 } 1077 } 1078 1079 notifyObservers(); 1080 } 1081 1082 AggregateGenre::AggregateGenre( Collections::AggregateCollection *coll, const Meta::GenrePtr &genre ) 1083 : Meta::Genre() 1084 , Meta::Observer() 1085 , m_collection( coll ) 1086 , m_name( genre->name() ) 1087 { 1088 m_genres.append( genre ); 1089 subscribeTo( genre ); 1090 } 1091 1092 AggregateGenre::~AggregateGenre() 1093 { 1094 } 1095 1096 QString 1097 AggregateGenre::name() const 1098 { 1099 return m_name; 1100 } 1101 1102 QString 1103 AggregateGenre::prettyName() const 1104 { 1105 return m_name; 1106 } 1107 1108 QString 1109 AggregateGenre::sortableName() const 1110 { 1111 if( !m_genres.isEmpty() ) 1112 return m_genres.first()->sortableName(); 1113 1114 return m_name; 1115 } 1116 1117 Meta::TrackList 1118 AggregateGenre::tracks() 1119 { 1120 QSet<AggregateTrack*> tracks; 1121 foreach( Meta::GenrePtr genre, m_genres ) 1122 { 1123 Meta::TrackList tmp = genre->tracks(); 1124 foreach( const Meta::TrackPtr &track, tmp ) 1125 { 1126 tracks.insert( m_collection->getTrack( track ) ); 1127 } 1128 } 1129 1130 Meta::TrackList result; 1131 foreach( AggregateTrack *track, tracks ) 1132 { 1133 result.append( Meta::TrackPtr( track ) ); 1134 } 1135 return result; 1136 } 1137 1138 bool 1139 AggregateGenre::hasCapabilityInterface(Capabilities::Capability::Type type ) const 1140 { 1141 1142 if( m_genres.count() == 1 ) 1143 { 1144 return m_genres.first()->hasCapabilityInterface( type ); 1145 } 1146 else 1147 { 1148 return false; 1149 } 1150 } 1151 1152 Capabilities::Capability* 1153 AggregateGenre::createCapabilityInterface( Capabilities::Capability::Type type ) 1154 { 1155 if( m_genres.count() == 1 ) 1156 { 1157 return m_genres.first()->createCapabilityInterface( type ); 1158 } 1159 else 1160 { 1161 return nullptr; 1162 } 1163 } 1164 1165 void 1166 AggregateGenre::add( const Meta::GenrePtr &genre ) 1167 { 1168 if( !genre || m_genres.contains( genre ) ) 1169 return; 1170 1171 m_genres.append( genre ); 1172 subscribeTo( genre ); 1173 1174 notifyObservers(); 1175 } 1176 1177 void 1178 AggregateGenre::metadataChanged( const Meta::GenrePtr &genre ) 1179 { 1180 if( !genre || !m_genres.contains( genre ) ) 1181 return; 1182 1183 if( genre->name() != m_name ) 1184 { 1185 if( m_genres.count() > 1 ) 1186 { 1187 m_collection->getGenre( genre ); 1188 unsubscribeFrom( genre ); 1189 m_genres.removeAll( genre ); 1190 } 1191 else 1192 { 1193 m_collection->removeGenre( m_name ); 1194 m_collection->setGenre( this ); 1195 m_name = genre->name(); 1196 } 1197 } 1198 1199 notifyObservers(); 1200 } 1201 1202 AggregateComposer::AggregateComposer( Collections::AggregateCollection *coll, const Meta::ComposerPtr &composer ) 1203 : Meta::Composer() 1204 , Meta::Observer() 1205 , m_collection( coll ) 1206 , m_name( composer->name() ) 1207 { 1208 m_composers.append( composer ); 1209 subscribeTo( composer ); 1210 } 1211 1212 AggregateComposer::~AggregateComposer() 1213 { 1214 } 1215 1216 QString 1217 AggregateComposer::name() const 1218 { 1219 return m_name; 1220 } 1221 1222 QString 1223 AggregateComposer::prettyName() const 1224 { 1225 return m_name; 1226 } 1227 1228 QString 1229 AggregateComposer::sortableName() const 1230 { 1231 if( !m_composers.isEmpty() ) 1232 return m_composers.first()->sortableName(); 1233 1234 return m_name; 1235 } 1236 1237 Meta::TrackList 1238 AggregateComposer::tracks() 1239 { 1240 QSet<AggregateTrack*> tracks; 1241 foreach( Meta::ComposerPtr composer, m_composers ) 1242 { 1243 Meta::TrackList tmp = composer->tracks(); 1244 foreach( const Meta::TrackPtr &track, tmp ) 1245 { 1246 tracks.insert( m_collection->getTrack( track ) ); 1247 } 1248 } 1249 1250 Meta::TrackList result; 1251 foreach( AggregateTrack *track, tracks ) 1252 { 1253 result.append( Meta::TrackPtr( track ) ); 1254 } 1255 return result; 1256 } 1257 1258 bool 1259 AggregateComposer::hasCapabilityInterface(Capabilities::Capability::Type type ) const 1260 { 1261 1262 if( m_composers.count() == 1 ) 1263 { 1264 return m_composers.first()->hasCapabilityInterface( type ); 1265 } 1266 else 1267 { 1268 return false; 1269 } 1270 } 1271 1272 Capabilities::Capability* 1273 AggregateComposer::createCapabilityInterface( Capabilities::Capability::Type type ) 1274 { 1275 if( m_composers.count() == 1 ) 1276 { 1277 return m_composers.first()->createCapabilityInterface( type ); 1278 } 1279 else 1280 { 1281 return nullptr; 1282 } 1283 } 1284 1285 void 1286 AggregateComposer::add( const Meta::ComposerPtr &composer ) 1287 { 1288 if( !composer || m_composers.contains( composer ) ) 1289 return; 1290 1291 m_composers.append( composer ); 1292 subscribeTo( composer ); 1293 1294 notifyObservers(); 1295 } 1296 1297 void 1298 AggregateComposer::metadataChanged(const ComposerPtr &composer ) 1299 { 1300 if( !composer || !m_composers.contains( composer ) ) 1301 return; 1302 1303 if( composer->name() != m_name ) 1304 { 1305 if( m_composers.count() > 1 ) 1306 { 1307 m_collection->getComposer( composer ); 1308 unsubscribeFrom( composer ); 1309 m_composers.removeAll( composer ); 1310 } 1311 else 1312 { 1313 m_collection->removeComposer( m_name ); 1314 m_collection->setComposer( this ); 1315 m_name = composer->name(); 1316 } 1317 } 1318 1319 notifyObservers(); 1320 } 1321 1322 AggreagateYear::AggreagateYear( Collections::AggregateCollection *coll, const Meta::YearPtr &year ) 1323 : Meta::Year() 1324 , Meta::Observer() 1325 , m_collection( coll ) 1326 , m_name( year->name() ) 1327 { 1328 m_years.append( year ); 1329 subscribeTo( year ); 1330 } 1331 1332 AggreagateYear::~AggreagateYear() 1333 { 1334 //nothing to do 1335 } 1336 1337 QString 1338 AggreagateYear::name() const 1339 { 1340 return m_name; 1341 } 1342 1343 QString 1344 AggreagateYear::prettyName() const 1345 { 1346 return m_name; 1347 } 1348 1349 QString 1350 AggreagateYear::sortableName() const 1351 { 1352 if( !m_years.isEmpty() ) 1353 return m_years.first()->sortableName(); 1354 1355 return m_name; 1356 } 1357 1358 Meta::TrackList 1359 AggreagateYear::tracks() 1360 { 1361 QSet<AggregateTrack*> tracks; 1362 foreach( Meta::YearPtr year, m_years ) 1363 { 1364 Meta::TrackList tmp = year->tracks(); 1365 foreach( const Meta::TrackPtr &track, tmp ) 1366 { 1367 tracks.insert( m_collection->getTrack( track ) ); 1368 } 1369 } 1370 1371 Meta::TrackList result; 1372 foreach( AggregateTrack *track, tracks ) 1373 { 1374 result.append( Meta::TrackPtr( track ) ); 1375 } 1376 return result; 1377 } 1378 1379 bool 1380 AggreagateYear::hasCapabilityInterface(Capabilities::Capability::Type type ) const 1381 { 1382 1383 if( m_years.count() == 1 ) 1384 { 1385 return m_years.first()->hasCapabilityInterface( type ); 1386 } 1387 else 1388 { 1389 return false; 1390 } 1391 } 1392 1393 Capabilities::Capability* 1394 AggreagateYear::createCapabilityInterface( Capabilities::Capability::Type type ) 1395 { 1396 if( m_years.count() == 1 ) 1397 { 1398 return m_years.first()->createCapabilityInterface( type ); 1399 } 1400 else 1401 { 1402 return nullptr; 1403 } 1404 } 1405 1406 void 1407 AggreagateYear::add( const Meta::YearPtr &year ) 1408 { 1409 if( !year || m_years.contains( year ) ) 1410 return; 1411 1412 m_years.append( year ); 1413 subscribeTo( year ); 1414 1415 notifyObservers(); 1416 } 1417 1418 void 1419 AggreagateYear::metadataChanged( const Meta::YearPtr &year ) 1420 { 1421 if( !year || !m_years.contains( year ) ) 1422 return; 1423 1424 if( year->name() != m_name ) 1425 { 1426 if( m_years.count() > 1 ) 1427 { 1428 m_collection->getYear( year ); 1429 unsubscribeFrom( year ); 1430 m_years.removeAll( year ); 1431 } 1432 else 1433 { 1434 if( m_collection->hasYear( year->name() ) ) 1435 { 1436 unsubscribeFrom( year ); 1437 m_collection->getYear( year ); 1438 m_years.removeAll( year ); 1439 m_collection->removeYear( m_name ); 1440 return; //do NOT notify observers, the instance is not valid anymore! 1441 } 1442 else 1443 { 1444 // be careful with the ordering of instructions here 1445 // AggregateCollection uses AmarokSharedPointer internally 1446 // so we have to make sure that there is more than one pointer 1447 // to this instance by registering this instance under the new name 1448 // before removing the old one. Otherwise kSharedPtr might delete this 1449 // instance in removeYear() 1450 QString tmpName = m_name; 1451 m_name = year->name(); 1452 m_collection->setYear( this ); 1453 m_collection->removeYear( tmpName ); 1454 } 1455 } 1456 } 1457 1458 notifyObservers(); 1459 } 1460 1461 AggregateLabel::AggregateLabel( Collections::AggregateCollection *coll, const Meta::LabelPtr &label ) 1462 : Meta::Label() 1463 , m_collection( coll ) 1464 , m_name( label->name() ) 1465 { 1466 m_labels.append( label ); 1467 Q_UNUSED(m_collection); // might be needed later 1468 } 1469 1470 AggregateLabel::~AggregateLabel() 1471 { 1472 //nothing to do 1473 } 1474 1475 QString 1476 AggregateLabel::name() const 1477 { 1478 return m_name; 1479 } 1480 1481 QString 1482 AggregateLabel::prettyName() const 1483 { 1484 return m_name; 1485 } 1486 1487 QString 1488 AggregateLabel::sortableName() const 1489 { 1490 if( !m_labels.isEmpty() ) 1491 return m_labels.first()->sortableName(); 1492 1493 return m_name; 1494 } 1495 1496 bool 1497 AggregateLabel::hasCapabilityInterface( Capabilities::Capability::Type type ) const 1498 { 1499 1500 if( m_labels.count() == 1 ) 1501 { 1502 return m_labels.first()->hasCapabilityInterface( type ); 1503 } 1504 else 1505 { 1506 return false; 1507 } 1508 } 1509 1510 Capabilities::Capability* 1511 AggregateLabel::createCapabilityInterface( Capabilities::Capability::Type type ) 1512 { 1513 if( m_labels.count() == 1 ) 1514 { 1515 return m_labels.first()->createCapabilityInterface( type ); 1516 } 1517 else 1518 { 1519 return nullptr; 1520 } 1521 } 1522 1523 void 1524 AggregateLabel::add( const Meta::LabelPtr &label ) 1525 { 1526 if( !label || m_labels.contains( label ) ) 1527 return; 1528 1529 m_labels.append( label ); 1530 } 1531 1532 } //namespace Meta