rework upload to be a class method + pass client into each component (#8179)

* rework upload to be a class method + pass client into each component

* add changeset

* Update client/js/src/utils/upload_files.ts

* fix storybook

* review comments

* Apply suggestions from code review

Co-authored-by: Hannah <hannahblair@users.noreply.github.com>

* format

* ts fix

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Hannah <hannahblair@users.noreply.github.com>
This commit is contained in:
pngwn 2024-05-01 15:55:41 +01:00 committed by GitHub
parent a0e70366a8
commit 6a218b4148
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 191 additions and 122 deletions

View File

@ -0,0 +1,22 @@
---
"@gradio/app": patch
"@gradio/audio": patch
"@gradio/client": patch
"@gradio/dataframe": patch
"@gradio/file": patch
"@gradio/gallery": patch
"@gradio/image": patch
"@gradio/imageeditor": patch
"@gradio/model3d": patch
"@gradio/multimodaltextbox": patch
"@gradio/simpleimage": patch
"@gradio/storybook": patch
"@gradio/tootils": patch
"@gradio/upload": patch
"@gradio/uploadbutton": patch
"@gradio/utils": patch
"@gradio/video": patch
"gradio": patch
---
fix:rework upload to be a class method + pass client into each component

View File

@ -14,6 +14,7 @@ import type {
} from "./types";
import { view_api } from "./utils/view_api";
import { upload_files } from "./utils/upload_files";
import { upload, FileData } from "./upload";
import { handle_blob } from "./utils/handle_blob";
import { post_data } from "./utils/post_data";
import { predict } from "./utils/predict";
@ -61,10 +62,11 @@ export class Client {
return fetch(input, init);
}
eventSource_factory(url: URL): EventSource | null {
eventSource_factory(url: URL): EventSource {
if (typeof window !== undefined && typeof EventSource !== "undefined") {
return new EventSource(url.toString());
}
// @ts-ignore
return null; // todo: polyfill eventsource for node envs
}
@ -74,6 +76,12 @@ export class Client {
files: (Blob | File)[],
upload_id?: string
) => Promise<UploadResponse>;
upload: (
file_data: FileData[],
root_url: string,
upload_id?: string,
max_file_size?: number
) => Promise<(FileData | null)[] | null>;
handle_blob: (
endpoint: string,
data: unknown[],
@ -109,6 +117,7 @@ export class Client {
this.predict = predict.bind(this);
this.open_stream = open_stream.bind(this);
this.resolve_config = resolve_config.bind(this);
this.upload = upload.bind(this);
}
private async init(): Promise<void> {

View File

@ -1,16 +1,12 @@
import type { UploadResponse } from "./types";
import { upload_files } from ".";
import type { Client } from "./client";
export async function upload(
this: Client,
file_data: FileData[],
root_url: string,
upload_id?: string,
max_file_size?: number,
upload_fn: (
root_url: string,
files: (Blob | File)[],
upload_id?: string
) => Promise<UploadResponse> = upload_files
max_file_size?: number
): Promise<(FileData | null)[] | null> {
let files = (Array.isArray(file_data) ? file_data : [file_data]).map(
(file_data) => file_data.blob!
@ -28,7 +24,7 @@ export async function upload(
}
return await Promise.all(
await upload_fn(root_url, files, upload_id).then(
await this.upload_files(root_url, files, upload_id).then(
async (response: { files?: string[]; error?: string }) => {
if (response.error) {
throw new Error(response.error);

View File

@ -11,9 +11,10 @@ export async function upload_files(
const headers: {
Authorization?: string;
} = {};
if (this.options.hf_token) {
if (this?.options?.hf_token) {
headers.Authorization = `Bearer ${this.options.hf_token}`;
}
const chunkSize = 1000;
const uploadResponses = [];
let response: Response;

View File

@ -2,7 +2,6 @@
import { tick } from "svelte";
import { _ } from "svelte-i18n";
import { Client } from "@gradio/client";
import { setContext } from "svelte";
import type { LoadingStatus, LoadingStatusCollection } from "./stores";
@ -522,8 +521,6 @@
function isCustomEvent(event: Event): event is CustomEvent {
return "detail" in event;
}
setContext("upload_files", app.upload_files);
</script>
<svelte:head>
@ -562,6 +559,7 @@
{version}
{autoscroll}
max_file_size={app.config.max_file_size}
client={app}
/>
{/if}
</div>

View File

@ -63,7 +63,7 @@
</script>
<script lang="ts">
import { onMount, setContext, createEventDispatcher } from "svelte";
import { onMount, createEventDispatcher } from "svelte";
import type { SpaceStatus } from "@gradio/client";
import Embed from "./Embed.svelte";
import type { ThemeMode } from "./types";
@ -100,11 +100,6 @@
loading_text = (event as CustomEvent).detail + "...";
});
}
export let fetch_implementation: typeof fetch = fetch;
setContext("fetch_implementation", fetch_implementation);
export let EventSource_factory: (url: URL) => EventSource = (url) =>
new EventSource(url);
setContext("EventSource_factory", EventSource_factory);
export let space: string | null;
export let host: string | null;

View File

@ -1,5 +1,6 @@
<script lang="ts">
import { onMount, createEventDispatcher } from "svelte";
import type { Client } from "@gradio/client";
import Render from "./Render.svelte";
export let rootNode: any;
@ -9,6 +10,7 @@
export let version: any;
export let autoscroll: boolean;
export let max_file_size: number | null = null;
export let client: Client;
const dispatch = createEventDispatcher<{ mount?: never }>();
onMount(() => {
@ -24,4 +26,5 @@
{version}
{autoscroll}
{max_file_size}
{client}
/>

View File

@ -2,6 +2,7 @@
import { Gradio, formatter } from "./gradio_helper";
import { onMount, createEventDispatcher, setContext } from "svelte";
import type { ComponentMeta, ThemeMode } from "./types";
import type { Client } from "@gradio/client";
import RenderComponent from "./RenderComponent.svelte";
export let root: string;
@ -13,6 +14,7 @@
export let version: string;
export let autoscroll: boolean;
export let max_file_size: number | null;
export let client: Client;
const dispatch = createEventDispatcher<{ mount: number; destroy: number }>();
let filtered_children: ComponentMeta[] = [];
@ -45,12 +47,6 @@
setContext("BLOCK_KEY", parent);
function handle_prop_change(e: { detail: Record<string, any> }): void {
// for (const k in e.detail) {
// instance_map[id].props[k] = e.detail[k];
// }
}
$: {
if (node.type === "form") {
if (node.children?.every((c) => !c.props.visible)) {
@ -70,7 +66,6 @@
elem_id={("elem_id" in node.props && node.props.elem_id) ||
`component-${node.id}`}
elem_classes={("elem_classes" in node.props && node.props.elem_classes) || []}
on:prop_change={handle_prop_change}
{target}
{...node.props}
{theme_mode}
@ -83,7 +78,8 @@
root,
autoscroll,
max_file_size,
formatter
formatter,
client
)}
>
{#if node.children && node.children.length}
@ -98,6 +94,7 @@
on:destroy
on:mount
{max_file_size}
{client}
/>
{/each}
{/if}

View File

@ -23,9 +23,7 @@
export let mount_css: typeof default_mount_css = default_mount_css;
export let Client: typeof ClientType;
export let worker_proxy: WorkerProxy | undefined = undefined;
export let fetch_implementation: typeof fetch = fetch;
export let EventSource_factory: (url: URL) => EventSource = (url) =>
new EventSource(url);
export let space: string | null;
export let host: string | null;
export let src: string | null;
@ -217,8 +215,6 @@
{mount_css}
{Client}
bind:worker_proxy
{fetch_implementation}
{EventSource_factory}
{space}
{host}
{src}

View File

@ -126,13 +126,6 @@ export function create(options: Options): GradioAppController {
mount_prebuilt_css(document.head);
const overridden_fetch: typeof fetch = (input, init?) => {
return wasm_proxied_fetch(worker_proxy, input, init);
};
const EventSource_factory = (url: URL): EventSource => {
return wasm_proxied_EventSource_factory(worker_proxy, url);
};
class LiteClient extends Client {
fetch_implementation(
input: RequestInfo | URL,
@ -222,8 +215,6 @@ export function create(options: Options): GradioAppController {
worker_proxy,
Client: LiteClient,
mount_css: overridden_mount_css,
fetch_implementation: overridden_fetch,
EventSource_factory,
// For playground
layout: options.layout
};

View File

@ -225,6 +225,8 @@
{waveform_settings}
{waveform_options}
{trim_region_settings}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
>
<UploadText i18n={gradio.i18n} type="audio" />
</InteractiveAudio>

View File

@ -1,12 +1,7 @@
<script lang="ts">
import { getContext, onDestroy, createEventDispatcher } from "svelte";
import { onDestroy, createEventDispatcher } from "svelte";
import { Upload, ModifyUpload } from "@gradio/upload";
import {
upload,
prepare_files,
type FileData,
type upload_files
} from "@gradio/client";
import { prepare_files, type FileData, type Client } from "@gradio/client";
import { BlockLabel } from "@gradio/atoms";
import { Music } from "@gradio/icons";
import AudioPlayer from "../player/AudioPlayer.svelte";
@ -39,9 +34,8 @@
export let handle_reset_value: () => void = () => {};
export let editable = true;
export let max_file_size: number | null = null;
// Needed for wasm support
const upload_fn = getContext<typeof upload_files>("upload_files");
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
$: dispatch("drag", dragging);
@ -98,9 +92,9 @@
let _audio_blob = new File(blobs, "audio.wav");
const val = await prepare_files([_audio_blob], event === "stream");
value = (
(
await upload(val, root, undefined, max_file_size ?? Infinity, upload_fn)
)?.filter(Boolean) as FileData[]
(await upload(val, root, undefined, max_file_size || undefined))?.filter(
Boolean
) as FileData[]
)[0];
dispatch(event, value);
@ -257,6 +251,8 @@
on:error={({ detail }) => dispatch("error", detail)}
{root}
{max_file_size}
{upload}
{stream_handler}
>
<slot />
</Upload>

View File

@ -150,5 +150,7 @@
i18n={gradio.i18n}
{line_breaks}
{column_widths}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
/>
</Block>

View File

@ -11,6 +11,7 @@
"dependencies": {
"@gradio/atoms": "workspace:^",
"@gradio/button": "workspace:^",
"@gradio/client": "workspace:^",
"@gradio/markdown": "workspace:^",
"@gradio/statustracker": "workspace:^",
"@gradio/upload": "workspace:^",

View File

@ -8,6 +8,7 @@
import EditableCell from "./EditableCell.svelte";
import type { SelectData } from "@gradio/utils";
import type { I18nFormatter } from "js/app/src/gradio_helper";
import { type Client } from "@gradio/client";
import VirtualTable from "./VirtualTable.svelte";
import type {
Headers,
@ -38,6 +39,8 @@
export let height = 500;
export let line_breaks = true;
export let column_widths: string[] = [];
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
let selected: false | [number, number] = false;
export let display_value: string[][] | null = null;
@ -722,6 +725,8 @@
</tbody>
</table>
<Upload
{upload}
{stream_handler}
flex={false}
center={false}
boundedheight={false}

View File

@ -87,6 +87,8 @@
/>
{:else}
<FileUpload
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
{label}
{show_label}
{value}

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { createEventDispatcher, tick } from "svelte";
import { Upload, ModifyUpload } from "@gradio/upload";
import type { FileData } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import { BlockLabel } from "@gradio/atoms";
import { File } from "@gradio/icons";
@ -19,6 +19,8 @@
export let height: number | undefined = undefined;
export let i18n: I18nFormatter;
export let max_file_size: number | null = null;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
async function handle_upload({
detail
@ -67,6 +69,8 @@
{root}
bind:dragging
on:error
{stream_handler}
{upload}
>
<slot />
</Upload>

View File

@ -77,6 +77,8 @@
file_count={"multiple"}
file_types={["image"]}
i18n={gradio.i18n}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
on:upload={(e) => {
const files = Array.isArray(e.detail) ? e.detail : [e.detail];
value = files.map((x) => ({ image: x, caption: null }));
@ -110,6 +112,7 @@
{show_share_button}
{show_download_button}
i18n={gradio.i18n}
fetch_implementation={gradio.client.fetch_implementation}
/>
{/if}
</Block>

View File

@ -4,7 +4,7 @@
import type { SelectData } from "@gradio/utils";
import { Image } from "@gradio/image/shared";
import { dequal } from "dequal";
import { createEventDispatcher, getContext } from "svelte";
import { createEventDispatcher } from "svelte";
import { tick } from "svelte";
import { Download, Image as ImageIcon } from "@gradio/icons";
@ -31,6 +31,7 @@
export let i18n: I18nFormatter;
export let selected_index: number | null = null;
export let interactive: boolean;
export let fetch_implementation: typeof fetch;
const dispatch = createEventDispatcher<{
change: undefined;
@ -159,14 +160,12 @@
}
}
let client_height = 0;
let window_height = 0;
// Unlike `gr.Image()`, images specified via remote URLs are not cached in the server
// and their remote URLs are directly passed to the client as `value[].image.url`.
// The `download` attribute of the <a> tag doesn't work for remote URLs (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download),
// so we need to download the image via JS as below.
const fetch_implementation = getContext<typeof fetch>("fetch_implementation");
async function download(file_url: string, name: string): Promise<void> {
let response;
try {
@ -279,11 +278,7 @@
</button>
{/if}
<div
bind:clientHeight={client_height}
class="grid-wrap"
class:fixed-height={!height || height == "auto"}
>
<div class="grid-wrap" class:fixed-height={!height || height == "auto"}>
<div
class="grid-container"
style="--grid-cols:{columns}; --grid-rows:{rows}; --object-fit: {object_fit}; height: {height};"

View File

@ -156,6 +156,8 @@
{mirror_webcam}
max_file_size={gradio.max_file_size}
i18n={gradio.i18n}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
>
{#if active_source === "upload" || !active_source}
<UploadText i18n={gradio.i18n} type="image" />

View File

@ -7,7 +7,7 @@
import Webcam from "./Webcam.svelte";
import { Upload } from "@gradio/upload";
import type { FileData } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import ClearImage from "./ClearImage.svelte";
import { SelectSource } from "@gradio/atoms";
import Image from "./Image.svelte";
@ -26,8 +26,10 @@
export let root: string;
export let i18n: I18nFormatter;
export let max_file_size: number | null = null;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
let upload: Upload;
let upload_input: Upload;
let uploading = false;
export let active_source: source_type = null;
@ -44,7 +46,9 @@
async function handle_save(img_blob: Blob | any): Promise<void> {
pending = true;
const f = await upload.load_files([new File([img_blob], `webcam.png`)]);
const f = await upload_input.load_files([
new File([img_blob], `webcam.png`)
]);
value = f?.[0] || null;
@ -86,7 +90,7 @@
): Promise<void> {
switch (source) {
case "clipboard":
upload.paste_clipboard();
upload_input.paste_clipboard();
break;
default:
break;
@ -108,7 +112,7 @@
<div class="upload-container">
<Upload
hidden={value !== null || active_source === "webcam"}
bind:this={upload}
bind:this={upload_input}
bind:uploading
bind:dragging
filetype={active_source === "clipboard" ? "clipboard" : "image/*"}
@ -117,6 +121,8 @@
{root}
{max_file_size}
disable_click={!sources.includes("upload")}
{upload}
{stream_handler}
>
{#if value === null}
<slot />
@ -135,6 +141,7 @@
mode="image"
include_audio={false}
{i18n}
{upload}
/>
{:else if value !== null && !streaming}
<!-- svelte-ignore a11y-click-events-have-key-events-->

View File

@ -2,8 +2,7 @@
import { createEventDispatcher, onMount } from "svelte";
import { Camera, Circle, Square, DropdownArrow } from "@gradio/icons";
import type { I18nFormatter } from "@gradio/utils";
import type { FileData } from "@gradio/client";
import { prepare_files, upload } from "@gradio/client";
import { type FileData, type Client, prepare_files } from "@gradio/client";
import WebcamPermissions from "./WebcamPermissions.svelte";
import { fade } from "svelte/transition";
import {
@ -25,6 +24,7 @@
export let mirror_webcam: boolean;
export let include_audio: boolean;
export let i18n: I18nFormatter;
export let upload: Client["upload"];
const dispatch = createEventDispatcher<{
stream: undefined;

View File

@ -199,11 +199,14 @@
{brush}
{eraser}
changeable={attached_events.includes("apply")}
realtime={attached_events.includes("change")}
i18n={gradio.i18n}
{transforms}
accept_blobs={server.accept_blobs}
{layers}
status={loading_status?.status}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
></InteractiveImageEditor>
</Block>
{/if}

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { type I18nFormatter } from "@gradio/utils";
import { prepare_files, upload, type FileData } from "@gradio/client";
import { prepare_files, type FileData, type Client } from "@gradio/client";
import ImageEditor from "./ImageEditor.svelte";
import Layers from "./layers/Layers.svelte";
@ -44,6 +44,9 @@
export let layers: boolean;
export let accept_blobs: (a: any) => void;
export let status: "pending" | "complete" | "error" = "complete";
export let realtime: boolean;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
const dispatch = createEventDispatcher<{
clear?: never;
@ -131,6 +134,7 @@
let uploading = false;
let pending = false;
async function handle_change(e: CustomEvent<Blob | any>): Promise<void> {
if (!realtime) return;
if (uploading) {
pending = true;
return;
@ -217,6 +221,8 @@
{i18n}
{root}
{sources}
{upload}
{stream_handler}
bind:bg
bind:active_mode
background_file={value?.background || value?.composite || null}

View File

@ -11,6 +11,7 @@
import { Webcam } from "@gradio/image";
import { type I18nFormatter } from "@gradio/utils";
import { IconButton } from "@gradio/atoms";
import { type Client } from "@gradio/client";
import { add_bg_color, add_bg_image } from "./sources";
import type { FileData } from "@gradio/client";
@ -24,6 +25,8 @@
];
export let mirror_webcam = true;
export let i18n: I18nFormatter;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
const { active_tool } = getContext<ToolContext>(TOOL_KEY);
const { pixi, dimensions, register_context, reset, editor_box } =
@ -43,7 +46,7 @@
order: 0,
id: "bg_upload",
cb() {
upload.open_file_upload();
upload_component.open_file_upload();
$active_tool = "bg";
}
@ -74,7 +77,7 @@
.map((src) => sources_meta[src])
.sort((a, b) => a.order - b.order);
let upload: Upload;
let upload_component: Upload;
async function process_clipboard(): Promise<void> {
const items = await navigator.clipboard.read();
@ -179,13 +182,15 @@
<div class="upload-container">
<Upload
hidden={true}
bind:this={upload}
bind:this={upload_component}
filetype="image/*"
on:load={handle_upload}
on:error
{root}
disable_click={!sources.includes("upload")}
format="blob"
{upload}
{stream_handler}
></Upload>
{#if active_mode === "webcam"}
<div
@ -196,6 +201,7 @@
>
<div class="modal-inner">
<Webcam
{upload}
{root}
on:capture={handle_upload}
on:error

View File

@ -127,6 +127,8 @@
}}
i18n={gradio.i18n}
max_file_size={gradio.max_file_size}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
>
<UploadText i18n={gradio.i18n} type="file" />
</Model3DUpload>

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { createEventDispatcher, tick } from "svelte";
import { Upload, ModifyUpload } from "@gradio/upload";
import type { FileData } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import { BlockLabel } from "@gradio/atoms";
import { File } from "@gradio/icons";
import type { I18nFormatter } from "@gradio/utils";
@ -24,6 +24,8 @@
null,
null
];
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
async function handle_upload({
detail
@ -86,6 +88,8 @@
{#if value === null}
<Upload
{upload}
{stream_handler}
on:load={handle_upload}
{root}
{max_file_size}

View File

@ -97,5 +97,7 @@
gradio.dispatch("error", detail);
}}
disabled={!interactive}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
/>
</Block>

View File

@ -9,7 +9,7 @@
import { BlockTitle } from "@gradio/atoms";
import { Upload } from "@gradio/upload";
import { Image } from "@gradio/image/shared";
import type { FileData, upload } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import { Clear, File, Music, Video, Send } from "@gradio/icons";
import type { SelectData } from "@gradio/utils";
@ -35,6 +35,8 @@
export let root: string;
export let file_types: string[] | null = null;
export let max_file_size: number | null = null;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
let upload_component: Upload;
let hidden_upload: HTMLInputElement;
@ -215,6 +217,8 @@
disable_click={true}
bind:hidden_upload
on:error
{upload}
{stream_handler}
>
{#if submit_btn !== null}
<button class:disabled class="submit-button" on:click={handle_submit}

View File

@ -90,6 +90,8 @@
/>
<ImageUploader
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
bind:value
{root}
on:clear={() => gradio.dispatch("clear")}

View File

@ -4,15 +4,17 @@
import { Image as ImageIcon } from "@gradio/icons";
import { Upload } from "@gradio/upload";
import type { FileData } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import ClearImage from "./ClearImage.svelte";
export let value: null | FileData;
export let label: string | undefined = undefined;
export let show_label: boolean;
export let root: string;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
let upload: Upload;
let upload_component: Upload;
let uploading = false;
function handle_upload({ detail }: CustomEvent<FileData>): void {
@ -45,8 +47,10 @@
{/if}
<div class="upload-container">
<Upload
{upload}
{stream_handler}
hidden={value !== null}
bind:this={upload}
bind:this={upload_component}
bind:uploading
bind:dragging
filetype="image/*"

View File

@ -21,7 +21,9 @@ const preview: Preview = {
"localhost:9876",
false,
null,
formatter
formatter,
// @ts-ignore
{ client: { fetch_implementation() {}, upload() {} } }
)
},
argTypes: {

View File

@ -92,6 +92,7 @@ export async function render<
props: {
loading_status,
...(props || {}),
//@ts-ignore
gradio: new Gradio(
id,
target,
@ -99,7 +100,11 @@ export async function render<
"2.0.0",
"http://localhost:8000",
false,
null
null,
//@ts-ignore
(s) => s,
// @ts-ignore
{ client: {} }
)
}
});

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { createEventDispatcher, tick, getContext } from "svelte";
import type { FileData } from "@gradio/client";
import { upload_files, upload, prepare_files } from "@gradio/client";
import { prepare_files, type Client } from "@gradio/client";
import { _ } from "svelte-i18n";
import UploadProgress from "./UploadProgress.svelte";
@ -19,14 +19,13 @@
export let hidden_upload: HTMLInputElement | null = null;
export let show_progress = true;
export let max_file_size: number | null = null;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
let upload_id: string;
let file_data: FileData[];
let accept_file_types: string | null;
// Needed for wasm support
const upload_fn = getContext<typeof upload_files>("upload_files");
const dispatch = createEventDispatcher();
const validFileTypes = ["image", "video", "audio", "text", "file"];
const processFileType = (type: string): string => {
@ -89,8 +88,7 @@
file_data,
root,
upload_id,
max_file_size ?? Infinity,
upload_fn
max_file_size ?? Infinity
);
dispatch("load", file_count === "single" ? _file_data?.[0] : _file_data);
uploading = false;
@ -201,7 +199,7 @@
</button>
{:else if uploading && show_progress}
{#if !hidden}
<UploadProgress {root} {upload_id} files={file_data} />
<UploadProgress {root} {upload_id} files={file_data} {stream_handler} />
{/if}
{:else}
<button

View File

@ -1,19 +1,15 @@
<script lang="ts">
import { FileData } from "@gradio/client";
import {
onMount,
createEventDispatcher,
getContext,
onDestroy
} from "svelte";
import { FileData, type Client } from "@gradio/client";
import { onMount, createEventDispatcher, onDestroy } from "svelte";
type FileDataWithProgress = FileData & { progress: number };
export let upload_id: string;
export let root: string;
export let files: FileData[];
export let stream_handler: Client["eventSource_factory"];
let event_source: EventSource;
let event_source: ReturnType<Client["eventSource_factory"]>;
let progress = false;
let current_file_upload: FileDataWithProgress;
let file_to_display: FileDataWithProgress;
@ -41,13 +37,14 @@
return (file.progress * 100) / (file.size || 0) || 0;
}
const EventSource_factory = getContext<(url: URL) => EventSource>(
"EventSource_factory"
);
onMount(() => {
event_source = EventSource_factory(
event_source = stream_handler(
new URL(`${root}/upload_progress?upload_id=${upload_id}`)
);
if (event_source == null) {
throw new Error("Event source is not defined");
}
// Event listener for progress updates
event_source.onmessage = async function (event) {
const _data = JSON.parse(event.data);

View File

@ -61,6 +61,7 @@
on:error={({ detail }) => {
gradio.dispatch("error", detail);
}}
upload={gradio.client.upload}
>
{label ? gradio.i18n(label) : ""}
</UploadButton>

View File

@ -1,12 +1,7 @@
<script lang="ts">
import { tick, createEventDispatcher, getContext } from "svelte";
import { tick, createEventDispatcher } from "svelte";
import { BaseButton } from "@gradio/button";
import {
upload,
prepare_files,
type FileData,
type upload_files
} from "@gradio/client";
import { prepare_files, type FileData, type Client } from "@gradio/client";
export let elem_id = "";
export let elem_classes: string[] = [];
@ -23,12 +18,10 @@
export let variant: "primary" | "secondary" | "stop" = "secondary";
export let disabled = false;
export let max_file_size: number | null = null;
export let upload: Client["upload"];
const dispatch = createEventDispatcher();
// Needed for wasm support
const upload_fn = getContext<typeof upload_files>("upload_files");
let hidden_upload: HTMLInputElement;
let accept_file_types: string | null;
@ -63,13 +56,7 @@
try {
all_file_data = (
await upload(
all_file_data,
root,
undefined,
max_file_size ?? Infinity,
upload_fn
)
await upload(all_file_data, root, undefined, max_file_size ?? Infinity)
)?.filter((x) => x !== null) as FileData[];
} catch (e) {
dispatch("error", (e as Error).message);

View File

@ -1,4 +1,5 @@
import type { ActionReturn } from "svelte/action";
import type { Client } from "@gradio/client";
export interface SelectData {
index: number | [number, number];
value: any;
@ -181,6 +182,7 @@ export class Gradio<T extends Record<string, any> = Record<string, any>> {
root: string;
autoscroll: boolean;
max_file_size: number | null;
client: Client;
constructor(
id: number,
@ -190,7 +192,8 @@ export class Gradio<T extends Record<string, any> = Record<string, any>> {
root: string,
autoscroll: boolean,
max_file_size: number | null,
i18n: I18nFormatter = (x: string): string => x
i18n: I18nFormatter = (x: string): string => x,
client: Client
) {
this.#id = id;
this.theme = theme;
@ -201,6 +204,7 @@ export class Gradio<T extends Record<string, any> = Record<string, any>> {
this.i18n = i18n;
this.root = root;
this.autoscroll = autoscroll;
this.client = client;
}
dispatch<E extends keyof T>(event_name: E, data?: T[E]): void {

View File

@ -156,6 +156,7 @@
on:share={({ detail }) => gradio.dispatch("share", detail)}
on:error={({ detail }) => gradio.dispatch("error", detail)}
i18n={gradio.i18n}
upload={gradio.client.upload}
/>
</Block>
{:else}
@ -206,6 +207,8 @@
on:stop_recording={() => gradio.dispatch("stop_recording")}
i18n={gradio.i18n}
max_file_size={gradio.max_file_size}
upload={gradio.client.upload}
stream_handler={gradio.client.eventSource_factory}
>
<UploadText i18n={gradio.i18n} type="video" />
</Video>

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { Upload, ModifyUpload } from "@gradio/upload";
import type { FileData } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import { BlockLabel } from "@gradio/atoms";
import { Webcam } from "@gradio/image";
import { Video } from "@gradio/icons";
@ -29,6 +29,8 @@
export let active_source: "webcam" | "upload" = "webcam";
export let handle_reset_value: () => void = () => {};
export let max_file_size: number | null = null;
export let upload: Client["upload"];
export let stream_handler: Client["eventSource_factory"];
const dispatch = createEventDispatcher<{
change: FileData | null;
@ -81,6 +83,8 @@
{max_file_size}
on:error={({ detail }) => dispatch("error", detail)}
{root}
{upload}
{stream_handler}
>
<slot />
</Upload>
@ -95,6 +99,7 @@
on:start_recording
on:stop_recording
{i18n}
{upload}
/>
{/if}
</div>
@ -107,6 +112,7 @@
{#if playable()}
{#key value?.url}
<Player
{upload}
{root}
interactive
{autoplay}

View File

@ -3,8 +3,8 @@
import { Play, Pause, Maximise, Undo } from "@gradio/icons";
import Video from "./Video.svelte";
import VideoControls from "./VideoControls.svelte";
import type { FileData } from "@gradio/client";
import { prepare_files, upload } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import { prepare_files } from "@gradio/client";
import { format_time } from "@gradio/utils";
export let root = "";
@ -16,6 +16,7 @@
export let interactive = false;
export let handle_change: (video: FileData) => void = () => {};
export let handle_reset_value: () => void = () => {};
export let upload: Client["upload"];
const dispatch = createEventDispatcher<{
play: undefined;

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { createEventDispatcher, afterUpdate, tick } from "svelte";
import { BlockLabel, Empty, IconButton, ShareButton } from "@gradio/atoms";
import type { FileData } from "@gradio/client";
import type { FileData, Client } from "@gradio/client";
import { Video, Download } from "@gradio/icons";
import { uploadToHuggingFace } from "@gradio/utils";
import { DownloadLink } from "@gradio/wasm/svelte";
@ -17,6 +17,7 @@
export let show_share_button = true;
export let show_download_button = true;
export let i18n: I18nFormatter;
export let upload: Client["upload"];
let old_value: FileData | null = null;
let old_subtitle: FileData | null = null;
@ -64,6 +65,7 @@
mirror={false}
{label}
interactive={false}
{upload}
/>
{/key}
<div class="icon-buttons" data-testid="download-div">

View File

@ -809,6 +809,9 @@ importers:
'@gradio/button':
specifier: workspace:^
version: link:../button
'@gradio/client':
specifier: workspace:^
version: link:../../client/js
'@gradio/markdown':
specifier: workspace:^
version: link:../markdown