XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/bilibili-API-collect

wbi、av2bv、bv2av 的 c++ 实现 (#1035)

* + wbi 添加 c++ demo * + bv<-->av 算法 c++ 实现 * + App API 签名的 C++ 实现

e7ab2d7
YuHuanTin <51024916+YuHuanTin@users.noreply.github.com>
提交于

代码差异

3 个文件 +256 -3
Modified docs/misc/bvid_desc.md +59 -1
@@ -12,7 +12,7 @@
12 12
13 13 ### 格式
14 14
15 “bvid”恒为长度为 12 的字符串,前两个固定为“BV1”,后 9 个为 base58 计算结果(不包含数字 `0` 和大写字母 `I`、 `O` 以及小写字母 `l`)
15 “bvid”恒为长度为 12 的字符串,前 3 个固定为“BV1”,后 9 个为 base58 计算结果(不包含数字 `0` 和大写字母 `I`、 `O` 以及小写字母 `l`)
16 16
17 17 ### 实质
18 18
@@ -328,6 +328,64 @@ public class AVBVConverter {
328 328
329 329 ```
330 330
331 ### C++
332 ```c++
333 #include <algorithm>
334 #include <cassert>
335 #include <print>
336 #include <string>
337
338 constexpr int64_t XOR_CODE = 0x1552356C4CDB;
339 constexpr int64_t MAX_AID = 0x8000000000000;
340 constexpr int64_t MASK_CODE = MAX_AID - 1;
341 constexpr int64_t BASE = 58;
342 constexpr char Table[BASE + 1] = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf";
343 constexpr char ReverseTable[128] = {
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347 0x00, 0x2c, 0x2d, 0x0b, 0x1a, 0x0e, 0x27, 0x11, 0x1d, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348 0x00, 0x03, 0x25, 0x2a, 0x31, 0x12, 0x00, 0x0c, 0x15, 0x00, 0x13, 0x06, 0x0f, 0x08, 0x05, 0x00,
349 0x04, 0x32, 0x35, 0x30, 0x07, 0x2f, 0x0d, 0x17, 0x33, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x1f, 0x1c, 0x01, 0x36, 0x21, 0x39, 0x0a, 0x1e, 0x23, 0x10, 0x29, 0x00, 0x2e, 0x14, 0x37,
351 0x16, 0x24, 0x28, 0x18, 0x1b, 0x09, 0x22, 0x02, 0x19, 0x2b, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00
352 };
353
354 std::string Av2bv(const int64_t Avid) {
355 assert(Avid > 0 && "Avid must be greater than 0");
356 std::string bv = "BV1";
357 bv.resize(12, '\0');
358
359 int64_t tmp = (Avid | MAX_AID) ^ XOR_CODE;
360 for (size_t i = bv.size() - 1; tmp > 0 && i > 2; --i) {
361 bv[i] = Table[tmp % BASE];
362 tmp /= BASE;
363 }
364 std::ranges::swap(bv.at(3), bv.at(9));
365 std::ranges::swap(bv.at(4), bv.at(7));
366 return bv;
367 }
368
369 int64_t Bv2av(const std::string &Bvid) {
370 assert(Bvid.starts_with("BV1") && "Bvid must start with 'BV1'");
371
372 auto Bvid_ = Bvid;
373 std::ranges::swap(Bvid_.at(3), Bvid_.at(9));
374 std::ranges::swap(Bvid_.at(4), Bvid_.at(7));
375
376 int64_t tmp = 0;
377 for (int i = 3; i < Bvid_.size(); ++i) {
378 tmp = ReverseTable[Bvid_.at(i)] + BASE * tmp;
379 }
380 return (tmp & MASK_CODE) ^ XOR_CODE;
381 }
382
383 int main() {
384 assert(Av2bv(1004871019) == "BV16x4y1H7M1");
385 assert(Bv2av("BV16x4y1H7M1") == 1004871019);
386 }
387 ```
388
331 389
332 390 ## 老版算法存档
333 391
Modified docs/misc/sign/APP.md +74 -1
@@ -23,7 +23,7 @@
23 23
24 24 ## Demo
25 25
26 该 Demo 提供 [Python](#Python) 和 [Java](#Java) 和 [TS/JS](#TypeScript/JavaScript) 和 [Swift](#Swift) 语言例程
26 该 Demo 提供 [Python](#Python)、[Java](#Java)、[TS/JS](#TypeScript/JavaScript)、[Swift](#Swift)、[C++](#CplusPlus) 语言例程
27 27
28 28 使用 appkey = `1d8b6e7d45233436`, appsec = `560c52ccd288fed045859ed18bffd973` 对如下 `params` 参数进行签名
29 29
@@ -214,3 +214,76 @@ print(signResult)
214 214
215 215 输出结果为:01479cf20504d865519ac50f33ba3a7d
216 216
217
218
219 ### CplusPlus
220
221 需要 c++ 23 标准库,[cpr](https://github.com/libcpr/cpr)、[cryptopp](https://github.com/weidai11/cryptopp)、[nlohmann/json](https://github.com/nlohmann/json) 等依赖
222
223 ```c++
224 #include <print> // std::println
225
226 /// thrid party libraries
227 #include <cpr/cpr.h> // cpr::util::urlEncode()
228 #include <cryptopp/md5.h>
229 #include <cryptopp/hex.h>
230 #include <nlohmann/json.hpp>
231
232 /*
233 * 注意,假定不会发生错误!
234 */
235
236 /* 获取 md5 hex(lower) */
237 std::string Get_md5_hex(const std::string &Input_str) {
238 CryptoPP::Weak1::MD5 hash;
239 std::string md5_hex;
240
241 CryptoPP::StringSource ss(Input_str, true,
242 new CryptoPP::HashFilter(hash,
243 new CryptoPP::HexEncoder(
244 new CryptoPP::StringSink(md5_hex)
245 )
246 )
247 );
248
249 std::ranges::for_each(md5_hex, [](char &x) { x = std::tolower(x); });
250 return md5_hex;
251 }
252
253 /* 将 json 转换为 url 编码字符串 */
254 std::string Json_to_url_encode_str(const nlohmann::json &Json) {
255 std::string encode_str;
256 for (const auto &[key, value]: Json.items()) {
257 encode_str.append(key).append("=").append(cpr::util::urlEncode(value.is_string() ? value.get<std::string>() : to_string(value))).append("&");
258 }
259
260 // remove the last '&'
261 encode_str.resize(encode_str.size() - 1, '\0');
262 return encode_str;
263 }
264
265 std::string App_sign(nlohmann::json &Params, const std::string &App_key, const std::string &App_sec) {
266 Params["appkey"] = App_key;
267 Params["sign"] = Get_md5_hex(Json_to_url_encode_str(Params) + App_sec);
268 return Json_to_url_encode_str(Params);
269 }
270
271 int main() {
272 nlohmann::json Params;
273 Params["id"] = 114514;
274 Params["str"] = "1919810";
275 Params["test"] = "いいよ,こいよ";
276
277 constexpr auto App_key = "1d8b6e7d45233436";
278 constexpr auto App_sec = "560c52ccd288fed045859ed18bffd973";
279 std::string sign = App_sign(Params, App_key, App_sec);
280 std::println("{}", to_string(Params));
281 std::println("{}", sign);
282 }
283 ```
284
285 ```text
286 {"appkey":"1d8b6e7d45233436","id":114514,"sign":"01479cf20504d865519ac50f33ba3a7d","str":"1919810","test":"いいよ,こいよ"}
287 appkey=1d8b6e7d45233436&id=114514&sign=01479cf20504d865519ac50f33ba3a7d&str=1919810&test=%E3%81%84%E3%81%84%E3%82%88%EF%BC%8C%E3%81%93%E3%81%84%E3%82%88
288 ```
289
Modified docs/misc/sign/wbi.md +123 -1
@@ -120,7 +120,7 @@
120 120
121 121 ## Demo
122 122
123 含 [Python](#Python)、[JavaScript](#JavaScript)、[Golang](#Golang)、[C#](#CSharp)、[Java](#Java) 和 [Swift](#Swift) 语言编写的 Demo 。
123 含 [Python](#Python)、[JavaScript](#JavaScript)、[Golang](#Golang)、[C#](#CSharp)、[Java](#Java)、[Swift](#Swift)、[C++](#CPlusPlus) 语言编写的 Demo 。
124 124
125 125 ### Python
126 126
@@ -934,3 +934,125 @@ func biliWbiSign(param: String, completion: @escaping (String?) -> Void) {
934 934 }
935 935
936 936 ```
937
938
939 ### CPlusPlus
940
941 需要 c++ 23 标准库,[cpr](https://github.com/libcpr/cpr)、[cryptopp](https://github.com/weidai11/cryptopp)、[nlohmann/json](https://github.com/nlohmann/json) 等依赖
942
943 ```c++
944 #include <array> // std::array
945 #include <locale> // std::locale
946 #include <print> // std::println
947
948 /// thrid party libraries
949 #include <cpr/cpr.h>
950 #include <cryptopp/md5.h>
951 #include <cryptopp/hex.h>
952 #include <nlohmann/json.hpp>
953
954 /*
955 * 注意,假定不会发生错误!
956 */
957 class Wbi {
958 constexpr static std::array<uint8_t, 64> MIXIN_KEY_ENC_TAB_ = {
959 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35,
960 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13,
961 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4,
962 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52
963 };
964
965 /* 获取 md5 hex(lower) */
966 static std::string Get_md5_hex(const std::string &Input_str) {
967 CryptoPP::Weak1::MD5 hash;
968 std::string md5_hex;
969
970 CryptoPP::StringSource ss(Input_str, true,
971 new CryptoPP::HashFilter(hash,
972 new CryptoPP::HexEncoder(
973 new CryptoPP::StringSink(md5_hex)
974 )
975 )
976 );
977
978 std::ranges::for_each(md5_hex, [](char &x) { x = std::tolower(x); });
979 return md5_hex;
980 }
981
982 public:
983 /* 将 json 转换为 url 编码字符串 */
984 static std::string Json_to_url_encode_str(const nlohmann::json &Json) {
985 std::string encode_str;
986 for (const auto &[key, value]: Json.items()) {
987 encode_str.append(key).append("=").append(cpr::util::urlEncode(value.is_string() ? value.get<std::string>() : to_string(value))).append("&");
988 }
989
990 // remove the last '&'
991 encode_str.resize(encode_str.size() - 1, '\0');
992 return encode_str;
993 }
994
995 /* 获取 wbi key */
996 static std::pair<std::string, std::string> Get_wbi_key() {
997 const auto url = cpr::Url {"https://api.bilibili.com/x/web-interface/nav"};
998 const auto cookie = cpr::Cookies {
999 {"SESSDATA", "xxxxxxxxxxxx"},
1000 };
1001 const auto header = cpr::Header {
1002 {"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"},
1003 {"Referer", "https://www.bilibili.com/"},
1004 };
1005 const auto response = cpr::Get(url, cookie, header);
1006
1007 nlohmann::json json = nlohmann::json::parse(response.text);
1008
1009 const std::string img_url = json["data"]["wbi_img"]["img_url"];
1010 const std::string sub_url = json["data"]["wbi_img"]["sub_url"];
1011
1012 std::string img_key = img_url.substr(img_url.find("wbi/") + 4, img_url.find(".png") - img_url.find("wbi/") - 4);
1013 std::string sub_key = sub_url.substr(sub_url.find("wbi/") + 4, sub_url.find(".png") - sub_url.find("wbi/") - 4);
1014 return {img_key, sub_key};
1015 }
1016
1017 /* 获取 mixin key */
1018 static std::string Get_mixin_key(const std::string &Img_key, const std::string &Sub_key) {
1019 std::string raw_wbi_key_str = Img_key + Sub_key;
1020 std::string result;
1021
1022 std::ranges::for_each(MIXIN_KEY_ENC_TAB_, [&result, &raw_wbi_key_str](const uint8_t x) {
1023 result.push_back(raw_wbi_key_str.at(x));
1024 });
1025
1026 return result.substr(0, 32);
1027 }
1028
1029 /* 计算签名(w_rid) */
1030 static std::string Calc_sign(nlohmann::json &Params, const std::string &Mixin_key) {
1031 Params["wts"] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
1032
1033 const std::string encode_str = Json_to_url_encode_str(Params).append(Mixin_key);
1034 return Get_md5_hex(encode_str);
1035 }
1036 };
1037
1038
1039 int main() {
1040 nlohmann::json Params;
1041 // qn=32&fnver=0&fnval=4048&fourk=1&avid=1755630705&cid=1574294582
1042 Params["qn"] = 32;
1043 Params["fnver"] = 0;
1044 Params["fnval"] = 4048;
1045 Params["fourk"] = 1;
1046 Params["avid"] = 1755630705;
1047 Params["cid"] = 1574294582;
1048
1049 auto [img_key, sub_key] = Wbi::Get_wbi_key();
1050 const auto mixin_key = Wbi::Get_mixin_key(img_key, sub_key);
1051 const auto w_rid = Wbi::Calc_sign(Params, mixin_key);
1052 std::println("{}", Wbi::Json_to_url_encode_str(Params) + "&w_rid=" + w_rid);
1053 }
1054 ```
1055
1056 ```text
1057 avid=1755630705&cid=1574294582&fnval=4048&fnver=0&fourk=1&qn=32&wts=1717922933&w_rid=43571b838a1611fa121189083cfc1784
1058 ```