auto-editor 24.3.1__tar.gz → 24.9.1__tar.gz
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-24.3.1/auto_editor.egg-info → auto-editor-24.9.1}/PKG-INFO +1 -1
- auto-editor-24.9.1/auto_editor/__init__.py +2 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/__main__.py +5 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/analyze.py +0 -82
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/edit.py +3 -3
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/ffwrapper.py +8 -5
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/fcp11.py +1 -4
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/fcp7.py +71 -132
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/help.py +5 -6
- auto-editor-24.9.1/auto_editor/lang/libmath.py +23 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lang/palet.py +165 -70
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/contracts.py +57 -10
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/data_structs.py +1 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/output.py +4 -2
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/video.py +30 -24
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/test.py +5 -7
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/cmdkw.py +41 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/types.py +1 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1/auto_editor.egg-info}/PKG-INFO +1 -1
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/SOURCES.txt +1 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/pyproject.toml +4 -4
- auto-editor-24.3.1/auto_editor/__init__.py +0 -2
- {auto-editor-24.3.1 → auto-editor-24.9.1}/LICENSE +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/README.md +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/ae-ffmpeg/ae_ffmpeg/__init__.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/ae-ffmpeg/ae_ffmpeg/py.typed +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/ae-ffmpeg/setup.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/__init__.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/json.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/shotcut.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/utils.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lang/__init__.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lang/json.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/__init__.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/err.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/make_layers.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/preview.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/__init__.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/audio.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/subtitle.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/__init__.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/desc.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/info.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/levels.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/palet.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/repl.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/subdump.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/timeline.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/__init__.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/bar.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/chunks.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/container.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/encoder.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/func.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/log.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/validate_input.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/vanparse.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/wavfile.py +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/dependency_links.txt +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/entry_points.txt +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/requires.txt +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/top_level.txt +0 -0
- {auto-editor-24.3.1 → auto-editor-24.9.1}/setup.cfg +0 -0
@@ -255,6 +255,11 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
|
|
255
255
|
help="Apply audio rendering to all audio tracks. Applied right before rendering the output file.",
|
256
256
|
)
|
257
257
|
parser.add_text("Miscellaneous:")
|
258
|
+
parser.add_argument(
|
259
|
+
"-sn",
|
260
|
+
flag=True,
|
261
|
+
help="Disable the inclusion of subtitle streams in the output file",
|
262
|
+
)
|
258
263
|
parser.add_argument(
|
259
264
|
"--extras",
|
260
265
|
metavar="CMD",
|
@@ -21,13 +21,10 @@ from auto_editor.lib.contracts import (
|
|
21
21
|
from auto_editor.lib.data_structs import Sym
|
22
22
|
from auto_editor.render.subtitle import SubtitleParser
|
23
23
|
from auto_editor.utils.cmdkw import (
|
24
|
-
ParserError,
|
25
24
|
Required,
|
26
|
-
parse_with_palet,
|
27
25
|
pAttr,
|
28
26
|
pAttrs,
|
29
27
|
)
|
30
|
-
from auto_editor.utils.func import boolop
|
31
28
|
from auto_editor.wavfile import read
|
32
29
|
|
33
30
|
if TYPE_CHECKING:
|
@@ -38,7 +35,6 @@ if TYPE_CHECKING:
|
|
38
35
|
from numpy.typing import NDArray
|
39
36
|
|
40
37
|
from auto_editor.ffwrapper import FileInfo
|
41
|
-
from auto_editor.lib.data_structs import Env
|
42
38
|
from auto_editor.output import Ensure
|
43
39
|
from auto_editor.utils.bar import Bar
|
44
40
|
from auto_editor.utils.log import Log
|
@@ -412,81 +408,3 @@ class Levels:
|
|
412
408
|
|
413
409
|
self.bar.end()
|
414
410
|
return self.cache("motion", mobj, threshold_list[:index])
|
415
|
-
|
416
|
-
|
417
|
-
def edit_method(val: str, filesetup: FileSetup, env: Env) -> NDArray[np.bool_]:
|
418
|
-
assert isinstance(filesetup, FileSetup)
|
419
|
-
src = filesetup.src
|
420
|
-
tb = filesetup.tb
|
421
|
-
ensure = filesetup.ensure
|
422
|
-
strict = filesetup.strict
|
423
|
-
bar = filesetup.bar
|
424
|
-
temp = filesetup.temp
|
425
|
-
log = filesetup.log
|
426
|
-
|
427
|
-
if ":" in val:
|
428
|
-
method, attrs = val.split(":", 1)
|
429
|
-
else:
|
430
|
-
method, attrs = val, ""
|
431
|
-
|
432
|
-
levels = Levels(ensure, src, tb, bar, temp, log)
|
433
|
-
|
434
|
-
if method == "none":
|
435
|
-
return levels.none()
|
436
|
-
if method == "all/e":
|
437
|
-
return levels.all()
|
438
|
-
|
439
|
-
try:
|
440
|
-
obj = parse_with_palet(attrs, builder_map[method], env)
|
441
|
-
except ParserError as e:
|
442
|
-
log.error(e)
|
443
|
-
|
444
|
-
try:
|
445
|
-
if method == "audio":
|
446
|
-
s = obj["stream"]
|
447
|
-
if s == "all" or s == Sym("all"):
|
448
|
-
total_list: NDArray[np.bool_] | None = None
|
449
|
-
for s in range(len(src.audios)):
|
450
|
-
audio_list = to_threshold(levels.audio(s), obj["threshold"])
|
451
|
-
if total_list is None:
|
452
|
-
total_list = audio_list
|
453
|
-
else:
|
454
|
-
total_list = boolop(total_list, audio_list, np.logical_or)
|
455
|
-
|
456
|
-
if total_list is None:
|
457
|
-
if strict:
|
458
|
-
log.error("Input has no audio streams.")
|
459
|
-
stream_data = levels.all()
|
460
|
-
else:
|
461
|
-
stream_data = total_list
|
462
|
-
else:
|
463
|
-
assert isinstance(s, int)
|
464
|
-
stream_data = to_threshold(levels.audio(s), obj["threshold"])
|
465
|
-
|
466
|
-
assert isinstance(obj["minclip"], int)
|
467
|
-
assert isinstance(obj["mincut"], int)
|
468
|
-
|
469
|
-
mut_remove_small(stream_data, obj["minclip"], replace=1, with_=0)
|
470
|
-
mut_remove_small(stream_data, obj["mincut"], replace=0, with_=1)
|
471
|
-
|
472
|
-
return stream_data
|
473
|
-
|
474
|
-
if method == "motion":
|
475
|
-
return to_threshold(
|
476
|
-
levels.motion(obj["stream"], obj["blur"], obj["width"]),
|
477
|
-
obj["threshold"],
|
478
|
-
)
|
479
|
-
|
480
|
-
if method == "subtitle":
|
481
|
-
return levels.subtitle(
|
482
|
-
obj["pattern"],
|
483
|
-
obj["stream"],
|
484
|
-
obj["ignore_case"],
|
485
|
-
obj["max_count"],
|
486
|
-
)
|
487
|
-
except LevelError as e:
|
488
|
-
if strict:
|
489
|
-
log.error(e)
|
490
|
-
|
491
|
-
return levels.all()
|
492
|
-
raise ValueError("Unreachable")
|
@@ -215,7 +215,7 @@ def edit_media(
|
|
215
215
|
|
216
216
|
if tl is None:
|
217
217
|
# Extract subtitles in their native format.
|
218
|
-
if src is not None and len(src.subtitles) > 0:
|
218
|
+
if src is not None and len(src.subtitles) > 0 and not args.sn:
|
219
219
|
cmd = ["-i", f"{src.path}", "-hide_banner"]
|
220
220
|
for s, sub in enumerate(src.subtitles):
|
221
221
|
cmd.extend(["-map", f"0:s:{s}"])
|
@@ -246,7 +246,7 @@ def edit_media(
|
|
246
246
|
if export["export"] == "premiere":
|
247
247
|
from auto_editor.formats.fcp7 import fcp7_write_xml
|
248
248
|
|
249
|
-
fcp7_write_xml(export["name"],
|
249
|
+
fcp7_write_xml(export["name"], output, tl, log)
|
250
250
|
return
|
251
251
|
|
252
252
|
if export["export"] in ("final-cut-pro", "resolve"):
|
@@ -283,7 +283,7 @@ def edit_media(
|
|
283
283
|
sub_output = []
|
284
284
|
apply_later = False
|
285
285
|
|
286
|
-
if ctr.allow_subtitle:
|
286
|
+
if ctr.allow_subtitle and not args.sn:
|
287
287
|
sub_output = make_new_subtitles(tl, ffmpeg, temp, log)
|
288
288
|
|
289
289
|
if ctr.allow_audio:
|
@@ -235,14 +235,17 @@ def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
|
|
235
235
|
]
|
236
236
|
)
|
237
237
|
_sar, c_range, c_space, c_primary, c_transfer = _raw.strip().split("\n")
|
238
|
-
except Exception
|
239
|
-
|
238
|
+
except Exception:
|
239
|
+
log.debug("Unexpected ffprobe shape")
|
240
240
|
|
241
241
|
if v.sample_aspect_ratio is None:
|
242
|
-
|
243
|
-
sar = Fraction(_sar.replace(":", "/"))
|
244
|
-
except Exception:
|
242
|
+
if _sar is None:
|
245
243
|
sar = Fraction(1)
|
244
|
+
else:
|
245
|
+
try:
|
246
|
+
sar = Fraction(_sar.replace(":", "/"))
|
247
|
+
except Exception:
|
248
|
+
sar = Fraction(1)
|
246
249
|
else:
|
247
250
|
sar = v.sample_aspect_ratio
|
248
251
|
|
@@ -143,9 +143,6 @@ def fcp11_write_xml(
|
|
143
143
|
"start": fraction(int(clip.offset // clip.speed)),
|
144
144
|
"tcFormat": "NDF",
|
145
145
|
}
|
146
|
-
if clip.start == 0:
|
147
|
-
del clip_properties["start"]
|
148
|
-
|
149
146
|
asset = SubElement(spine, "asset-clip", clip_properties)
|
150
147
|
if clip.speed != 1:
|
151
148
|
# See the "Time Maps" section.
|
@@ -171,7 +168,7 @@ def fcp11_write_xml(
|
|
171
168
|
if flavor == "resolve" and warn:
|
172
169
|
log.warning(
|
173
170
|
"DaVinci Resolve may take a very long time when importing timelines with "
|
174
|
-
"speed effects. Consider switching to
|
171
|
+
"speed effects. Consider switching to Premiere Pro, "
|
175
172
|
"Final Cut Pro, or ShotCut (free)"
|
176
173
|
)
|
177
174
|
tree = ElementTree(fcpxml)
|
@@ -10,7 +10,7 @@ from xml.etree.ElementTree import Element
|
|
10
10
|
from auto_editor.ffwrapper import FFmpeg, FileInfo, initFileInfo
|
11
11
|
from auto_editor.timeline import ASpace, TlAudio, TlVideo, VSpace, v3
|
12
12
|
|
13
|
-
from .utils import Validator,
|
13
|
+
from .utils import Validator, show
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
16
|
from auto_editor.utils.log import Log
|
@@ -25,9 +25,6 @@ Also, Premiere itself will happily output subtlety incorrect XML files that don'
|
|
25
25
|
come back the way they started.
|
26
26
|
"""
|
27
27
|
|
28
|
-
PIXEL_ASPECT_RATIO = "square"
|
29
|
-
COLORDEPTH = "24"
|
30
|
-
ANAMORPHIC = "FALSE"
|
31
28
|
DEPTH = "16"
|
32
29
|
|
33
30
|
|
@@ -121,21 +118,6 @@ def read_tb_ntsc(tb: int, ntsc: bool) -> Fraction:
|
|
121
118
|
return Fraction(tb)
|
122
119
|
|
123
120
|
|
124
|
-
"""
|
125
|
-
(define/c (read-tb-ntsc [tb int?] [ntsc bool?] -> frac?)
|
126
|
-
(if ntsc
|
127
|
-
(cond
|
128
|
-
((= tb 24) 24000/1001)
|
129
|
-
((= tb 30) 30000/1001)
|
130
|
-
((= tb 60) 60000/1001)
|
131
|
-
(else (* tb 999/1000))
|
132
|
-
)
|
133
|
-
(* tb 1/1)
|
134
|
-
)
|
135
|
-
)
|
136
|
-
"""
|
137
|
-
|
138
|
-
|
139
121
|
def speedup(speed: float) -> Element:
|
140
122
|
fil = Element("filter")
|
141
123
|
effect = ET.SubElement(fil, "effect")
|
@@ -164,28 +146,6 @@ def speedup(speed: float) -> Element:
|
|
164
146
|
return fil
|
165
147
|
|
166
148
|
|
167
|
-
"""
|
168
|
-
(define/c (speedup [speed float?] -> element?)
|
169
|
-
(xml (filter (effect
|
170
|
-
(name "Time Remap")
|
171
|
-
(effectid "timeremap")
|
172
|
-
(parameter #:authoringApp "PremierePro"
|
173
|
-
(parameterid "variablespeed") (name "variablespeed")
|
174
|
-
(valuemin "0") (valuemax "1") (value "0")
|
175
|
-
)
|
176
|
-
(parameter #:authoringApp "PremierePro"
|
177
|
-
(parameterid "speed") (name "speed")
|
178
|
-
(valuemin "-100000") (valuemax "100000") (value (number->string speed))
|
179
|
-
)
|
180
|
-
(parameter #:authoringApp "PremierePro"
|
181
|
-
(parameterid "frameblending") (name "frameblending")
|
182
|
-
(value "FALSE")
|
183
|
-
)
|
184
|
-
)))
|
185
|
-
)
|
186
|
-
"""
|
187
|
-
|
188
|
-
|
189
149
|
SUPPORTED_EFFECTS = ("timeremap",)
|
190
150
|
|
191
151
|
|
@@ -403,48 +363,32 @@ def media_def(
|
|
403
363
|
ET.SubElement(rate, "ntsc").text = ntsc
|
404
364
|
ET.SubElement(vschar, "width").text = f"{tl.res[0]}"
|
405
365
|
ET.SubElement(vschar, "height").text = f"{tl.res[1]}"
|
406
|
-
ET.SubElement(vschar, "
|
407
|
-
ET.SubElement(vschar, "pixelaspectratio").text = PIXEL_ASPECT_RATIO
|
366
|
+
ET.SubElement(vschar, "pixelaspectratio").text = "square"
|
408
367
|
|
409
|
-
|
368
|
+
for aud in src.audios:
|
410
369
|
audiodef = ET.SubElement(mediadef, "audio")
|
411
370
|
aschar = ET.SubElement(audiodef, "samplecharacteristics")
|
412
371
|
ET.SubElement(aschar, "depth").text = DEPTH
|
413
372
|
ET.SubElement(aschar, "samplerate").text = f"{tl.sr}"
|
414
|
-
ET.SubElement(audiodef, "channelcount").text = f"{
|
373
|
+
ET.SubElement(audiodef, "channelcount").text = f"{aud.channels}"
|
415
374
|
|
416
375
|
|
417
|
-
def fcp7_write_xml(name: str,
|
376
|
+
def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
418
377
|
width, height = tl.res
|
419
378
|
timebase, ntsc = set_tb_ntsc(tl.tb)
|
420
379
|
|
421
|
-
src_to_url: dict[
|
422
|
-
src_to_id: dict[
|
423
|
-
src_to_src: dict[tuple[FileInfo, int], FileInfo] = {}
|
380
|
+
src_to_url: dict[FileInfo, str] = {}
|
381
|
+
src_to_id: dict[FileInfo, str] = {}
|
424
382
|
|
425
383
|
file_defs: set[str] = set() # Contains urls
|
426
384
|
|
427
385
|
for src in set(tl.sources):
|
428
|
-
path_resolve = f"{src.path.resolve()}"
|
429
386
|
the_id = f"file-{len(src_to_id)+1}"
|
387
|
+
src_to_url[src] = f"{src.path.resolve()}"
|
388
|
+
src_to_id[src] = the_id
|
430
389
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
if len(src.audios) > 1:
|
435
|
-
fold = make_tracks_dir(src)
|
436
|
-
for i in range(1, len(src.audios)):
|
437
|
-
newtrack = fold / f"{i}.wav"
|
438
|
-
|
439
|
-
ffmpeg.run(["-i", path_resolve, "-map", f"0:a:{i}", f"{newtrack}"])
|
440
|
-
|
441
|
-
new_src = initFileInfo(f"{newtrack}", ffmpeg, log)
|
442
|
-
src_to_url[(src, i)] = f"{newtrack.resolve()}"
|
443
|
-
src_to_id[(src, i)] = f"file-{len(src_to_id)+1}"
|
444
|
-
src_to_src[(src, i)] = new_src
|
445
|
-
|
446
|
-
xmeml = ET.Element("xmeml", version="4")
|
447
|
-
sequence = ET.SubElement(xmeml, "sequence")
|
390
|
+
xmeml = ET.Element("xmeml", version="5")
|
391
|
+
sequence = ET.SubElement(xmeml, "sequence", explodedTracks="true")
|
448
392
|
ET.SubElement(sequence, "name").text = name
|
449
393
|
ET.SubElement(sequence, "duration").text = f"{int(tl.out_len())}"
|
450
394
|
rate = ET.SubElement(sequence, "rate")
|
@@ -455,14 +399,13 @@ def fcp7_write_xml(name: str, ffmpeg: FFmpeg, output: str, tl: v3, log: Log) ->
|
|
455
399
|
vformat = ET.SubElement(video, "format")
|
456
400
|
vschar = ET.SubElement(vformat, "samplecharacteristics")
|
457
401
|
|
402
|
+
ET.SubElement(vschar, "width").text = f"{width}"
|
403
|
+
ET.SubElement(vschar, "height").text = f"{height}"
|
404
|
+
ET.SubElement(vschar, "pixelaspectratio").text = "square"
|
405
|
+
|
458
406
|
rate = ET.SubElement(vschar, "rate")
|
459
407
|
ET.SubElement(rate, "timebase").text = f"{timebase}"
|
460
408
|
ET.SubElement(rate, "ntsc").text = ntsc
|
461
|
-
ET.SubElement(vschar, "fielddominance").text = "none"
|
462
|
-
ET.SubElement(vschar, "colordepth").text = COLORDEPTH
|
463
|
-
ET.SubElement(vschar, "width").text = f"{width}"
|
464
|
-
ET.SubElement(vschar, "height").text = f"{height}"
|
465
|
-
ET.SubElement(vschar, "pixelaspectratio").text = PIXEL_ASPECT_RATIO
|
466
409
|
|
467
410
|
if len(tl.v) > 0 and len(tl.v[0]) > 0:
|
468
411
|
track = ET.SubElement(video, "track")
|
@@ -476,25 +419,26 @@ def fcp7_write_xml(name: str, ffmpeg: FFmpeg, output: str, tl: v3, log: Log) ->
|
|
476
419
|
_out = f"{int(clip.offset / clip.speed) + clip.dur}"
|
477
420
|
|
478
421
|
clipitem = ET.SubElement(track, "clipitem", id=f"clipitem-{j+1}")
|
479
|
-
ET.SubElement(clipitem, "masterclipid").text = "masterclip-2"
|
480
422
|
ET.SubElement(clipitem, "name").text = src.path.stem
|
423
|
+
ET.SubElement(clipitem, "enabled").text = "TRUE"
|
481
424
|
ET.SubElement(clipitem, "start").text = _start
|
482
425
|
ET.SubElement(clipitem, "end").text = _end
|
483
426
|
ET.SubElement(clipitem, "in").text = _in
|
484
427
|
ET.SubElement(clipitem, "out").text = _out
|
485
428
|
|
486
|
-
_id = src_to_id[
|
429
|
+
_id = src_to_id[clip.src]
|
487
430
|
filedef = ET.SubElement(clipitem, "file", id=_id)
|
488
431
|
|
489
|
-
pathurl = src_to_url[
|
432
|
+
pathurl = src_to_url[clip.src]
|
490
433
|
if pathurl not in file_defs:
|
491
434
|
media_def(filedef, pathurl, clip.src, tl, timebase, ntsc)
|
492
435
|
file_defs.add(pathurl)
|
493
436
|
|
437
|
+
ET.SubElement(clipitem, "compositemode").text = "normal"
|
494
438
|
if clip.speed != 1:
|
495
439
|
clipitem.append(speedup(clip.speed * 100))
|
496
440
|
|
497
|
-
for i in range(len(src.audios) + 1):
|
441
|
+
for i in range(len(src.audios) * 2 + 1): # `2` because stereo.
|
498
442
|
link = ET.SubElement(clipitem, "link")
|
499
443
|
ET.SubElement(
|
500
444
|
link, "linkclipref"
|
@@ -502,8 +446,6 @@ def fcp7_write_xml(name: str, ffmpeg: FFmpeg, output: str, tl: v3, log: Log) ->
|
|
502
446
|
ET.SubElement(link, "mediatype").text = "video" if i == 0 else "audio"
|
503
447
|
ET.SubElement(link, "trackindex").text = str(max(i, 1))
|
504
448
|
ET.SubElement(link, "clipindex").text = str(j + 1)
|
505
|
-
if i > 0:
|
506
|
-
ET.SubElement(link, "groupindex").text = "1"
|
507
449
|
|
508
450
|
# Audio definitions and clips
|
509
451
|
audio = ET.SubElement(media, "audio")
|
@@ -513,66 +455,63 @@ def fcp7_write_xml(name: str, ffmpeg: FFmpeg, output: str, tl: v3, log: Log) ->
|
|
513
455
|
ET.SubElement(aschar, "depth").text = DEPTH
|
514
456
|
ET.SubElement(aschar, "samplerate").text = str(tl.sr)
|
515
457
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
_end = f"{aclip.start + aclip.dur}"
|
526
|
-
_in = f"{int(aclip.offset / aclip.speed)}"
|
527
|
-
_out = f"{int(aclip.offset / aclip.speed) + aclip.dur}"
|
528
|
-
|
529
|
-
if not src.videos:
|
530
|
-
clip_item_num = j + 1
|
531
|
-
master_id = "1"
|
532
|
-
else:
|
533
|
-
clip_item_num = len(aclips) + 1 + j + (t * len(aclips))
|
534
|
-
master_id = "2"
|
535
|
-
|
536
|
-
clipitem = ET.SubElement(
|
537
|
-
track,
|
538
|
-
"clipitem",
|
539
|
-
id=f"clipitem-{clip_item_num}",
|
540
|
-
premiereChannelType="stereo",
|
458
|
+
t = 0
|
459
|
+
for aclips in tl.a:
|
460
|
+
for channelcount in range(0, 2): # Because "stereo" is hardcoded.
|
461
|
+
t += 1
|
462
|
+
track = ET.Element(
|
463
|
+
"track",
|
464
|
+
currentExplodedTrackIndex=f"{channelcount}",
|
465
|
+
totalExplodedTrackCount="2", # Because "stereo" is hardcoded.
|
466
|
+
premiereTrackType="Stereo",
|
541
467
|
)
|
542
|
-
ET.SubElement(clipitem, "masterclipid").text = f"masterclip-{master_id}"
|
543
|
-
ET.SubElement(clipitem, "name").text = src.path.stem
|
544
|
-
ET.SubElement(clipitem, "start").text = _start
|
545
|
-
ET.SubElement(clipitem, "end").text = _end
|
546
|
-
ET.SubElement(clipitem, "in").text = _in
|
547
|
-
ET.SubElement(clipitem, "out").text = _out
|
548
|
-
|
549
|
-
_id = src_to_id[(aclip.src, aclip.stream)]
|
550
|
-
pathurl = src_to_url[(aclip.src, aclip.stream)]
|
551
468
|
|
552
|
-
if
|
553
|
-
|
554
|
-
else:
|
555
|
-
my_src = aclip.src
|
556
|
-
|
557
|
-
filedef = ET.SubElement(clipitem, "file", id=_id)
|
558
|
-
if pathurl not in file_defs:
|
559
|
-
media_def(filedef, pathurl, my_src, tl, timebase, ntsc)
|
560
|
-
file_defs.add(pathurl)
|
469
|
+
if src.videos:
|
470
|
+
ET.SubElement(track, "outputchannelindex").text = f"{channelcount + 1}"
|
561
471
|
|
562
|
-
|
563
|
-
|
564
|
-
ET.SubElement(sourcetrack, "trackindex").text = "1"
|
565
|
-
labels = ET.SubElement(clipitem, "labels")
|
566
|
-
ET.SubElement(labels, "label2").text = "Iris"
|
472
|
+
for j, aclip in enumerate(aclips):
|
473
|
+
src = aclip.src
|
567
474
|
|
568
|
-
|
569
|
-
|
475
|
+
_start = f"{aclip.start}"
|
476
|
+
_end = f"{aclip.start + aclip.dur}"
|
477
|
+
_in = f"{int(aclip.offset / aclip.speed)}"
|
478
|
+
_out = f"{int(aclip.offset / aclip.speed) + aclip.dur}"
|
570
479
|
|
571
|
-
|
572
|
-
|
480
|
+
if not src.videos:
|
481
|
+
clip_item_num = j + 1
|
482
|
+
else:
|
483
|
+
clip_item_num = len(aclips) + 1 + j + (t * len(aclips))
|
573
484
|
|
574
|
-
|
485
|
+
clipitem = ET.SubElement(
|
486
|
+
track,
|
487
|
+
"clipitem",
|
488
|
+
id=f"clipitem-{clip_item_num}",
|
489
|
+
premiereChannelType="stereo",
|
490
|
+
)
|
491
|
+
ET.SubElement(clipitem, "name").text = src.path.stem
|
492
|
+
ET.SubElement(clipitem, "enabled").text = "TRUE"
|
493
|
+
ET.SubElement(clipitem, "start").text = _start
|
494
|
+
ET.SubElement(clipitem, "end").text = _end
|
495
|
+
ET.SubElement(clipitem, "in").text = _in
|
496
|
+
ET.SubElement(clipitem, "out").text = _out
|
497
|
+
|
498
|
+
pathurl = src_to_url[aclip.src]
|
499
|
+
filedef = ET.SubElement(clipitem, "file", id=src_to_id[aclip.src])
|
500
|
+
if pathurl not in file_defs:
|
501
|
+
media_def(filedef, pathurl, aclip.src, tl, timebase, ntsc)
|
502
|
+
file_defs.add(pathurl)
|
503
|
+
|
504
|
+
sourcetrack = ET.SubElement(clipitem, "sourcetrack")
|
505
|
+
ET.SubElement(sourcetrack, "mediatype").text = "audio"
|
506
|
+
ET.SubElement(sourcetrack, "trackindex").text = f"{t}"
|
507
|
+
labels = ET.SubElement(clipitem, "labels")
|
508
|
+
ET.SubElement(labels, "label2").text = "Iris"
|
509
|
+
|
510
|
+
if aclip.speed != 1:
|
511
|
+
clipitem.append(speedup(aclip.speed * 100))
|
512
|
+
|
513
|
+
audio.append(track)
|
575
514
|
|
576
515
|
tree = ET.ElementTree(xmeml)
|
577
|
-
ET.indent(tree, space="
|
516
|
+
ET.indent(tree, space=" ", level=0)
|
578
517
|
tree.write(output, xml_declaration=True, encoding="utf-8")
|
@@ -25,16 +25,19 @@ will set the speed from 400 ticks to 800 ticks to 2.5x
|
|
25
25
|
If timebase is 30, 400 ticks to 800 means 13.33 to 26.66 seconds
|
26
26
|
""".strip(),
|
27
27
|
"--edit-based-on": """
|
28
|
-
|
28
|
+
Evaluates a palet expression that returns a bool-array?. The array is then used for
|
29
29
|
editing.
|
30
30
|
|
31
31
|
Editing Methods:
|
32
32
|
- audio ; Audio silence/loudness detection
|
33
33
|
- threshold threshold? : 4%
|
34
|
-
- stream (or/c nat? 'all
|
34
|
+
- stream (or/c nat? 'all) : 'all
|
35
35
|
- mincut nat? : 6
|
36
36
|
- minclip nat? : 3
|
37
37
|
|
38
|
+
; mincut is more significant, there it has a larger default value.
|
39
|
+
; minclip gets applied first, then mincut
|
40
|
+
|
38
41
|
- motion ; Motion detection specialized for noisy real-life videos
|
39
42
|
- threshold threshold? : 2%
|
40
43
|
- stream nat? : 0
|
@@ -150,10 +153,6 @@ The special value `unset` may also be used, and means: Don't pass any value to f
|
|
150
153
|
""".strip(),
|
151
154
|
"--video-bitrate": """
|
152
155
|
`--video-bitrate` sets the target bitrate for the video encoder. It accepts the same format as `--audio-bitrate` and the special `unset` value is allowed.
|
153
|
-
""".strip(),
|
154
|
-
"--silent-threshold": """
|
155
|
-
Silent threshold is a percentage where 0% represents absolute silence and 100% represents the highest volume in the media file.
|
156
|
-
Setting the threshold to `0%` will cut only out areas where area is absolutely silence.
|
157
156
|
""".strip(),
|
158
157
|
"--margin": """
|
159
158
|
Default value: 0.2s,0.2s
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import math
|
4
|
+
|
5
|
+
from auto_editor.lib.contracts import Proc, andc, between_c, gt_c, is_real
|
6
|
+
|
7
|
+
|
8
|
+
def all() -> dict[str, object]:
|
9
|
+
return {
|
10
|
+
"exp": Proc("exp", math.exp, (1, 1), is_real),
|
11
|
+
"ceil": Proc("ceil", math.ceil, (1, 1), is_real),
|
12
|
+
"floor": Proc("floor", math.floor, (1, 1), is_real),
|
13
|
+
"sin": Proc("sin", math.sin, (1, 1), is_real),
|
14
|
+
"cos": Proc("cos", math.cos, (1, 1), is_real),
|
15
|
+
"tan": Proc("tan", math.tan, (1, 1), is_real),
|
16
|
+
"asin": Proc("asin", math.asin, (1, 1), between_c(-1, 1)),
|
17
|
+
"acos": Proc("acos", math.acos, (1, 1), between_c(-1, 1)),
|
18
|
+
"atan": Proc("atan", math.atan, (1, 1), is_real),
|
19
|
+
"log": Proc("log", math.log, (1, 2), andc(is_real, gt_c(0))),
|
20
|
+
"pi": math.pi,
|
21
|
+
"e": math.e,
|
22
|
+
"tau": math.tau,
|
23
|
+
}
|