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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: yta-video-opengl
3
- Version: 0.0.24
3
+ Version: 0.0.25
4
4
  Summary: Youtube Autonomous Video OpenGL Module
5
5
  Author: danialcala94
6
6
  Author-email: danielalcalavalera@gmail.com
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "yta-video-opengl"
3
- version = "0.0.24"
3
+ version = "0.0.25"
4
4
  description = "Youtube Autonomous Video OpenGL Module"
5
5
  authors = [
6
6
  {name = "danialcala94",email = "danielalcalavalera@gmail.com"}
@@ -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 Node, TimedNode
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: '_Effects'
23
+ effects: 'Effects'
24
24
  ):
25
- self._effects: _Effects = 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: '_Effects'
69
+ effects: 'Effects'
70
70
  ):
71
- self._effects: _Effects = 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._opengl_editor.context,
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 OpenglEditor:
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.context = moderngl.create_context(standalone = True)
147
+ self.opengl_context = moderngl.create_context(standalone = True)
176
148
  """
177
- The context that will be shared by all
178
- the nodes.
149
+ The opengl context that will be shared
150
+ by all the opengl nodes.
179
151
  """
180
- self.effects: _Effects = _Effects(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
- # The class we pass has to inherit from this
195
- # 'Node' class, but could be other classes
196
- # in the middle, because an OpenglNode
197
- # inherits from other class
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 Node wrapper with the
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 effects(
239
+ def video_effects(
247
240
  self
248
241
  ) -> list[_EffectStacked]:
249
242
  """
250
- The effects but ordered from their 'start'
251
- time moment.
243
+ The video effects but ordered by 'priority'
244
+ and 'start' time moment.
252
245
  """
253
- return sorted(self._effects, key = lambda effect: (effect.priority, effect.effect.start))
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 most_priority_effect(
256
+ def audio_effects(
257
257
  self
258
- ) -> _EffectStacked:
258
+ ) -> list[_EffectStacked]:
259
259
  """
260
- The effect with the highest priority,
261
- that is the lower priority value.
260
+ The audio effects but ordered by 'priority'
261
+ and 'start' time moment.
262
262
  """
263
- return min(self._effects, key = lambda effect: effect.priority)
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 less_priority_effect(
287
+ def lowest_video_priority(
267
288
  self
268
- ) -> _EffectStacked:
289
+ ) -> int:
269
290
  """
270
- The effect with the lowest priority,
271
- that is the biggest priority value.
291
+ The priority of the video effect with the
292
+ lowest one, or 0 if no video effects.
272
293
  """
273
- return max(self._effects, key = lambda effect: effect.priority)
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 get_effects_for_t(
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
- applied within the 't' time moment
292
- provided because it is within the
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.effects
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.less_priority_effect.priority + 1
322
- if priority is None else
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: Node = 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
@@ -16,7 +16,6 @@ working in GPU. Doing this below most of the
16
16
  changes would work:
17
17
  - `import numpy as np` → `import cupy as np`
18
18
  """
19
- from yta_video_opengl.nodes import TimedNode
20
19
  from abc import abstractmethod
21
20
  from typing import Union
22
21
 
@@ -8,6 +8,10 @@ from abc import ABC, abstractmethod
8
8
 
9
9
  import moderngl
10
10
 
11
+
12
+ # TODO: Expose all the nodes here and move
13
+ # this abstract to an abstract file (?)
14
+
11
15
  class _VideoNode(ABC):
12
16
  """
13
17
  Base abstract class to represent a video
@@ -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.editor import OpenglEditor
609
+ from yta_video_opengl.effects import Effects
610
610
 
611
- editor = OpenglEditor()
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)