auto-editor 24.31.1__py3-none-any.whl → 25.0.1__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 -2
- auto_editor/__main__.py +15 -20
- auto_editor/analyze.py +26 -30
- auto_editor/edit.py +8 -12
- auto_editor/ffwrapper.py +4 -2
- auto_editor/lang/palet.py +5 -7
- auto_editor/lib/contracts.py +5 -2
- auto_editor/make_layers.py +28 -47
- auto_editor/output.py +3 -5
- auto_editor/preview.py +2 -2
- auto_editor/render/audio.py +2 -1
- auto_editor/render/video.py +2 -7
- auto_editor/subcommands/info.py +2 -2
- auto_editor/subcommands/levels.py +4 -9
- auto_editor/subcommands/repl.py +4 -7
- auto_editor/subcommands/test.py +40 -33
- auto_editor/timeline.py +10 -12
- auto_editor/utils/container.py +6 -0
- auto_editor/utils/func.py +10 -26
- auto_editor/utils/log.py +35 -8
- auto_editor/utils/types.py +2 -1
- auto_editor/validate_input.py +12 -7
- auto_editor/vanparse.py +1 -2
- {auto_editor-24.31.1.dist-info → auto_editor-25.0.1.dist-info}/METADATA +1 -1
- auto_editor-25.0.1.dist-info/RECORD +55 -0
- {auto_editor-24.31.1.dist-info → auto_editor-25.0.1.dist-info}/WHEEL +1 -1
- auto_editor-24.31.1.dist-info/RECORD +0 -55
- {auto_editor-24.31.1.dist-info → auto_editor-25.0.1.dist-info}/LICENSE +0 -0
- {auto_editor-24.31.1.dist-info → auto_editor-25.0.1.dist-info}/entry_points.txt +0 -0
- {auto_editor-24.31.1.dist-info → auto_editor-25.0.1.dist-info}/top_level.txt +0 -0
@@ -19,7 +19,6 @@ from auto_editor.utils.cmdkw import (
|
|
19
19
|
pAttr,
|
20
20
|
pAttrs,
|
21
21
|
)
|
22
|
-
from auto_editor.utils.func import setup_tempdir
|
23
22
|
from auto_editor.utils.log import Log
|
24
23
|
from auto_editor.utils.types import frame_rate
|
25
24
|
from auto_editor.vanparse import ArgumentParser
|
@@ -72,14 +71,11 @@ def print_arr(arr: NDArray) -> None:
|
|
72
71
|
print("")
|
73
72
|
|
74
73
|
|
75
|
-
def print_arr_gen(arr: Iterator[
|
74
|
+
def print_arr_gen(arr: Iterator[float | np.float32]) -> None:
|
76
75
|
print("")
|
77
76
|
print("@start")
|
78
77
|
for a in arr:
|
79
|
-
|
80
|
-
print(f"{a:.20f}")
|
81
|
-
else:
|
82
|
-
print(a)
|
78
|
+
print(f"{a:.20f}")
|
83
79
|
print("")
|
84
80
|
|
85
81
|
|
@@ -88,8 +84,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
88
84
|
args = parser.parse_args(LevelArgs, sys_args)
|
89
85
|
|
90
86
|
bar = Bar("none")
|
91
|
-
|
92
|
-
log = Log(quiet=True, temp=temp)
|
87
|
+
log = Log(quiet=True)
|
93
88
|
|
94
89
|
sources = [initFileInfo(path, log) for path in args.input]
|
95
90
|
if len(sources) < 1:
|
@@ -132,7 +127,7 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
132
127
|
except ParserError as e:
|
133
128
|
log.error(e)
|
134
129
|
|
135
|
-
levels = Levels(src, tb, bar,
|
130
|
+
levels = Levels(src, tb, bar, False, log, strict=True)
|
136
131
|
try:
|
137
132
|
if method == "audio":
|
138
133
|
print_arr_gen(iter_audio(src, tb, **obj))
|
auto_editor/subcommands/repl.py
CHANGED
@@ -5,13 +5,12 @@ from dataclasses import dataclass, field
|
|
5
5
|
from fractions import Fraction
|
6
6
|
|
7
7
|
import auto_editor
|
8
|
-
from auto_editor.analyze import
|
8
|
+
from auto_editor.analyze import Levels
|
9
9
|
from auto_editor.ffwrapper import initFileInfo
|
10
10
|
from auto_editor.lang.palet import ClosingError, Lexer, Parser, env, interpret
|
11
11
|
from auto_editor.lib.data_structs import print_str
|
12
12
|
from auto_editor.lib.err import MyError
|
13
13
|
from auto_editor.utils.bar import Bar
|
14
|
-
from auto_editor.utils.func import setup_tempdir
|
15
14
|
from auto_editor.utils.log import Log
|
16
15
|
from auto_editor.utils.types import frame_rate
|
17
16
|
from auto_editor.vanparse import ArgumentParser
|
@@ -59,18 +58,16 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
59
58
|
args = repl_options(ArgumentParser(None)).parse_args(REPL_Args, sys_args)
|
60
59
|
|
61
60
|
if args.input:
|
62
|
-
|
63
|
-
log = Log(quiet=True, temp=temp)
|
61
|
+
log = Log(quiet=True, temp_dir=args.temp_dir)
|
64
62
|
strict = len(args.input) < 2
|
65
63
|
sources = [initFileInfo(path, log) for path in args.input]
|
66
64
|
src = sources[0]
|
67
65
|
tb = src.get_fps() if args.timebase is None else args.timebase
|
68
66
|
bar = Bar("modern")
|
69
67
|
env["timebase"] = tb
|
70
|
-
env["@levels"] = Levels(src, tb, bar,
|
71
|
-
env["@filesetup"] = FileSetup(src, strict, tb, bar, temp, log)
|
68
|
+
env["@levels"] = Levels(src, tb, bar, False, log, strict)
|
72
69
|
|
73
|
-
print(f"Auto-Editor {auto_editor.
|
70
|
+
print(f"Auto-Editor {auto_editor.__version__}")
|
74
71
|
text = None
|
75
72
|
|
76
73
|
try:
|
auto_editor/subcommands/test.py
CHANGED
@@ -1,15 +1,13 @@
|
|
1
|
-
# type: ignore
|
2
1
|
from __future__ import annotations
|
3
2
|
|
4
3
|
import os
|
5
4
|
import shutil
|
6
5
|
import subprocess
|
7
6
|
import sys
|
8
|
-
from collections.abc import Callable
|
9
7
|
from dataclasses import dataclass, field
|
10
8
|
from fractions import Fraction
|
11
9
|
from time import perf_counter
|
12
|
-
from typing import
|
10
|
+
from typing import TYPE_CHECKING
|
13
11
|
|
14
12
|
import numpy as np
|
15
13
|
|
@@ -20,6 +18,12 @@ from auto_editor.lib.err import MyError
|
|
20
18
|
from auto_editor.utils.log import Log
|
21
19
|
from auto_editor.vanparse import ArgumentParser
|
22
20
|
|
21
|
+
if TYPE_CHECKING:
|
22
|
+
from collections.abc import Callable
|
23
|
+
from typing import Any
|
24
|
+
|
25
|
+
from auto_editor.vanparse import ArgumentParser
|
26
|
+
|
23
27
|
|
24
28
|
@dataclass(slots=True)
|
25
29
|
class TestArgs:
|
@@ -29,7 +33,7 @@ class TestArgs:
|
|
29
33
|
category: str = "cli"
|
30
34
|
|
31
35
|
|
32
|
-
def test_options(parser):
|
36
|
+
def test_options(parser: ArgumentParser) -> ArgumentParser:
|
33
37
|
parser.add_argument("--only", "-n", nargs="*")
|
34
38
|
parser.add_argument("--no-fail-fast", flag=True)
|
35
39
|
parser.add_required(
|
@@ -47,14 +51,6 @@ def pipe_to_console(cmd: list[str]) -> tuple[int, str, str]:
|
|
47
51
|
return process.returncode, stdout.decode("utf-8"), stderr.decode("utf-8")
|
48
52
|
|
49
53
|
|
50
|
-
class Checker:
|
51
|
-
def __init__(self, log: Log):
|
52
|
-
self.log = log
|
53
|
-
|
54
|
-
def check(self, path: str) -> FileInfo:
|
55
|
-
return initFileInfo(path, self.log)
|
56
|
-
|
57
|
-
|
58
54
|
class Runner:
|
59
55
|
def __init__(self) -> None:
|
60
56
|
self.program = [sys.executable, "-m", "auto_editor"]
|
@@ -176,7 +172,10 @@ def main(sys_args: list[str] | None = None):
|
|
176
172
|
args = test_options(ArgumentParser("test")).parse_args(TestArgs, sys_args)
|
177
173
|
|
178
174
|
run = Runner()
|
179
|
-
|
175
|
+
log = Log()
|
176
|
+
|
177
|
+
def fileinfo(path: str) -> FileInfo:
|
178
|
+
return initFileInfo(path, log)
|
180
179
|
|
181
180
|
### Tests ###
|
182
181
|
|
@@ -229,7 +228,7 @@ def main(sys_args: list[str] | None = None):
|
|
229
228
|
|
230
229
|
def example():
|
231
230
|
out = run.main(inputs=["example.mp4"], cmd=[])
|
232
|
-
cn =
|
231
|
+
cn = fileinfo(out)
|
233
232
|
video = cn.videos[0]
|
234
233
|
|
235
234
|
assert video.fps == 30
|
@@ -295,7 +294,7 @@ def main(sys_args: list[str] | None = None):
|
|
295
294
|
out = run.main(
|
296
295
|
["resources/only-video/man-on-green-screen.gif"], ["--edit", "none"]
|
297
296
|
)
|
298
|
-
assert
|
297
|
+
assert fileinfo(out).videos[0].codec == "gif"
|
299
298
|
|
300
299
|
return out
|
301
300
|
|
@@ -310,7 +309,7 @@ def main(sys_args: list[str] | None = None):
|
|
310
309
|
"""Input file must have an extension. Throw error if none is given."""
|
311
310
|
|
312
311
|
shutil.copy("example.mp4", "example")
|
313
|
-
run.check(["example", "--no-open"], "must have an extension
|
312
|
+
run.check(["example", "--no-open"], "must have an extension")
|
314
313
|
|
315
314
|
return "example"
|
316
315
|
|
@@ -319,11 +318,11 @@ def main(sys_args: list[str] | None = None):
|
|
319
318
|
out = run.main(inputs=["example.mp4"], cmd=[], output="out")
|
320
319
|
|
321
320
|
assert out == "out.mp4"
|
322
|
-
assert
|
321
|
+
assert fileinfo(out).videos[0].codec == "h264"
|
323
322
|
|
324
323
|
out = run.main(inputs=["resources/testsrc.mkv"], cmd=[], output="out")
|
325
324
|
assert out == "out.mkv"
|
326
|
-
assert
|
325
|
+
assert fileinfo(out).videos[0].codec == "h264"
|
327
326
|
|
328
327
|
return "out.mp4", "out.mkv"
|
329
328
|
|
@@ -356,15 +355,22 @@ def main(sys_args: list[str] | None = None):
|
|
356
355
|
def premiere_named_export():
|
357
356
|
run.main(["example.mp4"], ["--export", 'premiere:name="Foo Bar"'])
|
358
357
|
|
358
|
+
def export_subtitles():
|
359
|
+
cn = fileinfo(run.main(["resources/subtitle.mp4"], []))
|
360
|
+
|
361
|
+
assert len(cn.videos) == 1
|
362
|
+
assert len(cn.audios) == 1
|
363
|
+
assert len(cn.subtitles) == 1
|
364
|
+
|
359
365
|
def resolution_and_scale():
|
360
|
-
cn =
|
366
|
+
cn = fileinfo(run.main(["example.mp4"], ["--scale", "1.5"]))
|
361
367
|
|
362
368
|
assert cn.videos[0].fps == 30
|
363
369
|
assert cn.videos[0].width == 1920
|
364
370
|
assert cn.videos[0].height == 1080
|
365
371
|
assert cn.audios[0].samplerate == 48000
|
366
372
|
|
367
|
-
cn =
|
373
|
+
cn = fileinfo(run.main(["example.mp4"], ["--scale", "0.2"]))
|
368
374
|
|
369
375
|
assert cn.videos[0].fps == 30
|
370
376
|
assert cn.videos[0].width == 256
|
@@ -372,7 +378,7 @@ def main(sys_args: list[str] | None = None):
|
|
372
378
|
assert cn.audios[0].samplerate == 48000
|
373
379
|
|
374
380
|
out = run.main(["example.mp4"], ["-res", "700,380", "-b", "darkgreen"])
|
375
|
-
cn =
|
381
|
+
cn = fileinfo(out)
|
376
382
|
|
377
383
|
assert cn.videos[0].fps == 30
|
378
384
|
assert cn.videos[0].width == 700
|
@@ -416,7 +422,7 @@ def main(sys_args: list[str] | None = None):
|
|
416
422
|
["--edit", "audio:stream=1"],
|
417
423
|
"out.mov",
|
418
424
|
)
|
419
|
-
assert len(
|
425
|
+
assert len(fileinfo(out).audios) == 1
|
420
426
|
|
421
427
|
return out
|
422
428
|
|
@@ -427,7 +433,7 @@ def main(sys_args: list[str] | None = None):
|
|
427
433
|
|
428
434
|
def concat_mux_tracks():
|
429
435
|
out = run.main(["example.mp4", "resources/multi-track.mov"], [], "out.mov")
|
430
|
-
assert len(
|
436
|
+
assert len(fileinfo(out).audios) == 1
|
431
437
|
|
432
438
|
return out
|
433
439
|
|
@@ -437,30 +443,30 @@ def main(sys_args: list[str] | None = None):
|
|
437
443
|
["--keep-tracks-separate"],
|
438
444
|
"out.mov",
|
439
445
|
)
|
440
|
-
assert len(
|
446
|
+
assert len(fileinfo(out).audios) == 2
|
441
447
|
out = run.main(
|
442
448
|
["example.mp4", "resources/multi-track.mov"],
|
443
449
|
["--keep-tracks-separate"],
|
444
450
|
"out.mov",
|
445
451
|
)
|
446
|
-
assert len(
|
452
|
+
assert len(fileinfo(out).audios) == 2
|
447
453
|
|
448
454
|
return out
|
449
455
|
|
450
456
|
def frame_rate():
|
451
|
-
cn =
|
457
|
+
cn = fileinfo(run.main(["example.mp4"], ["-r", "15", "--no-seek"]))
|
452
458
|
video = cn.videos[0]
|
453
459
|
assert video.fps == 15
|
454
460
|
assert video.time_base == Fraction(1, 15)
|
455
461
|
assert float(video.duration) - 17.33333333333333333333333 < 3
|
456
462
|
|
457
|
-
cn =
|
463
|
+
cn = fileinfo(run.main(["example.mp4"], ["-r", "20"]))
|
458
464
|
video = cn.videos[0]
|
459
465
|
assert video.fps == 20
|
460
466
|
assert video.time_base == Fraction(1, 20)
|
461
467
|
assert float(video.duration) - 17.33333333333333333333333 < 2
|
462
468
|
|
463
|
-
cn =
|
469
|
+
cn = fileinfo(out := run.main(["example.mp4"], ["-r", "60"]))
|
464
470
|
video = cn.videos[0]
|
465
471
|
|
466
472
|
assert video.fps == 60
|
@@ -471,22 +477,22 @@ def main(sys_args: list[str] | None = None):
|
|
471
477
|
|
472
478
|
def embedded_image():
|
473
479
|
out1 = run.main(["resources/embedded-image/h264-png.mp4"], [])
|
474
|
-
cn =
|
480
|
+
cn = fileinfo(out1)
|
475
481
|
assert cn.videos[0].codec == "h264"
|
476
482
|
assert cn.videos[1].codec == "png"
|
477
483
|
|
478
484
|
out2 = run.main(["resources/embedded-image/h264-mjpeg.mp4"], [])
|
479
|
-
cn =
|
485
|
+
cn = fileinfo(out2)
|
480
486
|
assert cn.videos[0].codec == "h264"
|
481
487
|
assert cn.videos[1].codec == "mjpeg"
|
482
488
|
|
483
489
|
out3 = run.main(["resources/embedded-image/h264-png.mkv"], [])
|
484
|
-
cn =
|
490
|
+
cn = fileinfo(out3)
|
485
491
|
assert cn.videos[0].codec == "h264"
|
486
492
|
assert cn.videos[1].codec == "png"
|
487
493
|
|
488
494
|
out4 = run.main(["resources/embedded-image/h264-mjpeg.mkv"], [])
|
489
|
-
cn =
|
495
|
+
cn = fileinfo(out4)
|
490
496
|
assert cn.videos[0].codec == "h264"
|
491
497
|
assert cn.videos[1].codec == "mjpeg"
|
492
498
|
|
@@ -532,7 +538,7 @@ def main(sys_args: list[str] | None = None):
|
|
532
538
|
# Issue 280
|
533
539
|
def SAR():
|
534
540
|
out = run.main(["resources/SAR-2by3.mp4"], [])
|
535
|
-
assert
|
541
|
+
assert fileinfo(out).videos[0].sar == Fraction(2, 3)
|
536
542
|
|
537
543
|
return out
|
538
544
|
|
@@ -744,6 +750,7 @@ def main(sys_args: list[str] | None = None):
|
|
744
750
|
track_tests,
|
745
751
|
codec_tests,
|
746
752
|
premiere_named_export,
|
753
|
+
export_subtitles,
|
747
754
|
export,
|
748
755
|
motion,
|
749
756
|
resolution_and_scale,
|
auto_editor/timeline.py
CHANGED
@@ -1,21 +1,19 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from collections.abc import Iterator
|
4
3
|
from dataclasses import dataclass
|
5
|
-
from
|
6
|
-
from typing import Any
|
4
|
+
from typing import TYPE_CHECKING
|
7
5
|
|
8
|
-
from auto_editor.ffwrapper import FileInfo
|
9
6
|
from auto_editor.lib.contracts import *
|
10
|
-
from auto_editor.utils.chunks import Chunks
|
11
7
|
from auto_editor.utils.cmdkw import Required, pAttr, pAttrs
|
12
|
-
from auto_editor.utils.types import
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
8
|
+
from auto_editor.utils.types import anchor, color, natural, number, threshold
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from collections.abc import Iterator
|
12
|
+
from fractions import Fraction
|
13
|
+
from typing import Any
|
14
|
+
|
15
|
+
from auto_editor.ffwrapper import FileInfo
|
16
|
+
from auto_editor.utils.chunks import Chunks
|
19
17
|
|
20
18
|
|
21
19
|
@dataclass(slots=True)
|
auto_editor/utils/container.py
CHANGED
@@ -72,6 +72,8 @@ def container_constructor(ext: str) -> Container:
|
|
72
72
|
vdefault = container.default_video_codec
|
73
73
|
adefault = container.default_audio_codec
|
74
74
|
sdefault = container.default_subtitle_codec
|
75
|
+
if sdefault == "none" and ext == "mp4":
|
76
|
+
sdefault = "srt"
|
75
77
|
|
76
78
|
vcodecs = set()
|
77
79
|
acodecs = set()
|
@@ -83,6 +85,10 @@ def container_constructor(ext: str) -> Container:
|
|
83
85
|
vcodecs.add(codec)
|
84
86
|
if codec == "h264":
|
85
87
|
vcodecs.add("libx264")
|
88
|
+
if codec == "av1":
|
89
|
+
vcodecs.add("libsvtav1")
|
90
|
+
if codec == "hevc":
|
91
|
+
vcodecs.add("hevc_nvenc")
|
86
92
|
if kind == "audio":
|
87
93
|
acodecs.add(codec)
|
88
94
|
if kind == "subtitle":
|
auto_editor/utils/func.py
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from
|
4
|
-
from fractions import Fraction
|
3
|
+
from typing import TYPE_CHECKING
|
5
4
|
|
6
5
|
import numpy as np
|
7
|
-
from numpy.typing import NDArray
|
8
6
|
|
9
|
-
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from collections.abc import Callable
|
9
|
+
from fractions import Fraction
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
from numpy.typing import NDArray
|
12
|
+
|
13
|
+
from auto_editor.utils.log import Log
|
14
|
+
|
15
|
+
BoolList = NDArray[np.bool_]
|
16
|
+
BoolOperand = Callable[[BoolList, BoolList], BoolList]
|
13
17
|
|
14
18
|
|
15
19
|
def boolop(a: BoolList, b: BoolList, call: BoolOperand) -> BoolList:
|
@@ -25,26 +29,6 @@ def boolop(a: BoolList, b: BoolList, call: BoolOperand) -> BoolList:
|
|
25
29
|
return call(a, b)
|
26
30
|
|
27
31
|
|
28
|
-
def setup_tempdir(temp: str | None, log: Log) -> str:
|
29
|
-
if temp is None:
|
30
|
-
import tempfile
|
31
|
-
|
32
|
-
return tempfile.mkdtemp()
|
33
|
-
|
34
|
-
import os.path
|
35
|
-
from os import listdir, mkdir
|
36
|
-
|
37
|
-
if os.path.isfile(temp):
|
38
|
-
log.error("Temp directory cannot be an already existing file.")
|
39
|
-
if os.path.isdir(temp):
|
40
|
-
if len(listdir(temp)) != 0:
|
41
|
-
log.error("Temp directory should be empty!")
|
42
|
-
else:
|
43
|
-
mkdir(temp)
|
44
|
-
|
45
|
-
return temp
|
46
|
-
|
47
|
-
|
48
32
|
def to_timecode(secs: float | Fraction, fmt: str) -> str:
|
49
33
|
sign = ""
|
50
34
|
if secs < 0:
|
auto_editor/utils/log.py
CHANGED
@@ -3,45 +3,72 @@ from __future__ import annotations
|
|
3
3
|
import sys
|
4
4
|
from datetime import timedelta
|
5
5
|
from shutil import get_terminal_size, rmtree
|
6
|
+
from tempfile import mkdtemp
|
6
7
|
from time import perf_counter, sleep
|
7
8
|
from typing import NoReturn
|
8
9
|
|
9
10
|
|
10
11
|
class Log:
|
11
|
-
__slots__ = ("is_debug", "quiet", "
|
12
|
+
__slots__ = ("is_debug", "quiet", "machine", "no_color", "_temp", "_ut", "_s")
|
12
13
|
|
13
14
|
def __init__(
|
14
15
|
self,
|
15
16
|
is_debug: bool = False,
|
16
17
|
quiet: bool = False,
|
17
|
-
|
18
|
+
temp_dir: str | None = None,
|
18
19
|
machine: bool = False,
|
19
20
|
no_color: bool = True,
|
20
21
|
):
|
21
22
|
self.is_debug = is_debug
|
22
23
|
self.quiet = quiet
|
23
|
-
self.temp = temp
|
24
24
|
self.machine = machine
|
25
25
|
self.no_color = no_color
|
26
|
-
self.
|
26
|
+
self._temp: str | None = None
|
27
|
+
self._ut = temp_dir
|
28
|
+
self._s = 0 if self.quiet or self.machine else perf_counter()
|
27
29
|
|
28
30
|
def debug(self, message: object) -> None:
|
29
31
|
if self.is_debug:
|
30
32
|
self.conwrite("")
|
31
33
|
sys.stderr.write(f"Debug: {message}\n")
|
32
34
|
|
35
|
+
@property
|
36
|
+
def temp(self) -> str:
|
37
|
+
if self._temp is not None:
|
38
|
+
return self._temp
|
39
|
+
|
40
|
+
if self._ut is None:
|
41
|
+
result = mkdtemp()
|
42
|
+
else:
|
43
|
+
import os.path
|
44
|
+
from os import listdir, mkdir
|
45
|
+
|
46
|
+
if os.path.isfile(self._ut):
|
47
|
+
self.error("Temp directory cannot be an already existing file.")
|
48
|
+
|
49
|
+
if os.path.isdir(self._ut):
|
50
|
+
if len(listdir(self._ut)) != 0:
|
51
|
+
self.error("Temp directory should be empty!")
|
52
|
+
else:
|
53
|
+
mkdir(self._ut)
|
54
|
+
result = self._ut
|
55
|
+
|
56
|
+
self.debug(f"Temp Directory: {result}")
|
57
|
+
self._temp = result
|
58
|
+
return result
|
59
|
+
|
33
60
|
def cleanup(self) -> None:
|
34
|
-
if self.
|
61
|
+
if self._temp is None:
|
35
62
|
return
|
36
63
|
try:
|
37
|
-
rmtree(self.
|
64
|
+
rmtree(self._temp)
|
38
65
|
self.debug("Removed Temp Directory.")
|
39
66
|
except FileNotFoundError:
|
40
67
|
pass
|
41
68
|
except PermissionError:
|
42
69
|
sleep(0.1)
|
43
70
|
try:
|
44
|
-
rmtree(self.
|
71
|
+
rmtree(self._temp)
|
45
72
|
self.debug("Removed Temp Directory.")
|
46
73
|
except Exception as e:
|
47
74
|
self.debug(f"Failed to delete temp dir:\n{e}")
|
@@ -65,7 +92,7 @@ class Log:
|
|
65
92
|
|
66
93
|
def stop_timer(self) -> None:
|
67
94
|
if not self.quiet and not self.machine:
|
68
|
-
second_len = round(perf_counter() - self.
|
95
|
+
second_len = round(perf_counter() - self._s, 2)
|
69
96
|
minute_len = timedelta(seconds=round(second_len))
|
70
97
|
|
71
98
|
sys.stdout.write(f"Finished. took {second_len} seconds ({minute_len})\n")
|
auto_editor/utils/types.py
CHANGED
@@ -217,7 +217,7 @@ def resolution(val: str | None) -> tuple[int, int] | None:
|
|
217
217
|
return natural(vals[0]), natural(vals[1])
|
218
218
|
|
219
219
|
|
220
|
-
@dataclass
|
220
|
+
@dataclass(slots=True)
|
221
221
|
class Args:
|
222
222
|
yt_dlp_location: str = "yt-dlp"
|
223
223
|
download_format: str | None = None
|
@@ -255,6 +255,7 @@ class Args:
|
|
255
255
|
show_ffmpeg_output: bool = False
|
256
256
|
quiet: bool = False
|
257
257
|
preview: bool = False
|
258
|
+
no_cache: bool = False
|
258
259
|
margin: tuple[str, str] = ("0.2s", "0.2s")
|
259
260
|
silent_speed: float = 99999.0
|
260
261
|
video_speed: float = 1.0
|
auto_editor/validate_input.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import os
|
4
3
|
import re
|
5
4
|
import subprocess
|
6
5
|
import sys
|
6
|
+
from os.path import exists, isdir, isfile, lexists, splitext
|
7
7
|
|
8
8
|
from auto_editor.ffwrapper import FFmpeg
|
9
9
|
from auto_editor.utils.func import get_stdout
|
@@ -27,7 +27,7 @@ def download_video(my_input: str, args: Args, ffmpeg: FFmpeg, log: Log) -> str:
|
|
27
27
|
download_format = "bestvideo[ext=mp4]+bestaudio[ext=m4a]"
|
28
28
|
|
29
29
|
if args.output_format is None:
|
30
|
-
output_format = re.sub(r"\W+", "-",
|
30
|
+
output_format = re.sub(r"\W+", "-", splitext(my_input)[0]) + ".%(ext)s"
|
31
31
|
else:
|
32
32
|
output_format = args.output_format
|
33
33
|
|
@@ -57,10 +57,10 @@ def download_video(my_input: str, args: Args, ffmpeg: FFmpeg, log: Log) -> str:
|
|
57
57
|
msg += "pip or your favorite package manager and make sure it's in PATH."
|
58
58
|
log.error(msg)
|
59
59
|
|
60
|
-
if not
|
60
|
+
if not isfile(location):
|
61
61
|
subprocess.run([yt_dlp_path] + cmd)
|
62
62
|
|
63
|
-
if not
|
63
|
+
if not isfile(location):
|
64
64
|
log.error(f"Download file wasn't created: {location}")
|
65
65
|
|
66
66
|
return location
|
@@ -73,11 +73,16 @@ def valid_input(inputs: list[str], ffmpeg: FFmpeg, args: Args, log: Log) -> list
|
|
73
73
|
if my_input.startswith("http://") or my_input.startswith("https://"):
|
74
74
|
result.append(download_video(my_input, args, ffmpeg, log))
|
75
75
|
else:
|
76
|
-
_, ext =
|
76
|
+
_, ext = splitext(my_input)
|
77
77
|
if ext == "":
|
78
|
-
if
|
78
|
+
if isdir(my_input):
|
79
79
|
log.error("Input must be a file or a URL, not a directory.")
|
80
|
-
|
80
|
+
if exists(my_input):
|
81
|
+
log.error(f"Input file must have an extension: {my_input}")
|
82
|
+
if lexists(my_input):
|
83
|
+
log.error(f"Input file is a broken symbolic link: {my_input}")
|
84
|
+
if my_input.startswith("-"):
|
85
|
+
log.error(f"Option/Input file doesn't exist: {my_input}")
|
81
86
|
result.append(my_input)
|
82
87
|
|
83
88
|
return result
|
auto_editor/vanparse.py
CHANGED
@@ -4,7 +4,6 @@ import difflib
|
|
4
4
|
import re
|
5
5
|
import sys
|
6
6
|
import textwrap
|
7
|
-
from collections.abc import Iterator
|
8
7
|
from dataclasses import dataclass
|
9
8
|
from io import StringIO
|
10
9
|
from shutil import get_terminal_size
|
@@ -14,7 +13,7 @@ from auto_editor.utils.log import Log
|
|
14
13
|
from auto_editor.utils.types import CoerceError
|
15
14
|
|
16
15
|
if TYPE_CHECKING:
|
17
|
-
from collections.abc import Callable
|
16
|
+
from collections.abc import Callable, Iterator
|
18
17
|
from typing import Any, Literal, TypeVar
|
19
18
|
|
20
19
|
T = TypeVar("T")
|
@@ -0,0 +1,55 @@
|
|
1
|
+
auto_editor/__init__.py,sha256=HQ6qfTf_xOT2gRqJKN_PWXCV49oRXl228FqZ7w9Haeg,23
|
2
|
+
auto_editor/__main__.py,sha256=aXFUlP7v15CzBeYtLdEPyIX2hxtynXo_UKWUQlxQGbM,9852
|
3
|
+
auto_editor/analyze.py,sha256=pHoSZ_-wyV1hLJyJpPg9Ha7oecjdGHGJ0a4hbKnb9NY,11779
|
4
|
+
auto_editor/edit.py,sha256=fe6YT6O8B_hPOb4r4FdubvswbTuyx4_qlzlJTyMKuVc,11267
|
5
|
+
auto_editor/ffwrapper.py,sha256=NnhD4TvKyaap0Y2MQ7aIUQfeLJwfNbjTj5bLUpoMEI4,7888
|
6
|
+
auto_editor/help.py,sha256=BFiP7vBz42TUzum4-zaQIrV1OY7kHeN0pe0MPE0T5xw,7997
|
7
|
+
auto_editor/make_layers.py,sha256=ybTxPRD6bDbEX-7e9qu4OYUvYkrdNb4BjnN9hzwkRiQ,8496
|
8
|
+
auto_editor/output.py,sha256=D8NCJwwmcjDf5rvoBnWKu5XY7QtxF3thxbnTxKAxGu8,8043
|
9
|
+
auto_editor/preview.py,sha256=noWkgyzdE14zwG8ZDYxLDual5iVt6zYTt4HTe-QAgFY,3029
|
10
|
+
auto_editor/timeline.py,sha256=d9Qhup2pBwsj7j9kF-Iw22xHbQvGx-keDq2eLI11Mt4,7117
|
11
|
+
auto_editor/validate_input.py,sha256=_9vtbNxodhVPf4PzTAH67LSj2K-HS6kShJOARmUMJdY,2904
|
12
|
+
auto_editor/vanparse.py,sha256=f0vViZ-aYtDxEyVrFHJ5X2pPTQAfqtw3N2gZgzn51kU,10002
|
13
|
+
auto_editor/wavfile.py,sha256=7N2LX_WfFVRnoXrKveLvuyTYpIz2htpGqfCD8tR4kJ8,9168
|
14
|
+
auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
+
auto_editor/formats/fcp11.py,sha256=VwJWJs1qNDIVC8-pswipmKCk0e4V3LnE5fAMA0pPWVg,5857
|
16
|
+
auto_editor/formats/fcp7.py,sha256=fH86sxhlUysWisjvlqzZJgDrRpj3dSzFG-Eho2sehdc,17610
|
17
|
+
auto_editor/formats/json.py,sha256=Br-xHVHj59C0OPP2FwfJeht_fImepRXsaw0iDFvK7-w,7693
|
18
|
+
auto_editor/formats/shotcut.py,sha256=-ES854LLFCMCBe100JRJedDmuk8zPev17aQMTrzPv-g,4923
|
19
|
+
auto_editor/formats/utils.py,sha256=GIZw28WHuCIaZ_zMI0v6Kxbq0QaIpbLsdSegdYwQxQ8,1990
|
20
|
+
auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
+
auto_editor/lang/json.py,sha256=OsNcYlfEj8ZLlzLK-gkLcrCCKI7mJz9rpe-6XLr4f9U,9231
|
22
|
+
auto_editor/lang/libmath.py,sha256=z33A161Oe6vYYK7R6pgYjdZZe63dQkN38Qf36TL3prg,847
|
23
|
+
auto_editor/lang/palet.py,sha256=m22TQnKomUM2quxIH4bz7ya_gM166rbJ5pW7Ei-43IA,59712
|
24
|
+
auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
+
auto_editor/lib/contracts.py,sha256=a3ZT-bGMa3-UjWKKFrEwLayw2Gl-rhqd5Bmvmrj85oE,7413
|
26
|
+
auto_editor/lib/data_structs.py,sha256=xyB6aEcpdB9NNWp_dU3d2ds5Z8zOfHXNX4mNQLh2pNw,6977
|
27
|
+
auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
|
28
|
+
auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
+
auto_editor/render/audio.py,sha256=darKvlglNXkknSUaPnH-qEUyccM1Awnv03_Px4yY81I,8844
|
30
|
+
auto_editor/render/subtitle.py,sha256=g195kDN0LcwKlZeQMCflXPH5n_74iwCk1RPLSQ5eP34,4373
|
31
|
+
auto_editor/render/video.py,sha256=gMVcLehC_QpdtIzNLR_7tv2CZmYeEWisS_5as4ceHV0,12971
|
32
|
+
auto_editor/subcommands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
|
+
auto_editor/subcommands/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
|
34
|
+
auto_editor/subcommands/info.py,sha256=t5n43HLt9hpMFSIfGV777X4zIPBAFugOKlpCfRjiKxY,6921
|
35
|
+
auto_editor/subcommands/levels.py,sha256=ZB8_9jbOA5s1AQUcUNZDiLAjyJOKl7Be2YeVWdkOr0Q,4071
|
36
|
+
auto_editor/subcommands/palet.py,sha256=tbQoRWoT4jR3yu0etGApfprM-oQgXIjC-rIY-QG3nM0,655
|
37
|
+
auto_editor/subcommands/repl.py,sha256=OfxIOBjE7W12UemfaaxMnzHcmV5cUTt7g5328R7rAYU,3116
|
38
|
+
auto_editor/subcommands/subdump.py,sha256=af_XBf7kaevqHn1A71z8C-7x8pS5WKD9FE_ugkCw6rk,665
|
39
|
+
auto_editor/subcommands/test.py,sha256=d-StLyIZHkvJVaiXmr1gYTzUIwLhbG34tJfYosobmps,25227
|
40
|
+
auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
41
|
+
auto_editor/utils/bar.py,sha256=RJqkJ8gNr8op_Z-2hh48ExjSonmTPX-RshctK_itv14,3988
|
42
|
+
auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
|
43
|
+
auto_editor/utils/cmdkw.py,sha256=XApxw7FZBOEJV9N4LHhdw1GVfHbFfCjr-zCZ1gJsSvY,6002
|
44
|
+
auto_editor/utils/container.py,sha256=RnpoMmMYmn7o69LmMbBFHW4TsP3K52jYDhG9qzWXmAs,2720
|
45
|
+
auto_editor/utils/encoder.py,sha256=auNYo7HXbcU4iTUCc0LE5lpwFmSvdWvBm6-5KIaRK8w,2983
|
46
|
+
auto_editor/utils/func.py,sha256=kcxCOqe-tg6k-kxutIran8LpffRiHDjKB6rm-ngFiSU,4460
|
47
|
+
auto_editor/utils/log.py,sha256=M2QKeQHMRNLm3HMVUKedZPRprT2u5dipOStiO4miPBk,3613
|
48
|
+
auto_editor/utils/subtitle_tools.py,sha256=TjjVPiT8bWzZJcrZjF7ddpgfIsVkLE4LyxXzBswHAGU,693
|
49
|
+
auto_editor/utils/types.py,sha256=JdAwfuT9Ty_FXUm89GUTo0M8FPFrXbqnlk-g4pWP1_k,11609
|
50
|
+
auto_editor-25.0.1.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
|
51
|
+
auto_editor-25.0.1.dist-info/METADATA,sha256=FsEPvrdesILZ7duMkfSr7kRBhXEEN6dJ2ttjSr4Z9qw,6137
|
52
|
+
auto_editor-25.0.1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
53
|
+
auto_editor-25.0.1.dist-info/entry_points.txt,sha256=-H7zdTw4MqnAcwrN5xTNkGIhzZtJMxS9r6lTMeR9-aA,240
|
54
|
+
auto_editor-25.0.1.dist-info/top_level.txt,sha256=ky1HUkqq9i034c4CUU_0wBw0xZsxxyGEak1eTbdvpyA,12
|
55
|
+
auto_editor-25.0.1.dist-info/RECORD,,
|