XBMC-Remote-code-example-using-Qt-C: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
 
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
==Creating an <span class="caps">XBMC</span> remote using Qt C++==


I wanted to create an <span class="caps">XBMC</span> 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 <span class="caps">JSON</span> Http Api Frodo. [http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6 Found Here] ''[wiki.xbmc.org]''


This is a simple tutorial that will set you up with the basics of sending the Pause/Play command to <span class="caps">XBMC</span> over Http..
== 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. [http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v6 Found Here]


===Lets get started…===
This is a simple tutorial that will set you up with the basics of sending the Pause/Play command to XBMC over Http..


To start I created a standard Qt Gui Application. Then I added libraries.. <span class="caps">XBMC</span> now uses <span class="caps">JSON</span> for communicating with its Http interface. Unfortunately Qt doesn’t come with a <span class="caps">JSON</span> parsing library..
=== Lets get started… ===


However one has been built by Flavio Castelli: [http://qjson.sourceforge.net/ Found here] ''[qjson.sourceforge.net]''
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: [http://qjson.sourceforge.net/ 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:
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:


Windows might be slightly different… Stackoverflow has a few posts on it.
<code>INCLUDEPATH += /usr/include/qjson
 
LIBS+= -L/usr/local/lib -lqjson</code>
 
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:
We then also need to add networking to the project as well.. So we add this line to our .pro file:


===Setting up the Connection===
<code>QT += network </code>


=== 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.
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.


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 <span class="caps">URL</span> 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.
<code> QNetworkRequest req;
QNetworkAccessManager *manager;
QString ip;
QString port;
QString user;
QString pass;</code>
 
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.  
 
<code>
//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));
}


These will provide the connection to <span class="caps">XBMC</span>.. So we can now call the access manager to post requests to <span class="caps">XBMC</span>.
//authenticator slot
void xbmcRemote::provideAuthenication(QNetworkReply *reply, QAuthenticator '''ator)
{
ator->setUser(QString(user));
ator->setPassword(QString(pass));
}</code>


===Parsing the reply===
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
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


===Post Play/Pause request===
<code>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;
}</code>
 
 
=== 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.
 
<code>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}");


Now the fun part.. We will post a play pause request to <span class="caps">XBMC</span>. 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.
//connects the request manager to slot for dealing with the reply
connect (manager, SIGNAL (finished(QNetworkReply *)), this, SLOT (pauseButtonFinish (QNetworkReply  *)));


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 <span class="caps">XBMC</span>.
//Send the request:
manager->post(req, b);
}</code>


The request <span class="caps">JSON</span> without the formatting to make it a valid QString itself looks like:
The request JSON without the formatting to make it a valid QString itself looks like:
 
<code>{"jsonrpc":"2.0", "method":"Player.PlayPause", "params": {"playerid" : 1},"id":1}</code>


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
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
<code>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);;
}
}</code>


and thats our pause play request done!
and thats our pause play request done!


You can now build on this to make a full <span class="caps">XBMC</span> controller. A handy hint to find out what needs sent and what is replied is to get Firefox and get the addon “Live <span class="caps">HTTP</span> Headers”. Then use <span class="caps">XBMC</span>s web interface and use live http headers to see the requests it sends and what it gets in response..
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:
<code>#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</code>
 
cpp file:
<code>#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  *)));


===files in full:===
//gets the parsed JSON data
QVariantMap replyData = parseReply(QString::fromUtf8(reply->readAll()));


header file:<br />
//gets the results of the play/Pause request
QVariantMap resultValues = replyData["result"].toMap();


cpp file:<br />
//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);;
}
</code>

Latest revision as of 06:20, 31 March 2015


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