File indexing completed on 2024-05-05 04:48:17

0001 /****************************************************************************************
0002  * Copyright (c) 2007 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 AMAROK_META_PLAYLIST_H
0018 #define AMAROK_META_PLAYLIST_H
0019 
0020 #include "core/amarokcore_export.h"
0021 #include "core/meta/forward_declarations.h"
0022 
0023 #include <QList>
0024 #include <QMetaType>
0025 #include <QMutex>
0026 #include <QPixmap>
0027 #include <QSet>
0028 #include <QSharedData>
0029 #include <QString>
0030 #include <QTextStream>
0031 
0032 #include "AmarokSharedPointer.h"
0033 #include <QUrl>
0034 
0035 class QTextStream;
0036 
0037 class QAction;
0038 typedef QList<QAction *> QActionList;
0039 
0040 namespace Playlists
0041 {
0042     class Playlist;
0043     class PlaylistProvider;
0044 
0045     typedef AmarokSharedPointer<Playlist> PlaylistPtr;
0046     typedef QList<PlaylistPtr> PlaylistList;
0047 
0048     enum PlaylistCategory
0049     {
0050         UserPlaylist = 1,
0051         PodcastChannelPlaylist
0052     };
0053 
0054     /**
0055      * Subclass this class in order to be able to watch playlists as their metadata and
0056      * track list changes.
0057      */
0058     class AMAROKCORE_EXPORT PlaylistObserver
0059     {
0060         public:
0061             PlaylistObserver();
0062             virtual ~PlaylistObserver();
0063 
0064             /**
0065              * Subscribe to changes made by @p playlist. Does nothing if playlist is
0066              * null or if already subscribed.
0067              *
0068              * @param playlist the playlist
0069              *
0070              * This method is thread-safe.
0071              */
0072             void subscribeTo( PlaylistPtr playlist );
0073 
0074             /**
0075              * Unsubscribe from changes made by @p playlist. Does nothing if not yet
0076              * subscribed to playlist.
0077              *
0078              * @param playlist the playlist
0079              *
0080              * This method is thread-safe.
0081              */
0082             void unsubscribeFrom( PlaylistPtr playlist );
0083 
0084             /**
0085              * This method is called when playlist metadata (such as title) has changed.
0086              * This isn't called when just a list of tracks changes.
0087              *
0088              * @param playlist playlist whose metadata were changed
0089              *
0090              * @note this method may get called from non-main thread and must be
0091              * implemented in a thread-safe manner
0092              */
0093             virtual void metadataChanged( const PlaylistPtr &playlist );
0094 
0095             /**
0096              * This method is called when a track has been added to the playlist.
0097              *
0098              * @param playlist playlist whose track list was changed
0099              * @param track track that was added
0100              * @param position position where the track was inserted to, beginning from 0
0101              *
0102              * @note this method may get called from non-main thread and must be
0103              * implemented in a thread-safe manner
0104              */
0105             virtual void trackAdded( const PlaylistPtr &playlist, const Meta::TrackPtr &track, int position );
0106 
0107             /**
0108              * This method is called after a track is removed from to the playlist.
0109              *
0110              * @param playlist playlist whose track list was changed
0111              * @param position position occupied by the track right before it was removed
0112              *
0113              * @note this method may get called from non-main thread and must be
0114              * implemented in a thread-safe manner
0115              */
0116             virtual void trackRemoved( const PlaylistPtr &playlist, int position );
0117 
0118             /**
0119              * This method is called after loading of playlist is finished
0120              * (which was started by triggerTrackLoad()) and all tracks are already added.
0121              *
0122              * @param playlist playlist loading of which has finished
0123              *
0124              * @note this method may get called from non-main thread and must be
0125              * implemented in a thread-safe manner
0126              */
0127             virtual void tracksLoaded( PlaylistPtr playlist );
0128 
0129         private:
0130             QSet<PlaylistPtr> m_playlistSubscriptions;
0131             QMutex m_playlistSubscriptionsMutex; // guards access to m_playlistSubscriptions
0132     };
0133 
0134     class AMAROKCORE_EXPORT Playlist : public virtual QSharedData
0135     {
0136         public:
0137             Playlist();
0138             virtual ~Playlist();
0139 
0140             /**
0141              * @returns a unique identifier for a playlist. Should be similar to
0142              * Meta::Track::uidUrl
0143              */
0144             virtual QUrl uidUrl() const = 0;
0145 
0146             virtual QString name() const = 0;
0147             virtual QString prettyName() const { return name(); }
0148 
0149             virtual PlaylistProvider *provider() const { return nullptr; }
0150 
0151             virtual void setName( const QString &name );
0152 
0153             /**
0154              * Returns the number of tracks this playlist contains. -1 if tracks are not
0155              * yet loaded (call triggerTrackLoad() in this case). If you get non-negative
0156              * number, all tracks have been already loaded.
0157              */
0158             virtual int trackCount() const = 0;
0159 
0160             /**
0161              * Returns loaded tracks in this playlist. Note that the list may be incomplete,
0162              * to be sure, check that trackCount() is non-negative. Otherwise you have to
0163              * become playlist observer, watch for trackAdded() methods and call
0164              * triggerTrackLoad(). If you want to immediately play or
0165              * extract metadata of the tracks, be aware that many playlist implementations
0166              * initially return MetaProxy::Tracks that are resolved asynchronously.
0167              *
0168              * Convenient way to overcome the first and optionally the second
0169              * inconvenience is to use TrackLoader helper class.
0170              */
0171             virtual Meta::TrackList tracks() = 0;
0172 
0173             /**
0174              * Trigger full background loading of this playlist. Observer's trackAdded()
0175              * and metadataChanged() will be called as appropriate. This may even change
0176              * playlist metadata;
0177              *
0178              * Implementors, you should start a background job in this method to
0179              * actually load tracks, calling notifyObservers[Something]Added/Changed()
0180              * as appropriate.
0181              * It is guaranteed that tracksLoaded() observer method will be called
0182              * exactly once, either sooner (before returning from this method) or
0183              * later (asynchronously perhaps from a different thread).
0184              *
0185              * Implementors should also use MetaProxy::Track as a second-level
0186              * lazy-loading.
0187              *
0188              * Default implementation just calls notifyObserversTracksLoaded().
0189              */
0190             virtual void triggerTrackLoad();
0191 
0192             /**
0193              * Add the track to a certain position in the playlist
0194              *
0195              * @param track the track to add
0196              * @param position place to add this track. The default value -1 appends to
0197              *                 the end.
0198              *
0199              * @note if the position is larger then the size of the playlist append to the
0200              * end without generating an error.
0201              */
0202             virtual void addTrack( const Meta::TrackPtr &track, int position = -1 );
0203 
0204             /**
0205              * Remove track at the specified position
0206              */
0207             virtual void removeTrack( int position );
0208 
0209             /**
0210              * Sync track status between two tracks. This is only
0211              * useful for podcasts providers and some other exotic
0212              * playlist providers.
0213              */
0214             virtual void syncTrackStatus( int position, const Meta::TrackPtr &otherTrack );
0215 
0216             /**
0217              * A list of groups or labels this playlist belongs to.
0218              *
0219              * Can be used for grouping in folders (use ex. '/' as separator) or for
0220              * labels. Default implementation returns empty list.
0221              */
0222             virtual QStringList groups();
0223 
0224             /**
0225              * Labels the playlist as part of a group.
0226              *
0227              * In a folder-like hierarchy this means adding the playlist to the folder with
0228              * name groups.first(). If groups is empty that means removing all groups from
0229              * the playlist. Default implementation does nothing.
0230              */
0231             virtual void setGroups( const QStringList &groups );
0232 
0233             // FIXME: two methods below are a temporary solution
0234             // and should be removed after support of async loading will
0235             // added everywhere
0236             /**
0237              * Call this method to assure synchronously loading.
0238              * @note not all playlist implementations support asynchronous loading
0239              */
0240             QT_DEPRECATED void makeLoadingSync() { m_async = false; }
0241             /**
0242              * Allows to check if asynchronously loading is deactivated
0243              */
0244             bool isLoadingAsync() const { return m_async; }
0245 
0246         protected:
0247             /**
0248              * Implementations must call this when metadata such as title has changed. Do
0249              * not call this when just a list of track changes.
0250              *
0251              * @note calling this from (code called by) Playlist constructor is FORBIDDEN.
0252              *
0253              * TODO: find all occurrences where this should be called in Playlist subclasses
0254              * and add the call!
0255              */
0256             void notifyObserversMetadataChanged();
0257 
0258             /**
0259              * Implementations must call this when playlist loading started
0260              * by trriggerTrackLoad() is finished and all tracks are added.
0261              *
0262              * @note calling this from (code called by) Playlist constructor is FORBIDDEN.
0263              */
0264             void notifyObserversTracksLoaded();
0265 
0266             /**
0267              * Implementations must call this when a track is added to playlist
0268              *
0269              * @param track the track that was added
0270              * @param position is the actual new position of the added track, never negative
0271              * @note calling this from (code called by) Playlist constructor is FORBIDDEN.
0272              */
0273             void notifyObserversTrackAdded( const Meta::TrackPtr &track, int position );
0274 
0275             /**
0276              * Implementations must call this when a track is added to playlist
0277              *
0278              * @param position is the position where the track was before removal
0279              * @note calling this from (code called by) Playlist constructor is FORBIDDEN.
0280              */
0281             void notifyObserversTrackRemoved( int position );
0282 
0283         private:
0284             friend class PlaylistObserver; // so that it can call (un)subscribe()
0285             void subscribe( PlaylistObserver *observer );
0286             void unsubscribe( PlaylistObserver *observer );
0287 
0288             QSet<PlaylistObserver *> m_observers;
0289             /**
0290              * Guards access to m_observers. It would seem that QReadWriteLock would be
0291              * more efficient, but when it is locked for read, it cannot be relocked for
0292              * write, even if it is recursive. This can cause deadlocks, so it would be
0293              * never safe to lock it just for read.
0294              */
0295             QMutex m_observersMutex;
0296             bool m_async;
0297     };
0298 }
0299 
0300 Q_DECLARE_METATYPE( Playlists::PlaylistPtr )
0301 Q_DECLARE_METATYPE( Playlists::PlaylistList )
0302 
0303 #endif