Skip to content
Dag Wieers edited this page Mar 8, 2021 · 23 revisions

IPTV Manager was designed with add-on developers in mind. We wanted to make it as easy as possible to integrate your existing channels and EPG information with IPTV Manager.

Design

For this we decided that add-ons

  • do not require a separate service for communication
  • decide for themselves how they will get called by IPTV Manager
  • map the information they already have using simple JSON formats
  • can integrate with little effort and clean, simple code (see example below)

NOTE: If you like to understand why we decided to design IPTV Manager the way it works now, please read the Design wiki page for the decisions and rationale.

Advertise support to IPTV Manager

Add-ons can advertise supporting IPTV Manager by adding the following values to their settings.xml:

Setting Required Description Example
iptv.enabled yes Opt-in on polling true
iptv.channels_uri yes Endpoint for Channel data plugin://plugin.video.example/iptv/channels
iptv.epg_uri no Endpoint for EPG data plugin://plugin.video.example/iptv/epg

When IPTV Manager detects that your add-on has iptv.enabled set to true it will query for channel and EPG information to integrate with PVR IPTV Simple. Using the above example, IPTV Manager will call plugin://plugin.video.example/iptv/channels?port=XYZ and plugin://plugin.video.example/iptv/epg?port=XYZ

Sending channel and EPG data to IPTV Manager

Due to limitations of Kodi, an Add-on cannot just return data on an RunPlugin() or RunScript() call, so it needs another way to send the data back to IPTV Manager. Therefore, IPTV Manager temporary binds to a free port on localhost, and passes this port to the configured endpoint.

IPTV Manager will wait for a few seconds for the Add-on to call us back on the socket. If we do not get a connection back, we will consider the request to have failed and this indicates that something went wrong in the Add-on.

Since querying for EPG data could take a while, the Add-on should open the callback connection as soon as possible, so IPTV Manager does not timeout. Once the connection is opened, the Add-on can generate the EPG data and reply JSON-structured information (JSON-STREAMS format for channel listings and JSON-EPG format for EPG data) through the socket connection. IPTV Manager will wait as long as the connection is kept open.

When an exception occurs, the Add-on should not send any data and simply close the socket again. IPTV Manager will see this and knows that something went wrong and can continue.

IMPORTANT: Beware that if your add-on requires credentials in order to return the channel or EPG data, it must not show a pop-up to request for credentials. This could lead to confusing or unworkable situations where people get sudden pop-ups for credentials unrelated to what they are doing. Logging an error and closing the socket should be sufficient.

Example code

An add-on wanting to implement IPTV Manager support can use the below examples:

Add IPTV Manager support to your settings.xml

You could choose to enable IPTV Manager integration by default, by adding it as a requirement in your addon.xml and hidden settings in your settings.xml:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
    <setting id="iptv.enabled" default="true" visible="false"/>
    <setting id="iptv.channels_uri" default="plugin://plugin.video.example/iptv/channels" visible="false"/>
    <setting id="iptv.epg_uri" default="plugin://plugin.video.example/iptv/epg" visible="false"/>
</settings>

Or you could integrate IPTV Manager optionally, and give the user the option to install and enable this integration:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
    <category label="Integration">
        <setting label="Install IPTV Manager add-on" type="action" action="InstallAddon(service.iptv.manager)" option="close" visible="!System.HasAddon(service.iptv.manager)"/>
        <setting label="Enable IPTV Manager integration" type="bool" id="iptv.enabled" default="true" visible="System.HasAddon(service.iptv.manager)" />
        <setting label="IPTV Manager settings…" type="action" action="Addon.OpenSettings(service.iptv.manager)" enable="eq(-1,true)" option="close" visible="System.HasAddon(service.iptv.manager)" subsetting="true"/>
        <setting id="iptv.channels_uri" default="plugin://plugin.video.example/iptv/channels" visible="false"/>
        <setting id="iptv.epg_uri" default="plugin://plugin.video.example/iptv/epg" visible="false"/>
    </category>
</settings>

This would result in the following Integration settings category:

  • Integration
    • Install IPTV Manager add-on (when not installed)
    • Enable IPTV Manager integration (when installed)
    • IPTV Manager settings… (when enabled)

Add an IPTV Manager class for sending data over a socket

Next you need to add some code to map internal channel and EPG data, and send it over a socket. We do this below in an IPTV Manager class:

# -*- coding: utf-8 -*-
"""IPTV Manager Integration module"""

import json
import socket


class IPTVManager:
    """Interface to IPTV Manager"""

    def __init__(self, port):
        """Initialize IPTV Manager object"""
        self.port = port

    def via_socket(func):
        """Send the output of the wrapped function to socket"""

        def send(self):
            """Decorator to send over a socket"""
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect(('127.0.0.1', self.port))
            try:
                sock.sendall(json.dumps(func(self)).encode())
            finally:
                sock.close()

        return send

    @via_socket
    def send_channels(self):
        """Return JSON-STREAMS formatted python datastructure to IPTV Manager"""
        from resources.lib.channels import CHANNELS
        channels = []
        for entry in CHANNELS:
            channels.append(dict(
                id=entry.get('id'),
                name=entry.get('label'),
                logo=entry.get('logo'),
                stream=entry.get('url'),
            ))
        return dict(version=1, streams=channels)

    @via_socket
    def send_epg(self):
        """Return JSON-EPG formatted python data structure to IPTV Manager"""
        from resources.lib.tvguide import TVGuide
        return dict(version=1, epg=TVGuide().get_epg_data())

NOTE: The internal data structures of the channels and EPG data are simple Python data structures, not JSON. For your convenience, the via_socket wrapper's send() function will convert your Python data structure to JSON on-the-fly.

And finally you need to link the incoming requests to the respective routine that sends the data structure over a socket (here we use the popular routing module for this):

import routing
plugin = routing.Plugin()


@plugin.route('/iptv/channels')
def iptv_channels():
    """Return JSON-STREAMS formatted data for all live channels"""
    from resources.lib.iptvmanager import IPTVManager
    port = int(plugin.args.get('port')[0])
    IPTVManager(port).send_channels()


@plugin.route('/iptv/epg')
def iptv_epg():
    """Return JSON-EPG formatted data for all live channel EPG data"""
    from resources.lib.iptvmanager import IPTVManager
    port = int(plugin.args.get('port')[0])
    IPTVManager(port).send_epg()

NOTE: The above IPTVManager class may look a lot more complex than needed, but this is to ensure the socket is opened as quickly as possible, and the data only being sent when available. This offers a nice and clean interface that abstracts the connection-handling out of the data-fetching logic. In the future we may offer an alternative HTTP drop-in replacement.

Reference implementation

We decided to use an actual Kodi add-on as the reference implementation. Our choice is the Red Bull TV add-on as it is an internationally known add-on, the content is open to anyone and high quality even, and people can relate to the implementation. (REST API)

The following files have been modified for IPTV Manager integration:

INFO: If you like to experiment with the IPTV Manager integration, feel free to clone this add-on and add some print statements, or modify the data structures to understand how everything fits together.

For Radio integration this works exactly the same, a straightforward implementation is in the VRT Radio add-on.

Contact

If you would like to discuss the implementation or integration bits, we have an internal Slack #iptvmanager channel on the Add-ons developer organisation.

Please send your email address to @michaelarnauts or @dagwieers to get an invitation for the Add-ons developer organisation and the Slack channel.