auto-editor 26.0.0__py3-none-any.whl → 26.1.0__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.
@@ -2,13 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import io
4
4
  from pathlib import Path
5
- from platform import system
6
- from subprocess import PIPE
7
5
 
8
6
  import av
9
7
  import numpy as np
8
+ from av.filter.loudnorm import stats
10
9
 
11
- from auto_editor.ffwrapper import FFmpeg, FileInfo
10
+ from auto_editor.ffwrapper import FileInfo
12
11
  from auto_editor.lang.json import Lexer, Parser
13
12
  from auto_editor.lang.palet import env
14
13
  from auto_editor.lib.contracts import andc, between_c, is_int_or_float
@@ -17,6 +16,7 @@ from auto_editor.output import Ensure
17
16
  from auto_editor.timeline import TlAudio, v3
18
17
  from auto_editor.utils.bar import Bar
19
18
  from auto_editor.utils.cmdkw import ParserError, parse_with_palet, pAttr, pAttrs
19
+ from auto_editor.utils.container import Container
20
20
  from auto_editor.utils.log import Log
21
21
  from auto_editor.utils.types import Args
22
22
  from auto_editor.wavfile import AudioData, read, write
@@ -56,25 +56,11 @@ def parse_norm(norm: str, log: Log) -> dict | None:
56
56
  log.error(e)
57
57
 
58
58
 
59
- def parse_ebu_bytes(norm: dict, stderr: bytes, log: Log) -> tuple[str, str]:
60
- start = end = 0
61
- lines = stderr.splitlines()
62
-
63
- for index, line in enumerate(lines):
64
- if line.startswith(b"[Parsed_loudnorm"):
65
- start = index + 1
66
- continue
67
- if start != 0 and line.startswith(b"}"):
68
- end = index + 1
69
- break
70
-
71
- if start == 0 or end == 0:
72
- log.error(f"Invalid loudnorm stats.\n{stderr!r}")
73
-
59
+ def parse_ebu_bytes(norm: dict, stat: bytes, log: Log) -> tuple[str, str]:
74
60
  try:
75
- parsed = Parser(Lexer("loudnorm", b"\n".join(lines[start:end]))).expr()
61
+ parsed = Parser(Lexer("loudnorm", stat)).expr()
76
62
  except MyError:
77
- log.error(f"Invalid loudnorm stats.\n{start=},{end=}\n{stderr!r}")
63
+ log.error(f"Invalid loudnorm stats.\n{stat!r}")
78
64
 
79
65
  for key in ("input_i", "input_tp", "input_lra", "input_thresh", "target_offset"):
80
66
  val = float(parsed[key])
@@ -101,30 +87,17 @@ def parse_ebu_bytes(norm: dict, stderr: bytes, log: Log) -> tuple[str, str]:
101
87
 
102
88
 
103
89
  def apply_audio_normalization(
104
- ffmpeg: FFmpeg, norm: dict, pre_master: Path, path: Path, log: Log
90
+ norm: dict, pre_master: Path, path: Path, log: Log
105
91
  ) -> None:
106
92
  if norm["tag"] == "ebu":
107
93
  first_pass = (
108
- f"loudnorm=i={norm['i']}:lra={norm['lra']}:tp={norm['tp']}:"
109
- f"offset={norm['gain']}:print_format=json"
94
+ f"i={norm['i']}:lra={norm['lra']}:tp={norm['tp']}:" f"offset={norm['gain']}"
110
95
  )
111
96
  log.debug(f"audio norm first pass: {first_pass}")
112
- file_null = "NUL" if system() in ("Windows", "cli") else "/dev/null"
113
- cmd = [
114
- "-hide_banner",
115
- "-i",
116
- f"{pre_master}",
117
- "-af",
118
- first_pass,
119
- "-vn",
120
- "-sn",
121
- "-f",
122
- "null",
123
- file_null,
124
- ]
125
- process = ffmpeg.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
126
- stderr = process.communicate()[1]
127
- name, filter_args = parse_ebu_bytes(norm, stderr, log)
97
+ with av.open(f"{pre_master}") as container:
98
+ stats_ = stats(first_pass, container.streams.audio[0])
99
+
100
+ name, filter_args = parse_ebu_bytes(norm, stats_, log)
128
101
  else:
129
102
  assert "t" in norm
130
103
 
@@ -238,12 +211,84 @@ def process_audio_clip(
238
211
  return read(output_bytes)[1]
239
212
 
240
213
 
214
+ def mix_audio_files(sr: int, audio_paths: list[str], output_path: str) -> None:
215
+ mixed_audio = None
216
+ max_length = 0
217
+
218
+ # First pass: determine the maximum length
219
+ for path in audio_paths:
220
+ container = av.open(path)
221
+ stream = container.streams.audio[0]
222
+
223
+ # Calculate duration in samples
224
+ assert stream.duration is not None
225
+ assert stream.time_base is not None
226
+ duration_samples = int(stream.duration * sr / stream.time_base.denominator)
227
+ max_length = max(max_length, duration_samples)
228
+ container.close()
229
+
230
+ # Second pass: read and mix audio
231
+ for path in audio_paths:
232
+ container = av.open(path)
233
+ stream = container.streams.audio[0]
234
+
235
+ resampler = av.audio.resampler.AudioResampler(
236
+ format="s16", layout="mono", rate=sr
237
+ )
238
+
239
+ audio_array: list[np.ndarray] = []
240
+ for frame in container.decode(audio=0):
241
+ frame.pts = None
242
+ resampled = resampler.resample(frame)[0]
243
+ audio_array.extend(resampled.to_ndarray().flatten())
244
+
245
+ # Pad or truncate to max_length
246
+ current_audio = np.array(audio_array[:max_length])
247
+ if len(current_audio) < max_length:
248
+ current_audio = np.pad(
249
+ current_audio, (0, max_length - len(current_audio)), "constant"
250
+ )
251
+
252
+ if mixed_audio is None:
253
+ mixed_audio = current_audio.astype(np.float32)
254
+ else:
255
+ mixed_audio += current_audio.astype(np.float32)
256
+
257
+ container.close()
258
+
259
+ if mixed_audio is None:
260
+ raise ValueError("mixed_audio is None")
261
+
262
+ # Normalize the mixed audio
263
+ max_val = np.max(np.abs(mixed_audio))
264
+ if max_val > 0:
265
+ mixed_audio = mixed_audio * (32767 / max_val)
266
+ mixed_audio = mixed_audio.astype(np.int16) # type: ignore
267
+
268
+ output_container = av.open(output_path, mode="w")
269
+ output_stream = output_container.add_stream("pcm_s16le", rate=sr)
270
+
271
+ chunk_size = sr # Process 1 second at a time
272
+ for i in range(0, len(mixed_audio), chunk_size):
273
+ # Shape becomes (1, samples) for mono
274
+ chunk = np.array([mixed_audio[i : i + chunk_size]])
275
+
276
+ frame = av.AudioFrame.from_ndarray(chunk, format="s16", layout="mono")
277
+ frame.rate = sr
278
+ frame.pts = i # Set presentation timestamp
279
+
280
+ output_container.mux(output_stream.encode(frame))
281
+
282
+ output_container.mux(output_stream.encode(None))
283
+ output_container.close()
284
+
285
+
241
286
  def make_new_audio(
242
- tl: v3, ensure: Ensure, args: Args, ffmpeg: FFmpeg, bar: Bar, log: Log
287
+ tl: v3, ctr: Container, ensure: Ensure, args: Args, bar: Bar, log: Log
243
288
  ) -> list[str]:
244
289
  sr = tl.sr
245
290
  tb = tl.tb
246
- output = []
291
+ output: list[str] = []
247
292
  samples: dict[tuple[FileInfo, int], AudioData] = {}
248
293
 
249
294
  norm = parse_norm(args.audio_normalize, log)
@@ -313,7 +358,7 @@ def make_new_audio(
313
358
  with open(pre_master, "wb") as fid:
314
359
  write(fid, sr, arr)
315
360
 
316
- apply_audio_normalization(ffmpeg, norm, pre_master, path, log)
361
+ apply_audio_normalization(norm, pre_master, path, log)
317
362
 
318
363
  bar.end()
319
364
 
@@ -321,4 +366,9 @@ def make_new_audio(
321
366
  Path(temp, "asdf.map").unlink(missing_ok=True)
322
367
  except PermissionError:
323
368
  pass
369
+
370
+ if not (args.keep_tracks_separate and ctr.max_audios is None) and len(output) > 1:
371
+ new_a_file = f"{Path(temp, 'new_audio.wav')}"
372
+ mix_audio_files(sr, output, new_a_file)
373
+ return [new_a_file]
324
374
  return output
@@ -157,12 +157,12 @@ def make_srt(input_: Input, stream: int) -> str:
157
157
  return output_bytes.getvalue()
158
158
 
159
159
 
160
- def _ensure(input_: Input, format: str, stream: int, log: Log) -> str:
160
+ def _ensure(input_: Input, format: str, stream: int) -> str:
161
161
  output_bytes = io.BytesIO()
162
162
  output = av.open(output_bytes, "w", format=format)
163
163
 
164
164
  in_stream = input_.streams.subtitles[stream]
165
- out_stream = output.add_stream(template=in_stream)
165
+ out_stream = output.add_stream_from_template(in_stream)
166
166
 
167
167
  for packet in input_.demux(in_stream):
168
168
  if packet.dts is None:
@@ -187,7 +187,9 @@ def make_new_subtitles(tl: v3, log: Log) -> list[str]:
187
187
  continue
188
188
 
189
189
  parser = SubtitleParser(tl.tb)
190
- if sub.codec in ("webvtt", "ass", "ssa"):
190
+ if sub.codec == "ssa":
191
+ format = "ass"
192
+ elif sub.codec in ("webvtt", "ass"):
191
193
  format = sub.codec
192
194
  else:
193
195
  log.error(f"Unknown subtitle codec: {sub.codec}")
@@ -195,8 +197,8 @@ def make_new_subtitles(tl: v3, log: Log) -> list[str]:
195
197
  if sub.codec == "mov_text":
196
198
  ret = make_srt(input_, s)
197
199
  else:
198
- ret = _ensure(input_, format, s, log)
199
- parser.parse(ret, sub.codec)
200
+ ret = _ensure(input_, format, s)
201
+ parser.parse(ret, format)
200
202
  parser.edit(tl.v1.chunks)
201
203
 
202
204
  new_path = os.path.join(log.temp, f"new{s}s.{sub.ext}")
@@ -8,7 +8,6 @@ import numpy as np
8
8
 
9
9
  from auto_editor.output import parse_bitrate
10
10
  from auto_editor.timeline import TlImage, TlRect, TlVideo
11
- from auto_editor.utils.types import color
12
11
 
13
12
  if TYPE_CHECKING:
14
13
  from collections.abc import Iterator
@@ -203,7 +202,7 @@ def render_av(
203
202
 
204
203
  bar.start(tl.end, "Creating new video")
205
204
 
206
- bg = color(args.background)
205
+ bg = args.background
207
206
  null_frame = make_solid(target_width, target_height, target_pix_fmt, bg)
208
207
  frame_index = -1
209
208
 
@@ -163,6 +163,8 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
163
163
  file_info[file]["subtitle"].append(sub)
164
164
 
165
165
  if args.json:
166
+ if sys.platform == "win32":
167
+ sys.stdout.reconfigure(encoding="utf-8")
166
168
  dump(file_info, sys.stdout, indent=4)
167
169
  return
168
170
 
@@ -5,9 +5,10 @@ from dataclasses import dataclass, field
5
5
  from fractions import Fraction
6
6
  from typing import TYPE_CHECKING
7
7
 
8
+ import av
8
9
  import numpy as np
9
10
 
10
- from auto_editor.analyze import LevelError, Levels, iter_audio, iter_motion
11
+ from auto_editor.analyze import *
11
12
  from auto_editor.ffwrapper import initFileInfo
12
13
  from auto_editor.lang.palet import env
13
14
  from auto_editor.lib.contracts import is_bool, is_nat, is_nat1, is_str, is_void, orc
@@ -130,9 +131,19 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
130
131
  levels = Levels(src, tb, bar, False, log, strict=True)
131
132
  try:
132
133
  if method == "audio":
133
- print_arr_gen(iter_audio(src, tb, **obj))
134
+ container = av.open(src.path, "r")
135
+ audio_stream = container.streams.audio[obj["stream"]]
136
+ log.experimental(audio_stream.codec)
137
+ print_arr_gen(iter_audio(audio_stream, tb))
138
+ container.close()
139
+
134
140
  elif method == "motion":
135
- print_arr_gen(iter_motion(src, tb, **obj))
141
+ container = av.open(src.path, "r")
142
+ video_stream = container.streams.video[obj["stream"]]
143
+ log.experimental(video_stream.codec)
144
+ print_arr_gen(iter_motion(video_stream, tb, obj["blur"], obj["width"]))
145
+ container.close()
146
+
136
147
  elif method == "subtitle":
137
148
  print_arr(levels.subtitle(**obj))
138
149
  elif method == "none":
@@ -360,7 +360,7 @@ def main(sys_args: list[str] | None = None):
360
360
  """{"version": "1", "source": "example.mp4", "chunks": [ [0, 26, 1.0], [26, 34, 0] ]}"""
361
361
  )
362
362
 
363
- return run.main(["v1.json"], [])
363
+ return "v1.json", run.main(["v1.json"], [])
364
364
 
365
365
  def premiere_named_export():
366
366
  run.main(["example.mp4"], ["--export", 'premiere:name="Foo Bar"'])
auto_editor/timeline.py CHANGED
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
  from auto_editor.ffwrapper import initFileInfo, mux
7
7
  from auto_editor.lib.contracts import *
8
8
  from auto_editor.utils.cmdkw import Required, pAttr, pAttrs
9
- from auto_editor.utils.types import color, natural, number, threshold
9
+ from auto_editor.utils.types import natural, number, parse_color, threshold
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from collections.abc import Iterator
@@ -165,7 +165,7 @@ rect_builder = pAttrs(
165
165
  pAttr("y", Required, is_int, int),
166
166
  pAttr("width", Required, is_int, int),
167
167
  pAttr("height", Required, is_int, int),
168
- pAttr("fill", "#c4c4c4", is_str, color),
168
+ pAttr("fill", "#c4c4c4", is_str, parse_color),
169
169
  )
170
170
  visual_objects = {
171
171
  "rect": (TlRect, rect_builder),
@@ -35,11 +35,6 @@ class pAttrs:
35
35
  self.attrs = attrs
36
36
 
37
37
 
38
- def _norm_name(s: str) -> str:
39
- # Python does not allow - in variable names
40
- return s.replace("-", "_")
41
-
42
-
43
38
  class PLexer:
44
39
  __slots__ = ("text", "pos", "char")
45
40
 
@@ -101,6 +96,10 @@ def parse_with_palet(
101
96
  KEYWORD_SEP = "="
102
97
  kwargs: dict[str, Any] = {}
103
98
 
99
+ def _norm_name(s: str) -> str:
100
+ # Python does not allow - in variable names
101
+ return s.replace("-", "_")
102
+
104
103
  def go(text: str, c: Any) -> Any:
105
104
  try:
106
105
  env = _env if isinstance(_env, Env) else Env(_env)
@@ -174,9 +173,7 @@ def parse_with_palet(
174
173
  return kwargs
175
174
 
176
175
 
177
- def parse_method(
178
- name: str, text: str, env: Env
179
- ) -> tuple[str, list[Any], dict[str, Any]]:
176
+ def parse_method(name: str, text: str) -> tuple[str, list[Any], dict[str, Any]]:
180
177
  from auto_editor.lang.palet import Lexer, Parser
181
178
 
182
179
  # Positional Arguments
@@ -55,12 +55,9 @@ def codec_type(x: str) -> str:
55
55
  return "subtitle"
56
56
 
57
57
  try:
58
- return Codec(x, "r").type
58
+ return Codec(x, "w").type
59
59
  except Exception:
60
- try:
61
- return Codec(x, "w").type
62
- except Exception:
63
- return ""
60
+ return ""
64
61
 
65
62
 
66
63
  def container_constructor(ext: str) -> Container:
auto_editor/utils/func.py CHANGED
@@ -81,21 +81,10 @@ def mut_margin(arr: BoolList, start_m: int, end_m: int) -> None:
81
81
  arr[max(i + end_m, 0) : i] = False
82
82
 
83
83
 
84
- def merge(start_list: np.ndarray, end_list: np.ndarray) -> BoolList:
85
- result = np.zeros((len(start_list)), dtype=np.bool_)
86
-
87
- for i, item in enumerate(start_list):
88
- if item == True:
89
- where = np.where(end_list[i:])[0]
90
- if len(where) > 0:
91
- result[i : where[0]] = True
92
- return result
93
-
94
-
95
84
  def get_stdout(cmd: list[str]) -> str:
96
85
  from subprocess import DEVNULL, PIPE, Popen
97
86
 
98
- stdout, _ = Popen(cmd, stdin=DEVNULL, stdout=PIPE, stderr=PIPE).communicate()
87
+ stdout = Popen(cmd, stdin=DEVNULL, stdout=PIPE, stderr=PIPE).communicate()[0]
99
88
  return stdout.decode("utf-8", "replace")
100
89
 
101
90
 
@@ -116,25 +105,3 @@ def aspect_ratio(width: int, height: int) -> tuple[int, int]:
116
105
 
117
106
  c = gcd(width, height)
118
107
  return width // c, height // c
119
-
120
-
121
- def human_readable_time(time_in_secs: float) -> str:
122
- units = "seconds"
123
- if time_in_secs >= 3600:
124
- time_in_secs = round(time_in_secs / 3600, 1)
125
- if time_in_secs % 1 == 0:
126
- time_in_secs = round(time_in_secs)
127
- units = "hours"
128
- if time_in_secs >= 60:
129
- time_in_secs = round(time_in_secs / 60, 1)
130
- if time_in_secs >= 10 or time_in_secs % 1 == 0:
131
- time_in_secs = round(time_in_secs)
132
- units = "minutes"
133
- return f"{time_in_secs} {units}"
134
-
135
-
136
- def append_filename(path: str, val: str) -> str:
137
- from os.path import splitext
138
-
139
- root, ext = splitext(path)
140
- return root + val + ext
auto_editor/utils/log.py CHANGED
@@ -7,6 +7,8 @@ from tempfile import mkdtemp
7
7
  from time import perf_counter, sleep
8
8
  from typing import NoReturn
9
9
 
10
+ import av
11
+
10
12
 
11
13
  class Log:
12
14
  __slots__ = ("is_debug", "quiet", "machine", "no_color", "_temp", "_ut", "_s")
@@ -97,6 +99,10 @@ class Log:
97
99
 
98
100
  sys.stdout.write(f"Finished. took {second_len} seconds ({minute_len})\n")
99
101
 
102
+ def experimental(self, codec: av.Codec) -> None:
103
+ if codec.experimental:
104
+ self.error(f"`{codec.name}` is an experimental codec")
105
+
100
106
  def error(self, message: str | Exception) -> NoReturn:
101
107
  if self.is_debug and isinstance(message, Exception):
102
108
  self.cleanup()
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import re
4
4
  from dataclasses import dataclass, field
5
5
  from fractions import Fraction
6
- from typing import Literal
7
6
 
8
7
 
9
8
  class CoerceError(Exception):
@@ -156,16 +155,7 @@ def speed_range(val: str) -> tuple[float, str, str]:
156
155
  return number(a[0]), a[1], a[2]
157
156
 
158
157
 
159
- Stream = int | Literal["all"]
160
-
161
-
162
- def stream(val: str) -> Stream:
163
- if val == "all" or val == "'all":
164
- return "all"
165
- return natural(val)
166
-
167
-
168
- def color(val: str) -> str:
158
+ def parse_color(val: str) -> str:
169
159
  """
170
160
  Convert a color str into an RGB tuple
171
161
 
@@ -219,22 +209,18 @@ class Args:
219
209
  frame_rate: Fraction | None = None
220
210
  sample_rate: int | None = None
221
211
  resolution: tuple[int, int] | None = None
222
- background: str = "#000"
223
- edit_based_on: str = "audio"
212
+ background: str = "#000000"
213
+ edit: str = "audio"
224
214
  keep_tracks_separate: bool = False
225
215
  audio_normalize: str = "#f"
226
216
  export: str | None = None
227
217
  player: str | None = None
228
218
  no_open: bool = False
229
219
  temp_dir: str | None = None
230
- ffmpeg_location: str | None = None
231
- my_ffmpeg: bool = False
232
220
  progress: str = "modern"
233
221
  version: bool = False
234
222
  debug: bool = False
235
223
  config: bool = False
236
- show_ffmpeg_commands: bool = False
237
- show_ffmpeg_output: bool = False
238
224
  quiet: bool = False
239
225
  preview: bool = False
240
226
  no_cache: bool = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-editor
3
- Version: 26.0.0
3
+ Version: 26.1.0
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -11,9 +11,8 @@ Keywords: video,audio,media,editor,editing,processing,nonlinear,automatic,silenc
11
11
  Requires-Python: <3.14,>=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: numpy <3.0,>=1.23.0
15
- Requires-Dist: pyav ==13.1.*
16
- Requires-Dist: ae-ffmpeg ==1.2.*
14
+ Requires-Dist: numpy<3.0,>=1.24
15
+ Requires-Dist: pyav==14.*
17
16
 
18
17
  <p align="center"><img src="https://auto-editor.com/img/auto-editor-banner.webp" title="Auto-Editor" width="700"></p>
19
18
 
@@ -0,0 +1,55 @@
1
+ auto_editor/__init__.py,sha256=8MQdwPYn_Y7GCbtRLrmuh9XSy5S52w2pxd3bulKs9Ag,23
2
+ auto_editor/__main__.py,sha256=eAsNa1BP4Y6Oyp4l838YmcxEwsM0LUdbaGeNFELe4h0,11124
3
+ auto_editor/analyze.py,sha256=HyRdnty3VW9ZTwwPwjsZp3bLVRLvII_1Y6NlEItDKfw,11947
4
+ auto_editor/edit.py,sha256=eEMRaQbn0jylfJ6D_egnUXjoMCbdQVsAu7MDrn-xlGo,15950
5
+ auto_editor/ffwrapper.py,sha256=Tct_Q-uy5F51h8M7UFam50UzRFpgkBvUamJP1AoKVvc,4749
6
+ auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
7
+ auto_editor/make_layers.py,sha256=vEeJt0PnE1vc9-cQZ_AlXVDjvWhObRCWJSCQGraoMvU,9016
8
+ auto_editor/output.py,sha256=ho8Lpqz4Sv_Gw0Vj2OvG39s83xHpyZlvtRNryTPbXqc,2563
9
+ auto_editor/preview.py,sha256=HUsjmV9Fx73rZ26BXrpz9z-z_e4oiui3u9e7qbbGoBY,3037
10
+ auto_editor/timeline.py,sha256=XfaH9cH-RB-MObOpMr5IfLcqJcjmabO1XwkUkT3_FQM,8186
11
+ auto_editor/vanparse.py,sha256=f0vViZ-aYtDxEyVrFHJ5X2pPTQAfqtw3N2gZgzn51kU,10002
12
+ auto_editor/wavfile.py,sha256=1HbZ4L8IBD6Fbg3pd5MQG4ZXy48YZA05t8XllSplhWk,9499
13
+ auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ auto_editor/formats/fcp11.py,sha256=qzo-qpHYsiHjOPjGBWjBeJUAACDmo8ijJkjslHTQH6Q,5196
15
+ auto_editor/formats/fcp7.py,sha256=LYkGtZC_dmbHQDg1wYP7XQYS74NEon6ws8c5MDdTd90,20275
16
+ auto_editor/formats/json.py,sha256=Br-xHVHj59C0OPP2FwfJeht_fImepRXsaw0iDFvK7-w,7693
17
+ auto_editor/formats/shotcut.py,sha256=-ES854LLFCMCBe100JRJedDmuk8zPev17aQMTrzPv-g,4923
18
+ auto_editor/formats/utils.py,sha256=LYXDiqOk9WwUorLGw2D0M7In9BNDkoKikNawuks7hqE,1648
19
+ auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ auto_editor/lang/json.py,sha256=OsNcYlfEj8ZLlzLK-gkLcrCCKI7mJz9rpe-6XLr4f9U,9231
21
+ auto_editor/lang/libintrospection.py,sha256=6H1rGp0wqaCud5IPaoEmzULGnYt6ec7_0h32ATcw2oY,261
22
+ auto_editor/lang/libmath.py,sha256=z33A161Oe6vYYK7R6pgYjdZZe63dQkN38Qf36TL3prg,847
23
+ auto_editor/lang/palet.py,sha256=jHSO8R4eAbMeiQaGwCLih6z0pPGoJEcmPgSvsTpF8EA,24139
24
+ auto_editor/lang/stdenv.py,sha256=3UnVgLaTT7hQMhquDmv-mQx82jtr_YOYstwZAwvXfJY,43754
25
+ auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,7586
27
+ auto_editor/lib/data_structs.py,sha256=dcsXgsLLzbmFDUZucoirzewPALsKzoxz7z5L22_QJM8,7091
28
+ auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
29
+ auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ auto_editor/render/audio.py,sha256=1iOQCeRXfRz28cqnHp2XeK-f3_UnPf80AKQAfifGvdE,12584
31
+ auto_editor/render/subtitle.py,sha256=lf2l1QWJgFiqlpQWWBwSlKJnSgW8Lkfi59WrJMbIDqM,6240
32
+ auto_editor/render/video.py,sha256=dje0RNW2dKILfTzt0VAF0WR6REfGOsc6l17pP1Z4ooA,12215
33
+ auto_editor/subcommands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ auto_editor/subcommands/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
35
+ auto_editor/subcommands/info.py,sha256=UDdoxd6_fqSoRPwthkWXqnpxHp7dJQ0Dn96lYX_ubWc,7010
36
+ auto_editor/subcommands/levels.py,sha256=psSSIsGfzr9j0HGKp2yvK6nMlrkLwxkwsyI0uF2xb_c,4496
37
+ auto_editor/subcommands/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
38
+ auto_editor/subcommands/repl.py,sha256=TF_I7zsFY7-KdgidrqjafTz7o_eluVbLvgTcOBG-UWQ,3449
39
+ auto_editor/subcommands/subdump.py,sha256=af_XBf7kaevqHn1A71z8C-7x8pS5WKD9FE_ugkCw6rk,665
40
+ auto_editor/subcommands/test.py,sha256=bR3MQvW1P_cKDwd1hRa2t-n3GqgRGGv5D3IBnfrH8-0,25787
41
+ auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ auto_editor/utils/bar.py,sha256=hG_NiYeuM90TdILzAJORft-UOS5grwWN3SbRuj6upsI,3998
43
+ auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
44
+ auto_editor/utils/cmdkw.py,sha256=aUGBvBel2Ko1o6Rwmr4rEL-BMc5hEnzYLbyZ1GeJdcY,5729
45
+ auto_editor/utils/container.py,sha256=Wf1ZL0tvXWl6m1B9mK_SkgVl89ilV_LpwlQq0TVroCc,2704
46
+ auto_editor/utils/func.py,sha256=kB-pNDn20M6YT7sljyd_auve5teK-E2G4TgwVOAIuJw,2754
47
+ auto_editor/utils/log.py,sha256=C1b-vnszSsohMd5fyaRcCuf0OPobZVMkV77cP-_JNP4,3776
48
+ auto_editor/utils/types.py,sha256=7BF7R7DA5eKmtI6f5ia7bOYNL0u_2sviiPsE1VmP0lc,10724
49
+ docs/build.py,sha256=CM-ZWgQk8wSNjivx_-6wGIaG7cstrNKsX2d4TzFVivE,1642
50
+ auto_editor-26.1.0.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
51
+ auto_editor-26.1.0.dist-info/METADATA,sha256=QDfveFTnxTtnA2WqZTvgM4vNGQ1sTnziQ2MSAyqF5WQ,6109
52
+ auto_editor-26.1.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
53
+ auto_editor-26.1.0.dist-info/entry_points.txt,sha256=-H7zdTw4MqnAcwrN5xTNkGIhzZtJMxS9r6lTMeR9-aA,240
54
+ auto_editor-26.1.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
55
+ auto_editor-26.1.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5