|
|
from __future__ import unicode_literals |
|
|
|
|
|
from builtins import str |
|
|
from .dag import get_outgoing_edges |
|
|
from ._run import topo_sort |
|
|
import tempfile |
|
|
|
|
|
from ffmpeg.nodes import ( |
|
|
FilterNode, |
|
|
get_stream_spec_nodes, |
|
|
InputNode, |
|
|
OutputNode, |
|
|
stream_operator, |
|
|
) |
|
|
|
|
|
|
|
|
_RIGHT_ARROW = '\u2192' |
|
|
|
|
|
|
|
|
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(stream_spec, detail=False, filename=None, pipe=False, **kwargs): |
|
|
try: |
|
|
import graphviz |
|
|
except ImportError: |
|
|
raise ImportError( |
|
|
'failed to import graphviz; please make sure graphviz is installed (e.g. `pip install ' |
|
|
'graphviz`)' |
|
|
) |
|
|
|
|
|
show_labels = kwargs.pop('show_labels', True) |
|
|
if pipe and filename is not None: |
|
|
raise ValueError('Can\'t specify both `filename` and `pipe`') |
|
|
elif not pipe and filename is None: |
|
|
filename = tempfile.mktemp() |
|
|
|
|
|
nodes = get_stream_spec_nodes(stream_spec) |
|
|
|
|
|
sorted_nodes, outgoing_edge_maps = topo_sort(nodes) |
|
|
graph = graphviz.Digraph(format='png') |
|
|
graph.attr(rankdir='LR') |
|
|
if len(list(kwargs.keys())) != 0: |
|
|
raise ValueError( |
|
|
'Invalid kwargs key(s): {}'.format(', '.join(list(kwargs.keys()))) |
|
|
) |
|
|
|
|
|
for node in sorted_nodes: |
|
|
color = _get_node_color(node) |
|
|
|
|
|
if detail: |
|
|
lines = [node.short_repr] |
|
|
lines += ['{!r}'.format(arg) for arg in node.args] |
|
|
lines += [ |
|
|
'{}={!r}'.format(key, node.kwargs[key]) for key in sorted(node.kwargs) |
|
|
] |
|
|
node_text = '\n'.join(lines) |
|
|
else: |
|
|
node_text = node.short_repr |
|
|
graph.node( |
|
|
str(hash(node)), node_text, 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 = {} |
|
|
up_label = edge.upstream_label |
|
|
down_label = edge.downstream_label |
|
|
up_selector = edge.upstream_selector |
|
|
|
|
|
if show_labels and ( |
|
|
up_label is not None |
|
|
or down_label is not None |
|
|
or up_selector is not None |
|
|
): |
|
|
if up_label is None: |
|
|
up_label = '' |
|
|
if up_selector is not None: |
|
|
up_label += ":" + up_selector |
|
|
if down_label is None: |
|
|
down_label = '' |
|
|
if up_label != '' and down_label != '': |
|
|
middle = ' {} '.format(_RIGHT_ARROW) |
|
|
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) |
|
|
|
|
|
if pipe: |
|
|
return graph.pipe() |
|
|
else: |
|
|
graph.view(filename, cleanup=True) |
|
|
return stream_spec |
|
|
|
|
|
|
|
|
__all__ = ['view'] |
|
|
|