XBMC-Remote-code-example-using-Qt-C

From Qt Wiki
Revision as of 09:44, 24 February 2015 by Maintenance script (talk | contribs)
Jump to navigation Jump to search

h2. 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":http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6

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":http://qjson.sourceforge.net/

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
<br />LIBS''= -L/usr/local/lib <s>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 <code>
<br />h3. Setting up the Connection
<br />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.
<br />

QNetworkRequest req;
QNetworkAccessManager *manager;
QString ip;
QString port;
QString user;
QString pass;

<br />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..<br />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. 
<br />


//sets up connection objects
void xbmcRemote::setUpConnection()
{
//Access Details
ip = "192.168.1.2&quot;;
port = "80&quot;;
user = "xbmc&quot;;
pass= "somePassword&quot;;


//set request details
req.setUrl(QUrl("http://&quot; ip + ":" + port + "/jsonrpc&quot;));
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));
}

<br />These will provide the connection to XBMC.. So we can now call the access manager to post requests to XBMC.
<br />h3. Parsing the reply
<br />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
<br />

QVariantMap xbmcRemote::parseReply(QString json)
{
//allows for you to see what the request is replying with
qDebug() << "parsing data&quot;;
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&quot;;
//exit (1);
}


return result;
}

<br />h3. Post Play/Pause request
<br />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.
<br />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&quot;:"2.0&quot;, "method&quot;:"Player.PlayPause&quot;, "params&quot;: {"playerid&quot; : 1},"id&quot;: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)<br />{<br /> //disconnects this slot with the request manager, so it isn't called every time a request is made<br /> disconnect (manager, SIGNAL (finished(QNetworkReply *)), this, SLOT (pauseButtonFinish (QNetworkReply  *)));

//gets the parsed JSON data<br /> QVariantMap replyData = parseReply(QString::fromUtf8(reply-&gt;readAll()));

//gets the results of the play/Pause request<br /> QVariantMap resultValues = replyData[&quot;result&amp;quot;].toMap();

//speed tells us if the video is playing or not<br /> QString speed = resultValues[&quot;speed&amp;quot;].toString();<br /> if(speed.toInt())<br /> {<br /> ui-&gt;statusBar-&gt;showMessage(&quot;Playing&amp;quot;,700);<br /> }<br /> else<br /> {<br /> ui-&gt;statusBar-&gt;showMessage(&quot;Paused&amp;quot;,700);;<br /> }

}

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&quot;. 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<br />#define XBMCREMOTE_H

#include &lt;QMainWindow&amp;gt;<br />#include &lt;qjson/parser.h&amp;gt;<br />#include &lt;QNetworkAccessManager&amp;gt;<br />#include &lt;QUrl&amp;gt;<br />#include &lt;QNetworkRequest&amp;gt;<br />#include &lt;QNetworkReply&amp;gt;<br />#include &lt;QVariantMap&amp;gt;<br />#include &lt;QNetworkCookieJar&amp;gt;<br />#include &lt;QByteArray&amp;gt;<br />#include &lt;QDebug&amp;gt;<br />#include &lt;QAuthenticator&amp;gt;

namespace Ui {<br />class xbmcRemote;<br />}

class xbmcRemote : public QMainWindow<br />{<br /> Q_OBJECT

public:<br /> explicit xbmcRemote(QWidget *parent = 0);<br /> ~jsonsTests();

private:<br /> Ui::xbmcRemote *ui;<br /> QNetworkRequest req;<br /> QString ip;<br /> QString port;<br /> QString user;<br /> QString pass;

QNetworkAccessManager *manager;

void setUpConnection(void);<br /> QVariantMap parseReply(QString json);

private slots:<br /> void pauseButtonFinish(QNetworkReply *reply);

public slots:<br /> void on_pauseButton_clicked();

};

#endif // XBMCREMOTE_H

cpp file:
#include "xbmcremote.h&quot;
#include "ui_xbmcremote.h&quot;

xbmcRemote::xbmcRemote(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::xbmcRemote)
{
ui->setupUi(this);
this->setWindowTitle("XBMC Remote&quot;);

setUpConnection();
}

xbmcRemote::~xbmcRemote()
{
delete ui;
}

//sets up connection objects
void xbmcRemote::setUpConnection()
{
//Access Details
ip = "192.168.1.2&quot;;
port = "80&quot;;
user = "xbmc&quot;;
pass= "somePassword&quot;;

//set request details
req.setUrl(QUrl("http://&quot; + ip + ":" + port + "/jsonrpc&quot;));
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&quot;;
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&quot;;
//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&quot;].toMap();

//speed tells us if the video is playing or not
QString speed = resultValues["speed&quot;].toString();
if(speed.toInt())
{
ui->statusBar->showMessage("Playing&quot;,700);
}
else
{
ui->statusBar->showMessage("Paused&quot;,700);;
}