Reuse intro logo as outro and refine title fade

This commit is contained in:
hbrain 2026-05-28 14:48:10 +00:00
parent 61fbf6382e
commit 2ac00e0e67
2 changed files with 34 additions and 11 deletions

View file

@ -36,7 +36,7 @@ FFMPEG = BASE_DIR / "ffmpeg" / "ffmpeg"
FFPROBE = BASE_DIR / "ffmpeg" / "ffprobe"
INTRO_LOGO_NAME = "intro.png"
DEFAULT_INTRO_LOGO = BASE_DIR / "img" / "moto_travel.png"
INTRO_LOGO_TARGET_HEIGHT_RATIO = 0.75
INTRO_LOGO_TARGET_HEIGHT_RATIO = 0.90
INTRO_LOGO_EXTRA_HOLD = 6.0
INTRO_LOGO_FADE_OFFSET = 3.0
INTRO_LOGO_DEFAULT_ALPHA = 1.0
@ -883,6 +883,7 @@ def build_video(
text_dir: Path,
video_intro_duration: float | None = None,
intro_visible_lead: float = 0.0,
skip_video_fade_out: bool = False,
) -> None:
output.parent.mkdir(parents=True, exist_ok=True)
@ -937,7 +938,7 @@ def build_video(
fade_label = current
apply_fade_in = video_fade > 0 and INTRO_VIDEO_FADE_ENABLED
apply_fade_out = video_fade > 0
apply_fade_out = video_fade > 0 and not skip_video_fade_out
if apply_fade_in or apply_fade_out:
fade_in_start = intro_duration
fade_out_start = max(0.0, total_duration - video_fade)
@ -961,34 +962,44 @@ def build_video(
subtitle_font_size = max(40, height // 18)
main_title = data.title or title_lines[0]
subtitle = " | ".join(x for x in [data.date, data.place] if x)
title_alpha = "if(lt(t\\,2)\\,0\\,if(lt(t\\,3)\\,(t-2)/1\\,if(lt(t\\,7)\\,1\\,if(lt(t\\,8)\\,8-t\\,0))))"
fade_in_start = INTRO_TITLE_FADE_DELAY
fade_in_end = fade_in_start + INTRO_TITLE_FADE_LENGTH
visible_end = fade_in_end + INTRO_TITLE_VISIBLE
fade_out_end = visible_end + INTRO_TITLE_FADE_LENGTH
title_alpha = (
f"if(lt(t\\,{fade_in_start:.3f})\\,0\\,"
f"if(lt(t\\,{fade_in_end:.3f})\\,(t-{fade_in_start:.3f})/{INTRO_TITLE_FADE_LENGTH:.3f}\\,"
f"if(lt(t\\,{visible_end:.3f})\\,1\\,"
f"if(lt(t\\,{fade_out_end:.3f})\\,({fade_out_end:.3f}-t)/{INTRO_TITLE_FADE_LENGTH:.3f}\\,0))))"
)
title_enable = f"enable='between(t,{fade_in_start:.3f},{fade_out_end:.3f})'"
if subtitle:
title_y = f"(h-text_h)/2-{title_font_size // 2}"
filters.append(
f"[{current}]drawtext=fontfile='{title_font}':{textfile_arg(main_title)}:x=(w-text_w)/2:y={title_y}:"
f"fontsize={title_font_size}:fontcolor=white:borderw=0:shadowcolor=black@0.75:shadowx=3:shadowy=3:"
f"alpha='{title_alpha}':enable='between(t,0,7)'[title0]"
f"alpha='{title_alpha}':{title_enable}[title0]"
)
filters.append(
f"[title0]drawtext=fontfile='{title_font}':{textfile_arg(main_title)}:x=(w-text_w)/2+1:y={title_y}:"
f"fontsize={title_font_size}:fontcolor=white:borderw=0:shadowcolor=black@0.75:shadowx=3:shadowy=3:"
f"alpha='{title_alpha}':enable='between(t,0,7)'[title0b]"
f"alpha='{title_alpha}':{title_enable}[title0b]"
)
filters.append(
f"[title0b]drawtext=fontfile='{title_font}':{textfile_arg(subtitle)}:x=(w-text_w)/2:y=(h-text_h)/2+{subtitle_font_size}:"
f"fontsize={subtitle_font_size}:fontcolor=white:borderw=0:shadowcolor=black@0.75:shadowx=2:shadowy=2:"
f"alpha='{title_alpha}':enable='between(t,0,7)'[{title_label}]"
f"alpha='{title_alpha}':{title_enable}[{title_label}]"
)
else:
filters.append(
f"[{current}]drawtext=fontfile='{title_font}':{textfile_arg(main_title)}:x=(w-text_w)/2:y=(h-text_h)/2:"
f"fontsize={title_font_size}:fontcolor=white:borderw=0:shadowcolor=black@0.75:shadowx=3:shadowy=3:"
f"alpha='{title_alpha}':enable='between(t,0,7)'[title0]"
f"alpha='{title_alpha}':{title_enable}[title0]"
)
filters.append(
f"[title0]drawtext=fontfile='{title_font}':{textfile_arg(main_title)}:x=(w-text_w)/2+1:y=(h-text_h)/2:"
f"fontsize={title_font_size}:fontcolor=white:borderw=0:shadowcolor=black@0.75:shadowx=3:shadowy=3:"
f"alpha='{title_alpha}':enable='between(t,0,7)'[{title_label}]"
f"alpha='{title_alpha}':{title_enable}[{title_label}]"
)
else:
filters.append(f"[{current}]copy[{title_label}]")
@ -1265,6 +1276,8 @@ def main(argv: list[str] | None = None) -> int:
console("Audio metadata: " + " - ".join(x for x in [artist, title] if x))
video_intro_duration = audio_fade_in
intro_visible_lead = 0.0
intro_clip_path: Path | None = None
intro_clip_duration = 0.0
if intro_logo_path is not None and audio_fade_in > 0:
if first_media_resolution is None:
console("Warning: Could not determine first media resolution; skipping intro logo frame")
@ -1278,12 +1291,22 @@ def main(argv: list[str] | None = None) -> int:
normalize_clip(intro_item, intro_clip, width, height, fps, preset, crf)
clips = [intro_clip, *clips]
durations = [intro_item.duration, *durations]
intro_clip_path = intro_clip
intro_clip_duration = intro_item.duration
captions = ["", *captions]
clip_audio_paths = [None, *clip_audio_paths]
clip_stamps = ["", *clip_stamps]
intro_visible_lead = min(intro_item.duration, INTRO_LOGO_FADE_OFFSET)
video_intro_duration = 0.0
build_video(clips, durations, audio, clip_audio_paths, output, data, first_media_date, captions, clip_stamps, fade, audio_fade, audio_fade_in, video_fade, args.intro_logo, intro_logo_alpha, width, height, fps, preset, crf, tmp, video_intro_duration, intro_visible_lead=intro_visible_lead)
outro_added = False
if intro_clip_path is not None:
clips.append(intro_clip_path)
durations.append(intro_clip_duration)
captions.append("")
clip_audio_paths.append(None)
clip_stamps.append("")
outro_added = True
build_video(clips, durations, audio, clip_audio_paths, output, data, first_media_date, captions, clip_stamps, fade, audio_fade, audio_fade_in, video_fade, args.intro_logo, intro_logo_alpha, width, height, fps, preset, crf, tmp, video_intro_duration, intro_visible_lead=intro_visible_lead, skip_video_fade_out=outro_added)
console(f"Done: {output}")
return 0