From d13ea62b05ff9e0601caf117ec8041b69e14fefc Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Wed, 30 Jul 2025 16:46:11 -0700 Subject: [PATCH] player: fixes for restarts in corner cases --- js/app/components/mobile/desktop-ui.tsx | 3 +- .../mobile/offline-counter/index.tsx | 4 +-- js/app/components/mobile/player.tsx | 2 ++ js/app/components/mobile/ui.tsx | 2 -- js/app/components/player/controls.tsx | 3 +- js/app/components/player/player.tsx | 28 --------------- js/app/components/player/video-retry.tsx | 4 +-- .../mobile-player/fullscreen.native.tsx | 11 ++++-- .../src/components/mobile-player/player.tsx | 32 ----------------- .../ui/viewer-loading-overlay.tsx | 2 ++ .../components/mobile-player/use-webrtc.tsx | 12 +++++-- .../components/mobile-player/video-retry.tsx | 20 ++++------- .../components/mobile-player/video.native.tsx | 34 +++++++++++++------ .../src/components/mobile-player/video.tsx | 3 +- .../src/livestream-store/livestream-store.tsx | 18 +++++++++- .../src/player-store/player-state.tsx | 4 --- .../src/player-store/player-store.tsx | 3 -- 17 files changed, 80 insertions(+), 105 deletions(-) diff --git a/js/app/components/mobile/desktop-ui.tsx b/js/app/components/mobile/desktop-ui.tsx index 23a526cc..2da27e0d 100644 --- a/js/app/components/mobile/desktop-ui.tsx +++ b/js/app/components/mobile/desktop-ui.tsx @@ -2,6 +2,7 @@ import { PlayerUI, Toast, useLivestreamInfo, + useOffline, usePlayerDimensions, usePlayerStore, useSegment, @@ -50,7 +51,7 @@ export function DesktopUi() { const { width, height } = usePlayerDimensions(); const { safeAreaInsets, shouldShowFloatingMetrics } = useResponsiveLayout(); - const offline = usePlayerStore((state) => state.offline); + const offline = useOffline(); const showMetrics = usePlayerStore((state) => state.showDebugInfo); const pipAction = usePlayerStore((state) => state.pipAction); const videoRef = usePlayerStore((state) => state.videoRef); diff --git a/js/app/components/mobile/offline-counter/index.tsx b/js/app/components/mobile/offline-counter/index.tsx index 2ff6d42d..e3a68726 100644 --- a/js/app/components/mobile/offline-counter/index.tsx +++ b/js/app/components/mobile/offline-counter/index.tsx @@ -1,6 +1,6 @@ import { Text, - usePlayerStore, + useOffline, useSegment, View, zero, @@ -14,7 +14,7 @@ interface OfflineCounterProps { } export function OfflineCounter({ isMobile = false }: OfflineCounterProps) { - const offline = usePlayerStore((state) => state.offline); + const offline = useOffline(); const segment = useSegment(); // Live timer for offline overlay diff --git a/js/app/components/mobile/player.tsx b/js/app/components/mobile/player.tsx index 9da00f14..146f1ec8 100644 --- a/js/app/components/mobile/player.tsx +++ b/js/app/components/mobile/player.tsx @@ -6,6 +6,7 @@ import { Player as PlayerInnerInner, PlayerProps, PlayerProvider, + PlayerUI, Text, usePlayerDimensions, usePlayerStore, @@ -227,6 +228,7 @@ export function PlayerInner( > {(showBottomMetaPanel || fullscreen) && } + diff --git a/js/app/components/mobile/ui.tsx b/js/app/components/mobile/ui.tsx index 643b8f29..d27e7b1d 100644 --- a/js/app/components/mobile/ui.tsx +++ b/js/app/components/mobile/ui.tsx @@ -94,7 +94,6 @@ export function MobileUi() { return ( <> - - state.setMuted, props.playerId); const showControls = usePlayerStore((state) => state.showControls, playerId); const setPlayTime = usePlayerStore((state) => state.setPlayTime, playerId); - const offline = usePlayerStore((state) => state.offline, playerId); + const offline = useOffline(); const muteWasForced = usePlayerStore( (state) => state.muteWasForced, playerId, diff --git a/js/app/components/player/player.tsx b/js/app/components/player/player.tsx index 5a3d139c..3801361c 100644 --- a/js/app/components/player/player.tsx +++ b/js/app/components/player/player.tsx @@ -49,9 +49,6 @@ export function PlayerInner(props: Partial) { // Will get the first player ID from the store const playerId = getFirstPlayerID(); - const playing = usePlayerStore((x) => x.status === PlayerStatus.PLAYING); - - const setOffline = usePlayerStore((x) => x.setOffline); const setIngest = usePlayerStore((x) => x.setIngestConnectionState); const clearControlsTimeout = usePlayerStore((x) => x.clearControlsTimeout); @@ -80,31 +77,6 @@ export function PlayerInner(props: Partial) { const segment = useSegment(); const [lastCheck, setLastCheck] = useState(0); - useEffect(() => { - if (playing) { - setOffline(false); - return; - } - if (!segment) { - setOffline(false); - return; - } - const startTime = Date.parse(segment.startTime); - if (!startTime) { - console.error("startTime is not a number", segment.startTime); - return; - } - const timeSinceStart = Date.now() - startTime; - if (timeSinceStart > OFFLINE_THRESHOLD) { - setOffline(true); - return; - } - const handle = setTimeout(() => { - setLastCheck(Date.now()); - }, 1000); - return () => clearTimeout(handle); - }, [segment, playing, lastCheck]); - return ( diff --git a/js/app/components/player/video-retry.tsx b/js/app/components/player/video-retry.tsx index e5940517..a993760b 100644 --- a/js/app/components/player/video-retry.tsx +++ b/js/app/components/player/video-retry.tsx @@ -1,4 +1,4 @@ -import { usePlayerStore } from "@streamplace/components"; +import { useOffline } from "@streamplace/components"; import React, { useEffect, useRef, useState } from "react"; export default function VideoRetry(props: { children: React.ReactNode }) { @@ -6,7 +6,7 @@ export default function VideoRetry(props: { children: React.ReactNode }) { const [retries, setRetries] = useState(0); const [hasStarted, setHasStarted] = useState(false); - const offline = usePlayerStore((x) => x.offline); + const offline = useOffline(); useEffect(() => { if (!offline && !hasStarted) { diff --git a/js/components/src/components/mobile-player/fullscreen.native.tsx b/js/components/src/components/mobile-player/fullscreen.native.tsx index 1bf45381..f525c60c 100644 --- a/js/components/src/components/mobile-player/fullscreen.native.tsx +++ b/js/components/src/components/mobile-player/fullscreen.native.tsx @@ -4,7 +4,12 @@ import { useEffect, useRef, useState } from "react"; import { BackHandler, Dimensions, StyleSheet, View } from "react-native"; import { SystemBars } from "react-native-edge-to-edge"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { PlayerProtocol, useLivestreamStore, usePlayerStore } from "../.."; +import { + PlayerProtocol, + useLivestreamStore, + usePlayerStore, + VideoRetry, +} from "../.."; import Video from "./video.native"; // Standard 16:9 video aspect ratio @@ -166,7 +171,9 @@ export function Fullscreen(props: { src: string; children?: React.ReactNode }) { // Normal non-fullscreen mode return ( <> -