1 /***************************************************************************
2 * copyright : (C) 2006 Chris Muehlhaeuser <chris@chris.de> *
3 * : (C) 2006 Seb Ruiz <ruiz@kde.org> *
4 * : (C) 2006 Ian Monroe <ian@monroe.nu> *
5 * : (C) 2006 Mark Kretschmann <markey@web.de> *
6 **************************************************************************/
8 /***************************************************************************
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
15 ***************************************************************************/
17 #define DEBUG_PREFIX "LastFm"
21 #include "amarokconfig.h" //last.fm username and passwd
22 #include "amarok.h" //APP_VERSION, actioncollection
23 #include "collectiondb.h"
25 #include "enginecontroller.h"
27 #include "ContextStatusBar.h" //showError()
30 #include <Q3ValueList>
32 #include <QDomDocument>
33 #include <QDomElement>
39 #include <KApplication>
40 #include <KCodecs> //md5sum
44 #include <KMessageBox>
45 #include <KProtocolManager>
53 using namespace LastFm
;
55 ///////////////////////////////////////////////////////////////////////////////
57 // AmarokHttp is a hack written so that lastfm code could easily use something proxy aware.
58 // DO NOT use this class for anything else, use KIO directly instead.
59 ////////////////////////////////////////////////////////////////////////////////
60 AmarokHttp::AmarokHttp ( const QString
& hostname
, quint16 port
,
63 m_hostname( hostname
),
68 AmarokHttp::get ( const QString
& path
)
70 QString uri
= QString( "http://%1:%2/%3" )
76 m_error
= Q3Http::NoError
;
77 m_state
= Q3Http::Connecting
;
78 KIO::TransferJob
*job
= KIO::get(uri
, KIO::Reload
, KIO::HideProgressInfo
);
79 connect(job
, SIGNAL(data(KIO::Job
*, const QByteArray
&)),
80 this, SLOT(slotData(KIO::Job
*, const QByteArray
&)));
81 connect(job
, SIGNAL(result(KJob
*)),
82 this, SLOT(slotResult(KJob
*)));
88 AmarokHttp::state() const
94 AmarokHttp::readAll ()
106 AmarokHttp::slotData(KIO::Job
*, const QByteArray
& data
)
108 if( data
.size() == 0 ) {
111 else if ( m_result
.size() == 0 ) {
115 m_result
.resize( m_result
.size() + data
.size() );
116 memcpy( m_result
.end(), data
.data(), data
.size() );
121 AmarokHttp::slotResult(KJob
* job
)
123 bool err
= job
->error();
124 if( err
|| m_error
!= Q3Http::NoError
) {
125 m_error
= Q3Http::UnknownError
;
128 m_error
= Q3Http::NoError
;
131 m_state
= Q3Http::Unconnected
;
132 emit( requestFinished( 0, err
) );
137 ///////////////////////////////////////////////////////////////////////////////
139 ////////////////////////////////////////////////////////////////////////////////
141 Controller
*Controller::s_instance
= 0;
143 Controller::Controller()
144 : QObject( EngineController::instance() )
147 setObjectName( "lastfmController" );
148 KActionCollection
* ac
= Amarok::actionCollection();
149 KAction
*action
= new KAction( KIcon( Amarok::icon( "remove" ) ), i18n( "Ban" ), ac
);
150 connect( action
, SIGNAL( triggered( bool ) ), this, SLOT( ban() ) );
151 action
->setShortcut( QKeySequence( Qt::CTRL
| Qt::Key_B
) );
152 m_actionList
.append( action
);
154 action
= new KAction( KIcon( Amarok::icon( "love" ) ), i18n( "Love" ), ac
);
155 connect( action
, SIGNAL( triggered( bool ) ), this, SLOT( love() ) );
156 action
->setShortcut( QKeySequence( Qt::CTRL
| Qt::Key_L
) );
157 m_actionList
.append( action
);
159 action
= new KAction( KIcon( Amarok::icon( "next" ) ), i18n( "Skip" ), ac
);
160 connect( action
, SIGNAL( triggered( bool ) ), this, SLOT( skip() ) );
161 action
->setShortcut( QKeySequence( Qt::CTRL
| Qt::Key_K
) );
162 m_actionList
.append( action
);
163 setActionsEnabled( false );
168 Controller::instance()
170 if( !s_instance
) s_instance
= new Controller();
176 Controller::getNewProxy( QString genreUrl
)
180 m_genreUrl
= genreUrl
;
182 if ( m_service
) playbackStopped();
184 m_service
= new WebService( this );
186 if( checkCredentials() )
188 QString user
= AmarokConfig::scrobblerUsername();
189 QString pass
= AmarokConfig::scrobblerPassword();
191 if( !user
.isEmpty() && !pass
.isEmpty() &&
192 m_service
->handshake( user
, pass
) )
194 bool ok
= m_service
->changeStation( m_genreUrl
);
195 if( ok
) // else playbackStopped()
197 if( !AmarokConfig::submitPlayedSongs() )
198 m_service
->enableScrobbling( false );
199 setActionsEnabled( true );
200 return KUrl( m_service
->proxyUrl() );
205 // Some kind of failure happened, so crap out
212 Controller::playbackStopped() //SLOT
214 setActionsEnabled( false );
222 Controller::checkCredentials() //static
224 if( AmarokConfig::scrobblerUsername().isEmpty() || AmarokConfig::scrobblerPassword().isEmpty() )
226 LoginDialog
dialog( 0 );
227 dialog
.setCaption( "last.fm" );
228 return dialog
.exec() == QDialog::Accepted
;
235 Controller::createCustomStation() //static
238 CustomStationDialog
dialog( 0 );
240 if( dialog
.exec() == QDialog::Accepted
)
242 token
= dialog
.text();
274 Controller::setActionsEnabled( bool enable
)
275 { //pausing last.fm streams doesn't do anything good
276 Amarok::actionCollection()->action( "play_pause" )->setEnabled( !enable
);
277 Amarok::actionCollection()->action( "pause" )->setEnabled( !enable
);
280 for( action
= m_actionList
.first(); action
; action
= m_actionList
.next() )
281 action
->setEnabled( enable
);
284 /// return a translatable description of the station we are connected to
286 Controller::stationDescription( QString url
)
288 if( url
.isEmpty() && instance() && instance()->isPlaying() )
289 url
= instance()->getService()->currentStation();
291 if( url
.isEmpty() ) return QString();
293 QStringList elements
= url
.split( '/' );
296 // eg: lastfm://globaltag/rock
297 if ( elements
[1] == "globaltags" )
298 return i18n( "Global Tag Radio: %1", elements
[2] );
301 if ( elements
[1] == "artist" )
303 // eg: lastfm://artist/Queen/similarartists
304 if ( elements
[3] == "similarartists" )
305 return i18n( "Similar Artists to %1", elements
[2] );
307 if ( elements
[3] == "fans" )
308 return i18n( "Artist Fan Radio: %1", elements
[2] );
312 if ( elements
[1] == "artistnames" )
314 // eg: lastfm://artistnames/genesis,pink floyd,queen
316 // turn "genesis,pink floyd,queen" into "Genesis, Pink Floyd, Queen"
317 QString artists
= elements
[2];
318 artists
.replace( ",", ", " );
319 const QStringList words
= QString( artists
).remove( "," ).split( " " );
320 foreach( const QString
&word
, words
) {
321 QString capitalized
= word
;
322 capitalized
.replace( 0, 1, word
[0].toUpper() );
323 artists
.replace( word
, capitalized
);
326 return i18n( "Custom Station: %1", artists
);
330 else if ( elements
[1] == "user" )
332 // eg: lastfm://user/sebr/neighbours
333 if ( elements
[3] == "neighbours" )
334 return i18n( "%1's Neighbor Radio", elements
[2] );
336 // eg: lastfm://user/sebr/personal
337 if ( elements
[3] == "personal" )
338 return i18n( "%1's Personal Radio", elements
[2] );
340 // eg: lastfm://user/sebr/loved
341 if ( elements
[3] == "loved" )
342 return i18n( "%1's Loved Radio", elements
[2] );
344 // eg: lastfm://user/sebr/recommended/100 : 100 is number for how obscure the music should be
345 if ( elements
[3] == "recommended" )
346 return i18n( "%1's Recommended Radio", elements
[2] );
350 //eg: lastfm://group/Amarok%20users
351 else if ( elements
[1] == "group" )
352 return i18n( "Group Radio: %1", elements
[2] );
355 else if ( elements
[1] == "play" )
357 if ( elements
[2] == "tracks" )
358 return i18n( "Track Radio" );
359 else if ( elements
[2] == "artists" )
360 return i18n( "Artist Radio" );
368 ////////////////////////////////////////////////////////////////////////////////
370 ////////////////////////////////////////////////////////////////////////////////
372 WebService::WebService( QObject
* parent
)
376 setObjectName( "lastfmParent" );
377 debug() << "Initialising Web Service";
381 WebService::~WebService()
390 WebService::readProxy() //SLOT
394 while( m_server
->readln( line
) != -1 ) {
397 if( line
== "AMAROK_PROXY: SYNC" )
404 WebService::handshake( const QString
& username
, const QString
& password
)
408 m_username
= username
;
409 m_password
= password
;
411 AmarokHttp
http( "ws.audioscrobbler.com", 80 );
414 QString( "/radio/handshake.php?version=%1&platform=%2&username=%3&passwordmd5=%4&debug=%5" )
415 .arg( APP_VERSION
) //Muesli-approved: Amarok version, and Amarok-as-platform
416 .arg( QString("Amarok") )
417 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) )
418 .arg( KMD5( m_password
.toUtf8() ).hexDigest().data() )
424 kapp
->processEvents();
425 while( http
.state() != Q3Http::Unconnected
);
427 if ( http
.error() != Q3Http::NoError
)
430 const QString
result( http
.readAll() );
432 debug() << "result: " << result
;
434 m_session
= parameter( "session", result
);
435 m_baseHost
= parameter( "base_url", result
);
436 m_basePath
= parameter( "base_path", result
);
437 m_subscriber
= parameter( "subscriber", result
) == "1";
438 m_streamUrl
= Q3Url( parameter( "stream_url", result
) );
439 // bool banned = parameter( "banned", result ) == "1";
441 if ( m_session
.toLower() == "failed" ) {
442 Amarok::ContextStatusBar::instance()->longMessage( i18n(
443 "Amarok failed to establish a session with last.fm. <br>"
444 "Check if your last.fm user and password are correctly set."
449 Amarok::config( "Scrobbler" ).writeEntry( "Subscriber", m_subscriber
);
452 MyServerSocket
* socket
= new MyServerSocket();
453 const int port
= socket
->port();
454 debug() << "Proxy server using port: " << port
;
457 m_proxyUrl
= QString( "http://localhost:%1/lastfm.mp3" ).arg( port
);
459 m_server
= new ProcIO();
460 m_server
->setOutputChannelMode( ProcIO::MergedChannels
);
461 *m_server
<< "amarok_proxy.rb";
462 *m_server
<< "--lastfm";
463 *m_server
<< QString::number( port
);
464 *m_server
<< m_streamUrl
.toString();
465 *m_server
<< AmarokConfig::soundSystem();
466 *m_server
<< Amarok::proxyForUrl( m_streamUrl
.toString() );
471 if( m_server
->error() != QProcess::UnknownError
) return false;
473 kapp
->processEvents();
474 m_server
->readln( line
);
475 if( line
== "AMAROK_PROXY: startup" ) break;
478 connect( m_server
, SIGNAL( readReady( ProcIO
* ) ), this, SLOT( readProxy() ) );
479 connect( m_server
, SIGNAL( finished( int ) ), Controller::instance(), SLOT( playbackStopped() ) );
486 WebService::changeStation( QString url
)
488 debug() << "Changing station:" << url
;
490 AmarokHttp
http( m_baseHost
, 80 );
492 http
.get( QString( m_basePath
+ "/adjust.php?session=%1&url=%2&debug=0" )
497 kapp
->processEvents();
498 while( http
.state() != Q3Http::Unconnected
);
500 if ( http
.error() != Q3Http::NoError
)
502 showError( E_OTHER
); // default error
506 const QString
result( http
.readAll() );
507 const int errCode
= parameter( "error", result
).toInt();
511 showError( errCode
);
515 const QString _url
= parameter( "url", result
);
516 if ( _url
.startsWith( "lastfm://" ) )
518 m_station
= _url
; // parse it in stationDescription
519 emit
stationChanged( _url
, m_station
);
522 emit
stationChanged( _url
, QString() );
528 WebService::requestMetaData() //SLOT
530 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
531 foreach( QObject
*observer
, m_metaDataObservers
)
532 connect( http
, SIGNAL( requestFinished( int, bool ) ), observer
, SLOT( metaDataFinished( int, bool ) ) );
534 http
->get( QString( m_basePath
+ "/np.php?session=%1&debug=%2" )
541 WebService::metaDataFinished( int /*id*/, bool error
) //SLOT
545 AmarokHttp
* http
= (AmarokHttp
*) sender();
549 const QString
result( http
->readAll() );
552 int errCode
= parameter( "error", result
).toInt();
554 debug() << "Metadata failed with error code: " << errCode
;
555 showError( errCode
);
559 m_metaBundle
.setArtist( parameter( "artist", result
) );
560 m_metaBundle
.setAlbum ( parameter( "album", result
) );
561 m_metaBundle
.setTitle ( parameter( "track", result
) );
562 m_metaBundle
.setUrl ( KUrl( Controller::instance()->getGenreUrl() ) );
563 m_metaBundle
.setLength( parameter( "trackduration", result
).toInt() );
566 QString imageUrl
= parameter( "albumcover_medium", result
);
568 if( imageUrl
== "http://static.last.fm/coverart/" ||
569 imageUrl
== "http://static.last.fm/depth/catalogue/no_album_large.gif" )
572 lastFmStuff
.setImageUrl ( CollectionDB::instance()->notAvailCover( true ) );
573 lastFmStuff
.setArtistUrl( parameter( "artist_url", result
) );
574 lastFmStuff
.setAlbumUrl ( parameter( "album_url", result
) );
575 lastFmStuff
.setTitleUrl ( parameter( "track_url", result
) );
576 // bool discovery = parameter( "discovery", result ) != "-1";
578 m_metaBundle
.setLastFmBundle( lastFmStuff
);
580 const KUrl
u( imageUrl
);
582 debug() << "imageUrl empty or invalid.";
583 emit
metaDataResult( m_metaBundle
);
587 KIO::Job
* job
= KIO::storedGet( u
, KIO::Reload
, KIO::HideProgressInfo
);
588 connect( job
, SIGNAL( result( KIO::Job
* ) ), this, SLOT( fetchImageFinished( KIO::Job
* ) ) );
593 WebService::fetchImageFinished( KIO::Job
* job
) //SLOT
597 if( job
->error() == 0 ) {
598 const QString path
= Amarok::saveLocation() + "lastfm_image.png";
599 const int size
= AmarokConfig::coverPreviewSize();
601 QImage img
= QImage::fromData( static_cast<KIO::StoredTransferJob
*>( job
)->data() );
602 img
.scaled( size
, size
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
).save( path
, "PNG" );
604 m_metaBundle
.lastFmBundle()->setImageUrl( CollectionDB::makeShadowedImage( path
, false ) );
606 emit
metaDataResult( m_metaBundle
);
611 WebService::enableScrobbling( bool enabled
) //SLOT
614 debug() << "Enabling Scrobbling!";
616 debug() << "Disabling Scrobbling!";
618 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
619 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( enableScrobblingFinished( int, bool ) ) );
621 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=%2&debug=%3" )
623 .arg( enabled
? QString( "rtp" ) : QString( "nortp" ) )
629 WebService::enableScrobblingFinished( int /*id*/, bool error
) //SLOT
631 AmarokHttp
* http
= (AmarokHttp
*) sender();
635 emit
enableScrobblingDone();
640 WebService::love() //SLOT
642 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
643 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( loveFinished( int, bool ) ) );
645 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=love&debug=%2" )
648 Amarok::ContextStatusBar::instance()->shortMessage( i18nc("love, as in affection", "Loving song...") );
653 WebService::skip() //SLOT
655 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
656 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( skipFinished( int, bool ) ) );
658 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=skip&debug=%2" )
661 Amarok::ContextStatusBar::instance()->shortMessage( i18n("Skipping song...") );
666 WebService::ban() //SLOT
668 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
669 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( banFinished( int, bool ) ) );
671 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=ban&debug=%2" )
674 Amarok::ContextStatusBar::instance()->shortMessage( i18nc("Ban, as in dislike", "Banning song...") );
679 WebService::loveFinished( int /*id*/, bool error
) //SLOT
683 AmarokHttp
* http
= (AmarokHttp
*) sender();
692 WebService::skipFinished( int /*id*/, bool error
) //SLOT
696 AmarokHttp
* http
= (AmarokHttp
*) sender();
700 EngineController::engine()->flushBuffer();
706 WebService::banFinished( int /*id*/, bool error
) //SLOT
710 AmarokHttp
* http
= (AmarokHttp
*) sender();
714 EngineController::engine()->flushBuffer();
721 WebService::friends( QString username
)
723 if ( username
.isEmpty() )
724 username
= m_username
;
726 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
727 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( friendsFinished( bool ) ) );
729 http
->get( QString( "/1.0/user/%1/friends.xml" )
730 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
735 WebService::friendsFinished( int /*id*/, bool error
) //SLOT
737 AmarokHttp
* http
= (AmarokHttp
*) sender();
741 QDomDocument document
;
742 document
.setContent( http
->readAll() );
744 if ( document
.elementsByTagName( "friends" ).length() == 0 )
746 emit
friendsResult( QString( "" ), QStringList() );
751 QString user
= document
.elementsByTagName( "friends" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
752 QDomNodeList values
= document
.elementsByTagName( "user" );
753 for ( int i
= 0; i
< values
.count(); i
++ )
755 friends
<< values
.item( i
).attributes().namedItem( "username" ).nodeValue();
758 emit
friendsResult( user
, friends
);
763 WebService::neighbours( QString username
)
765 if ( username
.isEmpty() )
766 username
= m_username
;
768 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
769 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( neighboursFinished( bool ) ) );
771 http
->get( QString( "/1.0/user/%1/neighbours.xml" )
772 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
777 WebService::neighboursFinished( int /*id*/, bool error
) //SLOT
779 AmarokHttp
* http
= (AmarokHttp
*) sender();
783 QDomDocument document
;
784 document
.setContent( http
->readAll() );
786 if ( document
.elementsByTagName( "neighbours" ).length() == 0 )
788 emit
friendsResult( QString( "" ), QStringList() );
792 QStringList neighbours
;
793 QString user
= document
.elementsByTagName( "neighbours" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
794 QDomNodeList values
= document
.elementsByTagName( "user" );
795 for ( int i
= 0; i
< values
.count(); i
++ )
797 neighbours
<< values
.item( i
).attributes().namedItem( "username" ).nodeValue();
800 emit
neighboursResult( user
, neighbours
);
805 WebService::userTags( QString username
)
807 if ( username
.isEmpty() )
808 username
= m_username
;
810 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
811 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( userTagsFinished( bool ) ) );
813 http
->get( QString( "/1.0/user/%1/tags.xml?debug=%2" )
814 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
819 WebService::userTagsFinished( int /*id*/, bool error
) //SLOT
821 AmarokHttp
* http
= (AmarokHttp
*) sender();
825 QDomDocument document
;
826 document
.setContent( http
->readAll() );
828 if ( document
.elementsByTagName( "toptags" ).length() == 0 )
830 emit
userTagsResult( QString(), QStringList() );
835 QDomNodeList values
= document
.elementsByTagName( "tag" );
836 QString user
= document
.elementsByTagName( "toptags" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
837 for ( int i
= 0; i
< values
.count(); i
++ )
839 QDomNode item
= values
.item( i
).namedItem( "name" );
840 tags
<< item
.toElement().text();
842 emit
userTagsResult( user
, tags
);
847 WebService::recentTracks( QString username
)
849 if ( username
.isEmpty() )
850 username
= m_username
;
852 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
853 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( recentTracksFinished( bool ) ) );
855 http
->get( QString( "/1.0/user/%1/recenttracks.xml" )
856 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
861 WebService::recentTracksFinished( int /*id*/, bool error
) //SLOT
863 AmarokHttp
* http
= (AmarokHttp
*) sender();
867 Q3ValueList
< QPair
<QString
, QString
> > songs
;
868 QDomDocument document
;
869 document
.setContent( http
->readAll() );
871 if ( document
.elementsByTagName( "recenttracks" ).length() == 0 )
873 emit
recentTracksResult( QString(), songs
);
877 QDomNodeList values
= document
.elementsByTagName( "track" );
878 QString user
= document
.elementsByTagName( "recenttracks" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
879 for ( int i
= 0; i
< values
.count(); i
++ )
881 QPair
<QString
, QString
> song
;
882 song
.first
= values
.item( i
).namedItem( "artist" ).toElement().text();
883 song
.second
= values
.item( i
).namedItem( "name" ).toElement().text();
887 emit
recentTracksResult( user
, songs
);
892 WebService::recommend( int type
, QString username
, QString artist
, QString token
)
894 QString modeToken
= "";
898 modeToken
= QString( "artist_name=%1" ).arg( QString( Q3Url( artist
).encodedPathAndQuery() ) );
902 modeToken
= QString( "album_artist=%1&album_name=%2" )
903 .arg( QString( Q3Url( artist
).encodedPathAndQuery() ) )
904 .arg( QString( Q3Url( token
).encodedPathAndQuery() ) );
908 modeToken
= QString( "track_artist=%1&track_name=%2" )
909 .arg( QString( Q3Url( artist
).encodedPathAndQuery() ) )
910 .arg( QString( Q3Url( token
).encodedPathAndQuery() ) );
914 Q3Http
*http
= new Q3Http( "wsdev.audioscrobbler.com", 80, this );
915 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( recommendFinished( bool ) ) );
917 uint currentTime
= QDateTime::currentDateTime().toUTC().toTime_t();
918 QString challenge
= QString::number( currentTime
);
920 QByteArray md5pass
= KMD5( KMD5( m_password
.toUtf8() ).hexDigest().append( QString::number( currentTime
).toLocal8Bit() ) ).hexDigest();
922 QString atoken
= QString( "user=%1&auth=%2&nonce=%3recipient=%4" )
923 .arg( QString( Q3Url( currentUsername() ).encodedPathAndQuery() ) )
924 .arg( QString( Q3Url( md5pass
).encodedPathAndQuery() ) )
925 .arg( QString( Q3Url( challenge
).encodedPathAndQuery() ) )
926 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) );
928 Q3HttpRequestHeader
header( "POST", "/1.0/rw/recommend.php?" + atoken
.toUtf8() );
929 header
.setValue( "Host", "wsdev.audioscrobbler.com" );
930 header
.setContentType( "application/x-www-form-urlencoded" );
931 http
->request( header
, modeToken
.toUtf8() );
936 WebService::recommendFinished( int /*id*/, bool /*error*/ ) //SLOT
938 AmarokHttp
* http
= (AmarokHttp
*) sender();
941 debug() << "Recommendation:" << http
->readAll();
946 WebService::parameter( const QString keyName
, const QString data
) const
948 QStringList list
= data
.split( '\n' );
950 for ( int i
= 0; i
< list
.size(); i
++ )
952 QStringList values
= list
[i
].split( '=' );
953 if ( values
[0] == keyName
)
955 values
.removeAt( 0 );
956 return QString::fromUtf8( values
.join( "=" ).toAscii() );
960 return QString( "" );
965 WebService::parameterArray( const QString keyName
, const QString data
) const
968 QStringList list
= data
.split( '\n' );
970 for ( int i
= 0; i
< list
.size(); i
++ )
972 QStringList values
= list
[i
].split( '=' );
973 if ( values
[0].startsWith( keyName
) )
975 values
.removeAt( 0 );
976 result
.append( QString::fromUtf8( values
.join( "=" ).toAscii() ) );
985 WebService::parameterKeys( const QString keyName
, const QString data
) const
988 QStringList list
= data
.split( '\n' );
990 for ( int i
= 0; i
< list
.size(); i
++ )
992 QStringList values
= list
[i
].split( '=' );
993 if ( values
[0].startsWith( keyName
) )
995 values
= values
[0].split( '[' );
996 values
= values
[1].split( ']' );
997 result
.append( values
[0] );
1006 WebService::showError( int code
, QString message
)
1011 message
= i18n( "There is not enough content to play this station." );
1014 message
= i18n( "This group does not have enough members for radio." );
1017 message
= i18n( "This artist does not have enough fans for radio." );
1020 message
= i18n( "This item is not available for streaming." );
1022 case E_NOSUBSCRIBER
:
1023 message
= i18n( "This feature is only available to last.fm subscribers." );
1025 case E_NONEIGHBOURS
:
1026 message
= i18n( "There are not enough neighbors for this radio." );
1029 message
= i18n( "This stream has stopped. Please try another station." );
1032 if( message
.isEmpty() )
1033 message
= i18n( "Failed to play this last.fm stream." );
1036 Amarok::ContextStatusBar::instance()->longMessage( message
, KDE::StatusBar::Sorry
);
1039 ////////////////////////////////////////////////////////////////////////////////
1040 // CLASS LastFm::Bundle
1041 ////////////////////////////////////////////////////////////////////////////////
1043 Bundle::Bundle( const Bundle
& lhs
)
1044 : m_imageUrl( lhs
.m_imageUrl
)
1045 , m_albumUrl( lhs
.m_albumUrl
)
1046 , m_artistUrl( lhs
.m_artistUrl
)
1047 , m_titleUrl( lhs
.m_titleUrl
)
1050 ////////////////////////////////////////////////////////////////////////////////
1051 // CLASS LastFm::LoginDialog
1052 ////////////////////////////////////////////////////////////////////////////////
1053 LoginDialog::LoginDialog( QWidget
*parent
)
1057 setButtons( Ok
| Cancel
);
1059 //makeGridMainWidget( 1, Qt::Horizontal );
1060 KVBox
* vbox
= new KVBox( this );
1061 setMainWidget( vbox
);
1062 new QLabel( i18n( "To use last.fm with Amarok, you need a last.fm profile." ), vbox
);
1064 //makeGridMainWidget( 2, Qt::Horizontal );
1065 KHBox
* hbox
= new KHBox( vbox
);
1066 QLabel
*nameLabel
= new QLabel( i18n("&Username:"), hbox
);
1067 m_userLineEdit
= new KLineEdit( hbox
);
1068 nameLabel
->setBuddy( m_userLineEdit
);
1070 QLabel
*passLabel
= new QLabel( i18n("&Password:"), hbox
);
1071 m_passLineEdit
= new KLineEdit( hbox
);
1072 m_passLineEdit
->setEchoMode( QLineEdit::Password
);
1073 passLabel
->setBuddy( m_passLineEdit
);
1075 m_userLineEdit
->setFocus();
1079 void LoginDialog::slotButtonClicked( ButtonCode button
)
1081 if ( button
== Ok
) {
1082 AmarokConfig::setScrobblerUsername( m_userLineEdit
->text() );
1083 AmarokConfig::setScrobblerPassword( m_passLineEdit
->text() );
1086 KDialog::slotButtonClicked( button
);
1090 ////////////////////////////////////////////////////////////////////////////////
1091 // CLASS LastFm::CustomStationDialog
1092 ////////////////////////////////////////////////////////////////////////////////
1093 CustomStationDialog::CustomStationDialog( QWidget
*parent
)
1096 setCaption( i18n( "Create Custom Station" ) );
1098 setButtons( Ok
| Cancel
);
1101 KVBox
*vbox
= new KVBox( this );
1102 setMainWidget( vbox
);
1105 new QLabel( i18n( "Enter the name of a band or artist you like:" ), mainWidget() );
1107 m_edit
= new KLineEdit( mainWidget() );
1113 CustomStationDialog::text() const
1115 return m_edit
->text();
1119 #include "lastfm.moc"