1 /****************************************************************************************
2  * Copyright (c) 2010-2012 Leo Franchi <lfranchi@kde.org>                               *
3  *                                                                                      *
4  * This program is free software; you can redistribute it and/or modify it under        *
5  * the terms of the GNU General Public License as published by the Free Software        *
6  * Foundation; either version 2 of the License, or (at your option) any later           *
7  * version.                                                                             *
8  *                                                                                      *
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
11  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
12  *                                                                                      *
13  * You should have received a copy of the GNU General Public License along with         *
14  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
15  ****************************************************************************************/
16 
17 
18 #ifndef ECHONEST_PLAYLIST_H
19 #define ECHONEST_PLAYLIST_H
20 
21 #include "echonest_export.h"
22 #include "Song.h"
23 
24 #include <QSharedData>
25 #include <QDebug>
26 #include "Artist.h"
27 #include <QtCore/QString>
28 #include "Catalog.h"
29 
30 class QNetworkReply;
31 class DynamicPlaylistData;
32 
33 namespace Echonest{
34 
35     typedef struct {
36         QByteArray session_id;
37         Artists banned_artists;
38         Artists favorited_artists;
39         SongList banned_songs;
40         SongList favorited_songs;
41         QVariantMap options;
42         QVariantMap ratingsMap;
43         // TODO favorites_map
44         // TODO constraints
45 
46     } SessionInfo;
47 
48     /**
49      * This encapsulates an Echo Nest dynamic playlist. It contains a playlist ID and
50      *  the current song, and can fetch the next song.
51      *
52      *  See http://developer.echonest.com/docs/v4/playlist.html
53      *   for more information
54      */
55     class ECHONEST_EXPORT DynamicPlaylist
56     {
57     public:
58         /**
59          * The types of playlist that can be generated. Artist plays songs for the given artist,
60          *  ArtistRadio takes into account similar artists, and ArtistDescription plays songs matching
61          *  the given description.
62          */
63         enum ArtistTypeEnum {
64             ArtistType,
65             ArtistRadioType,
66             ArtistDescriptionType,
67             CatalogType,
68             CatalogRadioType,
69             SongRadioType,
70             GenreRadioType
71         };
72 
73         /**
74          * Different ways to sort a generated playlist
75          */
76         enum SortingType {
77             SortTempoAscending,
78             SortTempoDescending,
79             SortDurationAscending,
80             SortDurationDescending,
81             SortArtistFamiliarityAscending,
82             SortArtistFamiliarityDescending,
83             SortArtistHotttnessAscending,
84             SortArtistHotttnessDescending,
85             SortSongHotttnesssAscending,
86             SortSongHotttnesssDescending,
87             SortLatitudeAscending,
88             SortLatitudeDescending,
89             SortLongitudeAscending,
90             SortLongitudeDescending,
91             SortModeAscending,
92             SortModeDescending,
93             SortKeyAscending,
94             SortKeyDescending,
95             SortLoudnessAscending,
96             SortLoudnessDescending,
97             SortEnergyAscending,
98             SortEnergyDescending,
99             SortDanceabilityAscending,
100             SortDanceabilityDescending,
101             SortAcousticnessAscending,
102             SortAcousticnessDescending,
103             SortSpeechinessAscending,
104             SortSpeechinessDescending,
105             SortLivenessAscending,
106             SortLivenessDescending,
107             SortValenceAscending,
108             SortValenceDescending
109         };
110 
111         /**
112          * Different ways of picking artists in Artist radios.
113          */
114         enum ArtistPick {
115             PickSongHotttnesssAscending,
116             PickTempoAscending,
117             PickDurationAscending,
118             PickLoudnessAscending,
119             PickModeAscending,
120             PickKeyAscending,
121             PickSongHotttnesssDescending,
122             PickTempoDescending,
123             PickDurationDescending,
124             PickLoudnessDescending,
125             PickModeDescending,
126             PickKeyDescending
127         };
128 
129         /**
130          * The various parameters that can be passed to the playlist building
131          *  functions.
132          */
133         enum PlaylistParam {
134             Type, /// The type of playlist to generate. Value is the DynamicPlaylist::ArtistTypeEnum enum
135             Format, /// Either xml (default) or xspf. If the result is xspf, the raw xspf playlist is returned, else the xml is parsed and exposed programmatically. If using XSPF, you must specify a catalog, the tracks bucket, and limit = true
136             Pick,   /// How the artists are picked for each artist in ArtistType playlists. Value is Playlist::ArtistPick enum value.
137             Variety, /// 0 < variety < 1        The maximum variety of artists to be represented in the playlist. A higher number will allow for more variety in the artists.
138             ArtistId, ///  ID(s) of seed artist(s) for the playlist
139             Artist, /// Artist names of seeds for playlist
140             ArtistSeedCatalog, /// ID of seed artist catalog for the playlist
141             SourceCatalog, /// ID of catalog (artist or song) for catalog type playlists
142             SongId, /// IDs of seed songs for the playlist
143             Description, /// Textual description for sort of songs that can be included in the playlist
144             Results, /// 0-100, how many sonsg to include in the playlist, default 15
145             MaxTempo, /// 0.0 < tempo < 500.0 (BPM)       The maximum tempo for any included songs
146             MinTempo, /// 0.0 < tempo < 500.0 (BPM)       the minimum tempo for any included songs
147             MaxDuration, /// 0.0 < duration < 3600.0 (seconds)       the maximum duration of any song on the playlist
148             MinDuration, /// 0.0 < duration < 3600.0 (seconds)       the minimum duration of any song on the playlist
149             MaxLoudness, /// -100.0 < loudness < 100.0 (dB)  the maximum loudness of any song on the playlist
150             MinLoudness, /// -100.0 < loudness < 100.0 (dB)  the minimum loudness of any song on the playlist
151             MinDanceability, /// 0 < danceability < 1  a measure of the minimum danceability of the song
152             MaxDanceability, /// 0 < danceability < 1  a measure of the maximum danceability of the song
153             MinEnergy, /// 0 < danceability < 1  a measure of the maximum energy of the song
154             MaxEnergy, /// 0 < danceability < 1  a measure of the maximum energy of the song
155             ArtistMaxFamiliarity, ///  0.0 < familiarity < 1.0 the maximum artist familiarity for songs in the playlist
156             ArtistMinFamiliarity, ///  0.0 < familiarity < 1.0 the minimum artist familiarity for songs in the playlist
157             ArtistMaxHotttnesss, ///  0.0 < hotttnesss < 1.0  the maximum hotttnesss for artists in the playlist
158             ArtistMinHotttnesss, ///  0.0 < hotttnesss < 1.0  the maximum hotttnesss for artists in the playlist
159             SongMaxHotttnesss, ///  0.0 < hotttnesss < 1.0  the maximum hotttnesss for songs in the playlist
160             SongMinHotttnesss, ///  0.0 < hotttnesss < 1.0  the maximum hotttnesss for songs in the playlist
161             ArtistMinLongitude, /// -180.0 < longitude < 180.0      the minimum longitude for the location of artists in the playlist
162             ArtistMaxLongitude, /// -180.0 < longitude < 180.0      the maximum longitude for the location of artists in the playlist
163             ArtistMinLatitude,  /// -90.0 < latitude < 90.0 the minimum latitude for the location of artists in the playlist
164             ArtistMaxLatitude, /// -90.0 < latitude < 90.0 the maximum latitude for the location of artists in the playlist
165             Mode, /// (minor, major) 0, 1     the mode of songs in the playlist
166             Key, /// (c, c-sharp, d, e-flat, e, f, f-sharp, g, a-flat, a, b-flat, b) 0 - 11  the key of songs in the playlist
167             SongInformation, /// what sort of song information should be returned. Should be an Echonest::SongInformation object
168             Sort, /// SortingType enum, the type of sorting to use,
169             Limit, /// true, false    if true songs will be limited to those that appear in the catalog specified by the id: bucket
170             Audio, /// true, false,  if true songs will be limited to those that have associated audio
171             DMCA, /// true, false    Only valid for dynamic playlists. Sets if playlist will follow DMCA rules (see web api doc for details)
172             ChainXSPF, /// true, false    If true, returns an xspf for this dynamic playlist with 2 items. The second item will be a link to the API call for the next track in the chain. Please note that this sidesteps libechonest's handling of the tracks.
173             Mood, /// A mood to limit this playlist to, for example "happy" or "sad". Multiples of this param are okay. See the method Artist::listTerms for details on what moods are currently available
174             Style, /// A style to limit this playlist to, for example "happy" or "sad". Multiples of this param are okay. See the method Artist::listTerms for details on what styles are currently available
175             Adventurousness, /// A value of 0 means no adventurousness, only known and preferred music will be played. A value of 1 means high adventurousness, mostly unknown music will be played. This parameter only applies to catalog and catalog-radio type playlists.
176             MoreLikeThis, /// When steering: Supply a song id to steer this session towards. Can be boosted from 0-5 like so: SO12341234^2. Default is 1
177             LessLikeThis, /// When steering: Supply a song id to steer this session away from. Can be boosted from 0-5 like so: SO12341234^2. Default is 1
178             TargetTempo, /// When steering: 0.0 < tempo < 500. (BPM). Target a desired tempo for the songs in this dynamic playlist session
179             TargetLoudness, /// When steering: -100. < loudness < 100. (BPM)dB. Target a desired loudness for the songs in this dynamic playlist session
180             TargetDanceability, /// When steering: 0.0 < danceability < 1. Target a desired danceability for the songs in this dynamic playlist session
181             TargetEnergy, /// When steering: 0.0 < energy < 1.Target a desired energy for the songs in this dynamic playlist session
182             TargetSongHotttnesss, /// When steering: 0.0 < song_hotttnesss < 1.Target a desired song_hotttnesss for the songs in this dynamic playlist session
183             TargetArtistHotttnesss, /// When steering: 0.0 < artist_hottttnesss < 1.Target a desired artist_hottttnesss for the songs in this dynamic playlist session
184             TargetArtistFamiliarity, /// When steering: 0.0 < artist_familiarity < 1.Target a desired energy for the artist_familiarity in this dynamic playlist session
185             SongType, /// Type of Song (atm: Live, Studio, Christmas, Acoustic, Electric)
186             Genre, /// Genre Parameter needed for GenreRadio Playlist
187             ArtistStartYearBefore,
188             ArtistStartYearAfter,
189             ArtistEndYearBefore,
190             ArtistEndYearAfter,
191             MaxAcousticness, /// 0 < acousticness < 1  a measure of the maximum acousticness of the song
192             MinAcousticness, /// 0 < acousticness < 1  a measure of the minimum acousticness of the song
193             MaxSpeechiness, /// 0 < speechiness < 1  a measure of the maximum speechiness of the song
194             MinSpeechiness, /// 0 < speechiness < 1  a measure of the minimum speechiness of the song
195             MaxLiveness, /// 0 < liveness < 1  a measure of the maximum liveness of the song
196             MinLiveness, /// 0 < liveness < 1  a measure of the minimum liveness of the song
197             MaxValence, /// 0 < valence < 1  a measure of the maximum valence of the song
198             MinValence, /// 0 < valence < 1  a measure of the minimum valence of the song
199             Distribution, /// focused or wandering
200             GenrePreset /// core-best, core-shuffled, in_rotation-best, in_rotation-shuffled, emerging-best or emerging-shuffled
201         };
202 
203         /**
204          * The types of feedback that can be used to steer a dynamic playlist
205          */
206         enum DynamicFeedbackParam {
207             BanArtist, /// Ban this artist from this dynamic session. [artist_id, track_id, song_id, "last"]
208             FavoriteArtist, /// Mark this artist as 'liked' for this session. [artist_id, track_id, song_id, "last"]
209             BanSong, /// Ban this song from this dynamic session. [track_id, song_id, "last"]
210             SkipSong, /// Mark this song as skipped by the user. Will not appear for the rest of the session. [track_id, song_id, "last"]
211             FavoriteSong, /// Mark this song as a favorite. [track_id, song_id, "last"]
212             PlaySong, /// Mark this song as played. Unneeded unless you want to pre-seed a station. [track_id, song_id, "last"]
213             UnplaySong, /// Remove a song from a dynamic session's history. Will not blacklist the song. [track_id, song_id, "last"]
214             RateSong, /// Rate the desired song. [track_id, song_id, "last"]^[0-10]. E.g: "last^3" or "TRTLKZV12E5AC92E11^5"
215         };
216 
217         /**
218          * The parameters that can be passed to GenrePreset
219          */
220         enum GenrePresetParam {
221             CoreBest,
222             CoreShuffled,
223             InRotationBest,
224             InRotationShuffled,
225             EmergingBest,
226             EmerginShuffled,
227         };
228 
229         typedef QPair< PlaylistParam, QVariant > PlaylistParamData;
230         typedef QVector< PlaylistParamData > PlaylistParams;
231 
232         typedef QPair< DynamicFeedbackParam, QByteArray > DynamicFeedbackParamData;
233         typedef QVector< DynamicFeedbackParamData > DynamicFeedback;
234 
235         typedef QPair< SongList, SongList > FetchPair;
236 
237         DynamicPlaylist();
238         virtual ~DynamicPlaylist();
239         DynamicPlaylist( const DynamicPlaylist& other );
240         DynamicPlaylist& operator=( const DynamicPlaylist& playlist );
241 
242         /**
243          * Start a dynamic playlist with the given parameters.
244          *  Once the QNetworkReply has finished, pass it to parseStart()
245          *
246          *  To fetch tracks, call fetchNextSong(). The info() method can be used
247          *   to extract session information
248          */
249         QNetworkReply* create( const PlaylistParams& params ) const;
250         void parseCreate( QNetworkReply* ) throw( ParseError );
251 
252         /**
253          * Retart a dynamic playlist with the given parameters.
254          *  Once the QNetworkReply has finished, pass it to parseStart()
255          *
256          * This is the same as start(), except it maintains the history from an
257          *  already-existing playing station.
258          */
259         QNetworkReply* restart( const PlaylistParams& params ) const;
260 
261         /**
262          * The session id of this dynamic playlist. If the playlist has ended, or has not been started,
263          *  the result is empty.
264          *
265          */
266         QByteArray sessionId() const;
267         void setSessionId( const QByteArray& id );
268 
269         /**
270          * The current song of this dynamic playlist. Once this song has been played,
271          *  or whenever is desired, call fetchNextSong() to get the next song.
272          */
273         Song currentSong() const;
274         void setCurrentSong( const Song& song );
275 
276         /**
277          * Queries The Echo Nest for the next playable song(s) in this
278          *  dynamic playlist.
279          *
280          * \param @results How many results to return, 1-5. This lets you
281          *                 reduce the calls if you need more than one. Default is 1.
282          * \param @lookahead The potential next songs (after the results)
283          *                   if there is no steering applied. 0-5, default of 0.
284          */
285         QNetworkReply* next( int results = 1, int lookahead = 0 ) const;
286 
287         /**
288          * Return the result of a dynamic/next API call. This will return two lists:
289          * the "next" list and the "lookahead" list. Consult the \ref next() docs
290          * and The Echo Nest documentation for more information.
291          */
292         FetchPair parseNext( QNetworkReply* reply ) throw( ParseError );
293 
294         /**
295          * Returns feedback to The Echo Nest for the currently playing dynamic
296          *  playlist.
297          *
298          * See The Echo Nest api documentation for complete details.
299          *
300          * \param feedback A list of feedback items to apply.
301          */
302         QNetworkReply* feedback(const DynamicFeedback& feedback) const;
303 
304         /**
305          * Parses the result of the feedback call.
306          *
307          * Will throw an exception if the return code is not successful (as other parse methods do).
308          */
309         void parseFeedback(QNetworkReply* reply) const throw( ParseError );
310 
311         /**
312          * Modifies the upcoming tracks in this dynamic playlist session by steering it.
313          *
314          * Steering is additive, and can be reset for a dynamic playlist by calling reset.
315          *
316          * \param steerParams The desired steering params. Only use the enum values that correspond to valid steering commands.
317          */
318         QNetworkReply* steer(const PlaylistParams& steerParams) const;
319 
320         /**
321          * Parses the result of the steer call.
322          *
323          * Will throw an exception if the return code is not successful (as other parse methods do).
324          */
325         void parseSteer(QNetworkReply* reply) const throw( ParseError );
326 
327         /**
328          * Returns a description of this dynamic playlist session
329          */
330         QNetworkReply* fetchInfo() const;
331         SessionInfo parseInfo( QNetworkReply* reply ) throw( ParseError );
332 
333         /**
334          * Deletes a currently active playlist session. A non-commercial API can have, at most 1,000 active playlist sessions.
335          */
336         QNetworkReply* deleteSession() const;
337         void parseDeleteSession(QNetworkReply* reply);
338 
339         /**
340          * Generate a static playlist, according to the desired criteria. Use parseXSPFPlaylist if
341          *  you pass format=xspf to \c staticPlaylist().
342          */
343         static QNetworkReply* staticPlaylist( const PlaylistParams& params );
344         static SongList parseStaticPlaylist( QNetworkReply* reply ) throw( ParseError );
345 
346         /**
347          * Parse an xspf playlist. Returns the full xspf content with no modifications.
348          */
349         static QByteArray parseXSPFPlaylist( QNetworkReply* reply ) throw( ParseError );
350 
351     private:
352         static QByteArray playlistParamToString( PlaylistParam param );
353         static QNetworkReply* generateInternal( const PlaylistParams& params, const QByteArray& type );
354         static QByteArray playlistSortToString(SortingType sorting);
355         static QByteArray playlistArtistPickToString(ArtistPick pick);
356         static QByteArray playlistGenrePresetToString(GenrePresetParam param);
357         static QByteArray dynamicFeedbackToString(DynamicFeedbackParam param);
358 
359         QSharedDataPointer<DynamicPlaylistData> d;
360     };
361 
362     ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::DynamicPlaylist& playlist);
363 
364 
365 } // namespace
366 
367 
368 Q_DECLARE_METATYPE( Echonest::DynamicPlaylist )
369 
370 #endif
371