Developing plugins for ICTV¶
Plugins are located inside the ictv/plugins directory. Each plugin follows this file structure:
plugin_name
└── __init__.py
└── plugin_name.py
└── config.yaml
Each plugin must implement a get_content function that returns a list of objects that extends the
PluginCapsule abstract class. These files are the only three required
files for creating a minimal plugin for ICTV.
Plugin configuration file¶
The config.yaml file is a YAML formatted file with the following structure:
plugin:
webapp: (yes|no)
static: (yes|no)
description: |
A description of the plugin
dependencies:
- a list of dependencies
channels_params:
param_name:
name: 'The pretty name of the channel parameter'
placeholder: 'A placeholder for its input field'
type: string # The type of the parameter
default: 'A default value for the parameter'
The first block defines the plugin itself, the second one defines the parameters required for its functioning.
In the first block, the following parameters can be specified:
webapp– It defines whether the plugin embeds a web application or not. This possibility is explained in detail in Embedding a web application inside a plugin.static– It defines whether the plugin contains a static directory that will be made available at/static/plugins/plugin_nameby the ICTV server.description– A short description that will be shown to the user. Explain the basics about the plugin in a few lines here.dependencies– A list of strings that indicate the Python modules needed for this plugin to function.
Note
dependencies lists Python module names, not pip package names. This allows the ICTV server to check that
all required packages can be imported before executing the plugin. Missing modules will be reported.
The second block contains all the parameters that a channel should provide to a plugin to execute properly. Each
parameter must be have a unique id as key of channels_params and must define using the following arguments:
name– The name of the parameter that will be shown to the user.placeholder– The text that will fill the parameter input field when empty.type– A string indicating the type of the parameter. The supported types are:bool– A boolean value that will be rendered as a checkboxint– An integer value. One can specifyminandmaxarguments to constrain the range of the value.float– A floating point valuestring– A string valuetemplate– A template that can be chosen from the available templateslist[type]– A list of value of a given type
default– A default value for the parameter. If possible, provide a set of default working values for the plugin.
This configuration block is used by the ICTV server to generate a configuration page for each channel automatically.
Plugin Python module¶
The img-grabber plugin is a very simple plugin that we will use as an example to illustrate the code needed to
create a simple plugin. This plugin is given an url and a CSS selector and will extract the first image matching the
selector and output it on a single slide.
class ImgGrabberSlide(PluginSlide):
def __init__(self, img_src, text, duration, qrcode):
self._duration = duration
self._content = {'background-1': {'src': img_src, 'size': 'contain'}, 'text-1': {'text': text}}
if qrcode:
self._content['image-1'] = {'qrcode': qrcode}
self._has_qr_code = qrcode is not None
def get_duration(self):
return self._duration
def get_content(self):
return self._content
def get_template(self) -> str:
return 'template-background-text-qr'
def __repr__(self):
return str(self.__dict__)
The plugin module contains first a definition of a class representing a slide. The slide receives an URL to an image, a duration and a optional qrcode text value as input. The template that will be used to display the slide is fixed.
class ImgGrabberCapsule(PluginCapsule):
def __init__(self, img_src, text, duration, qrcode=None):
self._slides = [ImgGrabberSlide(img_src, text, duration, qrcode)]
def get_slides(self):
return self._slides
def get_theme(self):
return None
def __repr__(self):
return str(self.__dict__)
Then a capsule that will contain a single slide is defined. No theme is set because the slide mainly consists of a full-size image.
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 | def get_content(channel_id): channel = PluginChannel.get(channel_id) logger_extra = {'channel_name': channel.name, 'channel_id': channel.id} logger = get_logger('img-grabber', channel) url = channel.get_config_param('url') image_selector = channel.get_config_param('image_selector') attr = channel.get_config_param('src_attr') qrcode = channel.get_config_param('qrcode') if not url or not image_selector or not attr: logger.warning('Some of the required parameters are empty', extra=logger_extra) return [] try: doc = PyQuery(url=url) except Exception as e: raise MisconfiguredParameters('url', url, 'The following error was encountered: %s.' % str(e)) img = doc(image_selector).eq(0).attr(attr) if not img: message = 'Could not find img with CSS selector %s and attribute %s' % (image_selector, attr) raise MisconfiguredParameters('image_selector', image_selector, message).add_faulty_parameter('src_attr', attr, message) if img[:4] != 'http' and img[:4] != 'ftp:': img = '{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(url)) + img duration = channel.get_config_param('duration') * 1000 text = doc(channel.get_config_param('text_selector')).eq(0).text() alternative_text = channel.get_config_param('alternative_text') return [ImgGrabberCapsule(img, text if text else alternative_text, duration, qrcode=url if qrcode else None)] |
The get_content method receives a single argument, which is the id of the channel for which it should produce
content. The channel instance object can be retrieved as shown on line 2. The values for the channel parameters
defined in the config file can be retrieved using the get_config_param()
method as shown in line 5 to 8.
Plugin can report failures arising from configuration errors to the user in a very easy way.
The MisconfiguredParameters class allows to report parameters that caused
failures to the user. Lines 15 and 19 show two examples of usage of this class.
Finally, on line 25, the function returns a list containing a single capsule with the content extracted from the web page.
Embedding a web application inside a plugin¶
More complex plugins that integrate interactive web application can be developed. A full web.py application can be
included with a plugin. To interface with ICTV server, the plugin directory must contain a app.py file containing
a get_app function. This function will receive a reference to the main ICTV web.py application and should return
the web.py application of the plugin.
The web application can be used to interact with users. The editor and rss plugins embed web application. The
first allows user to create slides and capsules through the web application. The second embeds a more configuration
environment that ease its configuration.
The plugin web application will be embedded inside the ICTV server for each channels of its plugin. For example,
given that the channel with id 4 is a PluginChannel of editor, the remaining of all URLs starting with
/channels/4/ will be passed to its web application.
Plugins web page can use the ChannelGate class to ensure that only
authorised users for a given channel can access the application.
Storing files for plugins¶
TODO