openreader/docs/v1/issues-to-components.md
Richard Roberson 452bc2adfe feat: Add issue triage and mapping documentation for OpenReader v1
- Created `issues-to-components.md` to summarize open issues and their mappings to components for v1 development.
- Documented key features, bugs, and proposed solutions for issues #59, #48, #47, #44, and #40.
- Outlined global guardrails and cross-cutting improvements for the v1 architecture.

chore: Establish v1 todo and planning framework

- Created `todo.md` to capture the 1.0 rewrite plan, including scope, architecture overview, phased milestones, and a master checklist.
- Defined action items, dependency ordering, and issue mapping alignment for efficient development.

chore: Update pnpm workspace configuration

- Added `ignoredBuiltDependencies` for `canvas` in `pnpm-workspace.yaml` to prevent build issues.
2025-11-10 13:19:45 -07:00

160 lines
No EOL
8.4 KiB
Markdown
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.

# OpenReader v1 Issue Triage and Mapping
Repository: https://github.com/richardr1126/OpenReader-WebUI/issues
Reviewed via gh at 2025-11-10.
Summary of open items included:
- #59 Feature: Chapter-Based MP3 Export
- #48 Bug: Failed to export after complete render with Kokoro
- #47 Feature: Combine voices (Kokoro “plus” syntax)
- #44 Bug: Dialog not chunked together
- #40 Bug: PDF left/right extraction margins not working
Global 1.0 guardrails
- Streaming-first playback (replace Howler with HTMLAudioElement/MSE)
- Dexie DB replacing vanilla IndexedDB
- Single engine cut-over (no dual engines)
- Keep audiobook (m4b) and server-side sync
Issue #59 — Chapter-Based MP3 Export
Type: Feature
Hypothesis / intent:
- Users want one-mp3-per-chapter output (besides full-book m4b).
v1 mapping:
- Add chapterized MP3 pipeline that chunks by adapter “chapter” units.
- Provide ZIP export for many MP3 files (streamed).
Proposed modules:
- [src/v1/playback/audiobook.ts](src/v1/playback/audiobook.ts:1)
- [src/v1/adapters/EpubAdapter.ts](src/v1/adapters/EpubAdapter.ts:1)
- [src/app/api/audio/convert/route.ts](src/app/api/audio/convert/route.ts:1) (extend to “mp3-per-chapter” mode)
- [src/v1/db/repositories/DocumentsRepo.ts](src/v1/db/repositories/DocumentsRepo.ts:1)
API design:
- POST /api/audio/convert?mode=chapters&format=mp3 -> returns stream of a ZIP.
Logging to add:
- Chapter boundaries, byte sizes, cumulative progress in [src/app/api/audio/convert/route.ts](src/app/api/audio/convert/route.ts:1).
Acceptance:
- A multi-chapter EPUB produces N mp3 parts named “NN - Chapter Title.mp3”; total duration ~ sum of parts; ZIP streamed without timeouts in Docker.
Issue #48 — Failed export after complete render with Kokoro
Type: Bug (large-book export)
Observations:
- UI reaches 100% then resets, no download.
- Docker shows “fin … undefined”, likely final transfer problem (not TTS).
Likely root causes:
- Final m4b delivery uses single huge arrayBuffer; browser memory/timeout.
- Missing Content-Disposition/Range; no resumable download.
- Temp-file lifecycle cleanup racing with response.
v1 mitigation:
- Serve final artifact as file on disk with streaming and Range:
- New endpoint: GET /api/audio/convert/download?bookId=… that streams file with Accept-Ranges.
- UI performs streamed download; no arrayBuffer buffering.
- Optionally support S3-compatible offload in future.
Touched modules:
- [src/app/api/audio/convert/route.ts](src/app/api/audio/convert/route.ts:1)
- [src/v1/playback/audiobook.ts](src/v1/playback/audiobook.ts:1)
- [src/v1/components/Progress/MigrationProgress.tsx](src/v1/components/Progress/MigrationProgress.tsx:1)
Instrumentation to add:
- Book ID, file size on disk, stream chunk counts, and client-abort detection.
- Add explicit log/error surfaces at:
- [src/app/api/audio/convert/route.ts](src/app/api/audio/convert/route.ts:1)
Acceptance:
- 12 GB m4b exports download in Docker without UI reset; bookId persists until deletion; status 206 Range works.
Issue #47 — Kokoro combined voices (“+” syntax)
Type: Feature
Intent:
- Support voice strings like “bf_emma+af_heart” when provider is Kokoro/FastAPI or DeepInfra Kokoro.
v1 plan:
- Allow free-form voice string entry and pass-through if not in known set.
- Validate known providers; if “+” present, skip voice validation list.
Components:
- [src/v1/tts/types.ts](src/v1/tts/types.ts:1)
- [src/v1/tts/providers/DeepinfraProvider.ts](src/v1/tts/providers/DeepinfraProvider.ts:1)
- [src/v1/tts/providers/CustomOpenAIProvider.ts](src/v1/tts/providers/CustomOpenAIProvider.ts:1)
- [src/v1/components/player/VoicesControl.tsx](src/v1/components/player/VoicesControl.tsx:1) (v1)
- [src/app/api/tts/route.ts](src/app/api/tts/route.ts:1) ensure it does not coerce voice when provider is deepinfra/custom.
Logging:
- Emit provider, model, raw voice string in request (no PII).
Tests:
- “a+b” voices produce audible combined output; works via DeepInfra pass-through and Kokoro-FastAPI; voice dropdown offers “Custom voice…” input.
Issue #44 — Dialog not being chunked together
Type: Bug (NLP splitting)
Observations:
- Dialog lines split mid-quote; needs grouping.
Proposed improvements:
- Extend [splitIntoSentences()](src/utils/nlp.ts:34) to apply quote-aware grouping.
- When a sentence begins with an opening quote and the next ends with a closing quote, join them before MAX_BLOCK_LENGTH checks.
v1 mapping:
- Provide composable splitter strategy in [src/v1/nlp/sentences.ts](src/v1/nlp/sentences.ts:1) wrapping existing utility.
- Add “dialog-preserve” flag toggled in settings.
Logging to add:
- Count of quote-joined sentences; per-page example of before/after lengths.
Acceptance:
- Quoted dialog flows as single units unless exceeding MAX_BLOCK_LENGTH by > X%; regression-safe for non-dialog text.
Issue #40 — PDF left/right extraction margins not working
Type: Bug (PDF extraction)
Observations:
- Current filter uses transform[4]/[5] with width heuristics. Edge cases: missing width, skew, page scale differences.
Likely causes:
- Some pdf.js TextItem miss width; horizontal margins computed but not applied due to scale mismatch.
- Defaults in [src/contexts/ConfigContext.tsx](src/contexts/ConfigContext.tsx:216) set left/right to '0.0' on first run; UX confusion.
Remediation:
- Compute glyph bbox width when width absent using transform matrix.
- Normalize x to [0..1] by dividing by pageWidth; compare to margins reliably.
- Add visual debug overlay (dev mode) to draw margin boxes while extracting.
Code touchpoints:
- [src/utils/pdf.ts](src/utils/pdf.ts:60) extractTextFromPDF(): margin math and width fallback.
- v1 adapter: [src/v1/adapters/PdfAdapter.ts](src/v1/adapters/PdfAdapter.ts:1)
Diagnostics to add:
- Per-page: kept/filtered counts, extremes of x positions, computed margins.
Acceptance:
- Test PDFs show left/right trimming correctly; e2e highlight still robust.
Cross-cutting improvements from issues
- Abort discipline: Any config change cancels in-flight TTS and preloads with unique request keys. Implemented in [src/v1/playback/engine.ts](src/v1/playback/engine.ts:1).
- Streaming-first: Introduce [src/app/api/tts/stream/route.ts](src/app/api/tts/stream/route.ts:1) for chunked speech where supported; fallback to progressive MP3/AAC.
- Dexie-backed caches: Audio buffers cached with TTL and LRU in [src/v1/db/repositories/AudioCacheRepo.ts](src/v1/db/repositories/AudioCacheRepo.ts:1).
- Resume positions: store stable location tokens per adapter in [src/v1/playback/positionStore.ts](src/v1/playback/positionStore.ts:1).
Minimal logging plan (to validate assumptions)
- Export path (#48):
- Log: bookId, tmp file sizes, final m4b size; response headers; client disconnects.
- NLP chunking (#44):
- Log: dialog-join counts; sample joined strings (first 80 chars).
- PDF margins (#40):
- Log: normalized x min/max; filtered vs kept counts per page.
- Voice combo (#47):
- Log: provider/model/voice string; server echo ensures pass-through.
- Chapter MP3 export (#59):
- Log: chapter count, per-chapter byte sizes, total ZIP size.
Acceptance test inventory (added to 1.0)
- Streaming playback start-to-speech under 500ms on cached sentences.
- Voice switch mid-playback produces single cancellation and single rebuffer.
- 12 GB m4b export succeeds and downloads via streaming.
- Chapterized MP3 ZIP streams and extracts correctly.
- Dialog detection joins quotes for typical novels.
- PDF margin sliders alter extracted text deterministically.
Action items added to v1 backlog
- Implement chapterized MP3 export (ZIP) path.
- Add range-enabled download endpoint for m4b artifacts.
- Add custom voice string input and provider pass-through.
- Implement quote-aware sentence grouping in splitter.
- Harden PDF x/width margin filtering and add debug overlay.
References (current code)
- Export pipeline: [src/app/api/audio/convert/route.ts](src/app/api/audio/convert/route.ts:1)
- TTS routing: [src/app/api/tts/route.ts](src/app/api/tts/route.ts:1)
- PDF extraction: [src/utils/pdf.ts](src/utils/pdf.ts:60)
- NLP: [src/utils/nlp.ts](src/utils/nlp.ts:34)
References (v1 new modules)
- [src/v1/playback/engine.ts](src/v1/playback/engine.ts:1)
- [src/v1/playback/media/MediaController.ts](src/v1/playback/media/MediaController.ts:1)
- [src/v1/tts/providers/DeepinfraProvider.ts](src/v1/tts/providers/DeepinfraProvider.ts:1)
- [src/v1/adapters/PdfAdapter.ts](src/v1/adapters/PdfAdapter.ts:1)
- [src/v1/nlp/sentences.ts](src/v1/nlp/sentences.ts:1)
- [src/v1/db/repositories/AudioCacheRepo.ts](src/v1/db/repositories/AudioCacheRepo.ts:1)