Remove background music from videos - for accessibility and personal use
Project description
ELUATE
As a muslim, I've experienced this friction many times: the documentary I want to watch, the lecture I want to learn from, the long-form video I want to understand, almost always comes with a score running underneath. One day I sat down to watch a documentary, the soundtrack was on top of every scene, and I went looking for a tool that would just take the video and hand me back the same video with the music gone. Audio-only stem splitters, GUIs aimed at music producers, cloud services that wanted my upload: none of them did the simple thing.
ELUATE is the simple thing. You give it a video file, it gives you back a video file, the music is gone, the dialogue and sound effects stay, and the video stream is copied through untouched so the picture is bit-for-bit unchanged.
From the terminal:
eluate documentary.mp4
# → ~/Documents/ELUATE/documentary_eluted.mp4
From Python, the same engine behind a small stable API, so you can wire ELUATE into something larger (a content pipeline, a batch job, your own project):
import eluate
eluate.elute("documentary.mp4")
Under the hood it's an AI source-separation model (Bandit v2) running locally on your machine. It splits the audio into speech, music, and sfx, discards the music stem, mixes the other two back together, and remuxes the new audio into a copy of the video. The video stream is never re-encoded.
I built it for muslims who run into the same friction trying to learn, research, or produce content. The CLI is for one-off cleanup, the API is for building larger things on top. Other people have related reasons: hearing loss where the mix fights the narration, focus or auditory-processing conditions where the score becomes another competing stream you have to filter out. If that's you too, you're welcome here. The tool is generic, anyone can use it.
It's a one-maintainer project, and I'd rather you treat it as a useful tool you can fork than as infrastructure. If a fellow muslim dev takes what I've started here and makes it better than I could, that's honestly the outcome I'd be happiest with.
Why this and not something else
Most OSS in this space comes from music production, where the goal is to split a song into vocals, drums, bass, and other. That's the four-stem split you'll find in Demucs, in UVR5, in python-audio-separator. For cinematic content (documentaries, lectures, sermons, podcasts) that split is the wrong shape. What you actually want is speech, music, and sound effects. ELUATE is built around that three-stem split because that's what fits the use case.
The model behind it is Bandit v2, a research-grade separator from ICASSP 2025. It isn't shipped by default in UVR5, python-audio-separator, or Demucs, so as far as I know this is the only place you'll find it pre-wired into a video-in/video-out tool. ELUATE fetches the checkpoint and the config for you on first run; you don't have to chase them down.
The other thing I cared about was long files. Most separation tools
load the whole audio track into RAM, which is fine for a song and
rough for a feature-length documentary. ELUATE uses a fixed-size
ring buffer instead, processing the audio in a moving window without
ever holding the full track in memory. On an 84-minute documentary
on my M4 with 24 GB, that meant 19 GB of peak memory for ELUATE
versus 41 GB for the upstream reference, which only finished because
macOS swapped about 20 GB to disk. About 2.15× less in practice.
Numbers and methodology in docs/bench/.
What ELUATE isn't
Before you install it, a few things it doesn't do, so you don't find out the wrong way.
It's terminal only. There's no GUI and no drag-and-drop installer. If that's a dealbreaker, UVR5 is probably what you want for audio, or one of the cloud tools for one-shot video.
It isn't faster than Demucs. I optimised the memory path for long files, not raw throughput, and you'll feel that on short inputs where Demucs finishes first.
It doesn't take YouTube URLs, doesn't process anything in the cloud, and doesn't parallelise across machines. One local file at a time, or a folder processed sequentially.
It also doesn't give you the stems. ELUATE's job is to drop the music and hand back a video; if you want an isolated-stem export, that's a different tool.
It isn't easier to install than a signed binary. You'll need a
terminal, brew, and git. If that's a barrier, the
alternatives section at the bottom lists tools
that are.
Install
macOS on Apple Silicon is the only platform I actually develop and benchmark on. CUDA and CPU paths exist in the code but I haven't tested them; treat them as experimental. If you're on Linux or Windows, the alternatives section at the bottom of this README will get you there faster than waiting for me.
The first run downloads a ~450 MB model checkpoint from
Zenodo into ~/.eluate/.
It only happens once.
The quickest path on macOS:
git clone --recursive https://github.com/borderedprominent/ELUATE.git
cd eluate
./scripts/install.sh
This creates a venv at ~/.eluate/venv, installs ELUATE in editable
mode, downloads the default model, and (by default) appends
export PATH="$HOME/.eluate:$PATH" to your ~/.zshrc or ~/.bashrc
so you can run eluate from any terminal.
If you'd rather manage your own PATH:
./scripts/install.sh --no-rc-edit
Open a new terminal after installing (so the PATH change takes effect) and run:
eluate info # verify device, FFmpeg, and model are ready
Manual install
For contributors or anyone who doesn't want a shell installer:
git clone --recursive https://github.com/borderedprominent/ELUATE.git
cd eluate
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
brew install ffmpeg # if not already installed
eluate --version
The --recursive flag is required: ELUATE's separator calls into a
pinned copy of [ZFTurbo's Music-Source-Separation-Training framework]
(https://github.com/ZFTurbo/Music-Source-Separation-Training) as a git
submodule at vendor/mss-training/.
Usage
eluate # interactive mode (prompts for file)
eluate video.mp4 # single file → ~/Documents/ELUATE/
eluate video.mp4 -o custom.mp4 # custom output path
eluate --checkpoint eng video.mp4 # use the English-optimised model
eluate --batch files.txt # process paths from a list file
eluate --folder /path/to/videos # process every video in a folder
eluate --folder ./videos --batch-size 10 # folder in batches of 10
eluate info # system / model status
eluate video.mp4 --device cpu # force CPU, skip MPS
eluate video.mp4 --force # skip duration / disk-space checks
Supported input containers: mp4, mkv, avi, mov, webm, flv,
wmv, m4v.
Model
ELUATE ships a single model: Bandit v2, CC-BY-SA 4.0, 48 kHz. CC-BY-SA permits commercial use under share-alike terms; see Licensing for the caveats before shipping anything commercial.
Bandit v2 ships per-language checkpoints (multi, eng, deu, fra,
spa, cmn, fao). The default is multi. Swap with
--checkpoint eng etc.
Python API
For batch loops or wiring ELUATE into your own code, instantiate a
Session so the model loads once and is reused:
import eluate
with eluate.Session() as session:
for path in ["a.mp4", "b.mp4", "c.mp4"]:
session.elute(path)
eluate.elute(), eluate.Session, eluate.Result, and the typed
exception hierarchy under eluate.EluateError form the entire v1.x
public surface; semver applies to those names only. Full reference,
including progress callbacks and stem-only outputs, in
docs/api.md.
How it works
input.mp4 ─┬─▶ ffmpeg audio extract ─▶ 48 kHz WAV
│
│ Bandit v2 (streaming demix)
│ │
│ ┌─────────────┼─────────────┐
│ ▼ ▼ ▼
│ speech music sfx
│ │ drop │
│ └────────── mix ────────────┘
│ │
└─▶ ffmpeg mux ◀───── new audio track (speech + sfx)
│
▼
output_eluted.mp4 (video stream copied as-is)
The separator uses a streaming demix path: a fixed-size ring buffer
and virtual padded-chunk construction so the full audio track is never
held in memory. On an 84-minute documentary this uses ~2.15× less peak
memory than the vendor's batched demix(), which swaps 20 GB to disk
on a 24 GB Mac. See docs/bench/ for the plain-language
summary, full methodology in
memory-benchmark.md, and a
windowing bug this benchmarking uncovered and fixed.
Configuration and data
ELUATE writes data to two places, both under your home directory:
| Path | What lives there |
|---|---|
~/.eluate/models/ |
Downloaded model checkpoints (~450 MB each), configs |
~/.eluate/venv/ |
Python virtual env (created by install.sh) |
~/.eluate/telemetry.jsonl |
Local debug log (only if you enable it) |
~/Documents/ELUATE/ |
Processed output videos |
Nothing is sent over the network after the initial model download.
Local debug log (off by default)
ELUATE can write a local JSONL log of processing stages (wall time, peak memory, MPS allocation) to help debug performance issues. The log never leaves your machine. It's a plain file you can read, delete, or ignore.
Telemetry is off by default. Enable it when you want a paper trail:
ELUATE_TELEMETRY=1 eluate video.mp4
The log contains wall time, peak RSS, and MPS memory at each stage boundary; nothing that identifies you or your files. Delete it any time.
Security
The Bandit v2 checkpoint format is a Python pickle. torch.load has to
be called with weights_only=False to deserialize it, which means
loading a checkpoint executes arbitrary pickled code from that file.
This is a known caveat of running PyTorch checkpoints from any source.
ELUATE mitigates the risk with two belts:
- Pinned source. Model downloads only come from the specific Zenodo
record encoded in
eluate/utils/paths.py(12701995for Bandit v2). The URL is not user-configurable. - SHA256 verification against
CHECKPOINT_SHA256in the same file. A mismatched download is rejected before it's loaded.
The default Bandit v2 multi checkpoint has a verified digest recorded.
The other language variants (eng, deu, fra, spa, cmn, fao)
currently download without integrity checks; the CLI prints a clear
warning when this is the case. Digests will be populated after a
known-good download and review.
If you don't trust this chain, don't run ELUATE. The threat model here is "Zenodo or the install pipeline is compromised"; against that, only a signed release helps, which we don't have yet.
Development
pip install -e .[dev]
pytest
ruff check .
The test suite has a 35 % coverage floor (enforced in CI on macOS across
Python 3.10 / 3.11 / 3.12). Most tests are offline and fast. Tests that
exercise the actual model (numerical parity against the real checkpoint)
are gated behind ELUATE_RUN_MODEL_TESTS=1 so CI doesn't try to
download 450 MB of weights.
ELUATE_RUN_MODEL_TESTS=1 pytest tests/test_core_separator_streaming.py
An end-to-end test that drives EluatePipeline.process() on a tiny
synthetic video fixture is at tests/test_pipeline_e2e.py and runs
without model weights (it stubs the separator and uses real FFmpeg).
Licensing
ELUATE is a personal tool, not an enterprise product. Read this section before using it for anything beyond personal viewing.
- ELUATE's own code: MIT (see
LICENSE). - Bandit v2 model weights: CC-BY-SA 4.0, from Zenodo 12701995. CC-BY-SA permits commercial use under share-alike terms: any derivative work you distribute under these weights must itself be licensed CC-BY-SA. That clause is operationally hostile to a lot of commercial software (it arguably extends to downstream derivative works). Consult a lawyer before shipping a commercial product built on ELUATE. The README can't and doesn't give legal advice.
- Vendored separator framework at
vendor/mss-training/: ZFTurbo's Music-Source-Separation-Training, MIT-licensed.
Project risks
ELUATE carries real single-points-of-failure you should know about before building anything on top of it:
- Upstream model maintenance. The [Bandit v2 research repo] (https://github.com/kwatcharasupat/bandit-v2) has a small number of commits and no ongoing release cadence. If the author moves on, the model itself won't get fixes.
- Vendored separator framework. ELUATE calls into ZFTurbo/Music-Source-Separation-Training via a git submodule pinned to a specific SHA. Upstream inference-API changes would require ELUATE to track or fork.
- Forked model config.
eluate/configs/bandit_v2.yamlships an explicit copy of the Bandit v2 architecture hyperparameters (band count, RNN dim, etc.). If upstream releases a Bandit v2.1 with a different architecture, ELUATE's config will be silently incompatible with the new checkpoint; you'll get a state-dict shape mismatch, not a friendly error. Checkpoint downloads are pinned to the Zenodo record currently compatible with this config, so existing installs keep working; only voluntarily pointing at a new upstream release would trigger this. - One-maintainer project. Treat it as a useful personal tool that you can fork if it stops being maintained, not as infrastructure.
- Niche model. Bandit v2 isn't shipped by default in UVR5,
python-audio-separator, or Demucs, so there's no adjacent community to rely on for ecosystem-level fixes.
The MIT license and local-only design mean you can always fork and keep running what works for you. That's intentional.
Alternatives
If ELUATE isn't the right fit, use the right tool instead:
- asaah18/video-music-remover: closest direct competitor. Video in, video out, CLI, uses Demucs. Ship it if you specifically want Demucs or the 4-stem music-production split.
- UVR5 (Ultimate Vocal Remover GUI): the de-facto GUI. Audio-only (you'd still need to ffmpeg-remux). Signed Win/Mac/Linux bundles, huge community. Best choice for non-technical users on a desktop.
- python-audio-separator: maintained CLI/library successor for the UVR model collection (MDX-Net, Demucs, BS-RoFormer, etc.). Power-user-friendly.
- Demucs: the research-grade reference for music source separation. Parent repo is archived; active development is on the adefossez fork.
- Cloud services (MVSEP, Lalal.ai, vocalremover.org, etc.): if you'd rather not install anything and are comfortable uploading your audio. Usually the fastest path for a non-technical user.
ELUATE is specifically for the case where you want video-in / video-out / local / long-form / cinematic 3-stem. If your needs are different, one of the above is probably better.
Contributing
Open an issue or PR at https://github.com/borderedprominent/ELUATE/issues.
For bug reports, running with the local debug log enabled helps a lot:
ELUATE_TELEMETRY=1 eluate your-video.mp4
# then attach ~/.eluate/telemetry.jsonl (it never leaves your machine until you share it)
Acknowledgements
- Karn N. Watcharasupat, Chih-Wei Wu, and Iroro Orife for the Bandit v2 architecture and the Divide-and-Remaster v3 dataset.
- ZFTurbo for the Music-Source-Separation-Training framework that ELUATE's separator is built on.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file eluate-1.0.1.tar.gz.
File metadata
- Download URL: eluate-1.0.1.tar.gz
- Upload date:
- Size: 109.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63e7340afd855c7d3f50b48b81a9b6384aec61b33a89b900c62bfbc8df144c20
|
|
| MD5 |
0887c89f82b3fa407c1fc0240016c56e
|
|
| BLAKE2b-256 |
360190aeb83c412c035494b7174d8ff7dc56000350abf49914337f1993d8ba79
|
Provenance
The following attestation bundles were made for eluate-1.0.1.tar.gz:
Publisher:
publish.yml on borderedprominent/ELUATE
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
eluate-1.0.1.tar.gz -
Subject digest:
63e7340afd855c7d3f50b48b81a9b6384aec61b33a89b900c62bfbc8df144c20 - Sigstore transparency entry: 1417061381
- Sigstore integration time:
-
Permalink:
borderedprominent/ELUATE@6d74bad4b5ceac12600fc18b550115008540ebc0 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/borderedprominent
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6d74bad4b5ceac12600fc18b550115008540ebc0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file eluate-1.0.1-py3-none-any.whl.
File metadata
- Download URL: eluate-1.0.1-py3-none-any.whl
- Upload date:
- Size: 85.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
064029c7b678191dacd82513b26bdf5289dd156c34042eaa58c53bf2a28e72e1
|
|
| MD5 |
4b40eaa12ba9dbb7f117bf767305334e
|
|
| BLAKE2b-256 |
8d8c46ac9122bf4a7c206988ae6b0c54f1b3c2dcc50f63527213c91ffb8f4cdc
|
Provenance
The following attestation bundles were made for eluate-1.0.1-py3-none-any.whl:
Publisher:
publish.yml on borderedprominent/ELUATE
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
eluate-1.0.1-py3-none-any.whl -
Subject digest:
064029c7b678191dacd82513b26bdf5289dd156c34042eaa58c53bf2a28e72e1 - Sigstore transparency entry: 1417061435
- Sigstore integration time:
-
Permalink:
borderedprominent/ELUATE@6d74bad4b5ceac12600fc18b550115008540ebc0 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/borderedprominent
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6d74bad4b5ceac12600fc18b550115008540ebc0 -
Trigger Event:
release
-
Statement type: