auto-editor 24.29.1__py3-none-any.whl → 24.31.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 +2 -2
- auto_editor/analyze.py +143 -132
- auto_editor/edit.py +14 -21
- auto_editor/lang/palet.py +12 -16
- auto_editor/lib/data_structs.py +2 -0
- auto_editor/make_layers.py +3 -6
- auto_editor/output.py +5 -5
- auto_editor/preview.py +2 -3
- auto_editor/render/subtitle.py +1 -1
- auto_editor/render/video.py +8 -18
- auto_editor/subcommands/levels.py +18 -17
- auto_editor/subcommands/repl.py +3 -12
- auto_editor/subcommands/subdump.py +5 -8
- auto_editor/subcommands/test.py +2 -2
- auto_editor/utils/container.py +71 -313
- {auto_editor-24.29.1.dist-info → auto_editor-24.31.1.dist-info}/METADATA +2 -2
- {auto_editor-24.29.1.dist-info → auto_editor-24.31.1.dist-info}/RECORD +21 -21
- {auto_editor-24.29.1.dist-info → auto_editor-24.31.1.dist-info}/WHEEL +1 -1
- {auto_editor-24.29.1.dist-info → auto_editor-24.31.1.dist-info}/LICENSE +0 -0
- {auto_editor-24.29.1.dist-info → auto_editor-24.31.1.dist-info}/entry_points.txt +0 -0
- {auto_editor-24.29.1.dist-info → auto_editor-24.31.1.dist-info}/top_level.txt +0 -0
auto_editor/render/video.py
CHANGED
@@ -17,8 +17,6 @@ from auto_editor.utils.types import color
|
|
17
17
|
if TYPE_CHECKING:
|
18
18
|
from collections.abc import Iterator
|
19
19
|
|
20
|
-
from av.filter import FilterContext
|
21
|
-
|
22
20
|
from auto_editor.ffwrapper import FFmpeg, FileInfo
|
23
21
|
from auto_editor.timeline import v3
|
24
22
|
from auto_editor.utils.bar import Bar
|
@@ -33,11 +31,6 @@ class VideoFrame:
|
|
33
31
|
src: FileInfo
|
34
32
|
|
35
33
|
|
36
|
-
def link_nodes(*nodes: FilterContext) -> None:
|
37
|
-
for c, n in zip(nodes, nodes[1:]):
|
38
|
-
c.link_to(n)
|
39
|
-
|
40
|
-
|
41
34
|
# From: github.com/PyAV-Org/PyAV/blob/main/av/video/frame.pyx
|
42
35
|
allowed_pix_fmt = {
|
43
36
|
"yuv420p",
|
@@ -98,12 +91,11 @@ def make_image_cache(tl: v3) -> dict[tuple[FileInfo, int], np.ndarray]:
|
|
98
91
|
for frame in cn.decode(my_stream):
|
99
92
|
if obj.width != 0:
|
100
93
|
graph = av.filter.Graph()
|
101
|
-
link_nodes(
|
94
|
+
graph.link_nodes(
|
102
95
|
graph.add_buffer(template=my_stream),
|
103
96
|
graph.add("scale", f"{obj.width}:-1"),
|
104
97
|
graph.add("buffersink"),
|
105
|
-
)
|
106
|
-
graph.vpush(frame)
|
98
|
+
).vpush(frame)
|
107
99
|
frame = graph.vpull()
|
108
100
|
img_cache[(obj.src, obj.width)] = frame.to_ndarray(
|
109
101
|
format="rgb24"
|
@@ -177,7 +169,7 @@ def render_av(
|
|
177
169
|
target_width = max(round(tl.res[0] * args.scale), 2)
|
178
170
|
target_height = max(round(tl.res[1] * args.scale), 2)
|
179
171
|
scale_graph = av.filter.Graph()
|
180
|
-
link_nodes(
|
172
|
+
scale_graph.link_nodes(
|
181
173
|
scale_graph.add(
|
182
174
|
"buffer", video_size="1x1", time_base="1/1", pix_fmt=target_pix_fmt
|
183
175
|
),
|
@@ -213,7 +205,7 @@ def render_av(
|
|
213
205
|
if apply_video_later:
|
214
206
|
cmd += ["-c:v", "mpeg4", "-qscale:v", "1"]
|
215
207
|
else:
|
216
|
-
cmd += video_quality(args
|
208
|
+
cmd += video_quality(args)
|
217
209
|
|
218
210
|
# Setting SAR requires re-encoding so we do it here.
|
219
211
|
if src is not None and src.videos and (sar := src.videos[0].sar) is not None:
|
@@ -293,7 +285,7 @@ def render_av(
|
|
293
285
|
if (frame.width, frame.height) != tl.res:
|
294
286
|
width, height = tl.res
|
295
287
|
graph = av.filter.Graph()
|
296
|
-
link_nodes(
|
288
|
+
graph.link_nodes(
|
297
289
|
graph.add_buffer(template=my_stream),
|
298
290
|
graph.add(
|
299
291
|
"scale",
|
@@ -301,21 +293,19 @@ def render_av(
|
|
301
293
|
),
|
302
294
|
graph.add("pad", f"{width}:{height}:-1:-1:color={bg}"),
|
303
295
|
graph.add("buffersink"),
|
304
|
-
)
|
305
|
-
graph.vpush(frame)
|
296
|
+
).vpush(frame)
|
306
297
|
frame = graph.vpull()
|
307
298
|
elif isinstance(obj, TlRect):
|
308
299
|
graph = av.filter.Graph()
|
309
300
|
x, y = apply_anchor(obj.x, obj.y, obj.width, obj.height, obj.anchor)
|
310
|
-
link_nodes(
|
301
|
+
graph.link_nodes(
|
311
302
|
graph.add_buffer(template=my_stream),
|
312
303
|
graph.add(
|
313
304
|
"drawbox",
|
314
305
|
f"x={x}:y={y}:w={obj.width}:h={obj.height}:color={obj.fill}:t=fill",
|
315
306
|
),
|
316
307
|
graph.add("buffersink"),
|
317
|
-
)
|
318
|
-
graph.vpush(frame)
|
308
|
+
).vpush(frame)
|
319
309
|
frame = graph.vpull()
|
320
310
|
elif isinstance(obj, TlImage):
|
321
311
|
img = img_cache[(obj.src, obj.width)]
|
@@ -2,15 +2,15 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import sys
|
4
4
|
from dataclasses import dataclass, field
|
5
|
+
from fractions import Fraction
|
5
6
|
from typing import TYPE_CHECKING
|
6
7
|
|
7
8
|
import numpy as np
|
8
9
|
|
9
|
-
from auto_editor.analyze import LevelError, Levels
|
10
|
-
from auto_editor.ffwrapper import
|
10
|
+
from auto_editor.analyze import LevelError, Levels, iter_audio, iter_motion
|
11
|
+
from auto_editor.ffwrapper import initFileInfo
|
11
12
|
from auto_editor.lang.palet import env
|
12
13
|
from auto_editor.lib.contracts import is_bool, is_nat, is_nat1, is_str, is_void, orc
|
13
|
-
from auto_editor.output import Ensure
|
14
14
|
from auto_editor.utils.bar import Bar
|
15
15
|
from auto_editor.utils.cmdkw import (
|
16
16
|
ParserError,
|
@@ -25,6 +25,7 @@ from auto_editor.utils.types import frame_rate
|
|
25
25
|
from auto_editor.vanparse import ArgumentParser
|
26
26
|
|
27
27
|
if TYPE_CHECKING:
|
28
|
+
from collections.abc import Iterator
|
28
29
|
from fractions import Fraction
|
29
30
|
|
30
31
|
from numpy.typing import NDArray
|
@@ -35,8 +36,6 @@ class LevelArgs:
|
|
35
36
|
input: list[str] = field(default_factory=list)
|
36
37
|
edit: str = "audio"
|
37
38
|
timebase: Fraction | None = None
|
38
|
-
ffmpeg_location: str | None = None
|
39
|
-
my_ffmpeg: bool = False
|
40
39
|
help: bool = False
|
41
40
|
|
42
41
|
|
@@ -54,12 +53,6 @@ def levels_options(parser: ArgumentParser) -> ArgumentParser:
|
|
54
53
|
type=frame_rate,
|
55
54
|
help="Set custom timebase",
|
56
55
|
)
|
57
|
-
parser.add_argument("--ffmpeg-location", help="Point to your custom ffmpeg file")
|
58
|
-
parser.add_argument(
|
59
|
-
"--my-ffmpeg",
|
60
|
-
flag=True,
|
61
|
-
help="Use the ffmpeg on your PATH instead of the one packaged",
|
62
|
-
)
|
63
56
|
return parser
|
64
57
|
|
65
58
|
|
@@ -79,12 +72,21 @@ def print_arr(arr: NDArray) -> None:
|
|
79
72
|
print("")
|
80
73
|
|
81
74
|
|
75
|
+
def print_arr_gen(arr: Iterator[int | float]) -> None:
|
76
|
+
print("")
|
77
|
+
print("@start")
|
78
|
+
for a in arr:
|
79
|
+
if isinstance(a, float):
|
80
|
+
print(f"{a:.20f}")
|
81
|
+
else:
|
82
|
+
print(a)
|
83
|
+
print("")
|
84
|
+
|
85
|
+
|
82
86
|
def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
83
87
|
parser = levels_options(ArgumentParser("levels"))
|
84
88
|
args = parser.parse_args(LevelArgs, sys_args)
|
85
89
|
|
86
|
-
ffmpeg = FFmpeg(args.ffmpeg_location, args.my_ffmpeg)
|
87
|
-
|
88
90
|
bar = Bar("none")
|
89
91
|
temp = setup_tempdir(None, Log())
|
90
92
|
log = Log(quiet=True, temp=temp)
|
@@ -96,7 +98,6 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
96
98
|
src = sources[0]
|
97
99
|
|
98
100
|
tb = src.get_fps() if args.timebase is None else args.timebase
|
99
|
-
ensure = Ensure(ffmpeg, bar, src.get_sr(), temp, log)
|
100
101
|
|
101
102
|
if ":" in args.edit:
|
102
103
|
method, attrs = args.edit.split(":", 1)
|
@@ -131,12 +132,12 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
131
132
|
except ParserError as e:
|
132
133
|
log.error(e)
|
133
134
|
|
134
|
-
levels = Levels(
|
135
|
+
levels = Levels(src, tb, bar, temp, log)
|
135
136
|
try:
|
136
137
|
if method == "audio":
|
137
|
-
|
138
|
+
print_arr_gen(iter_audio(src, tb, **obj))
|
138
139
|
elif method == "motion":
|
139
|
-
|
140
|
+
print_arr_gen(iter_motion(src, tb, **obj))
|
140
141
|
elif method == "subtitle":
|
141
142
|
print_arr(levels.subtitle(**obj))
|
142
143
|
elif method == "none":
|
auto_editor/subcommands/repl.py
CHANGED
@@ -6,11 +6,10 @@ from fractions import Fraction
|
|
6
6
|
|
7
7
|
import auto_editor
|
8
8
|
from auto_editor.analyze import FileSetup, Levels
|
9
|
-
from auto_editor.ffwrapper import
|
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
|
-
from auto_editor.output import Ensure
|
14
13
|
from auto_editor.utils.bar import Bar
|
15
14
|
from auto_editor.utils.func import setup_tempdir
|
16
15
|
from auto_editor.utils.log import Log
|
@@ -48,12 +47,6 @@ def repl_options(parser: ArgumentParser) -> ArgumentParser:
|
|
48
47
|
type=frame_rate,
|
49
48
|
help="Set custom timebase",
|
50
49
|
)
|
51
|
-
parser.add_argument("--ffmpeg-location", help="Point to your custom ffmpeg file")
|
52
|
-
parser.add_argument(
|
53
|
-
"--my-ffmpeg",
|
54
|
-
flag=True,
|
55
|
-
help="Use the ffmpeg on your PATH instead of the one packaged",
|
56
|
-
)
|
57
50
|
parser.add_argument(
|
58
51
|
"--temp-dir",
|
59
52
|
metavar="PATH",
|
@@ -68,16 +61,14 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
68
61
|
if args.input:
|
69
62
|
temp = setup_tempdir(args.temp_dir, Log())
|
70
63
|
log = Log(quiet=True, temp=temp)
|
71
|
-
ffmpeg = FFmpeg(args.ffmpeg_location, args.my_ffmpeg, False)
|
72
64
|
strict = len(args.input) < 2
|
73
65
|
sources = [initFileInfo(path, log) for path in args.input]
|
74
66
|
src = sources[0]
|
75
67
|
tb = src.get_fps() if args.timebase is None else args.timebase
|
76
68
|
bar = Bar("modern")
|
77
|
-
ensure = Ensure(ffmpeg, bar, src.get_sr(), temp, log)
|
78
69
|
env["timebase"] = tb
|
79
|
-
env["@levels"] = Levels(
|
80
|
-
env["@filesetup"] = FileSetup(src,
|
70
|
+
env["@levels"] = Levels(src, tb, bar, temp, log)
|
71
|
+
env["@filesetup"] = FileSetup(src, strict, tb, bar, temp, log)
|
81
72
|
|
82
73
|
print(f"Auto-Editor {auto_editor.version} ({auto_editor.__version__})")
|
83
74
|
text = None
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import sys
|
2
2
|
|
3
3
|
import av
|
4
|
-
from av.subtitles.subtitle import AssSubtitle
|
4
|
+
from av.subtitles.subtitle import AssSubtitle
|
5
5
|
|
6
6
|
|
7
7
|
def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
@@ -9,13 +9,10 @@ def main(sys_args: list[str] = sys.argv[1:]) -> None:
|
|
9
9
|
with av.open(input_file) as container:
|
10
10
|
for s in range(len(container.streams.subtitles)):
|
11
11
|
print(f"file: {input_file} ({s}:{container.streams.subtitles[s].name})")
|
12
|
-
for
|
13
|
-
for
|
14
|
-
|
15
|
-
|
16
|
-
print(sub.ass.decode("utf-8", errors="ignore"))
|
17
|
-
elif isinstance(sub, TextSubtitle):
|
18
|
-
print(sub.text.decode("utf-8", errors="ignore"))
|
12
|
+
for subset in container.decode(subtitles=s):
|
13
|
+
for sub in subset:
|
14
|
+
if isinstance(sub, AssSubtitle):
|
15
|
+
print(sub.ass.decode("utf-8", errors="ignore"))
|
19
16
|
print("------")
|
20
17
|
|
21
18
|
|
auto_editor/subcommands/test.py
CHANGED
@@ -628,11 +628,11 @@ def main(sys_args: list[str] | None = None):
|
|
628
628
|
("(string #\\a #\\b)", "ab"),
|
629
629
|
("(string #\\a #\\b #\\c)", "abc"),
|
630
630
|
(
|
631
|
-
"(margin
|
631
|
+
"(margin (bool-array 0 0 0 1 0 0 0) 0)",
|
632
632
|
np.array([0, 0, 0, 1, 0, 0, 0], dtype=np.bool_),
|
633
633
|
),
|
634
634
|
(
|
635
|
-
"(margin
|
635
|
+
"(margin (bool-array 0 0 1 1 0 0 0) -2 2)",
|
636
636
|
np.array([0, 0, 0, 0, 1, 1, 0], dtype=np.bool_),
|
637
637
|
),
|
638
638
|
("(equal? 3 3)", True),
|
auto_editor/utils/container.py
CHANGED
@@ -1,338 +1,96 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass
|
4
4
|
from typing import TypedDict
|
5
5
|
|
6
|
+
import av
|
7
|
+
from av.codec import Codec
|
8
|
+
|
6
9
|
|
7
10
|
class DictContainer(TypedDict, total=False):
|
8
|
-
allow_video: bool
|
9
|
-
allow_audio: bool
|
10
|
-
allow_subtitle: bool
|
11
|
-
allow_image: bool
|
12
11
|
max_videos: int | None
|
13
12
|
max_audios: int | None
|
14
13
|
max_subtitles: int | None
|
15
|
-
vcodecs: list[str] | None
|
16
|
-
acodecs: list[str] | None
|
17
|
-
scodecs: list[str] | None
|
18
|
-
vstrict: bool
|
19
|
-
sstrict: bool
|
20
|
-
disallow_v: list[str]
|
21
14
|
samplerate: list[int] | None
|
22
15
|
|
23
16
|
|
24
17
|
@dataclass(slots=True)
|
25
18
|
class Container:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
19
|
+
allow_image: bool
|
20
|
+
vcodecs: set[str]
|
21
|
+
acodecs: set[str]
|
22
|
+
scodecs: set[str]
|
23
|
+
default_vid: str
|
24
|
+
default_aud: str
|
25
|
+
default_sub: str
|
30
26
|
max_videos: int | None = None
|
31
27
|
max_audios: int | None = None
|
32
28
|
max_subtitles: int | None = None
|
33
|
-
vcodecs: list[str] | None = None
|
34
|
-
acodecs: list[str] | None = None
|
35
|
-
scodecs: list[str] | None = None
|
36
|
-
vstrict: bool = False
|
37
|
-
sstrict: bool = False
|
38
|
-
disallow_v: list[str] = field(default_factory=list)
|
39
29
|
samplerate: list[int] | None = None # Any samplerate is allowed
|
40
30
|
|
41
31
|
|
42
|
-
|
43
|
-
"
|
44
|
-
"
|
45
|
-
"
|
46
|
-
"
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
"
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
opus_en = ["opus", "libopus"]
|
57
|
-
|
58
|
-
h265: DictContainer = {
|
59
|
-
"allow_video": True,
|
60
|
-
"vcodecs": hevc_en + ["mpeg4"] + h264_en,
|
61
|
-
}
|
62
|
-
h264: DictContainer = {
|
63
|
-
"allow_video": True,
|
64
|
-
"vcodecs": h264_en + ["mpeg4"] + hevc_en,
|
65
|
-
}
|
66
|
-
aac: DictContainer = {
|
67
|
-
"allow_audio": True,
|
68
|
-
"max_audios": 1,
|
69
|
-
"acodecs": aac_en,
|
70
|
-
}
|
71
|
-
ass: DictContainer = {
|
72
|
-
"allow_subtitle": True,
|
73
|
-
"scodecs": ["ass", "ssa"],
|
74
|
-
"max_subtitles": 1,
|
75
|
-
"sstrict": True,
|
76
|
-
}
|
77
|
-
mp4: DictContainer = {
|
78
|
-
"allow_video": True,
|
79
|
-
"allow_audio": True,
|
80
|
-
"allow_subtitle": True,
|
81
|
-
"allow_image": True,
|
82
|
-
"vcodecs": h264_en + hevc_en + av1_en + ["vp9", "mpeg4", "mpeg2video", "mjpeg"],
|
83
|
-
"acodecs": aac_en + opus_en + ["mp3", "flac", "vorbis", "libvorbis", "ac3", "mp2"],
|
84
|
-
"vstrict": True,
|
85
|
-
}
|
86
|
-
ogg: DictContainer = {
|
87
|
-
"allow_video": True,
|
88
|
-
"allow_audio": True,
|
89
|
-
"allow_subtitle": True,
|
90
|
-
"vcodecs": ["libtheora", "theora"],
|
91
|
-
"acodecs": opus_en + ["libvorbis", "vorbis", "flac", "speex"],
|
92
|
-
"vstrict": True,
|
32
|
+
containers: dict[str, DictContainer] = {
|
33
|
+
"aac": {"max_audios": 1},
|
34
|
+
"adts": {"max_audios": 1},
|
35
|
+
"ass": {"max_subtitles": 1},
|
36
|
+
"ssa": {"max_subtitles": 1},
|
37
|
+
"apng": {"max_videos": 1},
|
38
|
+
"gif": {"max_videos": 1},
|
39
|
+
"wav": {"max_audios": 1},
|
40
|
+
"ast": {"max_audios": 1},
|
41
|
+
"mp3": {"max_audios": 1},
|
42
|
+
"flac": {"max_audios": 1},
|
43
|
+
"srt": {"max_subtitles": 1},
|
44
|
+
"vtt": {"max_subtitles": 1},
|
45
|
+
"swf": {"samplerate": [44100, 22050, 11025]},
|
93
46
|
}
|
94
47
|
|
95
|
-
mka_audio = (
|
96
|
-
["libvorbis", "vorbis"]
|
97
|
-
+ aac_en
|
98
|
-
+ opus_en
|
99
|
-
+ [
|
100
|
-
"mp3",
|
101
|
-
"flac",
|
102
|
-
"ac3",
|
103
|
-
"mp2",
|
104
|
-
"wmav2",
|
105
|
-
"pcm_s16le",
|
106
|
-
"pcm_alaw",
|
107
|
-
"pcm_f32le",
|
108
|
-
"pcm_f64le",
|
109
|
-
"pcm_mulaw",
|
110
|
-
"pcm_s16be",
|
111
|
-
"pcm_s24be",
|
112
|
-
"pcm_s24le",
|
113
|
-
"pcm_s32be",
|
114
|
-
"pcm_s32le",
|
115
|
-
"pcm_u8",
|
116
|
-
]
|
117
|
-
)
|
118
48
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
"
|
123
|
-
|
124
|
-
"ssa":
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
"
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
"pcm_u8",
|
161
|
-
],
|
162
|
-
},
|
163
|
-
"ast": {
|
164
|
-
"allow_audio": True,
|
165
|
-
"max_audios": 1,
|
166
|
-
"acodecs": ["pcm_s16be_planar"],
|
167
|
-
},
|
168
|
-
"mp3": {
|
169
|
-
"allow_audio": True,
|
170
|
-
"max_audios": 1,
|
171
|
-
"acodecs": ["mp3"],
|
172
|
-
},
|
173
|
-
"opus": {
|
174
|
-
"allow_audio": True,
|
175
|
-
"acodecs": opus_en + ["flac", "libvorbis", "vorbis", "speex"],
|
176
|
-
},
|
177
|
-
"oga": {
|
178
|
-
"allow_audio": True,
|
179
|
-
"acodecs": opus_en + ["flac", "libvorbis", "vorbis", "speex"],
|
180
|
-
},
|
181
|
-
"flac": {
|
182
|
-
"allow_audio": True,
|
183
|
-
"max_audios": 1,
|
184
|
-
"acodecs": ["flac"],
|
185
|
-
},
|
186
|
-
"webm": {
|
187
|
-
"allow_video": True,
|
188
|
-
"allow_audio": True,
|
189
|
-
"allow_subtitle": True,
|
190
|
-
"vcodecs": ["vp9", "vp8"] + av1_en,
|
191
|
-
"acodecs": opus_en + ["vorbis", "libvorbis"],
|
192
|
-
"scodecs": ["webvtt"],
|
193
|
-
"vstrict": True,
|
194
|
-
"sstrict": True,
|
195
|
-
},
|
196
|
-
"srt": {
|
197
|
-
"allow_subtitle": True,
|
198
|
-
"scodecs": ["srt"],
|
199
|
-
"max_subtitles": 1,
|
200
|
-
"sstrict": True,
|
201
|
-
},
|
202
|
-
"vtt": {
|
203
|
-
"allow_subtitle": True,
|
204
|
-
"scodecs": ["webvtt"],
|
205
|
-
"max_subtitles": 1,
|
206
|
-
"sstrict": True,
|
207
|
-
},
|
208
|
-
"avi": {
|
209
|
-
"allow_video": True,
|
210
|
-
"allow_audio": True,
|
211
|
-
"vcodecs": ["mpeg4"] + h264_en + ["prores", "mjpeg", "mpeg2video", "rawvideo"],
|
212
|
-
"acodecs": ["mp3"]
|
213
|
-
+ aac_en
|
214
|
-
+ [
|
215
|
-
"flac",
|
216
|
-
"vorbis",
|
217
|
-
"libvorbis",
|
218
|
-
"mp2",
|
219
|
-
"wmav2",
|
220
|
-
"pcm_s16le",
|
221
|
-
"pcm_alaw",
|
222
|
-
"pcm_f32le",
|
223
|
-
"pcm_f64le",
|
224
|
-
"pcm_mulaw",
|
225
|
-
"pcm_s24le",
|
226
|
-
"pcm_s32le",
|
227
|
-
"pcm_u8",
|
228
|
-
],
|
229
|
-
"disallow_v": hevc_en + ["apng", "gif"],
|
230
|
-
},
|
231
|
-
"wmv": {
|
232
|
-
"allow_video": True,
|
233
|
-
"allow_audio": True,
|
234
|
-
"vcodecs": ["msmpeg4v3"]
|
235
|
-
+ h264_en
|
236
|
-
+ ["mpeg4", "mpeg2video", "mjpeg", "rawvideo"],
|
237
|
-
"acodecs": ["wmav2"]
|
238
|
-
+ aac_en
|
239
|
-
+ [
|
240
|
-
"mp3",
|
241
|
-
"flac",
|
242
|
-
"vorbis",
|
243
|
-
"libvorbis",
|
244
|
-
"ac3",
|
245
|
-
"mp2",
|
246
|
-
"pcm_s16le",
|
247
|
-
"pcm_alaw",
|
248
|
-
"pcm_f32le",
|
249
|
-
"pcm_f64le",
|
250
|
-
"pcm_mulaw",
|
251
|
-
"pcm_s24le",
|
252
|
-
"pcm_s32le",
|
253
|
-
"pcm_u8",
|
254
|
-
],
|
255
|
-
"vstrict": True,
|
256
|
-
},
|
257
|
-
"mkv": {
|
258
|
-
"allow_video": True,
|
259
|
-
"allow_audio": True,
|
260
|
-
"allow_subtitle": True,
|
261
|
-
"allow_image": True,
|
262
|
-
"vcodecs": h264_en
|
263
|
-
+ hevc_en
|
264
|
-
+ prores_en
|
265
|
-
+ [
|
266
|
-
"vp9",
|
267
|
-
"vp8",
|
268
|
-
"mpeg4",
|
269
|
-
"mpeg2video",
|
270
|
-
"msmpeg4v3",
|
271
|
-
"mjpeg",
|
272
|
-
"gif",
|
273
|
-
"rawvideo",
|
274
|
-
],
|
275
|
-
"acodecs": mka_audio,
|
276
|
-
"disallow_v": ["apng"],
|
277
|
-
},
|
278
|
-
"mka": {
|
279
|
-
"allow_audio": True,
|
280
|
-
"acodecs": mka_audio,
|
281
|
-
},
|
282
|
-
"mov": {
|
283
|
-
"allow_video": True,
|
284
|
-
"allow_audio": True,
|
285
|
-
"allow_subtitle": True,
|
286
|
-
"vcodecs": h264_en
|
287
|
-
+ hevc_en
|
288
|
-
+ prores_en
|
289
|
-
+ [
|
290
|
-
"mpeg4",
|
291
|
-
"mpeg2video",
|
292
|
-
"msmpeg4v3",
|
293
|
-
"mjpeg",
|
294
|
-
"gif",
|
295
|
-
"flv1",
|
296
|
-
"dvvideo",
|
297
|
-
"rawvideo",
|
298
|
-
],
|
299
|
-
"acodecs": aac_en
|
300
|
-
+ [
|
301
|
-
"mp3",
|
302
|
-
"vorbis",
|
303
|
-
"libvorbis",
|
304
|
-
"ac3",
|
305
|
-
"mp2",
|
306
|
-
"wmav2",
|
307
|
-
"pcm_s16le",
|
308
|
-
"pcm_alaw",
|
309
|
-
"pcm_f32be",
|
310
|
-
"pcm_f32le",
|
311
|
-
"pcm_f64be",
|
312
|
-
"pcm_f64le",
|
313
|
-
"pcm_mulaw",
|
314
|
-
"pcm_s16be",
|
315
|
-
"pcm_s24be",
|
316
|
-
"pcm_s24le",
|
317
|
-
"pcm_s32be",
|
318
|
-
"pcm_s32le",
|
319
|
-
"pcm_s8",
|
320
|
-
"pcm_u8",
|
321
|
-
],
|
322
|
-
"disallow_v": ["apng", "vp9", "vp8"],
|
323
|
-
},
|
324
|
-
"swf": {
|
325
|
-
"allow_video": True,
|
326
|
-
"allow_audio": True,
|
327
|
-
"vcodecs": ["flv1", "mjpeg"],
|
328
|
-
"acodecs": ["mp3"],
|
329
|
-
"vstrict": True,
|
330
|
-
"samplerate": [44100, 22050, 11025],
|
331
|
-
},
|
332
|
-
}
|
49
|
+
def codec_type(x: str) -> str:
|
50
|
+
if x in ("vp9", "vp8", "h264", "hevc", "av1", "gif", "apng"):
|
51
|
+
return "video"
|
52
|
+
if x in ("aac", "flac", "mp3"):
|
53
|
+
return "audio"
|
54
|
+
if x in ("ass", "ssa", "srt"):
|
55
|
+
return "subtitle"
|
56
|
+
|
57
|
+
try:
|
58
|
+
return Codec(x, "r").type
|
59
|
+
except Exception:
|
60
|
+
try:
|
61
|
+
return Codec(x, "w").type
|
62
|
+
except Exception:
|
63
|
+
return ""
|
64
|
+
|
65
|
+
|
66
|
+
def container_constructor(ext: str) -> Container:
|
67
|
+
with av.open(f".{ext}", "w") as container:
|
68
|
+
codecs = container.supported_codecs
|
69
|
+
if ext == "webm":
|
70
|
+
vdefault = "vp9"
|
71
|
+
else:
|
72
|
+
vdefault = container.default_video_codec
|
73
|
+
adefault = container.default_audio_codec
|
74
|
+
sdefault = container.default_subtitle_codec
|
75
|
+
|
76
|
+
vcodecs = set()
|
77
|
+
acodecs = set()
|
78
|
+
scodecs = set()
|
79
|
+
|
80
|
+
for codec in codecs:
|
81
|
+
kind = codec_type(codec)
|
82
|
+
if kind == "video":
|
83
|
+
vcodecs.add(codec)
|
84
|
+
if codec == "h264":
|
85
|
+
vcodecs.add("libx264")
|
86
|
+
if kind == "audio":
|
87
|
+
acodecs.add(codec)
|
88
|
+
if kind == "subtitle":
|
89
|
+
scodecs.add(codec)
|
333
90
|
|
91
|
+
allow_image = ext in ("mp4", "mkv")
|
92
|
+
kwargs = containers[ext] if ext in containers else {}
|
334
93
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
return Container(allow_video=True, allow_audio=True, allow_subtitle=True)
|
94
|
+
return Container(
|
95
|
+
allow_image, vcodecs, acodecs, scodecs, vdefault, adefault, sdefault, **kwargs
|
96
|
+
)
|