1
1
from __future__ import unicode_literals
2
+
2
3
from .dag import KwargReprNode
3
4
from ._utils import escape_chars , get_hash_int
4
5
from builtins import object
5
- import os , sys
6
- import inspect
6
+ import os
7
7
8
8
9
9
def _is_of_types (obj , types ):
@@ -19,13 +19,6 @@ def _get_types_str(types):
19
19
return ', ' .join (['{}.{}' .format (x .__module__ , x .__name__ ) for x in types ])
20
20
21
21
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
-
29
22
class Stream (object ):
30
23
"""Represents the outgoing edge of an upstream node; may be used to create more downstream nodes."""
31
24
@@ -37,7 +30,6 @@ def __init__(self, upstream_node, upstream_label, node_types, upstream_selector=
37
30
self .label = upstream_label
38
31
self .selector = upstream_selector
39
32
40
-
41
33
def __hash__ (self ):
42
34
return get_hash_int ([hash (self .node ), hash (self .label )])
43
35
@@ -54,14 +46,22 @@ def __repr__(self):
54
46
55
47
def __getitem__ (self , index ):
56
48
"""
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::
63
54
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 )
65
65
66
66
67
67
def get_stream_map (stream_spec ):
@@ -93,22 +93,6 @@ def get_stream_spec_nodes(stream_spec):
93
93
class Node (KwargReprNode ):
94
94
"""Node base"""
95
95
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
-
112
96
@classmethod
113
97
def __check_input_len (cls , stream_map , min_inputs , max_inputs ):
114
98
if min_inputs is not None and len (stream_map ) < min_inputs :
@@ -130,9 +114,8 @@ def __get_incoming_edge_map(cls, stream_map):
130
114
incoming_edge_map [downstream_label ] = (upstream .node , upstream .label , upstream .selector )
131
115
return incoming_edge_map
132
116
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 = {}):
136
119
stream_map = get_stream_map (stream_spec )
137
120
self .__check_input_len (stream_map , min_inputs , max_inputs )
138
121
self .__check_input_types (stream_map , incoming_stream_types )
@@ -141,92 +124,21 @@ def __init_fromscratch__(self, stream_spec, name, incoming_stream_types, outgoin
141
124
super (Node , self ).__init__ (incoming_edge_map , name , args , kwargs )
142
125
self .__outgoing_stream_type = outgoing_stream_type
143
126
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 )
198
127
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 ):
217
129
"""Create an outgoing stream originating from this node.
218
130
219
131
More nodes may be attached onto the outgoing stream.
220
132
"""
221
- return self .__outgoing_stream_type (self , label , upstream_selector = select )
133
+ return self .__outgoing_stream_type (self , label , upstream_selector = selector )
222
134
223
135
def __getitem__ (self , item ):
224
136
"""Create an outgoing stream originating from this node; syntactic sugar for ``self.stream(label)``.
225
137
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')``.
227
139
"""
228
140
if isinstance (item , slice ):
229
- return self .stream (label = item .start , select = item .stop )
141
+ return self .stream (label = item .start , selector = item .stop )
230
142
else :
231
143
return self .stream (label = item )
232
144
@@ -241,8 +153,8 @@ def __init__(self, upstream_node, upstream_label, upstream_selector=None):
241
153
class InputNode (Node ):
242
154
"""InputNode type"""
243
155
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__ (
246
158
stream_spec = None ,
247
159
name = name ,
248
160
incoming_stream_types = {},
@@ -253,18 +165,15 @@ def __init_fromscratch__(self, name, args=[], kwargs={}):
253
165
kwargs = kwargs
254
166
)
255
167
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
-
259
168
@property
260
169
def short_repr (self ):
261
170
return os .path .basename (self .kwargs ['filename' ])
262
171
263
172
264
173
# noinspection PyMethodOverriding
265
174
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__ (
268
177
stream_spec = stream_spec ,
269
178
name = name ,
270
179
incoming_stream_types = {FilterableStream },
@@ -303,13 +212,13 @@ def _get_filter(self, outgoing_edges):
303
212
304
213
# noinspection PyMethodOverriding
305
214
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__ (
308
217
stream_spec = stream ,
309
218
name = name ,
310
219
incoming_stream_types = {FilterableStream },
311
220
outgoing_stream_type = OutputStream ,
312
- min_inputs = 0 , # Allow streams to be mapped afterwards
221
+ min_inputs = 1 ,
313
222
max_inputs = None ,
314
223
args = args ,
315
224
kwargs = kwargs
@@ -328,8 +237,8 @@ def __init__(self, upstream_node, upstream_label, upstream_selector=None):
328
237
329
238
# noinspection PyMethodOverriding
330
239
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__ (
333
242
stream_spec = streams ,
334
243
name = name ,
335
244
incoming_stream_types = {OutputStream },
@@ -341,8 +250,8 @@ def __init_fromscratch__(self, streams, name):
341
250
342
251
# noinspection PyMethodOverriding
343
252
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__ (
346
255
stream_spec = stream ,
347
256
name = name ,
348
257
incoming_stream_types = {OutputStream },
@@ -353,9 +262,6 @@ def __init_fromscratch__(self, stream, name, args=[], kwargs={}):
353
262
kwargs = kwargs
354
263
)
355
264
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
-
359
265
360
266
def stream_operator (stream_classes = {Stream }, name = None ):
361
267
def decorator (func ):
0 commit comments