From 241ede2271a05236613561436916dcf38597b102 Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Wed, 5 Jul 2017 04:23:35 -0600 Subject: [PATCH 1/9] #18: add initial `graph.py` --- README.md | 2 +- ffmpeg/graph.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 ffmpeg/graph.py diff --git a/README.md b/README.md index d65b8c3..86976c1 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ overlay_file = ffmpeg.input('overlay.png') ) .overlay(overlay_file.hflip()) .drawbox(50, 50, 120, 120, color='red', thickness=5) - .output(TEST_OUTPUT_FILE) + .output('out.mp4') .run() ) ``` diff --git a/ffmpeg/graph.py b/ffmpeg/graph.py new file mode 100644 index 0000000..9e220df --- /dev/null +++ b/ffmpeg/graph.py @@ -0,0 +1,47 @@ +from __future__ import unicode_literals + +from .dag import get_outgoing_edges +from ._run import topo_sort +import graphviz +import os + +from ffmpeg.nodes import ( + InputNode, + OutputNode, + FilterNode, + operator, +) + + +@operator() +def view(*downstream_nodes, **kwargs): + sorted_nodes, outgoing_edge_maps = topo_sort(downstream_nodes) + graph = graphviz.Digraph() + graph.attr(rankdir='LR') + show_labels = kwargs.pop('show_labels', False) + if len(kwargs.keys()) != 0: + raise ValueError('Invalid kwargs key(s): {}'.format(', '.join(kwargs.keys()))) + + for node in sorted_nodes: + name = node.name + if '_kwargs' in dir(node) and 'filename' in node._kwargs: + name = os.path.basename(node._kwargs['filename']) + if isinstance(node, InputNode): + color = '#99cc00' + elif isinstance(node, OutputNode): + color = '#99ccff' + elif isinstance(node, FilterNode): + color = '#ffcc00' + else: + color = None + graph.node(str(hash(node)), name, shape='box', style='filled', fillcolor=color) + outgoing_edge_map = outgoing_edge_maps.get(node, {}) + for edge in get_outgoing_edges(node, outgoing_edge_map): + kwargs = {} + if show_labels: + kwargs['label'] = '{} -> {}'.format(edge.upstream_label, edge.downstream_label) + upstream_node_id = str(hash(edge.upstream_node)) + downstream_node_id = str(hash(edge.downstream_node)) + graph.edge(upstream_node_id, downstream_node_id, **kwargs) + + graph.view('graph.png') From 37c2094a9c039e80aa4a8ce2ba38e9c9dfb3b70d Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Wed, 5 Jul 2017 22:30:00 -0600 Subject: [PATCH 2/9] Rename graph.py to _view.py; handle graphviz import errors; use tempfile --- ffmpeg/__init__.py | 3 ++- ffmpeg/{graph.py => _view.py} | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) rename ffmpeg/{graph.py => _view.py} (80%) diff --git a/ffmpeg/__init__.py b/ffmpeg/__init__.py index 1953609..9f1bed7 100644 --- a/ffmpeg/__init__.py +++ b/ffmpeg/__init__.py @@ -3,4 +3,5 @@ from . import _filters, _ffmpeg, _run from ._filters import * from ._ffmpeg import * from ._run import * -__all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ +from ._view import * +__all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ + _view.__all__ diff --git a/ffmpeg/graph.py b/ffmpeg/_view.py similarity index 80% rename from ffmpeg/graph.py rename to ffmpeg/_view.py index 9e220df..aec939c 100644 --- a/ffmpeg/graph.py +++ b/ffmpeg/_view.py @@ -2,8 +2,8 @@ from __future__ import unicode_literals from .dag import get_outgoing_edges from ._run import topo_sort -import graphviz import os +import tempfile from ffmpeg.nodes import ( InputNode, @@ -15,10 +15,20 @@ from ffmpeg.nodes import ( @operator() def view(*downstream_nodes, **kwargs): + try: + import graphviz + except ImportError: + raise ImportError('failed to import graphviz; please make sure graphviz is installed (e.g. `pip install ' + 'graphviz`)') + + filename = kwargs.pop('filename', None) + show_labels = kwargs.pop('show_labels', False) + if filename is None: + filename = tempfile.mktemp() + sorted_nodes, outgoing_edge_maps = topo_sort(downstream_nodes) graph = graphviz.Digraph() graph.attr(rankdir='LR') - show_labels = kwargs.pop('show_labels', False) if len(kwargs.keys()) != 0: raise ValueError('Invalid kwargs key(s): {}'.format(', '.join(kwargs.keys()))) @@ -44,4 +54,10 @@ def view(*downstream_nodes, **kwargs): downstream_node_id = str(hash(edge.downstream_node)) graph.edge(upstream_node_id, downstream_node_id, **kwargs) - graph.view('graph.png') + graph.view(filename) + + + +__all__ = [ + 'view', +] From 19555472029f57ce2c9caabe757425d8201c0324 Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Thu, 6 Jul 2017 02:24:40 -0600 Subject: [PATCH 3/9] #18: fix to use latest #17 changes --- ffmpeg/_view.py | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/ffmpeg/_view.py b/ffmpeg/_view.py index aec939c..78b1151 100644 --- a/ffmpeg/_view.py +++ b/ffmpeg/_view.py @@ -6,15 +6,28 @@ import os import tempfile from ffmpeg.nodes import ( + FilterNode, InputNode, OutputNode, - FilterNode, - operator, + Stream, + stream_operator, ) -@operator() -def view(*downstream_nodes, **kwargs): +def _get_node_color(node): + if isinstance(node, InputNode): + color = '#99cc00' + elif isinstance(node, OutputNode): + color = '#99ccff' + elif isinstance(node, FilterNode): + color = '#ffcc00' + else: + color = None + return color + + +@stream_operator() +def view(*streams, **kwargs): try: import graphviz except ImportError: @@ -22,11 +35,17 @@ def view(*downstream_nodes, **kwargs): 'graphviz`)') filename = kwargs.pop('filename', None) - show_labels = kwargs.pop('show_labels', False) + show_labels = kwargs.pop('show_labels', True) if filename is None: filename = tempfile.mktemp() - sorted_nodes, outgoing_edge_maps = topo_sort(downstream_nodes) + nodes = [] + for stream in streams: + if not isinstance(stream, Stream): + raise TypeError('Expected Stream; got {}'.format(type(stream))) + nodes.append(stream.node) + + sorted_nodes, outgoing_edge_maps = topo_sort(nodes) graph = graphviz.Digraph() graph.attr(rankdir='LR') if len(kwargs.keys()) != 0: @@ -36,20 +55,17 @@ def view(*downstream_nodes, **kwargs): name = node.name if '_kwargs' in dir(node) and 'filename' in node._kwargs: name = os.path.basename(node._kwargs['filename']) - if isinstance(node, InputNode): - color = '#99cc00' - elif isinstance(node, OutputNode): - color = '#99ccff' - elif isinstance(node, FilterNode): - color = '#ffcc00' - else: - color = None + color = _get_node_color(node) + graph.node(str(hash(node)), name, shape='box', style='filled', fillcolor=color) outgoing_edge_map = outgoing_edge_maps.get(node, {}) + for edge in get_outgoing_edges(node, outgoing_edge_map): kwargs = {} - if show_labels: - kwargs['label'] = '{} -> {}'.format(edge.upstream_label, edge.downstream_label) + if show_labels and (edge.upstream_label is not None or edge.downstream_label is not None): + upstream_label = edge.upstream_label if edge.upstream_label is not None else '' + downstream_label = edge.downstream_label if edge.downstream_label is not None else '' + kwargs['label'] = '{} {}'.format(upstream_label, downstream_label) upstream_node_id = str(hash(edge.upstream_node)) downstream_node_id = str(hash(edge.downstream_node)) graph.edge(upstream_node_id, downstream_node_id, **kwargs) From 543cd1b4e3efe80b1311060bec23657515a9b551 Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Thu, 6 Jul 2017 02:32:30 -0600 Subject: [PATCH 4/9] #18: improve edge labelling --- ffmpeg/_view.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ffmpeg/_view.py b/ffmpeg/_view.py index 78b1151..d613456 100644 --- a/ffmpeg/_view.py +++ b/ffmpeg/_view.py @@ -62,10 +62,18 @@ def view(*streams, **kwargs): for edge in get_outgoing_edges(node, outgoing_edge_map): kwargs = {} - if show_labels and (edge.upstream_label is not None or edge.downstream_label is not None): - upstream_label = edge.upstream_label if edge.upstream_label is not None else '' - downstream_label = edge.downstream_label if edge.downstream_label is not None else '' - kwargs['label'] = '{} {}'.format(upstream_label, downstream_label) + up_label = edge.upstream_label + down_label = edge.downstream_label + if show_labels and (up_label is not None or down_label is not None): + if up_label is None: + up_label = '' + if down_label is None: + down_label = '' + if up_label != '' and down_label != '': + middle = ' -> ' + else: + middle = '' + kwargs['label'] = '{} {} {}'.format(up_label, middle, down_label) upstream_node_id = str(hash(edge.upstream_node)) downstream_node_id = str(hash(edge.downstream_node)) graph.edge(upstream_node_id, downstream_node_id, **kwargs) From 2d512994ff094d79f0b38f8f99a79dfa61875af3 Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Thu, 6 Jul 2017 02:41:57 -0600 Subject: [PATCH 5/9] #18: use better arrow --- ffmpeg/_view.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ffmpeg/_view.py b/ffmpeg/_view.py index d613456..ac02dff 100644 --- a/ffmpeg/_view.py +++ b/ffmpeg/_view.py @@ -14,6 +14,9 @@ from ffmpeg.nodes import ( ) +_RIGHT_ARROW = '\u2192' + + def _get_node_color(node): if isinstance(node, InputNode): color = '#99cc00' @@ -70,7 +73,7 @@ def view(*streams, **kwargs): if down_label is None: down_label = '' if up_label != '' and down_label != '': - middle = ' -> ' + middle = ' {} '.format(_RIGHT_ARROW) else: middle = '' kwargs['label'] = '{} {} {}'.format(up_label, middle, down_label) From 8a761f055dd2149aaf15a3a35fc5ed3cb9d8f197 Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Thu, 6 Jul 2017 03:35:50 -0600 Subject: [PATCH 6/9] #18: have `view` operator return stream_spec --- ffmpeg/_view.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ffmpeg/_view.py b/ffmpeg/_view.py index ac02dff..607c45d 100644 --- a/ffmpeg/_view.py +++ b/ffmpeg/_view.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from builtins import str from .dag import get_outgoing_edges from ._run import topo_sort import os @@ -7,6 +8,7 @@ import tempfile from ffmpeg.nodes import ( FilterNode, + get_stream_map, InputNode, OutputNode, Stream, @@ -30,7 +32,7 @@ def _get_node_color(node): @stream_operator() -def view(*streams, **kwargs): +def view(stream_spec, **kwargs): try: import graphviz except ImportError: @@ -43,7 +45,8 @@ def view(*streams, **kwargs): filename = tempfile.mktemp() nodes = [] - for stream in streams: + stream_map = get_stream_map(stream_spec) + for stream in stream_map.values(): if not isinstance(stream, Stream): raise TypeError('Expected Stream; got {}'.format(type(stream))) nodes.append(stream.node) @@ -51,8 +54,8 @@ def view(*streams, **kwargs): sorted_nodes, outgoing_edge_maps = topo_sort(nodes) graph = graphviz.Digraph() graph.attr(rankdir='LR') - if len(kwargs.keys()) != 0: - raise ValueError('Invalid kwargs key(s): {}'.format(', '.join(kwargs.keys()))) + if len(list(kwargs.keys())) != 0: + raise ValueError('Invalid kwargs key(s): {}'.format(', '.join(list(kwargs.keys())))) for node in sorted_nodes: name = node.name @@ -83,6 +86,7 @@ def view(*streams, **kwargs): graph.view(filename) + return stream_spec __all__ = [ From 0b1259238f745b25ad1c22d9107a2ba5dd16144f Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Thu, 6 Jul 2017 03:40:56 -0600 Subject: [PATCH 7/9] #18: re-add __init__.py changes --- ffmpeg/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ffmpeg/__init__.py b/ffmpeg/__init__.py index 154d8b6..d4f48d8 100644 --- a/ffmpeg/__init__.py +++ b/ffmpeg/__init__.py @@ -4,5 +4,6 @@ from . import _filters, _ffmpeg, _run from ._filters import * from ._ffmpeg import * from ._run import * +from ._view import * -__all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ +__all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ + _view.__all__ From a8b1cb63f23ed4c3acfa73fe638ef6df486ecb0f Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Thu, 6 Jul 2017 03:41:42 -0600 Subject: [PATCH 8/9] #18: use `short_repr` in `view` --- ffmpeg/_view.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ffmpeg/_view.py b/ffmpeg/_view.py index 607c45d..cd5e14e 100644 --- a/ffmpeg/_view.py +++ b/ffmpeg/_view.py @@ -58,12 +58,9 @@ def view(stream_spec, **kwargs): raise ValueError('Invalid kwargs key(s): {}'.format(', '.join(list(kwargs.keys())))) for node in sorted_nodes: - name = node.name - if '_kwargs' in dir(node) and 'filename' in node._kwargs: - name = os.path.basename(node._kwargs['filename']) color = _get_node_color(node) - graph.node(str(hash(node)), name, shape='box', style='filled', fillcolor=color) + graph.node(str(hash(node)), node.short_repr, shape='box', style='filled', fillcolor=color) outgoing_edge_map = outgoing_edge_maps.get(node, {}) for edge in get_outgoing_edges(node, outgoing_edge_map): From 2d6b0d473013c8e3c158cdf6e0adb090a232b3ae Mon Sep 17 00:00:00 2001 From: Karl Kroening Date: Sun, 9 Jul 2017 16:00:41 -0600 Subject: [PATCH 9/9] #18: use `get_stream_spec_nodes` in `view` --- ffmpeg/_view.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ffmpeg/_view.py b/ffmpeg/_view.py index cd5e14e..cdb41b0 100644 --- a/ffmpeg/_view.py +++ b/ffmpeg/_view.py @@ -8,7 +8,7 @@ import tempfile from ffmpeg.nodes import ( FilterNode, - get_stream_map, + get_stream_spec_nodes, InputNode, OutputNode, Stream, @@ -44,12 +44,7 @@ def view(stream_spec, **kwargs): if filename is None: filename = tempfile.mktemp() - nodes = [] - stream_map = get_stream_map(stream_spec) - for stream in stream_map.values(): - if not isinstance(stream, Stream): - raise TypeError('Expected Stream; got {}'.format(type(stream))) - nodes.append(stream.node) + nodes = get_stream_spec_nodes(stream_spec) sorted_nodes, outgoing_edge_maps = topo_sort(nodes) graph = graphviz.Digraph()