Expose timing constants and intro fade control
This commit is contained in:
parent
cdb57525ca
commit
4a2101b75e
3 changed files with 44 additions and 26 deletions
41
movmaker.py
41
movmaker.py
|
|
@ -36,12 +36,19 @@ 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_SCALE = 1.35
|
||||
INTRO_LOGO_TARGET_HEIGHT_RATIO = 0.75
|
||||
INTRO_LOGO_EXTRA_HOLD = 6.0
|
||||
INTRO_LOGO_FADE_OFFSET = 3.0
|
||||
INTRO_TITLE_FADE_DELAY = 2.0
|
||||
INTRO_TITLE_FADE_LENGTH = 1.0
|
||||
INTRO_TITLE_VISIBLE = 4.0
|
||||
INTRO_TITLE_VISIBLE = 6.0
|
||||
DEFAULT_IMAGE_DURATION = 6.0
|
||||
DEFAULT_CROSSFADE_DURATION = 3.0
|
||||
FIRST_IMAGE_DURATION_MULTIPLIER = 2.0
|
||||
DEFAULT_AUDIO_FADE_IN = 2.0
|
||||
DEFAULT_AUDIO_FADE_OUT = 10.0
|
||||
DEFAULT_VIDEO_FADE = 1.0
|
||||
INTRO_VIDEO_FADE_ENABLED = False
|
||||
JAMENDO_ENV = Path.home() / ".config" / "movmaker" / "jamendo.env"
|
||||
LOG_PATH: Path | None = None
|
||||
|
||||
|
|
@ -662,10 +669,9 @@ def render_intro_logo_frame(logo: Path, out: Path, width: int, height: int, alph
|
|||
if width <= 0 or height <= 0:
|
||||
raise ValueError("intro logo frame must have positive width/height")
|
||||
alpha = min(1.0, max(0.0, alpha))
|
||||
scale_expr_w = f"min(iw*{INTRO_LOGO_SCALE}, {width})"
|
||||
scale_expr_h = f"min(ih*{INTRO_LOGO_SCALE}, {height})"
|
||||
target_logo_height = max(1, int(round(height * INTRO_LOGO_TARGET_HEIGHT_RATIO)))
|
||||
filters = (
|
||||
f"[1:v]scale=w='{scale_expr_w}':h='{scale_expr_h}':force_original_aspect_ratio=decrease,"
|
||||
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][logo]overlay=x=(W-w)/2:y=(H-h)/2:format=auto,format=rgba[vout]"
|
||||
)
|
||||
|
|
@ -918,13 +924,18 @@ def build_video(
|
|||
|
||||
|
||||
fade_label = current
|
||||
if video_fade > 0:
|
||||
apply_fade_in = video_fade > 0 and INTRO_VIDEO_FADE_ENABLED
|
||||
apply_fade_out = video_fade > 0
|
||||
if apply_fade_in or apply_fade_out:
|
||||
fade_in_start = intro_duration
|
||||
fade_out_start = max(0.0, total_duration - video_fade)
|
||||
fade_label = "video_faded"
|
||||
filters.append(
|
||||
f"[{current}]fade=t=in:st={fade_in_start:.3f}:d={video_fade:.3f},fade=t=out:st={fade_out_start:.3f}:d={video_fade:.3f}[{fade_label}]"
|
||||
)
|
||||
fade_parts = []
|
||||
if apply_fade_in:
|
||||
fade_parts.append(f"fade=t=in:st={fade_in_start:.3f}:d={video_fade:.3f}")
|
||||
if apply_fade_out:
|
||||
fade_parts.append(f"fade=t=out:st={fade_out_start:.3f}:d={video_fade:.3f}")
|
||||
filters.append(f"[{current}]" + ",".join(fade_parts) + f"[{fade_label}]")
|
||||
current = fade_label
|
||||
|
||||
title_lines = [x for x in [data.title, data.date, data.place] if x]
|
||||
|
|
@ -1113,11 +1124,11 @@ def main(argv: list[str] | None = None) -> int:
|
|||
parser = argparse.ArgumentParser(description="Make a video from a flat folder of images/videos/audio.")
|
||||
parser.add_argument("input", type=Path, help="flat input directory")
|
||||
parser.add_argument("--out-dir", type=Path, default=Path("."), help="directory for auto-named output videos")
|
||||
parser.add_argument("--image-duration", type=float, default=6.0, help="seconds each image is visible")
|
||||
parser.add_argument("--fade", type=float, default=3.0, help="crossfade duration in seconds")
|
||||
parser.add_argument("--audio-fade", type=float, default=10.0, help="music fade-out duration in seconds")
|
||||
parser.add_argument("--audio-fade-in", type=float, default=2.0, help="music fade-in duration in seconds")
|
||||
parser.add_argument("--video-fade", type=float, default=1.0, help="video fade-in/fade-out duration in seconds")
|
||||
parser.add_argument("--image-duration", type=float, default=DEFAULT_IMAGE_DURATION, help="seconds each image is visible")
|
||||
parser.add_argument("--fade", type=float, default=DEFAULT_CROSSFADE_DURATION, help="crossfade duration in seconds")
|
||||
parser.add_argument("--audio-fade", type=float, default=DEFAULT_AUDIO_FADE_OUT, help="music fade-out duration in seconds")
|
||||
parser.add_argument("--audio-fade-in", type=float, default=DEFAULT_AUDIO_FADE_IN, help="music fade-in duration in seconds")
|
||||
parser.add_argument("--video-fade", type=float, default=DEFAULT_VIDEO_FADE, help="video fade-in/fade-out duration in seconds")
|
||||
parser.add_argument("--intro-logo", type=Path, default=DEFAULT_INTRO_LOGO, help="PNG logo shown centered over the black intro")
|
||||
parser.add_argument("--intro-logo-alpha", type=float, default=1.0, help="opacity for the intro logo, from 0 to 1")
|
||||
parser.add_argument("--resolution", type=parse_resolution, default=(1920, 1080), help="output resolution, e.g. 1920x1080")
|
||||
|
|
@ -1176,7 +1187,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||
if item.kind != "image":
|
||||
continue
|
||||
if idx == 0:
|
||||
item.duration = max(item.duration, (args.image_duration * 2) + fade)
|
||||
item.duration = max(item.duration, (args.image_duration * FIRST_IMAGE_DURATION_MULTIPLIER) + fade)
|
||||
elif idx == len(media) - 1:
|
||||
item.duration = item.duration + fade
|
||||
else:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue