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

Fluent API for effects (monkeypatch as methods) #1105

Merged
merged 26 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ee43464
Rework editor.py fx imports to include all fx
tburrows13 Mar 20, 2020
2fb768e
Remove debug print statements
tburrows13 Mar 20, 2020
91b8aef
use super
mgaitan Mar 24, 2020
4302303
audio loop support videoclip instance
mgaitan Mar 24, 2020
3c2f9c4
patch classes avoiding exec
mgaitan Mar 24, 2020
5629a94
explicitly set fx all
mgaitan Mar 24, 2020
f0f90f0
explicitly define __all__ on editor.py
mgaitan Mar 24, 2020
448029f
user warning to be explicit
mgaitan Mar 24, 2020
aa81368
Update moviepy/audio/fx/audio_loop.py
mgaitan Mar 24, 2020
8c77f9c
Merge remote-tracking branch 'zulko/v2' into fluent_api
mgaitan Mar 25, 2020
d46cb50
Merge branch 'fluent_api' of github.com:mgaitan/moviepy into fluent_api
mgaitan Mar 25, 2020
8eb54d6
black files
mgaitan Mar 25, 2020
19f15b2
fix regressions
mgaitan Mar 25, 2020
f1c4c3e
Merge remote-tracking branch 'zulko/master' into fluent_api
mgaitan Apr 4, 2020
f72522c
import package directly instead all modules
mgaitan Apr 4, 2020
72ffe1a
Merge branch 'master' into fluent_api
tburrows13 Apr 9, 2020
24f131e
Merge branch 'master' into fluent_api
tburrows13 Apr 26, 2020
92d8a91
Add missing import
tburrows13 Apr 29, 2020
6b594b3
Merge branch 'master' into fluent_api
tburrows13 Oct 4, 2020
e2f4d3d
Merge branch 'master' into fluent_api
tburrows13 Oct 8, 2020
3d8dd86
Fix undefined name from incorrect merging
tburrows13 Oct 8, 2020
7dc7b5f
cvsecs -> convert_to_seconds in editor.py
tburrows13 Oct 8, 2020
9cfd5fa
Minor docs cleanup
tburrows13 Oct 8, 2020
d8a0bd8
Add @audio_video_fx to audio_loop (should probably delete audio_loop …
tburrows13 Oct 8, 2020
9f174c2
Updated CHANGELOG.md
tburrows13 Oct 8, 2020
eee1e1f
Updated CHANGELOG.md
tburrows13 Oct 8, 2020
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed <!-- for changes in existing functionality -->

### Deprecated <!-- for soon-to-be removed features -->
### Deprecated <!-- for soon-to-be removed features -->
- `moviepy.video.fx.all` and `moviepy.audio.fx.all`. Use the fx method directly from the clip instance or import the fx function from `moviepy.video.fx` and `moviepy.audio.fx`. [#1105]

### Removed <!-- for now removed features -->

Expand Down
2 changes: 1 addition & 1 deletion moviepy/audio/AudioClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AudioClip(Clip):
"""

def __init__(self, make_frame=None, duration=None, fps=None):
Clip.__init__(self)
super().__init__()

if fps is not None:
self.fps = fps
Expand Down
12 changes: 8 additions & 4 deletions moviepy/audio/fx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
"""
This module contains transformation functions (clip->clip)
One file for one fx. The file's name is the fx's name
"""
# import every video fx function

from .audio_fadein import audio_fadein
from .audio_fadeout import audio_fadeout
from .audio_left_right import audio_left_right
from .audio_loop import audio_loop
from .audio_normalize import audio_normalize
from .volumex import volumex
18 changes: 7 additions & 11 deletions moviepy/audio/fx/all/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
"""
Loads all the fx!
Usage:
import moviepy.audio.fx.all as afx
audio_clip = afx.volume_x(some_clip, .5)
"""

import pkgutil
moviepy.audio.fx.all is deprecated.

import moviepy.audio.fx as fx
Use the fx method directly from the clip instance (e.g. ``clip.audio_loop(...)``)
or import the function from moviepy.audio.fx instead.
"""
import warnings

__all__ = [name for _, name, _ in pkgutil.iter_modules(fx.__path__) if name != "all"]
from .. import *

for name in __all__:
exec("from ..%s import %s" % (name, name))
warnings.warn(f"\nMoviePy: {__doc__}", UserWarning)
14 changes: 6 additions & 8 deletions moviepy/audio/fx/audio_loop.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from ..AudioClip import concatenate_audioclips
from moviepy.decorators import audio_video_fx


def audio_loop(audioclip, n_loops=None, duration=None):
@audio_video_fx
def audio_loop(clip, n_loops=None, duration=None):
"""Loops over an audio clip.

Returns an audio clip that plays the given clip either
Expand All @@ -17,12 +19,8 @@ def audio_loop(audioclip, n_loops=None, duration=None):
>>> videoclip.with_audio(audio)

"""

if duration is not None:
n_loops = int(duration / clip.duration) + 1
return concatenate_audioclips(n_loops * [clip]).with_duration(duration)

n_loops = int(duration / audioclip.duration) + 1
return concatenate_audioclips(n_loops * [audioclip]).with_duration(duration)

else:

return concatenate_audioclips(n_loops * [audioclip])
return concatenate_audioclips(n_loops * [clip])
85 changes: 46 additions & 39 deletions moviepy/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,44 @@

In particular it will load many effects from the video.fx and audio.fx
folders and turn them into VideoClip methods, so that instead of
>>> clip.fx( vfx.resize, 2 ) # or equivalently vfx.resize(clip, 2)
>>> clip.fx( vfx.resize, 2 ) or equivalently vfx.resize(clip, 2)
we can write
>>> clip.resize(2)

It also starts a PyGame session (if PyGame is installed) and enables
clip.preview().
"""

__all__ = [
"afx",
"AudioClip",
"AudioFileClip",
"BitmapClip",
"clips_array",
"ColorClip",
"CompositeAudioClip",
"CompositeVideoClip",
"concatenate_audioclips",
"concatenate_videoclips",
"convert_to_seconds",
"download_webfile",
"ffmpeg_tools",
"ImageClip",
"ImageSequenceClip",
"ipython_display",
"TextClip",
"transfx",
"vfx",
"VideoClip",
"VideoFileClip",
"videotools",
]

# Note that these imports could have been performed in the __init__.py
# file, but this would make the loading of moviepy slower.

import os
import sys
import inspect


# Hide the welcome message from pygame: https://github.com/pygame/pygame/issues/542
Expand All @@ -37,8 +62,8 @@

# FX

import moviepy.video.fx.all as vfx
import moviepy.audio.fx.all as afx
import moviepy.video.fx as vfx
import moviepy.audio.fx as afx
import moviepy.video.compositing.transitions as transfx

# Tools
Expand All @@ -50,44 +75,26 @@

try:
from .video.io.sliders import sliders

__all__.append("sliders")
except ImportError:
pass

# The next loop transforms many effects into VideoClip methods so that
# they can be called with myclip.resize(width=500) instead of
# myclip.fx( vfx.resize, width= 500)
for method in [
"afx.audio_fadein",
"afx.audio_fadeout",
"afx.audio_normalize",
"afx.volumex",
"transfx.crossfadein",
"transfx.crossfadeout",
"vfx.crop",
"vfx.fadein",
"vfx.fadeout",
"vfx.invert_colors",
"vfx.loop",
"vfx.margin",
"vfx.mask_and",
"vfx.mask_or",
"vfx.resize",
"vfx.rotate",
"vfx.speedx",
]:

exec("VideoClip.%s = %s" % (method.split(".")[1], method))


for method in [
"afx.audio_fadein",
"afx.audio_fadeout",
"afx.audio_loop",
"afx.audio_normalize",
"afx.volumex",
]:

exec("AudioClip.%s = %s" % (method.split(".")[1], method))
# Transforms the effects into Clip methods so that
# they can be called with clip.resize(width=500) instead of
# clip.fx(vfx.resize, width=500)
audio_fxs = inspect.getmembers(afx, inspect.isfunction)
video_fxs = (
inspect.getmembers(vfx, inspect.isfunction)
+ inspect.getmembers(transfx, inspect.isfunction)
+ audio_fxs
)

for name, function in video_fxs:
setattr(VideoClip, name, function)

for name, function in audio_fxs:
setattr(AudioClip, name, function)


# adds easy ipython integration
Expand Down
2 changes: 1 addition & 1 deletion moviepy/video/VideoClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class VideoClip(Clip):
def __init__(
self, make_frame=None, is_mask=False, duration=None, has_constant_size=True
):
Clip.__init__(self)
super().__init__()
self.mask = None
self.audio = None
self.pos = lambda t: (0, 0)
Expand Down
3 changes: 3 additions & 0 deletions moviepy/video/compositing/transitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
from .CompositeVideoClip import CompositeVideoClip


__all__ = ["crossfadein", "crossfadeout", "slide_in", "slide_out", "make_loopable"]


@requires_duration
@add_mask_if_none
def crossfadein(clip, duration):
Expand Down
36 changes: 32 additions & 4 deletions moviepy/video/fx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
"""
This module contains transformation functions (clip->clip)
One file for one fx. The file's name is the fx's name
"""
# import every video fx function

from .accel_decel import accel_decel
from .blackwhite import blackwhite
from .blink import blink
from .colorx import colorx
from .crop import crop
from .even_size import even_size
from .fadein import fadein
from .fadeout import fadeout
from .freeze import freeze
from .freeze_region import freeze_region
from .gamma_corr import gamma_corr
from .headblur import headblur
from .invert_colors import invert_colors
from .loop import loop
from .lum_contrast import lum_contrast
from .make_loopable import make_loopable
from .margin import margin
from .mask_and import mask_and
from .mask_color import mask_color
from .mask_or import mask_or
from .mirror_x import mirror_x
from .mirror_y import mirror_y
from .painting import painting
from .resize import resize
from .rotate import rotate
from .scroll import scroll
from .speedx import speedx
from .supersample import supersample
from .time_mirror import time_mirror
from .time_symmetrize import time_symmetrize
19 changes: 7 additions & 12 deletions moviepy/video/fx/all/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
"""
Loads all the fx !
Usage:
import moviepy.video.fx.all as vfx
clip = vfx.resize(some_clip, width=400)
clip = vfx.mirror_x(some_clip)
"""

import pkgutil
moviepy.video.fx.all is deprecated.

import moviepy.video.fx as fx
Use the fx method directly from the clip instance (e.g. ``clip.resize(...)``)
or import the function from moviepy.video.fx instead.
"""
import warnings

__all__ = [name for _, name, _ in pkgutil.iter_modules(fx.__path__) if name != "all"]
from .. import *

for name in __all__:
exec("from ..%s import %s" % (name, name))
warnings.warn(f"\nMoviePy: {__doc__}", UserWarning)
4 changes: 2 additions & 2 deletions moviepy/video/fx/time_mirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
@requires_duration
@apply_to_mask
@apply_to_audio
def time_mirror(self):
def time_mirror(clip):
"""
Returns a clip that plays the current clip backwards.
The clip must have its ``duration`` attribute set.
The same effect is applied to the clip's audio and mask if any.
"""
return self.time_transform(lambda t: self.duration - t - 1, keep_duration=True)
return clip.time_transform(lambda t: clip.duration - t - 1, keep_duration=True)
1 change: 1 addition & 0 deletions tests/test_issues.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
"""Issue tests meant to be run with pytest."""
import pytest
import os

from moviepy.editor import *
from moviepy.utils import close_all_clips
Expand Down