tts-voice-creator-clone-and.../architecture/voice-bundle.md
mARTin-B78 e0daafd4e1 feat(voices): export and import voice bundles
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 16:42:28 +02:00

14 lines
1.9 KiB
Markdown

# Voice export/import bundles
Code: `core/voice_bundle.py`; routes in `routes/library.py` (`GET /api/voices/{id}/export`, `POST /api/voices/import`); UI in `static/js/voice-library.js` + toolbar in `static/sections/s-voices.html`.
Format (v1), flat ZIP, fixed member names only: `manifest.json`, `audio.<wav|mp3|m4a|flac|ogg|opus>`, `meta.json`, `reference.txt`, `picture.<jpg|jpeg|png|webp>`, `consent.<audio ext>`. Manifest: `format` (1), `app_version` (from `VERSION`), `voice_id`, `files` {member: sha256}. One voice per bundle; multi-select exports one bundle per voice. `meta.json` is the voice sidecar plus `audio_problems`; `provenance.recorded_consent` is only kept when a consent member exists.
Export: streamed through a non-seekable ZIP writer (no temp files); manifest is written last so hashes are computed while streaming.
Import threat model (bundle is hostile): upload cap 150 MB (`_copy_limited`); member names must be in the fixed set, so absolute paths, `..`, subdirectories and unknown extensions are rejected before anything is read; names are never used as paths (staged under fixed names in a hidden `.import-*` dir inside the voices dir, removed in `finally`); declared sizes are checked and real bytes are re-capped while reading; total uncompressed 200 MB, ratio cap 200 for members over 1 MB; manifest must list exactly the members and every sha256 must match; voice id must match `[A-Za-z0-9_-][A-Za-z0-9_.-]{0,99}`; audio must decode; `meta.json` goes through `_validate_voice_meta` and `validate_provenance` (set_at is reset). Nothing is executed.
Name handling: never overwrite. Same id with identical audio hash -> `unchanged` (idempotent). Otherwise `_2`, `_3`, ... (max 99) -> `renamed`. Installed into the active voices dir and added to the voice index.
Not done: no signature (hashes detect corruption/tampering of members but not a re-built manifest); explicit overwrite/skip choice; one request per bundle for multi-file import.