ambujm22 commited on
Commit
5014a10
·
verified ·
1 Parent(s): 8071baa

Update app.py

Browse files

Fix infer_single.py not Found

Files changed (1) hide show
  1. app.py +163 -55
app.py CHANGED
@@ -1,6 +1,14 @@
 
 
 
 
 
1
  import os
2
  import sys
3
  import subprocess
 
 
 
4
  from pathlib import Path
5
  from typing import Tuple, Optional, List, Any
6
 
@@ -31,32 +39,120 @@ REPO_DIR = SPACE_ROOT / "SonicMasterRepo"
31
  REPO_URL = "https://github.com/AMAAI-Lab/SonicMaster"
32
  WEIGHTS_REPO = "amaai-lab/SonicMaster"
33
  WEIGHTS_FILE = "model.safetensors"
 
34
  CACHE_DIR = SPACE_ROOT / "weights"
35
  CACHE_DIR.mkdir(parents=True, exist_ok=True)
36
 
37
  # ================== SAFE repo handling (NO network at import) ==================
38
  _repo_ready: bool = False
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  def ensure_repo(progress: Optional[gr.Progress] = None) -> Path:
41
  """
42
  Ensure SonicMaster repo is available.
43
- IMPORTANT: Called lazily (on user action), not at import time.
 
 
 
44
  """
45
  global _repo_ready
46
- if _repo_ready and REPO_DIR.exists():
 
 
 
47
  if REPO_DIR.as_posix() not in sys.path:
48
  sys.path.append(REPO_DIR.as_posix())
49
  return REPO_DIR
50
 
 
 
 
 
51
  if not REPO_DIR.exists():
52
  if progress:
53
- progress(0.02, desc="Cloning SonicMaster repo (first run)")
54
- # Shallow clone to keep it fast
55
- subprocess.run(
56
- ["git", "clone", "--depth", "1", REPO_URL, REPO_DIR.as_posix()],
57
- check=True,
58
- capture_output=True,
59
- text=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  )
61
 
62
  if REPO_DIR.as_posix() not in sys.path:
@@ -65,9 +161,11 @@ def ensure_repo(progress: Optional[gr.Progress] = None) -> Path:
65
  _repo_ready = True
66
  return REPO_DIR
67
 
 
68
  # ================ Weights: lazy download (first click) ================
69
  _weights_path: Optional[Path] = None
70
 
 
71
  def get_weights_path(progress: Optional[gr.Progress] = None) -> Path:
72
  """
73
  Download/resolve weights lazily (keeps startup fast).
@@ -87,6 +185,7 @@ def get_weights_path(progress: Optional[gr.Progress] = None) -> Path:
87
  _weights_path = Path(wp)
88
  return _weights_path
89
 
 
90
  # ================== Audio helpers ==================
91
  def save_temp_wav(wav: np.ndarray, sr: int, path: Path):
92
  # Ensure shape (samples, channels)
@@ -96,12 +195,22 @@ def save_temp_wav(wav: np.ndarray, sr: int, path: Path):
96
  wav = wav.astype(np.float32)
97
  sf.write(path.as_posix(), wav, sr)
98
 
 
99
  def read_audio(path: str) -> Tuple[np.ndarray, int]:
100
  wav, sr = sf.read(path, always_2d=False)
101
  if isinstance(wav, np.ndarray) and wav.dtype == np.float64:
102
  wav = wav.astype(np.float32)
103
  return wav, sr
104
 
 
 
 
 
 
 
 
 
 
105
  # ================== CLI runner ==================
106
  def _candidate_commands(
107
  py: str, script: Path, ckpt: Path, inp: Path, prompt: str, out: Path
@@ -110,20 +219,15 @@ def _candidate_commands(
110
  Only support infer_single.py variants.
111
  Expected flags: --ckpt --input --prompt --output
112
  """
113
- return [
114
- [
115
- py,
116
- script.as_posix(),
117
- "--ckpt",
118
- ckpt.as_posix(),
119
- "--input",
120
- inp.as_posix(),
121
- "--prompt",
122
- prompt,
123
- "--output",
124
- out.as_posix(),
125
- ],
126
- ]
127
 
128
  def run_sonicmaster_cli(
129
  input_wav_path: Path,
@@ -147,23 +251,33 @@ def run_sonicmaster_cli(
147
 
148
  script = REPO_DIR / "infer_single.py"
149
  if not script.exists():
150
- return False, "infer_single.py not found in the SonicMaster repo."
151
 
152
  py = sys.executable or "python3"
153
  env = os.environ.copy()
154
 
 
 
 
155
  last_err = ""
156
- for cidx, cmd in enumerate(
157
- _candidate_commands(py, script, ckpt, input_wav_path, prompt, out_path), 1
158
- ):
159
  try:
160
  if progress:
161
  progress(min(0.25 + 0.10 * cidx, 0.70), desc=f"Running inference (try {cidx})")
162
- res = subprocess.run(cmd, capture_output=True, text=True, check=True, env=env)
 
 
 
 
 
 
 
 
163
  if out_path.exists() and out_path.stat().st_size > 0:
164
  if progress:
165
  progress(0.88, desc="Post-processing output")
166
- return True, (res.stdout or "Inference completed.").strip()
 
167
  last_err = "infer_single.py finished but produced no output file."
168
  except subprocess.CalledProcessError as e:
169
  snippet = "\n".join(filter(None, [e.stdout or "", e.stderr or ""])).strip()
@@ -174,20 +288,15 @@ def run_sonicmaster_cli(
174
 
175
  return False, last_err or "Inference failed."
176
 
 
177
  # ============ GPU path (ZeroGPU) ============
178
  @spaces.GPU(duration=60) # safe cap for ZeroGPU tiers
179
  def enhance_on_gpu(input_path: str, prompt: str, output_path: str) -> Tuple[bool, str]:
180
  from pathlib import Path as _P
181
  return run_sonicmaster_cli(_P(input_path), prompt, _P(output_path), progress=None)
182
 
183
- def _has_cuda() -> bool:
184
- try:
185
- import torch
186
- return torch.cuda.is_available()
187
- except Exception:
188
- return False
189
 
190
- # ================== Optional Examples (NO CLONE AT STARTUP) ==================
191
  PROMPTS_10 = [
192
  "Increase the clarity of this song by emphasizing treble frequencies.",
193
  "Make this song sound more boomy by amplifying the low end bass frequencies.",
@@ -201,21 +310,23 @@ PROMPTS_10 = [
201
  "Please, dereverb this audio.",
202
  ]
203
 
 
204
  def build_examples_if_repo_present() -> List[List[Any]]:
205
  """
206
- Build examples WITHOUT cloning. If repo isn't present yet, return [].
207
- This avoids slow startup + network calls.
208
  """
209
  wav_dir = REPO_DIR / "samples" / "inputs"
210
  if not wav_dir.exists():
211
  return []
212
  wav_paths = sorted(p for p in wav_dir.glob("*.wav") if p.is_file())
213
- ex = []
214
  for i, p in enumerate(wav_paths[:10]):
215
  pr = PROMPTS_10[i] if i < len(PROMPTS_10) else PROMPTS_10[-1]
216
  ex.append([p.as_posix(), pr])
217
  return ex
218
 
 
219
  # ================== Main callback ==================
220
  def enhance_audio_ui(
221
  audio_path: str,
@@ -227,7 +338,6 @@ def enhance_audio_ui(
227
  """
228
  try:
229
  prompt = (prompt or "").strip() or "Enhance the input audio"
230
-
231
  if not audio_path:
232
  raise gr.Error("Please upload or select an input audio file.")
233
 
@@ -237,11 +347,10 @@ def enhance_audio_ui(
237
 
238
  tmp_in = SPACE_ROOT / "tmp_in.wav"
239
  tmp_out = SPACE_ROOT / "tmp_out.wav"
 
 
240
  if tmp_out.exists():
241
- try:
242
- tmp_out.unlink()
243
- except Exception:
244
- pass
245
 
246
  save_temp_wav(wav, sr, tmp_in)
247
 
@@ -266,33 +375,32 @@ def enhance_audio_ui(
266
  import traceback
267
  return None, f"Unexpected error: {e}\n{traceback.format_exc()}"
268
 
 
269
  # ================== Gradio UI ==================
270
  with gr.Blocks(title="SonicMaster – Text-Guided Restoration & Mastering", fill_height=True) as _demo:
271
  gr.Markdown(
272
  "## 🎧 SonicMaster\n"
273
  "Upload audio, write a prompt (or leave blank), then click **Enhance**.\n"
274
  "If left blank, we use: _Enhance the input audio_.\n\n"
275
- "- First run will clone the repo + download weights (may take a bit).\n"
276
  "- Subsequent runs are much faster.\n"
277
- "If you enjoy this model, please cite the paper."
278
  )
279
 
280
  with gr.Row():
281
  with gr.Column(scale=1):
282
  in_audio = gr.Audio(label="Input Audio", type="filepath")
283
- prompt_box = gr.Textbox(label="Text Prompt", placeholder="e.g., Reduce reverb and brighten vocals. (Optional)")
 
 
 
284
  run_btn = gr.Button("🚀 Enhance", variant="primary")
285
 
286
- # Examples only if already present locally (no startup clone)
287
  examples = build_examples_if_repo_present()
288
  if examples:
289
- gr.Examples(
290
- examples=examples,
291
- inputs=[in_audio, prompt_box],
292
- label="Sample Inputs (10)",
293
- )
294
  else:
295
- gr.Markdown("> ℹ️ Samples will appear after the repo is cloned (first run).")
296
 
297
  with gr.Column(scale=1):
298
  out_audio = gr.Audio(label="Enhanced Audio (output)")
@@ -310,4 +418,4 @@ iface = demo
310
  app = demo
311
 
312
  if __name__ == "__main__":
313
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
+ # app.py — end-to-end Hugging Face Spaces app for SonicMaster
2
+ # - Works even if `git` is NOT available (falls back to GitHub ZIP download)
3
+ # - Lazily fetches code + weights only when user clicks "Enhance"
4
+ # - Runs inference ONLY via infer_single.py
5
+
6
  import os
7
  import sys
8
  import subprocess
9
+ import shutil
10
+ import zipfile
11
+ import urllib.request
12
  from pathlib import Path
13
  from typing import Tuple, Optional, List, Any
14
 
 
39
  REPO_URL = "https://github.com/AMAAI-Lab/SonicMaster"
40
  WEIGHTS_REPO = "amaai-lab/SonicMaster"
41
  WEIGHTS_FILE = "model.safetensors"
42
+
43
  CACHE_DIR = SPACE_ROOT / "weights"
44
  CACHE_DIR.mkdir(parents=True, exist_ok=True)
45
 
46
  # ================== SAFE repo handling (NO network at import) ==================
47
  _repo_ready: bool = False
48
 
49
+
50
+ def _safe_unlink(p: Path):
51
+ try:
52
+ if p.exists():
53
+ p.unlink()
54
+ except Exception:
55
+ pass
56
+
57
+
58
+ def _rmtree(p: Path):
59
+ try:
60
+ if p.exists():
61
+ shutil.rmtree(p)
62
+ except Exception:
63
+ pass
64
+
65
+
66
  def ensure_repo(progress: Optional[gr.Progress] = None) -> Path:
67
  """
68
  Ensure SonicMaster repo is available.
69
+ Tries:
70
+ 1) git clone (if git exists)
71
+ 2) GitHub ZIP download + extract (if git missing or clone fails)
72
+ Called lazily (on button click), not at import time.
73
  """
74
  global _repo_ready
75
+
76
+ # If already ready and infer exists, done
77
+ script0 = REPO_DIR / "infer_single.py"
78
+ if _repo_ready and script0.exists():
79
  if REPO_DIR.as_posix() not in sys.path:
80
  sys.path.append(REPO_DIR.as_posix())
81
  return REPO_DIR
82
 
83
+ # If directory exists but script missing, treat as corrupted and reset
84
+ if REPO_DIR.exists() and not script0.exists():
85
+ _rmtree(REPO_DIR)
86
+
87
  if not REPO_DIR.exists():
88
  if progress:
89
+ progress(0.02, desc="Fetching SonicMaster code (first run)")
90
+
91
+ git_bin = shutil.which("git")
92
+ cloned_ok = False
93
+
94
+ # Try git clone if available
95
+ if git_bin:
96
+ try:
97
+ subprocess.run(
98
+ [git_bin, "clone", "--depth", "1", REPO_URL, REPO_DIR.as_posix()],
99
+ check=True,
100
+ capture_output=True,
101
+ text=True,
102
+ )
103
+ cloned_ok = True
104
+ except Exception:
105
+ cloned_ok = False
106
+ # If partial directory created, remove and retry via ZIP
107
+ if REPO_DIR.exists():
108
+ _rmtree(REPO_DIR)
109
+
110
+ # Fallback: download ZIP
111
+ if not cloned_ok:
112
+ if progress:
113
+ progress(0.05, desc="Downloading SonicMaster ZIP (no git / clone failed)")
114
+
115
+ zip_url = "https://codeload.github.com/AMAAI-Lab/SonicMaster/zip/refs/heads/main"
116
+ zip_path = SPACE_ROOT / "SonicMaster.zip"
117
+
118
+ # Download ZIP
119
+ urllib.request.urlretrieve(zip_url, zip_path.as_posix())
120
+
121
+ if progress:
122
+ progress(0.08, desc="Extracting SonicMaster ZIP")
123
+
124
+ # Extract ZIP to SPACE_ROOT
125
+ with zipfile.ZipFile(zip_path, "r") as zf:
126
+ zf.extractall(SPACE_ROOT)
127
+
128
+ # GitHub zip usually extracts to SonicMaster-main/
129
+ extracted = SPACE_ROOT / "SonicMaster-main"
130
+ if extracted.exists() and (extracted / "infer_single.py").exists():
131
+ # If destination already exists, remove it
132
+ if REPO_DIR.exists():
133
+ _rmtree(REPO_DIR)
134
+ extracted.rename(REPO_DIR)
135
+ else:
136
+ # If structure differs, try to locate the folder containing infer_single.py
137
+ found = None
138
+ for cand in SPACE_ROOT.glob("SonicMaster-*"):
139
+ if cand.is_dir() and (cand / "infer_single.py").exists():
140
+ found = cand
141
+ break
142
+ if found:
143
+ if REPO_DIR.exists():
144
+ _rmtree(REPO_DIR)
145
+ found.rename(REPO_DIR)
146
+
147
+ _safe_unlink(zip_path)
148
+
149
+ # Final sanity check
150
+ if not (REPO_DIR / "infer_single.py").exists():
151
+ existing = sorted([p.name for p in REPO_DIR.glob("*")]) if REPO_DIR.exists() else []
152
+ raise RuntimeError(
153
+ "SonicMaster code fetch finished, but infer_single.py is still missing.\n"
154
+ f"REPO_DIR: {REPO_DIR}\n"
155
+ f"Contents: {existing[:80]}"
156
  )
157
 
158
  if REPO_DIR.as_posix() not in sys.path:
 
161
  _repo_ready = True
162
  return REPO_DIR
163
 
164
+
165
  # ================ Weights: lazy download (first click) ================
166
  _weights_path: Optional[Path] = None
167
 
168
+
169
  def get_weights_path(progress: Optional[gr.Progress] = None) -> Path:
170
  """
171
  Download/resolve weights lazily (keeps startup fast).
 
185
  _weights_path = Path(wp)
186
  return _weights_path
187
 
188
+
189
  # ================== Audio helpers ==================
190
  def save_temp_wav(wav: np.ndarray, sr: int, path: Path):
191
  # Ensure shape (samples, channels)
 
195
  wav = wav.astype(np.float32)
196
  sf.write(path.as_posix(), wav, sr)
197
 
198
+
199
  def read_audio(path: str) -> Tuple[np.ndarray, int]:
200
  wav, sr = sf.read(path, always_2d=False)
201
  if isinstance(wav, np.ndarray) and wav.dtype == np.float64:
202
  wav = wav.astype(np.float32)
203
  return wav, sr
204
 
205
+
206
+ def _has_cuda() -> bool:
207
+ try:
208
+ import torch
209
+ return torch.cuda.is_available()
210
+ except Exception:
211
+ return False
212
+
213
+
214
  # ================== CLI runner ==================
215
  def _candidate_commands(
216
  py: str, script: Path, ckpt: Path, inp: Path, prompt: str, out: Path
 
219
  Only support infer_single.py variants.
220
  Expected flags: --ckpt --input --prompt --output
221
  """
222
+ return [[
223
+ py,
224
+ script.as_posix(),
225
+ "--ckpt", ckpt.as_posix(),
226
+ "--input", inp.as_posix(),
227
+ "--prompt", prompt,
228
+ "--output", out.as_posix(),
229
+ ]]
230
+
 
 
 
 
 
231
 
232
  def run_sonicmaster_cli(
233
  input_wav_path: Path,
 
251
 
252
  script = REPO_DIR / "infer_single.py"
253
  if not script.exists():
254
+ return False, "infer_single.py not found in the SonicMaster repo (code fetch likely failed)."
255
 
256
  py = sys.executable or "python3"
257
  env = os.environ.copy()
258
 
259
+ # Make sure subprocess runs inside repo (some projects rely on relative paths)
260
+ cwd = REPO_DIR.as_posix()
261
+
262
  last_err = ""
263
+ for cidx, cmd in enumerate(_candidate_commands(py, script, ckpt, input_wav_path, prompt, out_path), 1):
 
 
264
  try:
265
  if progress:
266
  progress(min(0.25 + 0.10 * cidx, 0.70), desc=f"Running inference (try {cidx})")
267
+ res = subprocess.run(
268
+ cmd,
269
+ capture_output=True,
270
+ text=True,
271
+ check=True,
272
+ env=env,
273
+ cwd=cwd,
274
+ )
275
+
276
  if out_path.exists() and out_path.stat().st_size > 0:
277
  if progress:
278
  progress(0.88, desc="Post-processing output")
279
+ stdout = (res.stdout or "").strip()
280
+ return True, (stdout or "Inference completed.")
281
  last_err = "infer_single.py finished but produced no output file."
282
  except subprocess.CalledProcessError as e:
283
  snippet = "\n".join(filter(None, [e.stdout or "", e.stderr or ""])).strip()
 
288
 
289
  return False, last_err or "Inference failed."
290
 
291
+
292
  # ============ GPU path (ZeroGPU) ============
293
  @spaces.GPU(duration=60) # safe cap for ZeroGPU tiers
294
  def enhance_on_gpu(input_path: str, prompt: str, output_path: str) -> Tuple[bool, str]:
295
  from pathlib import Path as _P
296
  return run_sonicmaster_cli(_P(input_path), prompt, _P(output_path), progress=None)
297
 
 
 
 
 
 
 
298
 
299
+ # ================== Optional Examples (NO FETCH AT STARTUP) ==================
300
  PROMPTS_10 = [
301
  "Increase the clarity of this song by emphasizing treble frequencies.",
302
  "Make this song sound more boomy by amplifying the low end bass frequencies.",
 
310
  "Please, dereverb this audio.",
311
  ]
312
 
313
+
314
  def build_examples_if_repo_present() -> List[List[Any]]:
315
  """
316
+ Build examples WITHOUT cloning/downloading.
317
+ If repo isn't present yet, return [].
318
  """
319
  wav_dir = REPO_DIR / "samples" / "inputs"
320
  if not wav_dir.exists():
321
  return []
322
  wav_paths = sorted(p for p in wav_dir.glob("*.wav") if p.is_file())
323
+ ex: List[List[Any]] = []
324
  for i, p in enumerate(wav_paths[:10]):
325
  pr = PROMPTS_10[i] if i < len(PROMPTS_10) else PROMPTS_10[-1]
326
  ex.append([p.as_posix(), pr])
327
  return ex
328
 
329
+
330
  # ================== Main callback ==================
331
  def enhance_audio_ui(
332
  audio_path: str,
 
338
  """
339
  try:
340
  prompt = (prompt or "").strip() or "Enhance the input audio"
 
341
  if not audio_path:
342
  raise gr.Error("Please upload or select an input audio file.")
343
 
 
347
 
348
  tmp_in = SPACE_ROOT / "tmp_in.wav"
349
  tmp_out = SPACE_ROOT / "tmp_out.wav"
350
+
351
+ # Clean previous output to avoid stale returns
352
  if tmp_out.exists():
353
+ _safe_unlink(tmp_out)
 
 
 
354
 
355
  save_temp_wav(wav, sr, tmp_in)
356
 
 
375
  import traceback
376
  return None, f"Unexpected error: {e}\n{traceback.format_exc()}"
377
 
378
+
379
  # ================== Gradio UI ==================
380
  with gr.Blocks(title="SonicMaster – Text-Guided Restoration & Mastering", fill_height=True) as _demo:
381
  gr.Markdown(
382
  "## 🎧 SonicMaster\n"
383
  "Upload audio, write a prompt (or leave blank), then click **Enhance**.\n"
384
  "If left blank, we use: _Enhance the input audio_.\n\n"
385
+ "- First run will fetch code + download weights.\n"
386
  "- Subsequent runs are much faster.\n"
 
387
  )
388
 
389
  with gr.Row():
390
  with gr.Column(scale=1):
391
  in_audio = gr.Audio(label="Input Audio", type="filepath")
392
+ prompt_box = gr.Textbox(
393
+ label="Text Prompt",
394
+ placeholder="e.g., Reduce reverb and brighten vocals. (Optional)",
395
+ )
396
  run_btn = gr.Button("🚀 Enhance", variant="primary")
397
 
398
+ # Examples only if already present locally (no startup fetch)
399
  examples = build_examples_if_repo_present()
400
  if examples:
401
+ gr.Examples(examples=examples, inputs=[in_audio, prompt_box], label="Sample Inputs (10)")
 
 
 
 
402
  else:
403
+ gr.Markdown("> ℹ️ Samples will appear after the code is fetched (first run).")
404
 
405
  with gr.Column(scale=1):
406
  out_audio = gr.Audio(label="Enhanced Audio (output)")
 
418
  app = demo
419
 
420
  if __name__ == "__main__":
421
+ demo.launch(server_name="0.0.0.0", server_port=7860)