feat: add readyState binding for media elements (#6843)

Closes #6666

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/8342/head
Brad Dougherty 2 years ago committed by GitHub
parent c611f318d2
commit dd371f58fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -845,6 +845,7 @@ export interface HTMLMediaAttributes<T extends HTMLMediaElement> extends HTMLAtt
*/ */
volume?: number | undefined | null; volume?: number | undefined | null;
readonly 'bind:readyState'?: 0 | 1 | 2 | 3 | 4 | undefined | null;
readonly 'bind:duration'?: number | undefined | null; readonly 'bind:duration'?: number | undefined | null;
readonly 'bind:buffered'?: SvelteMediaTimeRange[] | undefined | null; readonly 'bind:buffered'?: SvelteMediaTimeRange[] | undefined | null;
readonly 'bind:played'?: SvelteMediaTimeRange[] | undefined | null; readonly 'bind:played'?: SvelteMediaTimeRange[] | undefined | null;

@ -714,7 +714,7 @@ Elements with the `contenteditable` attribute support `innerHTML` and `textConte
--- ---
Media elements (`<audio>` and `<video>`) have their own set of bindings — six *readonly* ones... Media elements (`<audio>` and `<video>`) have their own set of bindings — seven *readonly* ones...
* `duration` (readonly) — the total duration of the video, in seconds * `duration` (readonly) — the total duration of the video, in seconds
* `buffered` (readonly) — an array of `{start, end}` objects * `buffered` (readonly) — an array of `{start, end}` objects
@ -722,6 +722,7 @@ Media elements (`<audio>` and `<video>`) have their own set of bindings — six
* `seekable` (readonly) — ditto * `seekable` (readonly) — ditto
* `seeking` (readonly) — boolean * `seeking` (readonly) — boolean
* `ended` (readonly) — boolean * `ended` (readonly) — boolean
* `readyState` (readonly) — number between (and including) 0 and 4
...and five *two-way* bindings: ...and five *two-way* bindings:
@ -742,6 +743,7 @@ Videos additionally have readonly `videoWidth` and `videoHeight` bindings.
bind:seekable bind:seekable
bind:seeking bind:seeking
bind:ended bind:ended
bind:readyState
bind:currentTime bind:currentTime
bind:playbackRate bind:playbackRate
bind:paused bind:paused

@ -24,7 +24,8 @@ const read_only_media_attributes = new Set([
'videoHeight', 'videoHeight',
'videoWidth', 'videoWidth',
'naturalWidth', 'naturalWidth',
'naturalHeight' 'naturalHeight',
'readyState'
]); ]);
export default class Binding extends Node { export default class Binding extends Node {

@ -980,7 +980,8 @@ export default class Element extends Node {
name === 'muted' || name === 'muted' ||
name === 'playbackRate' || name === 'playbackRate' ||
name === 'seeking' || name === 'seeking' ||
name === 'ended' name === 'ended' ||
name === 'readyState'
) { ) {
if (this.name !== 'audio' && this.name !== 'video') { if (this.name !== 'audio' && this.name !== 'video') {
return component.error(binding, compiler_errors.invalid_binding_element_with('audio> or <video>', name)); return component.error(binding, compiler_errors.invalid_binding_element_with('audio> or <video>', name));

@ -63,13 +63,11 @@ const events = [
filter: (node: Element, _name: string) => filter: (node: Element, _name: string) =>
node.name === 'input' && node.get_static_attribute_value('type') === 'range' node.name === 'input' && node.get_static_attribute_value('type') === 'range'
}, },
{ {
event_names: ['elementresize'], event_names: ['elementresize'],
filter: (_node: Element, name: string) => filter: (_node: Element, name: string) =>
regex_dimensions.test(name) regex_dimensions.test(name)
}, },
// media events // media events
{ {
event_names: ['timeupdate'], event_names: ['timeupdate'],
@ -131,7 +129,14 @@ const events = [
node.is_media_node() && node.is_media_node() &&
(name === 'videoHeight' || name === 'videoWidth') (name === 'videoHeight' || name === 'videoWidth')
}, },
{
// from https://html.spec.whatwg.org/multipage/media.html#ready-states
// and https://html.spec.whatwg.org/multipage/media.html#loading-the-media-resource
event_names: ['loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'emptied'],
filter: (node: Element, name: string) =>
node.is_media_node() &&
name === 'readyState'
},
// details event // details event
{ {
event_names: ['toggle'], event_names: ['toggle'],

@ -30,18 +30,19 @@ function create_fragment(ctx) {
audio_updating = true; audio_updating = true;
} }
/*audio_timeupdate_handler*/ ctx[13].call(audio); /*audio_timeupdate_handler*/ ctx[14].call(audio);
} }
return { return {
c() { c() {
audio = element("audio"); audio = element("audio");
if (/*buffered*/ ctx[0] === void 0) add_render_callback(() => /*audio_progress_handler*/ ctx[11].call(audio)); if (/*buffered*/ ctx[0] === void 0) add_render_callback(() => /*audio_progress_handler*/ ctx[12].call(audio));
if (/*buffered*/ ctx[0] === void 0 || /*seekable*/ ctx[1] === void 0) add_render_callback(() => /*audio_loadedmetadata_handler*/ ctx[12].call(audio)); if (/*buffered*/ ctx[0] === void 0 || /*seekable*/ ctx[1] === void 0) add_render_callback(() => /*audio_loadedmetadata_handler*/ ctx[13].call(audio));
if (/*played*/ ctx[2] === void 0 || /*currentTime*/ ctx[3] === void 0 || /*ended*/ ctx[10] === void 0) add_render_callback(audio_timeupdate_handler); if (/*played*/ ctx[2] === void 0 || /*currentTime*/ ctx[3] === void 0 || /*ended*/ ctx[10] === void 0) add_render_callback(audio_timeupdate_handler);
if (/*duration*/ ctx[4] === void 0) add_render_callback(() => /*audio_durationchange_handler*/ ctx[14].call(audio)); if (/*duration*/ ctx[4] === void 0) add_render_callback(() => /*audio_durationchange_handler*/ ctx[15].call(audio));
if (/*seeking*/ ctx[9] === void 0) add_render_callback(() => /*audio_seeking_seeked_handler*/ ctx[18].call(audio)); if (/*seeking*/ ctx[9] === void 0) add_render_callback(() => /*audio_seeking_seeked_handler*/ ctx[19].call(audio));
if (/*ended*/ ctx[10] === void 0) add_render_callback(() => /*audio_ended_handler*/ ctx[19].call(audio)); if (/*ended*/ ctx[10] === void 0) add_render_callback(() => /*audio_ended_handler*/ ctx[20].call(audio));
if (/*readyState*/ ctx[11] === void 0) add_render_callback(() => /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21].call(audio));
}, },
m(target, anchor) { m(target, anchor) {
insert(target, audio, anchor); insert(target, audio, anchor);
@ -58,17 +59,24 @@ function create_fragment(ctx) {
if (!mounted) { if (!mounted) {
dispose = [ dispose = [
listen(audio, "progress", /*audio_progress_handler*/ ctx[11]), listen(audio, "progress", /*audio_progress_handler*/ ctx[12]),
listen(audio, "loadedmetadata", /*audio_loadedmetadata_handler*/ ctx[12]), listen(audio, "loadedmetadata", /*audio_loadedmetadata_handler*/ ctx[13]),
listen(audio, "timeupdate", audio_timeupdate_handler), listen(audio, "timeupdate", audio_timeupdate_handler),
listen(audio, "durationchange", /*audio_durationchange_handler*/ ctx[14]), listen(audio, "durationchange", /*audio_durationchange_handler*/ ctx[15]),
listen(audio, "play", /*audio_play_pause_handler*/ ctx[15]), listen(audio, "play", /*audio_play_pause_handler*/ ctx[16]),
listen(audio, "pause", /*audio_play_pause_handler*/ ctx[15]), listen(audio, "pause", /*audio_play_pause_handler*/ ctx[16]),
listen(audio, "volumechange", /*audio_volumechange_handler*/ ctx[16]), listen(audio, "volumechange", /*audio_volumechange_handler*/ ctx[17]),
listen(audio, "ratechange", /*audio_ratechange_handler*/ ctx[17]), listen(audio, "ratechange", /*audio_ratechange_handler*/ ctx[18]),
listen(audio, "seeking", /*audio_seeking_seeked_handler*/ ctx[18]), listen(audio, "seeking", /*audio_seeking_seeked_handler*/ ctx[19]),
listen(audio, "seeked", /*audio_seeking_seeked_handler*/ ctx[18]), listen(audio, "seeked", /*audio_seeking_seeked_handler*/ ctx[19]),
listen(audio, "ended", /*audio_ended_handler*/ ctx[19]) listen(audio, "ended", /*audio_ended_handler*/ ctx[20]),
listen(audio, "loadedmetadata", /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21]),
listen(audio, "loadeddata", /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21]),
listen(audio, "canplay", /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21]),
listen(audio, "canplaythrough", /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21]),
listen(audio, "playing", /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21]),
listen(audio, "waiting", /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21]),
listen(audio, "emptied", /*audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[21])
]; ];
mounted = true; mounted = true;
@ -119,6 +127,7 @@ function instance($$self, $$props, $$invalidate) {
let { playbackRate } = $$props; let { playbackRate } = $$props;
let { seeking } = $$props; let { seeking } = $$props;
let { ended } = $$props; let { ended } = $$props;
let { readyState } = $$props;
function audio_progress_handler() { function audio_progress_handler() {
buffered = time_ranges_to_array(this.buffered); buffered = time_ranges_to_array(this.buffered);
@ -173,6 +182,11 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(10, ended); $$invalidate(10, ended);
} }
function audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler() {
readyState = this.readyState;
$$invalidate(11, readyState);
}
$$self.$$set = $$props => { $$self.$$set = $$props => {
if ('buffered' in $$props) $$invalidate(0, buffered = $$props.buffered); if ('buffered' in $$props) $$invalidate(0, buffered = $$props.buffered);
if ('seekable' in $$props) $$invalidate(1, seekable = $$props.seekable); if ('seekable' in $$props) $$invalidate(1, seekable = $$props.seekable);
@ -185,6 +199,7 @@ function instance($$self, $$props, $$invalidate) {
if ('playbackRate' in $$props) $$invalidate(8, playbackRate = $$props.playbackRate); if ('playbackRate' in $$props) $$invalidate(8, playbackRate = $$props.playbackRate);
if ('seeking' in $$props) $$invalidate(9, seeking = $$props.seeking); if ('seeking' in $$props) $$invalidate(9, seeking = $$props.seeking);
if ('ended' in $$props) $$invalidate(10, ended = $$props.ended); if ('ended' in $$props) $$invalidate(10, ended = $$props.ended);
if ('readyState' in $$props) $$invalidate(11, readyState = $$props.readyState);
}; };
return [ return [
@ -199,6 +214,7 @@ function instance($$self, $$props, $$invalidate) {
playbackRate, playbackRate,
seeking, seeking,
ended, ended,
readyState,
audio_progress_handler, audio_progress_handler,
audio_loadedmetadata_handler, audio_loadedmetadata_handler,
audio_timeupdate_handler, audio_timeupdate_handler,
@ -207,7 +223,8 @@ function instance($$self, $$props, $$invalidate) {
audio_volumechange_handler, audio_volumechange_handler,
audio_ratechange_handler, audio_ratechange_handler,
audio_seeking_seeked_handler, audio_seeking_seeked_handler,
audio_ended_handler audio_ended_handler,
audio_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler
]; ];
} }
@ -226,9 +243,10 @@ class Component extends SvelteComponent {
muted: 7, muted: 7,
playbackRate: 8, playbackRate: 8,
seeking: 9, seeking: 9,
ended: 10 ended: 10,
readyState: 11
}); });
} }
} }
export default Component; export default Component;

@ -10,6 +10,7 @@
export let playbackRate; export let playbackRate;
export let seeking; export let seeking;
export let ended; export let ended;
export let readyState;
</script> </script>
<audio bind:buffered bind:seekable bind:played bind:currentTime bind:duration bind:paused bind:volume bind:muted bind:playbackRate bind:seeking bind:ended/> <audio bind:buffered bind:seekable bind:played bind:currentTime bind:duration bind:paused bind:volume bind:muted bind:playbackRate bind:seeking bind:ended bind:readyState/>

@ -30,23 +30,31 @@ function create_fragment(ctx) {
video_updating = true; video_updating = true;
} }
/*video_timeupdate_handler*/ ctx[4].call(video); /*video_timeupdate_handler*/ ctx[5].call(video);
} }
return { return {
c() { c() {
video = element("video"); video = element("video");
if (/*videoHeight*/ ctx[1] === void 0 || /*videoWidth*/ ctx[2] === void 0) add_render_callback(() => /*video_resize_handler*/ ctx[5].call(video)); if (/*videoHeight*/ ctx[1] === void 0 || /*videoWidth*/ ctx[2] === void 0) add_render_callback(() => /*video_resize_handler*/ ctx[6].call(video));
add_render_callback(() => /*video_elementresize_handler*/ ctx[6].call(video)); add_render_callback(() => /*video_elementresize_handler*/ ctx[7].call(video));
if (/*readyState*/ ctx[4] === void 0) add_render_callback(() => /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8].call(video));
}, },
m(target, anchor) { m(target, anchor) {
insert(target, video, anchor); insert(target, video, anchor);
video_resize_listener = add_resize_listener(video, /*video_elementresize_handler*/ ctx[6].bind(video)); video_resize_listener = add_resize_listener(video, /*video_elementresize_handler*/ ctx[7].bind(video));
if (!mounted) { if (!mounted) {
dispose = [ dispose = [
listen(video, "timeupdate", video_timeupdate_handler), listen(video, "timeupdate", video_timeupdate_handler),
listen(video, "resize", /*video_resize_handler*/ ctx[5]) listen(video, "resize", /*video_resize_handler*/ ctx[6]),
listen(video, "loadedmetadata", /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8]),
listen(video, "loadeddata", /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8]),
listen(video, "canplay", /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8]),
listen(video, "canplaythrough", /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8]),
listen(video, "playing", /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8]),
listen(video, "waiting", /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8]),
listen(video, "emptied", /*video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler*/ ctx[8])
]; ];
mounted = true; mounted = true;
@ -75,6 +83,7 @@ function instance($$self, $$props, $$invalidate) {
let { videoHeight } = $$props; let { videoHeight } = $$props;
let { videoWidth } = $$props; let { videoWidth } = $$props;
let { offsetWidth } = $$props; let { offsetWidth } = $$props;
let { readyState } = $$props;
function video_timeupdate_handler() { function video_timeupdate_handler() {
currentTime = this.currentTime; currentTime = this.currentTime;
@ -93,11 +102,17 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(3, offsetWidth); $$invalidate(3, offsetWidth);
} }
function video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler() {
readyState = this.readyState;
$$invalidate(4, readyState);
}
$$self.$$set = $$props => { $$self.$$set = $$props => {
if ('currentTime' in $$props) $$invalidate(0, currentTime = $$props.currentTime); if ('currentTime' in $$props) $$invalidate(0, currentTime = $$props.currentTime);
if ('videoHeight' in $$props) $$invalidate(1, videoHeight = $$props.videoHeight); if ('videoHeight' in $$props) $$invalidate(1, videoHeight = $$props.videoHeight);
if ('videoWidth' in $$props) $$invalidate(2, videoWidth = $$props.videoWidth); if ('videoWidth' in $$props) $$invalidate(2, videoWidth = $$props.videoWidth);
if ('offsetWidth' in $$props) $$invalidate(3, offsetWidth = $$props.offsetWidth); if ('offsetWidth' in $$props) $$invalidate(3, offsetWidth = $$props.offsetWidth);
if ('readyState' in $$props) $$invalidate(4, readyState = $$props.readyState);
}; };
return [ return [
@ -105,9 +120,11 @@ function instance($$self, $$props, $$invalidate) {
videoHeight, videoHeight,
videoWidth, videoWidth,
offsetWidth, offsetWidth,
readyState,
video_timeupdate_handler, video_timeupdate_handler,
video_resize_handler, video_resize_handler,
video_elementresize_handler video_elementresize_handler,
video_loadedmetadata_loadeddata_canplay_canplaythrough_playing_waiting_emptied_handler
]; ];
} }
@ -119,9 +136,10 @@ class Component extends SvelteComponent {
currentTime: 0, currentTime: 0,
videoHeight: 1, videoHeight: 1,
videoWidth: 2, videoWidth: 2,
offsetWidth: 3 offsetWidth: 3,
readyState: 4
}); });
} }
} }
export default Component; export default Component;

@ -3,6 +3,7 @@
export let videoHeight; export let videoHeight;
export let videoWidth; export let videoWidth;
export let offsetWidth; export let offsetWidth;
export let readyState;
</script> </script>
<video bind:currentTime bind:videoHeight bind:videoWidth bind:offsetWidth/> <video bind:currentTime bind:videoHeight bind:videoWidth bind:offsetWidth bind:readyState/>

Loading…
Cancel
Save