Repository navigation
Description
Describe the bug
Clearing src on a mounted <hyperframes-player type="video/mp4"> leaves the previous film playing. A host that clears its selected video still shows and plays that video, while ready and duration keep describing it. Setting src="" has the same effect.
Reproduced in Chrome at 4b3f70fb1b202a1422a199e8b7440a388d0b5a20, Player 0.9.26. The Player/core source is unchanged on latest main, 7bf19ab5f9d1690d596334822f251dece3921e1a, where both regression cases were rechecked and fail.
Minimal reproduction
Build the pinned source and generate a public synthetic clip:
git clone https://github.com/heygen-com/hyperframes.git hf-player-clear-src
cd hf-player-clear-src
git checkout 7bf19ab5f9d1690d596334822f251dece3921e1a
PUPPETEER_SKIP_DOWNLOAD=1 bun install --frozen-lockfile
bun run build
mkdir repro-player
cp packages/player/dist/hyperframes-player.global.js repro-player/
ffmpeg -f lavfi -i color=c=royalblue:s=320x180:r=30 -t 4 -c:v libx264 -pix_fmt yuv420p repro-player/film.mp4Save this as repro-player/repro.html:
<!doctype html>
<script src="./hyperframes-player.global.js"></script>
<hyperframes-player type="video/mp4" src="./film.mp4" muted controls
style="width:320px;height:180px"></hyperframes-player>
<script>
const player = document.querySelector('hyperframes-player');
player.addEventListener('ready', () => {
player.play();
setTimeout(() => {
player.removeAttribute('src');
// Replacing the line above with player.setAttribute('src', '') has the same bug.
setTimeout(() => console.log({
src: player.getAttribute('src'),
paused: player.paused,
ready: player.ready,
duration: player.duration,
currentTime: player.currentTime,
videoSrc: player.shadowRoot.querySelector('video').getAttribute('src'),
videoPaused: player.shadowRoot.querySelector('video').paused,
}), 250);
}, 250);
}, { once: true });
</script>Save this as repro-player/serve.mjs:
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
const files = {
'/': ['repro.html', 'text/html'],
'/hyperframes-player.global.js': ['hyperframes-player.global.js', 'text/javascript'],
'/film.mp4': ['film.mp4', 'video/mp4'],
};
createServer(async (req, res) => {
const file = files[req.url];
if (!file) { res.writeHead(404).end(); return; }
res.setHeader('Content-Type', file[1]);
res.end(await readFile(new URL(file[0], import.meta.url)));
}).listen(8080, '127.0.0.1');Steps to reproduce
- Run
node repro-player/serve.mjsand openhttp://127.0.0.1:8080/in Chrome. - The muted film starts at
ready. After 250 ms the example removessrcand then prints state 250 ms later. - Observe the blue film and Pause button remain on screen. Repeat with
player.setAttribute('src', '')instead ofremoveAttribute.
Expected behavior
The previous video is unloaded, playback is paused, ready becomes false, and the controls reset to 0:00 / 0:00. A new src can load another video.
Actual behavior
The player has no src but its inner video still has src="./film.mp4" and keeps playing. paused is false, ready is true, duration is 4, and currentTime continues advancing. The source attribute callback ignores both null and the empty string.
Environment
Linux arm64, Node 24.22.1, Bun 1.4.4, headless Chrome 153.0.8010.36 with audio muted. Local bundled Player built from the pinned commit above. No custom plugins, extensions, network media or private inputs. Safari and Firefox were not exercised.
Additional context
The existing video source reset already handles readiness, clocks and controls. The missing path is clearing the host's src, followed by unloading the inner video's source without assigning an empty URL.
Activity
docs(upstream): review c149730..9e346f9; adopt heygen-com#7338 and fi…