Skip to content

Commit

Permalink
Merge pull request #2282 from OsaAjani/fix-2160
Browse files Browse the repository at this point in the history
Fix issue #2160 based on PR #2163 relative to filenames starting with…
  • Loading branch information
OsaAjani authored Jan 3, 2025
2 parents be70a95 + d5a76da commit 39b6c5c
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 25 deletions.
Binary file added media/-video-with-dash-.mp4
Binary file not shown.
6 changes: 3 additions & 3 deletions moviepy/audio/io/ffmpeg_audiowriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from moviepy.config import FFMPEG_BINARY
from moviepy.decorators import requires_duration
from moviepy.tools import cross_platform_popen_params
from moviepy.tools import cross_platform_popen_params, ffmpeg_escape_filename


class FFMPEG_AudioWriter:
Expand Down Expand Up @@ -89,15 +89,15 @@ def __init__(
if input_video is None:
cmd.extend(["-vn"])
else:
cmd.extend(["-i", input_video, "-vcodec", "copy"])
cmd.extend(["-i", ffmpeg_escape_filename(input_video), "-vcodec", "copy"])

cmd.extend(["-acodec", codec] + ["-ar", "%d" % fps_input])
cmd.extend(["-strict", "-2"]) # needed to support codec 'aac'
if bitrate is not None:
cmd.extend(["-ab", bitrate])
if ffmpeg_params is not None:
cmd.extend(ffmpeg_params)
cmd.extend([filename])
cmd.extend([ffmpeg_escape_filename(filename)])

popen_params = cross_platform_popen_params(
{"stdout": sp.DEVNULL, "stderr": logfile, "stdin": sp.PIPE}
Expand Down
6 changes: 3 additions & 3 deletions moviepy/audio/io/readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import numpy as np

from moviepy.config import FFMPEG_BINARY
from moviepy.tools import cross_platform_popen_params
from moviepy.tools import cross_platform_popen_params, ffmpeg_escape_filename
from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos


Expand Down Expand Up @@ -80,13 +80,13 @@ def initialize(self, start_time=0):
"-ss",
"%.05f" % (start_time - offset),
"-i",
self.filename,
ffmpeg_escape_filename(self.filename),
"-vn",
"-ss",
"%.05f" % offset,
]
else:
i_arg = ["-i", self.filename, "-vn"]
i_arg = ["-i", ffmpeg_escape_filename(self.filename), "-vn"]

cmd = (
[FFMPEG_BINARY]
Expand Down
11 changes: 11 additions & 0 deletions moviepy/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ def subprocess_call(cmd, logger="bar"):
del proc


def ffmpeg_escape_filename(filename):
"""Escape a filename that we want to pass to the ffmpeg command line
That will ensure the filename doesn't start with a '-' (which would raise an error)
"""
if filename.startswith('-') :
filename = './' + filename

return filename


def convert_to_seconds(time):
"""Will convert any time into seconds.
Expand Down
12 changes: 8 additions & 4 deletions moviepy/video/io/ffmpeg_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
import numpy as np

from moviepy.config import FFMPEG_BINARY # ffmpeg, ffmpeg.exe, etc...
from moviepy.tools import convert_to_seconds, cross_platform_popen_params
from moviepy.tools import (
convert_to_seconds,
cross_platform_popen_params,
ffmpeg_escape_filename,
)


class FFMPEG_VideoReader:
Expand Down Expand Up @@ -91,12 +95,12 @@ def initialize(self, start_time=0):
"-ss",
"%.06f" % (start_time - offset),
"-i",
self.filename,
ffmpeg_escape_filename(self.filename),
"-ss",
"%.06f" % offset,
]
else:
i_arg = ["-i", self.filename]
i_arg = ["-i", ffmpeg_escape_filename(self.filename)]

cmd = (
[FFMPEG_BINARY]
Expand Down Expand Up @@ -801,7 +805,7 @@ def ffmpeg_parse_infos(
https://github.com/Zulko/moviepy/pull/1222).
"""
# Open the file in a pipe, read output
cmd = [FFMPEG_BINARY, "-hide_banner", "-i", filename]
cmd = [FFMPEG_BINARY, "-hide_banner", "-i", ffmpeg_escape_filename(filename)]
if decode_file:
cmd.extend(["-f", "null", "-"])

Expand Down
31 changes: 20 additions & 11 deletions moviepy/video/io/ffmpeg_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from moviepy.config import FFMPEG_BINARY
from moviepy.decorators import convert_parameter_to_seconds, convert_path_to_string
from moviepy.tools import subprocess_call
from moviepy.tools import ffmpeg_escape_filename, subprocess_call


@convert_path_to_string(("inputfile", "outputfile"))
Expand Down Expand Up @@ -41,7 +41,7 @@ def ffmpeg_extract_subclip(
"-ss",
"%0.2f" % start_time,
"-i",
inputfile,
ffmpeg_escape_filename(inputfile),
"-t",
"%0.2f" % (end_time - start_time),
"-map",
Expand All @@ -51,7 +51,7 @@ def ffmpeg_extract_subclip(
"-acodec",
"copy",
"-copyts",
outputfile,
ffmpeg_escape_filename(outputfile),
]
subprocess_call(cmd, logger=logger)

Expand Down Expand Up @@ -89,14 +89,14 @@ def ffmpeg_merge_video_audio(
FFMPEG_BINARY,
"-y",
"-i",
audiofile,
ffmpeg_escape_filename(audiofile),
"-i",
videofile,
ffmpeg_escape_filename(videofile),
"-vcodec",
video_codec,
"-acodec",
audio_codec,
outputfile,
ffmpeg_escape_filename(outputfile),
]

subprocess_call(cmd, logger=logger)
Expand Down Expand Up @@ -125,12 +125,12 @@ def ffmpeg_extract_audio(inputfile, outputfile, bitrate=3000, fps=44100, logger=
FFMPEG_BINARY,
"-y",
"-i",
inputfile,
ffmpeg_escape_filename(inputfile),
"-ab",
"%dk" % bitrate,
"-ar",
"%d" % fps,
outputfile,
ffmpeg_escape_filename(outputfile),
]
subprocess_call(cmd, logger=logger)

Expand All @@ -154,10 +154,10 @@ def ffmpeg_resize(inputfile, outputfile, size, logger="bar"):
cmd = [
FFMPEG_BINARY,
"-i",
inputfile,
ffmpeg_escape_filename(inputfile),
"-vf",
"scale=%d:%d" % (size[0], size[1]),
outputfile,
ffmpeg_escape_filename(outputfile),
]

subprocess_call(cmd, logger=logger)
Expand Down Expand Up @@ -194,7 +194,16 @@ def ffmpeg_stabilize_video(
outputfile = f"{name}_stabilized{ext}"

outputfile = os.path.join(output_dir, outputfile)
cmd = [FFMPEG_BINARY, "-i", inputfile, "-vf", "deshake", outputfile]
cmd = [
FFMPEG_BINARY,
"-i",
ffmpeg_escape_filename(inputfile),
"-vf",
"deshake",
ffmpeg_escape_filename(outputfile)
]

if overwrite_file:
cmd.append("-y")

subprocess_call(cmd, logger=logger)
8 changes: 4 additions & 4 deletions moviepy/video/io/ffmpeg_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from proglog import proglog

from moviepy.config import FFMPEG_BINARY
from moviepy.tools import cross_platform_popen_params
from moviepy.tools import cross_platform_popen_params, ffmpeg_escape_filename


class FFMPEG_VideoWriter:
Expand Down Expand Up @@ -119,7 +119,7 @@ def __init__(
"-",
]
if audiofile is not None:
cmd.extend(["-i", audiofile, "-acodec", "copy"])
cmd.extend(["-i", ffmpeg_escape_filename(audiofile), "-acodec", "copy"])
cmd.extend(["-vcodec", codec, "-preset", preset])
if ffmpeg_params is not None:
cmd.extend(ffmpeg_params)
Expand All @@ -131,7 +131,7 @@ def __init__(

if (codec == "libx264") and (size[0] % 2 == 0) and (size[1] % 2 == 0):
cmd.extend(["-pix_fmt", "yuv420p"])
cmd.extend([filename])
cmd.extend([ffmpeg_escape_filename(filename)])

popen_params = cross_platform_popen_params(
{"stdout": sp.DEVNULL, "stderr": logfile, "stdin": sp.PIPE}
Expand Down Expand Up @@ -306,7 +306,7 @@ def ffmpeg_write_image(filename, image, logfile=False, pixel_format=None):
pixel_format,
"-i",
"-",
filename,
ffmpeg_escape_filename(filename),
]

if logfile:
Expand Down
6 changes: 6 additions & 0 deletions tests/test_ffmpeg_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,9 @@ def test_write_gif(util, clip_class, loop, with_mask):
assert r == 0
assert g == 0
assert b == 255


def test_write_file_with_spaces(util):
filename = os.path.join(util.TMP_DIR, "name with spaces.mp4")
clip = ColorClip((1, 1), color=1, is_mask=True).with_fps(1).with_duration(0.3)
ffmpeg_write_video(clip, filename, fps=1)
9 changes: 9 additions & 0 deletions tests/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,5 +360,14 @@ def test_issue_1682_2(util):
clip.write_audiofile(output_audio_filepath)


def test_issue_2160(util):
filename = "media/-video-with-dash-.mp4"
clip = VideoFileClip(filename)
output_video_filepath = os.path.join(
util.TMP_DIR, "big_buck_bunny_0_30_cutout.webm"
)
clip.write_videofile(output_video_filepath)


if __name__ == "__main__":
pytest.main()
17 changes: 17 additions & 0 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ def test_subprocess_call(command):
tools.subprocess_call(command, logger=None)


@pytest.mark.parametrize(
"given, expected",
[
("-filenamethatstartswithdash-.mp4", "./-filenamethatstartswithdash-.mp4"),
("-path/that/starts/with/dash.mp4", "./-path/that/starts/with/dash.mp4"),
("file-name-.mp4", "file-name-.mp4"),
("/absolute/path/to/-file.mp4", "/absolute/path/to/-file.mp4"),
("filename with spaces.mp4", "filename with spaces.mp4")
],
)
def test_ffmpeg_escape_filename(given, expected):
"""Test the ffmpeg_escape_filename function outputs correct paths as per
the docstring.
"""
assert tools.ffmpeg_escape_filename(given) == expected


@pytest.mark.parametrize("os_name", (os.name, "nt"))
def test_cross_platform_popen_params(os_name, monkeypatch):
tools_module = importlib.import_module("moviepy.tools")
Expand Down

0 comments on commit 39b6c5c

Please sign in to comment.