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>
101 lines
4.2 KiB
Markdown
101 lines
4.2 KiB
Markdown
# User manual
|
|
|
|
Living manual. Every feature updates this file in the same commit as the code.
|
|
|
|
> Sections for the mature features (voice library, Read Aloud, audiobook
|
|
> casting, Rehearser) are still to be written — this file was created alongside
|
|
> the magazine feature and currently documents that area only.
|
|
|
|
---
|
|
|
|
## Magazines
|
|
|
|
### Purpose
|
|
|
|
Listen to a magazine or newspaper one **article** at a time, from its true
|
|
beginning to its true end, without adverts, sidebars or a neighbouring article
|
|
spliced into the middle. Each article can be narrated by a single voice, or
|
|
turned into a podcast discussion between two or three voices you pick.
|
|
|
|
### Prerequisites
|
|
|
|
- The magazine as a PDF (native text or scanned — both work).
|
|
- At least one voice in the library for narration.
|
|
- For podcast mode: an LLM configured in Settings (`llm_url`, `llm_model`), and
|
|
two or three voices chosen as hosts.
|
|
|
|
### Step by step
|
|
|
|
1. **Add the magazine.** Studio → *Add Magazine / Paper*. Give it a title and
|
|
issue, then upload the PDF.
|
|
2. **Ingest.** Ingestion runs in the background — you can close the browser. It
|
|
renders every page, extracts text (falling back to OCR on scanned pages),
|
|
classifies each region of the layout, finds the contents page, and proposes
|
|
articles with page ranges.
|
|
3. **Review the articles.** This step is not optional and cannot be skipped.
|
|
Each proposed article shows its title, author, page range, estimated
|
|
listening time and a confidence score. Confirm the boundaries, exclude a
|
|
sidebar you do not want read, correct the title, then **Approve**.
|
|
4. **Listen.** An approved article can be narrated with the default voice, or
|
|
sent to podcast mode.
|
|
|
|
**Expected result:** the article plays from its headline to its final paragraph.
|
|
Page furniture (running heads, page numbers), adverts and pull quotes are never
|
|
read aloud.
|
|
|
|
### Confidence, and what it means
|
|
|
|
| Shown | Meaning |
|
|
|---|---|
|
|
| ~90% | The contents page listed this article — title and start page are known. |
|
|
| ~55% | Detected from a headline alone; check the boundaries carefully. |
|
|
| Low | No body copy found. Usually an advert or a section divider — reject it. |
|
|
|
|
An article is never narrated until you approve it, whatever its confidence.
|
|
|
|
### Podcast mode
|
|
|
|
Choose two or three voices as hosts. The configured LLM writes a discussion of
|
|
that article for listeners who have not read it, and each host's turns are
|
|
synthesised with their voice.
|
|
|
|
The prompt forbids inventing facts, figures or quotes that are not in the
|
|
article; if the article reaches no conclusion the hosts say so. It is still a
|
|
generated discussion, not the author's words — treat it as a summary, and read
|
|
the original for anything that matters.
|
|
|
|
Podcast generation is refused for an article you have not approved, because
|
|
unreviewed boundaries may include a neighbouring article or an advert.
|
|
|
|
### Errors
|
|
|
|
| What you see | What happened | What to do |
|
|
|---|---|---|
|
|
| "Upload the PDF first" | The project has no `source.pdf`. | Upload it, then ingest. |
|
|
| Ingestion failed | The PDF is encrypted or corrupt. | Check it opens in a viewer. |
|
|
| "Approve the article before generating a podcast" | Review gate. | Approve it first. |
|
|
| "No LLM model configured" | `llm_model` is empty. | Set it in Settings. |
|
|
| "LLM did not return usable JSON" | The model ignored the format. | Retry, or pick a stronger model. |
|
|
| "Article has too little text" | Under 200 characters — usually a stub. | Check the boundaries in review. |
|
|
|
|
### Privacy
|
|
|
|
Ingestion, OCR and storage are entirely local to the container. Podcast mode is
|
|
the exception: **the article's text is sent to whatever LLM `llm_url` points
|
|
at.** If that is a hosted API rather than a model on your own network, the
|
|
article leaves your machine. Narration uses your local TTS stack and sends
|
|
nothing outward.
|
|
|
|
### Accessibility and mobile
|
|
|
|
To be completed with the P1 interface. Planned: keyboard-navigable contents
|
|
tree and review screen, sentence highlighting that respects
|
|
`prefers-reduced-motion`, large touch targets for playback, and a driving mode
|
|
that advances images without requiring interaction.
|
|
|
|
### Offline
|
|
|
|
Ingested magazines are stored server-side, so any device on the network sees the
|
|
same library and reading progress. Playback needs the server; there is no
|
|
offline cache yet.
|