Self-hosted music synergy
07/02 2023
I've had something very specific which has irritated me for a long time, namely that of what to do with my local MP3s. At face value the solution is simple, they're just MP3s right - just store them locally on your computer, and then sync them to your phone! Well, that's what I've been doing (in combination with Nextcloud), however it's always been a pain to make sure that what's deleted on the phone is deleted on the computer, and vice versa. Not to mention the fact that if I ever switched to an iPhone in the future I would be out of luck. The other solution is a cloud solution, which I did use back in ~2012 when Google Music was still around (RIP) but seeing how I already have a Nextcloud instance that sounds unnecessarily wasteful. Up until now I've been unable to think of any way of solving this, especially ways in how I could keep using my favorite MP3-player on the desktop (cmus) in tandem with that solution, but just yesterday I managed to come up with a solution: use Nextcloud in combination with Airsonic. Spoiler alert: it works flawlessly.
In order to make that work I had to have the following features: I needed to be
able to upload files via a Nextcloud instance as usual with the Nextcloud
desktop client (so that it appears as local files on my computer), files which
then can be seen from other programs. This is impossible using the regular
Nextcloud way of handling files as they have locked-down permissions. What I
ended up finding was the 'External storage' app on Nextcloud, which allowed me
to "mount" a local folder on the server for my account. I refer here to the
official documentation available
here,
but the steps are pretty simple: 1.) change ownership of the folder in question
to www-data
(or whatever user is running Nextcloud), 2.) change the
permission of that folder to 0750.
Airsonic is a self-hosted music streamer, specifically designed to handle very large music collections, as well as podcast support. This is totally overkill for my needs, but it was in my research the one that came both highly recommended and provided ubiquitous app support for both iOS and Android. The web UI leaves something to be desired, hearkening back to the days of PHP in the late 00's:
For my needs this isn't a big deal seeing that I won't touch the WebUI unless I have to do admin-stuff.
Setting up Airsonic was easy seeing as I already had docker set up on my
server, so I just used the linuxserver
docker package available
here
[copy-pasting the docker-compose block from the documentation]. Here you have
to change the PUID
& PGID
to the web user(which you can get from running
id WEBUSER
, replacing WEBUSER
with e.g. www-data
), as well as changing
CONTEXT_PATH
to your desired subdomain. For example, if you plan on hosting
it at https://YOURDOMAIN.com/airsonic
you change it to airsonic
. After
running docker-compose up -d
I was up and running locally at port 4040.
Everything at that point worked, with the exception of duplicate albums, which
was the result of me setting the podcast directory as identical to the music
directory (whoops!). However, as I put it behind nginx
(using the official
configuration as available
here) I ran into my first real
issue: all the album art was missing from the WebUI (but working on my phone).
I fixed it by finding some random nginx config file on GitHub (and
subsequently quickly forgot where I found it). The reverse proxy block looks
like follows:
location /music {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header Host $http_host;
proxy_max_temp_file_size 0;
# Use secure headers to avoid XSS and many other things
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "no-referrer";
# variables to keep line length under 80
set $SCR "script-src 'self' 'unsafe-inline' 'unsafe-eval'";
set $FRM "frame-src 'self'";
set $OBJ "object-src 'self'";
add_header Content-Security-Policy "${SCR}; ${FRM}; ${OBJ}";
proxy_pass http://127.0.0.1:4040;
proxy_redirect http:// https://;
}
Now I have seamless integration between my computer and my phone without me having to depend on syncing specific folders to my phone - now I can just play and sync whatever I like to, without any hassle. Neat!