habulaj commited on
Commit
5695cc6
·
verified ·
1 Parent(s): bff362b

Update routers/editor.py

Browse files
Files changed (1) hide show
  1. routers/editor.py +35 -4
routers/editor.py CHANGED
@@ -231,18 +231,23 @@ async def editor_chat(request: EditorChatRequest):
231
  with open(context_path, "w", encoding="utf-8") as f:
232
  json.dump(request.project, f, ensure_ascii=False)
233
 
234
- print(f"🤖 [EDITOR] Gerando resposta do agente de edição...")
235
  model_obj = get_gemini_model("flash")
236
  response = await g.client.generate_content(prompt, files=[context_path], model=model_obj)
 
237
 
238
  data = extract_json_from_text(response.text)
239
  if not data or not isinstance(data, dict) or "reply" not in data:
 
240
  return {"reply": response.text, "actions": []}
241
 
242
  actions = data.get("actions")
243
  if not isinstance(actions, list):
244
  actions = []
245
 
 
 
 
246
  return {"reply": data.get("reply", ""), "actions": actions}
247
 
248
  except Exception as e:
@@ -366,11 +371,22 @@ async def editor_smart_cut(request: SmartCutRequest):
366
  original_media_to_delete = None
367
  context_path = None
368
  try:
369
- srt_base, _, _audio_url, _word_level, original_media_path = await get_groq_srt_base(
 
 
370
  request.video_url, time_start=request.window_start, time_end=request.window_end,
371
  )
372
  original_media_to_delete = original_media_path
373
 
 
 
 
 
 
 
 
 
 
374
  # get_groq_srt_base's timestamps are relative to the TRIMMED window
375
  # (0-based) — shift back to the source file's own absolute time, the
376
  # same space Element.videoStartOffset already uses, so the frontend
@@ -378,27 +394,42 @@ async def editor_smart_cut(request: SmartCutRequest):
378
  srt_absolute = shift_srt_timestamps(srt_base, request.window_start)
379
  transcript_blocks = parse_srt(srt_absolute)
380
  if not transcript_blocks:
 
381
  return {"segments": [], "summary": "I couldn't find any speech in this clip to analyze."}
382
 
 
 
 
 
383
  temp_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static", "temp")
384
  os.makedirs(temp_dir, exist_ok=True)
385
  context_path = os.path.join(temp_dir, f"smart_cut_transcript_{uuid.uuid4().hex[:8]}.json")
386
  with open(context_path, "w", encoding="utf-8") as f:
387
  json.dump(transcript_blocks, f, ensure_ascii=False)
388
 
389
- print(f"🎬 [EDITOR] Analisando fala pra corte inteligente...")
390
  prompt = build_smart_cut_prompt(request.instruction)
391
  model_obj = get_gemini_model("flash")
392
  response = await g.client.generate_content(prompt, files=[context_path], model=model_obj)
 
393
 
394
  data = extract_json_from_text(response.text)
395
  if not data or not isinstance(data, dict):
 
396
  return {"segments": [], "summary": "Couldn't make sense of the speech analysis — try again."}
397
 
398
  raw_segments = data.get("segments")
399
- segments = snap_segments_to_transcript(raw_segments if isinstance(raw_segments, list) else [], transcript_blocks)
 
400
  summary = data.get("summary") or ""
401
 
 
 
 
 
 
 
 
402
  return {"segments": segments, "summary": summary}
403
 
404
  except Exception as e:
 
231
  with open(context_path, "w", encoding="utf-8") as f:
232
  json.dump(request.project, f, ensure_ascii=False)
233
 
234
+ print(f"🤖 [EDITOR/CHAT] Mensagem: {request.message!r} | histórico: {len(request.history or [])} turno(s) | elementos no snapshot: {len(request.project.get('elements', []))}")
235
  model_obj = get_gemini_model("flash")
236
  response = await g.client.generate_content(prompt, files=[context_path], model=model_obj)
237
+ print(f"🤖 [EDITOR/CHAT] Resposta bruta do Gemini:\n{response.text}")
238
 
239
  data = extract_json_from_text(response.text)
240
  if not data or not isinstance(data, dict) or "reply" not in data:
241
+ print(f"⚠️ [EDITOR/CHAT] Resposta não veio em JSON válido com 'reply' — devolvendo o texto bruto sem ações.")
242
  return {"reply": response.text, "actions": []}
243
 
244
  actions = data.get("actions")
245
  if not isinstance(actions, list):
246
  actions = []
247
 
248
+ action_types = [a.get("type") for a in actions if isinstance(a, dict)]
249
+ print(f"🤖 [EDITOR/CHAT] reply={data.get('reply', '')!r} | {len(actions)} ação(ões): {action_types}")
250
+
251
  return {"reply": data.get("reply", ""), "actions": actions}
252
 
253
  except Exception as e:
 
371
  original_media_to_delete = None
372
  context_path = None
373
  try:
374
+ print(f"🎬 [SMART-CUT] video_url={request.video_url} | janela=[{request.window_start:.2f}, {request.window_end:.2f}] | instrução={request.instruction!r}")
375
+
376
+ srt_base, _, processed_audio_url, _word_level, original_media_path = await get_groq_srt_base(
377
  request.video_url, time_start=request.window_start, time_end=request.window_end,
378
  )
379
  original_media_to_delete = original_media_path
380
 
381
+ # get_groq_srt_base's own `processed_audio_url` drops the "processed/"
382
+ # path segment (a pre-existing bug in media.py — unused by every
383
+ # other caller today, so never noticed: neither /subtitle nor
384
+ # /subtitle/groq put it in their response). Corrected just for this
385
+ # log line rather than touching shared code for an unrelated fix.
386
+ audio_log_url = processed_audio_url.replace("/static/", "/static/processed/", 1) if processed_audio_url else None
387
+ print(f"🔊 [SMART-CUT] Áudio processado (o que o Whisper de fato ouviu): {audio_log_url}")
388
+ print(f"🎙️ [SMART-CUT] Transcrição bruta (relativa à janela recortada):\n{srt_base}")
389
+
390
  # get_groq_srt_base's timestamps are relative to the TRIMMED window
391
  # (0-based) — shift back to the source file's own absolute time, the
392
  # same space Element.videoStartOffset already uses, so the frontend
 
394
  srt_absolute = shift_srt_timestamps(srt_base, request.window_start)
395
  transcript_blocks = parse_srt(srt_absolute)
396
  if not transcript_blocks:
397
+ print("⚠️ [SMART-CUT] Nenhum bloco de fala reconhecido nessa janela — abortando sem chamar o Gemini.")
398
  return {"segments": [], "summary": "I couldn't find any speech in this clip to analyze."}
399
 
400
+ print(f"🕒 [SMART-CUT] Transcrição em tempo absoluto ({len(transcript_blocks)} blocos):")
401
+ for b in transcript_blocks:
402
+ print(f" [{b['start']:.2f} - {b['end']:.2f}] {b['text']}")
403
+
404
  temp_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static", "temp")
405
  os.makedirs(temp_dir, exist_ok=True)
406
  context_path = os.path.join(temp_dir, f"smart_cut_transcript_{uuid.uuid4().hex[:8]}.json")
407
  with open(context_path, "w", encoding="utf-8") as f:
408
  json.dump(transcript_blocks, f, ensure_ascii=False)
409
 
410
+ print(f"🤖 [SMART-CUT] Pedindo pro Gemini escolher os melhores trechos...")
411
  prompt = build_smart_cut_prompt(request.instruction)
412
  model_obj = get_gemini_model("flash")
413
  response = await g.client.generate_content(prompt, files=[context_path], model=model_obj)
414
+ print(f"🤖 [SMART-CUT] Resposta bruta do Gemini:\n{response.text}")
415
 
416
  data = extract_json_from_text(response.text)
417
  if not data or not isinstance(data, dict):
418
+ print("⚠️ [SMART-CUT] Resposta do Gemini não veio em JSON válido.")
419
  return {"segments": [], "summary": "Couldn't make sense of the speech analysis — try again."}
420
 
421
  raw_segments = data.get("segments")
422
+ raw_segments = raw_segments if isinstance(raw_segments, list) else []
423
+ segments = snap_segments_to_transcript(raw_segments, transcript_blocks)
424
  summary = data.get("summary") or ""
425
 
426
+ print(f"✂️ [SMART-CUT] Gemini propôs {len(raw_segments)} segmento(s); {len(segments)} passaram na validação de timestamp.")
427
+ if len(segments) < len(raw_segments):
428
+ print(f"⚠️ [SMART-CUT] {len(raw_segments) - len(segments)} segmento(s) descartado(s) — start/end não bateu com nenhum bloco real da transcrição (tolerância 0.35s). Possível timestamp alucinado. Bruto: {raw_segments}")
429
+ for s in segments:
430
+ print(f" MANTIDO [{s['start']:.2f} - {s['end']:.2f}] \"{s['text']}\" — motivo: {s['reason']}")
431
+ print(f"📦 [SMART-CUT] Resumo devolvido: {summary!r}")
432
+
433
  return {"segments": segments, "summary": summary}
434
 
435
  except Exception as e: