From 0709f3293bcd840cf6b0d394b8bf795694f07c1e Mon Sep 17 00:00:00 2001
From: Thales <>
Date: Fri, 21 Aug 2026 17:23:34 +0100
Subject: [PATCH 1/3] Add on-demand lead/backing vocal split, fix stems
relocation bugs, and eager model pre-download (#275, #403)
Lead/backing vocal split:
- New on-demand POST /api/jobs/{id}/vocal-split endpoint, running UVR-MDX-NET
Karaoke 2 (audio-separator) as a second pass over Demucs's vocals.wav
- Desktop and mobile UI toggle to request the split, auto-chained once the
base separation finishes, for both foreground and background jobs
- Mixer shows Lead Vocals / Backing Vocals lanes in place of Vocals once split
Stems relocation fixes (#403):
- user-data.json (library metadata) now lives inside the jobs folder so it
follows a Settings relocation instead of staying behind in Documents
- The relocation endpoint's settings persist step was silently swallowing
write failures and reporting false success; it now reports persisted:
false and the Settings UI shows a clear warning instead
Desktop setup wizard:
- Demucs, beat-this, and the karaoke model now download eagerly during
first-boot setup instead of lazily on first use
Also:
- Credit audio-separator / Ultimate Vocal Remover in the README per its
license's attribution requirement, plus a license audit in docs/models.md
- Add models/ to .gitignore
---
.gitignore | 1 +
README.md | 11 +-
app/api/config.py | 9 +-
app/api/jobs.py | 61 ++++
app/api/stems.py | 40 ++-
app/core/config.py | 15 +
app/core/models.py | 7 +
app/core/registry.py | 11 +-
app/core/settings.py | 30 +-
app/core/stems_location.py | 14 +-
app/main.py | 17 +-
app/pipeline/collect.py | 32 ++
app/pipeline/vocal_split.py | 139 +++++++++
app/pipeline/vocal_split_worker.py | 76 +++++
app/pipeline/warmup.py | 66 ++++
desktop/src-tauri/src/main.rs | 162 +++++++++-
desktop/ui/index.html | 2 +-
desktop/ui/setup.js | 30 +-
docs/models.md | 53 ++++
pyproject.toml | 13 +
static/css/daw.css | 27 ++
static/index.html | 32 ++
static/js/catalog.js | 76 +++--
static/js/constants.js | 23 ++
static/js/job.js | 99 +++++-
static/js/main.js | 33 +-
static/js/mixer.js | 45 ++-
static/js/player.js | 102 +++++--
static/js/state.js | 19 ++
static/mobile/app.js | 88 +++++-
static/mobile/styles.css | 25 ++
tests/test_jobs_api.py | 96 ++++++
tests/test_pipeline_vocal_split.py | 105 +++++++
tests/test_registry_persistence.py | 34 +++
tests/test_stems_api.py | 55 ++++
tests/test_stems_location.py | 36 +++
uv.lock | 475 ++++++++++++++++++++++++++++-
37 files changed, 2009 insertions(+), 150 deletions(-)
create mode 100644 app/pipeline/vocal_split.py
create mode 100644 app/pipeline/vocal_split_worker.py
create mode 100644 app/pipeline/warmup.py
create mode 100644 docs/models.md
create mode 100644 tests/test_pipeline_vocal_split.py
diff --git a/.gitignore b/.gitignore
index dbc488b4..2691730b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,7 @@ htmlcov/
data/
jobs/
cache/
+models/
settings.json
.run/
.build
diff --git a/README.md b/README.md
index 9ff07f92..65878871 100644
--- a/README.md
+++ b/README.md
@@ -53,14 +53,15 @@ StemDeck is free and **does not accept any money, sponsorship, or funding** - no
| Name | What they do | Link |
|---|---|---|
+| Analog4Lyfe | Analog music gear | [@analog4lyfe](https://www.instagram.com/analog4lyfe) |
| Dlima Guitars | Custom guitars and basses | [@dlimaguitars](https://www.instagram.com/dlimaguitars) |
-| Lisbon Guitar Works | Guitar building | [dlimaguitars.com](https://dlimaguitars.com) |
+| Empress Effects | Effects pedals | [empresseffects.com](https://empresseffects.com) |
| Joao Gaspar | Producer/Film Scorer, Touring/Session Musician | [@jay_glaspar](https://www.instagram.com/jay_glaspar) |
| Kris Luthier | Luthier and Musical Instrument Repair, Lisboa | [@krisluthier](https://www.instagram.com/krisluthier) |
-| Thomann | Online Music Store | [@thomann.music](https://www.instagram.com/thomann.music) |
-| Analog4Lyfe | Analog music gear | [@analog4lyfe](https://www.instagram.com/analog4lyfe) |
-| Empress Effects | Effects pedals | [empresseffects.com](https://empresseffects.com) |
+| Lisbon Guitar Works | Guitar building | [dlimaguitars.com](https://dlimaguitars.com) |
| More Notes Less Talk | Instruments and gear with personality, recorded raw to tape. No hype, no gatekeeping. | [@morenoteslesstalk](https://www.youtube.com/@morenoteslesstalk) |
+| Seratone | Turns any TV into a studio-grade karaoke stage | [seratone.audio](https://seratone.audio/) |
+| Thomann | Online Music Store | [@thomann.music](https://www.instagram.com/thomann.music) |
---
@@ -153,7 +154,7 @@ Extract the zip anywhere, run `StemDeck.exe`. FFmpeg, the Demucs model, config,
-StemDeck is built on **[Python 3.12](https://python.org)** managed via **[uv](https://github.com/astral-sh/uv)**, with a **[FastAPI](https://fastapi.tiangolo.com)** backend serving REST and Server-Sent Events. Stem separation uses **[Demucs](https://github.com/facebookresearch/demucs)** (`htdemucs_6s`), Meta AI's open-source 6-stem neural network. YouTube audio is fetched via **[yt-dlp](https://github.com/yt-dlp/yt-dlp)**; transcoding and mixing use **[FFmpeg](https://ffmpeg.org)**. BPM detection and key analysis run on **[librosa](https://librosa.org)**; loudness measurement uses **[pyloudnorm](https://github.com/csteinmetz1/pyloudnorm)** (ITU-R BS.1770). The macOS and Windows desktop shells are **[Tauri v2](https://tauri.app)** (Rust/WKWebView on macOS, Rust/WebView2 on Windows). The frontend is vanilla JS with the Web Audio API, no framework and no build step; waveforms are rendered on `