tts-voice-creator-clone-and.../static/sections/s-magazine.html
2026-09-28 10:41:39 +02:00

328 lines
21 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-newspaper-variant-outline"></span></span>
<div class="section-title">
<h2>Magazines</h2>
<p>Listen to a magazine one article at a time &mdash; from its true beginning to its true end, without adverts, sidebars or a neighbouring piece spliced into the middle.</p>
</div>
</div>
<!-- ── Library ──────────────────────────────────────────────────────── -->
<div id="mag-view-library">
<div class="card mag-new">
<h3>Add a magazine or paper</h3>
<div class="reader-source-grid">
<div class="reader-paste-panel">
<div class="reader-source-method">
<span class="mdi mdi-file-document-edit-outline"></span>
<div><strong>Import &amp; Metadata</strong><span>Select a PDF file or configure optional title and issue details.</span></div>
</div>
<div class="mag-new-grid" style="margin-top: 4px; margin-bottom: 4px;">
<label for="mag-new-title">Title <span class="muted">(optional)</span>
<input id="mag-new-title" type="text" placeholder="Auto-filled from PDF filename if blank" autocomplete="off">
</label>
<label for="mag-new-issue">Issue <span class="muted">(optional)</span>
<input id="mag-new-issue" type="text" placeholder="e.g. September 2026" autocomplete="off">
</label>
</div>
<div class="reader-paste-actions" style="margin-top: auto; padding-top: 6px;">
<label class="btn-primary btn-sm reader-file-pick" id="mag-browse-btn" title="Choose a PDF file from your computer">
<span class="mdi mdi-folder-open-outline"></span> Durchsuchen
<input type="file" id="mag-new-file" accept="application/pdf" style="display:none">
</label>
</div>
</div>
<div class="reader-dropzone" id="mag-dropzone" style="cursor: pointer;">
<span class="mdi mdi-cloud-upload-outline reader-dropzone-icon" id="mag-dropzone-icon"></span>
<strong id="mag-dropzone-title">Drop a PDF document</strong>
<span class="note" id="mag-dropzone-note">Scanned issues work too &mdash; pages without selectable text are read with OCR.</span>
<div class="mag-dropzone-progress" id="mag-dropzone-progress" hidden>
<div class="mag-dropzone-progress-bar"></div>
</div>
<label class="btn-secondary btn-sm reader-file-pick" id="mag-dropzone-pick-btn" title="Choose a PDF file">
<span class="mdi mdi-file-upload-outline"></span> Import PDF
</label>
</div>
</div>
</div>
<h3>Your magazines</h3>
<div id="mag-library-list" class="mag-grid"></div>
</div>
<!-- ── Contents ─────────────────────────────────────────────────────── -->
<div id="mag-view-contents" hidden>
<div class="mag-bar">
<button type="button" class="btn-sm" onclick="magBackToLibrary()">
<span class="mdi mdi-arrow-left"></span> All magazines
</button>
<h3 id="mag-doc-title" contenteditable="true" spellcheck="false" title="Click to edit the magazine title"
onblur="magCommitDocField(this, 'title')"
onkeydown="if(event.key==='Enter'){event.preventDefault();this.blur();}">Magazine</h3>
<input id="mag-doc-issue" type="text" class="mag-doc-issue-input" placeholder="Issue, e.g. September 2026"
title="Issue date -- used for the year/month in exported MP3 filenames"
onblur="magCommitDocField(this, 'issue')"
onkeydown="if(event.key==='Enter'){this.blur();}">
<div class="mag-bar-actions">
<button type="button" class="btn-sm" id="mag-quality-run-btn" onclick="magQualityRun()"
title="Fix every page automatically (boxes, inset pull quotes, missed text, misread words, spelling) and list the pages that still need a look">
<span class="mdi mdi-clipboard-check-outline"></span> <span class="mag-qrun-label">Quality check / run</span>
</button>
<button type="button" class="btn-sm" id="mag-contents-view-toggle" onclick="magToggleContentsView()" title="Switch between an article list and a page-by-page thumbnail grid">
<span class="mdi mdi-view-grid-outline"></span> Thumbnails
</button>
<span class="mag-cols-control" id="mag-cols-control" hidden>
<label for="mag-cols-range">Columns</label>
<input type="range" id="mag-cols-range" min="1" max="12" step="1" value="8" oninput="magSetPagesCols(this.value)">
<span id="mag-cols-value" class="muted">8</span>
</span>
<button type="button" class="btn-sm" id="mag-contents-select-toggle" aria-pressed="false" onclick="magContentsToggleSelect()" title="Select several articles to approve or reject at once">
<span class="mdi mdi-checkbox-multiple-marked-outline"></span> Select
</button>
<button type="button" class="btn-sm" id="mag-playlist-toggle" aria-pressed="false" onclick="magTogglePlaylist()"
title="Show every article with a generated MP3, in reading order, with a player">
<span class="mdi mdi-playlist-music-outline"></span> Playlist
</button>
<button type="button" class="btn-sm" id="mag-driving-open" onclick="magOpenDriving()"
title="Open the magazine playlist with large, distraction-free controls">
<span class="mdi mdi-car-outline"></span> Driving mode
</button>
<button type="button" class="btn-sm primary" onclick="magPlayAll()">
<span class="mdi mdi-playlist-play"></span> Play all articles
</button>
<button type="button" class="btn-sm" onclick="magIngest()">
<span class="mdi mdi-refresh"></span> Re-run detection
</button>
<button type="button" class="btn-sm" onclick="magReocrMagazine()" title="Send every page through the configured OCR engine again, then re-detect articles">
<span class="mdi mdi-text-recognition"></span> OCR again (whole magazine)
</button>
<button type="button" class="btn-sm" id="mag-refine-all-figures-btn" onclick="magRefineAllFigures()"
title="Batch-check figure boundaries on every page in this magazine">
<span class="mdi mdi-image-auto-adjust"></span> Fix all figure boundaries
</button>
</div>
</div>
<div id="mag-quality-report" class="mag-quality-report" role="region" aria-label="Quality check result" aria-live="polite" hidden></div>
<div id="mag-contents-bulkbar" class="mag-bulkbar" hidden></div>
<div class="mag-audio-export">
<label for="mag-audio-voice">Narrator for exported MP3s</label>
<select id="mag-audio-voice"></select>
<label for="mag-audio-interview-voice">Interview voice 2 (optional)</label>
<select id="mag-audio-interview-voice" title="When an article contains two recurring speaker labels, the second speaker uses this voice. Normal articles are unaffected.">
<option value="">— use narrator only —</option>
</select>
<button type="button" class="btn-sm" id="mag-audio-export-btn" onclick="magGenerateAllAudio()"
title="Render every approved article that doesn't already have an MP3 -- title, author, cover image and full text embedded as tags -- for a phone's own music/podcast player or a car head unit. Safe to click again after an aborted run: already-generated articles are skipped.">
<span class="mdi mdi-file-music-outline"></span> Generate all audio (MP3)
</button>
<button type="button" class="btn-sm" id="mag-audio-rebuild-btn" onclick="magGenerateAllAudio(true)"
title="Rebuild every approved MP3 using the latest OCR cleanup, pronunciation and interview-voice settings. Existing files are replaced.">
<span class="mdi mdi-refresh"></span> Regenerate all MP3s
</button>
<a class="btn-sm" id="mag-audio-zip-link" href="#" hidden download>
<span class="mdi mdi-folder-zip-outline"></span> Download all (ZIP)
</a>
<span class="mag-audio-export-progress-wrap" hidden id="mag-audio-export-progress-wrap">
<progress id="mag-audio-export-progress" max="1" value="0"></progress>
<span id="mag-audio-export-count" class="muted"></span>
</span>
<span id="mag-audio-export-status" class="muted" role="status" aria-live="polite"></span>
</div>
<div id="mag-playlist" class="mag-playlist" hidden>
<div class="mag-playlist-heading">
<div>
<strong>Magazine playlist</strong>
<span class="muted">Page · article · duration</span>
</div>
<label class="mag-karaoke-toggle" title="Highlight the sentence and word approximately as the MP3 plays">
<input type="checkbox" id="mag-playlist-karaoke" checked onchange="magPlaylistSync()">
Follow spoken text
</label>
</div>
<div class="mag-playlist-player">
<img id="mag-playlist-cover" class="mag-playlist-cover" alt="" hidden>
<div class="mag-playlist-nowplaying">
<strong id="mag-playlist-title">—</strong>
<span id="mag-playlist-sub" class="muted"></span>
<audio id="mag-playlist-audio" controls preload="none" style="width:100%"></audio>
</div>
<button type="button" class="btn-sm" onclick="magTogglePlaylist()" title="Close playlist" aria-label="Close playlist">
<span class="mdi mdi-close"></span>
</button>
</div>
<ol id="mag-playlist-tracks" class="mag-playlist-tracks"></ol>
<div id="mag-playlist-companion" class="mag-playlist-companion" hidden>
<figure id="mag-playlist-visual" class="mag-playlist-visual" hidden></figure>
<div>
<div id="mag-playlist-sync-note" class="mag-playlist-sync-note muted">Text timing is estimated from the MP3 position.</div>
<article id="mag-playlist-transcript" class="mag-playlist-transcript" aria-label="Article transcript"></article>
</div>
</div>
</div>
<div id="mag-ingest-status" class="mag-status" hidden></div>
<!-- Decorative "watch it work" visualisation -- the essential
progress information (percentage, page count, done/failed) is
in #mag-ingest-status above, which is what a screen reader user
needs; this panel adds nothing beyond that but noisy churn. -->
<div id="mag-ingest-preview" class="mag-ingest-preview" hidden aria-hidden="true">
<div class="mag-ingest-preview-side">
<span class="mag-ingest-preview-label">Page <span id="mag-ingest-preview-stage"></span></span>
<div class="mag-ingest-preview-canvas">
<img id="mag-ingest-preview-img" alt="Page currently being read">
<div id="mag-ingest-preview-boxes" class="mag-ingest-preview-boxes"></div>
</div>
<div id="mag-ingest-preview-legend" class="mag-ingest-preview-legend"></div>
</div>
<div class="mag-ingest-preview-resize" id="mag-ingest-preview-resize" role="separator" aria-orientation="vertical" aria-label="Resize page and recognised text panes" title="Drag to resize page and recognised text panes"></div>
<div class="mag-ingest-preview-side">
<span class="mag-ingest-preview-label">Recognised text</span>
<div id="mag-ingest-preview-text" class="mag-ingest-preview-text"></div>
</div>
</div>
<div id="mag-contents"></div>
</div>
<!-- ── Review (Stage D) ─────────────────────────────────────────────── -->
<div id="mag-view-review" hidden>
<div class="mag-bar">
<button type="button" class="btn-sm" onclick="magBackToContents()">
<span class="mdi mdi-arrow-left"></span> Contents
</button>
<h3>Review article boundaries</h3>
</div>
<div id="mag-review"></div>
</div>
<!-- ── Reading ──────────────────────────────────────────────────────── -->
<div id="mag-view-read" hidden>
<div class="mag-bar">
<button type="button" class="btn-sm" onclick="magBackToContents()">
<span class="mdi mdi-arrow-left"></span> Contents
</button>
<div class="mag-read-id">
<h3 id="mag-read-title">Article</h3>
<span id="mag-read-by" class="muted"></span>
<span id="mag-read-pages" class="muted"></span>
</div>
<button type="button" class="btn-sm" onclick="magToggleDriving()" title="Large controls for listening without looking at the screen">
<span class="mdi mdi-car-outline"></span> Driving mode
</button>
</div>
<div class="mag-controls">
<div class="mag-voice-pick">
<label for="mag-voice">Narrator</label>
<select id="mag-voice"></select>
</div>
<button type="button" class="btn primary" id="mag-play" onclick="magPlay()"
aria-label="Play narration">
<span class="mdi mdi-play"></span> Listen
</button>
<button type="button" class="btn" id="mag-prepare" onclick="magPrepareAudio()"
title="Synthesise the whole article now, so it plays straight through without waiting sentence by sentence">
<span class="mdi mdi-waveform"></span> Prepare audio
</button>
<span id="mag-prep-status" class="muted" role="status" aria-live="polite"></span>
<span id="mag-read-progress" class="muted" aria-live="polite"></span>
<span id="mag-queue-progress" class="muted" aria-live="polite"></span>
<label class="mag-karaoke-toggle" title="Highlight each word approximately as it is spoken">
<input type="checkbox" id="mag-karaoke-cb" checked onchange="TTVC.magazine.karaoke = this.checked">
Word highlighting
</label>
<label class="mag-karaoke-toggle" title="Speak each figure's description where it appears, if one has been generated in review. Takes effect next time this article is opened.">
<input type="checkbox" id="mag-read-descriptions">
Read figure descriptions
</label>
<div class="mag-pane-tabs" role="group" aria-label="Reading view">
<button type="button" class="btn-sm active" data-mag-pane="reflow"
aria-pressed="true" onclick="magSetPane('reflow')">Article</button>
<button type="button" class="btn-sm" data-mag-pane="pdf"
aria-pressed="false" onclick="magSetPane('pdf')">Original page</button>
</div>
</div>
<div id="mag-pane-reflow" class="mag-pane">
<div id="mag-read-image" class="mag-read-image" hidden></div>
<div id="mag-article" class="mag-article"></div>
</div>
<div id="mag-pane-pdf" class="mag-pane" hidden>
<div class="mag-page-wrap">
<img id="mag-page-img" alt="">
<div id="mag-page-box" class="mag-page-box" hidden></div>
</div>
<p id="mag-page-label" class="muted"></p>
</div>
<!-- Pronunciation and spelling -->
<details class="card mag-checks" id="mag-checks" ontoggle="magChecksToggled(this)">
<summary><span class="mdi mdi-spellcheck"></span> Pronunciation &amp; spelling &mdash; check this article</summary>
<div id="mag-checks-body" aria-live="polite"></div>
</details>
<!-- Podcast mode -->
<details class="card mag-podcast">
<summary><span class="mdi mdi-podcast"></span> Podcast mode &mdash; discuss this article</summary>
<p class="muted">Two or three hosts discuss the article for someone who has not read it. The script is written by the configured LLM from the article text; it will not invent facts, but it is a generated discussion, not the author&rsquo;s words.</p>
<div class="mag-hosts">
<div class="mag-voice-pick"><label for="mag-host-a">Host A</label><select id="mag-host-a"></select></div>
<div class="mag-voice-pick"><label for="mag-host-b">Host B</label><select id="mag-host-b"></select></div>
<div class="mag-voice-pick"><label for="mag-host-c">Host C <span class="muted">(optional)</span></label><select id="mag-host-c"></select></div>
</div>
<div class="mag-podcast-actions">
<button type="button" class="btn" id="mag-podcast-gen" onclick="magGeneratePodcast()">Generate script</button>
<button type="button" class="btn primary" id="mag-podcast-play" onclick="magPlayPodcast()" hidden>Play podcast</button>
</div>
<div id="mag-podcast-script"></div>
</details>
</div>
<!-- ── Driving mode overlay ─────────────────────────────────────────── -->
<!-- Large controls, minimal text, no interaction required beyond play/
pause/skip -- reuses whatever is already playing rather than its
own playback path. -->
<div id="mag-driving" class="mag-driving" hidden role="dialog" aria-label="Driving mode">
<button type="button" class="mag-driving-exit" onclick="magToggleDriving()" aria-label="Exit driving mode">
<span class="mdi mdi-close"></span>
</button>
<div class="mag-driving-body">
<h2 id="mag-driving-title">Article</h2>
<p id="mag-driving-by" class="muted"></p>
<div id="mag-driving-queue" class="mag-driving-queue" hidden>
<span id="mag-queue-progress-2" class="muted"></span>
</div>
<figure id="mag-driving-visual" class="mag-driving-visual" hidden></figure>
<div id="mag-driving-lyrics" class="mag-driving-lyrics" tabindex="0" aria-label="Article text, following the audio" hidden></div>
<div class="mag-driving-seek">
<input type="range" id="mag-driving-seek" min="0" max="1000" value="0" step="1"
aria-label="Position in article" oninput="magDrivingSeek(this.value)">
<div class="mag-driving-time" id="mag-driving-time" aria-live="off">0:00 / 0:00</div>
</div>
<div class="mag-driving-controls">
<button type="button" class="mag-driving-btn is-small" onclick="magDrivingPrevious()" aria-label="Previous article" title="Previous article">
<span class="mdi mdi-skip-previous"></span>
</button>
<button type="button" class="mag-driving-btn" onclick="magDrivingSkip(-15)" aria-label="Back 15 seconds" title="Back 15 seconds">
<span class="mdi mdi-rewind-15"></span>
</button>
<button type="button" class="mag-driving-btn mag-driving-play" id="mag-driving-play" onclick="magDrivingTogglePlayback()" aria-label="Play">
<span class="mdi mdi-play"></span>
</button>
<button type="button" class="mag-driving-btn" onclick="magDrivingSkip(15)" aria-label="Forward 15 seconds" title="Forward 15 seconds">
<span class="mdi mdi-fast-forward-15"></span>
</button>
<button type="button" class="mag-driving-btn is-small" onclick="magDrivingNext()" aria-label="Next article" title="Next article">
<span class="mdi mdi-skip-next"></span>
</button>
</div>
<div class="mag-driving-options" role="group" aria-label="Playback options">
<button type="button" class="mag-driving-opt" id="mag-driving-speed" onclick="magDrivingCycleSpeed()"
aria-label="Playback speed 1×" title="Playback speed">1×</button>
<button type="button" class="mag-driving-opt" id="mag-driving-text-toggle" aria-pressed="true" onclick="magDrivingToggleText()"
title="Show or hide the text being read"><span class="mdi mdi-text-long" aria-hidden="true"></span> Text</button>
<button type="button" class="mag-driving-opt" id="mag-driving-image-toggle" aria-pressed="true" onclick="magDrivingToggleImages()"
title="Show or hide the article's pictures"><span class="mdi mdi-image-outline" aria-hidden="true"></span> Images</button>
</div>
</div>
</div>