diff --git a/src/libtomahawk-playdarapi/Api_v1.cpp b/src/libtomahawk-playdarapi/Api_v1.cpp index b58bf6e2e..8ab71ea2f 100644 --- a/src/libtomahawk-playdarapi/Api_v1.cpp +++ b/src/libtomahawk-playdarapi/Api_v1.cpp @@ -28,6 +28,7 @@ #include "utils/Logger.h" #include "utils/TomahawkUtils.h" +#include "Api_v1_5.h" #include "Pipeline.h" #include "Result.h" #include "Source.h" @@ -41,9 +42,15 @@ using namespace TomahawkUtils; Api_v1::Api_v1( QxtAbstractWebSessionManager* sm, QObject* parent ) : QxtWebSlotService(sm, parent) + , m_api_v1_5( new Api_v1_5( this ) ) { } +Api_v1::~Api_v1() +{ + delete m_api_v1_5; +} + void Api_v1::auth_1( QxtWebRequestEvent* event, QString arg ) { @@ -155,23 +162,67 @@ Api_v1::auth_2( QxtWebRequestEvent* event, QString arg ) } -// all v1 api calls go to /api/ +/** + * Handle API calls. + * + * All v1.0 API (standard Playdar) calls go to /api/?method= + * All v1.5 API (simple remote control) calls go to /api/1.5/ + */ void -Api_v1::api( QxtWebRequestEvent* event ) +Api_v1::api( QxtWebRequestEvent* event, const QString& version, const QString& method, const QString& arg1, const QString& arg2, const QString& arg3 ) { tDebug( LOGVERBOSE ) << "HTTP" << event->url.toString(); - const QUrl& url = event->url; - if ( urlHasQueryItem( url, "method" ) ) - { - const QString method = urlQueryItemValue( url, "method" ); + if ( version.isEmpty() ) { + // We dealing with API 1.0 - if ( method == "stat" ) return stat( event ); - if ( method == "resolve" ) return resolve( event ); - if ( method == "get_results" ) return get_results( event ); + const QUrl& url = event->url; + if ( urlHasQueryItem( url, "method" ) ) + { + const QString method = urlQueryItemValue( url, "method" ); + + if ( method == "stat" ) return stat( event ); + if ( method == "resolve" ) return resolve( event ); + if ( method == "get_results" ) return get_results( event ); + } + + send404( event ); + } + else if ( version == "1.5" ) + { + if ( !arg3.isEmpty() ) + { + if ( !QMetaObject::invokeMethod( m_api_v1_5, method.toLatin1().constData(), Q_ARG( QxtWebRequestEvent*, event ), Q_ARG( QString, arg1 ), Q_ARG( QString, arg2 ), Q_ARG( QString, arg3 ) ) ) + { + apiCallFailed(event, method); + } + } + else if ( !arg2.isEmpty() ) + { + if ( !QMetaObject::invokeMethod( m_api_v1_5, method.toLatin1().constData(), Q_ARG( QxtWebRequestEvent*, event ), Q_ARG( QString, arg1 ), Q_ARG( QString, arg2 ) ) ) + { + apiCallFailed(event, method); + } + } + else if ( !arg1.isEmpty() ) + { + if ( !QMetaObject::invokeMethod( m_api_v1_5, method.toLatin1().constData(), Q_ARG( QxtWebRequestEvent*, event ), Q_ARG( QString, arg1 ) ) ) + { + apiCallFailed(event, method); + } + } + else + { + if ( !QMetaObject::invokeMethod( m_api_v1_5, method.toLatin1().constData(), Q_ARG( QxtWebRequestEvent*, event ) ) ) + { + apiCallFailed(event, method); + } + } + } + else + { + sendPlain404( event, QString( "Unknown API version %1" ).arg( version ), "API version not found" ); } - - send404( event ); } @@ -226,6 +277,16 @@ Api_v1::send404( QxtWebRequestEvent* event ) postEvent( wpe ); } +void +Api_v1::sendPlain404( QxtWebRequestEvent* event, const QString& message, const QString& statusmessage ) +{ + QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, message.toUtf8() ); + e->contentType = "text/plain"; + e->status = 404; + e->statusMessage = statusmessage.toLatin1().constData(); + postEvent( e ); +} + void Api_v1::stat( QxtWebRequestEvent* event ) @@ -411,6 +472,7 @@ Api_v1::sendJSON( const QVariantMap& m, QxtWebRequestEvent* event ) QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, body ); e->contentType = ctype; e->headers.insert( "Content-Length", QString::number( body.length() ) ); + e->headers.insert( "Access-Control-Allow-Origin", "*" ); postEvent( e ); tDebug( LOGVERBOSE ) << "JSON response" << event->url.toString() << body; } @@ -447,3 +509,30 @@ Api_v1::index( QxtWebRequestEvent* event ) { send404( event ); } + +void +Api_v1::apiCallFailed( QxtWebRequestEvent* event, const QString& method ) +{ + sendPlain404( event, QString( "Method \"%1\" for API 1.5 not found" ).arg( method ), "Method in API 1.5 not found" ); +} + +void +Api_v1::sendJsonOk( QxtWebRequestEvent* event ) +{ + QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, "{ result: \"ok\" }" ); + e->headers.insert( "Access-Control-Allow-Origin", "*" ); + e->contentType = "application/json"; + postEvent( e ); +} + + +void +Api_v1::sendJsonError( QxtWebRequestEvent* event, const QString& message ) +{ + QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, QString( "{ result: \"error\", error: \"%1\" }" ).arg( message ).toUtf8().constData() ); + e->headers.insert( "Access-Control-Allow-Origin", "*" ); + e->contentType = "application/json"; + e->status = 500; + e->statusMessage = "Method call failed."; + postEvent( e ); +} diff --git a/src/libtomahawk-playdarapi/Api_v1.h b/src/libtomahawk-playdarapi/Api_v1.h index 25c2cccda..88b12b2f5 100644 --- a/src/libtomahawk-playdarapi/Api_v1.h +++ b/src/libtomahawk-playdarapi/Api_v1.h @@ -39,6 +39,8 @@ #include #include +class Api_v1_5; + namespace Tomahawk { class Result; @@ -51,6 +53,7 @@ Q_OBJECT public: Api_v1( QxtAbstractWebSessionManager* sm, QObject* parent = 0 ); + virtual ~Api_v1(); public slots: // authenticating uses /auth_1 @@ -59,7 +62,12 @@ public slots: void auth_2( QxtWebRequestEvent* event, QString unused = QString() ); // all v1 api calls go to /api/ - void api( QxtWebRequestEvent* event ); + void api( QxtWebRequestEvent* event, + const QString& version = QString(), + const QString& method = QString(), + const QString& arg1 = QString(), + const QString& arg2 = QString(), + const QString& arg3 = QString() ); // request for stream: /sid/ void sid( QxtWebRequestEvent* event, QString unused = QString() ); @@ -74,15 +82,22 @@ public slots: // load an html template from a file, replace args from map // then serve + void sendJsonError( QxtWebRequestEvent* event, const QString& message ); + void sendJsonOk( QxtWebRequestEvent* event ); void sendWebpageWithArgs( QxtWebRequestEvent* event, const QString& filenameSource, const QHash< QString, QString >& args ); void index( QxtWebRequestEvent* event ); +protected: + void apiCallFailed( QxtWebRequestEvent* event, const QString& method ); + void sendPlain404( QxtWebRequestEvent* event, const QString& message, const QString& statusmessage ); + private: void processSid( QxtWebRequestEvent* event, Tomahawk::result_ptr&, QSharedPointer< QIODevice >& ); QxtWebRequestEvent* m_storedEvent; QSharedPointer< QIODevice > m_ioDevice; + Api_v1_5* m_api_v1_5; }; #endif diff --git a/src/libtomahawk-playdarapi/Api_v1_5.cpp b/src/libtomahawk-playdarapi/Api_v1_5.cpp new file mode 100644 index 000000000..b122b9e45 --- /dev/null +++ b/src/libtomahawk-playdarapi/Api_v1_5.cpp @@ -0,0 +1,100 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2014, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "Api_v1_5.h" + +#include "Api_v1.h" + +#include "audio/AudioEngine.h" +#include "utils/Logger.h" + +// Assumptions: QxtWebRequestEvent instance is called event and result is true on success +#define JSON_REPLY( result, message ) jsonReply( event, Q_FUNC_INFO, message, !result ) + + +Api_v1_5::Api_v1_5( Api_v1* parent ) + : QObject( parent ) + , m_service( parent ) +{ +} + + +void +Api_v1_5::ping( QxtWebRequestEvent* event ) +{ + QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, "pong" ); + e->headers.insert( "Access-Control-Allow-Origin", "*" ); + e->contentType = "text/plain"; + m_service->postEvent( e ); +} + +void +Api_v1_5::playback( QxtWebRequestEvent* event, const QString& command ) +{ + if ( command == "next") + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "next", Qt::QueuedConnection ) , "Skipping to the next track failed." ); + } + else if ( command == "previous" ) + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "previous", Qt::QueuedConnection ), "Rewinding to the previous track failed." ); + } + else if ( command == "playpause" ) + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "playPause", Qt::QueuedConnection ), "Play/Pause failed." ); + } + else if ( command == "play" ) + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "play", Qt::QueuedConnection ), "Starting the playback failed." ); + } + else if ( command == "pause" ) + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "pause", Qt::QueuedConnection ), "Pausing the current track failed." ); + } + else if ( command == "stop" ) + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "stop", Qt::QueuedConnection ), "Stopping the current track failed." ); + } + else if ( command == "lowervolume" ) + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "lowerVolume", Qt::QueuedConnection ), "Lowering volume failed." ); + } + else if ( command == "raisevolume" ) + { + JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "raiseVolume", Qt::QueuedConnection ), "Raising volume failed." ); + } + else + { + m_service->sendJsonError( event, "No such playback command." ); + } +} + +void +Api_v1_5::jsonReply( QxtWebRequestEvent* event, const char* funcInfo, const QString& errorMessage, bool isError ) +{ + if ( isError ) + { + tLog( LOGVERBOSE ) << funcInfo << errorMessage; + m_service->sendJsonError( event, errorMessage ); + } + else + { + m_service->sendJsonOk( event ); + } +} + diff --git a/src/libtomahawk-playdarapi/Api_v1_5.h b/src/libtomahawk-playdarapi/Api_v1_5.h new file mode 100644 index 000000000..bab5af035 --- /dev/null +++ b/src/libtomahawk-playdarapi/Api_v1_5.h @@ -0,0 +1,55 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2014, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef API_V1_5_H +#define API_V1_5_H + +#include + +class Api_v1; +class QxtWebRequestEvent; + +class Api_v1_5 : public QObject +{ + Q_OBJECT +public: + Api_v1_5( Api_v1* parent = 0 ); + +signals: + +public slots: + /** + * Simple test to check for API 1.5 support. + * + * This call needs no authentication. + */ + void ping( QxtWebRequestEvent* event ); + + /** + * Control playback. + */ + void playback( QxtWebRequestEvent* event, const QString& command ); + +protected: + void jsonReply( QxtWebRequestEvent* event, const char* funcInfo, const QString& errorMessage, bool isError ); + +private: + Api_v1* m_service; +}; + +#endif // API_V1_5_H diff --git a/src/libtomahawk-playdarapi/CMakeLists.txt b/src/libtomahawk-playdarapi/CMakeLists.txt index 8afd25c43..bf251ab6f 100644 --- a/src/libtomahawk-playdarapi/CMakeLists.txt +++ b/src/libtomahawk-playdarapi/CMakeLists.txt @@ -3,6 +3,7 @@ set(TOMAHAWK_PLAYDARAPI_LIBRARY_TARGET tomahawk-playdarapi) list(APPEND ${TOMAHAWK_PLAYDARAPI_LIBRARY_TARGET}_SOURCES Api_v1.cpp + Api_v1_5.cpp PlaydarApi.cpp )