mot / renderer.py
digidau's picture
Update renderer.py
1f94741 verified
Raw
History Blame Contribute Delete
4.52 kB
# renderer.py
import subprocess
from pathlib import Path
from config import (
TEMP_DIR,
OUTPUT_DIR,
)
# =====================================================
# BUILD CONCAT FILE
# =====================================================
def build_concat_file(
timeline,
concat_file=None
):
"""
timeline:
[
{
"path": "clips/1_A.mp4",
"duration": 7.1,
...
}
]
"""
if concat_file is None:
concat_file = (
TEMP_DIR / "concat.txt"
)
concat_file = Path(concat_file)
with open(
concat_file,
"w",
encoding="utf-8"
) as f:
for scene in timeline:
clip_path = (
Path(scene["path"])
.resolve()
.as_posix()
)
f.write(
f"file '{clip_path}'\n"
)
return str(concat_file)
# =====================================================
# ESTIMATE TIMELINE DURATION
# =====================================================
def estimate_duration(
timeline
):
total = 0
for scene in timeline:
total += scene["duration"]
return round(total, 2)
# =====================================================
# RENDER TIMELINE (FAST)
# =====================================================
def render_timeline(
timeline,
output_file=None
):
"""
Fast concat mode.
Semua clip HARUS:
- 1280x720
- 30fps
- h264
- yuv420p
- no audio
"""
if not timeline:
raise ValueError(
"Timeline is empty"
)
if output_file is None:
output_file = (
OUTPUT_DIR
/ "video_only.mp4"
)
output_file = Path(output_file)
concat_file = build_concat_file(
timeline
)
cmd = [
"ffmpeg",
"-y",
"-f",
"concat",
"-safe",
"0",
"-i",
concat_file,
"-c",
"copy",
str(output_file)
]
subprocess.run(
cmd,
check=True,
capture_output=True
)
return str(output_file)
# =====================================================
# SAFE RENDER (RE-ENCODE)
# =====================================================
def render_timeline_safe(
timeline,
output_file=None
):
"""
Gunakan jika concat copy gagal.
Lebih lambat,
tetapi paling kompatibel.
"""
if not timeline:
raise ValueError(
"Timeline is empty"
)
if output_file is None:
output_file = (
OUTPUT_DIR
/ "video_only.mp4"
)
output_file = Path(output_file)
concat_file = build_concat_file(
timeline
)
cmd = [
"ffmpeg",
"-y",
"-f",
"concat",
"-safe",
"0",
"-i",
concat_file,
"-c:v",
"libx264",
"-preset",
"ultrafast",
"-crf",
"28",
"-pix_fmt",
"yuv420p",
str(output_file)
]
subprocess.run(
cmd,
check=True,
capture_output=True
)
return str(output_file)
# =====================================================
# AUTO RENDER
# =====================================================
def render(
timeline,
output_file=None
):
"""
Coba fast mode dulu.
Jika gagal, fallback ke safe mode.
"""
try:
return render_timeline(
timeline,
output_file
)
except Exception as e:
print(
"[Renderer] Fast concat failed:"
)
print(e)
print(
"[Renderer] Falling back to re-encode..."
)
return render_timeline_safe(
timeline,
output_file
)
# =====================================================
# TEST
# =====================================================
if __name__ == "__main__":
timeline = [
{
"path": "clips/1_A.mp4",
"duration": 7.0
},
{
"path": "clips/2_A.mp4",
"duration": 7.0
},
{
"path": "clips/3_A.mp4",
"duration": 7.0
},
]
print(
"Estimated:",
estimate_duration(
timeline
)
)
result = render(
timeline
)
print(
"Output:",
result
)