import gradio as gr import json import sys sys.path.insert(0, ".") # Models are loaded at startup, not per-request. # Cold start time on CPU Basic: 3–8 min. This cost is paid once per Space # wake-up, not on every extraction call. from feature_extractor_cpu import extract_mir_features, ENG_DIMS from clap_embedder import get_clap_embedding print("[startup] All models loaded. Space ready.") def extract(audio_file_path: str) -> str: """ Receives a local file path from Gradio (gr.File type='filepath'). Returns JSON with keys: l3, clap, eng_raw, true_peak_dbfs. Any exception propagates to gradio_client as a job error — no silent failures. """ if audio_file_path is None: raise ValueError("No audio file received.") l3, eng_raw, true_peak = extract_mir_features(audio_file_path) clap = get_clap_embedding(audio_file_path) if len(eng_raw) != len(ENG_DIMS): raise ValueError( f"eng_raw length mismatch: got {len(eng_raw)}, expected {len(ENG_DIMS)}" ) return json.dumps({ "l3": l3.tolist(), "clap": clap.tolist(), "eng_raw": eng_raw.tolist(), "true_peak_dbfs": float(true_peak), }) # demo.queue() is REQUIRED — not optional. # Without it, Gradio's async event loop kills any function that runs longer # than a few seconds. CPU demucs with shifts=5 runs for 33–136 min per track. # queue() enables proper long-running background job execution. demo = gr.Interface( fn=extract, inputs=gr.File(label="Audio file (.mp3 or .wav)", type="filepath"), outputs=gr.Textbox(label="Feature JSON"), title="Playlisting Feature Extractor (CPU Basic)", ) demo.queue(max_size=50) if __name__ == "__main__": demo.launch(show_error=True)