feat: add new argument `--download-vcode-priority` (#217)

This commit is contained in:
Nyakku Shigure 2024-01-13 16:05:06 +08:00 committed by GitHub
parent 3a196008e5
commit 6a5b99ebdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 9 deletions

View File

@ -199,7 +199,7 @@ yutto 支持一些基础参数,无论是批量下载还是单视频下载都
该参数略微复杂,前半部分表示在下载时**优先**选择哪一种编码的视频流,后半部分则表示在合并时如何编码视频流,两者使用 `:` 分隔。
值得注意的是,前半的下载编码只是优先下载的编码而已,如果不存在该编码,则仍会像视频清晰度调节机制一样自动选择其余编码
值得注意的是,前半的下载编码只是优先下载的编码而已,如果不存在该编码,则会根据 `--download-vcodec-priority` 自动选择其余编码,如未设置 `--download-vcodec-priority`,则会类似视频清晰度调节机制先降序后升序的方式来选择
而后半部分的参数如果设置成非 `copy` 的值则可以确保在下载完成后对其进行重新编码,而且不止支持 `av1`、`hevc` 与 `avc`,只要你的 FFmpeg 支持的视频编码器,它都可以完成。
@ -212,6 +212,26 @@ yutto 支持一些基础参数,无论是批量下载还是单视频下载都
详情同视频编码。
#### 指定视频下载编码优先级
- 参数 `--download-vcodec-priority`
- 默认值 `"auto"`
- 可选值 `"auto"` 或者使用 `,` 分隔的下载编码列表,如 `"hevc,avc,av1"`
当使用默认值 `"auto"`yutto 会类似视频清晰度调节机制先降序后升序的方式来选择。
当使用自定义下载编码列表时yutto 会严格按照列表中的顺序进行选择,如果不存在则会认为该视频无视频流。
<!--
这里使用 [!Warning] 渲染会出问题,因为 GitHub 尚不支持嵌套在 summary 中,因此暂时回退到 **Warning** 的写法
更多讨论见
https://github.com/orgs/community/discussions/16925#discussioncomment-7571187
-->
> **Warning**
>
> 如若设置本参数,请总是将 `--download-vcode-priority` 首选编码作为 `--vcodec` 的前半部分,否则可能会导致下载失败。
#### 指定输出格式
- 参数 `--output-format`

View File

@ -96,6 +96,11 @@ def cli() -> argparse.ArgumentParser:
metavar="DOWNLOAD_ACODEC:SAVE_ACODEC",
help="音频编码格式(<下载格式>:<生成格式>",
)
group_common.add_argument(
"--download-vcodec-priority",
default="auto",
help="视频编码格式优先级,使用 `,` 分隔,如 `hevc,avc,av1`,默认为 `auto`,即根据 vcodec 中「下载编码」自动推断",
)
group_common.add_argument(
"--output-format", default="infer", choices=["infer", "mp4", "mkv", "mov"], help="输出格式infer 为自动推断)"
)
@ -305,6 +310,9 @@ async def run(args_list: list[argparse.Namespace]):
"video_quality": args.video_quality,
"video_download_codec": args.vcodec.split(":")[0],
"video_save_codec": args.vcodec.split(":")[1],
"video_download_codec_priority": args.download_vcodec_priority.split(",")
if args.download_vcodec_priority != "auto"
else None,
"require_audio": args.require_audio,
"audio_quality": args.audio_quality,
"audio_download_codec": args.acodec.split(":")[0],

View File

@ -175,6 +175,7 @@ class DownloaderOptions(TypedDict):
video_quality: VideoQuality
video_download_codec: VideoCodec
video_save_codec: str
video_download_codec_priority: list[VideoCodec] | None
require_audio: bool
audio_quality: AudioQuality
audio_download_codec: AudioCodec

View File

@ -217,7 +217,9 @@ async def start_downloader(
video_path = tmp_dir.joinpath(filename + "_video.m4s")
audio_path = tmp_dir.joinpath(filename + "_audio.m4s")
video = select_video(videos, options["video_quality"], options["video_download_codec"])
video = select_video(
videos, options["video_quality"], options["video_download_codec"], options["video_download_codec_priority"]
)
audio = select_audio(audios, options["audio_quality"], options["audio_download_codec"])
will_download_video = video is not None and require_video
will_download_audio = audio is not None and require_audio

View File

@ -24,9 +24,12 @@ def select_video(
videos: list[VideoUrlMeta],
video_quality: VideoQuality = 127,
video_codec: VideoCodec = "hevc",
video_download_codec_priority: list[VideoCodec] | None = None,
) -> VideoUrlMeta | None:
video_quality_priority = gen_video_quality_priority(video_quality)
video_codec_priority = gen_vcodec_priority(video_codec)
video_codec_priority = (
gen_vcodec_priority(video_codec) if video_download_codec_priority is None else video_download_codec_priority
)
# fmt: off
video_combined_priority = [

View File

@ -10,10 +10,7 @@ import aiohttp
from yutto._typing import UserInfo
from yutto.api.user_info import get_user_info
from yutto.bilibili_typing.codec import (
audio_codec_priority_default,
video_codec_priority_default,
)
from yutto.bilibili_typing.codec import VideoCodec, audio_codec_priority_default, video_codec_priority_default
from yutto.exceptions import ErrorCode
from yutto.processor.selector import validate_episodes_selection
from yutto.utils.asynclib import initial_async_policy
@ -70,19 +67,47 @@ def validate_basic_arguments(args: argparse.Namespace):
ffmpeg = FFmpeg()
download_vcodec_priority: list[VideoCodec] = video_codec_priority_default
if args.download_vcodec_priority != "auto":
user_download_vcodec_priority = args.download_vcodec_priority.split(",")
if not user_download_vcodec_priority:
Logger.error("download_vcodec_priority 参数值为空哦")
sys.exit(ErrorCode.WRONG_ARGUMENT_ERROR.value)
for vcodec in user_download_vcodec_priority:
if vcodec not in video_codec_priority_default:
Logger.error(
"download_vcodec_priority 参数值({})不满足要求哦(允许值:{{{}}}".format(
vcodec, ", ".join(video_codec_priority_default)
)
)
sys.exit(ErrorCode.WRONG_ARGUMENT_ERROR.value)
download_vcodec_priority = user_download_vcodec_priority
if len(download_vcodec_priority) < len(video_codec_priority_default):
Logger.warning(
"download_vcodec_priority{})不包含所有下载视频编码({}),不包含部分将永远不会选择哦".format(
args.download_vcodec_priority, ", ".join(video_codec_priority_default)
)
)
# vcodec 检查
vcodec_splited = args.vcodec.split(":")
if len(vcodec_splited) != 2:
Logger.error(f"vcodec 参数值({args.vcodec})不满足要求哦(并非使用 : 分隔的值)")
sys.exit(ErrorCode.WRONG_ARGUMENT_ERROR.value)
video_download_codec, video_save_codec = vcodec_splited
if video_download_codec not in video_codec_priority_default:
if video_download_codec not in download_vcodec_priority:
Logger.error(
"download_vcodec 参数值({})不满足要求哦(允许值:{{{}}}".format(
video_download_codec, ", ".join(video_codec_priority_default)
video_download_codec, ", ".join(download_vcodec_priority)
)
)
sys.exit(ErrorCode.WRONG_ARGUMENT_ERROR.value)
if args.download_vcodec_priority != "auto" and download_vcodec_priority[0] != video_download_codec:
Logger.warning(
"download_vcodec 参数值({})不是优先级最高的编码({}),可能会导致下载失败哦".format(
video_download_codec, download_vcodec_priority[0]
)
)
if video_save_codec not in ffmpeg.video_encodecs + ["copy"]:
Logger.error(
"save_vcodec 参数值({})不满足要求哦(允许值:{{{}}}".format(