feat(audiobooks): Batch A — foundation, m4b ingest, playback sync, web player
Phase 1 (foundation): 'audiobooks' library type seeded (schema + DB);
audio_metadata JSONB on media_items (the kobo_metadata precedent —
only audio paths read it); reading_progress.audio_offset_seconds for
the honest {chapter, offset} locator; extensions/mimes registered for
ALL breadth formats (m4b/m4a/mp3/flac — deliberate per the DOC/LIT
doctrine, m4b scans richly first); classifyFormatGroup 'audiobook'
case (gotcha 39 recomputes on rescan); Go mime table extended at boot
so media elements accept the streams; ffmpeg/ffprobe in the image
(8.1.2 — also the xHE-AAC identification tripwire).
Phase 2 (ingest): sidecar-first (Libation/ABS metadata.json —
narrators/ASIN/curated chapters), ffprobe fallback/filler (duration,
bitrate, codec/profile, embedded chapters). Fixed three real
parser brittlenesses the Libation sidecar exposed: series accepts
string OR array-of-objects, publishedYear accepts number OR string,
ffprobe chapter timings accept number OR string (flexFloat). Folder-
structure fallback now runs on the UPDATE path too (rescans were
clearing sidecar-absent series/author); audio facts re-derive on
every rescan (gotcha 39).
Phase 3 (playback + sync): GET /api/media-items/:id/chapters (the
format-abstraction list); streaming rides the existing cookie-authed
Range /uploads path; PUT progress carries audio_offset_seconds +
elapsed_seconds (honest wall-clock playing time) — the reading-
history capture prefers player-reported elapsed over the inter-push
delta, same 2h ceiling; reading_mode='audio' marks the row.
Phase 4 (web player): dedicated audio reader page (foliate not
involved) — cover, chapter title, whole-book scrubber, skip ± (chips
1/5/10/15/30s, default 10s), speed (0.75×–2×), chapter list with
jump-to, Media Session lockscreen/notification transport, periodic
playing-only progress pushes, position restore on open.
Verified live: ingest of the Libation sample (Gardens of the Moon —
title/author/series/cover/date from sidecar+folders, 34 chapters,
26h duration, aac/LC); chapters endpoint; the reader route serving
the player; playback with position restore (0:57 round-trip);
progress rows in audio mode; wall-clock session rows in
reading_history.
This commit is contained in:
1 parent
a093529b80
commit
902cbe5915
20 files changed
+1425
-212
No files matched your search
@@ -17,7 +17,8 @@ CREATE TABLE IF NOT EXISTS library_types (
|
||||
INSERT INTO library_types (name, description, allowed_extensions) VALUES
|
||||
('ebooks', 'Ebook files including EPUB, PDF, MOBI, etc.', ARRAY['.epub', '.pdf', '.mobi', '.azw', '.azw3', '.txt', '.rtf', '.doc', '.docx', '.lit', '.fb2', '.pdb']),
|
||||
('comics', 'Comic book archives and image formats', ARRAY['.cbz', '.cbr', '.cb7', '.cbt', '.epub', '.pdf']),
|
||||
('manga', 'Manga files including archives and image folders', ARRAY['.cbz', '.cbr', '.epub', '.pdf', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.avif', '.tiff', '.tif'])
|
||||
('manga', 'Manga files including archives and image folders', ARRAY['.cbz', '.cbr', '.epub', '.pdf', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.avif', '.tiff', '.tif']),
|
||||
('audiobooks', 'Audiobook files; m4b first, breadth formats registered from day one', ARRAY['.m4b', '.m4a', '.mp3', '.flac'])
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
|
||||
-- Create users table
|
||||
@@ -235,7 +236,15 @@ CREATE TABLE IF NOT EXISTS media_items (
|
||||
-- }
|
||||
chapter_metadata JSONB,
|
||||
-- Need library_type_name for checks
|
||||
library_type_name VARCHAR(50)
|
||||
library_type_name VARCHAR(50),
|
||||
-- Audiobook metadata (format-scoped JSONB, the kobo_metadata
|
||||
-- precedent: only the audio paths read it — ebook paths never do).
|
||||
-- { "narrators": [], "duration_seconds": N, "codec_profile": "LC",
|
||||
-- "bit_rate": N, "asin": "...", "chapters": [
|
||||
-- { "index": 0, "title": "...", "start": 0.0, "end": 168.0 } ] }
|
||||
-- The chapters array IS the format abstraction: embedded m4b chapters
|
||||
-- and (later) folder-as-book files both write this exact shape.
|
||||
audio_metadata JSONB
|
||||
);
|
||||
|
||||
-- Add GIN indexes for fast array searches
|
||||
@@ -319,6 +328,9 @@ CREATE TABLE IF NOT EXISTS reading_progress (
|
||||
context_text TEXT,
|
||||
chapter INTEGER,
|
||||
chapter_progress FLOAT CHECK (chapter_progress >= 0 AND chapter_progress <= 1),
|
||||
-- Audiobook locator: seconds into the chapter (honest units, not an
|
||||
-- overloaded fraction — a 2h chapter at float precision drifts).
|
||||
audio_offset_seconds INTEGER CHECK (audio_offset_seconds >= 0),
|
||||
viewport_x FLOAT DEFAULT 0,
|
||||
viewport_y FLOAT DEFAULT 0,
|
||||
zoom_level FLOAT DEFAULT 1.0,
|
||||
|
||||
Reference in new issue
Block a user