auto-editor 26.3.1__py3-none-any.whl → 26.3.2__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/edit.py CHANGED
@@ -297,7 +297,15 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
297
297
  def make_media(tl: v3, output_path: str) -> None:
298
298
  assert src is not None
299
299
 
300
- output = av.open(output_path, "w")
300
+ if args.fragmented and not args.no_fragmented:
301
+ log.debug("Enabling fragmented mp4/mov")
302
+ options = {
303
+ "movflags": "+default_base_moof+faststart+frag_keyframe+separate_moof",
304
+ "frag_duration": "0.2",
305
+ }
306
+ else:
307
+ options = {"movflags": "faststart"}
308
+ output = av.open(output_path, "w", options=options)
301
309
 
302
310
  if ctr.default_sub != "none" and not args.sn:
303
311
  sub_paths = make_new_subtitles(tl, log)
@@ -444,6 +452,9 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
444
452
 
445
453
  if should_get_audio:
446
454
  audio_frames = [next(frames, None) for frames in audio_gen_frames]
455
+ if output_stream is None and audio_frames and audio_frames[-1]:
456
+ assert audio_frames[-1].time is not None
457
+ index = round(audio_frames[-1].time * tl.tb)
447
458
  else:
448
459
  audio_frames = [None]
449
460
  if should_get_sub:
@@ -478,8 +489,11 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
478
489
  while frame_queue and frame_queue[0].index <= index:
479
490
  item = heappop(frame_queue)
480
491
  frame_type = item.frame_type
492
+ bar_index = None
481
493
  try:
482
494
  if frame_type in {"video", "audio"}:
495
+ if item.frame.time is not None:
496
+ bar_index = round(item.frame.time * tl.tb)
483
497
  output.mux(item.stream.encode(item.frame))
484
498
  elif frame_type == "subtitle":
485
499
  output.mux(item.frame)
@@ -488,10 +502,13 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
488
502
  f"Generic error for encoder: {item.stream.name}\n"
489
503
  f"at {item.index} time_base\nPerhaps video quality settings are too low?"
490
504
  )
505
+ except av.FileNotFoundError:
506
+ log.error(f"File not found: {output_path}")
491
507
  except av.FFmpegError as e:
492
508
  log.error(e)
493
509
 
494
- bar.tick(index)
510
+ if bar_index:
511
+ bar.tick(bar_index)
495
512
 
496
513
  # Flush streams
497
514
  if output_stream is not None:
auto_editor/lang/palet.py CHANGED
@@ -9,11 +9,11 @@ from dataclasses import dataclass
9
9
  from difflib import get_close_matches
10
10
  from fractions import Fraction
11
11
  from io import StringIO
12
- from typing import TYPE_CHECKING
12
+ from typing import TYPE_CHECKING, cast
13
13
 
14
14
  import numpy as np
15
15
 
16
- from auto_editor.analyze import LevelError, mut_remove_small
16
+ from auto_editor.analyze import LevelError, Levels, mut_remove_small
17
17
  from auto_editor.lib.contracts import *
18
18
  from auto_editor.lib.data_structs import *
19
19
  from auto_editor.lib.err import MyError
@@ -21,7 +21,7 @@ from auto_editor.utils.func import boolop
21
21
 
22
22
  if TYPE_CHECKING:
23
23
  from collections.abc import Callable
24
- from typing import Any, NoReturn
24
+ from typing import Any, NoReturn, TypeGuard
25
25
 
26
26
  from numpy.typing import NDArray
27
27
 
@@ -510,15 +510,16 @@ def p_slice(
510
510
  return seq[start:end:step]
511
511
 
512
512
 
513
+ def is_boolean_array(v: object) -> TypeGuard[np.ndarray]:
514
+ return isinstance(v, np.ndarray) and v.dtype.kind == "b"
515
+
516
+
513
517
  is_iterable = Contract(
514
518
  "iterable?",
515
519
  lambda v: type(v) in {str, range, list, tuple, dict, Quoted}
516
520
  or isinstance(v, np.ndarray),
517
521
  )
518
- is_boolarr = Contract(
519
- "bool-array?",
520
- lambda v: isinstance(v, np.ndarray) and v.dtype.kind == "b",
521
- )
522
+ is_boolarr = Contract("bool-array?", is_boolean_array)
522
523
 
523
524
 
524
525
  def raise_(msg: str | Exception) -> NoReturn:
@@ -568,13 +569,10 @@ def edit_audio(
568
569
  if "@levels" not in env:
569
570
  raise MyError("Can't use `audio` if there's no input media")
570
571
 
571
- levels = env["@levels"]
572
- src = levels.src
573
- strict = levels.strict
574
-
572
+ levels = cast(Levels, env["@levels"])
575
573
  stream_data: NDArray[np.bool_] | None = None
576
574
  if stream == Sym("all"):
577
- stream_range = range(0, len(src.audios))
575
+ stream_range = range(0, len(levels.container.streams.audio))
578
576
  else:
579
577
  assert isinstance(stream, int)
580
578
  stream_range = range(stream, stream + 1)
@@ -586,17 +584,15 @@ def edit_audio(
586
584
  stream_data = audio_list
587
585
  else:
588
586
  stream_data = boolop(stream_data, audio_list, np.logical_or)
589
- except LevelError as e:
590
- raise_(e) if strict else levels.all()
591
-
592
- if stream_data is not None:
593
- mut_remove_small(stream_data, minclip, replace=1, with_=0)
594
- mut_remove_small(stream_data, mincut, replace=0, with_=1)
587
+ except LevelError:
588
+ return np.array([], dtype=np.bool_)
595
589
 
596
- return stream_data
590
+ if stream_data is None:
591
+ return np.array([], dtype=np.bool_)
597
592
 
598
- stream = 0 if stream == Sym("all") else stream
599
- return raise_(f"audio stream '{stream}' does not exist") if strict else levels.all()
593
+ mut_remove_small(stream_data, minclip, replace=1, with_=0)
594
+ mut_remove_small(stream_data, mincut, replace=0, with_=1)
595
+ return stream_data
600
596
 
601
597
 
602
598
  def edit_motion(
@@ -608,18 +604,18 @@ def edit_motion(
608
604
  if "@levels" not in env:
609
605
  raise MyError("Can't use `motion` if there's no input media")
610
606
 
611
- levels = env["@levels"]
607
+ levels = cast(Levels, env["@levels"])
612
608
  try:
613
609
  return levels.motion(stream, blur, width) >= threshold
614
- except LevelError as e:
615
- return raise_(e) if levels.strict else levels.all()
610
+ except LevelError:
611
+ return np.array([], dtype=np.bool_)
616
612
 
617
613
 
618
614
  def edit_subtitle(pattern, stream=0, **kwargs):
619
615
  if "@levels" not in env:
620
616
  raise MyError("Can't use `subtitle` if there's no input media")
621
617
 
622
- levels = env["@levels"]
618
+ levels = cast(Levels, env["@levels"])
623
619
  if "ignore-case" not in kwargs:
624
620
  kwargs["ignore-case"] = False
625
621
  if "max-count" not in kwargs:
@@ -628,8 +624,8 @@ def edit_subtitle(pattern, stream=0, **kwargs):
628
624
  max_count = kwargs["max-count"]
629
625
  try:
630
626
  return levels.subtitle(pattern, stream, ignore_case, max_count)
631
- except LevelError as e:
632
- return raise_(e) if levels.strict else levels.all()
627
+ except LevelError:
628
+ return np.array([], dtype=np.bool_)
633
629
 
634
630
 
635
631
  class StackTraceManager:
@@ -6,9 +6,9 @@ from typing import TYPE_CHECKING, NamedTuple
6
6
 
7
7
  import numpy as np
8
8
 
9
- from auto_editor.analyze import Levels
9
+ from auto_editor.analyze import initLevels
10
10
  from auto_editor.ffwrapper import FileInfo
11
- from auto_editor.lang.palet import Lexer, Parser, env, interpret, is_boolarr
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
14
  from auto_editor.timeline import ASpace, TlAudio, TlVideo, VSpace, v1, v3
@@ -122,7 +122,6 @@ def make_timeline(
122
122
 
123
123
  has_loud = np.array([], dtype=np.bool_)
124
124
  src_index = np.array([], dtype=np.int32)
125
- concat = np.concatenate
126
125
 
127
126
  try:
128
127
  stdenv = __import__("auto_editor.lang.stdenv", fromlist=["lang"])
@@ -137,6 +136,7 @@ def make_timeline(
137
136
  parser = Parser(Lexer("config.pal", file.read()))
138
137
  interpret(env, parser)
139
138
 
139
+ results = []
140
140
  for i, src in enumerate(sources):
141
141
  try:
142
142
  parser = Parser(Lexer("`--edit`", args.edit))
@@ -144,32 +144,43 @@ def make_timeline(
144
144
  log.debug(f"edit: {parser}")
145
145
 
146
146
  env["timebase"] = tb
147
- env["src"] = f"{src.path}"
148
- env["@levels"] = Levels(src, tb, bar, args.no_cache, log, len(sources) < 2)
147
+ env["@levels"] = initLevels(src, tb, bar, args.no_cache, log)
149
148
 
150
- results = interpret(env, parser)
151
-
152
- if len(results) == 0:
149
+ inter_result = interpret(env, parser)
150
+ if len(inter_result) == 0:
153
151
  log.error("Expression in --edit must return a bool-array, got nothing")
154
152
 
155
- result = results[-1]
153
+ result = inter_result[-1]
156
154
  if callable(result):
157
155
  result = result()
158
156
  except MyError as e:
159
157
  log.error(e)
160
158
 
161
- if not is_boolarr(result):
159
+ if not is_boolean_array(result):
162
160
  log.error(
163
161
  f"Expression in --edit must return a bool-array, got {print_str(result)}"
164
162
  )
165
- assert isinstance(result, np.ndarray)
166
-
167
163
  mut_margin(result, start_margin, end_margin)
168
-
169
- has_loud = concat((has_loud, result))
170
- src_index = concat((src_index, np.full(len(result), i, dtype=np.int32)))
171
-
172
- assert len(has_loud) > 0
164
+ results.append(result)
165
+
166
+ if all(len(result) == 0 for result in results):
167
+ if "subtitle" in args.edit:
168
+ log.error("No file(s) have the selected subtitle stream.")
169
+ if "motion" in args.edit:
170
+ log.error("No file(s) have the selected video stream.")
171
+ if "audio" in args.edit:
172
+ log.error("No file(s) have the selected audio stream.")
173
+
174
+ src_indexes = []
175
+ for i in range(0, len(results)):
176
+ if len(results[i]) == 0:
177
+ results[i] = initLevels(sources[i], tb, bar, args.no_cache, log).all()
178
+ src_indexes.append(np.full(len(results[i]), i, dtype=np.int32))
179
+
180
+ has_loud = np.concatenate(results)
181
+ src_index = np.concatenate(src_indexes)
182
+ if len(has_loud) == 0:
183
+ log.error("Empty timeline. Nothing to do.")
173
184
 
174
185
  # Setup for handling custom speeds
175
186
  speed_index = has_loud.astype(np.uint)
auto_editor/preview.py CHANGED
@@ -5,7 +5,7 @@ from fractions import Fraction
5
5
  from statistics import fmean, median
6
6
  from typing import TextIO
7
7
 
8
- from auto_editor.analyze import Levels
8
+ from auto_editor.analyze import initLevels
9
9
  from auto_editor.timeline import v3
10
10
  from auto_editor.utils.bar import initBar
11
11
  from auto_editor.utils.func import to_timecode
@@ -64,8 +64,9 @@ def preview(tl: v3, log: Log) -> None:
64
64
  all_sources.add(aclip.src)
65
65
 
66
66
  in_len = 0
67
+ bar = initBar("none")
67
68
  for src in all_sources:
68
- in_len += Levels(src, tb, initBar("none"), False, log, False).media_length
69
+ in_len += initLevels(src, tb, bar, False, log).media_length
69
70
 
70
71
  out_len = tl.out_len()
71
72
 
@@ -201,6 +201,8 @@ class Args:
201
201
  vprofile: str | None = None
202
202
  audio_bitrate: str = "auto"
203
203
  scale: float = 1.0
204
+ fragmented: bool = False
205
+ no_fragmented: bool = False
204
206
  sn: bool = False
205
207
  dn: bool = False
206
208
  no_seek: bool = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: auto-editor
3
- Version: 26.3.1
3
+ Version: 26.3.2
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -1,12 +1,12 @@
1
- auto_editor/__init__.py,sha256=cbuXE-xYiZp6P9Ipk0EAUgPxPvaOpg-ve_HurO96fCM,23
2
- auto_editor/__main__.py,sha256=g-9q3i6oFeNPeFMeNJEwZua6ZloOPLcaLN-B0FlxWXo,11371
3
- auto_editor/analyze.py,sha256=tkNZdRz1Nf1mQfC3jKHaiLbHRkacX_7-2TrKTKvwpxY,12463
4
- auto_editor/edit.py,sha256=unOxLPiPUMoLgM_ScdRA3rNnxOicUUu7iLO1lG0zcfc,20260
1
+ auto_editor/__init__.py,sha256=bDmtpN3tJ_1R818Djb_PZ7H7ZIgSCXSdbTx5GJ009SU,23
2
+ auto_editor/__main__.py,sha256=BQRStgcJxNVUOisP4JpcaxaM_KTGFS04Qgklyy9fumc,12012
3
+ auto_editor/analyze.py,sha256=YY3Io-IDdlliawx03w-L9AW5nZSOtmbXX_la1HanYmY,12658
4
+ auto_editor/edit.py,sha256=wjs1qj13Rx8JGgf7b_dPgVEmd_vDyU-N5GDQhVGSJX8,21130
5
5
  auto_editor/ffwrapper.py,sha256=1lYYfq8gVgMVkYWeAEYDPAHCwFCYbKQwy0FxYBxMzk8,4765
6
6
  auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
7
- auto_editor/make_layers.py,sha256=vEeJt0PnE1vc9-cQZ_AlXVDjvWhObRCWJSCQGraoMvU,9016
7
+ auto_editor/make_layers.py,sha256=Yl4xYkMHume_VvRW8Ii48hyx-ZtFr8WMmVYXJMSUmg0,9581
8
8
  auto_editor/output.py,sha256=ho8Lpqz4Sv_Gw0Vj2OvG39s83xHpyZlvtRNryTPbXqc,2563
9
- auto_editor/preview.py,sha256=HUsjmV9Fx73rZ26BXrpz9z-z_e4oiui3u9e7qbbGoBY,3037
9
+ auto_editor/preview.py,sha256=cqQdozM2IB-5qXHNxeqiSrSdEIzlMfjD4SU-NX9sYZ0,3052
10
10
  auto_editor/timeline.py,sha256=XfaH9cH-RB-MObOpMr5IfLcqJcjmabO1XwkUkT3_FQM,8186
11
11
  auto_editor/vanparse.py,sha256=Ug5A2QaRqGiw4l55Z_h9T2QU1x0WqRibR7yY5rQ0WTk,10002
12
12
  auto_editor/wavfile.py,sha256=afFfje8cK9lFjIkYoBbQHfvQIpsEPxWvspCtFhUKlAw,9499
@@ -14,11 +14,11 @@ auto_editor/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
14
14
  auto_editor/cmds/cache.py,sha256=bViYbtVXefTeEIUvSanDfA6cG35ep1N_Jvtz7ZjgIkY,1959
15
15
  auto_editor/cmds/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
16
16
  auto_editor/cmds/info.py,sha256=vYa1hYdE8kDTE8AS3kwXlnd59X6CrE2GtIEQ7UmlpRY,7010
17
- auto_editor/cmds/levels.py,sha256=RBBs0YM8Qb7XteCy7Pr4QpU9b5maMsDe6xEkAuyGs1c,5644
17
+ auto_editor/cmds/levels.py,sha256=6LqXfNBca__KGeA8kJqJqH0MBR3GudaFoWU0_dnv5bI,5635
18
18
  auto_editor/cmds/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
19
- auto_editor/cmds/repl.py,sha256=TF_I7zsFY7-KdgidrqjafTz7o_eluVbLvgTcOBG-UWQ,3449
19
+ auto_editor/cmds/repl.py,sha256=8DgMw-XyfR5XctSmwtk4_1-zxs3ooMs72BfMRlVqLvY,3412
20
20
  auto_editor/cmds/subdump.py,sha256=af_XBf7kaevqHn1A71z8C-7x8pS5WKD9FE_ugkCw6rk,665
21
- auto_editor/cmds/test.py,sha256=D8l4Zr0UjrUtFkqsADqlVQ1wvNP3s97ifNnyf1UBTFU,26327
21
+ auto_editor/cmds/test.py,sha256=SzjtsEteztXFbkvLRfL9BPSgK8Qez_-yXPR5ts_tu9E,26465
22
22
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  auto_editor/formats/fcp11.py,sha256=sqjC36jI47ICPLjZJYiqGwY7foOnWOiNjkPFLdgSnI4,5208
24
24
  auto_editor/formats/fcp7.py,sha256=x5cagTzGCAW3i3M6m7TZC1h8gLfSmX1UK-iiDuCpdfs,20289
@@ -29,7 +29,7 @@ auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
29
29
  auto_editor/lang/json.py,sha256=D84vwyLtX5t5xl8S4r4jFXGVkbIg1L4IcexlS_a_k6w,9231
30
30
  auto_editor/lang/libintrospection.py,sha256=6H1rGp0wqaCud5IPaoEmzULGnYt6ec7_0h32ATcw2oY,261
31
31
  auto_editor/lang/libmath.py,sha256=z33A161Oe6vYYK7R6pgYjdZZe63dQkN38Qf36TL3prg,847
32
- auto_editor/lang/palet.py,sha256=ZOzNxLfzY8FTCYlF_mZtU2XoNzdg-eseUVkMtnWkVaI,24139
32
+ auto_editor/lang/palet.py,sha256=XKMVMybHZK5c_iB368T4pFqI7Yfl5o4eDVa5XMNLEow,24082
33
33
  auto_editor/lang/stdenv.py,sha256=sQyOD3bttjcieWMtTMNpE6blEZriAfHtSZHbuvC09R8,43762
34
34
  auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,7586
@@ -46,11 +46,11 @@ auto_editor/utils/cmdkw.py,sha256=aUGBvBel2Ko1o6Rwmr4rEL-BMc5hEnzYLbyZ1GeJdcY,57
46
46
  auto_editor/utils/container.py,sha256=C_Ahh7nlMEX4DNQ2M_cITPPbYcIL68r4I_AgFy0OD6o,2487
47
47
  auto_editor/utils/func.py,sha256=C8ucgsSEzPyBc-8obhsCXd_uQW0cnCdBn1KVxB7FHjU,2747
48
48
  auto_editor/utils/log.py,sha256=wPNf6AabV-0cnoS_bPLv1Lh7llQBtNqPKeh07einOuc,3701
49
- auto_editor/utils/types.py,sha256=r5f6QB81xH7NRwGntITIOCVx-fupOl8l3X3LSFkt3nE,10756
49
+ auto_editor/utils/types.py,sha256=HxFafTgmL_5HQbfR7AR_4Q6o8iHk1z-T6WT90PgQHiY,10817
50
50
  docs/build.py,sha256=POy8X8QOBYe_8A8HI_yiVI_Qg9E5mLpn1z7AHQr0_vQ,1888
51
- auto_editor-26.3.1.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
52
- auto_editor-26.3.1.dist-info/METADATA,sha256=4r9rtkLWEDaVMcJauMcljdAw-cnz2h6xS9sixAnWVlQ,6111
53
- auto_editor-26.3.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
54
- auto_editor-26.3.1.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
- auto_editor-26.3.1.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
- auto_editor-26.3.1.dist-info/RECORD,,
51
+ auto_editor-26.3.2.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
52
+ auto_editor-26.3.2.dist-info/METADATA,sha256=lxdGmJqB_b7J1myUG1CFUnXFKo0Vt2Kr8cG7rl58xK8,6111
53
+ auto_editor-26.3.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
54
+ auto_editor-26.3.2.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
+ auto_editor-26.3.2.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
+ auto_editor-26.3.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5