diff --git a/gradio/components.py b/gradio/components.py index 098e216e56..abfa71443c 100644 --- a/gradio/components.py +++ b/gradio/components.py @@ -11,6 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple import numpy as np import PIL +from ffmpy import FFmpeg from gradio import processing_utils, test_data from gradio.blocks import Block @@ -507,7 +508,7 @@ class Slider(Component): if step is None: difference = maximum - minimum power = math.floor(math.log10(difference) - 2) - step = 10**power + step = 10 ** power self.step = step self.default = minimum if default is None else default self.test_input = self.default @@ -931,6 +932,7 @@ class Image(Component): ): """ Parameters: + default(str): IGNORED shape (Tuple[int, int]): (width, height) shape to crop and resize image to; if None, matches input image size. image_mode (str): "RGB" if color, or "L" if black and white. invert_colors (bool): whether to invert the image as a preprocessing step. @@ -1210,3 +1212,122 @@ class Image(Component): def restore_flagged(self, dir, data, encryption_key): return self.restore_flagged_file(dir, data, encryption_key)["data"] + + +class Video(Component): + """ + Component creates a video file upload that is converted to a file path. + + Input type: filepath + Output type: filepath + Demos: video_flip + """ + + def __init__( + self, + default="", + *, + type: Optional[str] = None, + source: str = "upload", + label: Optional[str] = None, + optional: bool = False, + ): + """ + Parameters: + default (str): IGNORED + type (str): Type of video format to be returned by component, such as 'avi' or 'mp4'. Use 'mp4' to ensure browser playability. If set to None, video will keep uploaded format. + source (str): Source of video. "upload" creates a box where user can drop an video file, "webcam" allows user to record a video from their webcam. + label (str): component name in interface. + optional (bool): If True, the interface can be submitted with no uploaded video, in which case the input value is None. + """ + self.type = type + self.source = source + super().__init__(label=label, optional=optional) + + @classmethod + def get_shortcut_implementations(cls): + return { + "video": {}, + "playable_video": {"type": "mp4"}, + } + + def get_template_context(self): + return { + "source": self.source, + "optional": self.optional, + **super().get_template_context(), + } + + def preprocess_example(self, x): + return {"name": x, "data": None, "is_example": True} + + def preprocess(self, x: Dict[str, str] | None) -> str | None: + """ + Parameters: + x (Dict[name: str, data: str]): JSON object with filename as 'name' property and base64 data as 'data' property + Returns: + (str): file path to video + """ + if x is None: + return x + file_name, file_data, is_example = ( + x["name"], + x["data"], + x.get("is_example", False), + ) + if is_example: + file = processing_utils.create_tmp_copy_of_file(file_name) + else: + file = processing_utils.decode_base64_to_file( + file_data, file_path=file_name + ) + file_name = file.name + uploaded_format = file_name.split(".")[-1].lower() + if self.type is not None and uploaded_format != self.type: + output_file_name = file_name[0: file_name.rindex(".") + 1] + self.type + ff = FFmpeg(inputs={file_name: None}, outputs={output_file_name: None}) + ff.run() + return output_file_name + else: + return file_name + + def serialize(self, x, called_directly): + raise NotImplementedError() + + def save_flagged(self, dir, label, data, encryption_key): + """ + Returns: (str) path to video file + """ + return self.save_flagged_file( + dir, label, None if data is None else data["data"], encryption_key + ) + # TODO: Might need to converge these two + # Output save_flagged: + # return self.save_flagged_file(dir, label, data["data"], encryption_key) + + def generate_sample(self): + return test_data.BASE64_VIDEO + + def postprocess(self, y): + """ + Parameters: + y (str): path to video + Returns: + (str): base64 url data + """ + returned_format = y.split(".")[-1].lower() + if self.type is not None and returned_format != self.type: + output_file_name = y[0: y.rindex(".") + 1] + self.type + ff = FFmpeg(inputs={y: None}, outputs={output_file_name: None}) + ff.run() + y = output_file_name + return { + "name": os.path.basename(y), + "data": processing_utils.encode_file_to_base64(y), + } + + def deserialize(self, x): + return processing_utils.decode_base64_to_file(x).name + + def restore_flagged(self, dir, data, encryption_key): + return self.restore_flagged_file(dir, data, encryption_key) diff --git a/gradio/inputs.py b/gradio/inputs.py index 248b6c68ec..cc532dba6d 100644 --- a/gradio/inputs.py +++ b/gradio/inputs.py @@ -278,6 +278,10 @@ class Image(Image): label (str): component name in interface. optional (bool): If True, the interface can be submitted with no uploaded image, in which case the input value is None. """ + warnings.warn( + "Usage of gradio.inputs is deprecated, and will not be supported in the future, please import your component from gradio.components", + DeprecationWarning, + ) super().__init__( shape=shape, image_mode=image_mode, @@ -290,6 +294,35 @@ class Image(Image): ) +class Video(Component): + """ + Component creates a video file upload that is converted to a file path. + + Input type: filepath + Demos: video_flip + """ + + def __init__( + self, + type: Optional[str] = None, + source: str = "upload", + label: Optional[str] = None, + optional: bool = False, + ): + """ + Parameters: + type (str): Type of video format to be returned by component, such as 'avi' or 'mp4'. If set to None, video will keep uploaded format. + source (str): Source of video. "upload" creates a box where user can drop an video file, "webcam" allows user to record a video from their webcam. + label (str): component name in interface. + optional (bool): If True, the interface can be submitted with no uploaded video, in which case the input value is None. + """ + warnings.warn( + "Usage of gradio.inputs is deprecated, and will not be supported in the future, please import your components from gradio.components", + DeprecationWarning, + ) + super().__init__(type=type, source=source, label=label, optional=optional) + + class InputComponent(Component): """ Input Component. All input components subclass this. @@ -373,92 +406,6 @@ class InputComponent(Component): } -class Video(InputComponent): - """ - Component creates a video file upload that is converted to a file path. - Input type: filepath - Demos: video_flip - """ - - def __init__( - self, - type: Optional[str] = None, - source: str = "upload", - label: Optional[str] = None, - optional: bool = False, - ): - """ - Parameters: - type (str): Type of video format to be returned by component, such as 'avi' or 'mp4'. If set to None, video will keep uploaded format. - source (str): Source of video. "upload" creates a box where user can drop an video file, "webcam" allows user to record a video from their webcam. - label (str): component name in interface. - optional (bool): If True, the interface can be submitted with no uploaded video, in which case the input value is None. - """ - self.type = type - self.source = source - super().__init__(label, optional=optional) - - @classmethod - def get_shortcut_implementations(cls): - return { - "video": {}, - } - - def get_template_context(self): - return { - "source": self.source, - "optional": self.optional, - **super().get_template_context(), - } - - def preprocess_example(self, x): - return {"name": x, "data": None, "is_example": True} - - def preprocess(self, x: Dict[str, str] | None) -> str | None: - """ - Parameters: - x (Dict[name: str, data: str]): JSON object with filename as 'name' property and base64 data as 'data' property - Returns: - (str): file path to video - """ - if x is None: - return x - file_name, file_data, is_example = ( - x["name"], - x["data"], - x.get("is_example", False), - ) - if is_example: - file = processing_utils.create_tmp_copy_of_file(file_name) - else: - file = processing_utils.decode_base64_to_file( - file_data, file_path=file_name - ) - file_name = file.name - uploaded_format = file_name.split(".")[-1].lower() - if self.type is not None and uploaded_format != self.type: - output_file_name = file_name[0 : file_name.rindex(".") + 1] + self.type - ff = FFmpeg(inputs={file_name: None}, outputs={output_file_name: None}) - ff.run() - return output_file_name - else: - return file_name - - def serialize(self, x, called_directly): - raise NotImplementedError() - - def save_flagged(self, dir, label, data, encryption_key): - """ - Returns: (str) path to video file - """ - return self.save_flagged_file( - dir, label, None if data is None else data["data"], encryption_key - ) - - def generate_sample(self): - return test_data.BASE64_VIDEO - - class Audio(InputComponent): """ Component accepts audio input files. diff --git a/gradio/outputs.py b/gradio/outputs.py index 480c69fb46..b0d906c8a2 100644 --- a/gradio/outputs.py +++ b/gradio/outputs.py @@ -21,7 +21,7 @@ import PIL from ffmpy import FFmpeg from gradio import processing_utils -from gradio.components import Component, Image, Textbox +from gradio.components import Component, Image, Textbox, Video if TYPE_CHECKING: # Only import for type checking (is False at runtime). from gradio import Interface @@ -65,6 +65,26 @@ class Image(Image): super().__init__(label=label, type=type, plot=plot) +class Video(Video): + """ + Used for video output. + Output type: filepath + Demos: video_flip + """ + + def __init__(self, type: Optional[str] = None, label: Optional[str] = None): + """ + Parameters: + type (str): Type of video format to be passed to component, such as 'avi' or 'mp4'. Use 'mp4' to ensure browser playability. If set to None, video will keep returned format. + label (str): component name in interface. + """ + warnings.warn( + "Usage of gradio.outputs is deprecated, and will not be supported in the future, please import your components from gradio.components", + DeprecationWarning, + ) + super().__init__(label=label, type=type) + + class OutputComponent(Component): """ Output Component. All output components subclass this. @@ -191,54 +211,6 @@ class Label(OutputComponent): return data -class Video(OutputComponent): - """ - Used for video output. - Output type: filepath - Demos: video_flip - """ - - def __init__(self, type: Optional[str] = None, label: Optional[str] = None): - """ - Parameters: - type (str): Type of video format to be passed to component, such as 'avi' or 'mp4'. Use 'mp4' to ensure browser playability. If set to None, video will keep returned format. - label (str): component name in interface. - """ - self.type = type - super().__init__(label) - - @classmethod - def get_shortcut_implementations(cls): - return {"video": {}, "playable_video": {"type": "mp4"}} - - def postprocess(self, y): - """ - Parameters: - y (str): path to video - Returns: - (str): base64 url data - """ - returned_format = y.split(".")[-1].lower() - if self.type is not None and returned_format != self.type: - output_file_name = y[0 : y.rindex(".") + 1] + self.type - ff = FFmpeg(inputs={y: None}, outputs={output_file_name: None}) - ff.run() - y = output_file_name - return { - "name": os.path.basename(y), - "data": processing_utils.encode_file_to_base64(y), - } - - def deserialize(self, x): - return processing_utils.decode_base64_to_file(x).name - - def save_flagged(self, dir, label, data, encryption_key): - return self.save_flagged_file(dir, label, data["data"], encryption_key) - - def restore_flagged(self, dir, data, encryption_key): - return self.restore_flagged_file(dir, data, encryption_key) - - class KeyValues(OutputComponent): """ Component displays a table representing values for multiple fields.