18
18
import numpy as np
19
19
from numpy import logical_and , logical_not , logical_or , logical_xor
20
20
21
- from auto_editor .analyze import edit_method , mut_remove_large , mut_remove_small
21
+ from auto_editor .analyze import (
22
+ LevelError ,
23
+ mut_remove_large ,
24
+ mut_remove_small ,
25
+ to_threshold ,
26
+ )
22
27
from auto_editor .lib .contracts import *
23
28
from auto_editor .lib .data_structs import *
24
29
from auto_editor .lib .err import MyError
@@ -49,7 +54,6 @@ class ClosingError(MyError):
49
54
LPAREN , RPAREN , LBRAC , RBRAC , LCUR , RCUR , EOF = "(" , ")" , "[" , "]" , "{" , "}" , "EOF"
50
55
VAL , QUOTE , SEC , DB , DOT , VLIT = "VAL" , "QUOTE" , "SEC" , "DB" , "DOT" , "VLIT"
51
56
SEC_UNITS = ("s" , "sec" , "secs" , "second" , "seconds" )
52
- METHODS = ("audio:" , "motion:" , "subtitle:" )
53
57
brac_pairs = {LPAREN : RPAREN , LBRAC : RBRAC , LCUR : RCUR }
54
58
55
59
str_escape = {
@@ -315,7 +319,6 @@ def get_next_token(self) -> Token:
315
319
316
320
result = ""
317
321
has_illegal = False
318
- is_method = False
319
322
320
323
def normal () -> bool :
321
324
return (
@@ -334,21 +337,14 @@ def handle_strings() -> bool:
334
337
335
338
while normal ():
336
339
result += self .char
337
- if (result + ":" ) in METHODS :
338
- is_method = True
339
- normal = handle_strings
340
+ # if (result + ":") in METHODS:
341
+ # is_method = True
342
+ # normal = handle_strings
340
343
341
344
if self .char in "'`|\\ " :
342
345
has_illegal = True
343
346
self .advance ()
344
347
345
- if is_method :
346
- return Token (VAL , Method (result ))
347
-
348
- for method in METHODS :
349
- if result == method [:- 1 ]:
350
- return Token (VAL , Method (result ))
351
-
352
348
if self .char == "." : # handle `object.method` syntax
353
349
self .advance ()
354
350
return Token (DOT , (Sym (result ), self .get_next_token ()))
@@ -368,16 +364,6 @@ def handle_strings() -> bool:
368
364
###############################################################################
369
365
370
366
371
- @dataclass (slots = True )
372
- class Method :
373
- val : str
374
-
375
- def __str__ (self ) -> str :
376
- return f"#<method:{ self .val } >"
377
-
378
- __repr__ = __str__
379
-
380
-
381
367
class Parser :
382
368
def __init__ (self , lexer : Lexer ):
383
369
self .lexer = lexer
@@ -807,7 +793,7 @@ def __call__(self, *args: Any) -> Any:
807
793
808
794
809
795
@dataclass (slots = True )
810
- class KeywordProc :
796
+ class KeywordUserProc :
811
797
env : Env
812
798
name : str
813
799
parms : list [str ]
@@ -952,7 +938,7 @@ def syn_define(env: Env, node: Node) -> None:
952
938
raise MyError (f"{ node [0 ]} : must be an identifier" )
953
939
954
940
if kw_only :
955
- env [n ] = KeywordProc (env , n , parms , kparms , body , (len (parms ), None ))
941
+ env [n ] = KeywordUserProc (env , n , parms , kparms , body , (len (parms ), None ))
956
942
else :
957
943
env [n ] = UserProc (env , n , parms , (), body )
958
944
return None
@@ -1481,6 +1467,60 @@ def edit_all() -> np.ndarray:
1481
1467
return env ["@levels" ].all ()
1482
1468
1483
1469
1470
+ def edit_audio (
1471
+ threshold : float = 0.04 , stream : object = "all" , mincut : int = 6 , minclip : int = 3
1472
+ ) -> np .ndarray :
1473
+ if "@levels" not in env or "@filesetup" not in env :
1474
+ raise MyError ("Can't use `audio` if there's no input media" )
1475
+
1476
+ levels = env ["@levels" ]
1477
+ src = env ["@filesetup" ].src
1478
+
1479
+ stream_data : NDArray [np .bool_ ] | None = None
1480
+ if stream == "all" or stream == Sym ("all" ):
1481
+ stream_range = range (0 , len (src .audios ))
1482
+ else :
1483
+ assert isinstance (stream , int )
1484
+ stream_range = range (stream , stream + 1 )
1485
+
1486
+ try :
1487
+ for s in stream_range :
1488
+ audio_list = to_threshold (levels .audio (s ), threshold )
1489
+ if stream_data is None :
1490
+ stream_data = audio_list
1491
+ else :
1492
+ stream_data = boolop (stream_data , audio_list , np .logical_or )
1493
+ except LevelError as e :
1494
+ raise MyError (e )
1495
+
1496
+ if stream_data is not None :
1497
+ mut_remove_small (stream_data , minclip , replace = 1 , with_ = 0 )
1498
+ mut_remove_small (stream_data , mincut , replace = 0 , with_ = 1 )
1499
+
1500
+ return stream_data
1501
+
1502
+ return levels .all ()
1503
+
1504
+
1505
+ def edit_motion (
1506
+ threshold : float = 0.02 ,
1507
+ stream : int = 0 ,
1508
+ blur : int = 9 ,
1509
+ width : int = 400 ,
1510
+ ) -> np .ndarray :
1511
+ if "@levels" not in env :
1512
+ raise MyError ("Can't use `motion` if there's no input media" )
1513
+
1514
+ return to_threshold (env ["@levels" ].motion (stream , blur , width ), threshold )
1515
+
1516
+
1517
+ def edit_subtitle (pattern , stream = 0 , ignore_case = False , max_count = None ):
1518
+ if "@levels" not in env :
1519
+ raise MyError ("Can't use `subtitle` if there's no input media" )
1520
+
1521
+ return env ["@levels" ].subtitle (pattern , stream , ignore_case , max_count )
1522
+
1523
+
1484
1524
def my_eval (env : Env , node : object ) -> Any :
1485
1525
if type (node ) is Sym :
1486
1526
val = env .get (node .val )
@@ -1494,11 +1534,6 @@ def my_eval(env: Env, node: object) -> Any:
1494
1534
)
1495
1535
return val
1496
1536
1497
- if isinstance (node , Method ):
1498
- if "@filesetup" not in env :
1499
- raise MyError ("Can't use edit methods if there's no input files" )
1500
- return edit_method (node .val , env ["@filesetup" ], env )
1501
-
1502
1537
if type (node ) is list :
1503
1538
return [my_eval (env , item ) for item in node ]
1504
1539
@@ -1530,7 +1565,21 @@ def my_eval(env: Env, node: object) -> Any:
1530
1565
if type (oper ) is Syntax :
1531
1566
return oper (env , node )
1532
1567
1533
- return oper (* (my_eval (env , c ) for c in node [1 :]))
1568
+ i = 1
1569
+ args : list [Any ] = []
1570
+ kwargs : dict [str , Any ] = {}
1571
+ while i < len (node ):
1572
+ result = my_eval (env , node [i ])
1573
+ if type (result ) is Keyword :
1574
+ i += 1
1575
+ if i >= len (node ):
1576
+ raise MyError ("todo: write good error message" )
1577
+ kwargs [result .val ] = my_eval (env , node [i ])
1578
+ else :
1579
+ args .append (result )
1580
+ i += 1
1581
+
1582
+ return oper (* args , ** kwargs )
1534
1583
1535
1584
return node
1536
1585
@@ -1545,6 +1594,16 @@ def my_eval(env: Env, node: object) -> Any:
1545
1594
# edit procedures
1546
1595
"none" : Proc ("none" , edit_none , (0 , 0 )),
1547
1596
"all/e" : Proc ("all/e" , edit_all , (0 , 0 )),
1597
+ "audio" : Proc ("audio" , edit_audio , (0 , 4 ),
1598
+ is_threshold , orc (is_nat , Sym ("all" ), "all" ), is_nat ,
1599
+ {"threshold" : 0 , "stream" : 1 , "minclip" : 2 , "mincut" : 2 }
1600
+ ),
1601
+ "motion" : Proc ("motion" , edit_motion , (0 , 4 ),
1602
+ is_threshold , is_nat , is_nat , is_nat1
1603
+ ),
1604
+ "subtitle" : Proc ("subtitle" , edit_subtitle , (1 , 4 ),
1605
+ is_str , is_nat , is_bool , orc (is_nat , is_void )
1606
+ ),
1548
1607
# syntax
1549
1608
"lambda" : Syntax (syn_lambda ),
1550
1609
"λ" : Syntax (syn_lambda ),
0 commit comments