Moovida Developer’s Guide

This document describes Moovida’s internals and its development process. It is targeted at people interested in contributing to Moovida and writing plugins for it.

The current version of this document is available in the file elisa-core/docs/developers_guide.rst in the source tree.

Getting Started

On Linux

Dependencies

Moovida needs a set of dependencies to run. If you already have it running you can skip to Getting the Source Code.

Required dependencies:

  • python (>= 2.5)
  • python-pgm (>= 0.3.12)
  • python-gst0.10
  • python-gobject
  • python-gtk2
  • python-cairo
  • python-setuptools
  • python-simplejson
  • python-twisted (>= 2.2)
  • python-twisted-web2
  • python-openssl
  • python-cssutils
  • python-dbus
  • python-storm
  • python-xdg
  • ttf-liberation

Optional dependencies:

  • python-twill - needed for the Flickr plugin
  • python-gpod - needed for iPod support
  • python-avahi - needed for DAAP support
  • python-coherence - needed for UPnP support

Getting the Source Code

Moovida developers use the Bazaar distributed version control system to publish and share the source code. The main line of code is hosted on Launchpad. In order to retrieve it you will need to have Bazaar installed on your system (see Bazaar download page). For more information on how to use Bazaar please refer to the official Bazaar tutorial.

On the command-line type the following to download the main development branch:

$ bzr branch lp:moovida

That will create a directory called moovida containing the latest version of the source code of Moovida. Depending on your Internet connection that might take a while; as of today this process downloads over 80MB of data.

Note

Moovida used to be called Elisa prior to June 2009. While the renaming was effective in most relevant places, some references to Elisa still remain, notably in the source code where the Python modules are still using the elisa namespaces.

Running the Development Version

Go into the directory where the source code was downloaded:

$ cd moovida

Append Moovida’s python modules to the PYTHONPATH environment variable:

$ export PYTHONPATH=$PWD/elisa-core:$PWD/elisa-plugins:$PYTHONPATH

Launch Moovida:

$ python elisa-core/bin/moovida

You should now have Moovida’s splash screen appearing and soon a fullscreen Moovida showing up. Congratulations!

To exit, press the ALT+F4 key combination, or alternatively press the ESC key and click the close button of the window.

Warning

If you have installed Moovida on your system previously, you need to uninstall it otherwise the development version will not work properly.

This is a bug that will be fixed; you can follow its progress there: https://bugs.launchpad.net/moovida/+bug/255739

On Windows

Warning

This section is a work in progress. It lacks a how to download and launch the SDK. Reuse http://www.moovida.com/wiki/TipsAndTricks/HackOnWindows

The first thing to do before setting yourself to hack on Moovida is installing it on your computer. Instructions are available on the website’s download page.

Architecture Overview

Moovida’s architecture aims to make adding new features very easy. To achieve that goal, it is designed to be as modular as possible and many modules are meant to be extensible, that is, new functionalities can be added to them and distributed without being included in the core thus favoring participation external to the core developers and designers.

Moovida’s API reference is available on the website and can be built from the source tree using the script elisa-core/builddoc.py.

Core Technologies

Moovida uses the following main technologies:

Making Your Way Around the Code

Warning

This section is a work in progress.

Moovida is divided into its core contained in elisa-core/elisa/core and its default plugins elisa-plugins/elisa/plugins. Be it the core or the plugins they all provide facilities freely accessible from any piece of code.

The elisa.core.application.Application object is the main entry point of Moovida. All the necessary components for Moovida to run are accessible from there.

To retrieve a reference to the global instance from anywhere in the code:

from elisa.core.common import application

It holds a reference to the following objects:

User Interface

Warning

This section is a work in progress. It lacks explanation about:

  • animation system elisa.plugins.pigment.animation
  • library of models concept elisa.plugins.base.models
  • view mode concept elisa.plugins.poblesec.base.list.GenericListViewMode
  • list of base widgets elisa.plugins.pigment.widgets

Moovida’s user interface code design strives to make easy the following tasks:

  • adding new features without the burden of writing graphical user interface code.
  • writing innovative, clean and appealing graphical user interface.
  • changing the appearance of existing graphical user interface (layout, icons, animations, etc.) without modifying it.
  • displaying differently the information without changing the way it is generated or retrieved
  • keeping a high level of visual and ergonomics consistency accross the whole interface

Moovida’s user interface is created by controller objects. A controller roughly corresponds to one screen of the user interface. It is in charge of pulling data, displaying it and reacting to user input.

The default user interface uses the Pigment toolkit for its rendering which in turn uses OpenGL taking full advantage of modern computer graphics. A library of widgets is available on top of Pigment in a Moovida plugin: elisa.plugins.pigment.widgets.

The default user interface is contained in a plugin (elisa.plugins.poblesec) and defines standard controllers that can be reused as-is thus avoiding UI code re-write and encouraging consistency which is critical to keeping the learning curve shallow for users. For example:

Note

Controllers can be decorated, that is, their behaviour, appearance and content can be altered by decorators. Decorators are Python functions that take as only parameter the controller they decorate and are called whenever their corresponding controller is instantiated. This allows a plugin to modify the user interface at will.

Resources

Warning

This section is a work in progress.

Data in Moovida can be retrieved through components called resource providers.

A resource provider provides access to resources via a REST API (GET/POST/PUT/DELETE methods, see Roy Fielding’s thesis for a detailed description of the principles of REST).

Resources are uniquely identified by their URI.

Resource providers are never used directly: they are hidden behind the resource manager that acts as a proxy. The resource manager knows of all the resource providers and is responsible for forwarding a query to the correct provider, and forwarding back the response to the client code. For the resource manager to be able to dispatch the queries, resource providers define all the types of URIs they can handle in the form of a regular expression.

The most basic query is the GET one. Given a URI, it returns a representation of the associated resource. In Moovida, resources are represented using models.

Database

Warning

This section is a work in progress.

Message Bus

Please consult:

Configuration

Warning

This section is a work in progress.

Services

Warning

This section is a work in progress.

Plugins Management

Warning

This section is a work in progress.

Input Handling

Warning

This section is a work in progress.

All user input in Moovida is handled received by elisa.core.components.input_provider.InputProvider objects which send these input events encapsulated into elisa.core.input_event.InputEvent instances to the input manager.

Every input provider implements a specific input protocol (e.g.: keyboard input using Pigment, remote control via LIRC, etc.).

Media Scanning

Warning

This section is a work in progress.

Player

Warning

This section is a work in progress.

Threading Model

By default all of Moovida’s code is executed in one thread referred to as the main thread. User input processing, networking are examples of operations that are dealt with in the main thread.

An essential point of Moovida is to provide a reactive user interface which never blocks and always answers the user’s requests. In order to achieve that, all method calls for which the return might not be fast enough (e.g. a long computation, retrieving data from the Internet, generating a thumbnail, etc.) have to be made non-blocking. A few techniques exist to do so:

In either case Moovida uses Twisted’s deferred object to handle delayed returns. Extensive explanation and examples explaining the concept exist at:

In short, a deferred is a promise that a function will at some point have a result.

Note

Some background threads exist that one does not need to be aware of:

  • GStreamer uses multiple threads to optimize its multimedia processing.
  • Pigment uses a thread dedicated to rendering.

Inter-Objects Communication

Apart from direct method calls, two messaging systems are used in Moovida:

Communication to the core components should use method calls as much as possible when it’s for 1 to 1 communication. In other cases, the following rules can help choosing between a bus message and a signal:

  • the message bus should be used for 1 to n asynchronous communication.
  • signals should be used for 1 to n synchronous communication. Their emission is blocking (gobject.GObject.emit) and should be used with care.

It is important to remember that the messages on the bus are dispatched in the main thread whereas signal emissions are done in the calling thread.

Logging

Moovida provides a logging framework allowing the addition of debugging, error and informative messages. It is useful for visualizing at what is happening behind the scenes at runtime without having to modify the code.

In order to use it the class where you want to add such logging must inherit from elisa.core.log.Loggable The core classes of Moovida already inherit from it. It provides with 5 new methods corresponding to the 5 logging levels:

  1. error(message): raises a SystemExit exception; don’t use it.
  2. warning(message): use that when an operation failed but it doesn’t prevent Moovida from running.
  3. info(message): display a human/user readable message.
  4. debug(message): display a message intended for developers debugging the application; it can be information about variable value for instance.
  5. log(message): display a message.

When launching Moovida it is then possible to filter what messages should be outputted using 2 different criteria:

  • by logging level
  • by class also referred to as logging category

This is achieved via the ELISA_DEBUG environment variable.

Logging levels are incremental: if you want to view messages from one level, say the LOG (5) level, you will also see the messages from all the levels below, in that case DEBUG (4), INFO (3), WARNING (2) and ERROR (1).

Examples:

# LOG level output (very verbose)
$ ELISA_DEBUG=5 moovida

# DEBUG level output
$ ELISA_DEBUG=4 moovida

You can filter messages by class/category as well:

# let's DEBUG the media_manager
$ ELISA_DEBUG="resource_manager:5" moovida

# let's see application INFO and config DEBUG
$ ELISA_DEBUG="application:3,config:5" moovida

As you may have guessed syntax is the following:

ELISA_DEBUG="log_category:integer[,...]"

log_category is the name of the class ‘’uncamelified’’ that is transformed to have the capital letters uncapitalized and with an ‘_’ between the words.

Example:

ResourceManager becomes 'resource_manager'
BrowserController becomes 'browser_controller'

This default can be overriden by setting a value to the class variable log_category.

integer is the logging level from 1 to 5.

Note

Messages are by default displayed on the standard error output.

Unicode and String Encoding

Warning

This section is a work in progress.

It requires an import of the information located at http://www.moovida.com/wiki/I18nIssues

Automated Testing

Warning

This section is a work in progress. Reuse elisa-core/docs/tests_code_coverage.rst from the source tree.

Writing Test Cases

Executing Test Cases

Measuring Code Coverage

Social Aspects

Communication Among the Community

If you wish to discuss ideas, ask questions, get involved or simply observe the Moovida community, a set of communication means are at your disposal:

  • The developers mailing list hosts many discussions about current and future developments.
  • The forum is the place to interact about anything related to Moovida: tips, features, ideas, problems, etc.
  • The #mooovida IRC channel on freenode irc://irc.freenode.net/#moovida is where you will meet Moovida users and developers live.

Current Development

Moovida’s development is largely published via its Launchpad project page.

You will find there a list of features and items that are being thought about or worked on in the list of blueprints.

It is also the place to report bugs and problems you might encounter. Up to date instructions as how to do this can be found on Moovida’s website.

Releases

Warning

This section is a work in progress.

A release of Moovida is made public every month ensuring a regular stream of improvements for our users.

The release procedure is available in the file elisa-core/docs/release_procedure.rst.

Submitting Code

All code changes coming in to Moovida’s core are reviewed by a core contributor. Code reviews happen on the Moovida merges mailing list. If you wish to have code you wrote added to Moovida’s codebase please follow these steps:

  1. Get the current development version of Moovida:
$ bzr branch lp:moovida
  1. Do the desired modifications.
  2. Create a patch:
$ bzr diff > my_changes.patch
  1. Subscribe to the merges mailing list.

  2. Send an e-mail to the list explaining the purpose of the change. Don’t forget to attach your patch. The subject must start with ‘[MERGE]’. See the following e-mail example:

    Subject: [MERGE] Fix FLV playback
    
    Hello everyone,
    
    The enclosed patch intends to fix the bug #12345 whereby FLV playback was
    not working under some conditions.
    
    Thanks for the review,
    
    Robert Johnson
  3. A core contributor will get back to you with a vote. There are three possible outcomes:

  • approve: the code and the related functionality is fine and complies with the guidelines, the code will be included in the main codebase.
  • tweak: the code requires some minor modifications that will not require another review before inclusion in the main codebase.
  • resubmit: the code needs major changes that will require another review before inclusion in the main codebase.

Guidance and advice will be provided in all cases explaining what the next steps are to go forward.

Please detail the merge request e-mail well including:

  • the reason why you are making this change.
  • how this change achieves this purpose.
  • anything else you may have fixed in passing.
  • anything significant that you thought of doing, such as a more extensive fix or a different approach, but did not or could not do now.
  • how should a careful reviewer test it (unit tests, functional tests).
  • links to the relevant bug(s)/blueprint(s) on Launchpad.

Note

Good reviews take time. They also regularly require a solid understanding of the overall code base. In practice, this means a small number of people often have a large review burden.

Once submitted, the merge request is tracked by the Bundle Buggy system letting you know about its current status.

The following, noncomprehensive checklist can help smoothening the process by ensuring high quality code submission and reviews. Before submitting the merge request please make sure that:

  • it does not introduce any functional regression and all Moovida’s unit tests still pass
  • it works on all supported platforms (as of now Linux, Windows XP, Vista and 7)
  • unit tests code coverage is reasonable (above 80% is a good indicator).
  • FIXMEs have been looked over.
  • no memory leak has been introduced (see Leak Prevention section on the wiki).
  • all new modules, new classes, public instance variables, class variables and public methods are documented (members, arguments, return value, exceptions).
  • Coding Style Guidelines are respected.

Coding Style Guidelines

General Rules

Moovida’s developers stick with official Python recommendations:

  • PEP 8 for coding style.
  • PEP 257 for inline documentation (docstrings).

Doctring format in Moovida follows the epytext markup language. This format makes the generation of pretty API documentation very easy.

A few extra rules specific to Moovida apply:

  • Indentation is done using 4 spaces. Tabs are not allowed.
  • Lines should not exceed a length of 80 characters.
  • All public methods, instance and class variables have to be documented.

Naming Conventions

  • Package and module names should be all lower-case, words may be separated by underscores.
  • Class names use CamelCase, example: PlayerPlugin.
  • The names of functions, variables and class members use all lower-case, with words separated by underscores, example: toggle_pause.
  • Class method names use CamelCase.
  • Variables representing user defined types use CamelCase.
  • Internal methods and variables are prefixed with a single underscore, example _do_something_internal.
  • Never name a variable _ (it is used by convention for internationalization).

Licensing

Moovida is free software. Its core is dual-licensed under the GPLv3 and Fluendo’s proprietary license.

In order to make sure that all of our copyrights can meet the recordkeeping, in order to dual license Moovida’s code and in order to be able to enforce the licenses most effectively, it is required that each author of code shipped with Moovida provides a copyright assignment. It grants back to the author a perpetual right to use, modify, and distribute his/her code and ensures that it will always be made available under the terms of the GNU Public License.

This is common practice in many open-source projects. The Free Sofware Foundation with the GNU project, Sun Microsystems, the Apache Software Foundation among others follow this rule.

Moovida’s copyright assignment form can be downloaded from there.

Plugin Development

Before starting developing a plugin you should of course have Moovida running on your computer. In order to retrieve the latest development version please follow the instructions from the Getting Started section.

Creating a Plugin

An example plugin has been designed to facilitate writing a new plugin. It illustrates how to implement Moovida functionalities often used in plugins, for example how to display data in a list, how to play media files, how to display informative messages, etc. Also creating a plugin requires writing a small amount of boilerplate code. As no one likes to write boilerplate code over and over, the example plugin can be used as a base by plugin writers eager to get their plugin working.

The example plugin is available as a zip file at the following URL:

http://www.moovida.com/documentation/developers_guide/moovida_example_plugin.zip

If you prefer to use the Bazaar version control system the example can also be found in the following repository:

lp:~moovida-developers/moovida/example-plugin/

Please refer to the README.txt file inside it to run it and understand its structure.

In order to personalize the example plugin to your needs you will need to change the name, description, long_description, author and author_email fields of the setup.py file.

Guidelines

In order to ensure a high visual quality, a high degree of usability and reliability, relevant guidelines have been crafted for plugin writers to follow.

Common Checklist

  • Provide a relevant plugin title and description (description and long_description keywords in the setup.py script). The title should not contain the words “Moovida” or “plugin” as that is redundant information.
  • Ensure the plugin contains an ‘icon’. (a PNG file in the elisa/plugins/<plugin_name>/icons/ folder). See UI & Presentation below for more information.
  • Check that all strings are internationalized and consistent with the rest of the UI.
  • Check that all source files contain headers mentioning the copyright, the license and the actual author (name, e-mail address and website/blog address if appropriate).
  • If the plugin uses or accesses a service, then the service providers website address must be supplied.
  • Ensure the code is documented where relevant, including comments which include complex code paths, hacks or workarounds.
  • If an i18n/ folder is present, ensure it is in synchronization with the code.
  • Take care that the name of the ‘.egg’ (see Publishing a Plugin for a definition) is independent from the version of Python used to generate it (e.g. does not contain “-py2.5”).
  • The plugin should be tested under both Windows and Linux.
  • Check that the UI does not become ‘frozen’ when the next level is loaded (you should be able to change the selection with up and down arrow key during the loading time/transition).
  • Check that the plugin does not raise any exception for the common cases.
  • Check if the title of the media is displayed in the player during ‘playback’.
  • Ensure lists and content are sorted alphabetically (where appropriate).
  • Check if the “alphabetical shortcut bar” is available for long lists of content.
  • Media should be accessible with the least number of clicks - maximum 3 clicks.

UI & Presentation

  • Provide a high quality ‘icon’. If you are able to supply a square picture file measuring 512 x 512px that best represents your plugin then this is preferred. Else, please provide a good high resolution logo of the website or service that supplies your plugin content (many websites and services have PR or press sections where this can be obtained).
  • Supply high quality provider logos for the player.
  • Every word on the lists, particularly main hierarchal steps, should be capitalised. Original formatting should be respected when ‘pulling in’ and listing the actual media or playable content.
  • Lists without artwork should not display the artwork box.
  • Thumbnail/artwork box aspect ratio should respect those of the actual image used.
  • Thumbnail size should be at least 350 x 350px.
  • Background image size should be at least 600 x 600px.

Publishing a Plugin

In order to publish a plugin you will need to provide a ‘.egg’ file stored on a publicly accessible server. Advanced users can then copy that file to their $HOME/.moovida/plugins directory to use it.

The ‘.egg’ file contains everything needed for your plugin to run. To generate it from the command-line go to the example plugin’s directory and type the following:

$ python setup.py bdist_egg

That will create the ‘.egg’ file which name starts with your plugin’s name in the dist directory.

If you wish your plugin to be installable from within the user interface of Moovida ('PLUGINS' > 'Available Plugins' section) it is required to send a request via e-mail to the Moovida developers at developers-list@moovida.com Please subscribe to it prior to sending the request using the registration form. A review of the plugin will be performed and guidance provided if needed. Once accepted it will appear immediately to all Moovida users.

Note

To distribute a plugin it is recommended to create a new project in Launchpad. To do so go to the ‘Register a Project’ page. All Moovida plugins follow the same convention: their name starts with “elisa-plugin-“. For example, the plugin for browsing movie trailers is called “elisa-plugin-movie-trailers” and is located at the following address: http://launchpad.net/elisa-plugin-movie-trailers

Once done it is also recommended to register your project in the Moovida Universe on Launchpad that regroups all the Moovida plugins.

Internationalizing a Plugin

Warning

This section is a work in progress:

  • it is up-to-date but requires a detailed review.
  • it has only been tested under Linux so far.

The following explains how to:

  • convert the strings from a plugin meant to be displayed in the user interface into translatable strings.
  • extract these strings to a PO template, reusable later on by translators.

i18n Package Preparation

Go into your plugin directory and create an empty i18n python package:

cd elisa-plugins/elisa/plugins/myplugin
mkdir i18n
touch i18n/__init__.py

This package will contain:

  • the .pot file
  • the .po and .mo files of each translation of the plugin

Edit your setup.py file, in the setup() function, update (or add) the package_data parameter like shown below:

setup(name='elisa-plugin-myplugin',
      ...
      package_data={'elisa.plugins.myplugin.i18n':
                    ['*.po', '*/LC_MESSAGES/*.mo', '*.pot'],
                   },
      ...)

Hence, the plugin tarball will contain the .po files supplied by the translators, the .pot template and the compiled .mo files.

Initial Generation of the elisa-plugin-myplugin.pot File

Go in your plugin directory and create the i18n/messages.pot file:

cd elisa-plugins/elisa/plugins/myplugin

Check that you have the setup.cfg file similar to the one in elisa/plugins/skeleton/basic and that you have updated it with your plugin name, copyright and email information and run:

PYTHONPATH=../../../../elisa-core python setup.py pot_update

That’s it! Translators can now reuse the elisa-plugin-myplugin.pot template to start new translations of the plugin. See Localising a plugin below

Every time the developer adds/updates translatable strings in their code, the .pot template should be updated, at least.

Easy Translation Using Launchpad’s Rosetta

Launchpad has a great tool to allow translations of your own project called Rosetta. You can use the Moovida translation project in Rosetta to host translations for your plugin. This way users translating Moovida core will also see your project and will be able to translate it in a streamlined process. Just use the ‘upload’ feature of Rosetta to put all current files in the system.

If you don’t want to host the translations in the Moovida project, you have followed the above instructions and have a project registered on Launchpad you can easily set it up for translations and automatic imports from Bazaar branches. Rosetta will then pick up all changes to the translation templates.

Now you will only need to download new translations and commit them to your branch from time to time. Also you will have to upload the template file whenever there are new or updated strings. It is advised to only commit and use complete translations (as in 100% complete), as having a mixed-language interface is the worst case scenario. Remember - using Moovida’s core translation project will probably speed up translation of your plugin as the translators will not have to jump through hoops to find all projects available for translation.

Localising a Plugin

Warning

This section is a work in progress:

  • it is up-to-date but requires a detailed review.
  • it has only been tested under Linux so far.

The following explains how to make a new translation for the existing plugin ‘elisa-plugin-myplugin’. The language we want to translate to is French - ‘fr’ (only use xx_YY locale notation if YY is not uppercase xx, i.e. it is a certain dialect, like ‘pt_BR’ for Brazilian Portuguese). We assume the plugin has already been internationalized and has an i18n directory already.

There are currently 2 supported ways to translate a plugin:

  • using Launchpad’s Rosetta
  • using Moovida i18n tools

Using Launchpad’s Rosetta

This is the only supported approach for Moovida core plugins and also recommended for external plugins as it greatly simplifies the whole process. All of Moovida’s core is handled in Rosetta and we are trying to get as much external plugins in there as well. To start translating, simply log in to Rosetta, set your languages in your profile, select the plugin you want to translate and that’s it! We will pick up new translations as soon as possible.

Using Moovida i18n Tools

This approach uses Edgewall’s Babel, integrated into Moovida’s i18n tools. It is useful if a plugin is not handled in Rosetta and the author will accept i18n patches.

Create a new po file

Go in the plugin directory and initialize a new catalog for the locale:

$ cd elisa-plugins/elisa/plugins/myplugin
$ PYTHONPATH=../../../../elisa-core/ python setup.py init_catalog \
  --output i18n/fr.po --locale fr

This will create a i18n/fr.po file. You can use a tool like poedit to translate the strings inside, or do it manually (not recommended).

Compile the po file to a mo file

Go in the plugin directory and compile the previously created catalogs:

$ cd elisa-plugins/elisa/plugins/myplugin
$ PYTHONPATH=../../../../elisa-core/ python setup.py build_po

This command will compile all the .po files it will find in the plugin’s i18n directory.

Update the po file

If the plugin developers add new strings to translate in their code, it is their responsability to update the po template file, usually located in i18n/elisa-plugin-myplugin.pot.

If the po template file has been updated, the po files need to be updated too, so that it’s possible to translate the new or updated strings:

$ cd elisa-plugins/elisa/plugins/myplugin
$ PYTHONPATH=../../../../elisa-core/ python setup.py update_catalog \
  --output i18n/fr.po --locale fr

If the –locale argument is not supplied, Babel will update all the .po files it will be able to find in the current directory.

Once new strings have been translated, the po file can be compiled to a mo file as described in the Compile the po file to a mo file section.