Music with MPD and beets

Author: akeil
Date: 2014-03-24
Version: 1

MPD [5] (Music Player Daemon) is a music player that runs in the background (as a daemon process). MPD does not have a user interface but requires a separate client application to control playback. One interesting thing about MPD is that it can be controlled over the network. MPD can also stream music over HTTP.

beets [1] is a command line application to manage a music library. It downloads album/track-information and cover art from various web services and manages your music-file's metadata ("tags").

The resulting setup will be a system where you can playback and manage music without the need for a graphical user interface (or any graphics). It can run on your desktop PC and be controlled by a locally installed (GUI-) client or it can run on a different machine and be controlled over the network. When controlled via network, no display is required on the computer running thee beets/MPD combo.

Install beets

The installation procedure is also described in the beets documentation [2].

Install From Repository

Ubuntu/Linux Mint:

# apt-get install beets

Arch Linux:

# pacman -S beets

Install Manually

beets can also be installed manually inside a virtualenv [4]. This allows to install it without root privileges (for a single user only) and also keeps the system-wide python installation clean. However, when installed like this, one must update it manually..

Install python-virtualenv if it is not already installed. beets will run with Python 2.7 only, so install that version of Python as well.

On Ubuntu:

# apt-get install python2.7 python2.7-dev python-virtualenv

On Arch Linux

# pacman -S python2 python2-virtualenv

Next, create a virtualenv and install beets:

$ mkdir ~/.virtualenvs
$ cd ~/.virtualenvs
$ virtualenv --python python2.7 beets
$ source beets/bin/activate
(beets)$ pip install beets

Now beets can be executed with the beet command - as long as you have the beets virtualenv activated. To execute outside the virtualenv, link the beet executable into ~/bin (assuming that ~/bin is in your $PATH):

$ ln -s ~/.virtualenvs/beets/bin/beet ~/bin/beet

Updating the virtualenv-install is done like this:

$ source ~/.virtualenvs/beets/bin/activate
(beets)$ pip install --upgrade beets

Plugins

Beets offers several plugins. For some of them, additional packages must be installed.

Some interesting of the many beets plugins [3]:

discogs: Use discogs [7] as an additional source for metadata (default is musicbrainz [6]).
lyrics: Fetches song lyrics.
echonest: Fetches tempo (bpm) from echonest [11]
fetchart: Fetches cover art. Requires ImageMagick or PIL to re-size downloaded art.
embedart: Embeds cover art into the music file. Also requires ImageMagick [8] or PIL [9] to re-size images.
lastgenre: Fetches genre from last.fm [10] and optionally reduces genres to a canonized list.
replaygain: Calculates Replay Gain, using either mp3gain or replaygain.
scrub: Removes unwanted tags from music files.
mbsync: Updates your library with new metadata from MusicBrainz.
missing: Finds missing tracks in your collection.
duplicates: Finds and optionally deletes duplicate albums/tracks.

Plugin requirements:

Plugin Requirement Install with
discogs discogs-client pip
echonest_tempo pyechonest pip
lastgenre pylast pip
mpdstats python-mpd pip
fetchart imagemagick (package manager)
replaygain mp3gain replaygain (package manager)

To install the python requirements (assuming that beets was installed in a virtualenv):

(beets)$ pip install discogs-client pyechonest pylast python-mpd

To install the additional packages required by beets or plugins:

# apt-get install mp3gain replaygain imagemagick

A simple update script for the complete beets virtualenv:

#!/bin/sh
VENV="$HOME/.virtualenvs/beets"
$VENV/bin/pip install --upgrade beets discogs-client pyechonest pylast

Configure

The beets config file is ~/.config/beets/config.yaml. The beets config is always user-based (this cannot be changed - however, since version 1.3.3, an additional configuration file can be supplied via the --config option).

Change the base directory where beets stores music files:

directory: /mnt/storage/music/

A complete list of beets configuration options [12] can be found in the beets documentation:

The default config from the installation directory can be used a a starting point for a personal config.

$ cp ~/.virtualenvs/beets/lib/python2.7/site-packages/beets/config_default.yaml \
  ~/.config/beets/config.yaml

Import Settings

Files to be imported will be placed in a special "import" folder, which is intended to contain to-be-imported files only. beets should clean up this directory after import. Settings are:

import:
    move: yes

Use the strong_rec_thresh setting to allow more or less imperfectly matched albums to be imported without prompt:

strong_rec_thresh: 0.1  # match 90% or better for auto-import

Genres

The lastgenre plugin will fetch genres from last.fm Additionally, the last.fm genre is compared against a whitelist, to exclude some of the genres. It is also possible to canonicalize the genres to a more coarser set of genres using a YAML-configuration file.

For both options, default files are included at:

~/.virtualenvs/beets/lib/python2.7/site-packages/beetsplug/lastgenre/genres.txt
~/.virtualenvs/beets/lib/python2.7/site-packages/beetsplug/lastgenre/genres-tree.yaml

Copy these to ~/.config/beets/ and configure beets to use these files. Edit the files as required.

lastgenre:
    whitelist: ~/.config/beets/genres.txt
    canonical: ~/.config/beets/genres-tree.yaml

MPD

Install

Arch Linux:

# pacman -S mpd

Ubuntu/Linux Mint:

# apt-get install mpd

Configure

The system wide config file for MPD is /etc/mpd.conf

relevant settings:

port                "6600"
music_directory     "/var/lib/mpd/music"
playlist_directory  "/var/lib/mpd/playlists"
db_file             "/var/lib/mpd/mpd.db"
state_file          "/var/lib/mpd/state"
sticker_file        "/var/lib/mpd/sticker.sql"
log_file            "/var/log/mpd.log"
pid_file            "/run/mpd/mpd.pid"
user                "mpd"
replaygain          "album"
metadata_to_use     "artist,album,title,track,name,genre,date,disc,albumartist"

To ensure that MPD runs smoothly, create the required files and chown them to the mpd-user.

# mkdir -p /var/lib/mpd/playlists
# touch /var/lib/mpd/{mpd.db,state,sticker.sql}
# chown -R mpd:mpd /var/lib/mpd
# touch /var/log/mpd.log
# chown mpd:mpd /var/log/mpd.log
# mkdir /run/mpd
# chown mpd:mpd /run/mpd
# ln -s /mnt/storage/music /var/lib/mpd

The music_directory is the only directory defined in the config that is not created during setup. Instead we want MPD to use the same directory as beets and symlink the music directory for MPD. Make sure that the mpd user has read permissions on all files as well as read+execute permissions on all directories within the music directory.

Port 6600 is the default for MPD. If possible, leave this unchanged as most clients will expect MPD to listen at this port.

user (and group) control the permissions under which MPD is run. On some systems, MPD must be a member of the audio group in order to use the sound card.

The var/lib/mpd directory should look as follows:

$ ls -l /var/lib/mpd/
total 4
-rw-r--r-- 1 mpd  mpd     0 Nov 23 18:32 mpd.db
lrwxrwxrwx 1 root root   20 Nov 23 18:35 music -> /mnt/storage/music
drwxr-xr-x 2 mpd  mpd  4096 Nov 17 19:26 playlists
-rw-r--r-- 1 mpd  mpd     0 Nov 23 18:32 state
-rw-r--r-- 1 mpd  mpd     0 Nov 23 18:32 sticker.sql

There is an extensive article in the Arch wiki on setting up MPD [13].

Metadata

This config-setting:

metadata_to_use     "artist,album,title,track,name,genre,date,disc,albumartist"

tells MPD which tags to include in its database. You can select tracks by any of these tags. The albumartist is not enabled by default.

If the metadata_to_use setting is changed later, MPD must rebuild its database for the change to take effect. MPD will rebuild the database if you delete (or rename) the existing one. The database is a plain text file; have a look at it.

Stream over HTTP

MPD version 0.15 or up has built in HTTP-streaming.

To enable it, add the following to /etc/mpd.conf:

audio_output {
    type    "httpd"
    name    "HTTP Stream"
    encoder "vorbis"
    port    "8000"
    quality "7.0"
    #bitrate "128"
    format  "44100:16:2"
}

quality settings for for variable bitrate (VBR) range from -1 (poor) to 10 (high). Alternatively, specify a constant bitrate, e.g. "128".

format 44100:16:2 means 44100khz 16 Bit Stereo

The URL for MPD's stream is then http://192.168.1.2:8000 (if 192.168.1.2 is the IP-address of the machine MPD is running on). For example:

$ mplayer http://192.168.1.2:8000

will have mplayer start playing whatever MPD currently plays.

MPD Clients

mpc [17]
a minimalist command line client. Good for testing or quick commands.
Ario [14]
A desktop client with a GUI.
GMPC [16]
The Gnome Music Player Client is another GUI client.
mpDris2 [15]
Adapts the dbus MPRIS interface for MPD. This makes MPD to occur in (and be controlled from) the sound menu of your desktop environment like "normal" music players.
MPDroid [19]

MPD client for Android.

MPDroid has a setting to list tracks by Albumartist instead of (Track-)Artist. The albumartist must be included in MPD's config as metadata_to_use or this setting will not work.

The MPD wiki has a large list of MPD clients [18].

Misc

Automatically Update Metadata

beets metadata is based primarily on MusicBrainz [6], which is permanently updated. To periodically and automatically sync your music collection, run a script like this (e.g. weekly via cron):

#!/bin/bash
$VENV=/home/username/.virtualenvs/beets

# sync metadata
$VENV/bin/beet mbsync > /dev/null

# fetch coverart
$VENV/bin/beet fetchart > /dev/null

Albumart for MPDroid

MPDroid can display the cover of the currently playing song. The cover is not provided by MPD but instead

Set up a web server like this

# pacman -S nginx
# systemctl enable nginx
# apt-get install nginx

The relevant nginx-configuration is this:

http {
    server{
        listen 80;
        root /mnt/storage/music;
        location /cover-art/ {
            rewrite /cover-art/(.*) /$1 break;
            try_files $uri $uri
            allow 192.168.1.0/24    # local network
            deny  all               # everybody else
        }
    }
}

Sample Beets Configuration

Complete sample configuration for beets:

beets.config.yaml (Source)

# Beets configuration --------------------------------------------------------

directory: /mnt/storage-1/music

import:
    # write metadata to music files
    write: yes

    # move imported files from source to the music directory
    move: yes
    copy: no
    delete: no

    # use auto-tagging where possible
    # do not require confirmation on strong matches
    autotag: yes
    timid: no

    resume: ask
    incremental: no
    none_rec_action: ask
    log:

    quiet: no  # enable with command line option
    quiet_fallback: skip
    default_action: apply

    singletons: no
    languages: []
    detail: no
    flat: no

# use the release-date of the original (first) release of an album?
original_date: no

# on multi-disk releases, assign track numbers for the whole album.
# If "per disk", make sure tracknames do not collide ("paths" setting).
per_disc_numbering: no

# files matching these patterns are deleted from source after import
clutter: ["Thumbs.DB", ".DS_Store", "*.m3u", ".pls", "*.jpg"]

# files/directories matching one of these patterns are ignored during import
ignore: [".*", "*~", "System Volume Information"]


# Paths ----------------------------------------------------------------------

# Paths and filenames for music files
# relative to music directory
paths:
    default: %asciify{$albumartist}/%asciify{$album}%aunique{}/$track %asciify{$title}
    singleton: Non-Album/%asciify{$artist}/%asciify{$title}
    comp: Compilations/%asciify{$album}%aunique{}/$track %asciify{$title}

# replace special characters in generated filenames
replace:
    '[\\/]': _
    '^\.': _
    '[\x00-\x1f]': _
    '[<>:"\?\*\|]': _
    '\.$': _
    '\s+$': ''

path_sep_replace: _

# filename for the album art
art_filename: cover  # results in "cover.jpg"

max_filename_length: 0  # unlimited


# General --------------------------------------------------------------------

# use mutliple threads during import
threaded: yes
timeout: 5.0
verbose: no


# User Interface -------------------------------------------------------------

color: yes
list_format_item: %upper{$artist} - $album - $track. $title
list_format_album: %upper{$albumartist} - $album
time_format: '%Y-%m-%d %H:%M:%S'
terminal_encoding: utf8

ui:
    terminal_width: 80
    length_diff_thresh: 10.0


# Auto Tagger ----------------------------------------------------------------

match:
    strong_rec_thresh: 0.1      # match 90% or better for auto import
    medium_rec_thresh: 0.25
    rec_gap_thresh: 0.25
    max_rec:
        missing_tracks: medium
        unmatched_tracks: medium
    distance_weights:
        source: 2.0
        artist: 3.0
        album: 3.0
        media: 1.0
        mediums: 1.0
        year: 1.0
        country: 0.5
        label: 0.5
        catalognum: 0.5
        albumdisambig: 0.5
        album_id: 5.0
        tracks: 2.0
        missing_tracks: 0.9
        unmatched_tracks: 0.6
        track_title: 3.0
        track_artist: 2.0
        track_index: 1.0
        track_length: 2.0
        track_id: 5.0
    preferred:
        countries: []
        media: []
        original_year: no
    ignored: []
    track_length_grace: 10
    track_length_max: 30


# Plugins --------------------------------------------------------------------

plugins: [
    discogs,
    lyrics,
    echonest_tempo,
    fetchart,
    embedart,
    lastgenre,
    scrub,
    mbsync,
    mpdupdate,
    #mpdstats,
    missing,
    duplicates
]
pluginpath: []


# Plugins Config -------------------------------------------------------------

lyrics:
    auto: yes
    fallback: ''

echonest_tempo:
    auto: yes

lastgenre:
    whitelist: ~/.config/beets/genres.txt
    canoncical: ~/.config/beets/genres-tree.yaml
    fallback: ''
    source: artist

fetchart:
    auto: yes
    maxwidth: 300
    cautious: true
    cover_names: cover folder

embedart:
    auto: yes
    maxwidth: 300

replaygain:
    auto: yes
    overwrite: yes
    albumgain: yes

scrub:
    auto: yes

# required for mpdstats
mpd:
    host: localhost
    port: 6600
    password:  # none

mpdstats:
    rating: False
    # two ratings are calculated:
    # "rolling" based on recent development
    # "stable" based on all-time development
    # rating mix 0.0 is all "rolling", 1.0 is all "stable"
    rating_mix: 0.75

missing:
    format: $albumartist - $album - $track - $title
    count: no
    total: no

duplicates:
    checksum: no  # expensive

[1] http://beets.radbox.org/
[2] https://beets.readthedocs.org/en/latest/
[3] http://beets.readthedocs.org/en/latest/plugins/index.html
[4] https://virtualenv.readthedocs.org/en/latest/
[5] http://www.musicpd.org/
[6] (1, 2) https://musicbrainz.org/
[7] http://www.discogs.com/
[8] http://www.imagemagick.org/
[9] http://www.pythonware.com/products/pil/
[10] (1, 2) http://www.last.fm/
[11] http://echonest.com/
[12] http://beets.readthedocs.org/en/latest/reference/config.html
[13] https://wiki.archlinux.org/index.php/Music_Player_Daemon
[14] http://ario-player.sourceforge.net/index.php
[15] https://github.com/eonpatapon/mpDris2
[16] http://gmpclient.org/
[17] http://www.musicpd.org/clients/mpc/
[18] http://mpd.wikia.com/wiki/Clients
[19] https://github.com/abarisain/dmix/
[20] https://github.com/abarisain/dmix/wiki/Album-Art-on-your-LAN