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

Add support for path-like objects with a decorator #1137

Merged
merged 4 commits into from
Apr 18, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support removed for Python versions 2.7, 3.4 & 3.5 [#1103, #1106]

### Added <!-- for new features -->
- Support for path-like objects as an option wherever filenames are passed in as arguments

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

Expand Down
7 changes: 4 additions & 3 deletions moviepy/audio/AudioClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from moviepy.audio.io.ffmpeg_audiowriter import ffmpeg_audiowrite
from moviepy.Clip import Clip
from moviepy.decorators import requires_duration
from moviepy.decorators import requires_duration, convert_path_to_string
from moviepy.tools import extensions_dict


Expand Down Expand Up @@ -159,6 +159,7 @@ def max_volume(self, stereo=False, chunksize=50000, logger=None):
return maxi

@requires_duration
@convert_path_to_string("filename")
def write_audiofile(
self,
filename,
Expand All @@ -178,11 +179,11 @@ def write_audiofile(
-----------

filename
Name of the output file
Name of the output file, as a string or a path-like object.

fps
Frames per second. If not set, it will try default to self.fps if
already set, otherwise it will default to 44100
already set, otherwise it will default to 44100.

nbytes
Sample width (set to 2 for 16-bit sound, 4 for 32-bit sound)
Expand Down
4 changes: 4 additions & 0 deletions moviepy/audio/io/AudioFileClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from moviepy.audio.AudioClip import AudioClip
from moviepy.audio.io.readers import FFMPEG_AudioReader
from moviepy.decorators import convert_path_to_string


class AudioFileClip(AudioClip):
Expand All @@ -18,6 +19,7 @@ class AudioFileClip(AudioClip):

filename
Either a soundfile name (of any extension supported by ffmpeg)
as a string or a path-like object,
or an array representing a sound. If the soundfile is not a .wav,
it will be converted to .wav first, using the ``fps`` and
``bitrate`` arguments.
Expand Down Expand Up @@ -62,6 +64,7 @@ class AudioFileClip(AudioClip):

"""

@convert_path_to_string("filename")
def __init__(self, filename, buffersize=200000, nbytes=2, fps=44100):

AudioClip.__init__(self)
Expand All @@ -74,6 +77,7 @@ def __init__(self, filename, buffersize=200000, nbytes=2, fps=44100):
self.duration = self.reader.duration
self.end = self.reader.duration
self.buffersize = self.reader.buffersize
self.filename = filename
tburrows13 marked this conversation as resolved.
Show resolved Hide resolved

self.make_frame = lambda t: self.reader.get_frame(t)
self.nchannels = self.reader.nchannels
Expand Down
11 changes: 9 additions & 2 deletions moviepy/decorators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
all decorators used in moviepy go there
"""
import os

import decorator

Expand Down Expand Up @@ -81,7 +82,8 @@ def wrapper(f, *a, **kw):

names = func_code.co_varnames
new_a = [
fun(arg) if (name in varnames) else arg for (arg, name) in zip(a, names)
fun(arg) if (name in varnames) and (arg is not None) else arg
for (arg, name) in zip(a, names)
]
new_kw = {k: fun(v) if k in varnames else v for (k, v) in kw.items()}
return f(*new_a, **new_kw)
Expand All @@ -90,10 +92,15 @@ def wrapper(f, *a, **kw):


def convert_to_seconds(varnames):
"Converts the specified variables to seconds"
"""Converts the specified variables to seconds"""
return preprocess_args(cvsecs, varnames)


def convert_path_to_string(varnames):
"""Converts the specified variables to a path string"""
return preprocess_args(os.fspath, varnames)


@decorator.decorator
def add_mask_if_none(f, clip, *a, **k):
""" Add a mask to the clip if there is none. """
Expand Down
18 changes: 12 additions & 6 deletions moviepy/video/VideoClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
outplace,
requires_duration,
use_clip_fps_by_default,
convert_path_to_string,
)
from ..tools import (
extensions_dict,
Expand Down Expand Up @@ -139,6 +140,7 @@ def save_frame(self, filename, t=0, withmask=True):
@requires_duration
@use_clip_fps_by_default
@convert_masks_to_RGB
@convert_path_to_string("filename")
def write_videofile(
self,
filename,
Expand Down Expand Up @@ -166,7 +168,7 @@ def write_videofile(
-----------

filename
Name of the video file to write in.
Name of the video file to write in, as a string or a path-like object.
The extension must correspond to the "codec" used (see below),
or simply be '.avi' (which will work with any codec).

Expand Down Expand Up @@ -395,6 +397,7 @@ def write_images_sequence(self, nameformat, fps=None, withmask=True, logger="bar

@requires_duration
@convert_masks_to_RGB
@convert_path_to_string("filename")
def write_gif(
self,
filename,
Expand All @@ -417,7 +420,7 @@ def write_gif(
-----------

filename
Name of the resulting gif file.
Name of the resulting gif file, as a string or a path-like object.

fps
Number of frames per second (see note below). If it
Expand Down Expand Up @@ -926,8 +929,8 @@ class ImageClip(VideoClip):
-----------

img
Any picture file (png, tiff, jpeg, etc.) or any array representing
an RGB image (for instance a frame from a VideoClip).
Any picture file (png, tiff, jpeg, etc.) as a string or a path-like object,
or any array representing an RGB image (for instance a frame from a VideoClip).

ismask
Set this parameter to `True` if the clip is a mask.
Expand All @@ -949,7 +952,8 @@ def __init__(
):
VideoClip.__init__(self, ismask=ismask, duration=duration)

if isinstance(img, str):
if not isinstance(img, np.ndarray):
# img is a string or path-like object, so read it in from disk
img = imread(img)

if len(img.shape) == 3: # img is (now) a RGB(a) numpy array
Expand Down Expand Up @@ -1066,7 +1070,8 @@ class TextClip(ImageClip):
``filename``.

filename
The name of a file in which there is the text to write.
The name of a file in which there is the text to write,
as a string or a path-like object.
Can be provided instead of argument ``txt``

size
Expand Down Expand Up @@ -1116,6 +1121,7 @@ class TextClip(ImageClip):

"""

@convert_path_to_string("filename")
def __init__(
self,
txt=None,
Expand Down
9 changes: 6 additions & 3 deletions moviepy/video/io/VideoFileClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from moviepy.audio.io.AudioFileClip import AudioFileClip
from moviepy.Clip import Clip
from moviepy.decorators import convert_path_to_string
from moviepy.video.io.ffmpeg_reader import FFMPEG_VideoReader
from moviepy.video.VideoClip import VideoClip

Expand All @@ -22,8 +23,9 @@ class VideoFileClip(VideoClip):
------------

filename:
The name of the video file. It can have any extension supported
by ffmpeg: .ogv, .mp4, .mpeg, .avi, .mov etc.
The name of the video file, as a string or a path-like object.
It can have any extension supported by ffmpeg:
.ogv, .mp4, .mpeg, .avi, .mov etc.

has_mask:
Set this to 'True' if there is a mask included in the videofile.
Expand Down Expand Up @@ -75,6 +77,7 @@ class VideoFileClip(VideoClip):

"""

@convert_path_to_string("filename")
def __init__(
self,
filename,
Expand Down Expand Up @@ -108,7 +111,7 @@ def __init__(
self.size = self.reader.size
self.rotation = self.reader.rotation

self.filename = self.reader.filename
self.filename = filename
tburrows13 marked this conversation as resolved.
Show resolved Hide resolved

if has_mask:

Expand Down
6 changes: 6 additions & 0 deletions moviepy/video/io/ffmpeg_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import sys

from moviepy.config import get_setting
from moviepy.decorators import convert_path_to_string
from moviepy.tools import subprocess_call


@convert_path_to_string("filename")
def ffmpeg_movie_from_frames(filename, folder, fps, digits=6, bitrate="v"):
"""
Writes a movie out of the frames (picture files) in a folder.
Expand All @@ -33,6 +35,7 @@ def ffmpeg_movie_from_frames(filename, folder, fps, digits=6, bitrate="v"):
subprocess_call(cmd)


@convert_path_to_string(("filename", "targetname"))
def ffmpeg_extract_subclip(filename, t1, t2, targetname=None):
""" Makes a new video file playing video file ``filename`` between
the times ``t1`` and ``t2``. """
Expand Down Expand Up @@ -62,6 +65,7 @@ def ffmpeg_extract_subclip(filename, t1, t2, targetname=None):
subprocess_call(cmd)


@convert_path_to_string(("video", "audio", "output"))
def ffmpeg_merge_video_audio(
video,
audio,
Expand Down Expand Up @@ -90,6 +94,7 @@ def ffmpeg_merge_video_audio(
subprocess_call(cmd, logger=logger)


@convert_path_to_string(("inputfile", "output"))
def ffmpeg_extract_audio(inputfile, output, bitrate=3000, fps=44100):
""" extract the sound from a video file and save it in ``output`` """
cmd = [
Expand All @@ -106,6 +111,7 @@ def ffmpeg_extract_audio(inputfile, output, bitrate=3000, fps=44100):
subprocess_call(cmd)


@convert_path_to_string(("video", "output"))
def ffmpeg_resize(video, output, size):
""" resizes ``video`` to new size ``size`` and write the result
in file ``output``. """
Expand Down
6 changes: 4 additions & 2 deletions moviepy/video/tools/credits.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
credits, even though it is difficult to fill everyone needs in this
matter.
"""

from moviepy.decorators import convert_path_to_string
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
from moviepy.video.fx.resize import resize
from moviepy.video.VideoClip import ImageClip, TextClip


@convert_path_to_string("creditfile")
def credits1(
creditfile,
width,
Expand All @@ -26,7 +27,8 @@ def credits1(
-----------

creditfile
A text file whose content must be as follows: ::
A string or path like object pointing to a text file
whose content must be as follows: ::

# This is a comment
# The next line says : leave 4 blank lines
Expand Down
7 changes: 5 additions & 2 deletions moviepy/video/tools/subtitles.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import numpy as np

from moviepy.decorators import convert_path_to_string
from moviepy.tools import cvsecs
from moviepy.video.VideoClip import TextClip, VideoClip

Expand All @@ -19,7 +20,7 @@ class SubtitlesClip(VideoClip):
==========

subtitles
Either the name of a file, or a list
Either the name of a file as a string or path-like object, or a list

Examples
=========
Expand All @@ -38,7 +39,8 @@ def __init__(self, subtitles, make_textclip=None):

VideoClip.__init__(self, has_constant_size=False)

if isinstance(subtitles, str):
if not isinstance(subtitles, list):
# `subtitles` is a string or path-like object
subtitles = file_to_subtitles(subtitles)

# subtitles = [(map(cvsecs, tt),txt) for tt, txt in subtitles]
Expand Down Expand Up @@ -144,6 +146,7 @@ def write_srt(self, filename):
f.write(str(self))


@convert_path_to_string("filename")
def file_to_subtitles(filename):
""" Converts a srt file into subtitles.

Expand Down
Loading