Octave

Octave

Docker app from johagan's Repository

Overview

Sync Spotify playlists into Jellyfin, request missing albums in Lidarr, and enrich matching with ListenBrainz and Last.fm. Browser-based setup — no config file editing needed.

Octave

Octave

Spotify → Jellyfin + Lidarr sync. With ListenBrainz & Last.fm enrichment.

License


Spotify playlist ──► match against Jellyfin library ──► add to Jellyfin playlist
                         │                               └──► set cover art
                         └──► request missing albums in Lidarr
                              └──► ListenBrainz MBID resolution
                                   └──► Last.fm similar-artist discovery

Octave keeps your Spotify playlists in Jellyfin. When tracks are missing from your library, it can request albums in Lidarr and track them across runs until they appear. One container, local-first, with a web UI for setup.


Features

  • Web UI — onyx dark dashboard, playlist manager, live log tail, config editor
  • Per-playlist sync modesadd_only (safe), full_sync (mirror), rebuild (wipe/recreate)
  • Bulk playlist management — select multiple, change mode, or remove in one click
  • Scheduled sync — cron via SYNC_SCHEDULE (default: 2 AM UTC)
  • Manual trigger — sync all or a single playlist from the dashboard
  • Fuzzy matching — RapidFuzz title + artist scoring with tuned thresholds
  • Track cache — persistent spotify_id → jellyfin_id mapping; >10× faster on warm runs
  • Persistent library index — Jellyfin library cached to disk for instant warm starts
  • Parallel Lidarr requests — up to 4 concurrent album lookups
  • Cover art — automatically pulled from Spotify and uploaded to Jellyfin playlists
  • Missing tracks view — browse unmatched tracks per playlist, download CSV
  • Duplicate detection — warns on in-playlist dupes; prevents cross-run Lidarr spam
  • waiting_for_lidarr — tracks queued albums across runs so you know what's in flight
  • Compilation guard — avoids matching compilation albums to wrong artists
  • SQLite history — every run stored; survives restarts cleanly
  • HTTP Basic Auth — optional browser-native auth for exposed deployments
  • PKCE Spotify OAuth — no client secret required for normal setup
  • Sync all playlists — discover owned/followed Spotify playlists automatically
  • Optional ListenBrainz — MBID resolution, global popularity data
  • Optional Last.fm — playcounts, similar track/artist discovery
  • Fully responsive — works on phone, tablet, and desktop

Quick start

1 — Clone and start

git clone https://github.com/jackohagan94-afk/octave.git
cd octave
cp .env.example .env
mkdir -p config data logs
# Linux only: make bind mounts writable by the container user.
sudo chown -R 1000:1000 config data logs
docker compose up -d --build

2 — Configure in the web UI

Open http://localhost:8000, then fill in Settings:

  • Spotify: create a Spotify app, paste its Client ID in Settings, then click Connect Spotify. PKCE does not need a Client Secret, though Octave still accepts one for legacy setups.
  • Jellyfin: URL, API key, and user ID.
  • Lidarr: URL and API key if you want missing albums requested automatically.

See docs/SETUP.md for a full walkthrough.

3 — Linux volume permissions

If you skipped the permission step before first start, run it and restart:

mkdir -p config data logs
sudo chown -R 1000:1000 config data logs
docker compose restart

4 — Add playlists

Browse to http://localhost:8000Playlists → paste a Spotify URL or ID.


Sync modes

Mode Behaviour
add_only Tracks are only ever added. Manual Jellyfin edits preserved.
full_sync Mirrors Spotify exactly — removals from Spotify are reflected.
rebuild Deletes and recreates the Jellyfin playlist from scratch every run.

Optional integrations

Service Env var What it does
Spotify SPOTIFY_CLIENT_ID Your Spotify app Client ID for PKCE auth
ListenBrainz LISTENBRAINZ_TOKEN MusicBrainz ID resolution, global popularity stats
Last.fm LASTFM_API_KEY Playcounts, similar tracks/artists, scrobble metadata

Environment variables

See docs/ENVIRONMENT.md for the full reference.

Variable Default Notes
SYNC_SCHEDULE 0 2 * * * Cron for auto-sync. Empty to disable.
SYNC_ON_STARTUP false Trigger sync immediately on boot.
TZ UTC Timezone for cron and log timestamps.
AUTH_USERNAME octave HTTP Basic Auth username.
AUTH_PASSWORD (empty) Empty disables auth; set a password before exposing Octave.
LISTENBRAINZ_TOKEN (empty) Optional — enables MBID/popularity features.
LASTFM_API_KEY (empty) Optional — enables playcounts/discovery.
LASTFM_USERNAME (empty) Optional — used for Last.fm scrobble import workflows.

Docs


License

MIT

Install Octave on Unraid in a few clicks.

Find Octave in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Octave Review the template variables and paths Click Install

Related apps

Details

Repository
ghcr.io/johagan94/octave:latest
Last Updated2026-07-15
First Seen2026-05-28

Runtime arguments

Web UI
http://[IP]:[PORT:8000]
Network
bridge
Shell
bash
Privileged
false

Template configuration

Web UI PortPorttcp

Octave web interface and API.

Target
8000
Default
8000
Value
8000
Config PathPathrw

Stores config.json (non-secret settings: Jellyfin library ID, playlists list).

Target
/app/config
Default
/mnt/user/appdata/octave/config
Value
/mnt/user/appdata/octave/config
Data PathPathrw

Runtime data: SQLite sync history, credentials (settings.json), Spotify PKCE token, track cache.

Target
/app/data
Default
/mnt/user/appdata/octave/data
Value
/mnt/user/appdata/octave/data
Logs PathPathrw

Octave log files (rotated automatically).

Target
/app/logs
Default
/mnt/user/appdata/octave/logs
Value
/mnt/user/appdata/octave/logs
Spotify Client IDVariable

Required to connect Spotify. Create a free app at developer.spotify.com/dashboard, copy its Client ID here (or into Settings later), and add your Octave callback URL — http://SERVER-IP:8000/callback — to that app's Redirect URIs. PKCE is used, so no client secret is needed.

Target
SPOTIFY_CLIENT_ID
Spotify Redirect URIVariable

Optional. Leave blank to let Octave infer the callback URI. Set this explicitly when using a reverse proxy, e.g. https://octave.example.com/callback.

Target
SPOTIFY_REDIRECT_URI
Jellyfin URLVariable

Optional — use 'Sign in to Jellyfin' in the Settings UI to configure automatically. URL reachable from the Octave container, e.g. http://192.168.1.10:8096 or http://jellyfin:8096.

Target
JELLYFIN_URL
Jellyfin API KeyVariable

Optional — auto-filled by 'Sign in to Jellyfin' in Settings. Jellyfin Dashboard → API Keys.

Target
JELLYFIN_API_KEY
Jellyfin User IDVariable

Optional — auto-filled by 'Sign in to Jellyfin' in Settings. The user ID that will own created playlists.

Target
JELLYFIN_USER_ID
Lidarr URLVariable

Optional — leave unset to disable Lidarr integration. URL reachable from the Octave container, e.g. http://lidarr:8686.

Target
LIDARR_URL
Lidarr API KeyVariable

Optional Lidarr API key. Lidarr → Settings → General → Security → API Key.

Target
LIDARR_API_KEY
Auth UsernameVariable

HTTP Basic Auth username for the web UI. Defaults to 'octave'.

Target
AUTH_USERNAME
Default
octave
Value
octave
Auth PasswordVariable

SECURITY: blank = NO authentication (anyone on the network can access Octave). Set a password before exposing Octave beyond a trusted LAN. Leave blank only for an isolated/LAN-trust setup.

Target
AUTH_PASSWORD
Sync ScheduleVariable

Cron schedule for automatic sync (uses container TZ). Default: 2:00 AM daily. Leave blank to disable scheduled sync.

Target
SYNC_SCHEDULE
Default
0 2 * * *
Value
0 2 * * *
Sync On StartupVariable

Set to 'true' to run a sync when the container starts.

Target
SYNC_ON_STARTUP
Default
false
Value
false
Sync All PlaylistsVariable

Set to 'true' to discover and sync every Spotify playlist the connected account can see. Spotify-owned editorial playlists are skipped automatically.

Target
SYNC_ALL_PLAYLISTS
TimezoneVariable

IANA timezone string for logs and scheduled syncs, e.g. Australia/Sydney.

Target
TZ
Default
UTC
Value
UTC
Log LevelVariable

Python logging level: DEBUG, INFO, WARNING, or ERROR.

Target
LOG_LEVEL
Default
INFO
Value
INFO
ListenBrainz TokenVariable

Optional. ListenBrainz user token from listenbrainz.org/profile/ — enables MusicBrainz ID resolution for better Lidarr matching.

Target
LISTENBRAINZ_TOKEN
Last.fm API KeyVariable

Optional. Last.fm API key from last.fm/api/account/create — enables playcounts and similar-artist discovery.

Target
LASTFM_API_KEY
Last.fm UsernameVariable

Optional. Last.fm username for scrobble history lookups.

Target
LASTFM_USERNAME