yta-video-frame-time 0.0.3__tar.gz → 0.0.5__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.

Potentially problematic release.


This version of yta-video-frame-time might be problematic. Click here for more details.

@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: yta-video-frame-time
3
- Version: 0.0.3
3
+ Version: 0.0.5
4
4
  Summary: Youtube Autonomous Video Frame Time Module
5
5
  Author: danialcala94
6
6
  Author-email: danielalcalavalera@gmail.com
7
7
  Requires-Python: ==3.9
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.9
10
- Requires-Dist: yta_general_utils (>=0.0.1,<1.0.0)
10
+ Requires-Dist: quicktions (>=0.0.1,<9.0.0)
11
11
  Requires-Dist: yta_validation (>=0.0.1,<1.0.0)
12
12
  Description-Content-Type: text/markdown
13
13
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "yta-video-frame-time"
3
- version = "0.0.3"
3
+ version = "0.0.5"
4
4
  description = "Youtube Autonomous Video Frame Time Module"
5
5
  authors = [
6
6
  {name = "danialcala94",email = "danielalcalavalera@gmail.com"}
@@ -9,7 +9,7 @@ readme = "README.md"
9
9
  requires-python = "==3.9"
10
10
  dependencies = [
11
11
  "yta_validation (>=0.0.1,<1.0.0)",
12
- "yta_general_utils (>=0.0.1,<1.0.0)"
12
+ "quicktions (>=0.0.1,<9.0.0)"
13
13
  ]
14
14
 
15
15
  [tool.poetry]
@@ -15,7 +15,6 @@ be 10 frames and it is happening due to a minimal
15
15
  floating point difference.
16
16
  """
17
17
 
18
-
19
18
  class T:
20
19
  """
21
20
  Class to wrap the functionality related
@@ -458,20 +457,20 @@ class T:
458
457
  ParameterValidator.validate_mandatory_positive_number('audio_fps', audio_fps, do_include_zero = False)
459
458
  ParameterValidator.validate_mandatory_bool('do_invert_order', do_invert_order)
460
459
 
461
- # TODO: This import is bringin many libraries...
462
- from yta_general_utils.math.progression import Progression
463
-
464
460
  audio_frames_per_video_frame = int(audio_fps / video_fps)
465
461
  audio_frame_duration = 1 / audio_fps
466
462
  video_frame_duration = 1 / video_fps
467
463
 
468
464
  t = T.get_frame_time_base(video_t, video_fps)
469
465
 
470
- audio_time_moments = Progression(
471
- start = t,
466
+ # This is replacing a 'Progression' from
467
+ # the 'yta_general_utils' library because
468
+ # it has many imports we don't want here...
469
+ audio_time_moments = _linspace(
470
+ start = t,
472
471
  end = t + video_frame_duration - audio_frame_duration,
473
472
  n = audio_frames_per_video_frame
474
- ).values
473
+ )
475
474
 
476
475
  return (
477
476
  audio_time_moments[::-1]
@@ -590,4 +589,29 @@ def get_number_of_frames(
590
589
  The formula is:
591
590
  - `int(duration * fps + SMALL_AMOUNT)`
592
591
  """
593
- return int(duration * fps + SMALL_AMOUNT_TO_FIX)
592
+ return int(duration * fps + SMALL_AMOUNT_TO_FIX)
593
+
594
+ def _linspace(
595
+ start: float,
596
+ end: float,
597
+ n: int,
598
+ ):
599
+ """
600
+ *For internal use only*
601
+
602
+ A simplified version of the 'Progression'
603
+ class we have in 'yta_general_utils', to
604
+ avoid importing that library that has other
605
+ dependencies I don't need here.
606
+
607
+ This method should be removed when the
608
+ Progression class is refactored and all
609
+ those dependencies are removed.
610
+ """
611
+ if n < 2:
612
+ raise Exception('The "n" parameter must be greater or equal than 2.')
613
+
614
+ return [
615
+ start + i * ((end - start) / (n - 1))
616
+ for i in range(n)
617
+ ]
@@ -0,0 +1,234 @@
1
+ """
2
+ This is an example of what a video has:
3
+ - fps = 60
4
+ - time_base = 1 / 15360
5
+ - tick = fps * time_base = 256
6
+
7
+ So, the first pts is 0 and the second
8
+ one is 256. The frame 16 will be 3840,
9
+ that is 256 * 15 (because first index
10
+ is 0).
11
+ """
12
+ from yta_validation.parameter import ParameterValidator
13
+ from yta_validation.number import NumberValidator
14
+ from yta_validation import PythonValidator
15
+ from quicktions import Fraction
16
+ from typing import Union
17
+
18
+
19
+ class T:
20
+ """
21
+ Class to simplify the way we work with a
22
+ 't' time moment but using the fractions
23
+ library to be precise and avoid any issue
24
+ related with commas.
25
+
26
+ This class must be used when trying to
27
+ apply a specific 't' time moment for a
28
+ video or audio frame, using the fps or
29
+ sample rate as time_base to be precise.
30
+ """
31
+
32
+ @property
33
+ def truncated(
34
+ self
35
+ ) -> Fraction:
36
+ """
37
+ The 't' but as a Fraction that is multiple
38
+ of the given 'time_base' and truncated.
39
+ """
40
+ return round_t(self._t, self.time_base)
41
+
42
+ @property
43
+ def rounded(
44
+ self
45
+ ) -> Fraction:
46
+ """
47
+ The 't' but as a Fraction that is multiple
48
+ of the given 'time_base' and rounded (the
49
+ value could be the same as truncated if it
50
+ is closer to the previous value).
51
+ """
52
+ return round_t(self._t, self.time_base, do_truncate = False)
53
+
54
+ @property
55
+ def truncated_pts(
56
+ self
57
+ ) -> int:
58
+ """
59
+ The 'truncated' value but as a pts, which
60
+ is the int value to be set in audio and
61
+ video frames in the pyav library to be
62
+ displayed in that moment.
63
+ """
64
+ return int(self.truncated / self.time_base)
65
+
66
+ @property
67
+ def rounded_pts(
68
+ self
69
+ ) -> int:
70
+ """
71
+ The 'rounded' value but as a pts, which
72
+ is the int value to be set in audio and
73
+ video frames in the pyav library to be
74
+ displayed in that moment.
75
+ """
76
+ return int(self.rounded / self.time_base)
77
+
78
+ def __init__(
79
+ self,
80
+ t: Union[int, float, Fraction],
81
+ time_base: Fraction
82
+ ):
83
+ ParameterValidator.validate_mandatory_instance_of('t', t, [int, float, 'Fraction'])
84
+ ParameterValidator.validate_mandatory_instance_of('time_base', time_base, 'Fraction')
85
+
86
+ self._t: Union[int, float, Fraction] = t
87
+ """
88
+ The 't' time moment as it was passed as
89
+ parameter.
90
+ """
91
+ self.time_base: Fraction = time_base
92
+ """
93
+ The time_base that will used to round the
94
+ values to be multiples of it.
95
+ """
96
+
97
+ def next(
98
+ self,
99
+ n: int = 1
100
+ ) -> 'T':
101
+ """
102
+ Get the value that is 'n' times ahead of
103
+ the 'truncated' property of this instance.
104
+
105
+ Useful when you need the next value for a
106
+ range in an iteration or similar.
107
+ """
108
+ return T(self.truncated + n * self.time_base, self.time_base)
109
+
110
+ def previous(
111
+ self,
112
+ n: int = 1
113
+ ) -> 'T':
114
+ """
115
+ Get the value that is 'n' times before the
116
+ 'truncated' property of this instance.
117
+
118
+ Useful when you need the previous value to
119
+ check if the current is the next one or
120
+ similar.
121
+
122
+ Be careful, if the 'truncated' value is 0
123
+ this will give you an unexpected negative
124
+ value.
125
+ """
126
+ return T(self.truncated - n * self.time_base, self.time_base)
127
+
128
+ @staticmethod
129
+ def from_fps(
130
+ t: Union[int, float, Fraction],
131
+ fps: Union[int, float, Fraction]
132
+ ) -> 'T':
133
+ """
134
+ Get the instance but providing the 'fps'
135
+ (or sample rate) value directly, that will
136
+ be turned into a time base.
137
+ """
138
+ return T(t, fps_to_time_base(fps))
139
+
140
+ @staticmethod
141
+ def from_pts(
142
+ pts: int,
143
+ time_base: Fraction
144
+ ) -> 'T':
145
+ """
146
+ Get the instance but providing the 'pts'
147
+ and the 'time_base'.
148
+ """
149
+ return T(pts * time_base, time_base)
150
+
151
+
152
+ # TODO: Careful with this below
153
+ """
154
+ To obtain the pts step, or frame duration in
155
+ ticks, you need to apply 2 formulas that are
156
+ different according to if the frame is video
157
+ or audio:
158
+ - Audio: .samples
159
+ - Video: int(round((1 / .fps) / .time_base))
160
+ """
161
+
162
+ def get_ts(
163
+ start: Union[int, float, Fraction],
164
+ end: Union[int, float, Fraction],
165
+ fps: Fraction
166
+ ) -> list[Fraction]:
167
+ """
168
+ Get all the 't' time moments between the given
169
+ 'start' and the given 'end', using the provided
170
+ 'time_base' for precision.
171
+
172
+ The 'end' is not included, we return a range
173
+ [start, end) because the last frame is the
174
+ start of another time range.
175
+ """
176
+ start = T.from_fps(start, fps).truncated
177
+ end = T.from_fps(end, fps).truncated
178
+
179
+ time_base = fps_to_time_base(fps)
180
+
181
+ return [
182
+ start + i * time_base
183
+ for i in range((end - start) // time_base)
184
+ ]
185
+
186
+ def round_t(
187
+ t: Union[int, float, Fraction],
188
+ time_base = Fraction(1, 60),
189
+ do_truncate: bool = True
190
+ ):
191
+ """
192
+ Round the given 't' time moment to the most
193
+ near multiple of the given 'time_base' (or
194
+ the previous one if 'do_truncate' is True)
195
+ using fractions module to be precise.
196
+
197
+ This method is very useful to truncate 't'
198
+ time moments in order to get the frames or
199
+ samples for the specific and exact time
200
+ moments according to their fps or sample
201
+ rate (that should be passed as the
202
+ 'time_base' parameter).
203
+
204
+ Examples below, with `time_base = 1/5`:
205
+ - `t = 0.25` => `0.2` (truncated or rounded)
206
+ - `t = 0.35` => `0.2` (truncated)
207
+ - `t = 0.45` => `0.4` (truncated or rounded)
208
+ - `t = 0.55` => `0.6` (rounded)
209
+ """
210
+ t = Fraction(t).limit_denominator()
211
+ steps = t / time_base
212
+
213
+ snapped_steps = (
214
+ steps.numerator // steps.denominator
215
+ if do_truncate else
216
+ round(steps) # round(float(steps))
217
+ )
218
+
219
+ return snapped_steps * time_base
220
+
221
+ def fps_to_time_base(
222
+ fps: Union[int, float, Fraction]
223
+ ) -> Fraction:
224
+ """
225
+ Get the pyav time base from the given
226
+ 'fps'.
227
+ """
228
+ return (
229
+ Fraction(1, fps)
230
+ if NumberValidator.is_int(fps) else
231
+ Fraction(1, 1) / fps
232
+ if PythonValidator.is_instance_of(fps, 'Fraction') else
233
+ Fraction(1, 1) / Fraction.from_float(fps).limit_denominator(1000000) # if float
234
+ )