"""Zoom (punch-in): por janela, por clipe e por trecho da transcrição. Extraído de models_api.py — a tabela de comandos segue lá. """ from __future__ import annotations import asyncio from pathlib import Path from . import shared from .shared import ( _derived_output, _load_cached_transcript, _transcript_json_path, ) def cmd_add_zoom(args: dict) -> int: """Add an ease-in/ease-out punch-in zoom to one clip.""" path = str(args.get("path", "")) clip_id = str(args.get("clip_id", "")).strip() if not path or not Path(path).exists(): shared.emit({"ok": False, "error": "Arquivo de projeto não encontrado."}) return 1 if not clip_id: shared.emit({"ok": False, "error": "Informe o nome do clipe."}) return 1 try: from server import handle_add_zoom output = _derived_output(path, "_zoom", args) contents = asyncio.run(handle_add_zoom({**args, "filepath": path, "output_path": output})) message = "\n".join(getattr(content, "text", str(content)) for content in contents) if not Path(output).exists(): shared.emit({"ok": False, "error": message}) return 1 shared.emit({"ok": True, "path": output, "message": message}) return 0 except Exception as exc: shared.emit({"ok": False, "error": str(exc)}) return 1 def cmd_zoom_clips(args: dict) -> int: """Return timeline clips with enough identity for the zoom picker.""" path = Path(str(args.get("path", ""))) output_dir = str(args.get("output_dir", "")).strip() if not path.exists(): shared.emit({"ok": False, "error": "Arquivo de projeto não encontrado."}) return 1 try: from server import _require_timeline _, timeline = _require_timeline(str(path)) clips = [] for index, clip in enumerate(timeline.clips): media = clip.media_path or "" cached = _load_cached_transcript(_transcript_json_path(media, output_dir)) if media else None clips.append({ "id": f"{index}:{clip.start.seconds:.6f}", "index": index, "name": clip.name, "start": clip.start.seconds, "duration": clip.duration_seconds, "media": Path(media).name if media else "", "preview": ((cached or {}).get("text", "") or "")[:180], "has_transcript": cached is not None, }) shared.emit({"ok": True, "clips": clips}) return 0 except Exception as exc: shared.emit({"ok": False, "error": str(exc)}) return 1 def cmd_zoom_segments(args: dict) -> int: """Return sentence/word ranges for one timeline clip.""" path = Path(str(args.get("path", ""))) output_dir = str(args.get("output_dir", "")).strip() try: from server import _require_timeline _, timeline = _require_timeline(str(path)) index = int(args.get("index", -1)) if index < 0 or index >= len(timeline.clips): raise ValueError("Clipe selecionado não existe.") clip = timeline.clips[index] if not clip.media_path: raise ValueError("Este clipe não possui mídia associada.") data = _load_cached_transcript(_transcript_json_path(clip.media_path, output_dir)) if data is None: shared.emit({"ok": True, "segments": [], "message": "Transcreva este clipe primeiro."}) return 0 segments = [] for number, segment in enumerate(data.get("segments", [])): text = str(segment.get("text", "")).strip() if text: segments.append({ "id": number, "start": float(segment.get("start", 0)), "end": float(segment.get("end", 0)), "text": text, }) shared.emit({"ok": True, "segments": segments}) return 0 except Exception as exc: shared.emit({"ok": False, "error": str(exc)}) return 1