VeuReu commited on
Commit
69ade9a
·
verified ·
1 Parent(s): e5817e2

Update main_process/moe_router.py

Browse files
Files changed (1) hide show
  1. main_process/moe_router.py +1073 -64
main_process/moe_router.py CHANGED
@@ -89,23 +89,7 @@ class GPT5Client:
89
  return content
90
 
91
 
92
- def get_video_duration(video_path: str) -> float:
93
- """
94
- Devuelve la duración total del vídeo en segundos.
95
- """
96
- cap = cv2.VideoCapture(video_path)
97
- if not cap.isOpened():
98
- raise RuntimeError(f"No s'ha pogut obrir el vídeo: {video_path}")
99
-
100
- fps = cap.get(cv2.CAP_PROP_FPS) or 25.0
101
- total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) or 0
102
- cap.release()
103
-
104
- duration_sec = total_frames / fps if total_frames > 0 else 0.0
105
- return duration_sec
106
-
107
  def generate_srt_con_silencios(path_srt_original, path_srt_silences, video_path):
108
- # Obtenir duració total del vídeo
109
  duracio_total = get_video_duration(video_path)
110
 
111
  with open(path_srt_original, "r", encoding="utf-8-sig") as f:
@@ -119,37 +103,48 @@ def generate_srt_con_silencios(path_srt_original, path_srt_silences, video_path)
119
  for block in blocks:
120
  lines = block.split("\n")
121
  time_range = lines[1]
122
- print(time_range)
123
  content = " ".join(line.strip() for line in lines[2:])
124
 
125
  start_str, end_str = time_range.split(" --> ")
126
  start_sec = srt_time_to_seconds(start_str)
127
  end_sec = srt_time_to_seconds(end_str)
128
 
129
- # Afegir silenci si hi ha espai
130
  if prev < start_sec:
131
  srt_entries.append(
132
  f"{idx}\n{seconds_to_srt_time(prev)} --> {seconds_to_srt_time(start_sec)}\n[silenci]\n"
133
  )
134
  idx += 1
135
 
136
- # Afegir clip amb text
137
  srt_entries.append(
138
  f"{idx}\n{seconds_to_srt_time(start_sec)} --> {seconds_to_srt_time(end_sec)}\n{content}\n"
139
  )
140
  idx += 1
141
  prev = end_sec
142
 
143
- # Afegir últim bloc de silenci si la duració del vídeo és més llarga que l'últim clip
144
  if prev < duracio_total:
145
  srt_entries.append(
146
  f"{idx}\n{seconds_to_srt_time(prev)} --> {seconds_to_srt_time(duracio_total)}\n[silenci]\n"
147
  )
148
 
149
- # Guardar a l'arxiu final
150
  with open(path_srt_silences, "w", encoding="utf-8") as f:
151
  f.write("\n".join(srt_entries))
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  def srt_time_to_seconds(s):
154
  h, m, rest = s.split(":")
155
  s, ms = rest.split(",")
@@ -166,17 +161,14 @@ class Add_AD:
166
  def __init__(self, data: DataHub):
167
  self.data = data
168
 
169
- def __call__(self, state: NState, srt_modified_silence, srt_modified_silence_con_ad) -> NState:
170
  print("Add_Ad.__call__ iniciado")
171
 
172
- # Leer SRT original
173
- with open(srt_modified_silence, "r", encoding="utf-8") as f:
174
  srt_text = f.read()
175
 
176
- # Frames del video
177
  frames = self.data.video.get('info_escenas', {})
178
 
179
- # Parsear SRT a bloques
180
  srt_blocks = []
181
  srt_blocks_modified=[]
182
  pattern = re.compile(
@@ -197,7 +189,11 @@ class Add_AD:
197
  })
198
 
199
  index=1
200
- # Procesar cada bloque
 
 
 
 
201
  for block in srt_blocks:
202
  if "[silenci]" in block["text"]:
203
  start_block = block["start"]
@@ -209,30 +205,75 @@ class Add_AD:
209
  "index":index,
210
  "start": start_block,
211
  "end": end_block,
212
- "text": f"(AD): {frame.get('descripcion', '')}"
213
  })
214
  index+=1
215
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  elif start_block<frame.get("end")<end_block:
217
  srt_blocks_modified.append({
218
  "index":index,
219
  "start": start_block,
220
  "end": frame.get("end"),
221
- "text": f"(AD): {frame.get('descripcion', '')}"
222
  })
223
  start_block=frame.get("end")
224
  index+=1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
  elif start_block==frame.get("start") and start_block<end_block and frame.get("end")>=end_block:
227
  srt_blocks_modified.append({
228
  "index":index,
229
  "start": start_block,
230
  "end": end_block,
231
- "text": f"(AD): {frame.get('descripcion', '')}"
232
  })
233
  start_block=end_block
234
  index+=1
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  else:
237
  srt_blocks_modified.append({
238
  "index": index,
@@ -242,7 +283,12 @@ class Add_AD:
242
  })
243
  index+=1
244
 
245
- # Reconstruir el SRT final
 
 
 
 
 
246
  srt_final = ""
247
 
248
  for block in srt_blocks_modified:
@@ -250,53 +296,658 @@ class Add_AD:
250
  end_tc = seconds_to_srt_time(block["end"])
251
  srt_final += f"{block['index']}\n{start_tc} --> {end_tc}\n{block['text']}\n\n"
252
 
253
- # Guardar en un nuevo archivo
254
- with open(srt_modified_silence_con_ad, "w", encoding="utf-8") as f:
255
  f.write(srt_final)
256
 
257
- # Actualizar estado
258
- state['srt_con_audiodescripcion'] = srt_final
 
 
 
259
  return state
260
 
261
- class Free_Narration:
262
- def __init__(self, data: DataHub):
263
- self.data = data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
- def __call__(self, state: NState, srt_original_silence_con_ad, story_path) -> NState:
266
- print("Free_Narration.__call__ iniciado")
 
 
 
 
 
 
 
267
 
268
- descriptions=[]
269
- frames = self.data.video.get('info_escenas', [])
270
- for frame in frames:
271
- descriptions.append(frame["descripcion"])
272
 
273
- full_transcription = self.data.video.get('full_transcription', [])
 
 
274
 
275
- with open(srt_original_silence_con_ad, "r", encoding="utf-8-sig") as f:
276
- diarization_text = f.read()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  prompt = f"""
279
- La teva tasca és elaborar una descripció lliure d'un vídeo d'unes 100 paraules a partir de la informació següent:
280
- 1.) A partir del vídeo s'han extret captures de pantalla en els moments en què es canviava d'escena i tens una descripció de cadascuna d'elles a: {descriptions}
281
- 2.) La transcripció completa del vídeo és: {full_transcription}
282
- Per tant, a partir de tota aquesta informació, genera'm la història completa, intentant incloure els personatges identificats i la trama general de la història.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  """
284
- out = state['llm_Salamandra'](prompt)
285
- print(out)
286
 
287
- with open(story_path, "w", encoding="utf-8-sig") as f:
288
- f.write(out["text"])
 
 
289
 
290
- state['free_narration'] = out
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
  return state
293
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  class Valoracion_Final:
295
- def __call__(self, state, srt_final, csv_evaluacion):
296
  print("Valoracion_Final.__call__ iniciat")
297
 
298
  # Llegeix el contingut del fitxer SRT
299
- with open(srt_final, "r", encoding="utf-8-sig") as f:
300
  srt_text = f.read().strip()
301
 
302
  # Defineix el prompt principal
@@ -341,6 +992,40 @@ class Valoracion_Final:
341
 
342
  return state
343
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  def srt_update(srt_video, srt_video_modified):
345
  with open(srt_video, "r", encoding="utf-8") as f:
346
  srt_text = f.read()
@@ -512,22 +1197,346 @@ async def generate_salamadra_result(
512
  generate_srt_con_silencios(srt_original, tmp.name, video_path)
513
 
514
  datahub=DataHub(informacion_json)
 
 
515
  add_ad = Add_AD(datahub)
516
- free_narration = Free_Narration(datahub)
 
 
 
 
 
 
517
  valoracion_final = Valoracion_Final()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
 
519
  GPTclient = GPT5Client(api_key=OPEN_AI_KEY)
520
- salamandraclient = SalamandraClient()
521
 
 
522
  state = {
523
  "llm_GPT": GPTclient.chat,
524
  "llm_Salamandra": salamandraclient.chat
525
  }
526
 
527
- state = add_ad(state, tmp.name, srt_final)
528
- state= free_narration(state, srt_final, free_narration_salamandra)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
529
  state = valoracion_final(state, srt_final, csv_evaluacion)
530
- tmp.close()
 
 
 
 
 
 
531
 
532
  srt_update(srt_final,srt_final)
533
 
 
89
  return content
90
 
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  def generate_srt_con_silencios(path_srt_original, path_srt_silences, video_path):
 
93
  duracio_total = get_video_duration(video_path)
94
 
95
  with open(path_srt_original, "r", encoding="utf-8-sig") as f:
 
103
  for block in blocks:
104
  lines = block.split("\n")
105
  time_range = lines[1]
 
106
  content = " ".join(line.strip() for line in lines[2:])
107
 
108
  start_str, end_str = time_range.split(" --> ")
109
  start_sec = srt_time_to_seconds(start_str)
110
  end_sec = srt_time_to_seconds(end_str)
111
 
 
112
  if prev < start_sec:
113
  srt_entries.append(
114
  f"{idx}\n{seconds_to_srt_time(prev)} --> {seconds_to_srt_time(start_sec)}\n[silenci]\n"
115
  )
116
  idx += 1
117
 
 
118
  srt_entries.append(
119
  f"{idx}\n{seconds_to_srt_time(start_sec)} --> {seconds_to_srt_time(end_sec)}\n{content}\n"
120
  )
121
  idx += 1
122
  prev = end_sec
123
 
 
124
  if prev < duracio_total:
125
  srt_entries.append(
126
  f"{idx}\n{seconds_to_srt_time(prev)} --> {seconds_to_srt_time(duracio_total)}\n[silenci]\n"
127
  )
128
 
 
129
  with open(path_srt_silences, "w", encoding="utf-8") as f:
130
  f.write("\n".join(srt_entries))
131
 
132
+
133
+ def get_video_duration(video_path: str) -> float:
134
+ """
135
+ Devuelve la duración total del vídeo en segundos.
136
+ """
137
+ cap = cv2.VideoCapture(video_path)
138
+ if not cap.isOpened():
139
+ raise RuntimeError(f"No s'ha pogut obrir el vídeo: {video_path}")
140
+
141
+ fps = cap.get(cv2.CAP_PROP_FPS) or 25.0
142
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) or 0
143
+ cap.release()
144
+
145
+ duration_sec = total_frames / fps if total_frames > 0 else 0.0
146
+ return duration_sec
147
+
148
  def srt_time_to_seconds(s):
149
  h, m, rest = s.split(":")
150
  s, ms = rest.split(",")
 
161
  def __init__(self, data: DataHub):
162
  self.data = data
163
 
164
+ def __call__(self, state: NState, srt_original_silence, srt_original_silence_con_ad) -> NState:
165
  print("Add_Ad.__call__ iniciado")
166
 
167
+ with open(srt_original_silence, "r", encoding="utf-8") as f:
 
168
  srt_text = f.read()
169
 
 
170
  frames = self.data.video.get('info_escenas', {})
171
 
 
172
  srt_blocks = []
173
  srt_blocks_modified=[]
174
  pattern = re.compile(
 
189
  })
190
 
191
  index=1
192
+ persona_keyframe = []
193
+ personas_per_second = []
194
+ ocr_text = []
195
+ descripcion_text =[]
196
+
197
  for block in srt_blocks:
198
  if "[silenci]" in block["text"]:
199
  start_block = block["start"]
 
205
  "index":index,
206
  "start": start_block,
207
  "end": end_block,
208
+ "text": f"(AD): OCR: {frame.get('ocr')}\nDescripción: {frame.get('descripcion', '')}"
209
  })
210
  index+=1
211
 
212
+ personas=frame.get("faces")
213
+ if personas==[]:
214
+ persona_keyframe.append([])
215
+ else:
216
+ person=[]
217
+ for p in personas:
218
+ person.append(p)
219
+ persona_keyframe.append(person)
220
+
221
+ persona=frame.get("counts",{})
222
+ personas_per_second.append(persona)
223
+
224
+ ocr_text.append(frame.get('ocr'))
225
+ descripcion_text.append(frame.get('descripcion', ''))
226
+
227
  elif start_block<frame.get("end")<end_block:
228
  srt_blocks_modified.append({
229
  "index":index,
230
  "start": start_block,
231
  "end": frame.get("end"),
232
+ "text": f"(AD): OCR: {frame.get('ocr')}\n Descripción: {frame.get('descripcion', '')}"
233
  })
234
  start_block=frame.get("end")
235
  index+=1
236
+
237
+ personas=frame.get("faces")
238
+ if personas==[]:
239
+ persona_keyframe.append([])
240
+ else:
241
+ person=[]
242
+ for p in personas:
243
+ person.append(p)
244
+ persona_keyframe.append(person)
245
+
246
+ persona=frame.get("counts",{})
247
+ personas_per_second.append(persona)
248
+
249
+ ocr_text.append(frame.get('ocr'))
250
+ descripcion_text.append(frame.get('descripcion', ''))
251
 
252
  elif start_block==frame.get("start") and start_block<end_block and frame.get("end")>=end_block:
253
  srt_blocks_modified.append({
254
  "index":index,
255
  "start": start_block,
256
  "end": end_block,
257
+ "text": f"(AD): OCR: {frame.get('ocr')}\n Descripción: {frame.get('descripcion', '')}"
258
  })
259
  start_block=end_block
260
  index+=1
261
 
262
+ personas=frame.get("faces")
263
+ if personas==[]:
264
+ persona_keyframe.append([])
265
+ else:
266
+ person=[]
267
+ for p in personas:
268
+ person.append(p)
269
+ persona_keyframe.append(person)
270
+
271
+ persona=frame.get("counts",{})
272
+ personas_per_second.append(persona)
273
+
274
+ ocr_text.append(frame.get('ocr'))
275
+ descripcion_text.append(frame.get('descripcion', ''))
276
+
277
  else:
278
  srt_blocks_modified.append({
279
  "index": index,
 
283
  })
284
  index+=1
285
 
286
+ persona_keyframe.append("")
287
+ personas_per_second.append({})
288
+
289
+ ocr_text.append("")
290
+ descripcion_text.append("")
291
+
292
  srt_final = ""
293
 
294
  for block in srt_blocks_modified:
 
296
  end_tc = seconds_to_srt_time(block["end"])
297
  srt_final += f"{block['index']}\n{start_tc} --> {end_tc}\n{block['text']}\n\n"
298
 
299
+ with open(srt_original_silence_con_ad, "w", encoding="utf-8") as f:
 
300
  f.write(srt_final)
301
 
302
+ state['personas_keyframes'] = persona_keyframe
303
+ state['personas_per_second'] = personas_per_second
304
+ state['ocr'] = ocr_text
305
+ state['descripcion'] = descripcion_text
306
+
307
  return state
308
 
309
+ class Add_Silence_AD:
310
+ def __call__(self, state: NState, srt_original_silence_con_ad, srt_original_silence_con_ad_silence) -> NState:
311
+ print("Add_Silence_AD.__call__ iniciado")
312
+
313
+ with open(srt_original_silence_con_ad, "r", encoding="utf-8") as f:
314
+ srt_text = f.read()
315
+
316
+ srt_blocks = []
317
+ srt_blocks_modified=[]
318
+ pattern = re.compile(
319
+ r"(\d+)\s+(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\s+(.*?)(?=\n\d+\n|\Z)",
320
+ re.S
321
+ )
322
+
323
+ for match in pattern.finditer(srt_text):
324
+ index = int(match.group(1))
325
+ start = srt_time_to_seconds(match.group(2))
326
+ end = srt_time_to_seconds(match.group(3))
327
+ text = match.group(4).strip()
328
+ srt_blocks.append({
329
+ "index": index,
330
+ "start": start,
331
+ "end": end,
332
+ "text": text
333
+ })
334
+
335
+ index=1
336
+
337
+ for block in srt_blocks:
338
+ if "(AD):" in block["text"]:
339
+ start_block = block["start"]
340
+ end_block = block["end"]
341
+
342
+ if end_block - start_block < 2.0:
343
+ srt_blocks_modified.append({
344
+ "index":index,
345
+ "start": start_block,
346
+ "end": end_block,
347
+ "text": f"(AD): "
348
+ })
349
+ index+=1
350
+
351
+ else:
352
+ srt_blocks_modified.append({
353
+ "index":index,
354
+ "start": start_block,
355
+ "end": end_block,
356
+ "text": block['text']
357
+ })
358
+ index+=1
359
+
360
+ else:
361
+ srt_blocks_modified.append({
362
+ "index": index,
363
+ "start": block["start"],
364
+ "end": block["end"],
365
+ "text": block["text"]
366
+ })
367
+ index+=1
368
+
369
+ srt_final = ""
370
+
371
+ for block in srt_blocks_modified:
372
+ start_tc = seconds_to_srt_time(block["start"])
373
+ end_tc = seconds_to_srt_time(block["end"])
374
+ srt_final += f"{block['index']}\n{start_tc} --> {end_tc}\n{block['text']}\n\n"
375
+
376
+ with open(srt_original_silence_con_ad_silence, "w", encoding="utf-8") as f:
377
+ f.write(srt_final)
378
+
379
+ return state
380
+
381
+ def es_silencio(texto):
382
+ if "(AD):" in texto:
383
+ if "OCR:" in texto:
384
+ return False
385
+
386
+ elif "[" in texto:
387
+ return False
388
+
389
+ else:
390
+ return True
391
+
392
+ else:
393
+ return False
394
+
395
+ class Unir_AD_Silence:
396
+ def __call__(self, state: NState, srt_original_silence_con_ad_silence, srt_original_silence_con_ad_silence_unidos) -> NState:
397
+ print("Unir_AD_Silence.__call__ iniciado")
398
+
399
+ with open(srt_original_silence_con_ad_silence, "r", encoding="utf-8") as f:
400
+ srt_text = f.read()
401
+
402
+ srt_blocks = []
403
+ pattern = re.compile(
404
+ r"(\d+)\s+(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\s+(.*?)(?=\n\d+\n|\Z)",
405
+ re.S
406
+ )
407
+
408
+ for match in pattern.finditer(srt_text):
409
+ index = int(match.group(1))
410
+ start = srt_time_to_seconds(match.group(2))
411
+ end = srt_time_to_seconds(match.group(3))
412
+ text = match.group(4).strip()
413
+ srt_blocks.append({
414
+ "index": index,
415
+ "start": start,
416
+ "end": end,
417
+ "text": text
418
+ })
419
+
420
+ index_unidos = 1
421
+ i=0
422
+ srt_blocks_unidos = []
423
+ bloques_unidos = []
424
+ ocr_text = state["ocr"]
425
+
426
+ descripcion = state["descripcion"]
427
+
428
+ while i < len(srt_blocks):
429
+ actual = srt_blocks[i]
430
+
431
+ if es_silencio(actual["text"]) and "(AD):" in actual["text"]:
432
+ origenes = [i]
433
+ start_time = actual["start"]
434
+ end_time = actual["end"]
435
+ j = i+1
436
+ texto_ocr = ocr_text[i]
437
+ texto_descripcion = descripcion[i]
438
+
439
+ while j < len(srt_blocks) and es_silencio(srt_blocks[j]["text"]) and "(AD):" in srt_blocks[j]["text"]:
440
+ end_time = srt_blocks[j]["end"]
441
+ origenes.append(j)
442
+ texto_ocr += "" + ocr_text[j]
443
+ if descripcion[j] is None:
444
+ descripcion[j] = ""
445
+ texto_descripcion += "" + descripcion[j]
446
+
447
+ j+=1
448
+
449
+ srt_blocks_unidos.append({
450
+ "index": index_unidos,
451
+ "start": start_time,
452
+ "end": end_time,
453
+ "text": f"(AD): OCR: {texto_ocr}\n Descripción: {texto_descripcion}"
454
+ })
455
+
456
+ i = j
457
+ index_unidos +=1
458
+
459
+ else:
460
+ origenes=[i]
461
+ srt_blocks_unidos.append({
462
+ "index": index_unidos,
463
+ "start": srt_blocks[i]["start"],
464
+ "end": srt_blocks[i]["end"],
465
+ "text": srt_blocks[i]["text"]
466
+ })
467
+ i +=1
468
+ index_unidos +=1
469
+
470
+ bloques_unidos.append(origenes)
471
+
472
+ srt_final = ""
473
+
474
+ for block in srt_blocks_unidos:
475
+ start_tc = seconds_to_srt_time(block["start"])
476
+ end_tc = seconds_to_srt_time(block["end"])
477
+ srt_final += f"{block['index']}\n{start_tc} --> {end_tc}\n{block['text']}\n\n"
478
+
479
+ with open(srt_original_silence_con_ad_silence_unidos, "w", encoding="utf-8") as f:
480
+ f.write(srt_final)
481
+
482
+ state["bloques_unidos"] = bloques_unidos
483
+
484
+ return state
485
+
486
+ class Unir_AD_Silences_a_ADs:
487
+ def __call__(self, state: NState, srt_original_silence_con_ad_silence_unidos_silence, srt_original_silence_con_ad_silence_unidos_silence_general) -> NState:
488
+ print("Unir_AD_Silences_a_ADs.__call__ iniciado")
489
+
490
+ with open(srt_original_silence_con_ad_silence_unidos_silence, "r", encoding="utf-8") as f:
491
+ srt_text = f.read()
492
+
493
+ srt_blocks = []
494
+ pattern = re.compile(
495
+ r"(\d+)\s+(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\s+(.*?)(?=\n\d+\n|\Z)",
496
+ re.S
497
+ )
498
+
499
+ for match in pattern.finditer(srt_text):
500
+ index = int(match.group(1))
501
+ start = srt_time_to_seconds(match.group(2))
502
+ end = srt_time_to_seconds(match.group(3))
503
+ text = match.group(4).strip()
504
+ srt_blocks.append({
505
+ "index": index,
506
+ "start": start,
507
+ "end": end,
508
+ "text": text
509
+ })
510
+
511
+ index = 1
512
+ srt_blocks_unidos = []
513
+
514
+ bloques_unidos = state["bloques_unidos"]
515
+ nuevos_bloques_unidos = []
516
+
517
+ for i, block in enumerate(srt_blocks):
518
+ antes = False
519
+ despues = False
520
+
521
+ if "(AD):" in block["text"]:
522
+
523
+ if es_silencio(block["text"]):
524
+ if i!=0 and ("(AD): OCR:" in srt_blocks[i-1]["text"]):
525
+ continue
526
+
527
+ elif i!=len(srt_blocks)-1 and ("(AD): OCR:" in srt_blocks[i+1]["text"]):
528
+ continue
529
 
530
+ else:
531
+ nuevos_bloques_unidos.append(bloques_unidos[i])
532
+ srt_blocks_unidos.append({
533
+ "index": index,
534
+ "start": block["start"],
535
+ "end": block["end"],
536
+ "text": block["text"]
537
+ })
538
+ index += 1
539
 
540
+ elif "(AD): OCR:" in block["text"]:
 
 
 
541
 
542
+ if i!=0 and es_silencio(srt_blocks[i-1]["text"]):
543
+ start_time = srt_blocks[i-1]["start"]
544
+ antes = True
545
 
546
+ if i!= len(srt_blocks)-1 and es_silencio(srt_blocks[i+1]["text"]):
547
+ end_time = srt_blocks[i+1]["end"]
548
+ despues = True
549
+
550
+ if antes == True and despues == True:
551
+ start = start_time
552
+ end = end_time
553
+
554
+ elif antes == True and despues == False:
555
+ start = start_time
556
+ end = block["end"]
557
+
558
+ elif antes == False and despues == True:
559
+ start = block["start"]
560
+ end = end_time
561
+
562
+ else:
563
+ start = block["start"]
564
+ end = block["end"]
565
+ nuevos_bloques_unidos.append(bloques_unidos[i])
566
+ srt_blocks_unidos.append({
567
+ "index": index,
568
+ "start": start,
569
+ "end": end,
570
+ "text": block["text"]
571
+ })
572
 
573
+ index += 1
574
+
575
+ else:
576
+ nuevos_bloques_unidos.append(bloques_unidos[i])
577
+ srt_blocks_unidos.append({
578
+ "index": index,
579
+ "start": block["start"],
580
+ "end": block["end"],
581
+ "text": block["text"]
582
+ })
583
+ index +=1
584
+
585
+ else:
586
+ nuevos_bloques_unidos.append(bloques_unidos[i])
587
+ srt_blocks_unidos.append({
588
+ "index": index,
589
+ "start": block["start"],
590
+ "end": block["end"],
591
+ "text": block["text"]
592
+ })
593
+ index +=1
594
+ srt_final = ""
595
+
596
+ for block in srt_blocks_unidos:
597
+ start_tc = seconds_to_srt_time(block["start"])
598
+ end_tc = seconds_to_srt_time(block["end"])
599
+ srt_final += f"{block['index']}\n{start_tc} --> {end_tc}\n{block['text']}\n\n"
600
+
601
+ with open(srt_original_silence_con_ad_silence_unidos_silence_general, "w", encoding="utf-8") as f:
602
+ f.write(srt_final)
603
+
604
+ state["bloques_unidos"] = nuevos_bloques_unidos
605
+
606
+ return state
607
+
608
+ def words_silence_srt(srt_silence_path):
609
+ with open(srt_silence_path, "r", encoding="utf-8-sig") as f:
610
+ srt_text=f.read()
611
+
612
+ silence_dict = {}
613
+
614
+ blocks = srt_text.strip().split("\n\n")
615
+ for block in blocks:
616
+ lines = block.split("\n")
617
+ idx = int(lines[0])
618
+ time_range = lines[1]
619
+ content = "\n".join(lines[2:]).strip()
620
+
621
+ start_str, end_str = time_range.split(" --> ")
622
+ start_sec = srt_time_to_seconds(start_str)
623
+ end_sec = srt_time_to_seconds(end_str)
624
+
625
+ if content.startswith("(AD"):
626
+ duration = end_sec - start_sec
627
+ words = max(1, round(duration * 2))
628
+ silence_dict[idx] = words
629
+
630
+ else:
631
+ silence_dict[idx] = 0
632
+ return silence_dict
633
+
634
+ class Introduccion_OCR:
635
+ def __call__(self, state: NState, srt_original_silence_con_ad_silence_unidos_silence_general, srt_original_silence_con_ad_silence_unidos_silence_general_ocr):
636
+ print("Introduccion_OCR.__call__ iniciat")
637
+
638
+ words_silence = words_silence_srt(srt_original_silence_con_ad_silence_unidos_silence_general)
639
+
640
+ with open(srt_original_silence_con_ad_silence_unidos_silence_general, "r", encoding="utf-8-sig") as f:
641
+ srt_text = f.read()
642
+
643
+ blocks = srt_text.strip().split("\n\n")
644
+ srt_text_modified = ""
645
+
646
+ bloques_unidos = state["bloques_unidos"]
647
+ nuevos_bloques_unidos = []
648
+
649
+ for i, block in enumerate(blocks):
650
+ lines = block.split("\n")
651
+ idx = int(lines[0])
652
+ time_range = lines[1]
653
+ content = "\n".join(lines[2:]).strip()
654
+
655
+ start_str, end_str = time_range.split(" --> ")
656
+ start_sec = srt_time_to_seconds(start_str)
657
+ end_sec = srt_time_to_seconds(end_str)
658
+
659
+ if content.startswith("(AD): OCR"):
660
+ lines = content.split("\n")
661
+ ocr_text = lines[0].split("OCR: ")[1].strip()
662
+ descripcion_text = lines[1].split("Descripción: ")[1].strip()
663
+
664
+ if ocr_text is None or ocr_text == "":
665
+ nuevos_bloques_unidos.append(bloques_unidos[i])
666
+ srt_text_modified += f"{idx}\n{time_range}\n(AD_Descripción): {descripcion_text}\n\n"
667
+
668
+ else:
669
+ count_palabras = len(ocr_text.split())
670
+ palabras_limite = words_silence[i+1]
671
+ if count_palabras <= palabras_limite:
672
+
673
+ prompt = f"""
674
+ Tens davant teu el text extret per OCR d'un frame d'un vídeo. El text està en català.
675
+ Només has de decidir si aquest text és català i té sentit com a frase o paraula en català, sense jutjar-ne la llargada ni si és molt simple.
676
+ Si és català i té sentit, respon només 'yes'.
677
+ Si no és català o no té sentit, respon només 'no'.
678
+
679
+ OCR: {ocr_text}
680
+ """
681
+ messages = [{'role': 'system', 'content': prompt}]
682
+
683
+ out = state['llm_GPT'](messages).strip()
684
+
685
+ if out =="yes":
686
+ end_sec_1 = start_sec + count_palabras / 2
687
+ end_str_1 = seconds_to_srt_time(end_sec_1)
688
+ time_range = f"{start_str} --> {end_str_1}"
689
+ nuevos_bloques_unidos.append(bloques_unidos[i])
690
+ srt_text_modified += f"{idx}\n{time_range}\n(AD_OCR): {ocr_text}\n\n"
691
+
692
+ start_str = end_str_1
693
+ time_range = f"{start_str} --> {end_str}"
694
+ nuevos_bloques_unidos.append(bloques_unidos[i])
695
+ srt_text_modified += f"{idx}\n{time_range}\n(AD_Descripción): {descripcion_text}\n\n"
696
+
697
+ else:
698
+ srt_text_modified += f"{idx}\n{time_range}\n(AD_Descripción): {descripcion_text}\n\n"
699
+ nuevos_bloques_unidos.append(bloques_unidos[i])
700
+
701
+ else:
702
+ nuevos_bloques_unidos.append(bloques_unidos[i])
703
+ srt_text_modified += f"{idx}\n{time_range}\n(AD_Descripción): {descripcion_text}\n\n"
704
+
705
+ else:
706
+ nuevos_bloques_unidos.append(bloques_unidos[i])
707
+ srt_text_modified += f"{idx}\n{time_range}\n{content}\n\n"
708
+
709
+ with open(srt_original_silence_con_ad_silence_unidos_silence_general_ocr, "w", encoding="utf-8-sig") as f:
710
+ f.write(srt_text_modified)
711
+
712
+ state["bloques_unidos"] = nuevos_bloques_unidos
713
+
714
+ return state
715
+
716
+ class Identity_Manager:
717
+ def __call__(self, state: NState, srt_original_silence_con_ad_ocr, srt_original_silence_con_ad_ocr_identity):
718
+ print("Identity_Manager.__call__ iniciat")
719
+
720
+ with open(srt_original_silence_con_ad_ocr, "r", encoding="utf-8-sig") as f:
721
+ srt_text = f.read()
722
+
723
+ blocks = srt_text.strip().split("\n\n")
724
+ srt_text_modified = ""
725
+
726
+ bloques_unidos = state["bloques_unidos"]
727
+
728
+ content_anterior = ""
729
+
730
+ for i, block in enumerate(blocks):
731
+ persona = state['personas_keyframes'][bloques_unidos[i][0]]
732
+ personas_per_second = state["personas_per_second"][bloques_unidos[i][0]]
733
+
734
+ lines = block.split("\n")
735
+ idx = int(lines[0])
736
+ time_range = lines[1]
737
+ content = lines[2].strip()
738
+
739
+ if content.startswith("(AD_Descripción):"):
740
+ if content == content_anterior:
741
+
742
+ prompt = (
743
+ f"Sobre la escena '{content}' (persona principal: {persona}) ya se ha escrito '{content_escena}'. "
744
+ f"Las personas detectadas en la escena actual son: {personas_per_second}. "
745
+ f"¿Hay algo nuevo y no repetitivo que añadir cumpliendo la norma UNE para ciegos? "
746
+ f"Si no hay nada nuevo, deja la respuesta vacía: ' (AD):'' '"
747
+ )
748
+ messages = [{'role': 'system', 'content': prompt}]
749
+ out = state['llm_GPT'](messages).strip()
750
+
751
+ salida = out or "" # manejar vacío
752
+ srt_text_modified += f"{idx}\n{time_range}\n{salida}\n\n"
753
+
754
+ content_escena += " " + salida
755
+
756
+ else:
757
+ # Aquí entra cuando hay una escena nueva
758
+ content_escena = ""
759
+
760
+ prompt = f"""
761
+ Sabent que aquesta és la frase que cal corregir: {content}, on apareix la persona identificada com a {persona}.
762
+ Si la descripció apareix de manera genèrica, per exemple "Una dona", substitueix-la pel nom concret de la persona identificada.
763
+
764
+ Informació addicional que pot ser útil i d’on també pots identificar els personatges segons el context:
765
+ 1.) Bloc concret {i} que s’està modificant de la diarització completa: {srt_text}
766
+ 2.) Personatges identificats en la escena completa con el número de veces que han aparecido: {personas_per_second}
767
+
768
+ Recorda:
769
+ - Torna només el text corregit en el format (AD_Descripción): "text"
770
+ """
771
+ messages = [{'role': 'system', 'content': prompt}]
772
+ out = state['llm_GPT'](messages).strip()
773
+
774
+ srt_text_modified += f"{idx}\n{time_range}\n{out}\n\n"
775
+
776
+ content_anterior = content
777
+ content_escena += out
778
+ salida = out
779
+
780
+ else:
781
+ srt_text_modified += f"{idx}\n{time_range}\n{content}\n\n"
782
+
783
+ # Guardem el SRT final amb identitats aplicades
784
+ with open(srt_original_silence_con_ad_ocr_identity, "w", encoding="utf-8-sig") as f:
785
+ f.write(srt_text_modified)
786
+
787
+ # Actualitzem l'estat
788
+ state['audiodescripcion_ad_identity'] = srt_text_modified
789
+
790
+ return state
791
+
792
+ class UNE_Actor_prev:
793
+ def __call__(self, state: NState, srt_original_silence_con_ad_ocr_identity, srt_original_silence_con_ad_ocr_identity_une_1):
794
+ print("UNE_Actor_prev.__call__ iniciado")
795
+
796
+ with open(srt_original_silence_con_ad_ocr_identity, "r", encoding="utf-8-sig") as f:
797
+ srt_text = f.read()
798
+
799
  prompt = f"""
800
+ PROMPT PER A LA GENERACIÓ D’AUDIODESCRIPCIÓ (AD) NORMA UNE 153020
801
+
802
+ Rol i Objectiu:
803
+ Ets un guionista d’audiodescripció expert en la norma UNE 153020 (Descripció del contingut visual per a persones cegues o amb baixa visió).
804
+ La teva tasca és revisar i generar (o corregir si ja existeixen) les audiodescripcions de l’arxiu SRT proporcionat.
805
+ Has de retornar l’arxiu SRT complet, mantenint la numeració i els temps originals, sense afegir cap text explicatiu fora del format SRT.
806
+
807
+ INSTRUCCIONS DETALLADES:
808
+
809
+ 1. **Format de sortida**
810
+ - Retorna l’arxiu SRT complet i corregit.
811
+ - No incloguis comentaris, explicacions ni encapçalaments fora del format de l’arxiu.
812
+ - Respecta la numeració, els temps i la resta del text original.
813
+
814
+ 2. **Etiquetes a modificar**
815
+ - Només modifica el contingut que estigui entre les etiquetes `(AD_Descripción):` o `(AD):`.
816
+ - Si una línia amb `(AD):` està buida, no la omplis (s’assumeix que hi ha so rellevant o que no hi ha espai per a la descripció).
817
+ - Substitueix o completa únicament aquestes línies, sense alterar la resta del subtítol.
818
+
819
+ 3. **Criteris d’Audiodescripció (segons UNE 153020)**
820
+ - Descriu **només la informació visual rellevant** que no aparegui a l’àudio.
821
+ - Fes servir un estil **objectiu, clar i concís**, sense interpretacions ni judicis subjectius.
822
+ - Descriu només allò necessari perquè una persona cega pugui comprendre l’escena.
823
+ - No descriguis durant diàlegs, música o efectes rellevants.
824
+ - Si el silenci és expressiu (suspens, comèdia, tensió), deixa la descripció en blanc.
825
+
826
+ 4. **Contingut que has d’incloure (Què descriure?)**
827
+ - **QUAN i ON:** lloc, moment del dia o època.
828
+ - **QUI:** identificació, roba, atributs físics rellevants.
829
+ - **QUÈ i COM:** llenguatge corporal, moviments, gestos, accions, expressions facials.
830
+ - **Altres:** text en pantalla, logotips, títols o rètols visibles.
831
+
832
+ 5. **Llenguatge i estil**
833
+ - Fes servir **temps present** (“Camina”, no “Va caminar”).
834
+ - Utilitza **veu activa**, evita la passiva.
835
+ - Lèxic clar, variat però concís.
836
+ - Sense metàfores, suposicions ni valoracions subjectives.
837
+ - Evita els verbs “veure” i “aparèixer”.
838
+ - Indica salts o transicions de temps (p. ex. “Tres anys després…”).
839
+
840
+ 6. **Errors que has d’evitar absolutament**
841
+ - No interpretis emocions ni intencions (“sembla trist”, “com si recordés”).
842
+ - No expliquis, no valoris (“una imatge preciosa”, “una escena intensa”).
843
+ - No afegeixis informació no visible o no verificable.
844
+
845
+ TASCA:
846
+ Revisa el següent arxiu SRT i substitueix, completa o corregeix les parts que continguin `(AD_Descripción)` o `(AD):` d’acord amb totes les regles anteriors.
847
+ Retorna’m **només l’arxiu SRT corregit**, sense cap comentari addicional.
848
+
849
+ ARXIU SRT A PROCESSAR: {srt_text}
850
  """
 
 
851
 
852
+ messages = [{'role': 'system', 'content': prompt}]
853
+
854
+ out = state['llm_GPT'](messages).strip()
855
+ out = out.replace('```', '')
856
 
857
+ blocks = re.split(r'\n\s*\n', out)
858
+
859
+ # Comprobar si el primer bloque empieza con un número
860
+ first_block = blocks[0].strip().split('\n')[0]
861
+
862
+ if first_block.isdigit():
863
+ # El primer bloque ya tiene número, no hacemos nada
864
+ fixed_content = out
865
+ else:
866
+ # Reindexamos todos los bloques
867
+ output_lines = []
868
+ for i, block in enumerate(blocks, start=1):
869
+ block = re.sub(r'^\d+\s*\n', '', block)
870
+ block = f"{i}\n{block.strip()}"
871
+ output_lines.append(block)
872
+
873
+ fixed_content = "\n\n".join(output_lines)
874
+
875
+ with open(srt_original_silence_con_ad_ocr_identity_une_1, "w", encoding="utf-8-sig") as f:
876
+ f.write(fixed_content)
877
 
878
  return state
879
+
880
+ class UNE_Actor:
881
+ def __call__(self, state: NState, srt_original_silence_con_ad_ocr_identity_une_1, srt_original_silence_con_ad_ocr_identity_une_2):
882
+ print("UNE_Actor.__call__ iniciado")
883
+
884
+ silence_dict = words_silence_srt(srt_original_silence_con_ad_ocr_identity_une_1)
885
+
886
+ with open(srt_original_silence_con_ad_ocr_identity_une_1, "r", encoding="utf-8-sig") as f:
887
+ srt_text = f.read()
888
+
889
+ srt_text_modified = ""
890
+
891
+ blocks = srt_text.strip().split("\n\n")
892
+ for block in blocks:
893
+ lines = block.split("\n")
894
+ idx = int(lines[0])
895
+ time_range = lines[1]
896
+ content = lines[2].strip()
897
+
898
+ start_str, end_str = time_range.split(" --> ")
899
+
900
+ if content.startswith("(AD_Descripción):"):
901
+ if silence_dict[idx] < 2:
902
+ out = '(AD): ""'
903
+
904
+ else:
905
+ # Construimos el prompt aquí, con los datos ya disponibles
906
+ sys_prompt = f"""
907
+ En primer lloc, has de generar un contingut amb un nombre determinat de paraules ({silence_dict[idx]})
908
+ que representi el mateix significat que aquest fragment: {content}.
909
+
910
+ D’altra banda, s’està modificant el fitxer SRT complet {srt_text}, concretament el fragment número {idx},
911
+ per si et pot servir de context. Aquí tens el contingut actualitzat de l’SRT fins ara: {srt_text_modified}
912
+
913
+ Has de complir amb la norma UNE: llenguatge clar, descriptiu i narratiu, sense repeticions i mostrant
914
+ les accions i emocions de manera natural.
915
+
916
+ Important:
917
+ - Revisa el contingut anterior de l’SRT i evita repetir frases o expressions ja utilitzades.
918
+ - Si hi ha informació semblant, expressa-la d’una manera diferent, mantenint la coherència i la claredat.
919
+ - El resultat ha de ser narratiu, natural i fluid.
920
+ - Regla estricta: si el nombre de paraules requerit és 1 o 2 i no és possible expressar el contingut de manera coherent amb tan poques paraules,
921
+ has de retornar exactament: (AD): "" (cometes buides), sense afegir res més.
922
+
923
+ La resposta s’ha de donar en el format següent:
924
+
925
+ (AD): "text amb exactament {silence_dict[idx]} paraules, que representi fidelment el text proporcionat ({content}),
926
+ sense repetir fórmules ja utilitzades a l’SRT i complint amb la norma UNE"
927
+ """
928
+
929
+ messages = [{'role': 'system', 'content': sys_prompt}]
930
+ out = state['llm_GPT'](messages)
931
+
932
+ srt_text_modified += f"{idx}\n{start_str} --> {end_str}\n{out}\n\n"
933
+ else:
934
+ srt_text_modified += f"{idx}\n{start_str} --> {end_str}\n{content}\n\n"
935
+
936
+ # Guardamos el resultado
937
+ with open(srt_original_silence_con_ad_ocr_identity_une_2, "w", encoding="utf-8-sig") as f:
938
+ f.write(srt_text_modified)
939
+
940
+ # Guardamos también en el estado
941
+ state['audiodescripcion_une'] = srt_text_modified
942
+
943
+ return state
944
+
945
  class Valoracion_Final:
946
+ def __call__(self, state, srt_original_silence_con_ad_ocr_identity_une_2, csv_evaluacion):
947
  print("Valoracion_Final.__call__ iniciat")
948
 
949
  # Llegeix el contingut del fitxer SRT
950
+ with open(srt_original_silence_con_ad_ocr_identity_une_2, "r", encoding="utf-8-sig") as f:
951
  srt_text = f.read().strip()
952
 
953
  # Defineix el prompt principal
 
992
 
993
  return state
994
 
995
+ class Free_Narration:
996
+ def __call__(self, state: NState, audio_descripcion_path_sin_une, story_path) -> NState:
997
+
998
+ with open(audio_descripcion_path_sin_une, "r", encoding="utf-8-sig") as f:
999
+ audio_descripcion = f.read()
1000
+
1001
+ sys_prompt = f"""
1002
+ Ets un relator objectiu. Tens la següent informació:
1003
+
1004
+ 1. Audiodescripció del vídeo (incloent diàlegs i descripcions visuals): {audio_descripcion}
1005
+
1006
+ Objectiu:
1007
+ - Resumeix de forma precisa i cronològica tot el que passa al vídeo.
1008
+ - Inclou només els esdeveniments essencials i les accions principals dels personatges.
1009
+ - Elimina qualsevol detall visual, emocional o descriptiu que no sigui necessari per entendre què passa.
1010
+ - No afegeixis cap informació que no aparegui explícitament a la font.
1011
+ - No reprodueixis diàlegs, només explica el que succeeix.
1012
+ - Mantén el relat neutre, breu i clar.
1013
+ - Usa els noms correctes dels personatges segons apareguin a la història.
1014
+
1015
+ Sortida:
1016
+ - Un únic text narratiu continu.
1017
+ """
1018
+
1019
+ messages = [{'role':'system','content': sys_prompt}]
1020
+ out = state['llm_GPT'](messages)
1021
+
1022
+ with open(story_path, "w", encoding="utf-8-sig") as f:
1023
+ f.write(out)
1024
+
1025
+ state['free_narration'] = out
1026
+
1027
+ return state
1028
+
1029
  def srt_update(srt_video, srt_video_modified):
1030
  with open(srt_video, "r", encoding="utf-8") as f:
1031
  srt_text = f.read()
 
1197
  generate_srt_con_silencios(srt_original, tmp.name, video_path)
1198
 
1199
  datahub=DataHub(informacion_json)
1200
+
1201
+ # Instancia de la herramienta como clase
1202
  add_ad = Add_AD(datahub)
1203
+ add_silence_ad = Add_Silence_AD()
1204
+ unir_ad_silence = Unir_AD_Silence()
1205
+ unir_ad_silences_a_ads = Unir_AD_Silences_a_ADs()
1206
+ introduccion_ocr = Introduccion_OCR()
1207
+ identity_manager = Identity_Manager()
1208
+ une_actor_prev = UNE_Actor_prev()
1209
+ une_actor = UNE_Actor()
1210
  valoracion_final = Valoracion_Final()
1211
+ free_narration = Free_Narration()
1212
+
1213
+ tools = [
1214
+ {
1215
+ "type": "function",
1216
+ "name": "Add_AD",
1217
+ "description": "Agregame las descripciones de lo que esta ocurriendo por pantalla",
1218
+ "parameters": {
1219
+ "type": "object",
1220
+ "properties": {
1221
+ "state": {
1222
+ "type": "object",
1223
+ "description": "Estado actual de procesamiento"
1224
+ }
1225
+ },
1226
+ "required": ["state", "srt_original_silence", "srt_original_silence_con_ad"],
1227
+ "additionalProperties": False
1228
+ },
1229
+ "function": add_ad
1230
+ },
1231
+ {
1232
+ "type": "function",
1233
+ "name": "Add_Silence_AD",
1234
+ "description": "Introduceme bloques de silencio en la audiodescripción",
1235
+ "parameters": {
1236
+ "type": "object",
1237
+ "properties": {
1238
+ "state": {
1239
+ "type": "object",
1240
+ "description": "Estado actual de procesamiento"
1241
+ }
1242
+ },
1243
+ "required": ["state", "srt_original_silence_con_ad", "srt_original_silence_con_ad_silence"],
1244
+ "additionalProperties": False
1245
+ },
1246
+ "function": add_silence_ad
1247
+ },
1248
+ {
1249
+ "type": "function",
1250
+ "name": "Unir_AD_Silence",
1251
+ "description": "Unificame bloques de silencio que son consecutivos en la audiodescripción",
1252
+ "parameters": {
1253
+ "type": "object",
1254
+ "properties": {
1255
+ "state": {
1256
+ "type": "object",
1257
+ "description": "Estado actual de procesamiento"
1258
+ }
1259
+ },
1260
+ "required": ["state", "srt_original_silence_con_ad_silence", "srt_original_silence_con_ad_silence_unidos"],
1261
+ "additionalProperties": False
1262
+ },
1263
+ "function": unir_ad_silence
1264
+ },
1265
+ {
1266
+ "type": "function",
1267
+ "name": "Unir_AD_Silences_a_ADs",
1268
+ "description": "Unificame los bloques de silencio a la audiodescripción en caso de que haya de manera consecutiva para aprovechar mejor los tiempos",
1269
+ "parameters": {
1270
+ "type": "object",
1271
+ "properties": {
1272
+ "state": {
1273
+ "type": "object",
1274
+ "description": "Estado actual de procesamiento"
1275
+ }
1276
+ },
1277
+ "required": ["state", "srt_original_silence_con_ad_silence_unidos", "srt_original_silence_con_ad_silence_unidos_general"],
1278
+ "additionalProperties": False
1279
+ },
1280
+ "function": unir_ad_silences_a_ads
1281
+ },
1282
+ {
1283
+ "type": "function",
1284
+ "name": "Introduccion_OCR",
1285
+ "description": "Introducción del texto OCR en la audiodescripción",
1286
+ "parameters": {
1287
+ "type": "object",
1288
+ "properties": {
1289
+ "state": {
1290
+ "type": "object",
1291
+ "description": "Estado actual de procesamiento"
1292
+ }
1293
+ },
1294
+ "required": ["state", "srt_original_silence_con_ad_silence_unidos_silence_general", "srt_original_silence_con_ad_silence_unidos_silence_general_ocr"],
1295
+ "additionalProperties": False
1296
+ },
1297
+ "function": introduccion_ocr
1298
+ },
1299
+ {
1300
+ "type": "function",
1301
+ "name": "Identity_Manager",
1302
+ "description": "Incluye en los fragmentos de audiodescripción las identidades de los actores presentes en la escena",
1303
+ "parameters": {
1304
+ "type": "object",
1305
+ "properties": {
1306
+ "state": {
1307
+ "type": "object",
1308
+ "description": "Estado actual de procesamiento"
1309
+ }
1310
+ },
1311
+ "required": ["state", "srt_original_silence_con_ad", "srt_original_silence_con_ad_ocr_identity"],
1312
+ "additionalProperties": False
1313
+ },
1314
+ "function": identity_manager
1315
+ },
1316
+ {
1317
+ "type": "function",
1318
+ "name": "UNE_Actor_prev",
1319
+ "description": "Verifica en la audiodescripción general quese verifica la norma UNE 153020",
1320
+ "parameters": {
1321
+ "type": "object",
1322
+ "properties": {
1323
+ "state": {
1324
+ "type": "object",
1325
+ "description": "Estado actual de procesamiento"
1326
+ }
1327
+ },
1328
+ "required": ["state", "srt_original_silence_con_ad_ocr_identity", "srt_original_silence_con_ad_ocr_identity_une_1"],
1329
+ "additionalProperties": False
1330
+ },
1331
+ "function": une_actor_prev
1332
+ },
1333
+ {
1334
+ "type": "function",
1335
+ "name": "UNE_Actor",
1336
+ "description": "Modifica la audiodescripción para que cumpla con el número de palabras según la norma UNE 153020",
1337
+ "parameters": {
1338
+ "type": "object",
1339
+ "properties": {
1340
+ "state": {
1341
+ "type": "object",
1342
+ "description": "Estado actual de procesamiento"
1343
+ }
1344
+ },
1345
+ "required": ["state", "srt_original_silence_con_ad_ocr_identity_une_1", "srt_original_silence_con_ad_ocr_identity_une_2"],
1346
+ "additionalProperties": False
1347
+ },
1348
+ "function": une_actor
1349
+ },
1350
+ {
1351
+ "type": "function",
1352
+ "name": "Valoracion_Final",
1353
+ "description": "Genera una valoración final de la audiodescripción según la norma UNE 153020",
1354
+ "parameters": {
1355
+ "type": "object",
1356
+ "properties": {
1357
+ "state": {
1358
+ "type": "object",
1359
+ "description": "Estado actual de procesamiento"
1360
+ }
1361
+ },
1362
+ "required": ["state", "srt_original_silence_con_ad_ocr_identity_une_2", "csv_evaluacion"],
1363
+ "additionalProperties": False
1364
+ },
1365
+ "function": valoracion_final
1366
+ },
1367
+ {
1368
+ "type": "function",
1369
+ "name": "Free_Narration",
1370
+ "description": "Genera una narración libre basada en la audiodescripción",
1371
+ "parameters": {
1372
+ "type": "object",
1373
+ "properties": {
1374
+ "state": {
1375
+ "type": "object",
1376
+ "description": "Estado actual de procesamiento"
1377
+ }
1378
+ },
1379
+ "required": ["state", "srt_final", "free_narration"],
1380
+ "additionalProperties": False
1381
+ },
1382
+ "function": free_narration
1383
+ }
1384
+ ]
1385
+
1386
+ # Aqui van las rutas temporales de los SRT intermedios hasta llegar al final
1387
+ srt_names = [
1388
+ "transcription_initial_silence",
1389
+ "transcription_initial_silence_con_ad",
1390
+ "transcription_initial_silence_con_ad_silence",
1391
+ "transcription_initial_silence_con_ad_silence_unidos",
1392
+ "transcription_initial_silence_con_ad_silence_unidos_silence",
1393
+ "transcription_initial_silence_con_ad_silence_unidos_silence_general",
1394
+ "transcription_initial_silence_con_ad_silence_unidos_silence_general_ocr",
1395
+ "transcription_initial_silence_con_ad_silence_unidos_silence_general_ocr_identity",
1396
+ "transcription_initial_silence_con_ad_ocr_identity_une_1"
1397
+ ]
1398
+
1399
+ # Crear archivos temporales
1400
+ temp_srt_files = []
1401
+
1402
+ for name in srt_names:
1403
+ tmp = tempfile.NamedTemporaryFile(mode="w+", suffix=".srt", prefix=name + "_", delete=False)
1404
+ temp_srt_files.append(tmp)
1405
+ print(tmp.name) # Aquí obtienes la ruta temporal del archivo
1406
+
1407
+ generate_srt_con_silencios(srt_original, temp_srt_files[0].name, video_path)
1408
 
1409
  GPTclient = GPT5Client(api_key=OPEN_AI_KEY)
 
1410
 
1411
+ salamandraclient = SalamandraClient()
1412
  state = {
1413
  "llm_GPT": GPTclient.chat,
1414
  "llm_Salamandra": salamandraclient.chat
1415
  }
1416
 
1417
+ def run_salamandra_agent(salamandra_client, state, tools, user_prompt, messages, count):
1418
+ messages = [{"role": "system", "content": "Eres un agente que puede ejecutar herramientas Python usando las herramientas disponibles."}]
1419
+
1420
+ messages.append({"role": "user", "content": user_prompt})
1421
+
1422
+ messages_registro.append({"role": "user", "content": user_prompt})
1423
+
1424
+ response = salamandra_client.chat(messages,tools)
1425
+ print(f"[Salamandra] {response}")
1426
+
1427
+ # Extraer lo que viene después de 'assistant'
1428
+ match_assistant = re.search(r"assistant\s*(.*)", response, re.DOTALL)
1429
+ assistant_text = match_assistant.group(1).strip() if match_assistant else ""
1430
+
1431
+ # Extraer <tool_call>
1432
+ match_tool = re.search(r"<tool_call>(.*?)</tool_call>", assistant_text, re.DOTALL)
1433
+ if match_tool:
1434
+ resp_json = json.loads(match_tool.group(1).strip())
1435
+ tool_name = resp_json["name"]
1436
+ tool_params = resp_json["arguments"]
1437
+
1438
+ tool = next((t['function'] for t in tools if t['name'] == tool_name), None)
1439
+ if tool:
1440
+
1441
+ if isinstance(tool, Add_AD):
1442
+ state = tool(state, temp_srt_files[0].name, temp_srt_files[1].name)
1443
+
1444
+ elif isinstance(tool, Add_Silence_AD) and count ==1:
1445
+ state = tool(state, temp_srt_files[1].name, temp_srt_files[2].name)
1446
+
1447
+ elif isinstance(tool, Unir_AD_Silence):
1448
+ state = tool(state, temp_srt_files[2].name, temp_srt_files[3].name)
1449
+
1450
+ elif isinstance(tool, Add_Silence_AD) and count ==2:
1451
+ state = tool(state, temp_srt_files[3].name, temp_srt_files[4].name)
1452
+
1453
+ elif isinstance(tool, Unir_AD_Silences_a_ADs):
1454
+ state = tool(state, temp_srt_files[4].name, temp_srt_files[5].name)
1455
+
1456
+ elif isinstance(tool, Introduccion_OCR):
1457
+ state = tool(state, temp_srt_files[5].name, temp_srt_files[6].name)
1458
+
1459
+ elif isinstance(tool, Identity_Manager):
1460
+ state = tool(state, temp_srt_files[6].name, temp_srt_files[7].name)
1461
+
1462
+ elif isinstance(tool, UNE_Actor_prev):
1463
+ state = tool(state, temp_srt_files[7].name, temp_srt_files[8].name)
1464
+
1465
+ elif isinstance(tool, UNE_Actor):
1466
+ state = tool(state, temp_srt_files[8].name, srt_final)
1467
+
1468
+ elif isinstance(tool, Valoracion_Final):
1469
+ state = tool(state, srt_final, csv_evaluacion)
1470
+
1471
+ elif isinstance(tool, Free_Narration):
1472
+ state = tool(state, srt_final, free_narration_moe)
1473
+
1474
+ messages_registro.append({"role": "assistant", "content": f"Ejecuté {tool_name} correctamente."})
1475
+ else:
1476
+ print("No se detectó ejecución de herramienta")
1477
+
1478
+ return state, messages_registro
1479
+
1480
+ messages_registro = [{"role": "system", "content": "Eres un agente que puede ejecutar herramientas Python usando las herramientas disponibles."}]
1481
+
1482
+ count = 1
1483
+
1484
+ user_prompt = "Ejecuta la función add_ad"
1485
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, state, tools, user_prompt, messages_registro, count)
1486
+ state = add_ad(state, temp_srt_files[0].name, temp_srt_files[1].name)
1487
+ print("Transcripción con AD guardada")
1488
+
1489
+ user_prompt = "Ejecuta la función add_silence_ad"
1490
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1491
+ state = add_silence_ad(state, temp_srt_files[1].name, temp_srt_files[2].name)
1492
+ print("Transcripción con AD y Add_Silence_AD guardada")
1493
+
1494
+ user_prompt = "Ejecuta la función unir_ad_silence"
1495
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1496
+ state = unir_ad_silence(state, temp_srt_files[2].name, temp_srt_files[3].name)
1497
+ print("Transcripción con AD y Unir_AD_Silence guardada")
1498
+
1499
+ count = 2
1500
+
1501
+ user_prompt = "Ejecuta la función add_silence_ad"
1502
+ state = add_silence_ad(state, temp_srt_files[3].name, temp_srt_files[4].name)
1503
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1504
+ print("Transcripción con AD y Add_Silence_AD guardada")
1505
+
1506
+ user_prompt = "Ejecuta la función Unir_AD_Silences_a_ADs"
1507
+ state = unir_ad_silences_a_ads(state, temp_srt_files[4].name, temp_srt_files[5].name)
1508
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1509
+ print("Transcripción con AD y Unir_AD_Silences_a_ADs guardada")
1510
+
1511
+ user_prompt = "Ejecuta la función Introduccion_OCR"
1512
+ state = introduccion_ocr(state, temp_srt_files[5].name, temp_srt_files[6].name)
1513
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1514
+ print("Transcripción con AD, Add_Silence_AD e Introduccion_OCR guardada")
1515
+
1516
+ user_prompt = "Ejecuta la función Identity_Manager"
1517
+ state = identity_manager(state, temp_srt_files[6].name, temp_srt_files[7].name)
1518
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1519
+ print("Transcripción con AD, Add_Silence_AD, Introduccion_OCR e Identity_Manager guardada")
1520
+
1521
+ user_prompt = "Ejecuta la función UNE_Actor_prev"
1522
+ state = une_actor_prev(state, temp_srt_files[7].name, temp_srt_files[8].name)
1523
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1524
+ print("Transcripción con AD, Add_Silence_AD, Introduccion_OCR, Identity_Manager y norma UNE guardada")
1525
+
1526
+ user_prompt = "Ejecuta la función UNE_Actor"
1527
+ state = une_actor(state, temp_srt_files[8].name, srt_final)
1528
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1529
+ print("Transcripción con AD, Add_Silence_AD, Introduccion_OCR, Identity_Manager y norma UNE guardada")
1530
+
1531
+ user_prompt = "Ejecuta la función Valoracion_Final"
1532
  state = valoracion_final(state, srt_final, csv_evaluacion)
1533
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1534
+ print("Valoración guardada")
1535
+
1536
+ user_prompt = "Ejecuta la función Free_Narration"
1537
+ #final_state, messages_registro = run_salamandra_agent(salamandraclient, final_state, tools, user_prompt, messages_registro, count)
1538
+ state = free_narration(state, srt_final, free_narration_salamandra)
1539
+ print("Free Narration guardada")
1540
 
1541
  srt_update(srt_final,srt_final)
1542