File indexing completed on 2025-01-19 04:24:14
0001 /**************************************************************************************** 0002 * Copyright (c) 2009 Simon Esneault <simon.esneault@gmail.com> * 0003 * Copyright (c) 2010 Daniel Faust <hessijames@gmail.com> * 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 #define DEBUG_PREFIX "LabelsEngine" 0019 0020 #include "LabelsEngine.h" 0021 0022 #include "EngineController.h" 0023 #include "context/ContextObserver.h" 0024 #include "context/ContextView.h" 0025 #include "core/collections/Collection.h" 0026 #include "core/collections/QueryMaker.h" 0027 #include "core/meta/Meta.h" 0028 #include "core/support/Debug.h" 0029 #include "core-impl/collections/support/CollectionManager.h" 0030 0031 #include <KIO/Job> 0032 #include <KLocalizedString> 0033 0034 #include <QDomDocument> 0035 0036 using namespace Context; 0037 0038 LabelsEngine::LabelsEngine( QObject *parent, const QList<QVariant> &args ) 0039 : DataEngine( parent ) 0040 , ContextObserver( ContextView::self() ) 0041 { 0042 Q_UNUSED( args ) 0043 m_sources << "lastfm" ; 0044 0045 m_timeoutTimer.setInterval( 10000 ); 0046 m_timeoutTimer.setSingleShot( true ); 0047 connect( &m_timeoutTimer, SIGNAL(timeout()), this, SLOT(timeout()) ); 0048 0049 EngineController *engine = The::engineController(); 0050 0051 connect( engine, SIGNAL(trackChanged(Meta::TrackPtr)), 0052 this, SLOT(update()) ); 0053 connect( engine, SIGNAL(trackMetadataChanged(Meta::TrackPtr)), 0054 this, SLOT(update()) ); 0055 } 0056 0057 LabelsEngine::~LabelsEngine() 0058 { 0059 DEBUG_BLOCK 0060 } 0061 0062 QStringList 0063 LabelsEngine::sources() const 0064 { 0065 return m_sources; 0066 } 0067 0068 bool 0069 LabelsEngine::sourceRequestEvent( const QString &name ) 0070 { 0071 DEBUG_BLOCK 0072 0073 Collections::Collection *coll = CollectionManager::instance()->primaryCollection(); 0074 if( coll ) 0075 { 0076 Collections::QueryMaker *qm = coll->queryMaker(); 0077 qm->setAutoDelete( true ); 0078 qm->setQueryType( Collections::QueryMaker::Label ); 0079 m_allLabels.clear(); 0080 0081 connect( qm, SIGNAL(newResultReady(Meta::LabelList)), 0082 SLOT(resultReady(Meta::LabelList)), Qt::QueuedConnection ); 0083 connect( qm, SIGNAL(queryDone()), SLOT(dataQueryDone()) ); 0084 0085 qm->run(); 0086 } 0087 0088 update( name == "reload" ); 0089 0090 return true; 0091 } 0092 0093 void 0094 LabelsEngine::resultReady( const Meta::LabelList &labels ) 0095 { 0096 foreach( const Meta::LabelPtr &label, labels ) 0097 { 0098 if( !label->name().isEmpty() ) 0099 m_allLabels << label->name(); 0100 } 0101 } 0102 0103 void 0104 LabelsEngine::dataQueryDone() 0105 { 0106 DEBUG_BLOCK 0107 QVariant varAll; 0108 varAll.setValue< QStringList >( m_allLabels ); 0109 setData( "labels", "all", varAll ); 0110 } 0111 0112 void 0113 LabelsEngine::update( bool reload ) 0114 { 0115 Meta::TrackPtr track = The::engineController()->currentTrack(); 0116 0117 if( !track ) 0118 { 0119 removeAllData( "labels" ); 0120 m_artist.clear(); 0121 m_title.clear(); 0122 m_album.clear(); 0123 m_userLabels.clear(); 0124 m_webLabels.clear(); 0125 setData( "labels", "state", "stopped" ); 0126 return; 0127 } 0128 0129 const QString title = track->name(); 0130 Meta::ArtistPtr artist = track->artist(); 0131 if( !artist ) 0132 { 0133 setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); 0134 debug() << "track has no artist, returning"; 0135 return; 0136 } 0137 0138 QStringList userLabels; 0139 0140 foreach( const Meta::LabelPtr &label, track->labels() ) 0141 userLabels += label->name(); 0142 0143 userLabels.sort(); 0144 m_userLabels.sort(); 0145 0146 // check what changed 0147 if( !reload && artist->name() == m_artist && title == m_title && userLabels == m_userLabels ) 0148 { 0149 // nothing important changed 0150 return; 0151 } 0152 else if( !reload && artist->name() == m_artist && title == m_title ) 0153 { 0154 // only the labels changed - no download necessary 0155 debug() << "only the labels changed - no download necessary"; 0156 QVariant varUser; 0157 varUser.setValue< QStringList >( userLabels ); 0158 setData( "labels", "user", varUser ); 0159 m_userLabels = userLabels; 0160 return; 0161 } 0162 0163 removeAllData( "labels" ); 0164 setData( "labels", "state", "started" ); 0165 0166 m_artist = artist->name(); 0167 m_title = title; 0168 if( track->album() ) 0169 m_album = track->album()->name(); 0170 else 0171 m_album.clear(); 0172 0173 m_userLabels = userLabels; 0174 m_webLabels.clear(); 0175 0176 QVariant varUser; 0177 varUser.setValue< QStringList >( m_userLabels ); 0178 setData( "labels", "user", varUser ); 0179 0180 m_try = 0; 0181 fetchLastFm(); 0182 } 0183 0184 void 0185 LabelsEngine::fetchLastFm() 0186 { 0187 QStringList separators; 0188 QString currentArtist; 0189 QString currentTitle; 0190 0191 if( m_title.isEmpty() || m_artist.isEmpty() ) 0192 { 0193 // stop timeout timer 0194 m_timeoutTimer.stop(); 0195 setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); 0196 debug() << "current track is invalid, returning"; 0197 return; 0198 } 0199 0200 if( m_try == 0 ) 0201 { 0202 currentArtist = m_artist; 0203 currentTitle = m_title; 0204 m_timeoutTimer.start(); 0205 } 0206 else if( m_try == 1 ) 0207 { 0208 currentArtist = m_artist; 0209 currentTitle = m_title; 0210 separators.clear(); 0211 separators << " (" << " [" << " - " << " featuring " << " feat. " << " feat " << " ft. " << " ft " << "/"; 0212 foreach( const QString &separator, separators ) 0213 { 0214 if( m_title.contains(separator,Qt::CaseInsensitive) ) 0215 { 0216 currentTitle = m_title.left( m_title.indexOf(separator,0,Qt::CaseInsensitive) ); 0217 break; 0218 } 0219 } 0220 if ( currentTitle == m_title ) 0221 { 0222 debug() << "try 2: title is the same, retrying"; 0223 m_try++; 0224 fetchLastFm(); 0225 return; 0226 } 0227 } 0228 else if( m_try == 2 ) 0229 { 0230 currentArtist = m_artist; 0231 currentTitle = m_title; 0232 separators.clear(); 0233 separators << " vs. " << " vs " << " featuring " << " feat. " << " feat " << " ft. " << " ft " << ", " << " and " << " & " << "/"; 0234 foreach( const QString &separator, separators ) 0235 { 0236 if( m_artist.contains(separator,Qt::CaseInsensitive) ) 0237 { 0238 currentArtist = m_artist.left( m_artist.indexOf(separator,0,Qt::CaseInsensitive) ); 0239 break; 0240 } 0241 } 0242 separators.clear(); 0243 separators << " (" << " [" << " - " << " featuring " << " feat. " << " feat " << " ft. " << " ft " << "/"; 0244 foreach( const QString &separator, separators ) 0245 { 0246 if( m_title.contains(separator,Qt::CaseInsensitive) ) 0247 { 0248 currentTitle = m_title.left( m_title.indexOf(separator,0,Qt::CaseInsensitive) ); 0249 break; 0250 } 0251 } 0252 if( currentArtist == m_artist ) // the title got modified the same way as on the last try 0253 { 0254 // stop timeout timer 0255 m_timeoutTimer.stop(); 0256 setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); 0257 debug() << "try 3: artist and title are the same, returning"; 0258 return; 0259 } 0260 } 0261 else 0262 { 0263 // shouldn't happen 0264 // stop timeout timer 0265 m_timeoutTimer.stop(); 0266 setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); 0267 debug() << "try > 2, returning"; 0268 return; 0269 } 0270 0271 if( !currentArtist.isEmpty() && !currentTitle.isEmpty() ) 0272 { 0273 setData( "labels", "message", "fetching"); 0274 // send the artist and title actually used for searching labels 0275 setData( "labels", "artist", currentArtist ); 0276 setData( "labels", "title", currentTitle ); 0277 setData( "labels", "album", m_album ); 0278 0279 // Query lastfm 0280 QUrl lastFmUrl; 0281 lastFmUrl.setScheme( "http" ); 0282 lastFmUrl.setHost( "ws.audioscrobbler.com" ); 0283 lastFmUrl.setPath( "/2.0/" ); 0284 lastFmUrl.addQueryItem( "method", "track.gettoptags" ); 0285 lastFmUrl.addQueryItem( "api_key", "402d3ca8e9bc9d3cf9b85e1202944ca5" ); 0286 lastFmUrl.addQueryItem( "artist", currentArtist.toLocal8Bit() ); 0287 lastFmUrl.addQueryItem( "track", currentTitle.toLocal8Bit() ); 0288 m_lastFmUrl = lastFmUrl; 0289 0290 QNetworkRequest req( lastFmUrl ); 0291 // req.setAttribute( QNetworkRequest::ConnectionEncryptedAttribute, QNetworkRequest::AlwaysNetwork ); 0292 The::networkAccessManager()->get( req ); 0293 The::networkAccessManager()->getData( lastFmUrl, this, 0294 SLOT(resultLastFm(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); 0295 } 0296 else 0297 { 0298 // stop timeout timer 0299 m_timeoutTimer.stop(); 0300 setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); 0301 debug() << "artist or track empty"; 0302 } 0303 } 0304 0305 void LabelsEngine::resultLastFm( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) 0306 { 0307 DEBUG_BLOCK; 0308 0309 if( m_lastFmUrl != url ) 0310 { 0311 debug() << "urls not matching, returning"; 0312 return; 0313 } 0314 0315 if( e.code != QNetworkReply::NoError ) 0316 { 0317 // stop timeout timer 0318 m_timeoutTimer.stop(); 0319 setData( "labels", "message", i18n( "Unable to retrieve from Last.fm" ) ); 0320 debug() << "Unable to retrieve last.fm information: " << e.description; 0321 return; 0322 } 0323 0324 QDomDocument xmlDoc; 0325 xmlDoc.setContent( data ); 0326 const QDomElement topElement = xmlDoc.elementsByTagName("toptags").at(0).toElement(); 0327 const QDomNodeList xmlNodeList = topElement.elementsByTagName( "tag" ); 0328 0329 for( uint i = 0; i < xmlNodeList.length(); i++ ) 0330 { 0331 // Get all the information 0332 const QDomElement nd = xmlNodeList.at( i ).toElement(); 0333 const QDomElement nameElement = nd.elementsByTagName("name").at(0).toElement(); 0334 const QString name = nameElement.text().toLower(); 0335 const QDomElement countElement = nd.elementsByTagName("count").at(0).toElement(); 0336 const int count = countElement.text().toInt(); 0337 m_webLabels.insert( name, count ); 0338 } 0339 0340 if( m_webLabels.isEmpty() ) 0341 { 0342 if( m_try < 2 ) 0343 { 0344 m_try++; 0345 fetchLastFm(); 0346 } 0347 else 0348 { 0349 // stop timeout timer 0350 m_timeoutTimer.stop(); 0351 setData( "labels", "message", i18n( "No labels found on Last.fm" ) ); 0352 } 0353 } 0354 else 0355 { 0356 // remove previous message 0357 removeData( "labels", "message" ); 0358 // stop timeout timer 0359 m_timeoutTimer.stop(); 0360 0361 QVariant varWeb; 0362 varWeb.setValue< QMap< QString, QVariant > > ( m_webLabels ); 0363 setData( "labels", "web", varWeb ); 0364 } 0365 } 0366 0367 void LabelsEngine::timeout() 0368 { 0369 setData( "labels", "message", i18n( "No connection to Last.fm" ) ); 0370 } 0371 0372 0373