From d2083e5e936e09468233e291e40d811bd8e7ed46 Mon Sep 17 00:00:00 2001 From: wanxiankai Date: Sat, 18 Jul 2026 23:17:09 +0800 Subject: [PATCH] fix: update duration when media duration changes --- src/Player.tsx | 28 ++++++++++++++++++++++++++++ test/Player.tsx | 21 +++++++++++++++++++++ test/helpers/server-safe-globals.js | 8 +++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/Player.tsx b/src/Player.tsx index c88fab27..4eeceb2f 100644 --- a/src/Player.tsx +++ b/src/Player.tsx @@ -15,6 +15,7 @@ const Player: Player = React.forwardRef((props, ref) => { const Player = props.activePlayer; const playerRef = useRef(null); const startOnPlayRef = useRef(true); + const lastDurationRef = useRef(); useEffect(() => { if (!playerRef.current) return; @@ -51,6 +52,7 @@ const Player: Player = React.forwardRef((props, ref) => { const handleLoadStart = (event: SyntheticEvent) => { startOnPlayRef.current = true; + lastDurationRef.current = undefined; props.onReady?.(); props.onLoadStart?.(event); }; @@ -63,6 +65,29 @@ const Player: Player = React.forwardRef((props, ref) => { props.onPlay?.(event); }; + const handleDurationChange = (event: SyntheticEvent) => { + lastDurationRef.current = event.currentTarget.duration; + props.onDurationChange?.(event); + }; + + const checkDurationChange = (event: SyntheticEvent) => { + const duration = event.currentTarget.duration; + if (Number.isNaN(duration) || duration === lastDurationRef.current) return; + + lastDurationRef.current = duration; + event.currentTarget.dispatchEvent(new Event('durationchange')); + }; + + const handleProgress = (event: SyntheticEvent) => { + checkDurationChange(event); + props.onProgress?.(event); + }; + + const handleTimeUpdate = (event: SyntheticEvent) => { + checkDurationChange(event); + props.onTimeUpdate?.(event); + }; + if (!Player) { return null; } @@ -109,6 +134,9 @@ const Player: Player = React.forwardRef((props, ref) => { config={props.config} onLoadStart={handleLoadStart} onPlay={handlePlay} + onDurationChange={handleDurationChange} + onProgress={handleProgress} + onTimeUpdate={handleTimeUpdate} > {props.children} diff --git a/test/Player.tsx b/test/Player.tsx index 48201759..5c08fb3e 100644 --- a/test/Player.tsx +++ b/test/Player.tsx @@ -112,3 +112,24 @@ await test('video.duration', async (t) => { t.equal(videoRef.current?.duration, 10); }); + +test('video.durationchange is dispatched when duration changes during timeupdate', (t) => { + const videoRef: React.Ref = React.createRef(); + const wrapper = render(); + const video = videoRef.current; + const durationchange = sinon.fake(); + + video?.addEventListener('durationchange', durationchange); + + if (video) { + video.duration = 20; + act(() => { + wrapper.root.findByType('video').props.onTimeUpdate({ currentTarget: video }); + }); + act(() => { + wrapper.root.findByType('video').props.onTimeUpdate({ currentTarget: video }); + }); + } + + t.ok(durationchange.calledOnce); +}); diff --git a/test/helpers/server-safe-globals.js b/test/helpers/server-safe-globals.js index 471848c9..621a4781 100644 --- a/test/helpers/server-safe-globals.js +++ b/test/helpers/server-safe-globals.js @@ -107,4 +107,10 @@ const globalThisShim = { globalThis.document = document; globalThis.window = globalThisShim; -Object.assign(globalThis, globalThisShim); +for (const [key, value] of Object.entries(globalThisShim)) { + Object.defineProperty(globalThis, key, { + value, + configurable: true, + writable: true, + }); +}