yta-video-frame-time 0.0.7__tar.gz → 0.0.13__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.
- {yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/PKG-INFO +3 -2
- {yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/pyproject.toml +1 -1
- yta_video_frame_time-0.0.13/src/yta_video_frame_time/interval.py +569 -0
- {yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/src/yta_video_frame_time/t_fraction.py +101 -9
- {yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/LICENSE +0 -0
- {yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/README.md +0 -0
- {yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/src/yta_video_frame_time/__init__.py +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: yta-video-frame-time
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.13
|
|
4
4
|
Summary: Youtube Autonomous Video Frame Time Module
|
|
5
|
+
License-File: LICENSE
|
|
5
6
|
Author: danialcala94
|
|
6
7
|
Author-email: danielalcalavalera@gmail.com
|
|
7
8
|
Requires-Python: ==3.9
|
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
from yta_validation.parameter import ParameterValidator
|
|
2
|
+
from quicktions import Fraction
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Tengo un elemento con una duración concreta, por lo que
|
|
8
|
+
el rango es [0, duration):
|
|
9
|
+
- Recortar solo por el principio => 2 segmentos
|
|
10
|
+
- Recortar solo por el final => 2 segmentos
|
|
11
|
+
- Recortar en medio => 3 segmentos
|
|
12
|
+
"""
|
|
13
|
+
Number = Union[int, float, Fraction]
|
|
14
|
+
"""
|
|
15
|
+
Custom type to represent numbers.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
TODO: Maybe we can add the possibility of having an
|
|
20
|
+
`fps` value when initializing it to be able to force
|
|
21
|
+
the time interval values to be multiple of `1/fps`.
|
|
22
|
+
But this, if implemented, should be `TimeIntervalFPS`
|
|
23
|
+
or similar, and inheritance from this one but forcing
|
|
24
|
+
the values to be transformed according to that `1/fps`.
|
|
25
|
+
"""
|
|
26
|
+
class TimeInterval:
|
|
27
|
+
"""
|
|
28
|
+
Class to represent a time interval, which is a tuple
|
|
29
|
+
of time moments representing the time range
|
|
30
|
+
`[start, end)`.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def start_base(
|
|
35
|
+
self
|
|
36
|
+
) -> float:
|
|
37
|
+
"""
|
|
38
|
+
The `start` of the interval but always as 0.
|
|
39
|
+
"""
|
|
40
|
+
return 0
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def end_base(
|
|
44
|
+
self
|
|
45
|
+
) -> float:
|
|
46
|
+
"""
|
|
47
|
+
The `end` of the interval but adapted to a `start=0`.
|
|
48
|
+
"""
|
|
49
|
+
return self.end - self.start
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def duration(
|
|
53
|
+
self
|
|
54
|
+
) -> float:
|
|
55
|
+
"""
|
|
56
|
+
The `duration` of the time interval.
|
|
57
|
+
"""
|
|
58
|
+
return self.end - self.start
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def copy(
|
|
62
|
+
self
|
|
63
|
+
) -> 'TimeInterval':
|
|
64
|
+
"""
|
|
65
|
+
A copy of this instance.
|
|
66
|
+
"""
|
|
67
|
+
return TimeInterval(
|
|
68
|
+
start = self.start,
|
|
69
|
+
end = self.end
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def as_tuple(
|
|
74
|
+
self
|
|
75
|
+
) -> tuple[float, float]:
|
|
76
|
+
"""
|
|
77
|
+
The time interval but as a `(start, end)` tuple.
|
|
78
|
+
"""
|
|
79
|
+
return (self.start, self.end)
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
start: Number,
|
|
84
|
+
end: Number,
|
|
85
|
+
):
|
|
86
|
+
"""
|
|
87
|
+
Provide the interval as it actually is, with the `start`
|
|
88
|
+
and `end`. These values will be adjusted to an internal
|
|
89
|
+
interval starting on 0.
|
|
90
|
+
|
|
91
|
+
The `end` value must be greater than the `start` value.
|
|
92
|
+
"""
|
|
93
|
+
if start > end:
|
|
94
|
+
raise Exception('The `start` value provided is greater than the `end` value provided.')
|
|
95
|
+
|
|
96
|
+
if start == end:
|
|
97
|
+
raise Exception('The `start` value provided is exactly the `end` value provided.')
|
|
98
|
+
|
|
99
|
+
self.start: float = start
|
|
100
|
+
"""
|
|
101
|
+
The original `start` of the time segment.
|
|
102
|
+
"""
|
|
103
|
+
self.end: float = end
|
|
104
|
+
"""
|
|
105
|
+
The original `end` of the time segment.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
def _validate_t(
|
|
109
|
+
self,
|
|
110
|
+
t: Number,
|
|
111
|
+
do_include_start: bool = False,
|
|
112
|
+
do_include_end: bool = False
|
|
113
|
+
) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Validate that the provided `t` value is between the `start`
|
|
116
|
+
and the `end` parameters provided, including them or not
|
|
117
|
+
according to the boolean parameters provided.
|
|
118
|
+
"""
|
|
119
|
+
ParameterValidator.validate_mandatory_number_between(
|
|
120
|
+
name = 't',
|
|
121
|
+
value = t,
|
|
122
|
+
lower_limit = self.start,
|
|
123
|
+
upper_limit = self.end,
|
|
124
|
+
do_include_lower_limit = do_include_start,
|
|
125
|
+
do_include_upper_limit = do_include_end
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def _cut(
|
|
129
|
+
self,
|
|
130
|
+
start: Number,
|
|
131
|
+
end: Number
|
|
132
|
+
) -> tuple[Union['TimeInterval', None], Union['TimeInterval', None], Union['TimeInterval', None]]:
|
|
133
|
+
"""
|
|
134
|
+
*For internal use only*
|
|
135
|
+
|
|
136
|
+
Cut a segment with the given `start` and `end` time moments.
|
|
137
|
+
|
|
138
|
+
This method will return a tuple of 3 elements including the
|
|
139
|
+
segments created by cutting this time interval. The tuple
|
|
140
|
+
will include all the segments at the begining and the rest
|
|
141
|
+
will be None.
|
|
142
|
+
|
|
143
|
+
Examples below:
|
|
144
|
+
- A time interval of `[2, 5)` cut with `start=3` and `end=4`
|
|
145
|
+
will generate `((2, 3), (3, 4), (4, 5))`.
|
|
146
|
+
- A time interval of `[2, 5)` cut with `start=2` and `end=4`
|
|
147
|
+
will generate `((2, 4), (4, 5), None)`.
|
|
148
|
+
- A time interval of `[2, 5)` cut with `start=4` and `end=5`
|
|
149
|
+
will generate `((2, 4), (4, 5), None)`.
|
|
150
|
+
- A time interval of `[2, 5)` cut with `start=2` and `end=5`
|
|
151
|
+
will generate `((2, 5), None, None)`.
|
|
152
|
+
|
|
153
|
+
As you can see, the result could be the same in different
|
|
154
|
+
situations, but it's up to you (and the specific method in
|
|
155
|
+
which you are calling to this one) to choose the tuple you
|
|
156
|
+
want to return.
|
|
157
|
+
|
|
158
|
+
(!) This will not modify the original instance.
|
|
159
|
+
"""
|
|
160
|
+
self._validate_t(start, do_include_start = True)
|
|
161
|
+
self._validate_t(end, do_include_end = True)
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
(
|
|
165
|
+
self.copy,
|
|
166
|
+
None,
|
|
167
|
+
None
|
|
168
|
+
)
|
|
169
|
+
if (
|
|
170
|
+
start == self.start and
|
|
171
|
+
end == self.end
|
|
172
|
+
) else
|
|
173
|
+
(
|
|
174
|
+
TimeInterval(
|
|
175
|
+
start = self.start,
|
|
176
|
+
end = end
|
|
177
|
+
),
|
|
178
|
+
TimeInterval(
|
|
179
|
+
start = end,
|
|
180
|
+
end = self.end
|
|
181
|
+
),
|
|
182
|
+
None
|
|
183
|
+
)
|
|
184
|
+
if start == self.start else
|
|
185
|
+
(
|
|
186
|
+
TimeInterval(
|
|
187
|
+
start = self.start,
|
|
188
|
+
end = start
|
|
189
|
+
),
|
|
190
|
+
TimeInterval(
|
|
191
|
+
start = start,
|
|
192
|
+
end = self.end
|
|
193
|
+
),
|
|
194
|
+
None
|
|
195
|
+
)
|
|
196
|
+
if end == self.end else
|
|
197
|
+
(
|
|
198
|
+
TimeInterval(
|
|
199
|
+
start = self.start,
|
|
200
|
+
end = start
|
|
201
|
+
),
|
|
202
|
+
TimeInterval(
|
|
203
|
+
start = start,
|
|
204
|
+
end = end
|
|
205
|
+
),
|
|
206
|
+
TimeInterval(
|
|
207
|
+
start = end,
|
|
208
|
+
end = self.end
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def cut_from_start_to(
|
|
214
|
+
self,
|
|
215
|
+
t: Number,
|
|
216
|
+
do_get_cut: bool = False
|
|
217
|
+
) -> Union['TimeInterval', None]:
|
|
218
|
+
"""
|
|
219
|
+
Cut the interval from the start to the `t` value
|
|
220
|
+
provided. The return will be the segment cut if
|
|
221
|
+
`do_get_cut` is False, or the remaining part if
|
|
222
|
+
True.
|
|
223
|
+
|
|
224
|
+
(!) This will not modify the original instance.
|
|
225
|
+
"""
|
|
226
|
+
intervals = self._cut(
|
|
227
|
+
start = self.start,
|
|
228
|
+
end = t
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
return (
|
|
232
|
+
intervals[0]
|
|
233
|
+
if (
|
|
234
|
+
len(intervals) == 1 or
|
|
235
|
+
do_get_cut
|
|
236
|
+
) else
|
|
237
|
+
intervals[1]
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def cut_to_end_from(
|
|
241
|
+
self,
|
|
242
|
+
t: Number,
|
|
243
|
+
do_get_cut: bool = False
|
|
244
|
+
) -> Union['TimeInterval', None]:
|
|
245
|
+
"""
|
|
246
|
+
Cut the interval from the start to the `t` value
|
|
247
|
+
provided. The return will be the segment cut if
|
|
248
|
+
`do_get_cut` is False, or the remaining part if
|
|
249
|
+
True.
|
|
250
|
+
|
|
251
|
+
(!) This will not modify the original instance.
|
|
252
|
+
"""
|
|
253
|
+
intervals = self._cut(
|
|
254
|
+
start = t,
|
|
255
|
+
end = self.end
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
intervals[0]
|
|
260
|
+
if (
|
|
261
|
+
len(intervals) == 1 or
|
|
262
|
+
not do_get_cut
|
|
263
|
+
) else
|
|
264
|
+
intervals[1]
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
def cut_from_to(
|
|
268
|
+
self,
|
|
269
|
+
from_t: Number,
|
|
270
|
+
to_t: Number,
|
|
271
|
+
# do_get_cut: bool = False
|
|
272
|
+
) -> Union['TimeInterval', None]:
|
|
273
|
+
"""
|
|
274
|
+
Cut the interval from the `from_t` value provided
|
|
275
|
+
to the `to_t` value given. The return will be the
|
|
276
|
+
segment cut.
|
|
277
|
+
|
|
278
|
+
(!) This will not modify the original instance.
|
|
279
|
+
|
|
280
|
+
TODO: By now we are only getting the cut
|
|
281
|
+
"""
|
|
282
|
+
intervals = self._cut(
|
|
283
|
+
start = from_t,
|
|
284
|
+
end = to_t
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
intervals[0]
|
|
289
|
+
if (
|
|
290
|
+
len(intervals) == 1 or
|
|
291
|
+
(
|
|
292
|
+
len(intervals) == 2 and
|
|
293
|
+
from_t == self.start
|
|
294
|
+
)
|
|
295
|
+
) else
|
|
296
|
+
intervals[1]
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def is_t_included(
|
|
300
|
+
self,
|
|
301
|
+
t: float,
|
|
302
|
+
do_include_end: bool = False
|
|
303
|
+
) -> bool:
|
|
304
|
+
"""
|
|
305
|
+
Check if the `t` time moment provided is included in
|
|
306
|
+
this time interval, including the `end` only if the
|
|
307
|
+
`do_include_end` parameter is set as `True`.
|
|
308
|
+
"""
|
|
309
|
+
return TimeIntervalUtils.a_includes_t(
|
|
310
|
+
t = t,
|
|
311
|
+
time_interval_a = self,
|
|
312
|
+
do_include_end = do_include_end
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def is_adjacent_to(
|
|
316
|
+
self,
|
|
317
|
+
time_interval: 'TimeInterval'
|
|
318
|
+
) -> bool:
|
|
319
|
+
"""
|
|
320
|
+
Check if the `time_interval` provided is adjacent
|
|
321
|
+
to this time interval, which means that the `end`
|
|
322
|
+
of one interval is also the `start` of the other
|
|
323
|
+
one.
|
|
324
|
+
|
|
325
|
+
(!) Giving the time intervals inverted will
|
|
326
|
+
provide the same result.
|
|
327
|
+
|
|
328
|
+
Example below:
|
|
329
|
+
- `a=[2, 5)` and `b=[5, 7)` => `True`
|
|
330
|
+
- `a=[5, 7)` and `b=[2, 5)` => `True`
|
|
331
|
+
- `a=[2, 5)` and `b=[3, 4)` => `False`
|
|
332
|
+
- `a=[2, 5)` and `b=[6, 8)` => `False`
|
|
333
|
+
"""
|
|
334
|
+
return TimeIntervalUtils.a_is_adjacent_to_b(
|
|
335
|
+
time_interval_a = self,
|
|
336
|
+
time_interval_b = time_interval
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def do_contains_a(
|
|
340
|
+
self,
|
|
341
|
+
time_interval: 'TimeInterval'
|
|
342
|
+
) -> bool:
|
|
343
|
+
"""
|
|
344
|
+
Check if this time interval includes the `time_interval`
|
|
345
|
+
provided or not, which means that the `time_interval`
|
|
346
|
+
provided is fully contained (included) in this one.
|
|
347
|
+
"""
|
|
348
|
+
return TimeIntervalUtils.a_contains_b(
|
|
349
|
+
time_interval_a = self,
|
|
350
|
+
time_interval_b = time_interval
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
def is_contained_in(
|
|
354
|
+
self,
|
|
355
|
+
time_interval: 'TimeInterval'
|
|
356
|
+
) -> bool:
|
|
357
|
+
"""
|
|
358
|
+
Check if this time interval is fully contained in
|
|
359
|
+
the `time_interval` provided, which is a synonim
|
|
360
|
+
of being fully overlapped by that `time_interval`.
|
|
361
|
+
"""
|
|
362
|
+
return TimeIntervalUtils.a_is_contained_in_b(
|
|
363
|
+
time_interval_a = self,
|
|
364
|
+
time_interval_b = time_interval
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def do_intersects_with(
|
|
368
|
+
self,
|
|
369
|
+
time_interval: 'TimeInterval'
|
|
370
|
+
) -> bool:
|
|
371
|
+
"""
|
|
372
|
+
Check if this time interval intersects with the one
|
|
373
|
+
provided as `time_interval`, which means that they
|
|
374
|
+
have at least a part in common.
|
|
375
|
+
"""
|
|
376
|
+
return TimeIntervalUtils.a_intersects_with_b(
|
|
377
|
+
time_interval_a = self,
|
|
378
|
+
time_interval_b = time_interval
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def get_intersection_with_a(
|
|
382
|
+
self,
|
|
383
|
+
time_interval: 'TimeInterval'
|
|
384
|
+
) -> Union['TimeInterval', None]:
|
|
385
|
+
"""
|
|
386
|
+
Get the time interval that intersects this one and the
|
|
387
|
+
one provided as `time_interval`. The result can be `None`
|
|
388
|
+
if there is no intersection in between both.
|
|
389
|
+
"""
|
|
390
|
+
return TimeIntervalUtils.get_intersection_of_a_and_b(
|
|
391
|
+
time_interval_a = self,
|
|
392
|
+
time_interval_b = time_interval
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class TimeIntervalUtils:
|
|
397
|
+
"""
|
|
398
|
+
Static class to wrap the utils related to time intervals.
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
@staticmethod
|
|
402
|
+
def a_includes_t(
|
|
403
|
+
t: float,
|
|
404
|
+
time_interval_a: 'TimeInterval',
|
|
405
|
+
do_include_end: bool = False
|
|
406
|
+
) -> bool:
|
|
407
|
+
"""
|
|
408
|
+
Check if the `t` time moment provided is included in
|
|
409
|
+
the `time_interval_a` given. The `time_interval_a.end`
|
|
410
|
+
is excluded unless the `do_include_end` parameter is
|
|
411
|
+
set as `True`.
|
|
412
|
+
|
|
413
|
+
A time interval is `[start, end)`, thats why the end is
|
|
414
|
+
excluded by default.
|
|
415
|
+
"""
|
|
416
|
+
return (
|
|
417
|
+
time_interval_a.start <= t <= time_interval_a.end
|
|
418
|
+
if do_include_end else
|
|
419
|
+
time_interval_a.start <= t < time_interval_a.end
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
@staticmethod
|
|
423
|
+
def a_is_adjacent_to_b(
|
|
424
|
+
time_interval_a: 'TimeInterval',
|
|
425
|
+
time_interval_b: 'TimeInterval',
|
|
426
|
+
) -> bool:
|
|
427
|
+
"""
|
|
428
|
+
Check if the `time_interval_a` provided and the
|
|
429
|
+
also given `time_interval_b` are adjacent, which
|
|
430
|
+
means that the `end` of one interval is also the
|
|
431
|
+
`start` of the other one.
|
|
432
|
+
|
|
433
|
+
(!) Giving the time intervals inverted will
|
|
434
|
+
provide the same result.
|
|
435
|
+
|
|
436
|
+
Examples below:
|
|
437
|
+
- `a=[2, 5)` and `b=[5, 7)` => `True`
|
|
438
|
+
- `a=[5, 7)` and `b=[2, 5)` => `True`
|
|
439
|
+
- `a=[2, 5)` and `b=[3, 4)` => `False`
|
|
440
|
+
- `a=[2, 5)` and `b=[6, 8)` => `False`
|
|
441
|
+
"""
|
|
442
|
+
return (
|
|
443
|
+
TimeIntervalUtils.a_is_inmediately_before_b(time_interval_a, time_interval_b) or
|
|
444
|
+
TimeIntervalUtils.a_is_inmediately_after_b(time_interval_a, time_interval_b)
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
@staticmethod
|
|
448
|
+
def a_is_inmediately_before_b(
|
|
449
|
+
time_interval_a: 'TimeInterval',
|
|
450
|
+
time_interval_b: 'TimeInterval',
|
|
451
|
+
) -> bool:
|
|
452
|
+
"""
|
|
453
|
+
Check if the `time_interval_a` provided is inmediately
|
|
454
|
+
before the also given `time_interval_b`, which means
|
|
455
|
+
that the `end` of the first one is also the `start` of
|
|
456
|
+
the second one.
|
|
457
|
+
|
|
458
|
+
Examples below:
|
|
459
|
+
- `a=[2, 5)` and `b=[5, 7)` => `True`
|
|
460
|
+
- `a=[5, 7)` and `b=[2, 5)` => `False`
|
|
461
|
+
- `a=[2, 5)` and `b=[3, 4)` => `False`
|
|
462
|
+
- `a=[2, 5)` and `b=[6, 8)` => `False`
|
|
463
|
+
"""
|
|
464
|
+
return time_interval_a.end == time_interval_b.start
|
|
465
|
+
|
|
466
|
+
@staticmethod
|
|
467
|
+
def a_is_inmediately_after_b(
|
|
468
|
+
time_interval_a: 'TimeInterval',
|
|
469
|
+
time_interval_b: 'TimeInterval',
|
|
470
|
+
) -> bool:
|
|
471
|
+
"""
|
|
472
|
+
Check if the `time_interval_a` provided is inmediately
|
|
473
|
+
after the also given `time_interval_b`, which means
|
|
474
|
+
that the `start` of the first one is also the `end` of
|
|
475
|
+
the second one.
|
|
476
|
+
|
|
477
|
+
Examples below:
|
|
478
|
+
- `a=[2, 5)` and `b=[5, 7)` => `False`
|
|
479
|
+
- `a=[5, 7)` and `b=[2, 5)` => `True`
|
|
480
|
+
- `a=[2, 5)` and `b=[3, 4)` => `False`
|
|
481
|
+
- `a=[2, 5)` and `b=[6, 8)` => `False`
|
|
482
|
+
"""
|
|
483
|
+
return time_interval_a.start == time_interval_b.end
|
|
484
|
+
|
|
485
|
+
@staticmethod
|
|
486
|
+
def a_contains_b(
|
|
487
|
+
time_interval_a: 'TimeInterval',
|
|
488
|
+
time_interval_b: 'TimeInterval'
|
|
489
|
+
) -> bool:
|
|
490
|
+
"""
|
|
491
|
+
Check if the `time_interval_a` time interval provided
|
|
492
|
+
includes the `time_interval_b` or not, which means that
|
|
493
|
+
the `time_interval_b` is fully contained in the first
|
|
494
|
+
one.
|
|
495
|
+
|
|
496
|
+
Examples below:
|
|
497
|
+
- `a=[2, 5)` and `b=[3, 4)` => `True`
|
|
498
|
+
- `a=[2, 5)` and `b=[2, 4)` => `True`
|
|
499
|
+
- `a=[2, 5)` and `b=[3, 6)` => `False`
|
|
500
|
+
- `a=[2, 5)` and `b=[6, 8)` => `False`
|
|
501
|
+
"""
|
|
502
|
+
return (
|
|
503
|
+
time_interval_a.start <= time_interval_b.start and
|
|
504
|
+
time_interval_a.end >= time_interval_b.end
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
@staticmethod
|
|
508
|
+
def a_is_contained_in_b(
|
|
509
|
+
time_interval_a: 'TimeInterval',
|
|
510
|
+
time_interval_b: 'TimeInterval',
|
|
511
|
+
) -> bool:
|
|
512
|
+
"""
|
|
513
|
+
Check if the `time_interval_a` provided is fully
|
|
514
|
+
contained into the also provided `time_interval_b`.
|
|
515
|
+
|
|
516
|
+
Examples below:
|
|
517
|
+
- `a=[2, 5)` and `b=[1, 6)` => `True`
|
|
518
|
+
- `a=[2, 5)` and `b=[0, 9)` => `True`
|
|
519
|
+
- `a=[2, 5)` and `b=[2, 4)` => `False`
|
|
520
|
+
- `a=[2, 5)` and `b=[4, 8)` => `False`
|
|
521
|
+
- `a=[2, 5)` and `b=[7, 8)` => `False`
|
|
522
|
+
"""
|
|
523
|
+
return TimeIntervalUtils.a_contains_b(
|
|
524
|
+
time_interval_a = time_interval_b,
|
|
525
|
+
time_interval_b = time_interval_a
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
@staticmethod
|
|
529
|
+
def a_intersects_with_b(
|
|
530
|
+
time_interval_a: 'TimeInterval',
|
|
531
|
+
time_interval_b: 'TimeInterval',
|
|
532
|
+
) -> bool:
|
|
533
|
+
"""
|
|
534
|
+
Check if the `time_interval_a` and the `time_interval_b`
|
|
535
|
+
provided has at least a part in common.
|
|
536
|
+
|
|
537
|
+
Examples below:
|
|
538
|
+
- `a=[2, 5)` and `b=[4, 6)` => `True`
|
|
539
|
+
- `a=[2, 5)` and `b=[1, 3)` => `True`
|
|
540
|
+
- `a=[2, 5)` and `b=[5, 6)` => `False`
|
|
541
|
+
- `a=[2, 5)` and `b=[7, 8)` => `False`
|
|
542
|
+
- `a=[2, 5)` and `b=[1, 2)` => `False`
|
|
543
|
+
"""
|
|
544
|
+
return (
|
|
545
|
+
time_interval_b.start < time_interval_a.end and
|
|
546
|
+
time_interval_a.start < time_interval_b.end
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
@staticmethod
|
|
550
|
+
def get_intersection_of_a_and_b(
|
|
551
|
+
time_interval_a: 'TimeInterval',
|
|
552
|
+
time_interval_b: 'TimeInterval'
|
|
553
|
+
) -> Union['TimeInterval', None]:
|
|
554
|
+
"""
|
|
555
|
+
Get the time interval that intersects the two time
|
|
556
|
+
intervals provided, that can be `None` if there is no
|
|
557
|
+
intersection in between both.
|
|
558
|
+
"""
|
|
559
|
+
return (
|
|
560
|
+
None
|
|
561
|
+
if not TimeIntervalUtils.a_intersects_with_b(
|
|
562
|
+
time_interval_a = time_interval_a,
|
|
563
|
+
time_interval_b = time_interval_b
|
|
564
|
+
) else
|
|
565
|
+
TimeInterval(
|
|
566
|
+
start = max(time_interval_a.start, time_interval_b.start),
|
|
567
|
+
end = min(time_interval_a.end, time_interval_b.end)
|
|
568
|
+
)
|
|
569
|
+
)
|
{yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/src/yta_video_frame_time/t_fraction.py
RENAMED
|
@@ -238,6 +238,8 @@ def round_pts(
|
|
|
238
238
|
and 'time_base', but here is an easier
|
|
239
239
|
example using the time moments.
|
|
240
240
|
|
|
241
|
+
(!) This is valid only for video.
|
|
242
|
+
|
|
241
243
|
Examples below, with `time_base = 1/5`:
|
|
242
244
|
- `t = 0.25` => `0.2` (truncated or rounded)
|
|
243
245
|
- `t = 0.35` => `0.2` (truncated)
|
|
@@ -256,8 +258,6 @@ def round_pts(
|
|
|
256
258
|
|
|
257
259
|
return int(frame_index * ticks_per_frame)
|
|
258
260
|
|
|
259
|
-
# TODO: Create a 'round_pts'
|
|
260
|
-
|
|
261
261
|
"""
|
|
262
262
|
When we are working with the 't' time
|
|
263
263
|
moment we need to use the fps, and when
|
|
@@ -306,6 +306,18 @@ class _T:
|
|
|
306
306
|
The formula:
|
|
307
307
|
- `pts * time_base`
|
|
308
308
|
"""
|
|
309
|
+
t = Fraction(pts * self._t_handler.time_base)
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
self._t_handler.t.truncated(t)
|
|
313
|
+
if do_truncate is True else
|
|
314
|
+
self._t_handler.t.rounded(t)
|
|
315
|
+
if do_truncate is False else
|
|
316
|
+
t # if None
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# TODO: Remove this below in the next
|
|
320
|
+
# commit
|
|
309
321
|
pts = (
|
|
310
322
|
self._t_handler.pts.truncated(pts)
|
|
311
323
|
if do_truncate is True else
|
|
@@ -342,7 +354,7 @@ class _T:
|
|
|
342
354
|
variation.
|
|
343
355
|
|
|
344
356
|
The formula:
|
|
345
|
-
- `int(t * fps)`
|
|
357
|
+
- `int(round(t * fps))`
|
|
346
358
|
"""
|
|
347
359
|
t = (
|
|
348
360
|
self.truncated(t)
|
|
@@ -370,7 +382,7 @@ class _T:
|
|
|
370
382
|
def truncated(
|
|
371
383
|
self,
|
|
372
384
|
t: Union[int, float, Fraction]
|
|
373
|
-
):
|
|
385
|
+
) -> Fraction:
|
|
374
386
|
"""
|
|
375
387
|
Get the 't' value provided but truncated.
|
|
376
388
|
|
|
@@ -380,10 +392,26 @@ class _T:
|
|
|
380
392
|
"""
|
|
381
393
|
return round_t(t, Fraction(1, self._t_handler.fps), do_truncate = True)
|
|
382
394
|
|
|
395
|
+
def is_truncated(
|
|
396
|
+
self,
|
|
397
|
+
t: Union[int, float, Fraction]
|
|
398
|
+
) -> bool:
|
|
399
|
+
"""
|
|
400
|
+
Check if the `t` value provided is the truncated
|
|
401
|
+
value, which means that the `t` provided is the
|
|
402
|
+
`start` from the `[start, end)` range defined by
|
|
403
|
+
the fps.
|
|
404
|
+
"""
|
|
405
|
+
return check_values_are_same(
|
|
406
|
+
value_a = t,
|
|
407
|
+
value_b = self.truncated(t),
|
|
408
|
+
tolerance = 0.000001
|
|
409
|
+
)
|
|
410
|
+
|
|
383
411
|
def rounded(
|
|
384
412
|
self,
|
|
385
413
|
t: Union[int, float, Fraction]
|
|
386
|
-
):
|
|
414
|
+
) -> Fraction:
|
|
387
415
|
"""
|
|
388
416
|
Get the 't' value provided but rounded.
|
|
389
417
|
|
|
@@ -530,7 +558,7 @@ class _Pts:
|
|
|
530
558
|
variation.
|
|
531
559
|
|
|
532
560
|
The formula:
|
|
533
|
-
- `int((pts * time_base) * fps)`
|
|
561
|
+
- `int(round((pts * time_base) * fps))`
|
|
534
562
|
"""
|
|
535
563
|
return self._t_handler.t.to_index(
|
|
536
564
|
self.to_t(pts, do_truncate = None),
|
|
@@ -552,12 +580,31 @@ class _Pts:
|
|
|
552
580
|
t = self._t_handler.t.from_index(index),
|
|
553
581
|
do_truncate = True
|
|
554
582
|
)
|
|
583
|
+
|
|
584
|
+
"""
|
|
585
|
+
These 2 methods below are here because they
|
|
586
|
+
seem to work for videos, but I think they
|
|
587
|
+
could work not if the video has dynamic frame
|
|
588
|
+
rate or in some other situations, thats why
|
|
589
|
+
this is here as a reminder.
|
|
590
|
+
|
|
591
|
+
I found one video that had audio_fps=44100
|
|
592
|
+
and time_base=256/11025, so it was impossible
|
|
593
|
+
to make a conversion using this formula with
|
|
594
|
+
the audio. With video seems to be ok, but...
|
|
595
|
+
|
|
596
|
+
Use these methods below at your own risk.
|
|
597
|
+
"""
|
|
555
598
|
|
|
556
599
|
def truncated(
|
|
557
600
|
self,
|
|
558
601
|
pts: int
|
|
559
602
|
):
|
|
560
603
|
"""
|
|
604
|
+
(!) This is valid only for video and/or
|
|
605
|
+
could work not properly. Use it at your
|
|
606
|
+
own risk.
|
|
607
|
+
|
|
561
608
|
Get the 'pts' value provided but truncated.
|
|
562
609
|
|
|
563
610
|
This means that if 't' is in a
|
|
@@ -576,6 +623,10 @@ class _Pts:
|
|
|
576
623
|
pts: int
|
|
577
624
|
) -> int:
|
|
578
625
|
"""
|
|
626
|
+
(!) This is valid only for video and/or
|
|
627
|
+
could work not properly. Use it at your
|
|
628
|
+
own risk.
|
|
629
|
+
|
|
579
630
|
Get the 'pts' value provided but rounded.
|
|
580
631
|
|
|
581
632
|
This means that if 't' is in a
|
|
@@ -598,6 +649,10 @@ class _Pts:
|
|
|
598
649
|
do_truncate: bool = True
|
|
599
650
|
) -> int:
|
|
600
651
|
"""
|
|
652
|
+
(!) This is valid only for video and/or
|
|
653
|
+
could work not properly. Use it at your
|
|
654
|
+
own risk.
|
|
655
|
+
|
|
601
656
|
Get the value that is 'n' times ahead of
|
|
602
657
|
the 'pts' value provided (truncated or
|
|
603
658
|
rounded according to the 'do_truncate'
|
|
@@ -624,6 +679,10 @@ class _Pts:
|
|
|
624
679
|
do_truncate: bool = True
|
|
625
680
|
) -> int:
|
|
626
681
|
"""
|
|
682
|
+
(!) This is valid only for video and/or
|
|
683
|
+
could work not properly. Use it at your
|
|
684
|
+
own risk.
|
|
685
|
+
|
|
627
686
|
Get the value that is 'n' times before
|
|
628
687
|
the 't' property of this instance
|
|
629
688
|
(truncated or rounded according to the
|
|
@@ -647,7 +706,7 @@ class _Pts:
|
|
|
647
706
|
)
|
|
648
707
|
|
|
649
708
|
return pts - n * get_ticks_per_frame(self._t_handler.fps, self._t_handler.time_base)
|
|
650
|
-
|
|
709
|
+
|
|
651
710
|
class THandler:
|
|
652
711
|
"""
|
|
653
712
|
Class to simplify the way we work with
|
|
@@ -715,9 +774,9 @@ def frame_t_to_index(
|
|
|
715
774
|
also provided 'fps'.
|
|
716
775
|
|
|
717
776
|
The formula:
|
|
718
|
-
- `int(t * fps)`
|
|
777
|
+
- `int(round(t * fps))`
|
|
719
778
|
"""
|
|
720
|
-
return int(
|
|
779
|
+
return int(round(t * fps))
|
|
721
780
|
|
|
722
781
|
def frame_index_to_t(
|
|
723
782
|
index: int,
|
|
@@ -745,6 +804,8 @@ def frame_t_to_pts(
|
|
|
745
804
|
moment provided, based on the also provided
|
|
746
805
|
'fps' and 'time_base'.
|
|
747
806
|
|
|
807
|
+
(!) This is valid only for videos.
|
|
808
|
+
|
|
748
809
|
The formula:
|
|
749
810
|
- `frame_index * ticks_per_frame`
|
|
750
811
|
"""
|
|
@@ -764,6 +825,23 @@ def frame_pts_to_t(
|
|
|
764
825
|
- `pts * time_base`
|
|
765
826
|
"""
|
|
766
827
|
return parse_fraction(pts * time_base)
|
|
828
|
+
|
|
829
|
+
def get_audio_frame_duration(
|
|
830
|
+
samples: int,
|
|
831
|
+
audio_fps: Fraction
|
|
832
|
+
) -> Fraction:
|
|
833
|
+
"""
|
|
834
|
+
Get the audio frame duration by giving the
|
|
835
|
+
number of '.samples' and also the rate (that
|
|
836
|
+
we call 'audio_fps').
|
|
837
|
+
|
|
838
|
+
This is useful when trying to guess the next
|
|
839
|
+
pts or t.
|
|
840
|
+
|
|
841
|
+
The formula:
|
|
842
|
+
- `samples / audio_fps`
|
|
843
|
+
"""
|
|
844
|
+
return Fraction(samples / audio_fps)
|
|
767
845
|
|
|
768
846
|
def get_ticks_per_frame(
|
|
769
847
|
fps: Union[float, int, Fraction],
|
|
@@ -820,3 +898,17 @@ def parse_fraction(
|
|
|
820
898
|
|
|
821
899
|
return fraction
|
|
822
900
|
|
|
901
|
+
def check_values_are_same(
|
|
902
|
+
value_a: Union[Fraction, float],
|
|
903
|
+
value_b: Union[Fraction, float],
|
|
904
|
+
tolerance: float = 1e-6
|
|
905
|
+
) -> bool:
|
|
906
|
+
"""
|
|
907
|
+
Check that the `value_a` and the `value_b` are the same
|
|
908
|
+
by applying the `tolerance` value, that is 0.000001 by
|
|
909
|
+
default.
|
|
910
|
+
|
|
911
|
+
For example, `0.016666666666666666` is the same value as
|
|
912
|
+
`1/60` with `tolerance=0.000001`.
|
|
913
|
+
"""
|
|
914
|
+
return abs(float(value_a) - float(value_b)) < tolerance
|
|
File without changes
|
|
File without changes
|
{yta_video_frame_time-0.0.7 → yta_video_frame_time-0.0.13}/src/yta_video_frame_time/__init__.py
RENAMED
|
File without changes
|