diff --git a/README.md b/README.md index e16d1d7..1f1f9b1 100644 --- a/README.md +++ b/README.md @@ -179,8 +179,8 @@ yutto 支持一些基础参数,无论是批量下载还是单视频下载都 #### 指定音频码率等级 - 参数 `-aq` 或 `--audio-quality` -- 可选值 `30280 | 30232 | 30216` -- 默认值 `30280` +- 可选值 `30251 | 30255 | 30250 | 30280 | 30232 | 30216` +- 默认值 `30251` 码率对应关系如下 @@ -188,6 +188,8 @@ yutto 支持一些基础参数,无论是批量下载还是单视频下载都 | code | 码率 | | :-: | :-: | | 30251 | - (Hi-Res) | +| 30255 | - (杜比音效) | +| 30250 | - (杜比全景声) | | 30280 | 320kbps | | 30232 | 128kbps | | 30216 | 64kbps | diff --git a/yutto/__main__.py b/yutto/__main__.py index 0386c75..c5c3405 100644 --- a/yutto/__main__.py +++ b/yutto/__main__.py @@ -76,7 +76,7 @@ def cli() -> argparse.ArgumentParser: default=127, choices=video_quality_priority_default, type=int, - help="视频清晰度等级(127:8K, 126: Dolby Vision, 125:HDR, 120:4K, 116:1080P60, 112:1080P+, 80:1080P, 74:720P60, 64:720P, 32:480P, 16:360P)", + help="视频清晰度等级(127:8K, 126:Dolby Vision, 125:HDR, 120:4K, 116:1080P60, 112:1080P+, 80:1080P, 74:720P60, 64:720P, 32:480P, 16:360P)", ) group_common.add_argument( "-aq", @@ -84,7 +84,7 @@ def cli() -> argparse.ArgumentParser: default=30251, choices=audio_quality_priority_default, type=int, - help="音频码率等级(30280:320kbps, 30232:128kbps, 30216:64kbps)", + help="音频码率等级(30251:Hi-Res, 30255:Dolby Audio, 30250:Dolby Atmos, 30280:320kbps, 30232:128kbps, 30216:64kbps)", ) group_common.add_argument( "--vcodec", diff --git a/yutto/api/ugc_video.py b/yutto/api/ugc_video.py index 1b3731f..507f257 100644 --- a/yutto/api/ugc_video.py +++ b/yutto/api/ugc_video.py @@ -177,25 +177,6 @@ async def get_ugc_video_playurl( ) if resp_json["data"].get("dash") is None: raise UnSupportedTypeError(f"该视频({format_ids(avid, cid)})尚不支持 DASH 格式") - # TODO: 处理 resp_json["data"]["dash"]["dolby"],应当是 Dolby 的音频流 - # { - # "type": 1 | 2, (1: Dolby Audio 杜比音效(例:BV1Fa41127J4),2: Dolby Atmos 杜比全景声(例:BV1eV411W7tt)) - # "audio": [ - # { - # "id": 30255 | 30250,(好像是只有这俩,分别对应上面两个) - # "base_url": "xxxx", - # "backup_url": ["xxxx", "xxxx"], - # "bandwidth": xxx, - # "mime_type": "audio/mp4", - # "codecs": "ec-3", - # "segment_base": { - # "initialization": "xxx", - # "index_range": "xxx", - # }, - # "size": xxx, - # } - # ], - # } videos: list[VideoUrlMeta] = ( [ { @@ -226,13 +207,26 @@ async def get_ugc_video_playurl( if resp_json["data"]["dash"]["audio"] else [] ) + if resp_json["data"]["dash"]["dolby"] is not None and resp_json["data"]["dash"]["dolby"]["audio"] is not None: + dolby_audios_json = resp_json["data"]["dash"]["dolby"]["audio"] + audios.extend( + { + "url": dolby_audio_json["base_url"], + "mirrors": dolby_audio_json["backup_url"] if dolby_audio_json["backup_url"] is not None else [], + "codec": "eac3", # TODO: 由于这里的 codecid 仍然是 0,所以无法通过 audio_codec_map 转换,暂时直接硬编码 + "width": 0, + "height": 0, + "quality": dolby_audio_json["id"], + } + for dolby_audio_json in dolby_audios_json + ) if resp_json["data"]["dash"]["flac"] is not None and resp_json["data"]["dash"]["flac"]["audio"] is not None: hi_res_audio_json = resp_json["data"]["dash"]["flac"]["audio"] audios.append( { "url": hi_res_audio_json["base_url"], "mirrors": hi_res_audio_json["backup_url"] if hi_res_audio_json["backup_url"] is not None else [], - "codec": "fLaC", # TODO: 由于这里的 codecid 仍然是 0,所以无法通过 audio_codec_map 转换,暂时直接硬编码 + "codec": "flac", # TODO: 同上,硬编码 "width": 0, "height": 0, "quality": hi_res_audio_json["id"], diff --git a/yutto/bilibili_typing/codec.py b/yutto/bilibili_typing/codec.py index 85a86f9..95d9d11 100644 --- a/yutto/bilibili_typing/codec.py +++ b/yutto/bilibili_typing/codec.py @@ -7,10 +7,10 @@ from yutto.utils.priority import gen_priority_sequence VideoCodecId = Literal[7, 12, 13] VideoCodec = Literal["avc", "hevc", "av1"] AudioCodecId = Literal[0] -AudioCodec = Literal["mp4a", "fLaC"] +AudioCodec = Literal["mp4a", "flac", "eac3"] video_codec_priority_default: list[VideoCodec] = ["avc", "hevc", "av1"] -audio_codec_priority_default: list[AudioCodec] = ["mp4a", "fLaC"] +audio_codec_priority_default: list[AudioCodec] = ["mp4a", "flac", "eac3"] video_codec_map: dict[VideoCodecId, VideoCodec] = { 7: "avc", diff --git a/yutto/bilibili_typing/quality.py b/yutto/bilibili_typing/quality.py index 30f75f5..2bdc16d 100644 --- a/yutto/bilibili_typing/quality.py +++ b/yutto/bilibili_typing/quality.py @@ -12,10 +12,10 @@ class Media(Enum): VideoQuality = Literal[127, 126, 125, 120, 116, 112, 80, 74, 64, 32, 16] -AudioQuality = Literal[30251, 30280, 30232, 30216] +AudioQuality = Literal[30251, 30255, 30250, 30280, 30232, 30216] video_quality_priority_default: list[VideoQuality] = [127, 126, 125, 120, 116, 112, 80, 74, 64, 32, 16] -audio_quality_priority_default: list[AudioQuality] = [30251, 30280, 30232, 30216] +audio_quality_priority_default: list[AudioQuality] = [30251, 30255, 30250, 30280, 30232, 30216] video_quality_map = { 127: { @@ -80,6 +80,14 @@ audio_quality_map = { "description": "Hi-Res", "bitrate": 999, }, # Example: BV1eV4y1P7fc + 30255: { + "description": "杜比音效", # Dolby Audio + "bitrate": 999, + }, # Example: BV1Fa41127J4,但现在好像没了,也没找到其他的杜比音效选项 + 30250: { + "description": "杜比全景声", # Dolby Atmos + "bitrate": 999, + }, # Example: BV1eV411W7tt 30280: { "description": "320kbps", "bitrate": 320, diff --git a/yutto/processor/downloader.py b/yutto/processor/downloader.py index df7db90..8d5170e 100644 --- a/yutto/processor/downloader.py +++ b/yutto/processor/downloader.py @@ -261,14 +261,14 @@ async def start_downloader( if not will_download_video: if options["output_format_audio_only"] != "infer": output_format = "." + options["output_format_audio_only"] - elif will_download_audio and audio["codec"] == "fLaC": # pyright: ignore [reportOptionalSubscript] + elif will_download_audio and audio["codec"] == "flac": # pyright: ignore [reportOptionalSubscript] output_format = ".flac" else: output_format = ".aac" else: if options["output_format"] != "infer": output_format = "." + options["output_format"] - elif will_download_audio and audio["codec"] == "fLaC": # pyright: ignore [reportOptionalSubscript] + elif will_download_audio and audio["codec"] == "flac": # pyright: ignore [reportOptionalSubscript] output_format = ".mkv" # MP4 does not support FLAC audio output_path = output_dir.joinpath(filename + output_format) diff --git a/yutto/processor/selector.py b/yutto/processor/selector.py index 58b2112..80d9606 100644 --- a/yutto/processor/selector.py +++ b/yutto/processor/selector.py @@ -3,7 +3,7 @@ from __future__ import annotations import re import sys -from yutto.api.ugc_video import AudioUrlMeta, VideoUrlMeta +from yutto._typing import AudioUrlMeta, VideoUrlMeta from yutto.bilibili_typing.codec import ( AudioCodec, VideoCodec, @@ -31,14 +31,12 @@ def select_video( gen_vcodec_priority(video_codec) if video_download_codec_priority is None else video_download_codec_priority ) - # fmt: off video_combined_priority = [ (vqn, vcodec) for vqn in video_quality_priority # TODO: Dolby Selector for vcodec in video_codec_priority - ] - # fmt: on + ] # fmt: skip for vqn, vcodec in video_combined_priority: for video in videos: @@ -55,13 +53,11 @@ def select_audio( audio_quality_priority = gen_audio_quality_priority(audio_quality) audio_codec_priority = gen_acodec_priority(audio_codec) - # fmt: off audio_combined_priority = [ (aqn, acodec) for aqn in audio_quality_priority for acodec in audio_codec_priority - ] - # fmt: on + ] # fmt: skip for aqn, acodec in audio_combined_priority: for audio in audios: diff --git a/yutto/utils/console/formatter.py b/yutto/utils/console/formatter.py index f2c4ec8..e001765 100644 --- a/yutto/utils/console/formatter.py +++ b/yutto/utils/console/formatter.py @@ -26,7 +26,6 @@ def size_format(size: float, ndigits: int = 2, base_unit_size: Literal[1024, 100 def get_char_width(char: str) -> int: """计算单个字符的宽度""" - # fmt: off widths = [ (126, 1), (159, 0), (687, 1), (710, 0), (711, 1), (727, 0), (733, 1), (879, 0), (1154, 1), (1161, 0), @@ -36,8 +35,7 @@ def get_char_width(char: str) -> int: (55203, 2), (63743, 1), (64106, 2), (65039, 1), (65059, 0), (65131, 2), (65279, 1), (65376, 2), (65500, 1), (65510, 2), (120831, 1), (262141, 2), (1114109, 1), - ] - # fmt: on + ] # fmt: skip o = ord(char) if o == 0xE or o == 0xF: