149 lines
5.4 KiB
Python
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()
|