Sep 182018
 

Introduction

For many years, I have been using Nightingale to play music on my local desktop computer.  Back in the day, it was one of the few Linux music players that was able to import Apple iTunes media libraries, which contained all of my music – and more importantly – my song ratings, which I use to dynamically create my playlists (e.g., 3-star, 4-star, 5-star, etc.).  (After investing lots of time into listening and rating all of my songs, I could not afford to start using a new application, resetting my ratings and playlists.)

Even though it is no longer maintained, Nightingale did everything I wanted – rate songs, update playlists automatically based on ratings, automatically add new songs dropped into the watched folder, export playlists to my phone, etc.  However, recently, nightingale started segfaulting (i.e., crashing with no useful message of explanation).  Often this is indicative of incompatible package dependencies, which frequently, eventually occurs to all unmaintained projects which have package dependencies, because no one is keeping the project current with the latest versions of its dependencies.  How could this be fixed?

Docker Solution

Docker provides a convenient method for running lightweight containers of single applications within an isolated root file system.  Using docker an image was constructed that starts with an Ubuntu base from the era when Nightingale was last supported, providing contemporary package dependencies, using this Dockefile:

FROM ubuntu:14.04
RUN apt update && apt install -y \
    autoconf \
    build-essential \
    cmake \
    firefox \
    g++ \
    git \
    gstreamer-1.0 \
    gstreamer1.0-alsa \
    gstreamer1.0-plugins-bad \
    gstreamer1.0-plugins-base \
    gstreamer1.0-plugins-good \
    gstreamer1.0-plugins-ugly \
    gstreamer1.0-pulseaudio \
    libasound2-dev \
    libdbus-glib-1-dev \
    libgnomevfs2-dev \
    libgstreamer-plugins-base1.0-dev \
    libgtk2.0-dev \
    libidl-dev \
    libnspr4-dev \
    libsqlite0-dev \
    libtag1-dev \
    pulseaudio \
    pulseaudio-utils \
    unzip \
    vim \
    xterm \
    zip \
    && \
    mkdir -p /local && \
    cd /local && \
    git clone --single-branch -b gstreamer-1.0 https://github.com/nightingale-media-player/nightingale-hacking.git && \
    cd /local/nightingale-hacking && \
    sed -i 's/sed \(.*\) nightingale/sed \1 compiled\/dist\/nightingale/g' build.sh && \
    ./build.sh && \
    make install
#RUN cd /local/nightingale-hacking/debian && dpkg-buildpackage -uc -us
CMD \
    groupadd -o -g $GID $GIDN; \
    groupadd -o -g $AID audio; \
    groupadd -o -g $AID audio2; \
    useradd -u $UID -g $GID -G sudo,audio,audio2,video,plugdev,staff,games,users -M -N -o -s /bin/bash $UIDN && \
    cd /home/$UIDN && \
    start-pulseaudio-x11 && \
    su $UIDN -c /usr/bin/nightingale

Building this Dockerfile does the following:

  • Start with a minimal, popular Ubuntu 14.04 base from the 2014 era.
  • Install all the packages necessary to build Nightingale from source code and run it.
  • Download the latest source code for Nightingale which uses GStreamer-1.0 instead of the old 0.1 version.
  • Make a small bug-fix to the build script.
  • Build Nightingale from source and install it inside the Docker image.
  • Define the command (CMD) to be executed whenever an active container is started based on this image.

This Dockefile can be built locally using the following command, executed from the same directory containing the above Dockerfile:

docker build --tag=nightingale .

This takes about 8 minutest to build on my box.

Please note there are no hard-coded references to user id names, directories, etc.  This makes the solution fully portable and reusable for other users on different computers.

Running Nightingale in Docker – Exporting Video and Audio

Because Docker isolates its containers, in many ways any running processes are as accessible – or inaccessible – as processes running on remote machines.  In fact, many older solutions to sharing the audio and video of a local computer with Docker containers are built around using SSH and VNC to export displays just like a remote machine.  However, this is a fairly heavyweight solution with unnecessary overhead, considering that Docker can map local resources (files and directories) into its containers.

Several solutions have been posted on the web and StackOverflow detailing how to do export audio and video from a local docker container to its host.  The solution I adapted for this case looks like:

alias music='xhost +local:$(docker inspect --format="{{ .Config.Hostname }}" \
    $(docker run -it --rm \
        --env="DISPLAY" \
        --env="QT_X11_NO_MITSHM=1" \
        --env="UID=$(id -u)" \
        --env="UIDN=$(id -un)" \
        --env="GID=$(id -g)" \
        --env="GIDN=$(id -gn)" \
        --env="AID=$(getent group audio | cut -d: -f3)" \
        --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
        --volume /etc/resolv.conf:/etc/resolv.conf:ro \
        --volume /home/$(id -un):/home/$(id -un) \
        --volume /run/dbus/:/run/dbus/ \
        --volume /dev/shm:/dev/shm \
        --volume /dev/snd:/dev/snd \
        --volume /dev/shm:/dev/shm \
        --volume /etc/machine-id:/etc/machine-id \
        --volume /run/user/$(id -u)/pulse:/run/user/$(id -u)/pulse \
        --volume /var/lib/dbus:/var/lib/dbus \
        --volume /run/media:/run/media \
        --privileged \
        --group-add $(getent group audio | cut -d: -f3) \
        --group-add $(getent group video | cut -d: -f3) \
        -d \
        --name=nightingale \
        nightingale))'

Please notice this is wrapped in a bash alias (stored in my .bashrc file), which allows me to execute this monstrosity simply as:

music

Briefly, the alias contains a few levels of complex nested commands, explained here:

  • docker run ... – This launches the docker container built using the above build command.
  • --env and -e ... – These arguments pass environment variables defined by the host into the running Docker container, which define and configure parts of the host’s graphical environment.  They also create new environment variables that are used by the container’s command, explained further below.
  • --volume and -v ... – These volume arguments map directory and file resources from the host into the container, which allow the container to manipulate the host system’s audio and vidoe resources.
  • --privileged – This elevates the permissions of the running container allowing it to manipulate resources that normally on root has capabilities to read or edit.
  • -groupadd – Probably redundant with the the CMD function, but it these add the internal root user to the video and audio groups of the host system
  • --name=nightingale – This labels the launched container with a name, in this case, nightingale, which is convenient for accessing it later.

The docker command launches a daemonized container, reporting its container id, which is captured and used to inspect the hostname of the container, which is then captured to open up permissions and allow only that container to modify the host’s audio and video resources.

Shortcuts

My keyboard provides previous, next, and pause/play buttons, which I can still use assuming that the Docker container is already launched and running, if defined similar to:

  • play/pausedocker exec nightingale nightingale -pause
  • previousdocker exec nightingale nightingale -previous
  • nextdocker exec nightingale nightingale -next

Please note that all of the above commands assume that the container “nightingale” contains the running instance of my music player.

Also, I believe that I have a command line plug-in installed which interprets the additional command line switches to perform an action instead of trying to spin up another instance of nightingale.

Issues

For users running some other audio system beside PulseAudio on their host box, both the Dockerfile and container launching command will need to be adjusted.

Resources

I found these resources helpful:

  1. http://wiki.ros.org/docker/Tutorials/GUI
  2. https://github.com/jessfraz/dockerfiles/issues/85
  3. http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/
  4. https://stackoverflow.com/questions/16296753/can-you-run-gui-applications-in-a-docker-container

Enjoy your music again!  🙂

Share
 Posted by at 1:47 PM