My ultimate guide to the Raspberry pi audio server I wanted — USB flash drives

Published: 2020-04-20

Part 5 — USB flash drives

This part gave me more thinking than the others. What I want is automatically playing audio files contained on a usb flash drivewhen it’s plugged in. But as the pi is still headless, we need some kind of remote to control the queue.
MPD seems again a good choice to do this. Unfortunately, our MPD instance is already running in satellite mode so managing a library from a folder isn’t possible. But we can very well run another instance of MPD on the Pi, which will use usb sticks as media folder. So we need a new mpd.conf file, and a new sytemd unit to manage this new MPD instance.

/etc/mpd-usb.conf

music_directory "/media/USB"  
db_file "/var/lib/mpd/mpd-usb.db"  
log_file "/var/log/mpd/mpd-usb.log"  
pid_file "/run/mpd/pid-usb"  
state_file "/var/lib/mpd/state-usb"   
sticker_file "/var/lib/mpd/sticker-usb.sql"  
   
user "mpd"  
bind_to_address "localhost"  
bind_to_address "192.168.0.15"   
port "6610"   
   
auto_update "yes"  
zeroconf_enabled "yes"  
zeroconf_name "Onkyo MPD USB"  
  
input {  
 plugin "curl"  
}  
   
audio_output {  
 type "pulse"  
 name "pulse audio"  
 server "127.0.01"  
}  
  
filesystem_charset "UTF-8"

We changed all file locations so that MPD instances do not collide between each other, and made it run on another port.
Don’t forget to create the /media/USB folder and set it appropriates permissions:

$ sudo mkdir /media/USB  
$ sudo chown mpd:audio /media/USB  
$ sudo chmod 775 mpd:audio /media/USB  
$ # We also copy existing mpd systemd file and change only a few lines:  
$ sudo cp /lib/systemd/system/mpd.service /etc/systemd/system/mpdusb.service  
$ sudo vim /etc/systemd/system/mpdusb.service
[Unit]   
Description=Music Player Daemon USB   
…  
  
[Service]   
Type=notify  
ExecStart=/usr/bin/mpd --no-daemon /etc/mpd-usb.conf

Let’s enable and start it

$ sudo systemctl daemon-reload  
$ sudo systemctl enable mpdusb.service  
$ sudo systemctl start mpdusb.service  
$ sudo systemctl status mpdusb.service   
 ● mpdusb.service — Music Player Daemon USB  
 Loaded: loaded (/etc/systemd/system/mpdusb.service; enabled; vendor preset: enabled)  
 Active: active (running) since Sat 2020–04–18 19:58:30 CEST; 12min ago  
 Docs: man:mpd(1)  
 man:mpd.conf(5)  
 file:///usr/share/doc/mpd/user-manual.html  
 Main PID: 5086 (mpd)  
 Memory: 8.8M  
 CGroup: /system.slice/mpdusb.service  
 └─5086 /usr/bin/mpd --no-daemon /etc/mpd-usb.conf  
   
 Apr 18 19:58:17 Ampli-BT systemd[1]: Starting Music Player Daemon USB…  
 Apr 18 19:58:30 Ampli-BT systemd[1]: Started Music Player Daemon USB.

Our new MPD instance is running and ready to play music, but nothing happens yet when we insert an USB stick, it’s not even mounted.

I was very lost about how to automount USB sticks in the folder I wanted with the appropriate options, there are quite a few way to do it but none pleased me. I ended up tweaking a solution I found on a blog from a cybersecurity expert using just a udev rule, a systemd unit and 2 bash scripts.

/etc/udev/rules.d/99-local.rules

KERNEL=="sd[a-z]*", SUBSYSTEM=="block", SUBSYSTEMS=="usb", ACTION=="add", RUN+="/bin/systemctl start usb-mount@%k.service"  
KERNEL=="sd[a-z]*", SUBSYSTEM=="block", SUBSYSTEMS=="usb", ACTION=="remove", RUN+="/bin/systemctl stop usb-mount@%k.service"

/etc/systemd/system/usb-mount@.service

[Unit]   
Description\=Mount USB Drive on %i  
   
[Service]   
Type=oneshot   
RemainAfterExit=true  
ExecStart=/usr/local/bin/usb-mount.sh add %i  
ExecStartPost=/usr/local/bin/mpc-usb.sh add %i  
ExecStop=/usr/local/bin/mpc-usb.sh remove %i  
ExecStop=/usr/local/bin/usb-mount.sh remove %i

/usr/local/bin/usb-mount.sh

#!/bin/bash  
   
ACTION=$1  
DEVBASE=$2  
DEVICE="/dev/${DEVBASE}"  
BASE_MOUNT_POINT="/media/USB"  
   
# See if this drive is already mounted  
MOUNT_POINT=$(/bin/mount | /bin/grep ${DEVICE} | /usr/bin/awk '{ print $3 }')  


do_mount()  
{  
 if [[ -n ${MOUNT_POINT} ]]; then  
    # Already mounted, exit  
    exit 1  
 fi  
   
 # Get info for this drive: $ID_FS_LABEL, $ID_FS_UUID, and $ID_FS_TYPE  
 eval $(/sbin/blkid -o udev ${DEVICE})  
   
 # Figure out a mount point to use  
 LABEL=${ID_FS_LABEL}  
 if [[ -z "${LABEL}" ]]; then  
     LABEL=${DEVBASE}  
 elif /bin/grep -q "$BASE_MOUNT_POINT/${LABEL}" /etc/mtab; then  
     # Already in use, make a unique one  
     LABEL+="-${DEVBASE}"  
 fi  
 MOUNT_POINT="${BASE_MOUNT_POINT}/${LABEL}"  
   
 /bin/mkdir -p ${MOUNT_POINT}  
   
 # Global mount options  
 OPTS="rw,relatime,uid=111,gid=29,umask=0002"  
   
 # File system type specific mount options  
 if [[ ${ID_FS_TYPE} == "vfat" ]]; then  
     OPTS+=",users,shortname=mixed,utf8=1,flush"  
 fi  
   
 if ! /bin/mount -o ${OPTS} ${DEVICE} ${MOUNT_POINT}; then  
     # Error during mount process: cleanup mountpoint  
     /bin/rmdir ${MOUNT_POINT}  
     exit 1  
 fi  
}  
   
 do_unmount()  
 {  
   if [[ -n ${MOUNT_POINT} ]]; then  
     /bin/umount -l ${DEVICE}  
   fi  
   
   # Delete all empty dirs in /media that aren’t being used as mount points.   
   for f in ${BASE_MOUNT_POINT}/* ; do  
     if [[ -n $(/usr/bin/find "$f" -maxdepth 0 -type d -empty) ]]; then  
        if ! /bin/grep -q "$f" /etc/mtab; then  
            /bin/rmdir "$f"  
        fi  
     fi  
   done  
   /bin/echo "${MOUNT_POINT} unmounted"  
 }

 case "${ACTION}" in  
 add)  
    do_mount  
 ;;  
 remove)  
    do_unmount  
 ;;  
 esac

/usr/local/bin/mpc-usb.sh

ACTION=$1  
DEVICE=$2  
MPDPORT=6610  
MPC="/usr/bin/mpc -p ${MPDPORT}"  
  
MOUNT_POINT=$(/bin/mount | /bin/grep ${DEVICE} | /usr/bin/awk '{ print $3 }')  
LABEL=$(/usr/bin/basename ${MOUNT_POINT})  
  
do_mount() {  
    /bin/echo "clearing previous queue"  
    ${MPC} clear  
    /bin/echo "updating mpd database..."  
    ${MPC} update --wait  
    /bin/echo "adding ${LABEL} files to queue"  
    ${MPC} add ${LABEL}  
    /bin/echo "playing queue"  
    ${MPC} play  
}  
  
do_unmount() {  
    /bin/echo "clearing previous queue from ${LABEL} files"  
    ${MPC} -f "%position% %file%" playlist | /bin/grep ${LABEL} | /usr/bin/awk '{ print $1 }' | ${MPC} del  
}  
  
case "${ACTION}" in  
    add)  
        do_mount  
        ;;  
    remove)  
        do_unmount  
        ;;  
esac  

Now let’s reload udev and systemd

$ sudo udevadm control --reload-rules  
$ systemctl daemon-reload

USB sticks are now mounted in a folder created automatically in /media/USB with mpd:audio rights and 775 permissions (thanks to mount options) to make inotify and autoupdate work.
With my first test, there was a GoT episode on the key I used alongside MP3, which was detected and played by mpd when I inserted the key.
So I created file /media/USB/.mpdignore to ignore video files when scanning for media

*.mkv  
*.mp4  
*.mpg  
*.mpeg  
*.wmv  
*.avi

So now when inserting a USB stick, files start playing after a few seconds, time for mouting and updating MPD-USB database. If another stick was already playing, queue is replaced with new stick content and starts playing, but old content is still accessible in MPD-USB database from M.A.L.P. or ympd when using MPD-USB server.

ympd on MPD-USBImage

M.A.L.P. on MPD-USBImage

Now I feel also should install a minidlna server on the Pi to make sticks content also available on DLNA ? Maybe another time.

Part 6 : Audio CD