返回提交历史
Modified
docs/misc/bvid_desc.md
+59
-1
Modified
docs/misc/sign/APP.md
+74
-1
Modified
docs/misc/sign/wbi.md
+123
-1
XFEstudio/bilibili-API-collect
wbi、av2bv、bv2av 的 c++ 实现 (#1035)
* + wbi 添加 c++ demo * + bv<-->av 算法 c++ 实现 * + App API 签名的 C++ 实现
e7ab2d7
代码差异
3 个文件
+256
-3
@@ -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
@@ -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
@@ -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
```