XBMC’s JSON-RPC API – REALLY Pausing a video

In an earlier post, “Calling XBMC’s JSON-RPC API using Python’s requests library – Pause a video“, I showed an example of pausing the actively playing file using the XBMC JSON-RPC API’s Player.PlayPause method. The problem with this method, as you could probably already tell from its name, is that it acts like a toggle – if the video is playing, it pauses it. If it is already paused, it is unpaused. Great for remote controls, but useless for anything else.

It took quite a bit of searching to figure out how to only pause a file if it is currently busy playing. The answer is to use the Player.GetProperties method to retrieve the “speed” of the currently playing file, which tells us if the file is playing or not. So, to pause we need 3 calls to the API:

– Player.GetActivePlayers to get the actively playing file’s “playerid”
– Player.GetProperties passing the playerid from the response of Player.GetActivePlayers to get the “speed” which should tell us if the file is paused or not
– Player.PlayPause called only if the speed returned is not 0

The code:

import requests
import json
import urllib

#Required header for XBMC JSON-RPC calls, otherwise you'll get a
#415 HTTP response code - Unsupported media type
headers = {'content-type': 'application/json'}

#Host name where XBMC is running, leave as localhost if on this PC
#Make sure "Allow control of XBMC via HTTP" is set to ON in Settings ->
#Services -> Webserver
xbmc_host = 'localhost'

#Configured in Settings -> Services -> Webserver -> Port
xbmc_port = 8888

#Base URL of the json RPC calls. For GET calls we append a "request" URI
#parameter. For POSTs, we add the payload as JSON the the HTTP request body
xbmc_json_rpc_url = "http://" + xbmc_host + ":" + str(xbmc_port) + "/jsonrpc"

#Payload for the method to get the currently playing / paused video or audio
payload = {"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}
url_param = urllib.urlencode({'request': json.dumps(payload)})

response = requests.get(xbmc_json_rpc_url + '?' + url_param,
                        headers=headers)

#response.text will look like this if something is playing
#{"id":1,"jsonrpc":"2.0","result":[{"playerid":1,"type":"video"}]}
#and if nothing is playing:
#{"id":1,"jsonrpc":"2.0","result":[]}

data = json.loads(response.text)
#result is an empty list if nothing is playing or paused.
if data['result']:
    #We need the specific "playerid" of the currently playing file in order
    #to pause it
    player_id = data['result'][0]["playerid"]
    
    payload = {"jsonrpc": "2.0", "method": "Player.GetProperties",
               "params": { "playerid": player_id, "properties" : ["speed"] }, "id": 1}
    url_param = urllib.urlencode({'request': json.dumps(payload)})
    response = requests.get(xbmc_json_rpc_url + '?' + url_param,
                        headers=headers)
                        
    data = json.loads(response.text)         

    if data["result"]["speed"]:
        payload = {"jsonrpc": "2.0", "method": "Player.PlayPause",
                   "params": { "playerid": player_id }, "id": 1}
        response = requests.post(xbmc_json_rpc_url, data=json.dumps(payload),
                                 headers=headers)

        #response.text will look like this if we're successful:
        #{"id":1,"jsonrpc":"2.0","result":{"speed":0}}

Calling XBMC’s (Kodi) JSON-RPC API using Python’s requests library – Pause a video

There aren’t many examples of using XBMC’s JSON-RPC API online, especially Python examples. Here is a simple example in Python, using the requests library.

Prerequisites: Make sure “Allow control of XBMC via HTTP” is set to ON in Settings -> Services -> Webserver.

The following code performs 2 requests: first, it performs an HTTP GET request to retrieve the currently playing or paused file (if any). If a result is returned, we perform a POST to the JSON-RPC to pause the currently playing file using the “playerid” returned from our GET request.

Edit – The Player.PlayPause method toggles between playing a video and pausing it. If a video is already paused, the code below will cause it to to start playing again. Please see my follow up post “XBMC’s JSON-RPC API – REALLY Pausing a video” for an example of how to only pause a video if it is already busy playing.

import requests
import json
import urllib

#Required header for XBMC JSON-RPC calls, otherwise you'll get a 
#415 HTTP response code - Unsupported media type
headers = {'content-type': 'application/json'}

#Host name where XBMC is running, leave as localhost if on this PC
#Make sure "Allow control of XBMC via HTTP" is set to ON in Settings -> 
#Services -> Webserver
xbmc_host = 'localhost'

#Configured in Settings -> Services -> Webserver -> Port
xbmc_port = 8888

#Base URL of the json RPC calls. For GET calls we append a "request" URI 
#parameter. For POSTs, we add the payload as JSON the the HTTP request body
xbmc_json_rpc_url = "http://" + xbmc_host + ":" + str(xbmc_port) + "/jsonrpc"

#Payload for the method to get the currently playing / paused video or audio
payload = {"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}
url_param = urllib.urlencode({'request': json.dumps(payload)})

response = requests.get(xbmc_json_rpc_url + '?' + url_param, 
                        headers=headers)

#response.text will look like this if something is playing
#{"id":1,"jsonrpc":"2.0","result":[{"playerid":1,"type":"video"}]}
#and if nothing is playing:
#{"id":1,"jsonrpc":"2.0","result":[]}
    
data = json.loads(response.text)
#result is an empty list if nothing is playing or paused. 
if data['result']:
    #We need the specific "playerid" of the currently playing file in order 
    #to pause it
    player_id = data['result'][0]["playerid"]

    payload = {"jsonrpc": "2.0", "method": "Player.PlayPause", 
               "params": { "playerid": player_id }, "id": 1}
    response = requests.post(xbmc_json_rpc_url, data=json.dumps(payload), 
                             headers=headers)
    
    #response.text will look like this if we're successful:
    #{"id":1,"jsonrpc":"2.0","result":{"speed":0}}

Python VS PHP

Aah, the old programming language debate, much akin to comparing religions or political parties – bound to end up in a fist fight. Some will argue that as long as you choose the right tool for the job, it doesn’t matter, but I’ve found it’s always the little things that break the camel’s (or in this case, the programmer’s) back.

I wrote a little Python pet project (mind the pun) this week after years of web development using only PHP. As a matter of fact, my first job was a Python position. Eventually, as I started doing GUI development in Delphi, Python was relegated to odd jobs and hacks. When I started doing PHP development, Python became redundant and eventually my skills became rusty as old nails. Until now.

Even after just one week using Python, I remember why I’ll always prefer it – it empowers me as a programmer. In the Python community, they refer to it as the “batteries included” philosophy – with the mere inclusion of a library, anything becomes possible. Combine that with clean and readable syntax, fluent interfaces, powerful list comprehensions, list slicing, an interactive console, and too many other features to mention, it’s clear to see why.

I’ll illustrate using a Python example (Yes, it’s a bad example):

date = details[1].split("<td>")[1].split("</td>")[0].strip()

This line takes the second element of a list (the Python equivalent of an array) and parses a date string out of an HTML table cell. Whether or not it is advisable to do this is a topic for another day, but it illustrates quite handily how the combination of “everything is an object”, great list operators and string functions makes a hack like this effortless. Here is the equivalent PHP code:

$parts = explode('<td>', $details[1]);
$otherParts = explode('</td>', $parts[1]);
$date = trim($otherParts[0]);

If you’re a PHP developer and haven’t worked with Python before, do yourself a big favour and head over to the Python tutorial or check out any of the multitude of hacks, projects and frameworks written in Python and see how it’s the little things that make programming enjoyable.