tts-voice-creator-clone-and.../static/sections/s-reader.html
mARTin-B78 36437478a6 feat: Read Aloud reader, multi-voice audiobook casting, character sheets (v1.7.0)
Read Aloud (new "Vorlesen" tab):
- PDF (real page render + overlay highlight) / TXT reader with live word
  highlighting, voice + speed, per-sentence synthesis-state colours, zoom
  (fit-width/height, two-page, ±), resume, and a server-side book library
  (syncs across devices; per-unit MP3 audio fetched on demand).

Book -> multi-speaker audiobook:
- "Cast as audiobook" attributes dialogue to characters via the LLM
  (guillemet/quote-style aware, turn-taking, recent-context), with a
  deterministic speech-tag fallback. Editable preview, non-blocking live
  casting panel, then auto-saved as a reopenable Script Rehearser play.
- Audiobook export: synthesise every cast line -> one MP3 per chapter.

Character sheets:
- LLM-extracted, self-filling RPG-style sheets (with page+quote sources)
  in both Read Aloud and the Rehearser.

Also: MP3 storage + per-page/sentence export, voice-library "Precompute
embeddings" pre-warm, German "Vorlesen" i18n + flag language toggle,
large-PDF performance (lazy raster, buffer/canvas eviction, yielded parse),
and the Seed Finder changelog entry.

New: routes/reader.py, POST /api/attribute-dialogue, POST /api/character-sheets,
static/js/{reader,audiobook,character-sheets}.js, static/sections/s-reader.html.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 15:08:46 +02:00

148 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div class="section-head">
<span class="section-icon"><span class="mdi mdi-book-open-page-variant-outline"></span></span>
<div class="section-title">
<h2>Read Aloud</h2>
<p>Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.</p>
</div>
</div>
<div class="tab-content" id="tab-reader">
<!-- ① Import + controls ─────────────────────────────────────────── -->
<div class="card">
<div class="reader-controls">
<div class="field reader-ctl">
<label>Backend</label>
<select id="reader-backend-select" aria-label="TTS backend"><option value="">Checking backends…</option></select>
</div>
<div class="field reader-ctl">
<label>Voice</label>
<div class="reader-voice-pick">
<select id="reader-voice-select" aria-label="Voice"><option value="">— fetch or pick a voice —</option></select>
<button class="btn-secondary btn-sm" id="reader-fetch-voices-btn" title="Fetch available voices"><span class="mdi mdi-refresh"></span></button>
</div>
</div>
<div class="field reader-ctl">
<label>Speed <span id="reader-speed-label" class="reader-speed-label">1.0×</span></label>
<input type="range" id="reader-speed" min="0.5" max="2" step="0.05" value="1" aria-label="Reading speed">
</div>
<div class="field reader-ctl">
<label>Voice consistency</label>
<select id="reader-chunk-mode" title="Synthesise larger chunks so the voice stays steady across a passage">
<option value="sentence">Per sentence (responsive)</option>
<option value="paragraph">Per paragraph (steadier)</option>
<option value="page">Per page (steadiest)</option>
</select>
</div>
<div class="field reader-ctl reader-ctl-grow">
<label>Tone / style <span class="note">(optional)</span></label>
<input type="text" id="reader-instruct" placeholder="e.g. calm, warm narration" autocomplete="off">
</div>
<div class="field reader-ctl reader-ctl-sm">
<label>Seed <span class="note">(blank = auto)</span></label>
<input type="number" id="reader-seed" placeholder="auto" autocomplete="off">
</div>
<div class="field reader-ctl reader-ctl-sm">
<label>Temperature <span class="note">(01)</span></label>
<input type="number" id="reader-temp" min="0" max="1.5" step="0.05" placeholder="default" autocomplete="off">
</div>
<label class="reader-ctl reader-norm-toggle" title="Even out loudness differences between chunks during playback">
<input type="checkbox" id="reader-normalize" checked> Normalise loudness
</label>
<label class="btn-primary reader-import-btn" title="Import a PDF or text file">
<span class="mdi mdi-file-upload-outline"></span> Import
<input type="file" id="reader-file-input" accept=".pdf,.txt,.md" style="display:none">
</label>
</div>
<div class="reader-backend-hint" id="reader-backend-hint"></div>
</div>
<!-- Saved books library ─────────────────────────────────────────── -->
<div class="card reader-library-card" id="reader-library-card" hidden>
<div class="reader-lib-head">
<h2 style="margin:0"><span class="mdi mdi-bookshelf"></span> My books</h2>
<span class="note">Saved documents with their synthesised audio — reopen to continue.</span>
</div>
<div class="reader-lib-list" id="reader-lib-list"></div>
</div>
<!-- ② Drag & drop / empty state ─────────────────────────────────── -->
<div class="reader-dropzone" id="reader-dropzone">
<span class="mdi mdi-cloud-upload-outline reader-dropzone-icon"></span>
<strong>Drag &amp; drop a document here</strong>
<span class="note">.pdf · .txt · .md — or use the Import button above</span>
</div>
<!-- ③ Transport ─────────────────────────────────────────────────── -->
<div class="reader-transport" id="reader-transport" hidden>
<div class="reader-tb-group">
<button class="reh-tb-btn" id="reader-prev" title="Previous sentence"><span class="mdi mdi-skip-previous"></span></button>
<button class="reh-tb-btn reh-tb-play" id="reader-play" title="Play / pause"><span class="mdi mdi-play"></span></button>
<button class="reh-tb-btn" id="reader-stop" title="Stop"><span class="mdi mdi-stop"></span></button>
<button class="reh-tb-btn" id="reader-next" title="Next sentence"><span class="mdi mdi-skip-next"></span></button>
</div>
<div class="reader-progress-wrap">
<div class="reader-progress-track"><div class="reader-progress-fill" id="reader-progress-fill"></div></div>
<span class="reader-progress-label" id="reader-progress-label">0 / 0</span>
</div>
<span class="reader-doc-title" id="reader-doc-title"></span>
</div>
<!-- ④ Synthesise-ahead bar ──────────────────────────────────────── -->
<div class="reader-synthbar" id="reader-synthbar" hidden>
<button class="btn-primary btn-sm" id="reader-synth-all" title="Pre-synthesise the chosen range for gap-free reading">
<span class="mdi mdi-lightning-bolt"></span> Synthesise <span id="reader-synth-scope">all</span>
</button>
<span class="reader-page-range" id="reader-page-range" hidden>
<span class="note">pages</span>
<input type="number" id="reader-page-from" min="1" value="1" class="reader-page-inp" aria-label="From page">
<span class="note"></span>
<input type="number" id="reader-page-to" min="1" value="1" class="reader-page-inp" aria-label="To page">
</span>
<button class="btn-secondary btn-sm" id="reader-select-toggle" title="Click sentences to mark a start and an end"><span class="mdi mdi-cursor-default-click-outline" id="reader-select-icon"></span> <span id="reader-select-label">Select range</span></button>
<span class="reader-sel-hint" id="reader-sel-hint" hidden></span>
<span class="reader-sel-info" id="reader-sel-info" hidden>
<span id="reader-sel-text">selection</span>
<button class="reader-sel-clear" id="reader-sel-clear" title="Clear selection"></button>
</span>
<button class="btn-secondary btn-sm" id="reader-save-lib" title="Save this document + its synthesised audio to your library"><span class="mdi mdi-content-save-outline"></span> Save to library</button>
<button class="btn-secondary btn-sm" id="reader-audiobook-btn" title="Let an LLM detect characters &amp; dialogue in this scope, then open it in the Script Rehearser to cast a voice per character"><span class="mdi mdi-drama-masks"></span> Cast as audiobook</button>
<button class="btn-secondary btn-sm" id="reader-charsheets-btn" title="Extract actor-facing RPG-style character sheets (with page + quote sources) from this scope"><span class="mdi mdi-account-details-outline"></span> Character sheets</button>
<span class="reader-export">
<select id="reader-export-mode" class="reader-page-inp" style="width:auto" aria-label="Export granularity" title="Choose how to group the exported MP3 files">
<option value="page">1 MP3 / page</option>
<option value="sentence">1 MP3 / sentence</option>
</select>
<button class="btn-secondary btn-sm" id="reader-export-btn" title="Download the synthesised audio as MP3 (synthesises any missing sentences first)"><span class="mdi mdi-download"></span> Export MP3</button>
</span>
<div class="reader-synth-prog" id="reader-synth-prog" hidden>
<div class="reader-synth-track"><div class="reader-synth-fill" id="reader-synth-fill"></div></div>
<span class="reader-synth-label" id="reader-synth-label">0 / 0</span>
<button class="btn-secondary btn-sm" id="reader-synth-cancel">Cancel</button>
</div>
</div>
<!-- ⑤ Zoom toolbar + status legend (PDF) ────────────────────────── -->
<div class="reader-toolbar" id="reader-toolbar" hidden>
<div class="reader-zoom" id="reader-zoom">
<button class="btn-secondary btn-sm" id="reader-zoom-fitw" title="Fit width"><span class="mdi mdi-arrow-expand-horizontal"></span> Fit width</button>
<button class="btn-secondary btn-sm" id="reader-zoom-fith" title="Fit height"><span class="mdi mdi-arrow-expand-vertical"></span> Fit height</button>
<button class="btn-secondary btn-sm" id="reader-zoom-two" title="Two pages side by side"><span class="mdi mdi-book-open-outline"></span> Two pages</button>
<span class="reader-zoom-sep"></span>
<button class="btn-secondary btn-sm" id="reader-zoom-out" title="Zoom out"><span class="mdi mdi-minus"></span></button>
<span class="reader-zoom-pct" id="reader-zoom-pct">100%</span>
<button class="btn-secondary btn-sm" id="reader-zoom-in" title="Zoom in"><span class="mdi mdi-plus"></span></button>
</div>
<div class="reader-legend">
<span class="reader-leg reader-leg-pending">Not synthesised</span>
<span class="reader-leg reader-leg-synth">Synthesising</span>
<span class="reader-leg reader-leg-ready">Ready</span>
<span class="reader-leg reader-leg-reading">Reading</span>
</div>
</div>
<!-- ⑥ Document viewport ─────────────────────────────────────────── -->
<div class="reader-doc" id="reader-doc"></div>
</div><!-- /tab-reader -->