8000 Change selector syntax from `[:a]` to `[a]`; remove `map` operator (f… · Powercoder64/ffmpeg-python@57abf6e · GitHub
[go: up one dir, main page]

Skip to content

Commit 57abf6e

Browse files
committed
Change selector syntax from [:a] to [a]; remove map operator (for now)
1 parent 6169b89 commit 57abf6e

File tree

4 files changed

+56
-186
lines changed

4 files changed

+56
-186
lines changed

ffmpeg/_ffmpeg.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
MergeOutputsNode,
1010
OutputNode,
1111
output_operator,
12-
OutputStream)
12+
)
1313

1414

1515
def input(filename, **kwargs):
@@ -68,26 +68,9 @@ def output(*streams_and_filename, **kwargs):
6868
return OutputNode(streams, output.__name__, kwargs=kwargs).stream()
6969

7070

71-
@output_operator()
72-
def map(*streams):
73-
"""Map multiple streams to the same output
74-
"""
75-
head = streams[0]
76-
tail = streams[1:]
77-
78-
if not isinstance(head, OutputStream):
79-
raise ValueError('First argument must be an output stream')
80-
81-
if not tail:
82-
return head
83-
84-
return OutputNode(head.node, tail).stream()
85-
86-
8771
__all__ = [
8872
'input',
8973
'merge_outputs',
9074
'output',
91-
'map',
9275
'overwrite_output',
9376
]

ffmpeg/dag.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,7 @@ def incoming_edge_map(self):
7676
def get_incoming_edges(downstream_node, incoming_edge_map):
7777
edges = []
7878
for downstream_label, upstream_info in incoming_edge_map.items():
79-
# `upstream_info` may contain the upstream_selector. [:2] trims it away
80-
upstream_node, upstream_label = upstream_info[:2]
81-
# Take into account the stream selector if it's present (i.e. len(upstream_info) >= 3)
82-
upstream_selector = None if len(upstream_info) < 3 else upstream_info[2]
79+
upstream_node, upstream_label, upstream_selector = upstream_info
8380
edges += [DagEdge(downstream_node, downstream_label, upstream_node, upstream_label, upstream_selector)]
8481
return edges
8582

@@ -88,10 +85,7 @@ def get_outgoing_edges(upstream_node, outgoing_edge_map):
8885
edges = []
8986
for upstream_label, downstream_infos in list(outgoing_edge_map.items()):
9087
for downstream_info in downstream_infos:
91-
# `downstream_info` may contain the downstream_selector. [:2] trims it away
92-
downstream_node, downstream_label = downstream_info[:2]
93-
# Take into account the stream selector if it's present
94-
downstream_selector = None if len(downstream_info) < 3 else downstream_info[2]
88+
downstream_node, downstream_label, downstream_selector = downstream_info
9589
edges += [DagEdge(downstream_node, downstream_label, upstream_node, upstream_label, downstream_selector)]
9690
return edges
9791

@@ -104,10 +98,8 @@ class KwargReprNode(DagNode):
10498
def __upstream_hashes(self):
10599
hashes = []
106100
for downstream_label, upstream_info in self.incoming_edge_map.items():
107-
# `upstream_info` may contain the upstream_selector. [:2] trims it away
108-
upstream_node, upstream_label = upstream_info[:2]
109-
# The stream selector is discarded when calculating the hash: the stream "as a whole" is still the same
110-
hashes += [hash(x) for x in [downstream_label, upstream_node, upstream_label]]
101+
upstream_node, upstream_label, upstream_selector = upstream_info
102+
hashes += [hash(x) for x in [downstream_label, upstream_node, upstream_label, upstream_selector]]
111103
return hashes
112104

113105
@property

ffmpeg/nodes.py

Lines changed: 34 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from __future__ import unicode_literals
2+
23
from .dag import KwargReprNode
34
from ._utils import escape_chars, get_hash_int
45
from builtins import object
5-
import os, sys
6-
import inspect
6+
import os
77

88

99
def _is_of_types(obj, types):
@@ -19,13 +19,6 @@ def _get_types_str(types):
1919
return ', '.join(['{}.{}'.format(x.__module__, x.__name__) for x in types])
2020

2121

22-
def _get_arg_count(callable):
23-
if sys.version_info.major >= 3:
24-
return len(inspect.getfullargspec(callable).args)
25-
else:
26-
return len(inspect.getargspec(callable).args)
27-
28-
2922
class Stream(object):
3023
"""Represents the outgoing edge of an upstream node; may be used to create more downstream nodes."""
3124

@@ -37,7 +30,6 @@ def __init__(self, upstream_node, upstream_label, node_types, upstream_selector=
3730
self.label = upstream_label
3831
self.selector = upstream_selector
3932

40-
4133
def __hash__(self):
4234
return get_hash_int([hash(self.node), hash(self.label)])
4335

@@ -54,14 +46,22 @@ def __repr__(self):
5446

5547
def __getitem__(self, index):
5648
"""
57-
Select a component of the stream. `stream[:X]` is analogous to `stream.node.stream(select=X)`.
58-
Please note that this can only be used to select a substream that already exist. If you want to split
59-
the stream, use the `split` filter.
60-
"""
61-
if not isinstance(index, slice) or index.start is not None:
62-
raise ValueError("Invalid syntax. Use `stream[:\'something\']`, not `stream[\'something\']`.")
49+
Select a component (audio, video) of the stream.
50+
51+
Example:
52+
53+
Process the audio and video portions of a stream independently::
6354
64-
return self.node.stream(select=index.stop)
55+
in = ffmpeg.input('in.mp4')
56+
audio = in['a'].filter_("aecho", 0.8, 0.9, 1000, 0.3)
57+
video = in['v'].hflip()
58+
ffmpeg.output(audio, video, 'out.mp4')
59+
"""
60+
if self.selector is not None:
61+
raise ValueError('Stream already has a selector: {}'.format(self))
62+
elif not isinstance(index, basestring):
63+
raise TypeError("Expected string index (e.g. 'v'); got {!r}".format(index))
64+
return self.node.stream(label=self.label, selector=index)
6565

6666

6767
def get_stream_map(stream_spec):
@@ -93,22 +93,6 @@ def get_stream_spec_nodes(stream_spec):
9393
class Node(KwargReprNode):
9494
"""Node base"""
9595

96-
@property
97-
def min_inputs(self):
98-
return self.__min_inputs
99-
100-
@property
101-
def max_inputs(self):
102-
return self.__max_inputs
103-
104-
@property
105-
def incoming_stream_types(self):
106-
return self.__incoming_stream_types
107-
108-
@property
109-
def outgoing_stream_type(self):
110-
return self.__outgoing_stream_type
111-
11296
@classmethod
11397
def __check_input_len(cls, stream_map, min_inputs, max_inputs):
11498
if min_inputs is not None and len(stream_map) < min_inputs:
@@ -130,9 +114,8 @@ def __get_incoming_edge_map(cls, stream_map):
130114
incoming_edge_map[downstream_label] = (upstream.node, upstream.label, upstream.selector)
131115
return incoming_edge_map
132116

133-
def __init_fromscratch__(self, stream_spec, name, incoming_stream_types, outgoing_stream_type, min_inputs,
134-
max_inputs, args=[],
135-
kwargs={}):
117+
def __init__(self, stream_spec, name, incoming_stream_types, outgoing_stream_type, min_inputs,
118+
max_inputs, args=[], kwargs={}):
136119
stream_map = get_stream_map(stream_spec)
137120
self.__check_input_len(stream_map, min_inputs, max_inputs)
138121
self.__check_input_types(stream_map, incoming_stream_types)
@@ -141,92 +124,21 @@ def __init_fromscratch__(self, stream_spec, name, incoming_stream_types, outgoin
141124
super(Node, self).__init__(incoming_edge_map, name, args, kwargs)
142125
self.__outgoing_stream_type = outgoing_stream_type
143126
self.__incoming_stream_types = incoming_stream_types
144-
self.__min_inputs = min_inputs
145-
self.__max_inputs = max_inputs
146-
147-
def __init_fromnode__(self, old_node, stream_spec):
148-
# Make sure old node and new node are of the same type
149-
if type(self) != type(old_node):
150-
raise TypeError('`old_node` should be of type {}'.format(self.__class__.__name__))
151-
152-
# Copy needed data from old node
153-
name = old_node.name
154-
incoming_stream_types = old_node.incoming_stream_types
155-
outgoing_stream_type = old_node.outgoing_stream_type
156-
min_inputs = old_node.min_inputs
157-
max_inputs = old_node.max_inputs
158-
prev_edges = old_node.incoming_edge_map.values()
159-
args = old_node.args
160-
kwargs = old_node.kwargs
161-
162-
# Check new stream spec - the old spec should have already been checked
163-
new_stream_map = get_stream_map(stream_spec)
164-
self.__check_input_types(new_stream_map, incoming_stream_types)
165-
166-
# Generate new edge map
167-
new_inc_edge_map = self.__get_incoming_edge_map(new_stream_map)
168-
new_edges = new_inc_edge_map.values()
169-
170-
# Rename all edges
171-
new_edge_map = dict(enumerate(list(prev_edges) + list(new_edges)))
172-
173-
# Check new length
174-
self.__check_input_len(new_edge_map, min_inputs, max_inputs)
175-
176-
super(Node, self).__init__(new_edge_map, name, args, kwargs)
177-
self.__outgoing_stream_type = outgoing_stream_type
178-
self.__incoming_stream_types = incoming_stream_types
179-
self.__min_inputs = min_inputs
180-
self.__max_inputs = max_inputs
181-
182-
# noinspection PyMissingConstructor
183-
def __init__(self, *args, **kwargs):
184-
"""
185-
If called with the following arguments, the new Node is created from scratch:
186-
- stream_spec, name, incoming_stream_types, outgoing_stream_type, min_inputs, max_inputs, args=[], kwargs={}
187-
188-
If called with the following arguments, the new node is a copy of `old_node` that includes the additional
189-
`stream_spec`:
190-
- old_node, stream_spec
191-
"""
192-
# Python doesn't support constructor overloading. This hacky code detects how we want to construct the object
193-
# based on the number of arguments and the type of the first argument, then calls the appropriate constructor
194-
# helper method
195-
196-
# "1+" is for `self`
197-
argc = 1 + len(args) + len(kwargs)
198127

199-
first_arg = None
200-
if 'old_node' in kwargs:
201-
first_arg = kwargs['old_node']
202-
elif len(args) > 0:
203-
first_arg = args[0]
204-
205-
if argc == _get_arg_count(self.__init_fromnode__) and type(first_arg) == type(self):
206-
self.__init_fromnode__(*args, **kwargs)
207-
else:
208-
if isinstance(first_arg, Node):
209-
raise ValueError(
210-
'{}.__init__() received an instance of {} as the first argument. If you want to create a '
211-
'copy of an existing node, the types must match and you must provide an additional stream_spec.'
212-
.format(self.__class__.__name__, first_arg.__class__.__name__)
213-
)
214-
self.__init_fromscratch__(*args, **kwargs)
215-
216-
def stream(self, label=None, select=None):
128+
def stream(self, label=None, selector=None):
217129
"""Create an outgoing stream originating from this node.
218130
219131
More nodes may be attached onto the outgoing stream.
220132
"""
221-
return self.__outgoing_stream_type(self, label, upstream_selector=select)
133+
return self.__outgoing_stream_type(self, label, upstream_selector=selector)
222134

223135
def __getitem__(self, item):
224136
"""Create an outgoing stream originating from this node; syntactic sugar for ``self.stream(label)``.
225137
It can also be used to apply a selector: e.g. ``node[0:'audio']`` returns a stream with label 0 and
226-
selector ``'audio'``, which is the same as ``node.stream(label=0, select='audio')``.
138+
selector ``'audio'``, which is the same as ``node.stream(label=0, selector='audio')``.
227139
"""
228140
if isinstance(item, slice):
229-
return self.stream(label=item.start, select=item.stop)
141+
return self.stream(label=item.start, selector=item.stop)
230142
else:
231143
return self.stream(label=item)
232144

@@ -241,8 +153,8 @@ def __init__(self, upstream_node, upstream_label, upstream_selector=None):
241153
class InputNode(Node):
242154
"""InputNode type"""
243155

244-
def __init_fromscratch__(self, name, args=[], kwargs={}):
245-
super(InputNode, self).__init_fromscratch__(
156+
def __init__(self, name, args=[], kwargs={}):
157+
super(InputNode, self).__init__(
246158
stream_spec=None,
247159
name=name,
248160
incoming_stream_types={},
@@ -253,18 +165,15 @@ def __init_fromscratch__(self, name, args=[], kwargs={}):
253165
kwargs=kwargs
254166
)
255167

256-
def __init_fromnode__(self, old_node, stream_spec):
257-
raise TypeError("{} can't be constructed from an existing node".format(self.__class__.__name__))
258-
259168
@property
260169
def short_repr(self):
261170
return os.path.basename(self.kwargs['filename'])
262171

263172

264173
# noinspection PyMethodOverriding
265174
class FilterNode(Node):
266-
def __init_fromscratch__(self, stream_spec, name, max_inputs=1, args=[], kwargs={}):
267-
super(FilterNode, self).__init_fromscratch__(
175+
def __init__(self, stream_spec, name, max_inputs=1, args=[], kwargs={}):
176+
super(FilterNode, self).__init__(
268177
stream_spec=stream_spec,
269178
name=name,
270179
incoming_stream_types={FilterableStream},
@@ -303,13 +212,13 @@ def _get_filter(self, outgoing_edges):
303212

304213
# noinspection PyMethodOverriding
305214
class OutputNode(Node):
306-
def __init_fromscratch__(self, stream, name, args=[], kwargs={}):
307-
super(OutputNode, self).__init_fromscratch__(
215+
def __init__(self, stream, name, args=[], kwargs={}):
216+
super(OutputNode, self).__init__(
308217
stream_spec=stream,
309218
name=name,
310219
incoming_stream_types={FilterableStream},
311220
outgoing_stream_type=OutputStream,
312-
min_inputs=0, # Allow streams to be mapped afterwards
221+
min_inputs=1,
313222
max_inputs=None,
314223
args=args,
315224
kwargs=kwargs
@@ -328,8 +237,8 @@ def __init__(self, upstream_node, upstream_label, upstream_selector=None):
328237

329238
# noinspection PyMethodOverriding
330239
class MergeOutputsNode(Node):
331-
def __init_fromscratch__(self, streams, name):
332-
super(MergeOutputsNode, self).__init_fromscratch__(
240+
def __init__(self, streams, name):
241+
super(MergeOutputsNode, self).__init__(
333242
stream_spec=streams,
334243
name=name,
335244
incoming_stream_types={OutputStream},
@@ -341,8 +250,8 @@ def __init_fromscratch__(self, streams, name):
341250

342251
# noinspection PyMethodOverriding
343252
class GlobalNode(Node):
344-
def __init_fromscratch__(self, stream, name, args=[], kwargs={}):
345-
super(GlobalNode, self).__init_fromscratch__(
253+
def __init__(self, stream, name, args=[], kwargs={}):
254+
super(GlobalNode, self).__init__(
346255
stream_spec=stream,
347256
name=name,
348257
incoming_stream_types={OutputStream},
@@ -353,9 +262,6 @@ def __init_fromscratch__(self, stream, name, args=[], kwargs={}):
353262
kwargs=kwargs
354263
)
355264

356-
def __init_fromnode__(self, old_node, stream_spec):
357-
raise TypeError("{} can't be constructed from an existing node".format(self.__class__.__name__))
358-
359265

360266
def stream_operator(stream_classes={Stream}, name=None):
361267
def decorator(func):

0 commit comments

Comments
 (0)
0