fix: add media listeners immediately when using `bind:paused` (#13502)

Fix a bug where play/pause events may never be added to the media element. Also simplifies the logic by making it an effect instead of a render effect

---------

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
pull/13529/head
Rich Harris 3 months ago committed by GitHub
parent 14ecedf33b
commit c6af26ffd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: add media listeners immediately when using `bind:paused`

@ -138,56 +138,28 @@ export function bind_playback_rate(media, get, set = get) {
* @param {(paused: boolean) => void} set
*/
export function bind_paused(media, get, set = get) {
var mounted = hydrating;
var paused = get();
var callback = () => {
var update = () => {
if (paused !== media.paused) {
paused = media.paused;
set((paused = media.paused));
}
};
if (paused == null) {
callback();
}
// Defer listening if not mounted yet so that the first canplay event doesn't cause a potentially wrong update
if (mounted) {
// If someone switches the src while media is playing, the player will pause.
// Listen to the canplay event to get notified of this situation.
listen(media, ['play', 'pause', 'canplay'], callback, false);
}
// If someone switches the src while media is playing, the player will pause.
// Listen to the canplay event to get notified of this situation.
listen(media, ['play', 'pause', 'canplay'], update, paused == null);
render_effect(() => {
paused = !!get();
if (paused !== media.paused) {
var toggle = () => {
mounted = true;
if (paused) {
media.pause();
} else {
media.play().catch(() => {
set((paused = true));
});
}
};
if (mounted) {
toggle();
// Needs to be an effect to ensure media element is mounted: else, if paused is `false` (i.e. should play right away)
// a "The play() request was interrupted by a new load request" error would be thrown because the resource isn't loaded yet.
effect(() => {
if ((paused = !!get()) !== media.paused) {
if (paused) {
media.pause();
} else {
// If this is the first invocation in dom mode, the media element isn't mounted yet,
// and therefore its resource isn't loaded yet. We need to wait for the canplay event
// in this case or else we'll get a "The play() request was interrupted by a new load request" error.
media.addEventListener(
'canplay',
() => {
listen(media, ['play', 'pause', 'canplay'], callback, false);
toggle();
},
{ once: true }
);
media.play().catch(() => {
set((paused = true));
});
}
}
});

Loading…
Cancel
Save