capcut-agent/capcut_agent/scene.py
hehihoho3@gmail.com bf1b387d6d chore: git 저장소 초기화 (기존 코드 스냅샷)
컷별 댓글 추천 작업을 태스크 단위로 되돌릴 수 있게 버전관리를 시작한다.
.gitignore 로 영상·캐시(.downloads 2.7G, .comments 72M, .media 28M)와
비밀키(.gemini_key)를 제외했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 11:36:05 +09:00

70 lines
2.4 KiB
Python

"""ffmpeg scene 필터 기반 시각적 장면전환 감지.
화면이 확 바뀌는 지점(컷/앵글전환/B롤)을 찾아 그 timestamp(초, 소스 기준)를 반환.
draft 의 비디오 세그먼트를 이 지점에서 추가로 쪼개면, 말이 이어져도(무음 없음)
장면이 바뀌는 곳에 캡컷 편집 컷이 생긴다. 시간은 그대로라 자막 싱크에 영향 없음.
"""
from __future__ import annotations
import re
import subprocess
from typing import List, Tuple
Segment = Tuple[float, float]
_PTS = re.compile(r"pts_time:([\d.]+)")
def detect_scene_changes(video_path: str, *, threshold: float = 0.4) -> List[float]:
"""장면전환 timestamp(초, 소스 기준) 리스트. threshold=scene score(0~1) 임계.
낮을수록 민감(과분할), 높을수록 큰 전환만. 0.4 = 실제 컷 위주.
"""
cmd = [
"ffmpeg", "-hide_banner", "-nostats", "-i", video_path,
"-vf", f"select='gt(scene,{threshold})',showinfo",
"-an", "-f", "null", "-",
]
# showinfo 는 stderr 로 출력. silence.py 와 동일하게 stderr→stdout 병합.
proc = subprocess.run(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, encoding="utf-8", errors="replace",
)
times: List[float] = []
for line in (proc.stdout or "").splitlines():
if "showinfo" not in line:
continue
m = _PTS.search(line)
if m:
times.append(float(m.group(1)))
return sorted(set(times))
def split_clips_at_scenes(
video_clips: List[Segment],
scene_times: List[float],
*,
min_len: float = 0.4,
) -> List[Segment]:
"""보존 구간(소스 시간)을 장면전환 지점에서 추가로 분할.
인접 분할이라 누적 길이·타임라인 매핑이 동일 → 자막 싱크 영향 없음.
분할 후 min_len 미만 조각은 앞 조각과 병합(너무 잘게 쪼개짐 방지).
"""
if not scene_times:
return video_clips
out: List[Segment] = []
for s, e in video_clips:
# 이 구간 내부의 장면전환 지점만(양끝 여유 배제)
cuts = [t for t in scene_times if s + min_len <= t <= e - min_len]
if not cuts:
out.append((s, e))
continue
prev = s
for t in cuts:
if t - prev >= min_len:
out.append((prev, t))
prev = t
out.append((prev, e))
return out