easyrip 4.11.2__py3-none-any.whl → 4.12.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.
- easyrip/__init__.py +1 -10
- easyrip/easyrip_command.py +18 -16
- easyrip/easyrip_main.py +33 -34
- easyrip/easyrip_mlang/__init__.py +2 -11
- easyrip/easyrip_mlang/global_lang_val.py +26 -164
- easyrip/easyrip_mlang/lang_en.py +2 -2
- easyrip/easyrip_mlang/lang_tag_val.py +145 -0
- easyrip/easyrip_mlang/lang_zh_Hans_CN.py +12 -12
- easyrip/easyrip_mlang/translator.py +24 -29
- easyrip/easyrip_prompt.py +104 -26
- easyrip/global_val.py +1 -1
- easyrip/ripper/param.py +66 -17
- easyrip/ripper/ripper.py +278 -203
- {easyrip-4.11.2.dist-info → easyrip-4.12.0.dist-info}/METADATA +1 -1
- easyrip-4.12.0.dist-info/RECORD +32 -0
- easyrip-4.11.2.dist-info/RECORD +0 -31
- {easyrip-4.11.2.dist-info → easyrip-4.12.0.dist-info}/WHEEL +0 -0
- {easyrip-4.11.2.dist-info → easyrip-4.12.0.dist-info}/entry_points.txt +0 -0
- {easyrip-4.11.2.dist-info → easyrip-4.12.0.dist-info}/licenses/LICENSE +0 -0
- {easyrip-4.11.2.dist-info → easyrip-4.12.0.dist-info}/top_level.txt +0 -0
easyrip/ripper/ripper.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
3
|
import shutil
|
|
4
|
+
import textwrap
|
|
4
5
|
from collections.abc import Callable, Iterable
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
from datetime import datetime
|
|
@@ -16,11 +17,8 @@ from ..easyrip_mlang import Global_lang_val, gettext, translate_subtitles
|
|
|
16
17
|
from ..utils import get_base62_time
|
|
17
18
|
from .media_info import Media_info, Stream_error
|
|
18
19
|
from .param import (
|
|
19
|
-
DEFAULT_PRESET_PARAMS,
|
|
20
20
|
FONT_SUFFIX_SET,
|
|
21
21
|
SUBTITLE_SUFFIX_SET,
|
|
22
|
-
X264_PARAMS_NAME,
|
|
23
|
-
X265_PARAMS_NAME,
|
|
24
22
|
)
|
|
25
23
|
from .sub_and_font import subset
|
|
26
24
|
|
|
@@ -53,13 +51,13 @@ class Ripper:
|
|
|
53
51
|
@dataclass(slots=True)
|
|
54
52
|
class Option:
|
|
55
53
|
preset_name: "Ripper.Preset_name"
|
|
56
|
-
|
|
54
|
+
encoder_format_str_list: list[str]
|
|
57
55
|
audio_encoder: "Ripper.Audio_codec | None"
|
|
58
56
|
muxer: "Ripper.Muxer | None"
|
|
59
|
-
|
|
57
|
+
muxer_format_str_list: list[str]
|
|
60
58
|
|
|
61
59
|
def __str__(self) -> str:
|
|
62
|
-
return f" preset_name = {self.preset_name}\n option_format = {self.
|
|
60
|
+
return f" preset_name = {self.preset_name}\n option_format = {self.encoder_format_str_list}"
|
|
63
61
|
|
|
64
62
|
input_path_list: list[Path]
|
|
65
63
|
output_prefix_list: list[str]
|
|
@@ -134,6 +132,13 @@ class Ripper:
|
|
|
134
132
|
)
|
|
135
133
|
|
|
136
134
|
def preset_name_to_option(self, preset_name: Preset_name) -> Option:
|
|
135
|
+
if os.name == "nt":
|
|
136
|
+
cmd_head_del = "del /Q"
|
|
137
|
+
cmd_head_copy = "copy"
|
|
138
|
+
else:
|
|
139
|
+
cmd_head_del = "rm /f"
|
|
140
|
+
cmd_head_copy = "cp"
|
|
141
|
+
|
|
137
142
|
if (
|
|
138
143
|
force_fps := self.option_map.get("r") or self.option_map.get("fps")
|
|
139
144
|
) == "auto":
|
|
@@ -238,28 +243,26 @@ class Ripper:
|
|
|
238
243
|
audio_option = ""
|
|
239
244
|
|
|
240
245
|
# Muxer
|
|
246
|
+
muxer_format_str_list: list[str]
|
|
241
247
|
if muxer := self.option_map.get("muxer"):
|
|
242
248
|
muxer = Ripper.Muxer(muxer)
|
|
243
249
|
|
|
244
250
|
match muxer:
|
|
245
251
|
case Ripper.Muxer.mp4:
|
|
246
|
-
|
|
247
|
-
'
|
|
252
|
+
muxer_format_str_list = [
|
|
253
|
+
'mp4box -add "{output}" -new "{output}" '
|
|
248
254
|
+ (
|
|
249
255
|
f"-chap {chapters} "
|
|
250
256
|
if (chapters := self.option_map.get("chapters"))
|
|
251
257
|
else ""
|
|
252
258
|
)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
+ ' -i "{output}"'
|
|
260
|
-
)
|
|
259
|
+
]
|
|
260
|
+
if self.preset_name != Ripper.Preset_name.flac:
|
|
261
|
+
muxer_format_str_list.append(
|
|
262
|
+
"mp4fpsmod "
|
|
263
|
+
+ (f"-r 0:{force_fps}" if force_fps else "")
|
|
264
|
+
+ ' -i "{output}"'
|
|
261
265
|
)
|
|
262
|
-
)
|
|
263
266
|
|
|
264
267
|
case Ripper.Muxer.mkv:
|
|
265
268
|
if (
|
|
@@ -270,46 +273,52 @@ class Ripper:
|
|
|
270
273
|
log.error("It is not a dir: {}", only_mux_sub_path)
|
|
271
274
|
only_mux_sub_path = None
|
|
272
275
|
|
|
273
|
-
|
|
274
|
-
'
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
276
|
+
muxer_format_str_list = [
|
|
277
|
+
'mkvpropedit "{output}" --add-track-statistics-tags',
|
|
278
|
+
'mkvmerge -o "{output}.temp.mkv" "{output}"',
|
|
279
|
+
(
|
|
280
|
+
'mkvmerge -o "{output}" '
|
|
281
|
+
+ (
|
|
282
|
+
f"--default-duration 0:{force_fps}fps --fix-bitstream-timing-information 0:1 "
|
|
283
|
+
if force_fps and only_mux_sub_path is None
|
|
284
|
+
else ""
|
|
285
|
+
)
|
|
286
|
+
+ (
|
|
287
|
+
f"--chapters {chapters} "
|
|
288
|
+
if (chapters := self.option_map.get("chapters"))
|
|
289
|
+
else ""
|
|
290
|
+
)
|
|
291
|
+
+ (
|
|
292
|
+
" ".join(
|
|
293
|
+
(
|
|
294
|
+
""
|
|
295
|
+
if len(
|
|
296
|
+
affixes := _file.stem.rsplit(
|
|
297
|
+
".", maxsplit=1
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
== 1
|
|
301
|
+
else "--attach-file "
|
|
302
|
+
if _file.suffix in FONT_SUFFIX_SET
|
|
303
|
+
else f"--language 0:{affixes[1]} --track-name 0:{Global_lang_val.language_tag_to_local_str(affixes[1])} "
|
|
291
304
|
)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if _file.suffix
|
|
295
|
-
|
|
305
|
+
+ f'"{_file.absolute()}"'
|
|
306
|
+
for _file in only_mux_sub_path.iterdir()
|
|
307
|
+
if _file.suffix
|
|
308
|
+
in (SUBTITLE_SUFFIX_SET | FONT_SUFFIX_SET)
|
|
296
309
|
)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if _file.suffix
|
|
300
|
-
in (SUBTITLE_SUFFIX_SET | FONT_SUFFIX_SET)
|
|
310
|
+
if only_mux_sub_path
|
|
311
|
+
else ""
|
|
301
312
|
)
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
)
|
|
313
|
+
+ ' --no-global-tags --no-track-tags --default-track-flag 0 "{output}.temp.mkv"'
|
|
314
|
+
),
|
|
315
|
+
cmd_head_del + ' "{output}.temp.mkv"',
|
|
316
|
+
]
|
|
307
317
|
|
|
308
318
|
else:
|
|
309
319
|
muxer = None
|
|
310
|
-
|
|
320
|
+
muxer_format_str_list = []
|
|
311
321
|
|
|
312
|
-
vspipe_input: str = ""
|
|
313
322
|
pipe_gvar_list = [
|
|
314
323
|
s for s in self.option_map.get("pipe:gvar", "").split(":") if s
|
|
315
324
|
]
|
|
@@ -319,15 +328,6 @@ class Ripper:
|
|
|
319
328
|
if sub_pathname:
|
|
320
329
|
pipe_gvar_dict["subtitle"] = sub_pathname
|
|
321
330
|
|
|
322
|
-
if self.input_path_list[0].suffix == ".vpy":
|
|
323
|
-
vspipe_input = f'vspipe -c y4m {" ".join(f'-a "{k}={v}"' for k, v in pipe_gvar_dict.items())} "{{input}}" - | '
|
|
324
|
-
elif vpy_pathname:
|
|
325
|
-
vspipe_input = f'vspipe -c y4m {" ".join(f'-a "{k}={v}"' for k, v in pipe_gvar_dict.items())} -a "input={{input}}" "{vpy_pathname}" - | '
|
|
326
|
-
|
|
327
|
-
hwaccel = (
|
|
328
|
-
f"-hwaccel {hwaccel}" if (hwaccel := self.option_map.get("hwaccel")) else ""
|
|
329
|
-
)
|
|
330
|
-
|
|
331
331
|
ffparams_ff = self.option_map.get("ff-params:ff") or self.option_map.get(
|
|
332
332
|
"ff-params", ""
|
|
333
333
|
)
|
|
@@ -342,10 +342,45 @@ class Ripper:
|
|
|
342
342
|
|
|
343
343
|
FFMPEG_HEADER = f"ffmpeg {'-hide_banner ' if self.option_map.get('_sub_ripper_num') else ''}-progress {FF_PROGRESS_LOG_FILE} -report {ffparams_ff} {ffparams_in}"
|
|
344
344
|
|
|
345
|
+
def get_vs_ff_cmd(enc_opt: str) -> str:
|
|
346
|
+
vspipe_input: str = ""
|
|
347
|
+
if self.input_path_list[0].suffix == ".vpy":
|
|
348
|
+
vspipe_input = f'vspipe -c y4m {" ".join(f'-a "{k}={v}"' for k, v in pipe_gvar_dict.items())} "{{input}}" - |'
|
|
349
|
+
elif vpy_pathname:
|
|
350
|
+
vspipe_input = f'vspipe -c y4m {" ".join(f'-a "{k}={v}"' for k, v in pipe_gvar_dict.items())} -a "input={{input}}" "{vpy_pathname}" - |'
|
|
351
|
+
|
|
352
|
+
hwaccel = (
|
|
353
|
+
[f"-hwaccel {hwaccel}"]
|
|
354
|
+
if (hwaccel := self.option_map.get("hwaccel"))
|
|
355
|
+
else []
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
return " ".join(
|
|
359
|
+
(
|
|
360
|
+
vspipe_input,
|
|
361
|
+
FFMPEG_HEADER,
|
|
362
|
+
*hwaccel,
|
|
363
|
+
" ".join(f"-i {s}" for s in ff_input_option),
|
|
364
|
+
" ".join(f"-map {s}" for s in ff_stream_option),
|
|
365
|
+
audio_option,
|
|
366
|
+
enc_opt,
|
|
367
|
+
ffparams_out,
|
|
368
|
+
(f'-vf "{",".join(ff_vf_option)}" ' if len(ff_vf_option) else ""),
|
|
369
|
+
'"{output}"',
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
preset_param_getted = {
|
|
374
|
+
_param_name: self.option_map.get(_param_name)
|
|
375
|
+
for _param_name in preset_name.get_param_name_set(set())
|
|
376
|
+
}
|
|
377
|
+
preset_param_default_dict = preset_name.get_param_default_dict({})
|
|
378
|
+
|
|
379
|
+
encoder_format_str_list: list[str]
|
|
345
380
|
match preset_name:
|
|
346
381
|
case Ripper.Preset_name.custom:
|
|
347
382
|
if not (
|
|
348
|
-
|
|
383
|
+
_encoder_format_str := self.option_map.get(
|
|
349
384
|
"custom:format",
|
|
350
385
|
self.option_map.get(
|
|
351
386
|
"custom:template", self.option_map.get("custom")
|
|
@@ -355,67 +390,67 @@ class Ripper:
|
|
|
355
390
|
log.warning(
|
|
356
391
|
"The preset custom must have custom:format or custom:template"
|
|
357
392
|
)
|
|
358
|
-
|
|
393
|
+
encoder_format_str_list = []
|
|
359
394
|
|
|
360
395
|
else:
|
|
361
|
-
if
|
|
362
|
-
|
|
396
|
+
if _encoder_format_str.startswith("''''"):
|
|
397
|
+
_encoder_format_str = _encoder_format_str[4:]
|
|
363
398
|
else:
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
399
|
+
_encoder_format_str = _encoder_format_str.replace("''", '"')
|
|
400
|
+
_encoder_format_str = (
|
|
401
|
+
_encoder_format_str.replace("\\34/", '"')
|
|
367
402
|
.replace("\\39/", "'")
|
|
368
403
|
.format_map(
|
|
369
404
|
self.option_map | {"input": "{input}", "output": "{output}"}
|
|
370
405
|
)
|
|
371
406
|
)
|
|
407
|
+
encoder_format_str_list = [_encoder_format_str]
|
|
372
408
|
|
|
373
409
|
case Ripper.Preset_name.copy:
|
|
374
|
-
|
|
375
|
-
f"
|
|
376
|
-
if (hwaccel := self.option_map.get("hwaccel"))
|
|
377
|
-
else ""
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
encoder_format_str = (
|
|
381
|
-
f"{FFMPEG_HEADER} {hwaccel} "
|
|
410
|
+
encoder_format_str_list = [
|
|
411
|
+
f"{FFMPEG_HEADER} "
|
|
382
412
|
'-i "{input}" -c copy '
|
|
383
413
|
f"{' '.join(f'-map {s}' for s in ff_stream_option)} "
|
|
384
414
|
+ audio_option
|
|
385
415
|
+ ffparams_out
|
|
386
416
|
+ '"{output}"'
|
|
387
|
-
|
|
417
|
+
]
|
|
388
418
|
|
|
419
|
+
_encoder_format_str: str | None = None
|
|
389
420
|
match self.option_map.get("c:a"):
|
|
390
421
|
case None | "flac":
|
|
391
422
|
if muxer == Ripper.Muxer.mp4:
|
|
392
|
-
|
|
423
|
+
_encoder_format_str = (
|
|
424
|
+
'mp4box -add "{input}" -new "{output}"'
|
|
425
|
+
)
|
|
393
426
|
for _audio_info in self.media_info.audio_info:
|
|
394
|
-
|
|
427
|
+
_encoder_format_str += f" -rem {_audio_info.index + 1}"
|
|
395
428
|
else:
|
|
396
|
-
|
|
429
|
+
_encoder_format_str = (
|
|
397
430
|
'mkvmerge -o "{output}" --no-audio "{input}"'
|
|
398
431
|
)
|
|
399
432
|
case "copy":
|
|
400
|
-
|
|
433
|
+
_encoder_format_str = (
|
|
401
434
|
'mp4box -add "{input}" -new "{output}"'
|
|
402
435
|
if muxer == Ripper.Muxer.mp4
|
|
403
436
|
else 'mkvmerge -o "{output}" "{input}"'
|
|
404
437
|
)
|
|
438
|
+
if _encoder_format_str is not None:
|
|
439
|
+
encoder_format_str_list = [_encoder_format_str]
|
|
405
440
|
|
|
406
441
|
case Ripper.Preset_name.flac:
|
|
407
442
|
_ff_encode_str: str = ""
|
|
408
|
-
|
|
443
|
+
_flac_encode_str_list: list[str] = []
|
|
409
444
|
_mux_flac_input_list: list[str] = []
|
|
410
|
-
|
|
411
|
-
_del_flac_str: str = ""
|
|
445
|
+
_del_flac_str_list: list[str] = []
|
|
412
446
|
|
|
413
447
|
for _audio_info in self.media_info.audio_info:
|
|
414
448
|
_encoder: str = (
|
|
415
449
|
"pcm_s24le"
|
|
416
|
-
if
|
|
417
|
-
|
|
418
|
-
|
|
450
|
+
if 24
|
|
451
|
+
in (
|
|
452
|
+
_audio_info.bits_per_raw_sample,
|
|
453
|
+
_audio_info.bits_per_sample,
|
|
419
454
|
)
|
|
420
455
|
else {
|
|
421
456
|
"u8": "pcm_u8",
|
|
@@ -433,47 +468,57 @@ class Ripper:
|
|
|
433
468
|
}.get(_audio_info.sample_fmt, "pcm_s32le")
|
|
434
469
|
)
|
|
435
470
|
|
|
436
|
-
_new_output_str: str =
|
|
471
|
+
_new_output_str: str = "{output}" + f".{_audio_info.index}.temp"
|
|
437
472
|
|
|
438
473
|
_ff_encode_str += (
|
|
439
474
|
f"-map 0:{_audio_info.index} -c:a {_encoder} {ffparams_out} "
|
|
440
475
|
f'"{_new_output_str}.wav" '
|
|
441
476
|
)
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
477
|
+
_flac_encode_str_list = [
|
|
478
|
+
(
|
|
479
|
+
f"flac -j 32 -8 -e -p -l {'19' if _audio_info.sample_rate > 48000 else '12'} "
|
|
480
|
+
f'-o "{_new_output_str}.flac" "{_new_output_str}.wav"'
|
|
481
|
+
),
|
|
482
|
+
f'{cmd_head_del} "{_new_output_str}.wav"',
|
|
483
|
+
]
|
|
446
484
|
|
|
447
485
|
_mux_flac_input_list.append(f'"{_new_output_str}.flac"')
|
|
448
486
|
|
|
449
|
-
|
|
487
|
+
_del_flac_str_list = [f'{cmd_head_del} "{_new_output_str}.flac" ']
|
|
450
488
|
|
|
451
489
|
match len(_mux_flac_input_list):
|
|
452
490
|
case 0:
|
|
453
491
|
raise RuntimeError(f'No audio in "{self.input_path_list[0]}"')
|
|
454
492
|
|
|
455
493
|
case 1 if muxer is None:
|
|
456
|
-
|
|
457
|
-
FFMPEG_HEADER + ' -i "{input}" '
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
494
|
+
encoder_format_str_list = [
|
|
495
|
+
FFMPEG_HEADER + f' -i "{{input}}" {_ff_encode_str}',
|
|
496
|
+
*_flac_encode_str_list,
|
|
497
|
+
(
|
|
498
|
+
f"{cmd_head_copy} {_mux_flac_input_list[0]} "
|
|
499
|
+
'"{output}"' # .
|
|
500
|
+
),
|
|
501
|
+
*_del_flac_str_list,
|
|
502
|
+
]
|
|
463
503
|
|
|
464
504
|
case _:
|
|
465
505
|
_mux_str = (
|
|
466
|
-
|
|
467
|
-
|
|
506
|
+
(
|
|
507
|
+
f"mp4box {' '.join(f'-add {s}' for s in _mux_flac_input_list)} "
|
|
508
|
+
'-new "{output}"'
|
|
509
|
+
)
|
|
468
510
|
if muxer == Ripper.Muxer.mp4
|
|
469
|
-
else
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
FFMPEG_HEADER + ' -i "{input}" '
|
|
474
|
-
f"{_ff_encode_str} {_flac_encode_str} "
|
|
475
|
-
f"&& {_mux_str} " + _del_flac_str
|
|
511
|
+
else (
|
|
512
|
+
'mkvmerge -o "{output}" '
|
|
513
|
+
+ " ".join(_mux_flac_input_list)
|
|
514
|
+
)
|
|
476
515
|
)
|
|
516
|
+
encoder_format_str_list = [
|
|
517
|
+
FFMPEG_HEADER + f' -i "{{input}}" {_ff_encode_str}',
|
|
518
|
+
*_flac_encode_str_list,
|
|
519
|
+
f"{_mux_str}",
|
|
520
|
+
*_del_flac_str_list,
|
|
521
|
+
]
|
|
477
522
|
|
|
478
523
|
case (
|
|
479
524
|
Ripper.Preset_name.x264
|
|
@@ -483,10 +528,7 @@ class Ripper:
|
|
|
483
528
|
_custom_option_map: dict[str, str] = {
|
|
484
529
|
k: v
|
|
485
530
|
for k, v in {
|
|
486
|
-
**
|
|
487
|
-
_param_name: self.option_map.get(_param_name)
|
|
488
|
-
for _param_name in X264_PARAMS_NAME
|
|
489
|
-
},
|
|
531
|
+
**preset_param_getted,
|
|
490
532
|
**dict(
|
|
491
533
|
s.split("=", maxsplit=1)
|
|
492
534
|
for s in str(self.option_map.get("x264-params", "")).split(
|
|
@@ -498,9 +540,7 @@ class Ripper:
|
|
|
498
540
|
if v is not None
|
|
499
541
|
}
|
|
500
542
|
|
|
501
|
-
_option_map =
|
|
502
|
-
DEFAULT_PRESET_PARAMS.get(preset_name, {}) | _custom_option_map
|
|
503
|
-
)
|
|
543
|
+
_option_map = preset_param_default_dict | _custom_option_map
|
|
504
544
|
|
|
505
545
|
if (
|
|
506
546
|
(_crf := _option_map.get("crf"))
|
|
@@ -512,14 +552,11 @@ class Ripper:
|
|
|
512
552
|
|
|
513
553
|
_param = ":".join(f"{key}={val}" for key, val in _option_map.items())
|
|
514
554
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
+ (f'-vf "{",".join(ff_vf_option)}" ' if len(ff_vf_option) else "")
|
|
521
|
-
+ '"{output}"'
|
|
522
|
-
)
|
|
555
|
+
encoder_format_str_list = [
|
|
556
|
+
get_vs_ff_cmd(
|
|
557
|
+
f'-c:v libx264 {"" if is_pipe_input else "-pix_fmt yuv420p"} -x264-params "{_param}" '
|
|
558
|
+
)
|
|
559
|
+
]
|
|
523
560
|
|
|
524
561
|
case (
|
|
525
562
|
Ripper.Preset_name.x265
|
|
@@ -533,10 +570,7 @@ class Ripper:
|
|
|
533
570
|
_custom_option_map: dict[str, str] = {
|
|
534
571
|
k: v
|
|
535
572
|
for k, v in {
|
|
536
|
-
**
|
|
537
|
-
_param_name: self.option_map.get(_param_name)
|
|
538
|
-
for _param_name in X265_PARAMS_NAME
|
|
539
|
-
},
|
|
573
|
+
**preset_param_getted,
|
|
540
574
|
**dict(
|
|
541
575
|
s.split("=", maxsplit=1)
|
|
542
576
|
for s in str(self.option_map.get("x265-params", "")).split(
|
|
@@ -548,9 +582,7 @@ class Ripper:
|
|
|
548
582
|
if v is not None
|
|
549
583
|
}
|
|
550
584
|
|
|
551
|
-
_option_map =
|
|
552
|
-
DEFAULT_PRESET_PARAMS.get(preset_name, {}) | _custom_option_map
|
|
553
|
-
)
|
|
585
|
+
_option_map = preset_param_default_dict | _custom_option_map
|
|
554
586
|
|
|
555
587
|
# HEVC 规范
|
|
556
588
|
if self.option_map.get(
|
|
@@ -593,14 +625,11 @@ class Ripper:
|
|
|
593
625
|
|
|
594
626
|
_param = ":".join(f"{key}={val}" for key, val in _option_map.items())
|
|
595
627
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
+ (f'-vf "{",".join(ff_vf_option)}" ' if len(ff_vf_option) else "")
|
|
602
|
-
+ '"{output}"'
|
|
603
|
-
)
|
|
628
|
+
encoder_format_str_list = [
|
|
629
|
+
get_vs_ff_cmd(
|
|
630
|
+
f'-c:v libx265 {"" if is_pipe_input else "-pix_fmt yuv420p10le"} -x265-params "{_param}"'
|
|
631
|
+
)
|
|
632
|
+
]
|
|
604
633
|
|
|
605
634
|
case (
|
|
606
635
|
Ripper.Preset_name.h264_amf
|
|
@@ -630,14 +659,9 @@ class Ripper:
|
|
|
630
659
|
(f"-{key} {val}" for key, val in _option_map.items() if val)
|
|
631
660
|
)
|
|
632
661
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
+ f"-c:v {preset_name.value} "
|
|
637
|
-
+ f"{_param} {ffparams_out} "
|
|
638
|
-
+ (f' -vf "{",".join(ff_vf_option)}" ' if len(ff_vf_option) else "")
|
|
639
|
-
+ ' "{output}"'
|
|
640
|
-
)
|
|
662
|
+
encoder_format_str_list = [
|
|
663
|
+
get_vs_ff_cmd(f'-c:v {preset_name.value} "{_param}"')
|
|
664
|
+
]
|
|
641
665
|
|
|
642
666
|
case Ripper.Preset_name.svtav1:
|
|
643
667
|
_option_map = {
|
|
@@ -654,14 +678,7 @@ class Ripper:
|
|
|
654
678
|
(f"-{key} {val}" for key, val in _option_map.items() if val)
|
|
655
679
|
)
|
|
656
680
|
|
|
657
|
-
|
|
658
|
-
f"{vspipe_input} {FFMPEG_HEADER} {hwaccel} {' '.join(f'-i {s}' for s in ff_input_option)} {' '.join(f'-map {s}' for s in ff_stream_option)} "
|
|
659
|
-
+ audio_option
|
|
660
|
-
+ "-c:v libsvtav1 "
|
|
661
|
-
+ f"{_param} {ffparams_out} "
|
|
662
|
-
+ (f'-vf "{",".join(ff_vf_option)}" ' if len(ff_vf_option) else "")
|
|
663
|
-
+ ' "{output}"'
|
|
664
|
-
)
|
|
681
|
+
encoder_format_str_list = [get_vs_ff_cmd(f'-c:v libsvtav1 "{_param}"')]
|
|
665
682
|
|
|
666
683
|
case Ripper.Preset_name.vvenc:
|
|
667
684
|
_option_map = {
|
|
@@ -677,20 +694,29 @@ class Ripper:
|
|
|
677
694
|
(f"-{key} {val}" for key, val in _option_map.items() if val)
|
|
678
695
|
)
|
|
679
696
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
697
|
+
encoder_format_str_list = [get_vs_ff_cmd(f'-c:v libvvenc "{_param}"')]
|
|
698
|
+
|
|
699
|
+
case Ripper.Preset_name.ffv1:
|
|
700
|
+
_option_map = {
|
|
701
|
+
"pix_fmt": self.option_map.get("pix_fmt"),
|
|
702
|
+
**preset_param_getted,
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
_param = " ".join(
|
|
706
|
+
(f"-{key} {val}" for key, val in _option_map.items() if val)
|
|
687
707
|
)
|
|
688
708
|
|
|
709
|
+
encoder_format_str_list = [get_vs_ff_cmd(f'-c:v ffv1 "{_param}"')]
|
|
710
|
+
|
|
689
711
|
case Ripper.Preset_name.subset:
|
|
690
|
-
|
|
712
|
+
encoder_format_str_list = []
|
|
691
713
|
|
|
692
714
|
return Ripper.Option(
|
|
693
|
-
preset_name,
|
|
715
|
+
preset_name,
|
|
716
|
+
encoder_format_str_list,
|
|
717
|
+
audio_encoder,
|
|
718
|
+
muxer,
|
|
719
|
+
muxer_format_str_list,
|
|
694
720
|
)
|
|
695
721
|
|
|
696
722
|
def _flush_progress(self, sleep_sec: float) -> None:
|
|
@@ -766,6 +792,7 @@ class Ripper:
|
|
|
766
792
|
suffix: str
|
|
767
793
|
|
|
768
794
|
# 根据格式判断
|
|
795
|
+
cmd_list: list[str]
|
|
769
796
|
match self.option.preset_name:
|
|
770
797
|
case Ripper.Preset_name.custom:
|
|
771
798
|
suffix = (
|
|
@@ -774,32 +801,45 @@ class Ripper:
|
|
|
774
801
|
else ""
|
|
775
802
|
)
|
|
776
803
|
temp_name = temp_name + suffix
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
"input": str(self.input_path_list[0]),
|
|
780
|
-
"output": os.path.join(self.output_dir, temp_name),
|
|
781
|
-
}
|
|
782
|
-
)
|
|
783
|
-
|
|
784
|
-
case Ripper.Preset_name.flac:
|
|
785
|
-
if self.option.muxer is not None or len(self.media_info.audio_info) > 1:
|
|
786
|
-
suffix = f".flac.{'mp4' if self.option.muxer == Ripper.Muxer.mp4 else 'mkv'}"
|
|
787
|
-
temp_name = temp_name + suffix
|
|
788
|
-
cmd = f"{self.option.encoder_format_str} {self.option.muxer_format_str}".format_map(
|
|
804
|
+
cmd_list = [
|
|
805
|
+
s.format_map(
|
|
789
806
|
{
|
|
790
807
|
"input": str(self.input_path_list[0]),
|
|
791
808
|
"output": os.path.join(self.output_dir, temp_name),
|
|
792
809
|
}
|
|
793
810
|
)
|
|
811
|
+
for s in self.option.encoder_format_str_list
|
|
812
|
+
]
|
|
813
|
+
|
|
814
|
+
case Ripper.Preset_name.flac:
|
|
815
|
+
if self.option.muxer is not None or len(self.media_info.audio_info) > 1:
|
|
816
|
+
suffix = f".flac.{'mp4' if self.option.muxer == Ripper.Muxer.mp4 else 'mkv'}"
|
|
817
|
+
temp_name = temp_name + suffix
|
|
818
|
+
cmd_list = [
|
|
819
|
+
s.format_map(
|
|
820
|
+
{
|
|
821
|
+
"input": str(self.input_path_list[0]),
|
|
822
|
+
"output": os.path.join(self.output_dir, temp_name),
|
|
823
|
+
}
|
|
824
|
+
)
|
|
825
|
+
for str_list in (
|
|
826
|
+
self.option.encoder_format_str_list,
|
|
827
|
+
self.option.muxer_format_str_list,
|
|
828
|
+
)
|
|
829
|
+
for s in str_list
|
|
830
|
+
]
|
|
794
831
|
else:
|
|
795
832
|
suffix = ".flac"
|
|
796
833
|
temp_name = temp_name + suffix
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
834
|
+
cmd_list = [
|
|
835
|
+
s.format_map(
|
|
836
|
+
{
|
|
837
|
+
"input": str(self.input_path_list[0]),
|
|
838
|
+
"output": os.path.join(self.output_dir, temp_name),
|
|
839
|
+
}
|
|
840
|
+
)
|
|
841
|
+
for s in self.option.encoder_format_str_list
|
|
842
|
+
]
|
|
803
843
|
|
|
804
844
|
case Ripper.Preset_name.subset:
|
|
805
845
|
# 临时翻译
|
|
@@ -913,12 +953,19 @@ class Ripper:
|
|
|
913
953
|
".va.mp4" if self.option.audio_encoder else ".v.mp4"
|
|
914
954
|
)
|
|
915
955
|
temp_name = temp_name + suffix
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
956
|
+
cmd_list = [
|
|
957
|
+
s.format_map(
|
|
958
|
+
{
|
|
959
|
+
"input": str(self.input_path_list[0]),
|
|
960
|
+
"output": os.path.join(self.output_dir, temp_name),
|
|
961
|
+
}
|
|
962
|
+
)
|
|
963
|
+
for str_list in (
|
|
964
|
+
self.option.encoder_format_str_list,
|
|
965
|
+
self.option.muxer_format_str_list,
|
|
966
|
+
)
|
|
967
|
+
for s in str_list
|
|
968
|
+
]
|
|
922
969
|
|
|
923
970
|
case Ripper.Muxer.mkv:
|
|
924
971
|
if self.option_map.get("auto-infix", "1") == "0":
|
|
@@ -928,12 +975,19 @@ class Ripper:
|
|
|
928
975
|
".va.mkv" if self.option.audio_encoder else ".v.mkv"
|
|
929
976
|
)
|
|
930
977
|
temp_name = temp_name + suffix
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
978
|
+
cmd_list = [
|
|
979
|
+
s.format_map(
|
|
980
|
+
{
|
|
981
|
+
"input": str(self.input_path_list[0]),
|
|
982
|
+
"output": os.path.join(self.output_dir, temp_name),
|
|
983
|
+
}
|
|
984
|
+
)
|
|
985
|
+
for str_list in (
|
|
986
|
+
self.option.encoder_format_str_list,
|
|
987
|
+
self.option.muxer_format_str_list,
|
|
988
|
+
)
|
|
989
|
+
for s in str_list
|
|
990
|
+
]
|
|
937
991
|
|
|
938
992
|
case _:
|
|
939
993
|
if self.option_map.get("auto-infix", "1") == "0":
|
|
@@ -943,15 +997,18 @@ class Ripper:
|
|
|
943
997
|
".va.mkv" if self.option.audio_encoder else ".v.mkv"
|
|
944
998
|
)
|
|
945
999
|
temp_name = temp_name + suffix
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1000
|
+
cmd_list = [
|
|
1001
|
+
s.format_map(
|
|
1002
|
+
{
|
|
1003
|
+
"input": str(self.input_path_list[0]),
|
|
1004
|
+
"output": os.path.join(
|
|
1005
|
+
self.output_dir,
|
|
1006
|
+
os.path.join(self.output_dir, temp_name),
|
|
1007
|
+
),
|
|
1008
|
+
}
|
|
1009
|
+
)
|
|
1010
|
+
for s in self.option.encoder_format_str_list
|
|
1011
|
+
]
|
|
955
1012
|
|
|
956
1013
|
# 执行
|
|
957
1014
|
output_filename = basename + suffix
|
|
@@ -992,8 +1049,26 @@ class Ripper:
|
|
|
992
1049
|
if self.preset_name is not Ripper.Preset_name.custom:
|
|
993
1050
|
os.environ["FFREPORT"] = f"file={FF_REPORT_LOG_FILE}:level=31"
|
|
994
1051
|
|
|
995
|
-
log.info(
|
|
996
|
-
|
|
1052
|
+
log.info(
|
|
1053
|
+
"Run the following commands in order:\n{}",
|
|
1054
|
+
textwrap.indent(
|
|
1055
|
+
"\n".join(f"{i}. {s}" for i, s in enumerate(cmd_list, 1)), prefix=" "
|
|
1056
|
+
),
|
|
1057
|
+
)
|
|
1058
|
+
is_cmd_run_failed: bool = False
|
|
1059
|
+
for i, cmd in enumerate(cmd_list, 1):
|
|
1060
|
+
log.info(
|
|
1061
|
+
"Run the command {}",
|
|
1062
|
+
f"{i}:\n {cmd}",
|
|
1063
|
+
)
|
|
1064
|
+
if _cmd_res := os.system(cmd) != 0:
|
|
1065
|
+
is_cmd_run_failed = True
|
|
1066
|
+
log.error(
|
|
1067
|
+
"Command run failed: status code {}\n Failed command: {}",
|
|
1068
|
+
_cmd_res,
|
|
1069
|
+
f"{i}. {cmd}",
|
|
1070
|
+
)
|
|
1071
|
+
break
|
|
997
1072
|
|
|
998
1073
|
# 读取编码速度
|
|
999
1074
|
speed: str = "N/A"
|