-
Welcome to ffmpeg-python’s documentation!¶
+
+
-
ffmpeg-python: Python bindings for FFmpeg¶
ffmpeg-python: Python bindings for FFmpeg
--
+
-
ffmpeg.
colorchannelmixer
(parent, *args, **kwargs)¶
- FIXME
+Adjust video input frames by re-mixing color channels.
+-
ffmpeg.
concat
(*parents, **kwargs)¶
- FIXME
-
-
ffmpeg.
drawbox
(parent, x, y, width, height, color, thickness=None, **kwargs)¶
- FIXME
-
-
-
-
-
ffmpeg.
file_input
(filename)¶
- Input from a file.
-Corresponds to ffmpeg -i option.
--
-
- Args: -
- filename: Input filename -
-
-
-
-
ffmpeg.
file_output
(parent, filename)¶
- FIXME
-
-
-
-
-
ffmpeg.
fluent
(node_classes=set([<class ‘ffmpeg.nodes.Node’>]))¶
- Decorator to make function show up as an instance method on specified node classes.
--
-- - - - -Parameters: node_classes – list of node classes -
-
-
-
-
ffmpeg.
get_args
(parent)¶
- FIXME
-
-
ffmpeg.
hflip
(parent)¶
- FIXME
-
-
ffmpeg.
hue
(parent, **kwargs)¶
- FIXME
-
-
-
-
-
ffmpeg.
merge_outputs
(*parents)¶
- FIXME
-
-
ffmpeg.
overlay
(main_parent, overlay_parent, eof_action=’repeat’, **kwargs)¶
- FIXME
-
-
-
-
-
ffmpeg.
overwrite_output
(parent)¶
- FIXME
-
-
-
-
-
ffmpeg.
run
(parent, cmd=’ffmpeg’)¶
- FIXME
-
-
ffmpeg.
setpts
(parent, expr)¶
- FIXME
-
-
ffmpeg.
trim
(parent, **kwargs)¶
- FIXME
-
-
ffmpeg.
vflip
(parent)¶
- FIXME
-
-
ffmpeg.
zoompan
(parent, **kwargs)¶
- FIXME
-
-
+
-
+
ffmpeg.
file_input
(filename)¶
+
-
+
-
+
ffmpeg.
file_output
(parent, filename)¶
+
-
+
-
+
ffmpeg.
merge_outputs
(*parents)¶
+
-
+
-
+
ffmpeg.
overwrite_output
(parent)¶
+
-
+
-
+
ffmpeg.
get_args
(parent)¶
+
-
+
-
+
ffmpeg.
run
(parent, cmd=’ffmpeg’)¶
+
@@ -179,7 +145,7 @@
Table Of Contents
-
-
- Welcome to ffmpeg-python’s documentation! +
- ffmpeg-python: Python bindings for FFmpeg
- Indices and tables
diff --git a/doc/html/objects.inv b/doc/html/objects.inv
index 2cc9cb5..70a5c88 100644
Binary files a/doc/html/objects.inv and b/doc/html/objects.inv differ
diff --git a/doc/html/searchindex.js b/doc/html/searchindex.js
index e65c02f..294eda3 100644
--- a/doc/html/searchindex.js
+++ b/doc/html/searchindex.js
@@ -1 +1 @@
-Search.setIndex({docnames:["index"],envversion:52,filenames:["index.rst"],objects:{"":{ffmpeg:[0,0,0,"-"]},ffmpeg:{colorchannelmixer:[0,1,1,""],concat:[0,1,1,""],drawbox:[0,1,1,""],file_input:[0,1,1,""],file_output:[0,1,1,""],fluent:[0,1,1,""],get_args:[0,1,1,""],hflip:[0,1,1,""],hue:[0,1,1,""],merge_outputs:[0,1,1,""],overlay:[0,1,1,""],overwrite_output:[0,1,1,""],run:[0,1,1,""],setpts:[0,1,1,""],trim:[0,1,1,""],vflip:[0,1,1,""],zoompan:[0,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:function"},terms:{"class":0,"function":0,arg:0,bind:0,cmd:0,color:0,colorchannelmix:0,concat:0,correspond:0,decor:0,drawbox:0,eof_act:0,expr:0,file:0,file_input:0,file_output:0,filenam:0,fixm:0,fluent:0,from:0,get_arg:0,height:0,hflip:0,hue:0,index:0,input:0,instanc:0,kwarg:0,list:0,main_par:0,make:0,merge_output:0,method:0,modul:0,node:0,node_class:0,none:0,option:0,overlai:0,overlay_par:0,overwrite_output:0,page:0,paramet:0,parent:0,repeat:0,run:0,search:0,set:0,setpt:0,show:0,specifi:0,thick:0,trim:0,vflip:0,width:0,zoompan:0},titles:["Welcome to ffmpeg-python\u2019s documentation!"],titleterms:{document:0,ffmpeg:0,indic:0,python:0,tabl:0,welcom:0}})
\ No newline at end of file
+Search.setIndex({docnames:["index"],envversion:52,filenames:["index.rst"],objects:{"":{ffmpeg:[0,0,0,"-"]},ffmpeg:{colorchannelmixer:[0,1,1,""],concat:[0,1,1,""],drawbox:[0,1,1,""],file_input:[0,1,1,""],file_output:[0,1,1,""],get_args:[0,1,1,""],hflip:[0,1,1,""],hue:[0,1,1,""],merge_outputs:[0,1,1,""],overlay:[0,1,1,""],overwrite_output:[0,1,1,""],run:[0,1,1,""],setpts:[0,1,1,""],trim:[0,1,1,""],vflip:[0,1,1,""],zoompan:[0,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:function"},terms:{"class":[],"function":[],adjust:0,arg:0,base:[],bind:[],channel:0,cmd:0,color:0,colorchannelmix:0,concat:0,correspond:[],decor:[],drawbox:0,eof_act:0,expr:0,file:[],file_input:0,file_output:0,filenam:0,filter:0,filternod:[],fixm:[],fluent:[],frame:0,from:[],get_arg:0,globalnod:[],height:0,hflip:0,hue:0,index:0,input:0,inputnod:[],instanc:[],kwarg:0,list:[],main_par:0,make:[],merge_output:0,method:[],mix:0,modul:0,name:[],node:[],node_class:[],none:0,object:[],oper:[],option:[],outputnod:[],overlai:0,overlay_par:0,overwrite_output:0,page:0,paramet:[],parent:0,repeat:0,run:0,search:0,set:[],setpt:0,show:[],specifi:[],thick:0,trim:0,vflip:0,video:0,width:0,zoompan:0},titles:["ffmpeg-python: Python bindings for FFmpeg"],titleterms:{bind:0,document:[],ffmpeg:0,indic:0,python:0,tabl:0,welcom:[]}})
\ No newline at end of file
diff --git a/ffmpeg/__init__.py b/ffmpeg/__init__.py
old mode 100755
new mode 100644
index 241c828..ff5c064
--- a/ffmpeg/__init__.py
+++ b/ffmpeg/__init__.py
@@ -1,234 +1,5 @@
-#!./venv/bin/python
-
-import hashlib
-import json
-import operator as _operator
-import subprocess
-
-
-class Node(object):
- def __init__(self, parents, name, *args, **kwargs):
- parent_hashes = [parent._hash for parent in parents]
- assert len(parent_hashes) == len(set(parent_hashes)), 'Same node cannot be included as parent multiple times'
- self._parents = parents
- self._name = name
- self._args = args
- self._kwargs = kwargs
- self._update_hash()
-
- @classmethod
- def _add_operator(cls, func):
- setattr(cls, func.__name__, func)
-
- def __repr__(self):
- formatted_props = ['{}'.format(arg) for arg in self._args]
- formatted_props += ['{}={!r}'.format(key, self._kwargs[key]) for key in sorted(self._kwargs)]
- return '{}({})'.format(self._name, ','.join(formatted_props))
-
- def __eq__(self, other):
- return self._hash == other._hash
-
- def _update_hash(self):
- props = {'args': self._args, 'kwargs': self._kwargs}
- my_hash = hashlib.md5(json.dumps(props, sort_keys=True)).hexdigest()
- parent_hashes = [parent._hash for parent in self._parents]
- hashes = parent_hashes + [my_hash]
- self._hash = hashlib.md5(','.join(hashes)).hexdigest()
-
-
-class InputNode(Node):
- def __init__(self, name, *args, **kwargs):
- super(InputNode, self).__init__(parents=[], name=name, *args, **kwargs)
-
-
-class FilterNode(Node):
- def _get_filter(self):
- params_text = self._name
- arg_params = ['{}'.format(arg) for arg in self._args]
- kwarg_params = ['{}={}'.format(k, self._kwargs[k]) for k in sorted(self._kwargs)]
- params = arg_params + kwarg_params
- if params:
- params_text += '={}'.format(':'.join(params))
- return params_text
-
-
-class OutputNode(Node):
- pass
-
-
-class GlobalNode(Node):
- def __init__(self, parent, name, *args, **kwargs):
- assert isinstance(parent, OutputNode), 'Global nodes can only be attached after output nodes'
- super(GlobalNode, self).__init__([parent], name, *args, **kwargs)
-
-
-def operator(node_classes={Node}):
- def decorator(func):
- [node_class._add_operator(func) for node_class in node_classes]
- return func
- return decorator
-
-
-def file_input(filename):
- return InputNode(file_input.__name__, filename=filename)
-
-
-@operator()
-def setpts(parent, expr):
- return FilterNode([parent], setpts.__name__, expr)
-
-
-@operator()
-def trim(parent, **kwargs):
- return FilterNode([parent], trim.__name__, **kwargs)
-
-
-@operator()
-def overlay(main_parent, overlay_parent, eof_action='repeat', **kwargs):
- kwargs['eof_action'] = eof_action
- return FilterNode([main_parent, overlay_parent], overlay.__name__, **kwargs)
-
-
-@operator()
-def hflip(parent):
- return FilterNode([parent], hflip.__name__)
-
-
-@operator()
-def vflip(parent):
- return FilterNode([parent], vflip.__name__)
-
-
-@operator()
-def drawbox(parent, x, y, width, height, color, thickness=None, **kwargs):
- if thickness:
- kwargs['t'] = thickness
- return FilterNode([parent], drawbox.__name__, x, y, width, height, color, **kwargs)
-
-
-@operator()
-def concat(*parents, **kwargs):
- kwargs['n'] = len(parents)
- return FilterNode(parents, concat.__name__, **kwargs)
-
-
-@operator()
-def zoompan(parent, **kwargs):
- return FilterNode([parent], zoompan.__name__, **kwargs)
-
-
-@operator()
-def hue(parent, **kwargs):
- return FilterNode([parent], hue.__name__, **kwargs)
-
-
-@operator()
-def colorchannelmixer(parent, *args, **kwargs):
- return FilterNode([parent], colorchannelmixer.__name__, **kwargs)
-
-
-@operator(node_classes={OutputNode, GlobalNode})
-def overwrite_output(parent):
- return GlobalNode(parent, overwrite_output.__name__)
-
-
-@operator(node_classes={OutputNode})
-def merge_outputs(*parents):
- return OutputNode(parents, merge_outputs.__name__)
-
-
-@operator(node_classes={InputNode, FilterNode})
-def file_output(parent, filename):
- return OutputNode([parent], file_output.__name__, filename=filename)
-
-
-def _get_stream_name(name):
- return '[{}]'.format(name)
-
-
-def _get_input_args(input_node):
- if input_node._name == file_input.__name__:
- args = ['-i', input_node._kwargs['filename']]
- else:
- assert False, 'Unsupported input node: {}'.format(input_node)
- return args
-
-
-def _topo_sort(start_node):
- marked_nodes = []
- sorted_nodes = []
- child_map = {}
- def visit(node, child):
- assert node not in marked_nodes, 'Graph is not a DAG'
- if child is not None:
- if node not in child_map:
- child_map[node] = []
- child_map[node].append(child)
- if node not in sorted_nodes:
- marked_nodes.append(node)
- [visit(parent, node) for parent in node._parents]
- marked_nodes.remove(node)
- sorted_nodes.append(node)
- unmarked_nodes = [start_node]
- while unmarked_nodes:
- visit(unmarked_nodes.pop(), None)
- return sorted_nodes, child_map
-
-
-def _get_filter_spec(i, node, stream_name_map):
- stream_name = _get_stream_name('v{}'.format(i))
- stream_name_map[node] = stream_name
- inputs = [stream_name_map[parent] for parent in node._parents]
- filter_spec = '{}{}{}'.format(''.join(inputs), node._get_filter(), stream_name)
- return filter_spec
-
-
-def _get_filter_arg(filter_nodes, stream_name_map):
- filter_specs = [_get_filter_spec(i, node, stream_name_map) for i, node in enumerate(filter_nodes)]
- return ';'.join(filter_specs)
-
-
-def _get_global_args(node):
- if node._name == overwrite_output.__name__:
- return ['-y']
- else:
- assert False, 'Unsupported global node: {}'.format(node)
-
-
-def _get_output_args(node, stream_name_map):
- args = []
- if node._name != merge_outputs.__name__:
- stream_name = stream_name_map[node._parents[0]]
- if stream_name != '[0]':
- args += ['-map', stream_name]
- if node._name == file_output.__name__:
- args += [node._kwargs['filename']]
- else:
- assert False, 'Unsupported output node: {}'.format(node)
- return args
-
-
-@operator(node_classes={OutputNode, GlobalNode})
-def get_args(parent):
- args = []
- # TODO: group nodes together, e.g. `-i somefile -r somerate`.
- sorted_nodes, child_map = _topo_sort(parent)
- input_nodes = [node for node in sorted_nodes if isinstance(node, InputNode)]
- output_nodes = [node for node in sorted_nodes if isinstance(node, OutputNode) and not
- isinstance(node, GlobalNode)]
- global_nodes = [node for node in sorted_nodes if isinstance(node, GlobalNode)]
- filter_nodes = [node for node in sorted_nodes if node not in (input_nodes + output_nodes + global_nodes)]
- stream_name_map = {node: _get_stream_name(i) for i, node in enumerate(input_nodes)}
- filter_arg = _get_filter_arg(filter_nodes, stream_name_map)
- args += reduce(_operator.add, [_get_input_args(node) for node in input_nodes])
- if filter_arg:
- args += ['-filter_complex', filter_arg]
- args += reduce(_operator.add, [_get_output_args(node, stream_name_map) for node in output_nodes])
- args += reduce(_operator.add, [_get_global_args(node) for node in global_nodes], [])
- return args
-
-
-@operator(node_classes={OutputNode, GlobalNode})
-def run(parent, cmd='ffmpeg'):
- args = [cmd] + parent.get_args()
- subprocess.check_call(args)
+from . import _filters, _ffmpeg, _run
+from ._filters import *
+from ._ffmpeg import *
+from ._run import *
+__all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__
diff --git a/ffmpeg/_ffmpeg.py b/ffmpeg/_ffmpeg.py
new file mode 100644
index 0000000..fe7c8e4
--- /dev/null
+++ b/ffmpeg/_ffmpeg.py
@@ -0,0 +1,34 @@
+from .nodes import (
+ FilterNode,
+ GlobalNode,
+ InputNode,
+ operator,
+ OutputNode,
+)
+
+
+def file_input(filename):
+ return InputNode(file_input.__name__, filename=filename)
+
+
+@operator(node_classes={OutputNode, GlobalNode})
+def overwrite_output(parent):
+ return GlobalNode(parent, overwrite_output.__name__)
+
+
+@operator(node_classes={OutputNode})
+def merge_outputs(*parents):
+ return OutputNode(parents, merge_outputs.__name__)
+
+
+@operator(node_classes={InputNode, FilterNode})
+def file_output(parent, filename):
+ return OutputNode([parent], file_output.__name__, filename=filename)
+
+
+__all__ = [
+ 'file_input',
+ 'file_output',
+ 'merge_outputs',
+ 'overwrite_output',
+]
diff --git a/ffmpeg/_filters.py b/ffmpeg/_filters.py
new file mode 100644
index 0000000..437ab8b
--- /dev/null
+++ b/ffmpeg/_filters.py
@@ -0,0 +1,79 @@
+from .nodes import (
+ FilterNode,
+ operator,
+)
+
+
+@operator()
+def setpts(parent, expr):
+ return FilterNode([parent], setpts.__name__, expr)
+
+
+@operator()
+def trim(parent, **kwargs):
+ return FilterNode([parent], trim.__name__, **kwargs)
+
+
+@operator()
+def overlay(main_parent, overlay_parent, eof_action='repeat', **kwargs):
+ kwargs['eof_action'] = eof_action
+ return FilterNode([main_parent, overlay_parent], overlay.__name__, **kwargs)
+
+
+@operator()
+def hflip(parent):
+ return FilterNode([parent], hflip.__name__)
+
+
+@operator()
+def vflip(parent):
+ return FilterNode([parent], vflip.__name__)
+
+
+@operator()
+def drawbox(parent, x, y, width, height, color, thickness=None, **kwargs):
+ if thickness:
+ kwargs['t'] = thickness
+ return FilterNode([parent], drawbox.__name__, x, y, width, height, color, **kwargs)
+
+
+@operator()
+def concat(*parents, **kwargs):
+ kwargs['n'] = len(parents)
+ return FilterNode(parents, concat.__name__, **kwargs)
+
+
+@operator()
+def zoompan(parent, **kwargs):
+ return FilterNode([parent], zoompan.__name__, **kwargs)
+
+
+@operator()
+def hue(parent, **kwargs):
+ return FilterNode([parent], hue.__name__, **kwargs)
+
+
+@operator()
+def colorchannelmixer(parent, *args, **kwargs):
+ """Adjust video input frames by re-mixing color channels.
+
+ `FFmpeg colorchannelmixer filter`_
+
+ .. _FFmpeg colorchannelmixer filter:
+ https://ffmpeg.org/ffmpeg-filters.html#toc-colorchannelmixer
+ """
+ return FilterNode([parent], colorchannelmixer.__name__, **kwargs)
+
+
+__all__ = [
+ 'colorchannelmixer',
+ 'concat',
+ 'drawbox',
+ 'hflip',
+ 'hue',
+ 'overlay',
+ 'setpts',
+ 'trim',
+ 'vflip',
+ 'zoompan',
+]
diff --git a/ffmpeg/_run.py b/ffmpeg/_run.py
new file mode 100644
index 0000000..501f1af
--- /dev/null
+++ b/ffmpeg/_run.py
@@ -0,0 +1,112 @@
+import operator as _operator
+import subprocess as _subprocess
+
+from ._ffmpeg import (
+ file_input,
+ file_output,
+ merge_outputs,
+ overwrite_output,
+)
+from .nodes import (
+ GlobalNode,
+ InputNode,
+ operator,
+ OutputNode,
+)
+
+def _get_stream_name(name):
+ return '[{}]'.format(name)
+
+
+def _get_input_args(input_node):
+ if input_node._name == file_input.__name__:
+ args = ['-i', input_node._kwargs['filename']]
+ else:
+ assert False, 'Unsupported input node: {}'.format(input_node)
+ return args
+
+
+def _topo_sort(start_node):
+ marked_nodes = []
+ sorted_nodes = []
+ child_map = {}
+ def visit(node, child):
+ assert node not in marked_nodes, 'Graph is not a DAG'
+ if child is not None:
+ if node not in child_map:
+ child_map[node] = []
+ child_map[node].append(child)
+ if node not in sorted_nodes:
+ marked_nodes.append(node)
+ [visit(parent, node) for parent in node._parents]
+ marked_nodes.remove(node)
+ sorted_nodes.append(node)
+ unmarked_nodes = [start_node]
+ while unmarked_nodes:
+ visit(unmarked_nodes.pop(), None)
+ return sorted_nodes, child_map
+
+
+def _get_filter_spec(i, node, stream_name_map):
+ stream_name = _get_stream_name('v{}'.format(i))
+ stream_name_map[node] = stream_name
+ inputs = [stream_name_map[parent] for parent in node._parents]
+ filter_spec = '{}{}{}'.format(''.join(inputs), node._get_filter(), stream_name)
+ return filter_spec
+
+
+def _get_filter_arg(filter_nodes, stream_name_map):
+ filter_specs = [_get_filter_spec(i, node, stream_name_map) for i, node in enumerate(filter_nodes)]
+ return ';'.join(filter_specs)
+
+
+def _get_global_args(node):
+ if node._name == overwrite_output.__name__:
+ return ['-y']
+ else:
+ assert False, 'Unsupported global node: {}'.format(node)
+
+
+def _get_output_args(node, stream_name_map):
+ args = []
+ if node._name != merge_outputs.__name__:
+ stream_name = stream_name_map[node._parents[0]]
+ if stream_name != '[0]':
+ args += ['-map', stream_name]
+ if node._name == file_output.__name__:
+ args += [node._kwargs['filename']]
+ else:
+ assert False, 'Unsupported output node: {}'.format(node)
+ return args
+
+
+@operator(node_classes={OutputNode, GlobalNode})
+def get_args(parent):
+ args = []
+ # TODO: group nodes together, e.g. `-i somefile -r somerate`.
+ sorted_nodes, child_map = _topo_sort(parent)
+ input_nodes = [node for node in sorted_nodes if isinstance(node, InputNode)]
+ output_nodes = [node for node in sorted_nodes if isinstance(node, OutputNode) and not
+ isinstance(node, GlobalNode)]
+ global_nodes = [node for node in sorted_nodes if isinstance(node, GlobalNode)]
+ filter_nodes = [node for node in sorted_nodes if node not in (input_nodes + output_nodes + global_nodes)]
+ stream_name_map = {node: _get_stream_name(i) for i, node in enumerate(input_nodes)}
+ filter_arg = _get_filter_arg(filter_nodes, stream_name_map)
+ args += reduce(_operator.add, [_get_input_args(node) for node in input_nodes])
+ if filter_arg:
+ args += ['-filter_complex', filter_arg]
+ args += reduce(_operator.add, [_get_output_args(node, stream_name_map) for node in output_nodes])
+ args += reduce(_operator.add, [_get_global_args(node) for node in global_nodes], [])
+ return args
+
+
+@operator(node_classes={OutputNode, GlobalNode})
+def run(parent, cmd='ffmpeg'):
+ args = [cmd] + parent.get_args()
+ _subprocess.check_call(args)
+
+
+__all__ = [
+ 'get_args',
+ 'run',
+]
diff --git a/ffmpeg/nodes.py b/ffmpeg/nodes.py
new file mode 100644
index 0000000..61c9ff0
--- /dev/null
+++ b/ffmpeg/nodes.py
@@ -0,0 +1,66 @@
+import hashlib
+import json
+
+
+class Node(object):
+ """Node base"""
+ def __init__(self, parents, name, *args, **kwargs):
+ parent_hashes = [parent._hash for parent in parents]
+ assert len(parent_hashes) == len(set(parent_hashes)), 'Same node cannot be included as parent multiple times'
+ self._parents = parents
+ self._name = name
+ self._args = args
+ self._kwargs = kwargs
+ self._update_hash()
+
+ def __repr__(self):
+ formatted_props = ['{}'.format(arg) for arg in self._args]
+ formatted_props += ['{}={!r}'.format(key, self._kwargs[key]) for key in sorted(self._kwargs)]
+ return '{}({})'.format(self._name, ','.join(formatted_props))
+
+ def __eq__(self, other):
+ return self._hash == other._hash
+
+ def _update_hash(self):
+ props = {'args': self._args, 'kwargs': self._kwargs}
+ my_hash = hashlib.md5(json.dumps(props, sort_keys=True)).hexdigest()
+ parent_hashes = [parent._hash for parent in self._parents]
+ hashes = parent_hashes + [my_hash]
+ self._hash = hashlib.md5(','.join(hashes)).hexdigest()
+
+
+class InputNode(Node):
+ """InputNode type"""
+ def __init__(self, name, *args, **kwargs):
+ super(InputNode, self).__init__(parents=[], name=name, *args, **kwargs)
+
+
+class FilterNode(Node):
+ """FilterNode"""
+ def _get_filter(self):
+ params_text = self._name
+ arg_params = ['{}'.format(arg) for arg in self._args]
+ kwarg_params = ['{}={}'.format(k, self._kwargs[k]) for k in sorted(self._kwargs)]
+ params = arg_params + kwarg_params
+ if params:
+ params_text += '={}'.format(':'.join(params))
+ return params_text
+
+
+class OutputNode(Node):
+ """OutputNode"""
+ pass
+
+
+class GlobalNode(Node):
+ def __init__(self, parent, name, *args, **kwargs):
+ assert isinstance(parent, OutputNode), 'Global nodes can only be attached after output nodes'
+ super(GlobalNode, self).__init__([parent], name, *args, **kwargs)
+
+
+def operator(node_classes={Node}, name=None):
+ def decorator(func):
+ func_name = name or func.__name__
+ [setattr(node_class, func_name, func) for node_class in node_classes]
+ return func
+ return decorator
diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py
index da1531d..2a6fc58 100644
--- a/ffmpeg/tests/test_ffmpeg.py
+++ b/ffmpeg/tests/test_ffmpeg.py
@@ -1,4 +1,3 @@
-
import ffmpeg
import os
import subprocess