diff --git a/capcut_agent/recommend.py b/capcut_agent/recommend.py index 002c70e..02c57e7 100644 --- a/capcut_agent/recommend.py +++ b/capcut_agent/recommend.py @@ -349,3 +349,24 @@ def build_highlight_cuts(hl, comments, *, key=None, quotas=None): "bottom": c.get("bottom") or "", "quota": quotas[i], "picks": picks[i]} for i, c in enumerate(cuts)] return out, sum(quotas), ai_failed + + +def cuts_from_state(places, orig_ranges, captions, comments, *, key=None): + """받아쓰기 상태 + 댓글 → 검토 화면용 (cuts[], need, ai_failed). 세 탭 공통. + + ⚠ 좌표계가 둘이다. 섞으면 카드가 통째로 어긋난다(스펙 §4): + - `places`·`captions` = 압축 타임라인 → 자막 추출·장수·배치 + - `orig_ranges` = 원본 영상 시각 → ⭐ 분:초 매칭 + 둘은 같은 길이여야 하고 인덱스로만 짝지어 다닌다. + """ + from .pipeline import captions_for_places # 순환 임포트 회피: pipeline은 draft·comments를 + # 임포트하고 app.py가 pipeline·recommend를 둘 다 임포트하므로, 파일 상단에서 + # pipeline을 가져오면 임포트 순환이 생긴다. 호출 시점(함수 안)에서만 가져온다. + if not places or len(places) != len(orig_ranges): + return [], 0, False + bottoms = captions_for_places(captions, places) + quotas = [max(1, int((p1 - p0) // CARD_SEC)) for p0, p1 in places] + cuts = [{"start": s, "end": e, "bottom": b, "effect": ""} + for (s, e), b in zip(orig_ranges, bottoms)] + return build_highlight_cuts({"paste": {"cuts": cuts}}, comments, + key=key, quotas=quotas)