Spaces:
Running
Running
import gradio as gr | |
import os | |
import asyncio | |
import shutil | |
from datetime import datetime | |
import urllib.request | |
import zipfile | |
import gdown | |
import requests | |
import logging | |
# Настройка логирования | |
logging.basicConfig(level=logging.INFO) | |
# Константы | |
f0_min = 50 | |
f0_max = 1100 | |
rvc_models_dir = './models' | |
input_folder = './input' | |
output_folder = './output' | |
# Создание необходимых директорий | |
os.makedirs(rvc_models_dir, exist_ok=True) | |
os.makedirs(input_folder, exist_ok=True) | |
os.makedirs(output_folder, exist_ok=True) | |
# Общие функции | |
async def run_command_async(command): | |
"""Асинхронный запуск команд.""" | |
process = await asyncio.create_subprocess_shell( | |
command, | |
stdout=asyncio.subprocess.PIPE, | |
stderr=asyncio.subprocess.PIPE | |
) | |
stdout, stderr = await process.communicate() | |
if process.returncode != 0: | |
logging.error(f"Error: {stderr.decode()}") | |
return stdout.decode() | |
def get_models_list(): | |
"""Получение списка моделей.""" | |
models = [] | |
if os.path.exists(rvc_models_dir): | |
models = [d for d in os.listdir(rvc_models_dir) if os.path.isdir(os.path.join(rvc_models_dir, d))] | |
return models | |
# Функции для первой вкладки | |
def process_uploaded_files(files): | |
"""Обработка загруженных файлов.""" | |
if os.path.exists(input_folder): | |
shutil.rmtree(input_folder) | |
os.makedirs(input_folder, exist_ok=True) | |
for file in files: | |
shutil.copy(file.name, input_folder) | |
return f"Uploaded {len(files)} files to {input_folder}" | |
async def convert_voice( | |
voicemodel_name, | |
pitch_vocal, | |
method_pitch, | |
hop_length, | |
index_rate, | |
filter_radius, | |
rms, | |
protect, | |
output_format | |
): | |
"""Конвертация голоса.""" | |
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") | |
output_ai_vocals_folder = os.path.join(output_folder, current_time) | |
os.makedirs(output_ai_vocals_folder, exist_ok=True) | |
output_test = { | |
"mp3": "-ab 320k", | |
"wav": "-c:a pcm_s32le", | |
"flac": "-c:a flac -af aformat=s16" | |
}[output_format] | |
for filename in os.listdir(input_folder): | |
if filename.endswith(("wav", "mp3", "flac")): | |
input_path = os.path.join(input_folder, filename) | |
base_name = os.path.splitext(filename)[0] | |
output_filename = f"mono_{base_name}_{current_time}_{method_pitch}_{pitch_vocal}.{output_format}" | |
output_path = os.path.join(output_ai_vocals_folder, output_filename) | |
# Выполнение команды RVC | |
await run_command_async( | |
f"python3 -m rvc.cli.rvc_cli -i \"{input_path}\" -m \"{voicemodel_name}\" -p {pitch_vocal} " | |
f"-ir {index_rate} -fr {filter_radius} -rms {rms} -f0 \"{method_pitch}\" " | |
f"-hop {hop_length} -pro {protect} -f0min {f0_min} -f0max {f0_max} -f \"wav\"" | |
) | |
# Конвертация в нужный формат | |
await run_command_async( | |
f"ffmpeg -y -i ./output/Voice_Converted.wav " | |
f"-vn -ar 44100 -ac 1 {output_test} \"{output_path}\"" | |
) | |
return [os.path.join(output_ai_vocals_folder, f) for f in os.listdir(output_ai_vocals_folder) if f.endswith(output_format)] | |
# Функции для второй вкладки | |
def extract_zip(extraction_folder, zip_name): | |
"""Распаковка архива.""" | |
os.makedirs(extraction_folder, exist_ok=True) | |
with zipfile.ZipFile(zip_name, 'r') as zip_ref: | |
zip_ref.extractall(extraction_folder) | |
os.remove(zip_name) | |
model_file = next((f for f in os.listdir(extraction_folder) if f.endswith('.pth')), None) | |
index_file = next((f for f in os.listdir(extraction_folder) if f.endswith('.index')), None) | |
if model_file: | |
os.rename(os.path.join(extraction_folder, model_file), | |
os.path.join(extraction_folder, model_file)) | |
if index_file: | |
os.rename(os.path.join(extraction_folder, index_file), | |
os.path.join(extraction_folder, index_file)) | |
async def download_model(url, dir_name): | |
"""Скачивание модели.""" | |
try: | |
zip_path = os.path.join(rvc_models_dir, f"{dir_name}.zip") | |
extraction_path = os.path.join(rvc_models_dir, dir_name) | |
if os.path.exists(extraction_path): | |
return f"Error: Directory {dir_name} already exists!" | |
if 'drive.google.com' in url: | |
file_id = url.split("file/d/")[1].split("/")[0] if "file/d/" in url else url.split("id=")[1].split("&")[0] | |
gdown.download(id=file_id, output=zip_path, quiet=False) | |
elif 'huggingface.co' in url: | |
urllib.request.urlretrieve(url, zip_path) | |
elif 'pixeldrain.com' in url: | |
file_id = url.split("pixeldrain.com/u/")[1] | |
response = requests.get(f"https://pixeldrain.com/api/file/{file_id}") | |
with open(zip_path, 'wb') as f: | |
f.write(response.content) | |
extract_zip(extraction_path, zip_path) | |
return f"Model {dir_name} successfully installed!" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
# Создание интерфейса | |
with gr.Blocks() as demo: | |
gr.Markdown("# VBach Lite WEBUI") | |
with gr.Tabs(): | |
# Первая вкладка | |
with gr.TabItem("Замена вокала"): | |
with gr.Row(): | |
with gr.Column(): | |
file_input = gr.File(file_count="multiple", label="Загрузить один или несколько файлов") | |
upload_status = gr.Textbox(label="Статус загрузки") | |
file_input.upload( | |
fn=process_uploaded_files, | |
inputs=file_input, | |
outputs=upload_status | |
) | |
with gr.Accordion("Настройки RVC:", open=True): | |
voicemodel_name = gr.Dropdown( | |
choices=get_models_list(), | |
label="Имя модели", | |
value="senko" if "senko" in get_models_list() else None | |
) | |
refresh_btn = gr.Button("Обновить") | |
pitch_vocal = gr.Slider(-48, 48, value=0, step=12, label="Высота тона") | |
method_pitch = gr.Dropdown( | |
["fcpe", "rmvpe+", "mangio-crepe"], | |
value="rmvpe+", | |
label="Метод извлечения тона" | |
) | |
hop_length = gr.Slider(0, 255, value=73, step=1, label="Длина шага для mangio-crepe") | |
index_rate = gr.Slider(0, 1, value=1, step=0.05, label="ИИ-акцент") | |
filter_radius = gr.Slider(0, 7, value=7, step=1, label="Радиус фильтра") | |
rms = gr.Slider(0, 1, value=0, step=0.1, label="Нормализация") | |
protect = gr.Slider(0, 0.5, value=0.35, step=0.05, label="Защита согласных") | |
output_format = gr.Dropdown( | |
["flac", "wav", "mp3"], | |
value="mp3", | |
label="Формат вывода" | |
) | |
convert_btn = gr.Button("Преобразовать!", variant="primary") | |
with gr.Column(): | |
output_files = gr.Files(label="Аудио с преобразованным вокалом") | |
# Обработчики | |
refresh_btn.click( | |
fn=lambda: gr.update(choices=get_models_list()), | |
outputs=voicemodel_name | |
) | |
convert_btn.click( | |
fn=convert_voice, | |
inputs=[ | |
voicemodel_name, | |
pitch_vocal, | |
method_pitch, | |
hop_length, | |
index_rate, | |
filter_radius, | |
rms, | |
protect, | |
output_format | |
], | |
outputs=output_files | |
) | |
# Вторая вкладка | |
with gr.TabItem("Скачать модель"): | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("## Здесь можно скачать модель по ссылке на архив с нею") | |
url_input = gr.Textbox( | |
label="Ссылка на архив с моделью", | |
placeholder="хаггингфейс.ко/модель.zip" | |
) | |
dir_name_input = gr.Textbox( | |
label="Имя модели", | |
placeholder="Имя модели" | |
) | |
download_btn = gr.Button("Скачать модель", variant="primary") | |
install_status = gr.Textbox(label="Статус скачивания модели") | |
download_btn.click( | |
fn=download_model, | |
inputs=[url_input, dir_name_input], | |
outputs=install_status | |
) | |
with gr.Column(): | |
gr.Markdown("## Проверить список моделей") | |
model_list = gr.Textbox( | |
value="\n".join(get_models_list()), | |
lines=10, | |
label="Установленные модели" | |
) | |
refresh_models_btn = gr.Button("Обновить") | |
refresh_models_btn.click( | |
fn=lambda: gr.update(value="\n".join(get_models_list())), | |
outputs=model_list | |
) | |
if __name__ == "__main__": | |
demo.launch(server_name="0.0.0.0", server_port=7860) |