An Open Music Setup
August 15, 2019
A Bit of Background
I have been belligerently reducing the number of online services I use, opting instead for open-source software that I can self-host or run locally. One service I wanted to replace was Spotify, which meant finding a proper alternative for managing my music.
I have had issues with Spotify for a while1, including unfair artist compensation and locking content behind DRM, which forces users to keep paying for a subscription or risk losing their music library. You are also restricted to using Spotify’s client or a client backed by their API, rather than the huge number of music players that can play normal audio files. And since you can’t download music from Spotify in a non-encrypted format, you can even lose access to music completely if it is removed from the service.
Switching to self-hosted / local software was the perfect opportunity to leave Spotify and start building an actual music library. Unfortunately, emulating the benefits of their service, such as cross-device synchronization and automatic library management, ended up being more trouble than I anticipated. In this post, I’ll detail some of my solutions to these problems as well as my current music setup, in case you too are looking for an alternative to online streaming services.
The first difficulty with managing your own music library is ensuring that the audio files are properly labeled, sorted, and categorized. Fortunately, there are a number of tools available that make it easy to process your music.
My library management tool of choice is an excellent command-line app called beets, which lets you easily tag and organize music using the MusicBrainz database, an “open music encyclopedia” with metadata for millions of songs. Beets requires some manual input to ensure it correctly identifies your music, but the payoff is a well-structured library that can be searched, sorted, and updated by just about any reasonable media player.
Beets also supports extensions, which can add features such as automatically downloading and embedding art and lyrics. Check out the documentation for the complete list, as well as instructions on how to get started.
In case a command-line app isn’t your cup of tea, there is also a tool published by MusicBrainz themselves called Picard. It features a GUI and, like beets, integrates directly with MusicBrainz to tag your music. I personally prefer beets' interface and customizability, but Picard also works well.
The main reason online services are popular is because they are convenient, so I needed find a setup that could offer similar features. Specifically, I wanted my music to be available on all of my devices, as well as automatically synchronize new music and playlists. Here are a couple of tools that fit the bill.
My initial solution to this problem was to set up my own “streaming service” using Funkwhale: a federated music sharing software built on top of the decentralized social networking protocol ActivityPub (the same protocol powering Mastodon). In addition to a web interface, Funkwhale exposes a Subsonic API endpoint2, which allows integration with a number of existing clients. I switched away from Funkwhale since it offered a lot more functionality and complexity than I needed, but I still think it’s a cool project with a welcoming community.
One of my favorite recently-discovered pieces of software is Syncthing: a peer-to-peer, decentralized file synchronization system. It allows you to register devices with one another, then share files and directories between them without any intermediary server. It sometimes takes a while to sync, but overall is a useful tool. I was already using it to sync phone backups to my server, and decided to try using it for music as well.
The setup was very straightforward; I “paired” my phone and laptop, then added my music directory as a shared folder between the two. Aside from a small hiccup where my backup software tried to cyclically back up the music directory and gobbled all the space on my phone, the entire process went smoothly and I was left with a synchronized music library on both my devices.
One nifty benefit of syncing the files directly between devices is that playlist management becomes very straightforward. Many music players support the m3u playlist format, which is basically just a list of audio file paths. One gotcha with this method is that the paths need to be the same on all devices, so using relative paths typically works better than absolute. I have my playlist files in the root of my music directory, and reference audio files with paths relative to that.
I feel I should at least give rsync an honorable mention, since I use it on an almost daily basis for transferring files between servers. I considered setting up a recurring script to periodically rsync music between my devices, but I wanted a “smarter” solution that only synchronized when there were new items or changes.
Additionally, it would be difficult to automatically rsync between a phone and laptop, since they would need to be on the same network. This could be solved by having a central music server that all devices sync against, but I prefer the reduced complexity of a decentralized setup.
There are a lot of media players out there. In fact, it wouldn’t surprise me if there are more media players than any other category of software. The great thing about having such a variety of players is that you can choose the one that best fits your use-cases. Whether you prefer a command-line tool for running in a terminal, a web-based player that’s accessible from anywhere, or a native desktop application, there are countless great options.
In my opinion, client lock-in is one of the worst problems with using online streaming services, since it greatly restricts how you can interact with your music. You’ll typically get a single web interface, desktop application, phone app, and, if you’re lucky, a handful of third-party clients hacked together to work with the service’s API. The lock-in becomes especially egregious when the company behind the client randomly decides to introduce unasked-for changes (looking at you, Spotify), forcing you to choose to continue using an increasingly worse client, or leave the service and lose your music. Thankfully, we suffer none of these issues when using open file formats and clients.
On the Computer
These days, most of my computer use is in a terminal session, so having a good command-line music player is a must. To that end, I am very happy with ncmpcpp backed by MPD. The setup is super simple; just point MPD to your media files and it’s off to the races. You can even do neat stuff like stream from MPD to remote devices3. MPD supports a lot of different configurations, so check out their documentation to get a feel for how it can best work for you.
For my Phone
Unfortunately, phones aren’t as well suited to command-line interfaces, so I needed to find a usable Android app for listening to music on the go. Almost any music player available for Android can play standard-format audio files and read m3u playlists, so I set out testing a bunch of players to see which one suited me best.
A problem I quickly ran into is how Android handles media and playlists. In an attempt to prevent lots of repeated filesystem calls from locking up your phone, the Android system will automatically scan and index local audio, video, image and playlist files, and expose them to apps through an API. While the pros for the system being built in this way probably outweigh the cons4, it makes rolling your own media management difficult due to two large issues:
- The only way for apps to access your media is through this API, so until new media is indexed, they don’t know it exists. There is also no reasonable way to force the system to index files, short of clearing the current index, restarting the phone, and waiting for the system to rebuild the index sometime thereafter5.
- Apps don’t handle playlist changes very well. Either as part of the their own internal storage system, or through the interaction with the media API, playlist changes that are updated from an external source (i.e. Syncthing) don’t get picked up quickly (or ever) by the app, and changes to the playlists within the app need to be manually exported back to the filesystem, which often breaks the path references for other devices.
I spent a good deal of time researching workarounds for these issues when I had a thought: perhaps I could just mimic my desktop setup, and use MPD? As it turns out, running MPD as a local media server on my phone works great, especially since there are a number of quality open-source apps for streaming music from MPD. My current favorite is M.A.L.P., which offers a nice UI and seamless MPD integration (plus the bonus of being able to connect to and control remote instances, like your computer).
For the MPD daemon, I originally used Termux (an app that provides a terminal emulator and faux Linux environment) with the MPD package. This setup is usable, although I have since switched to MPD’s official Android implementation which I find to be more convenient.
This post ended up being a lot longer than I anticipated, but hopefully it’s given you an idea of how you can set up your own media management system without relying on an online service. I set out to meet fairly specific goals which added some complexity, but parts of it could certainly be pared down or adapted to fulfill a different use-case.
There are a few kinks still left to sort out, including:
- Having to store my entire library on all devices is inefficient, especially if I am only listening to a certain playlist at a time. For now, my library is small enough that it’s not an issue, but in the future I might need to start filtering which songs get synchronized.
- The local-MPD phone instance is a bit of a hack, and M.A.L.P. has some quirks when connecting to a local server, including one related to volume control. I have an issue open to try and resolve this, so hopefully this won’t be a problem in the future!
Overall, I am super pleased with this setup, and enjoy the amount of control and customizability it has given me over my music. If you are also becoming disenchanted with Spotify or other online streaming services, then I encourage you to take it out for a spin! And if you come up with your own system or find a way to improve mine, I would love to hear about it.
These criticisms apply to most, if not all, online streaming platforms. ↩︎
Subsonic is another music server software that ended up going closed-source, although many successive projects borrow its API design. ↩︎
I did consider streaming music from a centralized MPD instance as an enabler for cross-device synchronization. Unfortunately, this isn’t an ideal option for listening to music on a phone, since MPD doesn’t directly support caching, and having to stream music live every time will quickly eat mobile data. ↩︎
It’s worth noting that mobile OS’s with proper file systems (like Sailfish OS) don’t suffer either of these issues, and basically Just Work™ as you’d expect them to. ↩︎
Technically, using Android’s MTP (Media Transfer Protocol) functionality to transfer media should cause an immediate reindex, but it only works over USB. ↩︎