auto-editor 26.3.2__py3-none-any.whl → 26.3.3__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "26.3.2"
1
+ __version__ = "26.3.3"
auto_editor/__main__.py CHANGED
@@ -3,6 +3,8 @@
3
3
  import platform as plat
4
4
  import re
5
5
  import sys
6
+ from dataclasses import dataclass, field
7
+ from fractions import Fraction
6
8
  from io import StringIO
7
9
  from os import environ
8
10
  from os.path import exists, isdir, isfile, lexists, splitext
@@ -12,21 +14,127 @@ import auto_editor
12
14
  from auto_editor.utils.func import get_stdout
13
15
  from auto_editor.utils.log import Log
14
16
  from auto_editor.utils.types import (
15
- Args,
17
+ CoerceError,
16
18
  frame_rate,
17
- margin,
19
+ natural,
18
20
  number,
19
21
  parse_color,
20
- resolution,
21
- sample_rate,
22
- speed,
23
- speed_range,
24
- time_range,
22
+ split_num_str,
25
23
  )
26
24
  from auto_editor.vanparse import ArgumentParser
27
25
 
28
26
 
27
+ @dataclass(slots=True)
28
+ class Args:
29
+ input: list[str] = field(default_factory=list)
30
+ help: bool = False
31
+
32
+ # Editing Options
33
+ margin: tuple[str, str] = ("0.2s", "0.2s")
34
+ edit: str = "audio"
35
+ export: str | None = None
36
+ output: str | None = None
37
+ silent_speed: float = 99999.0
38
+ video_speed: float = 1.0
39
+ cut_out: list[tuple[str, str]] = field(default_factory=list)
40
+ add_in: list[tuple[str, str]] = field(default_factory=list)
41
+ set_speed_for_range: list[tuple[float, str, str]] = field(default_factory=list)
42
+
43
+ # Timeline Options
44
+ frame_rate: Fraction | None = None
45
+ sample_rate: int | None = None
46
+ resolution: tuple[int, int] | None = None
47
+ background: str = "#000000"
48
+
49
+ # URL download Options
50
+ yt_dlp_location: str = "yt-dlp"
51
+ download_format: str | None = None
52
+ output_format: str | None = None
53
+ yt_dlp_extras: str | None = None
54
+
55
+ # Display Options
56
+ progress: str = "modern"
57
+ debug: bool = False
58
+ quiet: bool = False
59
+ preview: bool = False
60
+
61
+ # Container Settings
62
+ sn: bool = False
63
+ dn: bool = False
64
+ faststart: bool = False
65
+ no_faststart: bool = False
66
+ fragmented: bool = False
67
+ no_fragmented: bool = False
68
+
69
+ # Video Rendering
70
+ video_codec: str = "auto"
71
+ video_bitrate: str = "auto"
72
+ vprofile: str | None = None
73
+ scale: float = 1.0
74
+ no_seek: bool = False
75
+
76
+ # Audio Rendering
77
+ audio_codec: str = "auto"
78
+ audio_bitrate: str = "auto"
79
+ keep_tracks_separate: bool = False
80
+ audio_normalize: str = "#f"
81
+
82
+ # Misc.
83
+ config: bool = False
84
+ no_cache: bool = False
85
+ no_open: bool = False
86
+ temp_dir: str | None = None
87
+ player: str | None = None
88
+ version: bool = False
89
+
90
+
29
91
  def main_options(parser: ArgumentParser) -> ArgumentParser:
92
+ def margin(val: str) -> tuple[str, str]:
93
+ vals = val.strip().split(",")
94
+ if len(vals) == 1:
95
+ vals.append(vals[0])
96
+ if len(vals) != 2:
97
+ raise CoerceError("--margin has too many arguments.")
98
+ return vals[0], vals[1]
99
+
100
+ def speed(val: str) -> float:
101
+ _s = number(val)
102
+ if _s <= 0 or _s > 99999:
103
+ return 99999.0
104
+ return _s
105
+
106
+ def resolution(val: str | None) -> tuple[int, int] | None:
107
+ if val is None:
108
+ return None
109
+ vals = val.strip().split(",")
110
+ if len(vals) != 2:
111
+ raise CoerceError(f"'{val}': Resolution takes two numbers")
112
+ return natural(vals[0]), natural(vals[1])
113
+
114
+ def sample_rate(val: str) -> int:
115
+ num, unit = split_num_str(val)
116
+ if unit in {"kHz", "KHz"}:
117
+ return natural(num * 1000)
118
+ if unit not in {"", "Hz"}:
119
+ raise CoerceError(f"Unknown unit: '{unit}'")
120
+ return natural(num)
121
+
122
+ def _comma_coerce(name: str, val: str, num_args: int) -> list[str]:
123
+ vals = val.strip().split(",")
124
+ if num_args > len(vals):
125
+ raise CoerceError(f"Too few arguments for {name}.")
126
+ if len(vals) > num_args:
127
+ raise CoerceError(f"Too many arguments for {name}.")
128
+ return vals
129
+
130
+ def time_range(val: str) -> tuple[str, str]:
131
+ a = _comma_coerce("time_range", val, 2)
132
+ return a[0], a[1]
133
+
134
+ def speed_range(val: str) -> tuple[float, str, str]:
135
+ a = _comma_coerce("speed_range", val, 3)
136
+ return number(a[0]), a[1], a[2]
137
+
30
138
  parser.add_required("input", nargs="*", metavar="[file | url ...] [options]")
31
139
  parser.add_text("Editing Options:")
32
140
  parser.add_argument(
@@ -41,6 +149,16 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
41
149
  metavar="METHOD",
42
150
  help="Set an expression which determines how to make auto edits",
43
151
  )
152
+ parser.add_argument(
153
+ "--export", "-ex", metavar="EXPORT:ATTRS?", help="Choose the export mode"
154
+ )
155
+ parser.add_argument(
156
+ "--output",
157
+ "--output-file",
158
+ "-o",
159
+ metavar="FILE",
160
+ help="Set the name/path of the new output file",
161
+ )
44
162
  parser.add_argument(
45
163
  "--silent-speed",
46
164
  "-s",
@@ -111,12 +229,6 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
111
229
  metavar="COLOR",
112
230
  help="Set the background as a solid RGB color",
113
231
  )
114
- parser.add_argument(
115
- "--add",
116
- nargs="*",
117
- metavar="OBJ:START,DUR,ATTRS?",
118
- help="Insert an audio/video object to the timeline",
119
- )
120
232
  parser.add_text("URL Download Options:")
121
233
  parser.add_argument(
122
234
  "--yt-dlp-location",
@@ -138,28 +250,6 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
138
250
  metavar="CMD",
139
251
  help="Add extra options for yt-dlp. Must be in quotes",
140
252
  )
141
- parser.add_text("Utility Options:")
142
- parser.add_argument(
143
- "--export", "-ex", metavar="EXPORT:ATTRS?", help="Choose the export mode"
144
- )
145
- parser.add_argument(
146
- "--output-file",
147
- "--output",
148
- "-o",
149
- metavar="FILE",
150
- help="Set the name/path of the new output file",
151
- )
152
- parser.add_argument(
153
- "--player", "-p", metavar="CMD", help="Set player to open output media files"
154
- )
155
- parser.add_argument(
156
- "--no-open", flag=True, help="Do not open the output file after editing is done"
157
- )
158
- parser.add_argument(
159
- "--temp-dir",
160
- metavar="PATH",
161
- help="Set where the temporary directory is located",
162
- )
163
253
  parser.add_text("Display Options:")
164
254
  parser.add_argument(
165
255
  "--progress",
@@ -186,6 +276,16 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
186
276
  flag=True,
187
277
  help="Disable the inclusion of data streams in the output file",
188
278
  )
279
+ parser.add_argument(
280
+ "--faststart",
281
+ flag=True,
282
+ help="Enable movflags +faststart, recommended for web (default)",
283
+ )
284
+ parser.add_argument(
285
+ "--no-faststart",
286
+ flag=True,
287
+ help="Disable movflags +faststart, will be faster for large files",
288
+ )
189
289
  parser.add_argument(
190
290
  "--fragmented",
191
291
  flag=True,
@@ -258,6 +358,17 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
258
358
  parser.add_argument(
259
359
  "--no-cache", flag=True, help="Don't look for or write a cache file"
260
360
  )
361
+ parser.add_argument(
362
+ "--no-open", flag=True, help="Do not open the output file after editing is done"
363
+ )
364
+ parser.add_argument(
365
+ "--temp-dir",
366
+ metavar="PATH",
367
+ help="Set where the temporary directory is located",
368
+ )
369
+ parser.add_argument(
370
+ "--player", "-p", metavar="CMD", help="Set player to open output media files"
371
+ )
261
372
  parser.add_argument("--version", "-V", flag=True, help="Display version and halt")
262
373
  return parser
263
374
 
auto_editor/cmds/test.py CHANGED
@@ -153,36 +153,42 @@ class Runner:
153
153
  def desc(self):
154
154
  self.raw(["desc", "example.mp4"])
155
155
 
156
- def test_example(self) -> None:
157
- out = self.main(["example.mp4"], [], output="example_ALTERED.mp4")
156
+ def test_movflags(self) -> None:
157
+ file = "resources/testsrc.mp4"
158
+ out = self.main([file], ["--faststart"]) + ".mp4"
159
+ fast = calculate_sha256(out)
158
160
  with av.open(out) as container:
159
- video = container.streams[0]
160
- audio = container.streams[1]
161
+ assert isinstance(container.streams[0], av.VideoStream)
162
+ assert isinstance(container.streams[1], av.AudioStream)
161
163
 
162
- assert isinstance(video, av.VideoStream)
163
- assert isinstance(audio, av.AudioStream)
164
- assert video.base_rate == 30
165
- assert video.average_rate is not None
166
- assert video.average_rate == 30, video.average_rate
167
- assert (video.width, video.height) == (1280, 720)
168
- assert video.codec.name == "h264"
169
- assert video.language == "eng"
170
- assert audio.codec.name == "aac"
171
- assert audio.sample_rate == 48000
172
- assert audio.language == "eng"
164
+ out = self.main([file], ["--no-faststart"]) + ".mp4"
165
+ nofast = calculate_sha256(out)
166
+ with av.open(out) as container:
167
+ assert isinstance(container.streams[0], av.VideoStream)
168
+ assert isinstance(container.streams[1], av.AudioStream)
169
+
170
+ out = self.main([file], ["--fragmented"]) + ".mp4"
171
+ frag = calculate_sha256(out)
172
+ with av.open(out) as container:
173
+ assert isinstance(container.streams[0], av.VideoStream)
174
+ assert isinstance(container.streams[1], av.AudioStream)
173
175
 
174
- out1_sha = calculate_sha256(out)
176
+ assert fast != nofast, "+faststart is not being applied"
177
+ assert frag not in (fast, nofast), "fragmented output should diff."
175
178
 
176
- out = self.main(["example.mp4"], ["--fragmented"], output="example_ALTERED.mp4")
179
+ def test_example(self) -> None:
180
+ out = self.main(["example.mp4"], [], output="example_ALTERED.mp4")
177
181
  with av.open(out) as container:
182
+ assert container.duration is not None
183
+ assert container.duration > 17300000 and container.duration < 2 << 24
184
+
178
185
  video = container.streams[0]
179
186
  audio = container.streams[1]
180
-
181
187
  assert isinstance(video, av.VideoStream)
182
188
  assert isinstance(audio, av.AudioStream)
183
189
  assert video.base_rate == 30
184
190
  assert video.average_rate is not None
185
- assert round(video.average_rate) == 30, video.average_rate
191
+ assert video.average_rate == 30, video.average_rate
186
192
  assert (video.width, video.height) == (1280, 720)
187
193
  assert video.codec.name == "h264"
188
194
  assert video.language == "eng"
@@ -190,8 +196,6 @@ class Runner:
190
196
  assert audio.sample_rate == 48000
191
197
  assert audio.language == "eng"
192
198
 
193
- assert calculate_sha256(out) != out1_sha, "Fragmented output should be diff."
194
-
195
199
  # PR #260
196
200
  def test_high_speed(self):
197
201
  self.check(["example.mp4", "--video-speed", "99998"], "empty")
auto_editor/edit.py CHANGED
@@ -6,7 +6,7 @@ from fractions import Fraction
6
6
  from heapq import heappop, heappush
7
7
  from os.path import splitext
8
8
  from subprocess import run
9
- from typing import Any
9
+ from typing import TYPE_CHECKING, Any
10
10
 
11
11
  import av
12
12
  from av import AudioResampler, Codec
@@ -24,7 +24,9 @@ from auto_editor.utils.chunks import Chunk, Chunks
24
24
  from auto_editor.utils.cmdkw import ParserError, parse_with_palet, pAttr, pAttrs
25
25
  from auto_editor.utils.container import Container, container_constructor
26
26
  from auto_editor.utils.log import Log
27
- from auto_editor.utils.types import Args
27
+
28
+ if TYPE_CHECKING:
29
+ from auto_editor.__main__ import Args
28
30
 
29
31
 
30
32
  def set_output(
@@ -206,7 +208,7 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
206
208
 
207
209
  del paths
208
210
 
209
- output, export_ops = set_output(args.output_file, args.export, src, log)
211
+ output, export_ops = set_output(args.output, args.export, src, log)
210
212
  assert "export" in export_ops
211
213
  export = export_ops["export"]
212
214
 
@@ -219,10 +221,6 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
219
221
  if os.path.isdir(output):
220
222
  log.error("Output path already has an existing directory!")
221
223
 
222
- if os.path.isfile(output) and src is not None and src.path != output: # type: ignore
223
- log.debug(f"Removing already existing file: {output}")
224
- os.remove(output)
225
-
226
224
  if args.sample_rate is None:
227
225
  if tl is None:
228
226
  samplerate = 48000 if src is None else src.get_sr()
@@ -297,15 +295,19 @@ def edit_media(paths: list[str], args: Args, log: Log) -> None:
297
295
  def make_media(tl: v3, output_path: str) -> None:
298
296
  assert src is not None
299
297
 
298
+ options = {}
299
+ mov_flags = []
300
300
  if args.fragmented and not args.no_fragmented:
301
- log.debug("Enabling fragmented mp4/mov")
302
- options = {
303
- "movflags": "+default_base_moof+faststart+frag_keyframe+separate_moof",
304
- "frag_duration": "0.2",
305
- }
306
- else:
307
- options = {"movflags": "faststart"}
308
- output = av.open(output_path, "w", options=options)
301
+ mov_flags.extend(["default_base_moof", "frag_keyframe", "separate_moof"])
302
+ options["frag_duration"] = "0.2"
303
+ if args.faststart:
304
+ log.warning("Fragmented is enabled, will not apply faststart.")
305
+ elif not args.no_faststart:
306
+ mov_flags.append("faststart")
307
+ if mov_flags:
308
+ options["movflags"] = "+".join(mov_flags)
309
+
310
+ output = av.open(output_path, "w", container_options=options)
309
311
 
310
312
  if ctr.default_sub != "none" and not args.sn:
311
313
  sub_paths = make_new_subtitles(tl, log)
@@ -13,11 +13,12 @@ from auto_editor.lib.data_structs import print_str
13
13
  from auto_editor.lib.err import MyError
14
14
  from auto_editor.timeline import ASpace, TlAudio, TlVideo, VSpace, v1, v3
15
15
  from auto_editor.utils.func import mut_margin
16
- from auto_editor.utils.types import Args, CoerceError, time
16
+ from auto_editor.utils.types import CoerceError, time
17
17
 
18
18
  if TYPE_CHECKING:
19
19
  from numpy.typing import NDArray
20
20
 
21
+ from auto_editor.__main__ import Args
21
22
  from auto_editor.utils.bar import Bar
22
23
  from auto_editor.utils.chunks import Chunks
23
24
  from auto_editor.utils.log import Log
auto_editor/output.py CHANGED
@@ -9,12 +9,12 @@ from av.audio.resampler import AudioResampler
9
9
  from auto_editor.ffwrapper import FileInfo
10
10
  from auto_editor.utils.bar import Bar
11
11
  from auto_editor.utils.log import Log
12
- from auto_editor.utils.types import _split_num_str
12
+ from auto_editor.utils.types import split_num_str
13
13
 
14
14
 
15
15
  def parse_bitrate(input_: str, log: Log) -> int:
16
16
  try:
17
- val, unit = _split_num_str(input_)
17
+ val, unit = split_num_str(input_)
18
18
  except Exception as e:
19
19
  log.error(e)
20
20
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import io
4
4
  from pathlib import Path
5
+ from typing import TYPE_CHECKING
5
6
 
6
7
  import av
7
8
  import numpy as np
@@ -18,9 +19,11 @@ from auto_editor.utils.bar import Bar
18
19
  from auto_editor.utils.cmdkw import ParserError, parse_with_palet, pAttr, pAttrs
19
20
  from auto_editor.utils.container import Container
20
21
  from auto_editor.utils.log import Log
21
- from auto_editor.utils.types import Args
22
22
  from auto_editor.wavfile import AudioData, read, write
23
23
 
24
+ if TYPE_CHECKING:
25
+ from auto_editor.__main__ import Args
26
+
24
27
  norm_types = {
25
28
  "ebu": pAttrs(
26
29
  "ebu",
@@ -13,10 +13,10 @@ if TYPE_CHECKING:
13
13
  from collections.abc import Iterator
14
14
  from typing import Any
15
15
 
16
+ from auto_editor.__main__ import Args
16
17
  from auto_editor.ffwrapper import FileInfo
17
18
  from auto_editor.timeline import v3
18
19
  from auto_editor.utils.log import Log
19
- from auto_editor.utils.types import Args
20
20
 
21
21
 
22
22
  @dataclass(slots=True)
auto_editor/timeline.py CHANGED
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
  from auto_editor.ffwrapper import initFileInfo, mux
7
7
  from auto_editor.lib.contracts import *
8
8
  from auto_editor.utils.cmdkw import Required, pAttr, pAttrs
9
- from auto_editor.utils.types import natural, number, parse_color, threshold
9
+ from auto_editor.utils.types import CoerceError, natural, number, parse_color
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from collections.abc import Iterator
@@ -128,6 +128,13 @@ class TlRect:
128
128
  }
129
129
 
130
130
 
131
+ def threshold(val: str | float) -> float:
132
+ num = number(val)
133
+ if num > 1 or num < 0:
134
+ raise CoerceError(f"'{val}': Threshold must be between 0 and 1 (0%-100%)")
135
+ return num
136
+
137
+
131
138
  video_builder = pAttrs(
132
139
  "video",
133
140
  pAttr("start", Required, is_nat, natural),
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
- from dataclasses import dataclass, field
5
4
  from fractions import Fraction
6
5
 
7
6
 
@@ -9,16 +8,7 @@ class CoerceError(Exception):
9
8
  pass
10
9
 
11
10
 
12
- def _comma_coerce(name: str, val: str, num_args: int) -> list[str]:
13
- vals = val.strip().split(",")
14
- if num_args > len(vals):
15
- raise CoerceError(f"Too few arguments for {name}.")
16
- if len(vals) > num_args:
17
- raise CoerceError(f"Too many arguments for {name}.")
18
- return vals
19
-
20
-
21
- def _split_num_str(val: str | float) -> tuple[float, str]:
11
+ def split_num_str(val: str | float) -> tuple[float, str]:
22
12
  if isinstance(val, float | int):
23
13
  return val, ""
24
14
 
@@ -35,14 +25,9 @@ def _split_num_str(val: str | float) -> tuple[float, str]:
35
25
  return float(num), unit
36
26
 
37
27
 
38
- def _unit_check(unit: str, allowed_units: tuple[str, ...]) -> None:
39
- if unit not in allowed_units:
40
- raise CoerceError(f"Unknown unit: '{unit}'")
41
-
42
-
43
28
  # Numbers: 0, 1, 2, 3, ...
44
29
  def natural(val: str | float) -> int:
45
- num, unit = _split_num_str(val)
30
+ num, unit = split_num_str(val)
46
31
  if unit != "":
47
32
  raise CoerceError(f"'{val}': Natural does not allow units.")
48
33
  if not isinstance(num, int) and not num.is_integer():
@@ -69,25 +54,12 @@ def number(val: str | float) -> float:
69
54
  raise CoerceError(f"'{val}': Denominator must not be zero.")
70
55
  return vs[0] / vs[1]
71
56
 
72
- num, unit = _split_num_str(val)
57
+ num, unit = split_num_str(val)
73
58
  if unit == "%":
74
59
  return num / 100
75
- _unit_check(unit, ("",))
76
- return num
77
-
78
-
79
- def speed(val: str) -> float:
80
- _s = number(val)
81
- if _s <= 0 or _s > 99999:
82
- return 99999.0
83
- return _s
84
-
85
-
86
- def threshold(val: str | float) -> float:
87
- num = number(val)
88
- if num > 1 or num < 0:
89
- raise CoerceError(f"'{val}': Threshold must be between 0 and 1 (0%-100%)")
90
- return num
60
+ if unit == "":
61
+ return num
62
+ raise CoerceError(f"Unknown unit: '{unit}'")
91
63
 
92
64
 
93
65
  def frame_rate(val: str) -> Fraction:
@@ -102,14 +74,6 @@ def frame_rate(val: str) -> Fraction:
102
74
  return Fraction(val)
103
75
 
104
76
 
105
- def sample_rate(val: str) -> int:
106
- num, unit = _split_num_str(val)
107
- if unit in {"kHz", "KHz"}:
108
- return natural(num * 1000)
109
- _unit_check(unit, ("", "Hz"))
110
- return natural(num)
111
-
112
-
113
77
  def time(val: str, tb: Fraction) -> int:
114
78
  if ":" in val:
115
79
  boxes = val.split(":")
@@ -121,7 +85,7 @@ def time(val: str, tb: Fraction) -> int:
121
85
  )
122
86
  raise CoerceError(f"'{val}': Invalid time format")
123
87
 
124
- num, unit = _split_num_str(val)
88
+ num, unit = split_num_str(val)
125
89
  if unit in {"s", "sec", "secs", "second", "seconds"}:
126
90
  return round(num * tb)
127
91
  if unit in {"min", "mins", "minute", "minutes"}:
@@ -136,25 +100,6 @@ def time(val: str, tb: Fraction) -> int:
136
100
  return int(num)
137
101
 
138
102
 
139
- def margin(val: str) -> tuple[str, str]:
140
- vals = val.strip().split(",")
141
- if len(vals) == 1:
142
- vals.append(vals[0])
143
- if len(vals) != 2:
144
- raise CoerceError("--margin has too many arguments.")
145
- return vals[0], vals[1]
146
-
147
-
148
- def time_range(val: str) -> tuple[str, str]:
149
- a = _comma_coerce("time_range", val, 2)
150
- return a[0], a[1]
151
-
152
-
153
- def speed_range(val: str) -> tuple[float, str, str]:
154
- a = _comma_coerce("speed_range", val, 3)
155
- return number(a[0]), a[1], a[2]
156
-
157
-
158
103
  def parse_color(val: str) -> str:
159
104
  """
160
105
  Convert a color str into an RGB tuple
@@ -179,62 +124,6 @@ def parse_color(val: str) -> str:
179
124
  raise ValueError(f"Invalid Color: '{color}'")
180
125
 
181
126
 
182
- def resolution(val: str | None) -> tuple[int, int] | None:
183
- if val is None:
184
- return None
185
- vals = val.strip().split(",")
186
- if len(vals) != 2:
187
- raise CoerceError(f"'{val}': Resolution takes two numbers")
188
-
189
- return natural(vals[0]), natural(vals[1])
190
-
191
-
192
- @dataclass(slots=True)
193
- class Args:
194
- yt_dlp_location: str = "yt-dlp"
195
- download_format: str | None = None
196
- output_format: str | None = None
197
- yt_dlp_extras: str | None = None
198
- video_codec: str = "auto"
199
- audio_codec: str = "auto"
200
- video_bitrate: str = "auto"
201
- vprofile: str | None = None
202
- audio_bitrate: str = "auto"
203
- scale: float = 1.0
204
- fragmented: bool = False
205
- no_fragmented: bool = False
206
- sn: bool = False
207
- dn: bool = False
208
- no_seek: bool = False
209
- cut_out: list[tuple[str, str]] = field(default_factory=list)
210
- add_in: list[tuple[str, str]] = field(default_factory=list)
211
- set_speed_for_range: list[tuple[float, str, str]] = field(default_factory=list)
212
- frame_rate: Fraction | None = None
213
- sample_rate: int | None = None
214
- resolution: tuple[int, int] | None = None
215
- background: str = "#000000"
216
- edit: str = "audio"
217
- keep_tracks_separate: bool = False
218
- audio_normalize: str = "#f"
219
- export: str | None = None
220
- player: str | None = None
221
- no_open: bool = False
222
- temp_dir: str | None = None
223
- progress: str = "modern"
224
- version: bool = False
225
- debug: bool = False
226
- config: bool = False
227
- quiet: bool = False
228
- preview: bool = False
229
- no_cache: bool = False
230
- margin: tuple[str, str] = ("0.2s", "0.2s")
231
- silent_speed: float = 99999.0
232
- video_speed: float = 1.0
233
- output_file: str | None = None
234
- help: bool = False
235
- input: list[str] = field(default_factory=list)
236
-
237
-
238
127
  colormap = {
239
128
  # Taken from https://www.w3.org/TR/css-color-4/#named-color
240
129
  "aliceblue": "#f0f8ff",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: auto-editor
3
- Version: 26.3.2
3
+ Version: 26.3.3
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -21,7 +21,7 @@ Requires-Dist: pyav==14.2.*
21
21
  ---
22
22
 
23
23
  [![Actions Status](https://github.com/wyattblue/auto-editor/workflows/build/badge.svg)](https://github.com/wyattblue/auto-editor/actions)
24
- <a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
24
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
25
25
 
26
26
  Before doing the real editing, you first cut out the "dead space" which is typically silence. This is known as a "first pass". Cutting these is a boring task, especially if the video is very long.
27
27
 
@@ -1,13 +1,13 @@
1
- auto_editor/__init__.py,sha256=bDmtpN3tJ_1R818Djb_PZ7H7ZIgSCXSdbTx5GJ009SU,23
2
- auto_editor/__main__.py,sha256=BQRStgcJxNVUOisP4JpcaxaM_KTGFS04Qgklyy9fumc,12012
1
+ auto_editor/__init__.py,sha256=j0yX7sy8BkUOhfpG7Gpkxj4cMvCV3_EO8A6FT6Kyd_k,23
2
+ auto_editor/__main__.py,sha256=9zvn3N33SoC63Q-XfbtAYLzPSmi_rIhvNLR4PoPLdAw,15410
3
3
  auto_editor/analyze.py,sha256=YY3Io-IDdlliawx03w-L9AW5nZSOtmbXX_la1HanYmY,12658
4
- auto_editor/edit.py,sha256=wjs1qj13Rx8JGgf7b_dPgVEmd_vDyU-N5GDQhVGSJX8,21130
4
+ auto_editor/edit.py,sha256=6lqJRoc8AxepS1xnhAltLdcvvyRmE-FL2DUX3Nq61sA,21138
5
5
  auto_editor/ffwrapper.py,sha256=1lYYfq8gVgMVkYWeAEYDPAHCwFCYbKQwy0FxYBxMzk8,4765
6
6
  auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
7
- auto_editor/make_layers.py,sha256=Yl4xYkMHume_VvRW8Ii48hyx-ZtFr8WMmVYXJMSUmg0,9581
8
- auto_editor/output.py,sha256=ho8Lpqz4Sv_Gw0Vj2OvG39s83xHpyZlvtRNryTPbXqc,2563
7
+ auto_editor/make_layers.py,sha256=VKdBj6Kbe5CAoDIDK_MdqHVu2TXZ0sjeEbqyFTA27gI,9617
8
+ auto_editor/output.py,sha256=lMhxvXQkr3zW1GC6FLJQlzkqX9Lj4FZm9k0cofYi_yY,2561
9
9
  auto_editor/preview.py,sha256=cqQdozM2IB-5qXHNxeqiSrSdEIzlMfjD4SU-NX9sYZ0,3052
10
- auto_editor/timeline.py,sha256=XfaH9cH-RB-MObOpMr5IfLcqJcjmabO1XwkUkT3_FQM,8186
10
+ auto_editor/timeline.py,sha256=f1cxAhduoVHBTRgRwGqI3BKBb31aAVrWkEahH5gJ-0o,8379
11
11
  auto_editor/vanparse.py,sha256=Ug5A2QaRqGiw4l55Z_h9T2QU1x0WqRibR7yY5rQ0WTk,10002
12
12
  auto_editor/wavfile.py,sha256=afFfje8cK9lFjIkYoBbQHfvQIpsEPxWvspCtFhUKlAw,9499
13
13
  auto_editor/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -18,7 +18,7 @@ auto_editor/cmds/levels.py,sha256=6LqXfNBca__KGeA8kJqJqH0MBR3GudaFoWU0_dnv5bI,56
18
18
  auto_editor/cmds/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
19
19
  auto_editor/cmds/repl.py,sha256=8DgMw-XyfR5XctSmwtk4_1-zxs3ooMs72BfMRlVqLvY,3412
20
20
  auto_editor/cmds/subdump.py,sha256=af_XBf7kaevqHn1A71z8C-7x8pS5WKD9FE_ugkCw6rk,665
21
- auto_editor/cmds/test.py,sha256=SzjtsEteztXFbkvLRfL9BPSgK8Qez_-yXPR5ts_tu9E,26465
21
+ auto_editor/cmds/test.py,sha256=jdLSFRQfnD4oybCb_NZvnbac9yweyYYbL6m6EvGgEvI,26742
22
22
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  auto_editor/formats/fcp11.py,sha256=sqjC36jI47ICPLjZJYiqGwY7foOnWOiNjkPFLdgSnI4,5208
24
24
  auto_editor/formats/fcp7.py,sha256=x5cagTzGCAW3i3M6m7TZC1h8gLfSmX1UK-iiDuCpdfs,20289
@@ -36,9 +36,9 @@ auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,
36
36
  auto_editor/lib/data_structs.py,sha256=Hnzl5gWvo-geTU0g-lGejj6HQW3VvPv0NBEj2XoGskY,7089
37
37
  auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
38
38
  auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- auto_editor/render/audio.py,sha256=_GuX0WNY1YeumgBN3bWqgwVXiuhpvx7sijABxqyO2ag,12580
39
+ auto_editor/render/audio.py,sha256=Mmdw1AO8a_9nfHpiTECB3Nr7_AbIJACb9tXNdiG86tk,12633
40
40
  auto_editor/render/subtitle.py,sha256=jtNRKvgo1fpHTrAfGZqdkNeNgGgasw-K-4PwIKiWwfM,6231
41
- auto_editor/render/video.py,sha256=JBVl8w-hQ6zrs97iA527LPsBZ9s601SVSJs2bSMCq88,12185
41
+ auto_editor/render/video.py,sha256=jfc-lesZojB5XzBXmrUdnxf9tkNBrv_i73zJgJHIZVM,12182
42
42
  auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  auto_editor/utils/bar.py,sha256=Ky9JRf37JTgLyvNuIXDfucaUE8H1vBbCqKLjttmsmmo,4156
44
44
  auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
@@ -46,11 +46,11 @@ auto_editor/utils/cmdkw.py,sha256=aUGBvBel2Ko1o6Rwmr4rEL-BMc5hEnzYLbyZ1GeJdcY,57
46
46
  auto_editor/utils/container.py,sha256=C_Ahh7nlMEX4DNQ2M_cITPPbYcIL68r4I_AgFy0OD6o,2487
47
47
  auto_editor/utils/func.py,sha256=C8ucgsSEzPyBc-8obhsCXd_uQW0cnCdBn1KVxB7FHjU,2747
48
48
  auto_editor/utils/log.py,sha256=wPNf6AabV-0cnoS_bPLv1Lh7llQBtNqPKeh07einOuc,3701
49
- auto_editor/utils/types.py,sha256=HxFafTgmL_5HQbfR7AR_4Q6o8iHk1z-T6WT90PgQHiY,10817
49
+ auto_editor/utils/types.py,sha256=j2hd4zMQ9EftDy41Ji2_PFru_7HEZObd9yKA0BJxFaY,7616
50
50
  docs/build.py,sha256=POy8X8QOBYe_8A8HI_yiVI_Qg9E5mLpn1z7AHQr0_vQ,1888
51
- auto_editor-26.3.2.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
52
- auto_editor-26.3.2.dist-info/METADATA,sha256=lxdGmJqB_b7J1myUG1CFUnXFKo0Vt2Kr8cG7rl58xK8,6111
53
- auto_editor-26.3.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
54
- auto_editor-26.3.2.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
- auto_editor-26.3.2.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
- auto_editor-26.3.2.dist-info/RECORD,,
51
+ auto_editor-26.3.3.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
52
+ auto_editor-26.3.3.dist-info/METADATA,sha256=lUDeqqWRP5s6Ov-LHYpQ_neYQflY-cUOZsFT1HV3rqg,6130
53
+ auto_editor-26.3.3.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
54
+ auto_editor-26.3.3.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
+ auto_editor-26.3.3.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
+ auto_editor-26.3.3.dist-info/RECORD,,