컷별 댓글 추천 작업을 태스크 단위로 되돌릴 수 있게 버전관리를 시작한다. .gitignore 로 영상·캐시(.downloads 2.7G, .comments 72M, .media 28M)와 비밀키(.gemini_key)를 제외했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
"""템플릿 CLI: 잘라둔 클립 → 제목 띠 + 무음컷 + 자막 + 채널 띠 (9:16 숏폼 템플릿).
|
|
|
|
사용:
|
|
python build_template.py <video> [draft_name]
|
|
(제목/채널은 아래 추천값으로 들어가고, CapCut에서 편집 가능)
|
|
|
|
레이아웃: 상단 검은 띠=제목(2줄) / 중앙=영상(좌우 살짝 크롭) / 하단 검은 띠=채널.
|
|
제목·자막·채널은 캡컷 편집 가능한 텍스트, 영상 틀만 굽는다.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
|
|
from capcut_agent.probe import probe
|
|
from capcut_agent.transcribe import transcribe
|
|
from capcut_agent.highlight import clips_in_window
|
|
from capcut_agent.media import to_template
|
|
from capcut_agent.draft import build_template_draft
|
|
|
|
try:
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
except Exception:
|
|
pass
|
|
|
|
MEDIA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".media")
|
|
os.makedirs(MEDIA_DIR, exist_ok=True)
|
|
|
|
# 추천 제목/채널 (클립: 거제야호 애드립 비하인드). 캡컷에서 자유 수정.
|
|
REC_TITLE_TOP = "<리센느 비하인드>"
|
|
REC_TITLE_MAIN = "'거제야호'는 대본에 없었다"
|
|
REC_CHANNEL = "밥묵자 · 리센느"
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
if len(argv) < 2:
|
|
print("usage: python build_template.py <video> [name]")
|
|
return 2
|
|
video = os.path.abspath(argv[1])
|
|
if not os.path.isfile(video):
|
|
print(f"파일 없음: {video}")
|
|
return 2
|
|
name = argv[2] if len(argv) > 2 else (
|
|
os.path.splitext(os.path.basename(video))[0] + "_템플릿"
|
|
)
|
|
|
|
t0 = time.time()
|
|
src_meta = probe(video)
|
|
print(f"[probe] {src_meta.width}x{src_meta.height} @ {src_meta.fps}fps, {src_meta.duration:.1f}s")
|
|
|
|
tpl_path = os.path.join(MEDIA_DIR, f"{name}.mp4")
|
|
to_template(video, tpl_path)
|
|
tpl_meta = probe(tpl_path)
|
|
print(f"[template] {tpl_meta.width}x{tpl_meta.height} → {tpl_path}")
|
|
|
|
tr = transcribe(video, model_size="medium", language="ko", use_cache=True)
|
|
clips = clips_in_window(tr, 0.0, src_meta.duration)
|
|
if not clips:
|
|
print("[asr] 발화 없음")
|
|
return 1
|
|
kept = sum(e - s for s, e, _ in clips)
|
|
print(f"[cut] 발화 {len(clips)}구간, {kept:.1f}s ({src_meta.duration - kept:.1f}s 무음 제거)")
|
|
|
|
path = build_template_draft(
|
|
tpl_path, clips, tpl_meta, name,
|
|
title_top=REC_TITLE_TOP, title_main=REC_TITLE_MAIN, channel=REC_CHANNEL,
|
|
)
|
|
print(f"[draft] {path}")
|
|
print(f" 제목: {REC_TITLE_TOP} / {REC_TITLE_MAIN}")
|
|
print(f" 채널: {REC_CHANNEL}")
|
|
print(f"[done] {time.time()-t0:.1f}s. CapCut에서 '{name}' 열어 검증.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main(sys.argv))
|