No description
  • Python 98.5%
  • Shell 0.6%
  • Dockerfile 0.5%
  • PLSQL 0.4%
Find a file
florian faccb9b074
Some checks failed
ghcr-images / build-and-push (push) Failing after 11s
Merge pull request 'chore: Configure Renovate' (#33) from renovate/configure into main
Reviewed-on: #33
2026-06-14 02:50:46 +02:00
.github/workflows ci: build and push docker images to GHCR (#17) 2026-03-14 16:56:34 +01:00
app docs: add OpenAPI metadata (title, version, description, contact) (#32) 2026-03-16 11:39:01 +01:00
architecture arch: add text field to job schema; research: Slice 2 D1-D3, F1, G1-G4 2026-03-14 12:04:50 +01:00
caddy docs(caddy): make default Caddyfile Traefik-ready (http only) (#19) 2026-03-14 17:03:39 +01:00
docs docs: clarify deployment URL, auth token, mp3 output (#31) 2026-03-14 21:49:39 +01:00
pronunciation-dictionaries feat: ElevenLabs pronunciation dictionaries (#15) 2026-03-14 16:49:58 +01:00
scripts chore: initial project scaffold + architecture + tasks 2026-03-14 11:39:17 +01:00
tasks docs(tasks): switch TTS-G3 from nginx to caddy (#10) 2026-03-14 15:58:52 +01:00
tests chore: archive old pronunciation dictionaries after upload (#27) 2026-03-14 21:10:11 +01:00
.dockerignore chore: quality gates — ruff, coverage ≥80%, pre-commit (#4) 2026-03-14 15:24:43 +01:00
.env.example chore(elevenlabs): set default voice id + env override (#16) 2026-03-14 16:53:15 +01:00
.gitignore feat(TTS-A1/A2/A3/B1/B4): config, db, storage, synthesize route, auth middleware (#1) 2026-03-14 15:18:34 +01:00
.pre-commit-config.yaml chore: quality gates — ruff, coverage ≥80%, pre-commit (#4) 2026-03-14 15:24:43 +01:00
config.yaml feat(auth): support multiple consumer API keys from config.yaml (#22) 2026-03-14 17:48:30 +01:00
docker-compose.production.yml fix(docker): make worker healthcheck YAML-safe (CMD-SHELL) (#28) 2026-03-14 21:27:41 +01:00
docker-compose.yml fix(docker): make worker healthcheck YAML-safe (CMD-SHELL) (#28) 2026-03-14 21:27:41 +01:00
docker-entrypoint.sh chore: quality gates — ruff, coverage ≥80%, pre-commit (#4) 2026-03-14 15:24:43 +01:00
Dockerfile chore: quality gates — ruff, coverage ≥80%, pre-commit (#4) 2026-03-14 15:24:43 +01:00
pyproject.toml Implement F1/F2 backend stubs and B3 admin voices route (#6) 2026-03-14 15:47:50 +01:00
pytest.ini feat: implement D1 ElevenLabs backend, C1 queue, C2 worker, E1 router, E2 fallback (#2) 2026-03-14 15:25:37 +01:00
README.md docs: clarify deployment URL, auth token, mp3 output (#31) 2026-03-14 21:49:39 +01:00
REFINEMENT_REPORT_F1_F2.md chore: add refinement report and gitignore 2026-03-14 12:41:09 +01:00
renovate.json Add renovate.json 2026-06-14 00:50:13 +00:00
requirements.txt feat(B2/C3/G1/G2): jobs route, webhook dispatcher, Dockerfile, docker-compose (#3) 2026-03-14 15:26:37 +01:00

🔊 tts-service

Default deployment: https://tts.services.beisel.it

Python FastAPI ElevenLabs SQLite Docker License Coverage

Text rein → Job-ID + Audio-URL zurück → Audio erscheint dort wenn fertig.
Asynchroner TTS-Webservice für deutsche Sprachsynthese. Wir sind kein CDN — wir synthetisieren einmal, der Consumer cached selbst.


Features

  • 🚀 Promise-URL-Modell — sofortige Antwort mit deterministischer Audio-URL, keine Wartezeit
  • 🔄 Async Queue — SQLite-basierte Job-Queue mit Worker-Loop, horizontal skalierbar
  • 🎙️ Multi-Backend — ElevenLabs primär, Azure/Polly als Fallback-Kette
  • 🔐 API-Key AuthX-API-Key Header auf allen Endpunkten (außer /health)
  • 🪝 Webhooks — optionaler Callback nach Fertigstellung mit Retry-Logik
  • 📦 Docker-readydocker-compose up und fertig
  • 🧪 Testabdeckung ≥80% — pytest + coverage als pre-commit Gates

🏗️ Architektur

Consumer
  │
  ├─ POST /synthesize ──→ Job anlegen → 201 + audio_url (pending)
  │
  ├─ GET  /jobs/{id}  ──→ Status polling
  │
  └─ Webhook callback ──→ Push wenn fertig
          │
          ▼
    ┌─────────────┐     ┌─────────────┐     ┌──────────────────┐
    │  FastAPI API │────▶│ SQLite Queue│────▶│  Worker Process  │
    └─────────────┘     └─────────────┘     └──────┬───────────┘
                                                    │
                                          ┌─────────▼─────────┐
                                          │  Backend Router    │
                                          │  ElevenLabs → ...  │
                                          └─────────┬──────────┘
                                                    │
                                          ┌─────────▼──────────┐
                                          │  Local Storage      │
                                          │  /data/audio/{hash} │
                                          └────────────────────┘

Vollständige Architektur-Doku: architecture/ARCHITECTURE.md


🚀 Quickstart

Mit Docker Compose

cp .env.example .env
# .env befüllen (ELEVENLABS_API_KEY, TTS_API_KEY, etc.)

docker-compose up -d

API läuft auf http://localhost:8080.

Lokal (Dev)

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# Config anpassen
cp .env.example .env
cp config.yaml config.local.yaml

uvicorn app.main:app --reload

📡 API

Audio output: MP3 (ElevenLabs mp3_44100_128).

Pronunciation overrides (per token)

  • CRUD: GET/POST/PUT/DELETE /admin/pronunciations
  • Changes are synced to ElevenLabs automatically on the next /synthesize for that token.

POST /synthesize

POST /synthesize
X-API-Key: <YOUR_TTS_TOKEN>
Content-Type: application/json

{
  "text": "Hallo Welt, das ist ein Test.",
  "article_id": "article-123",        // optional, für Idempotenz
  "voice": "de-default",              // optional
  "backend": "elevenlabs",            // optional
  "webhook_url": "https://..."        // optional
}

Response 201 (neu):

{
  "job_id": "tts_7f3a2b1c",
  "status": "pending",
  "audio_url": "https://tts.service/audio/7f3a/7f3a2b1c...mp3",
  "poll_url": "https://tts.service/jobs/tts_7f3a2b1c",
  "cached": false
}

Response 200 (bereits vorhanden):

{
  "job_id": "tts_7f3a2b1c",
  "status": "done",
  "audio_url": "https://tts.service/audio/7f3a/7f3a2b1c...mp3",
  "cached": true
}

GET /jobs/{job_id}

GET /jobs/tts_7f3a2b1c
X-API-Key: <YOUR_TTS_TOKEN>
{
  "job_id": "tts_7f3a2b1c",
  "status": "done",
  "audio_url": "https://...",
  "duration_seconds": 4.2,
  "chars_processed": 29,
  "backend_used": "elevenlabs",
  "created_at": "2026-03-14T12:00:00Z",
  "completed_at": "2026-03-14T12:00:05Z"
}

GET /health

{"status": "healthy"}

⚙️ Konfiguration

Alle Werte via config.yaml + .env (Env überschreibt YAML):

Auth tokens können als api_keys: Liste in config.yaml gepflegt werden (alle gleichberechtigt).

Variable Beschreibung Pflicht
TTS_API_KEY API-Key(s) für Auth (single or list in config.yaml)
ELEVENLABS_API_KEY ElevenLabs Credentials
TTS_STORAGE__PUBLIC_BASE_URL Basis-URL für Audio-URLs
TTS_SQLITE_PATH Pfad zur jobs.db (default: /data/jobs.db)
TTS_WORKER__POLLING_INTERVAL_SECONDS Polling-Intervall in Sekunden (default: 2)

Vollständige Liste: .env.example


🧪 Tests & Quality Gates

# Tests mit Coverage
pytest

# Linter
ruff check app/ tests/
ruff format app/ tests/

# Pre-commit installieren (einmalig)
pre-commit install

Pre-commit Hooks (laufen automatisch vor jedem Commit/Push):

  • ruff — Lint + Format
  • pytest-cov — Tests + Coverage ≥80%
  • branch-name — Convention (feature|hotfix|chore|release)/<name>-<num>-<desc>

🏢 Stack

Komponente Technologie
API FastAPI 0.115
Queue SQLite (WAL-Modus)
TTS Primary ElevenLabs eleven_multilingual_v2
TTS Fallback Azure TTS / Amazon Polly (Stubs)
Storage Lokales Filesystem + Caddy (static /audio)
Worker Python polling worker (sqlite queue)
Container Docker + Compose
Tests pytest + pytest-cov + respx
Lint ruff

📁 Projektstruktur

tts-service/
├── app/
│   ├── api/          # FastAPI Routes (synthesize, jobs, admin)
│   ├── backends/     # TTS Backend Implementierungen
│   ├── db/           # SQLite Schema + CRUD
│   ├── middleware/   # Auth Middleware
│   ├── storage/      # Local File Storage
│   └── worker/       # Job Queue + Worker Loop + Webhook
├── tests/            # pytest Tests (≥80% Coverage)
├── architecture/     # Architektur-Dokumentation
├── tasks/todo/       # Task-Definitionen (TTS-A1 bis TTS-H2)
├── config.yaml       # Default-Konfiguration
├── .env.example      # Secrets-Template
├── Dockerfile        # Multi-stage Build
└── docker-compose.yml

📖 Docs


Built with 🤖 + by beisel-it

📦 Deployment

  • Local E2E stack: docker-compose.yml (includes Caddy on :8080, API on :8081)
  • Production stack: docker-compose.production.yml (pulls GHCR images, Caddy on :80/:443)
  • Container images (CI): ghcr.io/beisel-it/tts-service/tts-service-api and .../tts-service-worker

Consumer guide: docs/CONSUMER_IMPLEMENTATION_GUIDE.md