noblebarkrr's picture
Убраны комментарии и отформатирован код
6cc8dc1 verified
import gradio as gr
import os, sys, subprocess
import pandas as pd
from datetime import datetime
import tempfile
import json
if not __package__:
from __init__ import Separator
from downloader import dw_yt_dlp
else:
from .. import Separator
from ..downloader import dw_yt_dlp
class Plugin(Separator):
def __init__(self):
self.name = "Авто-цепочка разделений"
self.requirements = []
self.install_requirements(self.requirements)
def install_requirements(self, requirements: list):
if requirements:
cmd = [os.sys.executable, "-m", "pip", "install"]
for pkg in requirements:
cmd.append(pkg)
result = subprocess.run(cmd, text=True, capture_output=True)
class ModelManager(Separator):
def __init__(self):
self.data = []
self.dir_presets = os.path.join(tempfile.tempdir, "presets")
os.makedirs(self.dir_presets, exist_ok=True)
def save(self, name):
if not name:
name = "chainless_preset"
filepath = os.path.join(
self.dir_presets,
f"{self.namer.short(self.namer.sanitize(name), length=50)}.json",
)
with open(filepath, "w") as f:
json.dump(self.data, f, indent=4, ensure_ascii=False)
return filepath
def load(self, filepath):
with open(filepath, "r") as f:
self.data = json.load(f)
def add(self, mt, mn, s_stem, out_stem, int_stem):
if int_stem:
self.data.append((mt, mn, s_stem, out_stem, int_stem))
def replace(self, mt, mn, s_stem, out_stem, int_stem, index=1):
if self.data:
len_data = len(self.data)
if index >= 1:
if index <= len_data:
self.data[index - 1] = (mt, mn, s_stem, out_stem, int_stem)
elif index == 0:
self.data[0] = (mt, mn, s_stem, out_stem, int_stem)
def remove(self, index=1):
if self.data:
len_data = len(self.data)
if index >= 1:
if index <= len_data:
del self.data[index - 1]
elif index == 0:
del self.data[0]
def clear(self):
self.data = []
def get_df(self):
if not self.data:
columns = [
"#",
"Имя модели",
"Выбранные стемы",
"Остаток",
"Промежуточный стем",
]
return pd.DataFrame(columns=columns)
data = []
for i, model in enumerate(self.data):
data.append(
[
f"{i+1}",
model[1],
str(model[2]),
str(model[3]),
model[4],
]
)
columns = [
"#",
"Имя модели",
"Выбранные стемы",
"Остаток",
"Промежуточный стем",
]
return pd.DataFrame(data, columns=columns)
def UI(self):
def get_output_stems(mt, mn, s_stem):
output_stems = []
stems = self.model_manager.get_stems(mt, mn)
if not s_stem:
for stem in stems:
output_stems.append(stem)
if set(stems) == {"bass", "drums", "vocals", "other"} or set(stems) == {
"bass",
"drums",
"vocals",
"other",
"guitar",
"piano",
}:
output_stems.append("instrumental +")
output_stems.append("instrumental -")
elif s_stem:
if len(stems) > 2:
for stem in stems:
if stem in s_stem:
output_stems.append(stem)
output_stems.append("inverted -")
if len(stems) - len(s_stem) > 1:
output_stems.append("inverted +")
return output_stems
def get_invert_output_stems(mt, mn, s_stem, out_stem):
output_stems = []
stems = get_output_stems(mt, mn, s_stem)
for stem in stems:
if stem not in out_stem:
output_stems.append(stem)
return output_stems
default_model = {
"mt": self.model_manager.get_mt(),
"mn": self.model_manager.get_mn(self.model_manager.get_mt()[0]),
"stems": self.model_manager.get_stems(
self.model_manager.get_mt()[0],
self.model_manager.get_mn(self.model_manager.get_mt()[0])[0],
),
"output_stems": get_output_stems(
self.model_manager.get_mt()[0],
self.model_manager.get_mn(self.model_manager.get_mt()[0])[0],
[],
),
"int_stems": get_invert_output_stems(
self.model_manager.get_mt()[0],
self.model_manager.get_mn(self.model_manager.get_mt()[0])[0],
[],
"vocals",
),
}
chain_manager = self.ModelManager()
gr.Markdown("<h3>Пресет</h3>")
with gr.Group():
with gr.Row(equal_height=True):
export_preset_name = gr.Textbox(
label="Имя пресета",
interactive=True,
value="chainless_preset",
scale=9,
)
export_btn = gr.DownloadButton(
"Экспорт", variant="secondary", scale=3, interactive=True
)
import_btn = gr.UploadButton(
"Импорт",
file_types=[".json"],
file_count="single",
scale=3,
interactive=True,
)
gr.Markdown("<h3>Цепочка разделений</h3>")
with gr.Row():
with gr.Column(scale=3):
model_type = gr.Dropdown(
label="Тип модели",
choices=default_model["mt"],
value=default_model["mt"][0],
interactive=True,
filterable=False,
)
model_name = gr.Dropdown(
label="Имя модели",
choices=default_model["mn"],
value=default_model["mn"][0],
interactive=True,
filterable=False,
)
selected_stems = gr.Dropdown(
label="Выбранные стемы",
choices=default_model["stems"],
value=[],
multiselect=True,
interactive=False,
filterable=False,
)
output_stems = gr.Dropdown(
label="Остаток",
choices=default_model["output_stems"],
value=[default_model["output_stems"][0]],
multiselect=True,
interactive=True,
filterable=False,
)
intermediate_stem = gr.Dropdown(
label="Промежуточный стем",
choices=default_model["int_stems"],
value=default_model["int_stems"][0],
interactive=True,
filterable=False,
)
@model_type.change(inputs=[model_type], outputs=[model_name])
def update_model_names(mt):
model_names = self.model_manager.get_mn(mt)
new_mn = model_names[0] if model_names else ""
return gr.update(choices=model_names, value=new_mn)
@model_name.change(
inputs=[model_type, model_name],
outputs=[selected_stems, output_stems, intermediate_stem],
)
def update_stems_after_model_change(mt, mn):
stems = self.model_manager.get_stems(mt, mn)
_output_stems = get_output_stems(mt, mn, [])
invert_stems = get_invert_output_stems(
mt, mn, [], [_output_stems[0]]
)
new_out_stem = [_output_stems[0]]
new_inv_out_stem = invert_stems[0]
return (
gr.update(
choices=stems,
value=[],
interactive=False if len(stems) <= 2 else True,
),
gr.update(
choices=_output_stems,
value=new_out_stem,
max_choices=len(_output_stems) - 1,
),
gr.update(choices=invert_stems, value=new_inv_out_stem),
)
@selected_stems.change(
inputs=[model_type, model_name, selected_stems],
outputs=[output_stems, intermediate_stem],
)
def update_invert_stems(mt, mn, s_stem):
stems = get_output_stems(mt, mn, s_stem)
new_i_stem = [stems[0]]
invert_stems = get_invert_output_stems(mt, mn, s_stem, new_i_stem)
return gr.update(choices=stems, value=new_i_stem), gr.update(
choices=invert_stems, value=invert_stems[0]
)
@output_stems.change(
inputs=[model_type, model_name, selected_stems, output_stems],
outputs=[intermediate_stem],
)
def update_invert2_stems(mt, mn, s_stem, out_stem):
invert_stems = get_invert_output_stems(mt, mn, s_stem, out_stem)
return gr.update(choices=invert_stems, value=invert_stems[0])
model_add_button = gr.Button("Добавить", interactive=True)
with gr.Column(scale=10):
df = gr.DataFrame(
value=chain_manager.get_df(),
headers=[
"#",
"Имя модели",
"Выбранные стемы",
"Остаток",
"Промежуточный стем",
],
datatype=["number", "str", "str", "str", "str"],
interactive=False,
)
with gr.Group():
with gr.Row(equal_height=True):
with gr.Column():
model_index = gr.Number(
label="Индекс модели", value=1, interactive=True
)
model_clear_btn = gr.Button(
"Очистить", variant="stop", interactive=True
)
with gr.Column():
model_replace_btn = gr.Button(
"Заменить", variant="primary", interactive=True
)
model_delete_btn = gr.Button(
"Удалить", variant="stop", interactive=True
)
@model_add_button.click(
inputs=[
model_type,
model_name,
selected_stems,
output_stems,
intermediate_stem,
],
outputs=df,
)
def add_model_to_auto_ensemble(mt, mn, s_stem, out_stem, int_stem):
chain_manager.add(mt, mn, s_stem, out_stem, int_stem)
return chain_manager.get_df()
@model_replace_btn.click(
inputs=[
model_type,
model_name,
selected_stems,
output_stems,
intermediate_stem,
model_index,
],
outputs=df,
)
def replace_model_to_auto_ensemble(
mt, mn, s_stem, out_stem, int_stem, index
):
chain_manager.replace(mt, mn, s_stem, out_stem, int_stem, index)
return chain_manager.get_df()
@model_delete_btn.click(inputs=[model_index], outputs=df)
def delete_model_to_auto_ensemble(index):
chain_manager.remove(index)
return chain_manager.get_df()
@model_clear_btn.click(outputs=df)
def clear_model_to_auto_ensemble():
chain_manager.clear()
return chain_manager.get_df()
gr.on(fn=chain_manager.get_df, outputs=df)
df.change(
fn=chain_manager.save, inputs=export_preset_name, outputs=export_btn
)
export_preset_name.change(
fn=chain_manager.save, inputs=export_preset_name, outputs=export_btn
)
@import_btn.upload(inputs=import_btn, outputs=df)
def load_ensemble_preset(filepath):
chain_manager.load(filepath)
return chain_manager.get_df()
with gr.Row():
with gr.Column():
gr.Markdown("<h3>Входное аудио</h3>")
with gr.Group():
with gr.Group(visible=False) as add_inputs:
input_path = gr.Textbox(
label="Путь к входному файлу", interactive=True
)
add_inputs_btn = gr.Button("Загрузить файл", variant="primary")
with gr.Group(visible=False) as add_inputs_from_url:
input_url = gr.Textbox(
label="URL входного файла", interactive=True
)
with gr.Row(equal_height=True):
inputs_url_format = gr.Dropdown(
label="Формат входного файла",
interactive=True,
choices=self.audio.output_formats,
value="mp3",
filterable=False,
)
inputs_url_bitrate = gr.Slider(
label="Битрейт входного файла",
minimum=64,
maximum=512,
step=32,
value=320,
interactive=True,
)
with gr.Row(equal_height=True):
inputs_url_cookie = gr.UploadButton(
label="Файл cookie (необязательно)",
interactive=True,
type="filepath",
file_count="single",
file_types=[".txt", ".cookies"],
variant="secondary",
)
add_inputs_url_btn = gr.Button(
"Загрузить файл", variant="primary"
)
with gr.Row(visible=True, equal_height=True) as add_buttons_row:
add_path_btn = gr.Button(
"Загрузить файл по пути", variant="secondary"
)
add_url_btn = gr.Button(
"Загрузить файл по URL", variant="secondary"
)
with gr.Group():
input_audio = gr.File(
label="Входное аудио",
interactive=True,
type="filepath",
file_count="single",
file_types=[f".{of}" for of in self.audio.input_formats],
)
with gr.Column():
gr.Markdown("<h3>Настройки</h3>")
with gr.Group():
save_only_last_intermediate_stem_check = gr.Checkbox(
label="Сохранить только последний промежуточный стем",
interactive=True,
value=False,
)
output_format = gr.Dropdown(
label="Формат выходного файла",
interactive=True,
choices=self.audio.output_formats,
value="mp3",
filterable=False,
)
run_btn = gr.Button(
"Создать цепочку разделений",
variant="primary",
interactive=True,
)
with gr.Row():
with gr.Column():
gr.Markdown("<h3>Результаты</h3>")
last_intermediate_stem = gr.Audio(
label="Последний промежуточный стем",
type="filepath",
interactive=False,
show_download_button=True,
)
with gr.Group():
invert_method = gr.Radio(
choices=["waveform", "spectrogram"],
label="Метод создания инверсии",
value="waveform",
)
invert_btn = gr.Button("Инвертировать")
output_inverted_audio = gr.Audio(
label="Инверсия",
type="filepath",
interactive=False,
show_download_button=True,
)
@invert_btn.click(
inputs=[
input_audio,
last_intermediate_stem,
invert_method,
output_format,
],
outputs=[output_inverted_audio],
)
def invert_result_ensemble(input_file, output_file, method, out_format):
if input_file and output_file:
o_dir = os.path.dirname(output_file)
basename = os.path.splitext(os.path.basename(input_file))[0]
output_path = os.path.join(
o_dir,
f"chainless_{self.namer.short(basename, length=50)}_{method}_invert.{out_format}",
)
inverted = self.inverter.process_audio(
audio1_path=input_file,
audio2_path=output_file,
out_format=out_format,
method=method,
output_path=output_path,
)
return inverted
else:
return None
with gr.Column():
gr.Markdown("<h3>Исходники цепочки</h3>")
output_source_files = gr.Files(
type="filepath", interactive=False, show_label=False
)
output_source_preview_check = gr.Checkbox(
label="Показать плееры для исходников цепочки",
interactive=True,
value=False,
)
@gr.render(inputs=[output_source_preview_check, output_source_files])
def show_output_auto_ensemble_players(preview, audios):
if preview:
if audios:
with gr.Group():
for file in audios:
gr.Audio(
label=os.path.splitext(os.path.basename(file))[
0
],
value=file,
interactive=False,
show_download_button=False,
type="filepath",
)
@run_btn.click(
inputs=[input_audio, output_format, save_only_last_intermediate_stem_check],
outputs=[last_intermediate_stem, output_source_files],
)
def chain(
input_audio,
out_format,
save_only_last_intermediate_stem,
progress=gr.Progress(track_tqdm=True),
):
input_settings = chain_manager.data
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
o = tempfile.mkdtemp(prefix=f"chainless_outputs_{timestamp}_")
os.makedirs(o, exist_ok=True)
base_name = os.path.splitext(os.path.basename(input_audio))[0]
last_intermediate_stem = None
last_intermediate_stem_name = None
_output_stems = []
block_count = len(input_settings)
for i, model in enumerate(input_settings, start=1):
input_model_type = model[0]
input_model_name = model[1]
selected_stems = model[2]
selected_output_stems = model[3]
intermediate_stem = model[4]
output_p = self.separate(
input=(
input_audio
if not last_intermediate_stem
else last_intermediate_stem
),
output_dir=os.path.join(o, input_model_name),
model_type=input_model_type,
model_name=input_model_name,
ext_inst=True,
output_format=out_format,
template=f"NAME_{i}_{f'({i - 1}_{str(last_intermediate_stem_name)})_' if last_intermediate_stem_name else ''}MODEL_STEM",
selected_stems=selected_stems,
add_settings={
"add_single_sep_text_progress": f"{i} из {block_count}"
},
progress=progress,
)
for stem, file in output_p:
if stem in selected_output_stems:
_output_stems.append(file)
elif stem == intermediate_stem:
last_intermediate_stem = file
last_intermediate_stem_name = stem
_output_stems.append(file)
return last_intermediate_stem, (
_output_stems if not save_only_last_intermediate_stem else []
)
@add_inputs_btn.click(
inputs=[input_path, input_audio],
outputs=[add_inputs, input_audio, add_buttons_row],
)
def add_inputs_fn(input_p, input_a):
if input_p and os.path.exists(input_p):
if input_a is None:
input_a = None
if self.audio.check(input_p):
input_a = input_p
return (
gr.update(visible=False),
gr.update(value=input_a),
gr.update(visible=True),
)
return (
gr.update(visible=False),
gr.update(value=input_a),
gr.update(visible=True),
)
@add_inputs_url_btn.click(
inputs=[
input_url,
input_audio,
inputs_url_format,
inputs_url_bitrate,
inputs_url_cookie,
],
outputs=[add_inputs_from_url, input_audio, add_buttons_row],
)
def add_inputs_from_url_fn(input_u, input_a, fmt, br, cookie):
if input_u:
if input_a is None:
input_a = None
downloaded_file = dw_yt_dlp(
url=input_u,
output_format=fmt,
output_bitrate=str(int(br)),
cookie=cookie,
)
if downloaded_file and os.path.exists(downloaded_file):
if self.audio.check(downloaded_file):
input_a = downloaded_file
return (
gr.update(visible=False),
gr.update(value=input_a),
gr.update(visible=True),
)
return (
gr.update(visible=False),
gr.update(value=input_a),
gr.update(visible=True),
)
add_path_btn.click(
lambda: (gr.update(visible=True), gr.update(visible=False)),
outputs=[add_inputs, add_buttons_row],
)
add_url_btn.click(
lambda: (gr.update(visible=True), gr.update(visible=False)),
outputs=[add_inputs_from_url, add_buttons_row],
)
inputs_url_format.change(
lambda x: gr.update(
visible=False if x in ["wav", "flac", "aiff"] else True
),
inputs=inputs_url_format,
outputs=inputs_url_bitrate,
)