From 192c57ee138e19b73bcace0ded9dc258dde47793 Mon Sep 17 00:00:00 2001 From: hbrain Date: Thu, 28 May 2026 19:20:56 +0000 Subject: [PATCH] fix(intro): precompose alpha-adjusted logo PNG and overlay to respect transparency across ffmpeg builds --- movmaker.py | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/movmaker.py b/movmaker.py index fca9058..9326805 100755 --- a/movmaker.py +++ b/movmaker.py @@ -31,11 +31,15 @@ IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tif", ".tiff"} VIDEO_EXTS = {".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"} AUDIO_EXTS = {".mp3", ".wav", ".m4a", ".aac", ".flac", ".ogg"} DATA_NAMES = {"data.txt", "info.txt", "title.txt"} + BASE_DIR = Path(__file__).resolve().parent FFMPEG = BASE_DIR / "ffmpeg" / "ffmpeg" FFPROBE = BASE_DIR / "ffmpeg" / "ffprobe" INTRO_LOGO_NAME = "intro.png" DEFAULT_INTRO_LOGO = BASE_DIR / "img" / "moto_travel.png" +JAMENDO_ENV = Path.home() / ".config" / "movmaker" / "jamendo.env" +LOG_PATH: Path | None = None + INTRO_LOGO_TARGET_HEIGHT_RATIO = 0.99 INTRO_LOGO_EXTRA_HOLD = 6.0 INTRO_LOGO_FADE_OFFSET = 3.0 @@ -56,8 +60,6 @@ CLIP_AUDIO_SIDECHAIN_THRESHOLD = 0.04 CLIP_AUDIO_SIDECHAIN_RATIO = 8.0 CLIP_AUDIO_SIDECHAIN_ATTACK = 5.0 CLIP_AUDIO_SIDECHAIN_RELEASE = 250.0 -JAMENDO_ENV = Path.home() / ".config" / "movmaker" / "jamendo.env" -LOG_PATH: Path | None = None def tool_path(local_path: Path, fallback: str) -> str: @@ -707,6 +709,11 @@ def drawtext_escape(text: str) -> str: def render_intro_logo_frame(logo: Path, out: Path, width: int, height: int, alpha: float) -> None: """Build a single black frame with the intro logo centered and scaled. + This implementation first renders a temporary PNG of the logo with the + requested alpha applied, then overlays that precomposed PNG onto a black + background. This avoids passing problematic options to the overlay filter + and works reliably across ffmpeg builds. + Args: logo: Source PNG to overlay. out: Destination PNG path for the composed frame. @@ -718,17 +725,30 @@ def render_intro_logo_frame(logo: Path, out: Path, width: int, height: int, alph raise ValueError("intro logo frame must have positive width/height") alpha = min(1.0, max(0.0, alpha)) target_logo_height = max(1, int(round(height * INTRO_LOGO_TARGET_HEIGHT_RATIO))) - filters = ( - f"[1:v]scale=w={width}:h={target_logo_height}:force_original_aspect_ratio=decrease," - f"format=rgba,colorchannelmixer=aa={alpha:.3f}[logo];" - f"[0:v]format=rgba[bg];" - f"[bg][logo]overlay=x=(W-w)/2:y=(H-h)/2:format=rgba[vout]" - ) - cmd = [ - tool_path(FFMPEG, "ffmpeg"), "-y", - "-f", "lavfi", "-i", f"color=c=black:s={width}x{height}", + + ffmpeg_exe = tool_path(FFMPEG, "ffmpeg") + + # Prepare a temporary logo PNG with the requested alpha applied. + tmp_logo = out.with_name(out.stem + "_alpha.png") + cmd_logo = [ + ffmpeg_exe, "-y", "-i", str(logo), - "-filter_complex", filters, + "-vf", ( + f"scale=w={width}:h={target_logo_height}:force_original_aspect_ratio=decrease," + f"format=rgba,colorchannelmixer=aa={alpha:.3f}" + ), + "-frames:v", "1", + "-pix_fmt", "rgba", + str(tmp_logo), + ] + run(cmd_logo) + + # Overlay the precomposed logo onto a black background and write the single frame. + cmd = [ + ffmpeg_exe, "-y", + "-f", "lavfi", "-i", f"color=c=black:s={width}x{height}", + "-i", str(tmp_logo), + "-filter_complex", "[0:v][1:v]overlay=x=(W-w)/2:y=(H-h)/2:format=auto[vout]", "-map", "[vout]", "-frames:v", "1", "-pix_fmt", "rgba", @@ -736,7 +756,14 @@ def render_intro_logo_frame(logo: Path, out: Path, width: int, height: int, alph "-update", "1", str(out), ] - run(cmd) + try: + run(cmd) + finally: + try: + if tmp_logo.exists(): + tmp_logo.unlink() + except Exception: + pass def normalize_clip(item: MediaItem, out: Path, width: int, height: int, fps: int, preset: str, crf: int) -> None: