@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 +37 -9
- package/Index.svelte +7 -3
- package/dist/Index.svelte +8 -3
- package/dist/Index.svelte.d.ts +4 -0
- package/dist/shared/InteractiveVideo.svelte +35 -33
- package/dist/shared/InteractiveVideo.svelte.d.ts +1 -0
- package/dist/shared/Player.svelte +13 -2
- package/dist/shared/Player.svelte.d.ts +7 -0
- package/dist/shared/VideoControls.svelte +77 -83
- package/dist/shared/VideoControls.svelte.d.ts +6 -0
- package/dist/shared/VideoPreview.svelte +34 -35
- package/package.json +10 -10
- package/shared/InteractiveVideo.svelte +36 -33
- package/shared/Player.svelte +12 -0
- package/shared/VideoControls.svelte +77 -83
- package/shared/VideoPreview.svelte +34 -35
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
|
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.
|
29
|
-
- @gradio/statustracker@0.
|
30
|
-
- @gradio/atoms@0.8.1
|
31
|
-
- @gradio/
|
32
|
-
- @gradio/
|
33
|
-
- @gradio/
|
34
|
-
- @gradio/
|
35
|
-
- @gradio/image@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>
|
package/dist/Index.svelte.d.ts
CHANGED
@@ -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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
{
|
112
|
-
|
113
|
-
|
114
|
-
|
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} />
|
@@ -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
|
61
|
-
handle_change(
|
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
|
-
|
56
|
-
<div />
|
57
|
-
{/if}
|
58
|
-
|
59
|
-
<div class="settings-wrapper">
|
60
|
-
{#if showRedo && mode === ""}
|
63
|
+
<div class="edit-buttons">
|
61
64
|
<button
|
62
|
-
class=
|
63
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
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:
|
155
|
-
|
155
|
+
display: flex;
|
156
|
+
justify-content: space-between;
|
157
|
+
align-items: center;
|
156
158
|
margin: var(--spacing-lg);
|
157
159
|
overflow: hidden;
|
158
|
-
|
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
|
-
|
164
|
-
|
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 {
|
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
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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.
|
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.
|
15
|
-
"@gradio/client": "^1.6.0-beta.
|
16
|
-
"@gradio/
|
17
|
-
"@gradio/
|
18
|
-
"@gradio/upload": "^0.
|
19
|
-
"@gradio/utils": "^0.7.0-beta.
|
20
|
-
"@gradio/
|
21
|
-
"@gradio/
|
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
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
{
|
136
|
-
|
137
|
-
|
138
|
-
|
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} />
|
package/shared/Player.svelte
CHANGED
@@ -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
|
-
|
64
|
-
<div />
|
65
|
-
{/if}
|
66
|
-
|
67
|
-
<div class="settings-wrapper">
|
68
|
-
{#if showRedo && mode === ""}
|
71
|
+
<div class="edit-buttons">
|
69
72
|
<button
|
70
|
-
class=
|
71
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
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:
|
163
|
-
|
163
|
+
display: flex;
|
164
|
+
justify-content: space-between;
|
165
|
+
align-items: center;
|
164
166
|
margin: var(--spacing-lg);
|
165
167
|
overflow: hidden;
|
166
|
-
|
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
|
-
|
172
|
-
|
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 {
|
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
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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>
|