auto-editor 24.27.1__tar.gz → 24.29.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.
- {auto_editor-24.27.1/auto_editor.egg-info → auto_editor-24.29.1}/PKG-INFO +2 -5
- {auto_editor-24.27.1 → auto_editor-24.29.1}/README.md +0 -3
- auto_editor-24.29.1/auto_editor/__init__.py +2 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/analyze.py +30 -100
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lang/palet.py +29 -9
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lib/contracts.py +12 -6
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/make_layers.py +1 -19
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/levels.py +38 -24
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/repl.py +1 -1
- {auto_editor-24.27.1 → auto_editor-24.29.1/auto_editor.egg-info}/PKG-INFO +2 -5
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor.egg-info/SOURCES.txt +0 -3
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor.egg-info/requires.txt +1 -1
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor.egg-info/top_level.txt +0 -1
- {auto_editor-24.27.1 → auto_editor-24.29.1}/pyproject.toml +1 -1
- auto_editor-24.27.1/ae-ffmpeg/ae_ffmpeg/__init__.py +0 -16
- auto_editor-24.27.1/ae-ffmpeg/ae_ffmpeg/py.typed +0 -0
- auto_editor-24.27.1/ae-ffmpeg/setup.py +0 -65
- auto_editor-24.27.1/auto_editor/__init__.py +0 -2
- {auto_editor-24.27.1 → auto_editor-24.29.1}/LICENSE +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/__main__.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/edit.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/ffwrapper.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/formats/__init__.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/formats/fcp11.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/formats/fcp7.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/formats/json.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/formats/shotcut.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/formats/utils.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/help.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lang/__init__.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lang/json.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lang/libmath.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lib/__init__.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lib/data_structs.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/lib/err.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/output.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/preview.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/render/__init__.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/render/audio.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/render/subtitle.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/render/video.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/__init__.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/desc.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/info.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/palet.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/subdump.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/subcommands/test.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/timeline.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/__init__.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/bar.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/chunks.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/cmdkw.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/container.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/encoder.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/func.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/log.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/subtitle_tools.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/utils/types.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/validate_input.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/vanparse.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor/wavfile.py +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor.egg-info/dependency_links.txt +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/auto_editor.egg-info/entry_points.txt +0 -0
- {auto_editor-24.27.1 → auto_editor-24.29.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: auto-editor
|
3
|
-
Version: 24.
|
3
|
+
Version: 24.29.1
|
4
4
|
Summary: Auto-Editor: Effort free video editing!
|
5
5
|
Author-email: WyattBlue <wyattblue@auto-editor.com>
|
6
6
|
License: Unlicense
|
@@ -11,7 +11,7 @@ Keywords: video,audio,media,editor,editing,processing,nonlinear,automatic,silenc
|
|
11
11
|
Requires-Python: >=3.10
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
|
-
Requires-Dist: numpy>=1.
|
14
|
+
Requires-Dist: numpy>=1.23.0
|
15
15
|
Requires-Dist: pyav==12.2.*
|
16
16
|
Requires-Dist: ae-ffmpeg==1.2.*
|
17
17
|
|
@@ -182,6 +182,3 @@ auto-editor --margin --help
|
|
182
182
|
|
183
183
|
## Copyright
|
184
184
|
Auto-Editor is under the [Public Domain](https://github.com/WyattBlue/auto-editor/blob/master/LICENSE) and includes all directories besides the ones listed below. Auto-Editor was created by [these people.](https://auto-editor.com/blog/thank-you-early-testers)
|
185
|
-
|
186
|
-
ae-ffmpeg is under the [LGPLv3 License](https://github.com/WyattBlue/auto-editor/blob/master/ae-ffmpeg/LICENSE.txt). The ffmpeg and ffprobe programs were created by the FFmpeg team.
|
187
|
-
|
@@ -165,6 +165,3 @@ auto-editor --margin --help
|
|
165
165
|
|
166
166
|
## Copyright
|
167
167
|
Auto-Editor is under the [Public Domain](https://github.com/WyattBlue/auto-editor/blob/master/LICENSE) and includes all directories besides the ones listed below. Auto-Editor was created by [these people.](https://auto-editor.com/blog/thank-you-early-testers)
|
168
|
-
|
169
|
-
ae-ffmpeg is under the [LGPLv3 License](https://github.com/WyattBlue/auto-editor/blob/master/ae-ffmpeg/LICENSE.txt). The ffmpeg and ffprobe programs were created by the FFmpeg team.
|
170
|
-
|
@@ -9,22 +9,6 @@ from typing import TYPE_CHECKING
|
|
9
9
|
import numpy as np
|
10
10
|
|
11
11
|
from auto_editor import version
|
12
|
-
from auto_editor.lang.json import Lexer, Parser, dump
|
13
|
-
from auto_editor.lib.contracts import (
|
14
|
-
is_bool,
|
15
|
-
is_nat,
|
16
|
-
is_nat1,
|
17
|
-
is_str,
|
18
|
-
is_threshold,
|
19
|
-
is_void,
|
20
|
-
orc,
|
21
|
-
)
|
22
|
-
from auto_editor.lib.data_structs import Sym
|
23
|
-
from auto_editor.utils.cmdkw import (
|
24
|
-
Required,
|
25
|
-
pAttr,
|
26
|
-
pAttrs,
|
27
|
-
)
|
28
12
|
from auto_editor.utils.subtitle_tools import convert_ass_to_text
|
29
13
|
from auto_editor.wavfile import read
|
30
14
|
|
@@ -41,35 +25,6 @@ if TYPE_CHECKING:
|
|
41
25
|
from auto_editor.utils.log import Log
|
42
26
|
|
43
27
|
|
44
|
-
audio_builder = pAttrs(
|
45
|
-
"audio",
|
46
|
-
pAttr("threshold", 0.04, is_threshold),
|
47
|
-
pAttr("stream", 0, orc(is_nat, Sym("all"), "all")),
|
48
|
-
pAttr("mincut", 6, is_nat),
|
49
|
-
pAttr("minclip", 3, is_nat),
|
50
|
-
)
|
51
|
-
motion_builder = pAttrs(
|
52
|
-
"motion",
|
53
|
-
pAttr("threshold", 0.02, is_threshold),
|
54
|
-
pAttr("stream", 0, is_nat),
|
55
|
-
pAttr("blur", 9, is_nat),
|
56
|
-
pAttr("width", 400, is_nat1),
|
57
|
-
)
|
58
|
-
subtitle_builder = pAttrs(
|
59
|
-
"subtitle",
|
60
|
-
pAttr("pattern", Required, is_str),
|
61
|
-
pAttr("stream", 0, is_nat),
|
62
|
-
pAttr("ignore-case", False, is_bool),
|
63
|
-
pAttr("max-count", None, orc(is_nat, is_void)),
|
64
|
-
)
|
65
|
-
|
66
|
-
builder_map = {
|
67
|
-
"audio": audio_builder,
|
68
|
-
"motion": motion_builder,
|
69
|
-
"subtitle": subtitle_builder,
|
70
|
-
}
|
71
|
-
|
72
|
-
|
73
28
|
@dataclass(slots=True)
|
74
29
|
class FileSetup:
|
75
30
|
src: FileInfo
|
@@ -90,10 +45,6 @@ def link_nodes(*nodes: FilterContext) -> None:
|
|
90
45
|
c.link_to(n)
|
91
46
|
|
92
47
|
|
93
|
-
def to_threshold(arr: np.ndarray, t: int | float) -> NDArray[np.bool_]:
|
94
|
-
return np.fromiter((x >= t for x in arr), dtype=np.bool_)
|
95
|
-
|
96
|
-
|
97
48
|
def mut_remove_small(
|
98
49
|
arr: NDArray[np.bool_], lim: int, replace: int, with_: int
|
99
50
|
) -> None:
|
@@ -193,63 +144,43 @@ class Levels:
|
|
193
144
|
|
194
145
|
def read_cache(self, tag: str, obj: dict[str, Any]) -> None | np.ndarray:
|
195
146
|
workfile = os.path.join(
|
196
|
-
os.path.dirname(self.temp), f"ae-{version}", "cache.
|
147
|
+
os.path.dirname(self.temp), f"ae-{version}", "cache.npz"
|
197
148
|
)
|
198
149
|
|
199
150
|
try:
|
200
|
-
|
201
|
-
|
202
|
-
|
151
|
+
npzfile = np.load(workfile, allow_pickle=False)
|
152
|
+
except Exception as e:
|
153
|
+
self.log.debug(e)
|
203
154
|
return None
|
204
155
|
|
205
|
-
|
156
|
+
key = f"{self.src.path}:{obj_tag(tag, self.tb, obj)}"
|
157
|
+
if key not in npzfile.files:
|
206
158
|
return None
|
207
159
|
|
208
|
-
|
209
|
-
|
210
|
-
if key not in (root := cache[f"{self.src.path.resolve()}"]):
|
211
|
-
return None
|
212
|
-
|
213
|
-
return np.asarray(root[key]["arr"], dtype=root[key]["type"])
|
160
|
+
self.log.debug("Using cache")
|
161
|
+
return npzfile[key]
|
214
162
|
|
215
163
|
def cache(self, tag: str, obj: dict[str, Any], arr: np.ndarray) -> np.ndarray:
|
216
164
|
workdur = os.path.join(os.path.dirname(self.temp), f"ae-{version}")
|
217
|
-
workfile = os.path.join(workdur, "cache.json")
|
218
165
|
if not os.path.exists(workdur):
|
219
166
|
os.mkdir(workdur)
|
220
167
|
|
221
|
-
|
222
|
-
|
223
|
-
try:
|
224
|
-
with open(workfile, encoding="utf-8") as file:
|
225
|
-
json_object = Parser(Lexer(workfile, file)).expr()
|
226
|
-
except Exception:
|
227
|
-
json_object = {}
|
228
|
-
|
229
|
-
entry = {"type": str(arr.dtype), "arr": arr.tolist()}
|
230
|
-
src_key = f"{self.src.path}"
|
231
|
-
|
232
|
-
if src_key in json_object:
|
233
|
-
json_object[src_key][key] = entry
|
234
|
-
else:
|
235
|
-
json_object[src_key] = {key: entry}
|
236
|
-
|
237
|
-
with open(os.path.join(workdur, "cache.json"), "w", encoding="utf-8") as file:
|
238
|
-
dump(json_object, file)
|
168
|
+
tag = obj_tag(tag, self.tb, obj)
|
169
|
+
np.savez(os.path.join(workdur, "cache.npz"), **{f"{self.src.path}:{tag}": arr})
|
239
170
|
|
240
171
|
return arr
|
241
172
|
|
242
|
-
def audio(self,
|
243
|
-
if
|
244
|
-
raise LevelError(f"audio: audio stream '{
|
173
|
+
def audio(self, stream: int) -> NDArray[np.float64]:
|
174
|
+
if stream > len(self.src.audios) - 1:
|
175
|
+
raise LevelError(f"audio: audio stream '{stream}' does not exist.")
|
245
176
|
|
246
|
-
if (arr := self.read_cache("audio", {"stream":
|
177
|
+
if (arr := self.read_cache("audio", {"stream": stream})) is not None:
|
247
178
|
return arr
|
248
179
|
|
249
|
-
sr, samples = read(self.ensure.audio(self.src,
|
180
|
+
sr, samples = read(self.ensure.audio(self.src, stream))
|
250
181
|
|
251
182
|
if len(samples) == 0:
|
252
|
-
raise LevelError(f"audio: stream '{
|
183
|
+
raise LevelError(f"audio: stream '{stream}' has no samples.")
|
253
184
|
|
254
185
|
def get_max_volume(s: np.ndarray) -> float:
|
255
186
|
return max(float(np.max(s)), -float(np.min(s)))
|
@@ -262,7 +193,7 @@ class Levels:
|
|
262
193
|
|
263
194
|
if samp_per_ticks < 1:
|
264
195
|
self.log.error(
|
265
|
-
f"audio: stream '{
|
196
|
+
f"audio: stream '{stream}'\n Samplerate ({sr}) must be greater than "
|
266
197
|
f"or equal to timebase ({self.tb})\n"
|
267
198
|
" Try `-fps 30` and/or `--sample-rate 48000`"
|
268
199
|
)
|
@@ -289,11 +220,11 @@ class Levels:
|
|
289
220
|
threshold_list[i] = get_max_volume(samples[start:end]) / max_volume
|
290
221
|
|
291
222
|
self.bar.end()
|
292
|
-
return self.cache("audio", {"stream":
|
223
|
+
return self.cache("audio", {"stream": stream}, threshold_list)
|
293
224
|
|
294
225
|
def subtitle(
|
295
226
|
self,
|
296
|
-
|
227
|
+
pattern: str,
|
297
228
|
stream: int,
|
298
229
|
ignore_case: bool,
|
299
230
|
max_count: int | None,
|
@@ -303,8 +234,7 @@ class Levels:
|
|
303
234
|
|
304
235
|
try:
|
305
236
|
flags = re.IGNORECASE if ignore_case else 0
|
306
|
-
|
307
|
-
del patterns # make sure we don't accidentally use it
|
237
|
+
re_pattern = re.compile(pattern, flags)
|
308
238
|
except re.error as e:
|
309
239
|
self.log.error(e)
|
310
240
|
|
@@ -362,7 +292,7 @@ class Levels:
|
|
362
292
|
else:
|
363
293
|
continue
|
364
294
|
|
365
|
-
if line and re.search(
|
295
|
+
if line and re.search(re_pattern, line):
|
366
296
|
result[san_start:san_end] = 1
|
367
297
|
count += 1
|
368
298
|
|
@@ -370,22 +300,22 @@ class Levels:
|
|
370
300
|
|
371
301
|
return result
|
372
302
|
|
373
|
-
def motion(self,
|
303
|
+
def motion(self, stream: int, blur: int, width: int) -> NDArray[np.float64]:
|
374
304
|
import av
|
375
305
|
|
376
|
-
if
|
377
|
-
raise LevelError(f"motion: video stream '{
|
306
|
+
if stream >= len(self.src.videos):
|
307
|
+
raise LevelError(f"motion: video stream '{stream}' does not exist.")
|
378
308
|
|
379
|
-
mobj = {"stream":
|
309
|
+
mobj = {"stream": stream, "width": width, "blur": blur}
|
380
310
|
if (arr := self.read_cache("motion", mobj)) is not None:
|
381
311
|
return arr
|
382
312
|
|
383
313
|
container = av.open(f"{self.src.path}", "r")
|
384
314
|
|
385
|
-
|
386
|
-
|
315
|
+
video = container.streams.video[stream]
|
316
|
+
video.thread_type = "AUTO"
|
387
317
|
|
388
|
-
inaccurate_dur = 1 if
|
318
|
+
inaccurate_dur = 1 if video.duration is None else video.duration
|
389
319
|
self.bar.start(inaccurate_dur, "Analyzing motion")
|
390
320
|
|
391
321
|
prev_frame = None
|
@@ -395,7 +325,7 @@ class Levels:
|
|
395
325
|
|
396
326
|
graph = av.filter.Graph()
|
397
327
|
link_nodes(
|
398
|
-
graph.add_buffer(template=
|
328
|
+
graph.add_buffer(template=video),
|
399
329
|
graph.add("scale", f"{width}:-1"),
|
400
330
|
graph.add("format", "gray"),
|
401
331
|
graph.add("gblur", f"sigma={blur}"),
|
@@ -405,7 +335,7 @@ class Levels:
|
|
405
335
|
|
406
336
|
threshold_list = np.zeros((1024), dtype=np.float64)
|
407
337
|
|
408
|
-
for unframe in container.decode(
|
338
|
+
for unframe in container.decode(video):
|
409
339
|
graph.push(unframe)
|
410
340
|
frame = graph.pull()
|
411
341
|
|
@@ -18,12 +18,7 @@ from typing import TYPE_CHECKING
|
|
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
|
22
|
-
LevelError,
|
23
|
-
mut_remove_large,
|
24
|
-
mut_remove_small,
|
25
|
-
to_threshold,
|
26
|
-
)
|
21
|
+
from auto_editor.analyze import LevelError, mut_remove_large, mut_remove_small
|
27
22
|
from auto_editor.lib.contracts import *
|
28
23
|
from auto_editor.lib.data_structs import *
|
29
24
|
from auto_editor.lib.err import MyError
|
@@ -690,6 +685,9 @@ def palet_map(proc: Proc, seq: Any) -> Any:
|
|
690
685
|
return Quoted(tuple(map(proc, seq.val)))
|
691
686
|
if isinstance(seq, list | range):
|
692
687
|
return list(map(proc, seq))
|
688
|
+
elif isinstance(seq, np.ndarray):
|
689
|
+
vectorized_proc = np.vectorize(proc)
|
690
|
+
return vectorized_proc(seq)
|
693
691
|
return proc(seq)
|
694
692
|
|
695
693
|
|
@@ -1469,6 +1467,26 @@ def edit_all() -> np.ndarray:
|
|
1469
1467
|
return env["@levels"].all()
|
1470
1468
|
|
1471
1469
|
|
1470
|
+
def audio_levels(stream: int) -> np.ndarray:
|
1471
|
+
if "@levels" not in env:
|
1472
|
+
raise MyError("Can't use `audio` if there's no input media")
|
1473
|
+
|
1474
|
+
try:
|
1475
|
+
return env["@levels"].audio(stream)
|
1476
|
+
except LevelError as e:
|
1477
|
+
raise MyError(e)
|
1478
|
+
|
1479
|
+
|
1480
|
+
def motion_levels(stream: int, blur: int = 9, width: int = 400) -> np.ndarray:
|
1481
|
+
if "@levels" not in env:
|
1482
|
+
raise MyError("Can't use `motion` if there's no input media")
|
1483
|
+
|
1484
|
+
try:
|
1485
|
+
return env["@levels"].motion(stream, blur, width)
|
1486
|
+
except LevelError as e:
|
1487
|
+
raise MyError(e)
|
1488
|
+
|
1489
|
+
|
1472
1490
|
def edit_audio(
|
1473
1491
|
threshold: float = 0.04,
|
1474
1492
|
stream: object = Sym("all"),
|
@@ -1491,7 +1509,7 @@ def edit_audio(
|
|
1491
1509
|
|
1492
1510
|
try:
|
1493
1511
|
for s in stream_range:
|
1494
|
-
audio_list =
|
1512
|
+
audio_list = levels.audio(s) >= threshold
|
1495
1513
|
if stream_data is None:
|
1496
1514
|
stream_data = audio_list
|
1497
1515
|
else:
|
@@ -1521,7 +1539,7 @@ def edit_motion(
|
|
1521
1539
|
levels = env["@levels"]
|
1522
1540
|
strict = env["@filesetup"].strict
|
1523
1541
|
try:
|
1524
|
-
return
|
1542
|
+
return levels.motion(stream, blur, width) >= threshold
|
1525
1543
|
except LevelError as e:
|
1526
1544
|
return raise_(e) if strict else levels.all()
|
1527
1545
|
|
@@ -1582,7 +1600,7 @@ def my_eval(env: Env, node: object) -> Any:
|
|
1582
1600
|
return ref(oper, my_eval(env, node[1]))
|
1583
1601
|
|
1584
1602
|
raise MyError(
|
1585
|
-
f"
|
1603
|
+
f"{print_str(oper)} is not a function. Tried to run with args: {print_str(node[1:])}"
|
1586
1604
|
)
|
1587
1605
|
|
1588
1606
|
if type(oper) is Syntax:
|
@@ -1617,10 +1635,12 @@ env.update({
|
|
1617
1635
|
# edit procedures
|
1618
1636
|
"none": Proc("none", edit_none, (0, 0)),
|
1619
1637
|
"all/e": Proc("all/e", edit_all, (0, 0)),
|
1638
|
+
"audio-levels": Proc("audio-levels", audio_levels, (1, 1), is_nat),
|
1620
1639
|
"audio": Proc("audio", edit_audio, (0, 4),
|
1621
1640
|
is_threshold, orc(is_nat, Sym("all")), is_nat,
|
1622
1641
|
{"threshold": 0, "stream": 1, "minclip": 2, "mincut": 2}
|
1623
1642
|
),
|
1643
|
+
"motion-levels": Proc("motion-levels", motion_levels, (1, 3), is_nat, is_nat1, {"blur": 1, "width": 2}),
|
1624
1644
|
"motion": Proc("motion", edit_motion, (0, 4),
|
1625
1645
|
is_threshold, is_nat, is_nat1,
|
1626
1646
|
{"threshold": 0, "stream": 1, "blur": 1, "width": 2}
|
@@ -5,6 +5,8 @@ from dataclasses import dataclass
|
|
5
5
|
from fractions import Fraction
|
6
6
|
from typing import Any
|
7
7
|
|
8
|
+
from numpy import float64
|
9
|
+
|
8
10
|
from .data_structs import Sym, print_str
|
9
11
|
from .err import MyError
|
10
12
|
|
@@ -41,7 +43,7 @@ def check_contract(c: object, val: object) -> bool:
|
|
41
43
|
return val is True
|
42
44
|
if c is False:
|
43
45
|
return val is False
|
44
|
-
if type(c) in (int, float, Fraction, complex, str, Sym):
|
46
|
+
if type(c) in (int, float, float64, Fraction, complex, str, Sym):
|
45
47
|
return val == c
|
46
48
|
raise MyError(f"Invalid contract, got: {print_str(c)}")
|
47
49
|
|
@@ -163,17 +165,21 @@ is_int = Contract("int?", lambda v: type(v) is int)
|
|
163
165
|
is_nat = Contract("nat?", lambda v: type(v) is int and v > -1)
|
164
166
|
is_nat1 = Contract("nat1?", lambda v: type(v) is int and v > 0)
|
165
167
|
int_not_zero = Contract("(or/c (not/c 0) int?)", lambda v: v != 0 and is_int(v))
|
166
|
-
is_num = Contract(
|
167
|
-
|
168
|
-
|
168
|
+
is_num = Contract(
|
169
|
+
"number?", lambda v: type(v) in (int, float, float64, Fraction, complex)
|
170
|
+
)
|
171
|
+
is_real = Contract("real?", lambda v: type(v) in (int, float, float64, Fraction))
|
172
|
+
is_float = Contract("float?", lambda v: type(v) in (float, float64))
|
169
173
|
is_frac = Contract("frac?", lambda v: type(v) is Fraction)
|
170
174
|
is_str = Contract("string?", lambda v: type(v) is str)
|
171
175
|
any_p = Contract("any", lambda v: True)
|
172
176
|
is_void = Contract("void?", lambda v: v is None)
|
173
|
-
is_int_or_float = Contract(
|
177
|
+
is_int_or_float = Contract(
|
178
|
+
"(or/c int? float?)", lambda v: type(v) in (int, float, float64)
|
179
|
+
)
|
174
180
|
is_threshold = Contract(
|
175
181
|
"threshold?",
|
176
|
-
lambda v: type(v) in (int, float) and v >= 0 and v <= 1, # type: ignore
|
182
|
+
lambda v: type(v) in (int, float, float64) and v >= 0 and v <= 1, # type: ignore
|
177
183
|
)
|
178
184
|
is_proc = Contract("procedure?", lambda v: isinstance(v, Proc | Contract))
|
179
185
|
|
@@ -296,22 +296,4 @@ def make_timeline(
|
|
296
296
|
else:
|
297
297
|
v1_compatiable = None
|
298
298
|
|
299
|
-
|
300
|
-
|
301
|
-
# Additional monotonic check, o(n^2) time complexity so disable by default.
|
302
|
-
|
303
|
-
# if len(sources) != 1:
|
304
|
-
# return tl
|
305
|
-
|
306
|
-
# last_i = 0
|
307
|
-
# for index in range(tl.end):
|
308
|
-
# for layer in tl.v:
|
309
|
-
# for lobj in layer:
|
310
|
-
# if index >= lobj.start and index < (lobj.start + lobj.dur):
|
311
|
-
# _i = round((lobj.offset + index - lobj.start) * lobj.speed)
|
312
|
-
# if (_i < last_i):
|
313
|
-
# print(_i, last_i)
|
314
|
-
# raise ValueError("not monotonic")
|
315
|
-
# last_i = _i
|
316
|
-
|
317
|
-
return tl
|
299
|
+
return v3(inp, tb, sr, res, args.background, vtl, atl, v1_compatiable)
|
@@ -6,12 +6,19 @@ from typing import TYPE_CHECKING
|
|
6
6
|
|
7
7
|
import numpy as np
|
8
8
|
|
9
|
-
from auto_editor.analyze import LevelError, Levels
|
9
|
+
from auto_editor.analyze import LevelError, Levels
|
10
10
|
from auto_editor.ffwrapper import FFmpeg, initFileInfo
|
11
11
|
from auto_editor.lang.palet import env
|
12
|
+
from auto_editor.lib.contracts import is_bool, is_nat, is_nat1, is_str, is_void, orc
|
12
13
|
from auto_editor.output import Ensure
|
13
14
|
from auto_editor.utils.bar import Bar
|
14
|
-
from auto_editor.utils.cmdkw import
|
15
|
+
from auto_editor.utils.cmdkw import (
|
16
|
+
ParserError,
|
17
|
+
Required,
|
18
|
+
parse_with_palet,
|
19
|
+
pAttr,
|
20
|
+
pAttrs,
|
21
|
+
)
|
15
22
|
from auto_editor.utils.func import setup_tempdir
|
16
23
|
from auto_editor.utils.log import Log
|
17
24
|
from auto_editor.utils.types import frame_rate
|
@@ -57,6 +64,8 @@ def levels_options(parser: ArgumentParser) -> ArgumentParser:
|
|
57
64
|
|
58
65
|
|
59
66
|
def print_arr(arr: NDArray) -> None:
|
67
|
+
print("")
|
68
|
+
print("@start")
|
60
69
|
if arr.dtype == np.float64:
|
61
70
|
for a in arr:
|
62
71
|
sys.stdout.write(f"{a:.20f}\n")
|
@@ -66,6 +75,8 @@ def print_arr(arr: NDArray) -> None:
|
|
66
75
|
else:
|
67
76
|
for a in arr:
|
68
77
|
sys.stdout.write(f"{a}\n")
|
78
|
+
sys.stdout.flush()
|
79
|
+
print("")
|
69
80
|
|
70
81
|
|
71
82
|
def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
@@ -92,37 +103,42 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
92
103
|
else:
|
93
104
|
method, attrs = args.edit, ""
|
94
105
|
|
95
|
-
|
96
|
-
|
97
|
-
|
106
|
+
audio_builder = pAttrs("audio", pAttr("stream", 0, is_nat))
|
107
|
+
motion_builder = pAttrs(
|
108
|
+
"motion",
|
109
|
+
pAttr("stream", 0, is_nat),
|
110
|
+
pAttr("blur", 9, is_nat),
|
111
|
+
pAttr("width", 400, is_nat1),
|
112
|
+
)
|
113
|
+
subtitle_builder = pAttrs(
|
114
|
+
"subtitle",
|
115
|
+
pAttr("pattern", Required, is_str),
|
116
|
+
pAttr("stream", 0, is_nat),
|
117
|
+
pAttr("ignore-case", False, is_bool),
|
118
|
+
pAttr("max-count", None, orc(is_nat, is_void)),
|
119
|
+
)
|
98
120
|
|
99
|
-
|
121
|
+
builder_map = {
|
122
|
+
"audio": audio_builder,
|
123
|
+
"motion": motion_builder,
|
124
|
+
"subtitle": subtitle_builder,
|
125
|
+
}
|
100
126
|
|
127
|
+
for src in sources:
|
101
128
|
if method in builder_map:
|
102
|
-
builder = builder_map[method]
|
103
|
-
|
104
129
|
try:
|
105
|
-
obj = parse_with_palet(attrs,
|
130
|
+
obj = parse_with_palet(attrs, builder_map[method], env)
|
106
131
|
except ParserError as e:
|
107
132
|
log.error(e)
|
108
133
|
|
109
|
-
|
110
|
-
del obj["threshold"]
|
111
|
-
|
134
|
+
levels = Levels(ensure, src, tb, bar, temp, log)
|
112
135
|
try:
|
113
136
|
if method == "audio":
|
114
|
-
print_arr(levels.audio(obj
|
137
|
+
print_arr(levels.audio(**obj))
|
115
138
|
elif method == "motion":
|
116
|
-
print_arr(levels.motion(obj
|
139
|
+
print_arr(levels.motion(**obj))
|
117
140
|
elif method == "subtitle":
|
118
|
-
print_arr(
|
119
|
-
levels.subtitle(
|
120
|
-
obj["pattern"],
|
121
|
-
obj["stream"],
|
122
|
-
obj["ignore_case"],
|
123
|
-
obj["max_count"],
|
124
|
-
)
|
125
|
-
)
|
141
|
+
print_arr(levels.subtitle(**obj))
|
126
142
|
elif method == "none":
|
127
143
|
print_arr(levels.none())
|
128
144
|
elif method == "all/e":
|
@@ -132,8 +148,6 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
132
148
|
except LevelError as e:
|
133
149
|
log.error(e)
|
134
150
|
|
135
|
-
sys.stdout.flush()
|
136
|
-
print("")
|
137
151
|
log.cleanup()
|
138
152
|
|
139
153
|
|
@@ -73,7 +73,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
73
73
|
sources = [initFileInfo(path, log) for path in args.input]
|
74
74
|
src = sources[0]
|
75
75
|
tb = src.get_fps() if args.timebase is None else args.timebase
|
76
|
-
bar = Bar("
|
76
|
+
bar = Bar("modern")
|
77
77
|
ensure = Ensure(ffmpeg, bar, src.get_sr(), temp, log)
|
78
78
|
env["timebase"] = tb
|
79
79
|
env["@levels"] = Levels(ensure, src, tb, bar, temp, log)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: auto-editor
|
3
|
-
Version: 24.
|
3
|
+
Version: 24.29.1
|
4
4
|
Summary: Auto-Editor: Effort free video editing!
|
5
5
|
Author-email: WyattBlue <wyattblue@auto-editor.com>
|
6
6
|
License: Unlicense
|
@@ -11,7 +11,7 @@ Keywords: video,audio,media,editor,editing,processing,nonlinear,automatic,silenc
|
|
11
11
|
Requires-Python: >=3.10
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
|
-
Requires-Dist: numpy>=1.
|
14
|
+
Requires-Dist: numpy>=1.23.0
|
15
15
|
Requires-Dist: pyav==12.2.*
|
16
16
|
Requires-Dist: ae-ffmpeg==1.2.*
|
17
17
|
|
@@ -182,6 +182,3 @@ auto-editor --margin --help
|
|
182
182
|
|
183
183
|
## Copyright
|
184
184
|
Auto-Editor is under the [Public Domain](https://github.com/WyattBlue/auto-editor/blob/master/LICENSE) and includes all directories besides the ones listed below. Auto-Editor was created by [these people.](https://auto-editor.com/blog/thank-you-early-testers)
|
185
|
-
|
186
|
-
ae-ffmpeg is under the [LGPLv3 License](https://github.com/WyattBlue/auto-editor/blob/master/ae-ffmpeg/LICENSE.txt). The ffmpeg and ffprobe programs were created by the FFmpeg team.
|
187
|
-
|
@@ -1,16 +0,0 @@
|
|
1
|
-
__version__ = "1.2.0"
|
2
|
-
|
3
|
-
import os.path
|
4
|
-
from platform import machine, system
|
5
|
-
|
6
|
-
|
7
|
-
def get_path() -> str:
|
8
|
-
_os = system()
|
9
|
-
_arch = machine().lower()
|
10
|
-
_interdir = _os if _os != "Darwin" else f"{_os}-{_arch}"
|
11
|
-
program = "ffmpeg.exe" if _os == "Windows" else "ffmpeg"
|
12
|
-
|
13
|
-
dirpath = os.path.dirname(os.path.realpath(__file__))
|
14
|
-
file_path = os.path.join(dirpath, _interdir, program)
|
15
|
-
|
16
|
-
return file_path if os.path.isfile(file_path) else "ffmpeg"
|
File without changes
|
@@ -1,65 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
|
3
|
-
from setuptools import find_packages, setup
|
4
|
-
|
5
|
-
|
6
|
-
def pip_version():
|
7
|
-
with open("ae_ffmpeg/__init__.py") as f:
|
8
|
-
version_content = f.read()
|
9
|
-
|
10
|
-
version_match = re.search(
|
11
|
-
r"^__version__ = ['\"]([^'\"]*)['\"]", version_content, re.M
|
12
|
-
)
|
13
|
-
|
14
|
-
if version_match:
|
15
|
-
return version_match.group(1)
|
16
|
-
|
17
|
-
raise ValueError("Unable to find version string.")
|
18
|
-
|
19
|
-
|
20
|
-
with open("README.md") as f:
|
21
|
-
long_description = f.read()
|
22
|
-
|
23
|
-
setup(
|
24
|
-
name="ae-ffmpeg",
|
25
|
-
version=pip_version(),
|
26
|
-
description="Static FFmpeg binaries for Auto-Editor",
|
27
|
-
long_description=long_description,
|
28
|
-
long_description_content_type="text/markdown",
|
29
|
-
license="LGPLv3",
|
30
|
-
url="https://auto-editor.com",
|
31
|
-
project_urls={
|
32
|
-
"Bug Tracker": "https://github.com/WyattBlue/auto-editor/issues",
|
33
|
-
"Source Code": "https://github.com/WyattBlue/auto-editor",
|
34
|
-
},
|
35
|
-
author="WyattBlue",
|
36
|
-
author_email="wyattblue@auto-editor.com",
|
37
|
-
keywords="video audio media",
|
38
|
-
packages=find_packages(),
|
39
|
-
package_data={
|
40
|
-
"ae_ffmpeg": [
|
41
|
-
"LICENSE.txt",
|
42
|
-
"Windows/ffmpeg.exe",
|
43
|
-
"Windows/ffprobe.exe",
|
44
|
-
"Windows/libopenh264.dll",
|
45
|
-
"Darwin-x86_64/ffmpeg",
|
46
|
-
"Darwin-x86_64/ffprobe",
|
47
|
-
"Darwin-arm64/ffmpeg",
|
48
|
-
"Darwin-arm64/ffprobe",
|
49
|
-
"py.typed",
|
50
|
-
],
|
51
|
-
},
|
52
|
-
include_package_data=True,
|
53
|
-
zip_safe=False,
|
54
|
-
python_requires=">=3.8",
|
55
|
-
classifiers=[
|
56
|
-
"Topic :: Multimedia :: Sound/Audio",
|
57
|
-
"Topic :: Multimedia :: Video",
|
58
|
-
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
59
|
-
"Intended Audience :: Developers",
|
60
|
-
"Operating System :: MacOS :: MacOS X",
|
61
|
-
"Operating System :: Microsoft :: Windows",
|
62
|
-
"Development Status :: 5 - Production/Stable",
|
63
|
-
"Programming Language :: Python :: 3",
|
64
|
-
],
|
65
|
-
)
|
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
|