auto-editor 27.1.0__py3-none-any.whl → 28.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.
@@ -2,15 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import xml.etree.ElementTree as ET
4
4
  from fractions import Fraction
5
- from io import StringIO
6
5
  from math import ceil
7
6
  from typing import TYPE_CHECKING
8
7
  from xml.etree.ElementTree import Element
9
8
 
10
9
  from auto_editor.ffwrapper import FileInfo
11
- from auto_editor.timeline import ASpace, Template, TlAudio, TlVideo, VSpace, v3
12
-
13
- from .utils import Validator, show
10
+ from auto_editor.timeline import TlVideo, v3
14
11
 
15
12
  if TYPE_CHECKING:
16
13
  from auto_editor.utils.log import Log
@@ -28,67 +25,6 @@ come back the way they started.
28
25
  DEPTH = "16"
29
26
 
30
27
 
31
- def uri_to_path(uri: str) -> str:
32
- def de_norm(s: str) -> str:
33
- uri_escape = {
34
- "3C": "<",
35
- "3E": ">",
36
- "23": "#",
37
- "25": "%",
38
- "2B": "+",
39
- "7B": "{",
40
- "7D": "}",
41
- "7C": "|",
42
- "5C": "\\",
43
- "5E": "^",
44
- "7E": "~",
45
- "5B": "[",
46
- "5D": "]",
47
- "60": "`",
48
- "3F": "?",
49
- "3A": ":",
50
- "40": "@",
51
- "3D": "=",
52
- "2A": "*",
53
- "29": ")",
54
- "28": "(",
55
- "27": "'",
56
- "26": "&",
57
- "24": "$",
58
- "22": '"',
59
- "21": "!",
60
- "20": " ",
61
- }
62
- buf = StringIO()
63
- i = 0
64
- while i < len(s):
65
- if s[i] == "%" and len(s) > i + 3:
66
- tag = s[i + 1 : i + 3]
67
- if tag in uri_escape:
68
- buf.write(uri_escape[tag])
69
- i += 3
70
- else:
71
- buf.write(s[i])
72
- i += 1
73
- else:
74
- buf.write(s[i])
75
- i += 1
76
- return buf.getvalue()
77
-
78
- if uri.startswith("file://localhost/"):
79
- return de_norm(uri[16:])
80
- if uri.startswith("file://"):
81
- if uri[9] == ":": # Handle Windows-style paths
82
- return de_norm(uri[8:])
83
- return de_norm(uri[7:])
84
- return uri
85
-
86
- # /Users/wyattblue/projects/auto-editor/example.mp4
87
- # file:///Users/wyattblue/projects/auto-editor/example.mp4
88
- # file:///C:/Users/WyattBlue/projects/auto-editor/example.mp4
89
- # file://localhost/Users/wyattblue/projects/auto-editor/example.mp4
90
-
91
-
92
28
  def set_tb_ntsc(tb: Fraction) -> tuple[int, str]:
93
29
  # See chart: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/FinalCutPro_XML/FrameRate/FrameRate.html#//apple_ref/doc/uid/TP30001158-TPXREF103
94
30
  if tb == Fraction(24000, 1001):
@@ -105,19 +41,6 @@ def set_tb_ntsc(tb: Fraction) -> tuple[int, str]:
105
41
  return int(tb), "FALSE"
106
42
 
107
43
 
108
- def read_tb_ntsc(tb: int, ntsc: bool) -> Fraction:
109
- if ntsc:
110
- if tb == 24:
111
- return Fraction(24000, 1001)
112
- if tb == 30:
113
- return Fraction(30000, 1001)
114
- if tb == 60:
115
- return Fraction(60000, 1001)
116
- return tb * Fraction(999, 1000)
117
-
118
- return Fraction(tb)
119
-
120
-
121
44
  def speedup(speed: float) -> Element:
122
45
  fil = Element("filter")
123
46
  effect = ET.SubElement(fil, "effect")
@@ -179,167 +102,6 @@ def read_filters(clipitem: Element, log: Log) -> float:
179
102
  return 1.0
180
103
 
181
104
 
182
- def fcp7_read_xml(path: str, log: Log) -> v3:
183
- def xml_bool(val: str) -> bool:
184
- if val == "TRUE":
185
- return True
186
- if val == "FALSE":
187
- return False
188
- raise TypeError("Value must be 'TRUE' or 'FALSE'")
189
-
190
- try:
191
- tree = ET.parse(path)
192
- except FileNotFoundError:
193
- log.error(f"Could not find '{path}'")
194
-
195
- root = tree.getroot()
196
-
197
- valid = Validator(log)
198
-
199
- valid.check(root, "xmeml")
200
- valid.check(root[0], "sequence")
201
- result = valid.parse(
202
- root[0],
203
- {
204
- "name": str,
205
- "duration": int,
206
- "rate": {
207
- "timebase": Fraction,
208
- "ntsc": xml_bool,
209
- },
210
- "media": None,
211
- },
212
- )
213
-
214
- tb = read_tb_ntsc(result["rate"]["timebase"], result["rate"]["ntsc"])
215
-
216
- av = valid.parse(
217
- result["media"],
218
- {
219
- "video": None,
220
- "audio": None,
221
- },
222
- )
223
-
224
- sources: dict[str, FileInfo] = {}
225
- vobjs: VSpace = []
226
- aobjs: ASpace = []
227
-
228
- vclip_schema = {
229
- "format": {
230
- "samplecharacteristics": {
231
- "width": int,
232
- "height": int,
233
- },
234
- },
235
- "track": {
236
- "__arr": "",
237
- "clipitem": {
238
- "__arr": "",
239
- "start": int,
240
- "end": int,
241
- "in": int,
242
- "out": int,
243
- "file": None,
244
- "filter": None,
245
- },
246
- },
247
- }
248
-
249
- aclip_schema = {
250
- "format": {"samplecharacteristics": {"samplerate": int}},
251
- "track": {
252
- "__arr": "",
253
- "clipitem": {
254
- "__arr": "",
255
- "start": int,
256
- "end": int,
257
- "in": int,
258
- "out": int,
259
- "file": None,
260
- "filter": None,
261
- },
262
- },
263
- }
264
-
265
- sr = 48000
266
- res = (1920, 1080)
267
-
268
- if "video" in av:
269
- tracks = valid.parse(av["video"], vclip_schema)
270
-
271
- if "format" in tracks:
272
- width = tracks["format"]["samplecharacteristics"]["width"]
273
- height = tracks["format"]["samplecharacteristics"]["height"]
274
- res = width, height
275
-
276
- for t, track in enumerate(tracks["track"]):
277
- if len(track["clipitem"]) > 0:
278
- vobjs.append([])
279
- for i, clipitem in enumerate(track["clipitem"]):
280
- file_id = clipitem["file"].attrib["id"]
281
- if file_id not in sources:
282
- fileobj = valid.parse(clipitem["file"], {"pathurl": str})
283
-
284
- if "pathurl" in fileobj:
285
- sources[file_id] = FileInfo.init(
286
- uri_to_path(fileobj["pathurl"]),
287
- log,
288
- )
289
- else:
290
- show(clipitem["file"], 3)
291
- log.error(
292
- f"'pathurl' child element not found in {clipitem['file'].tag}"
293
- )
294
-
295
- if "filter" in clipitem:
296
- speed = read_filters(clipitem["filter"], log)
297
- else:
298
- speed = 1.0
299
-
300
- start = clipitem["start"]
301
- dur = clipitem["end"] - start
302
- offset = clipitem["in"]
303
-
304
- vobjs[t].append(
305
- TlVideo(start, dur, sources[file_id], offset, speed, stream=0)
306
- )
307
-
308
- if "audio" in av:
309
- tracks = valid.parse(av["audio"], aclip_schema)
310
- if "format" in tracks:
311
- sr = tracks["format"]["samplecharacteristics"]["samplerate"]
312
-
313
- for t, track in enumerate(tracks["track"]):
314
- if len(track["clipitem"]) > 0:
315
- aobjs.append([])
316
- for i, clipitem in enumerate(track["clipitem"]):
317
- file_id = clipitem["file"].attrib["id"]
318
- if file_id not in sources:
319
- fileobj = valid.parse(clipitem["file"], {"pathurl": str})
320
- sources[file_id] = FileInfo.init(
321
- uri_to_path(fileobj["pathurl"]), log
322
- )
323
-
324
- if "filter" in clipitem:
325
- speed = read_filters(clipitem["filter"], log)
326
- else:
327
- speed = 1.0
328
-
329
- start = clipitem["start"]
330
- dur = clipitem["end"] - start
331
- offset = clipitem["in"]
332
-
333
- aobjs[t].append(
334
- TlAudio(
335
- start, dur, sources[file_id], offset, speed, volume=1, stream=0
336
- )
337
- )
338
-
339
- T = Template.init(sources[next(iter(sources))], sr, res=res)
340
- return v3(tb, "#000", T, vobjs, aobjs, v1=None)
341
-
342
-
343
105
  def media_def(
344
106
  filedef: Element, url: str, src: FileInfo, tl: v3, tb: int, ntsc: str
345
107
  ) -> None:
@@ -512,7 +274,7 @@ def fcp7_write_xml(name: str, output: str, resolve: bool, tl: v3) -> None:
512
274
  sequence = ET.SubElement(xmeml, "sequence", explodedTracks="true")
513
275
 
514
276
  ET.SubElement(sequence, "name").text = name
515
- ET.SubElement(sequence, "duration").text = f"{int(tl.out_len())}"
277
+ ET.SubElement(sequence, "duration").text = f"{len(tl)}"
516
278
  rate = ET.SubElement(sequence, "rate")
517
279
  ET.SubElement(rate, "timebase").text = f"{timebase}"
518
280
  ET.SubElement(rate, "ntsc").text = ntsc
@@ -582,4 +344,7 @@ def fcp7_write_xml(name: str, output: str, resolve: bool, tl: v3) -> None:
582
344
 
583
345
  tree = ET.ElementTree(xmeml)
584
346
  ET.indent(tree, space=" ", level=0)
585
- tree.write(output, xml_declaration=True, encoding="utf-8")
347
+ if output == "-":
348
+ print(ET.tostring(xmeml, encoding="unicode"))
349
+ else:
350
+ tree.write(output, xml_declaration=True, encoding="utf-8")
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from typing import TYPE_CHECKING
5
+
6
+ from auto_editor.json import dump
7
+ from auto_editor.timeline import v3
8
+
9
+ if TYPE_CHECKING:
10
+ from auto_editor.utils.log import Log
11
+
12
+
13
+ def make_json_timeline(ver: str, out: str, tl: v3, log: Log) -> None:
14
+ if ver not in {"v1", "v3"}:
15
+ log.error(f"Unknown timeline version: {ver}")
16
+
17
+ if out == "-":
18
+ outfile = sys.stdout
19
+ else:
20
+ outfile = open(out, "w")
21
+
22
+ if ver == "v3":
23
+ dump(tl.as_dict(), outfile, indent=2)
24
+ else:
25
+ if tl.v1 is None:
26
+ log.error("Timeline can't be converted to v1 format")
27
+ dump(tl.v1.as_dict(), outfile, indent=2)
28
+
29
+ if out == "-":
30
+ print("") # Flush stdout
31
+ else:
32
+ outfile.close()
@@ -1,16 +1,12 @@
1
- from __future__ import annotations
2
-
3
1
  import xml.etree.ElementTree as ET
4
2
  from typing import TYPE_CHECKING, Any, cast
5
3
 
6
- from auto_editor.timeline import v3
4
+ from auto_editor.timeline import TlAudio, TlVideo, v3
7
5
  from auto_editor.utils.func import aspect_ratio, to_timecode
8
6
 
9
7
  if TYPE_CHECKING:
10
8
  from collections.abc import Sequence
11
9
 
12
- from auto_editor.timeline import TlAudio, TlVideo
13
- from auto_editor.utils.log import Log
14
10
 
15
11
  """
16
12
  Shotcut uses the MLT timeline format
@@ -21,10 +17,6 @@ https://mltframework.org/docs/mltxml/
21
17
  """
22
18
 
23
19
 
24
- def shotcut_read_mlt(path: str, log: Log) -> v3:
25
- raise NotImplementedError
26
-
27
-
28
20
  def shotcut_write_mlt(output: str, tl: v3) -> None:
29
21
  mlt = ET.Element(
30
22
  "mlt",
@@ -61,7 +53,7 @@ def shotcut_write_mlt(output: str, tl: v3) -> None:
61
53
  playlist_bin = ET.SubElement(mlt, "playlist", id="main_bin")
62
54
  ET.SubElement(playlist_bin, "property", name="xml_retain").text = "1"
63
55
 
64
- global_out = to_timecode(tl.out_len() / tb, "standard")
56
+ global_out = to_timecode(len(tl) / tb, "standard")
65
57
 
66
58
  producer = ET.SubElement(mlt, "producer", id="bg")
67
59
 
@@ -154,4 +146,7 @@ def shotcut_write_mlt(output: str, tl: v3) -> None:
154
146
 
155
147
  ET.indent(tree, space="\t", level=0)
156
148
 
157
- tree.write(output, xml_declaration=True, encoding="utf-8")
149
+ if output == "-":
150
+ print(ET.tostring(mlt, encoding="unicode"))
151
+ else:
152
+ tree.write(output, xml_declaration=True, encoding="utf-8")
auto_editor/ffwrapper.py CHANGED
@@ -66,7 +66,6 @@ class FileInfo:
66
66
  path: Path
67
67
  bitrate: int
68
68
  duration: float
69
- description: str | None
70
69
  videos: tuple[VideoStream, ...]
71
70
  audios: tuple[AudioStream, ...]
72
71
  subtitles: tuple[SubtitleStream, ...]
@@ -166,13 +165,12 @@ class FileInfo:
166
165
  ext = sub_exts.get(codec, "vtt")
167
166
  subtitles += (SubtitleStream(codec, ext, s.language),)
168
167
 
169
- desc = cont.metadata.get("description", None)
170
168
  bitrate = 0 if cont.bit_rate is None else cont.bit_rate
171
169
  dur = 0 if cont.duration is None else cont.duration / bv.time_base
172
170
 
173
171
  cont.close()
174
172
 
175
- return FileInfo(Path(path), bitrate, dur, desc, videos, audios, subtitles)
173
+ return FileInfo(Path(path), bitrate, dur, videos, audios, subtitles)
176
174
 
177
175
  def __repr__(self) -> str:
178
176
  return f"@{self.path.name}"
auto_editor/help.py CHANGED
@@ -71,26 +71,15 @@ This option controls how timelines are exported.
71
71
 
72
72
  Export Methods:
73
73
  - default ; Export as a regular media file
74
-
75
74
  - premiere ; Export as an XML timeline file for Adobe Premiere Pro
76
- - name string? : "Auto-Editor Media Group"
77
-
75
+ - name : "Auto-Editor Media Group"
78
76
  - resolve ; Export as an XML timeline file for DaVinci Resolve
79
- - name string? : "Auto-Editor Media Group"
80
-
77
+ - name : "Auto-Editor Media Group"
81
78
  - final-cut-pro ; Export as an XML timeline file for Final Cut Pro
82
- - name string? : "Auto-Editor Media Group"
83
-
84
- - shotcut ; Export as an XML timeline file for Shotcut
85
-
86
- - json ; Export as an auto-editor JSON timeline file
87
- - api string? : "3"
88
-
89
- - timeline ; Print the auto-editor timeline to stdout
90
- - api string? : "3"
91
-
92
- - audio ; Export as a WAV audio file
93
-
79
+ - name : "Auto-Editor Media Group"
80
+ - shotcut ; Export as an XML timeline file for Shotcut
81
+ - v3 ; Export as an auto-editor v3 timeline file
82
+ - v1 ; Export as an auto-editor v1 timeline file
94
83
  - clip-sequence ; Export as multiple numbered media files
95
84
 
96
85
  """.strip(),
File without changes