auto-editor 27.0.0__py3-none-any.whl → 27.1.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/timeline.py CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- from auto_editor.ffwrapper import initFileInfo, mux
6
+ from auto_editor.ffwrapper import FileInfo, mux
7
7
  from auto_editor.lib.contracts import *
8
8
  from auto_editor.utils.cmdkw import Required, pAttr, pAttrs
9
9
  from auto_editor.utils.types import CoerceError, natural, number, parse_color
@@ -187,13 +187,52 @@ ALayer = list[TlAudio]
187
187
  ASpace = list[ALayer]
188
188
 
189
189
 
190
+ @dataclass(slots=True)
191
+ class AudioTemplate:
192
+ lang: str | None
193
+
194
+
195
+ @dataclass(slots=True)
196
+ class SubtitleTemplate:
197
+ lang: str | None
198
+
199
+
200
+ @dataclass(slots=True)
201
+ class Template:
202
+ sr: int
203
+ layout: str
204
+ res: tuple[int, int]
205
+ audios: list[AudioTemplate]
206
+ subtitles: list[SubtitleTemplate]
207
+
208
+ @classmethod
209
+ def init(
210
+ self,
211
+ src: FileInfo,
212
+ sr: int | None = None,
213
+ layout: str | None = None,
214
+ res: tuple[int, int] | None = None,
215
+ ) -> Template:
216
+ alist = [AudioTemplate(x.lang) for x in src.audios]
217
+ slist = [SubtitleTemplate(x.lang) for x in src.subtitles]
218
+
219
+ if sr is None:
220
+ sr = src.get_sr()
221
+
222
+ if layout is None:
223
+ layout = "stereo" if not src.audios else src.audios[0].layout
224
+
225
+ if res is None:
226
+ res = src.get_res()
227
+
228
+ return Template(sr, layout, res, alist, slist)
229
+
230
+
190
231
  @dataclass
191
232
  class v3:
192
- src: FileInfo | None # Used as a template for timeline settings
193
233
  tb: Fraction
194
- sr: int
195
- res: tuple[int, int]
196
234
  background: str
235
+ template: Template
197
236
  v: VSpace
198
237
  a: ASpace
199
238
  v1: v1 | None # Is it v1 compatible (linear and only one source)?
@@ -286,14 +325,27 @@ video\n"""
286
325
 
287
326
  return {
288
327
  "version": "3",
289
- "resolution": self.res,
290
328
  "timebase": f"{self.tb.numerator}/{self.tb.denominator}",
291
- "samplerate": self.sr,
292
329
  "background": self.background,
330
+ "resolution": self.T.res,
331
+ "samplerate": self.T.sr,
332
+ "layout": self.T.layout,
293
333
  "v": v,
294
334
  "a": a,
295
335
  }
296
336
 
337
+ @property
338
+ def T(self) -> Template:
339
+ return self.template
340
+
341
+ @property
342
+ def res(self) -> tuple[int, int]:
343
+ return self.T.res
344
+
345
+ @property
346
+ def sr(self) -> int:
347
+ return self.T.sr
348
+
297
349
 
298
350
  def make_tracks_dir(path: Path) -> Path:
299
351
  from os import mkdir
@@ -310,9 +362,7 @@ def make_tracks_dir(path: Path) -> Path:
310
362
  return tracks_dir
311
363
 
312
364
 
313
- def set_stream_to_0(tl: v3, log: Log) -> None:
314
- src = tl.src
315
- assert src is not None
365
+ def set_stream_to_0(src: FileInfo, tl: v3, log: Log) -> None:
316
366
  fold = make_tracks_dir(src.path)
317
367
  cache: dict[Path, FileInfo] = {}
318
368
 
@@ -320,7 +370,7 @@ def set_stream_to_0(tl: v3, log: Log) -> None:
320
370
  newtrack = fold / f"{path.stem}_{i}.wav"
321
371
  if newtrack not in cache:
322
372
  mux(path, output=newtrack, stream=i)
323
- cache[newtrack] = initFileInfo(f"{newtrack}", log)
373
+ cache[newtrack] = FileInfo.init(f"{newtrack}", log)
324
374
  return cache[newtrack]
325
375
 
326
376
  for alayer in tl.a:
@@ -6,6 +6,8 @@ from typing import TypedDict
6
6
  import bv
7
7
  from bv.codec import Codec
8
8
 
9
+ from auto_editor.utils.log import Log
10
+
9
11
 
10
12
  class DictContainer(TypedDict, total=False):
11
13
  max_videos: int | None
@@ -60,18 +62,23 @@ def codec_type(x: str) -> str:
60
62
  return ""
61
63
 
62
64
 
63
- def container_constructor(ext: str) -> Container:
64
- with bv.open(f".{ext}", "w") as container:
65
- codecs = container.supported_codecs
66
- if ext == "webm":
67
- vdefault = "vp9"
68
- else:
69
- vdefault = container.default_video_codec
70
- adefault = container.default_audio_codec
71
- sdefault = container.default_subtitle_codec
72
- if sdefault == "none" and ext == "mp4":
73
- sdefault = "srt"
74
-
65
+ def container_constructor(ext: str, log: Log) -> Container:
66
+ try:
67
+ container = bv.open(f".{ext}", "w")
68
+ except ValueError:
69
+ log.error(f"Could not find a suitable format for extension: {ext}")
70
+
71
+ codecs = container.supported_codecs
72
+ if ext == "webm":
73
+ vdefault = "vp9"
74
+ else:
75
+ vdefault = container.default_video_codec
76
+ adefault = container.default_audio_codec
77
+ sdefault = container.default_subtitle_codec
78
+ if sdefault == "none" and ext == "mp4":
79
+ sdefault = "srt"
80
+
81
+ container.close()
75
82
  vcodecs = set()
76
83
  acodecs = set()
77
84
  scodecs = set()
auto_editor/utils/func.py CHANGED
@@ -4,6 +4,9 @@ from typing import TYPE_CHECKING
4
4
 
5
5
  import numpy as np
6
6
 
7
+ from auto_editor.utils.log import Log
8
+ from auto_editor.utils.types import split_num_str
9
+
7
10
  if TYPE_CHECKING:
8
11
  from collections.abc import Callable
9
12
  from fractions import Fraction
@@ -105,3 +108,21 @@ def aspect_ratio(width: int, height: int) -> tuple[int, int]:
105
108
 
106
109
  c = gcd(width, height)
107
110
  return width // c, height // c
111
+
112
+
113
+ def parse_bitrate(input_: str, log: Log) -> int:
114
+ try:
115
+ val, unit = split_num_str(input_)
116
+ except Exception as e:
117
+ log.error(e)
118
+
119
+ if unit.lower() == "k":
120
+ return int(val * 1000)
121
+ if unit == "M":
122
+ return int(val * 1_000_000)
123
+ if unit == "G":
124
+ return int(val * 1_000_000_000)
125
+ if unit == "":
126
+ return int(val)
127
+
128
+ log.error(f"Unknown bitrate: {input_}")
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: auto-editor
3
- Version: 27.0.0
3
+ Version: 27.1.0
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
- License: Unlicense
6
+ License-Expression: Unlicense
7
7
  Project-URL: Bug Tracker, https://github.com/WyattBlue/auto-editor/issues
8
8
  Project-URL: Source Code, https://github.com/WyattBlue/auto-editor
9
9
  Project-URL: homepage, https://auto-editor.com
@@ -1,56 +1,54 @@
1
- auto_editor/__init__.py,sha256=KViW4EBA7GbEItgvzRFMRG3Xq3ASIbwCSsNE9WwakVc,23
2
- auto_editor/__main__.py,sha256=iKfQKFOQDv0-uZecLzfb8A-0k5dAxxe94FaIIEayGXc,15575
1
+ auto_editor/__init__.py,sha256=GyXI0pHyyPF89C9QtYg4zFjEDtSMUJMNUdrc74jh27A,23
2
+ auto_editor/__main__.py,sha256=WfNtjKwx5fDMPpfSNLarigXD3Jp0My98FpzQqSAxQZ8,15807
3
3
  auto_editor/analyze.py,sha256=CeJG0LI9wXZk1R-QPrNGPS4za-_Avd8y7H-D437DqLg,12300
4
- auto_editor/edit.py,sha256=9rJC6IY-jMdQDolJBxfzng43ddYQpC8zuZQHLzEct8s,20698
5
- auto_editor/ffwrapper.py,sha256=b2XDsJMmBnYtRAGrIjqhYH909GNo30tMW3FCZUhhZ5k,4781
4
+ auto_editor/edit.py,sha256=UbKG5jhl5K3kCx9wIB_-Con6PF-4H0-Du0yVln3B5Qc,18921
5
+ auto_editor/ffwrapper.py,sha256=Wet6B5nohgnjpBX7o20Zq0rYr-H9mUuOqHrbQAPPj38,5128
6
6
  auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
7
7
  auto_editor/json.py,sha256=8IVhZJSLx2IVqJsbR5YKDvbHOhgIOvdQmYNpMdMG_xA,9332
8
- auto_editor/make_layers.py,sha256=VKdBj6Kbe5CAoDIDK_MdqHVu2TXZ0sjeEbqyFTA27gI,9617
9
- auto_editor/output.py,sha256=YgkZw0WyVfcTYH-4j6kYfvB6A1BjxGLmfOQVFi-QK_o,2561
8
+ auto_editor/make_layers.py,sha256=nSEeCHysMot2eze23q05g2HFDuskN_4Jk108xlk2Rw8,10102
10
9
  auto_editor/preview.py,sha256=cqQdozM2IB-5qXHNxeqiSrSdEIzlMfjD4SU-NX9sYZ0,3052
11
- auto_editor/timeline.py,sha256=f1cxAhduoVHBTRgRwGqI3BKBb31aAVrWkEahH5gJ-0o,8379
10
+ auto_editor/timeline.py,sha256=Ku6BdDDzdboO0DisO_KLDjAyxJLQVDybWVgFbGCcsmY,9416
12
11
  auto_editor/vanparse.py,sha256=Ug5A2QaRqGiw4l55Z_h9T2QU1x0WqRibR7yY5rQ0WTk,10002
13
- auto_editor/wavfile.py,sha256=afFfje8cK9lFjIkYoBbQHfvQIpsEPxWvspCtFhUKlAw,9499
14
12
  auto_editor/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
13
  auto_editor/cmds/cache.py,sha256=bViYbtVXefTeEIUvSanDfA6cG35ep1N_Jvtz7ZjgIkY,1959
16
- auto_editor/cmds/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
17
- auto_editor/cmds/info.py,sha256=Af2aa9vW_bNfFGbRgQMqc7jj17skC6koE0XL3aF0jl0,7005
18
- auto_editor/cmds/levels.py,sha256=EQ2hfRTe6779OtyzPIBZc5g3WhMZRddVkQWSCVV7UlE,5635
14
+ auto_editor/cmds/desc.py,sha256=DSAWPKt8PS9soBTgzpsFIEWoTe4gPTWwjXpNL-p3WsI,866
15
+ auto_editor/cmds/info.py,sha256=VA2WkTBbQPvrVHjDaJyqryoVMtiiN6cguiUMdWgBJfU,7002
16
+ auto_editor/cmds/levels.py,sha256=2Hbvoy6wMbRRoHdZf25PMqq1uvhRKyPcITNPMpZFo7s,5632
19
17
  auto_editor/cmds/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
20
- auto_editor/cmds/repl.py,sha256=8DgMw-XyfR5XctSmwtk4_1-zxs3ooMs72BfMRlVqLvY,3412
18
+ auto_editor/cmds/repl.py,sha256=HSUTDaVykPb5Bd-v_jz_8R7HvFmKOcT_ZVmP-d0AbUY,3247
21
19
  auto_editor/cmds/subdump.py,sha256=kHg8nfUi6I6VeJjEgMxupPa666qsYUh7ZEUxint7Gqo,2443
22
- auto_editor/cmds/test.py,sha256=DK7T5BEtMhfrsTJSmXN_tlBKgHdajS-bICiGttq8M0c,27416
20
+ auto_editor/cmds/test.py,sha256=UNN1r6J69-woqA6QU7TeY0WlPxukQzwCRl5DGBTLK8g,28837
23
21
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- auto_editor/formats/fcp11.py,sha256=sqjC36jI47ICPLjZJYiqGwY7foOnWOiNjkPFLdgSnI4,5208
25
- auto_editor/formats/fcp7.py,sha256=x5cagTzGCAW3i3M6m7TZC1h8gLfSmX1UK-iiDuCpdfs,20289
26
- auto_editor/formats/json.py,sha256=_dPrNr1ZC8kfSHRinG0rFudv1XFsZf2VlGAl084lMQ8,7663
22
+ auto_editor/formats/fcp11.py,sha256=uaJbLiwiVxqoVy5JCA14wZj-m1wqZIzWh7rS-BsnSQM,5219
23
+ auto_editor/formats/fcp7.py,sha256=rHjXXjpJ5YQQvCxor7FpUaqaAreqjCas4uLpiTdFclc,20255
24
+ auto_editor/formats/json.py,sha256=UUBhFR_79vn4Lxu73B0cVBFgw4qytrmMP-TiCmDFMd0,7666
27
25
  auto_editor/formats/shotcut.py,sha256=-ES854LLFCMCBe100JRJedDmuk8zPev17aQMTrzPv-g,4923
28
26
  auto_editor/formats/utils.py,sha256=LYXDiqOk9WwUorLGw2D0M7In9BNDkoKikNawuks7hqE,1648
29
27
  auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
28
  auto_editor/lang/libintrospection.py,sha256=6H1rGp0wqaCud5IPaoEmzULGnYt6ec7_0h32ATcw2oY,261
31
29
  auto_editor/lang/libmath.py,sha256=z33A161Oe6vYYK7R6pgYjdZZe63dQkN38Qf36TL3prg,847
32
30
  auto_editor/lang/palet.py,sha256=RQjyIZMJSWnzDkHTu-5mt74o9_4zO4VrcH-wLojCF7A,24113
33
- auto_editor/lang/stdenv.py,sha256=Acf9CVXzglh3KgsJXrO7IyMMjTY5jqpz5u0QADXtDHA,44127
31
+ auto_editor/lang/stdenv.py,sha256=o7kFu7EbaH71XPFGxJUXYGxSeZ8O3i1_C5Hmi9uya4Q,44150
34
32
  auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
33
  auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,7586
36
34
  auto_editor/lib/data_structs.py,sha256=Hnzl5gWvo-geTU0g-lGejj6HQW3VvPv0NBEj2XoGskY,7089
37
35
  auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
38
36
  auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- auto_editor/render/audio.py,sha256=7iQMtex8hzzLH80pwLD_nwN_ZH3GHjWRmER45ZVEzPk,12646
37
+ auto_editor/render/audio.py,sha256=ejV_NIvrmxMJt6mf7nPNIzlPHOumNhcIO2J-irHO-k8,17427
40
38
  auto_editor/render/subtitle.py,sha256=F27T8OsAojUIGTGBWqTdH36h0BnHXSExxIqzOtqyZoE,6129
41
- auto_editor/render/video.py,sha256=g2TbuCNzhbE8KsS-_3XRLLdmQKFROdwfkymwIbGmtqc,12012
39
+ auto_editor/render/video.py,sha256=uIrYzF4bDZ3vwfX2F6TdR6F73GI4yruGssto9xEQ-AA,11999
42
40
  auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
41
  auto_editor/utils/bar.py,sha256=Ky9JRf37JTgLyvNuIXDfucaUE8H1vBbCqKLjttmsmmo,4156
44
42
  auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
45
43
  auto_editor/utils/cmdkw.py,sha256=aUGBvBel2Ko1o6Rwmr4rEL-BMc5hEnzYLbyZ1GeJdcY,5729
46
- auto_editor/utils/container.py,sha256=UPqlAos7UIf_ARIAbB3wuaSv1iNLFgy22YFGK8559yM,2487
47
- auto_editor/utils/func.py,sha256=C8ucgsSEzPyBc-8obhsCXd_uQW0cnCdBn1KVxB7FHjU,2747
44
+ auto_editor/utils/container.py,sha256=CNHChHbhzIrjmDdWw6UzMqscrr9u7A-ZqKWejGjJwYE,2628
45
+ auto_editor/utils/func.py,sha256=ODyjXnzSDatEu08w398K8_xBKYdXMY3IPHiJpGRZDyQ,3250
48
46
  auto_editor/utils/log.py,sha256=wPNf6AabV-0cnoS_bPLv1Lh7llQBtNqPKeh07einOuc,3701
49
47
  auto_editor/utils/types.py,sha256=j2hd4zMQ9EftDy41Ji2_PFru_7HEZObd9yKA0BJxFaY,7616
50
- auto_editor-27.0.0.dist-info/licenses/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
48
+ auto_editor-27.1.0.dist-info/licenses/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
51
49
  docs/build.py,sha256=g1uc1H9T_naGaermUiVMMwUpbT0IWElRhjgT0fvCh8w,1914
52
- auto_editor-27.0.0.dist-info/METADATA,sha256=VhQA-UaBuv5unUT8MUVkQ_oVwGisctF3BS5tTSTkDfA,6165
53
- auto_editor-27.0.0.dist-info/WHEEL,sha256=tTnHoFhvKQHCh4jz3yCn0WPTYIy7wXx3CJtJ7SJGV7c,91
54
- auto_editor-27.0.0.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
- auto_editor-27.0.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
- auto_editor-27.0.0.dist-info/RECORD,,
50
+ auto_editor-27.1.0.dist-info/METADATA,sha256=JU6SgcQLr3thOG27aQRkt2r3INzU0q306BeK79qkwGs,6176
51
+ auto_editor-27.1.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
52
+ auto_editor-27.1.0.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
53
+ auto_editor-27.1.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
54
+ auto_editor-27.1.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.1)
2
+ Generator: setuptools (79.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
auto_editor/output.py DELETED
@@ -1,86 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import os.path
4
- from dataclasses import dataclass, field
5
-
6
- import bv
7
- from bv.audio.resampler import AudioResampler
8
-
9
- from auto_editor.ffwrapper import FileInfo
10
- from auto_editor.utils.bar import Bar
11
- from auto_editor.utils.log import Log
12
- from auto_editor.utils.types import split_num_str
13
-
14
-
15
- def parse_bitrate(input_: str, log: Log) -> int:
16
- try:
17
- val, unit = split_num_str(input_)
18
- except Exception as e:
19
- log.error(e)
20
-
21
- if unit.lower() == "k":
22
- return int(val * 1000)
23
- if unit == "M":
24
- return int(val * 1_000_000)
25
- if unit == "G":
26
- return int(val * 1_000_000_000)
27
- if unit == "":
28
- return int(val)
29
-
30
- log.error(f"Unknown bitrate: {input_}")
31
-
32
-
33
- @dataclass(slots=True)
34
- class Ensure:
35
- _bar: Bar
36
- _sr: int
37
- log: Log
38
- _audios: list[tuple[FileInfo, int]] = field(default_factory=list)
39
-
40
- def audio(self, src: FileInfo, stream: int) -> str:
41
- try:
42
- label = self._audios.index((src, stream))
43
- first_time = False
44
- except ValueError:
45
- self._audios.append((src, stream))
46
- label = len(self._audios) - 1
47
- first_time = True
48
-
49
- out_path = os.path.join(self.log.temp, f"{label:x}.wav")
50
-
51
- if first_time:
52
- sample_rate = self._sr
53
- bar = self._bar
54
- self.log.debug(f"Making external audio: {out_path}")
55
-
56
- in_container = bv.open(src.path, "r")
57
- out_container = bv.open(
58
- out_path, "w", format="wav", options={"rf64": "always"}
59
- )
60
- astream = in_container.streams.audio[stream]
61
-
62
- if astream.duration is None or astream.time_base is None:
63
- dur = 1.0
64
- else:
65
- dur = float(astream.duration * astream.time_base)
66
-
67
- bar.start(dur, "Extracting audio")
68
-
69
- output_astream = out_container.add_stream(
70
- "pcm_s16le", layout="stereo", rate=sample_rate
71
- )
72
- resampler = AudioResampler(format="s16", layout="stereo", rate=sample_rate)
73
- for i, frame in enumerate(in_container.decode(astream)):
74
- if i % 1500 == 0 and frame.time is not None:
75
- bar.tick(frame.time)
76
-
77
- for new_frame in resampler.resample(frame):
78
- out_container.mux(output_astream.encode(new_frame))
79
-
80
- out_container.mux(output_astream.encode(None))
81
-
82
- out_container.close()
83
- in_container.close()
84
- bar.end()
85
-
86
- return out_path