1 /***************************************************************************
2 * copyright : (C) 2006 Chris Muehlhaeuser <chris@chris.de> *
3 * : (C) 2006 Seb Ruiz <me@sebruiz.net> *
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"
26 #include "statusbar.h" //showError()
29 #include <Q3ValueList>
31 #include <QDomDocument>
32 #include <QDomElement>
38 #include <KApplication>
39 #include <KCodecs> //md5sum
43 #include <KMessageBox>
44 #include <KProtocolManager>
52 using namespace LastFm
;
54 ///////////////////////////////////////////////////////////////////////////////
56 // AmarokHttp is a hack written so that lastfm code could easily use something proxy aware.
57 // DO NOT use this class for anything else, use KIO directly instead.
58 ////////////////////////////////////////////////////////////////////////////////
59 AmarokHttp::AmarokHttp ( const QString
& hostname
, quint16 port
,
62 m_hostname( hostname
),
67 AmarokHttp::get ( const QString
& path
)
69 QString uri
= QString( "http://%1:%2/%3" )
75 m_error
= Q3Http::NoError
;
76 m_state
= Q3Http::Connecting
;
77 KIO::TransferJob
*job
= KIO::get(uri
, true, false);
78 connect(job
, SIGNAL(data(KIO::Job
*, const QByteArray
&)),
79 this, SLOT(slotData(KIO::Job
*, const QByteArray
&)));
80 connect(job
, SIGNAL(result(KJob
*)),
81 this, SLOT(slotResult(KJob
*)));
87 AmarokHttp::state() const
93 AmarokHttp::readAll ()
105 AmarokHttp::slotData(KIO::Job
*, const QByteArray
& data
)
107 if( data
.size() == 0 ) {
110 else if ( m_result
.size() == 0 ) {
114 m_result
.resize( m_result
.size() + data
.size() );
115 memcpy( m_result
.end(), data
.data(), data
.size() );
120 AmarokHttp::slotResult(KJob
* job
)
122 bool err
= job
->error();
123 if( err
|| m_error
!= Q3Http::NoError
) {
124 m_error
= Q3Http::UnknownError
;
127 m_error
= Q3Http::NoError
;
130 m_state
= Q3Http::Unconnected
;
131 emit( requestFinished( 0, err
) );
136 ///////////////////////////////////////////////////////////////////////////////
138 ////////////////////////////////////////////////////////////////////////////////
140 Controller
*Controller::s_instance
= 0;
142 Controller::Controller()
143 : QObject( EngineController::instance() )
146 setObjectName( "lastfmController" );
147 KActionCollection
* ac
= Amarok::actionCollection();
148 KAction
*action
= new KAction( KIcon( Amarok::icon( "remove" ) ), i18n( "Ban" ), ac
);
149 connect( action
, SIGNAL( triggered( bool ) ), this, SLOT( ban() ) );
150 action
->setShortcut( QKeySequence( Qt::CTRL
| Qt::Key_B
) );
151 m_actionList
.append( action
);
153 action
= new KAction( KIcon( Amarok::icon( "love" ) ), i18n( "Love" ), ac
);
154 connect( action
, SIGNAL( triggered( bool ) ), this, SLOT( love() ) );
155 action
->setShortcut( QKeySequence( Qt::CTRL
| Qt::Key_L
) );
156 m_actionList
.append( action
);
158 action
= new KAction( KIcon( Amarok::icon( "next" ) ), i18n( "Skip" ), ac
);
159 connect( action
, SIGNAL( triggered( bool ) ), this, SLOT( skip() ) );
160 action
->setShortcut( QKeySequence( Qt::CTRL
| Qt::Key_K
) );
161 m_actionList
.append( action
);
162 setActionsEnabled( false );
167 Controller::instance()
169 if( !s_instance
) s_instance
= new Controller();
175 Controller::getNewProxy( QString genreUrl
)
179 m_genreUrl
= genreUrl
;
181 if ( m_service
) playbackStopped();
183 m_service
= new WebService( this );
185 if( checkCredentials() )
187 QString user
= AmarokConfig::scrobblerUsername();
188 QString pass
= AmarokConfig::scrobblerPassword();
190 if( !user
.isEmpty() && !pass
.isEmpty() &&
191 m_service
->handshake( user
, pass
) )
193 bool ok
= m_service
->changeStation( m_genreUrl
);
194 if( ok
) // else playbackStopped()
196 if( !AmarokConfig::submitPlayedSongs() )
197 m_service
->enableScrobbling( false );
198 setActionsEnabled( true );
199 return KUrl( m_service
->proxyUrl() );
204 // Some kind of failure happened, so crap out
211 Controller::playbackStopped() //SLOT
213 setActionsEnabled( false );
221 Controller::checkCredentials() //static
223 if( AmarokConfig::scrobblerUsername().isEmpty() || AmarokConfig::scrobblerPassword().isEmpty() )
225 LoginDialog
dialog( 0 );
226 dialog
.setCaption( "last.fm" );
227 return dialog
.exec() == QDialog::Accepted
;
234 Controller::createCustomStation() //static
237 CustomStationDialog
dialog( 0 );
239 if( dialog
.exec() == QDialog::Accepted
)
241 token
= dialog
.text();
273 Controller::setActionsEnabled( bool enable
)
274 { //pausing last.fm streams doesn't do anything good
275 Amarok::actionCollection()->action( "play_pause" )->setEnabled( !enable
);
276 Amarok::actionCollection()->action( "pause" )->setEnabled( !enable
);
279 for( action
= m_actionList
.first(); action
; action
= m_actionList
.next() )
280 action
->setEnabled( enable
);
283 /// return a translatable description of the station we are connected to
285 Controller::stationDescription( QString url
)
287 if( url
.isEmpty() && instance() && instance()->isPlaying() )
288 url
= instance()->getService()->currentStation();
290 if( url
.isEmpty() ) return QString();
292 QStringList elements
= url
.split( '/' );
295 // eg: lastfm://globaltag/rock
296 if ( elements
[1] == "globaltags" )
297 return i18n( "Global Tag Radio: %1", elements
[2] );
300 if ( elements
[1] == "artist" )
302 // eg: lastfm://artist/Queen/similarartists
303 if ( elements
[3] == "similarartists" )
304 return i18n( "Similar Artists to %1", elements
[2] );
306 if ( elements
[3] == "fans" )
307 return i18n( "Artist Fan Radio: %1", elements
[2] );
311 if ( elements
[1] == "artistnames" )
313 // eg: lastfm://artistnames/genesis,pink floyd,queen
315 // turn "genesis,pink floyd,queen" into "Genesis, Pink Floyd, Queen"
316 QString artists
= elements
[2];
317 artists
.replace( ",", ", " );
318 const QStringList words
= QString( artists
).remove( "," ).split( " " );
319 oldForeach( words
) {
320 QString capitalized
= *it
;
321 capitalized
.replace( 0, 1, (*it
)[0].toUpper() );
322 artists
.replace( *it
, capitalized
);
325 return i18n( "Custom Station: %1", artists
);
329 else if ( elements
[1] == "user" )
331 // eg: lastfm://user/sebr/neighbours
332 if ( elements
[3] == "neighbours" )
333 return i18n( "%1's Neighbor Radio", elements
[2] );
335 // eg: lastfm://user/sebr/personal
336 if ( elements
[3] == "personal" )
337 return i18n( "%1's Personal Radio", elements
[2] );
339 // eg: lastfm://user/sebr/loved
340 if ( elements
[3] == "loved" )
341 return i18n( "%1's Loved Radio", elements
[2] );
343 // eg: lastfm://user/sebr/recommended/100 : 100 is number for how obscure the music should be
344 if ( elements
[3] == "recommended" )
345 return i18n( "%1's Recommended Radio", elements
[2] );
349 //eg: lastfm://group/Amarok%20users
350 else if ( elements
[1] == "group" )
351 return i18n( "Group Radio: %1", elements
[2] );
354 else if ( elements
[1] == "play" )
356 if ( elements
[2] == "tracks" )
357 return i18n( "Track Radio" );
358 else if ( elements
[2] == "artists" )
359 return i18n( "Artist Radio" );
367 ////////////////////////////////////////////////////////////////////////////////
369 ////////////////////////////////////////////////////////////////////////////////
371 WebService::WebService( QObject
* parent
)
375 setObjectName( "lastfmParent" );
376 debug() << "Initialising Web Service";
380 WebService::~WebService()
389 WebService::readProxy() //SLOT
393 while( m_server
->readln( line
) != -1 ) {
396 if( line
== "AMAROK_PROXY: SYNC" )
403 WebService::handshake( const QString
& username
, const QString
& password
)
407 m_username
= username
;
408 m_password
= password
;
410 AmarokHttp
http( "ws.audioscrobbler.com", 80 );
413 QString( "/radio/handshake.php?version=%1&platform=%2&username=%3&passwordmd5=%4&debug=%5" )
414 .arg( APP_VERSION
) //Muesli-approved: Amarok version, and Amarok-as-platform
415 .arg( QString("Amarok") )
416 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) )
417 .arg( KMD5( m_password
.toUtf8() ).hexDigest().data() )
423 kapp
->processEvents();
424 while( http
.state() != Q3Http::Unconnected
);
426 if ( http
.error() != Q3Http::NoError
)
429 const QString
result( http
.readAll() );
431 debug() << "result: " << result
;
433 m_session
= parameter( "session", result
);
434 m_baseHost
= parameter( "base_url", result
);
435 m_basePath
= parameter( "base_path", result
);
436 m_subscriber
= parameter( "subscriber", result
) == "1";
437 m_streamUrl
= Q3Url( parameter( "stream_url", result
) );
438 // bool banned = parameter( "banned", result ) == "1";
440 if ( m_session
.toLower() == "failed" ) {
441 Amarok::StatusBar::instance()->longMessage( i18n(
442 "Amarok failed to establish a session with last.fm. <br>"
443 "Check if your last.fm user and password are correctly set."
448 Amarok::config( "Scrobbler" ).writeEntry( "Subscriber", m_subscriber
);
451 MyServerSocket
* socket
= new MyServerSocket();
452 const int port
= socket
->port();
453 debug() << "Proxy server using port: " << port
;
456 m_proxyUrl
= QString( "http://localhost:%1/lastfm.mp3" ).arg( port
);
458 m_server
= new Amarok::ProcIO();
459 m_server
->setComm( K3Process::Communication( K3Process::AllOutput
) );
460 *m_server
<< "amarok_proxy.rb";
461 *m_server
<< "--lastfm";
462 *m_server
<< QString::number( port
);
463 *m_server
<< m_streamUrl
.toString();
464 *m_server
<< AmarokConfig::soundSystem();
465 *m_server
<< Amarok::proxyForUrl( m_streamUrl
.toString() );
467 if( !m_server
->start( K3ProcIO::NotifyOnExit
, true ) ) {
468 error() << "Failed to start amarok_proxy.rb" << endl
;
474 kapp
->processEvents();
475 m_server
->readln( line
);
476 if( line
== "AMAROK_PROXY: startup" ) break;
479 connect( m_server
, SIGNAL( readReady( K3ProcIO
* ) ), this, SLOT( readProxy() ) );
480 connect( m_server
, SIGNAL( processExited( K3Process
* ) ), Controller::instance(), SLOT( playbackStopped() ) );
487 WebService::changeStation( QString url
)
489 debug() << "Changing station:" << url
;
491 AmarokHttp
http( m_baseHost
, 80 );
493 http
.get( QString( m_basePath
+ "/adjust.php?session=%1&url=%2&debug=0" )
498 kapp
->processEvents();
499 while( http
.state() != Q3Http::Unconnected
);
501 if ( http
.error() != Q3Http::NoError
)
503 showError( E_OTHER
); // default error
507 const QString
result( http
.readAll() );
508 const int errCode
= parameter( "error", result
).toInt();
512 showError( errCode
);
516 const QString _url
= parameter( "url", result
);
517 if ( _url
.startsWith( "lastfm://" ) )
519 m_station
= _url
; // parse it in stationDescription
520 emit
stationChanged( _url
, m_station
);
523 emit
stationChanged( _url
, QString() );
529 WebService::requestMetaData() //SLOT
531 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
532 foreach( QObject
*observer
, m_metaDataObservers
)
533 connect( http
, SIGNAL( requestFinished( int, bool ) ), observer
, SLOT( metaDataFinished( int, bool ) ) );
535 http
->get( QString( m_basePath
+ "/np.php?session=%1&debug=%2" )
542 WebService::metaDataFinished( int /*id*/, bool error
) //SLOT
546 AmarokHttp
* http
= (AmarokHttp
*) sender();
550 const QString
result( http
->readAll() );
553 int errCode
= parameter( "error", result
).toInt();
555 debug() << "Metadata failed with error code: " << errCode
;
556 showError( errCode
);
560 m_metaBundle
.setArtist( parameter( "artist", result
) );
561 m_metaBundle
.setAlbum ( parameter( "album", result
) );
562 m_metaBundle
.setTitle ( parameter( "track", result
) );
563 m_metaBundle
.setUrl ( KUrl( Controller::instance()->getGenreUrl() ) );
564 m_metaBundle
.setLength( parameter( "trackduration", result
).toInt() );
567 QString imageUrl
= parameter( "albumcover_medium", result
);
569 if( imageUrl
== "http://static.last.fm/coverart/" ||
570 imageUrl
== "http://static.last.fm/depth/catalogue/no_album_large.gif" )
573 lastFmStuff
.setImageUrl ( CollectionDB::instance()->notAvailCover( true ) );
574 lastFmStuff
.setArtistUrl( parameter( "artist_url", result
) );
575 lastFmStuff
.setAlbumUrl ( parameter( "album_url", result
) );
576 lastFmStuff
.setTitleUrl ( parameter( "track_url", result
) );
577 // bool discovery = parameter( "discovery", result ) != "-1";
579 m_metaBundle
.setLastFmBundle( lastFmStuff
);
581 const KUrl
u( imageUrl
);
583 debug() << "imageUrl empty or invalid.";
584 emit
metaDataResult( m_metaBundle
);
588 KIO::Job
* job
= KIO::storedGet( u
, true, false );
589 connect( job
, SIGNAL( result( KIO::Job
* ) ), this, SLOT( fetchImageFinished( KIO::Job
* ) ) );
594 WebService::fetchImageFinished( KIO::Job
* job
) //SLOT
598 if( job
->error() == 0 ) {
599 const QString path
= Amarok::saveLocation() + "lastfm_image.png";
600 const int size
= AmarokConfig::coverPreviewSize();
602 QImage img
= QImage::fromData( static_cast<KIO::StoredTransferJob
*>( job
)->data() );
603 img
.scaled( size
, size
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
).save( path
, "PNG" );
605 m_metaBundle
.lastFmBundle()->setImageUrl( CollectionDB::makeShadowedImage( path
, false ) );
607 emit
metaDataResult( m_metaBundle
);
612 WebService::enableScrobbling( bool enabled
) //SLOT
615 debug() << "Enabling Scrobbling!";
617 debug() << "Disabling Scrobbling!";
619 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
620 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( enableScrobblingFinished( int, bool ) ) );
622 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=%2&debug=%3" )
624 .arg( enabled
? QString( "rtp" ) : QString( "nortp" ) )
630 WebService::enableScrobblingFinished( int /*id*/, bool error
) //SLOT
632 AmarokHttp
* http
= (AmarokHttp
*) sender();
636 emit
enableScrobblingDone();
641 WebService::love() //SLOT
643 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
644 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( loveFinished( int, bool ) ) );
646 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=love&debug=%2" )
649 Amarok::StatusBar::instance()->shortMessage( i18nc("love, as in affection", "Loving song...") );
654 WebService::skip() //SLOT
656 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
657 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( skipFinished( int, bool ) ) );
659 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=skip&debug=%2" )
662 Amarok::StatusBar::instance()->shortMessage( i18n("Skipping song...") );
667 WebService::ban() //SLOT
669 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
670 connect( http
, SIGNAL( requestFinished( int, bool ) ), this, SLOT( banFinished( int, bool ) ) );
672 http
->get( QString( m_basePath
+ "/control.php?session=%1&command=ban&debug=%2" )
675 Amarok::StatusBar::instance()->shortMessage( i18nc("Ban, as in dislike", "Banning song...") );
680 WebService::loveFinished( int /*id*/, bool error
) //SLOT
684 AmarokHttp
* http
= (AmarokHttp
*) sender();
693 WebService::skipFinished( int /*id*/, bool error
) //SLOT
697 AmarokHttp
* http
= (AmarokHttp
*) sender();
701 EngineController::engine()->flushBuffer();
707 WebService::banFinished( int /*id*/, bool error
) //SLOT
711 AmarokHttp
* http
= (AmarokHttp
*) sender();
715 EngineController::engine()->flushBuffer();
722 WebService::friends( QString username
)
724 if ( username
.isEmpty() )
725 username
= m_username
;
727 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
728 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( friendsFinished( bool ) ) );
730 http
->get( QString( "/1.0/user/%1/friends.xml" )
731 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
736 WebService::friendsFinished( int /*id*/, bool error
) //SLOT
738 AmarokHttp
* http
= (AmarokHttp
*) sender();
742 QDomDocument document
;
743 document
.setContent( http
->readAll() );
745 if ( document
.elementsByTagName( "friends" ).length() == 0 )
747 emit
friendsResult( QString( "" ), QStringList() );
752 QString user
= document
.elementsByTagName( "friends" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
753 QDomNodeList values
= document
.elementsByTagName( "user" );
754 for ( int i
= 0; i
< values
.count(); i
++ )
756 friends
<< values
.item( i
).attributes().namedItem( "username" ).nodeValue();
759 emit
friendsResult( user
, friends
);
764 WebService::neighbours( QString username
)
766 if ( username
.isEmpty() )
767 username
= m_username
;
769 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
770 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( neighboursFinished( bool ) ) );
772 http
->get( QString( "/1.0/user/%1/neighbours.xml" )
773 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
778 WebService::neighboursFinished( int /*id*/, bool error
) //SLOT
780 AmarokHttp
* http
= (AmarokHttp
*) sender();
784 QDomDocument document
;
785 document
.setContent( http
->readAll() );
787 if ( document
.elementsByTagName( "neighbours" ).length() == 0 )
789 emit
friendsResult( QString( "" ), QStringList() );
793 QStringList neighbours
;
794 QString user
= document
.elementsByTagName( "neighbours" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
795 QDomNodeList values
= document
.elementsByTagName( "user" );
796 for ( int i
= 0; i
< values
.count(); i
++ )
798 neighbours
<< values
.item( i
).attributes().namedItem( "username" ).nodeValue();
801 emit
neighboursResult( user
, neighbours
);
806 WebService::userTags( QString username
)
808 if ( username
.isEmpty() )
809 username
= m_username
;
811 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
812 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( userTagsFinished( bool ) ) );
814 http
->get( QString( "/1.0/user/%1/tags.xml?debug=%2" )
815 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
820 WebService::userTagsFinished( int /*id*/, bool error
) //SLOT
822 AmarokHttp
* http
= (AmarokHttp
*) sender();
826 QDomDocument document
;
827 document
.setContent( http
->readAll() );
829 if ( document
.elementsByTagName( "toptags" ).length() == 0 )
831 emit
userTagsResult( QString(), QStringList() );
836 QDomNodeList values
= document
.elementsByTagName( "tag" );
837 QString user
= document
.elementsByTagName( "toptags" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
838 for ( int i
= 0; i
< values
.count(); i
++ )
840 QDomNode item
= values
.item( i
).namedItem( "name" );
841 tags
<< item
.toElement().text();
843 emit
userTagsResult( user
, tags
);
848 WebService::recentTracks( QString username
)
850 if ( username
.isEmpty() )
851 username
= m_username
;
853 AmarokHttp
*http
= new AmarokHttp( m_baseHost
, 80, this );
854 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( recentTracksFinished( bool ) ) );
856 http
->get( QString( "/1.0/user/%1/recenttracks.xml" )
857 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) ) );
862 WebService::recentTracksFinished( int /*id*/, bool error
) //SLOT
864 AmarokHttp
* http
= (AmarokHttp
*) sender();
868 Q3ValueList
< QPair
<QString
, QString
> > songs
;
869 QDomDocument document
;
870 document
.setContent( http
->readAll() );
872 if ( document
.elementsByTagName( "recenttracks" ).length() == 0 )
874 emit
recentTracksResult( QString(), songs
);
878 QDomNodeList values
= document
.elementsByTagName( "track" );
879 QString user
= document
.elementsByTagName( "recenttracks" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
880 for ( int i
= 0; i
< values
.count(); i
++ )
882 QPair
<QString
, QString
> song
;
883 song
.first
= values
.item( i
).namedItem( "artist" ).toElement().text();
884 song
.second
= values
.item( i
).namedItem( "name" ).toElement().text();
888 emit
recentTracksResult( user
, songs
);
893 WebService::recommend( int type
, QString username
, QString artist
, QString token
)
895 QString modeToken
= "";
899 modeToken
= QString( "artist_name=%1" ).arg( QString( Q3Url( artist
).encodedPathAndQuery() ) );
903 modeToken
= QString( "album_artist=%1&album_name=%2" )
904 .arg( QString( Q3Url( artist
).encodedPathAndQuery() ) )
905 .arg( QString( Q3Url( token
).encodedPathAndQuery() ) );
909 modeToken
= QString( "track_artist=%1&track_name=%2" )
910 .arg( QString( Q3Url( artist
).encodedPathAndQuery() ) )
911 .arg( QString( Q3Url( token
).encodedPathAndQuery() ) );
915 Q3Http
*http
= new Q3Http( "wsdev.audioscrobbler.com", 80, this );
916 connect( http
, SIGNAL( requestFinished( bool ) ), this, SLOT( recommendFinished( bool ) ) );
918 uint currentTime
= QDateTime::currentDateTime().toUTC().toTime_t();
919 QString challenge
= QString::number( currentTime
);
921 QByteArray md5pass
= KMD5( KMD5( m_password
.toUtf8() ).hexDigest().append( QString::number( currentTime
).toLocal8Bit() ) ).hexDigest();
923 QString atoken
= QString( "user=%1&auth=%2&nonce=%3recipient=%4" )
924 .arg( QString( Q3Url( currentUsername() ).encodedPathAndQuery() ) )
925 .arg( QString( Q3Url( md5pass
).encodedPathAndQuery() ) )
926 .arg( QString( Q3Url( challenge
).encodedPathAndQuery() ) )
927 .arg( QString( Q3Url( username
).encodedPathAndQuery() ) );
929 Q3HttpRequestHeader
header( "POST", "/1.0/rw/recommend.php?" + atoken
.toUtf8() );
930 header
.setValue( "Host", "wsdev.audioscrobbler.com" );
931 header
.setContentType( "application/x-www-form-urlencoded" );
932 http
->request( header
, modeToken
.toUtf8() );
937 WebService::recommendFinished( int /*id*/, bool /*error*/ ) //SLOT
939 AmarokHttp
* http
= (AmarokHttp
*) sender();
942 debug() << "Recommendation:" << http
->readAll();
947 WebService::parameter( const QString keyName
, const QString data
) const
949 QStringList list
= data
.split( '\n' );
951 for ( int i
= 0; i
< list
.size(); i
++ )
953 QStringList values
= list
[i
].split( '=' );
954 if ( values
[0] == keyName
)
956 values
.removeAt( 0 );
957 return QString::fromUtf8( values
.join( "=" ).toAscii() );
961 return QString( "" );
966 WebService::parameterArray( const QString keyName
, const QString data
) const
969 QStringList list
= data
.split( '\n' );
971 for ( int i
= 0; i
< list
.size(); i
++ )
973 QStringList values
= list
[i
].split( '=' );
974 if ( values
[0].startsWith( keyName
) )
976 values
.removeAt( 0 );
977 result
.append( QString::fromUtf8( values
.join( "=" ).toAscii() ) );
986 WebService::parameterKeys( const QString keyName
, const QString data
) const
989 QStringList list
= data
.split( '\n' );
991 for ( int i
= 0; i
< list
.size(); i
++ )
993 QStringList values
= list
[i
].split( '=' );
994 if ( values
[0].startsWith( keyName
) )
996 values
= values
[0].split( '[' );
997 values
= values
[1].split( ']' );
998 result
.append( values
[0] );
1007 WebService::showError( int code
, QString message
)
1012 message
= i18n( "There is not enough content to play this station." );
1015 message
= i18n( "This group does not have enough members for radio." );
1018 message
= i18n( "This artist does not have enough fans for radio." );
1021 message
= i18n( "This item is not available for streaming." );
1023 case E_NOSUBSCRIBER
:
1024 message
= i18n( "This feature is only available to last.fm subscribers." );
1026 case E_NONEIGHBOURS
:
1027 message
= i18n( "There are not enough neighbors for this radio." );
1030 message
= i18n( "This stream has stopped. Please try another station." );
1033 if( message
.isEmpty() )
1034 message
= i18n( "Failed to play this last.fm stream." );
1037 Amarok::StatusBar::instance()->longMessage( message
, KDE::StatusBar::Sorry
);
1040 ////////////////////////////////////////////////////////////////////////////////
1041 // CLASS LastFm::Bundle
1042 ////////////////////////////////////////////////////////////////////////////////
1044 Bundle::Bundle( const Bundle
& lhs
)
1045 : m_imageUrl( lhs
.m_imageUrl
)
1046 , m_albumUrl( lhs
.m_albumUrl
)
1047 , m_artistUrl( lhs
.m_artistUrl
)
1048 , m_titleUrl( lhs
.m_titleUrl
)
1051 ////////////////////////////////////////////////////////////////////////////////
1052 // CLASS LastFm::LoginDialog
1053 ////////////////////////////////////////////////////////////////////////////////
1054 LoginDialog::LoginDialog( QWidget
*parent
)
1058 setButtons( Ok
| Cancel
);
1060 //makeGridMainWidget( 1, Qt::Horizontal );
1061 KVBox
* vbox
= new KVBox( this );
1062 setMainWidget( vbox
);
1063 new QLabel( i18n( "To use last.fm with Amarok, you need a last.fm profile." ), vbox
);
1065 //makeGridMainWidget( 2, Qt::Horizontal );
1066 KHBox
* hbox
= new KHBox( vbox
);
1067 QLabel
*nameLabel
= new QLabel( i18n("&Username:"), hbox
);
1068 m_userLineEdit
= new KLineEdit( hbox
);
1069 nameLabel
->setBuddy( m_userLineEdit
);
1071 QLabel
*passLabel
= new QLabel( i18n("&Password:"), hbox
);
1072 m_passLineEdit
= new KLineEdit( hbox
);
1073 m_passLineEdit
->setEchoMode( QLineEdit::Password
);
1074 passLabel
->setBuddy( m_passLineEdit
);
1076 m_userLineEdit
->setFocus();
1080 void LoginDialog::slotButtonClicked( ButtonCode button
)
1082 if ( button
== Ok
) {
1083 AmarokConfig::setScrobblerUsername( m_userLineEdit
->text() );
1084 AmarokConfig::setScrobblerPassword( m_passLineEdit
->text() );
1087 KDialog::slotButtonClicked( button
);
1091 ////////////////////////////////////////////////////////////////////////////////
1092 // CLASS LastFm::CustomStationDialog
1093 ////////////////////////////////////////////////////////////////////////////////
1094 CustomStationDialog::CustomStationDialog( QWidget
*parent
)
1097 setCaption( i18n( "Create Custom Station" ) );
1099 setButtons( Ok
| Cancel
);
1102 KVBox
*vbox
= new KVBox( this );
1103 setMainWidget( vbox
);
1106 new QLabel( i18n( "Enter the name of a band or artist you like:" ), mainWidget() );
1108 m_edit
= new KLineEdit( mainWidget() );
1114 CustomStationDialog::text() const
1116 return m_edit
->text();
1120 #include "lastfm.moc"