1.前置数据集获取工具-优化输入框

2.优化微调训练完成信息
This commit is contained in:
aoi-umi 2026-08-09 17:17:04 +08:00
parent 345f638df7
commit b4f704a54b
4 changed files with 231 additions and 20 deletions

View File

@ -0,0 +1,5 @@
"""GPT-SoVITS 可复用的 Gradio WebUI 组件。"""
from .path_input import create_path_input
from .path_picker import create_path_picker
__all__ = ["create_path_input", "create_path_picker"]

View File

@ -0,0 +1,40 @@
"""路径输入组件:文本框 + 「选择文件/文件夹」按钮(按钮在文本框下方且宽度自适应文字),复用 path_picker 的弹窗逻辑。"""
import gradio as gr
from GPT_SoVITS.web_components.path_picker import create_path_picker
def create_path_input(
i18n=None,
label=None,
value="",
placeholder=None,
btn_label=None,
mode="both",
initial_dir=None,
root_dir=None,
):
"""创建「文本框 + 选择文件/文件夹按钮」组件:文本框在上,按钮在下且宽度自适应文字,选中后自动填入文本框。
Args:
i18n: 翻译函数 webui i18n用于翻译按钮和对话框文案不传则使用原文
label / value / placeholder: 文本框的标签默认值占位提示
btn_label: 按钮文案默认经 i18n 翻译选择文件/文件夹
mode: 允许选择的类型'both' 文件或文件夹'file' 仅文件'folder' 仅文件夹
initial_dir: 弹窗的初始目录不传则用当前文本框值所在目录
root_dir: 项目根目录选中路径在该目录内时填入相对路径不传则用 path_picker PROJECT_ROOT
Returns:
gr.Column: 布局容器组件子组件通过 .textbox / .button 访问
"""
if label is None:
label = "路径"
if i18n is not None:
label = i18n(label)
with gr.Column() as col:
textbox = gr.Textbox(label=label, value=value, placeholder=placeholder)
with gr.Row():
button = create_path_picker(textbox, i18n=i18n, btn_label=btn_label, mode=mode, initial_dir=initial_dir, root_dir=root_dir)
col.textbox = textbox
col.button = button
return col

View File

@ -0,0 +1,120 @@
"""路径选择组件:给文本框加一个「选择文件/文件夹」按钮,点击后弹出原生对话框选择文件或文件夹,选中后自动填入文本框。
注意原生对话框会显示在运行 webui 的那台机器的屏幕上本机运行时即用户屏幕
"""
import os
import tkinter
import gradio as gr
PROJECT_ROOT = os.path.abspath(os.path.join(__file__, "..", "..", ".."))
def create_path_picker(
textbox,
i18n=None,
btn_label=None,
dialog_title=None,
file_title=None,
dir_title=None,
mode="both",
initial_dir=None,
root_dir=None,
scale=0,
min_width=170,
):
"""给现有文本框加一个「选择文件/文件夹」按钮,选中后自动填入文本框。
Args:
textbox: 要填入路径的 gr.Textbox 组件
i18n: 翻译函数 webui i18n用于翻译按钮和对话框文案不传则使用原文
btn_label: 按钮文案默认经 i18n 翻译选择文件/文件夹
dialog_title / file_title / dir_title: 原生对话框文案默认经 i18n 翻译
mode: 允许选择的类型'both' 文件或文件夹'file' 仅文件'folder' 仅文件夹
initial_dir: 弹窗的初始目录不传则用当前文本框值所在目录
root_dir: 项目根目录选中路径在该目录内时填入相对路径不传则用 PROJECT_ROOT
scale / min_width: 按钮布局参数
Returns:
gr.Button: 创建的选择按钮
"""
if i18n is None:
i18n = lambda x: x
if mode not in ("both", "file", "folder"):
mode = "both"
if btn_label is None:
btn_label = i18n({"both": "选择文件/文件夹", "file": "选择文件", "folder": "选择文件夹"}[mode])
if dialog_title is None:
dialog_title = i18n("选择文件/文件夹")
if file_title is None:
file_title = i18n("选择文件")
if dir_title is None:
dir_title = i18n("选择文件夹")
def _pick(current_value):
"""弹出原生对话框选择文件或文件夹,返回所选路径;未选择/取消时返回原值。"""
try:
from tkinter import filedialog
except ImportError:
return current_value
# 初始目录:优先用组件指定的 initial_dir否则用当前文本框值所在目录
start_dir = initial_dir
if not start_dir and current_value:
start_dir = current_value if os.path.isdir(current_value) else os.path.dirname(current_value)
result = {}
def _pick_file():
result["path"] = filedialog.askopenfilename(title=file_title, initialdir=start_dir)
root.destroy()
def _pick_dir():
result["path"] = filedialog.askdirectory(title=dir_title, initialdir=start_dir)
root.destroy()
root = tkinter.Tk()
root.withdraw()
root.attributes("-topmost", True)
if mode == "file":
_pick_file()
elif mode == "folder":
_pick_dir()
else:
chooser = tkinter.Toplevel(root)
chooser.title(dialog_title)
chooser.attributes("-topmost", True)
chooser.resizable(False, False)
chooser.protocol("WM_DELETE_WINDOW", root.destroy)
tkinter.Button(chooser, text=file_title, width=22, command=_pick_file).pack(padx=24, pady=(14, 6))
tkinter.Button(chooser, text=dir_title, width=22, command=_pick_dir).pack(padx=24, pady=(0, 14))
chooser.update_idletasks()
w, h = chooser.winfo_width(), chooser.winfo_height()
x = (chooser.winfo_screenwidth() - w) // 2
y = (chooser.winfo_screenheight() - h) // 3
chooser.geometry("+%d+%d" % (x, y))
root.mainloop()
try:
root.destroy()
except tkinter.TclError:
pass
# 取消时 askopenfilename/askdirectory 返回空串,保留文本框原值不清空
selected = result.get("path")
if not selected:
return current_value
# 选中路径在项目根目录内则填相对路径,否则保留绝对路径
base = os.path.normpath(root_dir or PROJECT_ROOT)
try:
if os.path.commonpath([selected, base]) == base:
selected = os.path.relpath(selected, base)
except ValueError:
pass # 不同盘符等无法计算相对路径,保留绝对路径
return selected
button = gr.Button(value=btn_label, scale=scale, min_width=min_width)
button.click(_pick, [textbox], [textbox], show_progress=False)
return button

View File

@ -62,6 +62,7 @@ from subprocess import Popen
from tools.assets import css, js, top_html
from tools.i18n.i18n import I18nAuto, scan_language_list
from GPT_SoVITS.web_components import create_path_input
language = sys.argv[-1] if sys.argv[-1] in scan_language_list() else "Auto"
os.environ["language"] = language
@ -367,6 +368,15 @@ from tools.asr.config import asr_dict
process_name_asr = i18n("语音识别")
default_asr_opt_dir = "output/asr_opt"
def sync_asr_opt_dir(asr_inp_dir_value):
"""ASR 输入文件夹变化时,自动把输出文件夹设为输入目录的父目录下的 asr_opt。"""
if not asr_inp_dir_value:
return {"__type__": "update"}
return {"__type__": "update", "value": os.path.join(os.path.dirname(my_utils.clean_path(asr_inp_dir_value)), "asr_opt")}
def open_asr(asr_inp_dir, asr_opt_dir, asr_model, asr_model_size, asr_lang, asr_precision):
global p_asr
@ -381,7 +391,7 @@ def open_asr(asr_inp_dir, asr_opt_dir, asr_model, asr_model_size, asr_lang, asr_
cmd += f" -l {asr_lang}"
cmd += f" -p {asr_precision}"
output_file_name = os.path.basename(asr_inp_dir)
output_folder = asr_opt_dir or "output/asr_opt"
output_folder = asr_opt_dir or default_asr_opt_dir
output_file_path = os.path.abspath(f"{output_folder}/{output_file_name}.list")
yield (
process_info(process_name_asr, "opened"),
@ -552,10 +562,12 @@ def open1Ba(
print(cmd)
p_train_SoVITS = Popen(cmd, shell=True)
p_train_SoVITS.wait()
train_returncode = p_train_SoVITS.returncode
p_train_SoVITS = None
SoVITS_dropdown_update, GPT_dropdown_update = change_choices()
result = "finish" if train_returncode == 0 else "failed"
yield (
process_info(process_name_sovits, "finish"),
process_info(process_name_sovits, result),
{"__type__": "update", "visible": True},
{"__type__": "update", "visible": False},
SoVITS_dropdown_update,
@ -644,10 +656,12 @@ def open1Bb(
print(cmd)
p_train_GPT = Popen(cmd, shell=True)
p_train_GPT.wait()
train_returncode = p_train_GPT.returncode
p_train_GPT = None
SoVITS_dropdown_update, GPT_dropdown_update = change_choices()
result = "finish" if train_returncode == 0 else "failed"
yield (
process_info(process_name_gpt, "finish"),
process_info(process_name_gpt, result),
{"__type__": "update", "visible": True},
{"__type__": "update", "visible": False},
SoVITS_dropdown_update,
@ -679,6 +693,22 @@ ps_slice = []
process_name_slice = i18n("语音切分")
def sync_slice_opt_dir(slice_inp_dir_value):
"""切分输入路径变化时,自动把输出根目录设为 output/父文件夹/文件名(去后缀) 或 output/文件夹名。"""
if not slice_inp_dir_value:
return {"__type__": "update"}
cleaned_path = my_utils.clean_path(slice_inp_dir_value)
if os.path.isdir(cleaned_path):
folder_name = os.path.basename(cleaned_path.rstrip("\\/"))
elif os.path.isfile(cleaned_path):
folder_name = os.path.splitext(os.path.basename(cleaned_path))[0]
else:
return {"__type__": "update"}
return {"__type__": "update", "value": "/".join(["output", folder_name, "slicer_opt"])}
def open_slice(inp, opt_root, threshold, min_length, min_interval, hop_size, max_sil_kept, _max, alpha, n_parts):
global ps_slice
inp = my_utils.clean_path(inp)
@ -1329,9 +1359,16 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
with gr.Row():
with gr.Column(scale=3):
with gr.Row():
slice_inp_path = gr.Textbox(label=i18n("音频自动切分输入路径,可文件可文件夹"), value="")
slice_opt_root = gr.Textbox(
label=i18n("切分后的子音频的输出根目录"), value="output/slicer_opt"
slice_inp_picker = create_path_input(
i18n=i18n,
label=i18n("音频自动切分输入路径,可文件可文件夹"),
value="",
)
slice_opt_root = create_path_input(
i18n=i18n,
label=i18n("切分后的子音频的输出根目录"),
value="output/slicer_opt",
mode="folder",
)
with gr.Row():
threshold = gr.Textbox(
@ -1400,11 +1437,17 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
with gr.Row():
with gr.Column(scale=3):
with gr.Row():
asr_inp_dir = gr.Textbox(
label=i18n("输入文件夹路径"), value="D:\\GPT-SoVITS\\raw\\xxx", interactive=True
asr_inp_dir = create_path_input(
i18n=i18n,
label=i18n("输入文件夹路径"),
value="D:\\GPT-SoVITS\\raw\\xxx",
mode="folder",
)
asr_opt_dir = gr.Textbox(
label=i18n("输出文件夹路径"), value="output/asr_opt", interactive=True
asr_opt_dir = create_path_input(
i18n=i18n,
label=i18n("输出文件夹路径"),
value=default_asr_opt_dir,
mode="folder",
)
with gr.Row():
asr_model = gr.Dropdown(
@ -1457,10 +1500,11 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
with gr.Row():
with gr.Column(scale=3):
with gr.Row():
path_list = gr.Textbox(
path_list = create_path_input(
i18n=i18n,
label=i18n("标注文件路径 (含文件后缀 *.list)"),
value="D:\\RVC1006\\GPT-SoVITS\\raw\\xxx.list",
interactive=True,
mode="file",
)
label_info = gr.Textbox(label=process_info(process_name_subfix, "info"))
open_label = gr.Button(
@ -1470,8 +1514,8 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
value=process_info(process_name_subfix, "close"), variant="primary", visible=False
)
open_label.click(change_label, [path_list], [label_info, open_label, close_label])
close_label.click(change_label, [path_list], [label_info, open_label, close_label])
open_label.click(change_label, [path_list.textbox], [label_info, open_label, close_label])
close_label.click(change_label, [path_list.textbox], [label_info, open_label, close_label])
open_uvr5.click(change_uvr5, [], [uvr5_info, open_uvr5, close_uvr5])
close_uvr5.click(change_uvr5, [], [uvr5_info, open_uvr5, close_uvr5])
@ -1637,15 +1681,17 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
pretrained_s2G.change(sync, [pretrained_s2G], [pretrained_s2G_])
open_asr_button.click(
open_asr,
[asr_inp_dir, asr_opt_dir, asr_model, asr_size, asr_lang, asr_precision],
[asr_info, open_asr_button, close_asr_button, path_list, inp_text, inp_wav_dir],
[asr_inp_dir.textbox, asr_opt_dir.textbox, asr_model, asr_size, asr_lang, asr_precision],
[asr_info, open_asr_button, close_asr_button, path_list.textbox, inp_text, inp_wav_dir],
)
close_asr_button.click(close_asr, [], [asr_info, open_asr_button, close_asr_button])
asr_inp_dir.textbox.change(sync_asr_opt_dir, [asr_inp_dir.textbox], [asr_opt_dir.textbox])
slice_inp_picker.textbox.change(sync_slice_opt_dir, [slice_inp_picker.textbox], [slice_opt_root.textbox])
open_slicer_button.click(
open_slice,
[
slice_inp_path,
slice_opt_root,
slice_inp_picker.textbox,
slice_opt_root.textbox,
threshold,
min_length,
min_interval,
@ -1655,13 +1701,13 @@ with gr.Blocks(title="GPT-SoVITS WebUI", analytics_enabled=False, js=js, css=css
alpha,
n_process,
],
[slicer_info, open_slicer_button, close_slicer_button, asr_inp_dir, denoise_input_dir, inp_wav_dir],
[slicer_info, open_slicer_button, close_slicer_button, asr_inp_dir.textbox, denoise_input_dir, inp_wav_dir],
)
close_slicer_button.click(close_slice, [], [slicer_info, open_slicer_button, close_slicer_button])
open_denoise_button.click(
open_denoise,
[denoise_input_dir, denoise_output_dir],
[denoise_info, open_denoise_button, close_denoise_button, asr_inp_dir, inp_wav_dir],
[denoise_info, open_denoise_button, close_denoise_button, asr_inp_dir.textbox, inp_wav_dir],
)
close_denoise_button.click(close_denoise, [], [denoise_info, open_denoise_button, close_denoise_button])