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.
Files changed (63) hide show
  1. {auto-editor-24.3.1/auto_editor.egg-info → auto-editor-24.9.1}/PKG-INFO +1 -1
  2. auto-editor-24.9.1/auto_editor/__init__.py +2 -0
  3. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/__main__.py +5 -0
  4. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/analyze.py +0 -82
  5. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/edit.py +3 -3
  6. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/ffwrapper.py +8 -5
  7. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/fcp11.py +1 -4
  8. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/fcp7.py +71 -132
  9. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/help.py +5 -6
  10. auto-editor-24.9.1/auto_editor/lang/libmath.py +23 -0
  11. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lang/palet.py +165 -70
  12. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/contracts.py +57 -10
  13. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/data_structs.py +1 -0
  14. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/output.py +4 -2
  15. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/video.py +30 -24
  16. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/test.py +5 -7
  17. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/cmdkw.py +41 -0
  18. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/types.py +1 -0
  19. {auto-editor-24.3.1 → auto-editor-24.9.1/auto_editor.egg-info}/PKG-INFO +1 -1
  20. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/SOURCES.txt +1 -0
  21. {auto-editor-24.3.1 → auto-editor-24.9.1}/pyproject.toml +4 -4
  22. auto-editor-24.3.1/auto_editor/__init__.py +0 -2
  23. {auto-editor-24.3.1 → auto-editor-24.9.1}/LICENSE +0 -0
  24. {auto-editor-24.3.1 → auto-editor-24.9.1}/README.md +0 -0
  25. {auto-editor-24.3.1 → auto-editor-24.9.1}/ae-ffmpeg/ae_ffmpeg/__init__.py +0 -0
  26. {auto-editor-24.3.1 → auto-editor-24.9.1}/ae-ffmpeg/ae_ffmpeg/py.typed +0 -0
  27. {auto-editor-24.3.1 → auto-editor-24.9.1}/ae-ffmpeg/setup.py +0 -0
  28. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/__init__.py +0 -0
  29. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/json.py +0 -0
  30. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/shotcut.py +0 -0
  31. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/formats/utils.py +0 -0
  32. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lang/__init__.py +0 -0
  33. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lang/json.py +0 -0
  34. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/__init__.py +0 -0
  35. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/lib/err.py +0 -0
  36. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/make_layers.py +0 -0
  37. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/preview.py +0 -0
  38. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/__init__.py +0 -0
  39. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/audio.py +0 -0
  40. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/render/subtitle.py +0 -0
  41. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/__init__.py +0 -0
  42. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/desc.py +0 -0
  43. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/info.py +0 -0
  44. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/levels.py +0 -0
  45. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/palet.py +0 -0
  46. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/repl.py +0 -0
  47. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/subcommands/subdump.py +0 -0
  48. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/timeline.py +0 -0
  49. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/__init__.py +0 -0
  50. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/bar.py +0 -0
  51. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/chunks.py +0 -0
  52. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/container.py +0 -0
  53. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/encoder.py +0 -0
  54. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/func.py +0 -0
  55. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/utils/log.py +0 -0
  56. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/validate_input.py +0 -0
  57. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/vanparse.py +0 -0
  58. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor/wavfile.py +0 -0
  59. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/dependency_links.txt +0 -0
  60. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/entry_points.txt +0 -0
  61. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/requires.txt +0 -0
  62. {auto-editor-24.3.1 → auto-editor-24.9.1}/auto_editor.egg-info/top_level.txt +0 -0
  63. {auto-editor-24.3.1 → auto-editor-24.9.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-editor
3
- Version: 24.3.1
3
+ Version: 24.9.1
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -0,0 +1,2 @@
1
+ __version__ = "24.9.1"
2
+ version = "24w09a"
@@ -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"], ffmpeg, output, tl, log)
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 as e:
239
- raise e
238
+ except Exception:
239
+ log.debug("Unexpected ffprobe shape")
240
240
 
241
241
  if v.sample_aspect_ratio is None:
242
- try:
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 a good editor, like Premiere Pro, "
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, make_tracks_dir, show
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, "anamorphic").text = ANAMORPHIC
407
- ET.SubElement(vschar, "pixelaspectratio").text = PIXEL_ASPECT_RATIO
366
+ ET.SubElement(vschar, "pixelaspectratio").text = "square"
408
367
 
409
- if len(src.audios) > 0:
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"{src.audios[0].channels}"
373
+ ET.SubElement(audiodef, "channelcount").text = f"{aud.channels}"
415
374
 
416
375
 
417
- def fcp7_write_xml(name: str, ffmpeg: FFmpeg, output: str, tl: v3, log: Log) -> None:
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[tuple[FileInfo, int], str] = {}
422
- src_to_id: dict[tuple[FileInfo, int], str] = {}
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
- src_to_url[(src, 0)] = path_resolve
432
- src_to_id[(src, 0)] = the_id
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[(clip.src, 0)]
429
+ _id = src_to_id[clip.src]
487
430
  filedef = ET.SubElement(clipitem, "file", id=_id)
488
431
 
489
- pathurl = src_to_url[(clip.src, 0)]
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
- for t, aclips in enumerate(tl.a):
517
- track = ET.Element(
518
- "track", currentExplodedTrackIndex="0", premiereTrackType="Stereo"
519
- )
520
-
521
- for j, aclip in enumerate(aclips):
522
- src = aclip.src
523
-
524
- _start = f"{aclip.start}"
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 aclip.stream > 0:
553
- my_src = src_to_src[(aclip.src, aclip.stream)]
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
- sourcetrack = ET.SubElement(clipitem, "sourcetrack")
563
- ET.SubElement(sourcetrack, "mediatype").text = "audio"
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
- if aclip.speed != 1:
569
- clipitem.append(speedup(aclip.speed * 100))
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
- if src.videos:
572
- ET.SubElement(clipitem, "outputchannelindex").text = "1"
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
- audio.append(track)
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="\t", level=0)
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
- Evalutes a palet expression that returns a bool-array?. The array is then used for
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 "all") : 0
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
+ }