auto-editor 24.7.1__tar.gz → 24.13.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.7.1/auto_editor.egg-info → auto-editor-24.13.1}/PKG-INFO +3 -3
- {auto-editor-24.7.1 → auto-editor-24.13.1}/ae-ffmpeg/ae_ffmpeg/__init__.py +1 -1
- auto-editor-24.13.1/auto_editor/__init__.py +2 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/analyze.py +0 -82
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/edit.py +2 -2
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/ffwrapper.py +23 -47
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/formats/fcp11.py +6 -6
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/formats/fcp7.py +11 -11
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/formats/json.py +8 -8
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/help.py +5 -6
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lang/palet.py +143 -60
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lib/contracts.py +57 -10
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lib/data_structs.py +1 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/output.py +14 -7
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/render/video.py +24 -23
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/subcommands/desc.py +2 -4
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/subcommands/info.py +24 -19
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/subcommands/levels.py +1 -1
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/subcommands/repl.py +1 -1
- auto-editor-24.13.1/auto_editor/subcommands/subdump.py +23 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/subcommands/test.py +11 -7
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/cmdkw.py +41 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/wavfile.py +3 -3
- {auto-editor-24.7.1 → auto-editor-24.13.1/auto_editor.egg-info}/PKG-INFO +3 -3
- auto-editor-24.13.1/auto_editor.egg-info/requires.txt +3 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/pyproject.toml +2 -2
- auto-editor-24.7.1/auto_editor/__init__.py +0 -2
- auto-editor-24.7.1/auto_editor/subcommands/subdump.py +0 -58
- auto-editor-24.7.1/auto_editor.egg-info/requires.txt +0 -3
- {auto-editor-24.7.1 → auto-editor-24.13.1}/LICENSE +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/README.md +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/ae-ffmpeg/ae_ffmpeg/py.typed +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/ae-ffmpeg/setup.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/__main__.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/formats/__init__.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/formats/shotcut.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/formats/utils.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lang/__init__.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lang/json.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lang/libmath.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lib/__init__.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/lib/err.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/make_layers.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/preview.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/render/__init__.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/render/audio.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/render/subtitle.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/subcommands/__init__.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/subcommands/palet.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/timeline.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/__init__.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/bar.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/chunks.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/container.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/encoder.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/func.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/log.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/utils/types.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/validate_input.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor/vanparse.py +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor.egg-info/SOURCES.txt +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor.egg-info/dependency_links.txt +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor.egg-info/entry_points.txt +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/auto_editor.egg-info/top_level.txt +0 -0
- {auto-editor-24.7.1 → auto-editor-24.13.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: auto-editor
|
3
|
-
Version: 24.
|
3
|
+
Version: 24.13.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.
|
16
|
-
Requires-Dist: ae-ffmpeg==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
|
|
@@ -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")
|
@@ -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],
|
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,
|
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
|
@@ -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:
|
139
|
-
color_space:
|
140
|
-
color_primaries:
|
141
|
-
color_transfer:
|
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,
|
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
|
-
|
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,33 +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
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
try:
|
243
|
-
sar = Fraction(_sar.replace(":", "/"))
|
244
|
-
except Exception:
|
245
|
-
sar = Fraction(1)
|
246
|
-
else:
|
247
|
-
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}")
|
248
223
|
|
249
224
|
videos += (
|
250
225
|
VideoStream(
|
@@ -255,11 +230,11 @@ def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
|
|
255
230
|
vdur,
|
256
231
|
sar,
|
257
232
|
v.time_base,
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
233
|
+
cc.pix_fmt,
|
234
|
+
cc.color_range,
|
235
|
+
cc.colorspace,
|
236
|
+
cc.color_primaries,
|
237
|
+
cc.color_trc,
|
263
238
|
0 if v.bit_rate is None else v.bit_rate,
|
264
239
|
v.language,
|
265
240
|
),
|
@@ -270,13 +245,14 @@ def initFileInfo(path: str, ffmpeg: FFmpeg, log: Log) -> FileInfo:
|
|
270
245
|
if a.duration is not None and a.time_base is not None:
|
271
246
|
adur = float(a.duration * a.time_base)
|
272
247
|
|
248
|
+
a_cc = a.codec_context
|
273
249
|
audios += (
|
274
250
|
AudioStream(
|
275
|
-
|
276
|
-
0 if
|
277
|
-
|
251
|
+
a_cc.name,
|
252
|
+
0 if a_cc.sample_rate is None else a_cc.sample_rate,
|
253
|
+
a_cc.channels,
|
278
254
|
adur,
|
279
|
-
0 if
|
255
|
+
0 if a_cc.bit_rate is None else a_cc.bit_rate,
|
280
256
|
a.language,
|
281
257
|
),
|
282
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 == "
|
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.
|
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"
|
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}",
|
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")
|
@@ -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:
|
@@ -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"]),
|
317
|
+
uri_to_path(fileobj["pathurl"]), log
|
319
318
|
)
|
320
319
|
|
321
320
|
if "filter" in clipitem:
|
@@ -363,7 +362,6 @@ def media_def(
|
|
363
362
|
ET.SubElement(rate, "ntsc").text = ntsc
|
364
363
|
ET.SubElement(vschar, "width").text = f"{tl.res[0]}"
|
365
364
|
ET.SubElement(vschar, "height").text = f"{tl.res[1]}"
|
366
|
-
ET.SubElement(vschar, "anamorphic").text = "FALSE"
|
367
365
|
ET.SubElement(vschar, "pixelaspectratio").text = "square"
|
368
366
|
|
369
367
|
for aud in src.audios:
|
@@ -389,7 +387,7 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
389
387
|
src_to_id[src] = the_id
|
390
388
|
|
391
389
|
xmeml = ET.Element("xmeml", version="5")
|
392
|
-
sequence = ET.SubElement(xmeml, "sequence")
|
390
|
+
sequence = ET.SubElement(xmeml, "sequence", explodedTracks="true")
|
393
391
|
ET.SubElement(sequence, "name").text = name
|
394
392
|
ET.SubElement(sequence, "duration").text = f"{int(tl.out_len())}"
|
395
393
|
rate = ET.SubElement(sequence, "rate")
|
@@ -400,13 +398,14 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
400
398
|
vformat = ET.SubElement(video, "format")
|
401
399
|
vschar = ET.SubElement(vformat, "samplecharacteristics")
|
402
400
|
|
403
|
-
rate = ET.SubElement(vschar, "rate")
|
404
|
-
ET.SubElement(rate, "timebase").text = f"{timebase}"
|
405
|
-
ET.SubElement(rate, "ntsc").text = ntsc
|
406
401
|
ET.SubElement(vschar, "width").text = f"{width}"
|
407
402
|
ET.SubElement(vschar, "height").text = f"{height}"
|
408
403
|
ET.SubElement(vschar, "pixelaspectratio").text = "square"
|
409
404
|
|
405
|
+
rate = ET.SubElement(vschar, "rate")
|
406
|
+
ET.SubElement(rate, "timebase").text = f"{timebase}"
|
407
|
+
ET.SubElement(rate, "ntsc").text = ntsc
|
408
|
+
|
410
409
|
if len(tl.v) > 0 and len(tl.v[0]) > 0:
|
411
410
|
track = ET.SubElement(video, "track")
|
412
411
|
|
@@ -420,6 +419,7 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
420
419
|
|
421
420
|
clipitem = ET.SubElement(track, "clipitem", id=f"clipitem-{j+1}")
|
422
421
|
ET.SubElement(clipitem, "name").text = src.path.stem
|
422
|
+
ET.SubElement(clipitem, "enabled").text = "TRUE"
|
423
423
|
ET.SubElement(clipitem, "start").text = _start
|
424
424
|
ET.SubElement(clipitem, "end").text = _end
|
425
425
|
ET.SubElement(clipitem, "in").text = _in
|
@@ -433,6 +433,7 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
433
433
|
media_def(filedef, pathurl, clip.src, tl, timebase, ntsc)
|
434
434
|
file_defs.add(pathurl)
|
435
435
|
|
436
|
+
ET.SubElement(clipitem, "compositemode").text = "normal"
|
436
437
|
if clip.speed != 1:
|
437
438
|
clipitem.append(speedup(clip.speed * 100))
|
438
439
|
|
@@ -444,8 +445,6 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
444
445
|
ET.SubElement(link, "mediatype").text = "video" if i == 0 else "audio"
|
445
446
|
ET.SubElement(link, "trackindex").text = str(max(i, 1))
|
446
447
|
ET.SubElement(link, "clipindex").text = str(j + 1)
|
447
|
-
if i > 0:
|
448
|
-
ET.SubElement(link, "groupindex").text = "1"
|
449
448
|
|
450
449
|
# Audio definitions and clips
|
451
450
|
audio = ET.SubElement(media, "audio")
|
@@ -489,6 +488,7 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
489
488
|
premiereChannelType="stereo",
|
490
489
|
)
|
491
490
|
ET.SubElement(clipitem, "name").text = src.path.stem
|
491
|
+
ET.SubElement(clipitem, "enabled").text = "TRUE"
|
492
492
|
ET.SubElement(clipitem, "start").text = _start
|
493
493
|
ET.SubElement(clipitem, "end").text = _end
|
494
494
|
ET.SubElement(clipitem, "in").text = _in
|
@@ -502,7 +502,7 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
502
502
|
|
503
503
|
sourcetrack = ET.SubElement(clipitem, "sourcetrack")
|
504
504
|
ET.SubElement(sourcetrack, "mediatype").text = "audio"
|
505
|
-
ET.SubElement(sourcetrack, "trackindex").text = f"{t
|
505
|
+
ET.SubElement(sourcetrack, "trackindex").text = f"{t}"
|
506
506
|
labels = ET.SubElement(clipitem, "labels")
|
507
507
|
ET.SubElement(labels, "label2").text = "Iris"
|
508
508
|
|
@@ -512,5 +512,5 @@ def fcp7_write_xml(name: str, output: str, tl: v3, log: Log) -> None:
|
|
512
512
|
audio.append(track)
|
513
513
|
|
514
514
|
tree = ET.ElementTree(xmeml)
|
515
|
-
ET.indent(tree, space="
|
515
|
+
ET.indent(tree, space=" ", level=0)
|
516
516
|
tree.write(output, xml_declaration=True, encoding="utf-8")
|
@@ -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
|
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,
|
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,
|
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,
|
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,7 +168,7 @@ def read_v1(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
|
|
168
168
|
|
169
169
|
check_file(path, log)
|
170
170
|
|
171
|
-
src = initFileInfo(path,
|
171
|
+
src = initFileInfo(path, log)
|
172
172
|
|
173
173
|
vtl: VSpace = []
|
174
174
|
atl: ASpace = [[] for _ in range(len(src.audios))]
|
@@ -194,7 +194,7 @@ def read_v1(tl: Any, ffmpeg: FFmpeg, log: Log) -> v3:
|
|
194
194
|
)
|
195
195
|
|
196
196
|
|
197
|
-
def read_json(path: str,
|
197
|
+
def read_json(path: str, log: Log) -> v3:
|
198
198
|
with open(path, encoding="utf-8", errors="ignore") as f:
|
199
199
|
try:
|
200
200
|
tl = Parser(Lexer(path, f)).expr()
|
@@ -206,9 +206,9 @@ def read_json(path: str, ffmpeg: FFmpeg, log: Log) -> v3:
|
|
206
206
|
ver = tl["version"]
|
207
207
|
|
208
208
|
if ver == "3":
|
209
|
-
return read_v3(tl,
|
209
|
+
return read_v3(tl, log)
|
210
210
|
if ver == "1":
|
211
|
-
return read_v1(tl,
|
211
|
+
return read_v1(tl, log)
|
212
212
|
if type(ver) is not str:
|
213
213
|
log.error("version needs to be a string")
|
214
214
|
log.error(f"Importing version {ver} timelines is not supported.")
|
@@ -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
|