auto-editor 25.3.0__py3-none-any.whl → 26.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.
@@ -141,7 +141,10 @@ def run_tests(tests: list[Callable], args: TestArgs) -> None:
141
141
  except Exception as e:
142
142
  dur = perf_counter() - start
143
143
  total_time += dur
144
- print(f"{name:<24} ({index}/{total}) {round(dur, 2):<4} secs [FAILED]")
144
+ print(
145
+ f"{name:<24} ({index}/{total}) {round(dur, 2):<4} secs \033[1;31m[FAILED]\033[0m",
146
+ flush=True,
147
+ )
145
148
  if args.no_fail_fast:
146
149
  print(f"\n{e}")
147
150
  else:
@@ -150,8 +153,10 @@ def run_tests(tests: list[Callable], args: TestArgs) -> None:
150
153
  raise e
151
154
  else:
152
155
  passed += 1
153
- print(f"{name:<24} ({index}/{total}) {round(dur, 2):<4} secs [PASSED]")
154
-
156
+ print(
157
+ f"{name:<24} ({index}/{total}) {round(dur, 2):<4} secs [\033[1;32mPASSED\033[0m]",
158
+ flush=True,
159
+ )
155
160
  if outputs is not None:
156
161
  if isinstance(outputs, str):
157
162
  outputs = [outputs]
@@ -186,7 +191,7 @@ def main(sys_args: list[str] | None = None):
186
191
  "wav/pcm-f32le.wav",
187
192
  "wav/pcm-s32le.wav",
188
193
  "multi-track.mov",
189
- "subtitle.mp4",
194
+ "mov_text.mp4",
190
195
  "testsrc.mkv",
191
196
  )
192
197
 
@@ -222,7 +227,8 @@ def main(sys_args: list[str] | None = None):
222
227
  run.raw(["levels", "resources/new-commentary.mp3"])
223
228
 
224
229
  def subdump():
225
- run.raw(["subdump", "resources/subtitle.mp4"])
230
+ run.raw(["subdump", "resources/mov_text.mp4"])
231
+ run.raw(["subdump", "resources/webvtt.mkv"])
226
232
 
227
233
  def desc():
228
234
  run.raw(["desc", "example.mp4"])
@@ -233,7 +239,7 @@ def main(sys_args: list[str] | None = None):
233
239
  video = cn.videos[0]
234
240
 
235
241
  assert video.fps == 30
236
- assert video.time_base == Fraction(1, 30)
242
+ # assert video.time_base == Fraction(1, 30)
237
243
  assert video.width == 1280
238
244
  assert video.height == 720
239
245
  assert video.codec == "h264"
@@ -338,7 +344,10 @@ def main(sys_args: list[str] | None = None):
338
344
  )
339
345
 
340
346
  def track_tests():
341
- return run.main(["resources/multi-track.mov"], ["--keep_tracks_seperate"])
347
+ out = run.main(["resources/multi-track.mov"], ["--keep_tracks_seperate"])
348
+ assert len(fileinfo(out).audios) == 2
349
+
350
+ return out
342
351
 
343
352
  def export_json_tests():
344
353
  out = run.main(["example.mp4"], ["--export_as_json"])
@@ -357,7 +366,13 @@ def main(sys_args: list[str] | None = None):
357
366
  run.main(["example.mp4"], ["--export", 'premiere:name="Foo Bar"'])
358
367
 
359
368
  def export_subtitles():
360
- cn = fileinfo(run.main(["resources/subtitle.mp4"], []))
369
+ # cn = fileinfo(run.main(["resources/mov_text.mp4"], []))
370
+
371
+ # assert len(cn.videos) == 1
372
+ # assert len(cn.audios) == 1
373
+ # assert len(cn.subtitles) == 1
374
+
375
+ cn = fileinfo(run.main(["resources/webvtt.mkv"], []))
361
376
 
362
377
  assert len(cn.videos) == 1
363
378
  assert len(cn.audios) == 1
@@ -458,47 +473,44 @@ def main(sys_args: list[str] | None = None):
458
473
  def frame_rate():
459
474
  cn = fileinfo(run.main(["example.mp4"], ["-r", "15", "--no-seek"]))
460
475
  video = cn.videos[0]
461
- assert video.fps == 15
462
- assert video.time_base == Fraction(1, 15)
463
- assert float(video.duration) - 17.33333333333333333333333 < 3
476
+ assert video.fps == 15, video.fps
477
+ assert video.duration - 17.33333333333333333333333 < 3, video.duration
464
478
 
465
479
  cn = fileinfo(run.main(["example.mp4"], ["-r", "20"]))
466
480
  video = cn.videos[0]
467
- assert video.fps == 20
468
- assert video.time_base == Fraction(1, 20)
469
- assert float(video.duration) - 17.33333333333333333333333 < 2
481
+ assert video.fps == 20, video.fps
482
+ assert video.duration - 17.33333333333333333333333 < 2
470
483
 
471
484
  cn = fileinfo(out := run.main(["example.mp4"], ["-r", "60"]))
472
485
  video = cn.videos[0]
473
486
 
474
- assert video.fps == 60
475
- assert video.time_base == Fraction(1, 60)
476
- assert float(video.duration) - 17.33333333333333333333333 < 0.3
487
+ assert video.fps == 60, video.fps
488
+ assert video.duration - 17.33333333333333333333333 < 0.3
477
489
 
478
490
  return out
479
491
 
480
- def embedded_image():
481
- out1 = run.main(["resources/embedded-image/h264-png.mp4"], [])
482
- cn = fileinfo(out1)
483
- assert cn.videos[0].codec == "h264"
484
- assert cn.videos[1].codec == "png"
492
+ # def embedded_image():
493
+ # out1 = run.main(["resources/embedded-image/h264-png.mp4"], [])
494
+ # cn = fileinfo(out1)
495
+ # assert cn.videos[0].codec == "h264"
496
+ # assert cn.videos[1].codec == "png"
485
497
 
486
- out2 = run.main(["resources/embedded-image/h264-mjpeg.mp4"], [])
487
- cn = fileinfo(out2)
488
- assert cn.videos[0].codec == "h264"
489
- assert cn.videos[1].codec == "mjpeg"
498
+ # out2 = run.main(["resources/embedded-image/h264-mjpeg.mp4"], [])
499
+ # cn = fileinfo(out2)
500
+ # assert cn.videos[0].codec == "h264"
501
+ # assert cn.videos[1].codec == "mjpeg"
490
502
 
491
- out3 = run.main(["resources/embedded-image/h264-png.mkv"], [])
492
- cn = fileinfo(out3)
493
- assert cn.videos[0].codec == "h264"
494
- assert cn.videos[1].codec == "png"
503
+ # out3 = run.main(["resources/embedded-image/h264-png.mkv"], [])
504
+ # cn = fileinfo(out3)
505
+ # assert cn.videos[0].codec == "h264"
506
+ # assert cn.videos[1].codec == "png"
495
507
 
496
- out4 = run.main(["resources/embedded-image/h264-mjpeg.mkv"], [])
497
- cn = fileinfo(out4)
498
- assert cn.videos[0].codec == "h264"
499
- assert cn.videos[1].codec == "mjpeg"
508
+ # out4 = run.main(["resources/embedded-image/h264-mjpeg.mkv"], [])
509
+ # cn = fileinfo(out4)
510
+ # assert cn.videos[0].codec == "h264"
511
+ # assert cn.videos[1].codec == "mjpeg"
500
512
 
501
- return out1, out2, out3, out4
513
+ # return out1, out2, out3, out4
502
514
 
503
515
  def motion():
504
516
  out = run.main(
@@ -744,7 +756,6 @@ def main(sys_args: list[str] | None = None):
744
756
  sr_units,
745
757
  backwards_range,
746
758
  cut_out,
747
- embedded_image,
748
759
  gif,
749
760
  margin_tests,
750
761
  input_extension,
@@ -80,6 +80,8 @@ def container_constructor(ext: str) -> Container:
80
80
  scodecs = set()
81
81
 
82
82
  for codec in codecs:
83
+ if ext == "wav" and codec == "aac":
84
+ continue
83
85
  kind = codec_type(codec)
84
86
  if kind == "video":
85
87
  vcodecs.add(codec)
auto_editor/utils/func.py CHANGED
@@ -10,8 +10,6 @@ if TYPE_CHECKING:
10
10
 
11
11
  from numpy.typing import NDArray
12
12
 
13
- from auto_editor.utils.log import Log
14
-
15
13
  BoolList = NDArray[np.bool_]
16
14
  BoolOperand = Callable[[BoolList, BoolList], BoolList]
17
15
 
@@ -43,7 +41,7 @@ def to_timecode(secs: float | Fraction, fmt: str) -> str:
43
41
  if h == 0:
44
42
  return f"{sign}{m:02d}:{s:06.3f}"
45
43
  return f"{sign}{h:02d}:{m:02d}:{s:06.3f}"
46
- if fmt == "mov_text":
44
+ if fmt == "srt" or fmt == "mov_text":
47
45
  return f"{sign}{h:02d}:{m:02d}:" + f"{s:06.3f}".replace(".", ",", 1)
48
46
  if fmt == "standard":
49
47
  return f"{sign}{h:02d}:{m:02d}:{s:06.3f}"
@@ -135,30 +133,6 @@ def human_readable_time(time_in_secs: float) -> str:
135
133
  return f"{time_in_secs} {units}"
136
134
 
137
135
 
138
- def open_with_system_default(path: str, log: Log) -> None:
139
- import sys
140
- from subprocess import run
141
-
142
- if sys.platform == "win32":
143
- from os import startfile
144
-
145
- try:
146
- startfile(path)
147
- except OSError:
148
- log.warning("Could not find application to open file.")
149
- else:
150
- try: # MacOS case
151
- run(["open", path])
152
- except Exception:
153
- try: # WSL2 case
154
- run(["cmd.exe", "/C", "start", path])
155
- except Exception:
156
- try: # Linux case
157
- run(["xdg-open", path])
158
- except Exception:
159
- log.warning("Could not open output file.")
160
-
161
-
162
136
  def append_filename(path: str, val: str) -> str:
163
137
  from os.path import splitext
164
138
 
@@ -111,17 +111,6 @@ def sample_rate(val: str) -> int:
111
111
  return natural(num)
112
112
 
113
113
 
114
- def bitrate(val: str) -> str:
115
- if val == "unset":
116
- return val
117
- _num, unit = _split_num_str(val)
118
- num = int(_num) if _num.is_integer() else _num
119
- if unit not in ("", "k", "K", "M"):
120
- extra = f". Did you mean `{num}M`?" if unit == "m" else ""
121
- raise CoerceError(f"`{val}` is not a valid bitrate format{extra}")
122
- return val
123
-
124
-
125
114
  def time(val: str, tb: Fraction) -> int:
126
115
  if ":" in val:
127
116
  boxes = val.split(":")
@@ -218,11 +207,9 @@ class Args:
218
207
  yt_dlp_extras: str | None = None
219
208
  video_codec: str = "auto"
220
209
  audio_codec: str = "auto"
221
- video_bitrate: str = "10M"
222
- audio_bitrate: str = "unset"
223
- video_quality_scale: str = "unset"
210
+ video_bitrate: str = "auto"
211
+ audio_bitrate: str = "auto"
224
212
  scale: float = 1.0
225
- extras: str | None = None
226
213
  sn: bool = False
227
214
  dn: bool = False
228
215
  no_seek: bool = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-editor
3
- Version: 25.3.0
3
+ Version: 26.0.0
4
4
  Summary: Auto-Editor: Effort free video editing!
5
5
  Author-email: WyattBlue <wyattblue@auto-editor.com>
6
6
  License: Unlicense
@@ -12,7 +12,7 @@ Requires-Python: <3.14,>=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: numpy <3.0,>=1.23.0
15
- Requires-Dist: pyav ==13.0.*
15
+ Requires-Dist: pyav ==13.1.*
16
16
  Requires-Dist: ae-ffmpeg ==1.2.*
17
17
 
18
18
  <p align="center"><img src="https://auto-editor.com/img/auto-editor-banner.webp" title="Auto-Editor" width="700"></p>
@@ -1,14 +1,13 @@
1
- auto_editor/__init__.py,sha256=V88FPgx32W4RJtvMHBXxRB94vigVyEqLl2H3hz7xxsc,23
2
- auto_editor/__main__.py,sha256=XUzQoN8mMrm0RVqOHnkl-ksw53Klk7fHdGhJObtDZfE,10102
3
- auto_editor/analyze.py,sha256=0WgeEzb1BB3C1MvaGfWZGg35MTYEL1YdHFYqwguSPbc,11869
4
- auto_editor/edit.py,sha256=0dYqFAP1TwBh-40k14dEAaLUDhzqoZFFLuaNxs45Dis,11740
5
- auto_editor/ffwrapper.py,sha256=88r-1DN0hD33myt4-tGMqphLJYU7wBl75lYs6MaNJQE,8634
6
- auto_editor/help.py,sha256=BFiP7vBz42TUzum4-zaQIrV1OY7kHeN0pe0MPE0T5xw,7997
1
+ auto_editor/__init__.py,sha256=WK8aO_sVSNz80fWXrArodylgnY_BkbEjGQlGWEzflks,23
2
+ auto_editor/__main__.py,sha256=ktxF22s9uR8U0rtB8TV-2Kj0hwjzyb2k7e1A1_yAJwQ,12006
3
+ auto_editor/analyze.py,sha256=uCi21659BB-lbPwZ6yxNLekS6Q3yoB2ypLNXPhmhTfg,11688
4
+ auto_editor/edit.py,sha256=DwkFnWs__F9RNz_7E_fgT93sJywAerhx75Xz4aw3Nls,16728
5
+ auto_editor/ffwrapper.py,sha256=XB-N0ex8zkIcItnr9kilC6SSf0TjUI7og-U5Q8H3BJ0,6059
6
+ auto_editor/help.py,sha256=62s3L0rlhA7nkkOjtXItRUl779EJ__A7_6E-VFH3J_E,7924
7
7
  auto_editor/make_layers.py,sha256=8uFy5SvMArAP-5slYJrxa_iGAEwimQBFeM-T01VORVw,8995
8
- auto_editor/output.py,sha256=Ai5JtSK0-wYED9cS0A2BXbMO6e09aMcaZOIsupOsPJ8,8002
8
+ auto_editor/output.py,sha256=9YBBEASsXpxuBQJeGTQ5Ed8a0Fr8660DjJ85LeSrPe8,2502
9
9
  auto_editor/preview.py,sha256=HUsjmV9Fx73rZ26BXrpz9z-z_e4oiui3u9e7qbbGoBY,3037
10
10
  auto_editor/timeline.py,sha256=tIty8O8jD6TR2Sw2bivUtYtdnpplfuOXT7Dfc-gekr8,8174
11
- auto_editor/validate_input.py,sha256=_9vtbNxodhVPf4PzTAH67LSj2K-HS6kShJOARmUMJdY,2904
12
11
  auto_editor/vanparse.py,sha256=f0vViZ-aYtDxEyVrFHJ5X2pPTQAfqtw3N2gZgzn51kU,10002
13
12
  auto_editor/wavfile.py,sha256=1HbZ4L8IBD6Fbg3pd5MQG4ZXy48YZA05t8XllSplhWk,9499
14
13
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -28,31 +27,30 @@ auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,
28
27
  auto_editor/lib/data_structs.py,sha256=dcsXgsLLzbmFDUZucoirzewPALsKzoxz7z5L22_QJM8,7091
29
28
  auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
30
29
  auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- auto_editor/render/audio.py,sha256=Jo6d4r75QkkDRPBLFd5UQ4JsWC9sx9rnJ80Gf8R2pfg,9715
32
- auto_editor/render/subtitle.py,sha256=CNcU_hmLwfZ2yvmSllIK-pB7ge_YdXHR-fnnOVUO9m8,4425
33
- auto_editor/render/video.py,sha256=OWtziP6l9AuMdeb3I7CBv8SlgaXS5BcRilCX47BkadU,12492
30
+ auto_editor/render/audio.py,sha256=OWkpKtB9cdcJztTTOPe6ONgWAnvuotKD1A_28gfart4,10675
31
+ auto_editor/render/subtitle.py,sha256=TTUGO77L_iGm9Qkjq5LGERjECA2xriSV9G1fN_SQiqM,6200
32
+ auto_editor/render/video.py,sha256=l1qgnS1ArAOdq_db8vIHcvtZiMCX1jF20lqYhZHMxik,12264
34
33
  auto_editor/subcommands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
34
  auto_editor/subcommands/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
36
35
  auto_editor/subcommands/info.py,sha256=t5n43HLt9hpMFSIfGV777X4zIPBAFugOKlpCfRjiKxY,6921
37
36
  auto_editor/subcommands/levels.py,sha256=ChJMDTd34-jgxewqHRmmd3VNhFdy964w0DcQG0ls-hY,4079
38
37
  auto_editor/subcommands/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
39
- auto_editor/subcommands/repl.py,sha256=DuMz5kImoZFSVMZh6sPQxqZXMbRXPCvXoW3G-MJfivc,3166
38
+ auto_editor/subcommands/repl.py,sha256=TF_I7zsFY7-KdgidrqjafTz7o_eluVbLvgTcOBG-UWQ,3449
40
39
  auto_editor/subcommands/subdump.py,sha256=af_XBf7kaevqHn1A71z8C-7x8pS5WKD9FE_ugkCw6rk,665
41
- auto_editor/subcommands/test.py,sha256=aGB9muIl4b49j_vpll-Sv8O-yqRU646_Dv_hL6GdWgU,25443
40
+ auto_editor/subcommands/test.py,sha256=rM6ihsmYyW745lpFVeGwWbRHLTEymLhm1pRpmm7LdAo,25776
42
41
  auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
42
  auto_editor/utils/bar.py,sha256=hG_NiYeuM90TdILzAJORft-UOS5grwWN3SbRuj6upsI,3998
44
43
  auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
45
44
  auto_editor/utils/cmdkw.py,sha256=uW_qDGQ6UzLPRbB20HTLrCmhMlWVXSfgyQMr4MyGErU,5734
46
- auto_editor/utils/container.py,sha256=RnpoMmMYmn7o69LmMbBFHW4TsP3K52jYDhG9qzWXmAs,2720
45
+ auto_editor/utils/container.py,sha256=ajIiMRu5ZoBxRkGgR4UZORMgsD6fwl1pT2C3aoI5FcM,2785
47
46
  auto_editor/utils/encoder.py,sha256=auNYo7HXbcU4iTUCc0LE5lpwFmSvdWvBm6-5KIaRK8w,2983
48
- auto_editor/utils/func.py,sha256=kcxCOqe-tg6k-kxutIran8LpffRiHDjKB6rm-ngFiSU,4460
47
+ auto_editor/utils/func.py,sha256=hASpY8zr41cBIByKY8zZUZGVqDz7B0snwV1RYODbKD8,3741
49
48
  auto_editor/utils/log.py,sha256=M2QKeQHMRNLm3HMVUKedZPRprT2u5dipOStiO4miPBk,3613
50
- auto_editor/utils/subtitle_tools.py,sha256=TjjVPiT8bWzZJcrZjF7ddpgfIsVkLE4LyxXzBswHAGU,693
51
- auto_editor/utils/types.py,sha256=BWj0YalUpWwXfEN7AEL5s_j22lscZOH-eb7x_dYuXfY,11471
52
- docs/build.py,sha256=8nJM_CgkSL5ilvzcJDGNjzoWpCphPJKZKguHazG6YK8,1641
53
- auto_editor-25.3.0.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
54
- auto_editor-25.3.0.dist-info/METADATA,sha256=2X6Gs23pxl1aDj5NT5yVlFHoluhP0z838ADlO3v2oQw,6148
55
- auto_editor-25.3.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
56
- auto_editor-25.3.0.dist-info/entry_points.txt,sha256=-H7zdTw4MqnAcwrN5xTNkGIhzZtJMxS9r6lTMeR9-aA,240
57
- auto_editor-25.3.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
58
- auto_editor-25.3.0.dist-info/RECORD,,
49
+ auto_editor/utils/types.py,sha256=FWa3SnOATMa_MSTjpdasl80b4WKTRIMLS0IlyYyMGxo,11043
50
+ docs/build.py,sha256=CM-ZWgQk8wSNjivx_-6wGIaG7cstrNKsX2d4TzFVivE,1642
51
+ auto_editor-26.0.0.dist-info/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
52
+ auto_editor-26.0.0.dist-info/METADATA,sha256=kHlPRQm-pu3oeW7_h3Ag1DIQYL_AwRvFkKx38yznj4k,6148
53
+ auto_editor-26.0.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
54
+ auto_editor-26.0.0.dist-info/entry_points.txt,sha256=-H7zdTw4MqnAcwrN5xTNkGIhzZtJMxS9r6lTMeR9-aA,240
55
+ auto_editor-26.0.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
+ auto_editor-26.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
docs/build.py CHANGED
@@ -49,5 +49,6 @@ def main():
49
49
  print(e)
50
50
  quit(1)
51
51
 
52
+
52
53
  if __name__ == "__main__":
53
54
  main()
@@ -1,29 +0,0 @@
1
- def convert_ass_to_text(ass_text: str) -> str:
2
- result = ""
3
- comma_count = i = 0
4
-
5
- while comma_count < 8 and i < len(ass_text):
6
- if ass_text[i] == ",":
7
- comma_count += 1
8
- i += 1
9
-
10
- state = False
11
- while i < len(ass_text):
12
- char = ass_text[i]
13
- next_char = "" if i + 1 >= len(ass_text) else ass_text[i + 1]
14
-
15
- if char == "\\" and next_char == "N":
16
- result += "\n"
17
- i += 2
18
- continue
19
-
20
- if not state:
21
- if char == "{":
22
- state = True
23
- else:
24
- result += ass_text[i]
25
- elif char == "}":
26
- state = False
27
- i += 1
28
-
29
- return result
@@ -1,88 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- import subprocess
5
- import sys
6
- from os.path import exists, isdir, isfile, lexists, splitext
7
-
8
- from auto_editor.ffwrapper import FFmpeg
9
- from auto_editor.utils.func import get_stdout
10
- from auto_editor.utils.log import Log
11
- from auto_editor.utils.types import Args
12
-
13
-
14
- def get_domain(url: str) -> str:
15
- from urllib.parse import urlparse
16
-
17
- t = urlparse(url).netloc
18
- return ".".join(t.split(".")[-2:])
19
-
20
-
21
- def download_video(my_input: str, args: Args, ffmpeg: FFmpeg, log: Log) -> str:
22
- log.conwrite("Downloading video...")
23
-
24
- download_format = args.download_format
25
-
26
- if download_format is None and get_domain(my_input) == "youtube.com":
27
- download_format = "bestvideo[ext=mp4]+bestaudio[ext=m4a]"
28
-
29
- if args.output_format is None:
30
- output_format = re.sub(r"\W+", "-", splitext(my_input)[0]) + ".%(ext)s"
31
- else:
32
- output_format = args.output_format
33
-
34
- yt_dlp_path = args.yt_dlp_location
35
-
36
- cmd = ["--ffmpeg-location", ffmpeg.path]
37
-
38
- if download_format is not None:
39
- cmd.extend(["-f", download_format])
40
-
41
- cmd.extend(["-o", output_format, my_input])
42
-
43
- if args.yt_dlp_extras is not None:
44
- cmd.extend(args.yt_dlp_extras.split(" "))
45
-
46
- try:
47
- location = get_stdout(
48
- [yt_dlp_path, "--get-filename", "--no-warnings"] + cmd
49
- ).strip()
50
- except FileNotFoundError:
51
- msg = "Could not find program 'yt-dlp' when attempting to download a URL. Install yt-dlp with "
52
- if sys.platform == "win32":
53
- msg += "your favorite package manager (pip, choco, winget)."
54
- elif sys.platform == "darwin":
55
- msg += "brew or pip and make sure it's in PATH."
56
- else:
57
- msg += "pip or your favorite package manager and make sure it's in PATH."
58
- log.error(msg)
59
-
60
- if not isfile(location):
61
- subprocess.run([yt_dlp_path] + cmd)
62
-
63
- if not isfile(location):
64
- log.error(f"Download file wasn't created: {location}")
65
-
66
- return location
67
-
68
-
69
- def valid_input(inputs: list[str], ffmpeg: FFmpeg, args: Args, log: Log) -> list[str]:
70
- result = []
71
-
72
- for my_input in inputs:
73
- if my_input.startswith("http://") or my_input.startswith("https://"):
74
- result.append(download_video(my_input, args, ffmpeg, log))
75
- else:
76
- _, ext = splitext(my_input)
77
- if ext == "":
78
- if isdir(my_input):
79
- log.error("Input must be a file or a URL, not a directory.")
80
- if exists(my_input):
81
- log.error(f"Input file must have an extension: {my_input}")
82
- if lexists(my_input):
83
- log.error(f"Input file is a broken symbolic link: {my_input}")
84
- if my_input.startswith("-"):
85
- log.error(f"Option/Input file doesn't exist: {my_input}")
86
- result.append(my_input)
87
-
88
- return result