Skip to content

Commit f3652eb

Browse files
dawoodkhan82abidlabsgradio-pr-bot
authored
Video gallery (#9052)
* video support * tests and backend changes * undo main merge * upload fix * Revert "undo main merge" This reverts commit e2a26e6. * type fixes * format * pr fixes * Update gradio/components/gallery.py Co-authored-by: Abubakar Abid <[email protected]> * Update gradio/components/gallery.py Co-authored-by: Abubakar Abid <[email protected]> * type fix * thumbnails * thumbnail type * remove thumbnail generation * add changeset * test fixes * test fixes * python test fix * python test fixc * fix * fix * story fix --------- Co-authored-by: Abubakar Abid <[email protected]> Co-authored-by: gradio-pr-bot <[email protected]>
1 parent 864cd0f commit f3652eb

File tree

19 files changed

+276
-103
lines changed

19 files changed

+276
-103
lines changed

.changeset/light-bats-arrive.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@gradio/core": minor
3+
"@gradio/gallery": minor
4+
"gradio": minor
5+
---
6+
7+
feat:Video gallery
51.9 KB
Loading
1.5 MB
Binary file not shown.

demo/gallery_component/run.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: gallery_component"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " cheetahs = [\n", " \"https://upload.wikimedia.org/wikipedia/commons/0/09/TheCheethcat.jpg\",\n", " \"https://nationalzoo.si.edu/sites/default/files/animals/cheetah-003.jpg\",\n", " \"https://img.etimg.com/thumb/msid-50159822,width-650,imgsize-129520,,resizemode-4,quality-100/.jpg\",\n", " \"https://nationalzoo.si.edu/sites/default/files/animals/cheetah-002.jpg\",\n", " \"https://images.theconversation.com/files/375893/original/file-20201218-13-a8h8uq.jpg?ixlib=rb-1.1.0&rect=16%2C407%2C5515%2C2924&q=45&auto=format&w=496&fit=clip\",\n", " ]\n", " gr.Gallery(value=cheetahs, columns=4)\n", "\n", "demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
1+
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: gallery_component"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('files')\n", "!wget -q -O files/cheetah.jpg https://github.com/gradio-app/gradio/raw/main/demo/gallery_component/files/cheetah.jpg\n", "!wget -q -O files/world.mp4 https://github.com/gradio-app/gradio/raw/main/demo/gallery_component/files/world.mp4"]}, {"cell_type": "code", "execution_count": null, "id": "44380577570523278879349135829904343037", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " gallery_items = [\n", " (\"https://upload.wikimedia.org/wikipedia/commons/0/09/TheCheethcat.jpg\", \"cheetah1\"),\n", " (\"https://nationalzoo.si.edu/sites/default/files/animals/cheetah-003.jpg\", \"cheetah2\"),\n", " (\"https://videos.pexels.com/video-files/3209828/3209828-uhd_2560_1440_25fps.mp4\", \"world\"),\n", " (\"files/cheetah.jpg\", \"cheetah3\"),\n", " (\"files/world.mp4\", \"world2\")\n", " ]\n", " gr.Gallery(value=gallery_items, columns=4)\n", "\n", "demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

demo/gallery_component/run.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import gradio as gr
22

33
with gr.Blocks() as demo:
4-
cheetahs = [
5-
"https://upload.wikimedia.org/wikipedia/commons/0/09/TheCheethcat.jpg",
6-
"https://nationalzoo.si.edu/sites/default/files/animals/cheetah-003.jpg",
7-
"https://img.etimg.com/thumb/msid-50159822,width-650,imgsize-129520,,resizemode-4,quality-100/.jpg",
8-
"https://nationalzoo.si.edu/sites/default/files/animals/cheetah-002.jpg",
9-
"https://images.theconversation.com/files/375893/original/file-20201218-13-a8h8uq.jpg?ixlib=rb-1.1.0&rect=16%2C407%2C5515%2C2924&q=45&auto=format&w=496&fit=clip",
4+
gallery_items = [
5+
("https://upload.wikimedia.org/wikipedia/commons/0/09/TheCheethcat.jpg", "cheetah1"),
6+
("https://nationalzoo.si.edu/sites/default/files/animals/cheetah-003.jpg", "cheetah2"),
7+
("https://videos.pexels.com/video-files/3209828/3209828-uhd_2560_1440_25fps.mp4", "world"),
8+
("files/cheetah.jpg", "cheetah3"),
9+
("files/world.mp4", "world2")
1010
]
11-
gr.Gallery(value=cheetahs, columns=4)
11+
gr.Gallery(value=gallery_items, columns=4)
1212

1313
demo.launch()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: gallery_component_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " cheetahs = [\n", " \"https://gradio-builds.s3.amazonaws.com/assets/cheetah-003.jpg\",\n", " \"https://gradio-builds.s3.amazonaws.com/assets/lite-logo.png\",\n", " \"https://gradio-builds.s3.amazonaws.com/assets/TheCheethcat.jpg\",\n", " ]\n", " with gr.Row():\n", " with gr.Column():\n", " gal = gr.Gallery(columns=4, interactive=True, label=\"Input Gallery\")\n", " btn = gr.Button()\n", " with gr.Column():\n", " output_gal = gr.Gallery(columns=4, interactive=True, label=\"Output Gallery\")\n", " with gr.Row():\n", " textbox = gr.Json(label=\"uploaded files\")\n", " num_upload = gr.Number(value=0, label=\"Num Upload\")\n", " num_change = gr.Number(value=0, label=\"Num Change\")\n", " select_output = gr.Textbox(label=\"Select Data\")\n", " gal.upload(lambda v,n: (v, v, n+1), [gal, num_upload], [textbox, output_gal, num_upload])\n", " gal.change(lambda v,n: (v, v, n+1), [gal, num_change], [textbox, output_gal, num_change])\n", "\n", " btn.click(lambda: cheetahs, None, [output_gal])\n", "\n", " def select(select_data: gr.SelectData):\n", " return select_data.value['image']['url']\n", "\n", " output_gal.select(select, None, select_output)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
1+
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: gallery_component_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " files = [\n", " \"https://gradio-builds.s3.amazonaws.com/assets/cheetah-003.jpg\",\n", " \"https://gradio-static-files.s3.amazonaws.com/world.mp4\",\n", " \"https://gradio-builds.s3.amazonaws.com/assets/TheCheethcat.jpg\",\n", " ]\n", " with gr.Row():\n", " with gr.Column():\n", " gal = gr.Gallery(columns=4, interactive=True, label=\"Input Gallery\")\n", " btn = gr.Button()\n", " with gr.Column():\n", " output_gal = gr.Gallery(columns=4, interactive=True, label=\"Output Gallery\")\n", " with gr.Row():\n", " textbox = gr.Json(label=\"uploaded files\")\n", " num_upload = gr.Number(value=0, label=\"Num Upload\")\n", " num_change = gr.Number(value=0, label=\"Num Change\")\n", " select_output = gr.Textbox(label=\"Select Data\")\n", " gal.upload(lambda v,n: (v, v, n+1), [gal, num_upload], [textbox, output_gal, num_upload])\n", " gal.change(lambda v,n: (v, v, n+1), [gal, num_change], [textbox, output_gal, num_change])\n", "\n", " btn.click(lambda: files, None, [output_gal])\n", "\n", " def select(select_data: gr.SelectData):\n", " return select_data.value['image']['url'] if 'image' in select_data.value else select_data.value['video']['url']\n", "\n", " output_gal.select(select, None, select_output)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

demo/gallery_component_events/run.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import gradio as gr
22

33
with gr.Blocks() as demo:
4-
cheetahs = [
4+
files = [
55
"https://gradio-builds.s3.amazonaws.com/assets/cheetah-003.jpg",
6-
"https://gradio-builds.s3.amazonaws.com/assets/lite-logo.png",
6+
"https://gradio-static-files.s3.amazonaws.com/world.mp4",
77
"https://gradio-builds.s3.amazonaws.com/assets/TheCheethcat.jpg",
88
]
99
with gr.Row():
@@ -20,10 +20,10 @@
2020
gal.upload(lambda v,n: (v, v, n+1), [gal, num_upload], [textbox, output_gal, num_upload])
2121
gal.change(lambda v,n: (v, v, n+1), [gal, num_change], [textbox, output_gal, num_change])
2222

23-
btn.click(lambda: cheetahs, None, [output_gal])
23+
btn.click(lambda: files, None, [output_gal])
2424

2525
def select(select_data: gr.SelectData):
26-
return select_data.value['image']['url']
26+
return select_data.value['image']['url'] if 'image' in select_data.value else select_data.value['video']['url']
2727

2828
output_gal.select(select, None, select_output)
2929

gradio/components/gallery.py

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import numpy as np
1818
import PIL.Image
1919
from gradio_client import handle_file
20+
from gradio_client import utils as client_utils
2021
from gradio_client.documentation import document
2122
from gradio_client.utils import is_http_url_like
2223

@@ -28,24 +29,29 @@
2829
if TYPE_CHECKING:
2930
from gradio.components import Timer
3031

31-
GalleryImageType = Union[np.ndarray, PIL.Image.Image, Path, str]
32-
CaptionedGalleryImageType = tuple[GalleryImageType, str]
32+
GalleryMediaType = Union[np.ndarray, PIL.Image.Image, Path, str]
33+
CaptionedGalleryMediaType = tuple[GalleryMediaType, str]
3334

3435

3536
class GalleryImage(GradioModel):
3637
image: FileData
3738
caption: Optional[str] = None
3839

3940

41+
class GalleryVideo(GradioModel):
42+
video: FileData
43+
caption: Optional[str] = None
44+
45+
4046
class GalleryData(GradioRootModel):
41-
root: list[GalleryImage]
47+
root: list[Union[GalleryImage, GalleryVideo]]
4248

4349

4450
@document()
4551
class Gallery(Component):
4652
"""
47-
Creates a gallery component that allows displaying a grid of images, and optionally captions. If used as an input, the user can upload images to the gallery.
48-
If used as an output, the user can click on individual images to view them at a higher resolution.
53+
Creates a gallery component that allows displaying a grid of images or videos, and optionally captions. If used as an input, the user can upload images or videos to the gallery.
54+
If used as an output, the user can click on individual images or videos to view them at a higher resolution.
4955
5056
Demos: fake_gan
5157
"""
@@ -63,6 +69,7 @@ def __init__(
6369
) = None,
6470
*,
6571
format: str = "webp",
72+
file_types: list[str] | None = None,
6673
label: str | None = None,
6774
every: Timer | float | None = None,
6875
inputs: Component | Sequence[Component] | set[Component] | None = None,
@@ -92,8 +99,9 @@ def __init__(
9299
):
93100
"""
94101
Parameters:
95-
value: List of images to display in the gallery by default. If callable, the function will be called whenever the app loads to set the initial value of the component.
102+
value: List of images or videos to display in the gallery by default. If callable, the function will be called whenever the app loads to set the initial value of the component.
96103
format: Format to save images before they are returned to the frontend, such as 'jpeg' or 'png'. This parameter only applies to images that are returned from the prediction function as numpy arrays or PIL Images. The format should be supported by the PIL library.
104+
file_types: List of file extensions or types of files to be uploaded (e.g. ['image', 'video']), when this is used as an input component. "image" allows only image files to be uploaded, "video" allows only video files to be uploaded, ".mp4" allows only mp4 files to be uploaded, etc. If None, any image and video files types are allowed.
97105
label: The label for this component. Appears above the component and is also used as the header if there are a table of examples for this component. If None and used in a `gr.Interface`, the label will be the name of the parameter this component is assigned to.
98106
every: Continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
99107
inputs: Components that are used as inputs to calculate `value` if `value` is a function (has no effect otherwise). `value` is recalculated any time the inputs change.
@@ -134,6 +142,7 @@ def __init__(
134142
self.selected_index = selected_index
135143
self.type = type
136144
self.show_fullscreen_button = show_fullscreen_button
145+
self.file_types = file_types
137146

138147
self.show_share_button = (
139148
(utils.get_space() is not None)
@@ -167,27 +176,31 @@ def preprocess(
167176
):
168177
"""
169178
Parameters:
170-
payload: a list of images, or list of (image, caption) tuples
179+
payload: a list of images or videos, or list of (media, caption) tuples
171180
Returns:
172-
Passes the list of images as a list of (image, caption) tuples, or a list of (image, None) tuples if no captions are provided (which is usually the case). The image can be a `str` file path, a `numpy` array, or a `PIL.Image` object depending on `type`.
181+
Passes the list of images or videos as a list of (media, caption) tuples, or a list of (media, None) tuples if no captions are provided (which is usually the case). Images can be a `str` file path, a `numpy` array, or a `PIL.Image` object depending on `type`. Videos are always `str` file path.
173182
"""
174183
if payload is None or not payload.root:
175184
return None
176185
data = []
177186
for gallery_element in payload.root:
178-
image = self.convert_to_type(gallery_element.image.path, self.type) # type: ignore
179-
data.append((image, gallery_element.caption))
187+
media = (
188+
gallery_element.video.path
189+
if (type(gallery_element) is GalleryVideo)
190+
else self.convert_to_type(gallery_element.image.path, self.type) # type: ignore
191+
)
192+
data.append((media, gallery_element.caption))
180193
return data
181194

182195
def postprocess(
183196
self,
184-
value: list[GalleryImageType | CaptionedGalleryImageType] | None,
197+
value: list[GalleryMediaType | CaptionedGalleryMediaType] | None,
185198
) -> GalleryData:
186199
"""
187200
Parameters:
188-
value: Expects the function to return a `list` of images, or `list` of (image, `str` caption) tuples. Each image can be a `str` file path, a `numpy` array, or a `PIL.Image` object.
201+
value: Expects the function to return a `list` of images or videos, or `list` of (media, `str` caption) tuples. Each image can be a `str` file path, a `numpy` array, or a `PIL.Image` object. Each video can be a `str` file path.
189202
Returns:
190-
a list of images, or list of (image, caption) tuples
203+
a list of images or videos, or list of (media, caption) tuples
191204
"""
192205
if value is None:
193206
return GalleryData(root=[])
@@ -197,6 +210,7 @@ def _save(img):
197210
url = None
198211
caption = None
199212
orig_name = None
213+
mime_type = None
200214
if isinstance(img, (tuple, list)):
201215
img, caption = img
202216
if isinstance(img, np.ndarray):
@@ -211,6 +225,7 @@ def _save(img):
211225
file_path = str(utils.abspath(file))
212226
elif isinstance(img, str):
213227
file_path = img
228+
mime_type = client_utils.get_mimetype(file_path)
214229
if is_http_url_like(img):
215230
url = img
216231
orig_name = Path(urlparse(img).path).name
@@ -220,12 +235,29 @@ def _save(img):
220235
elif isinstance(img, Path):
221236
file_path = str(img)
222237
orig_name = img.name
238+
mime_type = client_utils.get_mimetype(file_path)
223239
else:
224240
raise ValueError(f"Cannot process type as image: {type(img)}")
225-
return GalleryImage(
226-
image=FileData(path=file_path, url=url, orig_name=orig_name),
227-
caption=caption,
228-
)
241+
if mime_type is not None and "video" in mime_type:
242+
return GalleryVideo(
243+
video=FileData(
244+
path=file_path,
245+
url=url,
246+
orig_name=orig_name,
247+
mime_type=mime_type,
248+
),
249+
caption=caption,
250+
)
251+
else:
252+
return GalleryImage(
253+
image=FileData(
254+
path=file_path,
255+
url=url,
256+
orig_name=orig_name,
257+
mime_type=mime_type,
258+
),
259+
caption=caption,
260+
)
229261

230262
if wasm_utils.IS_WASM:
231263
for img in value:

js/core/src/lang/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
"drop_file": "Drop File Here",
113113
"drop_image": "Drop Image Here",
114114
"drop_video": "Drop Video Here",
115-
"drop_gallery": "Drop Image(s) Here",
115+
"drop_gallery": "Drop Media Here",
116116
"paste_clipboard": "Paste from Clipboard"
117117
}
118118
}

js/gallery/Gallery.stories.svelte

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@
8686
},
8787
caption: "A fast cheetah"
8888
},
89+
{
90+
video: {
91+
path: "https://gradio-builds.s3.amazonaws.com/demo-files/world.mp4",
92+
url: "https://gradio-builds.s3.amazonaws.com/demo-files/world.mp4",
93+
orig_name: "world.mp4"
94+
},
95+
caption: "The world"
96+
},
8997
{
9098
image: {
9199
path: "https://gradio-builds.s3.amazonaws.com/demo-files/cheetah-002.jpg",
@@ -139,7 +147,7 @@
139147
play={async ({ canvasElement }) => {
140148
const canvas = within(canvasElement);
141149

142-
const image = canvas.getByLabelText("Thumbnail 1 of 7");
150+
const image = canvas.getByLabelText("Thumbnail 1 of 8");
143151
await userEvent.click(image);
144152
const expand_btn = canvas.getByRole("button", {
145153
name: "View in full screen"

0 commit comments

Comments
 (0)