Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

example tweaks #2258

Merged
merged 13 commits into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

[![MoviePy page on the Python Package Index](https://badge.fury.io/py/moviepy.svg)](https://pypi.org/project/moviepy/) [![Discuss MoviePy on Gitter](https://img.shields.io/gitter/room/movie-py/gitter?color=46BC99&logo=gitter)](Gitter_) [![Build status on gh-actions](https://img.shields.io/github/actions/workflow/status/Zulko/moviepy/test_suite.yml?logo=github)](https://github.com/Zulko/moviepy/actions/workflows/test_suite.yml) [![Code coverage from coveralls.io](https://img.shields.io/coveralls/github/Zulko/moviepy/master?logo=coveralls)](https://coveralls.io/github/Zulko/moviepy?branch=master)

> [!NOTE] MoviePy recently upgraded to v2.0, introducing major breaking changes. You can consult the last v1 docs [here](https://zulko.github.io/moviepy/v1.0.3/) but beware that v1 is no longer maintained. For more info on how to update your code from v1 to v2, see [this guide](https://zulko.github.io/moviepy/getting_started/updating_to_v2.html).
> [!NOTE]
> MoviePy recently upgraded to v2.0, introducing major breaking changes. You can consult the last v1 docs [here](https://zulko.github.io/moviepy/v1.0.3/) but beware that v1 is no longer maintained. For more info on how to update your code from v1 to v2, see [this guide](https://zulko.github.io/moviepy/getting_started/updating_to_v2.html).

MoviePy (online documentation [here](https://zulko.github.io/moviepy/)) is a
Python library for video editing: cuts, concatenations, title
Expand All @@ -20,26 +21,28 @@ In this example we open a video file, select the subclip between 10 and
result to a new file:

``` python
# Import everything needed to edit video clips
from moviepy import *
from moviepy import VideoFileClip, TextClip, CompositeVideoClip

# Load file example.mp4 and keep only the subclip from 00:00:10 to 00:00:20
clip = VideoFileClip("long_examples/example2.mp4").subclipped(10, 20)

# Reduce the audio volume to 80% of its original volume
clip = clip.with_volume_scaled(0.8)

# Generate a text clip. You can customize the font, color, etc.
txt_clip = TextClip(font="example.ttf", text="Big Buck Bunny", font_size=70, color='white')
clip = (
VideoFileClip("long_examples/example2.mp4")
.subclipped(10, 20)
.with_volume_scaled(0.8)
)

#The text clip should appear for 10s at the center of the screen
txt_clip = txt_clip.with_duration(10).with_position('center')
# Generate a text clip. You can customize the font, color, etc.
txt_clip = TextClip(
font="Arial.ttf",
text="Hello there!",
font_size=70,
color='white'
).with_duration(10).with_position('center')

# Overlay the text clip on the first video clip
video = CompositeVideoClip([clip, txt_clip])

# Write the result to a file (many options available!)
video.write_videofile("result.mp4")
final_video = CompositeVideoClip([clip, txt_clip])
final_video.write_videofile("result.mp4")
```

# Installation
Expand Down
22 changes: 13 additions & 9 deletions docs/_static/code/user_guide/compositing/CompositeAudioClip.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
from moviepy import *
"""Let's first concatenate (one after the other) then composite
(on top of each other) three audio clips."""

from moviepy import AudioFileClip, CompositeAudioClip, concatenate_audioclips

# We load all the clips we want to compose
aclip1 = AudioFileClip("example.wav")
aclip2 = AudioFileClip("example2.wav")
aclip3 = AudioFileClip("example3.wav")
clip1 = AudioFileClip("example.wav")
clip2 = AudioFileClip("example2.wav")
clip3 = AudioFileClip("example3.wav")

# All clip will play one after the other
concat = concatenate_audioclips([aclip1, aclip2, aclip3])
concat = concatenate_audioclips([clip1, clip2, clip3])

# We will play aclip1, then ontop of it aclip2 after 5s, and the aclip3 on top of both after 9s
# We will play clip1, then on top of it clip2 starting at t=5s,
# and clip3 on top of both starting t=9s
compo = CompositeAudioClip(
[
aclip1.with_volume_scaled(1.2),
aclip2.with_start(5), # start at t=5s
aclip3.with_start(9),
clip1.with_volume_scaled(1.2),
clip2.with_start(5), # start at t=5s
clip3.with_start(9),
]
)
10 changes: 7 additions & 3 deletions docs/_static/code/user_guide/compositing/CompositeVideoClip.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from moviepy import *
"""Let's stack three video clips on top of each other with
CompositeVideoClip."""

from moviepy import VideoFileClip, CompositeVideoClip

# We load all the clips we want to compose
clip1 = VideoFileClip("example.mp4")
clip2 = VideoFileClip("example2.mp4").subclipped(0, 1)
clip3 = VideoFileClip("example3.mp4")
clip3 = VideoFileClip("example.mp4")

# We concatenate them and write theme stacked on top of each other, with clip3 over clip2 over clip1
# We concatenate them and write theme stacked on top of each other,
# with clip3 over clip2 over clip1
final_clip = CompositeVideoClip([clip1, clip2, clip3])
final_clip.write_videofile("final_clip.mp4")
2 changes: 2 additions & 0 deletions docs/_static/code/user_guide/compositing/concatenate.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Let's concatenate (play one after the other) three video clips."""

from moviepy import VideoFileClip, concatenate_videoclips

# We load all the clips we want to concatenate
Expand Down
23 changes: 10 additions & 13 deletions docs/_static/code/user_guide/compositing/crossfadein.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
from moviepy import *
"""In this example, we will concatenate two clips with a 1-second
crossfadein of the second clip."""

from moviepy import VideoFileClip, CompositeVideoClip, vfx

# We load all the clips we want to compose
clip1 = VideoFileClip("example.mp4")
clip2 = VideoFileClip("example2.mp4").subclipped(0, 1)

# Clip2 will be on top of clip1 for 1s
clip1 = clip1.with_end(2)
clip2 = clip2.with_start(1)

# We will add a crossfadein on clip2 for 1s
# As the other effects, transitions are added to Clip methods at runtime
clip2 = clip2.with_effects([vfx.CrossFadeIn(1)])

clip2 = VideoFileClip("example2.mp4")

# We write the result
final_clip = CompositeVideoClip([clip1, clip2])
clips = [
clip1.with_end(2),
clip2.with_start(1).with_effects([vfx.CrossFadeIn(1)]),
]
final_clip = CompositeVideoClip(clips)
final_clip.write_videofile("final_clip.mp4")
13 changes: 9 additions & 4 deletions docs/_static/code/user_guide/compositing/juxtaposing.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Let's juxtapose four video clips in a 2x2 grid."""

from moviepy import VideoFileClip, clips_array, vfx

# We will use the same clip and transform it in 3 ways
Expand All @@ -8,9 +10,12 @@

# The form of the final clip will depend of the shape of the array
# We want our clip to be our 4 videos, 2x2, so we make an array of 2x2
final_clip = clips_array([[clip1, clip2], [clip3, clip4]])
final_clip = final_clip.resized(
width=480
) # We resize the resulting clip to have the dimensions we want
array = [
[clip1, clip2],
[clip3, clip4],
]
final_clip = clips_array(array)
# let's resize the final clip so it has 480px of width
final_clip = final_clip.resized(width=480)

final_clip.write_videofile("final_clip.mp4")
12 changes: 8 additions & 4 deletions docs/_static/code/user_guide/compositing/with_position.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from moviepy import *
"""Let's position some text and images on a video."""

from moviepy import TextClip, VideoFileClip, CompositeVideoClip, ImageClip

# We load all the clips we want to compose
background = VideoFileClip("example2.mp4").subclipped(0, 2)
Expand Down Expand Up @@ -28,8 +30,9 @@
)
logo = ImageClip("./example2.png", duration=1).resized(height=50)

# We want our title to be at the center horizontaly and start at 25% of the video verticaly
# We can set as "center", "left", "right", "top" and "bottom", and % relative from the clip size
# We want our title to be at the center horizontaly and start at 25%
# of the video verticaly. We can set as "center", "left", "right",
# "top" and "bottom", and % relative from the clip size
title = title.with_position(("center", 0.25), relative=True)

# We want the author to be in the center, 30px under the title
Expand All @@ -42,7 +45,8 @@
copyright = copyright.with_position(("center", background.h - copyright.h - 30))

# Finally, we want the logo to be in the center, but to drop as time pass
# We can do so by setting position as a function that take time as argument, a lot like frame_function
# We can do so by setting position as a function that take time as argument,
# a lot like frame_function
top = (background.h - logo.h) / 2
logo = logo.with_position(lambda t: ("center", top + t * 30))

Expand Down
8 changes: 4 additions & 4 deletions docs/_static/code/user_guide/compositing/with_start.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from moviepy import *
from moviepy import VideoFileClip, CompositeVideoClip

# We load all the clips we want to compose
clip1 = VideoFileClip("example.mp4")
Expand All @@ -12,9 +12,9 @@
clip2 = clip2.with_start(1.5)

# We want to play clip3 at the end of clip2, and so for 3 seconds only
clip3 = clip3.with_start(clip2.end).with_duration(
1
) # Some times its more practical to modify the duration of a clip instead of his end
# Some times its more practical to modify the duration of a clip instead
# of his end
clip3 = clip3.with_start(clip2.end).with_duration(1)

# We write the result
final_clip = CompositeVideoClip([clip1, clip2, clip3])
Expand Down
6 changes: 5 additions & 1 deletion docs/_static/code/user_guide/effects/custom_effect.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""Let's write a custom effect that will add a basic progress bar
at the bottom of our clip."""

from moviepy import VideoClip
from moviepy.decorators import requires_duration

Expand All @@ -22,7 +25,8 @@ def filter(get_frame, t):
progression = t / clip.duration
bar_width = int(progression * clip.w)

# Showing a progress bar is just replacing bottom pixels on some part of our frame
# Showing a progress bar is just replacing bottom pixels
# on some part of our frame
frame = get_frame(t)
frame[-height:, 0:bar_width] = color

Expand Down
2 changes: 2 additions & 0 deletions docs/_static/code/user_guide/effects/image_transform.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Let's invert the green and blue channels of a video."""

from moviepy import VideoFileClip
import numpy

Expand Down
12 changes: 7 additions & 5 deletions docs/_static/code/user_guide/effects/modify_copy_example.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
# Import everything needed to edit video clips
from moviepy import *
from moviepy import VideoFileClip

# Load example.mp4
clip = VideoFileClip("example.mp4")

# This does nothing, as multiply_volume will return a copy of clip which you will loose immediatly as you dont store it
# This does nothing, as multiply_volume will return a copy of clip
# which you will loose immediatly as you dont store it
# If you was to render clip now, the audio would still be at full volume
clip.with_volume_scaled(0.1)

# This create a copy of clip in clip_whisper with a volume of only 10% the original, but does not modify the original clip
# This create a copy of clip in clip_whisper with a volume of only 10% the original,
# but does not modify the original clip
# If you was to render clip right now, the audio would still be at full volume
# If you was to render clip_whisper, the audio would be a 10% of the original volume
clip_whisper = clip.with_volume_scaled(0.1)

# This replace the original clip with a copy of it where volume is only 10% of the original
# If you was to render clip now, the audio would be at 10%
# This replace the original clip with a copy of it where volume is only 10% of
# the original. If you was to render clip now, the audio would be at 10%
# The original clip is now lost
clip = clip.with_volume_scaled(0.1)
12 changes: 3 additions & 9 deletions docs/_static/code/user_guide/effects/time_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@

my_clip = VideoFileClip("example.mp4")


# You can define a function the classical way
def accel_x3(time: float) -> float:
return time * 3


modified_clip1 = my_clip.time_transform(accel_x3)

# Of you can also use lambda function
# Let's accelerate the video by a factor of 3
modified_clip1 = my_clip.time_transform(lambda t: t * 3)
# Let's play the video back and forth with a "sine" time-warping effect
modified_clip2 = my_clip.time_transform(lambda t: 1 + math.sin(t))
3 changes: 2 additions & 1 deletion docs/_static/code/user_guide/effects/transform.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Let's create a scolling video effect from scratch."""

from moviepy import VideoFileClip
import math

my_clip = VideoFileClip("example.mp4")

Expand Down
13 changes: 6 additions & 7 deletions docs/_static/code/user_guide/effects/using_effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
from moviepy import vfx, afx

myclip = VideoFileClip("example.mp4")
myclip = myclip.with_effects(
[vfx.Resize(width=460)]
) # resize clip to be 460px in width, keeping aspect ratio
# resize clip to be 460px in width, keeping aspect ratio
myclip = myclip.with_effects([vfx.Resize(width=460)])

# fx method return a copy of the clip, so we can easily chain them
myclip = myclip.with_effects(
[vfx.MultiplySpeed(2), afx.MultiplyVolume(0.5)]
) # double the speed and half the audio volume
# double the speed and half the audio volume
myclip = myclip.with_effects([vfx.MultiplySpeed(2), afx.MultiplyVolume(0.5)])

# because effects are added to Clip at runtime, you can also call them directly from your clip as methods
# because effects are added to Clip at runtime, you can also call
# them directly from your clip as methods
myclip = myclip.with_effects([vfx.MultiplyColor(0.5)]) # darken the clip
1 change: 0 additions & 1 deletion docs/_static/code/user_guide/effects/using_with_methods.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from moviepy import VideoFileClip
from moviepy import vfx, afx

myclip = VideoFileClip("example.mp4")
myclip = myclip.with_end(5) # stop the clip after 5 sec
Expand Down
25 changes: 13 additions & 12 deletions docs/_static/code/user_guide/loading/AudioArrayClip.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Let's create an audioclip from values in a numpy array."""

import numpy as np
from moviepy import *
from moviepy import AudioArrayClip

# We want to play those notes
notes = {"A": 440, "B": 494, "C": 523, "D": 587, "E": 659, "F": 698}
Expand All @@ -9,26 +11,25 @@
sample_rate = 44100 # Number of samples per second

note_size = int(note_duration * sample_rate)
total_size = note_size * len(notes)
n_frames = note_size * len(notes)


def frame_function(t, note_frequency):
return np.sin(note_frequency * 2 * np.pi * t)


# We generate all frames timepoints
times = np.linspace(0, total_duration, total_size)
# At this point one could use this audioclip which generates the audio on the fly
# clip = AudioFileClip(frame_function)

# We make an array of size N*1, where N is the number of frames * total duration
audio_array = np.zeros((total_size, 2))
i = 0
for note, frequency in notes.items():
for _ in range(note_size):
audio_array[i][0] = frame_function(times[i], frequency)
i += 1
# We generate all frames timepoints

audio_frame_values = [
2 * [frame_function(t, freq)]
for freq in notes.values()
for t in np.arange(0, note_duration, 1.0 / sample_rate)
]
# Create an AudioArrayClip from the audio samples
audio_clip = AudioArrayClip(audio_array, fps=sample_rate)
audio_clip = AudioArrayClip(np.array(audio_frame_values), fps=sample_rate)

# Write the audio clip to a WAV file
audio_clip.write_audiofile("result.wav", fps=44100)
12 changes: 7 additions & 5 deletions docs/_static/code/user_guide/loading/AudioClip.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from moviepy import *
from moviepy import AudioClip
import numpy as np

# Producing a sinewave of 440 Hz -> note A
frame_function_audio = lambda t: np.sin(440 * 2 * np.pi * t)

# AUDIO CLIPS
clip = AudioClip(frame_function_audio, duration=3)
def audio_frame(t):
"""Producing a sinewave of 440 Hz -> note A"""
return np.sin(440 * 2 * np.pi * t)


audio_clip = AudioClip(frame_function=audio_frame, duration=3)
1 change: 0 additions & 1 deletion docs/_static/code/user_guide/loading/AudioFileClip.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from moviepy import *
import numpy as np

# Works for audio files, but also videos file where you only want the keep the audio track
clip = AudioFileClip("example.wav")
Expand Down
12 changes: 5 additions & 7 deletions docs/_static/code/user_guide/loading/ColorClip.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from moviepy import *
from moviepy import ColorClip

myclip = ColorClip(
size=(200, 100), color=(255, 0, 0), duration=1
) # Color is passed as a RGB tuple
myclip.write_videofile(
"result.mp4", fps=1
) # We really dont need more than 1 fps do we ?
# Color is passed as a RGB tuple
myclip = ColorClip(size=(200, 100), color=(255, 0, 0), duration=1)
# We really dont need more than 1 fps do we ?
myclip.write_videofile("result.mp4", fps=1)
Loading
Loading