Home

Awesome

Girandole

Girandole is an HTTP REST API on top of Beets. Its goal is to provide a more feature-rich alternative to the native Web plugin. We also like to think the code is more maintaible thanks to using FastAPI and Pydantic.

Much of the initial modeling was done by peeking at the source code of the Beets Web plugin, but over time the implementations have diverged increasingly. Therefore, the extent of their interchangeability cannot be guaranteed.

Motivation

The immediate motivation for this project was to have a HTTP interface to the Beets library that supports more than just reading from it. This is where the native Web plugin is too limited, so we decided to create something new altogether.

Having such an API has allowed as to create Red Candle, a web app which enables us to manage our Beets library.

Features

Currently, among other things, the following features are supported:

Installation and configuration

Whatever way you choose to install, the setup is basically as follows.

Dependencies

The immediate dependencies of Girandole itself are:

Furthermore Girandole is dependent on some tools:

Local installation

Simply install the aforementioned dependencies. It is recommended to use a virtual environment.

In a Python 3.7 environment, you can simply do:

$ pip install fastapi uvicorn beets

Installing the whatlastgenre fork is a bit more cumbersome for now, since the setup.py file does not include installing the Beets plugin. You have to obtain the code from Git and place the files some place which is discoverable by the used Python interpreter. Later you'll see how we'll make this package discoverable by using $PYTHONPATH.

For example:

$ git clone \
      --depth 1 \
      --branch feature/api-friendly \
      https://github.com/bartkl/whatlastgenre.git \
      /desired/target/path

Make sure that /desired/target/path is within the module search path, for example in your $PYTHONPATH.

When done, please proceed to the Configuration section.

Installing using Docker container

You can automate the installation described above in a Dockerfile.

Example Dockerfile

My own Dockerfile, the one I use on my Raspberry Pi, looks as follows:

FROM bartkl/uvicorn-fastapi:python3.7

ENV PYTHONPATH "${PYTHONPATH}:/opt/whatlastgenre:/opt"
ENV BEETSDIR "/etc/beets"

COPY ./requirements.txt /etc/girandole-requirements.txt
RUN pip install -r /etc/girandole-requirements.txt

RUN git clone \
        --single-branch \
        --branch feature/api-friendly \
        https://github.com/bartkl/whatlastgenre.git \
        /opt/whatlastgenre

COPY ./girandole /opt/girandole

WORKDIR /

As you can see, it is pretty straight-forward. Note that the installation of Beets is concealed in the requirements.txt file. The defined environment variables PYTHONPATH and BEETSDIR will be explained in the Configuration section.

Setup and configuration

Now that all the necessary installations have been done, we need to configure the environment, tools and application.

There's a few aspects to this.

Beets and whatlastgenre

You need to supply configuration for Beets and whatlastgenre. You can use your existing configs, or create dedicated new ones, but in both cases you have to make sure they will be available within the application.

Girandole environment

There are two environment variables with which you can control Girandole's behavior:

If running in a Docker container, make sure these environment variables are passed in the container. I recommend defining a docker-compose.yaml file in which you set the environment.

Girandole config

Finally, there's the application config: config.ini. Currently, the only two sections and settings can most easily be demonstrated by example:

[girandole: albums]
paths in response = yes

[beets]
windows paths = no

Example docker-compose.yaml

This is the docker-compose.yaml I use on my Raspberry Pi:

version: '3'
services:
  girandole:
    build:
      context: .
      dockerfile: Dockerfile.py37.armv7  # See `Dockerfile` earlier.
    ports:
      - ${GIRANDOLE_PORT}:8080
    volumes:
      - ./girandole:/opt/girandole  # Remove in production.
      - ${GIRANDOLE_MUSIC_DIR}:${GIRANDOLE_MUSIC_DIR}
      - ${GIRANDOLE_CONFIG_DIR}/config.ini:/etc/girandole/config.ini
      - ${GIRANDOLE_BEETS_DB}:/etc/beets/library.db
      - ${GIRANDOLE_BEETS_CONFIG}:/etc/beets/config.yaml
      - ${GIRANDOLE_WLG_DIR}:/root/.whatlastgenre
    environment:
      - GIRANDOLE_CONFIG_DIR:${GIRANDOLE_CONFIG_DIR}
    entrypoint: uvicorn --host 0.0.0.0 --port 8080 girandole.main:app --reload

The variables used here are defined in the accompanying .env file:

GIRANDOLE_PORT=8080
GIRANDOLE_MUSIC_DIR=/media/droppie/libraries/music
GIRANDOLE_BEETS_DB=/media/droppie/libraries/music/.meta/beets/library.db
GIRANDOLE_BEETS_CONFIG=/media/droppie/libraries/music/.meta/beets/config.yaml
GIRANDOLE_WLG_DIR=/home/bart/.whatlastgenre
GIRANDOLE_CONFIG_DIR=/home/bart/.dotfiles/local/oblomov/conf/girandole

Running the server

Local

As can be seen in the example docker-compose.yaml earlier, the way to serve the app using uvicorn is:

$ uvicorn --host 0.0.0.0 --port 8080 girandole.main:app --reload

Running the docker container

Start the Docker container including the mounts and environment:

$ docker-compose up

If you've changed something about the Docker configuration files, you should restart the container. If you've changed the Dockerfile contents in a way that requires rebuilding, you can call:

$ docker-compose up --build