diff --git a/README.md b/README.md index f7c11d8..95d0ca6 100644 --- a/README.md +++ b/README.md @@ -229,9 +229,10 @@ The most common visual timing/styling tweaks are exposed as constants near the t | `INTRO_LOGO_EXTRA_HOLD` | Extra seconds the synthesized intro still remains before the first crossfade. | `6.0` | | `INTRO_LOGO_FADE_OFFSET` | Minimum time (s) the logo stays fully visible before the first xfade is allowed to start. | `3.0` | | `INTRO_LOGO_DEFAULT_ALPHA` | Default opacity for the intro logo (1 = fully opaque, 0 = invisible). | `1.0` | -| `INTRO_TITLE_FADE_DELAY` | Seconds after start before the main title begins fading in. | `3.0` | +| `INTRO_TITLE_START_OFFSET` | Seconds after start before the title appears (or starts fading in). | `2.0` | | `INTRO_TITLE_FADE_LENGTH` | Seconds the fade-in/out lasts. | `1.0` | | `INTRO_TITLE_VISIBLE` | Seconds the title stays fully visible between fade in/out. | `4.0` | +| `INTRO_TITLE_FADE_ENABLED` | If `False`, the title pops in at `INTRO_TITLE_START_OFFSET` instead of fading in. | `True` | | `DEFAULT_IMAGE_DURATION` | Default fully visible time per still image before fades are added. | `6.0` | | `DEFAULT_CROSSFADE_DURATION` | Default crossfade duration between media items. | `3.0` | | `FIRST_IMAGE_DURATION_MULTIPLIER` | Multiplier applied to the first still’s `--image-duration` to delay the first transition. | `2.0` | @@ -259,7 +260,7 @@ Adjust these constants and rerun `movmaker.py` to change the intro behavior with - reads the Jamendo client ID from `JAMENDO_CLIENT_ID` or `~/.config/movmaker/jamendo.env` - fades music in over `DEFAULT_AUDIO_FADE_IN` seconds (2 s) and out over `DEFAULT_AUDIO_FADE_OUT` seconds (10 s) by default - builds a black intro still (matching the first media item’s resolution) when `--intro-logo` is provided, scales the logo to `INTRO_LOGO_TARGET_HEIGHT_RATIO` of the frame height (75 % by default), shows it at `INTRO_LOGO_DEFAULT_ALPHA` opacity (1.0 = fully opaque) from frame 0, and keeps it on screen for `audio_fade_in + INTRO_LOGO_EXTRA_HOLD` seconds (with defaults this is `DEFAULT_AUDIO_FADE_IN + INTRO_LOGO_EXTRA_HOLD`, i.e. 8 s) before allowing the first crossfade; the same clip is appended at the end so the last real scene crossfades back into the logo-on-black frame -- opening title text fades in after `INTRO_TITLE_FADE_DELAY` seconds (2 s), stays visible for `INTRO_TITLE_VISIBLE` seconds (4 s), then fades out over `INTRO_TITLE_FADE_LENGTH` seconds (1 s) +- opening title text shows up at `INTRO_TITLE_START_OFFSET` seconds (2 s). When `INTRO_TITLE_FADE_IN_ENABLED` is `True` it fades in/out using `INTRO_TITLE_FADE_LENGTH` (1 s); otherwise it appears instantly and only fades out (if `INTRO_TITLE_FADE_LENGTH` > 0). It remains fully visible for `INTRO_TITLE_VISIBLE` seconds (4 s). - fades the first real clip in with the `--video-fade` duration (default `DEFAULT_VIDEO_FADE`, currently 1 s) while leaving the logo intro instant-on when `INTRO_VIDEO_FADE_ENABLED` is `False`; when a logo intro is present, the reusable outro clip removes the need for a final global fade-out, so the crossfade into the outro handles the closing visuals while audio still follows `DEFAULT_AUDIO_FADE_OUT` - displays optional per-file captions at bottom center with slightly off-white text and a soft shadow; use `|` to split caption lines - adds persistent bottom-left title/date/place and bottom-right `Bubulescu.Org` stamps diff --git a/movmaker.py b/movmaker.py index 99dbc93..ce2d9d6 100755 --- a/movmaker.py +++ b/movmaker.py @@ -36,13 +36,14 @@ 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.90 +INTRO_LOGO_TARGET_HEIGHT_RATIO = 0.75 INTRO_LOGO_EXTRA_HOLD = 6.0 INTRO_LOGO_FADE_OFFSET = 3.0 INTRO_LOGO_DEFAULT_ALPHA = 1.0 -INTRO_TITLE_FADE_DELAY = 2.0 +INTRO_TITLE_START_OFFSET = 4.0 INTRO_TITLE_FADE_LENGTH = 1.0 INTRO_TITLE_VISIBLE = 6.0 +INTRO_TITLE_FADE_ENABLED = True DEFAULT_IMAGE_DURATION = 6.0 DEFAULT_CROSSFADE_DURATION = 3.0 FIRST_IMAGE_DURATION_MULTIPLIER = 2.0 @@ -1001,17 +1002,34 @@ 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) - 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})'" + fade_in_start = INTRO_TITLE_START_OFFSET + fade_in_enabled = INTRO_TITLE_FADE_IN_ENABLED and INTRO_TITLE_FADE_LENGTH > 0 + fade_out_enabled = INTRO_TITLE_FADE_LENGTH > 0 + fade_in_end = fade_in_start + (INTRO_TITLE_FADE_LENGTH if fade_in_enabled else 0.0) + visible_start = fade_in_end if fade_in_enabled else fade_in_start + visible_end = visible_start + INTRO_TITLE_VISIBLE + fade_out_end = visible_end + (INTRO_TITLE_FADE_LENGTH if fade_out_enabled else 0.0) + if fade_in_enabled: + 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))))" + ) + else: + if fade_out_enabled: + title_alpha = ( + f"if(lt(t\\,{fade_in_start:.3f})\\,0\\," + 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)))" + ) + else: + title_alpha = ( + f"if(lt(t\\,{fade_in_start:.3f})\\,0\\," + f"if(lt(t\\,{visible_end:.3f})\\,1\\,0))" + ) + title_enable_end = fade_out_end if fade_out_enabled else visible_end + title_enable = f"enable='between(t,{fade_in_start:.3f},{title_enable_end:.3f})'" if subtitle: title_y = f"(h-text_h)/2-{title_font_size // 2}" filters.append(