yta-video-opengl 0.0.24__tar.gz → 0.0.25__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/PKG-INFO +1 -1
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/pyproject.toml +1 -1
- yta_video_opengl-0.0.24/src/yta_video_opengl/editor.py → yta_video_opengl-0.0.25/src/yta_video_opengl/effects.py +131 -82
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/nodes/__init__.py +26 -1
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/nodes/audio/__init__.py +0 -1
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/nodes/video/__init__.py +4 -0
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/tests.py +2 -2
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/LICENSE +0 -0
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/README.md +0 -0
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/__init__.py +0 -0
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/nodes/video/opengl/__init__.py +0 -0
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/nodes/video/opengl/experimental.py +0 -0
- {yta_video_opengl-0.0.24 → yta_video_opengl-0.0.25}/src/yta_video_opengl/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
from yta_video_opengl.nodes.video.opengl import WavingNode
|
2
2
|
from yta_video_opengl.nodes.audio import ChorusNode
|
3
|
-
from yta_video_opengl.nodes import
|
3
|
+
from yta_video_opengl.nodes import TimedNode
|
4
4
|
from yta_validation.parameter import ParameterValidator
|
5
5
|
from yta_programming.decorators import singleton_old
|
6
6
|
from typing import Union
|
@@ -20,9 +20,9 @@ class _AudioEffects:
|
|
20
20
|
|
21
21
|
def __init__(
|
22
22
|
self,
|
23
|
-
effects: '
|
23
|
+
effects: 'Effects'
|
24
24
|
):
|
25
|
-
self._effects:
|
25
|
+
self._effects: Effects = effects
|
26
26
|
"""
|
27
27
|
The parent instance that includes this
|
28
28
|
class instance as a property.
|
@@ -66,9 +66,9 @@ class _VideoEffects:
|
|
66
66
|
|
67
67
|
def __init__(
|
68
68
|
self,
|
69
|
-
effects: '
|
69
|
+
effects: 'Effects'
|
70
70
|
):
|
71
|
-
self._effects:
|
71
|
+
self._effects: Effects = effects
|
72
72
|
"""
|
73
73
|
The parent instance that includes this
|
74
74
|
class instance as a property.
|
@@ -101,7 +101,7 @@ class _VideoEffects:
|
|
101
101
|
"""
|
102
102
|
return _create_node(
|
103
103
|
WavingNode(
|
104
|
-
context = self._effects.
|
104
|
+
context = self._effects.opengl_context,
|
105
105
|
size = size,
|
106
106
|
amplitude = amplitude,
|
107
107
|
frequency = frequency,
|
@@ -114,45 +114,8 @@ class _VideoEffects:
|
|
114
114
|
# TODO: Include definitive and tested video
|
115
115
|
# effects here below
|
116
116
|
|
117
|
-
class _Effects:
|
118
|
-
"""
|
119
|
-
*For internal use only*
|
120
|
-
|
121
|
-
Class to be used within the OpenglEditor
|
122
|
-
as a property to simplify the access to
|
123
|
-
the effect nodes and also to have the
|
124
|
-
single context always available through
|
125
|
-
the OpenglEditor instance that is a
|
126
|
-
singleton one.
|
127
|
-
|
128
|
-
Even though we can have more effects,
|
129
|
-
this class is also the way we expose only
|
130
|
-
the ones we actually want to expose to
|
131
|
-
the user.
|
132
|
-
"""
|
133
|
-
|
134
|
-
def __init__(
|
135
|
-
self,
|
136
|
-
opengl_editor: 'OpenglEditor'
|
137
|
-
):
|
138
|
-
self._opengl_editor: OpenglEditor = opengl_editor
|
139
|
-
"""
|
140
|
-
The parent instance that includes this
|
141
|
-
class instance as a property.
|
142
|
-
"""
|
143
|
-
self.audio: _AudioEffects = _AudioEffects(self)
|
144
|
-
"""
|
145
|
-
Shortcut to the audio effects that are
|
146
|
-
available.
|
147
|
-
"""
|
148
|
-
self.video: _VideoEffects = _VideoEffects(self)
|
149
|
-
"""
|
150
|
-
Shortcut to the video effects that are
|
151
|
-
available.
|
152
|
-
"""
|
153
|
-
|
154
117
|
@singleton_old
|
155
|
-
class
|
118
|
+
class Effects:
|
156
119
|
"""
|
157
120
|
Singleton instance.
|
158
121
|
|
@@ -163,6 +126,15 @@ class OpenglEditor:
|
|
163
126
|
the nodes we have available for the
|
164
127
|
user.
|
165
128
|
|
129
|
+
This class is to simplify the access to
|
130
|
+
the effect nodes and also to have the
|
131
|
+
single context always available.
|
132
|
+
|
133
|
+
Even though we can have more effects,
|
134
|
+
this class is also the way we expose only
|
135
|
+
the ones we actually want to expose to
|
136
|
+
the user.
|
137
|
+
|
166
138
|
The GPU will make the calculations in
|
167
139
|
parallel by itself, so we can handle a
|
168
140
|
single context to make the nodes share
|
@@ -172,32 +144,33 @@ class OpenglEditor:
|
|
172
144
|
def __init__(
|
173
145
|
self
|
174
146
|
):
|
175
|
-
self.
|
147
|
+
self.opengl_context = moderngl.create_context(standalone = True)
|
176
148
|
"""
|
177
|
-
The context that will be shared
|
178
|
-
the nodes.
|
149
|
+
The opengl context that will be shared
|
150
|
+
by all the opengl nodes.
|
179
151
|
"""
|
180
|
-
self.
|
152
|
+
self.audio: _AudioEffects = _AudioEffects(self)
|
181
153
|
"""
|
182
|
-
Shortcut to the effects
|
154
|
+
Shortcut to the audio effects that are
|
155
|
+
available.
|
156
|
+
"""
|
157
|
+
self.video: _VideoEffects = _VideoEffects(self)
|
158
|
+
"""
|
159
|
+
Shortcut to the video effects that are
|
160
|
+
available.
|
183
161
|
"""
|
184
|
-
# TODO: I should do something like
|
185
|
-
# editor.effects.waving_node() to create
|
186
|
-
# an instance of that effect node
|
187
|
-
|
188
162
|
|
189
163
|
def _create_node(
|
190
164
|
node: Union['_AudioNode', '_VideoNode'],
|
191
165
|
start: Union[int, float, 'Fraction'],
|
192
166
|
end: Union[int, float, 'Fraction', None]
|
193
167
|
):
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
|
198
|
-
ParameterValidator.validate_mandatory_subclass_of('node', node, ['_AudioNode', '_VideoNode'])
|
168
|
+
# We could be other classes in the middle,
|
169
|
+
# because an OpenglNode inherits from
|
170
|
+
# other class
|
171
|
+
ParameterValidator.validate_mandatory_subclass_of('node', node, ['_AudioNode', '_VideoNode', 'OpenglNodeBase'])
|
199
172
|
|
200
|
-
# We have to create a
|
173
|
+
# We have to create a node wrapper with the
|
201
174
|
# time range in which it has to be applied
|
202
175
|
# to all the frames.
|
203
176
|
return TimedNode(
|
@@ -215,6 +188,26 @@ class _EffectStacked:
|
|
215
188
|
and lower when higher value.
|
216
189
|
"""
|
217
190
|
|
191
|
+
@property
|
192
|
+
def is_audio_effect(
|
193
|
+
self
|
194
|
+
) -> bool:
|
195
|
+
"""
|
196
|
+
Flag to indicate if it is an audio effect
|
197
|
+
or not.
|
198
|
+
"""
|
199
|
+
return self.effect.is_audio_node
|
200
|
+
|
201
|
+
@property
|
202
|
+
def is_video_effect(
|
203
|
+
self
|
204
|
+
) -> bool:
|
205
|
+
"""
|
206
|
+
Flag to indicate if it is a video effect
|
207
|
+
or not.
|
208
|
+
"""
|
209
|
+
return self.effect.is_video_node
|
210
|
+
|
218
211
|
def __init__(
|
219
212
|
self,
|
220
213
|
effect: TimedNode,
|
@@ -243,34 +236,66 @@ class EffectsStack:
|
|
243
236
|
"""
|
244
237
|
|
245
238
|
@property
|
246
|
-
def
|
239
|
+
def video_effects(
|
247
240
|
self
|
248
241
|
) -> list[_EffectStacked]:
|
249
242
|
"""
|
250
|
-
The effects but ordered
|
251
|
-
time moment.
|
243
|
+
The video effects but ordered by 'priority'
|
244
|
+
and 'start' time moment.
|
252
245
|
"""
|
253
|
-
return sorted(
|
246
|
+
return sorted(
|
247
|
+
[
|
248
|
+
effect
|
249
|
+
for effect in self._effects
|
250
|
+
if effect.is_video_effect
|
251
|
+
],
|
252
|
+
key = lambda effect: (effect.priority, effect.effect.start)
|
253
|
+
)
|
254
254
|
|
255
255
|
@property
|
256
|
-
def
|
256
|
+
def audio_effects(
|
257
257
|
self
|
258
|
-
) -> _EffectStacked:
|
258
|
+
) -> list[_EffectStacked]:
|
259
259
|
"""
|
260
|
-
The
|
261
|
-
|
260
|
+
The audio effects but ordered by 'priority'
|
261
|
+
and 'start' time moment.
|
262
262
|
"""
|
263
|
-
return
|
263
|
+
return sorted(
|
264
|
+
[
|
265
|
+
effect
|
266
|
+
for effect in self._effects
|
267
|
+
if effect.is_audio_effect
|
268
|
+
],
|
269
|
+
key = lambda effect: (effect.priority, effect.effect.start)
|
270
|
+
)
|
271
|
+
|
272
|
+
@property
|
273
|
+
def lowest_audio_priority(
|
274
|
+
self
|
275
|
+
) -> int:
|
276
|
+
"""
|
277
|
+
The priority of the audio effect with the
|
278
|
+
lowest one, or 0 if no audio effects.
|
279
|
+
"""
|
280
|
+
return min(
|
281
|
+
self.audio_effects,
|
282
|
+
key = lambda effect: effect.priority,
|
283
|
+
default = 0
|
284
|
+
)
|
264
285
|
|
265
286
|
@property
|
266
|
-
def
|
287
|
+
def lowest_video_priority(
|
267
288
|
self
|
268
|
-
) ->
|
289
|
+
) -> int:
|
269
290
|
"""
|
270
|
-
The effect with the
|
271
|
-
|
291
|
+
The priority of the video effect with the
|
292
|
+
lowest one, or 0 if no video effects.
|
272
293
|
"""
|
273
|
-
return
|
294
|
+
return min(
|
295
|
+
self.video_effects,
|
296
|
+
key = lambda effect: effect.priority,
|
297
|
+
default = 0
|
298
|
+
)
|
274
299
|
|
275
300
|
def __init__(
|
276
301
|
self
|
@@ -281,20 +306,35 @@ class EffectsStack:
|
|
281
306
|
have been added to this stack, unordered.
|
282
307
|
"""
|
283
308
|
|
284
|
-
def
|
309
|
+
def get_audio_effects_for_t(
|
310
|
+
self,
|
311
|
+
t: Union[int, float, 'Fraction']
|
312
|
+
) -> list[TimedNode]:
|
313
|
+
"""
|
314
|
+
Get the audio effects, ordered by priority
|
315
|
+
and the 'start' field, that must be applied
|
316
|
+
within the 't' time moment provided because
|
317
|
+
it is within the [start, end) time range.
|
318
|
+
"""
|
319
|
+
return [
|
320
|
+
effect.effect
|
321
|
+
for effect in self.audio_effects
|
322
|
+
if effect.effect.is_within_time(t)
|
323
|
+
]
|
324
|
+
|
325
|
+
def get_video_effects_for_t(
|
285
326
|
self,
|
286
327
|
t: Union[int, float, 'Fraction']
|
287
328
|
) -> list[TimedNode]:
|
288
329
|
"""
|
289
|
-
Get the effects, ordered by priority
|
290
|
-
and the 'start' field, that must be
|
291
|
-
|
292
|
-
|
293
|
-
[start, end) time range.
|
330
|
+
Get the video effects, ordered by priority
|
331
|
+
and the 'start' field, that must be applied
|
332
|
+
within the 't' time moment provided because
|
333
|
+
it is within the [start, end) time range.
|
294
334
|
"""
|
295
335
|
return [
|
296
336
|
effect.effect
|
297
|
-
for effect in self.
|
337
|
+
for effect in self.video_effects
|
298
338
|
if effect.effect.is_within_time(t)
|
299
339
|
]
|
300
340
|
|
@@ -307,6 +347,7 @@ class EffectsStack:
|
|
307
347
|
Add the provided 'effect' to the stack.
|
308
348
|
"""
|
309
349
|
ParameterValidator.validate_mandatory_instance_of('effect', effect, TimedNode)
|
350
|
+
ParameterValidator.validate_positive_int('priority', priority, do_include_zero = True)
|
310
351
|
|
311
352
|
# TODO: What about the same effect added
|
312
353
|
# twice during the same time range? Can we
|
@@ -318,8 +359,16 @@ class EffectsStack:
|
|
318
359
|
# Should we let some effects have the same
|
319
360
|
# priority (?)
|
320
361
|
priority = (
|
321
|
-
self.
|
322
|
-
if
|
362
|
+
self.lowest_audio_priority + 1
|
363
|
+
if (
|
364
|
+
priority is None and
|
365
|
+
effect.is_audio_node
|
366
|
+
) else
|
367
|
+
self.lowest_video_priority + 1
|
368
|
+
if (
|
369
|
+
priority is None and
|
370
|
+
effect.is_video_node
|
371
|
+
) else
|
323
372
|
priority
|
324
373
|
)
|
325
374
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from yta_video_opengl.nodes.video import _VideoNode
|
2
2
|
from yta_video_opengl.nodes.audio import _AudioNode
|
3
3
|
from yta_validation.parameter import ParameterValidator
|
4
|
+
from yta_validation import PythonValidator
|
4
5
|
from typing import Union
|
5
6
|
|
6
7
|
import moderngl
|
@@ -32,6 +33,30 @@ class TimedNode:
|
|
32
33
|
The 'start' and 'end' values by default
|
33
34
|
"""
|
34
35
|
|
36
|
+
@property
|
37
|
+
def is_audio_node(
|
38
|
+
self
|
39
|
+
) -> bool:
|
40
|
+
"""
|
41
|
+
Flag to indicate if the node (or effect)
|
42
|
+
is an audio effect or not.
|
43
|
+
"""
|
44
|
+
# TODO: Is this checking not only the
|
45
|
+
# first level (?)
|
46
|
+
return PythonValidator.is_subclass_of(self.node, _AudioNode)
|
47
|
+
|
48
|
+
@property
|
49
|
+
def is_video_node(
|
50
|
+
self
|
51
|
+
) -> bool:
|
52
|
+
"""
|
53
|
+
Flag to indicate if the node (or effect)
|
54
|
+
is a video effect or not.
|
55
|
+
"""
|
56
|
+
# TODO: Is this checking not only the
|
57
|
+
# first level (?)
|
58
|
+
return PythonValidator.is_subclass_of(self.node, _VideoNode)
|
59
|
+
|
35
60
|
def __init__(
|
36
61
|
self,
|
37
62
|
node: Union[_VideoNode, _AudioNode],
|
@@ -47,7 +72,7 @@ class TimedNode:
|
|
47
72
|
):
|
48
73
|
raise Exception('The "end" parameter provided must be greater or equal to the "start" parameter.')
|
49
74
|
|
50
|
-
self.node:
|
75
|
+
self.node: Union[_VideoNode, _AudioNode] = node
|
51
76
|
"""
|
52
77
|
The node we are wrapping and we want to
|
53
78
|
apply as a modification of the frame in
|
@@ -606,9 +606,9 @@ def video_modified_stored():
|
|
606
606
|
# size = video.size,
|
607
607
|
# first_frame = video.next_frame
|
608
608
|
# )
|
609
|
-
from yta_video_opengl.
|
609
|
+
from yta_video_opengl.effects import Effects
|
610
610
|
|
611
|
-
editor =
|
611
|
+
editor = Effects()
|
612
612
|
# waving_node_effect = editor.effects.video.waving_node(video.size, amplitude = 0.2, frequency = 9, speed = 3)
|
613
613
|
# chorus_effect = editor.effects.audio.chorus(audio.sample_rate)
|
614
614
|
# print(waving_node_effect)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|