PND4

/dev/notes

JSON-RPC + Kodi = BFFs

The can of worms.

The other night I decided to do the long-overdue update of my Gentoo install on my netbook which serves as my XBMC machine. As expected, something along the way broke and XBMC's dependency, ffmpeg, failed to build. After a long struggle, I gave up on the XBMC ebuild and went with XBMC's successor, Kodi. Making haste, I neglected to enable any of Kodi's optional USE-flags. The result: everything perfect Kodi-side (faster actually), but my various remote-control browser-addons, mobile apps, and scripts were made useless.

Meet Kodi's fwiend, JSON-RPC

For months now I've been using a script called xbmc-play. It was simple to use, and lightweight. Problem is that, like most XBMC/Kodi remotes, the underlying mechanics that handle the communication required the webserver feature on the Kodi machine. Since I know a fair amount about scripting and very little of building extensions for browsers and Android apps, scripting became the first part of this journey to regain remoting ability.

In first discovering the lack of a webserver, running netstat -tuanp confirmed no process was listening on the defaut port 8080. The listing did reveal that after enabling "Allow programs on other systems to control Kodi" it listens on port 9090. And a quick google of Kodi's relation to this port will tell you that the JSON-RPC protocol is what's understood on Kodi's end.

First Impression

Looking over the JSON-RPC API articles at the Kodi Wiki and it's official documentation you can get ideas about the syntax of these 'requests' the commands have to make and go from there.

Prior experience manually interacting over TCP/IP came in handy. I was quickly able to test some prototype requests with Kodi using the wiki-suggested telnet tool. Ultimately, I chose to work with netcat as it seemed more fitting for use in the resulting script that follows:

(kodi-play.sh) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/bin/bash

## YouTube Kodi Script [http://github.com/pnd4/kodi-play] 
#by pnd4 
#
# - Portions from "YouTube XBMC Script" by Tom Laermans [tomlaermans.net]. 
#   This script is (also) released into the public domain.
# - Description: Uses Kodi's native JSON-RPC to play YouTube content remotely
#   without need for the webserver.
# - Requires: netcat (tested with GNU Netcat)
# - Usage: kodi-play < URL | YouTube-ID >
# -    ex: kodi-play hABj_mrP-no

## Configure Kodi's RPC details here
KODI_HOST=gen2
KODI_PORT=9090

## Don't touch anything under here

REGEX="^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*"

ID=$1

if [ "$ID" == "" ];
then
  echo "Syntax $0 <id|url>"
  exit
fi

if [[ $ID =~ $REGEX ]]; then
  ID=${BASH_REMATCH[7]}
fi

# Sends our JSON-RPC request to Kodi, and closes the connection.
function jrpc_req {
    echo -n "$1" EOF | nc -c $KODI_HOST $KODI_PORT;
}

echo -n "Opening video id $ID on $KODI_HOST ... "


jrpc_req '{"jsonrpc": "2.0", 
           "method": "Playlist.Clear", 
           "params":{"playlistid":1}, 
           "id": 0}';

jrpc_req '{"jsonrpc": "2.0", 
           "method": 
           "Playlist.Add", 
           "params":{"playlistid":1, 
           "item" :{ "file" : "plugin://plugin.video.youtube/?action=play_video&videoid='$ID'"}}, 
           "id": 0}';

jrpc_req '{"jsonrpc": "2.0", 
           "method": 
           "Player.Open", 
           "params":{"item":{"playlistid":1, "position" : 0}}, 
           "id": 0}';

echo "Done."

What's Next

Having got to dabble into communicating with Kodi over JSON-RPC and being with met less trouble than success. I'm thinking about pursuing a desktop application or at least framework for controlling Kodi/XBMC. It would certainly fulfull my need, and maybe help someone else looking for remote-control without the need for a excess bloat services like a webserver or unnecessary consumption of resources client-side from yet another browser-addon.

With the advent of compact low-powered embedded systems, people seem forget to leverage the power of older systems largely in part due to resource limitations. My netbook, for instance, at most can have 2GB of RAM. Modern machines come with at least 4GB these days, but modern applications like Chrome are quick to claim it. If we choose to design our systems and their appilcations intelligently life won't necessarily be over for such devices like my netbook and won't be for some time as long as we remain resourceful as users and continue to keep modularity in mind as developers.

Creating a PKGBUILD for lzo-2.08 on ArchLinuxARM

Using ArchLinuxARM with OpenVPN broke on my PogoPlug e02 after lzo2 was updated from 2.06-3 to 2.07-2 a few days ago. After another ALARM user confirmed the issue, a couple days passed without a solution and downgrading to 2.06-3 not only is bad practice due to "CVE-2014-4607" but paper-thin, since its disappearing from repos and its likely it won't be in your local package cache forever.. Fueled by boredom, I decided to fix the problem myself.

Using 2.07-2 as a base

Copied PKGBUILD for lzo2-2.07-2 from ABS.
Changed 'arch' to suit ALARM.
Deleted the stuff regarding 2.07 (patch: src, checksums).
Changed pkg version and release values from '2.07-2' to make '2.08-1' respectively.

Making it work

Seems like adding CFLAGS="-DLZO_DEBUG" before ./configure .. made the difference whether it built or not.

Maintaining Security?

However setting the CFLAGS environment variable showed a warning that if not using at least "-O" ("-O2" being the default makepkg.conf optimization CFLAG) then it would not use "-DFORTIFYSOURCE=2" which sounds important from a security-minded perspective.

After some light reading about GCC's flags:
Security Related Flags
-O option flag
Relationship: FORTIFY_SOURCE & O-Flag

Looks like the best option would be to disable 'FORTIFYSOURCE' but still maintain the highest level of security otherwise and retain the ability to protect from stack-smashing attacks by setting 'stack-protector-all'. It seems with 2.08 we have only two choices: "-O0" or no optimizations at all. Personally, I'd gladly sacrifice runtime-speed optimizations for security, when having both is not an option and since ARM devices don't have much memory, why not use "-O0" if we can.

This equates to CFLAGS="-Wall -O0 -U_FORTIFY_SOURCE -fstack-protector-all"
(seen on line #21)

Full PKGBUILD

(PKGBUILD) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#### PND4 - 07/02/14 07:30
# http://pnd4.github.io/downloads/code/lzo-2.08-1-arm/PKGBUILD

pkgname=lzo2
pkgver=2.08
pkgrel=1
pkgdesc="Portable lossless data compression library"
arch=('arm')
url="http://www.oberhumer.com/opensource/lzo"
license=('GPL')
depends=('glibc')
source=(http://www.oberhumer.com/opensource/lzo/download/lzo-${pkgver}.tar.gz)
md5sums=('fcec64c26a0f4f4901468f360029678f')

prepare() {
  cd "${srcdir}/lzo-${pkgver}"
}

build() {
  cd "${srcdir}/lzo-${pkgver}"
  CFLAGS="-Wall -O0 -U_FORTIFY_SOURCE -fstack-protector-all" ./configure --prefix=/usr --enable-shared

  make

  # build minilzo
  gcc $CFLAGS -fpic -Iinclude/lzo -o minilzo/minilzo.o -c minilzo/minilzo.c
  gcc $LDFLAGS -shared -o libminilzo.so.0 -Wl,-soname,libminilzo.so.0 minilzo/minilzo.o

}

check() {
  cd "${srcdir}/lzo-${pkgver}"
  make test # Larger test
  make check
}

package() {
  cd "${srcdir}/lzo-${pkgver}"
  make DESTDIR=${pkgdir} install

  # install minilzo
  install -m 755 libminilzo.so.0 ${pkgdir}/usr/lib
  install -p -m 644 minilzo/minilzo.h ${pkgdir}/usr/include/lzo
  cd ${pkgdir}/usr/lib
  ln -s libminilzo.so.0 libminilzo.so
}

TO-DO

  • Have someone proof/verify the PKGBUILD.

CLI Converting HD > SD

After purchasing a paintball web-series I was a little disappointed to find my loot only available in 1080p. At about ~$4 an episode, you'd think they'd at least offer at least 1 other format, possibly for the old iPod Video's or in my case, an old netbook streaming media off my NAS via 802.11g.

In any case, doing the deed myself was surprising easy. Though I'd imagine someone without any prior knowledge of codecs, aspect ratio, and bitrate may run into trouble. I'd suggest they give my commands a shot.

Lets start off by making sure we have ffmpeg installed on the buffest rig you've got. This can be preety heavy lifting and can take quite sometime on older machines.

Now assuming we have our original file EP1_HD_1080p.mov in our current directory, running the following command will get us going.

1
ffmpeg -i EP1_HD_1080p.mov -ac 2 -qscale 5 -f mp4 -s 854x480 ep1_sd_480p.mp4

To break it down, here's the same command with placeholders.

1
2
3
4
5
6
7
ffmpeg \
    -i [input-filename] \
    -ac [# of audio-channels] \
    -qscale [quality-scale: 1-31 (1 = highest quality)] \
    -f [format: mp4, avi, mkv, ..] \
    -s [Resolution: (Width)x(Height)] \
    [output-filename]

Now you may have checked out some examples before mine and noticed others' had a lot more options. It just goes to show that ffmpeg is the go-to utility. Whether small job like mine or the demands of a release-group like "YIFY", you can't go wrong.

As always, good luck!