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

(fix) escape dashes in the beginning of filenames in ffmpeg commands #2163

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added media/-filenamethatstartswithdash-.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, dash_escape


class FFMPEG_AudioWriter:
Expand Down Expand Up @@ -74,15 +74,15 @@ def __init__(
if input_video is None:
cmd.extend(["-vn"])
else:
cmd.extend(["-i", input_video, "-vcodec", "copy"])
cmd.extend(["-i", dash_escape(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([dash_escape(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, dash_escape
from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos


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

cmd = (
[FFMPEG_BINARY]
Expand Down
23 changes: 23 additions & 0 deletions moviepy/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,26 @@ def find_extension(codec):
"specify a temp_audiofile with the right extension "
"in write_videofile."
)


def dash_escape(path):
"""Makes paths that start with dash '-' intertreptable as command line arguments

Returns
-------

- ./path if path starts with dash '-'
- path otherwise

Examples
--------
>>> dash_escape('-filenamethatstartswithdash-.mp4')
'./-filenamethatstartswithdash-.mp4'
>>> dash_escape('-path/that/starts/with/dash.mp4')
'./-path/that/starts/with/dash.mp4'
>>> dash_escape('file-name-.mp4')
'file-name-.mp4'
>>> dash_escape('/absolute/path/to/-file.mp4')
'/absolute/path/to/-file.mp4'
"""
return "./" + path if path.startswith("-") else path
4 changes: 2 additions & 2 deletions moviepy/video/io/ffmpeg_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
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, dash_escape


class FFMPEG_VideoReader:
Expand Down Expand Up @@ -790,7 +790,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", dash_escape(filename)]
if decode_file:
cmd.extend(["-f", "null", "-"])

Expand Down
29 changes: 18 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 dash_escape, 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,
dash_escape(inputfile),
"-t",
"%0.2f" % (end_time - start_time),
"-map",
Expand All @@ -51,7 +51,7 @@ def ffmpeg_extract_subclip(
"-acodec",
"copy",
"-copyts",
outputfile,
dash_escape(outputfile),
]
subprocess_call(cmd, logger=logger)

Expand Down Expand Up @@ -89,14 +89,14 @@ def ffmpeg_merge_video_audio(
FFMPEG_BINARY,
"-y",
"-i",
audiofile,
dash_escape(audiofile),
"-i",
videofile,
dash_escape(videofile),
"-vcodec",
video_codec,
"-acodec",
audio_codec,
outputfile,
dash_escape(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,
dash_escape(inputfile),
"-ab",
"%dk" % bitrate,
"-ar",
"%d" % fps,
outputfile,
dash_escape(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,
dash_escape(inputfile),
"-vf",
"scale=%d:%d" % (size[0], size[1]),
outputfile,
dash_escape(outputfile),
]

subprocess_call(cmd, logger=logger)
Expand Down Expand Up @@ -194,7 +194,14 @@ 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",
dash_escape(inputfile),
"-vf",
"deshake",
dash_escape(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, dash_escape


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", dash_escape(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([dash_escape(filename)])

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

if logfile:
Expand Down
6 changes: 3 additions & 3 deletions moviepy/video/io/gif_writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from moviepy.config import FFMPEG_BINARY, IMAGEMAGICK_BINARY
from moviepy.decorators import requires_duration, use_clip_fps_by_default
from moviepy.tools import cross_platform_popen_params, subprocess_call
from moviepy.tools import cross_platform_popen_params, dash_escape, subprocess_call
from moviepy.video.fx.loop import loop as loop_fx


Expand Down Expand Up @@ -153,7 +153,7 @@ def write_gif_with_tempfiles(
"-r",
str(fps),
"-i",
file_root + "_GIFTEMP%04d.png",
dash_escape(file_root) + "_GIFTEMP%04d.png",
"-r",
str(fps),
filename,
Expand Down Expand Up @@ -328,7 +328,7 @@ def write_gif(
(pixel_format),
"-r",
"%.02f" % fps,
filename,
dash_escape(filename),
],
**popen_params,
)
Expand Down
10 changes: 10 additions & 0 deletions tests/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from moviepy.video.compositing.concatenate import concatenate_videoclips
from moviepy.video.compositing.transitions import crossfadein, crossfadeout
from moviepy.video.fx.resize import resize
from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos
from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy.video.VideoClip import ColorClip, ImageClip, VideoClip

Expand Down Expand Up @@ -401,5 +402,14 @@ def test_issue_655():
assert True


def test_issue_2160():
os.chdir("media")
try:
d = ffmpeg_parse_infos("-filenamethatstartswithdash-.mp4")
assert d["video_found"]
finally: # change back to working directory even if test fails
os.chdir("..")


if __name__ == "__main__":
pytest.main()
14 changes: 14 additions & 0 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,5 +329,19 @@ def test_decorators_argument_converters_consistency(
assert function_data["function_arguments"]


@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"),
],
)
def test_dash_escape(given, expected):
"""Test the dash_escape function outputs correct paths as per the docstring."""
assert tools.dash_escape(given) == expected


if __name__ == "__main__":
pytest.main()