Quinta 7501b46c15 Enhance README with system overview and setup instructions
Added detailed documentation about the music discovery and streaming system, including components, folder structure, configuration files, automation scripts, and tips for users.
2026-04-28 20:05:32 +02:00

Ainulindale

A fully self-hosted, automated music discovery and streaming system. No Spotify. No YouTube Music. No ads. Just your music, your server, your rules.


What This Stack Does

  • Streams your music library to any device via a Spotify-like interface
  • Automatically discovers new music weekly based on your listening habits
  • Downloads discovered tracks at high quality (FLAC/WAV preferred, 320kbps minimum) from the Soulseek network
  • Scrobbles everything you listen to Last.fm and ListenBrainz
  • Routes all P2P traffic through a VPN so your real IP is never exposed

Components

Component Role
Navidrome Music server — indexes your library, streams to clients
Substreamer Mobile app (iOS/Android) — plays music from Navidrome
Pangolin Reverse proxy — exposes Navidrome securely to the internet
Gluetun VPN gateway container — all Soulseek traffic exits via ProtonVPN
slskd Soulseek client — searches and downloads music from the P2P network
Explo Discovery engine — connects ListenBrainz recommendations to slskd
ListenBrainz Open-source scrobbler + recommendation engine
Last.fm Secondary scrobbler for stats and social features

Folder Structure

/mnt/user/appdata/
├── gluetun/               ← VPN state (auto-populated)
├── slskd/
│   └── slskd.yml          ← slskd config
└── explo/
    ├── .env               ← Explo config
    └── docker-compose.yml ← Full stack compose file

/mnt/user/data/media/music/
├── incomplete/            ← In-progress downloads (Navidrome ignores these)
├── explo/                 ← Explo-managed discovery downloads
└── (your library)/        ← Everything else

docker-compose.yml

services:

  gluetun:
    image: qmcgaw/gluetun:latest
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    environment:
      - VPN_SERVICE_PROVIDER=protonvpn
      - VPN_TYPE=wireguard
      - WIREGUARD_PRIVATE_KEY=YOUR_PRIVATE_KEY_HERE
      - WIREGUARD_ADDRESSES=10.2.0.2/32
      - SERVER_COUNTRIES=Switzerland
      - TZ=Europe/Zurich
    ports:
      - 5030:5030
    volumes:
      - /mnt/user/appdata/gluetun:/gluetun
    restart: unless-stopped

  slskd:
    image: slskd/slskd:latest
    container_name: slskd
    network_mode: "service:gluetun"
    depends_on:
      gluetun:
        condition: service_healthy
    environment:
      - SLSKD_REMOTE_CONFIGURATION=true
    volumes:
      - /mnt/user/appdata/slskd:/app
      - /mnt/user/data/media/music:/downloads
      - /mnt/user/data/media/music:/music:ro
    restart: unless-stopped

  explo-weekly:
    image: ghcr.io/lumepart/explo:latest
    container_name: explo-weekly
    environment:
      - EXECUTE_ON_START=true
      - WEEKLY_EXPLORATION_FLAGS=--playlist=weekly-exploration --persist
    volumes:
      - /mnt/user/appdata/explo/.env:/opt/explo/.env
      - /mnt/user/data/media/music/explo:/data
      - /mnt/user/data/media/music:/slskd
    restart: "no"

  explo-daily:
    image: ghcr.io/lumepart/explo:latest
    container_name: explo-daily
    environment:
      - EXECUTE_ON_START=true
      - DAILY_JAMS_FLAGS=--playlist=daily-jams --download-mode=skip
    volumes:
      - /mnt/user/appdata/explo/.env:/opt/explo/.env
      - /mnt/user/data/media/music/explo:/data
      - /mnt/user/data/media/music:/slskd
    restart: "no"

Explo .env

# --- Discovery ---
DISCOVERY_SERVICE=listenbrainz
LISTENBRAINZ_USER=your_listenbrainz_username
LISTENBRAINZ_DISCOVERY=playlist

# --- Navidrome (Subsonic API) ---
EXPLO_SYSTEM=subsonic
SYSTEM_URL=http://192.168.1.x:4533
SYSTEM_USERNAME=your_navidrome_username
SYSTEM_PASSWORD=your_navidrome_password

# --- Downloaders (slskd first, YouTube fallback) ---
DOWNLOAD_SERVICES=slskd,youtube
TRACK_EXTENSION=mp3

# --- slskd ---
SLSKD_URL=http://slskd:5030
SLSKD_API_KEY=your_slskd_api_key
MIGRATE_DOWNLOADS=true
RENAME_TRACK=true

# --- Quality gates (FLAC/WAV preferred, 320kbps minimum) ---
EXTENSIONS=flac,wav,mp3
MIN_BITRATE=320
MIN_BIT_DEPTH=16

# --- YouTube fallback (optional) ---
YOUTUBE_API_KEY=your_youtube_api_key

# --- Misc ---
SLEEP=2
LOG_LEVEL=INFO

slskd.yml

soulseek:
  username: your_soulseek_username
  password: your_soulseek_password

web:
  authentication:
    username: your_chosen_web_username
    password: your_chosen_web_password
    api_keys:
      explo_key:
        key: your_api_key_minimum_16_chars_long
        role: readwrite
        cidr: 0.0.0.0/0,::/0

directories:
  incomplete: /downloads/incomplete
  downloads: /downloads

transfers:
  download:
    slots: 50
  upload:
    slots: 20

shares:
  directories:
    - /music

Navidrome (Unraid Community Apps)

Navidrome is installed via Unraid's Community Apps store. Add these extra environment variables in the container template:

Variable Value
ND_LASTFM_APIKEY your Last.fm API key
ND_LASTFM_SECRET your Last.fm shared secret
ND_LASTFM_ENABLED true
ND_SCANSCHEDULE 1m

Last.fm API credentials: last.fm/api/account/create


Automation — Unraid User Scripts

Install the User Scripts plugin from Community Apps, then add two scripts:

Weekly Exploration (every Tuesday at 00:15)

Cron: 15 0 * * 2

#!/bin/bash
docker compose -f /mnt/user/appdata/explo/docker-compose.yml up explo-weekly

Daily Jams (every day at 00:15)

Cron: 15 0 * * *

#!/bin/bash
docker compose -f /mnt/user/appdata/explo/docker-compose.yml up explo-daily

How Discovery Works

You listen in Substreamer
        ↓
Navidrome scrobbles to Last.fm + ListenBrainz
        ↓
ListenBrainz builds your taste profile over time
        ↓
Every Monday night: ListenBrainz generates Weekly Exploration playlist
        ↓
Tuesday 00:15: Explo pulls recommendations
        ↓
slskd searches Soulseek network (behind ProtonVPN)
        ↓
Downloads FLAC/WAV/320kbps+ to /mnt/user/data/media/music
        ↓
Navidrome scans every 1 minute → appears in Substreamer
        ↓
You discover new music 🎵

Tips

  • ListenBrainz needs time — it takes a few weeks of scrobbling before Weekly Exploration playlists are generated. Speed this up by importing your Google Takeout YouTube Music watch history via ytm-extractor.
  • Manual downloads — search for any artist or album directly in the slskd web UI at http://your-nas-ip:5030. Downloads land straight in your Navidrome library.
  • Playlist import — use Soundiiz to transfer playlists from YouTube Music/Spotify directly into Navidrome. Tracks already in your library get matched automatically.
  • Quality — slskd is tried first for every download. YouTube is only used as a last resort fallback for tracks not found on Soulseek.
  • Sharing — slskd shares your music library back to the Soulseek network. The more you share, the better your download priority from other peers.
  • ProtonVPN — the WireGuard private key in Gluetun pins you to a specific server. Make sure to use a P2P-capable server when generating your WireGuard config on ProtonVPN's site.

Prerequisites

  • Unraid NAS (or any Linux server with Docker)
  • ProtonVPN account (paid, for P2P/WireGuard support)
  • Soulseek account — free at slsknet.org
  • ListenBrainz account — free at listenbrainz.org
  • Last.fm account — free at last.fm
  • Last.fm API key — free at last.fm/api
  • Substreamer app on your phone
  • Pangolin or any reverse proxy for external access
S
Description
A fully self-hosted, automated music discovery and streaming system. No Spotify. No YouTube Music. No ads. Just your music, your server, your rules.
Readme MIT
64 KiB
Languages
Python 86.8%
Shell 13.2%