@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.
@@ -10,19 +10,35 @@
10
10
  import { ModifyUpload } from "@gradio/upload";
11
11
  import type { FileData } from "@gradio/client";
12
12
 
13
- export let videoElement: HTMLVideoElement;
14
-
15
- export let showRedo = false;
16
- export let interactive = true;
17
- export let mode = "";
18
- export let handle_reset_value: () => void;
19
- export let handle_trim_video: (videoBlob: Blob) => void;
20
- export let processingVideo = false;
21
- export let i18n: (key: string) => string;
22
- export let value: FileData | null = null;
23
- export let show_download_button = false;
24
- export let handle_clear: () => void = () => {};
25
- export let has_change_history = false;
13
+ interface Props {
14
+ videoElement?: HTMLVideoElement;
15
+ showRedo?: boolean;
16
+ interactive?: boolean;
17
+ mode?: string;
18
+ handle_reset_value: () => void;
19
+ handle_trim_video: (videoBlob: Blob) => void;
20
+ processingVideo?: boolean;
21
+ i18n: (key: string) => string;
22
+ value?: FileData | null;
23
+ show_download_button?: boolean;
24
+ handle_clear?: () => void;
25
+ has_change_history?: boolean;
26
+ }
27
+
28
+ let {
29
+ videoElement = undefined,
30
+ showRedo = false,
31
+ interactive = true,
32
+ mode = $bindable(""),
33
+ handle_reset_value,
34
+ handle_trim_video,
35
+ processingVideo = $bindable(false),
36
+ i18n,
37
+ value = null,
38
+ show_download_button = false,
39
+ handle_clear = () => {},
40
+ has_change_history = false
41
+ }: Props = $props();
26
42
 
27
43
  let ffmpeg: FFmpeg;
28
44
 
@@ -30,19 +46,24 @@
30
46
  ffmpeg = await loadFfmpeg();
31
47
  });
32
48
 
33
- $: if (mode === "edit" && trimmedDuration === null && videoElement)
34
- trimmedDuration = videoElement.duration;
49
+ $effect(() => {
50
+ if (mode === "edit" && trimmedDuration === null && videoElement) {
51
+ trimmedDuration = videoElement.duration;
52
+ }
53
+ });
35
54
 
36
- let trimmedDuration: number | null = null;
37
- let dragStart = 0;
38
- let dragEnd = 0;
55
+ let trimmedDuration = $state<number | null>(null);
56
+ let dragStart = $state(0);
57
+ let dragEnd = $state(0);
39
58
 
40
- let loadingTimeline = false;
59
+ let loadingTimeline = $state(false);
41
60
 
42
61
  const toggleTrimmingMode = (): void => {
43
62
  if (mode === "edit") {
44
63
  mode = "";
45
- trimmedDuration = videoElement.duration;
64
+ if (videoElement) {
65
+ trimmedDuration = videoElement.duration;
66
+ }
46
67
  } else {
47
68
  mode = "edit";
48
69
  }
@@ -50,7 +71,7 @@
50
71
  </script>
51
72
 
52
73
  <div class="container" class:hidden={mode !== "edit"}>
53
- {#if mode === "edit"}
74
+ {#if mode === "edit" && videoElement}
54
75
  <div class="timeline-wrapper">
55
76
  <VideoTimeline
56
77
  {videoElement}
@@ -72,7 +93,8 @@
72
93
  <button
73
94
  class:hidden={loadingTimeline}
74
95
  class="text-button"
75
- on:click={() => {
96
+ onclick={() => {
97
+ if (!videoElement) return;
76
98
  mode = "";
77
99
  processingVideo = true;
78
100
  trimVideo(ffmpeg, dragStart, dragEnd, videoElement)
@@ -87,7 +109,7 @@
87
109
  <button
88
110
  class="text-button"
89
111
  class:hidden={loadingTimeline}
90
- on:click={toggleTrimmingMode}>Cancel</button
112
+ onclick={toggleTrimmingMode}>Cancel</button
91
113
  >
92
114
  </div>
93
115
  {:else}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher, afterUpdate, tick } from "svelte";
2
+ import { tick } from "svelte";
3
3
  import {
4
4
  BlockLabel,
5
5
  Empty,
@@ -16,47 +16,78 @@
16
16
  import Player from "./Player.svelte";
17
17
  import type { I18nFormatter } from "js/core/src/gradio_helper";
18
18
 
19
- export let value: FileData | null = null;
20
- export let subtitle: FileData | null = null;
21
- export let label: string | undefined = undefined;
22
- export let show_label = true;
23
- export let autoplay: boolean;
24
- export let buttons: (string | CustomButtonType)[] | null = null;
25
- export let on_custom_button_click: ((id: number) => void) | null = null;
26
- export let loop: boolean;
27
- export let i18n: I18nFormatter;
28
- export let upload: Client["upload"];
29
- export let display_icon_button_wrapper_top_corner = false;
30
- export let playback_position = 0;
19
+ interface Props {
20
+ value?: FileData | null;
21
+ subtitle?: FileData | null;
22
+ label?: string;
23
+ show_label?: boolean;
24
+ autoplay: boolean;
25
+ buttons?: (string | CustomButtonType)[] | null;
26
+ on_custom_button_click?: ((id: number) => void) | null;
27
+ loop: boolean;
28
+ i18n: I18nFormatter;
29
+ upload: Client["upload"];
30
+ display_icon_button_wrapper_top_corner?: boolean;
31
+ playback_position?: number;
32
+ onplay?: () => void;
33
+ onpause?: () => void;
34
+ onend?: () => void;
35
+ onstop?: () => void;
36
+ onload?: () => void;
37
+ onchange?: (value: FileData) => void;
38
+ onerror?: (error: string) => void;
39
+ onshare?: (detail: unknown) => void;
40
+ }
31
41
 
32
- let old_value: FileData | null = null;
33
- let old_subtitle: FileData | null = null;
42
+ let {
43
+ value = $bindable(null),
44
+ subtitle = null,
45
+ label = undefined,
46
+ show_label = true,
47
+ autoplay,
48
+ buttons = null,
49
+ on_custom_button_click = null,
50
+ loop,
51
+ i18n,
52
+ upload,
53
+ display_icon_button_wrapper_top_corner = false,
54
+ playback_position = $bindable(),
55
+ onplay,
56
+ onpause,
57
+ onend,
58
+ onstop,
59
+ onload,
60
+ onchange,
61
+ onerror,
62
+ onshare
63
+ }: Props = $props();
34
64
 
35
- const dispatch = createEventDispatcher<{
36
- change: FileData;
37
- play: undefined;
38
- pause: undefined;
39
- end: undefined;
40
- stop: undefined;
41
- load: undefined;
42
- }>();
65
+ let old_value = $state<FileData | null>(null);
66
+ let old_subtitle = $state<FileData | null>(null);
43
67
 
44
- $: value && dispatch("change", value);
68
+ $effect(() => {
69
+ if (value) {
70
+ onchange?.(value);
71
+ }
72
+ });
45
73
 
46
- afterUpdate(async () => {
47
- // needed to bust subtitle caching issues on Chrome
48
- if (
49
- value !== old_value &&
50
- subtitle !== old_subtitle &&
51
- old_subtitle !== null
52
- ) {
74
+ $effect(() => {
75
+ async function updateValue(): Promise<void> {
76
+ // needed to bust subtitle caching issues on Chrome
77
+ if (
78
+ value !== old_value &&
79
+ subtitle !== old_subtitle &&
80
+ old_subtitle !== null
81
+ ) {
82
+ old_value = value;
83
+ value = null;
84
+ await tick();
85
+ value = old_value;
86
+ }
53
87
  old_value = value;
54
- value = null;
55
- await tick();
56
- value = old_value;
88
+ old_subtitle = subtitle;
57
89
  }
58
- old_value = value;
59
- old_subtitle = subtitle;
90
+ updateValue();
60
91
  });
61
92
  </script>
62
93
 
@@ -70,15 +101,15 @@
70
101
  subtitle={subtitle?.url}
71
102
  is_stream={value.is_stream}
72
103
  {autoplay}
73
- on:play
74
- on:pause
75
- on:stop
76
- on:end
77
- on:loadedmetadata={() => {
104
+ onplay={() => onplay?.()}
105
+ onpause={() => onpause?.()}
106
+ onstop={() => onstop?.()}
107
+ onend={() => onend?.()}
108
+ onloadedmetadata={() => {
78
109
  // Deal with `<video>`'s `loadedmetadata` event as `VideoPreview`'s `load` event
79
110
  // to represent not only the video is loaded but also the metadata is loaded
80
111
  // so its dimensions (w/h) are known. This is used for Chatbot's auto scroll.
81
- dispatch("load");
112
+ onload?.();
82
113
  }}
83
114
  mirror={false}
84
115
  {label}
@@ -108,8 +139,8 @@
108
139
  {#if buttons?.some((btn) => typeof btn === "string" && btn === "share")}
109
140
  <ShareButton
110
141
  {i18n}
111
- on:error
112
- on:share
142
+ onerror={(detail) => onerror?.(detail)}
143
+ onshare={(detail) => onshare?.(detail)}
113
144
  {value}
114
145
  formatter={async (value) => {
115
146
  if (!value) return "";
@@ -1,27 +1,41 @@
1
1
  <script lang="ts">
2
2
  import { onMount, onDestroy } from "svelte";
3
3
 
4
- export let videoElement: HTMLVideoElement;
5
- export let trimmedDuration: number | null;
6
- export let dragStart: number;
7
- export let dragEnd: number;
8
- export let loadingTimeline: boolean;
4
+ interface Props {
5
+ videoElement: HTMLVideoElement;
6
+ trimmedDuration?: number | null;
7
+ dragStart?: number;
8
+ dragEnd?: number;
9
+ loadingTimeline?: boolean;
10
+ }
11
+
12
+ let {
13
+ videoElement,
14
+ trimmedDuration = $bindable(null),
15
+ dragStart = $bindable(0),
16
+ dragEnd = $bindable(0),
17
+ loadingTimeline = $bindable(false)
18
+ }: Props = $props();
9
19
 
10
- let thumbnails: string[] = [];
20
+ let thumbnails = $state<string[]>([]);
11
21
  let numberOfThumbnails = 10;
12
22
  let intervalId: ReturnType<typeof setInterval> | undefined;
13
23
  let videoDuration: number;
14
24
 
15
- let leftHandlePosition = 0;
16
- let rightHandlePosition = 100;
25
+ let leftHandlePosition = $state(0);
26
+ let rightHandlePosition = $state(100);
17
27
 
18
- let dragging: string | null = null;
28
+ let dragging = $state<string | null>(null);
19
29
 
20
30
  const startDragging = (side: string | null): void => {
21
31
  dragging = side;
22
32
  };
23
33
 
24
- $: loadingTimeline = thumbnails.length !== numberOfThumbnails;
34
+ let loadingTimelineValue = $derived(thumbnails.length !== numberOfThumbnails);
35
+
36
+ $effect(() => {
37
+ loadingTimeline = loadingTimelineValue;
38
+ });
25
39
 
26
40
  const stopDragging = (): void => {
27
41
  dragging = null;
@@ -149,7 +163,7 @@
149
163
  </script>
150
164
 
151
165
  <div class="container">
152
- {#if loadingTimeline}
166
+ {#if loadingTimelineValue}
153
167
  <div class="load-wrap">
154
168
  <span aria-label="loading timeline" class="loader" />
155
169
  </div>
@@ -158,20 +172,20 @@
158
172
  <button
159
173
  aria-label="start drag handle for trimming video"
160
174
  class="handle left"
161
- on:mousedown={() => startDragging("left")}
162
- on:blur={stopDragging}
163
- on:keydown={(e) => {
175
+ onmousedown={() => startDragging("left")}
176
+ onblur={stopDragging}
177
+ onkeydown={(e) => {
164
178
  if (e.key === "ArrowLeft" || e.key == "ArrowRight") {
165
179
  startDragging("left");
166
180
  }
167
181
  }}
168
182
  style="left: {leftHandlePosition}%;"
169
- />
183
+ ></button>
170
184
 
171
185
  <div
172
186
  class="opaque-layer"
173
187
  style="left: {leftHandlePosition}%; right: {100 - rightHandlePosition}%"
174
- />
188
+ ></div>
175
189
 
176
190
  {#each thumbnails as thumbnail, i (i)}
177
191
  <img src={thumbnail} alt={`frame-${i}`} draggable="false" />
@@ -179,15 +193,15 @@
179
193
  <button
180
194
  aria-label="end drag handle for trimming video"
181
195
  class="handle right"
182
- on:mousedown={() => startDragging("right")}
183
- on:blur={stopDragging}
184
- on:keydown={(e) => {
196
+ onmousedown={() => startDragging("right")}
197
+ onblur={stopDragging}
198
+ onkeydown={(e) => {
185
199
  if (e.key === "ArrowLeft" || e.key == "ArrowRight") {
186
200
  startDragging("right");
187
201
  }
188
202
  }}
189
203
  style="left: {rightHandlePosition}%;"
190
- />
204
+ ></button>
191
205
  </div>
192
206
  {/if}
193
207
  </div>
@@ -1,10 +1,17 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from "svelte";
3
3
 
4
- export let current_volume = 1;
5
- export let show_volume_slider = false;
4
+ interface Props {
5
+ current_volume?: number;
6
+ show_volume_slider?: boolean;
7
+ }
8
+
9
+ let {
10
+ current_volume = $bindable(1),
11
+ show_volume_slider = $bindable(false)
12
+ }: Props = $props();
6
13
 
7
- let volume_element: HTMLInputElement;
14
+ let volume_element: HTMLInputElement | undefined = $state();
8
15
 
9
16
  onMount(() => {
10
17
  adjustSlider();
@@ -19,7 +26,10 @@
19
26
  }%, rgba(255, 255, 255, 0.3) ${current_volume * 100}%)`;
20
27
  };
21
28
 
22
- $: (current_volume, adjustSlider());
29
+ $effect(() => {
30
+ current_volume;
31
+ adjustSlider();
32
+ });
23
33
  </script>
24
34
 
25
35
  <input
@@ -31,8 +41,8 @@
31
41
  max="1"
32
42
  step="0.01"
33
43
  value={current_volume}
34
- on:focusout={() => (show_volume_slider = false)}
35
- on:input={(e) => {
44
+ onfocusout={() => (show_volume_slider = false)}
45
+ oninput={(e) => {
36
46
  if (e.target instanceof HTMLInputElement) {
37
47
  current_volume = parseFloat(e.target.value);
38
48
  }