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