auto-editor 26.3.0__py3-none-any.whl → 26.3.2__py3-none-any.whl

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.
auto_editor/edit.py CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import os
4
4
  import sys
5
5
  from fractions import Fraction
6
+ from heapq import heappop, heappush
6
7
  from os.path import splitext
7
8
  from subprocess import run
8
9
  from typing import Any
@@ -296,7 +297,15 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
296
297
  def make_media(tl: v3, output_path: str) -> None:
297
298
  assert src is not None
298
299
 
299
- output = av.open(output_path, "w")
300
+ if args.fragmented and not args.no_fragmented:
301
+ log.debug("Enabling fragmented mp4/mov")
302
+ options = {
303
+ "movflags": "+default_base_moof+faststart+frag_keyframe+separate_moof",
304
+ "frag_duration": "0.2",
305
+ }
306
+ else:
307
+ options = {"movflags": "faststart"}
308
+ output = av.open(output_path, "w", options=options)
300
309
 
301
310
  if ctr.default_sub != "none" and not args.sn:
302
311
  sub_paths = make_new_subtitles(tl, log)
@@ -309,6 +318,13 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
309
318
  else:
310
319
  audio_paths = []
311
320
 
321
+ # Setup video
322
+ if ctr.default_vid != "none" and tl.v:
323
+ vframes = render_av(output, tl, args, log)
324
+ output_stream = next(vframes)
325
+ else:
326
+ output_stream, vframes = None, iter([])
327
+
312
328
  # Setup audio
313
329
  if audio_paths:
314
330
  try:
@@ -363,23 +379,16 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
363
379
  subtitle_streams.append(subtitle_stream)
364
380
  sub_gen_frames.append(subtitle_input.demux(subtitles=0))
365
381
 
366
- # Setup video
367
- if ctr.default_vid != "none" and tl.v:
368
- vframes = render_av(output, tl, args, log)
369
- output_stream = next(vframes)
370
- else:
371
- output_stream, vframes = None, iter([])
372
-
373
382
  no_color = log.no_color or log.machine
374
383
  encoder_titles = []
375
384
  if output_stream is not None:
376
385
  name = output_stream.codec.canonical_name
377
386
  encoder_titles.append(name if no_color else f"\033[95m{name}")
378
387
  if audio_streams:
379
- name = audio_streams[0].name
388
+ name = audio_streams[0].codec.canonical_name
380
389
  encoder_titles.append(name if no_color else f"\033[96m{name}")
381
390
  if subtitle_streams:
382
- name = subtitle_streams[0].name
391
+ name = subtitle_streams[0].codec.canonical_name
383
392
  encoder_titles.append(name if no_color else f"\033[32m{name}")
384
393
 
385
394
  title = f"({os.path.splitext(output_path)[1][1:]}) "
@@ -389,11 +398,71 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
389
398
  title += "\033[0m+".join(encoder_titles) + "\033[0m"
390
399
  bar.start(tl.end, title)
391
400
 
392
- # Process frames
401
+ MAX_AUDIO_AHEAD = 30 # In timebase, how far audio can be ahead of video.
402
+ MAX_SUB_AHEAD = 30
403
+
404
+ class Priority:
405
+ __slots__ = ("index", "frame_type", "frame", "stream")
406
+
407
+ def __init__(self, value: int | Fraction, frame, stream):
408
+ self.frame_type: str = stream.type
409
+ assert self.frame_type in ("audio", "subtitle", "video")
410
+ if self.frame_type in {"audio", "subtitle"}:
411
+ self.index: int | float = round(value * frame.time_base * tl.tb)
412
+ else:
413
+ self.index = float("inf") if value is None else int(value)
414
+ self.frame = frame
415
+ self.stream = stream
416
+
417
+ def __lt__(self, other):
418
+ return self.index < other.index
419
+
420
+ def __eq__(self, other):
421
+ return self.index == other.index
422
+
423
+ # Priority queue for ordered frames by time_base.
424
+ frame_queue: list[Priority] = []
425
+ latest_audio_index = float("-inf")
426
+ latest_sub_index = float("-inf")
427
+ earliest_video_index = None
428
+
393
429
  while True:
394
- audio_frames = [next(frames, None) for frames in audio_gen_frames]
430
+ if earliest_video_index is None:
431
+ should_get_audio = True
432
+ should_get_sub = True
433
+ else:
434
+ for item in frame_queue:
435
+ if item.frame_type == "audio":
436
+ latest_audio_index = max(latest_audio_index, item.index)
437
+ elif item.frame_type == "subtitle":
438
+ latest_sub_index = max(latest_sub_index, item.index)
439
+
440
+ should_get_audio = (
441
+ latest_audio_index <= earliest_video_index + MAX_AUDIO_AHEAD
442
+ )
443
+ should_get_sub = (
444
+ latest_sub_index <= earliest_video_index + MAX_SUB_AHEAD
445
+ )
446
+
395
447
  index, video_frame = next(vframes, (0, None))
396
- subtitle_frames = [next(packet, None) for packet in sub_gen_frames]
448
+
449
+ if video_frame:
450
+ earliest_video_index = index
451
+ heappush(frame_queue, Priority(index, video_frame, output_stream))
452
+
453
+ if should_get_audio:
454
+ audio_frames = [next(frames, None) for frames in audio_gen_frames]
455
+ if output_stream is None and audio_frames and audio_frames[-1]:
456
+ assert audio_frames[-1].time is not None
457
+ index = round(audio_frames[-1].time * tl.tb)
458
+ else:
459
+ audio_frames = [None]
460
+ if should_get_sub:
461
+ subtitle_frames = [next(packet, None) for packet in sub_gen_frames]
462
+ else:
463
+ subtitle_frames = [None]
464
+
465
+ # Break if no more frames
397
466
  if (
398
467
  all(frame is None for frame in audio_frames)
399
468
  and video_frame is None
@@ -401,29 +470,45 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
401
470
  ):
402
471
  break
403
472
 
404
- for audio_stream, audio_frame in zip(audio_streams, audio_frames):
405
- if audio_frame:
473
+ if should_get_audio:
474
+ for audio_stream, audio_frame in zip(audio_streams, audio_frames):
406
475
  for reframe in resampler.resample(audio_frame):
407
- output.mux(audio_stream.encode(reframe))
408
-
409
- for subtitle_stream, packet in zip(subtitle_streams, subtitle_frames):
410
- if not packet or packet.dts is None:
411
- continue
412
- packet.stream = subtitle_stream
413
- output.mux(packet)
414
-
415
- if video_frame:
476
+ assert reframe.pts is not None
477
+ heappush(
478
+ frame_queue,
479
+ Priority(reframe.pts, reframe, audio_stream),
480
+ )
481
+ if should_get_sub:
482
+ for subtitle_stream, packet in zip(subtitle_streams, subtitle_frames):
483
+ if packet and packet.pts is not None:
484
+ packet.stream = subtitle_stream
485
+ heappush(
486
+ frame_queue, Priority(packet.pts, packet, subtitle_stream)
487
+ )
488
+
489
+ while frame_queue and frame_queue[0].index <= index:
490
+ item = heappop(frame_queue)
491
+ frame_type = item.frame_type
492
+ bar_index = None
416
493
  try:
417
- output.mux(output_stream.encode(video_frame))
494
+ if frame_type in {"video", "audio"}:
495
+ if item.frame.time is not None:
496
+ bar_index = round(item.frame.time * tl.tb)
497
+ output.mux(item.stream.encode(item.frame))
498
+ elif frame_type == "subtitle":
499
+ output.mux(item.frame)
418
500
  except av.error.ExternalError:
419
501
  log.error(
420
- f"Generic error for encoder: {output_stream.name}\n"
421
- "Perhaps video quality settings are too low?"
502
+ f"Generic error for encoder: {item.stream.name}\n"
503
+ f"at {item.index} time_base\nPerhaps video quality settings are too low?"
422
504
  )
505
+ except av.FileNotFoundError:
506
+ log.error(f"File not found: {output_path}")
423
507
  except av.FFmpegError as e:
424
508
  log.error(e)
425
509
 
426
- bar.tick(index)
510
+ if bar_index:
511
+ bar.tick(bar_index)
427
512
 
428
513
  # Flush streams
429
514
  if output_stream is not None:
auto_editor/lang/palet.py CHANGED
@@ -9,11 +9,11 @@ from dataclasses import dataclass
9
9
  from difflib import get_close_matches
10
10
  from fractions import Fraction
11
11
  from io import StringIO
12
- from typing import TYPE_CHECKING
12
+ from typing import TYPE_CHECKING, cast
13
13
 
14
14
  import numpy as np
15
15
 
16
- from auto_editor.analyze import LevelError, mut_remove_small
16
+ from auto_editor.analyze import LevelError, Levels, mut_remove_small
17
17
  from auto_editor.lib.contracts import *
18
18
  from auto_editor.lib.data_structs import *
19
19
  from auto_editor.lib.err import MyError
@@ -21,7 +21,7 @@ from auto_editor.utils.func import boolop
21
21
 
22
22
  if TYPE_CHECKING:
23
23
  from collections.abc import Callable
24
- from typing import Any, NoReturn
24
+ from typing import Any, NoReturn, TypeGuard
25
25
 
26
26
  from numpy.typing import NDArray
27
27
 
@@ -510,15 +510,16 @@ def p_slice(
510
510
  return seq[start:end:step]
511
511
 
512
512
 
513
+ def is_boolean_array(v: object) -> TypeGuard[np.ndarray]:
514
+ return isinstance(v, np.ndarray) and v.dtype.kind == "b"
515
+
516
+
513
517
  is_iterable = Contract(
514
518
  "iterable?",
515
519
  lambda v: type(v) in {str, range, list, tuple, dict, Quoted}
516
520
  or isinstance(v, np.ndarray),
517
521
  )
518
- is_boolarr = Contract(
519
- "bool-array?",
520
- lambda v: isinstance(v, np.ndarray) and v.dtype.kind == "b",
521
- )
522
+ is_boolarr = Contract("bool-array?", is_boolean_array)
522
523
 
523
524
 
524
525
  def raise_(msg: str | Exception) -> NoReturn:
@@ -568,13 +569,10 @@ def edit_audio(
568
569
  if "@levels" not in env:
569
570
  raise MyError("Can't use `audio` if there's no input media")
570
571
 
571
- levels = env["@levels"]
572
- src = levels.src
573
- strict = levels.strict
574
-
572
+ levels = cast(Levels, env["@levels"])
575
573
  stream_data: NDArray[np.bool_] | None = None
576
574
  if stream == Sym("all"):
577
- stream_range = range(0, len(src.audios))
575
+ stream_range = range(0, len(levels.container.streams.audio))
578
576
  else:
579
577
  assert isinstance(stream, int)
580
578
  stream_range = range(stream, stream + 1)
@@ -586,17 +584,15 @@ def edit_audio(
586
584
  stream_data = audio_list
587
585
  else:
588
586
  stream_data = boolop(stream_data, audio_list, np.logical_or)
589
- except LevelError as e:
590
- raise_(e) if strict else levels.all()
591
-
592
- if stream_data is not None:
593
- mut_remove_small(stream_data, minclip, replace=1, with_=0)
594
- mut_remove_small(stream_data, mincut, replace=0, with_=1)
587
+ except LevelError:
588
+ return np.array([], dtype=np.bool_)
595
589
 
596
- return stream_data
590
+ if stream_data is None:
591
+ return np.array([], dtype=np.bool_)
597
592
 
598
- stream = 0 if stream == Sym("all") else stream
599
- return raise_(f"audio stream '{stream}' does not exist") if strict else levels.all()
593
+ mut_remove_small(stream_data, minclip, replace=1, with_=0)
594
+ mut_remove_small(stream_data, mincut, replace=0, with_=1)
595
+ return stream_data
600
596
 
601
597
 
602
598
  def edit_motion(
@@ -608,18 +604,18 @@ def edit_motion(
608
604
  if "@levels" not in env:
609
605
  raise MyError("Can't use `motion` if there's no input media")
610
606
 
611
- levels = env["@levels"]
607
+ levels = cast(Levels, env["@levels"])
612
608
  try:
613
609
  return levels.motion(stream, blur, width) >= threshold
614
- except LevelError as e:
615
- return raise_(e) if levels.strict else levels.all()
610
+ except LevelError:
611
+ return np.array([], dtype=np.bool_)
616
612
 
617
613
 
618
614
  def edit_subtitle(pattern, stream=0, **kwargs):
619
615
  if "@levels" not in env:
620
616
  raise MyError("Can't use `subtitle` if there's no input media")
621
617
 
622
- levels = env["@levels"]
618
+ levels = cast(Levels, env["@levels"])
623
619
  if "ignore-case" not in kwargs:
624
620
  kwargs["ignore-case"] = False
625
621
  if "max-count" not in kwargs:
@@ -628,8 +624,8 @@ def edit_subtitle(pattern, stream=0, **kwargs):
628
624
  max_count = kwargs["max-count"]
629
625
  try:
630
626
  return levels.subtitle(pattern, stream, ignore_case, max_count)
631
- except LevelError as e:
632
- return raise_(e) if levels.strict else levels.all()
627
+ except LevelError:
628
+ return np.array([], dtype=np.bool_)
633
629
 
634
630
 
635
631
  class StackTraceManager:
@@ -6,9 +6,9 @@ from typing import TYPE_CHECKING, NamedTuple
6
6
 
7
7
  import numpy as np
8
8
 
9
- from auto_editor.analyze import Levels
9
+ from auto_editor.analyze import initLevels
10
10
  from auto_editor.ffwrapper import FileInfo
11
- from auto_editor.lang.palet import Lexer, Parser, env, interpret, is_boolarr
11
+ from auto_editor.lang.palet import Lexer, Parser, env, interpret, is_boolean_array
12
12
  from auto_editor.lib.data_structs import print_str
13
13
  from auto_editor.lib.err import MyError
14
14
  from auto_editor.timeline import ASpace, TlAudio, TlVideo, VSpace, v1, v3
@@ -122,7 +122,6 @@ def make_timeline(
122
122
 
123
123
  has_loud = np.array([], dtype=np.bool_)
124
124
  src_index = np.array([], dtype=np.int32)
125
- concat = np.concatenate
126
125
 
127
126
  try:
128
127
  stdenv = __import__("auto_editor.lang.stdenv", fromlist=["lang"])
@@ -137,6 +136,7 @@ def make_timeline(
137
136
  parser = Parser(Lexer("config.pal", file.read()))
138
137
  interpret(env, parser)
139
138
 
139
+ results = []
140
140
  for i, src in enumerate(sources):
141
141
  try:
142
142
  parser = Parser(Lexer("`--edit`", args.edit))
@@ -144,32 +144,43 @@ def make_timeline(
144
144
  log.debug(f"edit: {parser}")
145
145
 
146
146
  env["timebase"] = tb
147
- env["src"] = f"{src.path}"
148
- env["@levels"] = Levels(src, tb, bar, args.no_cache, log, len(sources) < 2)
147
+ env["@levels"] = initLevels(src, tb, bar, args.no_cache, log)
149
148
 
150
- results = interpret(env, parser)
151
-
152
- if len(results) == 0:
149
+ inter_result = interpret(env, parser)
150
+ if len(inter_result) == 0:
153
151
  log.error("Expression in --edit must return a bool-array, got nothing")
154
152
 
155
- result = results[-1]
153
+ result = inter_result[-1]
156
154
  if callable(result):
157
155
  result = result()
158
156
  except MyError as e:
159
157
  log.error(e)
160
158
 
161
- if not is_boolarr(result):
159
+ if not is_boolean_array(result):
162
160
  log.error(
163
161
  f"Expression in --edit must return a bool-array, got {print_str(result)}"
164
162
  )
165
- assert isinstance(result, np.ndarray)
166
-
167
163
  mut_margin(result, start_margin, end_margin)
168
-
169
- has_loud = concat((has_loud, result))
170
- src_index = concat((src_index, np.full(len(result), i, dtype=np.int32)))
171
-
172
- assert len(has_loud) > 0
164
+ results.append(result)
165
+
166
+ if all(len(result) == 0 for result in results):
167
+ if "subtitle" in args.edit:
168
+ log.error("No file(s) have the selected subtitle stream.")
169
+ if "motion" in args.edit:
170
+ log.error("No file(s) have the selected video stream.")
171
+ if "audio" in args.edit:
172
+ log.error("No file(s) have the selected audio stream.")
173
+
174
+ src_indexes = []
175
+ for i in range(0, len(results)):
176
+ if len(results[i]) == 0:
177
+ results[i] = initLevels(sources[i], tb, bar, args.no_cache, log).all()
178
+ src_indexes.append(np.full(len(results[i]), i, dtype=np.int32))
179
+
180
+ has_loud = np.concatenate(results)
181
+ src_index = np.concatenate(src_indexes)
182
+ if len(has_loud) == 0:
183
+ log.error("Empty timeline. Nothing to do.")
173
184
 
174
185
  # Setup for handling custom speeds
175
186
  speed_index = has_loud.astype(np.uint)
auto_editor/preview.py CHANGED
@@ -5,7 +5,7 @@ from fractions import Fraction
5
5
  from statistics import fmean, median
6
6
  from typing import TextIO
7
7
 
8
- from auto_editor.analyze import Levels
8
+ from auto_editor.analyze import initLevels
9
9
  from auto_editor.timeline import v3
10
10
  from auto_editor.utils.bar import initBar
11
11
  from auto_editor.utils.func import to_timecode
@@ -64,8 +64,9 @@ def preview(tl: v3, log: Log) -> None:
64
64
  all_sources.add(aclip.src)
65
65
 
66
66
  in_len = 0
67
+ bar = initBar("none")
67
68
  for src in all_sources:
68
- in_len += Levels(src, tb, initBar("none"), False, log, False).media_length
69
+ in_len += initLevels(src, tb, bar, False, log).media_length
69
70
 
70
71
  out_len = tl.out_len()
71
72
 
auto_editor/utils/bar.py CHANGED
@@ -89,20 +89,26 @@ class Bar:
89
89
  percent = round(progress * 100, 1)
90
90
  p_pad = " " * (4 - len(str(percent)))
91
91
  columns = get_terminal_size().columns
92
- bar_len = max(1, columns - len_title - 32)
92
+ bar_len = max(1, columns - len_title - 35)
93
93
  bar_str = self._bar_str(progress, bar_len)
94
94
 
95
- bar = f" {self.icon}{title} {bar_str} {p_pad}{percent}% ETA {new_time}"
96
-
97
- if len(bar) > columns - 2:
98
- bar = bar[: columns - 2]
99
- else:
100
- bar += " " * (columns - len(bar) - 4)
101
-
102
- sys.stdout.write(bar + "\r")
95
+ bar = f" {self.icon}{title} {bar_str} {p_pad}{percent}% ETA {new_time} \r"
96
+ sys.stdout.write(bar)
103
97
 
104
98
  def start(self, total: float, title: str = "Please wait") -> None:
105
- self.stack.append((title, len(title), total, time()))
99
+ len_title = 0
100
+ in_escape = False
101
+
102
+ for char in title:
103
+ if not in_escape:
104
+ if char == "\033":
105
+ in_escape = True
106
+ else:
107
+ len_title += 1
108
+ elif char == "m":
109
+ in_escape = False
110
+
111
+ self.stack.append((title, len_title, total, time()))
106
112
 
107
113
  try:
108
114
  self.tick(0)
auto_editor/utils/log.py CHANGED
@@ -1,14 +1,9 @@
1
- from __future__ import annotations
2
-
3
1
  import sys
4
2
  from datetime import timedelta
5
3
  from shutil import get_terminal_size, rmtree
6
4
  from tempfile import mkdtemp
7
5
  from time import perf_counter, sleep
8
- from typing import TYPE_CHECKING, NoReturn
9
-
10
- if TYPE_CHECKING:
11
- import av
6
+ from typing import NoReturn
12
7
 
13
8
 
14
9
  class Log:
@@ -100,10 +95,6 @@ class Log:
100
95
 
101
96
  sys.stdout.write(f"Finished. took {second_len} seconds ({minute_len})\n")
102
97
 
103
- def experimental(self, codec: av.Codec) -> None:
104
- if codec.experimental:
105
- self.error(f"`{codec.name}` is an experimental codec")
106
-
107
98
  @staticmethod
108
99
  def deprecated(message: str) -> None:
109
100
  sys.stderr.write(f"\033[1m\033[33m{message}\033[0m\n")
@@ -201,6 +201,8 @@ class Args:
201
201
  vprofile: str | None = None
202
202
  audio_bitrate: str = "auto"
203
203
  scale: float = 1.0
204
+ fragmented: bool = False
205
+ no_fragmented: bool = False
204
206
  sn: bool = False
205
207
  dn: bool = False
206
208
  no_seek: bool = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: auto-editor
3
- Version: 26.3.0
3
+ Version: 26.3.2
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -12,7 +12,7 @@ Requires-Python: <3.14,>=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: numpy<3.0,>=1.24
15
- Requires-Dist: pyav==14.2.0
15
+ Requires-Dist: pyav==14.2.*
16
16
 
17
17
  <p align="center"><img src="https://auto-editor.com/img/auto-editor-banner.webp" title="Auto-Editor" width="700"></p>
18
18
 
@@ -1,12 +1,12 @@
1
- auto_editor/__init__.py,sha256=foFMRrw-Br8Dq0ZIes1siNTsxZj5nQywX_cfWHfB380,23
2
- auto_editor/__main__.py,sha256=g-9q3i6oFeNPeFMeNJEwZua6ZloOPLcaLN-B0FlxWXo,11371
3
- auto_editor/analyze.py,sha256=7p_SDRRKQzlc6wtoW6MXYvJkDV4NO4DSOtJPFAuYcwM,12721
4
- auto_editor/edit.py,sha256=gz2MocM5s1uz-hLrFWmiNl1TwOm4S_1EkYn-ZBuu_sE,17401
1
+ auto_editor/__init__.py,sha256=bDmtpN3tJ_1R818Djb_PZ7H7ZIgSCXSdbTx5GJ009SU,23
2
+ auto_editor/__main__.py,sha256=BQRStgcJxNVUOisP4JpcaxaM_KTGFS04Qgklyy9fumc,12012
3
+ auto_editor/analyze.py,sha256=YY3Io-IDdlliawx03w-L9AW5nZSOtmbXX_la1HanYmY,12658
4
+ auto_editor/edit.py,sha256=wjs1qj13Rx8JGgf7b_dPgVEmd_vDyU-N5GDQhVGSJX8,21130
5
5
  auto_editor/ffwrapper.py,sha256=1lYYfq8gVgMVkYWeAEYDPAHCwFCYbKQwy0FxYBxMzk8,4765
6
6
  auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
7
- auto_editor/make_layers.py,sha256=vEeJt0PnE1vc9-cQZ_AlXVDjvWhObRCWJSCQGraoMvU,9016
7
+ auto_editor/make_layers.py,sha256=Yl4xYkMHume_VvRW8Ii48hyx-ZtFr8WMmVYXJMSUmg0,9581
8
8
  auto_editor/output.py,sha256=ho8Lpqz4Sv_Gw0Vj2OvG39s83xHpyZlvtRNryTPbXqc,2563
9
- auto_editor/preview.py,sha256=HUsjmV9Fx73rZ26BXrpz9z-z_e4oiui3u9e7qbbGoBY,3037
9
+ auto_editor/preview.py,sha256=cqQdozM2IB-5qXHNxeqiSrSdEIzlMfjD4SU-NX9sYZ0,3052
10
10
  auto_editor/timeline.py,sha256=XfaH9cH-RB-MObOpMr5IfLcqJcjmabO1XwkUkT3_FQM,8186
11
11
  auto_editor/vanparse.py,sha256=Ug5A2QaRqGiw4l55Z_h9T2QU1x0WqRibR7yY5rQ0WTk,10002
12
12
  auto_editor/wavfile.py,sha256=afFfje8cK9lFjIkYoBbQHfvQIpsEPxWvspCtFhUKlAw,9499
@@ -14,11 +14,11 @@ auto_editor/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
14
14
  auto_editor/cmds/cache.py,sha256=bViYbtVXefTeEIUvSanDfA6cG35ep1N_Jvtz7ZjgIkY,1959
15
15
  auto_editor/cmds/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
16
16
  auto_editor/cmds/info.py,sha256=vYa1hYdE8kDTE8AS3kwXlnd59X6CrE2GtIEQ7UmlpRY,7010
17
- auto_editor/cmds/levels.py,sha256=qMxTOlj4ezna0K2jYWDFyZ0Srn4pT6nIdR-479IOEvw,5758
17
+ auto_editor/cmds/levels.py,sha256=6LqXfNBca__KGeA8kJqJqH0MBR3GudaFoWU0_dnv5bI,5635
18
18
  auto_editor/cmds/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
19
- auto_editor/cmds/repl.py,sha256=TF_I7zsFY7-KdgidrqjafTz7o_eluVbLvgTcOBG-UWQ,3449
19
+ auto_editor/cmds/repl.py,sha256=8DgMw-XyfR5XctSmwtk4_1-zxs3ooMs72BfMRlVqLvY,3412
20
20
  auto_editor/cmds/subdump.py,sha256=af_XBf7kaevqHn1A71z8C-7x8pS5WKD9FE_ugkCw6rk,665
21
- auto_editor/cmds/test.py,sha256=Wih-KQnv5Ld1CgbOUdtrp_fMnINsr_khzONvlrJ-bhw,26163
21
+ auto_editor/cmds/test.py,sha256=SzjtsEteztXFbkvLRfL9BPSgK8Qez_-yXPR5ts_tu9E,26465
22
22
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  auto_editor/formats/fcp11.py,sha256=sqjC36jI47ICPLjZJYiqGwY7foOnWOiNjkPFLdgSnI4,5208
24
24
  auto_editor/formats/fcp7.py,sha256=x5cagTzGCAW3i3M6m7TZC1h8gLfSmX1UK-iiDuCpdfs,20289
@@ -29,7 +29,7 @@ auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
29
29
  auto_editor/lang/json.py,sha256=D84vwyLtX5t5xl8S4r4jFXGVkbIg1L4IcexlS_a_k6w,9231
30
30
  auto_editor/lang/libintrospection.py,sha256=6H1rGp0wqaCud5IPaoEmzULGnYt6ec7_0h32ATcw2oY,261
31
31
  auto_editor/lang/libmath.py,sha256=z33A161Oe6vYYK7R6pgYjdZZe63dQkN38Qf36TL3prg,847
32
- auto_editor/lang/palet.py,sha256=ZOzNxLfzY8FTCYlF_mZtU2XoNzdg-eseUVkMtnWkVaI,24139
32
+ auto_editor/lang/palet.py,sha256=XKMVMybHZK5c_iB368T4pFqI7Yfl5o4eDVa5XMNLEow,24082
33
33
  auto_editor/lang/stdenv.py,sha256=sQyOD3bttjcieWMtTMNpE6blEZriAfHtSZHbuvC09R8,43762
34
34
  auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,7586
@@ -40,17 +40,17 @@ auto_editor/render/audio.py,sha256=_GuX0WNY1YeumgBN3bWqgwVXiuhpvx7sijABxqyO2ag,1
40
40
  auto_editor/render/subtitle.py,sha256=jtNRKvgo1fpHTrAfGZqdkNeNgGgasw-K-4PwIKiWwfM,6231
41
41
  auto_editor/render/video.py,sha256=JBVl8w-hQ6zrs97iA527LPsBZ9s601SVSJs2bSMCq88,12185
42
42
  auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- auto_editor/utils/bar.py,sha256=0ZSfuWdBA1zySwodkb4mw5uahC6UwCk2umaGnB8A7n0,3996
43
+ auto_editor/utils/bar.py,sha256=Ky9JRf37JTgLyvNuIXDfucaUE8H1vBbCqKLjttmsmmo,4156
44
44
  auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
45
45
  auto_editor/utils/cmdkw.py,sha256=aUGBvBel2Ko1o6Rwmr4rEL-BMc5hEnzYLbyZ1GeJdcY,5729
46
46
  auto_editor/utils/container.py,sha256=C_Ahh7nlMEX4DNQ2M_cITPPbYcIL68r4I_AgFy0OD6o,2487
47
47
  auto_editor/utils/func.py,sha256=C8ucgsSEzPyBc-8obhsCXd_uQW0cnCdBn1KVxB7FHjU,2747
48
- auto_editor/utils/log.py,sha256=8fOdyTG3vjKhA1tJTMKRjXVqhrY2q3tFnXU8tKm_twA,3937
49
- auto_editor/utils/types.py,sha256=r5f6QB81xH7NRwGntITIOCVx-fupOl8l3X3LSFkt3nE,10756
48
+ auto_editor/utils/log.py,sha256=wPNf6AabV-0cnoS_bPLv1Lh7llQBtNqPKeh07einOuc,3701
49
+ auto_editor/utils/types.py,sha256=HxFafTgmL_5HQbfR7AR_4Q6o8iHk1z-T6WT90PgQHiY,10817
50
50
  docs/build.py,sha256=POy8X8QOBYe_8A8HI_yiVI_Qg9E5mLpn1z7AHQr0_vQ,1888
51
- auto_editor-26.3.0.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
52
- auto_editor-26.3.0.dist-info/METADATA,sha256=YOFNFBSIMQveLah-PSIrBtlfY-xwEveHxKNAneUAs_0,6111
53
- auto_editor-26.3.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
54
- auto_editor-26.3.0.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
- auto_editor-26.3.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
- auto_editor-26.3.0.dist-info/RECORD,,
51
+ auto_editor-26.3.2.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
52
+ auto_editor-26.3.2.dist-info/METADATA,sha256=lxdGmJqB_b7J1myUG1CFUnXFKo0Vt2Kr8cG7rl58xK8,6111
53
+ auto_editor-26.3.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
54
+ auto_editor-26.3.2.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
+ auto_editor-26.3.2.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
+ auto_editor-26.3.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5