website/content/posts/2023-05-30-my-offline-music-streaming-setup/index.adoc

21 KiB
Raw Blame History


title: "My offline music streaming setup" date: 2023-05-30T00:00:00+00:00 tags: - Linux - Nix ---

My offline music streaming setup

Gabriel Arazas <foodogsquared@foodogsquared.one> v1.0.0, 2023-05-30: Initial version

I like some music alongside whatever Im doing something else… sometimes. [1] While I typically go with a music streaming service like Spotify, sometimes it is done with my offline music collection.

A music streaming service like Spotify, while nice with the vast library of music you have access to, is not a replacement with the local music collection. Especially with the dangers of a cloud service where if Spotify being down will make your music collection inaccessible for the time.

Its fun to document my setup every once in a while and the topic to tackle for this post is music management. It is especially nice for those who are into music archival or just want to have a nice digital, well-organized music collection.

Important

For future references, here are the following components assumed for this post as well as its versions.

  • Mopidy v3.4.1

  • systemd v253

  • Beets 1.6.0 with Python 3.10.x

  • gonic v0.15.2

  • home-manager version 23.05

  • NixOS version 23.05

Dialog on acquiring music

How do you download those into your local collection anyways? I hope youre not just downloading them with a tool without paying for it.

Says the one who bootlegs recordings borrowed from their friends…

Also, I paid for some of them. Otherwise, I just use a tool like github:spotDL/spotify-downloader[spotDL] and github:yt-dlp/yt-dlp[opts=repo]. Although sometimes I do use YouTube Music instead of Spo-

Hey, I paid for some of those cassettes, CDs, and whathaveyou!

Also, those bootlegs are for me! Completely legal.

Mopidy

Half of the personal music streaming setup is Mopidy, a music server with a very nice ecosystem. With more of the ecosystem, you can use other extensions that adds sources such as in github:mopidy/mopidy-spotify[Spotify], github:natumbri/mopidy-youtube[YouTube], github:tkem/mopidy-internetarchive[Internet Archive], and gitlab:funkwhale/mopidy[Funkwhale, domain=dev.funkwhale.audio].

Mopidy is a music server that comes with only a command-line interface which you can see more details on man:mopidy[1]. For a start, Mopidy has a comprehensive quickstart and repology:mopidy[it is widely available on mainstream Linux distributions] such as Debian, Arch Linux, and Fedora. While installing Mopidy varies between distributions, typically installing Mopidy consists of installing Mopidy itself and additional extensions.

  • The one I recommend the most is github:jaedb/iris[Iris] which is a Mopidy extension that adds a web-based interface for managing and playing music with your Mopidy configuration.

    A screenshot of Iris player on the albums section
    Figure 1. Iris music player
  • Another extension I would recommend is github:mopidy/mopidy-mpd[opts=repo] which adds an MPD-compatible server. With the MPD server enabled, you can then use MPD clients such as github:yktoo/ymuse[Ymuse] and github:ncmpcpp/ncmpcpp[opts=repo].

  • Lastly, I would install additional extensions for other sources such as YouTube, Internet Archive, and Funkwhale.

After installing the extensions you need, it is time to configure it with a plain-text file. You have to be a bit familiar with the configuration syntax of Mopidy which uses an wikipedia:INI_file[INI]-like format. For an example, heres my Mopidy configuration barring sensitive parts such as my Last.fm and Spotify account secrets.

~/.config/mopidy/mopidy.conf
Unresolved directive in <stdin> - include::./assets/mopidy.conf[]
Note

Since INI format doesnt have a clear way of specifying list, Mopidy has a format tweak where lists look like the following.

key =
  value1
  value2
  value3

This is seen with the value of file.media_dirs which is a list of directories with an optional name separated by a pipe (i.e., |) and in internetarchive.collections which is a list of Internet Archive playlists to be included in the server.

Among other things, the above configuration does the following…

  • Initiate the Mopidy server at localhost.

  • Adds local source from $XDG_MUSIC_DIR and other locations.

  • Interacts with the playlists (in wikipedia:M3U[M3U format]) from $XDG_MUSIC_DIR/playlists.

  • Adds Internet Archive playlists.

Points of interest for Mopidy documentation

Mopidys documentation is fairly comprehensive. Here are some of the pages youre more likely to go back repeatedly.

With the configuration complete, youll have to start the server by simply running mopidy. However, it would be preferable to activate the server at the startup. If youre using systemd, you could create a service unit as described from man:systemd.service[5].

~/.config/systemd/user/mopidy.service
Unresolved directive in <stdin> - include::./assets/mopidy.service[]
Tip

%E/mopidy/mopidy.conf corresponds to whatever XDG_CONFIG_HOME resolves to. You can see more of these specifiers from man:systemd.unit[5] "Specifiers" section.

Then you could enable the service with the following command.

systemctl --user enable --now mopidy.service

Hoorah! Now you have an offline music server! While you could manage your collection in Iris, there are better tools suited for that task which well cover next.

Beets

Beets is a music management system for organizing your music collection — embedding proper metadata, organizing file structure, and more. This is what I use to organize my music directory which makes it usable for other services that expect organized structure such as Mopidy and Gonic. The most notable thing with Beets is it uses metadata from MusicBrainz (at least by default).

Using Beets is very similar with using Mopidy:

  • It is repology:beets[widely available on mainstream Linux distributions].

  • It may require installing additional plugins for more capabilities.

  • It starts with configuring the program with a plain-text file.

Furthermore, Beets also comes with a command-line interface with a user manual at man:beet[1].

How you configure Beets is through a plain-text file stored at a default configuration file which you can show with the following command.

beet config --path --default

You can easily edit the configuration file with beet config --default --edit. There are a lot of tweaks and behaviors you can change but it is easier to show an example configuration like in the following listing.

~/.config/beets/config.yaml
Unresolved directive in <stdin> - include::./assets/beets.yml[]
Note

If youre changing how the auto-tagging match works with its related options, the matches that Beets accepts change significantly. For example, if youve modified match.required to enforce accepting matches with year and a label, youll be missing out on a lot of matches since not every database entry on MusicBrainz are uniform.

Among other things in the configuration, youll need to explicitly specify which plugins to use (which Beets comes with an extensive list of them). Anyways, heres what my configuration does.

  • It automatically creates playlists separated by year with the smartplaylist plugin. This is what part of my Mopidy configuration gets its playlist from. [2]

  • The import process also fetch the album art from MusicBrainz which is nice for music players. This is enabled by fetchart plugin.

  • The import process also scrubs the metadata in music files with the scrub plugin.

A few interesting Beets plugins

Theres still possible changes for my Beets configuration since it offers so much. A few points of interest for me are…

  • Importing playlists from a Subsonic server with the subsonicplaylist plugin. This is especially nice if you have a self-hosted Subsonic-compatible server such as github:sentriz/gonic[opts=repo].

  • Easily sharing my Beets library with the ipfs plugin.

  • Extracting the BPM with bpm plugin which prompts you to rhythmically confirm the BPM.

After configuring it, you can then start using Beets. The workflow of Beets is pretty simple: it is a music management system that comes with an library database (configured with library option). Beets only considers music files that are included in the library database. To get started, we have to fill the library database with some music files with the following command.

beet import ~/Downloads/music

Beets will then start to match metadata of the audio files into its sources (such as MusicBrainz and Deezer) and prompts the user what to do next.

beet import usage
Figure 2. beet import usage

Beets, with all of its niceties, have a lot of problems especially with the auto-tagging feature. The most notable thing being the performance which is already acknowledged from the user manual. It is pretty slow especially once you import multiple music files from multiple albums.

Another situation that performance can really hinder is importing larger albums where Beets prompts multiple times. Take note that importing music in the same collection make Beets not only prompt but also merge them which takes some more time. This means you really have to pay attention. Thankfully, Beets can resume import if the process has been interrupted. Still, auto-tagging can be a tedious experience.

Just imagine the previous image but Beets only recognizes one part of the album at a time. Rinse and repeat until all tracks are in the album. But then between each time it prompts theres an additional prompt to make you either merge the album which takes up more time.

Thats the dilemma for larger albums.

Sounds tedious. Why dont you just use something like MusicBrainz Picard? It is more integrated and seems to be more responsive as an auto-tagger.

Yeah but I want my well-organized and completely-managed-by-Beets music library though.

Gonic

While this component is not essential if youre the only user of the setup, it would be nice to have it distributed as a server. One of the most common way to distribute your local collection as a streaming server is with Subsonic which able to attract programs and extensions such as github:Prior99/mopidy-subidy[a Mopidy extension].

However, were not going to use Subsonic itself as it hasnt been active in its development which led to forks and compatible servers such as Airsonic, Navidrome, and Funkwhale. Instead, were using github:sentriz/gonic[opts=repo], one of the Subsonic-compatible servers and it is lightweight enough for my needs compared to the aforementioned servers. More specifically, I like that it has the following features.

  • Listenbrainz scrobbling.

  • It has internet radio support (which some Subsonic clients apparently make use of).

  • Multi-user with their own preferences, data, and whatnot.

The last one being the most important which I could then share the server with other music listeners.

Wait! Why do you have two music servers anyways?

I would like to distribute my audio for my other devices which Mopidy is not suited for. Mopidy is closer to MPD by design which is more focused on playing music on the device running the server.

I could also share the server with other users for their music collection.

Fair point, I guess.

Note

Unlike Mopidy (or the next tool), repology:gonic[Gonic is not as available as the other featured tools] (as of 2023-05-27) so youll have to build the source code yourself.

Using Gonic first starts with configuring the server. There are different ways to configure it.

  • With passing arguments on the command-line interface.

  • With a plain-text file which have to be indicated with -config-path option on the command-line interface.

  • With environment variables.

You can use them all if you want to. However, I recommend sticking to one and configuring with a plain-text file as it is easier to transfer the configuration between different servers and with different service managers (if you make use of them).

Anyways, heres the configuration file for my Gonic server for my system-wide installation.

/etc/gonic/gonic.conf
Unresolved directive in <stdin> - include::./assets/gonic.conf[]

The above configuration meant to be started with the following command to start the server.

gonic -config-path /etc/gonic/gonic.conf

However, it is better to handle this by the service manager (in this case, systemd). The following systemd service unit is sufficient enough.

/etc/systemd/system/gonic.service
Unresolved directive in <stdin> - include::./assets/gonic.service[]
Note

A better example of a systemd service unit can be seen in its swh:swh:1:cnt:4a2128ff5d679b1eceac27464f2ccbd5c3dabc90;origin=https://github.com/sentriz/gonic;visit=swh:1:snp:c9ba117bcd80577fa6dbae0293c45e3d6488c08c;anchor=swh:1:rev:2f4ea6caa86afb8c78fda4efcd1d4a5e8aa18c60;path=/contrib/gonic.service[source code].

You could also improve it by hardening the service which systemd definitely has options listed in man:systemd.exec[5]. For a more comprehensive example of a hardened version of the systemd service, you could look into swh:swh:1:cnt:65cf10f2c4b4623770b7006e927dc5838f3c7920;origin=https://github.com/NixOS/nixpkgs;visit=swh:1:snp:4b4f22375c9650345eb207a70c0158f951386514;anchor=swh:1:rev:1eae9f8f1b6a444b15d9037d4b8f2ca7da045a3f;path=/nixos/modules/services/audio/gonic.nix;lines=40-85[Gonic service implementation from nixpkgs].

After setting up the server, just dont forget to immediately log in to the service with the default account and change the password. This is especially important if youre going to deploy it for the public.

Then, I have to set up my go-to features: create a user token for Listenbrainz for them recommendations, add a list of internet radios, and subscribe to several podcasts. Finally, all I have to do is to deploy it on the public, set up the networking, and voila! My own little music streaming server suitable to be used for multiple users.

Note

Similar to Mopidy, Gonic doesnt organize them music folder for you. That is the job more suitable for Beets.

Overall, I find Gonic to be a very nice lightweight music streaming server. At this point, to make use of the service, youll have to use a Subsonic client. My client of choice is fdroid:org.moire.ultrasonic[] but fdroid:github.daneren2005.dsub[] is a close contender too.

Setting up in home-manager

If youve seen my recent writings, you would know Im a Nix enthusiast. Fortunately, theres a way to easily reproduce the music player setup with github:nix-community/home-manager[opts=repo] which is nice for user-specific configurations. In fact, home-manager does come with Nix modules to setup both Beets and Mopidy.

Heres the equivalent home-manager configuration for my Mopidy setup with additional extensions to be installed…

home-manager equivalent for Mopidy configuration
link:git:{doccontentref}[role=include]

for the Beets configuration,

home-manager equivalent for Beets configuration
link:git:{doccontentref}[role=include]

and for the Gonic service setup which you could make it accessible through a VPN setup like Wireguard or Tailscale.

home-manager setup for Gonic service
link:git:{doccontentref}[role=include]

You could create much more comprehensive offline music player with home-manager. The following listing is an example of such setup.

A more comprehensive setup for offline music management
link:git:{doccontentref}[role=include]

Just install or activate home-manager with the above configuration fragment and you should be able to reproduce my configuration in a snap. While unprivileged user services can be used for network-wide deployment as long as the networking is configured right, home-manager is oriented towards desktop usage. Youll be missing out on setting up things (mainly networking) so configuring it through NixOS would be better suited for this task.

Setting up in NixOS

We could also set a music streaming server with NixOS which is more suitable for servers compared to home-manager which is oriented towards desktop usage.

Heres how we would set up with Mopidy…

NixOS setup for Mopidy server
link:git:{doccontentref}[role=include]

and with Gonic.

NixOS setup for Gonic server
link:git:{doccontentref}[role=include]

As with Beets, you can simply alias it and pass the path of the custom configuration file. [3] Though, the file has to have appropriate permissions to easily access it for all.

Overriding Beets with custom configuration
link:git:{doccontentref}[role=include]

Anyways, here is the complete NixOS configuration for future reference which you can activate it with nixos-rebuild.

Complete music streaming setup for NixOS
link:git:{doccontentref}[role=include]

You could set this up within a VPS. If youre not comfortable with setting this up for the public, you could easily require the service to be accessed through a VPN. You can even set up a domain name that is only accessed through the VPN.


1. Not always, I find music sometimes distracting.
2. Though, you can also create your own playlist as long as it doesnt conflict with the autocreated ones.
3. You have to place the file in the appropriate location relative to the NixOS configuration repository.