Kodi and ansible

I run a couple of Kodi instances on Raspberry Pi’s. With more than one single instance I got the problem, that their configuration differs. You’ve got settings and add-ons that have different versions and configurations.

Occasionally something breaks. In my case reinstalling the Raspberry Pi is the quickes and easiest way. But it is still a tedious job and it needs at least one or two hours of fiddling around to get everything mostly right.

For server systems I use configuration management and deployment systems like Puppet or Ansible. For the Raspberry Pi using Ansible seems to be the right choice due to its agent-less architecture.

This article sums up some of the experience gathered when implementing this.


Distribution

Beyond the many distributions Kodi can be run is, I tried two of those: LibreElec and XBIAN.

LibreElec

A very compact, but also restricted platform that claims to have exactly enough installed to run Kodi. For the basic usage this might be enough. But when trying to fiddle around with the settings on a command line, I quickly hit some limitations: Mounting an NFS share using either a service-file or /etc/fstab does not seem to survive a reboot of the Raspberry Pi. The settings are in place, but are failing to get executed.

Using the graphical interface of Kodi sources (see below) can be configured. The whole charme of automation is however not to have to do it yourself.

XBIAN

A more complex, but probably not as fail proof setup of Kodi. I have been on XBIAN before, but then switched to LibreElec to avoid complexity. As mentioned above, this did not work, since my requirements exceeded the abilities of the LibreElec environment.


Ansible

Ansible - or the SSH on steroids - connects to the Kodi instance via SSH and runs all commands on the remote host.

There are two main parts that I put in into the configuration and therefore I will not have to do them again next time I have to setup the system.

Sources

The file /home/xbian/.kodi/userdata/sources.xml contains the configured sources on the kodi instance.

Initially I had mounted NFS network shares locally, put some entries in /etc/fstab and configured the sources manually. This approach did not work with LibreElec: Adding mounts to either as Systemd or in /etc/fstab had no effect and the nfs mount failed. Even it it could be added manually.

<sources>
 <programs>
     <default pathversion="1"></default>
 </programs>
 <video>
     <default pathversion="1"></default>
     <source>
         <name>serier</name>
         <path pathversion="1">nfs://nas/data/series/</path>
         <allowsharing>true</allowsharing>
     </source>
...

When adding the sources using the GUI of KODI, the file /home/xbian/.kodi/userdata/sources.xml is modified. Since this is a plain XML file, it can easily be modified and deployed using Ansible. The only thing to be aware of are the configured access rights on that file:

---
- name: Define the sources
  copy:
    src: sources.xml
    dest: "{{ xbian_path_dest_sourcesxml }}"
    owner: "{{ xbian_kodi_user }}"
    group: "{{ xbian_kodi_group }}"
  tags:
    - kodi_sources

The common patch I have already replaced with some variables like xbian_path_des_sourcesml - in case I need to transport this to another platform one day.


Add-ons

A bit more tricky are the add ons that can be installed.

They are basically often ( but not always) splittet in three components:

  1. The add-on package

  2. The add-on code

  3. The add-on settings

Besides adding them to the file system, you also need to take care of other stuff

  1. Make kodi aware of the added add-ons.

  2. Enable the add-on within kodi

Since there is more than one add-on involved and I do not want to repeat my Ansible code, I structure the add-ons in an array.

xbian_addons:
  - name: plugin.video.nrk
    source_package: plugin.video.nrk-4.6.0.zip
  - name: weather.yahoo
    source_package: weather.yahoo-4.3.1.zip
  ...

So, let’s have a look at this step-by-step.

The add-on package

The add-on package is usually automatically downloaded and (on XBIAN) stored in /home/xbian/.kodi/addons/packages/:

$ ls -1 /home/xbian/.kodi/addons/packages/ | head
context.altmovie.posters-1.0.0.zip
context.artwork.downloader.auto-1.0.1.zip
context.show.runtime-1.0.0.zip
context.trailer-1.0.0.zip
metadata.albums.theaudiodb.com-1.2.2.zip
metadata.artists.theaudiodb.com-1.1.1.zip
metadata.common.allmusic.com-3.2.0.zip
metadata.common.fanart.tv-3.4.0.zip
metadata.common.imdb.com-3.0.3.zip
metadata.common.themoviedb.org-3.1.4.zip

In this example the package context.trailer-1.0.0.zip is the add-on that adds the trailer option to the movie context menu.

Once the file is being downloaded by kodi, Ansible can easily distribute the file:

- name: ADDON> Ensure source package
  copy:
    dest: "{{ xbian_addons_path }}/packages/{{ item.source_package }}"
    src: "addons/{{ item.name }}/package/{{ item.source_package }}"
    force: no
    owner: "{{ xbian_kodi_user }}"
    group: "{{ xbian_kodi_group }}"
  with_items: "{{ xbian_addons}}"
  tags: kodi_addons

Here I already use te internal Ansible variable xbian_addons, which I have defined earlier.

The add-on code

The add-on package also need to be decompressed into /home/xbian/.kodi/addons.

- name: ADDON> Ensure plugin installation
  shell: "unzip -n {{ xbian_addons_path }}/packages/{{ item.source_package }} -d {{ xbian_addons_path }}"
  args:
    creates: "{{ xbian_addons_path }}/{{ item.name }}"
    warn: false
  with_items: "{{ xbian_addons }}"
  notify:
    - restart xbian_kodi
  tags: kodi_addon

Note

  • I use shell instead of unarchive, since it allows me better control of the where to decompress the package to.

  • I disabled the warnings. Ansible is otherwise constantly advising me to use unarchive.

  • In order to cover all changes later, I schedule a restart of the service for kodi using the handler restart xbian_kodi.

  • De-compressing the add-on package will always create a subdirectory with the add-on name. That is whywe can de-compress directly into the add-on directory xbian_addons_path.

The add-on settings

Some, but not all add-ons come with specific settings. In those cases, files are place into /home/xbian/.kodi/userdata/addons_data/<addon_name>/. In many cases this is just an settings.xml-file. Other add-ons also come with separate scripts and other data.

Ansible should here deploy the content of a local directory containing all settings files to the remote XBIAN instance:

- name: ADDON> Ensure settings
  copy:
    dest: "/home/xbian/.kodi/userdata/addon_data/{{ item.name }}/"
    src: "addons/{{ item.name }}/settings/"
  with_items: "{{ xbian_addons }}"
  when:
    - item.settings is defined
    - item.settings != false
  notify:
    - restart xbian_kodi
  tags: kodi_addons

Note

  • The whole directory for the settings is replace. Files that are not in the local filesystem but exists on KODI, will not be touched.

  • In order to get all changes in kodi, a service restart is scheduled.

  • The variable item.settings is optional. I use it with settings: false in order to skip this for particular add-ons.

Reload add-ons

Kodi needs to look for changes of its add-ons. This can either be done by restarting the whole XBIAN instance, or by triggering the reload within kodi:

$ xbmc-send.py --action=UpdateLocalAddons

Ansible can do this at this step:

# Let Kodi scan for new addons
# This will create entries in the sqlite database for addons
- name: ADDON> Scan for new addons
  shell: 'xbmc-send.py --action=UpdateLocalAddons'
  changed_when: false
  tags: kodi_addon

Enable add-on

Note

Running this command on XBIAN required to install the sqlite3 before.

$ sudo apt-get install -y sqlite3

Only adding an add-on is not sufficient. It also needs to be enabled. Kodi is managing the add-ons within and sqlite database at /home/xbian/.kodi/userdata/Database/Addons27.db. If Kodi is aware of an add-on, it can be enabled or disabled by setting a field value within the database file. Ansible will do this for all managed add-ons:

# Enable all addons. This requires entries to be present in the addon database
- name: ADDON> Enable Plugin
  shell: "sqlite3 /home/xbian/.kodi/userdata/Database/Addons27.db 'update installed set enabled=1 where addonid==\"{{ item.name }}\";'"
  with_items: "{{ xbian_addons }}"
  notify: restart xbian_kodi
  changed_when: false
  tags: kodi_addon

General advise

Depending on the add-on that shall be added, additonal steps might be necessary or steps might need to be altered. Based on the description above you should be able to get a general idea on how to proceed.


Problems

Some basic problems originate from the unknown Kodi system and it quirks and special functions that are not known yet.

The inability of mounting an NFS share within LibreElec e.g. is something like that.

Generally speaking so far I did not come over any settings that I needed to set without being able to do so.


Resources