import os import sys import gradio as gr import urllib.parse from audio_separator.separator import Separator from argparse import ArgumentParser from assets.presence.discord_presence import RPCManager, track_presence from variable import * from code import * now_dir = os.getcwd() sys.path.append(now_dir) device = "cuda" if torch.cuda.is_available() else "cpu" use_autocast = device == "cuda" if os.path.isdir("env"): if platform.system() == "Windows": python_location = ".\\env\\python.exe" separator_location = ".\\env\\Scripts\\audio-separator.exe" elif platform.system() == "Linux": python_location = "env/bin/python" separator_location = "env/bin/audio-separator" else: python_location = None separator_location = "audio-separator" def load_config_presence(): with open(config_file, "r", encoding="utf8") as file: config = json.load(file) return config["discord_presence"] def initialize_presence(): if load_config_presence(): RPCManager.start_presence() initialize_presence() with gr.Blocks(title="🎵 UVR5 UI 🎵") as app: gr.Markdown("

🎵 UVR5 UI 🎵

") gr.Markdown("If you liked this HF Space you can give us a ❤️") with gr.Tabs(): with gr.TabItem("BS/Mel Roformer"): with gr.Row(): roformer_model = gr.Dropdown( label="Select the model", choices=list(roformer_models.keys()), value=initial_settings.get("Roformer", {}).get("model", None), interactive=True ) roformer_output_format = gr.Dropdown( label="Select the output format", choices=output_format, value=initial_settings.get("Roformer", {}).get("output_format", None), interactive=True ) with gr.Row(): roformer_audio = gr.Audio( label="Input audio", type="filepath", interactive=True ) with gr.Row(): roformer_button = gr.Button("Separate!", variant="primary") with gr.Row(): roformer_stem1 = gr.Audio( show_download_button=True, interactive=False, label="Stem 1", type="filepath" ) roformer_stem2 = gr.Audio( show_download_button=True, interactive=False, label="Stem 2", type="filepath" ) with gr.Accordion("Separation by link", open=False): with gr.Row(): roformer_link = gr.Textbox( label="Link", placeholder="Paste the link here", interactive=True ) with gr.Row(): gr.Markdown("You can paste the link to the video/audio from many sites, check the complete list [here](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)") with gr.Row(): roformer_download_button = gr.Button("Download!", variant="primary") roformer_download_button.click(download_audio, [roformer_link], [roformer_audio]) with gr.Accordion("Batch separation", open=False): with gr.Row(): roformer_input_path = gr.Textbox( label="Input path", placeholder="Place the input path here", interactive=True ) roformer_output_path = gr.Textbox( label="Output path", placeholder="Place the output path here", interactive=True ) with gr.Row(): roformer_bath_button = gr.Button("Separate!", variant="primary") with gr.Row(): roformer_info = gr.Textbox( label="Output information", interactive=False ) with gr.TabItem("Roformer Processing"): with gr.Row(): roformer_segment_size = gr.Slider( label="Segment size", info="Larger consumes more resources, but may give better results", minimum=32, maximum=4000, step=32, value=initial_settings.get("Roformer", {}).get("segment_size", 256), interactive=True ) roformer_override_segment_size = gr.Checkbox( label="Override segment size", info="Override model default segment size instead of using the model default value", value=initial_settings.get("Roformer", {}).get("override_segment_size", False), interactive=True ) with gr.Row(): roformer_overlap = gr.Slider( label="Overlap", info="Amount of overlap between prediction windows", minimum=2, maximum=10, step=1, value=initial_settings.get("Roformer", {}).get("overlap", 8), interactive=True ) roformer_batch_size = gr.Slider( label="Batch size", info="Larger consumes more RAM but may process slightly faster", minimum=1, maximum=16, step=1, value=initial_settings.get("Roformer", {}).get("batch_size", 1), interactive=True ) with gr.Row(): roformer_normalization_threshold = gr.Slider( label="Normalization threshold", info="The threshold for audio normalization", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("Roformer", {}).get("normalization_threshold", 0.9), interactive=True ) roformer_amplification_threshold = gr.Slider( label="Amplification threshold", info="The threshold for audio amplification", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("Roformer", {}).get("amplification_threshold", 0.7), interactive=True ) with gr.Row(): roformer_single_stem = gr.Textbox( label="Output only single stem", placeholder="Write the stem you want, check the stems of each model on Leaderboard. e.g. Instrumental", value=initial_settings.get("Roformer", {}).get("single_stem", ""), interactive=True ) roformer_bath_button.click( roformer_batch, [roformer_input_path, roformer_output_path, roformer_model, roformer_output_format, roformer_segment_size, roformer_override_segment_size, roformer_overlap, roformer_batch_size, roformer_normalization_threshold, roformer_amplification_threshold, roformer_single_stem], [roformer_info] ) roformer_button.click( roformer_separator, [roformer_audio, roformer_model, roformer_output_format, roformer_segment_size, roformer_override_segment_size, roformer_overlap, roformer_batch_size, roformer_normalization_threshold, roformer_amplification_threshold, roformer_single_stem], [roformer_stem1, roformer_stem2] ) with gr.TabItem("MDX23C"): with gr.Row(): mdx23c_model = gr.Dropdown( label="Select the model", choices=mdx23c_models, value=initial_settings.get("MDX23C", {}).get("model", None), interactive=True ) mdx23c_output_format = gr.Dropdown( label="Select the output format", choices=output_format, value=initial_settings.get("MDX23C", {}).get("output_format", None), interactive=True ) with gr.Row(): mdx23c_audio = gr.Audio( label="Input audio", type="filepath", interactive=True ) with gr.Row(): mdx23c_button = gr.Button("Separate!", variant="primary") with gr.Row(): mdx23c_stem1 = gr.Audio( show_download_button=True, interactive=False, label="Stem 1", type="filepath" ) mdx23c_stem2 = gr.Audio( show_download_button=True, interactive=False, label="Stem 2", type="filepath" ) with gr.Accordion("Separation by link", open=False): with gr.Row(): mdx23c_link = gr.Textbox( label="Link", placeholder="Paste the link here", interactive=True ) with gr.Row(): gr.Markdown("You can paste the link to the video/audio from many sites, check the complete list [here](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)") with gr.Row(): mdx23c_download_button = gr.Button("Download!", variant="primary") mdx23c_download_button.click(download_audio, [mdx23c_link], [mdx23c_audio]) with gr.Accordion("Batch separation", open=False): with gr.Row(): mdx23c_input_path = gr.Textbox( label="Input path", placeholder="Place the input path here", interactive=True ) mdx23c_output_path = gr.Textbox( label="Output path", placeholder="Place the output path here", interactive=True ) with gr.Row(): mdx23c_bath_button = gr.Button("Separate!", variant="primary") with gr.Row(): mdx23c_info = gr.Textbox( label="Output information", interactive=False ) with gr.TabItem("MDX23C Processing"): with gr.Row(): mdx23c_segment_size = gr.Slider( minimum=32, maximum=4000, step=32, label="Segment size", info="Larger consumes more resources, but may give better results", value=initial_settings.get("MDX23C", {}).get("segment_size", 256), interactive=True ) mdx23c_override_segment_size = gr.Checkbox( label="Override segment size", info="Override model default segment size instead of using the model default value", value=initial_settings.get("MDX23C", {}).get("override_segment_size", False), interactive=True ) with gr.Row(): mdx23c_overlap = gr.Slider( minimum=2, maximum=50, step=1, label="Overlap", info="Amount of overlap between prediction windows", value=initial_settings.get("MDX23C", {}).get("overlap", 8), interactive=True ) mdx23c_batch_size = gr.Slider( label="Batch size", info="Larger consumes more RAM but may process slightly faster", minimum=1, maximum=16, step=1, value=initial_settings.get("MDX23C", {}).get("batch_size", 1), interactive=True ) with gr.Row(): mdx23c_normalization_threshold = gr.Slider( label="Normalization threshold", info="The threshold for audio normalization", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("MDX23C", {}).get("normalization_threshold", 0.9), interactive=True ) mdx23c_amplification_threshold = gr.Slider( label="Amplification threshold", info="The threshold for audio amplification", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("MDX23C", {}).get("amplification_threshold", 0.7), interactive=True ) with gr.Row(): mdx23c_single_stem = gr.Textbox( label="Output only single stem", placeholder="Write the stem you want, check the stems of each model on Leaderboard. e.g. Instrumental", value=initial_settings.get("MDX23C", {}).get("single_stem", ""), interactive=True ) mdx23c_bath_button.click( mdx23c_batch, [mdx23c_input_path, mdx23c_output_path, mdx23c_model, mdx23c_output_format, mdx23c_segment_size, mdx23c_override_segment_size, mdx23c_overlap, mdx23c_batch_size, mdx23c_normalization_threshold, mdx23c_amplification_threshold, mdx23c_single_stem], [mdx23c_info] ) mdx23c_button.click( mdxc_separator, [mdx23c_audio, mdx23c_model, mdx23c_output_format, mdx23c_segment_size, mdx23c_override_segment_size, mdx23c_overlap, mdx23c_batch_size, mdx23c_normalization_threshold, mdx23c_amplification_threshold, mdx23c_single_stem], [mdx23c_stem1, mdx23c_stem2] ) with gr.TabItem("MDX-NET"): with gr.Row(): mdxnet_model = gr.Dropdown( label="Select the model", choices=mdxnet_models, value=initial_settings.get("MDX-NET", {}).get("model", None), interactive=True ) mdxnet_output_format = gr.Dropdown( label="Select the output format", choices=output_format, value=initial_settings.get("MDX-NET", {}).get("output_format", None), interactive=True ) with gr.Row(): mdxnet_audio = gr.Audio( label="Input audio", type="filepath", interactive=True ) with gr.Row(): mdxnet_button = gr.Button("Separate!", variant="primary") with gr.Row(): mdxnet_stem1 = gr.Audio( show_download_button=True, interactive=False, label="Stem 1", type="filepath" ) mdxnet_stem2 = gr.Audio( show_download_button=True, interactive=False, label="Stem 2", type="filepath" ) with gr.Accordion("Separation by link", open=False): with gr.Row(): mdxnet_link = gr.Textbox( label="Link", placeholder="Paste the link here", interactive=True ) with gr.Row(): gr.Markdown("You can paste the link to the video/audio from many sites, check the complete list [here](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)") with gr.Row(): mdxnet_download_button = gr.Button("Download!", variant="primary") mdxnet_download_button.click(download_audio, [mdxnet_link], [mdxnet_audio]) with gr.Accordion("Batch separation", open=False): with gr.Row(): mdxnet_input_path = gr.Textbox( label="Input path", placeholder="Place the input path here", interactive=True ) mdxnet_output_path = gr.Textbox( label="Output path", placeholder="Place the output path here", interactive=True ) with gr.Row(): mdxnet_bath_button = gr.Button("Separate!", variant="primary") with gr.Row(): mdxnet_info = gr.Textbox( label="Output information", interactive=False ) with gr.TabItem("MDX-NET Processing"): with gr.Row(): mdxnet_hop_length = gr.Slider( label="Hop length", info="Usually called stride in neural networks; only change if you know what you're doing", minimum=32, maximum=2048, step=32, value=initial_settings.get("MDX-NET", {}).get("hop_length", 1024), interactive=True ) mdxnet_segment_size = gr.Slider( minimum=32, maximum=4000, step=32, label="Segment size", info="Larger consumes more resources, but may give better results", value=initial_settings.get("MDX-NET", {}).get("segment_size", 256), interactive=True ) mdxnet_denoise = gr.Checkbox( label="Denoise", info="Enable denoising during separation", value=initial_settings.get("MDX-NET", {}).get("denoise", True), interactive=True ) with gr.Row(): mdxnet_overlap = gr.Slider( label="Overlap", info="Amount of overlap between prediction windows", minimum=0.001, maximum=0.999, step=0.001, value=initial_settings.get("MDX-NET", {}).get("overlap", 0.25), interactive=True ) mdxnet_batch_size = gr.Slider( label="Batch size", info="Larger consumes more RAM but may process slightly faster", minimum=1, maximum=16, step=1, value=initial_settings.get("MDX-NET", {}).get("batch_size", 1), interactive=True ) with gr.Row(): mdxnet_normalization_threshold = gr.Slider( label="Normalization threshold", info="The threshold for audio normalization", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("MDX-NET", {}).get("normalization_threshold", 0.9), interactive=True ) mdxnet_amplification_threshold = gr.Slider( label="Amplification threshold", info="The threshold for audio amplification", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("MDX-NET", {}).get("amplification_threshold", 0.7), interactive=True ) with gr.Row(): mdxnet_single_stem = gr.Textbox( label="Output only single stem", placeholder="Write the stem you want, check the stems of each model on Leaderboard. e.g. Instrumental", value=initial_settings.get("MDX-NET", {}).get("single_stem", ""), interactive=True ) mdxnet_bath_button.click( mdxnet_batch, [mdxnet_input_path, mdxnet_output_path, mdxnet_model, mdxnet_output_format, mdxnet_hop_length, mdxnet_segment_size, mdxnet_denoise, mdxnet_overlap, mdxnet_batch_size, mdxnet_normalization_threshold, mdxnet_amplification_threshold, mdxnet_single_stem], [mdxnet_info] ) mdxnet_button.click( mdxnet_separator, [mdxnet_audio, mdxnet_model, mdxnet_output_format, mdxnet_hop_length, mdxnet_segment_size, mdxnet_denoise, mdxnet_overlap, mdxnet_batch_size, mdxnet_normalization_threshold, mdxnet_amplification_threshold, mdxnet_single_stem], [mdxnet_stem1, mdxnet_stem2] ) with gr.TabItem("VR ARCH"): with gr.Row(): vrarch_model = gr.Dropdown( label="Select the model", choices=vrarch_models, value=initial_settings.get("VR Arch", {}).get("model", None), interactive=True ) vrarch_output_format = gr.Dropdown( label="Select the output format", choices=output_format, value=initial_settings.get("VR Arch", {}).get("output_format", None), interactive=True ) with gr.Row(): vrarch_audio = gr.Audio( label="Input audio", type="filepath", interactive=True ) with gr.Row(): vrarch_button = gr.Button("Separate!", variant="primary") with gr.Row(): vrarch_stem1 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 1" ) vrarch_stem2 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 2" ) with gr.Accordion("Separation by link", open=False): with gr.Row(): vrarch_link = gr.Textbox( label="Link", placeholder="Paste the link here", interactive=True ) with gr.Row(): gr.Markdown("You can paste the link to the video/audio from many sites, check the complete list [here](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)") with gr.Row(): vrarch_download_button = gr.Button("Download!", variant="primary") vrarch_download_button.click(download_audio, [vrarch_link], [vrarch_audio]) with gr.Accordion("Batch separation", open=False): with gr.Row(): vrarch_input_path = gr.Textbox( label="Input path", placeholder="Place the input path here", interactive=True ) vrarch_output_path = gr.Textbox( label="Output path", placeholder="Place the output path here", interactive=True ) with gr.Row(): vrarch_bath_button = gr.Button("Separate!", variant="primary") with gr.Row(): vrarch_info = gr.Textbox( label="Output information", interactive=False ) with gr.TabItem("VR ARCH Processing"): with gr.Row(): vrarch_window_size = gr.Slider( label="Window size", info="Balance quality and speed. 1024 = fast but lower, 320 = slower but better quality", minimum=320, maximum=1024, step=32, value=initial_settings.get("VR Arch", {}).get("window_size", 512), interactive=True ) vrarch_agression = gr.Slider( minimum=1, maximum=50, step=1, label="Agression", info="Intensity of primary stem extraction", value=initial_settings.get("VR Arch", {}).get("aggression", 5), interactive=True ) vrarch_tta = gr.Checkbox( label="TTA", info="Enable Test-Time-Augmentation; slow but improves quality", value=initial_settings.get("VR Arch", {}).get("tta", True), interactive=True ) with gr.Row(): vrarch_post_process = gr.Checkbox( label="Post process", info="Identify leftover artifacts within vocal output; may improve separation for some songs", value=initial_settings.get("VR Arch", {}).get("post_process", False), interactive=True ) vrarch_post_process_threshold = gr.Slider( label="Post process threshold", info="Threshold for post-processing", minimum=0.1, maximum=0.3, step=0.1, value=initial_settings.get("VR Arch", {}).get("post_process_threshold", 0.2), interactive=True ) with gr.Row(): vrarch_high_end_process = gr.Checkbox( label="High end process", info="Mirror the missing frequency range of the output", value=initial_settings.get("VR Arch", {}).get("high_end_process", False), interactive=True, ) vrarch_batch_size = gr.Slider( label="Batch size", info="Larger consumes more RAM but may process slightly faster", minimum=1, maximum=16, step=1, value=initial_settings.get("VR Arch", {}).get("batch_size", 1), interactive=True ) with gr.Row(): vrarch_normalization_threshold = gr.Slider( label="Normalization threshold", info="The threshold for audio normalization", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("VR Arch", {}).get("normalization_threshold", 0.9), interactive=True ) vrarch_amplification_threshold = gr.Slider( label="Amplification threshold", info="The threshold for audio amplification", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("VR Arch", {}).get("amplification_threshold", 0.7), interactive=True ) with gr.Row(): vrarch_single_stem = gr.Textbox( label="Output only single stem", placeholder="Write the stem you want, check the stems of each model on Leaderboard. e.g. Instrumental", value=initial_settings.get("VR Arch", {}).get("single_stem", ""), interactive=True ) vrarch_bath_button.click( vrarch_batch, [vrarch_input_path, vrarch_output_path, vrarch_model, vrarch_output_format, vrarch_window_size, vrarch_agression, vrarch_tta, vrarch_post_process, vrarch_post_process_threshold, vrarch_high_end_process, vrarch_batch_size, vrarch_normalization_threshold, vrarch_amplification_threshold, vrarch_single_stem], [vrarch_info] ) vrarch_button.click( vrarch_separator, [vrarch_audio, vrarch_model, vrarch_output_format, vrarch_window_size, vrarch_agression, vrarch_tta, vrarch_post_process, vrarch_post_process_threshold, vrarch_high_end_process, vrarch_batch_size, vrarch_normalization_threshold, vrarch_amplification_threshold, vrarch_single_stem], [vrarch_stem1, vrarch_stem2] ) with gr.TabItem("Demucs"): with gr.Row(): demucs_model = gr.Dropdown( label="Select the model", choices=demucs_models, value=initial_settings.get("Demucs", {}).get("model", None), interactive=True ) demucs_output_format = gr.Dropdown( label="Select the output format", choices=output_format, value=initial_settings.get("Demucs", {}).get("output_format", None), interactive=True ) with gr.Row(): demucs_audio = gr.Audio( label="Input audio", type="filepath", interactive=True ) with gr.Row(): demucs_button = gr.Button("Separate!", variant="primary") with gr.Row(): demucs_stem1 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 1" ) demucs_stem2 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 2" ) with gr.Row(): demucs_stem3 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 3" ) demucs_stem4 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 4" ) with gr.Row(visible=False) as stem6: demucs_stem5 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 5" ) demucs_stem6 = gr.Audio( show_download_button=True, interactive=False, type="filepath", label="Stem 6" ) demucs_model.change(update_stems, inputs=[demucs_model], outputs=stem6) with gr.Accordion("Separation by link", open=False): with gr.Row(): demucs_link = gr.Textbox( label="Link", placeholder="Paste the link here", interactive=True ) with gr.Row(): gr.Markdown("You can paste the link to the video/audio from many sites, check the complete list [here](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)") with gr.Row(): demucs_download_button = gr.Button("Download!", variant="primary") demucs_download_button.click(download_audio, [demucs_link], [demucs_audio]) with gr.Accordion("Batch separation", open=False): with gr.Row(): demucs_input_path = gr.Textbox( label="Input path", placeholder="Place the input path here", interactive=True ) demucs_output_path = gr.Textbox( label="Output path", placeholder="Place the output path here", interactive=True ) with gr.Row(): demucs_bath_button = gr.Button("Separate!", variant="primary") with gr.Row(): demucs_info = gr.Textbox( label="Output information", interactive=False ) with gr.TabItem("Demucs Processing"): with gr.Row(): demucs_shifts = gr.Slider( label="Shifts", info="Number of predictions with random shifts, higher = slower but better quality", minimum=1, maximum=20, step=1, value=initial_settings.get("Demucs", {}).get("shifts", 2), interactive=True ) demucs_segment_size = gr.Slider( label="Segment size", info="Size of segments into which the audio is split. Higher = slower but better quality", minimum=1, maximum=100, step=1, value=initial_settings.get("Demucs", {}).get("segment_size", 40), interactive=True ) demucs_segments_enabled = gr.Checkbox( label="Segment-wise processing", info="Enable segment-wise processing", value=initial_settings.get("Demucs", {}).get("segments_enabled", True), interactive=True ) with gr.Row(): demucs_overlap = gr.Slider( label="Overlap", info="Overlap between prediction windows. Higher = slower but better quality", minimum=0.001, maximum=0.999, step=0.001, value=initial_settings.get("Demucs", {}).get("overlap", 0.25), interactive=True ) demucs_batch_size = gr.Slider( label="Batch size", info="Larger consumes more RAM but may process slightly faster", minimum=1, maximum=16, step=1, value=initial_settings.get("Demucs", {}).get("batch_size", 1), interactive=True ) with gr.Row(): demucs_normalization_threshold = gr.Slider( label="Normalization threshold", info="The threshold for audio normalization", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("Demucs", {}).get("normalization_threshold", 0.9), interactive=True ) demucs_amplification_threshold = gr.Slider( label="Amplification threshold", info="The threshold for audio amplification", minimum=0.1, maximum=1, step=0.1, value=initial_settings.get("Demucs", {}).get("amplification_threshold", 0.7), interactive=True ) demucs_bath_button.click( demucs_batch, [demucs_input_path, demucs_output_path, demucs_model, demucs_output_format, demucs_shifts, demucs_segment_size, demucs_segments_enabled, demucs_overlap, demucs_batch_size, demucs_normalization_threshold, demucs_amplification_threshold], [demucs_info] ) demucs_button.click( demucs_separator, [demucs_audio, demucs_model, demucs_output_format, demucs_shifts, demucs_segment_size, demucs_segments_enabled, demucs_overlap, demucs_batch_size, demucs_normalization_threshold, demucs_amplification_threshold], [demucs_stem1, demucs_stem2, demucs_stem3, demucs_stem4, demucs_stem5, demucs_stem6] ) with gr.TabItem("Leaderboard"): with gr.Group(): with gr.Row(equal_height=True): list_filter = gr.Dropdown( label="List filter", info="Filter and sort the model list by stem", choices=["vocals", "instrumental", "reverb", "echo", "noise", "crowd", "dry", "aspiration", "male", "woodwinds", "kick", "drums", "bass", "guitar", "piano", "other"], value=lambda: None ) list_button = gr.Button("Show list!", variant="primary") output_list = gr.HTML(label="Leaderboard") list_button.click(leaderboard, inputs=list_filter, outputs=output_list) with gr.TabItem("Credits"): gr.Markdown( """ UVR5 UI created by **[Eddycrack 864](https://github.com/Eddycrack864). Improved for HF only by [BF667](https://github.com/BF667) * python-audio-separator by [beveradb](https://github.com/beveradb). * Thanks to [Mikus](https://github.com/cappuch) for the help with the code. * Thanks to [Nick088](https://huggingface.co/Nick088) for the help to fix roformers. * Thanks to [yt_dlp](https://github.com/yt-dlp/yt-dlp) devs. * Thanks to [Bebra777228](https://github.com/Bebra777228)'s code for guiding me to improve my code. * Thanks to Nick088, MrM0dZ, BF667, lucinamari, perariroswe, Enes, Léo and the_undead0 for helping translate UVR5 UI. * Thanks to vadigr123 for creating the images for the Discord Rich Presence. You can donate to the original UVR5 project here: [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/uvr5) """ ) app.queue() app.launch()