File indexing completed on 2025-01-05 04:26:56
0001 /**************************************************************************************** 0002 * Copyright (c) 2007 Maximilian Kossick <maximilian.kossick@googlemail.com> * 0003 * Copyright (c) 2008 Leo Franchi <lfranchi@kde.org> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify it under * 0006 * the terms of the GNU General Public License as published by the Free Software * 0007 * Foundation; either version 2 of the License, or (at your option) any later * 0008 * version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License along with * 0015 * this program. If not, see <http://www.gnu.org/licenses/>. * 0016 ****************************************************************************************/ 0017 0018 #ifndef AMAROK_LASTFMMETA_P_H 0019 #define AMAROK_LASTFMMETA_P_H 0020 0021 #include "core/support/Debug.h" 0022 0023 #include "amarokconfig.h" 0024 #include "core/meta/Meta.h" 0025 #include "core/support/Amarok.h" 0026 #include "core-impl/support/TagStatisticsStore.h" 0027 0028 #include <KIO/Job> 0029 0030 #include <QDir> 0031 #include <QImage> 0032 #include <QList> 0033 #include <QPixmap> 0034 #include <QStandardPaths> 0035 #include <QStringList> 0036 0037 #include <Track.h> 0038 #include <ws.h> 0039 #include <RadioTuner.h> 0040 #include <XmlQuery.h> 0041 0042 namespace LastFm 0043 { 0044 0045 class Track::Private : public QObject 0046 { 0047 Q_OBJECT 0048 0049 public: 0050 Track *t; 0051 lastfm::Track lastFmTrack; // this is how we love, ban, etc 0052 QUrl trackPath; 0053 QUrl lastFmUri; 0054 0055 QImage albumArt; 0056 QString artist; 0057 QString album; 0058 QString track; 0059 qint64 length; 0060 0061 //not sure what these are for but they exist in the LastFmBundle 0062 QString albumUrl; 0063 QString artistUrl; 0064 QString trackUrl; 0065 QString imageUrl; 0066 0067 Meta::ArtistPtr artistPtr; 0068 Meta::AlbumPtr albumPtr; 0069 Meta::GenrePtr genrePtr; 0070 Meta::ComposerPtr composerPtr; 0071 Meta::YearPtr yearPtr; 0072 0073 QNetworkReply* trackFetch; 0074 QNetworkReply* wsReply; 0075 0076 Meta::StatisticsPtr statsStore; 0077 uint currentTrackStartTime; 0078 0079 public: 0080 Private() 0081 : lastFmUri( QUrl() ) 0082 , currentTrackStartTime( 0 ) 0083 { 0084 artist = QString ( "Last.fm" ); 0085 } 0086 0087 ~Private() override 0088 { 0089 } 0090 0091 void notifyObservers(); 0092 0093 void setTrackInfo( const lastfm::Track &trackInfo ) 0094 { 0095 DEBUG_BLOCK 0096 bool newTrackInfo = artist != trackInfo.artist() || 0097 album != trackInfo.album() || 0098 track != trackInfo.title(); 0099 0100 0101 lastFmTrack = trackInfo; 0102 artist = trackInfo.artist(); 0103 album = trackInfo.album(); 0104 track = trackInfo.title(); 0105 length = trackInfo.duration() * 1000; 0106 trackPath = trackInfo.url(); 0107 0108 // need to reset other items 0109 albumUrl = ""; 0110 trackUrl = ""; 0111 albumArt = QImage(); 0112 0113 if( newTrackInfo ) 0114 { 0115 statsStore = new TagStatisticsStore( t ); 0116 currentTrackStartTime = QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); 0117 } 0118 0119 notifyObservers(); 0120 0121 if( !trackInfo.isNull() ) 0122 { 0123 QMap< QString, QString > params; 0124 params[ "method" ] = "track.getInfo"; 0125 params[ "artist" ] = artist; 0126 params[ "track" ] = track; 0127 0128 m_userFetch = lastfm::ws::post( params ); 0129 0130 connect( m_userFetch, SIGNAL( finished() ), SLOT( requestResult() ) ); 0131 } 0132 } 0133 0134 public Q_SLOTS: 0135 void requestResult( ) 0136 { 0137 if( !m_userFetch ) 0138 return; 0139 if( m_userFetch->error() == QNetworkReply::NoError ) 0140 { 0141 lastfm::XmlQuery lfm; 0142 if( lfm.parse( m_userFetch->readAll() ) ) 0143 { 0144 albumUrl = lfm[ "track" ][ "album" ][ "url" ].text(); 0145 trackUrl = lfm[ "track" ][ "url" ].text(); 0146 artistUrl = lfm[ "track" ][ "artist" ][ "url" ].text(); 0147 0148 notifyObservers(); 0149 0150 imageUrl = lfm[ "track" ][ "album" ][ "image size=large" ].text(); 0151 0152 if( !imageUrl.isEmpty() ) 0153 { 0154 KIO::Job* job = KIO::storedGet( QUrl( imageUrl ), KIO::Reload, KIO::HideProgressInfo ); 0155 connect( job, SIGNAL( result( KJob* ) ), this, SLOT( fetchImageFinished( KJob* ) ) ); 0156 } 0157 } 0158 else 0159 { 0160 debug() << "Got exception in parsing from last.fm:" << lfm.parseError().message(); 0161 return; 0162 } 0163 } 0164 0165 } 0166 0167 void fetchImageFinished( KJob* job ) 0168 { 0169 if( job->error() == 0 ) 0170 { 0171 const int size = 100; 0172 0173 QImage img = QImage::fromData( static_cast<KIO::StoredTransferJob*>( job )->data() ); 0174 if( !img.isNull() ) 0175 { 0176 img.scaled( size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); 0177 0178 albumArt = img; 0179 } 0180 else 0181 albumArt = QImage(); 0182 } 0183 else 0184 { 0185 //use default image 0186 albumArt = QImage(); 0187 } 0188 notifyObservers(); 0189 } 0190 0191 private: 0192 QNetworkReply* m_userFetch; 0193 0194 }; 0195 0196 // internal helper classes 0197 0198 class LastFmArtist : public Meta::Artist 0199 { 0200 public: 0201 explicit LastFmArtist( Track::Private *dptr ) 0202 : Meta::Artist() 0203 , d( dptr ) 0204 {} 0205 0206 Meta::TrackList tracks() override 0207 { 0208 return Meta::TrackList(); 0209 } 0210 0211 QString name() const override 0212 { 0213 if( d ) 0214 return d->artist; 0215 return QStringLiteral( "Last.fm" ); 0216 } 0217 0218 Track::Private * const d; 0219 0220 friend class Track::Private; 0221 }; 0222 0223 class LastFmAlbum : public Meta::Album 0224 { 0225 public: 0226 explicit LastFmAlbum( Track::Private *dptr ) 0227 : Meta::Album() 0228 , d( dptr ) 0229 {} 0230 0231 bool isCompilation() const override { return false; } 0232 bool hasAlbumArtist() const override { return false; } 0233 Meta::ArtistPtr albumArtist() const override { return Meta::ArtistPtr(); } 0234 0235 Meta::TrackList tracks() override 0236 { 0237 return Meta::TrackList(); 0238 } 0239 0240 QString name() const override 0241 { 0242 if( d ) 0243 return d->album; 0244 return QString(); 0245 } 0246 0247 QImage image( int size ) const override 0248 { 0249 if( !d || d->albumArt.isNull() ) 0250 { 0251 //return Meta::Album::image( size, withShadow ); 0252 //TODO implement shadow 0253 //TODO improve this 0254 0255 if ( size <= 1 ) 0256 size = 100; 0257 QString sizeKey = QString::number( size ) + '@'; 0258 0259 QImage image; 0260 QDir cacheCoverDir = QDir( Amarok::saveLocation( "albumcovers/cache/" ) ); 0261 if( cacheCoverDir.exists( sizeKey + "lastfm-default-cover.png" ) ) 0262 image = QImage( cacheCoverDir.filePath( sizeKey + "lastfm-default-cover.png" ) ); 0263 else 0264 { 0265 QImage orgImage = QImage( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/lastfm-default-cover.png" ) ); //optimize this! 0266 //scaled() does not change the original image but returns a scaled copy 0267 image = orgImage.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation ); 0268 image.save( cacheCoverDir.filePath( sizeKey + "lastfm-default-cover.png" ), "PNG" ); 0269 } 0270 0271 return image; 0272 } 0273 0274 0275 if( d->albumArt.width() != size && size > 0 ) 0276 return d->albumArt.scaled( size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); 0277 return d->albumArt; 0278 } 0279 0280 QUrl imageLocation( int size ) override 0281 { 0282 Q_UNUSED( size ); 0283 if( d && !d->imageUrl.isEmpty() ) 0284 return QUrl( d->imageUrl ); 0285 return QUrl(); 0286 } 0287 0288 // return true since we handle our own fetching 0289 bool hasImage( int size = 1 ) const override { Q_UNUSED( size ); return true; } 0290 0291 Track::Private * const d; 0292 0293 friend class Track::Private; 0294 }; 0295 0296 class LastFmGenre : public Meta::Genre 0297 { 0298 public: 0299 explicit LastFmGenre( Track::Private *dptr ) 0300 : Meta::Genre() 0301 , d( dptr ) 0302 {} 0303 0304 QString name() const override 0305 { 0306 return QString(); 0307 } 0308 0309 Meta::TrackList tracks() override 0310 { 0311 return Meta::TrackList(); 0312 } 0313 0314 Track::Private * const d; 0315 0316 friend class Track::Private; 0317 }; 0318 0319 class LastFmComposer : public Meta::Composer 0320 { 0321 public: 0322 explicit LastFmComposer( Track::Private *dptr ) 0323 : Meta::Composer() 0324 , d( dptr ) 0325 {} 0326 0327 QString name() const override 0328 { 0329 return QString(); 0330 } 0331 0332 Meta::TrackList tracks() override 0333 { 0334 return Meta::TrackList(); 0335 } 0336 0337 Track::Private * const d; 0338 0339 friend class Track::Private; 0340 }; 0341 0342 class LastFmYear : public Meta::Year 0343 { 0344 public: 0345 explicit LastFmYear( Track::Private *dptr ) 0346 : Meta::Year() 0347 , d( dptr ) 0348 {} 0349 0350 QString name() const override 0351 { 0352 return QString(); 0353 } 0354 0355 Meta::TrackList tracks() override 0356 { 0357 return Meta::TrackList(); 0358 } 0359 0360 Track::Private * const d; 0361 0362 friend class Track::Private; 0363 }; 0364 0365 void 0366 Track::Private::notifyObservers() 0367 { 0368 // TODO: only notify what actually has changed 0369 t->notifyObservers(); 0370 static_cast<LastFmAlbum *>( t->album().data() )->notifyObservers(); 0371 static_cast<LastFmArtist *>( t->artist().data() )->notifyObservers(); 0372 } 0373 0374 } 0375 0376 #endif