Spaces:
Paused
Paused
Upload 6 files
Browse files- app.py +121 -29
- 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 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
"
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
"WAV",
|
| 281 |
]
|
| 282 |
-
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
start = time.time()
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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=
|
| 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
|