JSON-RPC over HTTP?
#1
I currently have some custom scripts that update my XBMC library automatically but they are using the (depricated) HTTP API.

I'd like to switch to the JSON-RPC API, but I am having trouble finding an example of how use the interface. I've read the wiki and have enabled the webserver. Browsing to httpL//mybmc/jsonprc tells me that json-rpc is up and running.

So, now I want to use the interface....

  1. Is it possible to get JSON data back by simply browsing to a URL/command such as http://myxbmc/jsonrp?[command] ? If so, can you give me an example [command]?
  2. If this is not possible, what is an easy way to test commands for debugging purposes?
Thanks for the help. I've been reading up on JSON-RPC, but am new to this, so I'm hoping my questions make sense.
XBMC.MyLibrary (add anything to the library)
ForTheLibrary (Argus TV & XBMC Library PVR Integration)
SageTV & XBMC PVR Integration
Reply
#2
1) No. You have to wrap your json query in a POST request to your desired port.
You can configure the http port in XBMC.
You can use raw port 9090 too. You need to send your request in brackets {} only.

2)I use an simple app in objective c to test the response.
I guess you can find some easy phyton examples which are part of XBMC too.
Reply
#3
http://boshdirect.com/blogs/tech/set-xbm...brary.html

Or if you just want to use curl.
Code:
curl -i -X POST -d '{"jsonrpc": "2.0", "method": "AudioPlaylist.Add", "params": { "file": "/home/xbmc/Music/Adele - 19/101-adele-daydreams.mp3"}, "id": 1}' http://localhost:9019/jsonrpc
Reply
#4
Thanks for the replies. I think I will try sending the parameters in a HTTP post. I will test this out tonight.


Thanks
XBMC.MyLibrary (add anything to the library)
ForTheLibrary (Argus TV & XBMC Library PVR Integration)
SageTV & XBMC PVR Integration
Reply
#5
darkscout Wrote:http://boshdirect.com/blogs/tech/set-xbm...brary.html

Or if you just want to use curl.
Code:
curl -i -X POST -d '{"jsonrpc": "2.0", "method": "AudioPlaylist.Add", "params": { "file": "/home/xbmc/Music/Adele - 19/101-adele-daydreams.mp3"}, "id": 1}' http://localhost:9019/jsonrpc

Ok, I'm using curl on windows. I'm trying to do a test to confirm it works. It is not working. I'm guessing my data syntax in not getting parsed correctly by the command line? Any ideas?
Code:
c:\curl>curl -i -X POST -d '{"jsonrpc": "2.0", "method": "AudioPlayer.PlayPause"}' -u xbmc:xbmc http://localhost:80/jsonrpc
curl: (6) Could not resolve host: 2.0,; No data record of requested type
curl: (6) Could not resolve host: method; No data record of requested type
curl: (3) [globbing] unmatched close brace/bracket at pos 22
HTTP/1.1 200 OK
Content-Length: 115
Content-Type: application/json
Date: Mon, 20 Dec 2010 17:13:39 GMT

{
   "error" : {
      "code" : -32700,
      "message" : "Parse error."
   },
   "id" : 0,
   "jsonrpc" : "2.0"
}
XBMC.MyLibrary (add anything to the library)
ForTheLibrary (Argus TV & XBMC Library PVR Integration)
SageTV & XBMC PVR Integration
Reply
#6
I never used curl on windows before but there are two things I noticed in your example:

1. Don't set XBMC to provide the JSON-RPC API on port 80 because port 80 is the default port for HTTP requests to the WWW.
2. You're request is missing the "id" field (which you can see in the HTTP response).
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
#7
Montellese Wrote:I never used curl on windows before but there are two things I noticed in your example:

1. Don't set XBMC to provide the JSON-RPC API on port 80 because port 80 is the default port for HTTP requests to the WWW.
2. You're request is missing the "id" field (which you can see in the HTTP response).

Thanks for the tips.
1) Is there a way to specify a different port for the JSON-RPC api to listen on. I thought it is always the same port as the web server. Either way, I changed it to 8000 and it did not help.
2) I added and id, but it's not returning it

I think the problem lies in the syntax of my command. curl is not seeing the data as a single object. It seems like its trying to connect to each parameter as though it thinks it's a server by the "Could not resolve host: 2.0" etc. messages.
Code:
c:\curl>curl -i -X POST -d '{"jsonrpc": "2.0", "method": "AudioPlayer.PlayPause", "id": "1"}' -u xbmc:xbmc http://localhost:8000/jsonrpc
curl: (6) Could not resolve host: 2.0,; No data record of requested type
curl: (6) Could not resolve host: method; No data record of requested type
curl: (6) Could not resolve host: AudioPlayer.PlayPause,; No data record of requested type
curl: (6) Could not resolve host: id; No data record of requested type
curl: (3) [globbing] unmatched close brace/bracket at pos 2
HTTP/1.1 200 OK
Content-Length: 115
Content-Type: application/json
Date: Mon, 20 Dec 2010 18:46:37 GMT

{
   "error" : {
      "code" : -32700,
      "message" : "Parse error."
   },
   "id" : 0,
   "jsonrpc" : "2.0"
}
For the -d option, I have also tried
Code:
"jsonrpc=2.0&method=AudioPlayer.PlayPause&id=1"
"{jsonrpc: 2.0, method: AudioPlayer.PlayPause, id: 1}"
"{""jsonrpc"": ""2.0"", ""method"": ""AudioPlayer.PlayPause"", ""id"": ""1""}"
"{'jsonrpc': '2.0', 'method': 'AudioPlayer.PlayPause', 'id': '1'}"
But none have worked successfully

EDIT: I have gotten this to work successfully by telnetting to the raw TCP 9090 port and sending this exact string:
Code:
{"jsonrpc": "2.0", "method": "AudioPlayer.PlayPause", "id": "1"}
I give up on the curl syntax for now.
Thanks to all for the help
XBMC.MyLibrary (add anything to the library)
ForTheLibrary (Argus TV & XBMC Library PVR Integration)
SageTV & XBMC PVR Integration
Reply
#8
It's not curl's problem. It's Window's bastardization of command line parameters.

Notice all the "Could not resolve hosts"? it's trying to look up a 2.0 as a host and do something with it.
Reply
#9
darkscout Wrote:It's not curl's problem. It's Window's bastardization of command line parameters.

Notice all the "Could not resolve hosts"? it's trying to look up a 2.0 as a host and do something with it.
Yeah, that's why I was trying all the different combinations... trying to get windows to parse it correctly.

maybe a windows cmd line veteran knows what the correct syntax is... or even if a syntax exists that will work.
XBMC.MyLibrary (add anything to the library)
ForTheLibrary (Argus TV & XBMC Library PVR Integration)
SageTV & XBMC PVR Integration
Reply
#10
dunno if this helps any, but i've written a simple python script to update my library. I use this on ubuntu but maybe you can adapt it for windows..
Code:
#!/usr/bin/python
import json
import httplib, urllib

url = 'http://your.xbmc.ip:port'
jsonrpcurl = url + '/jsonrpc'

updatestr = json.dumps({'jsonrpc': "2.0", 'method': "VideoLibrary.ScanForContent", 'id': "1"})

urllib.urlopen(jsonrpcurl, updatestr).close()
Reply
#11
Here's my example in .NET:


Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Web;
using System.Web.Script.Serialization;

namespace XBMCLib
{
    public class XBMCHelper
    {
        private static string _ServerName = "http://localhost:9091/jsonrpc";

        private static string PostCommand(string istrCommand)
        {
            byte[] byteArray = Encoding.ASCII.GetBytes(istrCommand);

            WebRequest wreq = WebRequest.Create(_ServerName);
            wreq.Method = "POST";
            wreq.ContentLength = byteArray.Length;
            wreq.ContentType = "application/x-www-form-urlencoded";
            Stream dataStream = wreq.GetRequestStream ();
            dataStream.Write (byteArray, 0, byteArray.Length);
            dataStream.Close();

            WebResponse wresp = wreq.GetResponse();
            dataStream = wresp.GetResponseStream();
            StreamReader reader = new StreamReader (dataStream);
            // Read the content.
            string responseFromServer = reader.ReadToEnd();
            
            reader.Close();
            dataStream.Close();
            wresp.Close();

            return responseFromServer.Replace(" ", " ");
        }

        protected struct PingResp
        {
            public string id;
            public string jsonrpc;
            public string result;
        }

        public static bool XBMCPing()
        {
            PingResp res;

            string cmd = "{\"jsonrpc\": \"2.0\", \"method\": \"JSONRPC.Ping\", \"id\": \"1\"}";

            JavaScriptSerializer ser = new JavaScriptSerializer();
            res = ser.Deserialize<PingResp>(PostCommand(cmd));

            return res.result == "pong";
        }
    }
}
Reply
#12
I wrote the following python code yesterday to be able to make json rpc calls over HTTP or TCP to run some test commands:
Code:
#!/usr/bin/python

import socket
import select
import json

class JsonRpcResponseError(Exception):

    def __init__(self, errorCode, message = ""):
        self.ErrorCode = errorCode
        self.Message = message

    def __str__(self):
        return "Error %d in Json RPC response: %s" % (self.ErrorCode, self.Message)

class JsonRpcProtocol(object):
    HTTP = 0
    TCP = 1

class JsonRpcConnection(object):

    def __init__(self, protocol, address, port, username = "", password = ""):
        if protocol != JsonRpcProtocol.HTTP and protocol != JsonRpcProtocol.TCP:
            raise ValueError("Invalid protocol (HTTP or TCP)")
        if len(address) <= 0:
            raise ValueError("Invalid address")
        if port < 0:
            raise ValueError("Invalid port")
            
        self.__protocol = protocol
        self.__address = address
        self.__port = port
        self.__username = username
        self.__password = password

        self.__con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__connected = False
        self.__id = 1

    def __del__(self):
        if self.__connected:
            self.__con.shutdown(socket.SHUT_RDWR)
            self.__con.close()

    def call(self, method, params = None):
        return self.__send(method, params)

    def __connect(self):
        if not self.__connected:
            socket.setdefaulttimeout(5.0)
            addr = self.__address
            if len(self.__username) > 0:
                addr = "@%s" % addr
                if len(self.__password) > 0:
                    addr = ":%s%s" % (self.__password, addr)
                addr = "%s%s" % (self.__username, addr)
            
            self.__con.connect((addr, self.__port))
            self.__connected = True

    def __send(self, method, params):
        self.__connect()
        request = "{\"jsonrpc\": \"2.0\", \"method\": \"%s\", " % method
        if not (params is None):
            request += "\"params\": %s, " % json.dumps(params)
            pass

        request += "\"id\": %d}" % self.__id
        
        self.__id += 1

        if self.__protocol == JsonRpcProtocol.HTTP:
            self.__con.send("POST /jsonrpc HTTP/1.1\x0D\x0A".encode("utf-8"))
            self.__con.send("\x0D\x0A".encode("utf-8"))

        self.__con.send(bytearray(request, 'utf-8'))
        
        response = ""
        while True:
            response += self.__con.recv(4096).decode("utf-8")

            if len(select.select([self.__con], [], [], 0)[0]) == 0:
                break

        return self.__parseResponse(response)

    def __parseResponse(self, response):
        if response.find("HTTP") == 0:
            response = response[response.find("\x0D\x0A\x0D\x0A") + 4:len(response)]
        
        jsonObj = json.loads(response)
        
        if "error" in jsonObj:
            raise JsonRpcResponseError(jsonObj["error"]["code"],
                                       jsonObj["error"]["message"])
        if "result" in jsonObj:
            return jsonObj["result"]
        
        raise JsonRpcResponseError("Invalid response")

I'm sure there are better ways to do this as this is basically my first python code I have ever written but maybe it will get you started.
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
#13
The following works:

Code:
curl.exe -i -X POST -d "{\"jsonrpc\": \"2.0\", \"method\": \"JSONRPC.Version\", \"id\": 1}" http://localhost:8080/jsonrpc
Reply
#14
Here's the PowerShell way of doing it - Ported from Adam's post:


Code:
$url = "http://localhost:8080/jsonrpc"

$command = '{"jsonrpc": "2.0", "method": "JSONRPC.Version", "id": 1}'

$bytes = [System.Text.Encoding]::ASCII.GetBytes($command)
$web = [System.Net.WebRequest]::Create($url)
$web.Method = "POST"
$web.ContentLength = $bytes.Length
$web.ContentType = "application/x-www-form-urlencoded"
$stream = $web.GetRequestStream()
$stream.Write($bytes,0,$bytes.Length)
$stream.close()

$reader = New-Object System.IO.Streamreader -ArgumentList $web.GetResponse().GetResponseStream()
$reader.ReadToEnd()
$reader.Close()
Reply
#15
Thanks for posting thing. I notice that there's no hardcoded method, ip, or port so I'm left to assume that these are command line options? If so, what's the format?

Thanks!

Montellese Wrote:I wrote the following python code yesterday to be able to make json rpc calls over HTTP or TCP to run some test commands:
Code:
#!/usr/bin/python

import socket
import select
import json

class JsonRpcResponseError(Exception):

    def __init__(self, errorCode, message = ""):
        self.ErrorCode = errorCode
        self.Message = message

    def __str__(self):
        return "Error %d in Json RPC response: %s" % (self.ErrorCode, self.Message)

class JsonRpcProtocol(object):
    HTTP = 0
    TCP = 1

class JsonRpcConnection(object):

    def __init__(self, protocol, address, port, username = "", password = ""):
        if protocol != JsonRpcProtocol.HTTP and protocol != JsonRpcProtocol.TCP:
            raise ValueError("Invalid protocol (HTTP or TCP)")
        if len(address) <= 0:
            raise ValueError("Invalid address")
        if port < 0:
            raise ValueError("Invalid port")
            
        self.__protocol = protocol
        self.__address = address
        self.__port = port
        self.__username = username
        self.__password = password

        self.__con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__connected = False
        self.__id = 1

    def __del__(self):
        if self.__connected:
            self.__con.shutdown(socket.SHUT_RDWR)
            self.__con.close()

    def call(self, method, params = None):
        return self.__send(method, params)

    def __connect(self):
        if not self.__connected:
            socket.setdefaulttimeout(5.0)
            addr = self.__address
            if len(self.__username) > 0:
                addr = "@%s" % addr
                if len(self.__password) > 0:
                    addr = ":%s%s" % (self.__password, addr)
                addr = "%s%s" % (self.__username, addr)
            
            self.__con.connect((addr, self.__port))
            self.__connected = True

    def __send(self, method, params):
        self.__connect()
        request = "{\"jsonrpc\": \"2.0\", \"method\": \"%s\", " % method
        if not (params is None):
            request += "\"params\": %s, " % json.dumps(params)
            pass

        request += "\"id\": %d}" % self.__id
        
        self.__id += 1

        if self.__protocol == JsonRpcProtocol.HTTP:
            self.__con.send("POST /jsonrpc HTTP/1.1\x0D\x0A".encode("utf-8"))
            self.__con.send("\x0D\x0A".encode("utf-8"))

        self.__con.send(bytearray(request, 'utf-8'))
        
        response = ""
        while True:
            response += self.__con.recv(4096).decode("utf-8")

            if len(select.select([self.__con], [], [], 0)[0]) == 0:
                break

        return self.__parseResponse(response)

    def __parseResponse(self, response):
        if response.find("HTTP") == 0:
            response = response[response.find("\x0D\x0A\x0D\x0A") + 4:len(response)]
        
        jsonObj = json.loads(response)
        
        if "error" in jsonObj:
            raise JsonRpcResponseError(jsonObj["error"]["code"],
                                       jsonObj["error"]["message"])
        if "result" in jsonObj:
            return jsonObj["result"]
        
        raise JsonRpcResponseError("Invalid response")

I'm sure there are better ways to do this as this is basically my first python code I have ever written but maybe it will get you started.
Reply

Logout Mark Read Team Forum Stats Members Help
JSON-RPC over HTTP?1