VeuReu commited on
Commit
f2f3f76
·
verified ·
1 Parent(s): 4b3aed1

Upload api.py

Browse files
Files changed (1) hide show
  1. api.py +91 -0
api.py CHANGED
@@ -1,5 +1,6 @@
1
  from __future__ import annotations
2
  from fastapi import FastAPI, UploadFile, File, Form, BackgroundTasks, HTTPException
 
3
  from fastapi.responses import JSONResponse, FileResponse
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from pathlib import Path
@@ -36,6 +37,8 @@ TEMP_ROOT = Path("/tmp/temp")
36
  TEMP_ROOT.mkdir(parents=True, exist_ok=True)
37
  VIDEOS_ROOT = Path("/tmp/data/videos")
38
  VIDEOS_ROOT.mkdir(parents=True, exist_ok=True)
 
 
39
 
40
  # Sistema de jobs asíncronos
41
  class JobStatus(str, Enum):
@@ -389,6 +392,94 @@ async def load_casting(
389
  n_voices = build_voices_index(Path(voices_dir), client, collection_name="index_voices", drop=drop_collections)
390
  return {"ok": True, "faces": n_faces, "voices": n_voices}
391
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  @app.post("/refine_narration")
393
  async def refine_narration(
394
  dialogues_srt: str = Form(...),
 
1
  from __future__ import annotations
2
  from fastapi import FastAPI, UploadFile, File, Form, BackgroundTasks, HTTPException
3
+ from fastapi import Body
4
  from fastapi.responses import JSONResponse, FileResponse
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from pathlib import Path
 
37
  TEMP_ROOT.mkdir(parents=True, exist_ok=True)
38
  VIDEOS_ROOT = Path("/tmp/data/videos")
39
  VIDEOS_ROOT.mkdir(parents=True, exist_ok=True)
40
+ IDENTITIES_ROOT = Path("/tmp/characters")
41
+ IDENTITIES_ROOT.mkdir(parents=True, exist_ok=True)
42
 
43
  # Sistema de jobs asíncronos
44
  class JobStatus(str, Enum):
 
392
  n_voices = build_voices_index(Path(voices_dir), client, collection_name="index_voices", drop=drop_collections)
393
  return {"ok": True, "faces": n_faces, "voices": n_voices}
394
 
395
+ @app.post("/finalize_casting")
396
+ async def finalize_casting(
397
+ payload: dict = Body(...),
398
+ ):
399
+ """
400
+ Consolidate selected face and voice clusters into identities directories and build indices.
401
+ Expected payload:
402
+ {
403
+ "video_name": str,
404
+ "base_dir": str, # engine temp base for this video
405
+ "characters": [
406
+ {"id": "char1", "name": "Nom", "folder": "/tmp/temp/<video>/char1", "kept_files": ["representative.jpg", ...], "description": "..."}, ...
407
+ ],
408
+ "voice_clusters": [
409
+ {"label": 0, "name": "SPEAKER_00", "clips": ["segment_000.wav", ...]}, ...
410
+ ]
411
+ }
412
+ """
413
+ import os
414
+ import shutil
415
+ from pathlib import Path as _P
416
+
417
+ video_name = payload.get("video_name")
418
+ base_dir = payload.get("base_dir")
419
+ characters = payload.get("characters", []) or []
420
+ voice_clusters = payload.get("voice_clusters", []) or []
421
+
422
+ if not video_name or not base_dir:
423
+ raise HTTPException(status_code=400, detail="Missing video_name or base_dir")
424
+
425
+ faces_out = IDENTITIES_ROOT / video_name / "faces"
426
+ voices_out = IDENTITIES_ROOT / video_name / "voices"
427
+ faces_out.mkdir(parents=True, exist_ok=True)
428
+ voices_out.mkdir(parents=True, exist_ok=True)
429
+
430
+ # Consolidate faces per character name (merge same names)
431
+ for ch in characters:
432
+ ch_name = (ch.get("name") or "Unknown").strip() or "Unknown"
433
+ ch_folder = ch.get("folder")
434
+ kept = ch.get("kept_files") or []
435
+ if not ch_folder or not os.path.isdir(ch_folder):
436
+ continue
437
+ dst_dir = faces_out / ch_name
438
+ dst_dir.mkdir(parents=True, exist_ok=True)
439
+ for fname in kept:
440
+ src = _P(ch_folder) / fname
441
+ if src.exists() and src.is_file():
442
+ try:
443
+ shutil.copy2(src, dst_dir / fname)
444
+ except Exception:
445
+ pass
446
+
447
+ # Consolidate voices per cluster name
448
+ clips_dir = _P(base_dir) / "clips"
449
+ for vc in voice_clusters:
450
+ v_name = (vc.get("name") or f"SPEAKER_{int(vc.get('label',0)):02d}").strip()
451
+ dst_dir = voices_out / v_name
452
+ dst_dir.mkdir(parents=True, exist_ok=True)
453
+ for wav in (vc.get("clips") or []):
454
+ src = clips_dir / wav
455
+ if src.exists() and src.is_file():
456
+ try:
457
+ shutil.copy2(src, dst_dir / wav)
458
+ except Exception:
459
+ pass
460
+
461
+ # Build indices using casting_loader helpers
462
+ db_dir = IDENTITIES_ROOT / video_name / "chroma_db"
463
+ client = ensure_chroma(db_dir)
464
+ n_faces = build_faces_index(faces_out, client, collection_name="index_faces", deepface_model='Facenet512', drop=True)
465
+ n_voices = build_voices_index(voices_out, client, collection_name="index_voices", drop=True)
466
+
467
+ # Summary of identities
468
+ face_identities = sorted([p.name for p in faces_out.iterdir() if p.is_dir()]) if faces_out.exists() else []
469
+ voice_identities = sorted([p.name for p in voices_out.iterdir() if p.is_dir()]) if voices_out.exists() else []
470
+
471
+ return {
472
+ "ok": True,
473
+ "video_name": video_name,
474
+ "faces_dir": str(faces_out),
475
+ "voices_dir": str(voices_out),
476
+ "db_dir": str(db_dir),
477
+ "n_faces_embeddings": n_faces,
478
+ "n_voices_embeddings": n_voices,
479
+ "face_identities": face_identities,
480
+ "voice_identities": voice_identities,
481
+ }
482
+
483
  @app.post("/refine_narration")
484
  async def refine_narration(
485
  dialogues_srt: str = Form(...),