Upload 2 files
Browse files- api_client.py +19 -0
- page_modules/new_video_processing.py +39 -7
api_client.py
CHANGED
|
@@ -163,6 +163,25 @@ class APIClient:
|
|
| 163 |
return {"error": str(e)}
|
| 164 |
|
| 165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
# --- Initial transcription (generate_initial_srt_and_info + downloads) ---
|
| 167 |
|
| 168 |
def generate_initial_srt_and_info(self, sha1sum: str) -> dict:
|
|
|
|
| 163 |
return {"error": str(e)}
|
| 164 |
|
| 165 |
|
| 166 |
+
# --- Upload original video to engine media storage ---
|
| 167 |
+
|
| 168 |
+
def upload_original_video(self, video_bytes: bytes, video_name: str) -> dict:
|
| 169 |
+
"""Sube el vídeo original al engine para procesamiento posterior.
|
| 170 |
+
|
| 171 |
+
Endpoint: POST /media/upload_original_video
|
| 172 |
+
El engine calcula el SHA1 y lo guarda en /data/media/<sha1>/clip/<video_name>
|
| 173 |
+
"""
|
| 174 |
+
url = f"{self.base_url}/media/upload_original_video"
|
| 175 |
+
token = os.getenv("VEUREU_TOKEN", "")
|
| 176 |
+
try:
|
| 177 |
+
files = {"video": (video_name, video_bytes, "video/mp4")}
|
| 178 |
+
params = {"token": token}
|
| 179 |
+
r = self.session.post(url, files=files, params=params, timeout=self.timeout * 5)
|
| 180 |
+
r.raise_for_status()
|
| 181 |
+
return r.json() if r.headers.get("content-type", "").startswith("application/json") else {"status": "ok"}
|
| 182 |
+
except requests.exceptions.RequestException as e:
|
| 183 |
+
return {"error": str(e)}
|
| 184 |
+
|
| 185 |
# --- Initial transcription (generate_initial_srt_and_info + downloads) ---
|
| 186 |
|
| 187 |
def generate_initial_srt_and_info(self, sha1sum: str) -> dict:
|
page_modules/new_video_processing.py
CHANGED
|
@@ -601,7 +601,8 @@ def render_process_video_page(api, backend_base_url: str) -> None:
|
|
| 601 |
st.session_state.casting_finalized = False
|
| 602 |
|
| 603 |
_log(f"[DETECT] Iniciando detección para vídeo: {v['name']}")
|
| 604 |
-
_log(f"[DETECT] Parámetros:
|
|
|
|
| 605 |
|
| 606 |
resp = api.create_initial_casting(
|
| 607 |
video_bytes=v["bytes"],
|
|
@@ -965,7 +966,7 @@ def render_process_video_page(api, backend_base_url: str) -> None:
|
|
| 965 |
with c2:
|
| 966 |
name_key = f"{key_prefix}_name"
|
| 967 |
desc_key = f"{key_prefix}_desc"
|
| 968 |
-
default_name =
|
| 969 |
st.text_input("Nom del clúster", value=st.session_state.get(name_key, default_name), key=name_key)
|
| 970 |
st.text_area("Descripció", value=st.session_state.get(desc_key, ""), key=desc_key, height=80)
|
| 971 |
|
|
@@ -979,7 +980,9 @@ def render_process_video_page(api, backend_base_url: str) -> None:
|
|
| 979 |
if not scene_clusters:
|
| 980 |
st.info("No s'han detectat clústers d'escenes en aquest clip.")
|
| 981 |
else:
|
|
|
|
| 982 |
for sidx, sc in enumerate(scene_clusters):
|
|
|
|
| 983 |
try:
|
| 984 |
folder_name = Path(sc.get("folder") or "").name
|
| 985 |
except Exception:
|
|
@@ -1002,12 +1005,24 @@ def render_process_video_page(api, backend_base_url: str) -> None:
|
|
| 1002 |
cur = 0
|
| 1003 |
st.session_state[f"{key_prefix}_idx"] = cur
|
| 1004 |
fname = frames[cur]
|
|
|
|
| 1005 |
if str(fname).startswith("/files/"):
|
| 1006 |
img_url = f"{backend_base_url}/preprocessing{fname}"
|
|
|
|
|
|
|
|
|
|
| 1007 |
else:
|
| 1008 |
-
|
| 1009 |
-
|
| 1010 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1011 |
st.markdown(f"**{sidx+1}. Escena — {sc.get('num_frames', 0)} frames**")
|
| 1012 |
spacer_col, main_content_col = st.columns([0.12, 0.88])
|
| 1013 |
with spacer_col:
|
|
@@ -1176,7 +1191,7 @@ def render_process_video_page(api, backend_base_url: str) -> None:
|
|
| 1176 |
clip_local = seg.get("clip_path")
|
| 1177 |
fname = os.path.basename(clip_local) if clip_local else None
|
| 1178 |
if fname:
|
| 1179 |
-
default_voice_name =
|
| 1180 |
voice_clusters.setdefault(lbl, {"label": lbl, "name": default_voice_name, "description": "", "clips": []})
|
| 1181 |
vpref = f"voice_{int(lbl):02d}"
|
| 1182 |
vname_custom = st.session_state.get(f"{vpref}_name")
|
|
@@ -1305,7 +1320,7 @@ def render_process_video_page(api, backend_base_url: str) -> None:
|
|
| 1305 |
if not (isinstance(lbl, int) and lbl >= 0):
|
| 1306 |
continue
|
| 1307 |
vpref = f"voice_{int(lbl):02d}"
|
| 1308 |
-
default_voice_name =
|
| 1309 |
vname_custom = st.session_state.get(f"{vpref}_name") or default_voice_name
|
| 1310 |
vname_normalized = normalize_name(vname_custom)
|
| 1311 |
vdesc = st.session_state.get(f"{vpref}_desc", "").strip()
|
|
@@ -1460,6 +1475,23 @@ def render_process_video_page(api, backend_base_url: str) -> None:
|
|
| 1460 |
except Exception as e_up:
|
| 1461 |
_log(f"[embeddings] Error pujant embeddings a engine: {e_up}")
|
| 1462 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1463 |
# 2) Pipeline inicial: generate_initial_srt_and_info + descarregar fitxers
|
| 1464 |
try:
|
| 1465 |
init_resp = api.generate_initial_srt_and_info(sha1)
|
|
|
|
| 601 |
st.session_state.casting_finalized = False
|
| 602 |
|
| 603 |
_log(f"[DETECT] Iniciando detección para vídeo: {v['name']}")
|
| 604 |
+
_log(f"[DETECT] Parámetros faces: k={face_max_groups}, min_cluster={face_min_cluster}, max_frames={max_frames}")
|
| 605 |
+
_log(f"[DETECT] Parámetros voices: k={voice_max_groups}, min_cluster={voice_min_cluster}")
|
| 606 |
|
| 607 |
resp = api.create_initial_casting(
|
| 608 |
video_bytes=v["bytes"],
|
|
|
|
| 966 |
with c2:
|
| 967 |
name_key = f"{key_prefix}_name"
|
| 968 |
desc_key = f"{key_prefix}_desc"
|
| 969 |
+
default_name = f"SPEAKER_{lbl:02d}"
|
| 970 |
st.text_input("Nom del clúster", value=st.session_state.get(name_key, default_name), key=name_key)
|
| 971 |
st.text_area("Descripció", value=st.session_state.get(desc_key, ""), key=desc_key, height=80)
|
| 972 |
|
|
|
|
| 980 |
if not scene_clusters:
|
| 981 |
st.info("No s'han detectat clústers d'escenes en aquest clip.")
|
| 982 |
else:
|
| 983 |
+
vname = st.session_state.video_name_from_engine
|
| 984 |
for sidx, sc in enumerate(scene_clusters):
|
| 985 |
+
_log(f"[SCENE] Cluster {sidx}: keys={list(sc.keys())}, frame_files={sc.get('frame_files', [])[:3]}")
|
| 986 |
try:
|
| 987 |
folder_name = Path(sc.get("folder") or "").name
|
| 988 |
except Exception:
|
|
|
|
| 1005 |
cur = 0
|
| 1006 |
st.session_state[f"{key_prefix}_idx"] = cur
|
| 1007 |
fname = frames[cur]
|
| 1008 |
+
# Construir URL per a frames d'escenes (frames complets, no crops)
|
| 1009 |
if str(fname).startswith("/files/"):
|
| 1010 |
img_url = f"{backend_base_url}/preprocessing{fname}"
|
| 1011 |
+
elif str(fname).startswith("http"):
|
| 1012 |
+
# URL absoluta
|
| 1013 |
+
img_url = fname
|
| 1014 |
else:
|
| 1015 |
+
# Construir URL relativa usant video_name del engine
|
| 1016 |
+
if vname:
|
| 1017 |
+
img_url = f"{backend_base_url}/preprocessing/files/{vname}/frames/{fname}"
|
| 1018 |
+
else:
|
| 1019 |
+
base = sc.get("image_url") or sc.get("folder") or ""
|
| 1020 |
+
if base.startswith("/files/"):
|
| 1021 |
+
base_dir = "/".join(base.split("/")[:-1])
|
| 1022 |
+
img_url = f"{backend_base_url}/preprocessing{base_dir}/{fname}"
|
| 1023 |
+
else:
|
| 1024 |
+
img_url = f"{backend_base_url}/preprocessing/files/{fname}"
|
| 1025 |
+
_log(f"[SCENE] img_url={img_url}, fname={fname}, vname={vname}")
|
| 1026 |
st.markdown(f"**{sidx+1}. Escena — {sc.get('num_frames', 0)} frames**")
|
| 1027 |
spacer_col, main_content_col = st.columns([0.12, 0.88])
|
| 1028 |
with spacer_col:
|
|
|
|
| 1191 |
clip_local = seg.get("clip_path")
|
| 1192 |
fname = os.path.basename(clip_local) if clip_local else None
|
| 1193 |
if fname:
|
| 1194 |
+
default_voice_name = f"SPEAKER_{int(lbl):02d}"
|
| 1195 |
voice_clusters.setdefault(lbl, {"label": lbl, "name": default_voice_name, "description": "", "clips": []})
|
| 1196 |
vpref = f"voice_{int(lbl):02d}"
|
| 1197 |
vname_custom = st.session_state.get(f"{vpref}_name")
|
|
|
|
| 1320 |
if not (isinstance(lbl, int) and lbl >= 0):
|
| 1321 |
continue
|
| 1322 |
vpref = f"voice_{int(lbl):02d}"
|
| 1323 |
+
default_voice_name = f"SPEAKER_{int(lbl):02d}"
|
| 1324 |
vname_custom = st.session_state.get(f"{vpref}_name") or default_voice_name
|
| 1325 |
vname_normalized = normalize_name(vname_custom)
|
| 1326 |
vdesc = st.session_state.get(f"{vpref}_desc", "").strip()
|
|
|
|
| 1475 |
except Exception as e_up:
|
| 1476 |
_log(f"[embeddings] Error pujant embeddings a engine: {e_up}")
|
| 1477 |
|
| 1478 |
+
# 1.5) Pujar el vídeo original al engine (necessari per al pipeline)
|
| 1479 |
+
try:
|
| 1480 |
+
video_bytes = v.get("bytes")
|
| 1481 |
+
video_name = v.get("name", "video.mp4")
|
| 1482 |
+
if video_bytes:
|
| 1483 |
+
upload_video_resp = api.upload_original_video(video_bytes, video_name)
|
| 1484 |
+
_log(f"[upload_video] upload_original_video resp: {upload_video_resp}")
|
| 1485 |
+
if isinstance(upload_video_resp, dict) and upload_video_resp.get("error"):
|
| 1486 |
+
result_placeholder.error(f"❌ Error pujant vídeo a engine: {upload_video_resp.get('error')}")
|
| 1487 |
+
return
|
| 1488 |
+
else:
|
| 1489 |
+
_log("[upload_video] No hi ha bytes del vídeo per pujar")
|
| 1490 |
+
except Exception as e_vid:
|
| 1491 |
+
_log(f"[upload_video] Error pujant vídeo a engine: {e_vid}")
|
| 1492 |
+
result_placeholder.error(f"❌ Error pujant vídeo: {e_vid}")
|
| 1493 |
+
return
|
| 1494 |
+
|
| 1495 |
# 2) Pipeline inicial: generate_initial_srt_and_info + descarregar fitxers
|
| 1496 |
try:
|
| 1497 |
init_resp = api.generate_initial_srt_and_info(sha1)
|