The slow process of de-googlification continues. Once again Google itself extends a helpful hand by abandoning a long-running service. Following a transition from Google Reader to tt-rss and from Google Talk to ejabberd+Gajim+Conversations the time has come for Google Music. It was recently announced that the service will be discontinued and users should migrate to Youtube Music. As good an excuse as any.
Music Player Daemon
MPD has been around for many years. Typically used for playing music on media servers, it keeps track of the music in an internal database and allows for easy remote control via its server-client protocol. However, what if you wanted to stream the music to your smartphone instead of remotely controlling playback on another device? MPD does support streaming but the implementation is a bit wonky - the music is transcoded on the fly and exposed as a http stream which introduces a lot of latency in playback and seeking. In addition, only one stream at a time is possible, so this solution is limited to one remote device. On top of that, some clients (looking at you M.A.L.P) will require you to restart streaming every time you reset the playlist. Fortunately, there is a better way to do it.
MPD satellite setup
You might be thinking - fine, so why don't I just put the music on a samba share or whatever and keep the music database on my phone? Unfortunately, updating the music database involves traversing the whole directory structure which would be slow and painful on a wireless connection. However, a perhaps less known feature of MPD is that two daemons can talk to each other. Specifically, the music database on one server can be accessed by another via the proxy database plugin. This way the server which has all the music can keep an up-to-date database that can be remotely accessed by mpd instances running on other devices. This is called a satellite setup.
Leveraging the VPN network set up previously this is very easy to do using the virtual subnet. In my case, the MPD instance on the media server is running on 10.8.0.1 (
bind_to_address "10.8.0.1") and exposes the music files via WebDAV. Meanwhile the mpd running on my Android phone uses the following config:
name "Android only supports OpenSL ES audio output"
Tune buffering to taste.
Then the Android MPD client such as M.A.L.P needs to be set to connect to localhost.
A side benefit is that you can have two profiles for M.A.L.P (one for the satellite setup and one remote) and control music playback on the media server with the same app when you get home.
One potential issue might be streaming FLACs (due to either bandwidth limitations or mobile data caps), but most of my music is kept as v0 mp3s anyway. Overall it works very well and feels much snappier than a streaming setup.
The helpful folks over at /r/selfhosted pointed out a few alternatives. Two caught my interest.
Waveline looks very slick and features a modern Android interface. Unfortunately, the app is a bit limited:
- can't sort albums and artists alphabetically, only by created date
- no sorting by folder structure
- as a consequence, poor experience with compilations/OSTs/ and other "various artists" albums
- transcoding is hardcoded to mp3 128kbps
In addition, the app did not seem very resiliant to network issues throwing java exception error pop-ups any time it could not connect to the server. Overall the experience was frustrating which is a shame considering the visual polish.
As a minor nit-pick, the server depends on Node and a few hundred megabytes of other packages which seems a bit excessive.
I turned my attention to the *sonic family of media servers. I did not dig deep but it seems the original project (subsonic) was closed-sourced at some point but API remained open and a number of competing implementations in various languages appeared. I initially wanted to check out navidrome but then I spotted the Node.js dependency again.
There is a pure-Go implementation called gonic with very manageable dependencies (alsa-lib, ffmpeg, taglib, sqlite) but the API implementation is not complete and once again there is limited transcoding support - mp3 128/opus 96 only, with all files being indiscriminately transcoded when enabled. Refactoring it would take too much time, so I settled on a Python implementation called supysonic which actually implements transcoding in a sensible way.
The Android client I am currently using is Ultrasonic.