XBMC-PHP-RPC - a PHP JSON-RPC library supporting HTTP and TCP
#1
Thumbs Up 
I've been playing around with some ideas for XBMC projects in the last couple of days, and most of them required the use of a solid RPC wrapper in PHP, so I wrote the rather catchily titled xbmc-php-rpc.

You can grab it via my GitHub: https://github.com/karlrixon/xbmc-php-rpc

The wrapper supports HTTP, as well as TCP for those who wish to avoid the HTTP overhead.

Usage is as simple as this:

PHP Code:
$params = array('host' => '192.168.0.123''port' => 9090);
require_once 
'rpc/TCPClient.php';
$client = new XBMC_RPC_TCPClient($params);
$response $client->System->GetInfoLabels(array('System.Time'));
printf('<p>The current time according to XBMC is %s</p>'$response['System.Time']); 

The README gives a bit more information:

Code:
xbmc-php-rpc
------------

xbmc-php-rpc is a PHP wrapper for making remote procedure calls to XBMC. It
supports HTTP and TCP transport mechanisms.


Copyright
---------

xbmc-php-rpc is copyright © 2011 Karl Rixon <[email protected]>

This program 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.

This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.


Getting Started
---------------

To use xbmc-php-rpc with your application, download and extract the source files,
and place the rpc directory somewhere sensible.

To begin using xbmc-php-rpc, follow the steps below.

1. Choose a transport mechanism

To use xbmc-php-rpc, first decide which transport mechanism you would like to use.
Available options are HTTP (slower) and TCP (faster).

2. Define connection parameters

Next define the connection parameters. This can either be done by creating an
associative array, or by using a URI string.

The associative array should take the following structure:

$params = array(
    'host' => '192.168.0.123', // Required. The IP or hostname of XBMC.
    'port' => 8080,            // Optional. The port to be used for connecting to XBMC.
    'user' => 'xbmc',          // Optional. The username with which to authenticate with XBMC.
    'pass' => 'password'       // Optional. The password with which to authenticate with XBMC.
);

The default port value is 8080, which is the default XBMC HTTP port. If you use a different
HTTP port, or use TCP, you must set this value. The default TCP port is 9090.

Username and password are only required if you are using HTTP, as there is no need
to authenticate via TCP.

Some examples of valid connection parameters arrays:

$params = array('host' => '192.168.0.123');
$params = array('host' => '192.168.0.123', 'port' => 9090);
$params = array('host' => '192.168.0.123', 'port' => 8181, 'user' => 'jdoe', 'pass' => 'foobar');

If you use a URI string, it should be in the following format:

user:pass@host:port

Again user, pass and port are optional, with the same notes mentioned above applicable.

Some examples of valid URI string:

192.168.0.123
192.168.0.123:9090
jdoe:[email protected]:8181

3. Create a client object

The last thing to do before you can start making remote procedure calls is create
an instance of the appropriate client class, and pass the connection parameters
to it.

If the client is unable to connect to the server, an XBMC_RPC_ConnectionException
will be thrown.

HTTP Client:

$params = 'jdoe:[email protected]';
require_once 'rpc/HTTPClient.php';
try {
    $client = new XBMC_RPC_HTTPClient($params);
} catch (XBMC_RPC_ConnectionException $e) {
    die($e->getMessage());
}

TCP Client:

$params = array('host' => '192.168.0.123', 'port' => 9090);
require_once 'rpc/TCPClient.php';
try {
    $client = new XBMC_RPC_TCPClient($params);
} catch (XBMC_RPC_ConnectionException $e) {
    die($e->getMessage());
}

4. Make some remote procedure calls!

You can now make RPCs by accessing namespaces and commands from the client object.
For example, to send the ToggleMute command from the XBMC namespace:

$response = $client->XBMC->ToggleMute();

Response data is automatically converted into a native PHP type. For example, the
above $response contains an int.

You can pass arguments to the commands like this:

$response = $client->XBMC->SetVolume(50);
$response = $client->System->GetInfoLabels(array('System.Time', 'System.FreeSpace'));

Although the XBMC JSON-RPC API only uses single level namespaces, xbmc-php-rpc
supports deeply nested namespaces. Should XBMC ever start to use nested namespaces,
xbmc-php-rpc will still work! For example:

$response = $client->Foo->Bar->Baz->Go();

A list of available commands is available here:
http://wiki.xbmc.org/index.php?title=JSON_RPC

You can also get a list of commands with descriptions from XBMC itself by calling
the JSONRPC.Introspect command:

$response = $client->JSONRPC->Introspect();

A list of InfoLabels which can be read via the System.GetInfoLabels command is
available here:
http://wiki.xbmc.org/index.php?title=InfoLabels


Documentation and Getting Help
------------------------------

Coming soon.

I hope someone finds this useful. Any questions, just ask!

P.S. I'll be very happy to hear of any bugs or suggestions
Reply
#2
Hi

This looks very nice. However, I think it is somewhat overengineered.
The code is very well documented.
Why create so many files that only contain an empty exception class when you're not using autoloading? I like that you use the different Exception classes though.

Have a look here, for a much more straight-forward implementation for PHP:
https://github.com/kaigoh/XBMC-PHP

You will notice that there is a lot less code. I don't know why exactly uses so much more, perhaps it supports more things that the aforementioned implementation doesn't cover - I haven't looked at your code that closely. Maybe it is also, because of the overengineering - but that may be an "unfortunate" first impression.
Reply
#3
Hi alshain,

It's not really down the the library to autoload imo. I would assume that any project implementing it would have autoloading functionality, which would conflict with any included here.

The reason for creating empty exception classes is to allow calling code to properly act on out-of-the-ordinary errors (ie, exceptions Wink). Of course I could just have everything throw a base Exception, but I don't think that would be helpful.

I'm sorry you think its over engineered. I actually think it's pretty compact myself Smile. I have taken a look at the code to which you linked. I agree it is more simple, but on the other hand it has fewer features (such as no TCP support, limited to single-level namespaces). Also bear in mind that xbmc-php-rpc lazy loads commands and namespaces, whereas the linked code creates objects for every possible command up front, which means that in practice there are actually considerably fewer class instances in scope at any given time with my code. I haven't benchmarked the two, but I would suspect that xbmc-php-rpc is actually more efficient, despite being more complex in terms of the code itself Smile

That's not to take anything away from kaigoh's code, there's a lot to be said for keeping things simple!
Reply
#4
I know why you have empty Exception classes, that's why I said I like that you have them Wink But I don't understand why every empty exception gets it's own file.

I suspected it had less features, I just don't have a clue about what the XBMC API offers really Smile - For example, I didn't know that you would need more "depth" to the namespaces, ( can easily be achieved through __get and a class recursively instantiating itself for example - and __get for the lazy-loading as well)

I don't think this code will be a bottleneck though Tongue
Reply
#5
Hi Mindzai,

Thanks for taking the time to write this code, also for sharing it with us Smile and a big thanks for your efforts with the documentation, you've done well!

I tried to use your connection object via TCP and it seems to work well, i have only just started to muck around with it, but so far so good.

I had only 1 hiccup and that was that i didnt see any call to include Client.php in the examples, TCPClient.php doesn't make a call but does require that it is loaded, so at first it didnt work, i added one line
PHP Code:
require_once 'plugins/XBMC/rpc/Client.php'
and things worked a treat.
Reply
#6
Hi dann0

Glad it's working OK for you Smile

Thanks for the bug report, I've fixed the issue now.
Reply
#7
i'm a little confused by an error message i get when using AudioPlayer->PlayPause() the code i try is
PHP Code:
try {
    
$response $rpc->AudioPlayer->PlayPause() ;
} catch (
XBMC_RPC_Exception $e) {
    die(
$e->getMessage());
}
print 
'<p>response from XBMC:</p>';

print_r($response); 

the error i get is
Quote:Notice: Undefined index: id in /mnt/hostwebinc/plugins/XBMC/rpc/Response.php on line 25 Invalid JSON RPC response

The reason i am confused is because from what i can see the index 'id' does exist at line 25 of response.php, i can print it at that point, so... i really don't get it!

I'm hoping you can make sense of it Mindzai
Reply
#8
Hi dann0

At first I couldn't replicate your issue using the HTTPClient, but then I tested with the TCPClient and indeed got the error you describe.

The issue is that an announcement is sent over the socket indicating the occurrence of the play/pause event. I stupidly didn't account for this, and assumed that the next chunk of data sent over the socket would be the response. I have now fixed this bug; any data received via TCP which is not a response to the initial request is now ignored.

In case you aren't using git to pull revisions, the files which have changed are: Client.php, HTTPClient.php and TCPClient.php.

The change itself is here:
https://github.com/karlrixon/xbmc-php-rp...774a00a4c8

Thanks for reporting these bugs. I haven't been through and tested every method of the API yet so it's very useful feedback Smile
Reply
#9
Hi,

I'm testing the wrapper. I would like to recover all information about movies.

This code work's fine :
PHP Code:
    $params 'xbmc:[email protected]:8080';
    require_once 
'rpc/HTTPClient.php';

    
// On se connect à XBMC
    
try {
        
$rpc = new XBMC_RPC_HTTPClient($params);
    } catch (
XBMC_RPC_ConnectionException $e) {
        die(
$e->getMessage());
    }

    
// On récupère la liste des films
    
try {
        
$response $rpc->VideoLibrary->GetMovies();
    } catch (
XBMC_RPC_Exception $e) {
        die(
$e->getMessage());
    }

    
// On affiche le résultat
    
echo "<pre>"print_r($response); echo "</pre>"

But, I don't kown how pass args. What is the equivalent of the following syntax?

Code:
{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "start": 0, "fields": ["genre", "director", "trailer", "tagline", "plot", "plotoutline", "title", "originaltitle", "lastplayed", "showtitle", "firstaired", "duration", "season", "episode", "runtime", "year", "playcount", "rating"] }, "id": 1}

Thank's in advance
Reply
#10
Hi DKreeK

If you want to pass a single argument you can do that directly. For example:

PHP Code:
$response $rpc->XBMC->SetVolume(75); 

If you want to pass multiple arguments you just need to wrap them in an array. For example, the call you posted could be made like this:

PHP Code:
$response $rpc->VideoLibrary->GetMovies(array(
    
'start' => 0
    
'fields' => array(
        
'genre''director''trailer''tagline''plot''plotoutline''title',
        
'originaltitle''lastplayed''showtitle''firstaired''duration''season',
        
'episode''runtime''year''playcount''rating'
    
)
)); 

I hope that helps Smile
Reply
#11
Thank you very much, I will test tonight as soon as I get backWink
Reply
#12
It works very fine. Thank you
Reply
#13
Good to hear Smile
Reply
#14
I have been using your wrapper but ran into a problem when trying to fetch my whole Movie DB.

I might be wrong but it looks like you are trying to decode a non utf-8 string which fails as soon as you have some weird foreign movies in your DB.

Changin this function to utf8_encode the string solved my problem:

PHP Code:
private function decodeResponse($json) {
        return 
json_decode(utf8_encode($json), true);


I checked the php.ini is running with a default_charset of utf-8 so I am not exactly sure if the problem is due to me having something configured the wrong way.

Anyways good job with the wrapper and maybe this is usefull info, if I did something wrong please let me know Smile
Reply
#15
Some small changes. xbmc.php, line 295. I was getting a warning that index 0 didn't exist, so I changed it to this:

Code:
    public function __call($method, $args = null) {
        if (array_key_exists(0,$args))
            return $this->_xbmcJson->rpc($this->_name.".".$method, $args[0]);
        return $this->_xbmcJson->rpc($this->_name.".".$method);
    }

Also, in the example wall, I changed the code to this, that way I could track down what movies I needed images for, plus I didn't have a broken img tag:

Code:
//Display the "movie-wall"
foreach($movies as $movie) {
    if (array_key_exists('thumbnail',$movie))
    $moviesHTML .= "<img src=\"http://".$xbmcHost->url()."/".$xbmcJson->Files->Download($movie['thumbnail'])->path."\" width=\"104\" height=\"156\" title=\"".$movie['title']."\">";
}

$moviesHTML .= "<br><br><h3>Movies Without Thumbs</h3>";
foreach($movies as $movie) {
    if (!array_key_exists('thumbnail',$movie))
    $moviesHTML .= "<br>".$movie['title'];
}

$moviesHTML .= "<br><br><h3>Movies Without Fanart</h3>";
foreach($movies as $movie) {
    if (!array_key_exists('fanart',$movie))
    $moviesHTML .= "<br>".$movie['title'];
}
Code:
GRANT ALL PRIVILEGES ON `xbmc_%`.* TO 'xbmc'@'%';
IF you have a mysql problem, find one of the 4 dozen threads already open.
Reply

Logout Mark Read Team Forum Stats Members Help
XBMC-PHP-RPC - a PHP JSON-RPC library supporting HTTP and TCP1