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.
Files changed (58) hide show
  1. {auto_editor-28.0.2.dist-info → auto_editor-29.0.0.dist-info}/METADATA +5 -4
  2. auto_editor-29.0.0.dist-info/RECORD +5 -0
  3. auto_editor-29.0.0.dist-info/top_level.txt +1 -0
  4. auto_editor/__init__.py +0 -1
  5. auto_editor/__main__.py +0 -503
  6. auto_editor/analyze.py +0 -393
  7. auto_editor/cmds/__init__.py +0 -0
  8. auto_editor/cmds/cache.py +0 -69
  9. auto_editor/cmds/desc.py +0 -32
  10. auto_editor/cmds/info.py +0 -213
  11. auto_editor/cmds/levels.py +0 -199
  12. auto_editor/cmds/palet.py +0 -29
  13. auto_editor/cmds/repl.py +0 -113
  14. auto_editor/cmds/subdump.py +0 -72
  15. auto_editor/cmds/test.py +0 -812
  16. auto_editor/edit.py +0 -548
  17. auto_editor/exports/__init__.py +0 -0
  18. auto_editor/exports/fcp11.py +0 -195
  19. auto_editor/exports/fcp7.py +0 -313
  20. auto_editor/exports/json.py +0 -63
  21. auto_editor/exports/shotcut.py +0 -147
  22. auto_editor/ffwrapper.py +0 -187
  23. auto_editor/help.py +0 -223
  24. auto_editor/imports/__init__.py +0 -0
  25. auto_editor/imports/fcp7.py +0 -275
  26. auto_editor/imports/json.py +0 -234
  27. auto_editor/json.py +0 -297
  28. auto_editor/lang/__init__.py +0 -0
  29. auto_editor/lang/libintrospection.py +0 -10
  30. auto_editor/lang/libmath.py +0 -23
  31. auto_editor/lang/palet.py +0 -724
  32. auto_editor/lang/stdenv.py +0 -1184
  33. auto_editor/lib/__init__.py +0 -0
  34. auto_editor/lib/contracts.py +0 -235
  35. auto_editor/lib/data_structs.py +0 -278
  36. auto_editor/lib/err.py +0 -2
  37. auto_editor/make_layers.py +0 -315
  38. auto_editor/preview.py +0 -93
  39. auto_editor/render/__init__.py +0 -0
  40. auto_editor/render/audio.py +0 -517
  41. auto_editor/render/subtitle.py +0 -205
  42. auto_editor/render/video.py +0 -312
  43. auto_editor/timeline.py +0 -331
  44. auto_editor/utils/__init__.py +0 -0
  45. auto_editor/utils/bar.py +0 -142
  46. auto_editor/utils/chunks.py +0 -2
  47. auto_editor/utils/cmdkw.py +0 -206
  48. auto_editor/utils/container.py +0 -102
  49. auto_editor/utils/func.py +0 -128
  50. auto_editor/utils/log.py +0 -124
  51. auto_editor/utils/types.py +0 -277
  52. auto_editor/vanparse.py +0 -313
  53. auto_editor-28.0.2.dist-info/RECORD +0 -56
  54. auto_editor-28.0.2.dist-info/entry_points.txt +0 -6
  55. auto_editor-28.0.2.dist-info/top_level.txt +0 -2
  56. docs/build.py +0 -70
  57. {auto_editor-28.0.2.dist-info → auto_editor-29.0.0.dist-info}/WHEEL +0 -0
  58. {auto_editor-28.0.2.dist-info → auto_editor-29.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -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)
@@ -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.")