|
|
|
/***************************************************************************
|
|
|
|
* copyright : (C) 2006 Chris Muehlhaeuser <chris@chris.de> *
|
|
|
|
* : (C) 2006 Seb Ruiz <me@sebruiz.net> *
|
|
|
|
* : (C) 2006 Ian Monroe <ian@monroe.nu> *
|
|
|
|
* : (C) 2006 Mark Kretschmann <markey@web.de> *
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#define DEBUG_PREFIX "LastFm"
|
|
|
|
|
|
|
|
#include "amarok.h" //APP_VERSION, actioncollection
|
|
|
|
#include "amarokconfig.h" //last.fm username and passwd
|
|
|
|
#include "collectiondb.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "enginecontroller.h"
|
|
|
|
#include "lastfm.h"
|
|
|
|
#include "statusbar.h" //showError()
|
|
|
|
|
|
|
|
#include <tqdeepcopy.h>
|
|
|
|
#include <tqdom.h>
|
|
|
|
#include <tqhttp.h>
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
#include <kaction.h>
|
|
|
|
#include <klineedit.h>
|
|
|
|
#include <kmdcodec.h> //md5sum
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <kio/job.h>
|
|
|
|
#include <kio/jobclasses.h>
|
|
|
|
#include <kprotocolmanager.h>
|
|
|
|
#include <kshortcut.h>
|
|
|
|
#include <kurl.h>
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
using namespace LastFm;
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CLASS AmarokHttp
|
|
|
|
// AmarokHttp is a hack written so that lastfm code could easily use something proxy aware.
|
|
|
|
// DO NOT use this class for anything else, use KIO directly instead.
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
AmarokHttp::AmarokHttp ( const TQString& hostname, TQ_UINT16 port,
|
|
|
|
TQObject* parent )
|
|
|
|
: TQObject( parent ),
|
|
|
|
m_hostname( hostname ),
|
|
|
|
m_port( port )
|
|
|
|
{}
|
|
|
|
|
|
|
|
int
|
|
|
|
AmarokHttp::get ( const TQString & path )
|
|
|
|
{
|
|
|
|
TQString uri = TQString( "http://%1:%2/%3" )
|
|
|
|
.arg( m_hostname )
|
|
|
|
.arg( m_port )
|
|
|
|
.arg( path );
|
|
|
|
|
|
|
|
m_done = false;
|
|
|
|
m_error = TQHttp::NoError;
|
|
|
|
m_state = TQHttp::Connecting;
|
|
|
|
KIO::TransferJob *job = KIO::get(uri, true, false);
|
|
|
|
connect(job, TQT_SIGNAL(data(KIO::Job*, const TQByteArray&)),
|
|
|
|
this, TQT_SLOT(slotData(KIO::Job*, const TQByteArray&)));
|
|
|
|
connect(job, TQT_SIGNAL(result(KIO::Job*)),
|
|
|
|
this, TQT_SLOT(slotResult(KIO::Job*)));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQHttp::State
|
|
|
|
AmarokHttp::state() const
|
|
|
|
{
|
|
|
|
return m_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQByteArray
|
|
|
|
AmarokHttp::readAll ()
|
|
|
|
{
|
|
|
|
return m_result;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQHttp::Error
|
|
|
|
AmarokHttp::error()
|
|
|
|
{
|
|
|
|
return m_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AmarokHttp::slotData(KIO::Job*, const TQByteArray& data)
|
|
|
|
{
|
|
|
|
if( data.size() == 0 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if ( m_result.size() == 0 ) {
|
|
|
|
m_result = data;
|
|
|
|
}
|
|
|
|
else if ( m_result.resize( m_result.size() + data.size() ) ) {
|
|
|
|
memcpy( m_result.end(), data.data(), data.size() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
AmarokHttp::slotResult(KIO::Job* job)
|
|
|
|
{
|
|
|
|
bool err = job->error();
|
|
|
|
if( err || m_error != TQHttp::NoError ) {
|
|
|
|
m_error = TQHttp::UnknownError;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_error = TQHttp::NoError;
|
|
|
|
}
|
|
|
|
m_done = true;
|
|
|
|
m_state = TQHttp::Unconnected;
|
|
|
|
emit( requestFinished( 0, err ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CLASS Controller
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
Controller *Controller::s_instance = 0;
|
|
|
|
|
|
|
|
Controller::Controller()
|
|
|
|
: TQObject( EngineController::instance(), "lastfmController" )
|
|
|
|
, m_service( 0 )
|
|
|
|
{
|
|
|
|
KActionCollection* ac = Amarok::actionCollection();
|
|
|
|
m_actionList.append( new KAction( i18n( "Ban" ), Amarok::icon( "remove" ),
|
|
|
|
KKey( TQt::CTRL | TQt::Key_B ), this, TQT_SLOT( ban() ), ac, "ban" ) );
|
|
|
|
|
|
|
|
m_actionList.append( new KAction( i18n( "Love" ), Amarok::icon( "love" ),
|
|
|
|
KKey( TQt::CTRL | TQt::Key_L ), this, TQT_SLOT( love() ), ac, "love" ) );
|
|
|
|
|
|
|
|
m_actionList.append( new KAction( i18n( "Skip" ), Amarok::icon( "next" ),
|
|
|
|
KKey( TQt::CTRL | TQt::Key_K ), this, TQT_SLOT( skip() ), ac, "skip" ) );
|
|
|
|
setActionsEnabled( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Controller*
|
|
|
|
Controller::instance()
|
|
|
|
{
|
|
|
|
if( !s_instance ) s_instance = new Controller();
|
|
|
|
return s_instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KURL
|
|
|
|
Controller::getNewProxy( TQString genreUrl, bool useProxy )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
m_genreUrl = genreUrl;
|
|
|
|
|
|
|
|
if ( m_service ) playbackStopped();
|
|
|
|
|
|
|
|
WebService* service;
|
|
|
|
// m_service might have already been reset until changeStation() and/or handshare()
|
|
|
|
// calls return
|
|
|
|
service = m_service = new WebService( this, useProxy );
|
|
|
|
|
|
|
|
if( checkCredentials() )
|
|
|
|
{
|
|
|
|
TQString user = AmarokConfig::scrobblerUsername();
|
|
|
|
TQString pass = AmarokConfig::scrobblerPassword();
|
|
|
|
|
|
|
|
if( !user.isEmpty() && !pass.isEmpty() &&
|
|
|
|
service->handshake( user, pass ) )
|
|
|
|
{
|
|
|
|
bool ok = service->changeStation( m_genreUrl );
|
|
|
|
if( ok ) // else playbackStopped()
|
|
|
|
{
|
|
|
|
if( !AmarokConfig::submitPlayedSongs() )
|
|
|
|
m_service->enableScrobbling( false );
|
|
|
|
setActionsEnabled( true );
|
|
|
|
return KURL( m_service->proxyUrl() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (service->wasCanceled()) {
|
|
|
|
// It was canceled before (during kapp->processEvents() loop)
|
|
|
|
delete service;
|
|
|
|
return KURL("lastfm://"); // construct invalid url
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some kind of failure happened, so crap out
|
|
|
|
playbackStopped();
|
|
|
|
return KURL();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
Controller::changeStation( TQString url )
|
|
|
|
{
|
|
|
|
if (isPlaying()) {
|
|
|
|
WebService* service = getService();
|
|
|
|
if (service->changeStation( url )) {
|
|
|
|
return 1; // success
|
|
|
|
} else if (service->wasCanceled()) {
|
|
|
|
delete service;
|
|
|
|
return -1; // canceled
|
|
|
|
} else {
|
|
|
|
return 0; // failed
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return 0; // impossible, failed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Controller::playbackStopped() //SLOT
|
|
|
|
{
|
|
|
|
setActionsEnabled( false );
|
|
|
|
|
|
|
|
if (m_service) {
|
|
|
|
if (m_service->cancel())
|
|
|
|
delete m_service;;
|
|
|
|
m_service = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
Controller::checkCredentials() //static
|
|
|
|
{
|
|
|
|
if( AmarokConfig::scrobblerUsername().isEmpty() || AmarokConfig::scrobblerPassword().isEmpty() )
|
|
|
|
{
|
|
|
|
LoginDialog dialog( 0 );
|
|
|
|
dialog.setCaption( "last.fm" );
|
|
|
|
return dialog.exec() == TQDialog::Accepted;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString
|
|
|
|
Controller::createCustomStation() //static
|
|
|
|
{
|
|
|
|
TQString token;
|
|
|
|
CustomStationDialog dialog( 0 );
|
|
|
|
|
|
|
|
if( dialog.exec() == TQDialog::Accepted )
|
|
|
|
{
|
|
|
|
token = dialog.text();
|
|
|
|
}
|
|
|
|
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Controller::ban()
|
|
|
|
{
|
|
|
|
if( m_service )
|
|
|
|
m_service->ban();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Controller::love()
|
|
|
|
{
|
|
|
|
if( m_service )
|
|
|
|
m_service->love();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Controller::skip()
|
|
|
|
{
|
|
|
|
if( m_service )
|
|
|
|
m_service->skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Controller::setActionsEnabled( bool enable )
|
|
|
|
{ //pausing last.fm streams doesn't do anything good
|
|
|
|
Amarok::actionCollection()->action( "play_pause" )->setEnabled( !enable );
|
|
|
|
Amarok::actionCollection()->action( "pause" )->setEnabled( !enable );
|
|
|
|
|
|
|
|
KAction* action;
|
|
|
|
for( action = m_actionList.first(); action; action = m_actionList.next() )
|
|
|
|
action->setEnabled( enable );
|
|
|
|
}
|
|
|
|
|
|
|
|
/// return a translatable description of the station we are connected to
|
|
|
|
TQString
|
|
|
|
Controller::stationDescription( TQString url )
|
|
|
|
{
|
|
|
|
if( url.isEmpty() && instance() && instance()->isPlaying() )
|
|
|
|
url = instance()->getService()->currentStation();
|
|
|
|
|
|
|
|
if( url.isEmpty() ) return TQString();
|
|
|
|
|
|
|
|
TQStringList elements = TQStringList::split( "/", url );
|
|
|
|
|
|
|
|
/// TAG RADIOS
|
|
|
|
// eg: lastfm://globaltag/rock
|
|
|
|
if ( elements[1] == "globaltags" )
|
|
|
|
return i18n( "Global Tag Radio: %1" ).arg( elements[2] );
|
|
|
|
|
|
|
|
/// ARTIST RADIOS
|
|
|
|
if ( elements[1] == "artist" )
|
|
|
|
{
|
|
|
|
// eg: lastfm://artist/Queen/similarartists
|
|
|
|
if ( elements[3] == "similarartists" )
|
|
|
|
return i18n( "Similar Artists to %1" ).arg( elements[2] );
|
|
|
|
|
|
|
|
if ( elements[3] == "fans" )
|
|
|
|
return i18n( "Artist Fan Radio: %1" ).arg( elements[2] );
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CUSTOM STATION
|
|
|
|
if ( elements[1] == "artistnames" )
|
|
|
|
{
|
|
|
|
// eg: lastfm://artistnames/genesis,pink floyd,queen
|
|
|
|
|
|
|
|
// turn "genesis,pink floyd,queen" into "Genesis, Pink Floyd, Queen"
|
|
|
|
TQString artists = elements[2];
|
|
|
|
artists.replace( ",", ", " );
|
|
|
|
const TQStringList words = TQStringList::split( " ", TQString( artists ).remove( "," ) );
|
|
|
|
foreach( words ) {
|
|
|
|
TQString capitalized = *it;
|
|
|
|
capitalized.replace( 0, 1, (*it)[0].upper() );
|
|
|
|
artists.replace( *it, capitalized );
|
|
|
|
}
|
|
|
|
|
|
|
|
return i18n( "Custom Station: %1" ).arg( artists );
|
|
|
|
}
|
|
|
|
|
|
|
|
/// USER RADIOS
|
|
|
|
else if ( elements[1] == "user" )
|
|
|
|
{
|
|
|
|
// eg: lastfm://user/sebr/neighbours
|
|
|
|
if ( elements[3] == "neighbours" )
|
|
|
|
return i18n( "%1's Neighbor Radio" ).arg( elements[2] );
|
|
|
|
|
|
|
|
// eg: lastfm://user/sebr/personal
|
|
|
|
if ( elements[3] == "personal" )
|
|
|
|
return i18n( "%1's Personal Radio" ).arg( elements[2] );
|
|
|
|
|
|
|
|
// eg: lastfm://user/sebr/loved
|
|
|
|
if ( elements[3] == "loved" )
|
|
|
|
return i18n( "%1's Loved Radio" ).arg( elements[2] );
|
|
|
|
|
|
|
|
// eg: lastfm://user/sebr/recommended/100 : 100 is number for how obscure the music should be
|
|
|
|
if ( elements[3] == "recommended" )
|
|
|
|
return i18n( "%1's Recommended Radio" ).arg( elements[2] );
|
|
|
|
}
|
|
|
|
|
|
|
|
/// GROUP RADIOS
|
|
|
|
//eg: lastfm://group/Amarok%20users
|
|
|
|
else if ( elements[1] == "group" )
|
|
|
|
return i18n( "Group Radio: %1" ).arg( elements[2] );
|
|
|
|
|
|
|
|
/// TRACK RADIOS
|
|
|
|
else if ( elements[1] == "play" )
|
|
|
|
{
|
|
|
|
if ( elements[2] == "tracks" )
|
|
|
|
return i18n( "Track Radio" );
|
|
|
|
else if ( elements[2] == "artists" )
|
|
|
|
return i18n( "Artist Radio" );
|
|
|
|
}
|
|
|
|
//kaput!
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CLASS WebService
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
WebService::WebService( TQObject* parent, bool useProxy )
|
|
|
|
: TQObject( parent, "lastfmParent" )
|
|
|
|
, m_useProxy( useProxy )
|
|
|
|
, m_deletionUnsafe( false )
|
|
|
|
, m_wasCanceled( false )
|
|
|
|
{
|
|
|
|
debug() << "Initialising Web Service" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
WebService::~WebService()
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WebService::cancel() {
|
|
|
|
m_wasCanceled = true;
|
|
|
|
return !m_deletionUnsafe;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::readProxy() //SLOT
|
|
|
|
{
|
|
|
|
TQString line;
|
|
|
|
|
|
|
|
while( m_server->readln( line ) != -1 ) {
|
|
|
|
debug() << line << endl;
|
|
|
|
|
|
|
|
if( line == "AMAROK_PROXY: SYNC" )
|
|
|
|
requestMetaData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
WebService::handshake( const TQString& username, const TQString& password )
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
m_username = username;
|
|
|
|
m_password = password;
|
|
|
|
|
|
|
|
AmarokHttp http( "ws.audioscrobbler.com", 80 );
|
|
|
|
|
|
|
|
const TQString path =
|
|
|
|
TQString( "/radio/handshake.php?version=%1&platform=%2&username=%3&passwordmd5=%4&debug=%5" )
|
|
|
|
.arg( APP_VERSION ) //Muesli-approved: Amarok version, and Amarok-as-platform
|
|
|
|
.arg( TQString("Amarok") )
|
|
|
|
.arg( TQString( TQUrl( username ).encodedPathAndQuery() ) )
|
|
|
|
.arg( KMD5( m_password.utf8() ).hexDigest().data() )
|
|
|
|
.arg( "0" );
|
|
|
|
|
|
|
|
http.get( path );
|
|
|
|
|
|
|
|
// We don't know what might happen within processEvents() loop.
|
|
|
|
// Therefore this service instance must be protected from deletion.
|
|
|
|
m_deletionUnsafe = true;
|
|
|
|
do
|
|
|
|
kapp->processEvents();
|
|
|
|
while( http.state() != TQHttp::Unconnected );
|
|
|
|
m_deletionUnsafe = false;
|
|
|
|
if (this->wasCanceled())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( http.error() != TQHttp::NoError )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const TQString result( TQDeepCopy<TQString>( http.readAll() ) );
|
|
|
|
|
|
|
|
debug() << "result: " << result << endl;
|
|
|
|
|
|
|
|
m_session = parameter( "session", result );
|
|
|
|
m_baseHost = parameter( "base_url", result );
|
|
|
|
m_basePath = parameter( "base_path", result );
|
|
|
|
m_subscriber = parameter( "subscriber", result ) == "1";
|
|
|
|
m_streamUrl = TQUrl( parameter( "stream_url", result ) );
|
|
|
|
// bool banned = parameter( "banned", result ) == "1";
|
|
|
|
|
|
|
|
if ( m_session.lower() == "failed" ) {
|
|
|
|
Amarok::StatusBar::instance()->longMessage( i18n(
|
|
|
|
"Amarok failed to establish a session with last.fm. <br>"
|
|
|
|
"Check if your last.fm user and password are correctly set."
|
|
|
|
) );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Amarok::config( "Scrobbler" )->writeEntry( "Subscriber", m_subscriber );
|
|
|
|
if( m_useProxy )
|
|
|
|
{
|
|
|
|
// Find free port
|
|
|
|
MyServerSocket* socket = new MyServerSocket();
|
|
|
|
const int port = socket->port();
|
|
|
|
debug() << "Proxy server using port: " << port << endl;
|
|
|
|
delete socket;
|
|
|
|
|
|
|
|
m_proxyUrl = TQString( "http://localhost:%1/lastfm.mp3" ).arg( port );
|
|
|
|
|
|
|
|
m_server = new Amarok::ProcIO();
|
|
|
|
m_server->setComm( KProcess::Communication( KProcess::AllOutput ) );
|
|
|
|
*m_server << "amarok_proxy.rb";
|
|
|
|
*m_server << "--lastfm";
|
|
|
|
*m_server << TQString::number( port );
|
|
|
|
*m_server << m_streamUrl.toString();
|
|
|
|
*m_server << AmarokConfig::soundSystem();
|
|
|
|
*m_server << Amarok::proxyForUrl( m_streamUrl.toString() );
|
|
|
|
|
|
|
|
if( !m_server->start( KProcIO::NotifyOnExit, true ) ) {
|
|
|
|
error() << "Failed to start amarok_proxy.rb" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString line;
|
|
|
|
m_deletionUnsafe = true;
|
|
|
|
while( true ) {
|
|
|
|
kapp->processEvents();
|
|
|
|
m_server->readln( line );
|
|
|
|
if( line == "AMAROK_PROXY: startup" ) break;
|
|
|
|
}
|
|
|
|
m_deletionUnsafe = false;
|
|
|
|
if (this->wasCanceled())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
connect( m_server, TQT_SIGNAL( readReady( KProcIO* ) ), this, TQT_SLOT( readProxy() ) );
|
|
|
|
connect( m_server, TQT_SIGNAL( processExited( KProcess* ) ), Controller::instance(), TQT_SLOT( playbackStopped() ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_proxyUrl = m_streamUrl.toString();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
WebService::changeStation( TQString url )
|
|
|
|
{
|
|
|
|
debug() << "Changing station:" << url << endl;
|
|
|
|
|
|
|
|
AmarokHttp http( m_baseHost, 80 );
|
|
|
|
|
|
|
|
http.get( TQString( m_basePath + "/adjust.php?session=%1&url=%2&debug=0" )
|
|
|
|
.arg( m_session )
|
|
|
|
.arg( url ) );
|
|
|
|
|
|
|
|
m_deletionUnsafe = true;
|
|
|
|
do
|
|
|
|
kapp->processEvents();
|
|
|
|
while( http.state() != TQHttp::Unconnected );
|
|
|
|
m_deletionUnsafe = false;
|
|
|
|
if (this->wasCanceled())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( http.error() != TQHttp::NoError )
|
|
|
|
{
|
|
|
|
showError( E_OTHER ); // default error
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString result( TQDeepCopy<TQString>( http.readAll() ) );
|
|
|
|
const int errCode = parameter( "error", result ).toInt();
|
|
|
|
|
|
|
|
if ( errCode )
|
|
|
|
{
|
|
|
|
showError( errCode );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString _url = parameter( "url", result );
|
|
|
|
if ( _url.startsWith( "lastfm://" ) )
|
|
|
|
{
|
|
|
|
m_station = _url; // parse it in stationDescription
|
|
|
|
emit stationChanged( _url, m_station );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
emit stationChanged( _url, TQString() );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::requestMetaData() //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( int, bool ) ), this, TQT_SLOT( metaDataFinished( int, bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( m_basePath + "/np.php?session=%1&debug=%2" )
|
|
|
|
.arg( m_session )
|
|
|
|
.arg( "0" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::metaDataFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if( error ) return;
|
|
|
|
|
|
|
|
const TQString result( http->readAll() );
|
|
|
|
debug() << result << endl;
|
|
|
|
|
|
|
|
int errCode = parameter( "error", result ).toInt();
|
|
|
|
if ( errCode > 0 ) {
|
|
|
|
debug() << "Metadata failed with error code: " << errCode << endl;
|
|
|
|
showError( errCode );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_metaBundle.setArtist( parameter( "artist", result ) );
|
|
|
|
m_metaBundle.setAlbum ( parameter( "album", result ) );
|
|
|
|
m_metaBundle.setTitle ( parameter( "track", result ) );
|
|
|
|
m_metaBundle.setUrl ( KURL( Controller::instance()->getGenreUrl() ) );
|
|
|
|
m_metaBundle.setLength( parameter( "trackduration", result ).toInt() );
|
|
|
|
|
|
|
|
Bundle lastFmStuff;
|
|
|
|
TQString imageUrl = parameter( "albumcover_medium", result );
|
|
|
|
|
|
|
|
if( imageUrl == "http://static.last.fm/coverart/" ||
|
|
|
|
imageUrl == "http://static.last.fm/depth/catalogue/no_album_large.gif" )
|
|
|
|
imageUrl = TQString();
|
|
|
|
|
|
|
|
lastFmStuff.setImageUrl ( CollectionDB::instance()->notAvailCover( true ) );
|
|
|
|
lastFmStuff.setArtistUrl( parameter( "artist_url", result ) );
|
|
|
|
lastFmStuff.setAlbumUrl ( parameter( "album_url", result ) );
|
|
|
|
lastFmStuff.setTitleUrl ( parameter( "track_url", result ) );
|
|
|
|
// bool discovery = parameter( "discovery", result ) != "-1";
|
|
|
|
|
|
|
|
m_metaBundle.setLastFmBundle( lastFmStuff );
|
|
|
|
|
|
|
|
const KURL u( imageUrl );
|
|
|
|
if( !u.isValid() ) {
|
|
|
|
debug() << "imageUrl empty or invalid." << endl;
|
|
|
|
emit metaDataResult( m_metaBundle );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KIO::Job* job = KIO::storedGet( u, true, false );
|
|
|
|
connect( job, TQT_SIGNAL( result( KIO::Job* ) ), this, TQT_SLOT( fetchImageFinished( KIO::Job* ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::fetchImageFinished( KIO::Job* job ) //SLOT
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
if( job->error() == 0 ) {
|
|
|
|
const TQString path = Amarok::saveLocation() + "lastfm_image.png";
|
|
|
|
const int size = AmarokConfig::coverPreviewSize();
|
|
|
|
|
|
|
|
TQImage img( static_cast<KIO::StoredTransferJob*>( job )->data() );
|
|
|
|
img.smoothScale( size, size ).save( path, "PNG" );
|
|
|
|
|
|
|
|
m_metaBundle.lastFmBundle()->setImageUrl( CollectionDB::makeShadowedImage( path, false ) );
|
|
|
|
}
|
|
|
|
emit metaDataResult( m_metaBundle );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::enableScrobbling( bool enabled ) //SLOT
|
|
|
|
{
|
|
|
|
if ( enabled )
|
|
|
|
debug() << "Enabling Scrobbling!" << endl;
|
|
|
|
else
|
|
|
|
debug() << "Disabling Scrobbling!" << endl;
|
|
|
|
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( int, bool ) ), this, TQT_SLOT( enableScrobblingFinished( int, bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( m_basePath + "/control.php?session=%1&command=%2&debug=%3" )
|
|
|
|
.arg( m_session )
|
|
|
|
.arg( enabled ? TQString( "rtp" ) : TQString( "nortp" ) )
|
|
|
|
.arg( "0" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::enableScrobblingFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if ( error ) return;
|
|
|
|
|
|
|
|
emit enableScrobblingDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::love() //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( int, bool ) ), this, TQT_SLOT( loveFinished( int, bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( m_basePath + "/control.php?session=%1&command=love&debug=%2" )
|
|
|
|
.arg( m_session )
|
|
|
|
.arg( "0" ) );
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( i18n("love, as in affection", "Loving song...") );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::skip() //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( int, bool ) ), this, TQT_SLOT( skipFinished( int, bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( m_basePath + "/control.php?session=%1&command=skip&debug=%2" )
|
|
|
|
.arg( m_session )
|
|
|
|
.arg( "0" ) );
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( i18n("Skipping song...") );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::ban() //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( int, bool ) ), this, TQT_SLOT( banFinished( int, bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( m_basePath + "/control.php?session=%1&command=ban&debug=%2" )
|
|
|
|
.arg( m_session )
|
|
|
|
.arg( "0" ) );
|
|
|
|
Amarok::StatusBar::instance()->shortMessage( i18n("Ban, as in dislike", "Banning song...") );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::loveFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if( error ) return;
|
|
|
|
|
|
|
|
emit loveDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::skipFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if( error ) return;
|
|
|
|
|
|
|
|
EngineController::engine()->flushBuffer();
|
|
|
|
emit skipDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::banFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
DEBUG_BLOCK
|
|
|
|
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if( error ) return;
|
|
|
|
|
|
|
|
EngineController::engine()->flushBuffer();
|
|
|
|
emit banDone();
|
|
|
|
emit skipDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::friends( TQString username )
|
|
|
|
{
|
|
|
|
if ( username.isEmpty() )
|
|
|
|
username = m_username;
|
|
|
|
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( bool ) ), this, TQT_SLOT( friendsFinished( bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( "/1.0/user/%1/friends.xml" )
|
|
|
|
.arg( TQString( TQUrl( username ).encodedPathAndQuery() ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::friendsFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if( error ) return;
|
|
|
|
|
|
|
|
TQDomDocument document;
|
|
|
|
document.setContent( http->readAll() );
|
|
|
|
|
|
|
|
if ( document.elementsByTagName( "friends" ).length() == 0 )
|
|
|
|
{
|
|
|
|
emit friendsResult( TQString( "" ), TQStringList() );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList friends;
|
|
|
|
TQString user = document.elementsByTagName( "friends" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
|
|
|
|
TQDomNodeList values = document.elementsByTagName( "user" );
|
|
|
|
for ( uint i = 0; i < values.count(); i++ )
|
|
|
|
{
|
|
|
|
friends << values.item( i ).attributes().namedItem( "username" ).nodeValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit friendsResult( user, friends );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::neighbours( TQString username )
|
|
|
|
{
|
|
|
|
if ( username.isEmpty() )
|
|
|
|
username = m_username;
|
|
|
|
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( bool ) ), this, TQT_SLOT( neighboursFinished( bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( "/1.0/user/%1/neighbours.xml" )
|
|
|
|
.arg( TQString( TQUrl( username ).encodedPathAndQuery() ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::neighboursFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if( error ) return;
|
|
|
|
|
|
|
|
TQDomDocument document;
|
|
|
|
document.setContent( http->readAll() );
|
|
|
|
|
|
|
|
if ( document.elementsByTagName( "neighbours" ).length() == 0 )
|
|
|
|
{
|
|
|
|
emit friendsResult( TQString( "" ), TQStringList() );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList neighbours;
|
|
|
|
TQString user = document.elementsByTagName( "neighbours" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
|
|
|
|
TQDomNodeList values = document.elementsByTagName( "user" );
|
|
|
|
for ( uint i = 0; i < values.count(); i++ )
|
|
|
|
{
|
|
|
|
neighbours << values.item( i ).attributes().namedItem( "username" ).nodeValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit neighboursResult( user, neighbours );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::userTags( TQString username )
|
|
|
|
{
|
|
|
|
if ( username.isEmpty() )
|
|
|
|
username = m_username;
|
|
|
|
|
|
|
|
AmarokHttp *http = new AmarokHttp( m_baseHost, 80, this );
|
|
|
|
connect( http, TQT_SIGNAL( requestFinished( bool ) ), this, TQT_SLOT( userTagsFinished( bool ) ) );
|
|
|
|
|
|
|
|
http->get( TQString( "/1.0/user/%1/tags.xml?debug=%2" )
|
|
|
|
.arg( TQString( TQUrl( username ).encodedPathAndQuery() ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::userTagsFinished( int /*id*/, bool error ) //SLOT
|
|
|
|
{
|
|
|
|
AmarokHttp* http = (AmarokHttp*) sender();
|
|
|
|
http->deleteLater();
|
|
|
|
if( error ) return;
|
|
|
|
|
|
|
|
TQDomDocument document;
|
|
|
|
document.setContent( http->readAll() );
|
|
|
|
|
|
|
|
if ( document.elementsByTagName( "toptags" ).length() == 0 )
|
|
|
|
{
|
|
|
|
emit userTagsResult( TQString(), TQStringList() );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQStringList tags;
|
|
|
|
TQDomNodeList values = document.elementsByTagName( "tag" );
|
|
|
|
TQString user = document.elementsByTagName( "toptags" ).item( 0 ).attributes().namedItem( "user" ).nodeValue();
|
|
|
|
for ( uint i = 0; i < values.count(); i++ )
|
|
|
|
{
|
|
|
|
TQDomNode item = values.item( i ).namedItem( "name" );
|
|
|
|
tags << item.toElement().text();
|
|
|
|
}
|
|
|
|
emit userTagsResult( user, tags );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
WebService::recentTracks( TQString username )
|
|
|
|
{
|
|