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/__init__.py +1 -1
- auto_editor/__main__.py +37 -22
- auto_editor/analyze.py +47 -45
- auto_editor/cmds/levels.py +1 -1
- auto_editor/cmds/repl.py +2 -3
- auto_editor/cmds/test.py +328 -384
- auto_editor/edit.py +19 -2
- auto_editor/lang/palet.py +23 -27
- auto_editor/make_layers.py +28 -17
- auto_editor/preview.py +3 -2
- auto_editor/utils/types.py +2 -0
- {auto_editor-26.3.1.dist-info → auto_editor-26.3.2.dist-info}/METADATA +1 -1
- {auto_editor-26.3.1.dist-info → auto_editor-26.3.2.dist-info}/RECORD +17 -17
- {auto_editor-26.3.1.dist-info → auto_editor-26.3.2.dist-info}/WHEEL +1 -1
- {auto_editor-26.3.1.dist-info → auto_editor-26.3.2.dist-info}/LICENSE +0 -0
- {auto_editor-26.3.1.dist-info → auto_editor-26.3.2.dist-info}/entry_points.txt +0 -0
- {auto_editor-26.3.1.dist-info → auto_editor-26.3.2.dist-info}/top_level.txt +0 -0
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
|
-
|
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
|
-
|
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(
|
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
|
590
|
-
|
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
|
-
|
590
|
+
if stream_data is None:
|
591
|
+
return np.array([], dtype=np.bool_)
|
597
592
|
|
598
|
-
|
599
|
-
|
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
|
615
|
-
return
|
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
|
632
|
-
return
|
627
|
+
except LevelError:
|
628
|
+
return np.array([], dtype=np.bool_)
|
633
629
|
|
634
630
|
|
635
631
|
class StackTraceManager:
|
auto_editor/make_layers.py
CHANGED
@@ -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
|
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,
|
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["
|
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
|
-
|
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 =
|
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
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
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 +=
|
69
|
+
in_len += initLevels(src, tb, bar, False, log).media_length
|
69
70
|
|
70
71
|
out_len = tl.out_len()
|
71
72
|
|
auto_editor/utils/types.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
auto_editor/__init__.py,sha256=
|
2
|
-
auto_editor/__main__.py,sha256=
|
3
|
-
auto_editor/analyze.py,sha256=
|
4
|
-
auto_editor/edit.py,sha256=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
52
|
-
auto_editor-26.3.
|
53
|
-
auto_editor-26.3.
|
54
|
-
auto_editor-26.3.
|
55
|
-
auto_editor-26.3.
|
56
|
-
auto_editor-26.3.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|