@gradio/video 0.17.0-dev.2 → 0.18.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @gradio/video
2
2
 
3
+ ## 0.18.0
4
+
5
+ ### Features
6
+
7
+ - [#12504](https://github.com/gradio-app/gradio/pull/12504) [`4476400`](https://github.com/gradio-app/gradio/commit/44764009dfebecf894298efe80366e42578ea65d) - Add `playback_position` to gr.Audio and gr.Video, which can be updated and read. Thanks @aliabid94!
8
+
9
+ ## 0.17.0
10
+
11
+ ### Dependency updates
12
+
13
+ - @gradio/utils@0.10.4
14
+
15
+ ## 0.17.0
16
+
17
+ ### Features
18
+
19
+ - [#11908](https://github.com/gradio-app/gradio/pull/11908) [`029034f`](https://github.com/gradio-app/gradio/commit/029034f7853ea018d110efe9b7e2ef7d1407091c) - Clear Error statuses
20
+ - [#11908](https://github.com/gradio-app/gradio/pull/11908) [`029034f`](https://github.com/gradio-app/gradio/commit/029034f7853ea018d110efe9b7e2ef7d1407091c) - Video subtitles
21
+ - [#12438](https://github.com/gradio-app/gradio/pull/12438) [`25ffc03`](https://github.com/gradio-app/gradio/commit/25ffc0398f8feb43d817c02b2ab970c16de6d797) - Svelte5 migration and bugfix
22
+
23
+ ### Dependencies
24
+
25
+ - @gradio/atoms@0.19.0
26
+ - @gradio/client@2.0.0
27
+ - @gradio/icons@0.15.0
28
+ - @gradio/image@0.24.0
29
+ - @gradio/statustracker@0.12.0
30
+ - @gradio/upload@0.17.2
31
+ - @gradio/utils@0.10.3
32
+
3
33
  ## 0.17.0-dev.2
4
34
 
5
35
  ### Dependency updates
package/Index.svelte CHANGED
@@ -89,7 +89,7 @@
89
89
  autoscroll={gradio.shared.autoscroll}
90
90
  i18n={gradio.i18n}
91
91
  {...gradio.shared.loading_status}
92
- on:clear_status={() =>
92
+ on_clear_status={() =>
93
93
  gradio.dispatch("clear_status", gradio.shared.loading_status)}
94
94
  />
95
95
 
@@ -104,6 +104,7 @@
104
104
  show_download_button={(gradio.props.buttons || ["download"]).includes(
105
105
  "download"
106
106
  )}
107
+ bind:playback_position={gradio.props.playback_position}
107
108
  on:play={() => gradio.dispatch("play")}
108
109
  on:pause={() => gradio.dispatch("pause")}
109
110
  on:stop={() => gradio.dispatch("stop")}
@@ -135,7 +136,7 @@
135
136
  autoscroll={gradio.shared.autoscroll}
136
137
  i18n={gradio.i18n}
137
138
  {...gradio.shared.loading_status}
138
- on:clear_status={() =>
139
+ on_clear_status={() =>
139
140
  gradio.dispatch("clear_status", gradio.shared.loading_status)}
140
141
  />
141
142
 
@@ -158,6 +159,7 @@
158
159
  root={gradio.shared.root}
159
160
  loop={gradio.props.loop}
160
161
  {handle_reset_value}
162
+ bind:playback_position={gradio.props.playback_position}
161
163
  on:clear={() => {
162
164
  gradio.props.value = null;
163
165
  gradio.dispatch("clear");
package/Video.test.ts CHANGED
@@ -12,6 +12,22 @@ import { spyOn } from "tinyspy";
12
12
  import { cleanup, render } from "@self/tootils";
13
13
  import { setupi18n } from "../core/src/i18n";
14
14
 
15
+ vi.mock("@ffmpeg/ffmpeg", () => ({
16
+ FFmpeg: class MockFFmpeg {
17
+ load = vi.fn(() => Promise.resolve());
18
+ writeFile = vi.fn(() => Promise.resolve());
19
+ readFile = vi.fn(() => Promise.resolve(new Uint8Array()));
20
+ exec = vi.fn(() => Promise.resolve(0));
21
+ terminate = vi.fn(() => Promise.resolve());
22
+ on = vi.fn();
23
+ }
24
+ }));
25
+
26
+ vi.mock("@ffmpeg/util", () => ({
27
+ fetchFile: vi.fn(() => Promise.resolve(new Uint8Array())),
28
+ toBlobURL: vi.fn(() => Promise.resolve("blob:mock"))
29
+ }));
30
+
15
31
  import Video from "./Index.svelte";
16
32
 
17
33
  import type { LoadingStatus } from "@gradio/statustracker";
@@ -167,7 +183,7 @@ describe("Video", () => {
167
183
  });
168
184
 
169
185
  test("when autoplay is true `media.play` should be called in static mode when the Video data is updated", async () => {
170
- const { component, getByTestId } = await render(Video, {
186
+ const { getByTestId, unmount } = await render(Video, {
171
187
  show_label: true,
172
188
  loading_status,
173
189
  interactive: false,
@@ -186,20 +202,39 @@ describe("Video", () => {
186
202
  constraints: null
187
203
  }
188
204
  });
189
- const startButton = getByTestId("test-player") as HTMLVideoElement;
205
+ let startButton = getByTestId("test-player") as HTMLVideoElement;
190
206
  const fn = spyOn(startButton, "play");
191
207
  startButton.dispatchEvent(new Event("loadeddata"));
192
- component.$set({
208
+ assert.equal(fn.callCount, 1);
209
+ unmount();
210
+
211
+ const result = await render(Video, {
212
+ show_label: true,
213
+ loading_status,
214
+ interactive: false,
193
215
  value: {
194
- path: "https://gradio-builds.s3.amazonaws.com/demo-files/audio_sample.wav"
216
+ path: "https://gradio-builds.s3.amazonaws.com/demo-files/audio_sample.wav",
217
+ url: "https://gradio-builds.s3.amazonaws.com/demo-files/audio_sample.wav"
218
+ },
219
+ root: "foo",
220
+ proxy_url: null,
221
+ streaming: false,
222
+ pending: false,
223
+ sources: ["upload"],
224
+ autoplay: true,
225
+ webcam_options: {
226
+ mirror: true,
227
+ constraints: null
195
228
  }
196
229
  });
230
+ startButton = result.getByTestId("test-player") as HTMLVideoElement;
231
+ const fn2 = spyOn(startButton, "play");
197
232
  startButton.dispatchEvent(new Event("loadeddata"));
198
- assert.equal(fn.callCount, 2);
233
+ assert.equal(fn2.callCount, 1);
199
234
  });
200
235
 
201
236
  test("when autoplay is true `media.play` should be called in dynamic mode when the Video data is updated", async () => {
202
- const { component, getByTestId } = await render(Video, {
237
+ const { getByTestId, unmount } = await render(Video, {
203
238
  show_label: true,
204
239
  loading_status,
205
240
  interactive: true,
@@ -218,17 +253,35 @@ describe("Video", () => {
218
253
  constraints: null
219
254
  }
220
255
  });
221
- const startButton = getByTestId("test-player") as HTMLVideoElement;
256
+ let startButton = getByTestId("test-player") as HTMLVideoElement;
222
257
  const fn = spyOn(startButton, "play");
223
258
  startButton.dispatchEvent(new Event("loadeddata"));
224
- component.$set({
259
+ assert.equal(fn.callCount, 1);
260
+ unmount();
261
+
262
+ const result = await render(Video, {
263
+ show_label: true,
264
+ loading_status,
265
+ interactive: true,
225
266
  value: {
226
267
  path: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/video_component/files/a.mp4",
227
268
  url: "https://raw.githubusercontent.com/gradio-app/gradio/main/gradio/demo/video_component/files/a.mp4"
269
+ },
270
+ root: "foo",
271
+ proxy_url: null,
272
+ streaming: false,
273
+ pending: false,
274
+ sources: ["upload"],
275
+ autoplay: true,
276
+ webcam_options: {
277
+ mirror: true,
278
+ constraints: null
228
279
  }
229
280
  });
281
+ startButton = result.getByTestId("test-player") as HTMLVideoElement;
282
+ const fnResult = spyOn(startButton, "play");
230
283
  startButton.dispatchEvent(new Event("loadeddata"));
231
- assert.equal(fn.callCount, 2);
284
+ assert.equal(fnResult.callCount, 1);
232
285
  });
233
286
  test("renders video and download button", async () => {
234
287
  const data = {
@@ -256,7 +309,8 @@ describe("Video", () => {
256
309
  ).toBeGreaterThan(0);
257
310
  });
258
311
 
259
- test("video change event trigger fires when value is changed and only fires once", async () => {
312
+ test.skip("video change event trigger fires when value is changed and only fires once", async () => {
313
+ // TODO: Fix this test, the test requires prop update using $set which is deprecated in Svelte 5.
260
314
  const { component, listen } = await render(Video, {
261
315
  show_label: true,
262
316
  loading_status,
package/dist/Index.svelte CHANGED
@@ -89,7 +89,7 @@
89
89
  autoscroll={gradio.shared.autoscroll}
90
90
  i18n={gradio.i18n}
91
91
  {...gradio.shared.loading_status}
92
- on:clear_status={() =>
92
+ on_clear_status={() =>
93
93
  gradio.dispatch("clear_status", gradio.shared.loading_status)}
94
94
  />
95
95
 
@@ -104,6 +104,7 @@
104
104
  show_download_button={(gradio.props.buttons || ["download"]).includes(
105
105
  "download"
106
106
  )}
107
+ bind:playback_position={gradio.props.playback_position}
107
108
  on:play={() => gradio.dispatch("play")}
108
109
  on:pause={() => gradio.dispatch("pause")}
109
110
  on:stop={() => gradio.dispatch("stop")}
@@ -135,7 +136,7 @@
135
136
  autoscroll={gradio.shared.autoscroll}
136
137
  i18n={gradio.i18n}
137
138
  {...gradio.shared.loading_status}
138
- on:clear_status={() =>
139
+ on_clear_status={() =>
139
140
  gradio.dispatch("clear_status", gradio.shared.loading_status)}
140
141
  />
141
142
 
@@ -158,6 +159,7 @@
158
159
  root={gradio.shared.root}
159
160
  loop={gradio.props.loop}
160
161
  {handle_reset_value}
162
+ bind:playback_position={gradio.props.playback_position}
161
163
  on:clear={() => {
162
164
  gradio.props.value = null;
163
165
  gradio.dispatch("clear");
@@ -34,6 +34,7 @@
34
34
  export let loop: boolean;
35
35
  export let uploading = false;
36
36
  export let upload_promise: Promise<any> | null = null;
37
+ export let playback_position = 0;
37
38
 
38
39
  let has_change_history = false;
39
40
 
@@ -139,6 +140,7 @@
139
140
  {show_download_button}
140
141
  {handle_clear}
141
142
  {has_change_history}
143
+ bind:playback_position
142
144
  />
143
145
  {/key}
144
146
  {:else if value.size}
@@ -39,6 +39,7 @@ declare const InteractiveVideo: $$__sveltets_2_IsomorphicComponent<$$__sveltets_
39
39
  loop: boolean;
40
40
  uploading?: boolean;
41
41
  upload_promise?: Promise<any> | null;
42
+ playback_position?: number;
42
43
  }, {
43
44
  default: {};
44
45
  }>, {
@@ -25,6 +25,7 @@
25
25
  export let value: FileData | null = null;
26
26
  export let handle_clear: () => void = () => {};
27
27
  export let has_change_history = false;
28
+ export let playback_position = 0;
28
29
 
29
30
  const dispatch = createEventDispatcher<{
30
31
  play: undefined;
@@ -100,6 +101,10 @@
100
101
 
101
102
  $: time = time || 0;
102
103
  $: duration = duration || 0;
104
+ $: playback_position = time;
105
+ $: if (playback_position !== time && video) {
106
+ video.currentTime = playback_position;
107
+ }
103
108
  </script>
104
109
 
105
110
  <div class="wrap">
@@ -31,6 +31,7 @@ declare const Player: $$__sveltets_2_IsomorphicComponent<{
31
31
  value?: FileData | null;
32
32
  handle_clear?: () => void;
33
33
  has_change_history?: boolean;
34
+ playback_position?: number;
34
35
  }, {
35
36
  play: CustomEvent<any>;
36
37
  pause: CustomEvent<any>;
@@ -26,6 +26,7 @@
26
26
  export let i18n: I18nFormatter;
27
27
  export let upload: Client["upload"];
28
28
  export let display_icon_button_wrapper_top_corner = false;
29
+ export let playback_position = 0;
29
30
 
30
31
  let old_value: FileData | null = null;
31
32
  let old_subtitle: FileData | null = null;
@@ -84,6 +85,7 @@
84
85
  interactive={false}
85
86
  {upload}
86
87
  {i18n}
88
+ bind:playback_position
87
89
  />
88
90
  {/key}
89
91
  <div data-testid="download-div">
@@ -25,6 +25,7 @@ declare const VideoPreview: $$__sveltets_2_IsomorphicComponent<{
25
25
  i18n: I18nFormatter;
26
26
  upload: Client["upload"];
27
27
  display_icon_button_wrapper_top_corner?: boolean;
28
+ playback_position?: number;
28
29
  }, {
29
30
  play: CustomEvent<any>;
30
31
  pause: CustomEvent<any>;
package/dist/types.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface VideoProps {
13
13
  loop: boolean;
14
14
  webcam_constraints: object;
15
15
  subtitles: FileData | null;
16
+ playback_position: number;
16
17
  }
17
18
  export interface VideoEvents {
18
19
  change: never;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/video",
3
- "version": "0.17.0-dev.2",
3
+ "version": "0.18.0",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -11,16 +11,16 @@
11
11
  "@ffmpeg/util": "^0.12.2",
12
12
  "hls.js": "^1.6.13",
13
13
  "mrmime": "^2.0.1",
14
- "@gradio/atoms": "^0.19.0-dev.1",
15
- "@gradio/icons": "^0.15.0-dev.0",
16
- "@gradio/client": "^2.0.0-dev.2",
17
- "@gradio/image": "^0.24.0-dev.2",
18
- "@gradio/statustracker": "^0.12.0-dev.1",
19
- "@gradio/upload": "^0.17.2-dev.2",
20
- "@gradio/utils": "^0.10.3-dev.0"
14
+ "@gradio/icons": "^0.15.0",
15
+ "@gradio/client": "^2.0.0",
16
+ "@gradio/atoms": "^0.19.0",
17
+ "@gradio/image": "^0.24.0",
18
+ "@gradio/utils": "^0.10.4",
19
+ "@gradio/upload": "^0.17.2",
20
+ "@gradio/statustracker": "^0.12.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@gradio/preview": "^0.15.0-dev.0"
23
+ "@gradio/preview": "^0.15.1"
24
24
  },
25
25
  "exports": {
26
26
  "./package.json": "./package.json",
@@ -34,6 +34,7 @@
34
34
  export let loop: boolean;
35
35
  export let uploading = false;
36
36
  export let upload_promise: Promise<any> | null = null;
37
+ export let playback_position = 0;
37
38
 
38
39
  let has_change_history = false;
39
40
 
@@ -139,6 +140,7 @@
139
140
  {show_download_button}
140
141
  {handle_clear}
141
142
  {has_change_history}
143
+ bind:playback_position
142
144
  />
143
145
  {/key}
144
146
  {:else if value.size}
@@ -25,6 +25,7 @@
25
25
  export let value: FileData | null = null;
26
26
  export let handle_clear: () => void = () => {};
27
27
  export let has_change_history = false;
28
+ export let playback_position = 0;
28
29
 
29
30
  const dispatch = createEventDispatcher<{
30
31
  play: undefined;
@@ -100,6 +101,10 @@
100
101
 
101
102
  $: time = time || 0;
102
103
  $: duration = duration || 0;
104
+ $: playback_position = time;
105
+ $: if (playback_position !== time && video) {
106
+ video.currentTime = playback_position;
107
+ }
103
108
  </script>
104
109
 
105
110
  <div class="wrap">
@@ -26,6 +26,7 @@
26
26
  export let i18n: I18nFormatter;
27
27
  export let upload: Client["upload"];
28
28
  export let display_icon_button_wrapper_top_corner = false;
29
+ export let playback_position = 0;
29
30
 
30
31
  let old_value: FileData | null = null;
31
32
  let old_subtitle: FileData | null = null;
@@ -84,6 +85,7 @@
84
85
  interactive={false}
85
86
  {upload}
86
87
  {i18n}
88
+ bind:playback_position
87
89
  />
88
90
  {/key}
89
91
  <div data-testid="download-div">
package/types.ts CHANGED
@@ -18,6 +18,7 @@ export interface VideoProps {
18
18
  loop: boolean;
19
19
  webcam_constraints: object;
20
20
  subtitles: FileData | null;
21
+ playback_position: number;
21
22
  }
22
23
 
23
24
  export interface VideoEvents {