File indexing completed on 2024-05-19 04:49:17

0001 /****************************************************************************************
0002  * Copyright (c) 2011 Bart Cerneels <bart.cerneels@kde.org>                             *
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 #ifndef MEMORYMETA_H
0018 #define MEMORYMETA_H
0019 
0020 #include "amarok_export.h"
0021 #include "MemoryCollection.h"
0022 #include "core/meta/Meta.h"
0023 
0024 using namespace Collections;
0025 
0026 /** These classes can be used with a MemoryCollection to populate the meta-type maps */
0027 namespace MemoryMeta {
0028 
0029 class Track;
0030 
0031 /**
0032  * Base class for all MemoryMeta:: entities that store a list of associated tracks:
0033  * Artist, Album, Composer, Genre, Year.
0034  *
0035  * All methods of this class are thread-safe.
0036  */
0037 class Base
0038 {
0039     public:
0040         explicit Base( const QString &name ) : m_name( name ) {}
0041         virtual ~Base() {}
0042 
0043         // Meta::{Artist,Album,Composer,Genre,Year} methods:
0044         virtual QString name() const { return m_name; }
0045         virtual Meta::TrackList tracks();
0046 
0047         // MemoryMeta::Base methods:
0048         void addTrack( Track *track );
0049         void removeTrack( Track *track );
0050 
0051     private:
0052         QString m_name;
0053         /* We cannot easily store AmarokSharedPointer to tracks, because it creates reference
0054          * counting cycle: MemoryMeta::Track::m_album -> MemoryMeta::Album::tracks() ->
0055          * MemoryMeta::Track. We therefore store plain pointers and rely on
0056          * MemoryMeta::Track to notify when it is destroyed. */
0057         QList<Track *> m_tracks;
0058         QReadWriteLock m_tracksLock;
0059 };
0060 
0061 class Artist : public Meta::Artist, public Base
0062 {
0063     public:
0064         explicit Artist( const QString &name ) : MemoryMeta::Base( name ) {}
0065 
0066         QString name() const override { return MemoryMeta::Base::name(); }
0067         Meta::TrackList tracks() override { return MemoryMeta::Base::tracks(); }
0068 };
0069 
0070 class Album : public Meta::Album, public Base
0071 {
0072     public:
0073         Album( const QString &name, const Meta::ArtistPtr &albumArtist )
0074             : MemoryMeta::Base( name )
0075             , m_albumArtist( albumArtist )
0076             , m_isCompilation( false )
0077             , m_canUpdateCompilation( false )
0078             , m_canUpdateImage( false )
0079             {}
0080         /**
0081          * Copy-like constructor for MapChanger
0082          */
0083         explicit Album( const Meta::AlbumPtr &other );
0084 
0085         /* Meta::MetaCapability virtual methods */
0086         bool hasCapabilityInterface( Capabilities::Capability::Type type ) const override;
0087         Capabilities::Capability* createCapabilityInterface( Capabilities::Capability::Type type ) override;
0088 
0089         /* Meta::Base virtual methods */
0090         QString name() const override { return MemoryMeta::Base::name(); }
0091 
0092         /* Meta::Album virtual methods */
0093         bool isCompilation() const override { return m_isCompilation; }
0094         bool canUpdateCompilation() const override { return m_canUpdateCompilation; }
0095         void setCompilation( bool isCompilation ) override;
0096 
0097         bool hasAlbumArtist() const override { return !m_albumArtist.isNull(); }
0098         Meta::ArtistPtr albumArtist() const override { return m_albumArtist; }
0099         Meta::TrackList tracks() override { return MemoryMeta::Base::tracks(); }
0100 
0101         bool hasImage( int /* size */ = 0 ) const override { return !m_image.isNull(); }
0102         QImage image( int size = 0 ) const override;
0103         bool canUpdateImage() const override { return m_canUpdateImage; }
0104         void setImage( const QImage &image ) override;
0105         void removeImage() override;
0106 
0107         /* MemoryMeta::Album methods: */
0108         /**
0109          * Re-read isCompilation, canUpdateCompilation, image, canUpdateImage from all
0110          * underlying tracks.
0111          */
0112         void updateCachedValues();
0113 
0114     private:
0115         Meta::ArtistPtr m_albumArtist;
0116         bool m_isCompilation;
0117         bool m_canUpdateCompilation;
0118         QImage m_image;
0119         bool m_canUpdateImage;
0120 };
0121 
0122 class Composer : public Meta::Composer, public Base
0123 {
0124     public:
0125         explicit Composer( const QString &name ) : MemoryMeta::Base( name ) {}
0126 
0127         QString name() const override { return MemoryMeta::Base::name(); }
0128         Meta::TrackList tracks() override { return  MemoryMeta::Base::tracks(); }
0129 };
0130 
0131 class Genre : public Meta::Genre, public Base
0132 {
0133     public:
0134         explicit Genre( const QString &name ) : MemoryMeta::Base( name ) {}
0135 
0136         QString name() const override { return MemoryMeta::Base::name(); }
0137         Meta::TrackList tracks() override { return MemoryMeta::Base::tracks(); }
0138 };
0139 
0140 class Year : public Meta::Year, public Base
0141 {
0142     public:
0143         explicit Year( const QString &name ) : MemoryMeta::Base( name ) {}
0144 
0145         QString name() const override { return MemoryMeta::Base::name(); }
0146         Meta::TrackList tracks() override { return MemoryMeta::Base::tracks(); }
0147 };
0148 
0149 class AMAROK_EXPORT Track : public Meta::Track
0150 {
0151     public:
0152         explicit Track( const Meta::TrackPtr &originalTrack );
0153         ~Track() override;
0154 
0155         /* Meta::MetaCapability methods */
0156         bool hasCapabilityInterface( Capabilities::Capability::Type type ) const override
0157             { return m_track->hasCapabilityInterface( type ); }
0158         Capabilities::Capability *createCapabilityInterface( Capabilities::Capability::Type type ) override
0159             { return m_track->createCapabilityInterface( type ); }
0160 
0161         /* Meta::Base virtual methods */
0162         QString name() const override { return m_track->name(); }
0163 
0164         /* Meta::Track virtual methods */
0165         QUrl playableUrl() const override { return m_track->playableUrl(); }
0166         QString prettyUrl() const override { return m_track->prettyUrl(); }
0167         QString uidUrl() const override { return m_track->uidUrl(); }
0168         QString notPlayableReason() const override { return m_track->notPlayableReason(); }
0169 
0170         //these functions return the proxy track values
0171         Meta::AlbumPtr album() const override { return m_album; }
0172         Meta::ArtistPtr artist() const override { return m_artist; }
0173         Meta::ComposerPtr composer() const override { return m_composer; }
0174         Meta::GenrePtr genre() const override { return m_genre; }
0175         Meta::YearPtr year() const override { return m_year; }
0176 
0177         //TODO:implement labels
0178         Meta::LabelList labels() const override { return Meta::LabelList(); }
0179         qreal bpm() const override { return m_track->bpm(); }
0180         QString comment() const override { return m_track->comment(); }
0181         qint64 length() const override { return m_track->length(); }
0182         int filesize() const override { return m_track->filesize(); }
0183         int sampleRate() const override { return m_track->sampleRate(); }
0184         int bitrate() const override { return m_track->bitrate(); }
0185         QDateTime createDate() const override { return m_track->createDate(); }
0186         QDateTime modifyDate() const override { return m_track->modifyDate(); }
0187         int trackNumber() const override { return m_track->trackNumber(); }
0188         int discNumber() const override { return m_track->discNumber(); }
0189 
0190         qreal replayGain( Meta::ReplayGainTag mode ) const override
0191                 { return m_track->replayGain( mode ); }
0192         QString type() const override { return m_track->type(); }
0193 
0194         void prepareToPlay() override { m_track->prepareToPlay(); }
0195         void finishedPlaying( double fraction ) override { m_track->finishedPlaying( fraction ); }
0196 
0197         bool inCollection() const override { return m_track->inCollection(); }
0198         Collections::Collection *collection() const override { return m_track->collection(); }
0199 
0200         QString cachedLyrics() const override { return m_track->cachedLyrics(); }
0201         void setCachedLyrics( const QString &lyrics ) override { m_track->setCachedLyrics( lyrics ); }
0202 
0203         //TODO: implement labels
0204         void addLabel( const QString &label ) override { Q_UNUSED( label ) }
0205         void addLabel( const Meta::LabelPtr &label ) override { Q_UNUSED( label ) }
0206         void removeLabel( const Meta::LabelPtr &label ) override { Q_UNUSED( label ) }
0207 
0208         Meta::TrackEditorPtr editor() override;
0209         Meta::StatisticsPtr statistics() override;
0210 
0211         // MemoryMeta::Track methods:
0212 
0213         /* All of these set* methods pass the pointer to AmarokSharedPointer (thus memory-manage it),
0214          * remove this track from previous {Album,Artist,Composer,Genre,Year} entity (if any)
0215          * and add this track to newly set entity. (if non-null)
0216          * All these methods are reentrant, but not thread-safe: caller must ensure that
0217          * only one of the following methods is called at a time on a single instance.
0218          */
0219         void setAlbum( Album *album );
0220         void setArtist( Artist *artist );
0221         void setComposer( Composer *composer );
0222         void setGenre( Genre *genre );
0223         void setYear( Year *year );
0224 
0225         /**
0226          * Return the original track this track proxies.
0227          */
0228         Meta::TrackPtr originalTrack() const { return m_track; }
0229 
0230         /**
0231          * Make notifyObservers() public so that MapChanger can call this
0232          */
0233         using Meta::Track::notifyObservers;
0234 
0235     private:
0236         Meta::TrackPtr m_track;
0237         Meta::AlbumPtr m_album;
0238         Meta::ArtistPtr m_artist;
0239         Meta::ComposerPtr m_composer;
0240         Meta::GenrePtr m_genre;
0241         Meta::YearPtr m_year;
0242 };
0243 
0244 /**
0245  * Helper class that facilitates adding, removing and changing tracks that are in
0246  * MemoryCollection. This class locks underlying MemoryCollection upon construction for
0247  * writing and releases the lock in destructor.
0248  *
0249  * Typical usage:
0250  * {
0251  *     MemoryMeta::MapChanger changer( memoryCollectionPtr );
0252  *     Meta::Track newTrack = changer.addTrack( trackPtr );
0253  *     ...
0254  *     changer.removeTrack( newTrack );
0255  * }
0256  *
0257  * All methods in this class are re-entrant and it operates on MemoryCollection in
0258  * a thread-safe way: you can run MapChangers from multiple threads on a single
0259  * MemoryCollection at once. (each thread constructing MapChanger when needed and deleting
0260  * it as soon as possible)
0261  *
0262  * All methods can be called multiple times on a single instance and can be combined.
0263  */
0264 class AMAROK_EXPORT MapChanger
0265 {
0266     public:
0267         explicit MapChanger( MemoryCollection *memoryCollection );
0268         ~MapChanger();
0269 
0270         /**
0271          * Adds a track to MemoryCollection by proxying it using @see MemoryMeta::Track
0272          * track artist, album, genre, composer and year are replaced in MemoryMeta::Track
0273          * by relevant MemoryMeta entities, based on their value. Refuses to add a track
0274          * whose proxy is already in MemoryCollection (returns null pointer in this case)
0275          *
0276          * @return pointer to a newly created MemoryMeta::Track (may be null if not
0277          * successful)
0278          */
0279         Meta::TrackPtr addTrack( Meta::TrackPtr track );
0280 
0281         /**
0282          * Removes a track from MemoryCollection. Pays attention to remove artists,
0283          * albums, genres, composers and years that may become dangling in
0284          * MemoryCollection.
0285          *
0286          * @param track MemoryMeta track to remove, it doesn't matter if this is the track
0287          * returned by MapChanger::addTrack or the underlying one passed to
0288          * MapChanger::addTrack - the real track is looked up using its uidUrl in
0289          * MemoryCollection.
0290          *
0291          * @return shared pointer to underlying track of the deleted track, i.e. the track
0292          * that you passed to MapChanger::addTrack() originally. May be null pointer if
0293          * @param track is not found in collection or if it wasn't added using MapChanger.
0294          */
0295         Meta::TrackPtr removeTrack( Meta::TrackPtr track );
0296 
0297         /**
0298          * Reflects changes made to underlying track to its proxy track in
0299          * MemoryCollection and to MemoryCollection maps.
0300          *
0301          * The one who called MapChanger::addTrack() is responsible to call this method
0302          * every time it detects that some metadata of underlying track have changed
0303          * (perhaps by becoming its observer), even in minor fields such as comment. This
0304          * method instructs proxy track to call notifyObservers().
0305          *
0306          * Please note that this method is currently unable to cope with changes
0307          * to track uidUrl(). If you really need it, change MemoryCollection's
0308          * trackMap manually _before_ calling this.
0309          *
0310          * @param track track whose metadata have changed, it doesn't matter if this is
0311          * the track returned by MapChanger::addTrack or the underlying one passed to
0312          * MapChanger::addTrack - the real track is looked up using its uidUrl in
0313          * MemoryCollection. Does nothing if @param track is not found in MemoryCollection.
0314          *
0315          * @return true if memory collection maps had to be changed, false for minor
0316          * changes where this is not required
0317          */
0318         bool trackChanged( Meta::TrackPtr track );
0319 
0320     private:
0321         /**
0322          * Worker for addTrack.
0323          *
0324          * @param track original underlying track - source of metadata
0325          * @param memoryTrack new track to add to MemoryCollection - target of metadata
0326          */
0327         Meta::TrackPtr addExistingTrack( Meta::TrackPtr track, Track *memoryTrack );
0328 
0329         /**
0330          * Return true if at least one of the tracks in @param needles is in
0331          * @param haystack, false otherwise. Comparison is done using track uidUrl.
0332          */
0333         static bool hasTrackInMap( const Meta::TrackList &needles, const TrackMap &haystack );
0334 
0335         /**
0336          * Return true if artist @param artist is referenced as albumArtist of one of the
0337          * albums from @param haystack. The match is done using Meta:ArtistPtr
0338          * operator==.
0339          */
0340         static bool referencedAsAlbumArtist( const Meta::ArtistPtr &artist, const AlbumMap &haystack );
0341 
0342         /**
0343          * Return true if @param first has different value than @param other. Specifically
0344          * returns true if one entity is null and the other non-null, but returns true if
0345          * both are null.
0346          */
0347         static bool entitiesDiffer( const Meta::Base *first, const Meta::Base *second );
0348         /**
0349          * Overload for albums, we compare album artist, isCollection and image too
0350          */
0351         static bool entitiesDiffer( const Meta::Album *first, const Meta::Album *second );
0352 
0353         MemoryCollection *m_mc;
0354 };
0355 
0356 }
0357 #endif