diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8e67e66e..0caf2eb624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: diff --git a/gradio/components.py b/gradio/components.py index f73f119d5d..4b3b97e4c0 100644 --- a/gradio/components.py +++ b/gradio/components.py @@ -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, diff --git a/gradio/events.py b/gradio/events.py index e2e2f9c607..83f2505e22 100644 --- a/gradio/events.py +++ b/gradio/events.py @@ -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): diff --git a/js/app/src/components/Audio/Audio.svelte b/js/app/src/components/Audio/Audio.svelte index fcda9dcfb8..6f2f4d83f9 100644 --- a/js/app/src/components/Audio/Audio.svelte +++ b/js/app/src/components/Audio/Audio.svelte @@ -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 || {}; diff --git a/js/app/src/components/Video/Video.svelte b/js/app/src/components/Video/Video.svelte index 108caf6e22..fd7a1f60ec 100644 --- a/js/app/src/components/Video/Video.svelte +++ b/js/app/src/components/Video/Video.svelte @@ -93,6 +93,7 @@ {show_label} on:play on:pause + on:stop /> {:else} diff --git a/js/audio/src/Audio.svelte b/js/audio/src/Audio.svelte index bdf344f12a..decf7756c8 100644 --- a/js/audio/src/Audio.svelte +++ b/js/audio/src/Audio.svelte @@ -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 { @@ -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); @@ -306,7 +315,7 @@ src={value.data} on:play on:pause - on:ended + on:ended={handle_ended} /> {#if mode === "edit" && player?.duration} diff --git a/js/audio/src/StaticAudio.svelte b/js/audio/src/StaticAudio.svelte index ee5f8322ff..2fd5c1870b 100644 --- a/js/audio/src/StaticAudio.svelte +++ b/js/audio/src/StaticAudio.svelte @@ -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"); + } @@ -44,7 +50,7 @@ src={value.data} on:play on:pause - on:ended + on:ended={handle_ended} /> {/if} diff --git a/js/image/src/Webcam.svelte b/js/image/src/Webcam.svelte index a4df59790a..b7ee685ba0 100644 --- a/js/image/src/Webcam.svelte +++ b/js/image/src/Webcam.svelte @@ -1,6 +1,7 @@
@@ -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 diff --git a/js/video/src/StaticVideo.svelte b/js/video/src/StaticVideo.svelte index ad48c51dd4..69b3bdff27 100644 --- a/js/video/src/StaticVideo.svelte +++ b/js/video/src/StaticVideo.svelte @@ -1,10 +1,5 @@