Using JSON-RPC API v6 to Build a WebSite
#1
I recently implemented a web site on my home network to augment my Kodi based entertainment system. Currently, the system can:
  • Start our Christmas Music in PartyMode
  • Launch NPR [for morning news and traffic]
  • <girlfriend's-favourite-tv-show> (rando) [This function looks up all the unwatched episodes of her fav tv show, then launches a random episode]
  • Jump straight to the Snap Judgement podcast
  • Play/Pause | Stop | Prev | Next | Fullscreen
  • Home | Videos | Movies | Music | TV
  • Displays a recent history of activities (last 10 actions, a log shows the full history) [This function was added because my gf wanted to know *what* random episode she had been watching.]
  • Lists all the current Favourites as Button-Links [Click to launch]
  • All functions work across multiple devices.
All the functionality is performed via the json-rpc API.

The documentation for the json-rpc is impressive, yet lacking. It should not be too offensive to say that despite the impressive documentation I struggled to implement a few of these features. Favourites, for example, is mentioned in the documentation (as far as I can tell) only in the namespace section the json-rpc. I had to dig through the C++ code to figure out the field names for favourites so that I could requests them as the defaults are not useful.

I began this project on Sunday. I am certain that by Wednesday night I had reached double digits of world wide google bandwidth...

What I found most useful was examples. The examples take what's in the documentation and make it real. We all need this because reading the documentation is just not enough. This mapping between the problem space and solution space needed to effectively program is beautifully described in Peter Naur's essay, "Programming as Theory Building." (http://pages.cs.wisc.edu/~remzi/Naur.pdf)

So I thought I would offer a summary of every call I implement in the hope that it might help others doing similar work. As usual, your mileage may vary and the API may change but here's what I did and few of the gotchas I ran into:

Helpful Links:
The API: http://kodi.wiki/view/JSON-RPC_API/v6
Some useful query/filter examples using json-rpc: https://github.com/KODeKarnage/script.la...service.py
Window Ids: http://kodi.wiki/view/Window_IDs
Windows and Dialogs: http://kodi.wiki/view/Opening_Windows_and_Dialogs and the enums: http://kodi.wiki/view/JSON-RPC_API/v6#GUI.Window
The skinning guide has a list of built in funtions from which I was able to infer some helpful info: http://kodi.wiki/view/Skinning_Manual#Ap..._Functions

These calls all worked for me:

Some commands have to be done in sequences. For example, to issue a play/pause, you need to find the active play and tell it to pause:

Play/Pause:
Code:
{"jsonrpc":"2.0","method":"Player.GetActivePlayers","id":1,"params":{}}
(the answer comes back with an array of playerid and I take the first)
Code:
{"jsonrpc":"2.0","method":"Player.PlayPause","id":1,"params":{"playerid":0}}
(put the int from the GetActivePlayers into the value for playerid before calling)

ZoomIn:
Code:
{ "jsonrpc": "2.0", "method": "Input.ExecuteAction", "params": { "action": "zoomin" }, "id": 1 }
ZoomOut:
Code:
{ "jsonrpc": "2.0", "method": "Input.ExecuteAction", "params": { "action": "zoomout" }, "id": 1 }
Stop:
Code:
{ "jsonrpc": "2.0", "method": "Input.ExecuteAction", "params": { "action": "stop" }, "id": 1 }

Change "zoom level" or "view mode" as it's called in Confluence. It cycles through all available. This took me forever to figure out because the documentation shows zoomlevel1...zoomlevel9 and I could not make those do anything. This is the command I wanted all along:
Code:
{ "jsonrpc": "2.0", "method": "Input.ExecuteAction", "params": { "action": "aspectratio" }, "id": 1 }

GUI.ShowNotification:
I tied this to Tasker on my Android phone and now have SMS messages showing up on my TV with no third party software. Works great:
Code:
{ "jsonrpc": "2.0", "method": "GUI.ShowNotification", "params": { "title": "Scott's Message", "message": "I should save this movie." }, "id": 1 }

This brings up the progress bar and player controls:
Code:
{ "jsonrpc": "2.0", "method": "Input.ShowOSD", "id": 1 }

Brings up the video settings screen:
Code:
{ "jsonrpc": "2.0", "method": "GUI.ActivateWindow", "params": { "window": "osdvideosettings" },"id": 1 }

To get a random unwatched episode of a particular TV show I needed to:
  1. Lookup the tvshowid using the title of the TV show.
  2. Get a list of all episodes for that tv show filtered by "playcount is 0".
  3. Randomly pull one entry out of the filtered list.
  4. Launch the file associated with that episode.
Lookup the tvshowid using the title "NOVA". (Just FYI, this was a royal pain because I'm using .NET and "operator" is a reserved word! Yikes.)
Code:
{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","id":1,"params":{"filter":{"field":"title", "operator":"is","value":"NOVA"},"properties":["lastplayed","playcount"],"sort":{"order":"descending","method":"lastplayed"}}}

Taking the tvshowid out of those results (234), I then "get a list of all episodes for that tv show filtered by 'playcount is 0' for tvshowid 234":
Code:
{"jsonrpc":"2.0","method":"VideoLibrary.GetEpisodes","id":1,"params":{"filter":{"field":"playcount", "operator":"is","value":"0"},"properties":["season","episode","runtime","resume","playcount","tvshowid","lastplayed","file"],"tvshowid":234,"sort":{"order":"descending","method":"lastplayed"}}}

After selecting a random entry out of the array of episodes, use the episode.file property (refered to in this example as "smb://userid:password/server/novaepisode.avi") to open the file:
Code:
{"jsonrpc":"2.0","method":"Player.Open","id":1,"params":{"item":{"file":"smb://userid:password/server/novaepisode.avi"}}}

Favourites.GetFavourites() - This works to retrieve the current favourites.
Had to track down the fields to list in the "properties" array here: https://github.com/xbmc/xbmc/blob/master...ations.cpp:
Code:
{ "jsonrpc": "2.0", "method": "Favourites.GetFavourites", "params": { "properties": ["window","path","thumbnail","windowparameter"] }, "id": 1 }

On my network, I found the performance is fine to query favourites each time I need them.

I've only implemented two types of favourites, type="media" and type="window"

For type="media" I do a Player.Open command. (see above)
For type="window" I do a GUI.ActivateWindow. But there's a catch. You need to take the "windowparameter" property and pass it to GUI.ActivateWindow in it's parameters array, but it MUST be quoted or commas in your file paths will cause the command to fail.
Here's an example of opening a window to a directory. Note: the parameters value has been quoted using a json escaped quote (\"). Thanks to @ironic_monkey for helping me debug this:
Code:
{"jsonrpc":"2.0","method":"GUI.ActivateWindow","id":1,"params":{"window":"videos","parameters": ["\"smb://myusername:mypassword@myserver/my-path-to-content/\""]}}

I hope this is helpful to someone else.

Scott
Reply
#2
very nice! Examples are always helpful.

I'm thinking to move this to the development section, if you don't mind.
Reply
#3
Please, thank you.
Reply
#4
Any improvement in documentation, particularly in an area like json, is much appreciated.
Reply
#5
Can you just integrate it in the wiki directly? (i mean - its a wiki for this very reason in the first place?)
AppleTV4/iPhone/iPod/iPad: HowTo find debug logs and everything else which the devs like so much: click here
HowTo setup NFS for Kodi: NFS (wiki)
HowTo configure avahi (zeroconf): Avahi_Zeroconf (wiki)
READ THE IOS FAQ!: iOS FAQ (wiki)
Reply
#6
adding it to the wiki would be nice indeed. Anyway, moved to JSON Dev section.
Up to Montellese if he wants it stickied.
Reply
#7
There is also a web application to quickly explore the json-rpc api which I found very useful during the development of my app to try out things
http://forum.kodi.tv/showthread.php?tid=172734

And instead of digging g through the source to find the correct parameters you can also take a look at the json schema. It is not very readable at first but once you get used to it you find all info you need

To show the schema you can simply open the following URL by replacing xbmc with the real ip
http://xbmc:8080/jsonrpc in a browser
Reply
#8
(2014-12-06, 20:28)Millencolin007 Wrote: And instead of digging g through the source to find the correct parameters you can also take a look at the json schema. It is not very readable at first but once you get used to it you find all info you need

To show the schema you can simply open the following URL by replacing xbmc with the real ip
http://xbmc:8080/jsonrpc in a browser

That's an awesome tip that I was unaware of (though I must have known it at one time because it feels like a memory I forgot).

EDIT!

Initially posted here that the schema didn't list the Favourites. However, I was incorrect. What I was thinking of was the schema in the documentation. The live schema - pulled as you describe - does indeed include all the details needed to code the Favourites.GetFavourites call.

Thank you. I realize now that I had looked at the online schema first and then just fell into the habit of looking at it and then started googling for things. What I was looking for had been published, but only in code.

To see the latest schema, I should have been checking the live version on my device.

That is a super helpful thing to point out. Thank you.
Reply
#9
The JSON-RPC Browser v0.9 is very slick and would have saved me hours.

BTW, the reason I worked so hard on favourites is that by exposing them on the website, it provides a way for family members to add things to the website without me having to code anything. Once I found the signature for Favourites I knew I had to implement it. Several of the items (NPR, Snap Judgement Podcast) could be removed and replaced with Favourites.

Use cases like the "random unwatched episode of a favourite show" however are still just as valid as there is an actual use case sequence.

"Play Christmas music in PartyMode" is still valid simply because there is no way to Favourite that function as far as I know. But it's not very interesting because there's no sequence or logic - it's just a hard coded command.
Reply
#10
I have Kodi system set up on a Raspberry Pi and the web interface is available. I'm using the AWX web interface but I want to create my own webpage with custom controls mostly just for navigating the menus like the remote control of the AWX interface but customized to my particular needs.

I've read through the JSON-RPC wiki and I've read this thread. All of the examples I see show me a bunch of JSON data in a string with brackets around it. I've written webpages where I've received JSON data as a response when I made a call to a REST API but I've never sent JSON data.

I feel like a total idiot because so-called examples such as you provided say that to play/pause I use:
Code:
{"jsonrpc":"2.0","method":"Player.GetActivePlayers","id":1,"params":{}}
however I have no idea what to do with that? For reference my access my web interface using this URL which is basically the static IP address that I've had my router assigned to the raspberry pi

Code:
http://192.168.1.116/

What do put at the end of that URL to send it the data?

What I really need is a "example" is a complete page of HTML code such that when I click one of 5 links such that when I click the link it moves the selection of a menu up, down, left, right, or select. It doesn't need to do anything else. If I knew how to do that much then I could plow to the documentation and figure out the rest of it but I can't even get off the ground to do that little bit.

Like I said I feel like a total idiot because I don't even know where to start. Any help you can give would be deeply appreciated.
Reply
#11
Cyborg5, You are certainly correct that my post assumes you know how to make the calls to the API in the first place.

I was having a lot of trouble finding working examples when I wrote my app, so I made this the original post to build the collection of examples and to point out a couple of "gotchas" that I ran across. You are correct that I did not show HOW to make the calls.

One of the reasons I didn't describe how is that the how is technology specific. I use .NET, and the Raspberry Pi does not. Many of the examples for Kodi are in PHP, etc.

The way you use the JSON is to make an HTTP POST to the API end point with the JSON that I show above as the playload. That may or may not make sense to you, given your level of experience.

When you get data from a web page or even sometimes from an API, you are using an HTTP GET. To send commands to the Kodi API you need to make an HTTP POST.

As Millencollin007 pointed out, this app allows you to play with the API and it's brilliant. I was able to get it working on my system in a few minutes (YMMV):

http://forum.kodi.tv/showthread.php?tid=172734

I wrote my system fast and dirty from code I had laying around (is there any other way?).

80% of my code is sent to the API with this function in C#. Note the hard coded username and password - good enough for an in-house (literally) web site:

Code:
private string SimpleCommand( string pEndpoint, JsonRpcDto pDto )
{
    var baseUrl = "http://{0}".F( pEndpoint );
    var url = baseUrl + "/jsonrpc?{0}".F( pDto.Method );
    var command = JsonConvert.SerializeObject( pDto );
    var cred = new NetworkCredential( "myUsername", "myPassword" );
    var resultsJson = new Uri(url).WebPostWithJsonParmsAsJson( command, cred );
    return (resultsJson.Contains( "\"error\"") ) ? "FAILED" : "OK";
}

The URI.WebPostWithJsonParmsAsJson( payload, credentials) command is an extension method on URI, and it looks like this:

Code:
public static string WebPostWithJsonParmsAsJson( this Uri pUri, string pJsonParameter, NetworkCredential pNetworkCredential )
{
    using( var wc = new WebClient() )
    {
        wc.Credentials = pNetworkCredential;
        wc.Headers.Add( "Content-Type", "application/json; charset=utf-8" );
        return wc.UploadString( pUri, pJsonParameter );
    }
}

As you can see, once I create an instance of WebClient, I set it's credentials then set the header to application/json and then return the results of WebClient.UploadString() In this case the string being uploaded is the JSON you see above in my original post on this thread. The return results come back as JSON, which is why this method is named WebPostWithJsonParmsAsJson - the second json refers to the return results.

The JsonRpcDto object is super simple:

Code:
[DataContract]
public class JsonRpcDto
{
    public JsonRpcDto()
    {
        JsonRpc = "2.0";
        Id = 1;
    }
    [DataMember(Name="jsonrpc")]
    public string JsonRpc { get; set; }
    [DataMember(Name="method")]
    public string Method { get; set; }
    [DataMember(Name="id")]
    public int Id { get; set; }
    [DataMember(Name = "params")]
    public Object Params { get; set; }
}

I use this object because most of the commands fit this pattern. When serialized as json you end up with the strings I posted in my original thread.

All the rest of the code is for managing the web UI and controls. This code shown here is how I send the json above to the API.

Does any of that help? Scott
Reply
#12
I asked a few questions in this thread several weeks ago and although I didn't get my questions answered directly it did put me on the right path. I downloaded and installed that browser tool to explore the API.

Among the things that you said in reply was…

(2015-03-31, 23:11)hurlbert Wrote: When you get data from a web page or even sometimes from an API, you are using an HTTP GET. To send commands to the Kodi API you need to make an HTTP POST.

I've learned that that's not exactly true. I've learned that you can do the following…

Code:
http://<my_ip_address>/jsonrpc?request={"jsonrpc": "2.0", "method": "whatever", "params": {whatever,whatever,etc. }, "id": 1}

… using http GET.

I just thought I would mention that in case anyone else was reading this thread.

One additional question. All of these calls have "id":1 in them. Is it safe to use the same ID number on every call or should I perhaps increment that value each time? Some of the examples I've seen always use "id":1 while others use string values. I understand that's part of the basic JSON-RPC specification and not kodi specific but I'm just curious what the common practice is regarding this.
Reply
#13
The "id" serves two purposes:
  1. It forces the server to send you a response. If you don't provide an "id" the server will either not send a response at all or will send an empty response (depending on the transport being used). This can make sense for commands that you don't care about the response but most often you want the response.
  2. It helps you match a response to your request in case you do multiple asynchronouse requests (e.g. over TCP). Then you need a way to match the response to the request which is done with the id but then you obviously have to put a "unique" id into every request.
Always read the online manual (wiki), FAQ (wiki) and search the forum before posting.
Do not e-mail Team Kodi members directly asking for support. Read/follow the forum rules (wiki).
Please read the pages on troubleshooting (wiki) and bug reporting (wiki) before reporting issues.
Reply

Logout Mark Read Team Forum Stats Members Help
Using JSON-RPC API v6 to Build a WebSite0