Skip to content

Commit

Permalink
Allow download button for interactive Audio and Video components (#7104)
Browse files Browse the repository at this point in the history
* allow download button for interactive audio and video components

* add changeset

* document show_download_button in video.py

* ensure show_download_button is by default true for output and false for input components

* fix component test

* tweak default val

* val tweak

* fix test and add story

* pass editable param where needed

* fix another test

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Dawood Khan <dawoodkhan82@gmail.com>
  • Loading branch information
3 people committed Jan 23, 2024
1 parent c35fac0 commit bc2cdc1
Show file tree
Hide file tree
Showing 15 changed files with 102 additions and 11 deletions.
8 changes: 8 additions & 0 deletions .changeset/hot-forks-report.md
@@ -0,0 +1,8 @@
---
"@gradio/audio": minor
"@gradio/upload": minor
"@gradio/video": minor
"gradio": minor
---

feat:Allow download button for interactive Audio and Video components
6 changes: 3 additions & 3 deletions gradio/components/audio.py
Expand Up @@ -92,7 +92,7 @@ def __init__(
render: bool = True,
format: Literal["wav", "mp3"] = "wav",
autoplay: bool = False,
show_download_button=True,
show_download_button: bool | None = None,
show_share_button: bool | None = None,
editable: bool = True,
min_length: int | None = None,
Expand All @@ -118,9 +118,9 @@ def __init__(
render: If False, component will not render be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.
format: The file format to save audio files. Either 'wav' or 'mp3'. wav files are lossless but will tend to be larger files. mp3 files tend to be smaller. Default is wav. Applies both when this component is used as an input (when `type` is "format") and when this component is used as an output.
autoplay: Whether to automatically play the audio when the component is used as an output. Note: browsers will not autoplay audio files if the user has not interacted with the page yet.
show_download_button: If True, will show a download button in the corner of the component for saving audio. If False, icon does not appear.
show_download_button: If True, will show a download button in the corner of the component for saving audio. If False, icon does not appear. By default, it will be True for output components and False for input components.
show_share_button: If True, will show a share icon in the corner of the component that allows user to share outputs to Hugging Face Spaces Discussions. If False, icon does not appear. If set to None (default behavior), then the icon appears if this Gradio app is launched on Spaces, but not otherwise.
editable: If True, allows users to manipulate the audio file (if the component is interactive).
editable: If True, allows users to manipulate the audio file if the component is interactive. Defaults to True.
min_length: The minimum length of audio (in seconds) that the user can pass into the prediction function. If None, there is no minimum length.
max_length: The maximum length of audio (in seconds) that the user can pass into the prediction function. If None, there is no maximum length.
waveform_options: A dictionary of options for the waveform display. Options include: waveform_color (str), waveform_progress_color (str), show_controls (bool), skip_length (int). Default is None, which uses the default values for these options.
Expand Down
3 changes: 3 additions & 0 deletions gradio/components/video.py
Expand Up @@ -83,6 +83,7 @@ def __init__(
include_audio: bool | None = None,
autoplay: bool = False,
show_share_button: bool | None = None,
show_download_button: bool | None = None,
min_length: int | None = None,
max_length: int | None = None,
):
Expand All @@ -108,6 +109,7 @@ def __init__(
include_audio: Whether the component should record/retain the audio track for a video. By default, audio is excluded for webcam videos and included for uploaded videos.
autoplay: Whether to automatically play the video when the component is used as an output. Note: browsers will not autoplay video files if the user has not interacted with the page yet.
show_share_button: If True, will show a share icon in the corner of the component that allows user to share outputs to Hugging Face Spaces Discussions. If False, icon does not appear. If set to None (default behavior), then the icon appears if this Gradio app is launched on Spaces, but not otherwise.
show_download_button: If True, will show a download icon in the corner of the component that allows user to download the output. If False, icon does not appear. By default, it will be True for output components and False for input components.
min_length: The minimum length of video (in seconds) that the user can pass into the prediction function. If None, there is no minimum length.
max_length: The maximum length of video (in seconds) that the user can pass into the prediction function. If None, there is no maximum length.
"""
Expand Down Expand Up @@ -140,6 +142,7 @@ def __init__(
if show_share_button is None
else show_share_button
)
self.show_download_button = show_download_button
self.min_length = min_length
self.max_length = max_length
super().__init__(
Expand Down
31 changes: 30 additions & 1 deletion js/audio/Audio.stories.svelte
Expand Up @@ -31,6 +31,35 @@
}}
/>

<Story
name="Audio Recorder with download button"
args={{
value: {
path: "https://audio-samples.github.io/samples/mp3/blizzard_unconditional/sample-0.mp3",
url: "https://audio-samples.github.io/samples/mp3/blizzard_unconditional/sample-0.mp3",
orig_name: "sample-0.mp3"
},
interactive: true,
show_download_button: true,
sources: ["microphone"],
label: "Audio Recorder"
}}
/>

<Story
name="output with hidden download button"
args={{
value: {
path: "https://audio-samples.github.io/samples/mp3/blizzard_unconditional/sample-0.mp3",
url: "https://audio-samples.github.io/samples/mp3/blizzard_unconditional/sample-0.mp3",
orig_name: "sample-0.mp3"
},
interactive: false,
show_download_button: false,
label: "Audio Recorder"
}}
/>

<Story
name="Upload Audio"
args={{
Expand All @@ -57,7 +86,7 @@
/>

<Story
name="with disabled editing"
name="upload with disabled editing"
args={{
value: {
path: "https://audio-samples.github.io/samples/mp3/blizzard_unconditional/sample-0.mp3",
Expand Down
4 changes: 3 additions & 1 deletion js/audio/Index.svelte
Expand Up @@ -32,7 +32,7 @@
export let min_width: number | undefined = undefined;
export let loading_status: LoadingStatus;
export let autoplay = false;
export let show_download_button = true;
export let show_download_button: boolean;
export let show_share_button = false;
export let editable = true;
export let waveform_options: WaveformOptions = {};
Expand Down Expand Up @@ -152,6 +152,7 @@
{label}
{waveform_settings}
{waveform_options}
{editable}
on:share={(e) => gradio.dispatch("share", e.detail)}
on:error={(e) => gradio.dispatch("error", e.detail)}
on:play={() => gradio.dispatch("play")}
Expand Down Expand Up @@ -179,6 +180,7 @@
<InteractiveAudio
{label}
{show_label}
{show_download_button}
value={_value}
on:change={({ detail }) => (value = detail)}
on:stream={({ detail }) => {
Expand Down
3 changes: 3 additions & 0 deletions js/audio/interactive/InteractiveAudio.svelte
Expand Up @@ -22,6 +22,7 @@
export let label: string;
export let root: string;
export let show_label = true;
export let show_download_button = false;
export let sources:
| ["microphone"]
| ["upload"]
Expand Down Expand Up @@ -235,6 +236,7 @@
<AudioRecorder
bind:mode
{i18n}
{editable}
{dispatch_blob}
{waveform_settings}
{waveform_options}
Expand All @@ -258,6 +260,7 @@
{i18n}
on:clear={clear}
on:edit={() => (mode = "edit")}
download={show_download_button ? value.url : null}
absolute={true}
/>

Expand Down
2 changes: 2 additions & 0 deletions js/audio/recorder/AudioRecorder.svelte
Expand Up @@ -21,6 +21,7 @@
show_recording_waveform: true
};
export let handle_reset_value: () => void;
export let editable = true;
let micWaveform: WaveSurfer;
let recordingWaveform: WaveSurfer;
Expand Down Expand Up @@ -238,6 +239,7 @@
{playing}
{audio_duration}
{i18n}
{editable}
interactive={true}
{handle_trim_audio}
bind:trimDuration
Expand Down
2 changes: 2 additions & 0 deletions js/audio/static/StaticAudio.svelte
Expand Up @@ -18,6 +18,7 @@
export let i18n: I18nFormatter;
export let waveform_settings: Record<string, any>;
export let waveform_options: WaveformOptions;
export let editable = true;
const dispatch = createEventDispatcher<{
change: FileData;
Expand Down Expand Up @@ -65,6 +66,7 @@
{i18n}
{waveform_settings}
{waveform_options}
{editable}
on:pause
on:play
on:stop
Expand Down
3 changes: 2 additions & 1 deletion js/upload/package.json
Expand Up @@ -11,7 +11,8 @@
"@gradio/icons": "workspace:^",
"@gradio/client": "workspace:^",
"@gradio/upload": "workspace:^",
"@gradio/utils": "workspace:^"
"@gradio/utils": "workspace:^",
"@gradio/wasm": "workspace:^"
},
"main_changeset": true,
"exports": {
Expand Down
10 changes: 9 additions & 1 deletion js/upload/src/ModifyUpload.svelte
@@ -1,12 +1,14 @@
<script lang="ts">
import { IconButton } from "@gradio/atoms";
import type { I18nFormatter } from "@gradio/utils";
import { Edit, Clear, Undo } from "@gradio/icons";
import { Edit, Clear, Undo, Download } from "@gradio/icons";
import { DownloadLink } from "@gradio/wasm/svelte";
import { createEventDispatcher } from "svelte";
export let editable = false;
export let undoable = false;
export let download: string | null = null;
export let absolute = true;
export let i18n: I18nFormatter;
Expand Down Expand Up @@ -37,6 +39,12 @@
/>
{/if}

{#if download}
<DownloadLink href={download} download>
<IconButton Icon={Download} label={i18n("common.download")} />
</DownloadLink>
{/if}

<IconButton
Icon={Clear}
label={i18n("common.clear")}
Expand Down
4 changes: 3 additions & 1 deletion js/video/Index.svelte
Expand Up @@ -35,6 +35,7 @@
export let min_width: number | undefined = undefined;
export let autoplay = false;
export let show_share_button = true;
export let show_download_button: boolean;
export let gradio: Gradio<{
change: never;
clear: never;
Expand Down Expand Up @@ -146,7 +147,7 @@
{show_label}
{autoplay}
{show_share_button}
show_download_button={true}
{show_download_button}
on:play={() => gradio.dispatch("play")}
on:pause={() => gradio.dispatch("pause")}
on:stop={() => gradio.dispatch("stop")}
Expand Down Expand Up @@ -185,6 +186,7 @@
on:error={handle_error}
{label}
{show_label}
{show_download_button}
{sources}
{active_source}
{mirror_webcam}
Expand Down
22 changes: 22 additions & 0 deletions js/video/Video.stories.svelte
Expand Up @@ -38,6 +38,7 @@
},
label: "world video",
show_label: true,
show_download_button: true,
interactive: false,
height: 200,
width: 400
Expand All @@ -55,6 +56,7 @@
},
label: "world video",
show_label: true,
show_download_button: false,
interactive: false,
height: 200,
width: 400
Expand All @@ -74,6 +76,26 @@
}}
/>

<Story
name="Upload video with download button"
args={{
label: "world video",
show_label: true,
interactive: true,
sources: ["upload", "webcam"],
show_download_button: true,
width: 400,
height: 400,
value: {
video: {
path: "https://gradio-static-files.s3.us-west-2.amazonaws.com/world.mp4",
url: "https://gradio-static-files.s3.us-west-2.amazonaws.com/world.mp4",
orig_name: "world.mp4"
}
}
}}
/>

<Story
name="Trim video"
args={{
Expand Down
7 changes: 6 additions & 1 deletion js/video/shared/InteractiveVideo.svelte
Expand Up @@ -19,6 +19,7 @@
| ["webcam", "upload"]
| ["upload", "webcam"] = ["webcam", "upload"];
export let label: string | undefined = undefined;
export let show_download_button = false;
export let show_label = true;
export let mirror_webcam = false;
export let include_audio: boolean;
Expand Down Expand Up @@ -96,7 +97,11 @@
{/if}
</div>
{:else}
<ModifyUpload {i18n} on:clear={handle_clear} />
<ModifyUpload
{i18n}
on:clear={handle_clear}
download={show_download_button ? value.url : null}
/>
{#if playable()}
{#key value?.url}
<Player
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions test/test_components.py
Expand Up @@ -816,7 +816,7 @@ def test_component_functions(self, gradio_temp_dir):
"autoplay": False,
"sources": ["upload", "microphone"],
"name": "audio",
"show_download_button": True,
"show_download_button": None,
"show_share_button": False,
"streaming": False,
"show_label": True,
Expand Down Expand Up @@ -867,7 +867,7 @@ def test_component_functions(self, gradio_temp_dir):
assert audio_output.get_config() == {
"autoplay": False,
"name": "audio",
"show_download_button": True,
"show_download_button": None,
"show_share_button": False,
"streaming": False,
"show_label": True,
Expand Down Expand Up @@ -1483,6 +1483,7 @@ def test_component_functions(self):
"container": True,
"min_width": 160,
"scale": None,
"show_download_button": None,
"height": None,
"width": None,
"elem_id": None,
Expand Down

0 comments on commit bc2cdc1

Please sign in to comment.