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

1.9 KiB

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.