Skip to content

feat: rework archive video cards with custom player and speed control#115

Merged
infinitel8p merged 12 commits intomainfrom
feat/72-87-archive-video-player
Apr 3, 2026
Merged

feat: rework archive video cards with custom player and speed control#115
infinitel8p merged 12 commits intomainfrom
feat/72-87-archive-video-player

Conversation

@infinitel8p
Copy link
Copy Markdown
Owner

@infinitel8p infinitel8p commented Apr 2, 2026

Summary

Changes

Archive cards

  • Grid view: Cards now show a thumbnail image with a play button overlay instead of a <video> element. Sprite hover-scrub renders correctly above the thumbnail (previously hidden behind <video>)
  • List view: Clicking the thumbnail or filename opens the player. Fallback placeholder icon when no thumbnail exists
  • Removed lazyVideo action (no longer needed)

Video player modal (VideoPlayerModal.svelte)

  • Custom seek bar with drag support (mouse + touch)
  • Play/pause, time display, volume toggle, fullscreen, download
  • Speed selector dropdown (0.5x, 1x, 2x, 4x, 8x) - resets to 1x on navigation
  • Previous/next arrows to step through filtered recordings
  • Auto-hiding controls after 3s of playback inactivity
  • Keyboard: Space/K = play/pause, Left/Right = seek 10s, Up/Down = volume, F = fullscreen, M = mute, Escape = close
  • Scroll lock when modal is open
  • {#key video.path} ensures clean video element reset on navigation

Test plan

  • Open archive, verify video cards show thumbnails (not video elements)
  • Hover a card with a sprite sheet - verify scrub overlay appears above thumbnail
  • Click a card - verify modal opens with auto-play
  • Test speed selector: change to 2x, 4x, 8x - verify playback accelerates
  • Navigate to next/prev video - verify speed resets to 1x
  • Test keyboard shortcuts (Space, arrows, F, M, Escape)
  • Test on mobile: tap to play, controls auto-hide, touch-drag seek bar
  • Verify list view rows open the player when clicked
  • Test fullscreen toggle
  • Verify download button works from within the player
  • Check i18n: switch language, verify player labels translate

Closes #72, closes #87

Replace native <video controls> in archive cards with thumbnail images
and a custom modal player. Fixes sprite hover-scrub z-order (#72) and
adds playback speed control (#87).

Card changes:
- Grid cards show thumbnail <img> instead of <video>, with a play
  button overlay that fades in on hover
- Sprite hover-scrub now renders above the thumbnail, fixing the
  previous z-order issue where it was hidden behind the video element
- List view rows are clickable to open the player
- Fallback placeholder icon when no thumbnail is available

Modal player:
- Custom controls: seek bar, play/pause, time display, volume,
  fullscreen, download
- Speed selector (0.5x, 1x, 2x, 4x, 8x), resets to 1x on navigation
- Previous/next arrows to step through filtered recordings
- Auto-hiding controls after 3s of inactivity
- Keyboard shortcuts: Space/K play, arrows seek/volume, F fullscreen,
  M mute, Escape close
- Consistent cross-browser experience (no native controls)

Also adds i18n keys for player UI in all 5 languages and updates
archive documentation.

Closes #72, closes #87
@infinitel8p infinitel8p added this to the Archive Rework milestone Apr 2, 2026
- Change logo flex alignment from items-center to items-start so the
  icon stays anchored when the git branch label appears
- Cache deploy_info in sessionStorage so the API is only called once
  per browser session instead of on every page navigation
- Add explicit space before @ separator in the branch label
Read git info at build time via vite.define in astro.config.mjs and
inject as compile-time constants. The Navbar now renders the branch
label on first paint with no pop-in or layout shift.

Removes the runtime deploy_info fetch and sessionStorage caching from
the Navbar entirely.
Switch stats page branch display from runtime API data to the same
build-time constants used by the Navbar. Fixes missing space before
the @ separator.
The CSS percentage formula for background-position-x was wrong: negative
percentages with an oversized background (1000%) flip direction because
(container - image) is negative. Frame 0 worked by coincidence (0% is
0% either way), but all other frames shifted the sprite off-screen,
revealing the thumbnail underneath.

Fix: use positive percentage with (FRAMES - 1) divisor so frame 0 maps
to 0% and frame 9 maps to 100%, which CSS resolves correctly.
- rAF-throttle sprite hover-scrub mousemove (was firing 60+ times/sec
  causing render thrashing on Pi)
- rAF-throttle seek bar drag in video player modal
- Debounce search input by 200ms so filtering/sorting only runs after
  the user stops typing, not on every keystroke
- Parallelize archive/snapshot/timelapse fetches with Promise.all
  instead of sequential calls (3x faster page load)
- Debounce SSE archive_updated refetch by 500ms to batch rapid events
Switch EventTracker, HealthTracker, AccessLog, and ActivityTimeline
from client:load to client:visible so they only hydrate and fetch
data when scrolled into view. Reduces initial JS parse time and API
requests on the Pi.
Backend:
- Change psutil.cpu_percent from interval=1 (blocks 1 second per
  request) to interval=0 (instant, compares with previous sample).
  Prime the counter at import time so the first call is meaningful.
- Add 30-second TTL cache to get_videos() and get_snapshots() to avoid
  full filesystem scans on every /archive and /snapshots request.
  Cache is invalidated on video delete, snapshot delete, and recording
  stop (when sidecars are ready).
- Cache auto-invalidates when VideoSaveLocation setting changes.
- Remove backdrop-blur from all player overlay buttons (close, nav
  arrows, play button, speed menu) - barely visible on 92% black
  backdrop and expensive on Pi GPU
- Add loading spinner when video is buffering (waiting/canplay events)
- Close speed menu on outside click (data-speed-menu boundary)
- Bump nav arrows to 44px touch targets (h-11 w-11)
- Truncate long filenames in the info bar on mobile
- Use solid bg-black/60 instead of backdrop-blur on grid/list play
  overlays for Pi performance
The blur is rendered in the client browser (phone/PC), not on the Pi.
Modern devices handle backdrop-blur-sm fine, and it gives the glassy
look that matches the rest of the UI.
Fullscreening the fixed overlay wrapper didn't enlarge the video since
it was constrained by max-w-5xl. Now fullscreens the player container
directly and removes max-width, padding, and border-radius constraints
when in fullscreen mode so the video fills the screen.
The download attribute on <a> is ignored for cross-origin URLs (frontend
on :4321, backend on :5005). Add a download query parameter to the
/stream_video endpoint that sets Content-Disposition: attachment, forcing
the browser to save the file instead of playing it inline.
@infinitel8p infinitel8p merged commit 006308a into main Apr 3, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[S] Video playback speed control [M] Rework archive video cards — thumbnail, sprite scrub, and playback

1 participant