auto-editor 26.3.2__py3-none-any.whl → 27.0.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.
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- import av
6
+ import bv
7
7
  import numpy as np
8
8
 
9
9
  from auto_editor.output import parse_bitrate
@@ -11,12 +11,11 @@ from auto_editor.timeline import TlImage, TlRect, TlVideo
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from collections.abc import Iterator
14
- from typing import Any
15
14
 
15
+ from auto_editor.__main__ import Args
16
16
  from auto_editor.ffwrapper import FileInfo
17
17
  from auto_editor.timeline import v3
18
18
  from auto_editor.utils.log import Log
19
- from auto_editor.utils.types import Args
20
19
 
21
20
 
22
21
  @dataclass(slots=True)
@@ -25,15 +24,12 @@ class VideoFrame:
25
24
  src: FileInfo
26
25
 
27
26
 
28
- allowed_pix_fmt = av.video.frame.supported_np_pix_fmts
29
-
30
-
31
- def make_solid(width: int, height: int, pix_fmt: str, bg: str) -> av.VideoFrame:
27
+ def make_solid(width: int, height: int, pix_fmt: str, bg: str) -> bv.VideoFrame:
32
28
  hex_color = bg.lstrip("#").upper()
33
29
  rgb_color = tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4))
34
30
 
35
31
  rgb_array = np.full((height, width, 3), rgb_color, dtype=np.uint8)
36
- rgb_frame = av.VideoFrame.from_ndarray(rgb_array, format="rgb24")
32
+ rgb_frame = bv.VideoFrame.from_ndarray(rgb_array, format="rgb24")
37
33
  return rgb_frame.reformat(format=pix_fmt)
38
34
 
39
35
 
@@ -42,11 +38,11 @@ def make_image_cache(tl: v3) -> dict[tuple[FileInfo, int], np.ndarray]:
42
38
  for clip in tl.v:
43
39
  for obj in clip:
44
40
  if isinstance(obj, TlImage) and (obj.src, obj.width) not in img_cache:
45
- with av.open(obj.src.path) as cn:
41
+ with bv.open(obj.src.path) as cn:
46
42
  my_stream = cn.streams.video[0]
47
43
  for frame in cn.decode(my_stream):
48
44
  if obj.width != 0:
49
- graph = av.filter.Graph()
45
+ graph = bv.filter.Graph()
50
46
  graph.link_nodes(
51
47
  graph.add_buffer(template=my_stream),
52
48
  graph.add("scale", f"{obj.width}:-1"),
@@ -61,17 +57,17 @@ def make_image_cache(tl: v3) -> dict[tuple[FileInfo, int], np.ndarray]:
61
57
 
62
58
 
63
59
  def render_av(
64
- output: av.container.OutputContainer, tl: v3, args: Args, log: Log
65
- ) -> Any:
66
- from_ndarray = av.VideoFrame.from_ndarray
60
+ output: bv.container.OutputContainer, tl: v3, args: Args, log: Log
61
+ ) -> Iterator[tuple[int, bv.VideoFrame]]:
62
+ from_ndarray = bv.VideoFrame.from_ndarray
67
63
 
68
64
  src = tl.src
69
- cns: dict[FileInfo, av.container.InputContainer] = {}
70
- decoders: dict[FileInfo, Iterator[av.VideoFrame]] = {}
65
+ cns: dict[FileInfo, bv.container.InputContainer] = {}
66
+ decoders: dict[FileInfo, Iterator[bv.VideoFrame]] = {}
71
67
  seek_cost: dict[FileInfo, int] = {}
72
68
  tous: dict[FileInfo, int] = {}
73
69
 
74
- target_pix_fmt = "yuv420p" # Reasonable default
70
+ pix_fmt = "yuv420p" # Reasonable default
75
71
  target_fps = tl.tb # Always constant
76
72
  img_cache = make_image_cache(tl)
77
73
 
@@ -81,7 +77,7 @@ def render_av(
81
77
  first_src = src
82
78
 
83
79
  if src not in cns:
84
- cns[src] = av.open(f"{src.path}")
80
+ cns[src] = bv.open(f"{src.path}")
85
81
 
86
82
  for src, cn in cns.items():
87
83
  if len(cn.streams.video) > 0:
@@ -101,30 +97,30 @@ def render_av(
101
97
  decoders[src] = cn.decode(stream)
102
98
 
103
99
  if src == first_src and stream.pix_fmt is not None:
104
- target_pix_fmt = stream.pix_fmt
100
+ pix_fmt = stream.pix_fmt
105
101
 
106
102
  log.debug(f"Tous: {tous}")
107
103
  log.debug(f"Clips: {tl.v}")
108
104
 
109
- codec = av.Codec(args.video_codec, "w")
105
+ codec = bv.Codec(args.video_codec, "w")
106
+
107
+ need_valid_fmt = True
108
+ if codec.video_formats is not None:
109
+ for video_format in codec.video_formats:
110
+ if pix_fmt == video_format.name:
111
+ need_valid_fmt = False
112
+ break
110
113
 
111
- if codec.canonical_name == "gif":
112
- if codec.video_formats is not None and target_pix_fmt in (
113
- f.name for f in codec.video_formats
114
- ):
115
- target_pix_fmt = target_pix_fmt
114
+ if need_valid_fmt:
115
+ if codec.canonical_name == "gif":
116
+ pix_fmt = "rgb8"
117
+ elif codec.canonical_name == "prores":
118
+ pix_fmt = "yuv422p10le"
116
119
  else:
117
- target_pix_fmt = "rgb8"
118
- elif codec.canonical_name == "prores":
119
- target_pix_fmt = "yuv422p10le"
120
- else:
121
- target_pix_fmt = (
122
- target_pix_fmt if target_pix_fmt in allowed_pix_fmt else "yuv420p"
123
- )
120
+ pix_fmt = "yuv420p"
124
121
 
125
122
  del codec
126
- ops = {"mov_flags": "faststart"}
127
- output_stream = output.add_stream(args.video_codec, rate=target_fps, options=ops)
123
+ output_stream = output.add_stream(args.video_codec, rate=target_fps)
128
124
 
129
125
  cc = output_stream.codec_context
130
126
  if args.vprofile is not None:
@@ -136,8 +132,8 @@ def render_av(
136
132
 
137
133
  cc.profile = args.vprofile.title()
138
134
 
139
- yield output_stream
140
- if not isinstance(output_stream, av.VideoStream):
135
+ yield output_stream # type: ignore
136
+ if not isinstance(output_stream, bv.VideoStream):
141
137
  log.error(f"Not a known video codec: {args.video_codec}")
142
138
  if src.videos and src.videos[0].lang is not None:
143
139
  output_stream.metadata["language"] = src.videos[0].lang
@@ -148,10 +144,10 @@ def render_av(
148
144
  else:
149
145
  target_width = max(round(tl.res[0] * args.scale), 2)
150
146
  target_height = max(round(tl.res[1] * args.scale), 2)
151
- scale_graph = av.filter.Graph()
147
+ scale_graph = bv.filter.Graph()
152
148
  scale_graph.link_nodes(
153
149
  scale_graph.add(
154
- "buffer", video_size="1x1", time_base="1/1", pix_fmt=target_pix_fmt
150
+ "buffer", video_size="1x1", time_base="1/1", pix_fmt=pix_fmt
155
151
  ),
156
152
  scale_graph.add("scale", f"{target_width}:{target_height}"),
157
153
  scale_graph.add("buffersink"),
@@ -159,7 +155,7 @@ def render_av(
159
155
 
160
156
  output_stream.width = target_width
161
157
  output_stream.height = target_height
162
- output_stream.pix_fmt = target_pix_fmt
158
+ output_stream.pix_fmt = pix_fmt
163
159
  output_stream.framerate = target_fps
164
160
 
165
161
  color_range = src.videos[0].color_range
@@ -191,7 +187,7 @@ def render_av(
191
187
  frames_saved = 0
192
188
 
193
189
  bg = args.background
194
- null_frame = make_solid(target_width, target_height, target_pix_fmt, bg)
190
+ null_frame = make_solid(target_width, target_height, pix_fmt, bg)
195
191
  frame_index = -1
196
192
 
197
193
  for index in range(tl.end):
@@ -250,7 +246,7 @@ def render_av(
250
246
 
251
247
  if (frame.width, frame.height) != tl.res:
252
248
  width, height = tl.res
253
- graph = av.filter.Graph()
249
+ graph = bv.filter.Graph()
254
250
  graph.link_nodes(
255
251
  graph.add_buffer(template=my_stream),
256
252
  graph.add(
@@ -262,7 +258,7 @@ def render_av(
262
258
  ).vpush(frame)
263
259
  frame = graph.vpull()
264
260
  elif isinstance(obj, TlRect):
265
- graph = av.filter.Graph()
261
+ graph = bv.filter.Graph()
266
262
  x, y = obj.x, obj.y
267
263
  graph.link_nodes(
268
264
  graph.add_buffer(template=my_stream),
@@ -308,9 +304,9 @@ def render_av(
308
304
  scale_graph.vpush(frame)
309
305
  frame = scale_graph.vpull()
310
306
 
311
- if frame.format.name != target_pix_fmt:
312
- frame = frame.reformat(format=target_pix_fmt)
313
-
314
- yield (index, from_ndarray(frame.to_ndarray(), format=frame.format.name))
307
+ frame = frame.reformat(format=pix_fmt)
308
+ frame.pts = None # type: ignore
309
+ frame.time_base = 0 # type: ignore
310
+ yield (index, frame)
315
311
 
316
312
  log.debug(f"Total frames saved seeking: {frames_saved}")
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 natural, number, parse_color, threshold
9
+ from auto_editor.utils.types import CoerceError, natural, number, parse_color
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from collections.abc import Iterator
@@ -128,6 +128,13 @@ class TlRect:
128
128
  }
129
129
 
130
130
 
131
+ def threshold(val: str | float) -> float:
132
+ num = number(val)
133
+ if num > 1 or num < 0:
134
+ raise CoerceError(f"'{val}': Threshold must be between 0 and 1 (0%-100%)")
135
+ return num
136
+
137
+
131
138
  video_builder = pAttrs(
132
139
  "video",
133
140
  pAttr("start", Required, is_nat, natural),
@@ -3,8 +3,8 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from typing import TypedDict
5
5
 
6
- import av
7
- from av.codec import Codec
6
+ import bv
7
+ from bv.codec import Codec
8
8
 
9
9
 
10
10
  class DictContainer(TypedDict, total=False):
@@ -61,7 +61,7 @@ def codec_type(x: str) -> str:
61
61
 
62
62
 
63
63
  def container_constructor(ext: str) -> Container:
64
- with av.open(f".{ext}", "w") as container:
64
+ with bv.open(f".{ext}", "w") as container:
65
65
  codecs = container.supported_codecs
66
66
  if ext == "webm":
67
67
  vdefault = "vp9"
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
- from dataclasses import dataclass, field
5
4
  from fractions import Fraction
6
5
 
7
6
 
@@ -9,16 +8,7 @@ class CoerceError(Exception):
9
8
  pass
10
9
 
11
10
 
12
- def _comma_coerce(name: str, val: str, num_args: int) -> list[str]:
13
- vals = val.strip().split(",")
14
- if num_args > len(vals):
15
- raise CoerceError(f"Too few arguments for {name}.")
16
- if len(vals) > num_args:
17
- raise CoerceError(f"Too many arguments for {name}.")
18
- return vals
19
-
20
-
21
- def _split_num_str(val: str | float) -> tuple[float, str]:
11
+ def split_num_str(val: str | float) -> tuple[float, str]:
22
12
  if isinstance(val, float | int):
23
13
  return val, ""
24
14
 
@@ -35,14 +25,9 @@ def _split_num_str(val: str | float) -> tuple[float, str]:
35
25
  return float(num), unit
36
26
 
37
27
 
38
- def _unit_check(unit: str, allowed_units: tuple[str, ...]) -> None:
39
- if unit not in allowed_units:
40
- raise CoerceError(f"Unknown unit: '{unit}'")
41
-
42
-
43
28
  # Numbers: 0, 1, 2, 3, ...
44
29
  def natural(val: str | float) -> int:
45
- num, unit = _split_num_str(val)
30
+ num, unit = split_num_str(val)
46
31
  if unit != "":
47
32
  raise CoerceError(f"'{val}': Natural does not allow units.")
48
33
  if not isinstance(num, int) and not num.is_integer():
@@ -69,25 +54,12 @@ def number(val: str | float) -> float:
69
54
  raise CoerceError(f"'{val}': Denominator must not be zero.")
70
55
  return vs[0] / vs[1]
71
56
 
72
- num, unit = _split_num_str(val)
57
+ num, unit = split_num_str(val)
73
58
  if unit == "%":
74
59
  return num / 100
75
- _unit_check(unit, ("",))
76
- return num
77
-
78
-
79
- def speed(val: str) -> float:
80
- _s = number(val)
81
- if _s <= 0 or _s > 99999:
82
- return 99999.0
83
- return _s
84
-
85
-
86
- def threshold(val: str | float) -> float:
87
- num = number(val)
88
- if num > 1 or num < 0:
89
- raise CoerceError(f"'{val}': Threshold must be between 0 and 1 (0%-100%)")
90
- return num
60
+ if unit == "":
61
+ return num
62
+ raise CoerceError(f"Unknown unit: '{unit}'")
91
63
 
92
64
 
93
65
  def frame_rate(val: str) -> Fraction:
@@ -102,14 +74,6 @@ def frame_rate(val: str) -> Fraction:
102
74
  return Fraction(val)
103
75
 
104
76
 
105
- def sample_rate(val: str) -> int:
106
- num, unit = _split_num_str(val)
107
- if unit in {"kHz", "KHz"}:
108
- return natural(num * 1000)
109
- _unit_check(unit, ("", "Hz"))
110
- return natural(num)
111
-
112
-
113
77
  def time(val: str, tb: Fraction) -> int:
114
78
  if ":" in val:
115
79
  boxes = val.split(":")
@@ -121,7 +85,7 @@ def time(val: str, tb: Fraction) -> int:
121
85
  )
122
86
  raise CoerceError(f"'{val}': Invalid time format")
123
87
 
124
- num, unit = _split_num_str(val)
88
+ num, unit = split_num_str(val)
125
89
  if unit in {"s", "sec", "secs", "second", "seconds"}:
126
90
  return round(num * tb)
127
91
  if unit in {"min", "mins", "minute", "minutes"}:
@@ -136,25 +100,6 @@ def time(val: str, tb: Fraction) -> int:
136
100
  return int(num)
137
101
 
138
102
 
139
- def margin(val: str) -> tuple[str, str]:
140
- vals = val.strip().split(",")
141
- if len(vals) == 1:
142
- vals.append(vals[0])
143
- if len(vals) != 2:
144
- raise CoerceError("--margin has too many arguments.")
145
- return vals[0], vals[1]
146
-
147
-
148
- def time_range(val: str) -> tuple[str, str]:
149
- a = _comma_coerce("time_range", val, 2)
150
- return a[0], a[1]
151
-
152
-
153
- def speed_range(val: str) -> tuple[float, str, str]:
154
- a = _comma_coerce("speed_range", val, 3)
155
- return number(a[0]), a[1], a[2]
156
-
157
-
158
103
  def parse_color(val: str) -> str:
159
104
  """
160
105
  Convert a color str into an RGB tuple
@@ -179,62 +124,6 @@ def parse_color(val: str) -> str:
179
124
  raise ValueError(f"Invalid Color: '{color}'")
180
125
 
181
126
 
182
- def resolution(val: str | None) -> tuple[int, int] | None:
183
- if val is None:
184
- return None
185
- vals = val.strip().split(",")
186
- if len(vals) != 2:
187
- raise CoerceError(f"'{val}': Resolution takes two numbers")
188
-
189
- return natural(vals[0]), natural(vals[1])
190
-
191
-
192
- @dataclass(slots=True)
193
- class Args:
194
- yt_dlp_location: str = "yt-dlp"
195
- download_format: str | None = None
196
- output_format: str | None = None
197
- yt_dlp_extras: str | None = None
198
- video_codec: str = "auto"
199
- audio_codec: str = "auto"
200
- video_bitrate: str = "auto"
201
- vprofile: str | None = None
202
- audio_bitrate: str = "auto"
203
- scale: float = 1.0
204
- fragmented: bool = False
205
- no_fragmented: bool = False
206
- sn: bool = False
207
- dn: bool = False
208
- no_seek: bool = False
209
- cut_out: list[tuple[str, str]] = field(default_factory=list)
210
- add_in: list[tuple[str, str]] = field(default_factory=list)
211
- set_speed_for_range: list[tuple[float, str, str]] = field(default_factory=list)
212
- frame_rate: Fraction | None = None
213
- sample_rate: int | None = None
214
- resolution: tuple[int, int] | None = None
215
- background: str = "#000000"
216
- edit: str = "audio"
217
- keep_tracks_separate: bool = False
218
- audio_normalize: str = "#f"
219
- export: str | None = None
220
- player: str | None = None
221
- no_open: bool = False
222
- temp_dir: str | None = None
223
- progress: str = "modern"
224
- version: bool = False
225
- debug: bool = False
226
- config: bool = False
227
- quiet: bool = False
228
- preview: bool = False
229
- no_cache: bool = False
230
- margin: tuple[str, str] = ("0.2s", "0.2s")
231
- silent_speed: float = 99999.0
232
- video_speed: float = 1.0
233
- output_file: str | None = None
234
- help: bool = False
235
- input: list[str] = field(default_factory=list)
236
-
237
-
238
127
  colormap = {
239
128
  # Taken from https://www.w3.org/TR/css-color-4/#named-color
240
129
  "aliceblue": "#f0f8ff",
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: auto-editor
3
- Version: 26.3.2
3
+ Version: 27.0.0
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -11,8 +11,9 @@ 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.24
15
- Requires-Dist: pyav==14.2.*
14
+ Requires-Dist: numpy<3.0,>=2
15
+ Requires-Dist: basswood-av<16,>=15.0.0
16
+ Dynamic: license-file
16
17
 
17
18
  <p align="center"><img src="https://auto-editor.com/img/auto-editor-banner.webp" title="Auto-Editor" width="700"></p>
18
19
 
@@ -21,7 +22,7 @@ Requires-Dist: pyav==14.2.*
21
22
  ---
22
23
 
23
24
  [![Actions Status](https://github.com/wyattblue/auto-editor/workflows/build/badge.svg)](https://github.com/wyattblue/auto-editor/actions)
24
- <a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
25
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
25
26
 
26
27
  Before doing the real editing, you first cut out the "dead space" which is typically silence. This is known as a "first pass". Cutting these is a boring task, especially if the video is very long.
27
28
 
@@ -175,9 +176,9 @@ auto-editor --margin --help
175
176
 
176
177
  ## Articles
177
178
  - [How to Install Auto-Editor](https://auto-editor.com/installing)
178
- - [All the Options (And What They Do)](https://auto-editor.com/options)
179
+ - [All the Options (And What They Do)](https://auto-editor.com/ref/options)
179
180
  - [Docs](https://auto-editor.com/docs)
180
- - [Blog](https://auto-editor.com/blog)
181
+ - [Blog](https://basswood-io.com/blog/)
181
182
 
182
183
  ## Copyright
183
184
  Auto-Editor is under the [Public Domain](https://github.com/WyattBlue/auto-editor/blob/master/LICENSE) and includes all directories besides the ones listed below. Auto-Editor was created by [these people.](https://auto-editor.com/blog/thank-you-early-testers)
@@ -1,56 +1,56 @@
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
- auto_editor/ffwrapper.py,sha256=1lYYfq8gVgMVkYWeAEYDPAHCwFCYbKQwy0FxYBxMzk8,4765
1
+ auto_editor/__init__.py,sha256=KViW4EBA7GbEItgvzRFMRG3Xq3ASIbwCSsNE9WwakVc,23
2
+ auto_editor/__main__.py,sha256=iKfQKFOQDv0-uZecLzfb8A-0k5dAxxe94FaIIEayGXc,15575
3
+ auto_editor/analyze.py,sha256=CeJG0LI9wXZk1R-QPrNGPS4za-_Avd8y7H-D437DqLg,12300
4
+ auto_editor/edit.py,sha256=9rJC6IY-jMdQDolJBxfzng43ddYQpC8zuZQHLzEct8s,20698
5
+ auto_editor/ffwrapper.py,sha256=b2XDsJMmBnYtRAGrIjqhYH909GNo30tMW3FCZUhhZ5k,4781
6
6
  auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
7
- auto_editor/make_layers.py,sha256=Yl4xYkMHume_VvRW8Ii48hyx-ZtFr8WMmVYXJMSUmg0,9581
8
- auto_editor/output.py,sha256=ho8Lpqz4Sv_Gw0Vj2OvG39s83xHpyZlvtRNryTPbXqc,2563
7
+ auto_editor/json.py,sha256=8IVhZJSLx2IVqJsbR5YKDvbHOhgIOvdQmYNpMdMG_xA,9332
8
+ auto_editor/make_layers.py,sha256=VKdBj6Kbe5CAoDIDK_MdqHVu2TXZ0sjeEbqyFTA27gI,9617
9
+ auto_editor/output.py,sha256=YgkZw0WyVfcTYH-4j6kYfvB6A1BjxGLmfOQVFi-QK_o,2561
9
10
  auto_editor/preview.py,sha256=cqQdozM2IB-5qXHNxeqiSrSdEIzlMfjD4SU-NX9sYZ0,3052
10
- auto_editor/timeline.py,sha256=XfaH9cH-RB-MObOpMr5IfLcqJcjmabO1XwkUkT3_FQM,8186
11
+ auto_editor/timeline.py,sha256=f1cxAhduoVHBTRgRwGqI3BKBb31aAVrWkEahH5gJ-0o,8379
11
12
  auto_editor/vanparse.py,sha256=Ug5A2QaRqGiw4l55Z_h9T2QU1x0WqRibR7yY5rQ0WTk,10002
12
13
  auto_editor/wavfile.py,sha256=afFfje8cK9lFjIkYoBbQHfvQIpsEPxWvspCtFhUKlAw,9499
13
14
  auto_editor/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
15
  auto_editor/cmds/cache.py,sha256=bViYbtVXefTeEIUvSanDfA6cG35ep1N_Jvtz7ZjgIkY,1959
15
16
  auto_editor/cmds/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
16
- auto_editor/cmds/info.py,sha256=vYa1hYdE8kDTE8AS3kwXlnd59X6CrE2GtIEQ7UmlpRY,7010
17
- auto_editor/cmds/levels.py,sha256=6LqXfNBca__KGeA8kJqJqH0MBR3GudaFoWU0_dnv5bI,5635
17
+ auto_editor/cmds/info.py,sha256=Af2aa9vW_bNfFGbRgQMqc7jj17skC6koE0XL3aF0jl0,7005
18
+ auto_editor/cmds/levels.py,sha256=EQ2hfRTe6779OtyzPIBZc5g3WhMZRddVkQWSCVV7UlE,5635
18
19
  auto_editor/cmds/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
19
20
  auto_editor/cmds/repl.py,sha256=8DgMw-XyfR5XctSmwtk4_1-zxs3ooMs72BfMRlVqLvY,3412
20
- auto_editor/cmds/subdump.py,sha256=af_XBf7kaevqHn1A71z8C-7x8pS5WKD9FE_ugkCw6rk,665
21
- auto_editor/cmds/test.py,sha256=SzjtsEteztXFbkvLRfL9BPSgK8Qez_-yXPR5ts_tu9E,26465
21
+ auto_editor/cmds/subdump.py,sha256=kHg8nfUi6I6VeJjEgMxupPa666qsYUh7ZEUxint7Gqo,2443
22
+ auto_editor/cmds/test.py,sha256=DK7T5BEtMhfrsTJSmXN_tlBKgHdajS-bICiGttq8M0c,27416
22
23
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
24
  auto_editor/formats/fcp11.py,sha256=sqjC36jI47ICPLjZJYiqGwY7foOnWOiNjkPFLdgSnI4,5208
24
25
  auto_editor/formats/fcp7.py,sha256=x5cagTzGCAW3i3M6m7TZC1h8gLfSmX1UK-iiDuCpdfs,20289
25
- auto_editor/formats/json.py,sha256=kpYioIgFaVvhmS4I2NKi4ax574sRATe0Xfs0Wht9GUY,7693
26
+ auto_editor/formats/json.py,sha256=_dPrNr1ZC8kfSHRinG0rFudv1XFsZf2VlGAl084lMQ8,7663
26
27
  auto_editor/formats/shotcut.py,sha256=-ES854LLFCMCBe100JRJedDmuk8zPev17aQMTrzPv-g,4923
27
28
  auto_editor/formats/utils.py,sha256=LYXDiqOk9WwUorLGw2D0M7In9BNDkoKikNawuks7hqE,1648
28
29
  auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
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=XKMVMybHZK5c_iB368T4pFqI7Yfl5o4eDVa5XMNLEow,24082
33
- auto_editor/lang/stdenv.py,sha256=sQyOD3bttjcieWMtTMNpE6blEZriAfHtSZHbuvC09R8,43762
32
+ auto_editor/lang/palet.py,sha256=RQjyIZMJSWnzDkHTu-5mt74o9_4zO4VrcH-wLojCF7A,24113
33
+ auto_editor/lang/stdenv.py,sha256=Acf9CVXzglh3KgsJXrO7IyMMjTY5jqpz5u0QADXtDHA,44127
34
34
  auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,7586
36
36
  auto_editor/lib/data_structs.py,sha256=Hnzl5gWvo-geTU0g-lGejj6HQW3VvPv0NBEj2XoGskY,7089
37
37
  auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
38
38
  auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- auto_editor/render/audio.py,sha256=_GuX0WNY1YeumgBN3bWqgwVXiuhpvx7sijABxqyO2ag,12580
40
- auto_editor/render/subtitle.py,sha256=jtNRKvgo1fpHTrAfGZqdkNeNgGgasw-K-4PwIKiWwfM,6231
41
- auto_editor/render/video.py,sha256=JBVl8w-hQ6zrs97iA527LPsBZ9s601SVSJs2bSMCq88,12185
39
+ auto_editor/render/audio.py,sha256=7iQMtex8hzzLH80pwLD_nwN_ZH3GHjWRmER45ZVEzPk,12646
40
+ auto_editor/render/subtitle.py,sha256=F27T8OsAojUIGTGBWqTdH36h0BnHXSExxIqzOtqyZoE,6129
41
+ auto_editor/render/video.py,sha256=g2TbuCNzhbE8KsS-_3XRLLdmQKFROdwfkymwIbGmtqc,12012
42
42
  auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
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
- auto_editor/utils/container.py,sha256=C_Ahh7nlMEX4DNQ2M_cITPPbYcIL68r4I_AgFy0OD6o,2487
46
+ auto_editor/utils/container.py,sha256=UPqlAos7UIf_ARIAbB3wuaSv1iNLFgy22YFGK8559yM,2487
47
47
  auto_editor/utils/func.py,sha256=C8ucgsSEzPyBc-8obhsCXd_uQW0cnCdBn1KVxB7FHjU,2747
48
48
  auto_editor/utils/log.py,sha256=wPNf6AabV-0cnoS_bPLv1Lh7llQBtNqPKeh07einOuc,3701
49
- auto_editor/utils/types.py,sha256=HxFafTgmL_5HQbfR7AR_4Q6o8iHk1z-T6WT90PgQHiY,10817
50
- docs/build.py,sha256=POy8X8QOBYe_8A8HI_yiVI_Qg9E5mLpn1z7AHQr0_vQ,1888
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,,
49
+ auto_editor/utils/types.py,sha256=j2hd4zMQ9EftDy41Ji2_PFru_7HEZObd9yKA0BJxFaY,7616
50
+ auto_editor-27.0.0.dist-info/licenses/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
51
+ docs/build.py,sha256=g1uc1H9T_naGaermUiVMMwUpbT0IWElRhjgT0fvCh8w,1914
52
+ auto_editor-27.0.0.dist-info/METADATA,sha256=VhQA-UaBuv5unUT8MUVkQ_oVwGisctF3BS5tTSTkDfA,6165
53
+ auto_editor-27.0.0.dist-info/WHEEL,sha256=tTnHoFhvKQHCh4jz3yCn0WPTYIy7wXx3CJtJ7SJGV7c,91
54
+ auto_editor-27.0.0.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
+ auto_editor-27.0.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
+ auto_editor-27.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (77.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
docs/build.py CHANGED
@@ -19,13 +19,22 @@ def main():
19
19
  parser = main_options(parser)
20
20
 
21
21
  with open("src/ref/options.html", "w") as file:
22
- file.write(
23
- '{{ headerdesc "Options" "These are the options and flags that auto-editor uses." }}\n'
24
- "<body>\n"
25
- "{{ nav }}\n"
26
- '<section class="section">\n'
27
- '<div class="container">\n'
28
- )
22
+ file.write("""\
23
+ <!DOCTYPE html>
24
+ <html lang="en">
25
+ <head>
26
+ {{ init_head }}
27
+ {{ headerdesc "Options" "These are the options and flags that auto-editor uses." }}
28
+ {{ head_icon }}
29
+ <style>
30
+ {{ core_style }}
31
+ </style>
32
+ <body>
33
+ {{ nav }}
34
+ <section class="section">
35
+ <div class="container">
36
+ """)
37
+
29
38
  for op in parser.args:
30
39
  if isinstance(op, OptionText):
31
40
  file.write(f"<h2>{escape(op.text)}</h2>\n")