ce366b16045fe392605e879c5ea6b940be821912
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
73ae1789c5 |
feat(reader): fullscreen mode; revert stacked volume; fix manga badges + ghost sidebar
Reader fullscreen: the topbar gains a fullscreen toggle (icon state + f shortcut + help row; f is guarded so Ctrl/Cmd+F stays find-in-page). Enter requests the Fullscreen API with fallbacks (desktop/Android get a true full display; iOS Safari has no page-fullscreen API and falls back to the soft visual mode only). Margins: the foliate paginator's closed shadow root only yields through its observed 'margin' attribute, so fullscreen fits a comfortable ratio (6% of the short side, floored at 48px) and floors it by the measured env insets — Android reports true cutout insets in fullscreen, so portrait phones keep text clear of the camera band; iOS letterboxes are already env-padded chrome. Fixed-layout renderers skip it (fxl/webtoon pack pages edge-to-edge by design). Exits the user makes outside the toggle (Esc, browser UI) sync via fullscreenchange(+webkit) and restore the ordinary 48px margin; a position-keeping re-render follows the ResizeObserver re-entry path. Verified on-device: enter -> true fullscreen + icon swap + camera band clear, exit -> chrome back, margins restored, position kept. Volume: revert the stacked panel — the popup returns to the liked shape (mute button left, horizontal dragger right, dialogue above the strip); one-line flex restore of the pre-89665fb layout. Manga badges: MediaBadgeKind makes the library type authoritative for the comic/manga split — manga volumes ship as fixed_layout scans as often as comic_archive (and manga_type is unreliable across sources: unknown/NULL on real rows), so the old comic_archive+manga mapping left the scan volumes unbadged. Anything in a manga library reads manga; anything in a comics library reads comic. Verified: /dashboard lands 24 title="Manga" cover badges for the Manga library and the sections API reports badge=manga for a fixed_layout item. Ghost sidebar: the >=1024px media block applied body padding-left:var(--sidebar-w) unconditionally, so pages that never render the sidebar (logged-out pages, the audio player, the reader itself) carried a phantom 16rem left margin that pushed content off center on desktop widths. The body:has(.app-sidebar) rule below was already the correct conditional source — it is now the only one. Verified in the built stylesheet: exactly one such rule remains. Infra: main.js ?v=3 stamp bump (reader.ts changed; hashed chunks churn their URLs themselves). |
||
|
|
89665fb2bc |
feat(annotations + player): bookmark rows in the banner, stacked volume panel, badge parity for comics/manga
Annotations banner: audio/text bookmarks join notes and highlights — third row kind (bookmark glyph indicator, H:MM:SS / Page N / Chapter N friendly labels), edit-note/delete actions branch by kind, and the banner's export now covers all annotations. The archived ledger (ListDeletedAnnotationsForBook) already tombstoned bookmarks equally and Restore/Purge take annotation_type=bookmark — verified live: delete → restore brings the row back with its note intact. Volume: the dragger sits BELOW the mute icon in the panel (stacked flyout shape) with the horizontal slider from the last round. Badges: the real fix for comics/manga — format_group only knows 'comic_archive' (manga_type is unreliable across sources), so the comic/manga split is resolved from the LIBRARY type server-side. handlers.MediaBadgeKind + BookInfo.Badge at every construction site (dashboard SSR + sections API, collections, series, and the bookshelf search grid — which was also missing FormatGroup entirely, so no card ever badged there). Both renderers (templ + dashboard.ts) switch on the resolved badge. Verified: All-Books grid renders 2 Comic + 3 Manga badges, dashboard the audiobook one. Infra: header.templ main.js src gains ?v=<stamp> — browsers keyed an intermediate bundle by URL and served it across rebuilds (the stale 'Add note' the user saw); bump the stamp when JS stale-shadows. |
||
|
|
2c714dcb60 |
fix(audio player + dashboard): markdown bookmark notes, horizontal volume, transport glyphs, client-side badges
Bookmarks: notes are markdown now — capture and drawer edit are
textareas (raw source), display renders through the reader's
markdown-it + DOMPurify pipeline (renderNoteHtml) as x-html; the
edit textarea drops the enter-to-save handler so newlines survive.
Volume: the panel dragger is horizontal (mute button beside it,
7.5rem slider) per user feedback — vertical orientation read as a
scrubber clone at a glance.
Play/pause: unicode glyphs replaced with SVG — the triangle is
optically centered (2px right-bias) and the pause bars are longer
and thicker rounded rects (4.2×15 at rx 1.7).
Dashboard: the client-side card renderer (dashboard.ts — the path the
JSON sections actually render through) now mirrors the templ BookCard:
cover-corner badges for audiobook/comic/manga (books unbadged) and the
hover action shows headphones with 'Listen to …' labels for audiobooks.
BookInfo type + Go struct carry format_group end to end.
Verified on-device: pause glyph centered/thicker, horizontal volume
panel, markdown note ('**bold**' + list) rendered in the bookmarks
drawer, badges on dashboard cards, sections JSON carrying
format_group.
|
||
|
|
5cf34102d6 |
feat(audio player): canonical audiobook transports + dropdown settings + volume panel
Transport row is now the five-button audiobook shape the user asked for: previous-chapter · circular-skip-back (seconds displayed inside the ring) · play/pause · circular-skip-forward · next-chapter — the ring arrows are stroke SVGs (rotate-ccw/rotate-cw) with the number as an overlay so it stays upright and follows the skip setting. Settings strip collapses to four controls: Skip dropdown, Speed dropdown, Bookmark (capture popover unchanged), Volume. Volume opens a panel with a vertical dragger (writing-mode:rtl slider) and a mute/unmute button; the strip icon is dynamic — 1–3 arcs by level (low <34%, mid <67%, high) and crossed when muted or zero. Volume/mute persist in localStorage and restore with the element; unmute floors zero-volume to 50% so unmuting is audible. Verified on-device: dropdowns open upward with current value highlighted, mute flips both icons to the crossed state, slider drag changes arc count, chapter buttons jump (pill → 15/34) and the caption tracks the drawer's chapter labels. |
||
|
|
86f0af632e |
feat(audiobooks): A2 — narrator metadata, audio-aware UX, player polish, bookmarks
A2a (metadata round): narrators TEXT[] + narrators_search (tags pattern: accumulator + GIN pairs) + abridged bool on media_items; scanner fills from the Libation/ABS sidecar (narrators verbatim, abridged flag) with audio-payload promotion; editorial fields ride the real columns while audio_metadata stays scan-derived (duration/ codec/chapters). Detail/edit/save paths carry the fields; the web edit dialog grows an Audio section (Narrator(s) comma-input, Abridged toggle, scanned duration read-only); search weights + narrator filter (word_similarity over narrators_search) + narrator autocomplete added. A2c (per-media-type wording): copy helpers keyed on format_group — Listen Now, Mark Complete/Not Complete, Listening Progress, Your Listening Stats, Time Listening, Time listened (pages tile swapped for audio), Position H:MM:SS / total H:MM:SS on detail + progress pages (duration parsed from audio_metadata). Dashboard: system collections swap to Continue Listening / Recently Listened at DISPLAY time when the audiobooks library is selected (stored names unchanged; restores keep working); week card = Listening Time / Sessions / Books Touched; cover-corner badges identify audiobook (headphones) / comic / manga cards on mixed grids — books stay unbadged; hover action icon follows the media type. A2b (player): the scrubber binds value to the playhead with an explicit scrub guard (the handle previously froze at its initial max); header row = reader-style back arrow + Chapter n/total launcher; chapters open in a floating top-down drawer (current chapter highlighted, page never shifts); Skip/Speed rows share one label-left layout; honest wall-clock session accounting (playing-only deltas, paused flush). A2d (bookmarks): note-bearing time bookmarks ride the existing media_bookmarks substrate (notes column + sync-aware service already there) — audio locator convention audio:<seconds>, chapter_number filled, auto-title '<chapter> — H:MM:SS', capture popover with optional note, drawer with jump/edit-note/delete. Zero server changes needed. Verified live on the Libation sample: narrators backfilled by rescan; PUT edit persisted + overrides detected (narrators, abridged); force rescan preserved the user edit; reset-to-scanned-of-defaults restored truth; narrator filter/autocomplete scoped by library; dashboard listsening wording + badges render; bookmark save with note deduped to one row. |
||
|
|
902cbe5915 |
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.
|