@gradio/video 0.20.2 → 0.20.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher, onMount, onDestroy } from "svelte";
2
+ import { onMount, onDestroy } from "svelte";
3
3
  import { Play, Pause, Maximize, Undo } from "@gradio/icons";
4
4
  import Video from "./Video.svelte";
5
5
  import VideoControls from "./VideoControls.svelte";
@@ -10,41 +10,72 @@
10
10
  import { format_time } from "@gradio/utils";
11
11
  import type { I18nFormatter } from "@gradio/utils";
12
12
 
13
- export let root = "";
14
- export let src: string;
15
- export let subtitle: string | null = null;
16
- export let mirror: boolean;
17
- export let autoplay: boolean;
18
- export let loop: boolean;
19
- export let label = "test";
20
- export let interactive = false;
21
- export let handle_change: (video: FileData) => void = () => {};
22
- export let handle_reset_value: () => void = () => {};
23
- export let upload: Client["upload"];
24
- export let is_stream: boolean | undefined;
25
- export let i18n: I18nFormatter;
26
- export let show_download_button = false;
27
- export let value: FileData | null = null;
28
- export let handle_clear: () => void = () => {};
29
- export let has_change_history = false;
30
- export let playback_position = 0;
31
-
32
- const dispatch = createEventDispatcher<{
33
- play: undefined;
34
- pause: undefined;
35
- stop: undefined;
36
- end: undefined;
37
- clear: undefined;
38
- }>();
39
-
40
- let time = 0;
41
- let duration: number;
42
- let paused = true;
43
- let video: HTMLVideoElement;
44
- let processingVideo = false;
45
- let show_volume_slider = false;
46
- let current_volume = 1;
47
- let is_fullscreen = false;
13
+ interface Props {
14
+ root?: string;
15
+ src: string;
16
+ subtitle?: string | null;
17
+ mirror: boolean;
18
+ autoplay: boolean;
19
+ loop: boolean;
20
+ label?: string;
21
+ interactive?: boolean;
22
+ handle_change?: (video: FileData) => void;
23
+ handle_reset_value?: () => void;
24
+ upload: Client["upload"];
25
+ is_stream?: boolean;
26
+ i18n: I18nFormatter;
27
+ show_download_button?: boolean;
28
+ value?: FileData | null;
29
+ handle_clear?: () => void;
30
+ has_change_history?: boolean;
31
+ playback_position?: number;
32
+ onplay?: () => void;
33
+ onpause?: () => void;
34
+ onstop?: () => void;
35
+ onend?: () => void;
36
+ onerror?: (error: string) => void;
37
+ onloadstart?: () => void;
38
+ onloadeddata?: () => void;
39
+ onloadedmetadata?: () => void;
40
+ }
41
+
42
+ let {
43
+ root = "",
44
+ src,
45
+ subtitle = null,
46
+ mirror,
47
+ autoplay,
48
+ loop,
49
+ label = "test",
50
+ interactive = false,
51
+ handle_change = () => {},
52
+ handle_reset_value = () => {},
53
+ upload,
54
+ is_stream = undefined,
55
+ i18n,
56
+ show_download_button = false,
57
+ value = null,
58
+ handle_clear = () => {},
59
+ has_change_history = false,
60
+ playback_position = $bindable(),
61
+ onplay,
62
+ onpause,
63
+ onstop,
64
+ onend,
65
+ onerror,
66
+ onloadstart,
67
+ onloadeddata,
68
+ onloadedmetadata
69
+ }: Props = $props();
70
+
71
+ let time = $state(0);
72
+ let duration = $state<number>(0);
73
+ let paused = $state(true);
74
+ let video = $state<HTMLVideoElement>();
75
+ let processingVideo = $state(false);
76
+ let show_volume_slider = $state(false);
77
+ let current_volume = $state(1);
78
+ let is_fullscreen = $state(false);
48
79
 
49
80
  function handleMove(e: TouchEvent | MouseEvent): void {
50
81
  if (!duration) return;
@@ -67,6 +98,7 @@
67
98
  }
68
99
 
69
100
  async function play_pause(): Promise<void> {
101
+ if (!video) return;
70
102
  if (document.fullscreenElement != video) {
71
103
  const isPlaying =
72
104
  video.currentTime > 0 &&
@@ -81,6 +113,7 @@
81
113
  }
82
114
 
83
115
  function handle_click(e: MouseEvent): void {
116
+ if (!duration) return;
84
117
  const { left, right } = (
85
118
  e.currentTarget as HTMLProgressElement
86
119
  ).getBoundingClientRect();
@@ -88,8 +121,8 @@
88
121
  }
89
122
 
90
123
  function handle_end(): void {
91
- dispatch("stop");
92
- dispatch("end");
124
+ onstop?.();
125
+ onend?.();
93
126
  }
94
127
 
95
128
  const handle_trim_video = async (videoBlob: Blob): Promise<void> => {
@@ -101,6 +134,7 @@
101
134
  };
102
135
 
103
136
  function open_full_screen(): void {
137
+ if (!video) return;
104
138
  if (!is_fullscreen) {
105
139
  video.requestFullscreen();
106
140
  } else {
@@ -140,30 +174,41 @@
140
174
  }
141
175
  });
142
176
 
143
- $: if (video && video !== previous_video) {
144
- if (previous_video) {
145
- previous_video.removeEventListener("volumechange", handleVolumeChange);
177
+ $effect(() => {
178
+ if (video && video !== previous_video) {
179
+ if (previous_video) {
180
+ previous_video.removeEventListener("volumechange", handleVolumeChange);
181
+ }
182
+ video.addEventListener("volumechange", handleVolumeChange);
183
+ previous_video = video;
146
184
  }
147
- video.addEventListener("volumechange", handleVolumeChange);
148
- previous_video = video;
149
- }
185
+ });
150
186
 
151
- $: time = time || 0;
152
- $: duration = duration || 0;
153
- $: playback_position = time;
154
- $: if (playback_position !== time && video) {
155
- video.currentTime = playback_position;
156
- }
157
- $: if (video && !is_fullscreen) {
158
- if (Math.abs(video.volume - current_volume) > VOLUME_EPSILON) {
159
- video.volume = current_volume;
160
- last_synced_volume = current_volume;
187
+ $effect(() => {
188
+ playback_position = time;
189
+ });
190
+
191
+ $effect(() => {
192
+ if (playback_position !== time && video) {
193
+ video.currentTime = playback_position;
161
194
  }
162
- video.controls = false;
163
- }
164
- $: if (video && is_fullscreen) {
165
- last_synced_volume = video.volume;
166
- }
195
+ });
196
+
197
+ $effect(() => {
198
+ if (video && !is_fullscreen) {
199
+ if (Math.abs(video.volume - current_volume) > VOLUME_EPSILON) {
200
+ video.volume = current_volume;
201
+ last_synced_volume = current_volume;
202
+ }
203
+ video.controls = false;
204
+ }
205
+ });
206
+
207
+ $effect(() => {
208
+ if (video && is_fullscreen) {
209
+ last_synced_volume = video.volume;
210
+ }
211
+ });
167
212
  </script>
168
213
 
169
214
  <div class="wrap">
@@ -175,20 +220,20 @@
175
220
  {loop}
176
221
  {is_stream}
177
222
  controls={is_fullscreen}
178
- on:click={play_pause}
179
- on:play
180
- on:pause
181
- on:error
182
- on:ended={handle_end}
223
+ onclick={play_pause}
224
+ onplay={() => onplay?.()}
225
+ onpause={() => onpause?.()}
226
+ onerror={(error) => onerror?.(error)}
227
+ onended={handle_end}
183
228
  bind:currentTime={time}
184
229
  bind:duration
185
230
  bind:paused
186
231
  bind:node={video}
187
232
  data-testid={`${label}-player`}
188
233
  {processingVideo}
189
- on:loadstart
190
- on:loadeddata
191
- on:loadedmetadata
234
+ onloadstart={() => onloadstart?.()}
235
+ onloadeddata={() => onloadeddata?.()}
236
+ onloadedmetadata={() => onloadedmetadata?.()}
192
237
  >
193
238
  <track kind="captions" src={subtitle} default />
194
239
  </Video>
@@ -201,8 +246,8 @@
201
246
  tabindex="0"
202
247
  class="icon"
203
248
  aria-label="play-pause-replay-button"
204
- on:click={play_pause}
205
- on:keydown={play_pause}
249
+ onclick={play_pause}
250
+ onkeydown={play_pause}
206
251
  >
207
252
  {#if time === duration}
208
253
  <Undo />
@@ -216,21 +261,28 @@
216
261
  <span class="time">{format_time(time)} / {format_time(duration)}</span>
217
262
 
218
263
  <!-- TODO: implement accessible video timeline for 4.0 -->
219
- <!-- svelte-ignore a11y-click-events-have-key-events -->
220
- <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
264
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
265
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
221
266
  <progress
222
267
  value={time / duration || 0}
223
- on:mousemove={handleMove}
224
- on:touchmove|preventDefault={handleMove}
225
- on:click|stopPropagation|preventDefault={handle_click}
226
- />
268
+ onmousemove={handleMove}
269
+ ontouchmove={(e) => {
270
+ e.preventDefault();
271
+ handleMove(e);
272
+ }}
273
+ onclick={(e) => {
274
+ e.stopPropagation();
275
+ e.preventDefault();
276
+ handle_click(e);
277
+ }}
278
+ ></progress>
227
279
 
228
280
  <div class="volume-control-wrapper">
229
281
  <button
230
282
  class="icon volume-button"
231
283
  style:color={show_volume_slider ? "var(--color-accent)" : "white"}
232
284
  aria-label="Adjust volume"
233
- on:click={() => (show_volume_slider = !show_volume_slider)}
285
+ onclick={() => (show_volume_slider = !show_volume_slider)}
234
286
  >
235
287
  <VolumeLevels currentVolume={current_volume} />
236
288
  </button>
@@ -246,8 +298,8 @@
246
298
  tabindex="0"
247
299
  class="icon"
248
300
  aria-label="full-screen"
249
- on:click={open_full_screen}
250
- on:keypress={open_full_screen}
301
+ onclick={open_full_screen}
302
+ onkeypress={open_full_screen}
251
303
  >
252
304
  <Maximize />
253
305
  </div>
@@ -1,19 +1,6 @@
1
1
  import type { FileData, Client } from "@gradio/client";
2
2
  import type { I18nFormatter } from "@gradio/utils";
3
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
- $$bindings?: Bindings;
6
- } & Exports;
7
- (internal: unknown, props: Props & {
8
- $$events?: Events;
9
- $$slots?: Slots;
10
- }): Exports & {
11
- $set?: any;
12
- $on?: any;
13
- };
14
- z_$$bindings?: Bindings;
15
- }
16
- declare const Player: $$__sveltets_2_IsomorphicComponent<{
3
+ interface Props {
17
4
  root?: string;
18
5
  src: string;
19
6
  subtitle?: string | null;
@@ -25,25 +12,22 @@ declare const Player: $$__sveltets_2_IsomorphicComponent<{
25
12
  handle_change?: (video: FileData) => void;
26
13
  handle_reset_value?: () => void;
27
14
  upload: Client["upload"];
28
- is_stream: boolean | undefined;
15
+ is_stream?: boolean;
29
16
  i18n: I18nFormatter;
30
17
  show_download_button?: boolean;
31
18
  value?: FileData | null;
32
19
  handle_clear?: () => void;
33
20
  has_change_history?: boolean;
34
21
  playback_position?: number;
35
- }, {
36
- play: CustomEvent<any>;
37
- pause: CustomEvent<any>;
38
- error: CustomEvent<any>;
39
- loadstart: Event;
40
- loadeddata: Event;
41
- loadedmetadata: Event;
42
- stop: CustomEvent<undefined>;
43
- end: CustomEvent<undefined>;
44
- clear: CustomEvent<undefined>;
45
- } & {
46
- [evt: string]: CustomEvent<any>;
47
- }, {}, {}, string>;
48
- type Player = InstanceType<typeof Player>;
22
+ onplay?: () => void;
23
+ onpause?: () => void;
24
+ onstop?: () => void;
25
+ onend?: () => void;
26
+ onerror?: (error: string) => void;
27
+ onloadstart?: () => void;
28
+ onloadeddata?: () => void;
29
+ onloadedmetadata?: () => void;
30
+ }
31
+ declare const Player: import("svelte").Component<Props, {}, "playback_position">;
32
+ type Player = ReturnType<typeof Player>;
49
33
  export default Player;
@@ -1,31 +1,71 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLVideoAttributes } from "svelte/elements";
3
- import { createEventDispatcher } from "svelte";
4
3
  import { loaded } from "./utils";
4
+ import type { Snippet } from "svelte";
5
5
 
6
6
  import Hls from "hls.js";
7
7
 
8
- export let src: HTMLVideoAttributes["src"] = undefined;
9
-
10
- export let muted: HTMLVideoAttributes["muted"] = undefined;
11
- export let playsinline: HTMLVideoAttributes["playsinline"] = undefined;
12
- export let preload: HTMLVideoAttributes["preload"] = undefined;
13
- export let autoplay: HTMLVideoAttributes["autoplay"] = undefined;
14
- export let controls: HTMLVideoAttributes["controls"] = undefined;
15
-
16
- export let currentTime: number | undefined = undefined;
17
- export let duration: number | undefined = undefined;
18
- export let paused: boolean | undefined = undefined;
19
-
20
- export let node: HTMLVideoElement | undefined = undefined;
21
- export let loop: boolean;
22
- export let is_stream;
23
-
24
- export let processingVideo = false;
25
-
26
- let stream_active = false;
8
+ interface Props {
9
+ src?: HTMLVideoAttributes["src"];
10
+ muted?: HTMLVideoAttributes["muted"];
11
+ playsinline?: HTMLVideoAttributes["playsinline"];
12
+ preload?: HTMLVideoAttributes["preload"];
13
+ autoplay?: HTMLVideoAttributes["autoplay"];
14
+ controls?: HTMLVideoAttributes["controls"];
15
+ currentTime?: number;
16
+ duration?: number;
17
+ paused?: boolean;
18
+ node?: HTMLVideoElement;
19
+ loop: boolean;
20
+ is_stream: boolean;
21
+ processingVideo?: boolean;
22
+ onloadeddata?: () => void;
23
+ onclick?: () => void;
24
+ onplay?: () => void;
25
+ onpause?: () => void;
26
+ onended?: () => void;
27
+ onmouseover?: () => void;
28
+ onmouseout?: () => void;
29
+ onfocus?: () => void;
30
+ onblur?: () => void;
31
+ onerror?: (error: string) => void;
32
+ onloadstart?: () => void;
33
+ onloadedmetadata?: () => void;
34
+ "data-testid"?: string;
35
+ children?: Snippet;
36
+ }
27
37
 
28
- const dispatch = createEventDispatcher();
38
+ let {
39
+ src = undefined,
40
+ muted = undefined,
41
+ playsinline = undefined,
42
+ preload = undefined,
43
+ autoplay = undefined,
44
+ controls = undefined,
45
+ currentTime = $bindable(undefined),
46
+ duration = $bindable(undefined),
47
+ paused = $bindable(undefined),
48
+ node = $bindable(undefined),
49
+ loop,
50
+ is_stream,
51
+ processingVideo = false,
52
+ onloadeddata,
53
+ onclick,
54
+ onplay,
55
+ onpause,
56
+ onended,
57
+ onmouseover,
58
+ onmouseout,
59
+ onfocus,
60
+ onblur,
61
+ onerror,
62
+ onloadstart,
63
+ onloadedmetadata,
64
+ "data-testid": dataTestid,
65
+ children
66
+ }: Props = $props();
67
+
68
+ let stream_active = $state(false);
29
69
 
30
70
  function load_stream(
31
71
  src: string | null | undefined,
@@ -70,11 +110,16 @@
70
110
  }
71
111
  }
72
112
 
73
- $: (src, (stream_active = false));
113
+ $effect(() => {
114
+ src;
115
+ stream_active = false;
116
+ });
74
117
 
75
- $: if (node && src && is_stream) {
76
- load_stream(src, is_stream, node);
77
- }
118
+ $effect(() => {
119
+ if (node && src && is_stream) {
120
+ load_stream(src, is_stream, node);
121
+ }
122
+ });
78
123
  </script>
79
124
 
80
125
  <!--
@@ -97,28 +142,29 @@ Then, even when `controls` is false, the compiled DOM would be `<video controls=
97
142
  {autoplay}
98
143
  {controls}
99
144
  {loop}
100
- on:loadeddata={dispatch.bind(null, "loadeddata")}
101
- on:click={dispatch.bind(null, "click")}
102
- on:play={dispatch.bind(null, "play")}
103
- on:pause={dispatch.bind(null, "pause")}
104
- on:ended={dispatch.bind(null, "ended")}
105
- on:mouseover={dispatch.bind(null, "mouseover")}
106
- on:mouseout={dispatch.bind(null, "mouseout")}
107
- on:focus={dispatch.bind(null, "focus")}
108
- on:blur={dispatch.bind(null, "blur")}
109
- on:error={dispatch.bind(null, "error", "Video not playable")}
110
- on:loadstart
111
- on:loadeddata
112
- on:loadedmetadata
145
+ onloadeddata={() => onloadeddata?.()}
146
+ onclick={() => onclick?.()}
147
+ onplay={() => onplay?.()}
148
+ onpause={() => onpause?.()}
149
+ onended={() => onended?.()}
150
+ onmouseover={() => onmouseover?.()}
151
+ onmouseout={() => onmouseout?.()}
152
+ onfocus={() => onfocus?.()}
153
+ onblur={() => onblur?.()}
154
+ onerror={() => onerror?.("Video not playable")}
155
+ onloadstart={() => onloadstart?.()}
156
+ onloadedmetadata={() => onloadedmetadata?.()}
113
157
  bind:currentTime
114
158
  bind:duration
115
159
  bind:paused
116
160
  bind:this={node}
117
161
  use:loaded={{ autoplay: autoplay ?? false }}
118
- data-testid={$$props["data-testid"]}
162
+ data-testid={dataTestid}
119
163
  crossorigin="anonymous"
120
164
  >
121
- <slot />
165
+ {#if children}
166
+ {@render children()}
167
+ {/if}
122
168
  </video>
123
169
 
124
170
  <style>
@@ -1,46 +1,34 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: Props & {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
15
- default: any;
16
- } ? Props extends Record<string, never> ? any : {
17
- children?: any;
18
- } : {});
19
- declare const Video: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
20
- [x: string]: any;
21
- src?: string | null | undefined;
22
- muted?: boolean | null | undefined;
23
- playsinline?: boolean | null | undefined;
24
- preload?: "" | "auto" | "none" | "metadata" | null | undefined;
25
- autoplay?: boolean | null | undefined;
26
- controls?: boolean | null | undefined;
27
- currentTime?: number | undefined | undefined;
28
- duration?: number | undefined | undefined;
29
- paused?: boolean | undefined | undefined;
30
- node?: HTMLVideoElement | undefined;
1
+ import type { HTMLVideoAttributes } from "svelte/elements";
2
+ import type { Snippet } from "svelte";
3
+ interface Props {
4
+ src?: HTMLVideoAttributes["src"];
5
+ muted?: HTMLVideoAttributes["muted"];
6
+ playsinline?: HTMLVideoAttributes["playsinline"];
7
+ preload?: HTMLVideoAttributes["preload"];
8
+ autoplay?: HTMLVideoAttributes["autoplay"];
9
+ controls?: HTMLVideoAttributes["controls"];
10
+ currentTime?: number;
11
+ duration?: number;
12
+ paused?: boolean;
13
+ node?: HTMLVideoElement;
31
14
  loop: boolean;
32
- is_stream: any;
33
- processingVideo?: boolean | undefined;
34
- }, {
35
- default: {};
36
- }>, {
37
- loadstart: Event;
38
- loadeddata: Event;
39
- loadedmetadata: Event;
40
- } & {
41
- [evt: string]: CustomEvent<any>;
42
- }, {
43
- default: {};
44
- }, {}, string>;
45
- type Video = InstanceType<typeof Video>;
15
+ is_stream: boolean;
16
+ processingVideo?: boolean;
17
+ onloadeddata?: () => void;
18
+ onclick?: () => void;
19
+ onplay?: () => void;
20
+ onpause?: () => void;
21
+ onended?: () => void;
22
+ onmouseover?: () => void;
23
+ onmouseout?: () => void;
24
+ onfocus?: () => void;
25
+ onblur?: () => void;
26
+ onerror?: (error: string) => void;
27
+ onloadstart?: () => void;
28
+ onloadedmetadata?: () => void;
29
+ "data-testid"?: string;
30
+ children?: Snippet;
31
+ }
32
+ declare const Video: import("svelte").Component<Props, {}, "currentTime" | "duration" | "paused" | "node">;
33
+ type Video = ReturnType<typeof Video>;
46
34
  export default Video;