From acace35e7e6aaa49081ce8f4540050a9dfde5519 Mon Sep 17 00:00:00 2001 From: TimG233 <54853713+TimG233@users.noreply.github.com> Date: Tue, 19 Mar 2024 21:08:22 -0400 Subject: [PATCH 1/5] refactor: remove unused & unneeded credential for get_real_url --- bilibili_api/utils/short.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bilibili_api/utils/short.py b/bilibili_api/utils/short.py index ad84896d..1aeff788 100644 --- a/bilibili_api/utils/short.py +++ b/bilibili_api/utils/short.py @@ -3,28 +3,24 @@ 一个很简单的处理短链接的模块,主要是读取跳转链接。 """ -from typing import Optional from .. import settings from .credential import Credential from .network import get_session, get_aiohttp_session -async def get_real_url(short_url: str, credential: Optional[Credential] = None) -> str: +async def get_real_url(short_url: str) -> str: """ 获取短链接跳转目标,以进行操作。 Args: short_url(str): 短链接。 - credential(Credential \| None): 凭据类。 - Returns: 目标链接(如果不是有效的链接会报错) 返回值为原 url 类型 """ - credential = credential if credential else Credential() try: if settings.http_client == settings.HTTPClient.HTTPX: From 314dcb78ccea05b5e98d7ded9bf7e88dc6675f21 Mon Sep 17 00:00:00 2001 From: TimG233 <54853713+TimG233@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:50:35 -0400 Subject: [PATCH 2/5] feat: add get_short_url method to generate the b23.tv url from the bilibili.com url and acquire_buvid method --- bilibili_api/__init__.py | 3 +- bilibili_api/utils/short.py | 91 ++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/bilibili_api/__init__.py b/bilibili_api/__init__.py index aa34198f..7e0e98b2 100644 --- a/bilibili_api/__init__.py +++ b/bilibili_api/__init__.py @@ -10,7 +10,7 @@ from .utils.sync import sync from .utils.credential_refresh import Credential from .utils.picture import Picture -from .utils.short import get_real_url +from .utils.short import get_real_url, get_short_url from .utils.parse_link import ResourceType, parse_link from .utils.aid_bvid_transformer import aid2bvid, bvid2aid from .utils.danmaku import DmMode, Danmaku, DmFontSize, SpecialDanmaku @@ -135,6 +135,7 @@ "game", "get_aiohttp_session", "get_real_url", + "get_short_url", "get_session", "homepage", "hot", diff --git a/bilibili_api/utils/short.py b/bilibili_api/utils/short.py index 1aeff788..bf147efe 100644 --- a/bilibili_api/utils/short.py +++ b/bilibili_api/utils/short.py @@ -3,10 +3,99 @@ 一个很简单的处理短链接的模块,主要是读取跳转链接。 """ +from typing import Optional from .. import settings from .credential import Credential -from .network import get_session, get_aiohttp_session +from .network import get_session, get_aiohttp_session, HEADERS +from ..exceptions import ApiException, ArgsException + +import re +import random +import string + + +def acquire_buvid(credential: Optional[Credential] = None) -> str: + """ + 从Credential中取出buvid3,若不存在则生成一个随机的buvid3。 + + Args: + credential(Credential \| None): 凭据类。 + + Returns: + buvid3的字符串 + """ + + # return given buvid3 if possible + if credential: + buvid3 = credential.get_cookies()['buvid3'] + + if buvid3 is not None: + return buvid3 + + # random generation + buvid3_pattern = '8-4-4-4-17' # current buvid3 char-length pattern, might be changed later + parts = buvid3_pattern.split('-') + + buvid3_rand_gen = ["".join(random.choices(string.digits + string.ascii_letters, k=int(part))) for part in parts] + + return "-".join(buvid3_rand_gen) + "infoc" + + +async def get_short_url(real_url: str, credential: Optional[Credential] = None) -> str: + """ + 获取目标链接的短链接。支持bilibili.com的相关链接。 + + Args: + real_url(str): 目标链接。 + + credential(Credential \| None): 凭据类。 + + Returns: + 目标链接的b23.tv短链接。 + + 尽管目标链接可能不存在,但仍然会生成短链接。 + + 相同的目标链接重复调用此方法会获得不同的短链接。 + """ + + # validate the starting part of url + url_start_pattern = re.compile(pattern=r'^https?:\/\/(?:www\.)?bilibili\.com') + + if not re.match(pattern=url_start_pattern, string=real_url): + raise ArgsException(msg=f"提供的real_url {real_url} 不符合格式。" + f"支持的格式为bilibili.com的相关链接并含有http或https协议。") + + # POST request + try: + post_data = { + 'build': str(random.randint(6000000, 10000000)), + 'buvid': acquire_buvid(credential=credential), + 'oid': real_url, + 'platform': random.choice(['android', 'ios']), + 'share_channel': 'COPY', + 'share_id': 'public.webview.0.0.pv', + 'share_mode': str(random.randint(1, 10)) + } + + api_url = 'https://api.biliapi.net/x/share/click' + + if settings.http_client == settings.HTTPClient.HTTPX: + resp_content = (await get_session().post(url=api_url, headers=HEADERS, data=post_data)).json() + else: + resp = await get_aiohttp_session().post( + url=api_url, data=post_data, headers=HEADERS + ) + resp_content = await resp.json() + + # the 'content' sometimes will not be in the returned content due to build version, real_url, or buvid (rarely) + if 'content' not in resp_content['data']: + raise ApiException(msg="生成短链接失败。若提供的目标链接格式确认正确,请反馈此bug以更新相应params") + + return resp_content['data']['content'] + + except Exception as e: + raise e async def get_real_url(short_url: str) -> str: From 995d7b261a46cc03cd4596dc507b9cb598ce8375 Mon Sep 17 00:00:00 2001 From: TimG233 <54853713+TimG233@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:51:13 -0400 Subject: [PATCH 3/5] feat: add docs related to get_short_url method and remove credential parameter for get_real_url in docs --- docs/b23tv.md | 9 ++++++++- docs/modules/bilibili_api.md | 14 +++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/b23tv.md b/docs/b23tv.md index f0109adc..c4e50d54 100644 --- a/docs/b23tv.md +++ b/docs/b23tv.md @@ -2,7 +2,14 @@ bilibili_api 从 10.0.0 开始支持短链接了(说白了就是支持查看短链目标了) +获取默认链接(长链接)的短链接 +``` python +from bilibili_api import get_short_url, sync +print(sync(get_short_url(real_url="https://www.bilibili.com/video/BV18X4y1N7Yh/"))) # optionally pass in Credential +``` + +获取短链接的对应默认链接(长链接) ``` python from bilibili_api import get_real_url, sync -print(sync(get_real_url("https://b23.tv/mx00St"))) # https://www.bilibili.com/video/BV1YQ4y127Rd?p=1&share_medium=android&share_plat=android&share_session_id=d6c56bd5-db84-4cc8-9bb7-8f91cd8edfe0&share_source=COPY&share_tag=s_i×tamp=1629155789&unique_k=mx00St +print(sync(get_real_url(short_url="https://b23.tv/mx00St"))) # https://www.bilibili.com/video/BV1YQ4y127Rd?p=1&share_medium=android&share_plat=android&share_session_id=d6c56bd5-db84-4cc8-9bb7-8f91cd8edfe0&share_source=COPY&share_tag=s_i×tamp=1629155789&unique_k=mx00St ``` diff --git a/docs/modules/bilibili_api.md b/docs/modules/bilibili_api.md index d22ff904..6d2eb32e 100644 --- a/docs/modules/bilibili_api.md +++ b/docs/modules/bilibili_api.md @@ -324,12 +324,24 @@ BV 号转 AV 号。 --- +## async def get_short_url() + +| name | type | description | +|------------|---------------------|-------------| +| real_url | str | 真实链接 | +| credential | Optional[Credential] | 凭据类. | + +获取bilibili真实链接对应的短链接。 + +**注意:** 这个函数对于同一个真实链接的每一次调用都会返回不同的短链接。并且,请注意短链接也会包含你的分享信息(因为会redirect)。 + +**Returns:** b23.tv的短链接 + ## async def get_real_url() | name | type | description | | - | - | - | | short_url | str | 短链接 | -| credential | Optional[Credential] | 凭据类. | 获取短链接对应的真实链接。 From 8a4d57545abe52774aeab5b32f3ede440a0b26b1 Mon Sep 17 00:00:00 2001 From: TimG233 <54853713+TimG233@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:47:46 -0400 Subject: [PATCH 4/5] refactor: add get_spi_buvid method for acquiring buvid3 and change params for post_data --- bilibili_api/utils/short.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/bilibili_api/utils/short.py b/bilibili_api/utils/short.py index bf147efe..7ec56929 100644 --- a/bilibili_api/utils/short.py +++ b/bilibili_api/utils/short.py @@ -7,7 +7,7 @@ from .. import settings from .credential import Credential -from .network import get_session, get_aiohttp_session, HEADERS +from .network import get_session, get_aiohttp_session, get_spi_buvid, HEADERS from ..exceptions import ApiException, ArgsException import re @@ -15,9 +15,9 @@ import string -def acquire_buvid(credential: Optional[Credential] = None) -> str: +async def acquire_buvid(credential: Optional[Credential] = None) -> str: """ - 从Credential中取出buvid3,若不存在则生成一个随机的buvid3。 + 从Credential中取出buvid3,若不存在则通过spi获取buvid,若都不存在则随机生成一个的buvid3。 Args: credential(Credential \| None): 凭据类。 @@ -33,7 +33,14 @@ def acquire_buvid(credential: Optional[Credential] = None) -> str: if buvid3 is not None: return buvid3 - # random generation + # use spi to get buvid3 + try: + return (await get_spi_buvid())['data']['b_3'] + + except KeyError: # if data or b_3 does not exist by spi + pass + + # random generation if spi is not possible buvid3_pattern = '8-4-4-4-17' # current buvid3 char-length pattern, might be changed later parts = buvid3_pattern.split('-') @@ -69,13 +76,13 @@ async def get_short_url(real_url: str, credential: Optional[Credential] = None) # POST request try: post_data = { - 'build': str(random.randint(6000000, 10000000)), - 'buvid': acquire_buvid(credential=credential), + 'build': 999999999, # number > 5.5M should work + 'buvid': await acquire_buvid(credential=credential), 'oid': real_url, 'platform': random.choice(['android', 'ios']), 'share_channel': 'COPY', 'share_id': 'public.webview.0.0.pv', - 'share_mode': str(random.randint(1, 10)) + 'share_mode': 3 } api_url = 'https://api.biliapi.net/x/share/click' From 7d215a99105b2b32255f174b97520029aecbcc7d Mon Sep 17 00:00:00 2001 From: z0z0r4 Date: Sat, 23 Mar 2024 23:43:44 +0800 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 不需要 try,不成功就应该报错 --- bilibili_api/utils/short.py | 132 +++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 47 deletions(-) diff --git a/bilibili_api/utils/short.py b/bilibili_api/utils/short.py index 7ec56929..a7819d3d 100644 --- a/bilibili_api/utils/short.py +++ b/bilibili_api/utils/short.py @@ -3,6 +3,7 @@ 一个很简单的处理短链接的模块,主要是读取跳转链接。 """ + from typing import Optional from .. import settings @@ -20,7 +21,7 @@ async def acquire_buvid(credential: Optional[Credential] = None) -> str: 从Credential中取出buvid3,若不存在则通过spi获取buvid,若都不存在则随机生成一个的buvid3。 Args: - credential(Credential \| None): 凭据类。 + credential(Credential | None): 凭据类。 Returns: buvid3的字符串 @@ -28,81 +29,118 @@ async def acquire_buvid(credential: Optional[Credential] = None) -> str: # return given buvid3 if possible if credential: - buvid3 = credential.get_cookies()['buvid3'] + buvid3 = credential.get_cookies()["buvid3"] if buvid3 is not None: return buvid3 # use spi to get buvid3 try: - return (await get_spi_buvid())['data']['b_3'] + return (await get_spi_buvid())["data"]["b_3"] except KeyError: # if data or b_3 does not exist by spi pass # random generation if spi is not possible - buvid3_pattern = '8-4-4-4-17' # current buvid3 char-length pattern, might be changed later - parts = buvid3_pattern.split('-') + buvid3_pattern = ( + "8-4-4-4-17" # current buvid3 char-length pattern, might be changed later + ) + parts = buvid3_pattern.split("-") - buvid3_rand_gen = ["".join(random.choices(string.digits + string.ascii_letters, k=int(part))) for part in parts] + buvid3_rand_gen = [ + "".join(random.choices(string.digits + string.ascii_letters, k=int(part))) + for part in parts + ] return "-".join(buvid3_rand_gen) + "infoc" -async def get_short_url(real_url: str, credential: Optional[Credential] = None) -> str: +async def get_short_url( + oid: Optional[int] = None, + share_content: Optional[str] = None, + share_title: Optional[str] = None, + share_origin: Optional[str] = "vinfo_share", + share_mode: Optional[int] = 3, + share_id: Optional[str] = "public.webview.0.0.pv", + platform: Optional[str] = "android", + mobi_app: Optional[str] = "android", + panel_type: Optional[int] = 1, + # regex_real_url: Optional[bool] = False, + credential: Optional[Credential] = None, +) -> str: """ - 获取目标链接的短链接。支持bilibili.com的相关链接。 + 获取目标链接的短链接。支持 bilibili.com 的相关链接。 - Args: - real_url(str): 目标链接。 + 尽管目标链接可能不存在,但仍然会生成短链接。 - credential(Credential \| None): 凭据类。 + 相同的目标链接重复调用此方法会获得不同的短链接。 - Returns: - 目标链接的b23.tv短链接。 + Args: + oid (int | None): 内容 oid。 - 尽管目标链接可能不存在,但仍然会生成短链接。 + share_content (str | None): 分享内容。 - 相同的目标链接重复调用此方法会获得不同的短链接。 - """ + share_title (str | None): 分享标题。 - # validate the starting part of url - url_start_pattern = re.compile(pattern=r'^https?:\/\/(?:www\.)?bilibili\.com') + share_origin (str | None): 分享来源。 - if not re.match(pattern=url_start_pattern, string=real_url): - raise ArgsException(msg=f"提供的real_url {real_url} 不符合格式。" - f"支持的格式为bilibili.com的相关链接并含有http或https协议。") + share_mode (int | None): 分享模式。 - # POST request - try: - post_data = { - 'build': 999999999, # number > 5.5M should work - 'buvid': await acquire_buvid(credential=credential), - 'oid': real_url, - 'platform': random.choice(['android', 'ios']), - 'share_channel': 'COPY', - 'share_id': 'public.webview.0.0.pv', - 'share_mode': 3 - } - - api_url = 'https://api.biliapi.net/x/share/click' + share_id (str | None): 分享 id。 - if settings.http_client == settings.HTTPClient.HTTPX: - resp_content = (await get_session().post(url=api_url, headers=HEADERS, data=post_data)).json() - else: - resp = await get_aiohttp_session().post( - url=api_url, data=post_data, headers=HEADERS - ) - resp_content = await resp.json() + platform (str | None): 平台。 - # the 'content' sometimes will not be in the returned content due to build version, real_url, or buvid (rarely) - if 'content' not in resp_content['data']: - raise ApiException(msg="生成短链接失败。若提供的目标链接格式确认正确,请反馈此bug以更新相应params") + mobi_app (str | None): 移动端应用。 - return resp_content['data']['content'] + panel_type (int | None): 面板类型。 - except Exception as e: - raise e + credential (Credential | None): 凭据类。 + + Returns: + str: 目标链接的 b23.tv 短链接信息 (不一定为单独 URL) + """ + # 应该具体为检测 oid 类型 + # if regex_real_url: + # # validate the starting part of url + # url_start_pattern = re.compile(pattern=r"^https?:\/\/(?:www\.)?bilibili\.com") + + # if not re.match(pattern=url_start_pattern, string=share_content): + # raise ArgsException( + # msg=f"提供的 {share_content} 不符合格式。\ + # 支持的格式为 bilibili.com 的相关链接并含有 http 或 https 协议。" + # ) + + post_data = { + "build": 7300400, + "buvid": await acquire_buvid(credential=credential), + "oid": oid, + "share_title": share_title, + "share_content": share_content, + "share_origin": share_origin, + "mobi_app": mobi_app, + "panel_type": panel_type, + "platform": platform, + "share_id": share_id, + "share_mode": share_mode, + } + + api_url = "https://api.biliapi.net/x/share/click" + + if settings.http_client == settings.HTTPClient.HTTPX: + resp_content = ( + await get_session().post(url=api_url, headers=HEADERS, data=post_data) + ).json() + else: + resp = await get_aiohttp_session().post( + url=api_url, data=post_data, headers=HEADERS + ) + resp_content = await resp.json() + + # the 'content' sometimes will not be in the returned content due to build version, real_url, or buvid (rarely) + if "content" not in resp_content["data"]: + raise ApiException(msg="生成短链接失败。") + + return resp_content["data"]["content"] async def get_real_url(short_url: str) -> str: