Skip to content

Commit 3e3e894

Browse files
author
StemSplit
committed
Initial release: stemsplit-python v0.1.0
Synchronous Python SDK for the StemSplit stem-separation API. - Resources: jobs, youtube_jobs, account, webhooks, uploads - Stripe-style error hierarchy with documented error codes - Webhook signature verification (verify_and_parse) - BPM and key detection on Job.audio_metadata - pydantic v2 models, mypy --strict clean, py.typed shipped - Three runtime dependencies: httpx, pydantic, typing-extensions - 62 tests, ~92 percent line coverage on the SDK code
0 parents  commit 3e3e894

43 files changed

Lines changed: 3923 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
id-token: write
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Install uv
19+
uses: astral-sh/setup-uv@v3
20+
21+
- name: Set up Python
22+
run: uv python install 3.12
23+
24+
- name: Build sdist + wheel
25+
run: uv build
26+
27+
- name: Publish to PyPI
28+
run: uv publish
29+
env:
30+
UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }}
31+
32+
- name: Upload distribution artifacts
33+
uses: actions/upload-artifact@v4
34+
with:
35+
name: dist
36+
path: dist/

.github/workflows/test.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
name: ${{ matrix.python-version }} on ${{ matrix.os }}
12+
runs-on: ${{ matrix.os }}
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
os: [ubuntu-latest, macos-latest, windows-latest]
17+
python-version: ["3.10", "3.11", "3.12", "3.13"]
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Install uv
23+
uses: astral-sh/setup-uv@v3
24+
25+
- name: Set up Python ${{ matrix.python-version }}
26+
run: uv python install ${{ matrix.python-version }}
27+
28+
- name: Install dev dependencies
29+
run: uv sync --extra dev
30+
31+
- name: Lint (ruff)
32+
run: |
33+
uv run ruff check src tests
34+
uv run ruff format --check src tests
35+
36+
- name: Type-check (mypy)
37+
run: uv run mypy src/stemsplit_python
38+
39+
- name: Run tests
40+
run: uv run pytest -q

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.venv/
2+
__pycache__/
3+
*.pyc
4+
*.egg-info/
5+
.pytest_cache/
6+
.ruff_cache/
7+
.mypy_cache/
8+
build/
9+
dist/
10+
.coverage
11+
htmlcov/
12+
.env
13+
.env.local
14+
15+
out/
16+
artifacts/
17+
18+
.tmp_*.py

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11

CHANGELOG.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Changelog
2+
3+
All notable changes to `stemsplit-python` are documented here. The format
4+
is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5+
this project adheres to [Semantic Versioning](https://semver.org/).
6+
7+
## [0.1.0] - 2026-05-21 — Initial release: sync SDK for the StemSplit API
8+
9+
First public release. Synchronous Python client for the official
10+
[StemSplit](https://stemsplit.io) hosted stem-separation API.
11+
12+
### Added
13+
14+
- **`StemSplit` sync client** (`AsyncStemSplit` placeholder pointing at
15+
v0.2). Bearer-auth, env-var key default (`STEMSPLIT_API_KEY`),
16+
configurable base URL, soft `sk_live_` prefix validation.
17+
- **Resources for every public endpoint:**
18+
- `client.jobs` — create (file / bytes / file-like / `source_url` /
19+
`upload_key`), get, list, `iter_all`, with the killer ergonomic
20+
layer: `JobHandle.wait()`, `.iter_progress()`, `.refresh()`,
21+
`.download_all()`.
22+
- `client.youtube_jobs` — create from YouTube URL, get, list,
23+
`iter_all`, same `wait()` / `download_all()` ergonomics. Surfaces
24+
video metadata (`video_title`, `video_duration`, `channel_name`,
25+
`youtube_url`) and the YouTube-specific `full_audio` output.
26+
- `client.uploads` — presigned-URL helpers (`create_ticket`,
27+
`upload_bytes`) for the rare case where you want to manage uploads
28+
yourself.
29+
- `client.account``balance()` / `get()` returning typed `Balance`.
30+
- `client.webhooks``create`, `list`, `delete`. Capture `secret`
31+
from `create()`; the API never returns it again.
32+
- **Webhook signature verification** (`stemsplit_python.webhooks`):
33+
`verify_signature` for raw-bytes verification and `verify_and_parse`
34+
that returns a typed `WebhookEvent`. Constant-time HMAC-SHA256
35+
comparison, accepts both `sha256=<hex>` and bare hex.
36+
- **Stripe-style error hierarchy** mapping API responses 1:1 to typed
37+
exceptions: `BadRequestError` (400), `AuthenticationError` (401),
38+
`InsufficientCreditsError` (402, with `.required_seconds` /
39+
`.purchase_url`), `PermissionDeniedError` (403), `NotFoundError`
40+
(404), `ConflictError` (409), `UnprocessableEntityError` (422),
41+
`RateLimitError` (429, with `.retry_after` / `.limit` /
42+
`.remaining` / `.reset_at`), `InternalServerError` (5xx). Plus
43+
logical errors `JobFailedError`, `JobExpiredError`,
44+
`SignatureVerificationError`.
45+
- **Retries with backoff** on `429` and `5xx` for safe-to-replay verbs
46+
(default `max_retries=3`). Honors `Retry-After` when present.
47+
- **Rate-limit awareness:** parses `X-RateLimit-*` headers on every
48+
response, exposed as `client.last_rate_limit`.
49+
- **Idempotency-Key passthrough** on `jobs.create`, `youtube_jobs.create`,
50+
`webhooks.create`. Forwarded as a header today; lights up
51+
automatically when the API ships server-side support.
52+
- **Audio metadata (BPM / key)** on completed jobs: `Job.audio_metadata`
53+
and `YouTubeJob.audio_metadata` typed as `AudioMetadata { bpm, key }`.
54+
Both fields are optional and degrade gracefully if the analysis
55+
doesn't run.
56+
- **Pydantic v2 models** with `frozen=True`, `extra="allow"` (forward-
57+
compatible against new server fields), snake_case Python attributes
58+
aliased to the camelCase wire fields, and `Literal[...]` unions for
59+
every status / quality / format value (no `enum.Enum`).
60+
- **Typing:** `py.typed` marker shipped, `mypy --strict` clean.
61+
- **Three runtime dependencies only:** `httpx`, `pydantic`,
62+
`typing-extensions` (Python 3.10 backport only). No PyTorch, no
63+
numpy, no native code.
64+
- **Snapshot of the live OpenAPI 3.1 spec** at
65+
`openapi/openapi.json` so future versions can diff API drift.
66+
67+
### Tested
68+
69+
- 62 tests, ~92 % line coverage, all `respx`-backed mocks. Full status-
70+
code → exception matrix, retry behavior, end-to-end
71+
`jobs.create(audio=Path)` chain, `wait()` happy / failed / timeout
72+
paths, webhook signature round-trip + tamper detection, and YouTube
73+
job lifecycle.
74+
75+
### Deferred
76+
77+
- **`AsyncStemSplit`** — lands in v0.2. Constructing it today raises
78+
`NotImplementedError` with a pointer at the tracking issue.
79+
- **`job.cancel()`** — the API does not yet expose `DELETE /jobs/{id}`;
80+
honest omission rather than a no-op stub. Will land alongside the
81+
endpoint.
82+
- **CLI** — a separate `stemsplit-cli` add-on is planned for v0.2 so
83+
the SDK stays dependency-light.
84+
85+
[0.1.0]: https://github.com/StemSplit/stemsplit-python/releases/tag/v0.1.0

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 StemSplit
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.

0 commit comments

Comments
 (0)