1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-20 07:49:42 +01:00

Merge pull request #220 from tomahawk-player/webapi-1.5

Add basic remote control API
This commit is contained in:
Uwe L. Korn 2014-01-29 02:51:58 -08:00
commit 0f175bcc37
5 changed files with 272 additions and 12 deletions

View File

@ -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 );
}

View File

@ -39,6 +39,8 @@
#include <QSharedPointer>
#include <QStringList>
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/<id>
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

View File

@ -0,0 +1,100 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2014, Uwe L. Korn <uwelk@xhochy.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 );
}
}

View File

@ -0,0 +1,55 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2014, Uwe L. Korn <uwelk@xhochy.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef API_V1_5_H
#define API_V1_5_H
#include <QObject>
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

View File

@ -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
)