NeoPy's picture
Update app.py
d5a6e23 verified
import gradio as gr
import requests
import random
import os
import zipfile
import librosa
import time
from infer_rvc_python import BaseLoader
from pydub import AudioSegment
from tts_voice import tts_order_voice
import edge_tts
import tempfile
from audio_separator.separator import Separator
import model_handler
import logging
import aiohttp
import asyncio
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
# Constants
TEMP_DIR = "temp"
MODEL_PREFIX = "model"
UVR_5_MODELS = [
{"model_name": "BS-Roformer-Viperx-1297", "checkpoint": "model_bs_roformer_ep_317_sdr_12.9755.ckpt"},
{"model_name": "MDX23C-InstVoc HQ 2", "checkpoint": "MDX23C-8KFFT-InstVoc_HQ_2.ckpt"},
{"model_name": "Kim Vocal 2", "checkpoint": "Kim_Vocal_2.onnx"},
{"model_name": "5_HP-Karaoke", "checkpoint": "5_HP-Karaoke-UVR.pth"},
{"model_name": "UVR-DeNoise by FoxJoy", "checkpoint": "UVR-DeNoise.pth"},
{"model_name": "UVR-DeEcho-DeReverb by FoxJoy", "checkpoint": "UVR-DeEcho-DeReverb.pth"},
]
MODELS = [
{"model": "model.pth", "index": "model.index", "model_name": "Test Model"},
]
BAD_WORDS = ['puttana', 'whore', 'badword3', 'badword4']
MAX_FILE_SIZE = 500_000_000 # 500 MB
os.makedirs(TEMP_DIR, exist_ok=True)
try:
import spaces
spaces_status = True
except ImportError:
spaces_status = False
logger.warning("Spaces module not found; running in CPU mode")
separator = Separator()
converter = BaseLoader(only_cpu=not spaces_status, hubert_path=None, rmvpe_path=None)
class BadWordError(Exception):
pass
async def text_to_speech_edge(text, language_code):
if not text.strip():
raise ValueError("Text input cannot be empty")
voice = tts_order_voice.get(language_code, tts_order_voice[list(tts_order_voice.keys())[0]])
communicate = edge_tts.Communicate(text, voice)
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
tmp_path = tmp_file.name
await communicate.save(tmp_path)
return tmp_path
async def download_from_url(url, name, progress=gr.Progress()):
if not url.startswith("https://huggingface.co"):
raise ValueError("URL must be from Hugging Face")
if not name.strip():
raise ValueError("Model name cannot be empty")
if any(bad_word in url.lower() or bad_word in name.lower() for bad_word in BAD_WORDS):
raise BadWordError("Input contains restricted words")
filename = os.path.join(TEMP_DIR, f"{MODEL_PREFIX}{random.randint(1, 1000)}.zip")
async with aiohttp.ClientSession() as session:
async with session.get(url.replace("/blob/", "/resolve/")) as response:
if response.status != 200:
raise ValueError("Failed to download file")
total = int(response.headers.get('content-length', 0))
if total > MAX_FILE_SIZE:
raise ValueError(f"File size exceeds {MAX_FILE_SIZE / 1_000_000} MB limit")
current = 0
with open(filename, "wb") as f:
async for data in response.content.iter_chunked(4096):
f.write(data)
current += len(data)
progress(current / total, desc="Downloading model")
try:
with zipfile.ZipFile(filename, 'r') as zip_ref:
zip_ref.extractall(os.path.join(TEMP_DIR, os.path.basename(filename).split(".")[0]))
except Exception as e:
logger.error(f"Failed to unzip file: {e}")
raise ValueError("Failed to unzip file")
unzipped_dir = os.path.join(TEMP_DIR, os.path.basename(filename).split(".")[0])
pth_files = [os.path.join(root, file) for root, _, files in os.walk(unzipped_dir) for file in files if file.endswith(".pth")]
index_files = [os.path.join(root, file) for root, _, files in os.walk(unzipped_dir) for file in files if file.endswith(".index")]
if not pth_files or not index_files:
raise ValueError("No .pth or .index files found in the zip")
pth_file = pth_files[0]
index_file = index_files[0]
name = name or os.path.basename(pth_file).split(".")[0]
MODELS.append({"model": pth_file, "index": index_file, "model_name": name})
return [f"Downloaded as {name}", pth_file, index_file]
def inf_handler(audio, model_name):
model_found = False
for model_info in UVR_5_MODELS:
if model_info["model_name"] == model_name:
separator.load_model(model_info["checkpoint"])
model_found = True
break
if not model_found:
separator.load_model()
output_files = separator.separate(audio)
return output_files[0], output_files[1]
def run(model, audio_files, pitch_alg, pitch_lvl, index_inf, r_m_f, e_r, c_b_p):
if not audio_files:
raise ValueError("Please upload an audio file")
if isinstance(audio_files, str):
audio_files = [audio_files]
random_tag = f"USER_{random.randint(10000000, 99999999)}"
file_m = model
file_index = None
for m in MODELS:
if m["model_name"] == file_m:
file_m = m["model"]
file_index = m["index"]
break
if not file_m.endswith(".pth"):
raise ValueError("Model file must be a .pth file")
logger.info(f"Running inference with model: {file_m}, tag: {random_tag}")
converter.apply_conf(
tag=random_tag,
file_model=file_m,
pitch_algo=pitch_alg,
pitch_lvl=pitch_lvl,
file_index=file_index,
index_influence=index_inf,
respiration_median_filtering=r_m_f,
envelope_ratio=e_r,
consonant_breath_protection=c_b_p,
resample_sr=44100 if audio_files[0].endswith('.mp3') else 0,
)
time.sleep(0.1)
result = convert_now(audio_files, random_tag, converter)
return result[0]
def convert_now(audio_files, random_tag, converter):
return converter(
audio_files,
random_tag,
overwrite=False,
parallel_workers=8
)
def upload_model(index_file, pth_file, model_name):
if not index_file or not pth_file:
raise ValueError("Both index and model files are required")
if not model_name.strip():
raise ValueError("Model name cannot be empty")
MODELS.append({"model": pth_file.name, "index": index_file.name, "model_name": model_name})
return "Model uploaded successfully!"
def json_to_markdown_table(json_data):
table = "| Key | Value |\n| --- | --- |\n"
for key, value in json_data.items():
table += f"| {key} | {value} |\n"
return table
def model_info(name):
for model in MODELS:
if model["model_name"] == name:
info = model_handler.model_info(model["model"])
info2 = {
"Model Name": model["model_name"],
"Model Config": info['config'],
"Epochs Trained": info['epochs'],
"Sample Rate": info['sr'],
"Pitch Guidance": info['f0'],
"Model Precision": info['size'],
}
return json_to_markdown_table(info2)
return "Model not found"
with gr.Blocks(theme=gr.themes.Soft(primary_hue="pink", secondary_hue="rose"), title="Ilaria RVC 💖") as app:
gr.Markdown("# Ilaria RVC 💖")
gr.Markdown("Support the project by donating on [Ko-Fi](https://ko-fi.com/ilariaowo)")
with gr.Tab("Inference"):
with gr.Row(equal_height=True):
models_dropdown = gr.Dropdown(label="Select Model", choices=[m["model_name"] for m in MODELS], value=MODELS[0]["model_name"])
refresh_button = gr.Button("Refresh Models", variant="secondary")
refresh_button.click(lambda: gr.Dropdown(choices=[m["model_name"] for m in MODELS]), outputs=models_dropdown)
sound_gui = gr.Audio(label="Input Audio", type="filepath")
with gr.Accordion("Text-to-Speech", open=False):
text_tts = gr.Textbox(label="Text Input", placeholder="Enter text to convert to speech", lines=3)
dropdown_tts = gr.Dropdown(label="Language and Voice", choices=list(tts_order_voice.keys()), value=list(tts_order_voice.keys())[0])
button_tts = gr.Button("Generate Speech", variant="primary")
button_tts.click(text_to_speech_edge, inputs=[text_tts, dropdown_tts], outputs=sound_gui)
with gr.Accordion("Conversion Settings", open=False):
pitch_algo_conf = gr.Radio(choices=["pm", "harvest", "crepe", "rmvpe", "rmvpe+"], value="rmvpe", label="Pitch Algorithm", info="Select the algorithm for pitch detection")
with gr.Row(equal_height=True):
pitch_lvl_conf = gr.Slider(label="Pitch Level", minimum=-24, maximum=24, step=1, value=0, info="Adjust pitch: negative for male, positive for female")
index_inf_conf = gr.Slider(minimum=0, maximum=1, value=0.75, label="Index Influence", info="Controls how much accent is applied")
with gr.Row(equal_height=True):
respiration_filter_conf = gr.Slider(minimum=0, maximum=7, value=3, step=1, label="Respiration Median Filtering")
envelope_ratio_conf = gr.Slider(minimum=0, maximum=1, value=0.25, label="Envelope Ratio")
consonant_protec_conf = gr.Slider(minimum=0, maximum=0.5, value=0.5, label="Consonant Breath Protection")
with gr.Row(equal_height=True):
button_conf = gr.Button("Convert Audio", variant="primary")
output_conf = gr.Audio(type="filepath", label="Converted Audio")
button_conf.click(run, inputs=[models_dropdown, sound_gui, pitch_algo_conf, pitch_lvl_conf, index_inf_conf, respiration_filter_conf, envelope_ratio_conf, consonant_protec_conf], outputs=output_conf)
with gr.Tab("Model Loader"):
with gr.Accordion("Download Model", open=False):
gr.Markdown("Download a model from Hugging Face (RVC model, max 500 MB)")
model_url = gr.Textbox(label="Hugging Face Model URL", placeholder="https://huggingface.co/username/model")
model_name = gr.Textbox(label="Model Name", placeholder="Enter a unique model name")
download_button = gr.Button("Download Model", variant="primary")
status = gr.Textbox(label="Status", interactive=False)
model_pth = gr.Textbox(label="Model .pth File", interactive=False)
index_pth = gr.Textbox(label="Index .index File", interactive=False)
download_button.click(download_from_url, [model_url, model_name], [status, model_pth, index_pth])
with gr.Accordion("Upload Model", open=False):
index_file_upload = gr.File(label="Index File (.index)")
pth_file_upload = gr.File(label="Model File (.pth)")
model_name_upload = gr.Textbox(label="Model Name", placeholder="Enter a unique model name")
upload_button = gr.Button("Upload Model", variant="primary")
upload_status = gr.Textbox(label="Status", interactive=False)
upload_button.click(upload_model, [index_file_upload, pth_file_upload, model_name_upload], upload_status)
with gr.Tab("Vocal Separator"):
gr.Markdown("Separate vocals and instruments using UVR models (CPU only)")
uvr5_audio_file = gr.Audio(label="Input Audio", type="filepath")
with gr.Row(equal_height=True):
uvr5_model = gr.Dropdown(label="UVR Model", choices=[m["model_name"] for m in UVR_5_MODELS])
uvr5_button = gr.Button("Separate", variant="primary")
uvr5_output_voc = gr.Audio(label="Vocals", type="filepath")
uvr5_output_inst = gr.Audio(label="Instrumental", type="filepath")
uvr5_button.click(inf_handler, [uvr5_audio_file, uvr5_model], [uvr5_output_voc, uvr5_output_inst])
app.queue().launch(share=True)