Spaces:
Sleeping
Sleeping
Move GPU decorator to transcription; provider chain runs on CPU; report chain errors
Browse files
app.py
CHANGED
|
@@ -61,14 +61,25 @@ def _audio_duration(path):
|
|
| 61 |
return 0.0
|
| 62 |
|
| 63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
def transcribe(audio_path, model_choice):
|
| 65 |
if not audio_path:
|
| 66 |
return None, "", "Upload a song first."
|
| 67 |
-
jid = _job()
|
| 68 |
try:
|
| 69 |
-
res =
|
| 70 |
-
except Exception
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
| 72 |
segs = res["segments"]
|
| 73 |
text = "\n".join(f"{_fmt_ts(s['start'])} {s['text']}" for s in segs)
|
| 74 |
return segs, text, f"Transcribed {len(segs)} lines ({res['language']}, {res['duration']:.0f}s). Now build the director plan."
|
|
@@ -183,10 +194,11 @@ def render_smooth(clips, windows, audio, segments, out_path):
|
|
| 183 |
return Path(out_path)
|
| 184 |
|
| 185 |
|
| 186 |
-
@spaces.GPU(duration=120)
|
| 187 |
def ai_clip(image_path, prompt, novai_key, deepinfra_key, novita_key, fal_key):
|
| 188 |
"""Generate ONE AI clip through the provider chain (first free tier that
|
| 189 |
-
answers wins).
|
|
|
|
|
|
|
| 190 |
keys = _keys(novai_key, deepinfra_key, novita_key, fal_key)
|
| 191 |
if not any(keys.values()):
|
| 192 |
raise gr.Error("No provider keys available — add one in the key fields above or save them as Space secrets.")
|
|
@@ -215,6 +227,10 @@ def make_video(audio_path, image_path, segs, shots, use_ai,
|
|
| 215 |
return
|
| 216 |
keys = _keys(novai_key, deepinfra_key, novita_key, fal_key)
|
| 217 |
chain_on = bool(use_ai and any(keys.values()))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
duration = _audio_duration(audio_path)
|
| 219 |
if segs is None:
|
| 220 |
progress(0.05, desc="Transcribing your song…")
|
|
@@ -238,8 +254,10 @@ def make_video(audio_path, image_path, segs, shots, use_ai,
|
|
| 238 |
try:
|
| 239 |
path, who = providers.generate_with_chain(keys, image_path, shot["prompt"], target, out)
|
| 240 |
made = (path, who)
|
| 241 |
-
except providers.ProviderError:
|
| 242 |
made = None # photo-zoom fills this shot
|
|
|
|
|
|
|
| 243 |
if made is None:
|
| 244 |
jid = _job()
|
| 245 |
res = do_photoclip(jid, Path(image_path), target, variant=i % 4)
|
|
@@ -257,7 +275,9 @@ def make_video(audio_path, image_path, segs, shots, use_ai,
|
|
| 257 |
res = do_render(jid, Path(audio_path), [p.name for p in clip_paths], "", segs)
|
| 258 |
final = FILES / Path(res["video_url"]).name
|
| 259 |
how = f"{total} shots, hard-cut render (crossfade unavailable)"
|
| 260 |
-
|
|
|
|
|
|
|
| 261 |
|
| 262 |
|
| 263 |
with gr.Blocks(title="AllOutMV") as demo:
|
|
|
|
| 61 |
return 0.0
|
| 62 |
|
| 63 |
|
| 64 |
+
@spaces.GPU(duration=120)
|
| 65 |
+
def _transcribe_gpu(audio_path, model_choice):
|
| 66 |
+
"""Real GPU work (Whisper on CUDA) — the Space's @spaces.GPU function.
|
| 67 |
+
ZeroGPU attaches the GPU only inside decorated calls and aborts decorated
|
| 68 |
+
functions that never use it, so the GPU belongs on transcription, not on
|
| 69 |
+
the HTTP provider chain."""
|
| 70 |
+
return do_transcribe(_job(), Path(audio_path), model_choice or "small")
|
| 71 |
+
|
| 72 |
+
|
| 73 |
def transcribe(audio_path, model_choice):
|
| 74 |
if not audio_path:
|
| 75 |
return None, "", "Upload a song first."
|
|
|
|
| 76 |
try:
|
| 77 |
+
res = _transcribe_gpu(audio_path, model_choice) # fast path: real GPU
|
| 78 |
+
except Exception:
|
| 79 |
+
try:
|
| 80 |
+
res = do_transcribe(_job(), Path(audio_path), model_choice or "small") # CPU fallback
|
| 81 |
+
except Exception as e:
|
| 82 |
+
return None, "", f"⚠️ Transcription failed — {type(e).__name__}: {e}"
|
| 83 |
segs = res["segments"]
|
| 84 |
text = "\n".join(f"{_fmt_ts(s['start'])} {s['text']}" for s in segs)
|
| 85 |
return segs, text, f"Transcribed {len(segs)} lines ({res['language']}, {res['duration']:.0f}s). Now build the director plan."
|
|
|
|
| 194 |
return Path(out_path)
|
| 195 |
|
| 196 |
|
|
|
|
| 197 |
def ai_clip(image_path, prompt, novai_key, deepinfra_key, novita_key, fal_key):
|
| 198 |
"""Generate ONE AI clip through the provider chain (first free tier that
|
| 199 |
+
answers wins). Deliberately NOT @spaces.GPU: ZeroGPU aborts decorated
|
| 200 |
+
functions that never touch the GPU, and this work is pure HTTP polling —
|
| 201 |
+
it runs on CPU. The decorator lives on _transcribe_gpu instead."""
|
| 202 |
keys = _keys(novai_key, deepinfra_key, novita_key, fal_key)
|
| 203 |
if not any(keys.values()):
|
| 204 |
raise gr.Error("No provider keys available — add one in the key fields above or save them as Space secrets.")
|
|
|
|
| 227 |
return
|
| 228 |
keys = _keys(novai_key, deepinfra_key, novita_key, fal_key)
|
| 229 |
chain_on = bool(use_ai and any(keys.values()))
|
| 230 |
+
chain_note = ""
|
| 231 |
+
if use_ai and not chain_on:
|
| 232 |
+
chain_note = "\n\n⚠️ Provider chain was OFF — no provider keys found (key fields empty and no Space secrets visible), so every shot used photo-zoom."
|
| 233 |
+
chain_error = None
|
| 234 |
duration = _audio_duration(audio_path)
|
| 235 |
if segs is None:
|
| 236 |
progress(0.05, desc="Transcribing your song…")
|
|
|
|
| 254 |
try:
|
| 255 |
path, who = providers.generate_with_chain(keys, image_path, shot["prompt"], target, out)
|
| 256 |
made = (path, who)
|
| 257 |
+
except providers.ProviderError as e:
|
| 258 |
made = None # photo-zoom fills this shot
|
| 259 |
+
if chain_error is None:
|
| 260 |
+
chain_error = str(e)
|
| 261 |
if made is None:
|
| 262 |
jid = _job()
|
| 263 |
res = do_photoclip(jid, Path(image_path), target, variant=i % 4)
|
|
|
|
| 275 |
res = do_render(jid, Path(audio_path), [p.name for p in clip_paths], "", segs)
|
| 276 |
final = FILES / Path(res["video_url"]).name
|
| 277 |
how = f"{total} shots, hard-cut render (crossfade unavailable)"
|
| 278 |
+
if chain_error:
|
| 279 |
+
chain_note += f"\n\n⚠️ Provider chain report (first failure): {chain_error}"
|
| 280 |
+
yield str(final), f"Done — your music video is ready ({how}).\n" + "\n".join(used) + chain_note
|
| 281 |
|
| 282 |
|
| 283 |
with gr.Blocks(title="AllOutMV") as demo:
|