jayman commited on
Commit
6ad7f1b
·
verified ·
1 Parent(s): c570932

Upload 6 files

Browse files
Files changed (2) hide show
  1. app.py +121 -29
  2. requirements.txt +1 -0
app.py CHANGED
@@ -7,6 +7,7 @@ import threading
7
  import time
8
  import uuid
9
  from pathlib import Path
 
10
  from zipfile import ZIP_DEFLATED, ZipFile
11
 
12
  from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
@@ -45,6 +46,7 @@ ALLOWED_FORMATS = {
45
  "mp3",
46
  }
47
  JOB_TTL_SECONDS = 2 * 60 * 60
 
48
 
49
  app = FastAPI(title=APP_NAME)
50
  app.add_middleware(
@@ -253,51 +255,141 @@ def _find_uvr_stems(output_dir: Path) -> tuple[Path, Path]:
253
  return vocal, instrumental
254
 
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  def _run_audio_separator(input_path: Path, work_dir: Path, model: str, mode: str) -> dict:
257
- if shutil.which("audio-separator") is None:
258
- raise RuntimeError("UVR MDX is not installed on this worker yet. Use Demucs Standard/Fine-Tuned, or deploy a dedicated UVR test worker.")
259
  if mode != "fast-2stem":
260
  raise RuntimeError("UVR MDX test model currently supports fast-2stem vocal/instrumental output only")
261
  model_file = UVR_MODEL_FILES.get(model)
262
  if not model_file:
263
  raise RuntimeError(f"Unsupported UVR model: {model}")
264
 
265
- output_dir = work_dir / "uvr-separated"
266
- model_dir = Path(os.getenv("NEURALIS_UVR_MODEL_DIR", "/tmp/neuralis-uvr-models"))
267
- output_dir.mkdir(parents=True, exist_ok=True)
268
- model_dir.mkdir(parents=True, exist_ok=True)
269
 
270
- cmd = [
271
- "audio-separator",
272
- str(input_path),
273
- "--model_filename",
274
- model_file,
275
- "--output_dir",
276
- str(output_dir),
277
- "--model_file_dir",
278
- str(model_dir),
279
- "--output_format",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  "WAV",
281
  ]
282
- if os.getenv("NEURALIS_UVR_USE_CUDA", "1").strip().lower() not in {"0", "false", "no", "off"}:
283
- cmd.append("--use_cuda")
 
 
 
284
 
285
  start = time.time()
286
- proc = subprocess.run(
287
- cmd,
288
- cwd=work_dir,
289
- text=True,
290
- stdout=subprocess.PIPE,
291
- stderr=subprocess.STDOUT,
292
- timeout=60 * 25,
293
- )
294
- if proc.returncode != 0:
295
- raise RuntimeError(proc.stdout[-6000:])
 
 
 
 
 
 
 
 
 
 
296
 
297
  vocal_path, instrumental_path = _find_uvr_stems(output_dir)
298
  elapsed = time.time() - start
299
  (work_dir / "neuralis-stem-report.txt").write_text(
300
- f"mode={mode}\nmodel={model}\nengine=audio-separator\nmodelFile={model_file}\nseconds={elapsed:.2f}\nsource={input_path.name}\n",
301
  encoding="utf-8",
302
  )
303
  return {
 
7
  import time
8
  import uuid
9
  from pathlib import Path
10
+ from urllib.request import urlretrieve
11
  from zipfile import ZIP_DEFLATED, ZipFile
12
 
13
  from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
 
46
  "mp3",
47
  }
48
  JOB_TTL_SECONDS = 2 * 60 * 60
49
+ DEFAULT_UVR_WORKER_URL = "https://jayman-neuralis-uvr-stem-worker.hf.space"
50
 
51
  app = FastAPI(title=APP_NAME)
52
  app.add_middleware(
 
255
  return vocal, instrumental
256
 
257
 
258
+ def _normalize_uvr_worker_url(value: str) -> str:
259
+ raw = (value or DEFAULT_UVR_WORKER_URL).strip()
260
+ marker = "huggingface.co/spaces/"
261
+ if marker in raw:
262
+ repo = raw.split(marker, 1)[1].split("?", 1)[0].split("#", 1)[0].strip("/")
263
+ parts = repo.split("/")
264
+ if len(parts) >= 2:
265
+ return f"https://{parts[0]}-{parts[1]}.hf.space"
266
+ return raw or DEFAULT_UVR_WORKER_URL
267
+
268
+
269
+ def _flatten_gradio_outputs(value) -> list[str]:
270
+ paths = []
271
+ if value is None:
272
+ return paths
273
+ if isinstance(value, (str, Path)):
274
+ return [str(value)]
275
+ if isinstance(value, dict):
276
+ for key in ("path", "name", "url"):
277
+ item = value.get(key)
278
+ if item:
279
+ paths.append(str(item))
280
+ for item in value.values():
281
+ if isinstance(item, (dict, list, tuple)):
282
+ paths.extend(_flatten_gradio_outputs(item))
283
+ return paths
284
+ if isinstance(value, (list, tuple)):
285
+ for item in value:
286
+ paths.extend(_flatten_gradio_outputs(item))
287
+ return paths
288
+
289
+
290
+ def _copy_gradio_output(value: str, output_dir: Path, index: int) -> Path | None:
291
+ raw = str(value or "").strip()
292
+ if not raw:
293
+ return None
294
+ suffix = Path(raw.split("?", 1)[0]).suffix.lower()
295
+ if suffix not in {".wav", ".mp3", ".flac"}:
296
+ suffix = ".wav"
297
+ raw_name = Path(raw.split("?", 1)[0]).name
298
+ target_name = _safe_name(raw_name or f"uvr-output-{index}{suffix}")
299
+ if Path(target_name).suffix.lower() not in {".wav", ".mp3", ".flac"}:
300
+ target_name = f"{Path(target_name).stem}{suffix}"
301
+ target = output_dir / target_name
302
+ if target.exists():
303
+ target = output_dir / f"{Path(target_name).stem}-{index}{Path(target_name).suffix}"
304
+ if raw.startswith(("http://", "https://")):
305
+ urlretrieve(raw, target)
306
+ return target
307
+ source = Path(raw)
308
+ if source.exists() and source.is_file():
309
+ if source.resolve() == target.resolve():
310
+ return target
311
+ shutil.copy2(source, target)
312
+ return target
313
+ return None
314
+
315
+
316
  def _run_audio_separator(input_path: Path, work_dir: Path, model: str, mode: str) -> dict:
 
 
317
  if mode != "fast-2stem":
318
  raise RuntimeError("UVR MDX test model currently supports fast-2stem vocal/instrumental output only")
319
  model_file = UVR_MODEL_FILES.get(model)
320
  if not model_file:
321
  raise RuntimeError(f"Unsupported UVR model: {model}")
322
 
323
+ try:
324
+ from gradio_client import Client, handle_file
325
+ except Exception as exc:
326
+ raise RuntimeError("UVR MDX remote adapter needs gradio_client installed in the stem worker") from exc
327
 
328
+ output_dir = work_dir / "uvr-remote-separated"
329
+ output_dir.mkdir(parents=True, exist_ok=True)
330
+ worker_url = _normalize_uvr_worker_url(os.getenv("NEURALIS_UVR_WORKER_URL", DEFAULT_UVR_WORKER_URL))
331
+ args = [
332
+ handle_file(str(input_path)),
333
+ ["vocal", "background"],
334
+ False,
335
+ False,
336
+ False,
337
+ False,
338
+ 0.15,
339
+ 0.7,
340
+ 0.8,
341
+ 0.2,
342
+ 0.0,
343
+ 0.0,
344
+ -18,
345
+ 2.0,
346
+ 5,
347
+ 100,
348
+ 0.0,
349
+ 20,
350
+ 20000,
351
+ 0.15,
352
+ 0.7,
353
+ 0.15,
354
+ -18,
355
+ 2.0,
356
+ 5,
357
+ 100,
358
+ 0.0,
359
  "WAV",
360
  ]
361
+ attempts = [
362
+ {"api_name": "/sound_separate"},
363
+ {"api_name": "/predict"},
364
+ *({"fn_index": index} for index in range(8)),
365
+ ]
366
 
367
  start = time.time()
368
+ errors = []
369
+ result = None
370
+ client = Client(worker_url)
371
+ for attempt in attempts:
372
+ try:
373
+ result = client.predict(*args, **attempt)
374
+ break
375
+ except Exception as exc:
376
+ errors.append(f"{attempt}: {str(exc)[:360]}")
377
+ if result is None:
378
+ detail = " | ".join(errors[-3:]) if errors else "No response from UVR worker"
379
+ raise RuntimeError(f"UVR MDX remote worker failed: {detail}")
380
+
381
+ copied = []
382
+ for index, path_value in enumerate(_flatten_gradio_outputs(result), start=1):
383
+ copied_path = _copy_gradio_output(path_value, output_dir, index)
384
+ if copied_path:
385
+ copied.append(copied_path)
386
+ if len(copied) < 2:
387
+ raise RuntimeError("UVR MDX remote worker returned no usable vocal/instrumental files")
388
 
389
  vocal_path, instrumental_path = _find_uvr_stems(output_dir)
390
  elapsed = time.time() - start
391
  (work_dir / "neuralis-stem-report.txt").write_text(
392
+ f"mode={mode}\nmodel={model}\nengine=remote-gradio-uvr\nworker={worker_url}\nmodelFile={model_file}\nseconds={elapsed:.2f}\nsource={input_path.name}\n",
393
  encoding="utf-8",
394
  )
395
  return {
requirements.txt CHANGED
@@ -4,3 +4,4 @@ python-multipart==0.0.20
4
  numpy<2
5
  demucs==4.0.1
6
  soundfile==0.12.1
 
 
4
  numpy<2
5
  demucs==4.0.1
6
  soundfile==0.12.1
7
+ gradio_client