DESOSA 2022

Quod Libet: From Vision to Architecture

Introduction

In this post, we look at Quod Libet’s architecture and how it achieves its goals. First, we examine the project’s architectural style and the forces that have guided it. Next, we show the structure of the program’s Containers, Components, and Connectors. Finally, we explain what the program’s structure means for Quod Libet’s development and Runtime, and how it has helped the project achieve its quality attributes.

Architectural Style

Quod Libet is generally structured as a typical GTK application. This means that it is fundamentally a model-view-controller architecture, except that GTK provides the controller. The program consists of UI elements (view) such as the browser and topbar, connected to the underlying Library, Player, and other data structures (model). These connections are usually made with GTK’s event system, though in the case of audio streams and OS features other techniques are used.

Containers

Containers view: The main execution environments, if applicable, as used to deploy the system.

Quod Libet relies on the single instance of the Application class, which is used to control the main sub-components of the system namely Window, Library, Librarians and the Audio Player itself, constitution and peculiarities of which are discussed in the Components section of this essay. The User may interact with the Application class via the Browser UI or via command-line using the CLI sub-component. The Application class acts as an interface for the aforementioned sub-components and has shared control on the Browser with the user, by updating the User Interface following changes of state. Through the Browser the user also mandates the application to interact with the Library and the Player, in addition to the Librarians which act as an interface so to manage multiple Libraries and takes care in keeping all the libraries up-to-date with changes. Until now, Librarians are transparent to Quod Libet, although their implementation is already in place in the other mentioned sub-components. The Application class also takes care in interfacing Quod Libet with any external audio streaming service and interfaces them to the Player component.

Worth noticing in the diagram, the Ex-Falso sub-system for library tagging may be seen as a sub-system on its own as it may be executed independently from Quod Libet and has sole access to the File System via the Library interface in the context of it being executed inside Quod Libet.

Components

Components view: Structural decomposition into components with explicit interfaces, and their inter-dependencies

Some interfaces shown were added for clarity. For example, there are several types of Plugins, but all have shared features, and the PluginManager class generally treats them interchangeably. In other languages an interface would have been necessary, but in Python heterogeneous lists are allowed, so a base “plugin” class only exists implicitly.

Application

Pictured is an isolated view of the Application class and its direct components. The Application class is constructed when the program is first launched, and contains the program’s state. Only one instance of the application is created, and it is made globally available. This is useful, because it gives plugins the freedom to alter different parts of the application. The Application is composed of the player, the library, the window, and the plugin manager.

Notice how the Quod Libet’s Window class has access to the application’s library and player. Those need to be constructed first by the Application, because they are passed to the window’s constructor. This is indicated by the empty diamonds, though because of Python’s ownership model the delineation between composition and aggregation is somewhat arbitrary.

Library

Figure: Library

Pictured is the isolated view of the library interface, and each of its implementations. The semantics of the library interface are similar to that of a dictionary, though what is used as they key depends on the implementation. Each implementation works as one view of the underlying data.

The SongLibrary class represents a collection of songs, which can be searched, filtered, and renamed.

The FileLibrary represents a collection of files, its dictionary uses file paths as keys.

The SongFileLibrary is the most commonly used. It extends both the SongLibrary and FileLibrary, and acts as a view of a collection of songs which are backed by audio files in the local file system.

The AlbumLibrary and PlaylistLibrary act as views of an underlying SongLibrary. A SongLibrary can be directly converted to an AlbumLibrary containing all the albums present in the original song list, or to a PlaylistLibrary in the same way.

The librarian is a library composed of the items in other libraries. It is important, because it allows the user to view their total music collection, even if it is distributed over a variety of sources.

Window

Figure: Window

Pictured is an isolated view of the window widget. It extends GTK’s window class (not shown), and has a constructor which performs most of the UI setup.

The window contains a hierarchy of UI elements (“widgets”), most of which are provided directly by GTK, though some are custom subclasses of GTK widgets. This hierarchy is not shown here, but it is worth mentioning that many of the widgets are created manually, rather than loaded from an XML file, as is often done for large applications.

The window also contains a browser, for showing the music collection. The user can easily replace the browser by selecting a different one in the window’s menu bar.

The PlaylistMux handles switching between different song orderings, it can play through an album, the user’s queue, or a selected playlist. It determines which song is played next, when one finishes.

The MainSongList is a widget which shows a table containing the user’s entire collection of songs. It may be shown or hidden depending on other parts of the UI.

Browser

Figure: Browser

The Browser is the primary way that the user selects what song to play. Each browser implementation is a UI element which displays the user’s collection in a different way.

For example, the CoverGrid browser displays all the user’s albums as a grid of icons, when the user clicks on an icon, the contents of the album are shown. The FileSystem browser shows the user’s music collection as it appears in their local file system.

Despite their varied appearances, all browser implementations share the same API.

Plugins

Figure: Plugins of Quod Libet

Plugins implement a variety of distinct archetypes. Because Quod Libet has nearly 100 individual plugins, each of the interfaces shown are implemented by several actual plugins. All plugins have unlimited permissions through their access to the global app object, so these archetypes are here for convenience only.

For example, the EventPlugin has a variety of handler function stubs which can be easily implemented by the plugin writer. These functions will automatically be connected to the relevant signals when the plugin is instantiated, so that it’s simple to create a new plugin with less boilerplate.

Similarly, it would be possible to add to the user interface by implementing any plugin, but implementing the UserInterfacePlugin interface makes this as easy as subclassing a GTK widget, because UserInterfacePlugins are added to the UI when they are loaded.

This plugin-archetype system recognizes how much functionality is shared between plugins, and makes creating new plugins as simple as possible.

Plugins are loaded and connected by the PluginManager class. This class uses the list of enabled plugins to load the relevant python code from file, producing a list of plugin objects. It then sets up each of the plugins, according to their type. For example, EventPlugins are connected to their relevant signals, and UserInterfacePlugins are added to the UI at this stage. Note that once this setup is done, other parts of the program will generally not interact directly with the plugins.

Connectors

Connectors view: Main types of connectors used between the components of Quod Libet. Software connectors perform transfer of control and data among components. The image below shows the structure of connectors in Quod Libet used. The connectors used in Quod Libet are mainly application API’s using TCP protcol.

Figure: Connectors structure of Quod Libet

Development

In this section we will show the Development view, which covers the system decomposition and the main modules and their dependencies, as embodied in the source code.

Quod Libet has the following build dependencies on all platforms.

  • Python 3.7 or greater
  • gettext 0.19.8 or greater
  • sphinx 1.3 or greater

Source Code

The most important folders are the following:1

  • browsers/* - Things in the View Menu
  • ext/* Extensions to Quod Libet / EF (i.e. the plugins)
  • formats/* - File format support
  • library/* Library management code
  • plugins/* Base classes and structural enabling plugins
  • operon/* Quod Libet CLI (command line) tool
  • qltk/* GTK+ widget sub-classes and extensions
  • util/* General utility functions

The image below shows the structure of the source code of Quod Libet, showing the most important folders and directories. Some small files have been omitted for readability sake. Quod Libet has a flat structure, with most directories being only 1 or 2 levels deep from root.

Figure: Source code structure of Quod Libet

Testing

For testing, Quod Libet requires pytest, flake8 and polib

Runtime

In this section we will show the Run time view, which indicates how components interact at run time to realize key scenarios, including typical run time dependencies. Quod Libet has the following Runtime dependencies on all platforms.1

  • Python 3.7 or greater
  • PyGObject 3.18 or greater
  • pycairo 1.8 or greater
  • mutagen 1.34 or greater
  • GTK+ 3.18 or greater
  • libsoup 2.25 or greater
  • feedparser and on Mac OS
  • PyGObject

To allow playback, Quod Libet has the following Runtime dependencies

  • GStreamer 1.8 or greater + typelibs and GStreamer Plugins Base or Xine-lib 1.1-1.2 There are also several highly recomended dependencies for the best user experience and to allow a wide range of formats to be played.
  • GStreamer Plugins Good
  • GStreamer Plugins Ugly
  • GStreamer Plugins Bad
  • GStreamer libav or ffmpeg
  • dbus-python
  • libkeyfnder-3.0 and typelib
  • libgtksourceview-3
  • libmodplug1

The image below shows a visualization of the Runtime view of Quod Libet.

Figure: Runtime of Quod Libet

Realization of Quality Attributes

In this section we will discuss the architecture realizes key quality attributes, and how potential trade-offs between them have been resolved. In Essay 1 the following quality attributes were identified to be essential to Quod Libet.

  • Usability
  • Performance
  • Multiplatfom solution Usability of Quod Libet is realized through having a simple codebase and strict coding guidelines2 to make the source code easy to read, understand and contribute to. Quod Libet also comes with a guide to help potential developers to get started with contributing to Quod Libet.

Performance of Quad Libet is realized through encouraged performance analysis [^Perfomance] where developers are encouraged to run performance analysis tools to test for regressions, memory leaks and slowdowns before they submit their patch. Quod Libet’s multiplatform attribute has been realized by using the GTK+ toolkit and python, both of which can run on a number of operating systems without any additional modifications. This is evident by the fact that the same source code can run on multiple platforms.

Modularity

Due the simplicity of writing plugins for Quod Libet, it is possible for users to add extra functionality without breaking any of the underlying Quod Libet codebase.

Testing

Quod Libet provides the developer with a number of tests that can be used to check for regression. 1 These tests can be executed by running

./ setup.py
test

Quod Libet requires that any user submitted patch does not break any of the Unit tests, and any changes to the test needs to be explained by the user before the patch is accepted.

API Design

In this section we will discuss which API design principles, if any, have been applied by Quod Libet. The API’s used in Quod Libet consist of package API’s for Gstreamer and XineList and the Python connect API is used by the Quod LibetWindow. An OS system API is used for connection to the file-system. Some behavior may be platform dependent, since calls are made to the operating system socket APIs.


  1. https://quodlibet.readthedocs.io/en/latest/development/guidelines.html ↩︎

  2. https://quodlibet.readthedocs.io/en/latest/development/guidelines.html ↩︎

Quodlibet
Authors
Richard Prihradský
Nienke Eijsvogel
Lorenzo Albani
Jackson Campolattaro