tts-voice-creator-clone-and.../PROJECT_MAP.md
mARTin-B78 736aeaaeac feat: add magazine projects with layout-aware article extraction
Magazines break the audiobook pipeline's assumptions: several unrelated
articles share a page, columns run in parallel, adverts and sidebars
interrupt body copy, and a feature routinely jumps twenty pages to its
tail. Narrating page-by-page walks out of an article mid-sentence and
into an advert, so the canonical object is the article, not the page.

core/magazine.py reconstructs articles from a flat PDF: PyMuPDF extracts
blocks with bboxes and font metrics, tesseract supplies a TSV fallback so
scanned pages join the same column grouping, blocks are classified and
ordered column-major, and articles are anchored by the parsed table of
contents with "continued on page N" tails linked back.

routes/magazine.py adds 13 endpoints. Ingestion is a restart-safe
background job. Nothing is narrated until a human approves the
boundaries; podcast generation refuses unapproved articles.

Podcast mode renders an approved article as a 2-3 host discussion using
the configured LLM and voices the user picks, alongside narration rather
than instead of it.

Verified end-to-end over HTTP on a synthetic 8-page issue: the feature
came back as pages [3, 8] with its jump tail absorbed and the advert page
excluded, and an image-only rendering recovered all 47 blocks via OCR.
44 new tests; full suite 90 passing.

The Studio UI is not built — this is reachable only via the API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 12:46:54 +02:00

3.5 KiB

Project map

Living index of where things are and what state they are in. Update in the same commit as the change it describes.

Current phase

Magazine projects, P0 (backend) complete. Audiobook/Rehearser/voice-library features are mature and in daily use.

Deployment

Self-hosted Docker on the DGX Spark, port 7890, image ghcr.io/martin-b78/tts-voice-creator-clone-and-design-2. Python and static files are bind-mounted, so code changes need only a container recreate; rebuild and push only when Dockerfile or requirements.txt change.

Layout

Path Role
server.py FastAPI app, router registration, auth middleware
core/ domain logic — config, database, audio, voice, routing, magazine
routes/ HTTP boundary, one module per feature area
static/js/ frontend, one module per feature area (no build step for most)
tests/ test_*.py (unittest) and *.spec.js (Playwright)
architecture/ SOPs for subsystems whose design is not obvious from code
workflows/ ComfyUI graphs for character portrait generation

Feature areas

Area Server Frontend State
Voice library / clone / design routes/library.py, core/voice.py library.js mature
Read Aloud (books, PDFs) routes/reader.py reader.js mature
Audiobook casting routes/conversation.py audiobook*.js mature
Rehearser routes/rehearsals_db.py rehearser-*.js mature
Studio (phase wizard) — studio.js mature
TTS engines / routing routes/tts.py, core/routing.py engines.js mature
STT routes/stt.py — mature
Magazines core/magazine.py, routes/magazine.py not started P0 backend

Data model

Documents (books and magazines alike) live under <CONFIG_DIR>/reader_library/<doc_id>/, discriminated by meta.json → kind:

  • kind: "pdf" / text — Read Aloud book. Text is extracted in the browser and not persisted; meta.json holds counts and a flat idx resume position.
  • kind: "magazine" — adds layout.json, articles.json, pages/, images/, podcast/, and a per-article progress map. See architecture/magazine-ingestion.md.

Settings, presets, routes and the SQLite database also live under CONFIG_DIR, which is the writable volume.

Environment

Variable Purpose
CONFIG_DIR writable state (settings, library, database)
VOICES_DIR / OUTPUT_DIR voice WAV library
TTS_CONTAINER_NAMES containers restarted after a voice change
DOCKER_HOST points at the socket proxy; the raw socket is not mounted
llm_url / llm_model / llm_api_key settings.json, OpenAI-compatible LLM

Pending decisions

  • Container registry: ghcr costs quota against a private package (≈427 MB per push, 500 MB free on the Free plan). Local registry vs. ghcr vs. no re-pull is unresolved — the compose files currently point at ghcr.
  • Magazine P1 scope: sentence-level highlighting confirmed as the starting point; word-level deferred to P2.

Known limitations

  • gitea backup remote is 157 commits behind origin; main tracks gitea.
  • Magazine sidebar detection is not implemented (P2).
  • No npm run qa aggregate command yet; Python tests run via unittest, browser tests via playwright test.

Next logical step

Magazine P1: magazine overview with the contents tree, the article review screen, reflowed + PDF views, and sentence-highlighted playback.