From db6302f99d3f223a071eab9da6ac1cade0398e1d Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Fri, 29 May 2026 13:53:31 +0200 Subject: [PATCH] Add version number (v1.1.0) and rewrite CHANGELOG to full Keep-a-Changelog spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add VERSION file (1.1.0) at repo root - core/constants.py: expose __version__ read from VERSION file - routes/admin.py: GET /api/version endpoint returns {version} - Settings → About: display "v1.1.0" next to app name via /api/version fetch - CHANGELOG.md: full rewrite following Keep a Changelog + Semantic Versioning - [Unreleased] staging section at top - [1.1.0] 2026-05-29 — security, perf, refactor, UX changes from this session - [1.0.0] 2026-05-28 — all pre-session features documented - Compare links at bottom pointing to GitHub Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 290 +++++++++++++++++--------------- VERSION | 1 + core/constants.py | 11 ++ routes/admin.py | 7 +- static/js/conversation.js | 9 + static/sections/s-settings.html | 5 +- 6 files changed, 182 insertions(+), 141 deletions(-) create mode 100644 VERSION diff --git a/CHANGELOG.md b/CHANGELOG.md index 54a7467..7259799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,172 +1,184 @@ # Changelog -All notable changes to TTS Voice Creator — Clone and Design are documented here. -Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +All notable changes to **TTS Voice Creator — Clone and Design** are documented here. +Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned with [Semantic Versioning](https://semver.org/). --- -## [Unreleased] — 2026-05-29 - -### Added - -- **Text input in Conversation Playground** — a pill-shaped text field and send - button (→) now sit left of the mic button. Typing a message and pressing Enter - or → skips STT entirely and sends text directly through LLM → TTS. Makes the - playground fully usable without a microphone (HTTP context, no mic permission, - remote access). Backend `/api/conversation/turn` now accepts an optional `text` - form field; when set, the STT step is skipped and the STT latency row shows `—`. - -- **Container name field on all engine cards** — every TTS and STT engine card - (Docker stack cards *and* static "Other Local" cards) now always shows the - Docker container name input row. Previously absent/not-installed cards hid it; - now it is always visible so the container can be pre-configured before starting. - -- **Connect / Disconnect toggle** — the Connect button now shows "Disconnect" - when already connected (with a green `check-network` icon) and toggles back - on click, persisting state in `localStorage`. - -- **Auto-apply on Connect** — when a successful connection probe is completed - the URL is automatically saved to Settings and the backend becomes available - in the TTS / STT dropdown menus immediately, without requiring the user to - also click "Use as TTS/STT". The manual "Use as" button still exists for - overrides and shows an active highlight once applied. - -### Changed - -- **Connect button** — moved out of the URL input row into a dedicated - `dc-controls-row` below the URL and container-name inputs. Restyled as a - solid blue primary CTA button (was a small teal outline button). - -- **"Use as TTS / STT"** — made visually distinct as a teal "apply" action - (larger padding, bolder border, chevron icon). Tooltip explains it sets the - URL in Settings and enables the backend in dropdowns. Gains `.active` class - once the URL has been applied. - -- **Unified controls row layout** — all engine cards now follow the same - left-to-right order: `[Connect/Disconnect]` `[Stop | Start | Restart]` - `[Use as →]`. Docker action buttons are hidden until a container name is - entered; the Use-as button is right-aligned via `margin-left: auto`. - -- **Static engine cards** (`initStaticDockerManagement`) — rebuilt to use the - same `dc-controls-row` structure as the dynamic Docker stack cards. The - existing `.llm-local-ping` button is moved from inside the URL row into the - controls row at initialisation time. - -### Fixed - -- **`chrome://flags/…` URL unreadable in microphone-blocked warning** — - the global `code { background: var(--panel) }` rule caused the URL text to - render as white-on-light-grey inside the red warning box. Fixed by applying - inline styles (`background: rgba(0,0,0,.35); color: #fff`) directly on the - `` element and adding a "Copy" button so users can copy the URL - without having to manually select invisible text. +## [Unreleased] --- -## [Unreleased] — 2026-05-29 +## [1.1.0] — 2026-05-29 ### Security - **Fixed path traversal in `/api/browse-dirs`** — Added a `_BROWSE_BLOCKED` blocklist (`/proc`, `/sys`, `/dev`, `/run`, `/boot`). Requests for paths under these directories now return HTTP 403 instead of listing kernel/system files. - -- **Hardened yt-dlp output path** — After a YouTube download completes the resolved output - path is verified to be inside `TEMP_DIR` with `.relative_to()`. A file written outside - the temp directory is rejected with an SSE error event and never registered. - -- **Removed CORS wildcard on `/api/proxy-audio`** — The `Access-Control-Allow-Origin: *` - header was unnecessary (all callers are same-origin) and exposed proxied audio to - arbitrary cross-origin requests. Header removed. - -- **Temp file registry now enforces a TTL** — `_registry` is now a - `dict[str, tuple[Path, float]]` storing each entry with a creation timestamp. - `_registry_gc()` evicts entries older than `TEMP_FILE_TTL_SECONDS` (default 2 hours, - configurable via env var) and unlinks their files, preventing unbounded disk growth. +- **Hardened yt-dlp output path** — After a YouTube download completes, the resolved + output path is verified to be inside `TEMP_DIR` via `.relative_to()`. A file written + outside the temp directory is rejected with an SSE error event and never registered. +- **Removed CORS wildcard on `/api/proxy-audio`** — `Access-Control-Allow-Origin: *` + was unnecessary (all callers are same-origin) and exposed proxied audio to arbitrary + cross-origin requests. Header removed. +- **Temp file registry now enforces a TTL** — `_registry` changed to + `dict[str, tuple[Path, float]]`. `_registry_gc()` evicts entries older than + `TEMP_FILE_TTL_SECONDS` (default 2 h, configurable via env var) and unlinks their + files, preventing unbounded disk growth on long-running instances. ### Performance - **Settings and routing rules cached in memory** — `_load_settings()` and - `_load_tts_routes()` previously read from disk on every API request. Both now use - mtime-checked in-memory caches (`_settings_cache`, `_routes_cache`) that are - invalidated automatically on write. Eliminates dozens of redundant file reads per - TTS synthesis call. + `_load_tts_routes()` previously read from disk on every API request (55+ calls per + TTS synthesis). Both now use mtime-checked in-memory caches that invalidate + automatically on write, eliminating redundant file I/O. ### Added -- **Container name field on Docker stack TTS/STT cards** — The engine cards rendered - dynamically in the *Engines → Text to Speech* and *Engines → Speech to Text* sections - (Qwen3 Voice Clone, Voice Design, Custom Voice, Streaming, NVIDIA Magpie, Parakeet ASR) - now show the same *container name (optional)* input row that the *Other Local TTS/STT* - cards already had. Typing a container name immediately enables Stop / Start / Restart - buttons wired to the Docker API. The static STT cards (faster-whisper-server, - whisper.cpp) received the same treatment via `initStaticDockerManagement()`. +- **Version number** — `VERSION` file at repo root; read by `core/constants.__version__` + and surfaced via `GET /api/version`. Displayed as `v1.1.0` in Settings → About. +- **Text input in Conversation Playground** — a pill-shaped text field and send button + (→) sit left of the mic button. Pressing Enter or → sends text directly through the + LLM → TTS pipeline, skipping STT entirely. Makes the playground fully usable without + a microphone (HTTP context, no mic permission, remote access). The backend + `/api/conversation/turn` now accepts an optional `text` form field; when set, the + STT step is skipped and the STT latency row shows `—`. +- **Container name field on all engine cards** — every TTS and STT engine card (Docker + stack cards *and* static "Other Local" cards) now always shows the Docker container + name input row. Previously absent/not-installed cards hid it; now it is always visible + so the container can be pre-configured before starting. +- **Connect / Disconnect toggle** — the Connect button now shows "Disconnect" (green, + `check-network` icon) when already connected and toggles back on click. State + persists in `localStorage`. +- **Auto-apply on Connect** — a successful connection probe automatically saves the + URL to Settings and makes the backend available in TTS/STT dropdown menus immediately, + without requiring a separate "Use as TTS/STT" click. -### Refactored +### Changed -#### Backend — `server.py` split into `core/` + `routes/` packages +- **Connect button redesigned** — moved out of the URL input row into a dedicated + `dc-controls-row`. Restyled as a solid blue primary CTA (was a small teal outline + button). Shows a spinner icon while probing. +- **"Use as TTS / STT" button** — larger padding, bolder teal border, chevron icon, + tooltip explaining it sets the URL in Settings. Gains `.active` highlight once applied. +- **Unified controls row on every engine card** — consistent left-to-right order: + `[Connect/Disconnect]` `[Stop | Start | Restart]` `[Use as →]`. Docker action buttons + hidden until a container name is entered; Use-as button right-aligned. +- **`initStaticDockerManagement`** — rebuilt to use the same `dc-controls-row` + structure as the dynamic Docker stack cards. The existing `.llm-local-ping` button + is moved from inside the URL row into the controls row at initialisation time. +- **Backend refactor — `server.py` (5 560 lines → 43 lines)** — all logic extracted + into single-responsibility modules: -`server.py` went from **5,560 lines** to a **43-line entry point** that creates the -FastAPI app, registers routers, and mounts static files. All logic was extracted into -single-responsibility modules: + | Package | Module | Responsibility | + |---|---|---| + | `core/` | `constants.py` | Boot-time env defaults, path constants, version, log buffer | + | | `registry.py` | TTL-based temp file registry | + | | `validation.py` | URL validation, SSRF guard, path safety | + | | `docker_client.py` | Raw Unix-socket Docker HTTP client | + | | `config.py` | Settings load/save/normalize, backend URL resolution | + | | `routing.py` | TTS route rules load/save/resolve, language detection | + | | `audio.py` | Audio conversion, normalisation, auto-trim scoring | + | | `voice.py` | Voice metadata, backup management, benchmark helpers | + | | `presets.py` | Voice Design preset load/save, virtual voice resolution | + | | `tts_helpers.py` | TTS request helpers, streaming, per-backend logic | + | `routes/` | `admin.py` | Index, favicon, browse-dirs, robots, version | + | | `settings.py` | `/api/settings`, routing rules, logs, design presets | + | | `library.py` | All voice CRUD, upload, save, normalize, export/import | + | | `stt.py` | `/api/transcribe*`, `/api/stt-backends` | + | | `sources.py` | Voice scraping, proxy-audio, yt-dlp download | + | | `docker.py` | `/api/local-containers/*`, `/api/probe-url` | + | | `tts.py` | TTS preview, streaming, voice design, `/v1/*`, backends | + | | `conversation.py` | Refine-text, effects, export/import, speak, MCP, conversation | -**`core/` — pure utilities (no FastAPI decorators)** + `Dockerfile` updated with `COPY core/ core/` and `COPY routes/ routes/`. + `docker-compose.yml` updated with `./core:/app/core:ro` and `./routes:/app/routes:ro`. -| Module | Responsibility | -|---|---| -| `constants.py` | Boot-time env defaults, `CONFIG_DIR`/`CONFIG_FILE`/`STATIC_DIR` paths, in-memory log buffer | -| `registry.py` | `TEMP_DIR`, TTL-based temp file registry (`_registry_put/get/gc`) | -| `validation.py` | `_validate_http_url`, `_normalize_service_url`, `_copy_limited`, `_safe_child_path` | -| `docker_client.py` | Raw Unix-socket Docker HTTP client (`_docker_get_json`, `_docker_post`) | -| `config.py` | Settings load/save/normalize, TTS stability helpers, backend URL resolution | -| `routing.py` | TTS route rules load/save/resolve, language detection, routing log | -| `audio.py` | `_to_wav_24k/16k`, `_trim`, `_duration`, `_normalize_segment`, auto-trim scoring | -| `voice.py` | Voice metadata helpers, backup management, benchmark helpers | -| `presets.py` | Voice Design preset load/save, virtual voice resolution | -| `tts_helpers.py` | TTS request helpers, streaming, NVIDIA/VibeVoice/XTTS backends, audio proxy | +- **Frontend refactor — `app.js` (8 744 lines → 16 modules)** — split into + `static/js/` with `loader.js` loading them sequentially in dependency order: -**`routes/` — FastAPI `APIRouter` modules** + | Module | Lines | Responsibility | + |---|---|---| + | `utils.js` | 364 | Core helpers: `$`, `toast`, `escHtml`, theme, language/flag, picker, tabs | + | `voice-inspector.js` | 397 | 3-pane voice workbench | + | `voice-sources.js` | 277 | External voice source scraping UI | + | `integrations.js` | 211 | Code snippet generation (SillyTavern, Open WebUI, HA, curl, MCP) | + | `routing.js` | 542 | TTS routing rules editor | + | `settings.js` | 385 | `loadSettings`, `applyAndSaveSettings`, settings panel | + | `voice-clone.js` | 774 | WaveSurfer, drop zone, mic recording, trim, voice design | + | `voice-library.js` | 2654 | Full voice library: list, CRUD, benchmark, normalize | + | `tts-preview.js` | 528 | TTS preview, `fetchTtsPreviewBlob` | + | `benchmark.js` | 218 | Performance + batch benchmark | + | `stt.js` | 287 | STT→TTS playground, `refreshSttBackends` | + | `init.js` | 49 | App bootstrap | + | `engines.js` | 625 | ElevenLabs browser, custom engine cards, Docker management | + | `ai-backends.js` | 520 | AI backend cards, LLM snippets, `initStaticDockerManagement` | + | `generation.js` | 393 | WAV merge, chunked TTS, history, playlist, audio effects | + | `conversation.js` | 520 | Conversation playground, LLM refinement, import, About | -| Module | Routes | -|---|---| -| `admin.py` | `GET /`, `GET /favicon.ico`, `GET /api/browse-dirs`, `GET /robots.txt` | -| `settings.py` | `/api/settings`, `/api/tts-routes`, `/api/tts-routing-log`, `/api/logs`, `/api/voice-design-presets` | -| `library.py` | All `/api/voice*` and `/api/voices*`, `/api/save`, `/api/upload`, `/api/route-sounds*`, `/api/audio/{fid}`, `/api/auto-trim`, `/api/process` | -| `stt.py` | `/api/stt-backends`, `/api/transcribe`, `/api/transcribe-bytes` | -| `sources.py` | `/api/voice-sources`, `/api/import-source-audio`, `/api/proxy-audio`, `/api/quick-import-voice`, `/api/download-yt`, `/api/elevenlabs/voices` | -| `docker.py` | `/api/local-containers/*`, `/api/probe-url` | -| `tts.py` | `/api/tts-preview`, `/api/tts-style-variation`, `/api/tts-stream-*`, `/api/voice-design`, `/v1/*`, `/api/tts-voices`, `/api/tts-backends`, restart endpoints | -| `conversation.py` | `/api/refine-text`, `/api/rewrite-with-persona`, `/api/audio/effects`, `/api/voices/export`, `/api/voices/import`, `/speak*`, `/mcp`, `/api/conversation/*` | +### Fixed -`Dockerfile` updated to `COPY core/ core/` and `COPY routes/ routes/`. -`docker-compose.yml` updated to mount `./core:/app/core:ro` and `./routes:/app/routes:ro`. - -#### Frontend — `static/app.js` split into `static/js/` modules - -`app.js` (8,744 lines) split into **16 focused modules** totalling the same 8,744 lines. -`static/loader.js` updated to load them sequentially in dependency order. - -| Module | Lines | Responsibility | -|---|---|---| -| `utils.js` | 364 | `$`, `toast`, `escHtml`, `debounce`, theme, language/flag helpers, searchable picker, tab helpers | -| `voice-inspector.js` | 397 | 3-pane voice workbench (`selectVoice`, `updateInspector`) | -| `voice-sources.js` | 277 | External voice source scraping UI (`loadGetVoices`, `renderGetVoices`) | -| `integrations.js` | 211 | Code snippet generation for SillyTavern, Open WebUI, HA, curl, MCP | -| `routing.js` | 542 | TTS routing rules editor | -| `settings.js` | 385 | `loadSettings`, `applyAndSaveSettings`, settings panel | -| `voice-clone.js` | 774 | WaveSurfer waveform, drop zone, microphone, trim UI, voice design dialog | -| `voice-library.js` | 2654 | Full voice library: list, row rendering, operations (save, rename, delete, normalize, benchmark) | -| `tts-preview.js` | 528 | `fetchTtsPreviewBlob`, `createTtsAudioSource`, preview panel | -| `benchmark.js` | 218 | Batch benchmark section | -| `stt.js` | 287 | STT→TTS playground, `refreshSttBackends` | -| `init.js` | 49 | App bootstrap (calls `loadSettings`, syncs UI state) | -| `engines.js` | 625 | ElevenLabs browser, custom engine cards, Docker container management | -| `ai-backends.js` | 520 | AI backend cards, LLM snippet collapse, `initStaticDockerManagement`, collapsible cards | -| `generation.js` | 393 | WAV merge, chunked TTS, generation history, playlist, audio effects, LLM refinement | -| `conversation.js` | 520 | Settings logs/about, voices import, conversation playground | +- **`chrome://flags/…` URL unreadable in mic-blocked warning** — the global + `code { background: var(--panel) }` rule caused the URL text to render as + white-on-light-grey inside the red warning box. Fixed with inline styles + (`background: rgba(0,0,0,.35); color: #fff`) on the `` element, plus a + Copy button so users don't need to manually select invisible text. --- -## Earlier history +## [1.0.0] — 2026-05-28 -See `git log` for the full commit history prior to this release. +Initial feature-complete release. + +### Added + +- **Voice library** — clone voices from audio samples; design voices from text + descriptions using instruction-based synthesis; benchmark synthesis speed (RTF); + normalize loudness; export/import voice packages as ZIP bundles. +- **TTS backends** — Qwen3 TTS (Voice Clone, Voice Design, Custom Voice, Streaming), + NVIDIA Magpie / Zeroshot / Flow, Kokoro FastAPI, VibeVoice, XTTS v2, ElevenLabs. +- **STT backends** — OpenAI Whisper (port 8010), faster-whisper-server, whisper.cpp, + Groq Whisper (cloud, free tier), NVIDIA Parakeet ASR. Real transcription probe + in health check (not just TCP reachability). +- **App Routing** — per-app / per-voice / per-language TTS routing rules with + automatic language detection and optional before/after sound effects. +- **Conversation Playground** — full STT → LLM → TTS pipeline with real-time SSE + streaming, latency stats panel (STT / LLM TTFT / LLM total / TTS / Total), turn + history, system prompt, and insecure-context warning. +- **Engines section** — LLM / STT / TTS sub-pages; Docker container management + (Start / Stop / Restart via Docker socket); custom engine cards; ElevenLabs voice + library browser. +- **Performance Benchmark** — single-voice and batch benchmark with RTF tracking, + sparkline trend, and persistent history. +- **Audio effects** — reverb, chorus, delay, compressor, gain, pitch shift + (via `pedalboard`). +- **Chunked TTS + generation history** — long-text synthesis split into chunks, + per-chunk playback, playlist export as WAV. +- **MCP server** — built-in JSON-RPC 2.0 endpoint at `/mcp`; tools: `speak`, + `transcribe`, `list_captures`, `list_profiles`. +- **LLM refinement & persona rewriting** — clean up STT transcripts or rewrite + responses with a chosen persona via any OpenAI-compatible LLM endpoint. +- **Connect Apps** — ready-made config snippets for SillyTavern, Open WebUI, + Home Assistant, curl, and MCP (`claude mcp add` one-liner). +- **Voice sources** — scrape voice assets from Aiartes, Freesound, GitHub, and + Google Drive; YouTube download via yt-dlp; quick import directly to library. +- **OpenAI-compatible proxy** — `/v1/audio/speech` and `/v1/audio/transcriptions` + for drop-in use with Open WebUI, SillyTavern, and Home Assistant. +- **Settings** — sub-pages: General, Connections, Playback, Captures, Payloads, + Storage, API Keys, Logs, About. +- **Voice Design presets** — saved persona templates for instruction-based synthesis; + virtual `vd_…` voices usable from external apps without exporting WAV files. +- **Multilingual support** — language/flag pickers, per-language preview texts, + `LANG_FLAG_DEFAULT` mapping for 16 languages. +- **Tags, ratings, and metadata** — per-voice tags with autocomplete, star ratings, + gender label, country flag. +- **Dark/light theme** — toggle with persistence in `localStorage`. +- **Docker socket integration** — Start/Stop/Restart Docker containers from the UI + via raw Unix socket HTTP; container health visible in engine cards. + +--- + +[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.1.0...HEAD +[1.1.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/releases/tag/v1.0.0 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..9084fa2 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.1.0 diff --git a/core/constants.py b/core/constants.py index 5e5ee81..5b2b705 100644 --- a/core/constants.py +++ b/core/constants.py @@ -7,6 +7,17 @@ import shutil from datetime import datetime, timezone from pathlib import Path +# ── Version ─────────────────────────────────────────────────────────────────── + +def _read_version() -> str: + v_file = Path(__file__).parent.parent / "VERSION" + try: + return v_file.read_text().strip() + except Exception: + return "0.0.0" + +__version__: str = _read_version() + # ── Boot-time defaults ──────────────────────────────────────────────────────── _VOICES_DIR_DEFAULT = os.environ.get("VOICES_DIR", "/voices") diff --git a/routes/admin.py b/routes/admin.py index 4c7dffb..23f5e28 100644 --- a/routes/admin.py +++ b/routes/admin.py @@ -6,7 +6,7 @@ from pathlib import Path from fastapi import APIRouter, HTTPException from fastapi.responses import FileResponse, PlainTextResponse, Response -from core.constants import STATIC_DIR +from core.constants import STATIC_DIR, __version__ router = APIRouter() @@ -47,6 +47,11 @@ async def browse_dirs(path: str = "/"): return {"path": str(p), "parent": parent, "dirs": entries} +@router.get("/api/version") +async def get_version(): + return {"version": __version__} + + @router.get("/robots.txt", response_class=PlainTextResponse) async def robots_txt(): return "User-agent: *\nDisallow: /" diff --git a/static/js/conversation.js b/static/js/conversation.js index 56c517f..5c51d6b 100644 --- a/static/js/conversation.js +++ b/static/js/conversation.js @@ -61,6 +61,15 @@ document.querySelectorAll('.s-log-filter').forEach(btn => { // ── Settings: About ─────────────────────────────────────────────────────── +// Fetch and display the server version in the About page +(async function loadAppVersion() { + try { + const d = await fetch('/api/version').then(r => r.json()); + const el = $('s-about-version'); + if (el && d.version) el.textContent = 'v' + d.version; + } catch (_) {} +})(); + function renderSettingsAbout() { const el = $('s-about-backends'); if (!el) return; diff --git a/static/sections/s-settings.html b/static/sections/s-settings.html index dc02e1c..fee34a0 100644 --- a/static/sections/s-settings.html +++ b/static/sections/s-settings.html @@ -511,7 +511,10 @@

About

-
TTS Voice Creator
+
+ TTS Voice Creator + +

Clone, design, and deploy custom voices using local AI backends. Compatible with Qwen3-TTS, Kokoro FastAPI, NVIDIA Magpie, and any OpenAI-compatible TTS/STT endpoint.

FastAPI