chuja 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chuja-0.1.0/.github/workflows/ci.yml +33 -0
- chuja-0.1.0/.github/workflows/release.yml +35 -0
- chuja-0.1.0/.gitignore +27 -0
- chuja-0.1.0/LICENSE +21 -0
- chuja-0.1.0/PKG-INFO +202 -0
- chuja-0.1.0/README.md +176 -0
- chuja-0.1.0/docs/01-intake.png +0 -0
- chuja-0.1.0/docs/02-progress.png +0 -0
- chuja-0.1.0/docs/03-console.png +0 -0
- chuja-0.1.0/install.sh +43 -0
- chuja-0.1.0/pyproject.toml +50 -0
- chuja-0.1.0/src/chuja/__init__.py +33 -0
- chuja-0.1.0/src/chuja/banner.py +77 -0
- chuja-0.1.0/src/chuja/cli.py +286 -0
- chuja-0.1.0/src/chuja/errors.py +26 -0
- chuja-0.1.0/src/chuja/export.py +112 -0
- chuja-0.1.0/src/chuja/pipeline.py +78 -0
- chuja-0.1.0/src/chuja/separator.py +206 -0
- chuja-0.1.0/src/chuja/server.py +245 -0
- chuja-0.1.0/src/chuja/sources.py +105 -0
- chuja-0.1.0/src/chuja/util.py +62 -0
- chuja-0.1.0/src/chuja/web/console.html +814 -0
- chuja-0.1.0/tests/test_pipeline.py +68 -0
- chuja-0.1.0/tests/test_separator.py +73 -0
- chuja-0.1.0/tests/test_server.py +125 -0
- chuja-0.1.0/tests/test_sources.py +46 -0
- chuja-0.1.0/tests/test_util.py +48 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.9", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
cache: pip
|
|
23
|
+
|
|
24
|
+
# Lightweight install: the test suite lazy-imports / mocks the heavy ML
|
|
25
|
+
# stack (torch, demucs), so CI skips it entirely and stays fast.
|
|
26
|
+
- name: Install (no ML stack)
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install pytest typer rich click
|
|
30
|
+
pip install -e . --no-deps
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: pytest -q
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes on a GitHub Release. Uses PyPI Trusted Publishing (OIDC) — no API
|
|
4
|
+
# token is ever stored or entered. PyPI must have a matching pending/trusted
|
|
5
|
+
# publisher configured for this repo + workflow (see README "Publishing").
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: pypi # must match the environment set on the PyPI publisher
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # required for OIDC trusted publishing
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Build sdist + wheel
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip build
|
|
27
|
+
python -m build
|
|
28
|
+
|
|
29
|
+
- name: Verify metadata
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install twine
|
|
32
|
+
python -m twine check dist/*
|
|
33
|
+
|
|
34
|
+
- name: Publish to PyPI
|
|
35
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
chuja-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Virtual environments (never commit the multi-hundred-MB ML stack)
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# Tooling
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
|
|
19
|
+
# chuja output
|
|
20
|
+
stems/
|
|
21
|
+
*.stems/
|
|
22
|
+
|
|
23
|
+
# OS / editor
|
|
24
|
+
.DS_Store
|
|
25
|
+
*.swp
|
|
26
|
+
.idea/
|
|
27
|
+
.vscode/
|
chuja-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Nakitare
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
chuja-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chuja
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Separate a song into stems (vocals/drums/bass/other) from a local file — or, opt-in, from a URL.
|
|
5
|
+
Project-URL: Homepage, https://github.com/dnakitare/chuja
|
|
6
|
+
Project-URL: Issues, https://github.com/dnakitare/chuja/issues
|
|
7
|
+
Author: Daniel Nakitare
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: audio,demucs,karaoke,music,source-separation,stems,vocals
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Requires-Dist: demucs>=4.0.0
|
|
18
|
+
Requires-Dist: rich>=13.0.0
|
|
19
|
+
Requires-Dist: soundfile>=0.12.0
|
|
20
|
+
Requires-Dist: typer>=0.12.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
23
|
+
Provides-Extra: url
|
|
24
|
+
Requires-Dist: yt-dlp>=2024.1.1; extra == 'url'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# chuja
|
|
28
|
+
|
|
29
|
+
[](https://github.com/Dnakitare/chuja/actions/workflows/ci.yml)
|
|
30
|
+
|
|
31
|
+
Separate a song into its stems — **vocals, drums, bass, other** — from a local
|
|
32
|
+
audio file, or (opt-in) from a YouTube / SoundCloud / direct URL. One command,
|
|
33
|
+
portable output.
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
> *The `chuja serve` mixing console — one channel strip per stem with a live
|
|
38
|
+
> waveform, solo/mute, faders, and sample-accurate synced playback.*
|
|
39
|
+
|
|
40
|
+
`chuja` is a thin, friendly wrapper around two excellent open-source engines:
|
|
41
|
+
|
|
42
|
+
- **[Demucs](https://github.com/facebookresearch/demucs)** (Meta) — state-of-the-art neural source separation.
|
|
43
|
+
- **[yt-dlp](https://github.com/yt-dlp/yt-dlp)** — used *only* for the optional URL-fetching feature.
|
|
44
|
+
|
|
45
|
+
It does not reinvent either. What it adds is the glue: one pipeline from a
|
|
46
|
+
source to clean, named, downloadable stem files, with sensible defaults and a
|
|
47
|
+
polished CLI + Python API.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Screenshots
|
|
52
|
+
|
|
53
|
+
**Intake** — drop a file or paste a URL, pick a model / format / split:
|
|
54
|
+
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
**Progress** — a real, per-chunk separation progress bar (not a fake spinner):
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
**Console** — the per-stem mixing board shown at the top of this README.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
Install from source (not yet published to PyPI):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/dnakitare/chuja
|
|
71
|
+
cd chuja
|
|
72
|
+
|
|
73
|
+
# Core: separate LOCAL files. Pulls in Demucs (PyTorch).
|
|
74
|
+
pip install .
|
|
75
|
+
|
|
76
|
+
# Optional: add URL ingestion (YouTube/SoundCloud/etc.)
|
|
77
|
+
pip install '.[url]'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Install globally (run `chuja` from anywhere)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
./install.sh
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
If this project already has a `.venv`, the installer just symlinks its `chuja`
|
|
87
|
+
onto your PATH (`~/.local/bin` by default) — instant, nothing re-downloaded.
|
|
88
|
+
With no `.venv`, it falls back to an isolated [pipx](https://pipx.pypa.io)
|
|
89
|
+
install. Override the link location with `CHUJA_BIN=/usr/local/bin ./install.sh`.
|
|
90
|
+
|
|
91
|
+
Prefer to do it by hand? `pipx install '.[url]'` from the project root, or
|
|
92
|
+
`pip install --user '.[url]'`.
|
|
93
|
+
|
|
94
|
+
### Requirements
|
|
95
|
+
|
|
96
|
+
Requires **Python 3.9+** and **ffmpeg** on your PATH:
|
|
97
|
+
|
|
98
|
+
| Platform | Install ffmpeg |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| macOS | `brew install ffmpeg` |
|
|
101
|
+
| Ubuntu/Debian | `sudo apt install ffmpeg` |
|
|
102
|
+
| Windows | `winget install Gyan.FFmpeg` |
|
|
103
|
+
|
|
104
|
+
> The first separation downloads the model weights (~150 MB) once and caches them.
|
|
105
|
+
|
|
106
|
+
## Usage (CLI)
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Local file → 4 stems as WAV under ./stems/<track>/
|
|
110
|
+
chuja separate song.mp3
|
|
111
|
+
|
|
112
|
+
# Pick a format and bundle into a portable zip
|
|
113
|
+
chuja separate song.flac --format mp3 --zip -o ~/Desktop/stems
|
|
114
|
+
|
|
115
|
+
# Karaoke / acapella split: one stem + everything else
|
|
116
|
+
chuja separate song.mp3 --two-stems vocals
|
|
117
|
+
|
|
118
|
+
# From a URL (requires the [url] extra)
|
|
119
|
+
chuja separate "https://www.youtube.com/watch?v=..." --format mp3
|
|
120
|
+
|
|
121
|
+
# Best-quality (slower) model, force a device
|
|
122
|
+
chuja separate song.wav --model htdemucs_ft --device cpu
|
|
123
|
+
|
|
124
|
+
# List available models
|
|
125
|
+
chuja models
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Usage (library)
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import chuja
|
|
132
|
+
|
|
133
|
+
result = chuja.separate(
|
|
134
|
+
"song.mp3",
|
|
135
|
+
out_dir="stems",
|
|
136
|
+
fmt="mp3",
|
|
137
|
+
two_stems=None, # or "vocals" for a 2-stem split
|
|
138
|
+
zip_output=True,
|
|
139
|
+
)
|
|
140
|
+
print(result.track) # "song"
|
|
141
|
+
print(result.stems) # {"vocals": Path(...), "drums": Path(...), ...}
|
|
142
|
+
print(result.archive) # Path("stems/song.zip")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Models
|
|
146
|
+
|
|
147
|
+
| Model | Stems | Notes |
|
|
148
|
+
| --- | --- | --- |
|
|
149
|
+
| `htdemucs` *(default)* | 4 | Best balance of speed and quality |
|
|
150
|
+
| `htdemucs_ft` | 4 | Fine-tuned — best quality, ~4× slower |
|
|
151
|
+
| `htdemucs_6s` | 6 | Adds piano + guitar (experimental) |
|
|
152
|
+
| `mdx_extra` | 4 | Alternative MDX-challenge model |
|
|
153
|
+
|
|
154
|
+
## A note on quality
|
|
155
|
+
|
|
156
|
+
Separation quality is bounded by your **source** quality. Demucs is excellent,
|
|
157
|
+
but it cannot recover information that lossy compression already discarded — a
|
|
158
|
+
128 kbps MP3 in means audible artifacts in the stems out. Feed it the highest-
|
|
159
|
+
fidelity source you have (WAV/FLAC > 320 kbps MP3 > a low-bitrate stream) for
|
|
160
|
+
the cleanest results. `chuja` deliberately does **not** transcode before
|
|
161
|
+
separation, so it never throws away quality you started with.
|
|
162
|
+
|
|
163
|
+
Performance: separation is compute-heavy. It runs on CPU everywhere, and uses
|
|
164
|
+
your GPU automatically when available — **CUDA** (NVIDIA) or **MPS** (Apple
|
|
165
|
+
Silicon). GPU is many times faster than CPU for full songs.
|
|
166
|
+
|
|
167
|
+
> **Platform note:** developed and tested on **macOS** (CPU and Apple
|
|
168
|
+
> Silicon/MPS). The code is written to be cross-platform — its dependencies
|
|
169
|
+
> (Demucs/PyTorch, soundfile, ffmpeg) all ship for Linux and Windows — but
|
|
170
|
+
> separation hasn't been exercised on those yet. CI runs the test suite on
|
|
171
|
+
> Linux. Reports from other platforms are welcome.
|
|
172
|
+
|
|
173
|
+
## Responsible use
|
|
174
|
+
|
|
175
|
+
The optional URL feature uses `yt-dlp`. Downloading content from YouTube,
|
|
176
|
+
SoundCloud, and similar platforms may violate their Terms of Service, and the
|
|
177
|
+
audio is almost always copyrighted. **You are solely responsible** for ensuring
|
|
178
|
+
you have the right to download and process any audio you give to `chuja`
|
|
179
|
+
(e.g. your own recordings, public-domain works, or content you are licensed to
|
|
180
|
+
use). The core install ships *without* this capability for exactly this reason.
|
|
181
|
+
|
|
182
|
+
## Releasing (maintainers)
|
|
183
|
+
|
|
184
|
+
Releases publish to PyPI via **Trusted Publishing** (OIDC) — no API token is
|
|
185
|
+
ever stored. One-time setup on PyPI
|
|
186
|
+
([Publishing settings](https://pypi.org/manage/account/publishing/) → add a
|
|
187
|
+
*pending publisher*):
|
|
188
|
+
|
|
189
|
+
| Field | Value |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| PyPI project name | `chuja` |
|
|
192
|
+
| Owner | `Dnakitare` |
|
|
193
|
+
| Repository name | `chuja` |
|
|
194
|
+
| Workflow name | `release.yml` |
|
|
195
|
+
| Environment name | `pypi` |
|
|
196
|
+
|
|
197
|
+
Then publish a version by cutting a GitHub Release (e.g. tag `v0.1.0`). The
|
|
198
|
+
`release.yml` workflow builds the sdist + wheel and uploads them automatically.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT.
|
chuja-0.1.0/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# chuja
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Dnakitare/chuja/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Separate a song into its stems — **vocals, drums, bass, other** — from a local
|
|
6
|
+
audio file, or (opt-in) from a YouTube / SoundCloud / direct URL. One command,
|
|
7
|
+
portable output.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
> *The `chuja serve` mixing console — one channel strip per stem with a live
|
|
12
|
+
> waveform, solo/mute, faders, and sample-accurate synced playback.*
|
|
13
|
+
|
|
14
|
+
`chuja` is a thin, friendly wrapper around two excellent open-source engines:
|
|
15
|
+
|
|
16
|
+
- **[Demucs](https://github.com/facebookresearch/demucs)** (Meta) — state-of-the-art neural source separation.
|
|
17
|
+
- **[yt-dlp](https://github.com/yt-dlp/yt-dlp)** — used *only* for the optional URL-fetching feature.
|
|
18
|
+
|
|
19
|
+
It does not reinvent either. What it adds is the glue: one pipeline from a
|
|
20
|
+
source to clean, named, downloadable stem files, with sensible defaults and a
|
|
21
|
+
polished CLI + Python API.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Screenshots
|
|
26
|
+
|
|
27
|
+
**Intake** — drop a file or paste a URL, pick a model / format / split:
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
**Progress** — a real, per-chunk separation progress bar (not a fake spinner):
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
**Console** — the per-stem mixing board shown at the top of this README.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
Install from source (not yet published to PyPI):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/dnakitare/chuja
|
|
45
|
+
cd chuja
|
|
46
|
+
|
|
47
|
+
# Core: separate LOCAL files. Pulls in Demucs (PyTorch).
|
|
48
|
+
pip install .
|
|
49
|
+
|
|
50
|
+
# Optional: add URL ingestion (YouTube/SoundCloud/etc.)
|
|
51
|
+
pip install '.[url]'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Install globally (run `chuja` from anywhere)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
./install.sh
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If this project already has a `.venv`, the installer just symlinks its `chuja`
|
|
61
|
+
onto your PATH (`~/.local/bin` by default) — instant, nothing re-downloaded.
|
|
62
|
+
With no `.venv`, it falls back to an isolated [pipx](https://pipx.pypa.io)
|
|
63
|
+
install. Override the link location with `CHUJA_BIN=/usr/local/bin ./install.sh`.
|
|
64
|
+
|
|
65
|
+
Prefer to do it by hand? `pipx install '.[url]'` from the project root, or
|
|
66
|
+
`pip install --user '.[url]'`.
|
|
67
|
+
|
|
68
|
+
### Requirements
|
|
69
|
+
|
|
70
|
+
Requires **Python 3.9+** and **ffmpeg** on your PATH:
|
|
71
|
+
|
|
72
|
+
| Platform | Install ffmpeg |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| macOS | `brew install ffmpeg` |
|
|
75
|
+
| Ubuntu/Debian | `sudo apt install ffmpeg` |
|
|
76
|
+
| Windows | `winget install Gyan.FFmpeg` |
|
|
77
|
+
|
|
78
|
+
> The first separation downloads the model weights (~150 MB) once and caches them.
|
|
79
|
+
|
|
80
|
+
## Usage (CLI)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Local file → 4 stems as WAV under ./stems/<track>/
|
|
84
|
+
chuja separate song.mp3
|
|
85
|
+
|
|
86
|
+
# Pick a format and bundle into a portable zip
|
|
87
|
+
chuja separate song.flac --format mp3 --zip -o ~/Desktop/stems
|
|
88
|
+
|
|
89
|
+
# Karaoke / acapella split: one stem + everything else
|
|
90
|
+
chuja separate song.mp3 --two-stems vocals
|
|
91
|
+
|
|
92
|
+
# From a URL (requires the [url] extra)
|
|
93
|
+
chuja separate "https://www.youtube.com/watch?v=..." --format mp3
|
|
94
|
+
|
|
95
|
+
# Best-quality (slower) model, force a device
|
|
96
|
+
chuja separate song.wav --model htdemucs_ft --device cpu
|
|
97
|
+
|
|
98
|
+
# List available models
|
|
99
|
+
chuja models
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Usage (library)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
import chuja
|
|
106
|
+
|
|
107
|
+
result = chuja.separate(
|
|
108
|
+
"song.mp3",
|
|
109
|
+
out_dir="stems",
|
|
110
|
+
fmt="mp3",
|
|
111
|
+
two_stems=None, # or "vocals" for a 2-stem split
|
|
112
|
+
zip_output=True,
|
|
113
|
+
)
|
|
114
|
+
print(result.track) # "song"
|
|
115
|
+
print(result.stems) # {"vocals": Path(...), "drums": Path(...), ...}
|
|
116
|
+
print(result.archive) # Path("stems/song.zip")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Models
|
|
120
|
+
|
|
121
|
+
| Model | Stems | Notes |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| `htdemucs` *(default)* | 4 | Best balance of speed and quality |
|
|
124
|
+
| `htdemucs_ft` | 4 | Fine-tuned — best quality, ~4× slower |
|
|
125
|
+
| `htdemucs_6s` | 6 | Adds piano + guitar (experimental) |
|
|
126
|
+
| `mdx_extra` | 4 | Alternative MDX-challenge model |
|
|
127
|
+
|
|
128
|
+
## A note on quality
|
|
129
|
+
|
|
130
|
+
Separation quality is bounded by your **source** quality. Demucs is excellent,
|
|
131
|
+
but it cannot recover information that lossy compression already discarded — a
|
|
132
|
+
128 kbps MP3 in means audible artifacts in the stems out. Feed it the highest-
|
|
133
|
+
fidelity source you have (WAV/FLAC > 320 kbps MP3 > a low-bitrate stream) for
|
|
134
|
+
the cleanest results. `chuja` deliberately does **not** transcode before
|
|
135
|
+
separation, so it never throws away quality you started with.
|
|
136
|
+
|
|
137
|
+
Performance: separation is compute-heavy. It runs on CPU everywhere, and uses
|
|
138
|
+
your GPU automatically when available — **CUDA** (NVIDIA) or **MPS** (Apple
|
|
139
|
+
Silicon). GPU is many times faster than CPU for full songs.
|
|
140
|
+
|
|
141
|
+
> **Platform note:** developed and tested on **macOS** (CPU and Apple
|
|
142
|
+
> Silicon/MPS). The code is written to be cross-platform — its dependencies
|
|
143
|
+
> (Demucs/PyTorch, soundfile, ffmpeg) all ship for Linux and Windows — but
|
|
144
|
+
> separation hasn't been exercised on those yet. CI runs the test suite on
|
|
145
|
+
> Linux. Reports from other platforms are welcome.
|
|
146
|
+
|
|
147
|
+
## Responsible use
|
|
148
|
+
|
|
149
|
+
The optional URL feature uses `yt-dlp`. Downloading content from YouTube,
|
|
150
|
+
SoundCloud, and similar platforms may violate their Terms of Service, and the
|
|
151
|
+
audio is almost always copyrighted. **You are solely responsible** for ensuring
|
|
152
|
+
you have the right to download and process any audio you give to `chuja`
|
|
153
|
+
(e.g. your own recordings, public-domain works, or content you are licensed to
|
|
154
|
+
use). The core install ships *without* this capability for exactly this reason.
|
|
155
|
+
|
|
156
|
+
## Releasing (maintainers)
|
|
157
|
+
|
|
158
|
+
Releases publish to PyPI via **Trusted Publishing** (OIDC) — no API token is
|
|
159
|
+
ever stored. One-time setup on PyPI
|
|
160
|
+
([Publishing settings](https://pypi.org/manage/account/publishing/) → add a
|
|
161
|
+
*pending publisher*):
|
|
162
|
+
|
|
163
|
+
| Field | Value |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| PyPI project name | `chuja` |
|
|
166
|
+
| Owner | `Dnakitare` |
|
|
167
|
+
| Repository name | `chuja` |
|
|
168
|
+
| Workflow name | `release.yml` |
|
|
169
|
+
| Environment name | `pypi` |
|
|
170
|
+
|
|
171
|
+
Then publish a version by cutting a GitHub Release (e.g. tag `v0.1.0`). The
|
|
172
|
+
`release.yml` workflow builds the sdist + wheel and uploads them automatically.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
chuja-0.1.0/install.sh
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Install `chuja` globally so you can run it from any directory.
|
|
4
|
+
#
|
|
5
|
+
# Default (fast): if this project's .venv exists, symlink the `chuja` it
|
|
6
|
+
# already contains onto your PATH. No re-download of the ML stack.
|
|
7
|
+
# Fallback (clean): if there's no .venv, do an isolated install with pipx.
|
|
8
|
+
#
|
|
9
|
+
# Override the link target with: CHUJA_BIN=/usr/local/bin ./install.sh
|
|
10
|
+
#
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
BIN="${CHUJA_BIN:-$HOME/.local/bin}"
|
|
15
|
+
|
|
16
|
+
if [ -x "$HERE/.venv/bin/chuja" ]; then
|
|
17
|
+
mkdir -p "$BIN"
|
|
18
|
+
ln -sf "$HERE/.venv/bin/chuja" "$BIN/chuja"
|
|
19
|
+
echo "✓ Linked chuja → $BIN/chuja (reuses $HERE/.venv — nothing re-downloaded)"
|
|
20
|
+
case ":$PATH:" in
|
|
21
|
+
*":$BIN:"*)
|
|
22
|
+
echo " Run it from anywhere: chuja"
|
|
23
|
+
;;
|
|
24
|
+
*)
|
|
25
|
+
echo " ⚠ $BIN is not on your PATH yet. Add this to ~/.zshrc, then restart your shell:"
|
|
26
|
+
echo " export PATH=\"$BIN:\$PATH\""
|
|
27
|
+
;;
|
|
28
|
+
esac
|
|
29
|
+
else
|
|
30
|
+
echo "No .venv here — installing in an isolated environment with pipx…"
|
|
31
|
+
if ! command -v pipx >/dev/null 2>&1; then
|
|
32
|
+
echo "✗ pipx not found. Install it first: brew install pipx (or: python3 -m pip install --user pipx)"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
pipx install --force "$HERE"
|
|
36
|
+
pipx inject chuja yt-dlp # optional URL-ingestion support
|
|
37
|
+
echo "✓ Installed with pipx. Run: chuja"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo
|
|
41
|
+
echo "Try: chuja # interactive"
|
|
42
|
+
echo " chuja serve # visual console"
|
|
43
|
+
echo " chuja song.mp3 # separate a file"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "chuja"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Separate a song into stems (vocals/drums/bass/other) from a local file — or, opt-in, from a URL."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Daniel Nakitare" }]
|
|
13
|
+
keywords = ["stems", "source-separation", "demucs", "audio", "music", "vocals", "karaoke"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: End Users/Desktop",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Multimedia :: Sound/Audio :: Analysis",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# Core stays deliberately lean and legally unambiguous: separate a LOCAL file.
|
|
23
|
+
dependencies = [
|
|
24
|
+
"demucs>=4.0.0",
|
|
25
|
+
"soundfile>=0.12.0",
|
|
26
|
+
"typer>=0.12.0",
|
|
27
|
+
"rich>=13.0.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
# URL ingestion (YouTube/SoundCloud/etc.) is opt-in. You are responsible for
|
|
32
|
+
# complying with each platform's Terms of Service and applicable copyright law.
|
|
33
|
+
url = ["yt-dlp>=2024.1.1"]
|
|
34
|
+
dev = ["pytest>=8.0.0"]
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
chuja = "chuja.cli:app"
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/dnakitare/chuja"
|
|
41
|
+
Issues = "https://github.com/dnakitare/chuja/issues"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/chuja"]
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
47
|
+
"src/chuja/web/console.html" = "chuja/web/console.html"
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""chuja — separate a song into stems from a local file or (opt-in) a URL.
|
|
2
|
+
|
|
3
|
+
>>> import chuja
|
|
4
|
+
>>> result = chuja.separate("song.mp3", out_dir="stems", fmt="mp3")
|
|
5
|
+
>>> result.stems
|
|
6
|
+
{'drums': PosixPath('stems/song/drums.mp3'), ...}
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .errors import (
|
|
10
|
+
ExportError,
|
|
11
|
+
FetchError,
|
|
12
|
+
MissingDependencyError,
|
|
13
|
+
SeparationError,
|
|
14
|
+
SourceError,
|
|
15
|
+
ChujaError,
|
|
16
|
+
)
|
|
17
|
+
from .pipeline import Result, separate
|
|
18
|
+
from .separator import MODELS
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"separate",
|
|
24
|
+
"Result",
|
|
25
|
+
"MODELS",
|
|
26
|
+
"__version__",
|
|
27
|
+
"ChujaError",
|
|
28
|
+
"SourceError",
|
|
29
|
+
"FetchError",
|
|
30
|
+
"MissingDependencyError",
|
|
31
|
+
"SeparationError",
|
|
32
|
+
"ExportError",
|
|
33
|
+
]
|