auto-editor 25.1.0__tar.gz → 25.2.0__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-25.1.0 → auto_editor-25.2.0}/PKG-INFO +4 -4
- auto_editor-25.2.0/auto_editor/__init__.py +1 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lang/palet.py +118 -57
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lang/stdenv.py +56 -5
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lib/contracts.py +4 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lib/data_structs.py +4 -2
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/render/video.py +2 -19
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/timeline.py +1 -7
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/cmdkw.py +5 -11
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/types.py +0 -7
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor.egg-info/PKG-INFO +4 -4
- auto_editor-25.2.0/auto_editor.egg-info/requires.txt +3 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/pyproject.toml +3 -3
- auto_editor-25.1.0/auto_editor/__init__.py +0 -1
- auto_editor-25.1.0/auto_editor.egg-info/requires.txt +0 -3
- {auto_editor-25.1.0 → auto_editor-25.2.0}/LICENSE +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/README.md +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/__main__.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/analyze.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/edit.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/ffwrapper.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/formats/__init__.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/formats/fcp11.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/formats/fcp7.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/formats/json.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/formats/shotcut.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/formats/utils.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/help.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lang/__init__.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lang/json.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lang/libintrospection.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lang/libmath.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lib/__init__.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/lib/err.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/make_layers.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/output.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/preview.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/render/__init__.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/render/audio.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/render/subtitle.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/__init__.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/desc.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/info.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/levels.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/palet.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/repl.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/subdump.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/subcommands/test.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/__init__.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/bar.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/chunks.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/container.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/encoder.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/func.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/log.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/utils/subtitle_tools.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/validate_input.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/vanparse.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor/wavfile.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor.egg-info/SOURCES.txt +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor.egg-info/dependency_links.txt +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor.egg-info/entry_points.txt +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/auto_editor.egg-info/top_level.txt +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/docs/build.py +0 -0
- {auto_editor-25.1.0 → auto_editor-25.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: auto-editor
|
3
|
-
Version: 25.
|
3
|
+
Version: 25.2.0
|
4
4
|
Summary: Auto-Editor: Effort free video editing!
|
5
5
|
Author-email: WyattBlue <wyattblue@auto-editor.com>
|
6
6
|
License: Unlicense
|
@@ -8,11 +8,11 @@ Project-URL: Bug Tracker, https://github.com/WyattBlue/auto-editor/issues
|
|
8
8
|
Project-URL: Source Code, https://github.com/WyattBlue/auto-editor
|
9
9
|
Project-URL: homepage, https://auto-editor.com
|
10
10
|
Keywords: video,audio,media,editor,editing,processing,nonlinear,automatic,silence-detect,silence-removal,silence-speedup,motion-detection
|
11
|
-
Requires-Python:
|
11
|
+
Requires-Python: <3.14,>=3.10
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
|
-
Requires-Dist: numpy
|
15
|
-
Requires-Dist: pyav==
|
14
|
+
Requires-Dist: numpy<3.0,>=1.23.0
|
15
|
+
Requires-Dist: pyav==13.0.*
|
16
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>
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "25.2.0"
|
@@ -60,6 +60,8 @@ str_escape = {
|
|
60
60
|
class Token:
|
61
61
|
type: str
|
62
62
|
value: Any
|
63
|
+
lineno: int
|
64
|
+
column: int
|
63
65
|
|
64
66
|
|
65
67
|
class Lexer:
|
@@ -156,21 +158,31 @@ class Lexer:
|
|
156
158
|
elif unit == "dB":
|
157
159
|
token = DB
|
158
160
|
elif unit != "i" and unit != "%":
|
159
|
-
return Token(
|
161
|
+
return Token(
|
162
|
+
VAL,
|
163
|
+
Sym(result + unit, self.lineno, self.column),
|
164
|
+
self.lineno,
|
165
|
+
self.column,
|
166
|
+
)
|
160
167
|
|
161
168
|
try:
|
162
169
|
if unit == "i":
|
163
|
-
return Token(VAL, complex(result + "j"))
|
170
|
+
return Token(VAL, complex(result + "j"), self.lineno, self.column)
|
164
171
|
elif unit == "%":
|
165
|
-
return Token(VAL, float(result) / 100)
|
172
|
+
return Token(VAL, float(result) / 100, self.lineno, self.column)
|
166
173
|
elif "/" in result:
|
167
|
-
return Token(token, Fraction(result))
|
174
|
+
return Token(token, Fraction(result), self.lineno, self.column)
|
168
175
|
elif "." in result:
|
169
|
-
return Token(token, float(result))
|
176
|
+
return Token(token, float(result), self.lineno, self.column)
|
170
177
|
else:
|
171
|
-
return Token(token, int(result))
|
178
|
+
return Token(token, int(result), self.lineno, self.column)
|
172
179
|
except ValueError:
|
173
|
-
return Token(
|
180
|
+
return Token(
|
181
|
+
VAL,
|
182
|
+
Sym(result + unit, self.lineno, self.column),
|
183
|
+
self.lineno,
|
184
|
+
self.column,
|
185
|
+
)
|
174
186
|
|
175
187
|
def hash_literal(self) -> Token:
|
176
188
|
if self.char == "\\":
|
@@ -180,7 +192,7 @@ class Lexer:
|
|
180
192
|
|
181
193
|
char = self.char
|
182
194
|
self.advance()
|
183
|
-
return Token(VAL, Char(char))
|
195
|
+
return Token(VAL, Char(char), self.lineno, self.column)
|
184
196
|
|
185
197
|
if self.char == ":":
|
186
198
|
self.advance()
|
@@ -190,14 +202,14 @@ class Lexer:
|
|
190
202
|
buf.write(self.char)
|
191
203
|
self.advance()
|
192
204
|
|
193
|
-
return Token(VAL, Keyword(buf.getvalue()))
|
205
|
+
return Token(VAL, Keyword(buf.getvalue()), self.lineno, self.column)
|
194
206
|
|
195
207
|
if self.char is not None and self.char in "([{":
|
196
208
|
brac_type = self.char
|
197
209
|
self.advance()
|
198
210
|
if self.char is None:
|
199
211
|
self.close_err(f"Expected a character after #{brac_type}")
|
200
|
-
return Token(VLIT, brac_pairs[brac_type])
|
212
|
+
return Token(VLIT, brac_pairs[brac_type], self.lineno, self.column)
|
201
213
|
|
202
214
|
buf = StringIO()
|
203
215
|
while self.char_is_norm():
|
@@ -207,10 +219,10 @@ class Lexer:
|
|
207
219
|
|
208
220
|
result = buf.getvalue()
|
209
221
|
if result in ("t", "T", "true"):
|
210
|
-
return Token(VAL, True)
|
222
|
+
return Token(VAL, True, self.lineno, self.column)
|
211
223
|
|
212
224
|
if result in ("f", "F", "false"):
|
213
|
-
return Token(VAL, False)
|
225
|
+
return Token(VAL, False, self.lineno, self.column)
|
214
226
|
|
215
227
|
self.error(f"Unknown hash literal `#{result}`")
|
216
228
|
|
@@ -231,17 +243,19 @@ class Lexer:
|
|
231
243
|
my_str = self.string()
|
232
244
|
if self.char == ".": # handle `object.method` syntax
|
233
245
|
self.advance()
|
234
|
-
return Token(
|
235
|
-
|
246
|
+
return Token(
|
247
|
+
DOT, (my_str, self.get_next_token()), self.lineno, self.column
|
248
|
+
)
|
249
|
+
return Token(VAL, my_str, self.lineno, self.column)
|
236
250
|
|
237
251
|
if self.char == "'":
|
238
252
|
self.advance()
|
239
|
-
return Token(QUOTE, "'")
|
253
|
+
return Token(QUOTE, "'", self.lineno, self.column)
|
240
254
|
|
241
255
|
if self.char in "(){}[]":
|
242
256
|
_par = self.char
|
243
257
|
self.advance()
|
244
|
-
return Token(_par, _par)
|
258
|
+
return Token(_par, _par, self.lineno, self.column)
|
245
259
|
|
246
260
|
if self.char in "+-":
|
247
261
|
_peek = self.peek()
|
@@ -339,18 +353,27 @@ class Lexer:
|
|
339
353
|
if is_method:
|
340
354
|
from auto_editor.utils.cmdkw import parse_method
|
341
355
|
|
342
|
-
return Token(
|
356
|
+
return Token(
|
357
|
+
M, parse_method(name, result, env), self.lineno, self.column
|
358
|
+
)
|
343
359
|
|
344
360
|
if self.char == ".": # handle `object.method` syntax
|
345
361
|
self.advance()
|
346
|
-
return Token(
|
362
|
+
return Token(
|
363
|
+
DOT,
|
364
|
+
(Sym(result, self.lineno, self.column), self.get_next_token()),
|
365
|
+
self.lineno,
|
366
|
+
self.column,
|
367
|
+
)
|
347
368
|
|
348
369
|
if has_illegal:
|
349
370
|
self.error(f"Symbol has illegal character(s): {result}")
|
350
371
|
|
351
|
-
return Token(
|
372
|
+
return Token(
|
373
|
+
VAL, Sym(result, self.lineno, self.column), self.lineno, self.column
|
374
|
+
)
|
352
375
|
|
353
|
-
return Token(EOF, "EOF")
|
376
|
+
return Token(EOF, "EOF", self.lineno, self.column)
|
354
377
|
|
355
378
|
|
356
379
|
###############################################################################
|
@@ -370,6 +393,7 @@ class Parser:
|
|
370
393
|
|
371
394
|
def expr(self) -> Any:
|
372
395
|
token = self.current_token
|
396
|
+
lineno, column = token.lineno, token.column
|
373
397
|
|
374
398
|
if token.type == VAL:
|
375
399
|
self.eat()
|
@@ -397,7 +421,7 @@ class Parser:
|
|
397
421
|
if token.type == M:
|
398
422
|
self.eat()
|
399
423
|
name, args, kwargs = token.value
|
400
|
-
_result = [Sym(name)] + args
|
424
|
+
_result = [Sym(name, lineno, column)] + args
|
401
425
|
for key, val in kwargs.items():
|
402
426
|
_result.append(Keyword(key))
|
403
427
|
_result.append(val)
|
@@ -413,7 +437,7 @@ class Parser:
|
|
413
437
|
|
414
438
|
if token.type == QUOTE:
|
415
439
|
self.eat()
|
416
|
-
return (Sym("quote"), self.expr())
|
440
|
+
return (Sym("quote", lineno, column), self.expr())
|
417
441
|
|
418
442
|
if token.type in brac_pairs:
|
419
443
|
self.eat()
|
@@ -610,16 +634,41 @@ def edit_subtitle(pattern, stream=0, **kwargs):
|
|
610
634
|
return raise_(e) if levels.strict else levels.all()
|
611
635
|
|
612
636
|
|
637
|
+
class StackTraceManager:
|
638
|
+
def __init__(self) -> None:
|
639
|
+
self.stack: list[Sym] = []
|
640
|
+
|
641
|
+
def push(self, sym: Sym) -> None:
|
642
|
+
self.stack.append(sym)
|
643
|
+
|
644
|
+
def pop(self) -> None:
|
645
|
+
if self.stack:
|
646
|
+
self.stack.pop()
|
647
|
+
|
648
|
+
def get_stacktrace(self) -> str:
|
649
|
+
return "\n".join(
|
650
|
+
f" at {sym.val} ({sym.lineno}:{sym.column})"
|
651
|
+
for sym in reversed(self.stack)
|
652
|
+
)
|
653
|
+
|
654
|
+
|
655
|
+
stack_trace_manager = StackTraceManager()
|
656
|
+
|
657
|
+
|
613
658
|
def my_eval(env: Env, node: object) -> Any:
|
659
|
+
def make_trace(sym: Sym) -> str:
|
660
|
+
return f" at {sym.val} ({sym.lineno}:{sym.column})"
|
661
|
+
|
614
662
|
if type(node) is Sym:
|
615
663
|
val = env.get(node.val)
|
616
664
|
if type(val) is NotFound:
|
665
|
+
stacktrace = make_trace(node)
|
617
666
|
if mat := get_close_matches(node.val, env.data):
|
618
667
|
raise MyError(
|
619
|
-
f"variable `{node.val}` not found. Did you mean: {mat[0]}"
|
668
|
+
f"variable `{node.val}` not found. Did you mean: {mat[0]}\n{stacktrace}"
|
620
669
|
)
|
621
670
|
raise MyError(
|
622
|
-
f"variable `{node.val}` not found. Did you mean a string literal
|
671
|
+
f"variable `{node.val}` not found. Did you mean a string literal.\n{stacktrace}"
|
623
672
|
)
|
624
673
|
return val
|
625
674
|
|
@@ -631,44 +680,56 @@ def my_eval(env: Env, node: object) -> Any:
|
|
631
680
|
raise MyError("Illegal () expression")
|
632
681
|
|
633
682
|
oper = my_eval(env, node[0])
|
634
|
-
if
|
635
|
-
|
636
|
-
...No one wants to write (aref a x y) when they could write a[x,y].
|
637
|
-
In this particular case there is a way to finesse our way out of the
|
638
|
-
problem. If we treat data structures as if they were functions on indexes,
|
639
|
-
we could write (a x y) instead, which is even shorter than the Perl form.
|
640
|
-
"""
|
641
|
-
if is_iterable(oper):
|
642
|
-
length = len(node[1:])
|
643
|
-
if length > 3:
|
644
|
-
raise MyError(f"{print_str(node[0])}: slice expects 1 argument")
|
645
|
-
if length in (2, 3):
|
646
|
-
return p_slice(oper, *(my_eval(env, c) for c in node[1:]))
|
647
|
-
if length == 1:
|
648
|
-
return ref(oper, my_eval(env, node[1]))
|
683
|
+
if isinstance(node[0], Sym):
|
684
|
+
stack_trace_manager.push(node[0])
|
649
685
|
|
650
|
-
|
651
|
-
|
652
|
-
|
686
|
+
try:
|
687
|
+
if not callable(oper):
|
688
|
+
"""
|
689
|
+
...No one wants to write (aref a x y) when they could write a[x,y].
|
690
|
+
In this particular case there is a way to finesse our way out of the
|
691
|
+
problem. If we treat data structures as if they were functions on indexes,
|
692
|
+
we could write (a x y) instead, which is even shorter than the Perl form.
|
693
|
+
"""
|
694
|
+
if is_iterable(oper):
|
695
|
+
length = len(node[1:])
|
696
|
+
if length > 3:
|
697
|
+
raise MyError(f"{print_str(node[0])}: slice expects 1 argument")
|
698
|
+
if length in (2, 3):
|
699
|
+
return p_slice(oper, *(my_eval(env, c) for c in node[1:]))
|
700
|
+
if length == 1:
|
701
|
+
return ref(oper, my_eval(env, node[1]))
|
653
702
|
|
654
|
-
|
655
|
-
|
703
|
+
raise MyError(
|
704
|
+
f"{print_str(oper)} is not a function. Tried to run with args: {print_str(node[1:])}"
|
705
|
+
)
|
656
706
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
707
|
+
if type(oper) is Syntax:
|
708
|
+
return oper(env, node)
|
709
|
+
|
710
|
+
i = 1
|
711
|
+
args: list[Any] = []
|
712
|
+
kwargs: dict[str, Any] = {}
|
713
|
+
while i < len(node):
|
714
|
+
result = my_eval(env, node[i])
|
715
|
+
if type(result) is Keyword:
|
716
|
+
i += 1
|
717
|
+
if i >= len(node):
|
718
|
+
raise MyError("Keyword need argument")
|
719
|
+
kwargs[result.val] = my_eval(env, node[i])
|
720
|
+
else:
|
721
|
+
args.append(result)
|
663
722
|
i += 1
|
664
|
-
if i >= len(node):
|
665
|
-
raise MyError("Keyword need argument")
|
666
|
-
kwargs[result.val] = my_eval(env, node[i])
|
667
|
-
else:
|
668
|
-
args.append(result)
|
669
|
-
i += 1
|
670
723
|
|
671
|
-
|
724
|
+
return oper(*args, **kwargs)
|
725
|
+
except MyError as e:
|
726
|
+
error_msg = str(e)
|
727
|
+
if not error_msg.endswith(make_trace(node[0])):
|
728
|
+
error_msg += f"\n{make_trace(node[0])}"
|
729
|
+
raise MyError(error_msg)
|
730
|
+
finally:
|
731
|
+
if isinstance(node[0], Sym):
|
732
|
+
stack_trace_manager.pop()
|
672
733
|
|
673
734
|
return node
|
674
735
|
|
@@ -20,6 +20,7 @@ if TYPE_CHECKING:
|
|
20
20
|
|
21
21
|
|
22
22
|
def make_standard_env() -> dict[str, Any]:
|
23
|
+
import os.path
|
23
24
|
from cmath import sqrt as complex_sqrt
|
24
25
|
from functools import reduce
|
25
26
|
from operator import add, ge, gt, is_, le, lt, mod, mul
|
@@ -135,6 +136,22 @@ def make_standard_env() -> dict[str, Any]:
|
|
135
136
|
Sym("float64"): np.float64,
|
136
137
|
}
|
137
138
|
|
139
|
+
@dataclass(slots=True)
|
140
|
+
class InputPort:
|
141
|
+
name: str
|
142
|
+
port: Any
|
143
|
+
closed: bool
|
144
|
+
|
145
|
+
def close(self) -> None:
|
146
|
+
if not self.closed:
|
147
|
+
self.closed = True
|
148
|
+
self.port.close()
|
149
|
+
|
150
|
+
def __str__(self) -> str:
|
151
|
+
return f"#<input-port:{self.name}>"
|
152
|
+
|
153
|
+
__repr__ = __str__
|
154
|
+
|
138
155
|
@dataclass(slots=True)
|
139
156
|
class OutputPort:
|
140
157
|
name: str
|
@@ -152,6 +169,13 @@ def make_standard_env() -> dict[str, Any]:
|
|
152
169
|
|
153
170
|
__repr__ = __str__
|
154
171
|
|
172
|
+
def initInPort(name: str) -> InputPort | Literal[False]:
|
173
|
+
try:
|
174
|
+
port = open(name, encoding="utf-8")
|
175
|
+
except Exception:
|
176
|
+
return False
|
177
|
+
return InputPort(name, port, False)
|
178
|
+
|
155
179
|
def initOutPort(name: str) -> OutputPort | Literal[False]:
|
156
180
|
try:
|
157
181
|
port = open(name, "w", encoding="utf-8")
|
@@ -198,14 +222,20 @@ def make_standard_env() -> dict[str, Any]:
|
|
198
222
|
parms: list[str] = []
|
199
223
|
for item in node[1]:
|
200
224
|
if type(item) is not Sym:
|
201
|
-
raise MyError(f"{node[0]}: must be an identifier")
|
225
|
+
raise MyError(f"{node[0]}: must be an identifier, got: {item} {type(item)}")
|
202
226
|
|
203
227
|
parms.append(f"{item}")
|
204
228
|
|
205
229
|
return UserProc(env, "", parms, (), node[2:])
|
206
230
|
|
207
231
|
def syn_define(env: Env, node: Node) -> None:
|
232
|
+
if len(node) < 2:
|
233
|
+
raise MyError(f"{node[0]}: too few terms")
|
208
234
|
if len(node) < 3:
|
235
|
+
if type(node[1]) is Sym:
|
236
|
+
raise MyError(f"{node[0]}: what should `{node[1]}` be defined as?")
|
237
|
+
elif type(node[1]) is tuple and len(node[1]) > 0:
|
238
|
+
raise MyError(f"{node[0]}: function `{node[1][0]}` needs a body.")
|
209
239
|
raise MyError(f"{node[0]}: too few terms")
|
210
240
|
|
211
241
|
if type(node[1]) is tuple:
|
@@ -437,6 +467,11 @@ def make_standard_env() -> dict[str, Any]:
|
|
437
467
|
raise MyError(f"{node[0]}: Expected string? got: {print_str(num)}")
|
438
468
|
env[name] += num
|
439
469
|
|
470
|
+
def syn_while(env: Env, node: Node) -> None:
|
471
|
+
while my_eval(env, node[1]) == True:
|
472
|
+
for c in node[2:]:
|
473
|
+
my_eval(env, c)
|
474
|
+
|
440
475
|
def syn_for(env: Env, node: Node) -> None:
|
441
476
|
var, my_iter = check_for_syntax(env, node)
|
442
477
|
|
@@ -808,6 +843,12 @@ def make_standard_env() -> dict[str, Any]:
|
|
808
843
|
return f"{val.real}{join}{val.imag}i"
|
809
844
|
return f"{val}"
|
810
845
|
|
846
|
+
def string_to_number(val) -> float:
|
847
|
+
try:
|
848
|
+
return float(val)
|
849
|
+
except Exception:
|
850
|
+
raise MyError(f"failed to convert {val} to number")
|
851
|
+
|
811
852
|
def palet_join(v: Any, s: str) -> str:
|
812
853
|
try:
|
813
854
|
return s.join(v)
|
@@ -943,6 +984,7 @@ def make_standard_env() -> dict[str, Any]:
|
|
943
984
|
# loops
|
944
985
|
"for": Syntax(syn_for),
|
945
986
|
"for-items": Syntax(syn_for_items),
|
987
|
+
"while": Syntax(syn_while),
|
946
988
|
# contracts
|
947
989
|
"number?": is_num,
|
948
990
|
"real?": is_real,
|
@@ -1102,10 +1144,20 @@ def make_standard_env() -> dict[str, Any]:
|
|
1102
1144
|
),
|
1103
1145
|
),
|
1104
1146
|
# i/o
|
1147
|
+
"file-exists?": Proc("file-exists", os.path.isfile, (1, 1), is_str),
|
1148
|
+
"open-input-file": Proc("open-input-file", initInPort, (1, 1), is_str),
|
1149
|
+
"input-port?": (ip := Contract("input-port?", lambda v: type(v) is InputPort)),
|
1150
|
+
"read-line": Proc("read-line",
|
1151
|
+
lambda f: (r := f.port.readline(), None if r == "" else r.rstrip())[-1],
|
1152
|
+
(1, 1), ip),
|
1105
1153
|
"open-output-file": Proc("open-output-file", initOutPort, (1, 1), is_str),
|
1106
1154
|
"output-port?": (op := Contract("output-port?", lambda v: type(v) is OutputPort)),
|
1107
|
-
"
|
1108
|
-
"
|
1155
|
+
"port?": (port := Contract("port?", orc(ip, op))),
|
1156
|
+
"close-port": Proc("close-port", lambda p: p.close, (1, 1), port),
|
1157
|
+
"closed?": Proc("closed?", lambda o: o.closed, (1, 1), port),
|
1158
|
+
# subprocess
|
1159
|
+
"system": Proc("system", palet_system, (1, 1), is_str),
|
1160
|
+
"system*": Proc("system*", palet_system_star, (1, None), is_str),
|
1109
1161
|
# printing
|
1110
1162
|
"display": Proc("display",
|
1111
1163
|
lambda v, f=None: print(display_str(v), end="", file=f), (1, 2), any_p, op),
|
@@ -1119,10 +1171,9 @@ def make_standard_env() -> dict[str, Any]:
|
|
1119
1171
|
"assert": Proc("assert", palet_assert, (1, 2), any_p, orc(is_str, False)),
|
1120
1172
|
"error": Proc("error", raise_, (1, 1), is_str),
|
1121
1173
|
"sleep": Proc("sleep", sleep, (1, 1), is_int_or_float),
|
1122
|
-
"system": Proc("system", palet_system, (1, 1), is_str),
|
1123
|
-
"system*": Proc("system*", palet_system_star, (1, None), is_str),
|
1124
1174
|
# conversions
|
1125
1175
|
"number->string": Proc("number->string", number_to_string, (1, 1), is_num),
|
1176
|
+
"string->number": Proc("string->number", string_to_number, (1, 1), is_str),
|
1126
1177
|
"string->vector": Proc(
|
1127
1178
|
"string->vector", lambda s: [Char(c) for c in s], (1, 1), is_str
|
1128
1179
|
),
|
@@ -118,6 +118,10 @@ class Proc:
|
|
118
118
|
|
119
119
|
if kws is not None:
|
120
120
|
for key, val in kwargs.items():
|
121
|
+
if key not in kws:
|
122
|
+
raise MyError(
|
123
|
+
f"{self.name} got an unexpected keyword argument: {key}"
|
124
|
+
)
|
121
125
|
check = cont[-1] if kws[key] >= len(cont) else cont[kws[key]]
|
122
126
|
if not check_contract(check, val):
|
123
127
|
exp = f"{check}" if callable(check) else print_str(check)
|
@@ -54,12 +54,14 @@ class Env:
|
|
54
54
|
|
55
55
|
|
56
56
|
class Sym:
|
57
|
-
__slots__ = ("val", "hash")
|
57
|
+
__slots__ = ("val", "hash", "lineno", "column")
|
58
58
|
|
59
|
-
def __init__(self, val: str):
|
59
|
+
def __init__(self, val: str, lineno: int = -1, column: int = -1):
|
60
60
|
assert isinstance(val, str)
|
61
61
|
self.val = val
|
62
62
|
self.hash = hash(val)
|
63
|
+
self.lineno = lineno
|
64
|
+
self.column = column
|
63
65
|
|
64
66
|
def __str__(self) -> str:
|
65
67
|
return self.val
|
@@ -57,21 +57,6 @@ allowed_pix_fmt = {
|
|
57
57
|
}
|
58
58
|
|
59
59
|
|
60
|
-
def apply_anchor(x: int, y: int, w: int, h: int, anchor: str) -> tuple[int, int]:
|
61
|
-
if anchor == "ce":
|
62
|
-
x = (x * 2 - w) // 2
|
63
|
-
y = (y * 2 - h) // 2
|
64
|
-
if anchor == "tr":
|
65
|
-
x -= w
|
66
|
-
if anchor == "bl":
|
67
|
-
y -= h
|
68
|
-
if anchor == "br":
|
69
|
-
x -= w
|
70
|
-
y -= h
|
71
|
-
# Use 'tl' by default
|
72
|
-
return x, y
|
73
|
-
|
74
|
-
|
75
60
|
def make_solid(width: int, height: int, pix_fmt: str, bg: str) -> av.VideoFrame:
|
76
61
|
hex_color = bg.lstrip("#").upper()
|
77
62
|
rgb_color = tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4))
|
@@ -292,7 +277,7 @@ def render_av(
|
|
292
277
|
frame = graph.vpull()
|
293
278
|
elif isinstance(obj, TlRect):
|
294
279
|
graph = av.filter.Graph()
|
295
|
-
x, y =
|
280
|
+
x, y = obj.x, obj.y
|
296
281
|
graph.link_nodes(
|
297
282
|
graph.add_buffer(template=my_stream),
|
298
283
|
graph.add(
|
@@ -307,9 +292,7 @@ def render_av(
|
|
307
292
|
array = frame.to_ndarray(format="rgb24")
|
308
293
|
|
309
294
|
overlay_h, overlay_w, _ = img.shape
|
310
|
-
x_pos, y_pos =
|
311
|
-
obj.x, obj.y, overlay_w, overlay_h, obj.anchor
|
312
|
-
)
|
295
|
+
x_pos, y_pos = obj.x, obj.y
|
313
296
|
|
314
297
|
x_start = max(x_pos, 0)
|
315
298
|
y_start = max(y_pos, 0)
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
|
5
5
|
|
6
6
|
from auto_editor.lib.contracts import *
|
7
7
|
from auto_editor.utils.cmdkw import Required, pAttr, pAttrs
|
8
|
-
from auto_editor.utils.types import
|
8
|
+
from auto_editor.utils.types import color, natural, number, threshold
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
11
|
from collections.abc import Iterator
|
@@ -88,7 +88,6 @@ class TlImage:
|
|
88
88
|
y: int
|
89
89
|
width: int
|
90
90
|
opacity: float
|
91
|
-
anchor: str
|
92
91
|
|
93
92
|
def as_dict(self) -> dict:
|
94
93
|
return {
|
@@ -100,7 +99,6 @@ class TlImage:
|
|
100
99
|
"y": self.y,
|
101
100
|
"width": self.width,
|
102
101
|
"opacity": self.opacity,
|
103
|
-
"anchor": self.anchor,
|
104
102
|
}
|
105
103
|
|
106
104
|
|
@@ -112,7 +110,6 @@ class TlRect:
|
|
112
110
|
y: int
|
113
111
|
width: int
|
114
112
|
height: int
|
115
|
-
anchor: str
|
116
113
|
fill: str
|
117
114
|
|
118
115
|
def as_dict(self) -> dict:
|
@@ -124,7 +121,6 @@ class TlRect:
|
|
124
121
|
"y": self.y,
|
125
122
|
"width": self.width,
|
126
123
|
"height": self.height,
|
127
|
-
"anchor": self.anchor,
|
128
124
|
"fill": self.fill,
|
129
125
|
}
|
130
126
|
|
@@ -157,7 +153,6 @@ img_builder = pAttrs(
|
|
157
153
|
pAttr("y", Required, is_int, int),
|
158
154
|
pAttr("width", 0, is_nat, natural),
|
159
155
|
pAttr("opacity", 1, is_threshold, threshold),
|
160
|
-
pAttr("anchor", "ce", is_str, anchor),
|
161
156
|
)
|
162
157
|
rect_builder = pAttrs(
|
163
158
|
"rect",
|
@@ -167,7 +162,6 @@ rect_builder = pAttrs(
|
|
167
162
|
pAttr("y", Required, is_int, int),
|
168
163
|
pAttr("width", Required, is_int, int),
|
169
164
|
pAttr("height", Required, is_int, int),
|
170
|
-
pAttr("anchor", "ce", is_str, anchor),
|
171
165
|
pAttr("fill", "#c4c4c4", is_str, color),
|
172
166
|
)
|
173
167
|
visual_objects = {
|
@@ -177,8 +177,7 @@ def parse_with_palet(
|
|
177
177
|
def parse_method(
|
178
178
|
name: str, text: str, env: Env
|
179
179
|
) -> tuple[str, list[Any], dict[str, Any]]:
|
180
|
-
from auto_editor.lang.palet import Lexer, Parser
|
181
|
-
from auto_editor.lib.err import MyError
|
180
|
+
from auto_editor.lang.palet import Lexer, Parser
|
182
181
|
|
183
182
|
# Positional Arguments
|
184
183
|
# audio:0.04,0,6,3
|
@@ -197,18 +196,13 @@ def parse_method(
|
|
197
196
|
if "=" in arg:
|
198
197
|
key, val = arg.split("=", 1)
|
199
198
|
|
200
|
-
|
201
|
-
|
202
|
-
raise MyError("Results must be of length > 0")
|
203
|
-
|
204
|
-
kwargs[key] = results[-1]
|
199
|
+
result = Parser(Lexer(name, val)).expr()
|
200
|
+
kwargs[key] = result
|
205
201
|
allow_positional_args = False
|
206
202
|
|
207
203
|
elif allow_positional_args:
|
208
|
-
|
209
|
-
|
210
|
-
raise MyError("Results must be of length > 0")
|
211
|
-
args.append(results[-1])
|
204
|
+
result = Parser(Lexer(name, arg)).expr()
|
205
|
+
args.append(result)
|
212
206
|
else:
|
213
207
|
raise ParserError(f"{name} positional argument follows keyword argument.")
|
214
208
|
|
@@ -148,13 +148,6 @@ def time(val: str, tb: Fraction) -> int:
|
|
148
148
|
return int(num)
|
149
149
|
|
150
150
|
|
151
|
-
def anchor(val: str) -> str:
|
152
|
-
allowed = ("tl", "tr", "bl", "br", "ce")
|
153
|
-
if val not in allowed:
|
154
|
-
raise CoerceError(f"Anchor must be: {' '.join(allowed)}")
|
155
|
-
return val
|
156
|
-
|
157
|
-
|
158
151
|
def margin(val: str) -> tuple[str, str]:
|
159
152
|
vals = val.strip().split(",")
|
160
153
|
if len(vals) == 1:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: auto-editor
|
3
|
-
Version: 25.
|
3
|
+
Version: 25.2.0
|
4
4
|
Summary: Auto-Editor: Effort free video editing!
|
5
5
|
Author-email: WyattBlue <wyattblue@auto-editor.com>
|
6
6
|
License: Unlicense
|
@@ -8,11 +8,11 @@ Project-URL: Bug Tracker, https://github.com/WyattBlue/auto-editor/issues
|
|
8
8
|
Project-URL: Source Code, https://github.com/WyattBlue/auto-editor
|
9
9
|
Project-URL: homepage, https://auto-editor.com
|
10
10
|
Keywords: video,audio,media,editor,editing,processing,nonlinear,automatic,silence-detect,silence-removal,silence-speedup,motion-detection
|
11
|
-
Requires-Python:
|
11
|
+
Requires-Python: <3.14,>=3.10
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
|
-
Requires-Dist: numpy
|
15
|
-
Requires-Dist: pyav==
|
14
|
+
Requires-Dist: numpy<3.0,>=1.23.0
|
15
|
+
Requires-Dist: pyav==13.0.*
|
16
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>
|
@@ -7,10 +7,10 @@ description = "Auto-Editor: Effort free video editing!"
|
|
7
7
|
readme = "README.md"
|
8
8
|
license = { text = "Unlicense" }
|
9
9
|
authors = [{ name = "WyattBlue", email = "wyattblue@auto-editor.com" }]
|
10
|
-
requires-python = ">=3.10"
|
10
|
+
requires-python = ">=3.10,<3.14"
|
11
11
|
dependencies = [
|
12
|
-
"numpy>=1.23.0",
|
13
|
-
"pyav==
|
12
|
+
"numpy>=1.23.0,<3.0",
|
13
|
+
"pyav==13.0.*",
|
14
14
|
"ae-ffmpeg==1.2.*",
|
15
15
|
]
|
16
16
|
keywords = [
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "25.1.0"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|