From a184551aea6561a9ad1e301190e3d6068a5a4a81 Mon Sep 17 00:00:00 2001 From: Mattia Lecci Date: Mon, 6 Mar 2023 17:51:19 +0100 Subject: [PATCH 1/2] Add cmdline to Error Having stdout, sterr, and the command used to launch ffmpeg in Error makes debugging easier --- ffmpeg/_probe.py | 5 +++-- ffmpeg/_run.py | 9 ++++++--- ffmpeg/_utils.py | 13 +++++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ffmpeg/_probe.py b/ffmpeg/_probe.py index 090d7ab..2c11217 100644 --- a/ffmpeg/_probe.py +++ b/ffmpeg/_probe.py @@ -1,7 +1,7 @@ import json import subprocess from ._run import Error -from ._utils import convert_kwargs_to_cmd_line_args +from ._utils import convert_kwargs_to_cmd_line_args, convert_cmd_line_args_to_cmd_line_str def probe(filename, cmd='ffprobe', timeout=None, **kwargs): @@ -23,7 +23,8 @@ def probe(filename, cmd='ffprobe', timeout=None, **kwargs): communicate_kwargs['timeout'] = timeout out, err = p.communicate(**communicate_kwargs) if p.returncode != 0: - raise Error('ffprobe', out, err) + cmdline = convert_cmd_line_args_to_cmd_line_str(args) + raise Error('ffprobe', out, err, cmdline) return json.loads(out.decode('utf-8')) diff --git a/ffmpeg/_run.py b/ffmpeg/_run.py index f42d1d7..febff5c 100644 --- a/ffmpeg/_run.py +++ b/ffmpeg/_run.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals from .dag import get_outgoing_edges, topo_sort -from ._utils import basestring, convert_kwargs_to_cmd_line_args +from ._utils import basestring, convert_kwargs_to_cmd_line_args, convert_cmd_line_args_to_cmd_line_str from builtins import str from functools import reduce import copy @@ -24,12 +24,13 @@ except ImportError: class Error(Exception): - def __init__(self, cmd, stdout, stderr): + def __init__(self, cmd, stdout, stderr, cmdline): super(Error, self).__init__( '{} error (see stderr output for detail)'.format(cmd) ) self.stdout = stdout self.stderr = stderr + self.cmdline = cmdline def _get_input_args(input_node): @@ -334,7 +335,9 @@ def run( out, err = process.communicate(input) retcode = process.poll() if retcode: - raise Error('ffmpeg', out, err) + args = compile(stream_spec, cmd, overwrite_output=overwrite_output) + cmdline = convert_cmd_line_args_to_cmd_line_str(args) + raise Error('ffmpeg', out, err, cmdline) return out, err diff --git a/ffmpeg/_utils.py b/ffmpeg/_utils.py index 9baa2c7..772b677 100644 --- a/ffmpeg/_utils.py +++ b/ffmpeg/_utils.py @@ -2,8 +2,9 @@ from __future__ import unicode_literals from builtins import str from past.builtins import basestring import hashlib -import sys - +import sys, os +import subprocess +import shlex if sys.version_info.major == 2: # noinspection PyUnresolvedReferences,PyShadowingBuiltins @@ -106,3 +107,11 @@ def convert_kwargs_to_cmd_line_args(kwargs): if v is not None: args.append('{}'.format(v)) return args + + +def convert_cmd_line_args_to_cmd_line_str(args): + """Helper function to build command line string from command line args.""" + if os.name == "nt": # Windows system + return subprocess.list2cmdline(args) + # Non-windows systems + return shlex.join(args) From c81e246846407bb6134726eb6a1afbd139cdcf51 Mon Sep 17 00:00:00 2001 From: Mattia Lecci Date: Mon, 6 Mar 2023 17:57:57 +0100 Subject: [PATCH 2/2] Add Error.cmdline print to examples --- examples/get_video_thumbnail.py | 2 +- examples/show_progress.py | 2 +- examples/transcribe.py | 2 +- examples/video_info.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/get_video_thumbnail.py b/examples/get_video_thumbnail.py index b905642..860965a 100755 --- a/examples/get_video_thumbnail.py +++ b/examples/get_video_thumbnail.py @@ -26,7 +26,7 @@ def generate_thumbnail(in_filename, out_filename, time, width): .run(capture_stdout=True, capture_stderr=True) ) except ffmpeg.Error as e: - print(e.stderr.decode(), file=sys.stderr) + print(f"$ {e.cmdline}\n{e.stderr.decode()}", file=sys.stderr) sys.exit(1) diff --git a/examples/show_progress.py b/examples/show_progress.py index dd0253a..81d24f5 100755 --- a/examples/show_progress.py +++ b/examples/show_progress.py @@ -125,6 +125,6 @@ if __name__ == '__main__': .run(capture_stdout=True, capture_stderr=True) ) except ffmpeg.Error as e: - print(e.stderr, file=sys.stderr) + print(f"$ {e.cmdline}\n{e.stderr.decode()}", file=sys.stderr) sys.exit(1) diff --git a/examples/transcribe.py b/examples/transcribe.py index 0b7200c..eb03ec9 100755 --- a/examples/transcribe.py +++ b/examples/transcribe.py @@ -27,7 +27,7 @@ def decode_audio(in_filename, **input_kwargs): .run(capture_stdout=True, capture_stderr=True) ) except ffmpeg.Error as e: - print(e.stderr, file=sys.stderr) + print(f"$ {e.cmdline}\n{e.stderr.decode()}", file=sys.stderr) sys.exit(1) return out diff --git a/examples/video_info.py b/examples/video_info.py index df9c992..525b22d 100755 --- a/examples/video_info.py +++ b/examples/video_info.py @@ -15,7 +15,7 @@ if __name__ == '__main__': try: probe = ffmpeg.probe(args.in_filename) except ffmpeg.Error as e: - print(e.stderr, file=sys.stderr) + print(f"$ {e.cmdline}\n{e.stderr.decode()}", file=sys.stderr) sys.exit(1) video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)