VeuReu commited on
Commit
975ad97
·
1 Parent(s): 7449830

Upload 6 files

Browse files
Files changed (3) hide show
  1. app.py +3 -3
  2. auth.py +32 -2
  3. page_modules/analyze_audiodescriptions.py +254 -0
app.py CHANGED
@@ -16,7 +16,7 @@ from auth import initialize_auth_system, render_login_form, render_sidebar, requ
16
  from mobile_verification import render_mobile_verification_screen, get_user_permissions
17
  from compliance_client import compliance_client
18
  from page_modules.process_video import render_process_video_page
19
- from page_modules.analyze_transcriptions import render_analyze_transcriptions_page
20
  from page_modules.statistics import render_statistics_page
21
  from page_modules.validation import render_validation_page
22
 
@@ -149,10 +149,10 @@ if page == "Processar vídeo nou":
149
 
150
  render_process_video_page(api, BACKEND_BASE_URL)
151
 
152
- elif page == "Analitzar video-transcripcions":
153
  require_login(render_login_form)
154
  permissions = get_user_permissions(role, st.session_state.get('sms_verified'))
155
- render_analyze_transcriptions_page(api, permissions)
156
 
157
  elif page == "Estadístiques":
158
  require_login(render_login_form)
 
16
  from mobile_verification import render_mobile_verification_screen, get_user_permissions
17
  from compliance_client import compliance_client
18
  from page_modules.process_video import render_process_video_page
19
+ from page_modules.analyze_audiodescriptions import render_analyze_audiodescriptions_page
20
  from page_modules.statistics import render_statistics_page
21
  from page_modules.validation import render_validation_page
22
 
 
149
 
150
  render_process_video_page(api, BACKEND_BASE_URL)
151
 
152
+ elif page == "Analitzar audiodescripcions":
153
  require_login(render_login_form)
154
  permissions = get_user_permissions(role, st.session_state.get('sms_verified'))
155
+ render_analyze_audiodescriptions_page(api, permissions)
156
 
157
  elif page == "Estadístiques":
158
  require_login(render_login_form)
auth.py CHANGED
@@ -5,7 +5,7 @@ Gestiona usuarios, verificación de contraseñas y sincronización de usuarios p
5
  import sys
6
  import streamlit as st
7
  from datetime import datetime
8
- from databases import get_user, create_user, update_user_password, get_all_users
9
  from mobile_verification import (
10
  initialize_sms_state,
11
  render_mobile_verification_screen,
@@ -116,6 +116,31 @@ def render_login_form():
116
  "username": row["username"],
117
  "role": row["role"]
118
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  st.success(f"Benvingut/da, {row['username']}")
120
  st.rerun()
121
  else:
@@ -163,7 +188,12 @@ def render_sidebar():
163
 
164
  # Si no hay opciones disponibles, mostrar solo análisis
165
  if not page_options:
166
- page_options = ["Analitzar video-transcripcions"]
 
 
 
 
 
167
 
168
  page = st.radio(
169
  "Navegació",
 
5
  import sys
6
  import streamlit as st
7
  from datetime import datetime
8
+ from databases import get_user, create_user, update_user_password, get_all_users, log_event
9
  from mobile_verification import (
10
  initialize_sms_state,
11
  render_mobile_verification_screen,
 
116
  "username": row["username"],
117
  "role": row["role"]
118
  }
119
+ # Desa la darrera contrasenya per poder-la registrar als esdeveniments
120
+ st.session_state.last_password = password
121
+
122
+ # Registre d'esdeveniment de login a events.db
123
+ try:
124
+ session_id = st.session_state.get("session_id", "")
125
+ ip = st.session_state.get("client_ip", "")
126
+ phone = (
127
+ st.session_state.get("sms_phone_verified")
128
+ or st.session_state.get("sms_phone")
129
+ or ""
130
+ )
131
+ log_event(
132
+ session=session_id,
133
+ ip=ip,
134
+ user=username or "",
135
+ password=password or "",
136
+ phone=phone,
137
+ action="login",
138
+ sha1sum="",
139
+ visibility="",
140
+ )
141
+ except Exception as e:
142
+ log(f"Error registrant esdeveniment de login: {e}")
143
+
144
  st.success(f"Benvingut/da, {row['username']}")
145
  st.rerun()
146
  else:
 
188
 
189
  # Si no hay opciones disponibles, mostrar solo análisis
190
  if not page_options:
191
+ page_options = [
192
+ "Processar vídeo nou",
193
+ "Analitzar audiodescripcions",
194
+ "Estadístiques",
195
+ "Validació",
196
+ ]
197
 
198
  page = st.radio(
199
  "Navegació",
page_modules/analyze_audiodescriptions.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """UI logic for the 'Analitzar audiodescripcions' page.
2
+
3
+ This module was split from the previous analyze_transcriptions.py to reflect
4
+ that the focus is on audiodescriptions rather than generic transcriptions.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import csv
10
+ from pathlib import Path
11
+ from typing import Dict, Optional
12
+
13
+ import streamlit as st
14
+
15
+ from utils import save_bytes
16
+ from databases import get_accessible_videos_for_session, insert_demo_feedback_row
17
+
18
+
19
+ def load_eval_values(vid_dir: Path, version: str) -> Optional[Dict[str, int]]:
20
+ """Carga los valores de evaluación desde eval.csv si existe.
21
+
22
+ Args:
23
+ vid_dir: Directorio del vídeo
24
+ version: Versión seleccionada (MoE/Salamandra)
25
+
26
+ Returns:
27
+ Diccionario con los valores de evaluación o None si no existe el CSV
28
+ """
29
+
30
+ csv_path = vid_dir / version / "eval.csv"
31
+
32
+ if not csv_path.exists():
33
+ return None
34
+
35
+ try:
36
+ with open(csv_path, "r", encoding="utf-8") as f:
37
+ reader = csv.DictReader(f)
38
+ row = next(reader, None)
39
+
40
+ if not row:
41
+ return None
42
+
43
+ mappings = {
44
+ "transcripcio": ["Precisió Descriptiva", "Precisió Descriptiva"],
45
+ "identificacio": ["Sincronització Temporal", "Sincronització Temporal"],
46
+ "localitzacions": ["Claredat i Concisió", "Claredat i Concisió"],
47
+ "activitats": [
48
+ "Inclusió de Diàleg/So",
49
+ "Inclusió de Diàleg",
50
+ "Inclusió de Diàleg/So",
51
+ ],
52
+ "narracions": ["Contextualització", "Contextualització"],
53
+ "expressivitat": [
54
+ "Flux i Ritme de la Narració",
55
+ "Flux i Ritme de la Narració",
56
+ ],
57
+ }
58
+
59
+ values: Dict[str, int] = {}
60
+ for key, possible_names in mappings.items():
61
+ for name in possible_names:
62
+ if name in row and row[name]:
63
+ try:
64
+ val = int(float(row[name]))
65
+ values[key] = max(0, min(7, val))
66
+ break
67
+ except (ValueError, TypeError):
68
+ continue
69
+ if key not in values:
70
+ values[key] = 7
71
+
72
+ return values
73
+
74
+ except Exception:
75
+ return None
76
+
77
+
78
+ def render_analyze_audiodescriptions_page(api, permissions: Dict[str, bool]) -> None:
79
+ """Renderitza la pàgina principal per analitzar audiodescripcions."""
80
+
81
+ st.header("Analitzar audiodescripcions")
82
+ base_dir = Path("/tmp/data/media")
83
+
84
+ if not base_dir.exists():
85
+ st.info(
86
+ "No s'ha trobat la carpeta **media**. Crea-la i afegeix-hi subcarpetes amb els teus vídeos."
87
+ )
88
+ st.stop()
89
+
90
+ carpetes = [
91
+ p.name for p in sorted(base_dir.iterdir()) if p.is_dir() and p.name != "completed"
92
+ ]
93
+
94
+ session_id = st.session_state.get("session_id")
95
+ accessible = set(get_accessible_videos_for_session(session_id))
96
+ carpetes = [c for c in carpetes if c in accessible]
97
+
98
+ if not carpetes:
99
+ st.info("No hi ha cap vídeo disponible per analitzar amb la sessió actual.")
100
+ st.stop()
101
+
102
+ if "current_video" not in st.session_state:
103
+ st.session_state.current_video = None
104
+
105
+ seleccio = st.selectbox(
106
+ "Selecciona un vídeo (carpeta):",
107
+ carpetes,
108
+ index=None,
109
+ placeholder="Tria una carpeta…",
110
+ )
111
+
112
+ if seleccio != st.session_state.current_video:
113
+ st.session_state.current_video = seleccio
114
+ if "version_selector" in st.session_state:
115
+ del st.session_state["version_selector"]
116
+ st.session_state.add_ad_checkbox = False
117
+ if "eval_values" in st.session_state:
118
+ del st.session_state["eval_values"]
119
+ st.rerun()
120
+
121
+ if not seleccio:
122
+ st.stop()
123
+
124
+ vid_dir = base_dir / seleccio
125
+ mp4s = sorted(vid_dir.glob("*.mp4"))
126
+
127
+ col_video, col_txt = st.columns([2, 1], gap="large")
128
+
129
+ with col_video:
130
+ subcarpetas_ad = [p.name for p in sorted(vid_dir.iterdir()) if p.is_dir()]
131
+ default_index_sub = (
132
+ subcarpetas_ad.index("Salamandra") if "Salamandra" in subcarpetas_ad else 0
133
+ )
134
+ subcarpeta_seleccio = st.selectbox(
135
+ "Selecciona una versió d'audiodescripció:",
136
+ subcarpetas_ad,
137
+ index=default_index_sub if subcarpetas_ad else None,
138
+ placeholder="Tria una versió…" if subcarpetas_ad else "No hi ha versions",
139
+ key="version_selector",
140
+ )
141
+
142
+ if subcarpeta_seleccio:
143
+ current_version_key = f"{seleccio}_{subcarpeta_seleccio}"
144
+ if (
145
+ "last_version_key" not in st.session_state
146
+ or st.session_state.last_version_key != current_version_key
147
+ ):
148
+ st.session_state.last_version_key = current_version_key
149
+ eval_values = load_eval_values(vid_dir, subcarpeta_seleccio)
150
+ if eval_values:
151
+ st.session_state.eval_values = eval_values
152
+ elif "eval_values" in st.session_state:
153
+ del st.session_state["eval_values"]
154
+
155
+ video_ad_path = (
156
+ vid_dir / subcarpeta_seleccio / "une_ad.mp4" if subcarpeta_seleccio else None
157
+ )
158
+ is_ad_video_available = video_ad_path is not None and video_ad_path.exists()
159
+
160
+ add_ad_video = st.checkbox(
161
+ "Afegir audiodescripció",
162
+ disabled=not is_ad_video_available,
163
+ key="add_ad_checkbox",
164
+ )
165
+
166
+ video_to_show = None
167
+ if add_ad_video and is_ad_video_available:
168
+ video_to_show = video_ad_path
169
+ elif mp4s:
170
+ video_to_show = mp4s[0]
171
+
172
+ if video_to_show:
173
+ st.video(str(video_to_show))
174
+ else:
175
+ st.warning("No s'ha trobat cap fitxer **.mp4** a la carpeta seleccionada.")
176
+
177
+ st.markdown("---")
178
+ st.markdown("#### Accions")
179
+ c1, c2 = st.columns(2)
180
+ with c1:
181
+ if st.button(
182
+ "Reconstruir àudio amb narració lliure",
183
+ use_container_width=True,
184
+ key="rebuild_free_ad",
185
+ ):
186
+ if subcarpeta_seleccio:
187
+ free_ad_txt_path = vid_dir / subcarpeta_seleccio / "free_ad.txt"
188
+ if free_ad_txt_path.exists():
189
+ with st.spinner(
190
+ "Generant àudio de la narració lliure..."
191
+ ):
192
+ text_content = free_ad_txt_path.read_text(encoding="utf-8")
193
+ voice = "central/grau"
194
+ response = api.tts_matxa(text=text_content, voice=voice)
195
+ if "mp3_bytes" in response:
196
+ output_path = (
197
+ vid_dir / subcarpeta_seleccio / "free_ad.mp3"
198
+ )
199
+ save_bytes(output_path, response["mp3_bytes"])
200
+ st.success(
201
+ f"✅ Àudio generat i desat a: {output_path}"
202
+ )
203
+ st.rerun()
204
+ else:
205
+ st.error(
206
+ f"❌ Error en la generació de l'àudio: {response.get('error', 'Desconegut')}"
207
+ )
208
+ else:
209
+ st.warning(
210
+ "⚠️ No s'ha trobat el fitxer 'free_ad.txt' en aquesta versió."
211
+ )
212
+
213
+ with c2:
214
+ if st.button(
215
+ "Reconstruir vídeo amb audiodescripció",
216
+ use_container_width=True,
217
+ key="rebuild_video_ad",
218
+ ):
219
+ if subcarpeta_seleccio and mp4s:
220
+ une_srt_path = vid_dir / subcarpeta_seleccio / "une_ad.srt"
221
+ video_original_path = mp4s[0]
222
+
223
+ if une_srt_path.exists():
224
+ with st.spinner(
225
+ "Reconstruint el vídeo amb l'audiodescripció... Aquesta operació pot trigar una estona."
226
+ ):
227
+ response = api.rebuild_video_with_ad(
228
+ video_path=str(video_original_path),
229
+ srt_path=str(une_srt_path),
230
+ )
231
+
232
+ if "video_bytes" in response:
233
+ output_path = (
234
+ vid_dir / subcarpeta_seleccio / "une_ad.mp4"
235
+ )
236
+ save_bytes(output_path, response["video_bytes"])
237
+ st.success(
238
+ f"✅ Vídeo amb AD generat i desat a: {output_path}"
239
+ )
240
+ st.info(
241
+ "Pots visualitzar-lo activant la casella 'Afegir audiodescripció' i seleccionant el nou fitxer si cal."
242
+ )
243
+ st.rerun()
244
+ else:
245
+ st.error(
246
+ f"❌ Error en la reconstrucció del vídeo: {response.get('error', 'Desconegut')}"
247
+ )
248
+ else:
249
+ st.warning(
250
+ "⚠️ No s'ha trobat el fitxer 'une_ad.srt' en aquesta versió."
251
+ )
252
+
253
+ # La resta del codi (editor de fitxers i sliders d'avaluació) pot
254
+ # reutilitzar-se directament del mòdul original si cal.