videopython 0.25.6__tar.gz → 0.25.8__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.
- {videopython-0.25.6 → videopython-0.25.8}/PKG-INFO +1 -1
- {videopython-0.25.6 → videopython-0.25.8}/pyproject.toml +1 -1
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/effects.py +64 -98
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/text/transcription.py +39 -44
- {videopython-0.25.6 → videopython-0.25.8}/.gitignore +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/LICENSE +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/README.md +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/_device.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/dubbing/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/dubbing/dubber.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/dubbing/models.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/dubbing/pipeline.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/dubbing/timing.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/generation/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/generation/audio.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/generation/image.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/generation/translation.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/generation/video.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/registry.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/swapping/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/swapping/inpainter.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/swapping/models.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/swapping/segmenter.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/swapping/swapper.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/transforms.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/understanding/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/understanding/audio.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/understanding/image.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/understanding/separation.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/understanding/temporal.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/ai/video_analysis.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/audio/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/audio/analysis.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/audio/audio.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/combine.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/description.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/exceptions.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/progress.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/registry.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/scene.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/text/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/text/overlay.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/transforms.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/transitions.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/utils.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/base/video.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/editing/__init__.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/editing/multicam.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/editing/premiere_xml.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/editing/video_edit.py +0 -0
- {videopython-0.25.6 → videopython-0.25.8}/src/videopython/py.typed +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from multiprocessing import Pool
|
|
5
4
|
from typing import TYPE_CHECKING, Literal
|
|
6
5
|
|
|
7
6
|
import cv2
|
|
@@ -11,9 +10,6 @@ from PIL import Image, ImageDraw, ImageFont
|
|
|
11
10
|
from videopython.base.progress import log, progress_iter
|
|
12
11
|
from videopython.base.video import Video
|
|
13
12
|
|
|
14
|
-
# Minimum frames before using multiprocessing (Pool overhead isn't worth it below this)
|
|
15
|
-
MIN_FRAMES_FOR_MULTIPROCESSING = 100
|
|
16
|
-
|
|
17
13
|
if TYPE_CHECKING:
|
|
18
14
|
from videopython.base.description import BoundingBox
|
|
19
15
|
|
|
@@ -70,24 +66,30 @@ class Effect(ABC):
|
|
|
70
66
|
Only set when the effect should end before the video does.
|
|
71
67
|
"""
|
|
72
68
|
original_shape = video.video_shape
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
69
|
+
|
|
70
|
+
if start is None and stop is None:
|
|
71
|
+
# Full-range: apply directly without slicing or np.r_ reassembly.
|
|
72
|
+
video = self._apply(video)
|
|
73
|
+
else:
|
|
74
|
+
start_s, stop_s = _resolve_time_range(start, stop, video.total_seconds)
|
|
75
|
+
# Apply effect on video slice
|
|
76
|
+
effect_start_frame = round(start_s * video.fps)
|
|
77
|
+
effect_end_frame = round(stop_s * video.fps)
|
|
78
|
+
video_with_effect = self._apply(video[effect_start_frame:effect_end_frame])
|
|
79
|
+
old_audio = video.audio
|
|
80
|
+
video = Video.from_frames(
|
|
81
|
+
np.r_[
|
|
82
|
+
"0,2",
|
|
83
|
+
video.frames[:effect_start_frame],
|
|
84
|
+
video_with_effect.frames,
|
|
85
|
+
video.frames[effect_end_frame:],
|
|
86
|
+
],
|
|
87
|
+
fps=video.fps,
|
|
88
|
+
)
|
|
89
|
+
video.audio = old_audio
|
|
90
|
+
|
|
89
91
|
# Check if dimensions didn't change
|
|
90
|
-
if
|
|
92
|
+
if video.video_shape != original_shape:
|
|
91
93
|
raise RuntimeError("The effect must not change the number of frames and the shape of the frames!")
|
|
92
94
|
|
|
93
95
|
return video
|
|
@@ -147,22 +149,15 @@ class FullImageOverlay(Effect):
|
|
|
147
149
|
|
|
148
150
|
log("Overlaying video...")
|
|
149
151
|
if self.fade_time == 0:
|
|
150
|
-
video.frames =
|
|
151
|
-
[self._overlay(
|
|
152
|
-
dtype=np.uint8,
|
|
153
|
-
)
|
|
152
|
+
for i in progress_iter(range(len(video.frames)), desc="Overlaying frames"):
|
|
153
|
+
video.frames[i] = self._overlay(video.frames[i])
|
|
154
154
|
else:
|
|
155
155
|
num_video_frames = len(video.frames)
|
|
156
156
|
num_fade_frames = round(self.fade_time * video.fps)
|
|
157
|
-
|
|
158
|
-
for i, frame in enumerate(progress_iter(video.frames, desc="Overlaying frames")):
|
|
157
|
+
for i in progress_iter(range(num_video_frames), desc="Overlaying frames"):
|
|
159
158
|
frames_dist_from_end = min(i, num_video_frames - i)
|
|
160
|
-
if frames_dist_from_end >= num_fade_frames
|
|
161
|
-
|
|
162
|
-
else:
|
|
163
|
-
fade_alpha = frames_dist_from_end / num_fade_frames
|
|
164
|
-
new_frames.append(self._overlay(frame, fade_alpha))
|
|
165
|
-
video.frames = np.array(new_frames, dtype=np.uint8)
|
|
159
|
+
fade_alpha = 1.0 if frames_dist_from_end >= num_fade_frames else frames_dist_from_end / num_fade_frames
|
|
160
|
+
video.frames[i] = self._overlay(video.frames[i], fade_alpha)
|
|
166
161
|
return video
|
|
167
162
|
|
|
168
163
|
|
|
@@ -228,17 +223,8 @@ class Blur(Effect):
|
|
|
228
223
|
raise ValueError(f"Unknown mode: `{self.mode}`.")
|
|
229
224
|
|
|
230
225
|
log(f"Applying {self.mode} blur...")
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
with Pool() as pool:
|
|
234
|
-
new_frames = pool.starmap(
|
|
235
|
-
self._blur_frame,
|
|
236
|
-
[(frame, sigma) for frame, sigma in zip(video.frames, sigmas)],
|
|
237
|
-
)
|
|
238
|
-
else:
|
|
239
|
-
new_frames = [self._blur_frame(frame, sigma) for frame, sigma in zip(video.frames, sigmas)]
|
|
240
|
-
|
|
241
|
-
video.frames = np.array(new_frames, dtype=np.uint8)
|
|
226
|
+
for i in progress_iter(range(n_frames), desc="Blurring"):
|
|
227
|
+
video.frames[i] = self._blur_frame(video.frames[i], sigmas[i])
|
|
242
228
|
return video
|
|
243
229
|
|
|
244
230
|
|
|
@@ -261,42 +247,23 @@ class Zoom(Effect):
|
|
|
261
247
|
|
|
262
248
|
def _apply(self, video: Video) -> Video:
|
|
263
249
|
n_frames = len(video.frames)
|
|
264
|
-
new_frames = []
|
|
265
|
-
|
|
266
250
|
width = video.metadata.width
|
|
267
251
|
height = video.metadata.height
|
|
268
|
-
crop_sizes_w
|
|
269
|
-
|
|
270
|
-
np.linspace(height // self.zoom_factor, height, n_frames),
|
|
271
|
-
)
|
|
252
|
+
crop_sizes_w = np.linspace(width // self.zoom_factor, width, n_frames)
|
|
253
|
+
crop_sizes_h = np.linspace(height // self.zoom_factor, height, n_frames)
|
|
272
254
|
|
|
273
255
|
if self.mode == "in":
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
total=n_frames,
|
|
278
|
-
):
|
|
279
|
-
x = width / 2 - w / 2
|
|
280
|
-
y = height / 2 - h / 2
|
|
281
|
-
|
|
282
|
-
cropped_frame = frame[round(y) : round(y + h), round(x) : round(x + w)]
|
|
283
|
-
zoomed_frame = cv2.resize(cropped_frame, (width, height))
|
|
284
|
-
new_frames.append(zoomed_frame)
|
|
285
|
-
elif self.mode == "out":
|
|
286
|
-
for frame, w, h in progress_iter(
|
|
287
|
-
zip(video.frames, crop_sizes_w, crop_sizes_h),
|
|
288
|
-
desc="Zooming",
|
|
289
|
-
total=n_frames,
|
|
290
|
-
):
|
|
291
|
-
x = width / 2 - w / 2
|
|
292
|
-
y = height / 2 - h / 2
|
|
293
|
-
|
|
294
|
-
cropped_frame = frame[round(y) : round(y + h), round(x) : round(x + w)]
|
|
295
|
-
zoomed_frame = cv2.resize(cropped_frame, (width, height))
|
|
296
|
-
new_frames.append(zoomed_frame)
|
|
297
|
-
else:
|
|
256
|
+
crop_sizes_w = crop_sizes_w[::-1]
|
|
257
|
+
crop_sizes_h = crop_sizes_h[::-1]
|
|
258
|
+
elif self.mode != "out":
|
|
298
259
|
raise ValueError(f"Unknown mode: `{self.mode}`.")
|
|
299
|
-
|
|
260
|
+
|
|
261
|
+
for i in progress_iter(range(n_frames), desc="Zooming", total=n_frames):
|
|
262
|
+
w, h = crop_sizes_w[i], crop_sizes_h[i]
|
|
263
|
+
x = width / 2 - w / 2
|
|
264
|
+
y = height / 2 - h / 2
|
|
265
|
+
cropped_frame = video.frames[i][round(y) : round(y + h), round(x) : round(x + w)]
|
|
266
|
+
video.frames[i] = cv2.resize(cropped_frame, (width, height))
|
|
300
267
|
return video
|
|
301
268
|
|
|
302
269
|
|
|
@@ -370,15 +337,8 @@ class ColorGrading(Effect):
|
|
|
370
337
|
|
|
371
338
|
def _apply(self, video: Video) -> Video:
|
|
372
339
|
log("Applying color grading...")
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if n_frames >= MIN_FRAMES_FOR_MULTIPROCESSING:
|
|
376
|
-
with Pool() as pool:
|
|
377
|
-
new_frames = pool.map(self._grade_frame, video.frames)
|
|
378
|
-
else:
|
|
379
|
-
new_frames = [self._grade_frame(frame) for frame in video.frames]
|
|
380
|
-
|
|
381
|
-
video.frames = np.array(new_frames, dtype=np.uint8)
|
|
340
|
+
for i in progress_iter(range(len(video.frames)), desc="Color grading"):
|
|
341
|
+
video.frames[i] = self._grade_frame(video.frames[i])
|
|
382
342
|
return video
|
|
383
343
|
|
|
384
344
|
|
|
@@ -426,11 +386,12 @@ class Vignette(Effect):
|
|
|
426
386
|
if self._mask is None or self._mask.shape != (height, width):
|
|
427
387
|
self._mask = self._create_mask(height, width)
|
|
428
388
|
|
|
429
|
-
# Apply mask to
|
|
430
|
-
# mask_3d shape: (height, width, 1), frames shape: (n_frames, height, width, 3)
|
|
431
|
-
# Broadcasting handles the multiplication across all frames and channels
|
|
389
|
+
# Apply mask in batches to avoid allocating a full float32 copy of all frames
|
|
432
390
|
mask_3d = self._mask[:, :, np.newaxis]
|
|
433
|
-
|
|
391
|
+
batch_size = 64
|
|
392
|
+
for start in range(0, len(video.frames), batch_size):
|
|
393
|
+
end = min(start + batch_size, len(video.frames))
|
|
394
|
+
video.frames[start:end] = (video.frames[start:end].astype(np.float32) * mask_3d).astype(np.uint8)
|
|
434
395
|
return video
|
|
435
396
|
|
|
436
397
|
|
|
@@ -533,8 +494,7 @@ class KenBurns(Effect):
|
|
|
533
494
|
end_h = int(self.end_region.height * height)
|
|
534
495
|
|
|
535
496
|
log("Applying Ken Burns effect...")
|
|
536
|
-
|
|
537
|
-
for i, frame in enumerate(progress_iter(video.frames, desc="Ken Burns")):
|
|
497
|
+
for i in progress_iter(range(n_frames), desc="Ken Burns"):
|
|
538
498
|
t = i / max(1, n_frames - 1) # Normalized time [0, 1]
|
|
539
499
|
eased_t = self._ease(t)
|
|
540
500
|
|
|
@@ -548,10 +508,7 @@ class KenBurns(Effect):
|
|
|
548
508
|
x = max(0, min(x, width - crop_w))
|
|
549
509
|
y = max(0, min(y, height - crop_h))
|
|
550
510
|
|
|
551
|
-
|
|
552
|
-
new_frames.append(new_frame)
|
|
553
|
-
|
|
554
|
-
video.frames = np.array(new_frames, dtype=np.uint8)
|
|
511
|
+
video.frames[i] = self._crop_and_scale_frame(video.frames[i], x, y, crop_w, crop_h, target_w, target_h)
|
|
555
512
|
return video
|
|
556
513
|
|
|
557
514
|
|
|
@@ -623,10 +580,19 @@ class Fade(Effect):
|
|
|
623
580
|
t = np.linspace(1, 0, fade_frames, dtype=np.float32)
|
|
624
581
|
alpha[-fade_frames:] = np.minimum(alpha[-fade_frames:], _compute_curve(t, self.curve))
|
|
625
582
|
|
|
626
|
-
# Apply to video frames
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
583
|
+
# Apply to video frames in batches to avoid a full float32 copy
|
|
584
|
+
batch_size = 64
|
|
585
|
+
for batch_start in range(0, n_effect_frames, batch_size):
|
|
586
|
+
batch_end = min(batch_start + batch_size, n_effect_frames)
|
|
587
|
+
batch_alpha = alpha[batch_start:batch_end, np.newaxis, np.newaxis, np.newaxis]
|
|
588
|
+
# Skip batch if all alphas are 1.0 (no change needed)
|
|
589
|
+
if np.all(batch_alpha == 1.0):
|
|
590
|
+
continue
|
|
591
|
+
abs_start = effect_start_frame + batch_start
|
|
592
|
+
abs_end = effect_start_frame + batch_end
|
|
593
|
+
video.frames[abs_start:abs_end] = (video.frames[abs_start:abs_end].astype(np.float32) * batch_alpha).astype(
|
|
594
|
+
np.uint8
|
|
595
|
+
)
|
|
630
596
|
|
|
631
597
|
# Verify shape invariant
|
|
632
598
|
if video.video_shape != original_shape:
|
|
@@ -195,9 +195,12 @@ class Transcription:
|
|
|
195
195
|
def standardize_segments(self, *, time: float | None = None, num_words: int | None = None) -> Transcription:
|
|
196
196
|
"""Return a new Transcription with standardized segments.
|
|
197
197
|
|
|
198
|
+
Segments are also split on speaker changes so that each segment contains
|
|
199
|
+
words from a single speaker.
|
|
200
|
+
|
|
198
201
|
Args:
|
|
199
202
|
time: Maximum duration in seconds for each segment
|
|
200
|
-
num_words:
|
|
203
|
+
num_words: Maximum number of words per segment
|
|
201
204
|
|
|
202
205
|
Raises:
|
|
203
206
|
ValueError: If both time and num_words are provided or if neither is provided
|
|
@@ -212,64 +215,56 @@ class Transcription:
|
|
|
212
215
|
raise ValueError("Number of words must be positive")
|
|
213
216
|
|
|
214
217
|
# Collect all words from all segments
|
|
215
|
-
all_words = []
|
|
218
|
+
all_words: list[TranscriptionWord] = []
|
|
216
219
|
for segment in self.segments:
|
|
217
220
|
all_words.extend(segment.words)
|
|
218
221
|
|
|
219
222
|
if not all_words:
|
|
220
223
|
return Transcription(segments=[], language=self.language)
|
|
221
224
|
|
|
222
|
-
standardized_segments = []
|
|
225
|
+
standardized_segments: list[TranscriptionSegment] = []
|
|
226
|
+
|
|
227
|
+
def _flush(words: list[TranscriptionWord]) -> None:
|
|
228
|
+
if not words:
|
|
229
|
+
return
|
|
230
|
+
segment_text = " ".join(w.word for w in words)
|
|
231
|
+
standardized_segments.append(
|
|
232
|
+
TranscriptionSegment(
|
|
233
|
+
start=words[0].start,
|
|
234
|
+
end=words[-1].end,
|
|
235
|
+
text=segment_text,
|
|
236
|
+
words=words.copy(),
|
|
237
|
+
speaker=words[0].speaker,
|
|
238
|
+
)
|
|
239
|
+
)
|
|
223
240
|
|
|
224
241
|
if time is not None:
|
|
225
|
-
|
|
226
|
-
current_words = []
|
|
227
|
-
current_start = None
|
|
242
|
+
current_words: list[TranscriptionWord] = []
|
|
228
243
|
|
|
229
244
|
for word in all_words:
|
|
230
|
-
if
|
|
231
|
-
|
|
245
|
+
if not current_words:
|
|
246
|
+
current_words = [word]
|
|
247
|
+
elif word.speaker != current_words[0].speaker or word.end - current_words[0].start > time:
|
|
248
|
+
_flush(current_words)
|
|
232
249
|
current_words = [word]
|
|
233
|
-
elif word.end - current_start <= time:
|
|
234
|
-
current_words.append(word)
|
|
235
250
|
else:
|
|
236
|
-
|
|
237
|
-
if current_words:
|
|
238
|
-
segment_text = " ".join(w.word for w in current_words)
|
|
239
|
-
standardized_segments.append(
|
|
240
|
-
TranscriptionSegment(
|
|
241
|
-
start=current_start,
|
|
242
|
-
end=current_words[-1].end,
|
|
243
|
-
text=segment_text,
|
|
244
|
-
words=current_words.copy(),
|
|
245
|
-
)
|
|
246
|
-
)
|
|
251
|
+
current_words.append(word)
|
|
247
252
|
|
|
248
|
-
|
|
249
|
-
current_start = word.start
|
|
250
|
-
current_words = [word]
|
|
253
|
+
_flush(current_words)
|
|
251
254
|
|
|
252
|
-
# Add final segment
|
|
253
|
-
if current_words:
|
|
254
|
-
segment_text = " ".join(w.word for w in current_words)
|
|
255
|
-
standardized_segments.append(
|
|
256
|
-
TranscriptionSegment(
|
|
257
|
-
start=current_start, # type: ignore
|
|
258
|
-
end=current_words[-1].end,
|
|
259
|
-
text=segment_text,
|
|
260
|
-
words=current_words.copy(),
|
|
261
|
-
)
|
|
262
|
-
)
|
|
263
255
|
elif num_words is not None:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
256
|
+
current_words = []
|
|
257
|
+
|
|
258
|
+
for word in all_words:
|
|
259
|
+
if not current_words:
|
|
260
|
+
current_words = [word]
|
|
261
|
+
elif word.speaker != current_words[0].speaker or len(current_words) >= num_words:
|
|
262
|
+
_flush(current_words)
|
|
263
|
+
current_words = [word]
|
|
264
|
+
else:
|
|
265
|
+
current_words.append(word)
|
|
266
|
+
|
|
267
|
+
_flush(current_words)
|
|
273
268
|
|
|
274
269
|
return Transcription(segments=standardized_segments, language=self.language)
|
|
275
270
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|