@gradio/video 0.11.0-beta.1 → 0.11.0-beta.3

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,32 @@
1
1
  # @gradio/video
2
2
 
3
+ ## 0.11.0-beta.3
4
+
5
+ ### Dependency updates
6
+
7
+ - @gradio/upload@0.13.0-beta.3
8
+ - @gradio/client@1.6.0-beta.3
9
+ - @gradio/image@0.16.0-beta.3
10
+
11
+ ## 0.11.0-beta.2
12
+
13
+ ### Features
14
+
15
+ - [#9339](https://github.com/gradio-app/gradio/pull/9339) [`4c8c6f2`](https://github.com/gradio-app/gradio/commit/4c8c6f2fe603081941c5fdc43f48a0632b9f31ad) - Ssr part 2. Thanks @pngwn!
16
+ - [#9250](https://github.com/gradio-app/gradio/pull/9250) [`350b0a5`](https://github.com/gradio-app/gradio/commit/350b0a5cafb9176f914f62e7c90de51d4352cc77) - Improve Icon Button consistency. Thanks @hannahblair!
17
+ - [#9253](https://github.com/gradio-app/gradio/pull/9253) [`99648ec`](https://github.com/gradio-app/gradio/commit/99648ec7c4443e74799941e47b0015ac9ca581e1) - Adds ability to block event trigger when file is uploading. Thanks @dawoodkhan82!
18
+
19
+ ### Dependency updates
20
+
21
+ - @gradio/atoms@0.9.0-beta.2
22
+ - @gradio/upload@0.13.0-beta.2
23
+ - @gradio/wasm@0.14.0-beta.2
24
+ - @gradio/client@1.6.0-beta.2
25
+ - @gradio/icons@0.8.0-beta.2
26
+ - @gradio/statustracker@0.8.0-beta.2
27
+ - @gradio/utils@0.7.0-beta.2
28
+ - @gradio/image@0.16.0-beta.2
29
+
3
30
  ## 0.11.0-beta.1
4
31
 
5
32
  ### Dependency updates
@@ -13,11 +40,12 @@
13
40
  - @gradio/upload@0.12.4-beta.1
14
41
  - @gradio/wasm@0.13.1-beta.1
15
42
 
16
- ## 0.11.0-beta.0
43
+ ## 0.11.0
17
44
 
18
45
  ### Features
19
46
 
20
47
  - [#8941](https://github.com/gradio-app/gradio/pull/8941) [`97a7bf6`](https://github.com/gradio-app/gradio/commit/97a7bf66a79179d1b91a3199d68e5c11216ca500) - Streaming inputs for 5.0. Thanks @freddyaboulton!
48
+ ## 0.10.4
21
49
 
22
50
  ### Fixes
23
51
 
@@ -25,14 +53,14 @@
25
53
 
26
54
  ### Dependency updates
27
55
 
28
- - @gradio/utils@0.7.0-beta.0
29
- - @gradio/statustracker@0.8.0-beta.0
30
- - @gradio/atoms@0.8.1-beta.0
31
- - @gradio/client@1.6.0-beta.0
32
- - @gradio/icons@0.8.0-beta.0
33
- - @gradio/upload@0.12.4-beta.0
34
- - @gradio/wasm@0.13.1-beta.0
35
- - @gradio/image@0.16.0-beta.0
56
+ - @gradio/utils@0.6.1
57
+ - @gradio/statustracker@0.7.6
58
+ - @gradio/atoms@0.8.1
59
+ - @gradio/icons@0.7.2
60
+ - @gradio/wasm@0.13.1
61
+ - @gradio/client@1.5.2
62
+ - @gradio/upload@0.12.4
63
+ - @gradio/image@0.15.1
36
64
 
37
65
  ## 0.10.3
38
66
 
package/Index.svelte CHANGED
@@ -54,6 +54,9 @@
54
54
  export let mirror_webcam: boolean;
55
55
  export let include_audio: boolean;
56
56
  export let loop = false;
57
+ export let input_ready: boolean;
58
+ let uploading = false;
59
+ $: input_ready = !uploading;
57
60
 
58
61
  let _video: FileData | null = null;
59
62
  let _subtitle: FileData | null = null;
@@ -158,7 +161,7 @@
158
161
  on:share={({ detail }) => gradio.dispatch("share", detail)}
159
162
  on:error={({ detail }) => gradio.dispatch("error", detail)}
160
163
  i18n={gradio.i18n}
161
- upload={gradio.client.upload}
164
+ upload={(...args) => gradio.client.upload(...args)}
162
165
  />
163
166
  </Block>
164
167
  {:else}
@@ -189,6 +192,7 @@
189
192
  on:change={handle_change}
190
193
  on:drag={({ detail }) => (dragging = detail)}
191
194
  on:error={handle_error}
195
+ bind:uploading
192
196
  {label}
193
197
  {show_label}
194
198
  {show_download_button}
@@ -210,8 +214,8 @@
210
214
  on:stop_recording={() => gradio.dispatch("stop_recording")}
211
215
  i18n={gradio.i18n}
212
216
  max_file_size={gradio.max_file_size}
213
- upload={gradio.client.upload}
214
- stream_handler={gradio.client.stream}
217
+ upload={(...args) => gradio.client.upload(...args)}
218
+ stream_handler={(...args) => gradio.client.stream(...args)}
215
219
  >
216
220
  <UploadText i18n={gradio.i18n} type="video" />
217
221
  </Video>
package/dist/Index.svelte CHANGED
@@ -27,6 +27,10 @@ export let interactive;
27
27
  export let mirror_webcam;
28
28
  export let include_audio;
29
29
  export let loop = false;
30
+ export let input_ready;
31
+ let uploading = false;
32
+ $:
33
+ input_ready = !uploading;
30
34
  let _video = null;
31
35
  let _subtitle = null;
32
36
  let active_source;
@@ -115,7 +119,7 @@ function handle_error({ detail }) {
115
119
  on:share={({ detail }) => gradio.dispatch("share", detail)}
116
120
  on:error={({ detail }) => gradio.dispatch("error", detail)}
117
121
  i18n={gradio.i18n}
118
- upload={gradio.client.upload}
122
+ upload={(...args) => gradio.client.upload(...args)}
119
123
  />
120
124
  </Block>
121
125
  {:else}
@@ -146,6 +150,7 @@ function handle_error({ detail }) {
146
150
  on:change={handle_change}
147
151
  on:drag={({ detail }) => (dragging = detail)}
148
152
  on:error={handle_error}
153
+ bind:uploading
149
154
  {label}
150
155
  {show_label}
151
156
  {show_download_button}
@@ -167,8 +172,8 @@ function handle_error({ detail }) {
167
172
  on:stop_recording={() => gradio.dispatch("stop_recording")}
168
173
  i18n={gradio.i18n}
169
174
  max_file_size={gradio.max_file_size}
170
- upload={gradio.client.upload}
171
- stream_handler={gradio.client.stream}
175
+ upload={(...args) => gradio.client.upload(...args)}
176
+ stream_handler={(...args) => gradio.client.stream(...args)}
172
177
  >
173
178
  <UploadText i18n={gradio.i18n} type="video" />
174
179
  </Video>
@@ -43,6 +43,7 @@ declare const __propDef: {
43
43
  mirror_webcam: boolean;
44
44
  include_audio: boolean;
45
45
  loop?: boolean | undefined;
46
+ input_ready: boolean;
46
47
  };
47
48
  events: {
48
49
  [evt: string]: CustomEvent<any>;
@@ -153,5 +154,8 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
153
154
  get loop(): boolean | undefined;
154
155
  /**accessor*/
155
156
  set loop(_: boolean | undefined);
157
+ get input_ready(): boolean;
158
+ /**accessor*/
159
+ set input_ready(_: boolean);
156
160
  }
157
161
  export {};
@@ -24,6 +24,8 @@ export let max_file_size = null;
24
24
  export let upload;
25
25
  export let stream_handler;
26
26
  export let loop;
27
+ export let uploading = false;
28
+ let has_change_history = false;
27
29
  const dispatch = createEventDispatcher();
28
30
  function handle_load({ detail }) {
29
31
  value = detail;
@@ -36,6 +38,7 @@ function handle_clear() {
36
38
  dispatch("clear");
37
39
  }
38
40
  function handle_change(video) {
41
+ has_change_history = true;
39
42
  dispatch("change", video);
40
43
  }
41
44
  function handle_capture({
@@ -55,6 +58,7 @@ $:
55
58
  {#if active_source === "upload"}
56
59
  <Upload
57
60
  bind:dragging
61
+ bind:uploading
58
62
  filetype="video/x-m4v,video/*"
59
63
  on:load={handle_load}
60
64
  {max_file_size}
@@ -81,39 +85,37 @@ $:
81
85
  />
82
86
  {/if}
83
87
  </div>
84
- {:else}
85
- <ModifyUpload
86
- {i18n}
87
- on:clear={handle_clear}
88
- download={show_download_button ? value.url : null}
89
- />
90
- {#if playable()}
91
- {#key value?.url}
92
- <Player
93
- {upload}
94
- {root}
95
- interactive
96
- {autoplay}
97
- src={value.url}
98
- subtitle={subtitle?.url}
99
- is_stream={false}
100
- on:play
101
- on:pause
102
- on:stop
103
- on:end
104
- mirror={mirror_webcam && active_source === "webcam"}
105
- {label}
106
- {handle_change}
107
- {handle_reset_value}
108
- {loop}
109
- />
110
- {/key}
111
- {:else if value.size}
112
- <div class="file-name">{value.orig_name || value.url}</div>
113
- <div class="file-size">
114
- {prettyBytes(value.size)}
115
- </div>
116
- {/if}
88
+ {:else if playable()}
89
+ {#key value?.url}
90
+ <Player
91
+ {upload}
92
+ {root}
93
+ interactive
94
+ {autoplay}
95
+ src={value.url}
96
+ subtitle={subtitle?.url}
97
+ is_stream={false}
98
+ on:play
99
+ on:pause
100
+ on:stop
101
+ on:end
102
+ mirror={mirror_webcam && active_source === "webcam"}
103
+ {label}
104
+ {handle_change}
105
+ {handle_reset_value}
106
+ {loop}
107
+ {value}
108
+ {i18n}
109
+ {show_download_button}
110
+ {handle_clear}
111
+ {has_change_history}
112
+ />
113
+ {/key}
114
+ {:else if value.size}
115
+ <div class="file-name">{value.orig_name || value.url}</div>
116
+ <div class="file-size">
117
+ {prettyBytes(value.size)}
118
+ </div>
117
119
  {/if}
118
120
 
119
121
  <SelectSource {sources} bind:active_source {handle_clear} />
@@ -20,6 +20,7 @@ declare const __propDef: {
20
20
  upload: Client["upload"];
21
21
  stream_handler: Client["stream"];
22
22
  loop: boolean;
23
+ uploading?: boolean | undefined;
23
24
  };
24
25
  events: {
25
26
  error: CustomEvent<any>;
@@ -18,6 +18,12 @@ export let handle_reset_value = () => {
18
18
  };
19
19
  export let upload;
20
20
  export let is_stream;
21
+ export let i18n;
22
+ export let show_download_button = false;
23
+ export let value = null;
24
+ export let handle_clear = () => {
25
+ };
26
+ export let has_change_history = false;
21
27
  const dispatch = createEventDispatcher();
22
28
  let time = 0;
23
29
  let duration;
@@ -57,8 +63,8 @@ function handle_end() {
57
63
  const handle_trim_video = async (videoBlob) => {
58
64
  let _video_blob = new File([videoBlob], "video.mp4");
59
65
  const val = await prepare_files([_video_blob]);
60
- let value = ((await upload(val, root))?.filter(Boolean))[0];
61
- handle_change(value);
66
+ let value2 = ((await upload(val, root))?.filter(Boolean))[0];
67
+ handle_change(value2);
62
68
  };
63
69
  function open_full_screen() {
64
70
  video.requestFullscreen();
@@ -140,6 +146,11 @@ function open_full_screen() {
140
146
  {handle_trim_video}
141
147
  {handle_reset_value}
142
148
  bind:processingVideo
149
+ {value}
150
+ {i18n}
151
+ {show_download_button}
152
+ {handle_clear}
153
+ {has_change_history}
143
154
  />
144
155
  {/if}
145
156
 
@@ -1,5 +1,6 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import type { FileData, Client } from "@gradio/client";
3
+ import type { I18nFormatter } from "@gradio/utils";
3
4
  declare const __propDef: {
4
5
  props: {
5
6
  root?: string | undefined;
@@ -14,6 +15,11 @@ declare const __propDef: {
14
15
  handle_reset_value?: (() => void) | undefined;
15
16
  upload: Client["upload"];
16
17
  is_stream: boolean | undefined;
18
+ i18n: I18nFormatter;
19
+ show_download_button?: boolean | undefined;
20
+ value?: (FileData | null) | undefined;
21
+ handle_clear?: (() => void) | undefined;
22
+ has_change_history?: boolean | undefined;
17
23
  };
18
24
  events: {
19
25
  play: CustomEvent<any>;
@@ -21,6 +27,7 @@ declare const __propDef: {
21
27
  load: Event;
22
28
  stop: CustomEvent<undefined>;
23
29
  end: CustomEvent<undefined>;
30
+ clear: CustomEvent<undefined>;
24
31
  } & {
25
32
  [evt: string]: CustomEvent<any>;
26
33
  };
@@ -1,10 +1,12 @@
1
- <script>import { Undo, Trim } from "@gradio/icons";
1
+ <script>import { Undo, Trim, Clear } from "@gradio/icons";
2
2
  import VideoTimeline from "./VideoTimeline.svelte";
3
3
  import { trimVideo } from "./utils";
4
4
  import { FFmpeg } from "@ffmpeg/ffmpeg";
5
5
  import loadFfmpeg from "./utils";
6
6
  import { onMount } from "svelte";
7
7
  import { format_time } from "@gradio/utils";
8
+ import { IconButton } from "@gradio/atoms";
9
+ import { ModifyUpload } from "@gradio/upload";
8
10
  export let videoElement;
9
11
  export let showRedo = false;
10
12
  export let interactive = true;
@@ -12,6 +14,12 @@ export let mode = "";
12
14
  export let handle_reset_value;
13
15
  export let handle_trim_video;
14
16
  export let processingVideo = false;
17
+ export let i18n;
18
+ export let value = null;
19
+ export let show_download_button = false;
20
+ export let handle_clear = () => {
21
+ };
22
+ export let has_change_history = false;
15
23
  let ffmpeg;
16
24
  onMount(async () => {
17
25
  ffmpeg = await loadFfmpeg();
@@ -33,7 +41,7 @@ const toggleTrimmingMode = () => {
33
41
  };
34
42
  </script>
35
43
 
36
- <div class="container">
44
+ <div class="container" class:hidden={mode !== "edit"}>
37
45
  {#if mode === "edit"}
38
46
  <div class="timeline-wrapper">
39
47
  <VideoTimeline
@@ -52,62 +60,61 @@ const toggleTrimmingMode = () => {
52
60
  aria-label="duration of selected region in seconds"
53
61
  class:hidden={loadingTimeline}>{format_time(trimmedDuration)}</time
54
62
  >
55
- {:else}
56
- <div />
57
- {/if}
58
-
59
- <div class="settings-wrapper">
60
- {#if showRedo && mode === ""}
63
+ <div class="edit-buttons">
61
64
  <button
62
- class="action icon"
63
- disabled={processingVideo}
64
- aria-label="Reset video to initial value"
65
+ class:hidden={loadingTimeline}
66
+ class="text-button"
65
67
  on:click={() => {
66
- handle_reset_value();
67
68
  mode = "";
68
- }}
69
+ processingVideo = true;
70
+ trimVideo(ffmpeg, dragStart, dragEnd, videoElement)
71
+ .then((videoBlob) => {
72
+ handle_trim_video(videoBlob);
73
+ })
74
+ .then(() => {
75
+ processingVideo = false;
76
+ });
77
+ }}>Trim</button
69
78
  >
70
- <Undo />
71
- </button>
72
- {/if}
73
-
74
- {#if interactive}
75
- {#if mode === ""}
76
- <button
77
- disabled={processingVideo}
78
- class="action icon"
79
- aria-label="Trim video to selection"
80
- on:click={toggleTrimmingMode}
81
- >
82
- <Trim />
83
- </button>
84
- {:else}
85
- <button
86
- class:hidden={loadingTimeline}
87
- class="text-button"
88
- on:click={() => {
89
- mode = "";
90
- processingVideo = true;
91
- trimVideo(ffmpeg, dragStart, dragEnd, videoElement)
92
- .then((videoBlob) => {
93
- handle_trim_video(videoBlob);
94
- })
95
- .then(() => {
96
- processingVideo = false;
97
- });
98
- }}>Trim</button
99
- >
100
- <button
101
- class="text-button"
102
- class:hidden={loadingTimeline}
103
- on:click={toggleTrimmingMode}>Cancel</button
104
- >
105
- {/if}
106
- {/if}
107
- </div>
79
+ <button
80
+ class="text-button"
81
+ class:hidden={loadingTimeline}
82
+ on:click={toggleTrimmingMode}>Cancel</button
83
+ >
84
+ </div>
85
+ {:else}
86
+ <div />
87
+ {/if}
108
88
  </div>
109
89
  </div>
110
90
 
91
+ <ModifyUpload
92
+ {i18n}
93
+ on:clear={() => handle_clear()}
94
+ download={show_download_button ? value?.url : null}
95
+ >
96
+ {#if showRedo && mode === ""}
97
+ <IconButton
98
+ Icon={Undo}
99
+ label="Reset video to initial value"
100
+ disabled={processingVideo || !has_change_history}
101
+ on:click={() => {
102
+ handle_reset_value();
103
+ mode = "";
104
+ }}
105
+ />
106
+ {/if}
107
+
108
+ {#if interactive && mode === ""}
109
+ <IconButton
110
+ Icon={Trim}
111
+ label="Trim video to selection"
112
+ disabled={processingVideo}
113
+ on:click={toggleTrimmingMode}
114
+ />
115
+ {/if}
116
+ </ModifyUpload>
117
+
111
118
  <style>
112
119
  .container {
113
120
  width: 100%;
@@ -124,10 +131,7 @@ const toggleTrimmingMode = () => {
124
131
  justify-content: center;
125
132
  width: 100%;
126
133
  }
127
- .settings-wrapper {
128
- display: flex;
129
- justify-self: self-end;
130
- }
134
+
131
135
  .text-button {
132
136
  border: 1px solid var(--neutral-400);
133
137
  border-radius: var(--radius-sm);
@@ -140,9 +144,6 @@ const toggleTrimmingMode = () => {
140
144
  padding: 0 5px;
141
145
  margin-left: 5px;
142
146
  }
143
- .hidden {
144
- display: none;
145
- }
146
147
 
147
148
  .text-button:hover,
148
149
  .text-button:focus {
@@ -151,17 +152,26 @@ const toggleTrimmingMode = () => {
151
152
  }
152
153
 
153
154
  .controls {
154
- display: grid;
155
- grid-template-columns: 1fr 1fr;
155
+ display: flex;
156
+ justify-content: space-between;
157
+ align-items: center;
156
158
  margin: var(--spacing-lg);
157
159
  overflow: hidden;
158
- text-align: left;
160
+ }
161
+
162
+ .edit-buttons {
163
+ display: flex;
164
+ gap: var(--spacing-sm);
159
165
  }
160
166
 
161
167
  @media (max-width: 320px) {
162
168
  .controls {
163
- display: flex;
164
- flex-wrap: wrap;
169
+ flex-direction: column;
170
+ align-items: flex-start;
171
+ }
172
+
173
+ .edit-buttons {
174
+ margin-top: var(--spacing-sm);
165
175
  }
166
176
 
167
177
  .controls * {
@@ -172,29 +182,13 @@ const toggleTrimmingMode = () => {
172
182
  margin-left: 0;
173
183
  }
174
184
  }
175
- .action {
176
- width: var(--size-5);
177
- width: var(--size-5);
178
- color: var(--neutral-400);
179
- margin-left: var(--spacing-md);
180
- }
181
-
182
- .action:disabled {
183
- cursor: not-allowed;
184
- color: var(--border-color-accent-subdued);
185
- }
186
-
187
- .action:disabled:hover {
188
- color: var(--border-color-accent-subdued);
189
- }
190
-
191
- .icon:hover,
192
- .icon:focus {
193
- color: var(--color-accent);
194
- }
195
185
 
196
186
  .container {
197
187
  display: flex;
198
188
  flex-direction: column;
199
189
  }
190
+
191
+ .hidden {
192
+ display: none;
193
+ }
200
194
  </style>
@@ -1,4 +1,5 @@
1
1
  import { SvelteComponent } from "svelte";
2
+ import type { FileData } from "@gradio/client";
2
3
  declare const __propDef: {
3
4
  props: {
4
5
  videoElement: HTMLVideoElement;
@@ -8,6 +9,11 @@ declare const __propDef: {
8
9
  handle_reset_value: () => void;
9
10
  handle_trim_video: (videoBlob: Blob) => void;
10
11
  processingVideo?: boolean | undefined;
12
+ i18n: (key: string) => string;
13
+ value?: (FileData | null) | undefined;
14
+ show_download_button?: boolean | undefined;
15
+ handle_clear?: (() => void) | undefined;
16
+ has_change_history?: boolean | undefined;
11
17
  };
12
18
  events: {
13
19
  [evt: string]: CustomEvent<any>;
@@ -1,5 +1,11 @@
1
1
  <script>import { createEventDispatcher, afterUpdate, tick } from "svelte";
2
- import { BlockLabel, Empty, IconButton, ShareButton } from "@gradio/atoms";
2
+ import {
3
+ BlockLabel,
4
+ Empty,
5
+ IconButton,
6
+ ShareButton,
7
+ IconButtonWrapper
8
+ } from "@gradio/atoms";
3
9
  import { Video, Download } from "@gradio/icons";
4
10
  import { uploadToHuggingFace } from "@gradio/utils";
5
11
  import { DownloadLink } from "@gradio/wasm/svelte";
@@ -51,41 +57,34 @@ afterUpdate(async () => {
51
57
  {loop}
52
58
  interactive={false}
53
59
  {upload}
60
+ {i18n}
54
61
  />
55
62
  {/key}
56
- <div class="icon-buttons" data-testid="download-div">
57
- {#if show_download_button}
58
- <DownloadLink
59
- href={value.is_stream
60
- ? value.url?.replace("playlist.m3u8", "playlist-file")
61
- : value.url}
62
- download={value.orig_name || value.path}
63
- >
64
- <IconButton Icon={Download} label="Download" />
65
- </DownloadLink>
66
- {/if}
67
- {#if show_share_button}
68
- <ShareButton
69
- {i18n}
70
- on:error
71
- on:share
72
- {value}
73
- formatter={async (value) => {
74
- if (!value) return "";
75
- let url = await uploadToHuggingFace(value.data, "url");
76
- return url;
77
- }}
78
- />
79
- {/if}
63
+ <div data-testid="download-div">
64
+ <IconButtonWrapper>
65
+ {#if show_download_button}
66
+ <DownloadLink
67
+ href={value.is_stream
68
+ ? value.url?.replace("playlist.m3u8", "playlist-file")
69
+ : value.url}
70
+ download={value.orig_name || value.path}
71
+ >
72
+ <IconButton Icon={Download} label="Download" />
73
+ </DownloadLink>
74
+ {/if}
75
+ {#if show_share_button}
76
+ <ShareButton
77
+ {i18n}
78
+ on:error
79
+ on:share
80
+ {value}
81
+ formatter={async (value) => {
82
+ if (!value) return "";
83
+ let url = await uploadToHuggingFace(value.data, "url");
84
+ return url;
85
+ }}
86
+ />
87
+ {/if}
88
+ </IconButtonWrapper>
80
89
  </div>
81
90
  {/if}
82
-
83
- <style>
84
- .icon-buttons {
85
- display: flex;
86
- position: absolute;
87
- top: 6px;
88
- right: 6px;
89
- gap: var(--size-1);
90
- }
91
- </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/video",
3
- "version": "0.11.0-beta.1",
3
+ "version": "0.11.0-beta.3",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -11,17 +11,17 @@
11
11
  "@ffmpeg/util": "^0.12.1",
12
12
  "hls.js": "^1.5.13",
13
13
  "mrmime": "^2.0.0",
14
- "@gradio/atoms": "^0.8.1-beta.1",
15
- "@gradio/client": "^1.6.0-beta.1",
16
- "@gradio/image": "^0.16.0-beta.1",
17
- "@gradio/statustracker": "^0.8.0-beta.1",
18
- "@gradio/upload": "^0.12.4-beta.1",
19
- "@gradio/utils": "^0.7.0-beta.1",
20
- "@gradio/icons": "^0.8.0-beta.1",
21
- "@gradio/wasm": "^0.13.1-beta.1"
14
+ "@gradio/atoms": "^0.9.0-beta.2",
15
+ "@gradio/client": "^1.6.0-beta.3",
16
+ "@gradio/icons": "^0.8.0-beta.2",
17
+ "@gradio/image": "^0.16.0-beta.3",
18
+ "@gradio/upload": "^0.13.0-beta.3",
19
+ "@gradio/utils": "^0.7.0-beta.2",
20
+ "@gradio/wasm": "^0.14.0-beta.2",
21
+ "@gradio/statustracker": "^0.8.0-beta.2"
22
22
  },
23
23
  "devDependencies": {
24
- "@gradio/preview": "^0.11.1-beta.0"
24
+ "@gradio/preview": "^0.11.1"
25
25
  },
26
26
  "exports": {
27
27
  "./package.json": "./package.json",
@@ -32,6 +32,9 @@
32
32
  export let upload: Client["upload"];
33
33
  export let stream_handler: Client["stream"];
34
34
  export let loop: boolean;
35
+ export let uploading = false;
36
+
37
+ let has_change_history = false;
35
38
 
36
39
  const dispatch = createEventDispatcher<{
37
40
  change: FileData | null;
@@ -59,6 +62,7 @@
59
62
  }
60
63
 
61
64
  function handle_change(video: FileData): void {
65
+ has_change_history = true;
62
66
  dispatch("change", video);
63
67
  }
64
68
 
@@ -79,6 +83,7 @@
79
83
  {#if active_source === "upload"}
80
84
  <Upload
81
85
  bind:dragging
86
+ bind:uploading
82
87
  filetype="video/x-m4v,video/*"
83
88
  on:load={handle_load}
84
89
  {max_file_size}
@@ -105,39 +110,37 @@
105
110
  />
106
111
  {/if}
107
112
  </div>
108
- {:else}
109
- <ModifyUpload
110
- {i18n}
111
- on:clear={handle_clear}
112
- download={show_download_button ? value.url : null}
113
- />
114
- {#if playable()}
115
- {#key value?.url}
116
- <Player
117
- {upload}
118
- {root}
119
- interactive
120
- {autoplay}
121
- src={value.url}
122
- subtitle={subtitle?.url}
123
- is_stream={false}
124
- on:play
125
- on:pause
126
- on:stop
127
- on:end
128
- mirror={mirror_webcam && active_source === "webcam"}
129
- {label}
130
- {handle_change}
131
- {handle_reset_value}
132
- {loop}
133
- />
134
- {/key}
135
- {:else if value.size}
136
- <div class="file-name">{value.orig_name || value.url}</div>
137
- <div class="file-size">
138
- {prettyBytes(value.size)}
139
- </div>
140
- {/if}
113
+ {:else if playable()}
114
+ {#key value?.url}
115
+ <Player
116
+ {upload}
117
+ {root}
118
+ interactive
119
+ {autoplay}
120
+ src={value.url}
121
+ subtitle={subtitle?.url}
122
+ is_stream={false}
123
+ on:play
124
+ on:pause
125
+ on:stop
126
+ on:end
127
+ mirror={mirror_webcam && active_source === "webcam"}
128
+ {label}
129
+ {handle_change}
130
+ {handle_reset_value}
131
+ {loop}
132
+ {value}
133
+ {i18n}
134
+ {show_download_button}
135
+ {handle_clear}
136
+ {has_change_history}
137
+ />
138
+ {/key}
139
+ {:else if value.size}
140
+ <div class="file-name">{value.orig_name || value.url}</div>
141
+ <div class="file-size">
142
+ {prettyBytes(value.size)}
143
+ </div>
141
144
  {/if}
142
145
 
143
146
  <SelectSource {sources} bind:active_source {handle_clear} />
@@ -6,6 +6,7 @@
6
6
  import type { FileData, Client } from "@gradio/client";
7
7
  import { prepare_files } from "@gradio/client";
8
8
  import { format_time } from "@gradio/utils";
9
+ import type { I18nFormatter } from "@gradio/utils";
9
10
 
10
11
  export let root = "";
11
12
  export let src: string;
@@ -19,12 +20,18 @@
19
20
  export let handle_reset_value: () => void = () => {};
20
21
  export let upload: Client["upload"];
21
22
  export let is_stream: boolean | undefined;
23
+ export let i18n: I18nFormatter;
24
+ export let show_download_button = false;
25
+ export let value: FileData | null = null;
26
+ export let handle_clear: () => void = () => {};
27
+ export let has_change_history = false;
22
28
 
23
29
  const dispatch = createEventDispatcher<{
24
30
  play: undefined;
25
31
  pause: undefined;
26
32
  stop: undefined;
27
33
  end: undefined;
34
+ clear: undefined;
28
35
  }>();
29
36
 
30
37
  let time = 0;
@@ -167,6 +174,11 @@
167
174
  {handle_trim_video}
168
175
  {handle_reset_value}
169
176
  bind:processingVideo
177
+ {value}
178
+ {i18n}
179
+ {show_download_button}
180
+ {handle_clear}
181
+ {has_change_history}
170
182
  />
171
183
  {/if}
172
184
 
@@ -1,11 +1,14 @@
1
1
  <script lang="ts">
2
- import { Undo, Trim } from "@gradio/icons";
2
+ import { Undo, Trim, Clear } from "@gradio/icons";
3
3
  import VideoTimeline from "./VideoTimeline.svelte";
4
4
  import { trimVideo } from "./utils";
5
5
  import { FFmpeg } from "@ffmpeg/ffmpeg";
6
6
  import loadFfmpeg from "./utils";
7
7
  import { onMount } from "svelte";
8
8
  import { format_time } from "@gradio/utils";
9
+ import { IconButton } from "@gradio/atoms";
10
+ import { ModifyUpload } from "@gradio/upload";
11
+ import type { FileData } from "@gradio/client";
9
12
 
10
13
  export let videoElement: HTMLVideoElement;
11
14
 
@@ -15,6 +18,11 @@
15
18
  export let handle_reset_value: () => void;
16
19
  export let handle_trim_video: (videoBlob: Blob) => void;
17
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;
18
26
 
19
27
  let ffmpeg: FFmpeg;
20
28
 
@@ -41,7 +49,7 @@
41
49
  };
42
50
  </script>
43
51
 
44
- <div class="container">
52
+ <div class="container" class:hidden={mode !== "edit"}>
45
53
  {#if mode === "edit"}
46
54
  <div class="timeline-wrapper">
47
55
  <VideoTimeline
@@ -60,62 +68,61 @@
60
68
  aria-label="duration of selected region in seconds"
61
69
  class:hidden={loadingTimeline}>{format_time(trimmedDuration)}</time
62
70
  >
63
- {:else}
64
- <div />
65
- {/if}
66
-
67
- <div class="settings-wrapper">
68
- {#if showRedo && mode === ""}
71
+ <div class="edit-buttons">
69
72
  <button
70
- class="action icon"
71
- disabled={processingVideo}
72
- aria-label="Reset video to initial value"
73
+ class:hidden={loadingTimeline}
74
+ class="text-button"
73
75
  on:click={() => {
74
- handle_reset_value();
75
76
  mode = "";
76
- }}
77
+ processingVideo = true;
78
+ trimVideo(ffmpeg, dragStart, dragEnd, videoElement)
79
+ .then((videoBlob) => {
80
+ handle_trim_video(videoBlob);
81
+ })
82
+ .then(() => {
83
+ processingVideo = false;
84
+ });
85
+ }}>Trim</button
77
86
  >
78
- <Undo />
79
- </button>
80
- {/if}
81
-
82
- {#if interactive}
83
- {#if mode === ""}
84
- <button
85
- disabled={processingVideo}
86
- class="action icon"
87
- aria-label="Trim video to selection"
88
- on:click={toggleTrimmingMode}
89
- >
90
- <Trim />
91
- </button>
92
- {:else}
93
- <button
94
- class:hidden={loadingTimeline}
95
- class="text-button"
96
- on:click={() => {
97
- mode = "";
98
- processingVideo = true;
99
- trimVideo(ffmpeg, dragStart, dragEnd, videoElement)
100
- .then((videoBlob) => {
101
- handle_trim_video(videoBlob);
102
- })
103
- .then(() => {
104
- processingVideo = false;
105
- });
106
- }}>Trim</button
107
- >
108
- <button
109
- class="text-button"
110
- class:hidden={loadingTimeline}
111
- on:click={toggleTrimmingMode}>Cancel</button
112
- >
113
- {/if}
114
- {/if}
115
- </div>
87
+ <button
88
+ class="text-button"
89
+ class:hidden={loadingTimeline}
90
+ on:click={toggleTrimmingMode}>Cancel</button
91
+ >
92
+ </div>
93
+ {:else}
94
+ <div />
95
+ {/if}
116
96
  </div>
117
97
  </div>
118
98
 
99
+ <ModifyUpload
100
+ {i18n}
101
+ on:clear={() => handle_clear()}
102
+ download={show_download_button ? value?.url : null}
103
+ >
104
+ {#if showRedo && mode === ""}
105
+ <IconButton
106
+ Icon={Undo}
107
+ label="Reset video to initial value"
108
+ disabled={processingVideo || !has_change_history}
109
+ on:click={() => {
110
+ handle_reset_value();
111
+ mode = "";
112
+ }}
113
+ />
114
+ {/if}
115
+
116
+ {#if interactive && mode === ""}
117
+ <IconButton
118
+ Icon={Trim}
119
+ label="Trim video to selection"
120
+ disabled={processingVideo}
121
+ on:click={toggleTrimmingMode}
122
+ />
123
+ {/if}
124
+ </ModifyUpload>
125
+
119
126
  <style>
120
127
  .container {
121
128
  width: 100%;
@@ -132,10 +139,7 @@
132
139
  justify-content: center;
133
140
  width: 100%;
134
141
  }
135
- .settings-wrapper {
136
- display: flex;
137
- justify-self: self-end;
138
- }
142
+
139
143
  .text-button {
140
144
  border: 1px solid var(--neutral-400);
141
145
  border-radius: var(--radius-sm);
@@ -148,9 +152,6 @@
148
152
  padding: 0 5px;
149
153
  margin-left: 5px;
150
154
  }
151
- .hidden {
152
- display: none;
153
- }
154
155
 
155
156
  .text-button:hover,
156
157
  .text-button:focus {
@@ -159,17 +160,26 @@
159
160
  }
160
161
 
161
162
  .controls {
162
- display: grid;
163
- grid-template-columns: 1fr 1fr;
163
+ display: flex;
164
+ justify-content: space-between;
165
+ align-items: center;
164
166
  margin: var(--spacing-lg);
165
167
  overflow: hidden;
166
- text-align: left;
168
+ }
169
+
170
+ .edit-buttons {
171
+ display: flex;
172
+ gap: var(--spacing-sm);
167
173
  }
168
174
 
169
175
  @media (max-width: 320px) {
170
176
  .controls {
171
- display: flex;
172
- flex-wrap: wrap;
177
+ flex-direction: column;
178
+ align-items: flex-start;
179
+ }
180
+
181
+ .edit-buttons {
182
+ margin-top: var(--spacing-sm);
173
183
  }
174
184
 
175
185
  .controls * {
@@ -180,29 +190,13 @@
180
190
  margin-left: 0;
181
191
  }
182
192
  }
183
- .action {
184
- width: var(--size-5);
185
- width: var(--size-5);
186
- color: var(--neutral-400);
187
- margin-left: var(--spacing-md);
188
- }
189
-
190
- .action:disabled {
191
- cursor: not-allowed;
192
- color: var(--border-color-accent-subdued);
193
- }
194
-
195
- .action:disabled:hover {
196
- color: var(--border-color-accent-subdued);
197
- }
198
-
199
- .icon:hover,
200
- .icon:focus {
201
- color: var(--color-accent);
202
- }
203
193
 
204
194
  .container {
205
195
  display: flex;
206
196
  flex-direction: column;
207
197
  }
198
+
199
+ .hidden {
200
+ display: none;
201
+ }
208
202
  </style>
@@ -1,6 +1,12 @@
1
1
  <script lang="ts">
2
2
  import { createEventDispatcher, afterUpdate, tick } from "svelte";
3
- import { BlockLabel, Empty, IconButton, ShareButton } from "@gradio/atoms";
3
+ import {
4
+ BlockLabel,
5
+ Empty,
6
+ IconButton,
7
+ ShareButton,
8
+ IconButtonWrapper
9
+ } from "@gradio/atoms";
4
10
  import type { FileData, Client } from "@gradio/client";
5
11
  import { Video, Download } from "@gradio/icons";
6
12
  import { uploadToHuggingFace } from "@gradio/utils";
@@ -70,41 +76,34 @@
70
76
  {loop}
71
77
  interactive={false}
72
78
  {upload}
79
+ {i18n}
73
80
  />
74
81
  {/key}
75
- <div class="icon-buttons" data-testid="download-div">
76
- {#if show_download_button}
77
- <DownloadLink
78
- href={value.is_stream
79
- ? value.url?.replace("playlist.m3u8", "playlist-file")
80
- : value.url}
81
- download={value.orig_name || value.path}
82
- >
83
- <IconButton Icon={Download} label="Download" />
84
- </DownloadLink>
85
- {/if}
86
- {#if show_share_button}
87
- <ShareButton
88
- {i18n}
89
- on:error
90
- on:share
91
- {value}
92
- formatter={async (value) => {
93
- if (!value) return "";
94
- let url = await uploadToHuggingFace(value.data, "url");
95
- return url;
96
- }}
97
- />
98
- {/if}
82
+ <div data-testid="download-div">
83
+ <IconButtonWrapper>
84
+ {#if show_download_button}
85
+ <DownloadLink
86
+ href={value.is_stream
87
+ ? value.url?.replace("playlist.m3u8", "playlist-file")
88
+ : value.url}
89
+ download={value.orig_name || value.path}
90
+ >
91
+ <IconButton Icon={Download} label="Download" />
92
+ </DownloadLink>
93
+ {/if}
94
+ {#if show_share_button}
95
+ <ShareButton
96
+ {i18n}
97
+ on:error
98
+ on:share
99
+ {value}
100
+ formatter={async (value) => {
101
+ if (!value) return "";
102
+ let url = await uploadToHuggingFace(value.data, "url");
103
+ return url;
104
+ }}
105
+ />
106
+ {/if}
107
+ </IconButtonWrapper>
99
108
  </div>
100
109
  {/if}
101
-
102
- <style>
103
- .icon-buttons {
104
- display: flex;
105
- position: absolute;
106
- top: 6px;
107
- right: 6px;
108
- gap: var(--size-1);
109
- }
110
- </style>