Germanized commited on
Commit
d8ec62e
·
verified ·
1 Parent(s): 6b5f4a0

This (PATCH) replaced your kind of not so the best download logic with better logic and automatic downloading of models thats faster and more visual progress

Browse files
Files changed (1) hide show
  1. webUI(LocalPatchByGermanized).py +346 -0
webUI(LocalPatchByGermanized).py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import shutil
4
+ import librosa
5
+ import soundfile
6
+ import numpy as np
7
+ import gradio as gr
8
+ from UVR_interface import root, UVRInterface, VR_MODELS_DIR, MDX_MODELS_DIR, DEMUCS_MODELS_DIR
9
+ from gui_data.constants import *
10
+ from typing import List, Dict, Callable, Union
11
+ import wget
12
+
13
+ class UVRWebUI:
14
+
15
+ def __init__(self, uvr: UVRInterface, online_data_path: str) -> None:
16
+ self.uvr = uvr
17
+ self.models_url = self.get_models_url(online_data_path)
18
+ self.define_layout()
19
+ self.input_temp_dir = '__temp'
20
+ self.export_path = 'out'
21
+ if not os.path.exists(self.input_temp_dir):
22
+ os.makedirs(self.input_temp_dir, exist_ok=True)
23
+ if not os.path.exists(self.export_path):
24
+ os.makedirs(self.export_path, exist_ok=True)
25
+
26
+ def get_models_url(self, models_info_path: str) -> Dict[str, Dict]:
27
+ with open(models_info_path, 'r') as f:
28
+ online_data = json.loads(f.read())
29
+ models_url = {}
30
+ for arch, download_list_key in zip([VR_ARCH_TYPE, MDX_ARCH_TYPE], ['vr_download_list', 'mdx_download_list']):
31
+ models_url[arch] = {model_name: NORMAL_REPO + model_filename_part for model_name, model_filename_part in online_data[download_list_key].items()}
32
+ models_url[DEMUCS_ARCH_TYPE] = online_data['demucs_download_list']
33
+ return models_url
34
+
35
+ def get_local_models(self, arch: str) -> List[str]:
36
+ model_config = {VR_ARCH_TYPE: (VR_MODELS_DIR, '.pth'), MDX_ARCH_TYPE: (MDX_MODELS_DIR, '.onnx'), DEMUCS_ARCH_TYPE: (DEMUCS_MODELS_DIR, '.yaml')}
37
+ try:
38
+ model_dir, suffix = model_config[arch]
39
+ if not os.path.exists(model_dir):
40
+ os.makedirs(model_dir, exist_ok=True)
41
+ return []
42
+ except KeyError:
43
+ print(f'Error: Unknown arch type: {arch} in get_local_models')
44
+ return []
45
+ if not os.path.exists(model_dir):
46
+ print(f'Warning: Model directory {model_dir} still does not exist for arch {arch}.')
47
+ return []
48
+ return sorted([os.path.splitext(f)[0] for f in os.listdir(model_dir) if f.endswith(suffix) and os.path.isfile(os.path.join(model_dir, f))])
49
+
50
+ def set_arch_setting_value(self, arch: str, setting1, setting2):
51
+ if arch == VR_ARCH_TYPE:
52
+ root.window_size_var.set(setting1)
53
+ root.aggression_setting_var.set(setting2)
54
+ elif arch == MDX_ARCH_TYPE:
55
+ root.mdx_batch_size_var.set(setting1)
56
+ root.compensate_var.set(setting2)
57
+ elif arch == DEMUCS_ARCH_TYPE:
58
+ pass
59
+
60
+ def arch_select_update(self, arch: str) -> List[Dict]:
61
+ choices = self.get_local_models(arch)
62
+ if not choices:
63
+ print(f'Warning: No local models found for {arch}. Dropdown will be empty.')
64
+ model_update_label = CHOOSE_MODEL
65
+ if arch == VR_ARCH_TYPE:
66
+ model_update_label = SELECT_VR_MODEL_MAIN_LABEL
67
+ setting1_update = self.arch_setting1.update(choices=VR_WINDOW, label=WINDOW_SIZE_MAIN_LABEL, value=root.window_size_var.get())
68
+ setting2_update = self.arch_setting2.update(choices=VR_AGGRESSION, label=AGGRESSION_SETTING_MAIN_LABEL, value=root.aggression_setting_var.get())
69
+ elif arch == MDX_ARCH_TYPE:
70
+ model_update_label = CHOOSE_MDX_MODEL_MAIN_LABEL
71
+ setting1_update = self.arch_setting1.update(choices=BATCH_SIZE, label=BATCHES_MDX_MAIN_LABEL, value=root.mdx_batch_size_var.get())
72
+ setting2_update = self.arch_setting2.update(choices=VOL_COMPENSATION, label=VOL_COMP_MDX_MAIN_LABEL, value=root.compensate_var.get())
73
+ elif arch == DEMUCS_ARCH_TYPE:
74
+ model_update_label = CHOOSE_DEMUCS_MODEL_MAIN_LABEL
75
+ setting1_update = self.arch_setting1.update(choices=[], label='Demucs Setting 1', value=None, visible=False)
76
+ setting2_update = self.arch_setting2.update(choices=[], label='Demucs Setting 2', value=None, visible=False)
77
+ else:
78
+ gr.Error(f'Unknown arch type: {arch}')
79
+ model_update = self.model_choice.update(choices=[], value=CHOOSE_MODEL, label='Error: Unknown Arch')
80
+ setting1_update = self.arch_setting1.update(choices=[], value=None, label='Setting 1')
81
+ setting2_update = self.arch_setting2.update(choices=[], value=None, label='Setting 2')
82
+ return [model_update, setting1_update, setting2_update]
83
+ model_update = self.model_choice.update(choices=choices, value=CHOOSE_MODEL, label=model_update_label)
84
+ return [model_update, setting1_update, setting2_update]
85
+
86
+ def model_select_update(self, arch: str, model_name: str) -> List[Union[str, Dict, None]]:
87
+ if model_name == CHOOSE_MODEL or model_name is None:
88
+ return [self.primary_stem_only.update(label=f'{PRIMARY_STEM} only'), self.secondary_stem_only.update(label=f'{SECONDARY_STEM} only'), self.primary_stem_out.update(label=f'Output {PRIMARY_STEM}'), self.secondary_stem_out.update(label=f'Output {SECONDARY_STEM}')]
89
+ model_data_list = self.uvr.assemble_model_data(model_name, arch)
90
+ if not model_data_list:
91
+ gr.Error(f'Cannot get model data for model {model_name}, arch {arch}. Model list empty.')
92
+ return [None for _ in range(4)]
93
+ model = model_data_list[0]
94
+ if not model.model_status:
95
+ gr.Error(f'Cannot get model data, model hash = {model.model_hash}')
96
+ return [None for _ in range(4)]
97
+ stem1_check_update = self.primary_stem_only.update(label=f'{model.primary_stem} Only')
98
+ stem2_check_update = self.secondary_stem_only.update(label=f'{model.secondary_stem} Only')
99
+ stem1_out_update = self.primary_stem_out.update(label=f'Output {model.primary_stem}')
100
+ stem2_out_update = self.secondary_stem_out.update(label=f'Output {model.secondary_stem}')
101
+ return [stem1_check_update, stem2_check_update, stem1_out_update, stem2_out_update]
102
+
103
+ def checkbox_set_root_value(self, checkbox: gr.Checkbox, root_attr: str):
104
+ checkbox.change(lambda value: root.__getattribute__(root_attr).set(value), inputs=checkbox)
105
+
106
+ def set_checkboxes_exclusive(self, checkboxes: List[gr.Checkbox], pure_callbacks: List[Callable], exclusive_value=True):
107
+
108
+ def exclusive_onchange(i, callback_i):
109
+
110
+ def new_onchange(*check_values):
111
+ current_values = [cb.value for cb in checkboxes]
112
+ if current_values[i] == exclusive_value:
113
+ return_values = []
114
+ for j, value_j in enumerate(current_values):
115
+ if j != i and value_j == exclusive_value:
116
+ return_values.append(not exclusive_value)
117
+ else:
118
+ return_values.append(current_values[j])
119
+ return_values[i] = exclusive_value
120
+ else:
121
+ return_values = current_values
122
+ for cb_idx, final_val in enumerate(return_values):
123
+ pure_callbacks[cb_idx](final_val)
124
+ return tuple(return_values)
125
+ return new_onchange
126
+ for i, (checkbox, callback) in enumerate(zip(checkboxes, pure_callbacks)):
127
+
128
+ def create_exclusive_handler(changed_idx, all_checkboxes, all_callbacks):
129
+
130
+ def handler(is_checked):
131
+ outputs = []
132
+ all_callbacks[changed_idx](is_checked)
133
+ for k_idx, cb_k in enumerate(all_checkboxes):
134
+ if k_idx == changed_idx:
135
+ outputs.append(is_checked)
136
+ elif is_checked:
137
+ all_callbacks[k_idx](False)
138
+ outputs.append(False)
139
+ else:
140
+ outputs.append(gr.update())
141
+ return tuple(outputs)
142
+ return handler
143
+ checkbox.change(create_exclusive_handler(i, checkboxes, pure_callbacks), inputs=checkbox, outputs=checkboxes)
144
+
145
+ def process(self, input_audio, input_filename, model_name, arch, setting1, setting2, progress=gr.Progress(track_tqdm=True)):
146
+ if input_audio is None:
147
+ return (None, None, 'Error: No input audio provided.')
148
+ if model_name == CHOOSE_MODEL or model_name is None:
149
+ return (None, None, 'Error: Please select a model.')
150
+
151
+ def set_progress_func(step, inference_iterations=0):
152
+ pass
153
+ sampling_rate, audio_data = input_audio
154
+ if np.issubdtype(audio_data.dtype, np.integer):
155
+ audio_data = (audio_data / np.iinfo(audio_data.dtype).max).astype(np.float32)
156
+ elif not np.issubdtype(audio_data.dtype, np.floating):
157
+ return (None, None, f'Error: Unsupported audio data type {audio_data.dtype}')
158
+ if len(audio_data.shape) > 1 and audio_data.shape[0] > 5:
159
+ audio_data = audio_data.T
160
+ if len(audio_data.shape) > 1:
161
+ audio_data = librosa.to_mono(audio_data)
162
+ if not input_filename:
163
+ input_filename = 'audio_input.wav'
164
+ elif not input_filename.lower().endswith(('.wav', '.mp3', '.flac')):
165
+ input_filename += '.wav'
166
+ input_path = os.path.join(self.input_temp_dir, os.path.basename(input_filename))
167
+ try:
168
+ soundfile.write(input_path, audio_data, sampling_rate, format='wav')
169
+ except Exception as e:
170
+ return (None, None, f'Error writing temporary input file: {e}')
171
+ self.set_arch_setting_value(arch, setting1, setting2)
172
+ separator = self.uvr.process(model_name=model_name, arch_type=arch, audio_file=input_path, export_path=self.export_path, is_model_sample_mode=root.model_sample_mode_var.get(), set_progress_func=set_progress_func)
173
+ if separator is None:
174
+ if os.path.exists(input_path):
175
+ os.remove(input_path)
176
+ return (None, None, 'Error during processing. Separator object is None.')
177
+ primary_audio_out = None
178
+ secondary_audio_out = None
179
+ msg = ''
180
+ if separator.export_path and separator.audio_file_base and separator.primary_stem:
181
+ if not separator.is_secondary_stem_only:
182
+ primary_stem_path = os.path.join(separator.export_path, f'{separator.audio_file_base}_({separator.primary_stem}).wav')
183
+ if os.path.exists(primary_stem_path):
184
+ audio_p, rate_p = soundfile.read(primary_stem_path)
185
+ primary_audio_out = (rate_p, audio_p)
186
+ msg += f'{separator.primary_stem} saved at {primary_stem_path}\n'
187
+ else:
188
+ msg += f'Error: Primary stem file not found at {primary_stem_path}\n'
189
+ else:
190
+ msg += 'Error: Missing data in separator object for primary stem.\n'
191
+ if separator.export_path and separator.audio_file_base and separator.secondary_stem:
192
+ if not separator.is_primary_stem_only:
193
+ secondary_stem_path = os.path.join(separator.export_path, f'{separator.audio_file_base}_({separator.secondary_stem}).wav')
194
+ if os.path.exists(secondary_stem_path):
195
+ audio_s, rate_s = soundfile.read(secondary_stem_path)
196
+ secondary_audio_out = (rate_s, audio_s)
197
+ msg += f'{separator.secondary_stem} saved at {secondary_stem_path}\n'
198
+ else:
199
+ msg += f'Error: Secondary stem file not found at {secondary_stem_path}\n'
200
+ else:
201
+ msg += 'Error: Missing data in separator object for secondary stem.\n'
202
+ if os.path.exists(input_path):
203
+ os.remove(input_path)
204
+ return (primary_audio_out, secondary_audio_out, msg.strip())
205
+
206
+ def define_layout(self):
207
+ with gr.Blocks() as app:
208
+ self.app = app
209
+ gr.HTML('<h1> 🎵 Ultimate Vocal Remover WebUI Local Patch By Germanized🎵 </h1>')
210
+ gr.Markdown('This is an experimental demo with CPU. Duplicate the space for use in private')
211
+ gr.Markdown('[![Duplicate this Space](https://huggingface.co/datasets/huggingface/badges/raw/main/duplicate-this-space-sm-dark.svg)](https://huggingface.co/spaces/r3gm/Ultimate-Vocal-Remover-WebUI?duplicate=true)\n\n')
212
+ with gr.Tabs():
213
+ with gr.TabItem('Process'):
214
+ with gr.Row():
215
+ self.arch_choice = gr.Dropdown(choices=[VR_ARCH_TYPE, MDX_ARCH_TYPE], value=VR_ARCH_TYPE, label=CHOOSE_PROC_METHOD_MAIN_LABEL, interactive=True)
216
+ self.model_choice = gr.Dropdown(choices=self.get_local_models(VR_ARCH_TYPE), value=CHOOSE_MODEL, label=SELECT_VR_MODEL_MAIN_LABEL + ' 👋Select a model', interactive=True)
217
+ with gr.Row():
218
+ self.arch_setting1 = gr.Dropdown(choices=VR_WINDOW, value=root.window_size_var.get(), label=WINDOW_SIZE_MAIN_LABEL + ' 👋Select one', interactive=True)
219
+ self.arch_setting2 = gr.Dropdown(choices=VR_AGGRESSION, value=root.aggression_setting_var.get(), label=AGGRESSION_SETTING_MAIN_LABEL, interactive=True)
220
+ with gr.Row():
221
+ self.use_gpu = gr.Checkbox(label='Rhythmic Transmutation Device', value=True, interactive=True)
222
+ self.primary_stem_only = gr.Checkbox(label=f'{PRIMARY_STEM} only', value=root.is_primary_stem_only_var.get(), interactive=True)
223
+ self.secondary_stem_only = gr.Checkbox(label=f'{SECONDARY_STEM} only', value=root.is_secondary_stem_only_var.get(), interactive=True)
224
+ self.sample_mode = gr.Checkbox(label=SAMPLE_MODE_CHECKBOX(root.model_sample_mode_duration_var.get()), value=root.model_sample_mode_var.get(), interactive=True)
225
+ with gr.Row():
226
+ self.input_filename = gr.Textbox(label='Input filename (e.g., song.wav)', value='temp.wav', interactive=True)
227
+ with gr.Row():
228
+ self.audio_in = gr.Audio(label='Input audio', type='numpy', interactive=True)
229
+ with gr.Row():
230
+ self.process_submit = gr.Button(START_PROCESSING, variant='primary')
231
+ with gr.Row():
232
+ self.primary_stem_out = gr.Audio(label=f'Output {PRIMARY_STEM}', interactive=False)
233
+ self.secondary_stem_out = gr.Audio(label=f'Output {SECONDARY_STEM}', interactive=False)
234
+ with gr.Row():
235
+ self.out_message = gr.Textbox(label='Output Message', interactive=False)
236
+ with gr.TabItem('Settings'):
237
+ with gr.Tabs():
238
+ with gr.TabItem('Settings Guide (Placeholder)'):
239
+ gr.Markdown('Details about settings would go here.')
240
+ with gr.TabItem('Additional Settings'):
241
+ self.wav_type = gr.Dropdown(choices=WAV_TYPE, label='Wav Type Output', value='PCM_16', interactive=True)
242
+ self.mp3_rate = gr.Dropdown(choices=MP3_BIT_RATES, label='MP3 Bitrate Output', value='320k', interactive=True)
243
+ with gr.TabItem('Download Models'):
244
+ gr.Markdown('Select a model category and model name to see its download URL. Models are downloaded automatically on startup if missing.')
245
+
246
+ def md_url(url, text=None):
247
+ if text is None:
248
+ text = url
249
+ return f'[{text}]({url})'
250
+ with gr.Row():
251
+ vr_models_for_dl = self.models_url.get(VR_ARCH_TYPE, {})
252
+ self.vr_download_choice = gr.Dropdown(choices=list(vr_models_for_dl.keys()), label=f'Select {VR_ARCH_TYPE} Model', interactive=True)
253
+ self.vr_download_url = gr.Markdown()
254
+ self.vr_download_choice.change(lambda model: md_url(vr_models_for_dl.get(model, 'URL not found')) if model else '', inputs=self.vr_download_choice, outputs=self.vr_download_url)
255
+ with gr.Row():
256
+ mdx_models_for_dl = self.models_url.get(MDX_ARCH_TYPE, {})
257
+ self.mdx_download_choice = gr.Dropdown(choices=list(mdx_models_for_dl.keys()), label=f'Select {MDX_ARCH_TYPE} Model', interactive=True)
258
+ self.mdx_download_url = gr.Markdown()
259
+ self.mdx_download_choice.change(lambda model: md_url(mdx_models_for_dl.get(model, 'URL not found')) if model else '', inputs=self.mdx_download_choice, outputs=self.mdx_download_url)
260
+ with gr.Row():
261
+ demucs_models_for_dl: Dict[str, Dict] = self.models_url.get(DEMUCS_ARCH_TYPE, {})
262
+ self.demucs_download_choice = gr.Dropdown(choices=list(demucs_models_for_dl.keys()), label=f'Select {DEMUCS_ARCH_TYPE} Model', interactive=True)
263
+ self.demucs_download_url = gr.Markdown()
264
+ self.demucs_download_choice.change(lambda model: '\n'.join(['- ' + md_url(url, text=filename) for filename, url in demucs_models_for_dl.get(model, {}).items()]) if model else '', inputs=self.demucs_download_choice, outputs=self.demucs_download_url)
265
+ self.arch_choice.change(self.arch_select_update, inputs=self.arch_choice, outputs=[self.model_choice, self.arch_setting1, self.arch_setting2])
266
+ self.model_choice.change(self.model_select_update, inputs=[self.arch_choice, self.model_choice], outputs=[self.primary_stem_only, self.secondary_stem_only, self.primary_stem_out, self.secondary_stem_out])
267
+ self.checkbox_set_root_value(self.use_gpu, 'is_gpu_conversion_var')
268
+ self.checkbox_set_root_value(self.sample_mode, 'model_sample_mode_var')
269
+
270
+ def make_exclusive_primary(is_checked_primary):
271
+ root.is_primary_stem_only_var.set(is_checked_primary)
272
+ if is_checked_primary:
273
+ root.is_secondary_stem_only_var.set(False)
274
+ return (gr.update(value=is_checked_primary), gr.update(value=False))
275
+ return (gr.update(value=is_checked_primary), gr.update())
276
+
277
+ def make_exclusive_secondary(is_checked_secondary):
278
+ root.is_secondary_stem_only_var.set(is_checked_secondary)
279
+ if is_checked_secondary:
280
+ root.is_primary_stem_only_var.set(False)
281
+ return (gr.update(value=False), gr.update(value=is_checked_secondary))
282
+ return (gr.update(), gr.update(value=is_checked_secondary))
283
+ self.primary_stem_only.change(make_exclusive_primary, inputs=self.primary_stem_only, outputs=[self.primary_stem_only, self.secondary_stem_only])
284
+ self.secondary_stem_only.change(make_exclusive_secondary, inputs=self.secondary_stem_only, outputs=[self.primary_stem_only, self.secondary_stem_only])
285
+ self.process_submit.click(self.process, inputs=[self.audio_in, self.input_filename, self.model_choice, self.arch_choice, self.arch_setting1, self.arch_setting2], outputs=[self.primary_stem_out, self.secondary_stem_out, self.out_message])
286
+
287
+ def launch(self, **kwargs):
288
+ self.app.queue().launch(**kwargs)
289
+ uvr_interface_instance = UVRInterface()
290
+ uvr_interface_instance.cached_sources_clear()
291
+ webui_instance = UVRWebUI(uvr_interface_instance, online_data_path='models/download_checks.json')
292
+ print('INFO: Checking and downloading models if necessary...')
293
+ model_dict_to_download = webui_instance.models_url
294
+ for category, models_in_category in model_dict_to_download.items():
295
+ target_model_dir = None
296
+ expected_suffix = None
297
+ if category == VR_ARCH_TYPE:
298
+ target_model_dir = VR_MODELS_DIR
299
+ expected_suffix = '.pth'
300
+ elif category == MDX_ARCH_TYPE:
301
+ target_model_dir = MDX_MODELS_DIR
302
+ expected_suffix = '.onnx'
303
+ elif category == DEMUCS_ARCH_TYPE:
304
+ print(f'INFO: Skipping direct download for {category} in this loop. Demucs models are handled by their own mechanism or need specific download paths.')
305
+ continue
306
+ else:
307
+ print(f'INFO: Unknown category for download: {category}')
308
+ continue
309
+ if not target_model_dir:
310
+ continue
311
+ if not os.path.exists(target_model_dir):
312
+ os.makedirs(target_model_dir, exist_ok=True)
313
+ print(f'INFO: Created directory: {target_model_dir}')
314
+ print(f'\nINFO: Checking/Downloading models for {category} into {target_model_dir}...')
315
+ if not isinstance(models_in_category, dict):
316
+ print(f'Warning: Expected a dictionary of models for {category}, but got {type(models_in_category)}. Skipping.')
317
+ continue
318
+ for model_base_name, model_full_url in models_in_category.items():
319
+ filename_from_url = model_full_url.split('/')[-1]
320
+ if not filename_from_url.endswith(expected_suffix):
321
+ correct_local_filename = model_base_name + expected_suffix
322
+ local_file_path = os.path.join(target_model_dir, correct_local_filename)
323
+ else:
324
+ local_file_path = os.path.join(target_model_dir, filename_from_url)
325
+ if not os.path.exists(local_file_path):
326
+ print(f'INFO: Downloading {model_full_url} to {target_model_dir} (expected as {os.path.basename(local_file_path)})...')
327
+ try:
328
+ downloaded_filepath_actual = wget.download(model_full_url, out=target_model_dir)
329
+ if os.path.basename(downloaded_filepath_actual) != os.path.basename(local_file_path):
330
+ print(f'INFO: Downloaded as {os.path.basename(downloaded_filepath_actual)}, renaming to {os.path.basename(local_file_path)}')
331
+ if os.path.exists(local_file_path):
332
+ os.remove(local_file_path)
333
+ shutil.move(downloaded_filepath_actual, local_file_path)
334
+ print(f'INFO: Download successful: {local_file_path}')
335
+ except Exception as e:
336
+ print(f'ERROR: wget download failed for {model_full_url}: {e}')
337
+ else:
338
+ print(f'INFO: Skipping {local_file_path}, already exists.')
339
+ print(f'INFO: Finished checking/downloading for {category}.')
340
+ print('INFO: Model download check complete.')
341
+ initial_model_choices = webui_instance.get_local_models(VR_ARCH_TYPE)
342
+ webui_instance.model_choice.choices = initial_model_choices
343
+ print('INFO: Re-initializing WebUI to pick up downloaded models for dropdowns...')
344
+ webui_instance = UVRWebUI(uvr_interface_instance, online_data_path='models/download_checks.json')
345
+ print('INFO: Launching WebUI...')
346
+ webui_instance.launch()