컷별 댓글 추천 작업을 태스크 단위로 되돌릴 수 있게 버전관리를 시작한다. .gitignore 로 영상·캐시(.downloads 2.7G, .comments 72M, .media 28M)와 비밀키(.gemini_key)를 제외했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
42 lines
1.2 KiB
Python
42 lines
1.2 KiB
Python
"""ffprobe 기반 영상 메타데이터 추출."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass
|
|
class VideoMeta:
|
|
width: int
|
|
height: int
|
|
fps: int
|
|
duration: float # seconds
|
|
|
|
|
|
def probe(video_path: str) -> VideoMeta:
|
|
cmd = [
|
|
"ffprobe", "-v", "error",
|
|
"-select_streams", "v:0",
|
|
"-show_entries", "stream=width,height,avg_frame_rate:format=duration",
|
|
"-of", "json", video_path,
|
|
]
|
|
# 한글 경로 대비 UTF-8 고정 (Windows 기본 cp949 디코드 에러 방지)
|
|
out = subprocess.run(
|
|
cmd, capture_output=True, text=True, check=True,
|
|
encoding="utf-8", errors="replace",
|
|
).stdout
|
|
data = json.loads(out)
|
|
stream = data["streams"][0]
|
|
width = int(stream["width"])
|
|
height = int(stream["height"])
|
|
|
|
# avg_frame_rate 는 "30000/1001" 형태 → 정수 fps 로 반올림
|
|
num, _, den = stream.get("avg_frame_rate", "30/1").partition("/")
|
|
den = den or "1"
|
|
fps_val = float(num) / float(den) if float(den) else 30.0
|
|
fps = max(1, round(fps_val))
|
|
|
|
duration = float(data["format"]["duration"])
|
|
return VideoMeta(width=width, height=height, fps=fps, duration=duration)
|