Metadata-Version: 2.4
Name: cyphr-sdk
Version: 0.1.2
Summary: Official Python SDK for the Cyphr AI mixing API
Project-URL: Homepage, https://cyphrmusic.com
Project-URL: Documentation, https://cyphrmusic.com
Project-URL: Repository, https://gitlab.com/cyphr1/cyphr
Author-email: Cyphr LLC <team@cyphrmusic.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,audio,cyphr,mixing,music,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Description-Content-Type: text/markdown

# Cyphr Python SDK

Official client for the [Cyphr](https://cyphrmusic.com) AI mixing API. Upload stems, run an automated AI mix, and download the finished WAV — all from Python.

## Install

```bash
pip install cyphr-sdk
```

Requires Python 3.9+.

## Authentication

1. Sign in at [cyphrmusic.com](https://www.cyphrmusic.com).
2. Open **Account → API keys** and create a key (`cyphr_sk_…`).
3. Pass it to the client or set `CYPHR_API_KEY`.

The account page also shows the API base URL to use with `base_url=` or `CYPHR_API_BASE_URL`.

## Quick start

```python
from pathlib import Path
from cyphr import CyphrClient, StemSpec

client = CyphrClient(api_key="cyphr_sk_…")

mix_path = client.mix_project(
    title="My Song",
    stems=[
        StemSpec("vocals.wav", "vox_lead", Path("vocals.wav")),
        StemSpec("beat.wav", "multiple_instruments", Path("beat.wav")),
    ],
    output_path="mix.wav",
)
print("Downloaded:", mix_path)
```

## Step-by-step workflow

Use this when you want control over uploads or status polling:

```python
from cyphr import CyphrClient

client = CyphrClient(api_key="cyphr_sk_…")

# 1. Create project — returns presigned upload URLs
created = client.create_project(
    title="My Song",
    stems=[
        {"filename": "vocals.wav", "trackType": "vox_lead"},
        {"filename": "beat.wav", "trackType": "multiple_instruments"},
    ],
)

# 2. Upload each stem (PUT to presigned URL; headers must match)
for upload, path in zip(created.uploads, ["vocals.wav", "beat.wav"]):
    client.upload_stem(upload, path)

# 3. Queue for mixing
client.queue_project(created.project.id)

# 4. Wait until status is "completed"
project = client.wait_for_completion(created.project.id)
print(project.status)

# 5. Download the mix WAV (requires subscription)
client.download_mix(created.project.id, "mix.wav")

# Delete a project to free a slot on the free plan
client.delete_project(created.project.id)
```

## Configuration

| Setting | Environment variable | Constructor kwarg |
|---------|---------------------|-------------------|
| API key | `CYPHR_API_KEY` | `api_key=` |
| API base URL | `CYPHR_API_BASE_URL` | `base_url=` |

```python
# Explicit configuration
client = CyphrClient(
    api_key="cyphr_sk_…",
    base_url="https://your-api-url.lambda-url.us-east-1.on.aws",
    timeout=60.0,  # HTTP timeout in seconds
)
```

If `base_url` is omitted, the SDK uses its built-in production URL when set, otherwise the dev API URL shipped with the package.

## Track types

Every stem needs a `trackType` when creating a project:

| Value | Use for |
|-------|---------|
| `vox_lead` | Lead vocal |
| `vox_back` | Backing vocals / harmonies |
| `multiple_instruments` | Full beat or multi-instrument stem |
| `bass` | Bass line |
| `808` | 808 / sub bass |
| `drum_kit` | Full drum mix |
| `kick` | Kick drum |
| `snare` | Snare / clap |
| `hi_hat_cymbals` | Hats / cymbals |
| `keys` | Piano / synth keys |
| `guitar` | Guitar |
| `sfx` | Risers, sweeps, FX |
| `multi_normalize` | Already-processed stem (normalize only) |

Supported audio formats: `.wav`, `.flac`, `.mp3`, `.m4a`, `.aiff`, `.aif`

## API reference

### `CyphrClient`

| Method | Description |
|--------|-------------|
| `verify()` | Check API key; returns `{ "userId": "…" }` |
| `list_projects()` | List AI mix projects |
| `create_project(title, stems, genre=None)` | Create project + presigned upload URLs |
| `upload_stem(upload, source)` | Upload one stem file |
| `queue_project(project_id)` | Start mixing after uploads |
| `get_project(project_id)` | Get project status |
| `delete_project(project_id)` | Delete project and its files |
| `wait_for_completion(project_id, …)` | Poll until done |
| `get_mix_download(project_id)` | Presigned download URL (subscription required) |
| `download_mix(project_id, dest=None)` | Download WAV to disk |
| `mix_project(title, stems, …)` | End-to-end helper |

### Types

- `StemSpec(filename, track_type, source)` — local file to upload in `mix_project`
- `Project` — `id`, `title`, `status`, `mix_status`, …
- `StemUpload` — presigned upload target from `create_project`
- `MixDownload` — presigned download URL from `get_mix_download`

### Exceptions

- `CyphrConfigError` — missing API key or invalid client setup
- `CyphrApiError` — API returned an error (`status_code`, `body`)
- `TimeoutError` — `wait_for_completion` exceeded its timeout

In Python: `help(CyphrClient)` for full method documentation.

## Project statuses

Typical flow: `uploading` → `queued` → `pending` → `completed`

If classification needs manual review in the app, status may become `awaiting-review`. Failed mixes return `failed`.

Free accounts are limited to 3 AI mix projects. Downloading finished mixes requires an active subscription.

## License

MIT
