1 /*
2    Copyright 2009-2010 Last.fm Ltd.
3       - Primarily authored by Max Howell, Jono Cole, Doug Mansell and Michael Coffey
4 
5    This file is part of liblastfm.
6 
7    liblastfm is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11 
12    liblastfm is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with liblastfm.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 #ifndef LASTFM_TRACK_H
21 #define LASTFM_TRACK_H
22 
23 #include <QDateTime>
24 #include <QExplicitlySharedDataPointer>
25 #include <QPointer>
26 
27 #include "Album.h"
28 
29 namespace lastfm {
30 
31 class TrackData;
32 
33 class LASTFM_DLLEXPORT TrackContext
34 {
35 public:
36     enum Type
37     {
38         UnknownType,
39         User,
40         Friend,
41         Neighbour,
42         Artist
43     };
44 
45     TrackContext();
46     TrackContext( const QString& type, const QList<QString>& values );
47     TrackContext( const TrackContext& that );
48     ~TrackContext();
49 
50     Type type() const;
51     QList<QString> values() const;
52     TrackContext& operator=( const TrackContext& that );
53 
54 private:
55     class TrackContextPrivate * const d;
56 };
57 
58 
59 /** Our track type. It's quite good, you may want to use it as your track type
60   * in general. It is explicitly shared. Which means when you make a copy, they
61   * both point to the same data still. This is like Qt's implicitly shared
62   * classes, eg. QString, however if you mod a copy of a QString, the copy
63   * detaches first, so then you have two copies. Our Track object doesn't
64   * detach, which is very handy for our usage in the client, but perhaps not
65   * what you want. If you need a deep copy for eg. work in a thread, call
66   * clone(). */
67 class LASTFM_DLLEXPORT Track : public AbstractType
68 {
69 public:
70     enum Source
71     {
72         // DO NOT UNDER ANY CIRCUMSTANCES CHANGE THE ORDER OR VALUES OF THIS ENUM!
73         // you will cause broken settings and b0rked scrobbler cache submissions
74 
75         UnknownSource = 0,
76         LastFmRadio,
77         Player,
78         MediaDevice,
79         NonPersonalisedBroadcast, // eg Shoutcast, BBC Radio 1, etc.
80         PersonalisedRecommendation // eg Pandora, but not Last.fm
81     };
82 
83     enum LoveStatus
84     {
85         UnknownLoveStatus = 0,
86         Loved,
87         Unloved
88     };
89 
90     enum ScrobbleStatus
91     {
92         Null = 0,
93         Cached,
94         Submitted,
95         Error
96     };
97 
98     enum Corrections
99     {
100         Original = 0,
101         Corrected
102     };
103 
104     enum ScrobbleError
105     {
106         None = 0,
107         FilteredArtistName = 113,
108         FilteredTrackName = 114,
109         FilteredAlbumName = 115,
110         FilteredTimestamp = 116,
111         ExceededMaxDailyScrobbles = 118,
112         InvalidStreamAuth = 119,
113         Invalid = 300
114     };
115 
116     Track();
117     explicit Track( const QDomElement& );
118     Track( const Track& that );
119     ~Track();
120 
121     Track clone() const;
122 
123     /** this track and that track point to the same object, so they are the same
124       * in fact. This doesn't do a deep data comparison. So even if all the
125       * fields are the same it will return false if they aren't in fact spawned
126       * from the same initial Track object */
127     bool sameObject( const Track& that );
128     bool operator==( const Track& that ) const;
129     bool operator!=( const Track& that ) const;
130     Track& operator=( const Track& that );
131 
132     /**
133      * Track's private class emits three signals that may be useful for
134      * applications:
135      *
136      *   loveToggled( bool love )
137      *   scrobbleStatusChanged( short scrobbleStatus )
138      *   corrected( QString correction )
139      *
140      * signalProxy() lets applications connect to them.
141      * */
142     const QObject* signalProxy() const;
143 
144     /** only a Track() is null */
145     bool isNull() const;
146 
147     bool corrected() const;
148 
149     Artist artist( Corrections corrected = Original ) const;
150     Artist albumArtist( Corrections corrected = Original ) const;
151     Album album( Corrections corrected = Original ) const;
152     QString title( Corrections corrected = Original ) const;
153 
154     uint trackNumber() const;
155     uint duration() const; // in seconds
156     Mbid mbid() const;
157     QUrl url() const;
158     QDateTime timestamp() const;
159     Source source() const;
160     uint fingerprintId() const;
161     bool isLoved() const;
162     LoveStatus loveStatus() const;
163     QUrl imageUrl( ImageSize size, bool square ) const;
164 
165     QString durationString() const;
166     static QString durationString( int seconds );
167 
168     ScrobbleStatus scrobbleStatus() const;
169     ScrobbleError scrobbleError() const;
170     QString scrobbleErrorText() const;
171 
172     /** default separator is an en-dash */
173     QString toString() const;
174     QString toString( Corrections corrections ) const;
175     QString toString( const QChar& separator, Corrections corrections = Original ) const;
176     /** the standard representation of this object as an XML node */
177     QDomElement toDomElement( class QDomDocument& ) const;
178 
179     TrackContext context() const;
180 
181     // iTunes tracks might be podcasts or videos
182     bool isPodcast() const;
183     bool isVideo() const;
184 
185     QString extra( const QString& key ) const;
186 
187     bool operator<( const Track &that ) const;
188 
189     bool isMp3() const;
190 
191     operator QVariant() const;
192 
193 //////////// lastfm::Ws
194 
195     /** See last.fm/api Track section */
196     QNetworkReply* share( const QStringList& recipients, const QString& message = "", bool isPublic = true ) const;
197 
198     QNetworkReply* getSimilar( int limit = -1 ) const;
199     /** The match percentage is returned from last.fm as a 4 significant
200       * figure floating point value. So we multply it by 100 to make an
201       * integer in the range of 0 to 10,000. This is possible confusing
202       * for you, but I felt it best not to lose any precision, and floats
203       * aren't much fun. */
204     static QMap<int, QPair< QString, QString > > getSimilar( QNetworkReply* );
205 
206     /** you can get any QNetworkReply TagList using Tag::list( QNetworkReply* ) */
207     QNetworkReply* getTags() const; // for the logged in user
208     QNetworkReply* getTopTags() const;
209     QNetworkReply* getTopFans() const;
210 
211     /** method should be a method name of reciever that takes a QByteArray
212     If that fails it will try invoking method with no arguments.
213     */
214     void getInfo( QObject* receiver, const char * method, const QString& username = "" ) const;
215     QNetworkReply* getBuyLinks( const QString& country ) const;
216 
217     static QNetworkReply* playlinks( const QList<Track>& tracks );
218 
219     /** you can only add 10 tags, we submit everything you give us, but the
220       * docs state 10 only. Will return 0 if the list is empty. */
221     QNetworkReply* addTags( const QStringList& ) const;
222     /** will return 0 if the string is "" */
223     QNetworkReply* removeTag( const QString& ) const;
224 
225     /** scrobble the track */
226     QNetworkReply* updateNowPlaying() const;
227     QNetworkReply* updateNowPlaying( int duration ) const;
228     QNetworkReply* removeNowPlaying() const;
229     QNetworkReply* scrobble() const;
230     static QNetworkReply* scrobble(const QList<lastfm::Track>& tracks);
231 
232     /** the url for this track's page at last.fm */
233     QUrl www() const;
234 
235 protected:
236     QExplicitlySharedDataPointer<TrackData> d;
237     QMap<QString, QString> params( const QString& method, bool use_mbid = false ) const;
238 };
239 
240 
241 
242 /** This class allows you to change Track objects, it is easy to use:
243   * MutableTrack( some_track_object ).setTitle( "Arse" );
244   *
245   * We have a separate MutableTrack class because in our usage, tracks
246   * only get mutated once, and then after that, very rarely. This pattern
247   * encourages such usage, which is generally sensible. You can feel more
248   * comfortable that the data hasn't accidently changed behind your back.
249   */
250 class LASTFM_DLLEXPORT MutableTrack : public Track
251 {
252 public:
253     MutableTrack();
254 
255     /** NOTE that passing a Track() to this ctor will automatically make it non
256       * null. Which may not be what you want. So be careful
257       * Rationale: this is the most maintainable way to do it
258       */
259     MutableTrack( const Track& that );
260 
261     void setFromLfm( const XmlQuery& lfm );
262     void setImageUrl( ImageSize size, const QString& url );
263 
264     void setArtist( QString artist );
265     void setAlbumArtist( QString albumArtist );
266     void setAlbum( QString album );
267     void setTitle( QString title );
268     void setCorrections( QString title, QString album, QString artist, QString albumArtist );
269     void setTrackNumber( uint n );
270     void setDuration( uint duration );
271     void setUrl( QUrl url );
272     void setSource( Source s );
273     void setLoved( bool loved );
274 
275     void setMbid( Mbid id );
276     void setFingerprintId( uint id );
277 
278     void setScrobbleStatus( ScrobbleStatus scrobbleStatus );
279     void setScrobbleError( ScrobbleError scrobbleError );
280     void setScrobbleErrorText( const QString& scrobbleErrorText );
281 
282     void love();
283     void unlove();
284     QNetworkReply* ban();
285 
286     void stamp();
287 
288     void setExtra( const QString& key, const QString& value );
289     void removeExtra( QString key );
290     void setTimeStamp( const QDateTime& dt );
291 
292     void setContext( TrackContext context );
293 
294     // iTunes tracks might be podcasts or videos
295     void setPodcast( bool podcast );
296     void setVideo( bool video );
297 
298 };
299 
300 
301 } //namespace lastfm
302 
303 
304 LASTFM_DLLEXPORT QDebug operator<<( QDebug d, const lastfm::Track& t );
305 
306 Q_DECLARE_METATYPE( lastfm::Track )
307 
308 #endif //LASTFM_TRACK_H
309