reels-app/scripts/clip.py

149 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""
clip.py — Vse v enem: vzemi 16:9 video, izreži klip, reframe na 9:16, dodaj podnapise.
Primer:
# Cel video → 9:16 z face tracking + slovenskimi podnapisi
python3 clip.py input.mp4 reel.mp4 --lang sl
# 30s klip od 1:20 dalje, blur ozadje, brez podnapisov
python3 clip.py input.mp4 reel.mp4 --start 80 --duration 30 --mode blur --no-subs
# Več klipov hkrati prek timestamp seznama
python3 clip.py input.mp4 out_dir/ --clips "0:30-1:00,2:15-2:45,5:00-5:30" --lang sl
"""
import argparse
import shutil
import subprocess
import sys
import os
import tempfile
from pathlib import Path
def parse_ts(s):
"""'1:23' → 83.0, '1:23.5' → 83.5, '90' → 90.0"""
s = s.strip()
if ":" in s:
parts = s.split(":")
if len(parts) == 2:
return int(parts[0]) * 60 + float(parts[1])
if len(parts) == 3:
return int(parts[0]) * 3600 + int(parts[1]) * 60 + float(parts[2])
return float(s)
def parse_clips(spec):
"""'0:30-1:00,2:15-2:45' → [(30.0, 60.0), (135.0, 165.0)]"""
out = []
for c in spec.split(","):
a, b = c.split("-")
out.append((parse_ts(a), parse_ts(b)))
return out
SCRIPT_DIR = Path(__file__).parent
def run_clip(src, dst, start, duration, mode, lang, model, style, no_subs, quality,
fade_in=0.0, fade_out=0.0, srt_path=None):
"""Naredi en klip src → dst."""
print(f"🎯 run_clip args: src={src}, dst={dst}, start={start!r}, duration={duration!r}, "
f"mode={mode}, fade_in={fade_in}, fade_out={fade_out}, "
f"srt={'yes' if srt_path else 'no'}", file=sys.stderr)
tmp = tempfile.mkdtemp(prefix="reel_")
try:
reframed = Path(tmp) / "reframed.mp4"
# 1. Reframe (in trim hkrati)
cmd = [
"python3", str(SCRIPT_DIR / "reframe.py"),
str(src), str(reframed),
"--mode", mode,
"--quality", quality,
]
if start is not None:
cmd += ["--start", str(start)]
if duration is not None:
cmd += ["--duration", str(duration)]
if fade_in > 0:
cmd += ["--fade-in", str(fade_in)]
if fade_out > 0:
cmd += ["--fade-out", str(fade_out)]
print(f"🔧 REFRAME CMD: {' '.join(cmd)}", file=sys.stderr)
print(f"\n▶ Klip: {dst.name}")
r = subprocess.run(cmd)
if r.returncode != 0:
print(f"❌ Reframe napaka pri {dst.name}", file=sys.stderr)
return False
# 2. Subtitles (opcijsko)
if no_subs:
shutil.move(str(reframed), str(dst))
else:
cmd = [
"python3", str(SCRIPT_DIR / "subtitle.py"),
str(reframed), str(dst),
"--model", model,
"--style", style,
]
if lang:
cmd += ["--lang", lang]
if srt_path:
cmd += ["--srt", str(srt_path)]
r = subprocess.run(cmd)
if r.returncode != 0:
print(f"❌ Subtitle napaka — shranim brez", file=sys.stderr)
shutil.move(str(reframed), str(dst))
return True
finally:
shutil.rmtree(tmp, ignore_errors=True)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("input")
ap.add_argument("output", help="Datoteka (en klip) ali mapa (več klipov)")
ap.add_argument("--start", type=str, default=None, help="Začetek (s ali mm:ss)")
ap.add_argument("--duration", type=float, default=None, help="Trajanje v s")
ap.add_argument("--fade-in", type=float, default=0.0, help="Audio fade in (s)")
ap.add_argument("--fade-out", type=float, default=0.0, help="Audio fade out (s)")
ap.add_argument("--clips", type=str, default=None,
help="Več klipov: '0:30-1:00,2:15-2:45'")
ap.add_argument("--mode", default="track", choices=["track", "center", "blur"])
ap.add_argument("--lang", default=None, help="sl, de, en, ... (privzeto auto)")
ap.add_argument("--model", default="large-v3",
choices=["tiny", "base", "small", "medium", "large-v3"])
ap.add_argument("--style", default="reels", choices=["reels", "yellow", "minimal"])
ap.add_argument("--no-subs", action="store_true")
ap.add_argument("--quality", default="medium", choices=["fast", "medium", "high"])
ap.add_argument("--srt", default=None, help="Že-pripravljen SRT (preskoči Whisper)")
args = ap.parse_args()
src = Path(args.input)
if not src.exists():
print(f"{src} ne obstaja", file=sys.stderr)
sys.exit(1)
if args.clips:
clips = parse_clips(args.clips)
out_dir = Path(args.output)
out_dir.mkdir(parents=True, exist_ok=True)
ok = 0
for i, (s, e) in enumerate(clips, 1):
dst = out_dir / f"reel_{i:02d}.mp4"
if run_clip(src, dst, s, e - s, args.mode, args.lang, args.model,
args.style, args.no_subs, args.quality):
ok += 1
print(f"\n✅ Dokončano: {ok}/{len(clips)} klipov v {out_dir}")
else:
start = parse_ts(args.start) if args.start else None
run_clip(src, Path(args.output), start, args.duration, args.mode,
args.lang, args.model, args.style, args.no_subs, args.quality,
fade_in=args.fade_in, fade_out=args.fade_out,
srt_path=args.srt)
if __name__ == "__main__":
main()