Studio -> Magazines: library, contents tree grouped by section with
confidence/pages/listening-time/audio status, the Stage D review
screen (tick/untick layout blocks, correct title/author, approve or
reject), a reflowed article view with click-to-seek sentence
highlighting, a linked original-page view showing the source bounding
box, and a podcast panel (2-3 host pickers, script generation,
turn-by-turn playback).
Wired into the existing lazy-module system (module-manifest.json,
loader.js, nav.js) rather than a separate app. Reuses /api/tts-preview
for both narration and podcast turns -- no new audio pipeline.
Fixes found building this:
- The pane toggle used role="tab" without the full ARIA tabs pattern
(aria-selected, roving tabindex, arrow-key nav) -- fails WCAG 2.2 AA
and hid the buttons from role-based tooling. Replaced with a button
group + aria-pressed, matching the plain-click interaction it
actually has.
- Print layout cues ("(continued on page 8)") were being sent to TTS
verbatim -- narration would have spoken the parenthetical aloud.
Stripped from the narration copy only; layout.json keeps the
original for the review screen.
- has_podcast now comes from the articles listing so opening an
article without a generated script skips a doomed GET instead of a
routine 404 on every open.
Verified against the real API contract with Playwright (library,
review pre-selection, approval, sentence highlighting + PDF-page
linking, podcast approval gate, zero console errors across the flow)
and manually end-to-end against a real ingested fixture, screenshots
included. Full suite: 94 Python tests, 33/34 desktop Playwright tests
(one unrelated pre-existing flaky DOM-count assertion).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
3.6 KiB
Markdown
87 lines
3.6 KiB
Markdown
# 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 complete, P1 mostly complete** (frontend reachable at Studio → Magazines). 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` | `magazine.js`, `s-magazine.html` | **P0 + P1** |
|
|
|
|
## 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](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 remainder: article images/page crops shown during playback, a
|
|
cross-article "play all" queue, and OCR-engine dispatch to the KDL/Baidu
|
|
parsers (see architecture/magazine-ingestion.md). Otherwise P2 per TODO.md.
|