media-converter / functions.py
Lazarus
Refactor media output components and setup listeners
3673342
"""
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 """
# deband=[i.get("name") for i in data["deband"]]
# deflicker=[i.get("name") for i in data["deflicker"]]
# deshake=[i.get("name") for i in data["deshake"]]
# dejudder=[i.get("name") for i in data["dejudder"]]
# denoise=[i.get("name") for i in data["denoise"]]
# deinterlace=[i.get("name") for i in data["deinterlace"]]
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)
# for comp in self._component:
# state = gr.State()
# if comp.label is not None:
# state.value = comp.label
# comp.change(fn=self.changefunc, inputs=[state, comp], outputs=[])
# self.changefunc(state.value, comp.value)
# comp.change(fn=self.changefunc, inputs=[state, comp], outputs=[])
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:
# self.changefunc(comp, comp.value)
# print(
# comp.label,
# comp,
# )
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())
# self.commands = " ".join(filter(None, [self.vf, self.af, self.extra]))
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:
# print(comp, "comp")
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:
# print(i,hasattr(i,"children"))
# print(
# type(i),
# "type",
# # isinstance(i, gr.components.Component),
# # isinstance(i, gr.Blocks),
# hasattr(i, "children"),
# )
if not hasattr(i, "children"):
# res.append(gr.components.get_component_instance(i,render=True))
# if isinstance(i, gr.components.Component):
res += [gr.components.get_component_instance(i)]
else:
res += self._get_component_instance(i)
# print(res)
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.vf.append(arg)
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))
# print(lst_extra, "temp x")
# if vf:self.vf=f"-vf '{vf}'"
# if af:self.af=f"-af '{af}'"
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")
# self.formatOutputDict["vf"].pop(label, "Key is None or similar")
# self.formatOutputDict["af"].pop(label, "Key is None or similar")
# self.formatOutputDict.pop(label, "Key is None or similar")
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 clear(*input):
# print(input, " clear_func")
# # for i in [inp for i in input for inp in i]:
# # print(i, hasattr(i,"cleared_value"),type(i))
# # a=default_clear(input_components)
# def clear_func(x): return [component.cleared_value if hasattr(
# component, "cleared_value") else None for component in x]
# print(clear_func(input))
# return clear_func(input)
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=""),
]
# def updateOutput(file: _TemporaryFileWrapper) -> Component:
# if file:
# print(file.name)
# return gr.update(value=file.name)
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 += super()._get_component_instance(i)
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:
# print(i,hasattr(i,"children"))
if not hasattr(i, "children"):
# res.append(gr.components.get_component_instance(i,render=True))
res += [gr.components.get_component_instance(i)]
# print(i)
else:
res += self.__get_component_instance(i)
# res=[gr.components.get_component_instance(i, render=True) for i in inputs.children if not hasattr(i, "children")]
# print(res)
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)