From 25bda398c97b9eb25f7128782f94c91a1edd6215 Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Sat, 27 Jan 2018 21:19:19 -0800 Subject: [PATCH 1/5] Add ffprobe support --- ffmpeg/__init__.py | 5 +++-- ffmpeg/_probe.py | 28 ++++++++++++++++++++++++++++ ffmpeg/tests/test_ffmpeg.py | 6 ++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100755 ffmpeg/_probe.py diff --git a/ffmpeg/__init__.py b/ffmpeg/__init__.py index d4f48d8..7e913d3 100644 --- a/ffmpeg/__init__.py +++ b/ffmpeg/__init__.py @@ -1,9 +1,10 @@ from __future__ import unicode_literals -from . import _filters, _ffmpeg, _run +from . import _filters, _ffmpeg, _run, _probe from ._filters import * from ._ffmpeg import * from ._run import * from ._view import * +from ._probe import * -__all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ + _view.__all__ +__all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ + _view.__all__ + _probe.__all__ diff --git a/ffmpeg/_probe.py b/ffmpeg/_probe.py new file mode 100755 index 0000000..300cc89 --- /dev/null +++ b/ffmpeg/_probe.py @@ -0,0 +1,28 @@ +import json +import subprocess + + +class ProbeException(Exception): + def __init__(self, stderr_output): + super(ProbeException, self).__init__('ffprobe error') + self.stderr_output = stderr_output + + +def probe(filename): + """Run ffprobe on the specified file and return a JSON representation of the output. + + Raises: + ProbeException: if ffprobe returns a non-zero exit code, a ``ProbeException`` is returned with a generic error + message. The stderr output can be retrieved by accessing the ``stderr_output`` property of the exception. + """ + args = ['ffprobe', '-show_format', '-show_streams', '-of', 'json', filename] + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if p.returncode != 0: + raise ExecException(err) + return json.loads(out) + + +__all__ = [ + 'probe', +] diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 1380fdb..2650f12 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -353,3 +353,9 @@ def test_pipe(): out_data = p.stdout.read() assert len(out_data) == frame_size * (frame_count - start_frame) assert out_data == in_data[start_frame*frame_size:] + + +def test_ffprobe(): + data = ffmpeg.probe(TEST_INPUT_FILE1) + assert set(data.keys()) == {'format', 'streams'} + assert data['format']['duration'] == '7.036000' From 24e737f78e068cd81a925b7e1df940b72bb74b07 Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Sat, 27 Jan 2018 21:32:11 -0800 Subject: [PATCH 2/5] Fix ffprobe string decoding --- ffmpeg/_probe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffmpeg/_probe.py b/ffmpeg/_probe.py index 300cc89..0287939 100755 --- a/ffmpeg/_probe.py +++ b/ffmpeg/_probe.py @@ -20,7 +20,7 @@ def probe(filename): out, err = p.communicate() if p.returncode != 0: raise ExecException(err) - return json.loads(out) + return json.loads(out.decode('utf-8')) __all__ = [ From 2fff94af6c61530230d27c1a2e84ac1f1bc07c0c Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Sat, 27 Jan 2018 22:51:05 -0800 Subject: [PATCH 3/5] Fix probe exception handling and add test --- ffmpeg/_probe.py | 3 ++- ffmpeg/tests/test_ffmpeg.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ffmpeg/_probe.py b/ffmpeg/_probe.py index 0287939..ea3a52c 100755 --- a/ffmpeg/_probe.py +++ b/ffmpeg/_probe.py @@ -19,10 +19,11 @@ def probe(filename): p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode != 0: - raise ExecException(err) + raise ProbeException(err) return json.loads(out.decode('utf-8')) __all__ = [ 'probe', + 'ProbeException', ] diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 2650f12..07c5c4b 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -16,6 +16,7 @@ TEST_INPUT_FILE1 = os.path.join(SAMPLE_DATA_DIR, 'in1.mp4') TEST_OVERLAY_FILE = os.path.join(SAMPLE_DATA_DIR, 'overlay.png') TEST_OUTPUT_FILE1 = os.path.join(SAMPLE_DATA_DIR, 'out1.mp4') TEST_OUTPUT_FILE2 = os.path.join(SAMPLE_DATA_DIR, 'out2.mp4') +BOGUS_INPUT_FILE = os.path.join(SAMPLE_DATA_DIR, 'bogus') subprocess.check_call(['ffmpeg', '-version']) @@ -359,3 +360,10 @@ def test_ffprobe(): data = ffmpeg.probe(TEST_INPUT_FILE1) assert set(data.keys()) == {'format', 'streams'} assert data['format']['duration'] == '7.036000' + + +def test_ffprobe_exception(): + with pytest.raises(ffmpeg.ProbeException) as excinfo: + ffmpeg.probe(BOGUS_INPUT_FILE) + assert excinfo.value.message == 'ffprobe error' + assert 'No such file or directory' in excinfo.value.stderr_output From 87a168a063323555665c239c539f4dec52e6c95a Mon Sep 17 00:00:00 2001 From: Davide Depau Date: Thu, 8 Mar 2018 22:52:06 +0100 Subject: [PATCH 4/5] Do not use Exception.message, use str(Exception) instead --- ffmpeg/tests/test_ffmpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 07c5c4b..ad32784 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -365,5 +365,5 @@ def test_ffprobe(): def test_ffprobe_exception(): with pytest.raises(ffmpeg.ProbeException) as excinfo: ffmpeg.probe(BOGUS_INPUT_FILE) - assert excinfo.value.message == 'ffprobe error' + assert str(excinfo.value) == 'ffprobe error' assert 'No such file or directory' in excinfo.value.stderr_output From 4927bbeea9037145c21a85df71c1d83c63a7274f Mon Sep 17 00:00:00 2001 From: Davide Depau Date: Thu, 8 Mar 2018 23:02:04 +0100 Subject: [PATCH 5/5] Fix string type inconsistency error in ffprobe test --- ffmpeg/tests/test_ffmpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index ad32784..b874a86 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -366,4 +366,4 @@ def test_ffprobe_exception(): with pytest.raises(ffmpeg.ProbeException) as excinfo: ffmpeg.probe(BOGUS_INPUT_FILE) assert str(excinfo.value) == 'ffprobe error' - assert 'No such file or directory' in excinfo.value.stderr_output + assert b'No such file or directory' in excinfo.value.stderr_output