ASesYusuf1 commited on
Commit
bcc6e08
Β·
verified Β·
1 Parent(s): f9159bd

Create models_config.py

Browse files
Files changed (1) hide show
  1. models_config.py +822 -0
models_config.py ADDED
@@ -0,0 +1,822 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ models_config.py - Extended model configuration for SESA HuggingFace Space
3
+
4
+ Provides:
5
+ 1. EXTENDED_MODELS: All models from the main SESA project merged with base ROFORMER_MODELS
6
+ 2. Custom model management (add/delete/list from custom_models.json)
7
+ 3. Audio segmentation helpers for large file handling
8
+ 4. Batch processing support
9
+ """
10
+
11
+ import os
12
+ import json
13
+ import subprocess
14
+ import logging
15
+ import re
16
+ import shutil
17
+ import requests
18
+ import urllib.parse
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ # ─── Paths ───────────────────────────────────────────────────────────────────
23
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
24
+ CUSTOM_MODELS_FILE = os.path.join(BASE_DIR, 'custom_models.json')
25
+ MODEL_CACHE_DIR = "/tmp/audio-separator-models/"
26
+
27
+ # ─── Segmentation Constants ─────────────────────────────────────────────────
28
+ MAX_UNSPLIT_DURATION = 1800 # 30 minutes
29
+ SEGMENT_DURATION = 600 # 10 minutes per segment
30
+
31
+ # ─── Extended Model Registry ────────────────────────────────────────────────
32
+ # Merged from main project model.py + existing ROFORMER_MODELS
33
+ # Format: { "Category": { "Display Name": "checkpoint_filename.ckpt" } }
34
+ EXTENDED_MODELS = {
35
+ "Vocals": {
36
+ # === From main project model.py ===
37
+ 'BS Roformer HyperACEv2 Voc (by unwa)': 'bs_roformer_voc_hyperacev2.ckpt',
38
+ 'BS-Roformer-Resurrection (by unwa)': 'BS-Roformer-Resurrection.ckpt',
39
+ 'BS Roformer Revive3e (by unwa)': 'bs_roformer_revive3e.ckpt',
40
+ 'BS Roformer Revive2 (by unwa)': 'bs_roformer_revive2.ckpt',
41
+ 'BS Roformer Revive (by unwa)': 'bs_roformer_revive.ckpt',
42
+ 'Karaoke BS Roformer (by anvuew)': 'karaoke_bs_roformer_anvuew.ckpt',
43
+ 'VOCALS big_beta6X (by Unwa)': 'big_beta6x.ckpt',
44
+ 'VOCALS big_beta6 (by Unwa)': 'big_beta6.ckpt',
45
+ 'VOCALS Mel-Roformer FT 3 Preview (by unwa)': 'kimmel_unwa_ft3_prev.ckpt',
46
+ 'VOCALS InstVocHQ (MDX23C)': 'model_vocals_mdx23c_sdr_10.17.ckpt',
47
+ 'VOCALS MelBand-Roformer (by KimberleyJSN)': 'MelBandRoformer.ckpt',
48
+ 'VOCALS BS-Roformer 1297 (by viperx)': 'model_bs_roformer_ep_317_sdr_12.9755.ckpt',
49
+ 'VOCALS BS-Roformer 1296 (by viperx)': 'model_bs_roformer_ep_368_sdr_12.9628.ckpt',
50
+ 'VOCALS BS-RoformerLargeV1 (by unwa)': 'BS-Roformer_LargeV1.ckpt',
51
+ 'VOCALS Mel-Roformer big beta 4 (by unwa)': 'melband_roformer_big_beta4.ckpt',
52
+ 'VOCALS Melband-Roformer BigBeta5e (by unwa)': 'big_beta5e.ckpt',
53
+ 'VOCALS VitLarge23 (by ZFTurbo)': 'model_vocals_segm_models_sdr_9.77.ckpt',
54
+ 'VOCALS MelBand-Roformer Kim FT (by Unwa)': 'kimmel_unwa_ft.ckpt',
55
+ 'VOCALS MelBand-Roformer Kim FT 2 (by Unwa)': 'kimmel_unwa_ft2.ckpt',
56
+ 'VOCALS MelBand-Roformer Kim FT 2 Bleedless (by unwa)': 'kimmel_unwa_ft2_bleedless.ckpt',
57
+ 'VOCALS MelBand-Roformer (by Becruily)': 'mel_band_roformer_vocals_becruily.ckpt',
58
+ 'VOCALS Male-Female BS-RoFormer 7.2889 (by aufr33)': 'bs_roformer_male_female_by_aufr33_sdr_7.2889.ckpt',
59
+ 'voc_gaboxBSroformer (by Gabox)': 'voc_gaboxBSR.ckpt',
60
+ 'voc_gaboxMelReformer (by Gabox)': 'voc_gabox.ckpt',
61
+ 'Voc FV3 (by Gabox)': 'voc_Fv3.ckpt',
62
+ 'FullnessVocalModel (by Amane)': 'FullnessVocalModel.ckpt',
63
+ 'voc_fv4 (by Gabox)': 'voc_fv4.ckpt',
64
+ 'voc_fv5 (by Gabox)': 'voc_fv5.ckpt',
65
+ 'voc_fv6 (by Gabox)': 'voc_fv6.ckpt',
66
+ 'voc_fv7 (by Gabox)': 'voc_fv7.ckpt',
67
+ 'vocfv7beta1 (by Gabox)': 'vocfv7beta1.ckpt',
68
+ 'vocfv7beta2 (by Gabox)': 'vocfv7beta2.ckpt',
69
+ 'vocfv7beta3 (by Gabox)': 'vocfv7beta3.ckpt',
70
+ 'MelBandRoformerSYHFTV3Epsilon (by SYH99999)': 'MelBandRoformerSYHFTV3Epsilon.ckpt',
71
+ 'MelBandRoformerBigSYHFTV1 (by SYH99999)': 'MelBandRoformerBigSYHFTV1.ckpt',
72
+ 'model_chorus_bs_roformer_ep_146 (by Sucial)': 'model_chorus_bs_roformer_ep_146_sdr_23.8613.ckpt',
73
+ 'model_chorus_bs_roformer_ep_267 (by Sucial)': 'model_chorus_bs_roformer_ep_267_sdr_24.1275.ckpt',
74
+ 'BS-Rofo-SW-Fixed (by jarredou)': 'BS-Rofo-SW-Fixed.ckpt',
75
+ 'BS_ResurrectioN (by Gabox)': 'BS_ResurrectioN.ckpt',
76
+ # === Original ROFORMER_MODELS ===
77
+ 'MelBand Roformer | Big Beta 6X by unwa': 'melband_roformer_big_beta6x.ckpt',
78
+ 'MelBand Roformer Kim | Big Beta 4 FT by unwa': 'melband_roformer_big_beta4.ckpt',
79
+ 'MelBand Roformer | Kim FT by unwa': 'kimmel_unwa_ft.ckpt',
80
+ 'MelBand Roformer | Kim FT 2 by unwa': 'kimmel_unwa_ft2.ckpt',
81
+ 'MelBand Roformer | Kim FT 2 Bleedless by unwa': 'kimmel_unwa_ft2_bleedless.ckpt',
82
+ 'MelBand Roformer | Kim FT 3 Preview by unwa': 'kimmel_unwa_ft3_prev.ckpt',
83
+ 'MelBand Roformer | Vocals FV1 by Gabox': 'mel_band_roformer_vocals_fv1_gabox.ckpt',
84
+ 'MelBand Roformer | Vocals FV2 by Gabox': 'mel_band_roformer_vocals_fv2_gabox.ckpt',
85
+ 'MelBand Roformer | Vocals FV3 by Gabox': 'mel_band_roformer_vocals_fv3_gabox.ckpt',
86
+ 'MelBand Roformer | Vocals FV4 by Gabox': 'mel_band_roformer_vocals_fv4_gabox.ckpt',
87
+ 'BS Roformer | Chorus Male-Female by Sucial': 'model_chorus_bs_roformer_ep_267_sdr_24.1275.ckpt',
88
+ 'BS Roformer | Male-Female by aufr33': 'bs_roformer_male_female_by_aufr33_sdr_7.2889.ckpt',
89
+ },
90
+ "Instrumentals": {
91
+ # === From main project ===
92
+ 'Neo_InstVFX (by natanworkspace)': 'Neo_InstVFX.ckpt',
93
+ 'BS-Roformer-Resurrection-Inst (by unwa)': 'BS-Roformer-Resurrection-Inst.ckpt',
94
+ 'BS Roformer HyperACEv2 Inst (by unwa)': 'bs_roformer_inst_hyperacev2.ckpt',
95
+ 'BS-Roformer-Large-Inst (by unwa)': 'bs_large_v2_inst.ckpt',
96
+ 'BS Roformer FNO (by unwa)': 'bs_roformer_fno.ckpt',
97
+ 'Rifforge final 14.24 (by meskvlla33)': 'rifforge_full_sdr_14.2436.ckpt',
98
+ 'Inst_GaboxFv8 (by Gabox)': 'Inst_GaboxFv8.ckpt',
99
+ 'Inst_GaboxFv9 (by Gabox)': 'Inst_GaboxFv9.ckpt',
100
+ 'inst_gaboxFlowersV10 (by Gabox)': 'inst_gaboxFlowersV10.ckpt',
101
+ 'INST Mel-Roformer v1 (by unwa)': 'melband_roformer_inst_v1.ckpt',
102
+ 'INST Mel-Roformer v1e+ (by unwa)': 'inst_v1e_plus.ckpt',
103
+ 'INST Mel-Roformer v1+ (by unwa)': 'inst_v1_plus_test.ckpt',
104
+ 'INST Mel-Roformer v2 (by unwa)': 'melband_roformer_inst_v2.ckpt',
105
+ 'INST MelBand-Roformer (by Becruily)': 'mel_band_roformer_instrumental_becruily.ckpt',
106
+ 'inst_v1e (by unwa)': 'inst_v1e.ckpt',
107
+ 'inst_gabox (by Gabox)': 'inst_gabox.ckpt',
108
+ 'inst_gaboxBV1 (by Gabox)': 'inst_gaboxBv1.ckpt',
109
+ 'inst_gaboxBV2 (by Gabox)': 'inst_gaboxBv2.ckpt',
110
+ 'inst_gaboxFV2 (by Gabox)': 'inst_gaboxFv2.ckpt',
111
+ 'inst_Fv3 (by Gabox)': 'inst_gaboxFv3.ckpt',
112
+ 'Intrumental_Gabox (by Gabox)': 'intrumental_gabox.ckpt',
113
+ 'inst_Fv4Noise (by Gabox)': 'inst_Fv4Noise.ckpt',
114
+ 'inst_Fv4 (by Gabox)': 'inst_Fv4.ckpt',
115
+ 'INSTV5 (by Gabox)': 'INSTV5.ckpt',
116
+ 'INSTV5N (by Gabox)': 'INSTV5N.ckpt',
117
+ 'INSTV6N (by Gabox)': 'INSTV6N.ckpt',
118
+ 'Inst_GaboxV7 (by Gabox)': 'Inst_GaboxV7.ckpt',
119
+ 'INSTV7N (by Gabox)': 'INSTV7N.ckpt',
120
+ 'inst_fv7b (by Gabox)': 'inst_fv7b.ckpt',
121
+ 'inst_fv7z (by Gabox)': 'Inst_GaboxFv7z.ckpt',
122
+ 'Inst_FV8b (by Gabox)': 'Inst_FV8b.ckpt',
123
+ # === Original ROFORMER_MODELS ===
124
+ 'MelBand Roformer | FVX by Gabox': 'mel_band_roformer_instrumental_fvx_gabox.ckpt',
125
+ 'MelBand Roformer | INSTV8N by Gabox': 'mel_band_roformer_instrumental_instv8n_gabox.ckpt',
126
+ 'MelBand Roformer | Instrumental V1 by unwa': 'melband_roformer_inst_v1.ckpt',
127
+ 'MelBand Roformer | Instrumental V2 by unwa': 'melband_roformer_inst_v2.ckpt',
128
+ 'MelBand Roformer | Instrumental 2 by Gabox': 'mel_band_roformer_instrumental_2_gabox.ckpt',
129
+ 'MelBand Roformer | Instrumental 3 by Gabox': 'mel_band_roformer_instrumental_3_gabox.ckpt',
130
+ 'MelBand Roformer | Instrumental Bleedless V1 by Gabox': 'mel_band_roformer_instrumental_bleedless_v1_gabox.ckpt',
131
+ 'MelBand Roformer | Instrumental Bleedless V2 by Gabox': 'mel_band_roformer_instrumental_bleedless_v2_gabox.ckpt',
132
+ 'MelBand Roformer | Inst V1E by unwa': 'inst_v1e.ckpt',
133
+ 'MelBand Roformer | Inst V1E+ by unwa': 'inst_v1e_plus.ckpt',
134
+ 'MelBand Roformer | INSTV5N by Gabox': 'mel_band_roformer_instrumental_instv5n_gabox.ckpt',
135
+ 'MelBand Roformer | INSTV6N by Gabox': 'mel_band_roformer_instrumental_instv6n_gabox.ckpt',
136
+ 'MelBand Roformer | INSTV7 by Gabox': 'mel_band_roformer_instrumental_instv7_gabox.ckpt',
137
+ },
138
+ "InstVoc Duality": {
139
+ 'MelBand Roformer Kim | InstVoc Duality V1 by Unwa': 'melband_roformer_instvoc_duality_v1.ckpt',
140
+ 'MelBand Roformer Kim | InstVoc Duality V2 by Unwa': 'melband_roformer_instvox_duality_v2.ckpt',
141
+ 'INST-VOC Duality v1 (by unwa)': 'melband_roformer_instvoc_duality_v1.ckpt',
142
+ 'INST-VOC Duality v2 (by unwa)': 'melband_roformer_instvox_duality_v2.ckpt',
143
+ },
144
+ "De-Reverb": {
145
+ 'DE-REVERB MDX23C (by aufr33 & jarredou)': 'dereverb_mdx23c_sdr_6.9096.ckpt',
146
+ 'DE-REVERB MelBand-Roformer 19.1729 (by anvuew)': 'dereverb_mel_band_roformer_anvuew_sdr_19.1729.ckpt',
147
+ 'DE-REVERB-Echo MelBand-Roformer (by Sucial)': 'dereverb-echo_mel_band_roformer_sdr_10.0169.ckpt',
148
+ 'dereverb_less_aggressive (by anvuew)': 'dereverb_mel_band_roformer_less_aggressive_anvuew_sdr_18.8050.ckpt',
149
+ 'dereverb_mono (by anvuew)': 'dereverb_mel_band_roformer_mono_anvuew_sdr_20.4029.ckpt',
150
+ 'dereverb-echo 128_4_4 (by Sucial)': 'dereverb-echo_128_4_4_mel_band_roformer_sdr_dry_12.4235.ckpt',
151
+ 'dereverb_echo_mbr_v2 (by Sucial)': 'dereverb_echo_mbr_v2_sdr_dry_13.4843.ckpt',
152
+ 'de_big_reverb_mbr_ep_362 (by Sucial)': 'de_big_reverb_mbr_ep_362.ckpt',
153
+ 'de_super_big_reverb_mbr_ep_346 (by Sucial)': 'de_super_big_reverb_mbr_ep_346.ckpt',
154
+ 'dereverb_room (by anvuew)': 'dereverb_room_anvuew_sdr_13.7432.ckpt',
155
+ # === Original ROFORMER_MODELS ===
156
+ 'MelBand Roformer | De-Reverb by anvuew': 'dereverb_mel_band_roformer_anvuew_sdr_19.1729.ckpt',
157
+ 'MelBand Roformer | De-Reverb Less Aggressive by anvuew': 'dereverb_mel_band_roformer_less_aggressive_anvuew_sdr_18.8050.ckpt',
158
+ 'MelBand Roformer | De-Reverb-Echo V2 by Sucial': 'dereverb-echo_mel_band_roformer_sdr_13.4843_v2.ckpt',
159
+ 'MelBand Roformer | De-Reverb-Echo Fused by Sucial': 'dereverb_echo_mbr_fused.ckpt',
160
+ },
161
+ "Denoise": {
162
+ 'DENOISE MelBand-Roformer-1 (by aufr33)': 'denoise_mel_band_roformer_aufr33_sdr_27.9959.ckpt',
163
+ 'DENOISE MelBand-Roformer-2 aggr (by aufr33)': 'denoise_mel_band_roformer_aufr33_aggr_sdr_27.9768.ckpt',
164
+ 'denoisedebleed (by Gabox)': 'denoisedebleed.ckpt',
165
+ 'bleed_suppressor_v1 (by unwa)': 'bleed_suppressor_v1.ckpt',
166
+ # === Original ROFORMER_MODELS ===
167
+ 'Mel-Roformer-Denoise-Aufr33': 'denoise_mel_band_roformer_aufr33_sdr_27.9959.ckpt',
168
+ 'Mel-Roformer-Denoise-Aufr33-Aggr': 'denoise_mel_band_roformer_aufr33_aggr_sdr_27.9768.ckpt',
169
+ 'MelBand Roformer | Denoise by Gabox': 'mel_band_roformer_denoise_gabox.ckpt',
170
+ 'MelBand Roformer | Karaoke by Gabox': 'mel_band_roformer_karaoke_gabox.ckpt',
171
+ 'MelBand Roformer | Karaoke by becruily': 'mel_band_roformer_karaoke_becruily.ckpt',
172
+ },
173
+ "Karaoke": {
174
+ 'KARAOKE MelBand-Roformer (by aufr33 & viperx)': 'mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.ckpt',
175
+ 'KaraokeGabox (by Gabox)': 'Karaoke_GaboxV1.ckpt',
176
+ 'bs_karaoke_gabox_IS (by Gabox)': 'bs_karaoke_gabox_IS.ckpt',
177
+ 'bs_roformer_karaoke_frazer_becruily': 'bs_roformer_karaoke_frazer_becruily.ckpt',
178
+ 'mel_band_roformer_karaoke_becruily': 'mel_band_roformer_karaoke_becruily.ckpt',
179
+ },
180
+ "4-Stem": {
181
+ '4STEMS SCNet MUSDB18 (by starrytong)': 'scnet_checkpoint_musdb18.ckpt',
182
+ '4STEMS SCNet XL MUSDB18 (by ZFTurbo)': 'model_scnet_ep_54_sdr_9.8051.ckpt',
183
+ '4STEMS SCNet Large (by starrytong)': 'SCNet-large_starrytong_fixed.ckpt',
184
+ '4STEMS BS-Roformer MUSDB18 (by ZFTurbo)': 'model_bs_roformer_ep_17_sdr_9.6568.ckpt',
185
+ 'MelBandRoformer4StemFTLarge (by SYH99999)': 'MelBandRoformer4StemFTLarge.ckpt',
186
+ },
187
+ "General Purpose": {
188
+ # === From main project ===
189
+ 'OTHER BS-Roformer 1053 (by viperx)': 'model_bs_roformer_ep_937_sdr_10.5309.ckpt',
190
+ 'CROWD-REMOVAL MelBand-Roformer (by aufr33)': 'mel_band_roformer_crowd_aufr33_viperx_sdr_8.7144.ckpt',
191
+ 'CINEMATIC BandIt Plus (by kwatcharasupat)': 'model_bandit_plus_dnr_sdr_11.47.chpt',
192
+ 'CINEMATIC BandIt v2 multi (by kwatcharasupat)': 'checkpoint-multi_state_dict.ckpt',
193
+ 'DRUMSEP MDX23C 6stem (by aufr33 & jarredou)': 'aufr33-jarredou_DrumSep_model_mdx23c_ep_141_sdr_10.8059.ckpt',
194
+ 'bs_hyperace (by unwa)': 'bs_hyperace.ckpt',
195
+ 'becruily_deux (by becruily)': 'becruily_deux.ckpt',
196
+ 'becruily_guitar (by becruily)': 'becruily_guitar.ckpt',
197
+ 'aspiration MelBand-Roformer (by Sucial)': 'aspiration_mel_band_roformer_sdr_18.9845.ckpt',
198
+ 'mdx23c_similarity (by ZFTurbo)': 'model_mdx23c_ep_271_l1_freq_72.2383.ckpt',
199
+ 'Lead_Rhythm_Guitar (by listra92)': 'model_mel_band_roformer_ep_72_sdr_3.2232.ckpt',
200
+ 'last_bs_roformer_4stem (by Amane)': 'last_bs_roformer.ckpt',
201
+ 'bs_roformer_4stems_ft (by SYH99999)': 'bs_roformer_4stems_ft.pth',
202
+ 'CINEMATIC BandIt v2 Eng (by kwatcharasupat)': 'checkpoint-eng_state_dict.ckpt',
203
+ # === Original ROFORMER_MODELS ===
204
+ 'BS-Roformer-Viperx-1297': 'model_bs_roformer_ep_317_sdr_12.9755.ckpt',
205
+ 'BS-Roformer-Viperx-1296': 'model_bs_roformer_ep_368_sdr_12.9628.ckpt',
206
+ 'BS-Roformer-De-Reverb': 'deverb_bs_roformer_8_384dim_10depth.ckpt',
207
+ 'Mel-Roformer-Denoise-Aufr33': 'denoise_mel_band_roformer_aufr33_sdr_27.9959.ckpt',
208
+ 'MelBand Roformer | Crowd Removal by aufr33': 'mel_band_roformer_crowd_aufr33_viperx_sdr_8.7144.ckpt',
209
+ 'MelBand Roformer | Aspiration by Sucial': 'aspiration_mel_band_roformer_sdr_18.9845.ckpt',
210
+ 'MelBand Roformer | Aspiration Less Aggressive by Sucial': 'aspiration_mel_band_roformer_less_aggr_sdr_18.1201.ckpt',
211
+ }
212
+ }
213
+
214
+
215
+ # ─── Model Download URLs ────────────────────────────────────────────────────
216
+ # Maps checkpoint filenames to their download URLs (config + checkpoint + optional .py)
217
+ # Extracted from main SESA project's MODEL_CONFIGS in model.py
218
+ MODEL_DOWNLOAD_URLS = {
219
+ # === Vocal Models ===
220
+ 'big_beta6x.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/big_beta6x.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/big_beta6x.ckpt'],
221
+ 'big_beta6.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/big_beta6.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/big_beta6.ckpt'],
222
+ 'kimmel_unwa_ft3_prev.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft3_prev.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft3_prev.ckpt'],
223
+ 'model_vocals_mdx23c_sdr_10.17.ckpt': ['https://huggingface.co/ASesYusuf1/MODELS/resolve/main/model_2_stem_full_band_8k.yaml', 'https://huggingface.co/ASesYusuf1/MODELS/resolve/main/model_vocals_mdx23c_sdr_10.17.ckpt'],
224
+ 'MelBandRoformer.ckpt': ['https://huggingface.co/KimberleyJSN/melbandroformer/resolve/main/MelBandRoformer.yaml', 'https://huggingface.co/KimberleyJSN/melbandroformer/resolve/main/MelBandRoformer.ckpt'],
225
+ 'model_bs_roformer_ep_317_sdr_12.9755.ckpt': ['https://raw.githubusercontent.com/TRvlvr/application_data/main/mdx_model_data/mdx_c_configs/model_bs_roformer_ep_317_sdr_12.9755.yaml', 'https://github.com/TRvlvr/model_repo/releases/download/all_public_uvr_models/model_bs_roformer_ep_317_sdr_12.9755.ckpt'],
226
+ 'model_bs_roformer_ep_368_sdr_12.9628.ckpt': ['https://raw.githubusercontent.com/TRvlvr/application_data/main/mdx_model_data/mdx_c_configs/model_bs_roformer_ep_368_sdr_12.9628.yaml', 'https://github.com/TRvlvr/model_repo/releases/download/all_public_uvr_models/model_bs_roformer_ep_368_sdr_12.9628.ckpt'],
227
+ 'BS-Roformer_LargeV1.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-1076/resolve/main/BS-Roformer_LargeV1.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-1076/resolve/main/BS-Roformer_LargeV1.ckpt'],
228
+ 'melband_roformer_big_beta4.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/melband_roformer_big_beta4.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/melband_roformer_big_beta4.ckpt'],
229
+ 'big_beta5e.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/big_beta5e.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-big/resolve/main/big_beta5e.ckpt'],
230
+ 'model_vocals_segm_models_sdr_9.77.ckpt': ['https://huggingface.co/ZFTurbo/MelBandRoformerVitLarge23/resolve/main/config_vocals_segm_models.yaml', 'https://huggingface.co/ZFTurbo/MelBandRoformerVitLarge23/resolve/main/model_vocals_segm_models_sdr_9.77.ckpt'],
231
+ 'kimmel_unwa_ft.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft.ckpt'],
232
+ 'kimmel_unwa_ft2.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft2.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft2.ckpt'],
233
+ 'kimmel_unwa_ft2_bleedless.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft2.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-kim/resolve/main/kimmel_unwa_ft2_bleedless.ckpt'],
234
+ 'mel_band_roformer_vocals_becruily.ckpt': ['https://huggingface.co/becruily/mel-band-roformer-vocals/resolve/main/config_vocals_becruily.yaml', 'https://huggingface.co/becruily/mel-band-roformer-vocals/resolve/main/mel_band_roformer_vocals_becruily.ckpt'],
235
+ 'bs_roformer_male_female_by_aufr33_sdr_7.2889.ckpt': ['https://huggingface.co/Sucial/Chorus_Male_Female_BS_RoFormer/resolve/main/bs_roformer_male_female_by_aufr33_sdr_7.2889.yaml', 'https://huggingface.co/Sucial/Chorus_Male_Female_BS_RoFormer/resolve/main/bs_roformer_male_female_by_aufr33_sdr_7.2889.ckpt'],
236
+ 'voc_gaboxBSR.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/bsroformers/bs_roformer_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/bsroformers/voc_gaboxBSR.ckpt'],
237
+ 'voc_gabox.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.ckpt'],
238
+ 'voc_Fv3.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_Fv3.ckpt'],
239
+ 'FullnessVocalModel.ckpt': ['https://huggingface.co/SYH99999/MelBandRoformerSYHFTV3Epsilon/resolve/main/config.yaml', 'https://huggingface.co/SYH99999/MelBandRoformerSYHFTV3Epsilon/resolve/main/FullnessVocalModel.ckpt'],
240
+ 'voc_fv4.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_fv4.ckpt'],
241
+ 'voc_fv5.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_fv5.ckpt'],
242
+ 'voc_fv6.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_fv6.ckpt'],
243
+ 'voc_fv7.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_fv7.ckpt'],
244
+ 'vocfv7beta1.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/vocfv7beta1.ckpt'],
245
+ 'vocfv7beta2.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/vocfv7beta2.ckpt'],
246
+ 'vocfv7beta3.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/voc_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/vocals/vocfv7beta3.ckpt'],
247
+ 'MelBandRoformerSYHFTV3Epsilon.ckpt': ['https://huggingface.co/SYH99999/MelBandRoformerSYHFTV3Epsilon/resolve/main/config.yaml', 'https://huggingface.co/SYH99999/MelBandRoformerSYHFTV3Epsilon/resolve/main/MelBandRoformerSYHFTV3Epsilon.ckpt'],
248
+ 'MelBandRoformerBigSYHFTV1.ckpt': ['https://huggingface.co/SYH99999/MelBandRoformerBigSYHFTV1/resolve/main/config.yaml', 'https://huggingface.co/SYH99999/MelBandRoformerBigSYHFTV1/resolve/main/MelBandRoformerBigSYHFTV1.ckpt'],
249
+ 'model_chorus_bs_roformer_ep_146_sdr_23.8613.ckpt': ['https://huggingface.co/Sucial/Chorus_Male_Female_BS_RoFormer/resolve/main/model_chorus_bs_roformer.yaml', 'https://huggingface.co/Sucial/Chorus_Male_Female_BS_RoFormer/resolve/main/model_chorus_bs_roformer_ep_146_sdr_23.8613.ckpt'],
250
+ 'model_chorus_bs_roformer_ep_267_sdr_24.1275.ckpt': ['https://huggingface.co/Sucial/Chorus_Male_Female_BS_RoFormer/resolve/main/model_chorus_bs_roformer.yaml', 'https://huggingface.co/Sucial/Chorus_Male_Female_BS_RoFormer/resolve/main/model_chorus_bs_roformer_ep_267_sdr_24.1275.ckpt'],
251
+ 'BS-Rofo-SW-Fixed.ckpt': ['https://huggingface.co/jarredou/BS-Rofo-SW-Fixed/resolve/main/BS-Rofo-SW-Fixed.yaml', 'https://huggingface.co/jarredou/BS-Rofo-SW-Fixed/resolve/main/BS-Rofo-SW-Fixed.ckpt'],
252
+ 'bs_roformer_voc_hyperacev2.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-HyperACEv2/resolve/main/bs_roformer_voc_hyperacev2.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-HyperACEv2/resolve/main/bs_roformer_voc_hyperacev2.ckpt'],
253
+ 'BS-Roformer-Resurrection.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-Resurrection/resolve/main/BS-Roformer-Resurrection.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-Resurrection/resolve/main/BS-Roformer-Resurrection.ckpt'],
254
+ 'bs_roformer_revive3e.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-Revive/resolve/main/bs_roformer_revive3e.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-Revive/resolve/main/bs_roformer_revive3e.ckpt'],
255
+ 'bs_roformer_revive2.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-Revive/resolve/main/bs_roformer_revive2.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-Revive/resolve/main/bs_roformer_revive2.ckpt'],
256
+ 'bs_roformer_revive.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-Revive/resolve/main/bs_roformer_revive.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-Revive/resolve/main/bs_roformer_revive.ckpt'],
257
+ 'karaoke_bs_roformer_anvuew.ckpt': ['https://huggingface.co/anvuew/karaoke_bs_roformer/resolve/main/karaoke_bs_roformer_anvuew.yaml', 'https://huggingface.co/anvuew/karaoke_bs_roformer/resolve/main/karaoke_bs_roformer_anvuew.ckpt'],
258
+ 'BS_ResurrectioN.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/bsroformers/BS_ResurrectioN.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/bsroformers/BS_ResurrectioN.ckpt'],
259
+ # === Instrumental Models ===
260
+ 'Neo_InstVFX.ckpt': ['https://huggingface.co/naitotomato/Neo_InstVFX/resolve/main/Neo_InstVFX.yaml', 'https://huggingface.co/naitotomato/Neo_InstVFX/resolve/main/Neo_InstVFX.ckpt'],
261
+ 'BS-Roformer-Resurrection-Inst.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-Resurrection/resolve/main/BS-Roformer-Resurrection-Inst.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-Resurrection/resolve/main/BS-Roformer-Resurrection-Inst.ckpt'],
262
+ 'bs_roformer_inst_hyperacev2.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-HyperACEv2/resolve/main/bs_roformer_inst_hyperacev2.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-HyperACEv2/resolve/main/bs_roformer_inst_hyperacev2.ckpt'],
263
+ 'bs_large_v2_inst.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-1076/resolve/main/BS-Roformer_LargeV1.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-1076/resolve/main/bs_large_v2_inst.ckpt'],
264
+ 'bs_roformer_fno.ckpt': ['https://huggingface.co/pcunwa/BS-Roformer-FNO/resolve/main/bs_roformer_fno.yaml', 'https://huggingface.co/pcunwa/BS-Roformer-FNO/resolve/main/bs_roformer_fno.ckpt'],
265
+ 'rifforge_full_sdr_14.2436.ckpt': ['https://huggingface.co/meskvlla33/rifforge/resolve/main/rifforge_config.yaml', 'https://huggingface.co/meskvlla33/rifforge/resolve/main/rifforge_full_sdr_14.2436.ckpt'],
266
+ 'melband_roformer_inst_v1.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/config_melbandroformer_inst.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/melband_roformer_inst_v1.ckpt'],
267
+ 'inst_v1e_plus.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/config_melbandroformer_inst.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/inst_v1e_plus.ckpt'],
268
+ 'inst_v1_plus_test.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/config_melbandroformer_inst.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/inst_v1_plus_test.ckpt'],
269
+ 'melband_roformer_inst_v2.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/config_melbandroformer_inst.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/melband_roformer_inst_v2.ckpt'],
270
+ 'mel_band_roformer_instrumental_becruily.ckpt': ['https://huggingface.co/becruily/mel-band-roformer-instrumental/resolve/main/config_instrumental_becruily.yaml', 'https://huggingface.co/becruily/mel-band-roformer-instrumental/resolve/main/mel_band_roformer_instrumental_becruily.ckpt'],
271
+ 'inst_v1e.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/config_melbandroformer_inst.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-Inst/resolve/main/inst_v1e.ckpt'],
272
+ 'inst_gabox.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.ckpt'],
273
+ 'inst_gaboxBv1.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gaboxBv1.ckpt'],
274
+ 'inst_gaboxBv2.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gaboxBv2.ckpt'],
275
+ 'inst_gaboxFv2.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gaboxFv2.ckpt'],
276
+ 'inst_gaboxFv3.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gaboxFv3.ckpt'],
277
+ 'intrumental_gabox.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/intrumental_gabox.ckpt'],
278
+ 'inst_Fv4Noise.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_Fv4Noise.ckpt'],
279
+ 'inst_Fv4.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_Fv4.ckpt'],
280
+ 'INSTV5.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/INSTV5.ckpt'],
281
+ 'INSTV5N.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/INSTV5N.ckpt'],
282
+ 'INSTV6N.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/INSTV6N.ckpt'],
283
+ 'Inst_GaboxV7.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/Inst_GaboxV7.ckpt'],
284
+ 'INSTV7N.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/INSTV7N.ckpt'],
285
+ 'inst_fv7b.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/experimental/inst_fv7b.ckpt'],
286
+ 'Inst_GaboxFv7z.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/Inst_GaboxFv7z.ckpt'],
287
+ 'Inst_GaboxFv9.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/Inst_GaboxFv9.ckpt'],
288
+ 'inst_gaboxFlowersV10.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/v10.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gaboxFlowersV10.ckpt'],
289
+ 'Inst_FV8b.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/experimental/Inst_FV8b.ckpt'],
290
+ 'Inst_Fv8.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/experimental/Inst_Fv8.ckpt'],
291
+ 'Inst_GaboxFv8.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/Inst_GaboxFv8.ckpt'],
292
+ 'inst_gaboxFv1.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gaboxFv1.ckpt'],
293
+ 'INSTV6.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/INSTV6.ckpt'],
294
+ 'gaboxFv1.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gabox.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/inst_gaboxFv1.ckpt'],
295
+ # === InstVoc Duality ===
296
+ 'melband_roformer_instvoc_duality_v1.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-InstVoc-Duality/resolve/main/melband_roformer_instvoc_duality_v1.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-InstVoc-Duality/resolve/main/melband_roformer_instvoc_duality_v1.ckpt'],
297
+ 'melband_roformer_instvox_duality_v2.ckpt': ['https://huggingface.co/pcunwa/Mel-Band-Roformer-InstVoc-Duality/resolve/main/melband_roformer_instvox_duality_v2.yaml', 'https://huggingface.co/pcunwa/Mel-Band-Roformer-InstVoc-Duality/resolve/main/melband_roformer_instvox_duality_v2.ckpt'],
298
+ # === 4-Stem Models ===
299
+ 'scnet_checkpoint_musdb18.ckpt': ['https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v.1.0.6/config_musdb18_scnet.yaml', 'https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v.1.0.6/scnet_checkpoint_musdb18.ckpt'],
300
+ 'model_scnet_ep_54_sdr_9.8051.ckpt': ['https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.13/config_musdb18_scnet_xl.yaml', 'https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.13/model_scnet_ep_54_sdr_9.8051.ckpt'],
301
+ 'SCNet-large_starrytong_fixed.ckpt': ['https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.9/config_musdb18_scnet_large_starrytong.yaml', 'https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.9/SCNet-large_starrytong_fixed.ckpt'],
302
+ 'model_bs_roformer_ep_17_sdr_9.6568.ckpt': ['https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.12/config_bs_roformer_384_8_2_485100.yaml', 'https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.12/model_bs_roformer_ep_17_sdr_9.6568.ckpt'],
303
+ 'MelBandRoformer4StemFTLarge.ckpt': ['https://huggingface.co/SYH99999/MelBandRoformer4StemFTLarge/resolve/main/config.yaml', 'https://huggingface.co/SYH99999/MelBandRoformer4StemFTLarge/resolve/main/MelBandRoformer4StemFTLarge.ckpt'],
304
+ # === Denoise Models ===
305
+ 'denoise_mel_band_roformer_aufr33_sdr_27.9959.ckpt': ['https://huggingface.co/jarredou/aufr33_MelBand_Denoise/resolve/main/model_mel_band_roformer_denoise.yaml', 'https://huggingface.co/jarredou/aufr33_MelBand_Denoise/resolve/main/denoise_mel_band_roformer_aufr33_sdr_27.9959.ckpt'],
306
+ 'denoise_mel_band_roformer_aufr33_aggr_sdr_27.9768.ckpt': ['https://huggingface.co/jarredou/aufr33_MelBand_Denoise/resolve/main/model_mel_band_roformer_denoise.yaml', 'https://huggingface.co/jarredou/aufr33_MelBand_Denoise/resolve/main/denoise_mel_band_roformer_aufr33_aggr_sdr_27.9768.ckpt'],
307
+ 'denoisedebleed.ckpt': ['https://huggingface.co/poiqazwsx/melband-roformer-denoise/resolve/main/model_mel_band_roformer_denoise.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/instrumental/denoisedebleed.ckpt'],
308
+ 'bleed_suppressor_v1.ckpt': ['https://huggingface.co/ASesYusuf1/MODELS/resolve/main/config_bleed_suppressor_v1.yaml', 'https://huggingface.co/ASesYusuf1/MODELS/resolve/main/bleed_suppressor_v1.ckpt'],
309
+ # === Dereverb Models ===
310
+ 'dereverb_mdx23c_sdr_6.9096.ckpt': ['https://huggingface.co/jarredou/aufr33_jarredou_MDXv3_DeReverb/resolve/main/config_dereverb_mdx23c.yaml', 'https://huggingface.co/jarredou/aufr33_jarredou_MDXv3_DeReverb/resolve/main/dereverb_mdx23c_sdr_6.9096.ckpt'],
311
+ 'dereverb_mel_band_roformer_anvuew_sdr_19.1729.ckpt': ['https://huggingface.co/anvuew/dereverb_mel_band_roformer/resolve/main/dereverb_mel_band_roformer_anvuew.yaml', 'https://huggingface.co/anvuew/dereverb_mel_band_roformer/resolve/main/dereverb_mel_band_roformer_anvuew_sdr_19.1729.ckpt'],
312
+ 'dereverb-echo_mel_band_roformer_sdr_10.0169.ckpt': ['https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/config_dereverb-echo_mel_band_roformer.yaml', 'https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/dereverb-echo_mel_band_roformer_sdr_10.0169.ckpt'],
313
+ 'dereverb_mel_band_roformer_less_aggressive_anvuew_sdr_18.8050.ckpt': ['https://huggingface.co/anvuew/dereverb_mel_band_roformer/resolve/main/dereverb_mel_band_roformer_anvuew.yaml', 'https://huggingface.co/anvuew/dereverb_mel_band_roformer/resolve/main/dereverb_mel_band_roformer_less_aggressive_anvuew_sdr_18.8050.ckpt'],
314
+ 'dereverb_mel_band_roformer_mono_anvuew_sdr_20.4029.ckpt': ['https://huggingface.co/anvuew/dereverb_mel_band_roformer/resolve/main/dereverb_mel_band_roformer_anvuew.yaml', 'https://huggingface.co/anvuew/dereverb_mel_band_roformer/resolve/main/dereverb_mel_band_roformer_mono_anvuew_sdr_20.4029.ckpt'],
315
+ 'dereverb-echo_128_4_4_mel_band_roformer_sdr_dry_12.4235.ckpt': ['https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/config_dereverb-echo_128_4_4_mel_band_roformer.yaml', 'https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/dereverb-echo_128_4_4_mel_band_roformer_sdr_dry_12.4235.ckpt'],
316
+ 'dereverb_echo_mbr_v2_sdr_dry_13.4843.ckpt': ['https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/config_dereverb_echo_mbr_v2.yaml', 'https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/dereverb_echo_mbr_v2_sdr_dry_13.4843.ckpt'],
317
+ 'de_big_reverb_mbr_ep_362.ckpt': ['https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/config_dereverb_echo_mbr_v2.yaml', 'https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/de_big_reverb_mbr_ep_362.ckpt'],
318
+ 'de_super_big_reverb_mbr_ep_346.ckpt': ['https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/config_dereverb_echo_mbr_v2.yaml', 'https://huggingface.co/Sucial/Dereverb-Echo_Mel_Band_Roformer/resolve/main/de_super_big_reverb_mbr_ep_346.ckpt'],
319
+ 'dereverb_room_anvuew_sdr_13.7432.ckpt': ['https://huggingface.co/anvuew/dereverb_room/resolve/main/dereverb_room_anvuew.yaml', 'https://huggingface.co/anvuew/dereverb_room/resolve/main/dereverb_room_anvuew_sdr_13.7432.ckpt'],
320
+ # === Karaoke Models ===
321
+ 'mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.ckpt': ['https://huggingface.co/jarredou/aufr33-viperx-karaoke-melroformer-model/resolve/main/config_mel_band_roformer_karaoke.yaml', 'https://huggingface.co/jarredou/aufr33-viperx-karaoke-melroformer-model/resolve/main/mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.ckpt'],
322
+ 'Karaoke_GaboxV1.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/karaoke/karaokegabox_1750911344.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/melbandroformers/karaoke/Karaoke_GaboxV1.ckpt'],
323
+ 'bs_karaoke_gabox_IS.ckpt': ['https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/bsroformers/karaoke_bs_roformer.yaml', 'https://huggingface.co/GaboxR67/MelBandRoformers/resolve/main/bsroformers/bs_karaoke_gabox_IS.ckpt'],
324
+ 'bs_roformer_karaoke_frazer_becruily.ckpt': ['https://huggingface.co/becruily/bs-roformer-karaoke/resolve/main/config_karaoke_frazer_becruily.yaml', 'https://huggingface.co/becruily/bs-roformer-karaoke/resolve/main/bs_roformer_karaoke_frazer_becruily.ckpt'],
325
+ 'mel_band_roformer_karaoke_becruily.ckpt': ['https://huggingface.co/becruily/mel-band-roformer-karaoke/resolve/main/config_karaoke_becruily.yaml', 'https://huggingface.co/becruily/mel-band-roformer-karaoke/resolve/main/mel_band_roformer_karaoke_becruily.ckpt'],
326
+ # === Other / General Purpose Models ===
327
+ 'model_bs_roformer_ep_937_sdr_10.5309.ckpt': ['https://raw.githubusercontent.com/TRvlvr/application_data/main/mdx_model_data/mdx_c_configs/model_bs_roformer_ep_937_sdr_10.5309.yaml', 'https://github.com/TRvlvr/model_repo/releases/download/all_public_uvr_models/model_bs_roformer_ep_937_sdr_10.5309.ckpt'],
328
+ 'mel_band_roformer_crowd_aufr33_viperx_sdr_8.7144.ckpt': ['https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v.1.0.4/model_mel_band_roformer_crowd.yaml', 'https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v.1.0.4/mel_band_roformer_crowd_aufr33_viperx_sdr_8.7144.ckpt'],
329
+ 'model_bandit_plus_dnr_sdr_11.47.chpt': ['https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v.1.0.3/config_dnr_bandit_bsrnn_multi_mus64.yaml', 'https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v.1.0.3/model_bandit_plus_dnr_sdr_11.47.chpt'],
330
+ 'checkpoint-multi_state_dict.ckpt': ['https://raw.githubusercontent.com/ZFTurbo/Music-Source-Separation-Training/refs/heads/main/configs/config_dnr_bandit_v2_mus64.yaml', 'https://huggingface.co/jarredou/banditv2_state_dicts_only/resolve/main/checkpoint-multi_state_dict.ckpt'],
331
+ 'aufr33-jarredou_DrumSep_model_mdx23c_ep_141_sdr_10.8059.ckpt': ['https://github.com/jarredou/models/releases/download/aufr33-jarredou_MDX23C_DrumSep_model_v0.1/aufr33-jarredou_DrumSep_model_mdx23c_ep_141_sdr_10.8059.yaml', 'https://github.com/jarredou/models/releases/download/aufr33-jarredou_MDX23C_DrumSep_model_v0.1/aufr33-jarredou_DrumSep_model_mdx23c_ep_141_sdr_10.8059.ckpt'],
332
+ 'bs_hyperace.ckpt': [('https://huggingface.co/pcunwa/BS-Roformer-HyperACE/resolve/main/config.yaml', 'config_hyperace.yaml'), 'https://huggingface.co/pcunwa/BS-Roformer-HyperACE/resolve/main/bs_hyperace.ckpt'],
333
+ 'becruily_deux.ckpt': ['https://huggingface.co/becruily/mel-band-roformer-deux/resolve/main/config_deux_becruily.yaml', 'https://huggingface.co/becruily/mel-band-roformer-deux/resolve/main/becruily_deux.ckpt'],
334
+ 'becruily_guitar.ckpt': ['https://huggingface.co/becruily/mel-band-roformer-guitar/resolve/main/config_guitar_becruily.yaml', 'https://huggingface.co/becruily/mel-band-roformer-guitar/resolve/main/becruily_guitar.ckpt'],
335
+ 'aspiration_mel_band_roformer_sdr_18.9845.ckpt': ['https://huggingface.co/Sucial/Aspiration_Mel_Band_Roformer/resolve/main/config_aspiration_mel_band_roformer.yaml', 'https://huggingface.co/Sucial/Aspiration_Mel_Band_Roformer/resolve/main/aspiration_mel_band_roformer_sdr_18.9845.ckpt'],
336
+ 'model_mdx23c_ep_271_l1_freq_72.2383.ckpt': ['https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.10/config_mdx23c_similarity.yaml', 'https://github.com/ZFTurbo/Music-Source-Separation-Training/releases/download/v1.0.10/model_mdx23c_ep_271_l1_freq_72.2383.ckpt'],
337
+ 'model_mel_band_roformer_ep_72_sdr_3.2232.ckpt': ['https://huggingface.co/listra92/MyModels/resolve/main/misc/config_mel_band_roformer_Lead_Rhythm_Guitar.yaml', 'https://huggingface.co/listra92/MyModels/resolve/main/misc/model_mel_band_roformer_ep_72_sdr_3.2232.ckpt'],
338
+ 'last_bs_roformer.ckpt': ['https://huggingface.co/listra92/MyModels/resolve/main/misc/config.yaml', 'https://huggingface.co/listra92/MyModels/resolve/main/misc/last_bs_roformer.ckpt'],
339
+ 'bs_roformer_4stems_ft.pth': ['https://huggingface.co/SYH99999/bs_roformer_4stems_ft/resolve/main/config.yaml', 'https://huggingface.co/SYH99999/bs_roformer_4stems_ft/resolve/main/bs_roformer_4stems_ft.pth'],
340
+ 'checkpoint-eng_state_dict.ckpt': ['https://huggingface.co/jarredou/banditv2_state_dicts_only/resolve/main/config_dnr_bandit_v2_mus64.yaml', 'https://huggingface.co/jarredou/banditv2_state_dicts_only/resolve/main/checkpoint-eng_state_dict.ckpt'],
341
+ }
342
+
343
+ # Custom model URLs for models needing a custom .py file
344
+ MODEL_CUSTOM_PY_URLS = {
345
+ 'bs_hyperace.ckpt': 'https://huggingface.co/pcunwa/BS-Roformer-HyperACE/resolve/main/bs_roformer.py',
346
+ }
347
+
348
+
349
+ def ensure_model_files_downloaded(checkpoint_filename, model_file_dir=None):
350
+ """Pre-download all files for a model (checkpoint + config + optional .py) before loading.
351
+
352
+ This must be called BEFORE separator.load_model() so the bypass in separator.py
353
+ can find the model file locally and skip the registry check.
354
+
355
+ Args:
356
+ checkpoint_filename: The checkpoint filename (e.g. 'big_beta6x.ckpt')
357
+ model_file_dir: Target directory (defaults to MODEL_CACHE_DIR)
358
+
359
+ Returns: (success: bool, message: str)
360
+ """
361
+ target_dir = model_file_dir or MODEL_CACHE_DIR
362
+ os.makedirs(target_dir, exist_ok=True)
363
+
364
+ urls = MODEL_DOWNLOAD_URLS.get(checkpoint_filename)
365
+ if not urls:
366
+ # Not in our URL registry β€” might be a base model handled by audio-separator
367
+ logger.debug(f"No download URLs for {checkpoint_filename}, assuming base model")
368
+ return True, "Base model (no pre-download needed)"
369
+
370
+ for url_entry in urls:
371
+ # Handle (url, target_filename) tuples for files that need renaming
372
+ if isinstance(url_entry, tuple):
373
+ url, target_name = url_entry
374
+ else:
375
+ url = url_entry
376
+ target_name = os.path.basename(urllib.parse.urlparse(url).path.split('?')[0])
377
+
378
+ success, result = download_model_from_url(url, target_name, target_dir)
379
+ if not success:
380
+ return False, f"Failed to download {target_name}: {result}"
381
+
382
+ # Download custom .py if needed
383
+ py_url = MODEL_CUSTOM_PY_URLS.get(checkpoint_filename)
384
+ if py_url:
385
+ py_name = os.path.basename(urllib.parse.urlparse(py_url).path.split('?')[0])
386
+ success, result = download_model_from_url(py_url, py_name, target_dir)
387
+ if not success:
388
+ logger.warning(f"Failed to download custom .py: {result}")
389
+
390
+ return True, f"All files downloaded for {checkpoint_filename}"
391
+
392
+
393
+ # ─── URL Helpers ─────────────────────────────────────────────────────────────
394
+
395
+ def fix_huggingface_url(url):
396
+ """Auto-fix Hugging Face URLs to use /resolve/main/ for direct download."""
397
+ if not url:
398
+ return url
399
+ url = url.strip()
400
+ # Convert 'blob' to 'resolve' for HF URLs
401
+ if 'huggingface.co' in url and '/blob/' in url:
402
+ url = url.replace('/blob/', '/resolve/')
403
+ return url
404
+
405
+
406
+ def download_model_from_url(url, target_filename=None, target_dir=None):
407
+ """Download a model file from URL to the model cache directory.
408
+
409
+ Args:
410
+ url: Direct download URL
411
+ target_filename: Optional filename (auto-detected from URL if not provided)
412
+ target_dir: Target directory (defaults to MODEL_CACHE_DIR)
413
+
414
+ Returns: (success: bool, file_path: str or error message)
415
+ """
416
+ if not url or not url.strip():
417
+ return False, "URL is required"
418
+
419
+ url = fix_huggingface_url(url.strip())
420
+ save_dir = target_dir or MODEL_CACHE_DIR
421
+ os.makedirs(save_dir, exist_ok=True)
422
+
423
+ if not target_filename:
424
+ parsed = urllib.parse.urlparse(url)
425
+ target_filename = os.path.basename(parsed.path.split('?')[0])
426
+
427
+ file_path = os.path.join(save_dir, target_filename)
428
+
429
+ # Skip if already exists
430
+ if os.path.exists(file_path) and os.path.getsize(file_path) > 0:
431
+ logger.info(f"Model file already exists: {file_path}")
432
+ return True, file_path
433
+
434
+ try:
435
+ logger.info(f"Downloading: {url} β†’ {file_path}")
436
+ response = requests.get(url, stream=True, timeout=300)
437
+ response.raise_for_status()
438
+
439
+ total_size = int(response.headers.get('content-length', 0))
440
+ downloaded = 0
441
+
442
+ with open(file_path, 'wb') as f:
443
+ for chunk in response.iter_content(chunk_size=8192):
444
+ f.write(chunk)
445
+ downloaded += len(chunk)
446
+
447
+ if os.path.exists(file_path) and os.path.getsize(file_path) > 0:
448
+ size_mb = os.path.getsize(file_path) / (1024 * 1024)
449
+ logger.info(f"βœ… Downloaded: {target_filename} ({size_mb:.1f} MB)")
450
+ return True, file_path
451
+ else:
452
+ return False, f"Download failed: file is empty"
453
+ except Exception as e:
454
+ # Clean up partial download
455
+ if os.path.exists(file_path):
456
+ try:
457
+ os.remove(file_path)
458
+ except:
459
+ pass
460
+ return False, f"Download error: {str(e)}"
461
+
462
+
463
+ # ─── Custom Model Management ────────────────────────────────────────────────
464
+
465
+ def load_custom_models():
466
+ """Load custom models from JSON file.
467
+
468
+ Custom models are stored as:
469
+ {
470
+ "Model Name": {
471
+ "checkpoint_url": "https://...",
472
+ "config_url": "https://...", # optional
473
+ "custom_model_url": "https://...", # optional .py file
474
+ "checkpoint_filename": "model.ckpt"
475
+ }
476
+ }
477
+ """
478
+ if os.path.exists(CUSTOM_MODELS_FILE):
479
+ try:
480
+ with open(CUSTOM_MODELS_FILE, 'r', encoding='utf-8') as f:
481
+ return json.load(f)
482
+ except Exception as e:
483
+ logger.error(f"Error loading custom models: {e}")
484
+ return {}
485
+
486
+
487
+ def save_custom_models(models):
488
+ """Save custom models to JSON file."""
489
+ os.makedirs(os.path.dirname(os.path.abspath(CUSTOM_MODELS_FILE)), exist_ok=True)
490
+ with open(CUSTOM_MODELS_FILE, 'w', encoding='utf-8') as f:
491
+ json.dump(models, f, indent=2, ensure_ascii=False)
492
+
493
+
494
+ def add_custom_model(model_name, checkpoint_url, config_url=None, custom_model_url=None):
495
+ """Add a custom model with download URLs.
496
+
497
+ Args:
498
+ model_name: Display name for the model
499
+ checkpoint_url: Direct URL to the .ckpt/.pth checkpoint file
500
+ config_url: Optional URL to the config .yaml file
501
+ custom_model_url: Optional URL to a custom .py model file
502
+
503
+ Returns: (success: bool, message: str)
504
+ """
505
+ if not model_name or not model_name.strip():
506
+ return False, "Model name is required"
507
+ if not checkpoint_url or not checkpoint_url.strip():
508
+ return False, "Checkpoint URL is required"
509
+
510
+ model_name = model_name.strip()
511
+ checkpoint_url = fix_huggingface_url(checkpoint_url.strip())
512
+ config_url = fix_huggingface_url(config_url.strip()) if config_url and config_url.strip() else None
513
+ custom_model_url = fix_huggingface_url(custom_model_url.strip()) if custom_model_url and custom_model_url.strip() else None
514
+
515
+ # Extract checkpoint filename from URL
516
+ parsed = urllib.parse.urlparse(checkpoint_url)
517
+ checkpoint_filename = os.path.basename(parsed.path.split('?')[0])
518
+ if not checkpoint_filename:
519
+ return False, "Could not extract filename from checkpoint URL"
520
+
521
+ models = load_custom_models()
522
+ if model_name in models:
523
+ return False, f"Model '{model_name}' already exists"
524
+
525
+ models[model_name] = {
526
+ 'checkpoint_url': checkpoint_url,
527
+ 'config_url': config_url,
528
+ 'custom_model_url': custom_model_url,
529
+ 'checkpoint_filename': checkpoint_filename,
530
+ }
531
+ save_custom_models(models)
532
+ return True, f"βœ… Model '{model_name}' added successfully"
533
+
534
+
535
+ def delete_custom_model(model_name):
536
+ """Delete a custom model and optionally its cached files.
537
+
538
+ Returns: (success: bool, message: str)
539
+ """
540
+ models = load_custom_models()
541
+ if model_name not in models:
542
+ return False, f"Model '{model_name}' not found"
543
+
544
+ # Try to clean up downloaded files
545
+ model_config = models[model_name]
546
+ if isinstance(model_config, dict):
547
+ ckpt_file = os.path.join(MODEL_CACHE_DIR, model_config.get('checkpoint_filename', ''))
548
+ if os.path.exists(ckpt_file):
549
+ try:
550
+ os.remove(ckpt_file)
551
+ logger.info(f"Deleted cached model file: {ckpt_file}")
552
+ except Exception as e:
553
+ logger.warning(f"Could not delete cached file: {e}")
554
+
555
+ del models[model_name]
556
+ save_custom_models(models)
557
+ return True, f"βœ… Model '{model_name}' deleted"
558
+
559
+
560
+ def get_custom_models_list():
561
+ """Get list of custom models as [(name, url)] tuples."""
562
+ models = load_custom_models()
563
+ result = []
564
+ for name, config in models.items():
565
+ if isinstance(config, dict):
566
+ result.append((name, config.get('checkpoint_url', '')))
567
+ else:
568
+ result.append((name, str(config)))
569
+ return result
570
+
571
+
572
+ def ensure_custom_model_downloaded(model_name):
573
+ """Download custom model files if not already in cache.
574
+
575
+ Returns: (success: bool, checkpoint_filename: str or error)
576
+ """
577
+ models = load_custom_models()
578
+ if model_name not in models:
579
+ return False, f"Custom model '{model_name}' not found"
580
+
581
+ config = models[model_name]
582
+ if not isinstance(config, dict):
583
+ # Legacy format: just a filename string
584
+ return True, str(config)
585
+
586
+ # Download checkpoint
587
+ ckpt_url = config.get('checkpoint_url')
588
+ ckpt_filename = config.get('checkpoint_filename')
589
+ if ckpt_url:
590
+ success, result = download_model_from_url(ckpt_url, ckpt_filename)
591
+ if not success:
592
+ return False, f"Failed to download checkpoint: {result}"
593
+
594
+ # Download config if provided
595
+ config_url = config.get('config_url')
596
+ if config_url:
597
+ config_filename = f"config_{model_name.replace(' ', '_').lower()}.yaml"
598
+ success, result = download_model_from_url(config_url, config_filename)
599
+ if not success:
600
+ logger.warning(f"Failed to download config: {result}")
601
+
602
+ # Download custom .py if provided
603
+ py_url = config.get('custom_model_url')
604
+ if py_url:
605
+ py_filename = os.path.basename(urllib.parse.urlparse(py_url).path.split('?')[0])
606
+ success, result = download_model_from_url(py_url, py_filename)
607
+ if not success:
608
+ logger.warning(f"Failed to download custom .py: {result}")
609
+
610
+ return True, ckpt_filename
611
+
612
+
613
+ def get_all_models():
614
+ """Get EXTENDED_MODELS merged with custom models under 'Custom Models' category.
615
+
616
+ Custom models are shown as {name: checkpoint_filename} for dropdown compatibility.
617
+ """
618
+ all_models = dict(EXTENDED_MODELS)
619
+ custom = load_custom_models()
620
+ if custom:
621
+ # Flatten custom models to {name: checkpoint_filename} for UI dropdowns
622
+ custom_flat = {}
623
+ for name, config in custom.items():
624
+ if isinstance(config, dict):
625
+ custom_flat[name] = config.get('checkpoint_filename', '')
626
+ else:
627
+ custom_flat[name] = str(config)
628
+ all_models["Custom Models"] = custom_flat
629
+ return all_models
630
+
631
+
632
+ def get_model_choices(category):
633
+ """Get model choices for a given category."""
634
+ all_models = get_all_models()
635
+ return list(all_models.get(category, {}).keys())
636
+
637
+
638
+ def get_categories():
639
+ """Get all available categories."""
640
+ return list(get_all_models().keys())
641
+
642
+
643
+ def find_model_filename(model_key):
644
+ """Find checkpoint filename for a given model display name.
645
+
646
+ For custom models, also ensures the file is downloaded to cache.
647
+ """
648
+ # Check built-in models first
649
+ for category, models in EXTENDED_MODELS.items():
650
+ if model_key in models:
651
+ return models[model_key]
652
+
653
+ # Check custom models
654
+ custom = load_custom_models()
655
+ if model_key in custom:
656
+ config = custom[model_key]
657
+ if isinstance(config, dict):
658
+ # Auto-download from URL if needed
659
+ success, result = ensure_custom_model_downloaded(model_key)
660
+ if success:
661
+ return result
662
+ else:
663
+ logger.error(f"Failed to download custom model '{model_key}': {result}")
664
+ return None
665
+ else:
666
+ return str(config)
667
+
668
+ return None
669
+
670
+
671
+ # ─── Audio Segmentation Helpers ──────────────────────────────────────────────
672
+
673
+ def get_audio_duration(file_path):
674
+ """Get audio duration in seconds using ffprobe."""
675
+ try:
676
+ result = subprocess.run(
677
+ ['ffprobe', '-v', 'quiet', '-show_entries', 'format=duration',
678
+ '-of', 'default=noprint_wrappers=1:nokey=1', file_path],
679
+ capture_output=True, text=True, timeout=30
680
+ )
681
+ if result.returncode == 0 and result.stdout.strip():
682
+ return float(result.stdout.strip())
683
+ except Exception as e:
684
+ logger.warning(f"Could not get duration for {file_path}: {e}")
685
+ return 0
686
+
687
+
688
+ def split_audio_segments(input_path, output_dir, segment_duration=SEGMENT_DURATION):
689
+ """Split audio into segments of given duration using ffmpeg.
690
+
691
+ Returns list of segment file paths.
692
+ """
693
+ os.makedirs(output_dir, exist_ok=True)
694
+ base_name = os.path.splitext(os.path.basename(input_path))[0]
695
+
696
+ total_duration = get_audio_duration(input_path)
697
+ if total_duration <= 0:
698
+ logger.error(f"Could not determine duration for {input_path}")
699
+ return []
700
+
701
+ segments = []
702
+ seg_idx = 0
703
+ start_time = 0
704
+
705
+ while start_time < total_duration:
706
+ seg_filename = f"{base_name}_seg{seg_idx:03d}.wav"
707
+ seg_path = os.path.join(output_dir, seg_filename)
708
+
709
+ cmd = [
710
+ 'ffmpeg', '-y', '-i', input_path,
711
+ '-ss', str(start_time),
712
+ '-t', str(segment_duration),
713
+ '-acodec', 'pcm_s16le',
714
+ '-ar', '44100', '-ac', '2',
715
+ seg_path
716
+ ]
717
+
718
+ try:
719
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
720
+ if result.returncode == 0 and os.path.exists(seg_path):
721
+ segments.append(seg_path)
722
+ logger.info(f"βœ… Segment {seg_idx}: {seg_filename}")
723
+ else:
724
+ logger.warning(f"⚠️ Failed to create segment {seg_idx}: {result.stderr[-200:]}")
725
+ except Exception as e:
726
+ logger.warning(f"⚠️ Error creating segment {seg_idx}: {e}")
727
+
728
+ seg_idx += 1
729
+ start_time += segment_duration
730
+
731
+ return segments
732
+
733
+
734
+ def concatenate_audio_files(file_list, output_path, output_format='wav'):
735
+ """Concatenate multiple audio files into one using ffmpeg concat.
736
+
737
+ Returns: output file path or None on failure.
738
+ """
739
+ if not file_list:
740
+ return None
741
+
742
+ if len(file_list) == 1:
743
+ shutil.copy(file_list[0], output_path)
744
+ return output_path
745
+
746
+ concat_list = output_path + '.concat.txt'
747
+ try:
748
+ with open(concat_list, 'w') as f:
749
+ for fp in file_list:
750
+ f.write(f"file '{fp}'\n")
751
+
752
+ cmd = [
753
+ 'ffmpeg', '-y', '-f', 'concat', '-safe', '0',
754
+ '-i', concat_list, '-c', 'copy', output_path
755
+ ]
756
+
757
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
758
+
759
+ if result.returncode == 0 and os.path.exists(output_path):
760
+ return output_path
761
+ else:
762
+ logger.error(f"Concatenation failed: {result.stderr[-300:]}")
763
+ return None
764
+ except Exception as e:
765
+ logger.error(f"Concatenation error: {e}")
766
+ return None
767
+ finally:
768
+ if os.path.exists(concat_list):
769
+ try:
770
+ os.remove(concat_list)
771
+ except:
772
+ pass
773
+
774
+
775
+ def concatenate_segment_outputs(output_dir, output_format='wav'):
776
+ """After processing segments, concatenate per-instrument outputs.
777
+
778
+ Each segment produces files like: seg000_vocals.wav, seg000_instrumental.wav
779
+ This function concatenates all vocal segments, all instrumental segments, etc.
780
+ """
781
+ # Order matters: check longer names first to avoid substring conflicts
782
+ instrument_types = ['instrumental', 'instrument', 'phaseremix', 'vocals', 'drum',
783
+ 'bass', 'other', 'effects', 'speech', 'music', 'dry',
784
+ 'male', 'female', 'bleed', 'karaoke']
785
+
786
+ output_files = sorted(os.listdir(output_dir))
787
+ already_processed = set()
788
+
789
+ for inst_type in instrument_types:
790
+ # Regex exact suffix matching: _instrument. but NOT _instrumental.
791
+ pattern = re.compile(r'_' + re.escape(inst_type) + r'\.', re.IGNORECASE)
792
+
793
+ inst_files = sorted([
794
+ os.path.join(output_dir, f) for f in output_files
795
+ if pattern.search(f) and '_seg' in f.lower() and f not in already_processed
796
+ ])
797
+
798
+ if len(inst_files) <= 1:
799
+ continue
800
+
801
+ for f in inst_files:
802
+ already_processed.add(os.path.basename(f))
803
+
804
+ logger.info(f"πŸ”— Concatenating {len(inst_files)} {inst_type} segments...")
805
+
806
+ # Determine output filename (remove _segXXX from first segment's name)
807
+ first_name = os.path.basename(inst_files[0])
808
+ concat_output_name = re.sub(r'_seg\d+', '', first_name)
809
+ concat_output = os.path.join(output_dir, concat_output_name)
810
+
811
+ result = concatenate_audio_files(inst_files, concat_output, output_format)
812
+
813
+ if result:
814
+ # Remove segment files
815
+ for seg_file in inst_files:
816
+ try:
817
+ os.remove(seg_file)
818
+ except:
819
+ pass
820
+ logger.info(f"βœ… Concatenated {inst_type}: {os.path.basename(concat_output)}")
821
+ else:
822
+ logger.warning(f"⚠️ Concatenation failed for {inst_type}")