auto-editor 28.0.2__py3-none-any.whl → 29.0.0__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-28.0.2.dist-info → auto_editor-29.0.0.dist-info}/METADATA +5 -4
- auto_editor-29.0.0.dist-info/RECORD +5 -0
- auto_editor-29.0.0.dist-info/top_level.txt +1 -0
- auto_editor/__init__.py +0 -1
- auto_editor/__main__.py +0 -503
- auto_editor/analyze.py +0 -393
- auto_editor/cmds/__init__.py +0 -0
- auto_editor/cmds/cache.py +0 -69
- auto_editor/cmds/desc.py +0 -32
- auto_editor/cmds/info.py +0 -213
- auto_editor/cmds/levels.py +0 -199
- auto_editor/cmds/palet.py +0 -29
- auto_editor/cmds/repl.py +0 -113
- auto_editor/cmds/subdump.py +0 -72
- auto_editor/cmds/test.py +0 -812
- auto_editor/edit.py +0 -548
- auto_editor/exports/__init__.py +0 -0
- auto_editor/exports/fcp11.py +0 -195
- auto_editor/exports/fcp7.py +0 -313
- auto_editor/exports/json.py +0 -63
- auto_editor/exports/shotcut.py +0 -147
- auto_editor/ffwrapper.py +0 -187
- auto_editor/help.py +0 -223
- auto_editor/imports/__init__.py +0 -0
- auto_editor/imports/fcp7.py +0 -275
- auto_editor/imports/json.py +0 -234
- auto_editor/json.py +0 -297
- auto_editor/lang/__init__.py +0 -0
- auto_editor/lang/libintrospection.py +0 -10
- auto_editor/lang/libmath.py +0 -23
- auto_editor/lang/palet.py +0 -724
- auto_editor/lang/stdenv.py +0 -1184
- auto_editor/lib/__init__.py +0 -0
- auto_editor/lib/contracts.py +0 -235
- auto_editor/lib/data_structs.py +0 -278
- auto_editor/lib/err.py +0 -2
- auto_editor/make_layers.py +0 -315
- auto_editor/preview.py +0 -93
- auto_editor/render/__init__.py +0 -0
- auto_editor/render/audio.py +0 -517
- auto_editor/render/subtitle.py +0 -205
- auto_editor/render/video.py +0 -312
- auto_editor/timeline.py +0 -331
- auto_editor/utils/__init__.py +0 -0
- auto_editor/utils/bar.py +0 -142
- auto_editor/utils/chunks.py +0 -2
- auto_editor/utils/cmdkw.py +0 -206
- auto_editor/utils/container.py +0 -102
- auto_editor/utils/func.py +0 -128
- auto_editor/utils/log.py +0 -124
- auto_editor/utils/types.py +0 -277
- auto_editor/vanparse.py +0 -313
- auto_editor-28.0.2.dist-info/RECORD +0 -56
- auto_editor-28.0.2.dist-info/entry_points.txt +0 -6
- auto_editor-28.0.2.dist-info/top_level.txt +0 -2
- docs/build.py +0 -70
- {auto_editor-28.0.2.dist-info → auto_editor-29.0.0.dist-info}/WHEEL +0 -0
- {auto_editor-28.0.2.dist-info → auto_editor-29.0.0.dist-info}/licenses/LICENSE +0 -0
auto_editor/imports/fcp7.py
DELETED
@@ -1,275 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import xml.etree.ElementTree as ET
|
4
|
-
from fractions import Fraction
|
5
|
-
from typing import TYPE_CHECKING
|
6
|
-
from urllib.parse import unquote
|
7
|
-
from xml.etree.ElementTree import Element
|
8
|
-
|
9
|
-
from auto_editor.ffwrapper import FileInfo
|
10
|
-
from auto_editor.timeline import ASpace, Clip, Template, VSpace, v3
|
11
|
-
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
from auto_editor.utils.log import Log
|
14
|
-
|
15
|
-
|
16
|
-
SUPPORTED_EFFECTS = ("timeremap",)
|
17
|
-
|
18
|
-
|
19
|
-
def show(ele: Element, limit: int, depth: int = 0) -> None:
|
20
|
-
print(
|
21
|
-
f"{' ' * (depth * 4)}<{ele.tag} {ele.attrib}> {ele.text.strip() if ele.text is not None else ''}"
|
22
|
-
)
|
23
|
-
for child in ele:
|
24
|
-
if isinstance(child, Element) and depth < limit:
|
25
|
-
show(child, limit, depth + 1)
|
26
|
-
|
27
|
-
|
28
|
-
def read_filters(clipitem: Element, log: Log) -> float:
|
29
|
-
for effect_tag in clipitem:
|
30
|
-
if effect_tag.tag in {"enabled", "start", "end"}:
|
31
|
-
continue
|
32
|
-
if len(effect_tag) < 3:
|
33
|
-
log.error("<effect> requires: <effectid> <name> and one <parameter>")
|
34
|
-
for i, effects in enumerate(effect_tag):
|
35
|
-
if i == 0 and effects.tag != "name":
|
36
|
-
log.error("<effect>: <name> must be first tag")
|
37
|
-
if i == 1 and effects.tag != "effectid":
|
38
|
-
log.error("<effect>: <effectid> must be second tag")
|
39
|
-
if effects.text not in SUPPORTED_EFFECTS:
|
40
|
-
log.error(f"`{effects.text}` is not a supported effect.")
|
41
|
-
|
42
|
-
if i > 1:
|
43
|
-
for j, parms in enumerate(effects):
|
44
|
-
if j == 0:
|
45
|
-
if parms.tag != "parameterid":
|
46
|
-
log.error("<parameter>: <parameterid> must be first tag")
|
47
|
-
if parms.text != "speed":
|
48
|
-
break
|
49
|
-
|
50
|
-
if j > 0 and parms.tag == "value":
|
51
|
-
if parms.text is None:
|
52
|
-
log.error("<value>: number required")
|
53
|
-
return float(parms.text) / 100
|
54
|
-
|
55
|
-
return 1.0
|
56
|
-
|
57
|
-
|
58
|
-
def uri_to_path(uri: str) -> str:
|
59
|
-
# Handle inputs like:
|
60
|
-
# /Users/wyattblue/projects/auto-editor/example.mp4
|
61
|
-
# file:///Users/wyattblue/projects/auto-editor/example.mp4
|
62
|
-
# file:///C:/Users/WyattBlue/projects/auto-editor/example.mp4
|
63
|
-
# file://localhost/Users/wyattblue/projects/auto-editor/example.mp4
|
64
|
-
|
65
|
-
if uri.startswith("file://localhost/"):
|
66
|
-
uri = uri[16:]
|
67
|
-
elif uri.startswith("file://"):
|
68
|
-
# Windows-style paths
|
69
|
-
uri = uri[8:] if len(uri) > 8 and uri[9] == ":" else uri[7:]
|
70
|
-
else:
|
71
|
-
return uri
|
72
|
-
return unquote(uri)
|
73
|
-
|
74
|
-
|
75
|
-
def read_tb_ntsc(tb: int, ntsc: bool) -> Fraction:
|
76
|
-
if ntsc:
|
77
|
-
if tb == 24:
|
78
|
-
return Fraction(24000, 1001)
|
79
|
-
if tb == 30:
|
80
|
-
return Fraction(30000, 1001)
|
81
|
-
if tb == 60:
|
82
|
-
return Fraction(60000, 1001)
|
83
|
-
return tb * Fraction(999, 1000)
|
84
|
-
|
85
|
-
return Fraction(tb)
|
86
|
-
|
87
|
-
|
88
|
-
def fcp7_read_xml(path: str, log: Log) -> v3:
|
89
|
-
def xml_bool(val: str) -> bool:
|
90
|
-
if val == "TRUE":
|
91
|
-
return True
|
92
|
-
if val == "FALSE":
|
93
|
-
return False
|
94
|
-
raise TypeError("Value must be 'TRUE' or 'FALSE'")
|
95
|
-
|
96
|
-
try:
|
97
|
-
tree = ET.parse(path)
|
98
|
-
except FileNotFoundError:
|
99
|
-
log.error(f"Could not find '{path}'")
|
100
|
-
|
101
|
-
root = tree.getroot()
|
102
|
-
|
103
|
-
def parse(ele: Element, schema: dict) -> dict:
|
104
|
-
new: dict = {}
|
105
|
-
for key, val in schema.items():
|
106
|
-
if isinstance(val, dict) and "__arr" in val:
|
107
|
-
new[key] = []
|
108
|
-
|
109
|
-
is_arr = False
|
110
|
-
for child in ele:
|
111
|
-
if child.tag not in schema:
|
112
|
-
continue
|
113
|
-
|
114
|
-
if schema[child.tag] is None:
|
115
|
-
new[child.tag] = child
|
116
|
-
continue
|
117
|
-
|
118
|
-
if isinstance(schema[child.tag], dict):
|
119
|
-
val = parse(child, schema[child.tag])
|
120
|
-
is_arr = "__arr" in schema[child.tag]
|
121
|
-
else:
|
122
|
-
val = schema[child.tag](child.text)
|
123
|
-
|
124
|
-
if child.tag in new:
|
125
|
-
if not is_arr:
|
126
|
-
log.error(f"<{child.tag}> can only occur once")
|
127
|
-
new[child.tag].append(val)
|
128
|
-
else:
|
129
|
-
new[child.tag] = [val] if is_arr else val
|
130
|
-
|
131
|
-
return new
|
132
|
-
|
133
|
-
def check(ele: Element, tag: str) -> None:
|
134
|
-
if tag != ele.tag:
|
135
|
-
log.error(f"Expected '{tag}' tag, got '{ele.tag}'")
|
136
|
-
|
137
|
-
check(root, "xmeml")
|
138
|
-
check(root[0], "sequence")
|
139
|
-
result = parse(
|
140
|
-
root[0],
|
141
|
-
{
|
142
|
-
"name": str,
|
143
|
-
"duration": int,
|
144
|
-
"rate": {
|
145
|
-
"timebase": Fraction,
|
146
|
-
"ntsc": xml_bool,
|
147
|
-
},
|
148
|
-
"media": None,
|
149
|
-
},
|
150
|
-
)
|
151
|
-
|
152
|
-
tb = read_tb_ntsc(result["rate"]["timebase"], result["rate"]["ntsc"])
|
153
|
-
av = parse(
|
154
|
-
result["media"],
|
155
|
-
{
|
156
|
-
"video": None,
|
157
|
-
"audio": None,
|
158
|
-
},
|
159
|
-
)
|
160
|
-
|
161
|
-
sources: dict[str, FileInfo] = {}
|
162
|
-
vobjs: VSpace = []
|
163
|
-
aobjs: ASpace = []
|
164
|
-
|
165
|
-
vclip_schema = {
|
166
|
-
"format": {
|
167
|
-
"samplecharacteristics": {
|
168
|
-
"width": int,
|
169
|
-
"height": int,
|
170
|
-
},
|
171
|
-
},
|
172
|
-
"track": {
|
173
|
-
"__arr": "",
|
174
|
-
"clipitem": {
|
175
|
-
"__arr": "",
|
176
|
-
"start": int,
|
177
|
-
"end": int,
|
178
|
-
"in": int,
|
179
|
-
"out": int,
|
180
|
-
"file": None,
|
181
|
-
"filter": None,
|
182
|
-
},
|
183
|
-
},
|
184
|
-
}
|
185
|
-
|
186
|
-
aclip_schema = {
|
187
|
-
"format": {"samplecharacteristics": {"samplerate": int}},
|
188
|
-
"track": {
|
189
|
-
"__arr": "",
|
190
|
-
"clipitem": {
|
191
|
-
"__arr": "",
|
192
|
-
"start": int,
|
193
|
-
"end": int,
|
194
|
-
"in": int,
|
195
|
-
"out": int,
|
196
|
-
"file": None,
|
197
|
-
"filter": None,
|
198
|
-
},
|
199
|
-
},
|
200
|
-
}
|
201
|
-
|
202
|
-
sr = 48000
|
203
|
-
res = (1920, 1080)
|
204
|
-
|
205
|
-
if "video" in av:
|
206
|
-
tracks = parse(av["video"], vclip_schema)
|
207
|
-
|
208
|
-
if "format" in tracks:
|
209
|
-
width = tracks["format"]["samplecharacteristics"]["width"]
|
210
|
-
height = tracks["format"]["samplecharacteristics"]["height"]
|
211
|
-
res = width, height
|
212
|
-
|
213
|
-
for t, track in enumerate(tracks["track"]):
|
214
|
-
if len(track["clipitem"]) > 0:
|
215
|
-
vobjs.append([])
|
216
|
-
for clipitem in track["clipitem"]:
|
217
|
-
file_id = clipitem["file"].attrib["id"]
|
218
|
-
if file_id not in sources:
|
219
|
-
fileobj = parse(clipitem["file"], {"pathurl": str})
|
220
|
-
|
221
|
-
if "pathurl" in fileobj:
|
222
|
-
sources[file_id] = FileInfo.init(
|
223
|
-
uri_to_path(fileobj["pathurl"]),
|
224
|
-
log,
|
225
|
-
)
|
226
|
-
else:
|
227
|
-
show(clipitem["file"], 3)
|
228
|
-
log.error(
|
229
|
-
f"'pathurl' child element not found in {clipitem['file'].tag}"
|
230
|
-
)
|
231
|
-
|
232
|
-
if "filter" in clipitem:
|
233
|
-
speed = read_filters(clipitem["filter"], log)
|
234
|
-
else:
|
235
|
-
speed = 1.0
|
236
|
-
|
237
|
-
start = clipitem["start"]
|
238
|
-
dur = clipitem["end"] - start
|
239
|
-
offset = clipitem["in"]
|
240
|
-
|
241
|
-
vobjs[t].append(
|
242
|
-
Clip(start, dur, sources[file_id], offset, stream=0, speed=speed)
|
243
|
-
)
|
244
|
-
|
245
|
-
if "audio" in av:
|
246
|
-
tracks = parse(av["audio"], aclip_schema)
|
247
|
-
if "format" in tracks:
|
248
|
-
sr = tracks["format"]["samplecharacteristics"]["samplerate"]
|
249
|
-
|
250
|
-
for t, track in enumerate(tracks["track"]):
|
251
|
-
if len(track["clipitem"]) > 0:
|
252
|
-
aobjs.append([])
|
253
|
-
for clipitem in track["clipitem"]:
|
254
|
-
file_id = clipitem["file"].attrib["id"]
|
255
|
-
if file_id not in sources:
|
256
|
-
fileobj = parse(clipitem["file"], {"pathurl": str})
|
257
|
-
sources[file_id] = FileInfo.init(
|
258
|
-
uri_to_path(fileobj["pathurl"]), log
|
259
|
-
)
|
260
|
-
|
261
|
-
if "filter" in clipitem:
|
262
|
-
speed = read_filters(clipitem["filter"], log)
|
263
|
-
else:
|
264
|
-
speed = 1.0
|
265
|
-
|
266
|
-
start = clipitem["start"]
|
267
|
-
dur = clipitem["end"] - start
|
268
|
-
offset = clipitem["in"]
|
269
|
-
|
270
|
-
aobjs[t].append(
|
271
|
-
Clip(start, dur, sources[file_id], offset, stream=0, speed=speed)
|
272
|
-
)
|
273
|
-
|
274
|
-
T = Template.init(sources[next(iter(sources))], sr, res=res)
|
275
|
-
return v3(tb, "#000", T, vobjs, aobjs, v1=None)
|
auto_editor/imports/json.py
DELETED
@@ -1,234 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import os
|
4
|
-
from difflib import get_close_matches
|
5
|
-
from fractions import Fraction
|
6
|
-
from typing import TYPE_CHECKING, Any
|
7
|
-
|
8
|
-
from auto_editor.ffwrapper import FileInfo
|
9
|
-
from auto_editor.json import load
|
10
|
-
from auto_editor.lib.err import MyError
|
11
|
-
from auto_editor.timeline import (
|
12
|
-
Clip,
|
13
|
-
Template,
|
14
|
-
audio_builder,
|
15
|
-
v1,
|
16
|
-
v3,
|
17
|
-
visual_objects,
|
18
|
-
)
|
19
|
-
from auto_editor.utils.cmdkw import ParserError, Required, pAttrs
|
20
|
-
from auto_editor.utils.types import CoerceError
|
21
|
-
|
22
|
-
if TYPE_CHECKING:
|
23
|
-
from auto_editor.timeline import ASpace, VSpace
|
24
|
-
from auto_editor.utils.log import Log
|
25
|
-
|
26
|
-
|
27
|
-
def check_attrs(data: object, log: Log, *attrs: str) -> None:
|
28
|
-
if not isinstance(data, dict):
|
29
|
-
log.error("Data is in wrong shape!")
|
30
|
-
|
31
|
-
for attr in attrs:
|
32
|
-
if attr not in data:
|
33
|
-
log.error(f"'{attr}' attribute not found!")
|
34
|
-
|
35
|
-
|
36
|
-
def check_file(path: str, log: Log) -> None:
|
37
|
-
if not os.path.isfile(path):
|
38
|
-
log.error(f"Could not locate media file: '{path}'")
|
39
|
-
|
40
|
-
|
41
|
-
def read_v3(tl: Any, log: Log) -> v3:
|
42
|
-
check_attrs(
|
43
|
-
tl,
|
44
|
-
log,
|
45
|
-
"background",
|
46
|
-
"v",
|
47
|
-
"a",
|
48
|
-
"timebase",
|
49
|
-
"resolution",
|
50
|
-
"samplerate",
|
51
|
-
)
|
52
|
-
|
53
|
-
srcs: dict[str, FileInfo] = {}
|
54
|
-
|
55
|
-
def make_src(v: str) -> FileInfo:
|
56
|
-
if v in srcs:
|
57
|
-
return srcs[v]
|
58
|
-
temp = FileInfo.init(v, log)
|
59
|
-
srcs[v] = temp
|
60
|
-
return temp
|
61
|
-
|
62
|
-
def parse_obj(obj: dict[str, Any], build: pAttrs) -> dict[str, Any]:
|
63
|
-
kwargs: dict[str, Any] = {}
|
64
|
-
del obj["name"]
|
65
|
-
|
66
|
-
for attr in build.attrs:
|
67
|
-
assert attr.coerce is not None
|
68
|
-
if attr.default is Required:
|
69
|
-
kwargs[attr.n] = Required
|
70
|
-
else:
|
71
|
-
assert attr.coerce != "source"
|
72
|
-
kwargs[attr.n] = attr.coerce(attr.default)
|
73
|
-
|
74
|
-
for key, val in obj.items():
|
75
|
-
found = False
|
76
|
-
for attr in build.attrs:
|
77
|
-
if key == attr.n:
|
78
|
-
try:
|
79
|
-
if attr.coerce == "source":
|
80
|
-
kwargs[key] = make_src(val)
|
81
|
-
else:
|
82
|
-
assert attr.coerce is not None
|
83
|
-
kwargs[key] = attr.coerce(val)
|
84
|
-
except CoerceError as e:
|
85
|
-
raise ParserError(e)
|
86
|
-
found = True
|
87
|
-
break
|
88
|
-
|
89
|
-
if not found:
|
90
|
-
all_names = {attr.n for attr in build.attrs}
|
91
|
-
if matches := get_close_matches(key, all_names):
|
92
|
-
more = f"\n Did you mean:\n {', '.join(matches)}"
|
93
|
-
else:
|
94
|
-
more = (
|
95
|
-
f"\n attributes available:\n {', '.join(all_names)}"
|
96
|
-
)
|
97
|
-
|
98
|
-
raise ParserError(
|
99
|
-
f"{build.name} got an unexpected keyword '{key}'\n{more}"
|
100
|
-
)
|
101
|
-
|
102
|
-
for k, v in kwargs.items():
|
103
|
-
if v is Required:
|
104
|
-
raise ParserError(f"'{k}' must be specified.")
|
105
|
-
|
106
|
-
return kwargs
|
107
|
-
|
108
|
-
bg = tl["background"]
|
109
|
-
sr = tl["samplerate"]
|
110
|
-
res = (tl["resolution"][0], tl["resolution"][1])
|
111
|
-
tb = Fraction(tl["timebase"])
|
112
|
-
|
113
|
-
v: Any = []
|
114
|
-
a: list[list[Clip]] = []
|
115
|
-
|
116
|
-
for vlayers in tl["v"]:
|
117
|
-
if vlayers:
|
118
|
-
v_out: VSpace = []
|
119
|
-
for vdict in vlayers:
|
120
|
-
if "name" not in vdict:
|
121
|
-
log.error("Invalid video object: name not specified")
|
122
|
-
if vdict["name"] not in visual_objects:
|
123
|
-
log.error(f"Unknown video object: {vdict['name']}")
|
124
|
-
my_vobj, my_build = visual_objects[vdict["name"]]
|
125
|
-
|
126
|
-
try:
|
127
|
-
v_out.append(my_vobj(**parse_obj(vdict, my_build)))
|
128
|
-
except ParserError as e:
|
129
|
-
log.error(e)
|
130
|
-
|
131
|
-
v.append(v_out)
|
132
|
-
|
133
|
-
for alayers in tl["a"]:
|
134
|
-
if alayers:
|
135
|
-
a_out = []
|
136
|
-
for adict in alayers:
|
137
|
-
if "name" not in adict:
|
138
|
-
log.error("Invalid audio object: name not specified")
|
139
|
-
if adict["name"] != "audio":
|
140
|
-
log.error(f"Unknown audio object: {adict['name']}")
|
141
|
-
|
142
|
-
try:
|
143
|
-
a_out.append(Clip(**parse_obj(adict, audio_builder)))
|
144
|
-
except ParserError as e:
|
145
|
-
log.error(e)
|
146
|
-
|
147
|
-
a.append(a_out)
|
148
|
-
|
149
|
-
try:
|
150
|
-
T = Template.init(srcs[next(iter(srcs))])
|
151
|
-
except StopIteration:
|
152
|
-
T = Template(sr, "stereo", res, [], [])
|
153
|
-
|
154
|
-
return v3(tb, bg, T, v, a, v1=None)
|
155
|
-
|
156
|
-
|
157
|
-
def read_v1(tl: Any, log: Log) -> v3:
|
158
|
-
from auto_editor.make_layers import clipify
|
159
|
-
|
160
|
-
check_attrs(tl, log, "source", "chunks")
|
161
|
-
|
162
|
-
chunks = tl["chunks"]
|
163
|
-
path = tl["source"]
|
164
|
-
|
165
|
-
check_file(path, log)
|
166
|
-
|
167
|
-
src = FileInfo.init(path, log)
|
168
|
-
|
169
|
-
vtl: VSpace = []
|
170
|
-
atl: ASpace = [[] for _ in range(len(src.audios))]
|
171
|
-
|
172
|
-
# Verify chunks
|
173
|
-
last_end: int | None = None
|
174
|
-
if type(chunks) is not list:
|
175
|
-
log.error("chunks key must be an array")
|
176
|
-
|
177
|
-
for i, chunk in enumerate(chunks):
|
178
|
-
if type(chunk) is not list or len(chunk) != 3:
|
179
|
-
log.error(f"Invalid chunk at chunk {i}")
|
180
|
-
if type(chunk[0]) not in (int, float) or chunk[0] < 0:
|
181
|
-
log.error(f"Invalid start at chunk {i}")
|
182
|
-
if type(chunk[1]) not in (int, float) or chunk[1] <= chunk[0]:
|
183
|
-
log.error(f"Invalid end at chunk {i}")
|
184
|
-
if type(chunk[2]) not in (int, float) or chunk[2] < 0.0 or chunk[2] > 99999.0:
|
185
|
-
log.error(f"Invalid speed at chunk {i}")
|
186
|
-
|
187
|
-
if i == 0 and chunk[0] != 0:
|
188
|
-
log.error("First chunk must start with 0")
|
189
|
-
if i != 0 and chunk[0] != last_end:
|
190
|
-
log.error(f"Invalid start at chunk {i}")
|
191
|
-
last_end = chunk[1]
|
192
|
-
|
193
|
-
if type(chunk[0]) is float or type(chunk[1]) is float or type(chunk[2]) is int:
|
194
|
-
chunks[i] = (int(chunk[0]), int(chunk[1]), float(chunk[2]))
|
195
|
-
|
196
|
-
for c in clipify(chunks, src):
|
197
|
-
if src.videos:
|
198
|
-
if len(vtl) == 0:
|
199
|
-
vtl.append([])
|
200
|
-
vtl[0].append(Clip(c.start, c.dur, c.src, c.offset, 0, c.speed))
|
201
|
-
|
202
|
-
for a in range(len(src.audios)):
|
203
|
-
atl[a].append(Clip(c.start, c.dur, c.src, c.offset, a, c.speed))
|
204
|
-
|
205
|
-
return v3(
|
206
|
-
src.get_fps(),
|
207
|
-
"#000",
|
208
|
-
Template.init(src),
|
209
|
-
vtl,
|
210
|
-
atl,
|
211
|
-
v1(src, chunks),
|
212
|
-
)
|
213
|
-
|
214
|
-
|
215
|
-
def read_json(path: str, log: Log) -> v3:
|
216
|
-
try:
|
217
|
-
with open(path, encoding="utf-8", errors="ignore") as f:
|
218
|
-
tl = load(path, f)
|
219
|
-
except FileNotFoundError:
|
220
|
-
log.error(f"File not found: {path}")
|
221
|
-
except MyError as e:
|
222
|
-
log.error(e)
|
223
|
-
|
224
|
-
check_attrs(tl, log, "version")
|
225
|
-
|
226
|
-
ver = tl["version"]
|
227
|
-
|
228
|
-
if ver == "3":
|
229
|
-
return read_v3(tl, log)
|
230
|
-
if ver == "1":
|
231
|
-
return read_v1(tl, log)
|
232
|
-
if type(ver) is not str:
|
233
|
-
log.error("version needs to be a string")
|
234
|
-
log.error(f"Importing version {ver} timelines is not supported.")
|