| """ |
| util functions and classes |
| """ |
|
|
| import json |
| from tempfile import _TemporaryFileWrapper |
| from typing import List |
|
|
| import gradio as gr |
| from gradio.components import Component |
|
|
|
|
| def parse(param: str) -> dict: |
| with open(param, encoding="utf-8") as file: |
| return json.load(file) |
|
|
|
|
| data = parse("./data.json") |
| codecs = parse("./codecs.json") |
|
|
| """Video""" |
| containers = [j.get("name") for i in data["containers"] for j in data["containers"][i]] |
| video_containers = [i.get("name") for i in data["containers"]["video"]] |
| video_codecs = [i.get("value") for i in data["codecs"]["video"]] |
| video_aspect_ratio = [i.get("name") for i in data["aspects"]] |
| video_scaling = [i.get("name") for i in data["scalings"]] |
| """ Audio """ |
| audio_containers = [i.get("name") for i in data["containers"]["audio"]] |
| audio_codecs = [i.get("value") for i in data["codecs"]["audio"]] |
| audio_channels = [i.get("name") for i in data["audioChannels"]] |
| audio_quality = [i.get("name") for i in data["audioQualities"]] |
| audio_sample_rates = [i.get("name") for i in data["sampleRates"]] |
|
|
| """ Video & Audio Filters """ |
| |
| |
| |
| |
| |
| |
| video_filters = ["deband", "deflicker", "deshake", "dejudder", "denoise", "deinterlace"] |
| VF = [{vFilter: names} for vFilter in video_filters for names in [list(data[vFilter])]] |
|
|
| presets = [i.get("name") for i in data["presets"]] |
| profiles = [i.get("name") for i in data["profiles"]] |
| speeds = [i.get("name") for i in data["speeds"]] |
|
|
|
|
| outputMap = parse("./mappings.json") |
| newoutputMap = parse("./new_mappings.json") |
| """Output Mappings of commands to value |
| audioQuality -b:a 128k |
| """ |
|
|
|
|
| class CommandBuilder: |
| """Takes a collection of gradio layout elements and attaches |
| a function to each component in the context |
| to build an array of ffmpeg commands""" |
|
|
| def __init__(self, *inputs: gr.Row | gr.Column) -> None: |
| """ |
| Parameters: |
| *inputs: A tuple of layout blocks containing components(Textbox,Button...). |
| """ |
|
|
| self.output_dict = {"vf": {}, "af": {}} |
| self.formatoutputdict = {"vf": {}, "af": {}} |
|
|
| self._component: List[Component] = [] |
| self.vf, self.af, self.extra = ([] for _ in range(3)) |
| self.commands = "" |
| if inputs is None: |
| return None |
| for i in inputs: |
| self._component += self._get_component_instance(i) |
| |
| |
| |
| |
| |
| |
| |
|
|
| def __call__(self, *args, **kwds): |
| return [i.value for i in self._component] |
|
|
| def setup_listener(self, *inputs, **kwds): |
| """ |
| Sets up listeners for component updates in the current instance. |
| |
| """ |
| for comp in self._component: |
| if comp.label is not None: |
| |
| |
| |
| |
| |
| state = gr.State(value=comp.label) |
| comp.change(fn=self.changefunc, inputs=[state, comp], outputs=[]) |
|
|
| def reset(self): |
| self.output_dict = {"vf": {}, "af": {}} |
| self.commands = "" |
| self.vf, self.af, self.extra = ([] for _ in range(3)) |
|
|
| def changefunc(self, component_label: str | None, new_value=""): |
| label, *_ = ( |
| component_label.strip(": \n").lower().split() |
| if component_label |
| else "" |
| if not isinstance(component_label, list) |
| else "".join(component_label).strip(": ").lower().split() |
| ) |
| label += "".join(_).title() |
| key = newoutputMap.get(label, "") |
| lst_extra, vf, af = ([] for _ in range(3)) |
| if new_value not in [None, "Source", "Auto", "", "None", "none", 0]: |
| self.set_vf(label, new_value) |
| self.set_af(label, new_value) |
| self.set_f(label, new_value) |
| for val in self.output_dict: |
| if val == "vf": |
| vf = self.output_dict.get(val, {}).values() |
| vf = ",".join(list(vf)) |
| elif val == "af": |
| af = self.output_dict.get(val, {}).values() |
| af = ",".join(list(af)) |
| else: |
| lst_extra.extend([val, self.output_dict.get(val)]) |
|
|
| else: |
| self.output_dict.pop(key, "No Key Exists") |
| self.output_dict["vf"].pop(label, "No Key Exists") |
| self.output_dict["af"].pop(label, "No Key Exists") |
| self.vf = f"-vf '{vf}'" if vf else "" |
| self.af = f"-af '{af}'" if af else "" |
| self.extra = " ".join(lst_extra) |
| self.commands = " ".join(f"{self.vf} {self.af} {self.extra}".strip().split()) |
| |
|
|
| print(self.vf, self.af, self.extra) |
|
|
| def set_vf(self, label: str, new_value: "str| int"): |
| """Sets Video filters |
| |
| Args: |
| label : label of components |
| newValue : value of component |
| """ |
| if newoutputMap["vf"].get(label): |
| key = newoutputMap["vf"].get(label) |
| if label in ["deinterlace", "denoise"]: |
| value = "_".join(str(new_value).lower().split()) |
| arg = key.get(value, None) |
| self.output_dict["vf"].update({label: arg}) |
| else: |
| self.output_dict["vf"].update({key: key}) |
|
|
| def set_f(self, label: str, new_value: "str| int"): |
| """Sets Extra filters |
| Args: |
| label : label of components |
| newValue : value of component |
| """ |
| if newoutputMap.get(label): |
| key = newoutputMap.get(label, "") |
| if label in ["video", "audio"]: |
| codec_label = codecs.get(label) |
| value = ( |
| codec_label.get(new_value, new_value) if codec_label else new_value |
| ) |
| print(value) |
| self.output_dict.update({key: value}) |
| elif label in ["startTime", "stopTime"]: |
| self.output_dict.update({key: new_value}) |
| else: |
| value = "".join( |
| [ |
| i.get("value", "None") |
| for i in data.get(label, []) |
| if i.get("name", None) == new_value |
| ] |
| ) |
| self.output_dict.update({key: value}) |
|
|
| def set_af(self, label: str, new_value: "str|int"): |
| """Sets Audio filters |
| Args: |
| label : label of components |
| newValue : value of component |
| """ |
| if newoutputMap["af"].get(label): |
| value = int(new_value) / 100 |
| arg = f"{label}={value}" |
| self.output_dict["af"].update({label: arg}) |
|
|
| def update(self, component: Component): |
| for comp in self._component: |
| |
| comp.change( |
| lambda: gr.update(value=f"$ {self.commands}"), |
| [], |
| [component], |
| ) |
|
|
| def _get_component_instance(self, inputs: gr.Row | gr.Column) -> List[Component]: |
| """ |
| returns components present in a layout block |
| Parameters: |
| inputs: layout block |
| """ |
| res = [] |
| for i in inputs.children: |
| |
| |
| |
| |
| |
| |
| |
| |
| if not hasattr(i, "children"): |
| |
| |
| res += [gr.components.get_component_instance(i)] |
| else: |
| res += self._get_component_instance(i) |
| |
| return res |
|
|
| def set_video_filters(self, options): |
| value = self.output_dict.get(options, "-") |
| filters = newoutputMap.get(options, None) |
| arg = "" |
| if options in ["deinterlace", "denoise"]: |
| value = "_".join(value.lower().split()) |
| arg = filters.get(value, None) |
| |
| self.output_dict["vf"].update({options: arg}) |
| return True |
| if options in ["deband", "deflicker", "deshake", "dejudder"]: |
| arg = filters |
| self.output_dict["vf"].update({options: arg}) |
| return True |
|
|
| return False |
|
|
| def set_audio_filters(self, options): |
| value = self.output_dict.get(options, "-") |
| if options in ["acontrast"]: |
| value = int(value) / 100 |
| arg = f"{options}={value}" |
|
|
| self.output_dict["af"].update({options: arg}) |
| return True |
| return |
|
|
| def set_format(self, options): |
| value = self.output_dict.get(options, "-") |
| filters = newoutputMap.get(options, None) |
| if options in ["video", "audio"]: |
| value = "".join( |
| [ |
| i.get("value", "None") |
| for i in data.get("codecs").get(options) |
| if i.get("name", None) == value |
| ] |
| ) |
| arg = f"{filters} {value}" |
| self.output_dict.update({options: arg}) |
| return True |
| elif data.get(options) is None: |
| arg = f"{filters} {value}" |
| self.output_dict.update({options: arg}) |
| return True |
| elif options != "clip": |
| value = "".join( |
| [ |
| i.get("value", "None") |
| for i in data.get(options) |
| if i.get("name", None) == value |
| ] |
| ) |
| arg = f"{filters} {value}" |
| self.output_dict.update({options: arg}) |
|
|
| def build(self): |
| for i in self.output_dict: |
| if self.set_video_filters(i): |
| continue |
| elif self.set_audio_filters(i): |
| continue |
| else: |
| self.set_format(i) |
| lst_extra, vf, af = ([] for _ in range(3)) |
| for val in self.output_dict: |
| if val == "vf": |
| vf = self.output_dict.get(val).values() |
| vf = ",".join(list(vf)) |
| elif val == "af": |
| af = self.output_dict.get(val).values() |
| af = ",".join(list(af)) |
| else: |
| lst_extra.append(self.output_dict.get(val)) |
| |
| |
| |
| self.vf = f"-vf '{vf}'" if vf else "" |
| self.af = f"-af '{af}'" if af else "" |
| self.extra = " ".join(lst_extra) |
| self.commands = f"{self.vf} {self.af} {self.extra}" |
|
|
| def startfunc(self, component: gr.components.Component, new_value=""): |
| label, *_ = ( |
| component.label.strip(": ").lower().split() |
| if not isinstance(component.label, list) |
| else "".join(component.label).strip(": ").lower().split() |
| ) |
| label += "".join(_).title() |
| if new_value not in [None, "Source", "Auto", "", "None", 0]: |
| self.output_dict["vf"].update({label: new_value}) |
| self.output_dict["af"].update({label: new_value}) |
| self.output_dict.update({label: new_value}) |
| else: |
| self.output_dict.pop(label, "No Key Exists") |
| self.output_dict["vf"].pop(label, "No Key Exists") |
| self.output_dict["af"].pop(label, "No Key Exists") |
| |
| |
| |
| print(self.output_dict) |
| self.build() |
|
|
|
|
| def media_change(option: str, state) -> List[Component]: |
| """ |
| Allows playing the media in various options, |
| Video, Audio or File |
| |
| Args: |
| option : Clicked buttons value |
| |
| Returns: |
| List[Component]: list of toggled output components to display |
| """ |
| print(state, "state") |
| ops = {"Audio": gr.Audio(visible=True, value=state)} |
| ops2 = {"Video": gr.Video(visible=True, value=state)} |
| ops3 = {"File": gr.File(visible=True, value=state, interactive=False)} |
|
|
| def chosen(x: dict) -> Component: |
| return x.get(option, gr.update(visible=False)) |
|
|
| return [chosen(ops), chosen(ops2), chosen(ops3)] |
|
|
|
|
| """Helper Functions for Processing """ |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| def set_custom_bitrate(choice: int) -> Component: |
| """ |
| Toggle a component for custom Audio Quality |
| visible/none |
| Args: |
| choice : Custom audio quality |
| |
| Returns: |
| Component: component toggle state |
| """ |
| if choice == "Custom": |
| return gr.Number(visible=True) |
| return gr.Number(visible=False, value=0) |
|
|
|
|
| def supported_codecs(codec: str) -> List[Component]: |
| """ |
| Changes video and audio components with appropriate |
| options according to passed format |
| |
| Args: |
| format: passed media codec (x264,x265) |
| |
| Returns: |
| List[Component]: list of components with updated choices |
| """ |
| if codec: |
| codec = codec.lower() |
| video_lst = [ |
| val.get("value") |
| for val in data["codecs"]["video"] |
| if val.get("supported") is None or codec in val["supported"] |
| ] |
| audio_lst = [ |
| val.get("value") |
| for val in data["codecs"]["audio"] |
| if val.get("supported") is None or codec in val["supported"] |
| ] |
| return [gr.Dropdown(choices=video_lst), gr.Dropdown(choices=audio_lst)] |
|
|
|
|
| def supported_presets(preset: str) -> Component: |
| """ |
| Changes presets component with appropriate |
| options according to passed format |
| Args: |
| format: passed media codec (slow,fast,ultrafast) |
| |
| Returns: |
| Component: component with updated choice list (video codecs) |
| """ |
| if preset: |
| preset = preset.lower() |
| print(preset, "preset") |
| video_lst = [ |
| val.get("name") |
| for val in data["presets"] |
| if val.get("supported") is None or preset in val["supported"] |
| ] |
| return gr.Dropdown(choices=video_lst) |
|
|
|
|
| def change_clipbox(choice: str) -> List[Component]: |
| """ |
| Toggles the clipping Textbox |
| |
| Args: |
| choice: Enabled/None |
| |
| Returns: |
| List[Component]: list of components with visible state of the clip components |
| """ |
| print(choice, " now choice") |
| if choice == "Enabled": |
| return [ |
| gr.Textbox( |
| label="Start Time:", placeholder="00:00", visible=True, value="00:00" |
| ), |
| gr.Textbox( |
| label="Stop Time:", placeholder="00:00", visible=True, value="00:10" |
| ), |
| ] |
| else: |
| return [ |
| gr.Textbox(visible=False, value=""), |
| gr.Textbox(visible=False, value=""), |
| ] |
|
|
|
|
| |
| |
| |
| |
|
|
|
|
| class Clear(CommandBuilder): |
| """Class for clearing components in layouts""" |
|
|
| def __init__(self, *input_component: gr.Row | gr.Column) -> None: |
| """ |
| Parameters: |
| *input_component: A tuple of layout blocks containing components |
| """ |
| super().__init__(*input_component) |
| self._component = [] |
| if input_component is not None: |
| for i in input_component: |
| |
| self._component += self.__get_component_instance(i) |
|
|
| def __call__(self, *args, **kwds): |
| return self._component |
|
|
| def __str__(self): |
| return f"{self._component} __clear__ class" |
|
|
| def __get_component_instance(self, inputs: gr.Row | gr.Column) -> list: |
| res = [] |
| for i in inputs.children: |
| |
| if not hasattr(i, "children"): |
| |
| res += [gr.components.get_component_instance(i)] |
| |
| else: |
| res += self.__get_component_instance(i) |
| |
| |
| return res |
|
|
| def add(self, *args): |
| print(args, type(args)) |
| if args is not None: |
| for i in args: |
| self._component += self.__get_component_instance(i) |
| return self._component |
|
|
| def clear(self, *args): |
| """ |
| Function to clear components from a Block in the class instance |
| """ |
|
|
| def clear_func(x): |
| return [ |
| ( |
| component.cleared_value |
| if hasattr(component, "cleared_value") |
| else component.value |
| ) |
| for component in x |
| ] |
|
|
| return clear_func(self._component) |
|
|