auto-editor 27.0.0__py3-none-any.whl → 27.1.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.
- auto_editor/__init__.py +1 -1
- auto_editor/__main__.py +8 -0
- auto_editor/cmds/desc.py +2 -2
- auto_editor/cmds/info.py +2 -2
- auto_editor/cmds/levels.py +2 -2
- auto_editor/cmds/repl.py +3 -8
- auto_editor/cmds/test.py +36 -2
- auto_editor/edit.py +51 -88
- auto_editor/ffwrapper.py +88 -84
- auto_editor/formats/fcp11.py +10 -8
- auto_editor/formats/fcp7.py +23 -64
- auto_editor/formats/json.py +8 -9
- auto_editor/lang/stdenv.py +1 -0
- auto_editor/make_layers.py +18 -8
- auto_editor/render/audio.py +219 -84
- auto_editor/render/video.py +1 -2
- auto_editor/timeline.py +69 -16
- auto_editor/utils/container.py +19 -12
- auto_editor/utils/func.py +21 -0
- {auto_editor-27.0.0.dist-info → auto_editor-27.1.1.dist-info}/METADATA +2 -2
- {auto_editor-27.0.0.dist-info → auto_editor-27.1.1.dist-info}/RECORD +25 -27
- {auto_editor-27.0.0.dist-info → auto_editor-27.1.1.dist-info}/WHEEL +1 -1
- auto_editor/output.py +0 -86
- auto_editor/wavfile.py +0 -310
- {auto_editor-27.0.0.dist-info → auto_editor-27.1.1.dist-info}/entry_points.txt +0 -0
- {auto_editor-27.0.0.dist-info → auto_editor-27.1.1.dist-info}/licenses/LICENSE +0 -0
- {auto_editor-27.0.0.dist-info → auto_editor-27.1.1.dist-info}/top_level.txt +0 -0
auto_editor/formats/fcp7.py
CHANGED
@@ -2,13 +2,12 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import xml.etree.ElementTree as ET
|
4
4
|
from fractions import Fraction
|
5
|
-
from io import StringIO
|
6
5
|
from math import ceil
|
7
6
|
from typing import TYPE_CHECKING
|
8
7
|
from xml.etree.ElementTree import Element
|
9
8
|
|
10
|
-
from auto_editor.ffwrapper import FileInfo
|
11
|
-
from auto_editor.timeline import ASpace, TlAudio, TlVideo, VSpace, v3
|
9
|
+
from auto_editor.ffwrapper import FileInfo
|
10
|
+
from auto_editor.timeline import ASpace, Template, TlAudio, TlVideo, VSpace, v3
|
12
11
|
|
13
12
|
from .utils import Validator, show
|
14
13
|
|
@@ -29,59 +28,20 @@ DEPTH = "16"
|
|
29
28
|
|
30
29
|
|
31
30
|
def uri_to_path(uri: str) -> str:
|
32
|
-
|
33
|
-
uri_escape = {
|
34
|
-
"3C": "<",
|
35
|
-
"3E": ">",
|
36
|
-
"23": "#",
|
37
|
-
"25": "%",
|
38
|
-
"2B": "+",
|
39
|
-
"7B": "{",
|
40
|
-
"7D": "}",
|
41
|
-
"7C": "|",
|
42
|
-
"5C": "\\",
|
43
|
-
"5E": "^",
|
44
|
-
"7E": "~",
|
45
|
-
"5B": "[",
|
46
|
-
"5D": "]",
|
47
|
-
"60": "`",
|
48
|
-
"3F": "?",
|
49
|
-
"3A": ":",
|
50
|
-
"40": "@",
|
51
|
-
"3D": "=",
|
52
|
-
"2A": "*",
|
53
|
-
"29": ")",
|
54
|
-
"28": "(",
|
55
|
-
"27": "'",
|
56
|
-
"26": "&",
|
57
|
-
"24": "$",
|
58
|
-
"22": '"',
|
59
|
-
"21": "!",
|
60
|
-
"20": " ",
|
61
|
-
}
|
62
|
-
buf = StringIO()
|
63
|
-
i = 0
|
64
|
-
while i < len(s):
|
65
|
-
if s[i] == "%" and len(s) > i + 3:
|
66
|
-
tag = s[i + 1 : i + 3]
|
67
|
-
if tag in uri_escape:
|
68
|
-
buf.write(uri_escape[tag])
|
69
|
-
i += 3
|
70
|
-
else:
|
71
|
-
buf.write(s[i])
|
72
|
-
i += 1
|
73
|
-
else:
|
74
|
-
buf.write(s[i])
|
75
|
-
i += 1
|
76
|
-
return buf.getvalue()
|
31
|
+
urllib = __import__("urllib.parse", fromlist=["parse"])
|
77
32
|
|
78
33
|
if uri.startswith("file://localhost/"):
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
34
|
+
uri = uri[16:]
|
35
|
+
elif uri.startswith("file://"):
|
36
|
+
# Windows-style paths
|
37
|
+
if len(uri) > 8 and uri[9] == ":":
|
38
|
+
uri = uri[8:]
|
39
|
+
else:
|
40
|
+
uri = uri[7:]
|
41
|
+
else:
|
42
|
+
return uri
|
43
|
+
|
44
|
+
return urllib.parse.unquote(uri)
|
85
45
|
|
86
46
|
# /Users/wyattblue/projects/auto-editor/example.mp4
|
87
47
|
# file:///Users/wyattblue/projects/auto-editor/example.mp4
|
@@ -282,7 +242,7 @@ def fcp7_read_xml(path: str, log: Log) -> v3:
|
|
282
242
|
fileobj = valid.parse(clipitem["file"], {"pathurl": str})
|
283
243
|
|
284
244
|
if "pathurl" in fileobj:
|
285
|
-
sources[file_id] =
|
245
|
+
sources[file_id] = FileInfo.init(
|
286
246
|
uri_to_path(fileobj["pathurl"]),
|
287
247
|
log,
|
288
248
|
)
|
@@ -317,7 +277,7 @@ def fcp7_read_xml(path: str, log: Log) -> v3:
|
|
317
277
|
file_id = clipitem["file"].attrib["id"]
|
318
278
|
if file_id not in sources:
|
319
279
|
fileobj = valid.parse(clipitem["file"], {"pathurl": str})
|
320
|
-
sources[file_id] =
|
280
|
+
sources[file_id] = FileInfo.init(
|
321
281
|
uri_to_path(fileobj["pathurl"]), log
|
322
282
|
)
|
323
283
|
|
@@ -336,10 +296,8 @@ def fcp7_read_xml(path: str, log: Log) -> v3:
|
|
336
296
|
)
|
337
297
|
)
|
338
298
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
return v3(primary_src, tb, sr, res, "#000", vobjs, aobjs, v1=None)
|
299
|
+
T = Template.init(sources[next(iter(sources))], sr, res=res)
|
300
|
+
return v3(tb, "#000", T, vobjs, aobjs, v1=None)
|
343
301
|
|
344
302
|
|
345
303
|
def media_def(
|
@@ -424,13 +382,14 @@ def resolve_write_audio(audio: Element, make_filedef, tl: v3) -> None:
|
|
424
382
|
clipitem.append(speedup(aclip.speed * 100))
|
425
383
|
|
426
384
|
|
427
|
-
def premiere_write_audio(audio: Element, make_filedef,
|
385
|
+
def premiere_write_audio(audio: Element, make_filedef, tl: v3) -> None:
|
428
386
|
ET.SubElement(audio, "numOutputChannels").text = "2"
|
429
387
|
aformat = ET.SubElement(audio, "format")
|
430
388
|
aschar = ET.SubElement(aformat, "samplecharacteristics")
|
431
389
|
ET.SubElement(aschar, "depth").text = DEPTH
|
432
390
|
ET.SubElement(aschar, "samplerate").text = f"{tl.sr}"
|
433
391
|
|
392
|
+
has_video = tl.v and tl.v[0]
|
434
393
|
t = 0
|
435
394
|
for aclips in tl.a:
|
436
395
|
for channelcount in range(0, 2): # Because "stereo" is hardcoded.
|
@@ -442,7 +401,7 @@ def premiere_write_audio(audio: Element, make_filedef, src: FileInfo, tl: v3) ->
|
|
442
401
|
premiereTrackType="Stereo",
|
443
402
|
)
|
444
403
|
|
445
|
-
if
|
404
|
+
if has_video:
|
446
405
|
ET.SubElement(track, "outputchannelindex").text = f"{channelcount + 1}"
|
447
406
|
|
448
407
|
for j, aclip in enumerate(aclips):
|
@@ -453,7 +412,7 @@ def premiere_write_audio(audio: Element, make_filedef, src: FileInfo, tl: v3) ->
|
|
453
412
|
_in = f"{aclip.offset}"
|
454
413
|
_out = f"{aclip.offset + aclip.dur}"
|
455
414
|
|
456
|
-
if not
|
415
|
+
if not has_video:
|
457
416
|
clip_item_num = j + 1
|
458
417
|
else:
|
459
418
|
clip_item_num = len(aclips) + 1 + j + (t * len(aclips))
|
@@ -579,7 +538,7 @@ def fcp7_write_xml(name: str, output: str, resolve: bool, tl: v3) -> None:
|
|
579
538
|
if resolve:
|
580
539
|
resolve_write_audio(audio, make_filedef, tl)
|
581
540
|
else:
|
582
|
-
premiere_write_audio(audio, make_filedef,
|
541
|
+
premiere_write_audio(audio, make_filedef, tl)
|
583
542
|
|
584
543
|
tree = ET.ElementTree(xmeml)
|
585
544
|
ET.indent(tree, space=" ", level=0)
|
auto_editor/formats/json.py
CHANGED
@@ -6,11 +6,12 @@ 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 FileInfo
|
9
|
+
from auto_editor.ffwrapper import FileInfo
|
10
10
|
from auto_editor.json import dump, load
|
11
11
|
from auto_editor.lib.err import MyError
|
12
12
|
from auto_editor.timeline import (
|
13
13
|
ASpace,
|
14
|
+
Template,
|
14
15
|
TlAudio,
|
15
16
|
TlVideo,
|
16
17
|
VSpace,
|
@@ -59,7 +60,7 @@ def read_v3(tl: Any, log: Log) -> v3:
|
|
59
60
|
def make_src(v: str) -> FileInfo:
|
60
61
|
if v in srcs:
|
61
62
|
return srcs[v]
|
62
|
-
temp =
|
63
|
+
temp = FileInfo.init(v, log)
|
63
64
|
srcs[v] = temp
|
64
65
|
return temp
|
65
66
|
|
@@ -151,11 +152,11 @@ def read_v3(tl: Any, log: Log) -> v3:
|
|
151
152
|
a.append(a_out)
|
152
153
|
|
153
154
|
try:
|
154
|
-
|
155
|
+
T = Template.init(srcs[next(iter(srcs))])
|
155
156
|
except StopIteration:
|
156
|
-
|
157
|
+
T = Template(sr, "stereo", res, [], [])
|
157
158
|
|
158
|
-
return v3(
|
159
|
+
return v3(tb, bg, T, v, a, v1=None)
|
159
160
|
|
160
161
|
|
161
162
|
def read_v1(tl: Any, log: Log) -> v3:
|
@@ -168,7 +169,7 @@ def read_v1(tl: Any, log: Log) -> v3:
|
|
168
169
|
|
169
170
|
check_file(path, log)
|
170
171
|
|
171
|
-
src =
|
172
|
+
src = FileInfo.init(path, log)
|
172
173
|
|
173
174
|
vtl: VSpace = []
|
174
175
|
atl: ASpace = [[] for _ in range(len(src.audios))]
|
@@ -207,11 +208,9 @@ def read_v1(tl: Any, log: Log) -> v3:
|
|
207
208
|
atl[a].append(TlAudio(c.start, c.dur, c.src, c.offset, c.speed, 1, a))
|
208
209
|
|
209
210
|
return v3(
|
210
|
-
src,
|
211
211
|
src.get_fps(),
|
212
|
-
src.get_sr(),
|
213
|
-
src.get_res(),
|
214
212
|
"#000",
|
213
|
+
Template.init(src),
|
215
214
|
vtl,
|
216
215
|
atl,
|
217
216
|
v1(src, chunks),
|
auto_editor/lang/stdenv.py
CHANGED
auto_editor/make_layers.py
CHANGED
@@ -11,7 +11,7 @@ from auto_editor.ffwrapper import FileInfo
|
|
11
11
|
from auto_editor.lang.palet import Lexer, Parser, env, interpret, is_boolean_array
|
12
12
|
from auto_editor.lib.data_structs import print_str
|
13
13
|
from auto_editor.lib.err import MyError
|
14
|
-
from auto_editor.timeline import ASpace, TlAudio, TlVideo, VSpace, v1, v3
|
14
|
+
from auto_editor.timeline import ASpace, Template, TlAudio, TlVideo, VSpace, v1, v3
|
15
15
|
from auto_editor.utils.func import mut_margin
|
16
16
|
from auto_editor.utils.types import CoerceError, time
|
17
17
|
|
@@ -99,16 +99,17 @@ def parse_time(val: str, arr: NDArray, tb: Fraction) -> int: # raises: `CoerceE
|
|
99
99
|
|
100
100
|
|
101
101
|
def make_timeline(
|
102
|
-
sources: list[FileInfo],
|
103
|
-
args: Args,
|
104
|
-
sr: int,
|
105
|
-
bar: Bar,
|
106
|
-
log: Log,
|
102
|
+
sources: list[FileInfo], args: Args, sr: int, bar: Bar, log: Log
|
107
103
|
) -> v3:
|
108
104
|
inp = None if not sources else sources[0]
|
109
105
|
|
110
106
|
if inp is None:
|
111
|
-
tb
|
107
|
+
tb = (
|
108
|
+
Fraction(30)
|
109
|
+
if args.frame_rate is None
|
110
|
+
else make_sane_timebase(args.frame_rate)
|
111
|
+
)
|
112
|
+
res = (1920, 1080) if args.resolution is None else args.resolution
|
112
113
|
else:
|
113
114
|
tb = make_sane_timebase(
|
114
115
|
inp.get_fps() if args.frame_rate is None else args.frame_rate
|
@@ -302,4 +303,13 @@ def make_timeline(
|
|
302
303
|
else:
|
303
304
|
v1_compatiable = None
|
304
305
|
|
305
|
-
|
306
|
+
if len(vtl) == 0 and len(atl) == 0:
|
307
|
+
log.error("Timeline is empty, nothing to do.")
|
308
|
+
|
309
|
+
if inp is None:
|
310
|
+
layout = "stereo" if args.audio_layout is None else args.audio_layout
|
311
|
+
template = Template(sr, layout, res, [], [])
|
312
|
+
else:
|
313
|
+
template = Template.init(inp, sr, args.audio_layout, res)
|
314
|
+
|
315
|
+
return v3(tb, args.background, template, vtl, atl, v1_compatiable)
|