Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI
on:
- push
- pull_request
jobs:
test:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
python-version:
- "2.7"
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- "3.9"
- "3.10"
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install ffmpeg
run: |
sudo apt update
sudo apt install ffmpeg
- name: Setup pip + tox
run: |
python -m pip install --upgrade \
"pip==20.3.4; python_version < '3.6'" \
"pip==21.3.1; python_version >= '3.6'"
python -m pip install tox==3.24.5 tox-gh-actions==2.9.1
- name: Test with tox
run: tox
black:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Black
run: |
# TODO: use standard `psf/black` action after dropping Python 2 support.
pip install black==21.12b0 click==8.0.2 # https://stackoverflow.com/questions/71673404
black ffmpeg --check --color --diff
32 changes: 0 additions & 32 deletions .travis.yml

This file was deleted.

29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# ffmpeg-python: Python bindings for FFmpeg

[![Build status](https://travis-ci.org/kkroening/ffmpeg-python.svg?branch=master)](https://travis-ci.org/kkroening/ffmpeg-python)
[![CI][ci-badge]][ci]

[ci-badge]: https://github.com/kkroening/ffmpeg-python/actions/workflows/ci.yml/badge.svg
[ci]: https://github.com/kkroening/ffmpeg-python/actions/workflows/ci.yml

<img src="https://raw.githubusercontent.com/kkroening/ffmpeg-python/master/doc/formula.png" alt="ffmpeg-python logo" width="60%" />

Expand Down Expand Up @@ -78,9 +81,11 @@ Real-world signal graphs can get a heck of a lot more complex, but `ffmpeg-pytho

## Installation

### Installing `ffmpeg-python`

The latest version of `ffmpeg-python` can be acquired via a typical pip install:

```
```bash
pip install ffmpeg-python
```

Expand All @@ -90,6 +95,24 @@ git clone git@github.com:kkroening/ffmpeg-python.git
pip install -e ./ffmpeg-python
```

> **Note**: `ffmpeg-python` makes no attempt to download/install FFmpeg, as `ffmpeg-python` is merely a pure-Python wrapper - whereas FFmpeg installation is platform-dependent/environment-specific, and is thus the responsibility of the user, as described below.

### Installing FFmpeg

Before using `ffmpeg-python`, FFmpeg must be installed and accessible via the `$PATH` environment variable.

There are a variety of ways to install FFmpeg, such as the [official download links](https://ffmpeg.org/download.html), or using your package manager of choice (e.g. `sudo apt install ffmpeg` on Debian/Ubuntu, `brew install ffmpeg` on OS X, etc.).

Regardless of how FFmpeg is installed, you can check if your environment path is set correctly by running the `ffmpeg` command from the terminal, in which case the version information should appear, as in the following example (truncated for brevity):

```
$ ffmpeg
ffmpeg version 4.2.4-1ubuntu0.1 Copyright (c) 2000-2020 the FFmpeg developers
built with gcc 9 (Ubuntu 9.3.0-10ubuntu2)
```

> **Note**: The actual version information displayed here may vary from one system to another; but if a message such as `ffmpeg: command not found` appears instead of the version information, FFmpeg is not properly installed.

## [Examples](https://github.com/kkroening/ffmpeg-python/tree/master/examples)

When in doubt, take a look at the [examples](https://github.com/kkroening/ffmpeg-python/tree/master/examples) to see if there's something that's close to whatever you're trying to do.
Expand Down Expand Up @@ -194,7 +217,7 @@ When in doubt, refer to the [existing filters](https://github.com/kkroening/ffmp

**Why do I get an import/attribute/etc. error from `import ffmpeg`?**

Make sure you ran `pip install ffmpeg-python` and not `pip install ffmpeg` or `pip install python-ffmpeg`.
Make sure you ran `pip install ffmpeg-python` and _**not**_ `pip install ffmpeg` (wrong) or `pip install python-ffmpeg` (also wrong).

**Why did my audio stream get dropped?**

Expand Down
6 changes: 3 additions & 3 deletions doc/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ <h1>ffmpeg-python: Python bindings for FFmpeg<a class="headerlink" href="#ffmpeg
<dt id="ffmpeg.compile">
<code class="sig-prename descclassname">ffmpeg.</code><code class="sig-name descname">compile</code><span class="sig-paren">(</span><em class="sig-param">stream_spec</em>, <em class="sig-param">cmd='ffmpeg'</em>, <em class="sig-param">overwrite_output=False</em><span class="sig-paren">)</span><a class="headerlink" href="#ffmpeg.compile" title="Permalink to this definition">¶</a></dt>
<dd><p>Build command-line for invoking ffmpeg.</p>
<p>The <a class="reference internal" href="#ffmpeg.run" title="ffmpeg.run"><code class="xref py py-meth docutils literal notranslate"><span class="pre">run()</span></code></a> function uses this to build the commnad line
<p>The <a class="reference internal" href="#ffmpeg.run" title="ffmpeg.run"><code class="xref py py-meth docutils literal notranslate"><span class="pre">run()</span></code></a> function uses this to build the command line
arguments and should work in most cases, but calling this function
directly is useful for debugging or if you need to invoke ffmpeg
manually for whatever reason.</p>
Expand Down Expand Up @@ -340,7 +340,7 @@ <h1>ffmpeg-python: Python bindings for FFmpeg<a class="headerlink" href="#ffmpeg
<li><p><strong>y</strong> – The vertical position, in the input video, of the top edge of the
output video.</p></li>
<li><p><strong>width</strong> – The width of the output video. Must be greater than 0.</p></li>
<li><p><strong>heigth</strong> – The height of the output video. Must be greater than 0.</p></li>
<li><p><strong>height</strong> – The height of the output video. Must be greater than 0.</p></li>
</ul>
</dd>
</dl>
Expand All @@ -357,7 +357,7 @@ <h1>ffmpeg-python: Python bindings for FFmpeg<a class="headerlink" href="#ffmpeg
<li><p><strong>x</strong> – The expression which specifies the top left corner x coordinate of the box. It defaults to 0.</p></li>
<li><p><strong>y</strong> – The expression which specifies the top left corner y coordinate of the box. It defaults to 0.</p></li>
<li><p><strong>width</strong> – Specify the width of the box; if 0 interpreted as the input width. It defaults to 0.</p></li>
<li><p><strong>heigth</strong> – Specify the height of the box; if 0 interpreted as the input height. It defaults to 0.</p></li>
<li><p><strong>height</strong> – Specify the height of the box; if 0 interpreted as the input height. It defaults to 0.</p></li>
<li><p><strong>color</strong> – Specify the color of the box to write. For the general syntax of this option, check the “Color” section
in the ffmpeg-utils manual. If the special value invert is used, the box edge color is the same as the
video with inverted luma.</p></li>
Expand Down
6 changes: 3 additions & 3 deletions examples/split_silence.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
parser.add_argument('--end-time', type=float, help='End time (seconds)')
parser.add_argument('-v', dest='verbose', action='store_true', help='Verbose mode')

silence_start_re = re.compile(' silence_start: (?P<start>[0-9]+(\.?[0-9]*))$')
silence_end_re = re.compile(' silence_end: (?P<end>[0-9]+(\.?[0-9]*)) ')
silence_start_re = re.compile(r' silence_start: (?P<start>[0-9]+(\.?[0-9]*))$')
silence_end_re = re.compile(r' silence_end: (?P<end>[0-9]+(\.?[0-9]*)) ')
total_duration_re = re.compile(
'size=[^ ]+ time=(?P<hours>[0-9]{2}):(?P<minutes>[0-9]{2}):(?P<seconds>[0-9\.]{5}) bitrate=')
r'size=[^ ]+ time=(?P<hours>[0-9]{2}):(?P<minutes>[0-9]{2}):(?P<seconds>[0-9\.]{5}) bitrate=')


def _logged_popen(cmd_line, *args, **kwargs):
Expand Down
4 changes: 2 additions & 2 deletions ffmpeg/_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def crop(stream, x, y, width, height, **kwargs):
y: The vertical position, in the input video, of the top edge of the
output video.
width: The width of the output video. Must be greater than 0.
heigth: The height of the output video. Must be greater than 0.
height: The height of the output video. Must be greater than 0.

Official documentation: `crop <https://ffmpeg.org/ffmpeg-filters.html#crop>`__
"""
Expand All @@ -213,7 +213,7 @@ def drawbox(stream, x, y, width, height, color, thickness=None, **kwargs):
It defaults to 0.
width: Specify the width of the box; if 0 interpreted as the input width. It
defaults to 0.
heigth: Specify the height of the box; if 0 interpreted as the input height. It
height: Specify the height of the box; if 0 interpreted as the input height. It
defaults to 0.
color: Specify the color of the box to write. For the general syntax of this
option, check the "Color" section in the ffmpeg-utils manual. If the
Expand Down
10 changes: 6 additions & 4 deletions ffmpeg/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from ._utils import basestring, convert_kwargs_to_cmd_line_args
from builtins import str
from functools import reduce
import collections
import copy
import operator
import subprocess
Expand All @@ -18,6 +17,11 @@
output_operator,
)

try:
from collections.abc import Iterable
except ImportError:
from collections import Iterable


class Error(Exception):
def __init__(self, cmd, stdout, stderr):
Expand Down Expand Up @@ -136,9 +140,7 @@ def _get_output_args(node, stream_name_map):
args += ['-b:a', str(kwargs.pop('audio_bitrate'))]
if 'video_size' in kwargs:
video_size = kwargs.pop('video_size')
if not isinstance(video_size, basestring) and isinstance(
video_size, collections.Iterable
):
if not isinstance(video_size, basestring) and isinstance(video_size, Iterable):
video_size = '{}x{}'.format(video_size[0], video_size[1])
args += ['-video_size', video_size]
args += convert_kwargs_to_cmd_line_args(kwargs)
Expand Down
9 changes: 6 additions & 3 deletions ffmpeg/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
from past.builtins import basestring
import hashlib
import sys
import collections


if sys.version_info.major == 2:
# noinspection PyUnresolvedReferences,PyShadowingBuiltins
str = str

try:
from collections.abc import Iterable
except ImportError:
from collections import Iterable


# `past.builtins.basestring` module can't be imported on Python3 in some environments (Ubuntu).
# This code is copy-pasted from it to avoid crashes.
Expand Down Expand Up @@ -40,7 +44,6 @@ def __new__(cls, name, this_bases, d):
class basestring(with_metaclass(BaseBaseString)):
pass


else:
# noinspection PyUnresolvedReferences,PyCompatibility
from builtins import basestring
Expand Down Expand Up @@ -93,7 +96,7 @@ def convert_kwargs_to_cmd_line_args(kwargs):
args = []
for k in sorted(kwargs.keys()):
v = kwargs[k]
if isinstance(v, collections.Iterable) and not isinstance(v, str):
if isinstance(v, Iterable) and not isinstance(v, str):
for value in v:
args.append('-{}'.format(k))
if value is not None:
Expand Down
2 changes: 1 addition & 1 deletion ffmpeg/dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def incoming_edge_map(self):
"""Provides information about all incoming edges that connect to this node.

The edge map is a dictionary that maps an ``incoming_label`` to
``(outgoing_node, outgoing_label)``. Note that implicity, ``incoming_node`` is
``(outgoing_node, outgoing_label)``. Note that implicitly, ``incoming_node`` is
``self``. See "Edges" section above.
"""
raise NotImplementedError()
Expand Down
2 changes: 1 addition & 1 deletion ffmpeg/tests/test_ffmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


def test_escape_chars():
assert ffmpeg._utils.escape_chars('a:b', ':') == 'a\:b'
assert ffmpeg._utils.escape_chars('a:b', ':') == r'a\:b'
assert ffmpeg._utils.escape_chars('a\\:b', ':\\') == 'a\\\\\\:b'
assert (
ffmpeg._utils.escape_chars('a:b,c[d]e%{}f\'g\'h\\i', '\\\':,[]%')
Expand Down
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@
setup(
name='ffmpeg-python',
packages=['ffmpeg'],
setup_requires=['pytest-runner'],
tests_require=['pytest', 'pytest-mock'],
version=version,
description='Python bindings for FFmpeg - with complex filtering support',
author='Karl Kroening',
Expand Down Expand Up @@ -94,5 +92,9 @@
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
],
)
12 changes: 11 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@
# and then run "tox" from this directory.

[tox]
envlist = py27, py34, py35, py36, py37, pypy
envlist = py27, py35, py36, py37, py38, py39, py310

[gh-actions]
python =
2.7: py27
3.5: py35
3.6: py36
3.7: py37
3.8: py38
3.9: py39
3.10: py310

[testenv]
commands = py.test -vv
Expand Down