VeuReu commited on
Commit
f12646c
·
1 Parent(s): 630a0ba

Upload 6 files

Browse files
api_client.py CHANGED
@@ -204,6 +204,49 @@ class APIClient:
204
  return {"error": str(e)}
205
 
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  def update_databases(self, payload: dict) -> dict:
208
  """Envia les sentències SQL generades a l'endpoint /update_databases."""
209
 
 
204
  return {"error": str(e)}
205
 
206
 
207
+ # ---- Pending videos (peding_videos) ----
208
+
209
+ def upload_pending_video(self, video_bytes: bytes, filename: str) -> dict:
210
+ """Sube un vídeo pendiente al engine (carpeta /data/peding_videos).
211
+
212
+ Usa el endpoint POST /peding_videos/upload_peding_video.
213
+ """
214
+
215
+ url = f"{self.base_url}/peding_videos/upload_peding_video"
216
+ files = {"video": (filename, io.BytesIO(video_bytes), "video/mp4")}
217
+ try:
218
+ r = self.session.post(url, files=files, timeout=self.timeout * 5)
219
+ r.raise_for_status()
220
+ return r.json() if r.headers.get("content-type", "").startswith("application/json") else {"status": "ok"}
221
+ except requests.exceptions.RequestException as e:
222
+ return {"error": str(e)}
223
+
224
+
225
+ def list_pending_videos(self) -> dict:
226
+ """Llista els vídeos pendents al backend (endpoint GET /peding_videos/list_peding_videos)."""
227
+
228
+ url = f"{self.base_url}/peding_videos/list_peding_videos"
229
+ try:
230
+ r = self.session.get(url, timeout=self.timeout * 5)
231
+ r.raise_for_status()
232
+ return r.json()
233
+ except requests.exceptions.RequestException as e:
234
+ return {"error": str(e)}
235
+
236
+
237
+ def download_pending_video(self, sha1sum: str) -> dict:
238
+ """Descarrega un vídeo pendent per sha1 (GET /peding_videos/download_peding_video)."""
239
+
240
+ url = f"{self.base_url}/peding_videos/download_peding_video"
241
+ params = {"sha1": sha1sum}
242
+ try:
243
+ r = self.session.get(url, params=params, timeout=self.timeout * 5)
244
+ r.raise_for_status()
245
+ return {"video_bytes": r.content}
246
+ except requests.exceptions.RequestException as e:
247
+ return {"error": str(e)}
248
+
249
+
250
  def update_databases(self, payload: dict) -> dict:
251
  """Envia les sentències SQL generades a l'endpoint /update_databases."""
252
 
page_modules/process_video.py CHANGED
@@ -15,7 +15,7 @@ import streamlit as st
15
  from PIL import Image, ImageDraw
16
  from databases import log_event, has_video_approval_event
17
  from compliance_client import compliance_client
18
- from persistent_data_gate import ensure_temp_databases
19
 
20
 
21
  def get_all_catalan_names():
@@ -371,6 +371,26 @@ def render_process_video_page(api, backend_base_url: str) -> None:
371
  except Exception as e:
372
  print(f"[events] Error registrant esdeveniment de pujada: {e}")
373
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  # Marcar vídeo com pendent de validació i enviar SMS al validor
375
  st.session_state.video_requires_validation = True
376
  st.session_state.video_validation_approved = False
 
15
  from PIL import Image, ImageDraw
16
  from databases import log_event, has_video_approval_event
17
  from compliance_client import compliance_client
18
+ from persistent_data_gate import ensure_temp_databases, _load_data_origin
19
 
20
 
21
  def get_all_catalan_names():
 
371
  except Exception as e:
372
  print(f"[events] Error registrant esdeveniment de pujada: {e}")
373
 
374
+ # Si treballem en mode external, enviar el vídeo a pending_videos de l'engine
375
+ try:
376
+ base_dir = Path(__file__).parent.parent
377
+ data_origin = _load_data_origin(base_dir)
378
+ if data_origin == "external":
379
+ pending_root = base_dir / "temp" / "pending_videos" / sha1
380
+ pending_root.mkdir(parents=True, exist_ok=True)
381
+ local_pending_path = pending_root / "video.mp4"
382
+ # Guardar còpia local del vídeo pendent
383
+ with local_pending_path.open("wb") as f_pending:
384
+ f_pending.write(video_bytes)
385
+
386
+ # Enviar el vídeo al backend engine perquè aparegui a la llista de pendents
387
+ try:
388
+ api.upload_pending_video(video_bytes, uploaded_file.name)
389
+ except Exception:
390
+ pass
391
+ except Exception:
392
+ pass
393
+
394
  # Marcar vídeo com pendent de validació i enviar SMS al validor
395
  st.session_state.video_requires_validation = True
396
  st.session_state.video_validation_approved = False
page_modules/validation.py CHANGED
@@ -6,9 +6,11 @@ from datetime import datetime
6
  from pathlib import Path
7
  from typing import Dict
8
 
 
9
  import streamlit as st
10
 
11
  from databases import get_accessible_videos_with_sha1, log_event
 
12
 
13
 
14
  def render_validation_page(
@@ -25,37 +27,88 @@ def render_validation_page(
25
 
26
  tab_videos, tab_ads = st.tabs(["📹 Validar Vídeos", "🎬 Validar Audiodescripcions"])
27
 
28
- # Llista de vídeos accessibles des de demo/temp/videos.db
 
 
 
29
  session_id = st.session_state.get("session_id")
30
- accessible_rows = get_accessible_videos_with_sha1(session_id)
31
 
32
- # Base de media: demo/temp/media/<sha1sum>
33
- base_media_dir = Path(__file__).resolve().parent.parent / "temp" / "media"
 
34
 
35
  with tab_videos:
36
  st.subheader("📹 Validar Vídeos Pujats")
37
 
38
  video_folders = []
39
- for row in accessible_rows:
40
- sha1 = row["sha1sum"]
41
- video_name = row["video_name"] or row["sha1sum"]
42
- folder = base_media_dir / sha1
43
- if not folder.exists() or not folder.is_dir():
44
- continue
45
- video_files = list(folder.glob("*.mp4")) + list(folder.glob("*.avi")) + list(folder.glob("*.mov"))
46
- if not video_files:
47
- continue
48
- mod_time = folder.stat().st_mtime
49
- fecha = datetime.fromtimestamp(mod_time).strftime("%Y-%m-%d %H:%M")
50
- video_folders.append(
51
- {
52
- "sha1sum": sha1,
53
- "video_name": video_name,
54
- "path": str(folder),
55
- "created_at": fecha,
56
- "video_files": video_files,
57
- }
58
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  if not video_folders:
61
  st.info("📝 No hi ha vídeos pujats pendents de validació.")
@@ -79,6 +132,28 @@ def render_validation_page(
79
  st.markdown(f"**Data:** {video_seleccionat['created_at']}")
80
  st.markdown(f"**Arxius:** {len(video_seleccionat['video_files'])} vídeos trobats")
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  if video_seleccionat["video_files"]:
83
  video_path = str(video_seleccionat["video_files"][0])
84
  st.markdown("**Vídeo principal:**")
@@ -126,6 +201,22 @@ def render_validation_page(
126
  else:
127
  st.error("❌ Error registrant el veredicte al servei de compliance")
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  with col_btn2:
130
  if st.button("❌ Rebutjar", type="secondary", key=f"reject_video_{video_seleccionat['video_name']}"):
131
  success = compliance_client.record_validator_decision(
 
6
  from pathlib import Path
7
  from typing import Dict
8
 
9
+ import shutil
10
  import streamlit as st
11
 
12
  from databases import get_accessible_videos_with_sha1, log_event
13
+ from persistent_data_gate import _load_data_origin
14
 
15
 
16
  def render_validation_page(
 
27
 
28
  tab_videos, tab_ads = st.tabs(["📹 Validar Vídeos", "🎬 Validar Audiodescripcions"])
29
 
30
+ base_dir = Path(__file__).resolve().parent.parent
31
+ data_origin = _load_data_origin(base_dir)
32
+
33
+ # Llista de vídeos accessibles (mode internal) o pendents al backend (mode external)
34
  session_id = st.session_state.get("session_id")
35
+ accessible_rows = get_accessible_videos_with_sha1(session_id) if data_origin == "internal" else []
36
 
37
+ # Rutes base per a media i vídeos pendents
38
+ base_media_dir = base_dir / "temp" / "media"
39
+ pending_root = base_dir / "temp" / "pending_videos"
40
 
41
  with tab_videos:
42
  st.subheader("📹 Validar Vídeos Pujats")
43
 
44
  video_folders = []
45
+
46
+ if data_origin == "internal":
47
+ # Mode intern: llistar vídeos accessibles des de temp/media
48
+ for row in accessible_rows:
49
+ sha1 = row["sha1sum"]
50
+ video_name = row["video_name"] or row["sha1sum"]
51
+ folder = base_media_dir / sha1
52
+ if not folder.exists() or not folder.is_dir():
53
+ continue
54
+ video_files = list(folder.glob("*.mp4")) + list(folder.glob("*.avi")) + list(folder.glob("*.mov"))
55
+ if not video_files:
56
+ continue
57
+ mod_time = folder.stat().st_mtime
58
+ fecha = datetime.fromtimestamp(mod_time).strftime("%Y-%m-%d %H:%M")
59
+ video_folders.append(
60
+ {
61
+ "sha1sum": sha1,
62
+ "video_name": video_name,
63
+ "path": str(folder),
64
+ "created_at": fecha,
65
+ "video_files": video_files,
66
+ }
67
+ )
68
+ else:
69
+ # Mode external: llistar vídeos pendents des de l'engine
70
+ api_client = st.session_state.get("api_client")
71
+ if api_client is not None:
72
+ try:
73
+ resp = api_client.list_pending_videos()
74
+ except Exception:
75
+ resp = {"error": "exception"}
76
+
77
+ pending_list = []
78
+ if isinstance(resp, dict) and not resp.get("error"):
79
+ # Pot ser un dict amb clau "videos" o directament una llista
80
+ if isinstance(resp.get("videos"), list):
81
+ pending_list = resp["videos"]
82
+ elif isinstance(resp.get("items"), list):
83
+ pending_list = resp["items"]
84
+ elif isinstance(resp.get("results"), list):
85
+ pending_list = resp["results"]
86
+ elif isinstance(resp, list):
87
+ pending_list = resp
88
+ elif isinstance(resp, list):
89
+ pending_list = resp
90
+
91
+ for item in pending_list:
92
+ sha1 = item.get("sha1") or item.get("video_hash") or item.get("id")
93
+ if not sha1:
94
+ continue
95
+ video_name = item.get("latest_video") or sha1
96
+ # Carpeta local on descarregarem el vídeo pendent si cal
97
+ folder = pending_root / sha1
98
+ if folder.exists():
99
+ video_files = list(folder.glob("*.mp4"))
100
+ else:
101
+ video_files = []
102
+ created_at = item.get("created_at") or datetime.utcnow().strftime("%Y-%m-%d %H:%M")
103
+ video_folders.append(
104
+ {
105
+ "sha1sum": sha1,
106
+ "video_name": video_name,
107
+ "path": str(folder),
108
+ "created_at": created_at,
109
+ "video_files": video_files,
110
+ }
111
+ )
112
 
113
  if not video_folders:
114
  st.info("📝 No hi ha vídeos pujats pendents de validació.")
 
132
  st.markdown(f"**Data:** {video_seleccionat['created_at']}")
133
  st.markdown(f"**Arxius:** {len(video_seleccionat['video_files'])} vídeos trobats")
134
 
135
+ # Assegurar que disposem del fitxer local en mode external
136
+ if data_origin == "external" and not video_seleccionat["video_files"]:
137
+ api_client = st.session_state.get("api_client")
138
+ if api_client is not None:
139
+ try:
140
+ resp = api_client.download_pending_video(video_seleccionat["sha1sum"])
141
+ except Exception:
142
+ resp = {"error": "exception"}
143
+
144
+ video_bytes = (
145
+ resp.get("video_bytes")
146
+ if isinstance(resp, dict)
147
+ else None
148
+ )
149
+ if video_bytes:
150
+ local_folder = pending_root / video_seleccionat["sha1sum"]
151
+ local_folder.mkdir(parents=True, exist_ok=True)
152
+ local_path = local_folder / "video.mp4"
153
+ with local_path.open("wb") as f:
154
+ f.write(video_bytes)
155
+ video_seleccionat["video_files"] = [local_path]
156
+
157
  if video_seleccionat["video_files"]:
158
  video_path = str(video_seleccionat["video_files"][0])
159
  st.markdown("**Vídeo principal:**")
 
201
  else:
202
  st.error("❌ Error registrant el veredicte al servei de compliance")
203
 
204
+ # 3) En mode external, moure el vídeo de temp/pending_videos a temp/media
205
+ if data_origin == "external":
206
+ sha1 = video_seleccionat["sha1sum"]
207
+ local_pending_dir = pending_root / sha1
208
+ local_media_dir = base_media_dir / sha1
209
+ try:
210
+ local_media_dir.mkdir(parents=True, exist_ok=True)
211
+ src = local_pending_dir / "video.mp4"
212
+ if src.exists():
213
+ dst = local_media_dir / "video.mp4"
214
+ shutil.copy2(src, dst)
215
+ if local_pending_dir.exists():
216
+ shutil.rmtree(local_pending_dir)
217
+ except Exception:
218
+ pass
219
+
220
  with col_btn2:
221
  if st.button("❌ Rebutjar", type="secondary", key=f"reject_video_{video_seleccionat['video_name']}"):
222
  success = compliance_client.record_validator_decision(