v18.0.0.a2
Pre-releasev18.0.0.a2
此预发布版本代码位于 dev-dyn-fp 分支。
接 v18.0.0.a1 相关新功能,在上次更新的基础上,此次 a2 版本对凭据类进行了进一步调整。
Credential 类
首先需要说明,dev-dyn-fp 中凭据类 proxy 字段已被移除,Credential 类目前仅用于携带 cookies 信息和用于刷新 cookeis 的 ac_time_value。但是,关于代理和 cookies 绑定携带的功能,模块会在将来版本用更妥当的方式解决。(因为现有方式是在 Api 类中对代理进行处理,并不能覆盖全部请求)推荐的传入代理的方法为 request_settings.set_proxy("xxx") 可供参考。
获取 buvid 和 bili_ticket 的过程在 get_cookies 中统一进行。因此 get_cookies 正式变为异步函数,同时提供不携带风控 cookies 的 get_core_cookies 同步函数,可获取 SESSDATA / bili_jct 等 cookies。
现在所有 Credential 将单独获取 buvid 和 bili_ticket,而 Credential 中分出一部分 blank 作为空白凭据类,对此类空白凭据类使用一个单独的 global 凭据类,buvid 和 bili_ticket 与其保持同步,避免空白凭据类带来重复多次的网络请求。剩下的非空白类凭据类称之为 normal。
同时,部分 API 设计选择性提供
Credential,如果不提供默认使用空白的Credential(),为减少无意义的网络请求次数,也为了达到在过程中持久化 buvid,将对所有空白Credential使用全局维护的一份 buvid (也可以理解将所有的Credential()视为相同的一块白板,给予相同的 buvid)。若将上述示例程序改成cred1, cred2 = Credential(), Credential(),则输出的两份 buvid 仍会保持一致,就是因为此处的机制。
同时亦有 buvid_global_persistence 设置可选,选择后所有 Credential 将与 global 保持一致。
如果需要回到以前的模式,可以通过加入设置。运行后会发现两个 Credential 的 buvid 项完全一致,都复制了一个全局的 buvid 值。
而此次更新对这一整套机制进行了进一步完善,对部分逻辑亦进行了调整。现在的逻辑如下:
global为模块初始化时定义的独一无二的凭据类。blank为get_core_cookies字段全为None的凭据类,即Credential()。可通过check_blank()检查凭据类是否为blank。- 其余凭据类均为
normal,即使传入sessdata="", bili_jct=""亦视为normal。 get_xxx函数拆分为ensure_xxx和obtain_xxx,接受凭据类传入。ensure保证buvid/bili_ticket存在且可用,只有凭据类中的buvid和bili_ticket不可用才进行obtain。ensure在已有 cookies 情况下不会修改 cookies。obtain总是发起网络请求获取新的buvid/bili_ticket。
blank或在global_persistence下,凭据类进行ensure或obtain将先ensure global或obtain global,再复制global相关字段,称此复制过程为同步。get_cookies中直接调用ensure,不会直接调用obtain。在禁用buvid与bili_ticket自动获取时只同步不请求。ensure与obtain若没有传入凭据类,将创建一个新的blank作为凭据类带入。因此获取global字段直接不带参调用ensure,更新global字段直接不带参调用obtain。
看着有些复杂,实际上确实复杂。不过看几个使用案例大抵就能搞明白了。下面可能出现部分极其刁钻的案例,正好用于理解这套逻辑。
1. 单独获取一份 buvid
buvid3, buvid4, buvid_fp = await ensure_buvid()
# 获取 global 的字段,blank 也会用这些字段,若开启全局可持久化则所有凭据类都会用这些字段。
buvid3, buvid4, buvid_fp = await obtain_buvid()
# 新生成一份 buvid,绝对不会和先前生成的重复,但 global 字段亦会改变。
request_settings.set_enable_buvid_global_persistence(False)
cred = Credential(sessdata="", bili_jct="") # 保证不是 blank
buvid3, buvid4, buvid_fp = await ensure_buvid(cred)
# 新生成一份 buvid,其他凭据类不会受到任何影响。2. 为当前凭据类换一换 buvid
cred = Credential(sessdata="", bili_jct="") # 保证不是 blank
await ensure_buvid(cred) # 没有 buvid 怎么刷新呢?
await obtain_buvid(cred) # 如此即可,注意 obtain 总是会生成新的 buvid
# 以下,法二
cred.clear_buvid() # 将所有 buvid 相关字段清空
await ensure_buvid(cred) # 没有 buvid,ensure 自己就会 obtain 了。此处顺便提一下判断 buvid 和 bili_ticket 是否生成 / 有效的函数:is_buvid_generated() 和 is_bili_ticket_valid()
3. 过程中突然切换到全局可持久化
切记 ensure 在已有 cookies 情况下不会修改 cookies。
cred = Credential(sessdata="", bili_jct="", buvid3="114514", buvid4="1919810") # 保证不是 blank
await ensure_buvid(cred) # 114514 / 1919810
request_settings.set_enable_buvid_global_persistence(True)
await ensure_buvid(cred) # 114514 / 1919810
# ensure 在已有 cookies 情况下不会修改 cookies。
cred.clear_buvid()
await ensure_buvid(cred) # clear 后即可与 global 保持同步以上 cred 若为 blank 运行逻辑也类似,blank 也是凭据类,已有 cookies 时不会修改 cookies,只有 clear_buvid 后才能使它和 global 同步。
4. 补充说明
上文写了一大堆自动生成 cookies,其实 Credential 类仍然保留手动传入 buvid 的选项,甚至现在亦可手动传入 bili_ticket。
但如果传入 buvid 不全 (buvid3 和 buvid4 都要传),bili_ticket 不全 (需要 bili_ticket_expires),那模块仍会单独生成新的值覆盖,除非将 enable_auto_buvid 和 enable_bili_ticket 关掉,因为此种情况将视为未生成过 buvid 或 bili_ticket。
5. 最后说些个人想法
blank 本意上是防止模块代码中大量出现的 Credential() 每个都单独用一份 buvid,这也太浪费了,因为基本上都是 credential = credential if credential else Credential(),任何 blank 出现基本都是用户没给凭据类。因此对 blank 的处理直接照抄模块早期方法,即生成一个全局的 buvid 糊上去。
事实上,现在的 blank 和 normal 除了同步以外其他逻辑完全相同,这点还是考虑到许多人无意间便会自己写出 Credential() 作为参数传入。考虑到有种情况,先初始化凭据类,再一个个字段赋值,在此强烈推荐下面这种写法,防止凭据类被识别为 blank。虽然真的被识别了也就相当于开了个 global_persistence
cred = Credential(sessdata="", bili_jct="", ...)
# check_blank() 当且仅当 get_core_cookies 字段全为 None 时返回 True,即使是空字符串也不会被算作 None。
# 另外,因为 check_blank() 只在 get_cookies() 中调用,因此若在初始化后及时赋值,凭据类也不会被当作 blank 处理。
# 即使被当作 blank 处理,只要赋个值便不会被当作 blank 了。
# get_core_cookies 字段为 SESSDATA, bili_jct, DedeUserID, DedeUserID__ckMd5, sid, ac_time_value