diff --git a/docs/conf.py b/docs/conf.py index 6861f340b..b0d31a73b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,9 +5,6 @@ import os import sys -import pydata_sphinx_theme -from pygments.styles import get_all_styles - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -116,15 +113,17 @@ "name": "GitHub", # URL where the link will redirect "url": "https://github.com/Zulko/moviepy/", # required - # Icon class (if "type": "fontawesome"), or path to local image (if "type": "local") + # Icon class (if "type": "fontawesome"), or path to local image (if + # "type": "local") "icon": "fa-brands fa-square-github", # The type of image to be used (see below for details) "type": "fontawesome", } ], - "announcement": '
MoviePy v2.0 have introduced breaking changes, see "Updating from v1.X to v2.X" for more info.
'.format( - v2_page - ), + "announcement": f""" +MoviePy v2.0 have introduced breaking changes, + see Updating from v1.X to v2.X for more info.
+ """, } html_context = { diff --git a/moviepy/Clip.py b/moviepy/Clip.py index 7f30fed31..af0937b77 100644 --- a/moviepy/Clip.py +++ b/moviepy/Clip.py @@ -460,8 +460,8 @@ def with_cutout(self, start_time, end_time): return new_clip def with_multiply_speed(self, factor: float = None, final_duration: float = None): - """Returns a clip playing the current clip but at a speed multiplied by ``factor``. - For info on the parameters, please see ``vfx.MultiplySpeed`` + """Returns a clip playing the current clip but at a speed multiplied + by ``factor``. For info on the parameters, please see ``vfx.MultiplySpeed``. """ from moviepy.video.fx.MultiplySpeed import MultiplySpeed diff --git a/moviepy/audio/fx/MultiplyStereoVolume.py b/moviepy/audio/fx/MultiplyStereoVolume.py index c0a8b523a..de5e95ca5 100644 --- a/moviepy/audio/fx/MultiplyStereoVolume.py +++ b/moviepy/audio/fx/MultiplyStereoVolume.py @@ -17,8 +17,10 @@ class MultiplyStereoVolume(Effect): >>> from moviepy import AudioFileClip >>> music = AudioFileClip('music.ogg') - >>> audio_r = music.with_effects([afx.MultiplyStereoVolume(left=0, right=1)]) # mute left channel/s - >>> audio_h = music.with_effects([afx.MultiplyStereoVolume(left=0.5, right=0.5)]) # half audio + >>> # mutes left channel + >>> audio_r = music.with_effects([afx.MultiplyStereoVolume(left=0, right=1)]) + >>> # halves audio volume + >>> audio_h = music.with_effects([afx.MultiplyStereoVolume(left=0.5, right=0.5)]) """ left: float = 1 diff --git a/moviepy/audio/fx/MultiplyVolume.py b/moviepy/audio/fx/MultiplyVolume.py index 6c8229563..453f793c4 100644 --- a/moviepy/audio/fx/MultiplyVolume.py +++ b/moviepy/audio/fx/MultiplyVolume.py @@ -32,12 +32,14 @@ class MultiplyVolume(Effect): >>> from moviepy import AudioFileClip >>> - >>> music = AudioFileClip('music.ogg') - >>> doubled_audio_clip = clip.with_effects([afx.MultiplyVolume(2)]) # doubles audio volume - >>> half_audio_clip = clip.with_effects([afx.MultiplyVolume(0.5)]) # half audio - >>> - >>> # silenced clip during one second at third - >>> silenced_clip = clip.with_effects([afx.MultiplyVolume(0, start_time=2, end_time=3)]) + >>> music = AudioFileClip("music.ogg") + >>> # doubles audio volume + >>> doubled_audio_clip = music.with_effects([afx.MultiplyVolume(2)]) + >>> # halves audio volume + >>> half_audio_clip = music.with_effects([afx.MultiplyVolume(0.5)]) + >>> # silences clip during one second at third + >>> effect = afx.MultiplyVolume(0, start_time=2, end_time=3) + >>> silenced_clip = clip.with_effects([effect]) """ factor: float diff --git a/moviepy/audio/io/readers.py b/moviepy/audio/io/readers.py index 6173616aa..dd49d08e6 100644 --- a/moviepy/audio/io/readers.py +++ b/moviepy/audio/io/readers.py @@ -123,8 +123,10 @@ def initialize(self, start_time=0): def skip_chunk(self, chunksize): """ - This method skips a chunk of audio data by reading and discarding the specified number of frames from the audio stream. - The audio stream is read from the `proc` stdout. After skipping the chunk, the `pos` attribute is updated accordingly. + This method skips a chunk of audio data by reading and discarding + the specified number of frames from the audio stream. The audio + stream is read from the `proc` stdout. After skipping the chunk, the + `pos` attribute is updated accordingly. Parameters ----------- @@ -140,10 +142,13 @@ def read_chunk(self, chunksize): """ Reads a chunk of audio data from the audio stream. - This method reads a chunk of audio data from the audio stream. The specified number of frames, given by `chunksize`, - is read from the `proc` stdout. The audio data is returned as a NumPy array, where each row corresponds to a frame and - each column corresponds to a channel. If there is not enough audio left to read, the remaining portion is padded with - zeros, ensuring that the returned array has the desired length. The `pos` attribute is updated accordingly. + This method reads a chunk of audio data from the audio stream. The + specified number of frames, given by `chunksize`, is read from the + `proc` stdout. The audio data is returned as a NumPy array, where + each row corresponds to a frame and each column corresponds to a + channel. If there is not enough audio left to read, the remaining + portion is padded with zeros, ensuring that the returned array has + the desired length. The `pos` attribute is updated accordingly. Parameters: ------------ @@ -193,9 +198,10 @@ def get_frame(self, tt): Parameters ----------- - tt (float or numpy.ndarray): The timestamp(s) at which to retrieve the audio frame(s). - If `tt` is a single float value, the frame corresponding to that timestamp is returned. - If `tt` is a NumPy array of timestamps, an array of frames corresponding to each timestamp is returned. + tt (float or numpy.ndarray): The timestamp(s) at which to retrieve the + audio frame(s). If `tt` is a single float value, the frame corresponding + to that timestamp is returned. If `tt` is a NumPy array of timestamps, + an array of frames corresponding to each timestamp is returned. """ if isinstance(tt, np.ndarray): # lazy implementation, but should not cause problems in diff --git a/moviepy/config.py b/moviepy/config.py index 61c0b2b5a..2c3827135 100644 --- a/moviepy/config.py +++ b/moviepy/config.py @@ -6,10 +6,6 @@ from moviepy.tools import cross_platform_popen_params - -if os.name == "nt": - import winreg as wr - try: from dotenv import find_dotenv, load_dotenv diff --git a/moviepy/tools.py b/moviepy/tools.py index 9fe74b0d0..c2bc34724 100644 --- a/moviepy/tools.py +++ b/moviepy/tools.py @@ -222,11 +222,11 @@ def no_display_available() -> bool: """ system = platform.system() if system in ["Linux", "FreeBSD", "NetBSD", "OpenBSD", "SunOS", "AIX"]: - if not "DISPLAY" in os.environ and not "WAYLAND_DISPLAY" in os.environ: + if ("DISPLAY" not in os.environ) and ("WAYLAND_DISPLAY" not in os.environ): return True if "CYGWIN_NT" in system: - if not "DISPLAY" in os.environ and not "WAYLAND_DISPLAY" in os.environ: + if ("DISPLAY" not in os.environ) and ("WAYLAND_DISPLAY" not in os.environ): return True return False diff --git a/moviepy/video/VideoClip.py b/moviepy/video/VideoClip.py index 2c22ef81e..fc76fbd0e 100644 --- a/moviepy/video/VideoClip.py +++ b/moviepy/video/VideoClip.py @@ -8,12 +8,12 @@ import os import threading from numbers import Real -from typing import TYPE_CHECKING, List, Union +from typing import TYPE_CHECKING, List import numpy as np import proglog from imageio.v2 import imread as imread_v2 -from imageio.v3 import imread, imwrite +from imageio.v3 import imwrite from PIL import Image, ImageDraw, ImageFont from moviepy.video.io.ffplay_previewer import ffplay_preview_video @@ -22,7 +22,6 @@ if TYPE_CHECKING: from moviepy.Effect import Effect -from moviepy.audio.fx.MultiplyVolume import MultiplyVolume from moviepy.Clip import Clip from moviepy.decorators import ( add_mask_if_none, @@ -37,7 +36,6 @@ ) from moviepy.tools import extensions_dict, find_extension from moviepy.video.fx.Crop import Crop -from moviepy.video.fx.MultiplySpeed import MultiplySpeed from moviepy.video.fx.Resize import Resize from moviepy.video.fx.Rotate import Rotate from moviepy.video.io.ffmpeg_writer import ffmpeg_write_video @@ -552,10 +550,10 @@ def show(self, t=0, with_mask=True): # it broke compositevideoclip and it does nothing on normal clip with alpha # if with_mask and (self.mask is not None): - # # Hate it, but cannot figure a better way with python awful circular dependency - # from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip - - # clip = CompositeVideoClip([self.with_position((0, 0))]) + # # Hate it, but cannot figure a better way with python awful circular + # # dependency + # from mpy.video.compositing.CompositeVideoClip import CompositeVideoClip + # clip = CompositeVideoClip([self.with_position((0, 0))]) frame = clip.get_frame(t) pil_img = Image.fromarray(frame.astype("uint8")) @@ -677,9 +675,11 @@ def image_transform(self, image_func, apply_to=None): def fill_array(self, pre_array, shape=(0, 0)): """Fills an array to match the specified shape. - If the `pre_array` is smaller than the desired shape, the missing rows or columns are added with ones to the bottom or right, - respectively, until the shape matches. If the `pre_array` is larger than the desired shape, the excess rows or columns are cropped - from the bottom or right, respectively, until the shape matches. + If the `pre_array` is smaller than the desired shape, the missing rows + or columns are added with ones to the bottom or right, respectively, + until the shape matches. If the `pre_array` is larger than the desired + shape, the excess rows or columns are cropped from the bottom or right, + respectively, until the shape matches. The resulting array with the filled shape is returned. @@ -1387,10 +1387,11 @@ class TextClip(ImageClip): it will then be auto-determined. margin - Margin to be added arround the text as a tuple of two (symmetrical) or four (asymmetrical). - Either ``(horizontal, vertical)`` or ``(left, top, right, bottom)``. - By default no margin (None, None). - This is especially usefull for auto-compute size to give the text some extra room. + Margin to be added arround the text as a tuple of two (symmetrical) or + four (asymmetrical). Either ``(horizontal, vertical)`` or + ``(left, top, right, bottom)``. By default no margin (None, None). + This is especially usefull for auto-compute size to give the text some + extra room. bg_color Color of the background. Default to None for no background. Can be @@ -1703,19 +1704,19 @@ def find_optimum_font_size( else: raise ValueError("Method must be either `caption` or `label`.") - + # Compute the margin and apply it - if len(margin) == 2 : + if len(margin) == 2: left_margin = right_margin = int(margin[0] or 0) top_margin = bottom_margin = int(margin[1] or 0) - elif len(margin) == 4 : + elif len(margin) == 4: left_margin = int(margin[0] or 0) top_margin = int(margin[1] or 0) right_margin = int(margin[2] or 0) bottom_margin = int(margin[3] or 0) - else : - raise ValueError('Margin must be a tuple of either 2 or 4 elements.') - + else: + raise ValueError("Margin must be a tuple of either 2 or 4 elements.") + img_width += left_margin + right_margin img_height += top_margin + bottom_margin @@ -1756,12 +1757,14 @@ def find_optimum_font_size( y += top_margin - # So, pillow multiline support is horrible, in particular multiline_text and multiline_textbbox - # are not intuitive at all. They cannot use left top (see https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html) - # as anchor, so we always have to use left middle instead. Else we would always have a useless - # margin (the diff between ascender and top) on any text. - # That mean our Y is actually not from 0 for top, but need to be increment by half our text height, - # since we have to reference from middle line. + # So, pillow multiline support is horrible, in particular multiline_text + # and multiline_textbbox are not intuitive at all. They cannot use left + # top (see https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html) + # as anchor, so we always have to use left middle instead. Else we would + # always have a useless margin (the diff between ascender and top) on any + # text. That mean our Y is actually not from 0 for top, but need to be + # increment by half our text height, since we have to reference from + # middle line. y += text_height / 2 print(y) diff --git a/moviepy/video/fx/Crop.py b/moviepy/video/fx/Crop.py index 498a373dd..cb564acda 100644 --- a/moviepy/video/fx/Crop.py +++ b/moviepy/video/fx/Crop.py @@ -35,8 +35,7 @@ class Crop(Effect): Crop a rectangle centered in x,y=(300,400), width=50, height=150 : - >>> Crop(x_center=300 , y_center=400, - width=50, height=150) + >>> Crop(x_center=300, y_center=400, width=50, height=150) Any combination of the above should work, like for this rectangle centered in x=300, with explicit y-boundaries: diff --git a/moviepy/video/fx/FreezeRegion.py b/moviepy/video/fx/FreezeRegion.py index 02305ad51..62c6112f4 100644 --- a/moviepy/video/fx/FreezeRegion.py +++ b/moviepy/video/fx/FreezeRegion.py @@ -41,7 +41,7 @@ class FreezeRegion(Effect): def apply(self, clip: Clip) -> Clip: if self.region is not None: - x1, y1, x2, y2 = self.region + x1, y1, _x2, _y2 = self.region freeze = ( clip.with_effects([Crop(*self.region)]) .to_ImageClip(t=self.t) @@ -60,6 +60,8 @@ def apply(self, clip: Clip) -> Clip: elif self.mask is not None: freeze = ( - clip.to_ImageClip(t=t).with_duration(clip.duration).with_mask(self.mask) + clip.to_ImageClip(t=self.t) + .with_duration(clip.duration) + .with_mask(self.mask) ) return CompositeVideoClip([clip, freeze]) diff --git a/moviepy/video/fx/Margin.py b/moviepy/video/fx/Margin.py index 4f6f8dbbd..91f463280 100644 --- a/moviepy/video/fx/Margin.py +++ b/moviepy/video/fx/Margin.py @@ -3,7 +3,6 @@ import numpy as np from moviepy.Clip import Clip -from moviepy.decorators import apply_to_mask from moviepy.Effect import Effect from moviepy.video.VideoClip import ImageClip diff --git a/moviepy/video/fx/MasksOr.py b/moviepy/video/fx/MasksOr.py index 22c30b1ac..d1ce74ab1 100644 --- a/moviepy/video/fx/MasksOr.py +++ b/moviepy/video/fx/MasksOr.py @@ -35,7 +35,7 @@ class MasksOr(Effect): def apply(self, clip: Clip) -> Clip: # to ensure that 'or' of two ImageClips will be an ImageClip if isinstance(self.other_clip, ImageClip): - other_clip = self.other_clip.img + self.other_clip = self.other_clip.img if isinstance(self.other_clip, np.ndarray): return clip.image_transform( diff --git a/moviepy/video/fx/Resize.py b/moviepy/video/fx/Resize.py index 354f546da..67ca0a7e4 100644 --- a/moviepy/video/fx/Resize.py +++ b/moviepy/video/fx/Resize.py @@ -35,7 +35,7 @@ class Resize(Effect): >>> myClip.with_effects([vfx.Resize((460,720))]) # New resolution: (460,720) >>> myClip.with_effects([vfx.Resize(0.6)]) # width and height multiplied by 0.6 >>> myClip.with_effects([vfx.Resize(width=800)]) # height computed automatically. - >>> myClip.with_effects([vfx.Resize(lambda t : 1+0.02*t)]) # slow swelling of the clip + >>> myClip.with_effects([vfx.Resize(lambda t : 1+0.02*t)]) # slow clip swelling """ new_size: Union[tuple, float, callable] = None diff --git a/moviepy/video/fx/Rotate.py b/moviepy/video/fx/Rotate.py index fc3b42ef0..e6c9f2af1 100644 --- a/moviepy/video/fx/Rotate.py +++ b/moviepy/video/fx/Rotate.py @@ -1,5 +1,4 @@ import math -import warnings from dataclasses import dataclass import numpy as np diff --git a/moviepy/video/io/ffplay_previewer.py b/moviepy/video/io/ffplay_previewer.py index 3137767d1..23bfb685b 100644 --- a/moviepy/video/io/ffplay_previewer.py +++ b/moviepy/video/io/ffplay_previewer.py @@ -5,10 +5,7 @@ import subprocess as sp -import numpy as np -from proglog import proglog - -from moviepy.config import FFMPEG_BINARY, FFPLAY_BINARY +from moviepy.config import FFPLAY_BINARY from moviepy.tools import cross_platform_popen_params @@ -129,7 +126,8 @@ def ffplay_preview_video( for t, frame in clip.iter_frames(with_times=True, fps=fps, dtype="uint8"): previewer.show_frame(frame) - # After first frame is shown, if we have audio/video flag, set video ready and wait for audio + # After first frame is shown, if we have audio/video flag, set video ready + # and wait for audio if first_frame: first_frame = False diff --git a/moviepy/video/tools/cuts.py b/moviepy/video/tools/cuts.py index 27873fda4..2baaa6dad 100644 --- a/moviepy/video/tools/cuts.py +++ b/moviepy/video/tools/cuts.py @@ -343,7 +343,8 @@ def select_scenes( >>> from moviepy.video.tools.cuts import FramesMatches >>> >>> ch_clip = VideoFileClip("media/chaplin.mp4").with_subclip(1, 4) - >>> clip = concatenate_videoclips([ch_clip.with_effects([vfx.TimeMirror()]), ch_clip]) + >>> mirror_and_clip = [ch_clip.with_effects([vfx.TimeMirror()]), ch_clip] + >>> clip = concatenate_videoclips(mirror_and_clip) >>> >>> result = FramesMatches.from_clip(clip, 10, 3).select_scenes( ... 1, 2, nomatch_threshold=0, diff --git a/tests/conftest.py b/tests/conftest.py index b6ef40e34..f1752d1f1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,10 +7,8 @@ import importlib import inspect import io -import pathlib import pkgutil import socketserver -import sys import tempfile import threading diff --git a/tests/test_doc_examples.py b/tests/test_doc_examples.py index 294dba062..bd77317f2 100644 --- a/tests/test_doc_examples.py +++ b/tests/test_doc_examples.py @@ -1,4 +1,5 @@ -"""Try to run all the documentation examples with runpy and check they dont raise exception""" +"""Try to run all the documentation examples with runpy and check they dont raise +exception""" import os import pathlib diff --git a/tests/test_fx.py b/tests/test_fx.py index 339e83fb4..b90aa6297 100644 --- a/tests/test_fx.py +++ b/tests/test_fx.py @@ -1,12 +1,10 @@ """MoviePy video and audio effects tests.""" import decimal -import importlib import math import numbers import os import random -import sys import numpy as np diff --git a/tests/test_tools.py b/tests/test_tools.py index 7d8d04456..1a0c477bb 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,7 +1,6 @@ """Tool tests meant to be run with pytest. Taken from PR #121 (grimley517).""" import contextlib -import filecmp import importlib import io import os @@ -11,7 +10,6 @@ import pytest import moviepy.tools as tools -from moviepy import * @pytest.mark.parametrize(