auto-editor 24.9.1__py3-none-any.whl → 24.19.1__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.
@@ -1,4 +1,4 @@
1
- __version__ = "1.1.2"
1
+ __version__ = "1.2.0"
2
2
 
3
3
  import os.path
4
4
  from platform import machine, system
auto_editor/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "24.9.1"
2
- version = "24w09a"
1
+ __version__ = "24.19.1"
2
+ version = "24w19a"
auto_editor/edit.py CHANGED
@@ -177,11 +177,11 @@ def edit_media(
177
177
  elif path_ext == ".json":
178
178
  from auto_editor.formats.json import read_json
179
179
 
180
- tl = read_json(paths[0], ffmpeg, log)
180
+ tl = read_json(paths[0], log)
181
181
  sources = [] if tl.src is None else [tl.src]
182
182
  src = tl.src
183
183
  else:
184
- sources = [initFileInfo(path, ffmpeg, log) for path in paths]
184
+ sources = [initFileInfo(path, log) for path in paths]
185
185
  src = None if not sources else sources[0]
186
186
 
187
187
  del paths
auto_editor/ffwrapper.py CHANGED
@@ -135,10 +135,10 @@ class VideoStream:
135
135
  sar: Fraction
136
136
  time_base: Fraction | None
137
137
  pix_fmt: str | None
138
- color_range: str | None
139
- color_space: str | None
140
- color_primaries: str | None
141
- color_transfer: str | None
138
+ color_range: int
139
+ color_space: int
140
+ color_primaries: int
141
+ color_transfer: int
142
142
  bitrate: int
143
143
  lang: str | None
144
144
 
@@ -189,7 +189,7 @@ class FileInfo:
189
189
  return f"@{self.path.name}"
190
190
 
191
191
 
192
- def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
192
+ def initFileInfo(path: str, log: Log) -> FileInfo:
193
193
  import av
194
194
 
195
195
  av.logging.set_level(av.logging.PANIC)
@@ -203,14 +203,11 @@ def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
203
203
  audios: tuple[AudioStream, ...] = ()
204
204
  subtitles: tuple[SubtitleStream, ...] = ()
205
205
 
206
- _dir = os.path.dirname(ffmpeg.path)
207
- _ext = os.path.splitext(ffmpeg.path)[1]
208
- ffprobe = os.path.join(_dir, f"ffprobe{_ext}")
209
-
210
- for i, v in enumerate(cont.streams.video):
211
- vdur = 0.0
206
+ for v in cont.streams.video:
212
207
  if v.duration is not None and v.time_base is not None:
213
208
  vdur = float(v.duration * v.time_base)
209
+ else:
210
+ vdur = 0.0
214
211
 
215
212
  fps = v.average_rate
216
213
  if (fps is None or fps < 1) and v.name in ("png", "mjpeg", "webp"):
@@ -218,36 +215,11 @@ def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
218
215
  if fps is None or fps == 0:
219
216
  fps = Fraction(30)
220
217
 
221
- _sar = c_range = c_space = c_primary = c_transfer = None
222
- try:
223
- _raw = get_stdout(
224
- [
225
- ffprobe,
226
- "-v",
227
- "error",
228
- "-select_streams",
229
- f"v:{i}",
230
- "-show_entries",
231
- "stream=sample_aspect_ratio:stream=color_range:stream=color_space:stream=color_primaries:stream=color_transfer",
232
- "-of",
233
- "default=noprint_wrappers=1:nokey=1",
234
- path,
235
- ]
236
- )
237
- _sar, c_range, c_space, c_primary, c_transfer = _raw.strip().split("\n")
238
- except Exception:
239
- log.debug("Unexpected ffprobe shape")
240
-
241
- if v.sample_aspect_ratio is None:
242
- if _sar is None:
243
- sar = Fraction(1)
244
- else:
245
- try:
246
- sar = Fraction(_sar.replace(":", "/"))
247
- except Exception:
248
- sar = Fraction(1)
249
- else:
250
- sar = v.sample_aspect_ratio
218
+ sar = Fraction(1) if v.sample_aspect_ratio is None else v.sample_aspect_ratio
219
+ cc = v.codec_context
220
+
221
+ if v.name is None:
222
+ log.error(f"Can't detect codec for video stream {v}")
251
223
 
252
224
  videos += (
253
225
  VideoStream(
@@ -258,11 +230,11 @@ def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
258
230
  vdur,
259
231
  sar,
260
232
  v.time_base,
261
- v.codec_context.pix_fmt,
262
- c_range,
263
- c_space,
264
- c_primary,
265
- c_transfer,
233
+ cc.pix_fmt,
234
+ cc.color_range,
235
+ cc.colorspace,
236
+ cc.color_primaries,
237
+ cc.color_trc,
266
238
  0 if v.bit_rate is None else v.bit_rate,
267
239
  v.language,
268
240
  ),
@@ -273,13 +245,14 @@ def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
273
245
  if a.duration is not None and a.time_base is not None:
274
246
  adur = float(a.duration * a.time_base)
275
247
 
248
+ a_cc = a.codec_context
276
249
  audios += (
277
250
  AudioStream(
278
- a.codec_context.name,
279
- 0 if a.sample_rate is None else a.sample_rate,
280
- a.channels,
251
+ a_cc.name,
252
+ 0 if a_cc.sample_rate is None else a_cc.sample_rate,
253
+ a_cc.channels,
281
254
  adur,
282
- 0 if a.bit_rate is None else a.bit_rate,
255
+ 0 if a_cc.bit_rate is None else a_cc.bit_rate,
283
256
  a.language,
284
257
  ),
285
258
  )
@@ -32,13 +32,13 @@ def get_colorspace(src: FileInfo) -> str:
32
32
  s = src.videos[0]
33
33
  if s.pix_fmt == "rgb24":
34
34
  return "sRGB IEC61966-2.1"
35
- if s.color_space == "smpte170m":
36
- return "6-1-6 (Rec. 601 NTSC)"
37
- if s.color_space == "bt470bg":
35
+ if s.color_space == 5: # "bt470bg"
38
36
  return "5-1-6 (Rec. 601 PAL)"
39
- if s.color_primaries == "bt2020":
37
+ if s.color_space == 6: # "smpte170m"
38
+ return "6-1-6 (Rec. 601 NTSC)"
39
+ if s.color_primaries == 9: # "bt2020"
40
40
  # See: https://video.stackexchange.com/questions/22059/how-to-identify-hdr-video
41
- if s.color_transfer in ("arib-std-b67", "smpte2084"):
41
+ if s.color_transfer in (16, 18): # "smpte2084" "arib-std-b67"
42
42
  return "9-18-9 (Rec. 2020 HLG)"
43
43
  return "9-1-9 (Rec. 2020)"
44
44
 
@@ -79,7 +79,7 @@ def fcp11_write_xml(
79
79
  ffmpeg.run(
80
80
  ["-i", f"{src.path.resolve()}", "-map", f"0:a:{i}", f"{newtrack}"]
81
81
  )
82
- all_srcs.append(initFileInfo(f"{newtrack}", ffmpeg, log))
82
+ all_srcs.append(initFileInfo(f"{newtrack}", log))
83
83
  all_refs.append(f"r{(i + 1) * 2}")
84
84
 
85
85
  fcpxml = Element("fcpxml", version="1.10" if flavor == "resolve" else "1.11")
@@ -140,7 +140,7 @@ def fcp11_write_xml(
140
140
  "ref": ref,
141
141
  "offset": fraction(clip.start),
142
142
  "duration": fraction(clip.dur),
143
- "start": fraction(int(clip.offset // clip.speed)),
143
+ "start": fraction(clip.offset),
144
144
  "tcFormat": "NDF",
145
145
  }
146
146
  asset = SubElement(spine, "asset-clip", clip_properties)
@@ -281,7 +281,6 @@ def fcp7_read_xml(path: str, ffmpeg: FFmpeg, log: Log) -> v3:
281
281
  if "pathurl" in fileobj:
282
282
  sources[file_id] = initFileInfo(
283
283
  uri_to_path(fileobj["pathurl"]),
284
- ffmpeg,
285
284
  log,
286
285
  )
287
286
  else:
@@ -297,7 +296,7 @@ def fcp7_read_xml(path: str, ffmpeg: FFmpeg, log: Log) -> v3:
297
296
 
298
297
  start = clipitem["start"]
299
298
  dur = clipitem["end"] - start
300
- offset = int(clipitem["in"] * speed)
299
+ offset = clipitem["in"]
301
300
 
302
301
  vobjs[t].append(
303
302
  TlVideo(start, dur, sources[file_id], offset, speed, stream=0)
@@ -315,7 +314,7 @@ def fcp7_read_xml(path: str, ffmpeg: FFmpeg, log: Log) -> v3:
315
314
  if file_id not in sources:
316
315
  fileobj = valid.parse(clipitem["file"], {"pathurl": str})
317
316
  sources[file_id] = initFileInfo(
318
- uri_to_path(fileobj["pathurl"]), ffmpeg, log
317
+ uri_to_path(fileobj["pathurl"]), log
319
318
  )
320
319
 
321
320
  if "filter" in clipitem:
@@ -325,7 +324,7 @@ def fcp7_read_xml(path: str, ffmpeg: FFmpeg, log: Log) -> v3:
325
324
 
326
325
  start = clipitem["start"]
327
326
  dur = clipitem["end"] - start
328
- offset = int(clipitem["in"] * speed)
327
+ offset = clipitem["in"]
329
328
 
330
329
  aobjs[t].append(
331
330
  TlAudio(
@@ -415,8 +414,8 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
415
414
 
416
415
  _start = f"{clip.start}"
417
416
  _end = f"{clip.start + clip.dur}"
418
- _in = f"{int(clip.offset / clip.speed)}"
419
- _out = f"{int(clip.offset / clip.speed) + clip.dur}"
417
+ _in = f"{clip.offset}"
418
+ _out = f"{clip.offset + clip.dur}"
420
419
 
421
420
  clipitem = ET.SubElement(track, "clipitem", id=f"clipitem-{j+1}")
422
421
  ET.SubElement(clipitem, "name").text = src.path.stem
@@ -474,8 +473,8 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
474
473
 
475
474
  _start = f"{aclip.start}"
476
475
  _end = f"{aclip.start + aclip.dur}"
477
- _in = f"{int(aclip.offset / aclip.speed)}"
478
- _out = f"{int(aclip.offset / aclip.speed) + aclip.dur}"
476
+ _in = f"{aclip.offset}"
477
+ _out = f"{aclip.offset + aclip.dur}"
479
478
 
480
479
  if not src.videos:
481
480
  clip_item_num = j + 1
@@ -6,7 +6,7 @@ from difflib import get_close_matches
6
6
  from fractions import Fraction
7
7
  from typing import Any
8
8
 
9
- from auto_editor.ffwrapper import FFmpeg, FileInfo, initFileInfo
9
+ from auto_editor.ffwrapper import FileInfo, initFileInfo
10
10
  from auto_editor.lang.json import Lexer, Parser, dump
11
11
  from auto_editor.lib.err import MyError
12
12
  from auto_editor.timeline import (
@@ -42,7 +42,7 @@ def check_file(path: str, log: Log) -> None:
42
42
  log.error(f"Could not locate media file: '{path}'")
43
43
 
44
44
 
45
- def read_v3(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
45
+ def read_v3(tl: Any, log: Log) -> v3:
46
46
  check_attrs(
47
47
  tl,
48
48
  log,
@@ -59,7 +59,7 @@ def read_v3(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
59
59
  def make_src(v: str) -> FileInfo:
60
60
  if v in srcs:
61
61
  return srcs[v]
62
- temp = initFileInfo(v, ffmpeg, log)
62
+ temp = initFileInfo(v, log)
63
63
  srcs[v] = temp
64
64
  return temp
65
65
 
@@ -158,7 +158,7 @@ def read_v3(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
158
158
  return v3(src, tb, sr, res, bg, v, a, v1=None)
159
159
 
160
160
 
161
- def read_v1(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
161
+ def read_v1(tl: Any, log: Log) -> v3:
162
162
  from auto_editor.make_layers import clipify
163
163
 
164
164
  check_attrs(tl, log, "source", "chunks")
@@ -168,11 +168,32 @@ def read_v1(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
168
168
 
169
169
  check_file(path, log)
170
170
 
171
- src = initFileInfo(path, ffmpeg, log)
171
+ src = initFileInfo(path, log)
172
172
 
173
173
  vtl: VSpace = []
174
174
  atl: ASpace = [[] for _ in range(len(src.audios))]
175
175
 
176
+ # Verify chunks
177
+ last_end: int | None = None
178
+ if type(chunks) is not list:
179
+ log.error("chunks key must be an array")
180
+
181
+ for i, chunk in enumerate(chunks):
182
+ if type(chunk) is not list or len(chunk) != 3:
183
+ log.error(f"Invalid chunk at chunk {i}")
184
+ if type(chunk[0]) is not int or chunk[0] < 0:
185
+ log.error(f"Invalid start at chunk {i}")
186
+ if type(chunk[1]) is not int or chunk[1] <= chunk[0]:
187
+ log.error(f"Invalid end at chunk {i}")
188
+ if type(chunk[2]) is not float or chunk[2] < 0.0 or chunk[2] > 99999.0:
189
+ log.error(f"Invalid speed at chunk {i}")
190
+
191
+ if i == 0 and chunk[0] != 0:
192
+ log.error("First chunk must start with 0")
193
+ if i != 0 and chunk[0] != last_end:
194
+ log.error(f"Invalid start at chunk {i}")
195
+ last_end = chunk[1]
196
+
176
197
  for c in clipify(chunks, src):
177
198
  if src.videos:
178
199
  if len(vtl) == 0:
@@ -194,7 +215,7 @@ def read_v1(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
194
215
  )
195
216
 
196
217
 
197
- def read_json(path: str, ffmpeg: FFmpeg, log: Log) -> v3:
218
+ def read_json(path: str, log: Log) -> v3:
198
219
  with open(path, encoding="utf-8", errors="ignore") as f:
199
220
  try:
200
221
  tl = Parser(Lexer(path, f)).expr()
@@ -206,9 +227,9 @@ def read_json(path: str, ffmpeg: FFmpeg, log: Log) -> v3:
206
227
  ver = tl["version"]
207
228
 
208
229
  if ver == "3":
209
- return read_v3(tl, ffmpeg, log)
230
+ return read_v3(tl, log)
210
231
  if ver == "1":
211
- return read_v1(tl, ffmpeg, log)
232
+ return read_v1(tl, log)
212
233
  if type(ver) is not str:
213
234
  log.error("version needs to be a string")
214
235
  log.error(f"Importing version {ver} timelines is not supported.")
@@ -92,7 +92,7 @@ def shotcut_write_mlt(output: str, tl: v3) -> None:
92
92
 
93
93
  for clip in clips:
94
94
  src = clip.src
95
- length = to_timecode((clip.offset / clip.speed + clip.dur) / tb, "standard")
95
+ length = to_timecode((clip.offset + clip.dur) / tb, "standard")
96
96
 
97
97
  if clip.speed == 1:
98
98
  resource = f"{src.path}"
@@ -127,8 +127,8 @@ def shotcut_write_mlt(output: str, tl: v3) -> None:
127
127
 
128
128
  producers = 0
129
129
  for i, clip in enumerate(clips):
130
- _in = to_timecode(clip.offset / clip.speed / tb, "standard")
131
- _out = to_timecode((clip.offset / clip.speed + clip.dur) / tb, "standard")
130
+ _in = to_timecode(clip.offset / tb, "standard")
131
+ _out = to_timecode((clip.offset + clip.dur) / tb, "standard")
132
132
 
133
133
  tag_name = f"chain{i}"
134
134
  if clip.speed != 1:
@@ -42,7 +42,7 @@ def clipify(chunks: Chunks, src: FileInfo, start: int = 0) -> list[Clip]:
42
42
  if dur == 0:
43
43
  continue
44
44
 
45
- offset = chunk[0]
45
+ offset = int(chunk[0] / chunk[2])
46
46
 
47
47
  if not (clips and clips[-1].start == round(start)):
48
48
  clips.append(Clip(start, dur, offset, chunk[2], src))
@@ -125,7 +125,14 @@ def make_timeline(
125
125
  if inp is None:
126
126
  tb, res = Fraction(30), (1920, 1080)
127
127
  else:
128
- tb = inp.get_fps() if args.frame_rate is None else args.frame_rate
128
+ tb = round(inp.get_fps() if args.frame_rate is None else args.frame_rate, 2)
129
+ ntsc = Fraction(30_000, 1001)
130
+ film_ntsc = Fraction(24_000, 1001)
131
+ if tb == round(ntsc, 2):
132
+ tb = ntsc
133
+ elif tb == round(film_ntsc, 2):
134
+ tb = film_ntsc
135
+
129
136
  res = inp.get_res() if args.resolution is None else args.resolution
130
137
 
131
138
  try:
@@ -228,7 +235,7 @@ def make_timeline(
228
235
  if dur == 0:
229
236
  continue
230
237
 
231
- offset = chunk[1]
238
+ offset = int(chunk[1] / chunk[3])
232
239
 
233
240
  if not (clips and clips[-1].start == round(start)):
234
241
  clips.append(Clip(start, dur, offset, chunk[3], chunk[0]))
auto_editor/output.py CHANGED
@@ -182,12 +182,19 @@ def mux_quality_media(
182
182
  cmd += _ffset("-c:a", args.audio_codec) + _ffset("-b:a", args.audio_bitrate)
183
183
 
184
184
  if same_container and v_tracks > 0:
185
- cmd += (
186
- _ffset("-color_range", src.videos[0].color_range)
187
- + _ffset("-colorspace", src.videos[0].color_space)
188
- + _ffset("-color_primaries", src.videos[0].color_primaries)
189
- + _ffset("-color_trc", src.videos[0].color_transfer)
190
- )
185
+ color_range = src.videos[0].color_range
186
+ colorspace = src.videos[0].color_space
187
+ color_prim = src.videos[0].color_primaries
188
+ color_trc = src.videos[0].color_transfer
189
+
190
+ if color_range == 1 or color_range == 2:
191
+ cmd.extend(["-color_range", f"{color_range}"])
192
+ if colorspace in (0, 1) or (colorspace >= 3 and colorspace < 16):
193
+ cmd.extend(["-colorspace", f"{colorspace}"])
194
+ if color_prim in (0, 1) or (color_prim >= 4 and color_prim < 17):
195
+ cmd.extend(["-color_primaries", f"{color_prim}"])
196
+ if color_trc == 1 or (color_trc >= 4 and color_trc < 22):
197
+ cmd.extend(["-color_trc", f"{color_trc}"])
191
198
 
192
199
  if args.extras is not None:
193
200
  cmd.extend(args.extras.split(" "))
auto_editor/preview.py CHANGED
@@ -32,7 +32,8 @@ def all_cuts(tl: v3, in_len: int) -> list[int]:
32
32
  oe: list[tuple[int, int]] = []
33
33
 
34
34
  for clip in tl.a[0]:
35
- oe.append((clip.offset, clip.offset + clip.dur))
35
+ old_offset = clip.offset * clip.speed
36
+ oe.append((round(old_offset * clip.speed), round(old_offset + clip.dur)))
36
37
 
37
38
  cut_lens = []
38
39
  i = 0
@@ -208,8 +208,8 @@ def make_new_audio(
208
208
  del leng
209
209
 
210
210
  samp_list = samples[(clip.src, clip.stream)]
211
- samp_start = clip.offset * sr // tb
212
- samp_end = round((clip.offset + clip.dur * clip.speed) * sr / tb)
211
+ samp_start = round(clip.offset * clip.speed * sr / tb)
212
+ samp_end = round((clip.offset + clip.dur) * clip.speed * sr / tb)
213
213
  if samp_end > len(samp_list):
214
214
  samp_end = len(samp_list)
215
215
 
@@ -97,7 +97,6 @@ def make_image_cache(tl: v3) -> dict[tuple[FileInfo, int], np.ndarray]:
97
97
  for obj in clip:
98
98
  if isinstance(obj, TlImage) and obj.src not in img_cache:
99
99
  with av.open(obj.src.path) as cn:
100
- assert isinstance(cn, av.InputContainer)
101
100
  my_stream = cn.streams.video[0]
102
101
  for frame in cn.decode(my_stream):
103
102
  if obj.width != 0:
@@ -245,7 +244,7 @@ def render_av(
245
244
  for lobj in layer:
246
245
  if isinstance(lobj, TlVideo):
247
246
  if index >= lobj.start and index < (lobj.start + lobj.dur):
248
- _i = lobj.offset + round((index - lobj.start) * lobj.speed)
247
+ _i = round((lobj.offset + index - lobj.start) * lobj.speed)
249
248
  obj_list.append(VideoFrame(_i, lobj.src))
250
249
  elif index >= lobj.start and index < lobj.start + lobj.dur:
251
250
  obj_list.append(lobj)
@@ -3,28 +3,26 @@ from __future__ import annotations
3
3
  import sys
4
4
  from dataclasses import dataclass, field
5
5
 
6
- from auto_editor.ffwrapper import FFmpeg, initFileInfo
6
+ from auto_editor.ffwrapper import initFileInfo
7
7
  from auto_editor.utils.log import Log
8
8
  from auto_editor.vanparse import ArgumentParser
9
9
 
10
10
 
11
11
  @dataclass(slots=True)
12
12
  class DescArgs:
13
- ffmpeg_location: str | None = None
14
13
  help: bool = False
15
14
  input: list[str] = field(default_factory=list)
16
15
 
17
16
 
18
17
  def desc_options(parser: ArgumentParser) -> ArgumentParser:
19
18
  parser.add_required("input", nargs="*")
20
- parser.add_argument("--ffmpeg-location", help="Point to your custom ffmpeg file")
21
19
  return parser
22
20
 
23
21
 
24
22
  def main(sys_args: list[str] = sys.argv[1:]) -> None:
25
23
  args = desc_options(ArgumentParser("desc")).parse_args(DescArgs, sys_args)
26
24
  for path in args.input:
27
- src = initFileInfo(path, FFmpeg(args.ffmpeg_location), Log())
25
+ src = initFileInfo(path, Log())
28
26
  if src.description is not None:
29
27
  sys.stdout.write(f"\n{src.description}\n\n")
30
28
  else:
@@ -5,7 +5,7 @@ import sys
5
5
  from dataclasses import dataclass, field
6
6
  from typing import Any, Literal, TypedDict
7
7
 
8
- from auto_editor.ffwrapper import FFmpeg, initFileInfo
8
+ from auto_editor.ffwrapper import initFileInfo
9
9
  from auto_editor.lang.json import dump
10
10
  from auto_editor.timeline import v3
11
11
  from auto_editor.utils.func import aspect_ratio
@@ -16,8 +16,6 @@ from auto_editor.vanparse import ArgumentParser
16
16
  @dataclass(slots=True)
17
17
  class InfoArgs:
18
18
  json: bool = False
19
- ffmpeg_location: str | None = None
20
- my_ffmpeg: bool = False
21
19
  help: bool = False
22
20
  input: list[str] = field(default_factory=list)
23
21
 
@@ -25,12 +23,6 @@ class InfoArgs:
25
23
  def info_options(parser: ArgumentParser) -> ArgumentParser:
26
24
  parser.add_required("input", nargs="*")
27
25
  parser.add_argument("--json", flag=True, help="Export info in JSON format")
28
- parser.add_argument("--ffmpeg-location", help="Point to your custom ffmpeg file")
29
- parser.add_argument(
30
- "--my-ffmpeg",
31
- flag=True,
32
- help="Use the ffmpeg on your PATH instead of the one packaged",
33
- )
34
26
  return parser
35
27
 
36
28
 
@@ -42,10 +34,10 @@ class VideoJson(TypedDict):
42
34
  pixel_aspect_ratio: str
43
35
  duration: float
44
36
  pix_fmt: str | None
45
- color_range: str | None
46
- color_space: str | None
47
- color_primaries: str | None
48
- color_transfer: str | None
37
+ color_range: int
38
+ color_space: int
39
+ color_primaries: int
40
+ color_transfer: int
49
41
  timebase: str
50
42
  bitrate: int
51
43
  lang: str | None
@@ -83,7 +75,6 @@ class MediaJson(TypedDict, total=False):
83
75
  def main(sys_args: list[str] = sys.argv[1:]) -> None:
84
76
  args = info_options(ArgumentParser("info")).parse_args(InfoArgs, sys_args)
85
77
 
86
- ffmpeg = FFmpeg(args.ffmpeg_location, args.my_ffmpeg)
87
78
  log = Log(quiet=not args.json)
88
79
 
89
80
  file_info: dict[str, MediaJson] = {}
@@ -96,7 +87,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
96
87
  if ext == ".json":
97
88
  from auto_editor.formats.json import read_json
98
89
 
99
- tl = read_json(file, ffmpeg, log)
90
+ tl = read_json(file, log)
100
91
  file_info[file] = {"type": "timeline"}
101
92
  file_info[file]["version"] = "v3" if isinstance(tl, v3) else "v1"
102
93
 
@@ -109,7 +100,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
109
100
  file_info[file] = {"type": "timeline"}
110
101
  continue
111
102
 
112
- src = initFileInfo(file, ffmpeg, log)
103
+ src = initFileInfo(file, log)
113
104
 
114
105
  if len(src.videos) + len(src.audios) + len(src.subtitles) == 0:
115
106
  file_info[file] = {"type": "unknown"}
@@ -177,13 +168,27 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
177
168
  text += f" - track {s}:\n"
178
169
  for key, value in stream.items():
179
170
  if not is_null(key, value):
180
- key = key.replace("_", " ")
181
171
  if isinstance(value, list):
182
172
  sep = "x" if key == "resolution" else ":"
183
-
184
173
  value = sep.join(f"{x}" for x in value)
185
174
 
186
- text += f" - {key}: {value}\n"
175
+ if key in (
176
+ "color_range",
177
+ "color_space",
178
+ "color_transfer",
179
+ "color_primaries",
180
+ ):
181
+ if key == "color_range":
182
+ if value == 1:
183
+ text += " - color range: 1 (tv)\n"
184
+ elif value == 2:
185
+ text += " - color range: 2 (pc)\n"
186
+ elif value == 1:
187
+ text += f" - {key.replace('_', ' ')}: 1 (bt709)\n"
188
+ elif value != 2:
189
+ text += f" - {key.replace('_', ' ')}: {value}\n"
190
+ else:
191
+ text += f" - {key.replace('_', ' ')}: {value}\n"
187
192
  return text
188
193
 
189
194
  text = ""
@@ -78,7 +78,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
78
78
  temp = setup_tempdir(None, Log())
79
79
  log = Log(quiet=True, temp=temp)
80
80
 
81
- sources = [initFileInfo(path, ffmpeg, log) for path in args.input]
81
+ sources = [initFileInfo(path, log) for path in args.input]
82
82
  if len(sources) < 1:
83
83
  log.error("levels needs at least one input file")
84
84
 
@@ -70,7 +70,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
70
70
  log = Log(quiet=True, temp=temp)
71
71
  ffmpeg = FFmpeg(args.ffmpeg_location, args.my_ffmpeg, False)
72
72
  strict = len(args.input) < 2
73
- sources = [initFileInfo(path, ffmpeg, log) for path in args.input]
73
+ sources = [initFileInfo(path, log) for path in args.input]
74
74
  src = sources[0]
75
75
  tb = src.get_fps() if args.timebase is None else args.timebase
76
76
  ensure = Ensure(ffmpeg, src.get_sr(), temp, log)
@@ -1,57 +1,22 @@
1
- from __future__ import annotations
2
-
3
- import os
4
1
  import sys
5
- import tempfile
6
- from dataclasses import dataclass, field
7
-
8
- from auto_editor.ffwrapper import FFmpeg, initFileInfo
9
- from auto_editor.utils.log import Log
10
- from auto_editor.vanparse import ArgumentParser
11
-
12
2
 
13
- @dataclass(slots=True)
14
- class SubArgs:
15
- ffmpeg_location: str | None = None
16
- my_ffmpeg: bool = False
17
- help: bool = False
18
- input: list[str] = field(default_factory=list)
19
-
20
-
21
- def subdump_options(parser: ArgumentParser) -> ArgumentParser:
22
- parser.add_required("input", nargs="*")
23
- parser.add_argument("--ffmpeg-location", help="Point to your custom ffmpeg file")
24
- parser.add_argument(
25
- "--my-ffmpeg",
26
- flag=True,
27
- help="Use the ffmpeg on your PATH instead of the one packaged",
28
- )
29
- return parser
3
+ import av
4
+ from av.subtitles.subtitle import SubtitleSet
30
5
 
31
6
 
32
7
  def main(sys_args: list[str] = sys.argv[1:]) -> None:
33
- args = subdump_options(ArgumentParser("subdump")).parse_args(SubArgs, sys_args)
34
-
35
- ffmpeg = FFmpeg(args.ffmpeg_location, args.my_ffmpeg, debug=False)
36
-
37
- temp = tempfile.mkdtemp()
38
- log = Log(temp=temp)
39
-
40
- for i, input_file in enumerate(args.input):
41
- src = initFileInfo(input_file, ffmpeg, log)
42
-
43
- cmd = ["-i", input_file]
44
- for s, sub in enumerate(src.subtitles):
45
- cmd.extend(["-map", f"0:s:{s}", os.path.join(temp, f"{i}s{s}.{sub.ext}")])
46
- ffmpeg.run(cmd)
47
-
48
- for s, sub in enumerate(src.subtitles):
49
- print(f"file: {input_file} ({s}:{sub.lang}:{sub.ext})")
50
- with open(os.path.join(temp, f"{i}s{s}.{sub.ext}")) as file:
51
- print(file.read())
52
- print("------")
53
-
54
- log.cleanup()
8
+ for i, input_file in enumerate(sys_args):
9
+ with av.open(input_file) as container:
10
+ for s in range(len(container.streams.subtitles)):
11
+ print(f"file: {input_file} ({s}:{container.streams.subtitles[s].name})")
12
+ for packet in container.demux(subtitles=s):
13
+ for item in packet.decode():
14
+ if type(item) is SubtitleSet and item:
15
+ if item[0].type == b"ass":
16
+ print(item[0].ass.decode("utf-8"))
17
+ elif item[0].type == b"text":
18
+ print(item[0].text)
19
+ print("------")
55
20
 
56
21
 
57
22
  if __name__ == "__main__":
@@ -13,7 +13,7 @@ from typing import Any
13
13
 
14
14
  import numpy as np
15
15
 
16
- from auto_editor.ffwrapper import FFmpeg, FileInfo, initFileInfo
16
+ from auto_editor.ffwrapper import FileInfo, initFileInfo
17
17
  from auto_editor.lang.palet import Lexer, Parser, env, interpret
18
18
  from auto_editor.lib.data_structs import Char
19
19
  from auto_editor.lib.err import MyError
@@ -48,12 +48,11 @@ def pipe_to_console(cmd: list[str]) -> tuple[int, str, str]:
48
48
 
49
49
 
50
50
  class Checker:
51
- def __init__(self, ffmpeg: FFmpeg, log: Log):
52
- self.ffmpeg = ffmpeg
51
+ def __init__(self, log: Log):
53
52
  self.log = log
54
53
 
55
54
  def check(self, path: str) -> FileInfo:
56
- return initFileInfo(path, self.ffmpeg, self.log)
55
+ return initFileInfo(path, self.log)
57
56
 
58
57
 
59
58
  class Runner:
@@ -177,7 +176,7 @@ def main(sys_args: list[str] | None = None):
177
176
  args = test_options(ArgumentParser("test")).parse_args(TestArgs, sys_args)
178
177
 
179
178
  run = Runner()
180
- checker = Checker(FFmpeg(), Log())
179
+ checker = Checker(Log())
181
180
 
182
181
  ### Tests ###
183
182
 
@@ -525,7 +524,9 @@ def main(sys_args: list[str] | None = None):
525
524
  # Issue 280
526
525
  def SAR():
527
526
  out = run.main(["resources/SAR-2by3.mp4"], [])
528
- assert checker.check(out).videos[0].sar == Fraction(2, 3)
527
+
528
+ # It's working, PyAV just can't detect the changes.
529
+ # assert checker.check(out).videos[0].sar == Fraction(2, 3)
529
530
 
530
531
  return out
531
532
 
@@ -53,7 +53,7 @@ hevc_en = ["hevc", "libx265", "hevc_videotoolbox", "hevc_amf", "hevc_nvenc", "he
53
53
  av1_en = ["av1", "libaom-av1", "av1_nvenc", "av1_amf"]
54
54
  prores_en = ["prores", "prores_videotoolbox", "prores_aw", "prores_ks"]
55
55
  aac_en = ["aac", "aac_at", "libfdk_aac"]
56
-
56
+ opus_en = ["opus", "libopus"]
57
57
 
58
58
  h265: DictContainer = {
59
59
  "allow_video": True,
@@ -80,7 +80,7 @@ mp4: DictContainer = {
80
80
  "allow_subtitle": True,
81
81
  "allow_image": True,
82
82
  "vcodecs": h264_en + hevc_en + av1_en + ["vp9", "mpeg4", "mpeg2video", "mjpeg"],
83
- "acodecs": aac_en + ["mp3", "opus", "flac", "vorbis", "libvorbis", "ac3", "mp2"],
83
+ "acodecs": aac_en + opus_en + ["mp3", "flac", "vorbis", "libvorbis", "ac3", "mp2"],
84
84
  "vstrict": True,
85
85
  }
86
86
  ogg: DictContainer = {
@@ -88,16 +88,16 @@ ogg: DictContainer = {
88
88
  "allow_audio": True,
89
89
  "allow_subtitle": True,
90
90
  "vcodecs": ["libtheora", "theora"],
91
- "acodecs": ["libvorbis", "vorbis", "flac", "opus", "speex"],
91
+ "acodecs": opus_en + ["libvorbis", "vorbis", "flac", "speex"],
92
92
  "vstrict": True,
93
93
  }
94
94
 
95
95
  mka_audio = (
96
96
  ["libvorbis", "vorbis"]
97
97
  + aac_en
98
+ + opus_en
98
99
  + [
99
100
  "mp3",
100
- "opus",
101
101
  "flac",
102
102
  "ac3",
103
103
  "mp2",
@@ -172,11 +172,11 @@ containers: dict[str, DictContainer] = {
172
172
  },
173
173
  "opus": {
174
174
  "allow_audio": True,
175
- "acodecs": ["opus", "flac", "libvorbis", "vorbis", "speex"],
175
+ "acodecs": opus_en + ["flac", "libvorbis", "vorbis", "speex"],
176
176
  },
177
177
  "oga": {
178
178
  "allow_audio": True,
179
- "acodecs": ["flac", "libvorbis", "vorbis", "opus", "speex"],
179
+ "acodecs": opus_en + ["flac", "libvorbis", "vorbis", "speex"],
180
180
  },
181
181
  "flac": {
182
182
  "allow_audio": True,
@@ -188,7 +188,7 @@ containers: dict[str, DictContainer] = {
188
188
  "allow_audio": True,
189
189
  "allow_subtitle": True,
190
190
  "vcodecs": ["vp9", "vp8"] + av1_en,
191
- "acodecs": ["opus", "vorbis", "libvorbis"],
191
+ "acodecs": opus_en + ["vorbis", "libvorbis"],
192
192
  "scodecs": ["webvtt"],
193
193
  "vstrict": True,
194
194
  "sstrict": True,
auto_editor/wavfile.py CHANGED
@@ -45,9 +45,9 @@ def _read_fmt_chunk(
45
45
  raw_guid = extensible_chunk_data[6:22]
46
46
 
47
47
  if bytes_order == "big":
48
- tail = b"\x00\x00\x00\x10\x80\x00\x00\xAA\x00\x38\x9B\x71"
48
+ tail = b"\x00\x00\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71"
49
49
  else:
50
- tail = b"\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71"
50
+ tail = b"\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71"
51
51
  if raw_guid.endswith(tail):
52
52
  format_tag = int.from_bytes(raw_guid[:4], bytes_order)
53
53
  else:
@@ -249,7 +249,7 @@ def write(fid: io.BufferedWriter, sr: int, arr: np.ndarray) -> None:
249
249
  total_size = 44 + data_size # Basic WAV header size + data size
250
250
 
251
251
  if is_rf64 := total_size > 0xFFFFFFFF:
252
- fid.write(b"RF64\xFF\xFF\xFF\xFFWAVE")
252
+ fid.write(b"RF64\xff\xff\xff\xffWAVE")
253
253
  ds64_size = 28
254
254
  ds64_chunk_data = (0).to_bytes(ds64_size, "little") # placeholder values
255
255
  fid.write(b"ds64" + struct.pack("<I", ds64_size) + ds64_chunk_data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-editor
3
- Version: 24.9.1
3
+ Version: 24.19.1
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -12,8 +12,8 @@ Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: numpy >=1.22.0
15
- Requires-Dist: pyav ==12.0.2
16
- Requires-Dist: ae-ffmpeg ==1.1.*
15
+ Requires-Dist: pyav ==12.0.5
16
+ Requires-Dist: ae-ffmpeg ==1.2.*
17
17
 
18
18
  <p align="center"><img src="https://auto-editor.com/img/auto-editor-banner.webp" title="Auto-Editor" width="700"></p>
19
19
 
@@ -60,10 +60,10 @@ The `--edit` option is how auto-editor makes automated cuts.
60
60
  For example, edit out motionlessness in a video by setting `--edit motion`.
61
61
 
62
62
  ```
63
- # cut out sections where percentage of motion is less than 2.
64
- auto-editor example.mp4 --edit motion:threshold=2%
63
+ # cut out sections where the total motion is less than 2%.
64
+ auto-editor example.mp4 --edit motion:threshold=0.02
65
65
 
66
- # --edit is set to "audio:threshold=4%" by default.
66
+ # `--edit audio:threshold=0.04,stream=all` is used by defaut.
67
67
  auto-editor example.mp4
68
68
 
69
69
  # Different tracks can be set with different attribute.
@@ -73,34 +73,14 @@ auto-editor multi-track.mov --edit "(or audio:stream=0 audio:threshold=10%,strea
73
73
  Different editing methods can be used together.
74
74
  ```
75
75
  # 'threshold' is always the first argument for edit-method objects
76
- auto-editor example.mp4 --edit "(or audio:3% motion:6%)"
76
+ auto-editor example.mp4 --edit "(or audio:0.03 motion:0.06)"
77
77
  ```
78
78
 
79
79
  You can also use `dB` unit, a volume unit familiar to video-editors (case sensitive):
80
80
  ```
81
- auto-editor example.mp4 --edit audio:threshold=-19dB
81
+ auto-editor example.mp4 --edit audio:-19dB
82
82
  auto-editor example.mp4 --edit audio:-7dB
83
83
  auto-editor example.mp4 --edit motion:-19dB
84
-
85
- # The `dB` unit is a just a macro that expands into an S-expression:
86
- # '-19dB
87
- # > '(pow 10 (/ -19 20))
88
- # (eval '(pow 10 (/ -19 20)))
89
- # > 0.11220184543019636
90
- ```
91
-
92
- ### Working With Multiple Audio Tracks
93
- By default, only the first audio track will used for editing (track 0). You can change this with these commands.
94
-
95
- Use all audio tracks for editing:
96
- ```
97
- auto-editor multi-track.mov --edit audio:stream=all
98
- ```
99
-
100
- Use only the second, fourth, and sixth audio track:
101
- ```
102
- # track numbers start at 0
103
- auto-editor so-many-tracks.mp4 --edit "(or audio:stream=1 audio:stream=3 audio:stream=5)"
104
84
  ```
105
85
 
106
86
  ### See What Auto-Editor Cuts Out
@@ -119,12 +99,10 @@ auto-editor example.mp4 --export premiere
119
99
  ```
120
100
 
121
101
  Auto-Editor can also export to:
122
-
123
102
  - DaVinci Resolve with `--export resolve`
124
103
  - Final Cut Pro with `--export final-cut-pro`
125
104
  - ShotCut with `--export shotcut`
126
-
127
- Other editors, like Sony Vegas, can understand the `premiere` format. If your favorite editor doesn't, you can use ` --export clip-sequence` which creates many video clips that can be imported and manipulated like normal.
105
+ - Individual media clips with `--export clip-sequence`
128
106
 
129
107
  ### Naming Timelines
130
108
  By default, auto-editor will name the timeline to "Auto-Editor Media Group" if the export supports naming.
@@ -188,14 +166,11 @@ List all available options:
188
166
  auto-editor --help
189
167
  ```
190
168
 
191
- Use `--help` with a specific option for more information:
169
+ Use `--help` with a specific option to learn more about it:
192
170
 
193
171
  ```
194
- auto-editor --scale --help
195
- --scale NUM
196
-
197
- default: 1.0
198
- Scale the output video's resolution by NUM factor
172
+ auto-editor -c:v --help
173
+ auto-editor --margin --help
199
174
  ```
200
175
 
201
176
  <h3 align="center">Auto-Editor is available on all major platforms</h3>
@@ -1,24 +1,24 @@
1
1
  ae-ffmpeg/setup.py,sha256=HeORyrs8OyJ32lSnMaIhI2B7U1lkk3QP6wOjxpoiF3Y,1891
2
- ae-ffmpeg/ae_ffmpeg/__init__.py,sha256=Y_dn5Uoh1fOrhhJ9B3agOsPF2VXnCbIaXuN3ZRft-pk,453
2
+ ae-ffmpeg/ae_ffmpeg/__init__.py,sha256=Fd2YsCINa0dB3tf9VVKDTPT9P6MDH-ve3RT2pqArImI,453
3
3
  ae-ffmpeg/ae_ffmpeg/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- auto_editor/__init__.py,sha256=RJIMlcL3cdE7LOygV0nkUUrFZqTrOQb2DimvfAvLfF8,42
4
+ auto_editor/__init__.py,sha256=BXNz58mS_b5gDCtj4v8_n9d7v_Jd5A4lD8YOPTbEtS0,43
5
5
  auto_editor/__main__.py,sha256=Lb_0h7Zop0SHK-nLWgwp7MWFrznuir8Ilo17Vx_0aKs,9827
6
6
  auto_editor/analyze.py,sha256=zvN4hXyEGXdUUVkfnYlyrCXPgBRl3DoQtBwIfHo7q68,11938
7
- auto_editor/edit.py,sha256=oL8-oCHMbTARb89IY4tsnSs_vWeqk6lwVvNhdw4Xp5M,12013
8
- auto_editor/ffwrapper.py,sha256=TrYdEyjzbpRif4mGWfi8FHkRl5chbI07-tUUyg-CpBA,8667
7
+ auto_editor/edit.py,sha256=ZH0AgGBCTv_0KTqKbybZWbMCDx_OV_i15PklTGpEBC4,11997
8
+ auto_editor/ffwrapper.py,sha256=af6j5257npuwJAyHMVKM7BbHGpjZ0bYkN1SGvIGLLu4,7662
9
9
  auto_editor/help.py,sha256=BFiP7vBz42TUzum4-zaQIrV1OY7kHeN0pe0MPE0T5xw,7997
10
- auto_editor/make_layers.py,sha256=_YyuV7JvF9sneu3FJQPDkvRqzja8Fzscr4624bXN4iI,8214
11
- auto_editor/output.py,sha256=NkdwGbiUAXvieOqjmsH4jMj3pplSKAOrbhN503U2o-o,6311
12
- auto_editor/preview.py,sha256=K10TyP0_LWD4yEIUHzqSHs_97hKl0VIaIuA1xGHI8ZI,3023
10
+ auto_editor/make_layers.py,sha256=6HzwnAajkXNAcZlB9lRVF5a2up3mSCHIlq8TOrLGuag,8460
11
+ auto_editor/output.py,sha256=ySTt0WiU4-VszsATLxpsz5HIIL-7FzoOm-yJrJRqi3E,6714
12
+ auto_editor/preview.py,sha256=fo2BDIkvl96q_ssq8AAu1tl6FN_j23h8987aDPSmjDs,3094
13
13
  auto_editor/timeline.py,sha256=JwcS-8AS5vsoTL_m03aosYijScQef4AGa2lyutQ8wbI,7069
14
14
  auto_editor/validate_input.py,sha256=G4LzUdt0fSrIPRd-wvP7x9cOzXmHTd7-BPrFk2ZNEWk,2671
15
15
  auto_editor/vanparse.py,sha256=kHvGK7itqt37q0MPTSriPljB7ilFpjG5LuEVdulUbyg,9902
16
- auto_editor/wavfile.py,sha256=UatK5yRJGe2vIpHC1U0ccxyixaLyzLoNvApzWcrxLy8,9168
16
+ auto_editor/wavfile.py,sha256=7N2LX_WfFVRnoXrKveLvuyTYpIz2htpGqfCD8tR4kJ8,9168
17
17
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- auto_editor/formats/fcp11.py,sha256=3Z7-AYkGmlnk2WyzNG11483krZqj3XRAnvw491RTxHE,5860
19
- auto_editor/formats/fcp7.py,sha256=zzkcq6U0NYlckc1X81ioStd4QbdN4pj_de4EHlV5dbo,17757
20
- auto_editor/formats/json.py,sha256=AFrQwjzmMMoEJFgYNEipnz2X75ftRfHmPHJKWW_KPwo,6741
21
- auto_editor/formats/shotcut.py,sha256=9XM-NGDVSrHyG05Tsq6RR6WacyatQxGa6wPuRu-QtXU,5023
18
+ auto_editor/formats/fcp11.py,sha256=VwJWJs1qNDIVC8-pswipmKCk0e4V3LnE5fAMA0pPWVg,5857
19
+ auto_editor/formats/fcp7.py,sha256=i6MKTErzROu0VveHfZSTIJXDrH3VL8u_IXD9pblXsIk,17613
20
+ auto_editor/formats/json.py,sha256=w0feeD0IwP3KGX4bsfc4qJ28qDHGJJwY0KszMtdL_q4,7507
21
+ auto_editor/formats/shotcut.py,sha256=pbBQwOZ8Kqfm5ns0k_rBUX0XH_urIGfp77GORrzoW5Y,4984
22
22
  auto_editor/formats/utils.py,sha256=GIZw28WHuCIaZ_zMI0v6Kxbq0QaIpbLsdSegdYwQxQ8,1990
23
23
  auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  auto_editor/lang/json.py,sha256=OsNcYlfEj8ZLlzLK-gkLcrCCKI7mJz9rpe-6XLr4f9U,9231
@@ -29,29 +29,29 @@ auto_editor/lib/contracts.py,sha256=CTay7wMWnMSbTD1QAwE-b6yvIcSPvkpxkFQRXoJiqZE,
29
29
  auto_editor/lib/data_structs.py,sha256=EXNcdMsdmZxMRlpbXmIbRoC-YfGzvPZi7EdBQGwvpP4,6887
30
30
  auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
31
31
  auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- auto_editor/render/audio.py,sha256=lEmPuRKW5QipJV6ncCx8CHjg7wyK1knwqRM5p7zKvBY,8816
32
+ auto_editor/render/audio.py,sha256=pUhD4rQZfUnyzKgpuxNxl_2CUGwbkAWo2356HUAW7VM,8835
33
33
  auto_editor/render/subtitle.py,sha256=D4WDiY4iM9HsNfJvZay7zv_gvZPvyd12nd9Fi9vbPjQ,4646
34
- auto_editor/render/video.py,sha256=LufVJJW-r0_5xWSGGpOIH3Clh63FWxEJuct-TORErDQ,13286
34
+ auto_editor/render/video.py,sha256=fEyu-30FYW-f1h-ulbRrZdXrR0bhy0BkBKiJniuwV8Y,13225
35
35
  auto_editor/subcommands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- auto_editor/subcommands/desc.py,sha256=CWnRk3LCTV_sMVJVA45ZMBfPy_ESfwqkySMVuW8Fbrg,1032
37
- auto_editor/subcommands/info.py,sha256=etsjaKBEy0AxDtZ7XZNiizSmBnx8Wf2HeRfUyPyBCqA,6237
38
- auto_editor/subcommands/levels.py,sha256=utCuRmpa2mirnQ_t1ogPexqjZAUBTC9KrCSS_BbSyBg,4009
36
+ auto_editor/subcommands/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
37
+ auto_editor/subcommands/info.py,sha256=N6dXeJf8WXAJjVtfY99fQNu2d-kiX86X6SH0QblWkOg,6571
38
+ auto_editor/subcommands/levels.py,sha256=XHMG3jsdoXBvG0TlP1bBbtjD0m5EgWnOMBTIYx8VAnA,4001
39
39
  auto_editor/subcommands/palet.py,sha256=tbQoRWoT4jR3yu0etGApfprM-oQgXIjC-rIY-QG3nM0,655
40
- auto_editor/subcommands/repl.py,sha256=j5jxWgscaaRULa6ZsrV4tDJQB4vOzxiEQR0jI90v5s0,3725
41
- auto_editor/subcommands/subdump.py,sha256=GGekYMnqLkcqfihKjlHcuWkMazvgsYTF0q4ulEDOrRc,1669
42
- auto_editor/subcommands/test.py,sha256=YOhmvehSohM1nCbU-Ivub_xEJEM57NwHuadLbrojJ84,24823
40
+ auto_editor/subcommands/repl.py,sha256=xoNq88PtbvX3r1-FLStOb5jNoJ_rFzrl7R3Tk8a7zyI,3717
41
+ auto_editor/subcommands/subdump.py,sha256=Bm1PI1Gd2kQR2FFdgG9kXSXSZsAEOsSToSE5_BGF8UA,836
42
+ auto_editor/subcommands/test.py,sha256=2N1Hk03Oofs9WssvrUfDMaDFEp9ngcu9IwIgXkYfcGk,24810
43
43
  auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
44
  auto_editor/utils/bar.py,sha256=eWpiXZpRc2v2LW-EaoAgG_cTtMh5275or8Ttda3Ei-I,3974
45
45
  auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
46
46
  auto_editor/utils/cmdkw.py,sha256=XApxw7FZBOEJV9N4LHhdw1GVfHbFfCjr-zCZ1gJsSvY,6002
47
- auto_editor/utils/container.py,sha256=WOMlUJ5pxVmeYsy79uCWMU5fCzM4tBRupAx-_Q5-PLg,7939
47
+ auto_editor/utils/container.py,sha256=cl8wN5w-PjShPabnppil56r2dykQCfWdsR45jBbCkuo,7976
48
48
  auto_editor/utils/encoder.py,sha256=auNYo7HXbcU4iTUCc0LE5lpwFmSvdWvBm6-5KIaRK8w,2983
49
49
  auto_editor/utils/func.py,sha256=H38xO6Wxg1TZILVrx-nCowCzj_mqBUtJuOFp4DV3Hsc,4843
50
50
  auto_editor/utils/log.py,sha256=6j2EWE97_urQijBvxhk2Gr2-VO_KNR1XbEobcAtTG-w,2668
51
51
  auto_editor/utils/types.py,sha256=aWyJpVBjmctxlxiL5o8r6lplKnaFSjVNQlcoXFgfmSk,11533
52
- auto_editor-24.9.1.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
53
- auto_editor-24.9.1.dist-info/METADATA,sha256=KeE8QXLu3b7IVUd5-3c6A_rax_5LXoqzuSN96wKp4gk,7092
54
- auto_editor-24.9.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
55
- auto_editor-24.9.1.dist-info/entry_points.txt,sha256=-H7zdTw4MqnAcwrN5xTNkGIhzZtJMxS9r6lTMeR9-aA,240
56
- auto_editor-24.9.1.dist-info/top_level.txt,sha256=xwV1JV1ZeRmlH9VeBRZXgXtWHpWSD4w1mY5II56D3ns,22
57
- auto_editor-24.9.1.dist-info/RECORD,,
52
+ auto_editor-24.19.1.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
53
+ auto_editor-24.19.1.dist-info/METADATA,sha256=VME77PVtWtT0La0M_iPxS6uDiqJufmvfesvyMx5MYyg,6284
54
+ auto_editor-24.19.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
55
+ auto_editor-24.19.1.dist-info/entry_points.txt,sha256=-H7zdTw4MqnAcwrN5xTNkGIhzZtJMxS9r6lTMeR9-aA,240
56
+ auto_editor-24.19.1.dist-info/top_level.txt,sha256=xwV1JV1ZeRmlH9VeBRZXgXtWHpWSD4w1mY5II56D3ns,22
57
+ auto_editor-24.19.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5