Skip to content

core

Functions:

create_transition

create_transition(
    image_from: str | Path,
    image_to: str | Path,
    effect: str,
    output: str | Path,
    *,
    sound: str | Path | None = None,
    duration: float = 1.0,
    fps: int = 30,
    **effect_kwargs: Any,
) -> Path
Source code in src/movfx/core.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def create_transition(
    image_from: str | Path,
    image_to: str | Path,
    effect: str,
    output: str | Path,
    *,
    sound: str | Path | None = None,
    duration: float = 1.0,
    fps: int = 30,
    **effect_kwargs: Any,
) -> Path:
    """
    title: Create a transition video between two images
    summary: |
        Loads both images, applies the named effect to produce
        a transition video, optionally overlays audio, and writes
        the result to disk.
    parameters:
        image_from: Path to the source image
        image_to: Path to the destination image
        effect: >
            Name of the effect (e.g. ``fade``, ``dissolve``,
            ``wipe``, ``push``)
        output: Path for the output video file
        sound: Optional path to an audio file
        duration: >
            Duration in seconds (default 1.0).
            May be overridden by sound length or effect minimum.
        fps: Frames per second (default 30)
        effect_kwargs: Additional keyword arguments for the effect
    returns: Path to the generated video file
    raises:
        ValueError: If the effect name is not recognized
    """
    output = Path(output)

    if effect not in EFFECTS:
        available = ", ".join(sorted(EFFECTS.keys()))
        raise ValueError(
            f"Unknown effect '{effect}'. Available effects: {available}"
        )

    effect_cls = EFFECTS[effect]
    effect_instance = effect_cls(**effect_kwargs)

    img_a = load_image(image_from)
    img_b = load_image(image_to)
    img_a, img_b = resize_to_match(img_a, img_b)

    final_duration = resolve_duration(duration, effect_instance, sound)

    clip = effect_instance.build_clip(img_a, img_b, final_duration, fps)

    if sound is not None:
        audio = AudioFileClip(str(sound))
        if audio.duration > final_duration:
            audio = audio.subclipped(0, final_duration)
        clip = clip.with_audio(audio)

    clip.write_videofile(
        str(output),
        fps=fps,
        codec="libx264",
        audio_codec="aac" if sound else None,
        logger=None,
    )

    return output