Playlist architecture
---------------------
note: this is about the media sources playlist, *not* The Playlist as the playback queue has been called for historic reasons.

A playlist is an ordered list of tracks. The creation method of this list determines the type (or category):
* When created by the user themselves and manually editable it's a User Playlist.

* A Dynamic Playlist is generated based on some statistical rules. It's an amarok specific type that also reacts to changes made in the playback queue by the user.

* A Smart Playlist is the result of a query of the Collections. It doesn't change unless it's being re-generated at startup or at the user's request.

A Radio Channel has tracks that are not or only limited determined by the user but rather come in as a "stream" (high level). Implementation can be a single HTTP/RTSP URL like icecast or a pre-selected list of stream-able URLs such as with last.fm. The implementations can allow some control over track selection with functionality like last.fm's love/ban or tag selection like spotify.

* Podcast Channels are also playlists that are generated by parsing a podcast feed (RSS or atom). They only contain tracks of the PodcastEpisode type. In amarok they are displayed separately from the other playlists but internally they use the same code such as the ItemModels, PlaylistManager and the synchronization support.

[note] The Playlist base class currently lives in the Playlists:: namespace. It is to be moved to Meta::.

Asynchronous Track Loading
--------------------------
The tracks() method will return immediately, possibly with a list of tracks that were already in memory. A call to the tracks() method might also do a triggerTrackLoad() which will start the asynchronous loading of tracks in the background. PlaylistObserver::trackAdded() will be called for each track that is added additionally to the playlist. trackRemoved() can also be called when the previous tracklist is affected.
[note] automatic loading and async depends on the implementation.

PlaylistProvider
----------------
The PlaylistProvider base class is registered with PlaylistManager so it can act as a central point to distribute playlist to other Amarok components.
Playlist loading can also be made asynchronous in the implementations. PlaylistProvider::playlistCount() can return -1 to indicate a full load is required to calculate this. After calling playlists(), which can return an empty list, playlistAdded() signals might be emitted while the asynchronous loading is going on.
PlaylistProviders can offer the UI QActions for the provider, a specific playlist or a specific track in a playlist. Playlist also has these last 2 methods which in the default implementation call the provider's implementations. It's recommended to implement playlist and track actions in the provider.

Playlist Synchronization
------------------------
It's possible to set up a link between playlists of the same category but from different PlaylistProviders. PlaylistManager will try to bring those playlists in the same state i.e. the same tracks in the same order.
The synchronization logic is implemented in SyncedPlaylist, a virtual class created with a factory pattern so other syncing algorithms can be applied at runtime. An example where this could be used: GPodderProvider's playlists are representations of the state of data on a web-service (gpodder.net). It has certain restrictions but also features that go beyond regular playlist synchronization. GPodderProvider can create a SyncedPlaylist class that specifically handles podcasts and keeps the limitations of the service and PodcastChannels in mind.
[note] currently podcast synchronization uses the special function Playlist::syncTrackStatus() which should be replaced by the above mechanism to avoid implementation-at-high-level-creep.

A SynchronizedPlaylist will appear to the higher layers as one playlist which exists on multiple PlaylistProviders. In order to display the multiple copies to the user a QItemProxyModel is used by the UI.

The synchronization algorithm uses only those functions that are also needed by the regular playlist use cases. In theory Playlist implementations don't need to think about synchronization. In practice synchronization will only properly work with fully functional PlaylistProvider and Playlist implementations, which are desirable anyway.

PlaylistBrowserModel
--------------------
PlaylistBrowserModel is the base class for ItemModels used to display the various playlist types. Although all playlist can be displayed using this base class all categories have their own specialized implementation to implement setData(), dropMimeData() and category specific actions (ex. PodcastModel::refreshPodcasts()). Not that these are usually convenience version of Provider actions.
PlaylistBrowser model requests playlist for it's category from PlaylistManager who gathers them from the providers and group Synchronized playlists together in a SyncedPlaylist. PlaylistBrowserModel is a tree with playlist on the first level and tracks as it's children. In order to have playlists grouped by origin (i.e. PlaylistProvider) the PlaylistByProvider proxy.

Playlist Tracking by PlaybackQueue
----------------------------------
The PlaybackQueue can be made to track a single playlist's state. In general this just means that all current tracks of the playlist are appended to the queue and that it will act on trackAdded() signals. However, because the playback queue only has the upcoming tracks changes to tracks that have already been played are not possible. The distinction between a playlist and the queue has to be clear to the user.
Using playlist tracking it's possible to implement all modes of the old Playlist by creating categories implementing the behavior of the modes.

User Playlists
--------------
File based: XSPFPlaylist, M3UPlaylist, PLSPlaylist
Database based: SqlPlaylist

These playlists will load tracks using MetaProxy::Track which will start a worker thread to get a real track object using CollectionManager::trackForUrl(). It depends on the implementation and contents of the file which URL is used to resolve the track.
M3U and PLS by specification require their contents to be playable URLs. For most collections the playable URL and uidUrl won't match, so these tracks won't be playable until their "real" track is resolved.
XSPF does support storing of the uidUrl (identifier tag). Amarok always writes track's uidUrl to XSPF. The current playback queue state is also saved with XSPF to a dedicated file ($KDEHOME/share/apps/amarok/current.xspf).
The playlist implementation fills the proxy tracks with any tag information it has stored. This way a recently loaded playlist has usable content while loading tracks is done asynchronously.
If the playback queue is restored with invalid (empty) tracks, XSPFPlaylist, MetaProxy and CollectionManager::trackForUrl() are to be investigated.

Dynamic Playlists
-----------------
Using a custom UI the settings for selecting tracks are configured. The Dynamic Playlist will select tracks and make them available from the tracks() method. These tracks can be displayed to the user as a preview of the settings but are mainly used in the playback queue.
Using the Selector/Bias system other components and plugins can add track selection conditions or statistical bias options to the dynamic playlist generator.
[note] current implementation does not derive from Playlists::Playlist nor does it use a PlaylistProvider. It's a mode of the playback queue, not a loadable playlist.

Smart Playlists
---------------
The basis of a Smart Playlist is a QueryMaker query and some additional limiters such as track count and total file size (useful to fill a media player). The list of tracks is not kept between sessions so a smart playlist is generated when first accessed. To avoid unneeded recalculation an active smart playlist will only be regenerated when the user requests it or when the synchronization of a playlist requires an up-to-date list.

Podcast Channels
----------------
This type is used for both the local (SQL based) PodcastProvider, some media device implementations and the gpodder web service. In the local collection the utility class PodcastReader is used to parse a podcast feed and update the PodcastChannel implementation. The base classes of PodcastMeta.cpp are used as intermediary data storage for new Channels and Episodes that have not been saved in the PodcastProvider yet.

Radio Channels
--------------
The most common form of a radio channel is a HTTP stream. This implementation listens to signals from EngineController/PlaybackController/Phonon while playing the stream and determines when a metadata change represents a real track change. It will then push append the a new track with that metadata but the same url. This will not cause a real track change but rather a transition in the PlaybackController to the new playing track. It's a seamless progression in the PlaybackQueue as well.

Services like last.fm and spotify also provider Radio Channels but it's implementation in possible user interaction is implementation specific.

Random/Shuffle Playlist
-----------------------
This is a very simple randomizing playlist that takes the current play queue and shuffles the upcoming tracks at the start of a playing track. This way the user can see the next upcoming track already and change or remove them if desired. This playlist will respect the manual changes made to the playback order and not shuffle any of the tracks queued by the user.
The shuffling of tracks should be a visual animation to make it clear what is happening and be somewhat pleasing to watch.

Old Playlist Queue Manager Behavior
-----------------------------------

Implemented in PlaybackQueue.
