diff --git a/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.css b/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.css index ec6b95bfb..b36d0ef6a 100644 --- a/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.css +++ b/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.css @@ -1257,6 +1257,7 @@ /* Match qcamera.ts so the visible frame does not widen during the full-quality swap. */ aspect-ratio: 526 / 330; background: #07080b; + contain: layout paint; overflow: hidden; position: relative; width: 100%; @@ -1264,12 +1265,16 @@ .dashcam-player-overlay .dashcam-player .dashcam-video-shell video { border-radius: 0; - height: 100%; + display: block; + height: 100% !important; inset: 0; - /* Keep the visible frame size fixed when qcamera swaps to a wider full stream. */ + max-height: none; + max-width: none; + /* Crop both encodes into the same viewport instead of letting intrinsic media sizing win. */ object-fit: cover; + object-position: center center; position: absolute; - width: 100%; + width: 100% !important; } .dashcam-player-overlay .dashcam-player .dashcam-video-shell video.active { diff --git a/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.js b/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.js index 7ac402262..ceab760af 100644 --- a/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.js +++ b/starpilot/system/the_galaxy/assets/components/recordings/dashcam_routes.js @@ -359,6 +359,30 @@ async function openOverlay(route) { let upgradeTimer = null let isUpgrading = false + const clearDeferredNativeControls = video => { + if (typeof video._dashcamControlsCleanup === "function") video._dashcamControlsCleanup() + } + const setNativeControls = (video, enabled) => { + clearDeferredNativeControls(video) + video.controls = enabled + } + const deferNativeControlsUntilInteraction = video => { + clearDeferredNativeControls(video) + video.controls = false + + const restore = () => { + if (video !== activeVideo || !overlay) return + setNativeControls(video, true) + } + const events = ["pointermove", "pointerdown", "focus"] + const cleanup = () => { + events.forEach(eventName => video.removeEventListener(eventName, restore)) + delete video._dashcamControlsCleanup + } + video._dashcamControlsCleanup = cleanup + events.forEach(eventName => video.addEventListener(eventName, restore)) + } + const setPlayerMessage = (message, isError = false) => { playerState.textContent = message playerState.hidden = !message @@ -386,6 +410,7 @@ async function openOverlay(route) { upgradeController = null stagingVideo.pause() stagingVideo.removeAttribute("src") + setNativeControls(stagingVideo, false) stagingVideo.load() } @@ -452,11 +477,12 @@ async function openOverlay(route) { activeVideo.classList.remove("active") activeVideo.classList.add("staging") - activeVideo.controls = false + setNativeControls(activeVideo, false) stagingVideo.classList.remove("staging") stagingVideo.classList.add("active") - stagingVideo.controls = true + if (isPlaying) deferNativeControlsUntilInteraction(stagingVideo) + else setNativeControls(stagingVideo, true) const oldActive = activeVideo activeVideo = stagingVideo @@ -559,11 +585,11 @@ async function openOverlay(route) { stagingVideo.removeAttribute("src") stagingVideo.classList.remove("active") stagingVideo.classList.add("staging") - stagingVideo.controls = false + setNativeControls(stagingVideo, false) activeVideo.classList.remove("staging") activeVideo.classList.add("active") - activeVideo.controls = true + setNativeControls(activeVideo, true) activeVideo.src = cameraVideoUrl(segmentUrl, camera, showingPreview ? "low" : undefined) activeVideo.load() if (autoplay) activeVideo.play().catch(() => {}) @@ -737,6 +763,7 @@ function closeOverlay() { document.removeEventListener("keydown", overlay._closeOnEscape) const videos = overlay.querySelectorAll("video") videos.forEach(v => { + v._dashcamControlsCleanup?.() v.pause() v.removeAttribute("src") v.load() diff --git a/starpilot/system/the_galaxy/tests/test_dashcam_routes_helpers.py b/starpilot/system/the_galaxy/tests/test_dashcam_routes_helpers.py index 1e1a5c4c9..75a3dae8a 100644 --- a/starpilot/system/the_galaxy/tests/test_dashcam_routes_helpers.py +++ b/starpilot/system/the_galaxy/tests/test_dashcam_routes_helpers.py @@ -90,9 +90,15 @@ def test_sort_order_select_and_route_items_are_keyed_and_reactive(): def test_player_shell_keeps_both_quality_levels_at_one_fixed_size(): source = COMPONENT_CSS_PATH.read_text(encoding="utf-8") + component = COMPONENT_PATH.read_text(encoding="utf-8") assert "aspect-ratio: 526 / 330;" in source + assert "contain: layout paint;" in source + assert "height: 100% !important;" in source + assert "width: 100% !important;" in source assert "object-fit: cover;" in source + assert "deferNativeControlsUntilInteraction(stagingVideo)" in component + assert "stagingVideo.controls = true" not in component def test_groups_routes_into_today_yesterday_dates_and_unknown():