auto-editor 26.3.3__py3-none-any.whl → 27.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.
- auto_editor/__init__.py +1 -1
- auto_editor/__main__.py +17 -5
- auto_editor/analyze.py +30 -36
- auto_editor/cmds/desc.py +2 -2
- auto_editor/cmds/info.py +3 -3
- auto_editor/cmds/levels.py +5 -5
- auto_editor/cmds/repl.py +3 -8
- auto_editor/cmds/subdump.py +62 -8
- auto_editor/cmds/test.py +92 -42
- auto_editor/edit.py +59 -111
- auto_editor/ffwrapper.py +91 -87
- auto_editor/formats/fcp11.py +10 -8
- auto_editor/formats/fcp7.py +11 -12
- auto_editor/formats/json.py +10 -11
- auto_editor/{lang/json.py → json.py} +39 -43
- auto_editor/lang/palet.py +2 -2
- auto_editor/lang/stdenv.py +13 -0
- auto_editor/make_layers.py +18 -8
- auto_editor/render/audio.py +239 -102
- auto_editor/render/subtitle.py +10 -14
- auto_editor/render/video.py +41 -46
- auto_editor/timeline.py +60 -10
- auto_editor/utils/container.py +21 -14
- auto_editor/utils/func.py +21 -0
- {auto_editor-26.3.3.dist-info → auto_editor-27.1.0.dist-info}/METADATA +8 -7
- auto_editor-27.1.0.dist-info/RECORD +54 -0
- {auto_editor-26.3.3.dist-info → auto_editor-27.1.0.dist-info}/WHEEL +1 -1
- docs/build.py +16 -7
- auto_editor/output.py +0 -86
- auto_editor/wavfile.py +0 -310
- auto_editor-26.3.3.dist-info/RECORD +0 -56
- {auto_editor-26.3.3.dist-info → auto_editor-27.1.0.dist-info}/entry_points.txt +0 -0
- {auto_editor-26.3.3.dist-info → auto_editor-27.1.0.dist-info/licenses}/LICENSE +0 -0
- {auto_editor-26.3.3.dist-info → auto_editor-27.1.0.dist-info}/top_level.txt +0 -0
auto_editor/render/video.py
CHANGED
@@ -3,15 +3,14 @@ from __future__ import annotations
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from typing import TYPE_CHECKING
|
5
5
|
|
6
|
-
import
|
6
|
+
import bv
|
7
7
|
import numpy as np
|
8
8
|
|
9
|
-
from auto_editor.output import parse_bitrate
|
10
9
|
from auto_editor.timeline import TlImage, TlRect, TlVideo
|
10
|
+
from auto_editor.utils.func import parse_bitrate
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
13
|
from collections.abc import Iterator
|
14
|
-
from typing import Any
|
15
14
|
|
16
15
|
from auto_editor.__main__ import Args
|
17
16
|
from auto_editor.ffwrapper import FileInfo
|
@@ -25,15 +24,12 @@ class VideoFrame:
|
|
25
24
|
src: FileInfo
|
26
25
|
|
27
26
|
|
28
|
-
|
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 =
|
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
|
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 =
|
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,16 @@ def make_image_cache(tl: v3) -> dict[tuple[FileInfo, int], np.ndarray]:
|
|
61
57
|
|
62
58
|
|
63
59
|
def render_av(
|
64
|
-
output:
|
65
|
-
) ->
|
66
|
-
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
|
-
|
69
|
-
|
70
|
-
decoders: dict[FileInfo, Iterator[av.VideoFrame]] = {}
|
64
|
+
cns: dict[FileInfo, bv.container.InputContainer] = {}
|
65
|
+
decoders: dict[FileInfo, Iterator[bv.VideoFrame]] = {}
|
71
66
|
seek_cost: dict[FileInfo, int] = {}
|
72
67
|
tous: dict[FileInfo, int] = {}
|
73
68
|
|
74
|
-
|
69
|
+
pix_fmt = "yuv420p" # Reasonable default
|
75
70
|
target_fps = tl.tb # Always constant
|
76
71
|
img_cache = make_image_cache(tl)
|
77
72
|
|
@@ -81,7 +76,7 @@ def render_av(
|
|
81
76
|
first_src = src
|
82
77
|
|
83
78
|
if src not in cns:
|
84
|
-
cns[src] =
|
79
|
+
cns[src] = bv.open(f"{src.path}")
|
85
80
|
|
86
81
|
for src, cn in cns.items():
|
87
82
|
if len(cn.streams.video) > 0:
|
@@ -101,30 +96,30 @@ def render_av(
|
|
101
96
|
decoders[src] = cn.decode(stream)
|
102
97
|
|
103
98
|
if src == first_src and stream.pix_fmt is not None:
|
104
|
-
|
99
|
+
pix_fmt = stream.pix_fmt
|
105
100
|
|
106
101
|
log.debug(f"Tous: {tous}")
|
107
102
|
log.debug(f"Clips: {tl.v}")
|
108
103
|
|
109
|
-
codec =
|
104
|
+
codec = bv.Codec(args.video_codec, "w")
|
105
|
+
|
106
|
+
need_valid_fmt = True
|
107
|
+
if codec.video_formats is not None:
|
108
|
+
for video_format in codec.video_formats:
|
109
|
+
if pix_fmt == video_format.name:
|
110
|
+
need_valid_fmt = False
|
111
|
+
break
|
110
112
|
|
111
|
-
if
|
112
|
-
if codec.
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
if need_valid_fmt:
|
114
|
+
if codec.canonical_name == "gif":
|
115
|
+
pix_fmt = "rgb8"
|
116
|
+
elif codec.canonical_name == "prores":
|
117
|
+
pix_fmt = "yuv422p10le"
|
116
118
|
else:
|
117
|
-
|
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
|
-
)
|
119
|
+
pix_fmt = "yuv420p"
|
124
120
|
|
125
121
|
del codec
|
126
|
-
|
127
|
-
output_stream = output.add_stream(args.video_codec, rate=target_fps, options=ops)
|
122
|
+
output_stream = output.add_stream(args.video_codec, rate=target_fps)
|
128
123
|
|
129
124
|
cc = output_stream.codec_context
|
130
125
|
if args.vprofile is not None:
|
@@ -136,8 +131,8 @@ def render_av(
|
|
136
131
|
|
137
132
|
cc.profile = args.vprofile.title()
|
138
133
|
|
139
|
-
yield output_stream
|
140
|
-
if not isinstance(output_stream,
|
134
|
+
yield output_stream # type: ignore
|
135
|
+
if not isinstance(output_stream, bv.VideoStream):
|
141
136
|
log.error(f"Not a known video codec: {args.video_codec}")
|
142
137
|
if src.videos and src.videos[0].lang is not None:
|
143
138
|
output_stream.metadata["language"] = src.videos[0].lang
|
@@ -148,10 +143,10 @@ def render_av(
|
|
148
143
|
else:
|
149
144
|
target_width = max(round(tl.res[0] * args.scale), 2)
|
150
145
|
target_height = max(round(tl.res[1] * args.scale), 2)
|
151
|
-
scale_graph =
|
146
|
+
scale_graph = bv.filter.Graph()
|
152
147
|
scale_graph.link_nodes(
|
153
148
|
scale_graph.add(
|
154
|
-
"buffer", video_size="1x1", time_base="1/1", pix_fmt=
|
149
|
+
"buffer", video_size="1x1", time_base="1/1", pix_fmt=pix_fmt
|
155
150
|
),
|
156
151
|
scale_graph.add("scale", f"{target_width}:{target_height}"),
|
157
152
|
scale_graph.add("buffersink"),
|
@@ -159,7 +154,7 @@ def render_av(
|
|
159
154
|
|
160
155
|
output_stream.width = target_width
|
161
156
|
output_stream.height = target_height
|
162
|
-
output_stream.pix_fmt =
|
157
|
+
output_stream.pix_fmt = pix_fmt
|
163
158
|
output_stream.framerate = target_fps
|
164
159
|
|
165
160
|
color_range = src.videos[0].color_range
|
@@ -191,7 +186,7 @@ def render_av(
|
|
191
186
|
frames_saved = 0
|
192
187
|
|
193
188
|
bg = args.background
|
194
|
-
null_frame = make_solid(target_width, target_height,
|
189
|
+
null_frame = make_solid(target_width, target_height, pix_fmt, bg)
|
195
190
|
frame_index = -1
|
196
191
|
|
197
192
|
for index in range(tl.end):
|
@@ -250,7 +245,7 @@ def render_av(
|
|
250
245
|
|
251
246
|
if (frame.width, frame.height) != tl.res:
|
252
247
|
width, height = tl.res
|
253
|
-
graph =
|
248
|
+
graph = bv.filter.Graph()
|
254
249
|
graph.link_nodes(
|
255
250
|
graph.add_buffer(template=my_stream),
|
256
251
|
graph.add(
|
@@ -262,7 +257,7 @@ def render_av(
|
|
262
257
|
).vpush(frame)
|
263
258
|
frame = graph.vpull()
|
264
259
|
elif isinstance(obj, TlRect):
|
265
|
-
graph =
|
260
|
+
graph = bv.filter.Graph()
|
266
261
|
x, y = obj.x, obj.y
|
267
262
|
graph.link_nodes(
|
268
263
|
graph.add_buffer(template=my_stream),
|
@@ -308,9 +303,9 @@ def render_av(
|
|
308
303
|
scale_graph.vpush(frame)
|
309
304
|
frame = scale_graph.vpull()
|
310
305
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
yield (index,
|
306
|
+
frame = frame.reformat(format=pix_fmt)
|
307
|
+
frame.pts = None # type: ignore
|
308
|
+
frame.time_base = 0 # type: ignore
|
309
|
+
yield (index, frame)
|
315
310
|
|
316
311
|
log.debug(f"Total frames saved seeking: {frames_saved}")
|
auto_editor/timeline.py
CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from typing import TYPE_CHECKING
|
5
5
|
|
6
|
-
from auto_editor.ffwrapper import
|
6
|
+
from auto_editor.ffwrapper import FileInfo, mux
|
7
7
|
from auto_editor.lib.contracts import *
|
8
8
|
from auto_editor.utils.cmdkw import Required, pAttr, pAttrs
|
9
9
|
from auto_editor.utils.types import CoerceError, natural, number, parse_color
|
@@ -187,13 +187,52 @@ ALayer = list[TlAudio]
|
|
187
187
|
ASpace = list[ALayer]
|
188
188
|
|
189
189
|
|
190
|
+
@dataclass(slots=True)
|
191
|
+
class AudioTemplate:
|
192
|
+
lang: str | None
|
193
|
+
|
194
|
+
|
195
|
+
@dataclass(slots=True)
|
196
|
+
class SubtitleTemplate:
|
197
|
+
lang: str | None
|
198
|
+
|
199
|
+
|
200
|
+
@dataclass(slots=True)
|
201
|
+
class Template:
|
202
|
+
sr: int
|
203
|
+
layout: str
|
204
|
+
res: tuple[int, int]
|
205
|
+
audios: list[AudioTemplate]
|
206
|
+
subtitles: list[SubtitleTemplate]
|
207
|
+
|
208
|
+
@classmethod
|
209
|
+
def init(
|
210
|
+
self,
|
211
|
+
src: FileInfo,
|
212
|
+
sr: int | None = None,
|
213
|
+
layout: str | None = None,
|
214
|
+
res: tuple[int, int] | None = None,
|
215
|
+
) -> Template:
|
216
|
+
alist = [AudioTemplate(x.lang) for x in src.audios]
|
217
|
+
slist = [SubtitleTemplate(x.lang) for x in src.subtitles]
|
218
|
+
|
219
|
+
if sr is None:
|
220
|
+
sr = src.get_sr()
|
221
|
+
|
222
|
+
if layout is None:
|
223
|
+
layout = "stereo" if not src.audios else src.audios[0].layout
|
224
|
+
|
225
|
+
if res is None:
|
226
|
+
res = src.get_res()
|
227
|
+
|
228
|
+
return Template(sr, layout, res, alist, slist)
|
229
|
+
|
230
|
+
|
190
231
|
@dataclass
|
191
232
|
class v3:
|
192
|
-
src: FileInfo | None # Used as a template for timeline settings
|
193
233
|
tb: Fraction
|
194
|
-
sr: int
|
195
|
-
res: tuple[int, int]
|
196
234
|
background: str
|
235
|
+
template: Template
|
197
236
|
v: VSpace
|
198
237
|
a: ASpace
|
199
238
|
v1: v1 | None # Is it v1 compatible (linear and only one source)?
|
@@ -286,14 +325,27 @@ video\n"""
|
|
286
325
|
|
287
326
|
return {
|
288
327
|
"version": "3",
|
289
|
-
"resolution": self.res,
|
290
328
|
"timebase": f"{self.tb.numerator}/{self.tb.denominator}",
|
291
|
-
"samplerate": self.sr,
|
292
329
|
"background": self.background,
|
330
|
+
"resolution": self.T.res,
|
331
|
+
"samplerate": self.T.sr,
|
332
|
+
"layout": self.T.layout,
|
293
333
|
"v": v,
|
294
334
|
"a": a,
|
295
335
|
}
|
296
336
|
|
337
|
+
@property
|
338
|
+
def T(self) -> Template:
|
339
|
+
return self.template
|
340
|
+
|
341
|
+
@property
|
342
|
+
def res(self) -> tuple[int, int]:
|
343
|
+
return self.T.res
|
344
|
+
|
345
|
+
@property
|
346
|
+
def sr(self) -> int:
|
347
|
+
return self.T.sr
|
348
|
+
|
297
349
|
|
298
350
|
def make_tracks_dir(path: Path) -> Path:
|
299
351
|
from os import mkdir
|
@@ -310,9 +362,7 @@ def make_tracks_dir(path: Path) -> Path:
|
|
310
362
|
return tracks_dir
|
311
363
|
|
312
364
|
|
313
|
-
def set_stream_to_0(tl: v3, log: Log) -> None:
|
314
|
-
src = tl.src
|
315
|
-
assert src is not None
|
365
|
+
def set_stream_to_0(src: FileInfo, tl: v3, log: Log) -> None:
|
316
366
|
fold = make_tracks_dir(src.path)
|
317
367
|
cache: dict[Path, FileInfo] = {}
|
318
368
|
|
@@ -320,7 +370,7 @@ def set_stream_to_0(tl: v3, log: Log) -> None:
|
|
320
370
|
newtrack = fold / f"{path.stem}_{i}.wav"
|
321
371
|
if newtrack not in cache:
|
322
372
|
mux(path, output=newtrack, stream=i)
|
323
|
-
cache[newtrack] =
|
373
|
+
cache[newtrack] = FileInfo.init(f"{newtrack}", log)
|
324
374
|
return cache[newtrack]
|
325
375
|
|
326
376
|
for alayer in tl.a:
|
auto_editor/utils/container.py
CHANGED
@@ -3,8 +3,10 @@ from __future__ import annotations
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from typing import TypedDict
|
5
5
|
|
6
|
-
import
|
7
|
-
from
|
6
|
+
import bv
|
7
|
+
from bv.codec import Codec
|
8
|
+
|
9
|
+
from auto_editor.utils.log import Log
|
8
10
|
|
9
11
|
|
10
12
|
class DictContainer(TypedDict, total=False):
|
@@ -60,18 +62,23 @@ def codec_type(x: str) -> str:
|
|
60
62
|
return ""
|
61
63
|
|
62
64
|
|
63
|
-
def container_constructor(ext: str) -> Container:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
65
|
+
def container_constructor(ext: str, log: Log) -> Container:
|
66
|
+
try:
|
67
|
+
container = bv.open(f".{ext}", "w")
|
68
|
+
except ValueError:
|
69
|
+
log.error(f"Could not find a suitable format for extension: {ext}")
|
70
|
+
|
71
|
+
codecs = container.supported_codecs
|
72
|
+
if ext == "webm":
|
73
|
+
vdefault = "vp9"
|
74
|
+
else:
|
75
|
+
vdefault = container.default_video_codec
|
76
|
+
adefault = container.default_audio_codec
|
77
|
+
sdefault = container.default_subtitle_codec
|
78
|
+
if sdefault == "none" and ext == "mp4":
|
79
|
+
sdefault = "srt"
|
80
|
+
|
81
|
+
container.close()
|
75
82
|
vcodecs = set()
|
76
83
|
acodecs = set()
|
77
84
|
scodecs = set()
|
auto_editor/utils/func.py
CHANGED
@@ -4,6 +4,9 @@ from typing import TYPE_CHECKING
|
|
4
4
|
|
5
5
|
import numpy as np
|
6
6
|
|
7
|
+
from auto_editor.utils.log import Log
|
8
|
+
from auto_editor.utils.types import split_num_str
|
9
|
+
|
7
10
|
if TYPE_CHECKING:
|
8
11
|
from collections.abc import Callable
|
9
12
|
from fractions import Fraction
|
@@ -105,3 +108,21 @@ def aspect_ratio(width: int, height: int) -> tuple[int, int]:
|
|
105
108
|
|
106
109
|
c = gcd(width, height)
|
107
110
|
return width // c, height // c
|
111
|
+
|
112
|
+
|
113
|
+
def parse_bitrate(input_: str, log: Log) -> int:
|
114
|
+
try:
|
115
|
+
val, unit = split_num_str(input_)
|
116
|
+
except Exception as e:
|
117
|
+
log.error(e)
|
118
|
+
|
119
|
+
if unit.lower() == "k":
|
120
|
+
return int(val * 1000)
|
121
|
+
if unit == "M":
|
122
|
+
return int(val * 1_000_000)
|
123
|
+
if unit == "G":
|
124
|
+
return int(val * 1_000_000_000)
|
125
|
+
if unit == "":
|
126
|
+
return int(val)
|
127
|
+
|
128
|
+
log.error(f"Unknown bitrate: {input_}")
|
@@ -1,9 +1,9 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: auto-editor
|
3
|
-
Version:
|
3
|
+
Version: 27.1.0
|
4
4
|
Summary: Auto-Editor: Effort free video editing!
|
5
5
|
Author-email: WyattBlue <wyattblue@auto-editor.com>
|
6
|
-
License: Unlicense
|
6
|
+
License-Expression: Unlicense
|
7
7
|
Project-URL: Bug Tracker, https://github.com/WyattBlue/auto-editor/issues
|
8
8
|
Project-URL: Source Code, https://github.com/WyattBlue/auto-editor
|
9
9
|
Project-URL: homepage, https://auto-editor.com
|
@@ -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,>=
|
15
|
-
Requires-Dist:
|
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
|
|
@@ -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://
|
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)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
auto_editor/__init__.py,sha256=GyXI0pHyyPF89C9QtYg4zFjEDtSMUJMNUdrc74jh27A,23
|
2
|
+
auto_editor/__main__.py,sha256=WfNtjKwx5fDMPpfSNLarigXD3Jp0My98FpzQqSAxQZ8,15807
|
3
|
+
auto_editor/analyze.py,sha256=CeJG0LI9wXZk1R-QPrNGPS4za-_Avd8y7H-D437DqLg,12300
|
4
|
+
auto_editor/edit.py,sha256=UbKG5jhl5K3kCx9wIB_-Con6PF-4H0-Du0yVln3B5Qc,18921
|
5
|
+
auto_editor/ffwrapper.py,sha256=Wet6B5nohgnjpBX7o20Zq0rYr-H9mUuOqHrbQAPPj38,5128
|
6
|
+
auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
|
7
|
+
auto_editor/json.py,sha256=8IVhZJSLx2IVqJsbR5YKDvbHOhgIOvdQmYNpMdMG_xA,9332
|
8
|
+
auto_editor/make_layers.py,sha256=nSEeCHysMot2eze23q05g2HFDuskN_4Jk108xlk2Rw8,10102
|
9
|
+
auto_editor/preview.py,sha256=cqQdozM2IB-5qXHNxeqiSrSdEIzlMfjD4SU-NX9sYZ0,3052
|
10
|
+
auto_editor/timeline.py,sha256=Ku6BdDDzdboO0DisO_KLDjAyxJLQVDybWVgFbGCcsmY,9416
|
11
|
+
auto_editor/vanparse.py,sha256=Ug5A2QaRqGiw4l55Z_h9T2QU1x0WqRibR7yY5rQ0WTk,10002
|
12
|
+
auto_editor/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
+
auto_editor/cmds/cache.py,sha256=bViYbtVXefTeEIUvSanDfA6cG35ep1N_Jvtz7ZjgIkY,1959
|
14
|
+
auto_editor/cmds/desc.py,sha256=DSAWPKt8PS9soBTgzpsFIEWoTe4gPTWwjXpNL-p3WsI,866
|
15
|
+
auto_editor/cmds/info.py,sha256=VA2WkTBbQPvrVHjDaJyqryoVMtiiN6cguiUMdWgBJfU,7002
|
16
|
+
auto_editor/cmds/levels.py,sha256=2Hbvoy6wMbRRoHdZf25PMqq1uvhRKyPcITNPMpZFo7s,5632
|
17
|
+
auto_editor/cmds/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
|
18
|
+
auto_editor/cmds/repl.py,sha256=HSUTDaVykPb5Bd-v_jz_8R7HvFmKOcT_ZVmP-d0AbUY,3247
|
19
|
+
auto_editor/cmds/subdump.py,sha256=kHg8nfUi6I6VeJjEgMxupPa666qsYUh7ZEUxint7Gqo,2443
|
20
|
+
auto_editor/cmds/test.py,sha256=UNN1r6J69-woqA6QU7TeY0WlPxukQzwCRl5DGBTLK8g,28837
|
21
|
+
auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
|
+
auto_editor/formats/fcp11.py,sha256=uaJbLiwiVxqoVy5JCA14wZj-m1wqZIzWh7rS-BsnSQM,5219
|
23
|
+
auto_editor/formats/fcp7.py,sha256=rHjXXjpJ5YQQvCxor7FpUaqaAreqjCas4uLpiTdFclc,20255
|
24
|
+
auto_editor/formats/json.py,sha256=UUBhFR_79vn4Lxu73B0cVBFgw4qytrmMP-TiCmDFMd0,7666
|
25
|
+
auto_editor/formats/shotcut.py,sha256=-ES854LLFCMCBe100JRJedDmuk8zPev17aQMTrzPv-g,4923
|
26
|
+
auto_editor/formats/utils.py,sha256=LYXDiqOk9WwUorLGw2D0M7In9BNDkoKikNawuks7hqE,1648
|
27
|
+
auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
|
+
auto_editor/lang/libintrospection.py,sha256=6H1rGp0wqaCud5IPaoEmzULGnYt6ec7_0h32ATcw2oY,261
|
29
|
+
auto_editor/lang/libmath.py,sha256=z33A161Oe6vYYK7R6pgYjdZZe63dQkN38Qf36TL3prg,847
|
30
|
+
auto_editor/lang/palet.py,sha256=RQjyIZMJSWnzDkHTu-5mt74o9_4zO4VrcH-wLojCF7A,24113
|
31
|
+
auto_editor/lang/stdenv.py,sha256=o7kFu7EbaH71XPFGxJUXYGxSeZ8O3i1_C5Hmi9uya4Q,44150
|
32
|
+
auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
|
+
auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,7586
|
34
|
+
auto_editor/lib/data_structs.py,sha256=Hnzl5gWvo-geTU0g-lGejj6HQW3VvPv0NBEj2XoGskY,7089
|
35
|
+
auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
|
36
|
+
auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
+
auto_editor/render/audio.py,sha256=ejV_NIvrmxMJt6mf7nPNIzlPHOumNhcIO2J-irHO-k8,17427
|
38
|
+
auto_editor/render/subtitle.py,sha256=F27T8OsAojUIGTGBWqTdH36h0BnHXSExxIqzOtqyZoE,6129
|
39
|
+
auto_editor/render/video.py,sha256=uIrYzF4bDZ3vwfX2F6TdR6F73GI4yruGssto9xEQ-AA,11999
|
40
|
+
auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
41
|
+
auto_editor/utils/bar.py,sha256=Ky9JRf37JTgLyvNuIXDfucaUE8H1vBbCqKLjttmsmmo,4156
|
42
|
+
auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
|
43
|
+
auto_editor/utils/cmdkw.py,sha256=aUGBvBel2Ko1o6Rwmr4rEL-BMc5hEnzYLbyZ1GeJdcY,5729
|
44
|
+
auto_editor/utils/container.py,sha256=CNHChHbhzIrjmDdWw6UzMqscrr9u7A-ZqKWejGjJwYE,2628
|
45
|
+
auto_editor/utils/func.py,sha256=ODyjXnzSDatEu08w398K8_xBKYdXMY3IPHiJpGRZDyQ,3250
|
46
|
+
auto_editor/utils/log.py,sha256=wPNf6AabV-0cnoS_bPLv1Lh7llQBtNqPKeh07einOuc,3701
|
47
|
+
auto_editor/utils/types.py,sha256=j2hd4zMQ9EftDy41Ji2_PFru_7HEZObd9yKA0BJxFaY,7616
|
48
|
+
auto_editor-27.1.0.dist-info/licenses/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
|
49
|
+
docs/build.py,sha256=g1uc1H9T_naGaermUiVMMwUpbT0IWElRhjgT0fvCh8w,1914
|
50
|
+
auto_editor-27.1.0.dist-info/METADATA,sha256=JU6SgcQLr3thOG27aQRkt2r3INzU0q306BeK79qkwGs,6176
|
51
|
+
auto_editor-27.1.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
|
52
|
+
auto_editor-27.1.0.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
|
53
|
+
auto_editor-27.1.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
|
54
|
+
auto_editor-27.1.0.dist-info/RECORD,,
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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")
|
auto_editor/output.py
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import os.path
|
4
|
-
from dataclasses import dataclass, field
|
5
|
-
|
6
|
-
import av
|
7
|
-
from av.audio.resampler import AudioResampler
|
8
|
-
|
9
|
-
from auto_editor.ffwrapper import FileInfo
|
10
|
-
from auto_editor.utils.bar import Bar
|
11
|
-
from auto_editor.utils.log import Log
|
12
|
-
from auto_editor.utils.types import split_num_str
|
13
|
-
|
14
|
-
|
15
|
-
def parse_bitrate(input_: str, log: Log) -> int:
|
16
|
-
try:
|
17
|
-
val, unit = split_num_str(input_)
|
18
|
-
except Exception as e:
|
19
|
-
log.error(e)
|
20
|
-
|
21
|
-
if unit.lower() == "k":
|
22
|
-
return int(val * 1000)
|
23
|
-
if unit == "M":
|
24
|
-
return int(val * 1_000_000)
|
25
|
-
if unit == "G":
|
26
|
-
return int(val * 1_000_000_000)
|
27
|
-
if unit == "":
|
28
|
-
return int(val)
|
29
|
-
|
30
|
-
log.error(f"Unknown bitrate: {input_}")
|
31
|
-
|
32
|
-
|
33
|
-
@dataclass(slots=True)
|
34
|
-
class Ensure:
|
35
|
-
_bar: Bar
|
36
|
-
_sr: int
|
37
|
-
log: Log
|
38
|
-
_audios: list[tuple[FileInfo, int]] = field(default_factory=list)
|
39
|
-
|
40
|
-
def audio(self, src: FileInfo, stream: int) -> str:
|
41
|
-
try:
|
42
|
-
label = self._audios.index((src, stream))
|
43
|
-
first_time = False
|
44
|
-
except ValueError:
|
45
|
-
self._audios.append((src, stream))
|
46
|
-
label = len(self._audios) - 1
|
47
|
-
first_time = True
|
48
|
-
|
49
|
-
out_path = os.path.join(self.log.temp, f"{label:x}.wav")
|
50
|
-
|
51
|
-
if first_time:
|
52
|
-
sample_rate = self._sr
|
53
|
-
bar = self._bar
|
54
|
-
self.log.debug(f"Making external audio: {out_path}")
|
55
|
-
|
56
|
-
in_container = av.open(src.path, "r")
|
57
|
-
out_container = av.open(
|
58
|
-
out_path, "w", format="wav", options={"rf64": "always"}
|
59
|
-
)
|
60
|
-
astream = in_container.streams.audio[stream]
|
61
|
-
|
62
|
-
if astream.duration is None or astream.time_base is None:
|
63
|
-
dur = 1.0
|
64
|
-
else:
|
65
|
-
dur = float(astream.duration * astream.time_base)
|
66
|
-
|
67
|
-
bar.start(dur, "Extracting audio")
|
68
|
-
|
69
|
-
output_astream = out_container.add_stream(
|
70
|
-
"pcm_s16le", layout="stereo", rate=sample_rate
|
71
|
-
)
|
72
|
-
resampler = AudioResampler(format="s16", layout="stereo", rate=sample_rate)
|
73
|
-
for i, frame in enumerate(in_container.decode(astream)):
|
74
|
-
if i % 1500 == 0 and frame.time is not None:
|
75
|
-
bar.tick(frame.time)
|
76
|
-
|
77
|
-
for new_frame in resampler.resample(frame):
|
78
|
-
out_container.mux(output_astream.encode(new_frame))
|
79
|
-
|
80
|
-
out_container.mux(output_astream.encode(None))
|
81
|
-
|
82
|
-
out_container.close()
|
83
|
-
in_container.close()
|
84
|
-
bar.end()
|
85
|
-
|
86
|
-
return out_path
|