yta-editor 0.0.1__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.
Files changed (32) hide show
  1. yta_editor-0.0.1/LICENSE +19 -0
  2. yta_editor-0.0.1/PKG-INFO +22 -0
  3. yta_editor-0.0.1/README.md +3 -0
  4. yta_editor-0.0.1/pyproject.toml +32 -0
  5. yta_editor-0.0.1/src/yta_editor/__init__.py +41 -0
  6. yta_editor-0.0.1/src/yta_editor/decorators.py +33 -0
  7. yta_editor-0.0.1/src/yta_editor/media/__init__.py +16 -0
  8. yta_editor-0.0.1/src/yta_editor/media/abstract.py +160 -0
  9. yta_editor-0.0.1/src/yta_editor/media/audio/__init__.py +140 -0
  10. yta_editor-0.0.1/src/yta_editor/media/video/__init__.py +354 -0
  11. yta_editor-0.0.1/src/yta_editor/sources/__init__.py +0 -0
  12. yta_editor-0.0.1/src/yta_editor/sources/abstract.py +107 -0
  13. yta_editor-0.0.1/src/yta_editor/sources/audio/__init__.py +191 -0
  14. yta_editor-0.0.1/src/yta_editor/sources/video/__init__.py +457 -0
  15. yta_editor-0.0.1/src/yta_editor/tests.py +158 -0
  16. yta_editor-0.0.1/src/yta_editor/timeline.py +566 -0
  17. yta_editor-0.0.1/src/yta_editor/track/__init__.py +18 -0
  18. yta_editor-0.0.1/src/yta_editor/track/abstract.py +421 -0
  19. yta_editor-0.0.1/src/yta_editor/track/audio/__init__.py +44 -0
  20. yta_editor-0.0.1/src/yta_editor/track/media/__init__.py +11 -0
  21. yta_editor-0.0.1/src/yta_editor/track/media/abstract.py +174 -0
  22. yta_editor-0.0.1/src/yta_editor/track/media/audio.py +25 -0
  23. yta_editor-0.0.1/src/yta_editor/track/media/video.py +24 -0
  24. yta_editor-0.0.1/src/yta_editor/track/parts/__init__.py +21 -0
  25. yta_editor-0.0.1/src/yta_editor/track/parts/abstract.py +279 -0
  26. yta_editor-0.0.1/src/yta_editor/track/parts/audio.py +27 -0
  27. yta_editor-0.0.1/src/yta_editor/track/parts/video.py +27 -0
  28. yta_editor-0.0.1/src/yta_editor/track/video/__init__.py +61 -0
  29. yta_editor-0.0.1/src/yta_editor/utils/__init__.py +218 -0
  30. yta_editor-0.0.1/src/yta_editor/utils/frame_combinator.py +228 -0
  31. yta_editor-0.0.1/src/yta_editor/utils/frame_generator.py +319 -0
  32. yta_editor-0.0.1/src/yta_editor/utils/frame_wrapper.py +135 -0
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.3
2
+ Name: yta-editor
3
+ Version: 0.0.1
4
+ Summary: Youtube Autonomous Main Editor
5
+ Author: danialcala94
6
+ Author-email: danielalcalavalera@gmail.com
7
+ Requires-Python: ==3.9
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Requires-Dist: av (>=0.0.1,<19.0.0)
11
+ Requires-Dist: numpy (>=0.0.1,<9.0.0)
12
+ Requires-Dist: pillow (>=0.0.1,<99.0.0)
13
+ Requires-Dist: quicktions (>=0.0.1,<9.0.0)
14
+ Requires-Dist: yta_validation (>=0.0.1,<1.0.0)
15
+ Requires-Dist: yta_video_frame_time (>=0.0.1,<1.0.0)
16
+ Requires-Dist: yta_video_opengl (>=0.0.1,<1.0.0)
17
+ Requires-Dist: yta_video_pyav (>=0.0.1,<1.0.0)
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Youtube Autonomous Main Editor
21
+
22
+ The main Editor that works using PyAv and OpenGL.
@@ -0,0 +1,3 @@
1
+ # Youtube Autonomous Main Editor
2
+
3
+ The main Editor that works using PyAv and OpenGL.
@@ -0,0 +1,32 @@
1
+ [project]
2
+ name = "yta-editor"
3
+ version = "0.0.1"
4
+ description = "Youtube Autonomous Main Editor"
5
+ authors = [
6
+ {name = "danialcala94",email = "danielalcalavalera@gmail.com"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = "==3.9"
10
+ dependencies = [
11
+ "yta_validation (>=0.0.1,<1.0.0)",
12
+ "yta_video_frame_time (>=0.0.1,<1.0.0)",
13
+ "yta_video_opengl (>=0.0.1,<1.0.0)",
14
+ "yta_video_pyav (>=0.0.1,<1.0.0)",
15
+ "av (>=0.0.1,<19.0.0)",
16
+ "numpy (>=0.0.1,<9.0.0)",
17
+ "quicktions (>=0.0.1,<9.0.0)",
18
+ # I need this 'pillow' just to read an image
19
+ # and transform into a numpy array to be able
20
+ # to handle the ImageMedia
21
+ "pillow (>=0.0.1,<99.0.0)",
22
+ ]
23
+
24
+ [tool.poetry]
25
+ packages = [{include = "yta_editor", from = "src"}]
26
+
27
+ [tool.poetry.group.dev.dependencies]
28
+ pytest = "^8.3.5"
29
+
30
+ [build-system]
31
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
32
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,41 @@
1
+ """
2
+ Our awesome editor module in which we
3
+ have all the classes that interact with
4
+ it and make it possible.
5
+
6
+ An editor includes a single timeline,
7
+ built by tracks, in which we place media
8
+ elements and we are able to apply effects
9
+ to them.
10
+
11
+ Here is a brief explanation about the
12
+ hierarchy:
13
+
14
+ - The editor has a timeline.
15
+ - The timeline has audio and video tracks
16
+ (that can handle when we play or not the
17
+ audio and video).
18
+ - The tracks have parts, that are virtual
19
+ items to simplify the way we combine
20
+ tracks. Those parts include media
21
+ instances. The tracks have priorities
22
+ ones against the others.
23
+ - The media instances have a time range
24
+ in which they must be played within the
25
+ track (within the timeline). Those media
26
+ instances have the start and end time
27
+ range for the media source and can apply
28
+ effects to the frames.
29
+ - The media sources are just the way we
30
+ obtain the frames for the specific media
31
+ items (read a file, constant color, etc.).
32
+ """
33
+
34
+ def main():
35
+ from yta_editor.tests import video_modified_stored
36
+
37
+ video_modified_stored()
38
+
39
+
40
+ if __name__ == '__main__':
41
+ main()
@@ -0,0 +1,33 @@
1
+ from functools import wraps
2
+
3
+
4
+ def with_t_adjusted_to_media(
5
+ method
6
+ ):
7
+ """
8
+ Get the real 't' time moment based on the
9
+ video 'start' and 'end'. If they were
10
+ asking for the t=0.5s but our video was
11
+ subclipped to [1.0, 2.0), the 0.5s must be
12
+ actually the 1.5s of the video because of
13
+ the subclipped time range.
14
+
15
+ The formula:
16
+ - `t + self.start`
17
+ """
18
+ @wraps(method)
19
+ def wrapper(
20
+ self,
21
+ t,
22
+ *args,
23
+ **kwargs
24
+ ):
25
+ t += self.start
26
+
27
+ print(f'The video/audio real t is {str(float(t))}')
28
+ if t >= self.end:
29
+ raise Exception(f'The "t" ({str(t)}) provided is out of range. This video/audio lasts from [{str(self.start)}, {str(self.end)}).')
30
+
31
+ return method(self, t, *args, **kwargs)
32
+
33
+ return wrapper
@@ -0,0 +1,16 @@
1
+ """
2
+ Media elements, that are the ones we will
3
+ use and place on the tracks, able to access
4
+ to the frames, using the different sources,
5
+ and to apply the effects we want.
6
+ """
7
+ # from yta_editor.media.audio import AudioFileMedia
8
+ # from yta_editor.media.video import VideoFileMedia, VideoColorMedia, VideoImageMedia
9
+
10
+
11
+ # __all__ = [
12
+ # 'AudioFileMedia',
13
+ # 'VideoFileMedia',
14
+ # 'VideoColorMedia',
15
+ # 'VideoImageMedia',
16
+ # ]
@@ -0,0 +1,160 @@
1
+ from yta_video_opengl.effects import EffectsStack
2
+ from yta_validation.parameter import ParameterValidator
3
+ from quicktions import Fraction
4
+ from typing import Union
5
+ from abc import ABC, abstractmethod
6
+
7
+
8
+ class _Media(ABC):
9
+ """
10
+ Abstract class to be inherited by any
11
+ media element.
12
+
13
+ The media element is an element that
14
+ includes a source and a 'start' and
15
+ 'end' values to be able to subclip that
16
+ media source and use only the part we
17
+ want to use.
18
+ """
19
+
20
+ @property
21
+ @abstractmethod
22
+ def copy(
23
+ self
24
+ ) -> '_Media':
25
+ """
26
+ Get a copy of this instance with the same
27
+ source, time range and effects.
28
+ """
29
+ pass
30
+
31
+ @property
32
+ def duration(
33
+ self
34
+ ) -> Fraction:
35
+ """
36
+ The duration of the media, that can be
37
+ shorter than the source duration if the
38
+ user requested it.
39
+
40
+ The formula:
41
+ - `self.end - self.start`
42
+ """
43
+ return self.end - self.start
44
+
45
+ def __init__(
46
+ self,
47
+ source: Union['AudioFileSource', 'AudioNumpySource', 'VideoFileSource', 'VideoColorSource', 'VideoImageSource', 'VideoNumpySource'],
48
+ start: Union[int, float, Fraction] = 0.0,
49
+ end: Union[int, float, Fraction, None] = None,
50
+ ):
51
+ self.source: Union['AudioFileSource', 'AudioNumpySource', 'VideoFileSource', 'VideoColorSource', 'VideoImageSource', 'VideoNumpySource'] = source
52
+ """
53
+ The source of this media element that
54
+ is the entity from which we can obtain
55
+ the frames.
56
+ """
57
+ self._effects: EffectsStack = EffectsStack()
58
+ """
59
+ The effects we want to apply on the
60
+ media.
61
+ """
62
+ self.start: Fraction
63
+ """
64
+ The time moment 't' in which the media
65
+ should start being played/displayed.
66
+ """
67
+ self.end: Union[Fraction, None]
68
+ """
69
+ The time moment 't' in which the media
70
+ should end being played/displayed.
71
+ """
72
+
73
+ # Set 'start' and 'end'
74
+ self.set_time_range(start, end)
75
+
76
+ def set_time_range(
77
+ self,
78
+ start: Union[int, float, Fraction],
79
+ end: Union[int, float, Fraction, None] = None,
80
+ ) -> '_Media':
81
+ """
82
+ Set the media 'start' and 'end' time
83
+ moments range from the original source
84
+ that will be played/displayed.
85
+
86
+ - If `end = None`, the source duration
87
+ (if available) will be set.
88
+ - If `end > source.duration` it will be
89
+ replaced by the source duration value.
90
+ """
91
+ ParameterValidator.validate_mandatory_positive_number('start', start, do_include_zero = True)
92
+ ParameterValidator.validate_positive_number('end', end, do_include_zero = False)
93
+
94
+ self.start: Fraction = Fraction(start)
95
+ self.end: Union[Fraction, None] = Fraction(
96
+ # TODO: Is this 'end' ok (?)
97
+ self.source.duration
98
+ if (
99
+ end is None or
100
+ end > self.source.duration
101
+ ) else
102
+ end
103
+ )
104
+
105
+ # If the source has a duration, the 'start'
106
+ # and 'end' must be valid
107
+ if self.source.duration is not None:
108
+ if (
109
+ self.start >= self.source.duration and
110
+ self.end >= self.source.duration
111
+ ):
112
+ raise Exception(f'The provided "start" and "end" are invalid values considering the real media duration of {str(float(self.source.duration))}s')
113
+
114
+ if self.end <= self.start:
115
+ raise Exception('The "end" value cannot be equal or smaller than the "start" value.')
116
+
117
+ self.end = (
118
+ self.source.duration
119
+ if self.end > self.source.duration else
120
+ self.end
121
+ )
122
+
123
+ return self
124
+
125
+ # TODO: This method has been created
126
+ # to be inherited by the other classes
127
+ # and being able to copy the instance
128
+ # properly by using the same 'source'
129
+ # reference and creating not a new one
130
+ @classmethod
131
+ def _init_with_source(
132
+ cls,
133
+ source: Union['AudioFileSource', 'AudioNumpySource', 'VideoFileSource', 'VideoColorSource', 'VideoImageSource', 'VideoNumpySource'],
134
+ start: Union[int, float, Fraction] = 0.0,
135
+ end: Union[int, float, Fraction, None] = None
136
+ ):
137
+ """
138
+ *For internal use only*
139
+
140
+ Alternative '__init__' to create the
141
+ instance from the 'source' directly. This
142
+ method must be called by the specific
143
+ implementations of this abstract class
144
+ to be able to instantiate them directly
145
+ from the 'source' to make copies.
146
+
147
+ We created this method to avoid generating
148
+ a new 'source' instance but preserving the
149
+ same reference.
150
+ """
151
+ # Create new instance skipping '__init__'
152
+ instance = cls.__new__(cls)
153
+ super(cls, instance).__init__(
154
+ source = source,
155
+ start = start,
156
+ end = end
157
+ )
158
+
159
+ return instance
160
+
@@ -0,0 +1,140 @@
1
+ from yta_editor.media.abstract import _Media
2
+ from yta_editor.sources.abstract import _AudioSource
3
+ from yta_editor.sources.audio import AudioFileSource, AudioNumpySource
4
+ from yta_editor.decorators import with_t_adjusted_to_media
5
+ from yta_editor.utils import apply_audio_effects_to_frame_at_t
6
+ from yta_video_opengl.nodes import TimedNode
7
+ from yta_validation.parameter import ParameterValidator
8
+ from quicktions import Fraction
9
+ from typing import Union
10
+
11
+
12
+ class _AudioMedia(_Media):
13
+ """
14
+ Abstract class to be inherited by any
15
+ media element.
16
+
17
+ The media element is an element that
18
+ includes a source and a 'start' and
19
+ 'end' values to be able to subclip that
20
+ media source and use only the part we
21
+ want to use.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ source: _AudioSource,
27
+ start: Union[int, float, Fraction] = 0.0,
28
+ end: Union[int, float, Fraction, None] = None,
29
+ ):
30
+ super().__init__(
31
+ source = source,
32
+ start = start,
33
+ end = end
34
+ )
35
+
36
+ def add_effect(
37
+ self,
38
+ effect: TimedNode
39
+ ) -> '_AudioMedia':
40
+ """
41
+ Add the provided 'effect' to the audio.
42
+ """
43
+ ParameterValidator.validate_mandatory_instance_of('effect', effect, 'TimedNode')
44
+
45
+ if not effect.is_audio_node:
46
+ raise Exception('The provided "effect" is not an audio effect.')
47
+
48
+ self._effects.add_effect(effect)
49
+
50
+ return self
51
+
52
+ @with_t_adjusted_to_media
53
+ def get_audio_frames_at_t(
54
+ self,
55
+ t: Union[int, float, Fraction],
56
+ video_fps: Union[int, float, Fraction]
57
+ ):
58
+ """
59
+ Get the sequence of audio frames for a
60
+ given video 't' time moment, using the
61
+ audio cache system.
62
+
63
+ This is useful when we want to write a
64
+ video frame with its audio, so we obtain
65
+ all the audio frames associated to it
66
+ (remember that a video frame is associated
67
+ with more than 1 audio frame).
68
+ """
69
+ print(f'Getting audio frames from {str(float(t + self.start))} that is actually {str(float(t))}')
70
+ for frame in self.source.get_audio_frames_at_t(t, video_fps):
71
+ yield apply_audio_effects_to_frame_at_t(
72
+ effects_stack = self._effects,
73
+ frame = frame,
74
+ t = t
75
+ )
76
+
77
+ class AudioFileMedia(_AudioMedia):
78
+ """
79
+ An audio media that is read from an audio
80
+ file.
81
+ """
82
+
83
+ @property
84
+ def copy(
85
+ self
86
+ ) -> 'AudioFileMedia':
87
+ """
88
+ Get a copy of this instance with the same
89
+ source, time range and effects.
90
+ """
91
+ copy = AudioFileMedia._init_with_source(
92
+ source = self.source,
93
+ start = self.start,
94
+ end = self.end
95
+ )
96
+
97
+ copy._effects = self._effects.copy
98
+
99
+ return copy
100
+
101
+ @property
102
+ def duration(
103
+ self
104
+ ) -> Fraction:
105
+ """
106
+ The duration of the video.
107
+ """
108
+ return self.end - self.start
109
+
110
+ @property
111
+ def audio_fps(
112
+ self
113
+ ) -> Union[int, None]:
114
+ """
115
+ The frames per second of the audio.
116
+ """
117
+ return self.source.audio_fps
118
+
119
+ @property
120
+ def audio_time_base(
121
+ self
122
+ ) -> Union[Fraction, None]:
123
+ """
124
+ The time base of the audio.
125
+ """
126
+ return self.source.audio_time_base
127
+
128
+ def __init__(
129
+ self,
130
+ filename: str,
131
+ start: Union[int, float, Fraction] = 0.0,
132
+ end: Union[int, float, Fraction, None] = None,
133
+ ):
134
+ super().__init__(
135
+ source = AudioFileSource(filename),
136
+ start = start,
137
+ end = end
138
+ )
139
+
140
+ # TODO: Create 'AudioNumpyMedia'