# Freevo Plugin Writing HOWTO

Writing your own plugins for Freevo
2003
Dirk Meyer

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license can be obtained at http://www.gnu.org/licenses/fdl.html and is included as an appendix in this document.

Dirk
Meyer

dmeyer@tzi.de

Mike
Ruelle

mikeruelle@comcast.net

This document contains some usefull information on how to write a plugin for Freevo. This includes some documentation about the interbal structure of Freevo. This howto is not complete (and maybe never will be). If you have additional questions, please contact the developer list (freevo-devel@lists.sourceforge.net).

## Introduction

### Introduction

To make it easier to add or remove functions for Freevo, we integrated a plugin system into Freevo. By adding a file into the Freevo system, you can add new functions without changing anything else in the core of Freevo. It's also possible to distribute a plugin with your application and not within the Freevo distribution.

If you wrote a plugin, please send it to the Freevo developer list. Maybe other people also find it usefull.

### Disclaimer

This document is available for free, and, while I have done the best I can to make it accurate and up to date, I take no responsibility for any problems you may encounter resulting from the use of this document.

### Contact / Feedback

If you have found this HOWTO to be helpful or you have found errors in this HOWTO please feel free to contact me at dmeyer@tzi.de or contact the Freevo developer list at freevo-devel@lists.sourceforge.net.

### Revision History

v0.1 2003-10-31 DM Initial Release

## Internal Structure

### Plugin Location

Plugins are intended to be grouped by type and then there is a general location for hard to fit plugins. The main location is in the /usr/local/freevo/src/plugins. The type locations for plugins is in the various plugins subdirectories of the tv, video, audio, and image in /usr/local/freevo/src.

### Events

The communication between input devices (remote control or keyboard) and the menu or the application is based on events. It's also possible that a plugin sends an event to other parts of Freevo. The mapping between keys and the events depends on the application type (menu, video, audio, etc). See `event.py` for details about the default event mapping.

A event is an identifier string and optional an argument. The event itself can be compared with a string, comparing two event objects only compares the identifier. This makes it possible to check for events without caring for the argument.

An event is send into Freevo with the function `rc.post_event`. The events are stored in a fifo queue. If an event is in the queue, Freevo tries to find the correct eventhandler for this event. If the menu is active, Freevo will pass the event to the current selected item. Each item will send the event to it's parent if it doesn't consume the event. If the event wasn't consumed by one of the menu items, it will be passed to the DaemonPlugins (see DaemonPlugins section for deatils).

Instead of the class `Event`, the plugin can use the functions helper functions in `plugin.py` `event` to create and `isevent` to get the name of an event. This functions adds the string `PLUGIN_EVENT` to the event name. The media menu has some extra redraw checkings if the event has something to do with a plugin.

### The Menu System

Menu's in freevo are done using the classes described below. But in general they are essential lists of items which have their names displayed in the list. Each item then has actions associated with them. The first action is the one used when selected and you can use enter to see the others. Menu's also have actions that can be associated with them to perform actions or updates.

#### Menu

Menu is essentially a class wrapped around an array of choices. It has several methods but the constructor is the most commonly used. It takes a title, then an array of options, and then some optional parameters. The reload_func is the most commonly used. The reload_func is used when you come back from executing an item. It's only used when you want to show something other than the menu you started with when you come back.

#### Item

Item is the base class for anything that appears in the Menu. Generally you create a subclass of Item and then create an actions method to tell the menu what to do when the item is clicked. The name property is what the Menu object uses to display on the screen. You can then create other variables to hold important data points in.

#### MenuItem

This is a convenience class it is useful in two different situations. The first and most common is for creating Menus to display error conditions. The second use is for when you only need a very simple item with a single easy action.

To use MenuItem in the error condition case you call the constructor with three parameters. The first parameter is what to display in the menu, the second is the action to take when the item to select and the third is put the arg to the action function. In this case you typically wrap the constructor call into an append to your list of items to be given to menu.

To use MenuItem as a simple Item and not bother with creating your own sub item class, you again call the constructor with the set of three parameters. The first parameter is what to display in the menu, the second is the action to take when the item to select and the third is put the arg to the action function. But typically you save a reference to this item and set a few additional parameters manually.

#### MenuWidget

MenuWidget or menuw as it is often labelled in the code. Is a handy utility class where most of the menu magic happens. It has most of the default utility actions for menus as well as the methods to manage the menu stack.

Common Menu Actions:

back_one_menu -- goes to the previous menu. Typically used after deleteing the current menu. see cdbackup.py for an example.

goto_main_menu -- jumps all the way back to the main menu.

List of menu stack actions:

pushmenu -- used after constructing a menu, then typically a call to refresh to display the menu.

refresh -- redraw the top item on the menu stack. It is usually called after a pushmenu call.

delete_menu -- remove the currently displayed menu.

#### A full example to bring it all together

### GUI Objects

not written yet

### Using the skin

Some plugins may want to draw something on the screen. One example is the weather plugin (external plugin, download it from `http://freevo.sf.net/addons/`). Since this has nothing to do with the normal menu system, such plugins also need an eventhandler to react on buttons.

Since Freevo knows nothing about which elements should be displayed and were to put them, the plugin needs to define a fxd file in `share/skins/plugins` with the needed information.

Now the freevo skin has fxd information about the type `foo`, but doesn't know which areas are allowed (ok, the skin could guess it). So the plugin needs to call

    skin.register('foo', ('screen', 'title', 'view', 'plugin'))

once. Now the plugin can call

    skin.draw('foo', item)

to draw `item` with the settings of `foo`.

So far so good, but it may happen that the plugin needs an area which isn't defined right now. The default areas for Freevo are `screen`, `title`, `subtitle`, `listing`, `info`, `view` and `plugin`. The plugin area is used for smaller plugins to draw on the screen, e.g. the idlebar.

To create an area of your own, you first need to define it in the fxd file:

Now the skin has fxd information about an area `foo`. The skin knows were it is and can also draw the background of the layout. But it needs an object to draw the real content. The following example defines a class which inherits from `skin.Area`. It defines itself as class to draw `foo_area`. The skin now calls the function `update_content_needed` to check if the area needs an update (return `True` or `False`). When Freevo knows that an update is needed, this function may not be called after all. The real work is done is `update_content`.

(add more doc here)

## The Different Types of Plugins

### Basic Plugin

It's possible to inherit directly from the class Plugin. But by doing that, the only thing happens is that the object is created, nothing more. You may need this to create a thread in the background an connect it to the plugin interface. Other plugins may now use this thread.

This is done for the media plugins. The mplayer video plugin only starts the mplayer thread and register it to the plugin interface as `VIDEO_PLAYER`. The video item asks the plugin interface to give him the `VIDEO_PLAYER`.

### DaemonPlugin

A DaemonPlugin is somthing that works in the background of Freevo. Examples for this kind of plugin are the idlebar, the usb plugin, the thread watching your rom drives and the LCD plugin. A DaemonPlugin can react on events, can be called in a specific time intervall and can draw something to the skin.

The `shutdown` function will be called when Freevo shuts down to do some cleanup. Most plugins won't need that function.

#### Polling

A plugin can be called in a specific time intervall. To do this, it has to set the variable `poll_intervall` and define a function `poll`. After that, the `poll` will be called every `0.01 * poll_intervall` seconds. When the menu is not active (e.g. watching a movie or listening to music), the function won't be called. If you want the plugin to be called even than, you can set the variable `poll_menu_only` to True.

Example: a plugin that sends the Event `foo` every second:

#### Action on events

To act on specific events, the class must define the function `eventhandler`. This function will be called with the event if nothing before consumed this event. If you create your own kind of event, you can be sure you get it. If the function handles this event, it should return True, if not False.

If the plugin should see all events, the plugin should set the variable `event_listener` to True. After that, the plugin will see all events and it doesn't matter, if the function return True or not.

Example: a plugin that reacts on the Event `foo` and counts the number of the events:

#### Drawing on the Screen

not written yet

### MainMenuPlugin

A MainMenuPlugin is a plugin that adds items to the main menu. The main menu can also be the main menu for the different types of media, like the video main menu. E.g. if you put your plugin in `video/plugins`, it will be shown in the video main menu. The user can also force which kind of menu by setting the `type` when calling `plugin.activate`.

Examples for this kind of plugin are all items in the main menu and the `rom_drives` plugin, adding all possible rom drives to the sub main menus.

A MainMenuPlugin only needs to define the function

    def items(self, parent):

returning a list of items for the menu.

A full up example:

### ItemPlugin

An ItemPlugin adds something to the submenu of an item. You get this menu by pressing `ENTER` on an item. When the submenu is generated, all global ItemPlugins and for this media type are called.

The function to be called is

    def actions(self, item):

and should return a list of actions. The function can check all attributes of the item to decide what actions to return. On action can either be a MenuItem (from file `menu.py`) or a list. This list conatins a callback function, a text for the menu and optional a shortcut which can be put on a button for the remote.

Examples for this kind of plugin are the coversearch and the imdb plugin. They check if it makes sense to call the function and return actions.

Here is a simple example for an ItemPlugin. It checks if it's a video item and adds two actions to turn the software scaler on and off (by changing the global config variable, Ouch):

### IdlebarPlugin

Idlebar plugins are deceptively simple. Only two methods are reccomedded to be implemented. There is the init method which most often calls the parent init and sets any class variables based on arguments to it. The other method is draw. Draw as its name suggests draws in the idlebar area assigned to the plugin.

Draw is where most of the action happens. Basically you get a reference to the plugin, the X coordinate to start drawing at, an osd object to use to call all the drawing methods, and a tuple containing a type and object (what do they do? have never seen them used).

The most complicated part is probably the drawing methods. Basically we can write text and, draw images. The osd variable actually is the Plugin_Area class which is a skin_area subclass and not the real osd class you would expect. You can look in freevo/src/skins/main/area.py and freevo/src/skins/main/main.py for class definitions and possible methods. The osd variable is also how we get and set the font we wish to use.

Here is a full out example:

### MimetypePlugin

not written yet

## Notes for writing a plugin

### General

It's possible to add or remove a plugin in the `local_conf.py`. Freevo will search the plugins the main plugin directory, the source directory and (based on the plugin name) in the media subdirectories like video or audio.

You should place your plugin into the main plugin directory if it doesn't depend on the media type. If it only works for e.g. video, you should place it in the video plugin directory. By doing that, the string video fill be part of the plugin name.

Everything inside Freevo is some sort of plugin. Without plugin, the main menu will be empty and Freevo couldn't play a file at all. So it's possible to change (mostly) everything in Freevo by writing a plugin.

### Documenting the Plugin

You should not forget to document the plugin. Freevo can scan all available plugins by calling the plugins helper:

    -> freevo plugins -l
    <list of plugins>

    -> freevo plugins -i audio.playlist
    Name: audio.playlist
    Type: ItemPlugin
    File: src/audio/plugins/playlist.py

    Description:
    <...>

This documentation comes directly from the python source code. Use the Python docstring to document the plugin class. The first line will be used to show the short information for `-l`, the hole text will be used for `-i`.

Example:

### Which files to Import

You will need to import other parts of Freevo do make your plugin work. Most common are the following modules:

#### Module list

config | Basic configuration of Freevo. This also contains the settings from `local_conf.py`.
plugin | This contains all basic classes and functions for the plugin interface.
event | Some pre-defined events and the class `Event`to build your own events.
menu | Contains the class `Menu` to create menus for the plugin. This module also conatins `MenuItem`, a pre-defined item to put in a menu
item | This module contains the basic `Item` class to put into a menu.
rc | Functions for putting an event into the event queue

### User Configuartion

A plugin may require some special configuration. There are two possible ways:

#### Adding parameter to the constructor

A plugin may add more than the `self` to the `__init__` function. The user has to add the values when the plugin is loaded. This makes it possible to load a plugin more than once with different settings.

The following example adds two parameter. The first one has no default value and has to be added when activating the plugin. The second is optional.

#### Using local_conf.py

The second way is to use the `local_conf.py` to let the user set the variables. Since a plugin should be self contained, it should not add something to `freevo_config.py`. A better way is to use the member function `config` to return a list of variables for this plugin. If the user doesn't define the variable the default values will be added to the `config` module. Warning: the `config` function will be called in the `__init__` function of the base plugin class, do not try to use the variables before that.

Example: the plugin needs `FOO_NAME` and `FOO_FUNCTION` to be set. It defines the default values and a small description (not used at the moment)

### i18n support

Please keep in mind that Freevo has i18n support, meaning that the plugin can be translated into different languages. To support that, please add `_()` to each visible text string.

If your plugin is an external plugin and not distributed with Freevo, please use `self._()` for the translation and set the module for the translation with the function `translation`. This function exists for items and plugins. A item will inherit the translation settings from it's parent, so you only need to load it once.

To update the translation you need to call the script `update.py` in the i18n directory of Freevo or when you have an external plugin, call `setup.py` (see Plugin Distribution for details).

## Plugin Distribution

If you wrote a plugin, it would be nice to send it to the Freevo mailing list. If you like (and we), we could integrate the plugin into the Freevo distribution. If you don't want that, or your plugin is very special and we won't include it, there is a way to build an external plugin with the Freevo distutils as installer. We will add a plugin (or a link to a plugin) on the Freevo homepage if it's build for the Freevo distutils.

The Freevo distutils are an enhancement for Freevo of the normal Python distutils. To install a plugin, the user only needs to call `freevo install tarball.tgz`. To make this work, the plugin needs to use the same directory structure as the Freevo distribution and a `setup.py` and `MANIFEST.in` file in the root directory.

The directory structure should only conatin the needed directories and an empty `__init__.py` in the plugin directory. The `__init__.py` is needed by Python. Since the `__init__.py` isn't empty for `plugins/idlebar/`, it's not possible to write external idlebar plugins at the moment (you could place it in the normal plugin dir, that works). We are working on a solution to fix that. E.g if your plugin is a video plugin (video.foo) and contains one image `foo.png`, the directory structure may look like this:

The MANIFEST.in file describes a list of files to be included in the distribution (you can build a source distribution by calling `python setup.py sdist`).

The `setup.py` script is a Python distutils script with some default attributes for Freevo. If the plugin uses the Freevo file structure, a setup script could look like this:

For more details about the parameter for the setup function, read the Python manual.