add new audio + video events (#4422)

This commit is contained in:
pngwn 2023-06-08 12:54:02 +09:00 committed by GitHub
parent 9c5a1d871c
commit fd2538bd05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 136 additions and 21 deletions

View File

@ -2,7 +2,7 @@
## New Features:
No changes to highlight.
- Add `start_recording` and `stop_recording` events to `Video` and `Audio` components by [@pngwn](https://github.com/pngwn) in [PR 4422](https://github.com/gradio-app/gradio/pull/4422)
## Bug Fixes:

View File

@ -64,6 +64,7 @@ from gradio.events import (
EventListenerMethod,
Inputable,
Playable,
Recordable,
Releaseable,
Selectable,
Streamable,
@ -2134,6 +2135,7 @@ class Video(
Changeable,
Clearable,
Playable,
Recordable,
Uploadable,
IOComponent,
VideoSerializable,
@ -2482,6 +2484,7 @@ class Audio(
Changeable,
Clearable,
Playable,
Recordable,
Streamable,
Uploadable,
IOComponent,

View File

@ -131,6 +131,11 @@ class EventListenerMethod:
warnings.warn(
"The 'status_tracker' parameter has been deprecated and has no effect."
)
if self.event_name == "stop":
warnings.warn(
"The `stop` event on Video and Audio has been deprecated and will be remove in a future version. Use `ended` instead."
)
if isinstance(self, Streamable):
self.check_streamable()
if isinstance(show_progress, bool):
@ -234,13 +239,19 @@ class Playable(EventListener):
self.pause = EventListenerMethod(self, "pause")
"""
This listener is triggered when the user pauses the component (e.g. audio or video).
This listener is triggered when the media stops playing for any reason (e.g. audio or video).
This method can be used when this component is in a Gradio Blocks.
"""
self.stop = EventListenerMethod(self, "stop")
"""
This listener is triggered when the user stops the component (e.g. audio or video).
This listener is triggered when the user reaches the end of the media track (e.g. audio or video).
This method can be used when this component is in a Gradio Blocks.
"""
self.end = EventListenerMethod(self, "end")
"""
This listener is triggered when the user reaches the end of the media track (e.g. audio or video).
This method can be used when this component is in a Gradio Blocks.
"""
@ -264,6 +275,22 @@ class Streamable(EventListener):
pass
@document("*start_recording", "*stop_recording", inherit=True)
class Recordable(EventListener):
def __init__(self):
self.start_recording = EventListenerMethod(self, "start_recording")
"""
This listener is triggered when the user starts recording with the component (e.g. audio or video).
This method can be used when this component is in a Gradio Blocks.
"""
self.stop_recording = EventListenerMethod(self, "stop_recording")
"""
This listener is triggered when the user stops recording with the component (e.g. audio or video).
This method can be used when this component is in a Gradio Blocks.
"""
@document("*blur", inherit=True)
class Blurrable(EventListener):
def __init__(self):

View File

@ -78,7 +78,10 @@
on:edit
on:play
on:pause
on:ended
on:stop
on:end
on:start_recording
on:stop_recording
on:upload
on:error={({ detail }) => {
loading_status = loading_status || {};

View File

@ -93,6 +93,7 @@
{show_label}
on:play
on:pause
on:stop
/>
{:else}
<Video
@ -114,6 +115,10 @@
on:play
on:pause
on:upload
on:stop
on:end
on:start_recording
on:stop_recording
>
<UploadText type="video" />
</Video>

View File

@ -64,11 +64,14 @@
edit: AudioData;
play: undefined;
pause: undefined;
ended: undefined;
stop: undefined;
end: undefined;
drag: boolean;
error: string;
upload: FileData;
clear: undefined;
start_recording: undefined;
stop_recording: undefined;
}>();
function blob_to_data_url(blob: Blob): Promise<string> {
@ -164,7 +167,7 @@
async function record() {
recording = true;
dispatch("start_recording");
if (!inited) await prepare_audio();
header = undefined;
if (streaming) {
@ -181,6 +184,7 @@
});
const stop = async () => {
dispatch("stop_recording");
recorder.stop();
if (streaming) {
recording = false;
@ -250,6 +254,11 @@
dispatch("upload", detail);
}
function handle_ended() {
dispatch("stop");
dispatch("end");
}
export let dragging = false;
$: dispatch("drag", dragging);
</script>
@ -306,7 +315,7 @@
src={value.data}
on:play
on:pause
on:ended
on:ended={handle_ended}
/>
{#if mode === "edit" && player?.duration}

View File

@ -22,7 +22,8 @@
change: AudioData;
play: undefined;
pause: undefined;
ended: undefined;
end: undefined;
stop: undefined;
}>();
$: value &&
@ -30,6 +31,11 @@
name: name,
data: value?.data
});
function handle_ended() {
dispatch("stop");
dispatch("end");
}
</script>
<BlockLabel {show_label} Icon={Music} float={false} label={label || "Audio"} />
@ -44,7 +50,7 @@
src={value.data}
on:play
on:pause
on:ended
on:ended={handle_ended}
/>
{/if}

View File

@ -1,6 +1,7 @@
<script lang="ts">
import { createEventDispatcher, onMount } from "svelte";
import { Camera, Circle, Square } from "@gradio/icons";
import type { FileData } from "@gradio/upload";
let video_source: HTMLVideoElement;
let canvas: HTMLCanvasElement;
@ -11,7 +12,19 @@
export let mirror_webcam: boolean;
export let include_audio: boolean;
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
stream: undefined;
capture:
| {
data: FileReader["result"];
name: string;
is_example?: boolean;
}
| string;
error: string;
start_recording: undefined;
stop_recording: undefined;
}>();
onMount(() => (canvas = document.createElement("canvas")));
@ -61,6 +74,7 @@
function take_recording() {
if (recording) {
dispatch("stop_recording");
media_recorder.stop();
let video_blob = new Blob(recorded_blobs, { type: mimeType });
let ReaderObj = new FileReader();
@ -75,6 +89,7 @@
};
ReaderObj.readAsDataURL(video_blob);
} else {
dispatch("start_recording");
recorded_blobs = [];
let validMimeTypes = ["video/webm", "video/mp4"];
for (let validMimeType of validMimeTypes) {

View File

@ -1,10 +1,18 @@
<script lang="ts">
import { tick, createEventDispatcher } from "svelte";
import { Play, Pause, Maximise, Undo } from "@gradio/icons";
export let src: string;
export let subtitle: string | null = null;
export let mirror: boolean;
const dispatch = createEventDispatcher<{
play: undefined;
pause: undefined;
stop: undefined;
end: undefined;
}>();
let time: number = 0;
let duration: number;
let paused: boolean = true;
@ -69,6 +77,44 @@
return `${minutes}:${_seconds}`;
}
async function checkforVideo() {
transition = "0s";
await tick();
wrap_opacity = 0.8;
opacity = 0;
await tick();
var b = setInterval(async () => {
if (video.readyState >= 3) {
video.currentTime = 9999;
paused = true;
transition = "0.2s";
setTimeout(async () => {
video.currentTime = 0.0;
opacity = 1;
wrap_opacity = 1;
}, 50);
clearInterval(b);
}
}, 15);
}
async function _load() {
checkforVideo();
}
let opacity: number = 0;
let wrap_opacity: number = 0;
let transition: string = "0.5s";
$: src && _load();
function handle_end() {
dispatch("stop");
dispatch("end");
}
</script>
<div class="wrap">
@ -79,7 +125,7 @@
on:click={play_pause}
on:play
on:pause
on:ended
on:ended={handle_end}
bind:currentTime={time}
bind:duration
bind:paused

View File

@ -1,10 +1,5 @@
<script lang="ts">
import {
createEventDispatcher,
afterUpdate,
tick,
beforeUpdate
} from "svelte";
import { createEventDispatcher, afterUpdate, tick } from "svelte";
import { BlockLabel, Empty, IconButton } from "@gradio/atoms";
import type { FileData } from "@gradio/upload";
import { Video, Download } from "@gradio/icons";
@ -22,7 +17,8 @@
change: FileData;
play: undefined;
pause: undefined;
ended: undefined;
end: undefined;
stop: undefined;
}>();
$: value && dispatch("change", value);

View File

@ -18,14 +18,16 @@
export let include_audio: boolean;
const dispatch = createEventDispatcher<{
change: FileData | null;
change: any;
clear: undefined;
play: undefined;
pause: undefined;
ended: undefined;
end: undefined;
drag: boolean;
error: string;
upload: FileData;
start_recording: undefined;
stop_recording: undefined;
}>();
function handle_load({ detail }: CustomEvent<FileData | null>) {
@ -57,6 +59,8 @@
mode="video"
on:error
on:capture={({ detail }) => dispatch("change", detail)}
on:start_recording
on:stop_recording
/>
{/if}
{:else}
@ -68,7 +72,8 @@
subtitle={subtitle?.data}
on:play
on:pause
on:ended
on:stop
on:end
mirror={mirror_webcam && source === "webcam"}
/>
{:else if value.size}