XBMC-Remote-code-example-using-Qt-C
Creating an XBMC remote using Qt C++
I wanted to create an XBMC remote, but I couldn't find any tutorials of how to do this. So after numerous hours of Googling and reading forums, I decided to write my own… This is written for the JSON Http Api Frodo. Found Here
This is a simple tutorial that will set you up with the basics of sending the Pause/Play command to XBMC over Http..
Lets get started…
To start I created a standard Qt Gui Application. Then I added libraries.. XBMC now uses JSON for communicating with its Http interface. Unfortunately Qt doesn't come with a JSON parsing library..
However one has been built by Flavio Castelli: Found here
we need to add it to our project so its included in the build. On Ubuntu I downloaded the qjson dev files in Synaptic Package Manager and just added these lines to the .pro file:
INCLUDEPATH += /usr/include/qjson
LIBS+= -L/usr/local/lib -lqjson
Windows might be slightly different… Stackoverflow has a few posts on it.
We then also need to add networking to the project as well.. So we add this line to our .pro file:
QT += network
Setting up the Connection
first we need to set up the variables needed for the connection. For this we need a QNetworkRequest, QNetworkAccessManager and strings for username, password, hostname/ip and port. So add these to your header file.
QNetworkRequest req;
QNetworkAccessManager *manager;
QString ip;
QString port;
QString user;
QString pass;
then we need to set them up.. so we shall make a method that we can call from the constructor to set up the access manager and network request.. this method will set he request URL and header. It also links the network manager to a Authenticator Slot that provides the username and password. I only set the access details here for convenience, it would be better to let the user set them by a line edit.
//sets up connection objects
void xbmcRemote::setUpConnection()
{
//Access Details
ip = "192.168.1.2";
port = "80";
user = "xbmc";
pass= "somePassword";
//set request details
req.setUrl(QUrl("http://"'' ip + ":" + port + "/jsonrpc"));
req.setHeader(QNetworkRequest::ContentTypeHeader,"application/json;");
manager = new QNetworkAccessManager (this);
//connect to authenticator
connect(manager, SIGNAL (authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT (provideAuthenication(QNetworkReply*,QAuthenticator*)));
//Now create a QCookieJar:
manager->setCookieJar(new QNetworkCookieJar(manager));
}
//authenticator slot
void xbmcRemote::provideAuthenication(QNetworkReply *reply, QAuthenticator '''ator)
{
ator->setUser(QString(user));
ator->setPassword(QString(pass));
}
These will provide the connection to XBMC.. So we can now call the access manager to post requests to XBMC.
Parsing the reply
Now we will set up a method to parse the reply. This is where we use QJson. It will parse the reply and return a QVariantMap that can be used to access info from the reply
QVariantMap xbmcRemote::parseReply(QString json)
{
//allows for you to see what the request is replying with
qDebug() << "parsing data";
qDebug() << json;
QJson::Parser parser;
bool ok;
QByteArray jsonData = json.toStdString().c_str();
QVariantMap result = parser.parse (jsonData, &ok).toMap();
if (!ok)
{
qDebug() << "An error occurred during parsing";
//exit (1);
}
return result;
}
Post Play/Pause request
Now the fun part.. We will post a play pause request to XBMC. This is done in 2 stages.. the first is to build the request and send it.. the second part is to accept and parse the reply.
To post the request we create a slot that can be connected to Ui items. (In this case a play/pause button).note*: I use the 'playerid' of 1, but on more complex remote it would be prudent to get the if from XBMC.
void xbmcRemote::on_pauseButton_clicked()
{
QByteArray b;
b.append("{quot;jsonrpcquot;:quot;2.0quot;,quot;methodquot;:quot;Player.PlayPausequot;, quot;paramsquot;: {quot;playeridquot; : 1},quot;idquot;:1}");
//connects the request manager to slot for dealing with the reply
connect (manager, SIGNAL (finished(QNetworkReply *)), this, SLOT (pauseButtonFinish (QNetworkReply *)));
//Send the request:
manager->post(req, b);
}
The request JSON without the formatting to make it a valid QString itself looks like:
{"jsonrpc":"2.0", "method":"Player.PlayPause", "params": {"playerid" : 1},"id":1}
Now we have the code to post the request, we need a slot to deal with the reply. This slot will disconnect the request manager from itself. Then parse the reply data and use it accordingly
void xbmcRemote::pauseButtonFinish(QNetworkReply *reply)
{
//disconnects this slot with the request manager, so it isn't called every time a request is made
disconnect (manager, SIGNAL (finished(QNetworkReply *)), this, SLOT (pauseButtonFinish (QNetworkReply *)));
//gets the parsed JSON data
QVariantMap replyData = parseReply(QString::fromUtf8(reply->readAll()));
//gets the results of the play/Pause request
QVariantMap resultValues = replyData["result"].toMap();
//speed tells us if the video is playing or not
QString speed = resultValues["speed"].toString();
if(speed.toInt())
{
ui->statusBar->showMessage("Playing",700);
}
else
{
ui->statusBar->showMessage("Paused",700);;
}
}
and thats our pause play request done!
You can now build on this to make a full XBMC controller. A handy hint to find out what needs sent and what is replied is to get Firefox and get the addon "Live HTTP Headers". Then use XBMCs web interface and use live http headers to see the requests it sends and what it gets in response..
files in full:
header file:
#ifndef XBMCREMOTE_H
#define XBMCREMOTE_H
#include <QMainWindow>
#include <qjson/parser.h>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QVariantMap>
#include <QNetworkCookieJar>
#include <QByteArray>
#include <QDebug>
#include <QAuthenticator>
namespace Ui {
class xbmcRemote;
}
class xbmcRemote : public QMainWindow
{
Q_OBJECT
public:
explicit xbmcRemote(QWidget *parent = 0);
~jsonsTests();
private:
Ui::xbmcRemote *ui;
QNetworkRequest req;
QString ip;
QString port;
QString user;
QString pass;
QNetworkAccessManager *manager;
void setUpConnection(void);
QVariantMap parseReply(QString json);
private slots:
void pauseButtonFinish(QNetworkReply *reply);
public slots:
void on_pauseButton_clicked();
};
#endif // XBMCREMOTE_H
cpp file:
#include "xbmcremote.h"
#include "ui_xbmcremote.h"
xbmcRemote::xbmcRemote(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::xbmcRemote)
{
ui->setupUi(this);
this->setWindowTitle("XBMC Remote");
setUpConnection();
}
xbmcRemote::~xbmcRemote()
{
delete ui;
}
//sets up connection objects
void xbmcRemote::setUpConnection()
{
//Access Details
ip = "192.168.1.2";
port = "80";
user = "xbmc";
pass= "somePassword";
//set request details
req.setUrl(QUrl("http://" + ip + ":" + port + "/jsonrpc"));
req.setHeader(QNetworkRequest::ContentTypeHeader,"application/json;");
manager = new QNetworkAccessManager (this);
//connect to authenticator
connect(manager, SIGNAL (authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT (provideAuthenication(QNetworkReply*,QAuthenticator*)));
//Now create a QCookieJar:
manager->setCookieJar(new QNetworkCookieJar(manager));
}
//authenticator slot
void xbmcRemote::provideAuthenication(QNetworkReply *reply, QAuthenticator *ator)
{
ator->setUser(QString(user));
ator->setPassword(QString(pass));
}
QVariantMap xbmcRemote::parseReply(QString json)
{
//allows for you to see what the request is replying with
qDebug() << "parsing data";
qDebug() << json;
QJson::Parser parser;
bool ok;
QByteArray jsonData = json.toStdString().c_str();
QVariantMap result = parser.parse (jsonData, &ok).toMap();
if (!ok)
{
qDebug() << "An error occurred during parsing";
//exit (1);
}
return result;
}
void xbmcRemote::on_pauseButton_clicked()
{
QByteArray b;
b.append("{quot;jsonrpcquot;:quot;2.0quot;,quot;methodquot;:quot;Player.PlayPausequot;, quot;paramsquot;: {quot;playeridquot; : 1},quot;idquot;:1}");
//connects the request manager to slot for dealing with the reply
connect (manager, SIGNAL (finished(QNetworkReply *)), this, SLOT (pauseButtonFinish (QNetworkReply *)));
//Send the request:
manager->post(req, b);
}
void xbmcRemote::pauseButtonFinish(QNetworkReply *reply)
{
//disconnects this slot with the request manager, so it isn't called every time a request is made
disconnect (manager, SIGNAL (finished(QNetworkReply *)), this, SLOT (pauseButtonFinish (QNetworkReply *)));
//gets the parsed JSON data
QVariantMap replyData = parseReply(QString::fromUtf8(reply->readAll()));
//gets the results of the play/Pause request
QVariantMap resultValues = replyData["result"].toMap();
//speed tells us if the video is playing or not
QString speed = resultValues["speed"].toString();
if(speed.toInt())
{
ui->statusBar->showMessage("Playing",700);
}
else
{
ui->statusBar->showMessage("Paused",700);;
}