返回提交历史
Modified
docs/misc/bvid_desc.md
+187
-14
XFEstudio/bilibili-API-collect
更新 bvid 算法 (#857)
* 更新 bvid 算法 * 更新 bvid 算法相关说明
6430bff
代码差异
1 个文件
+187
-14
@@ -19,14 +19,183 @@
19
19
20
20
### avid发号方式的变化
21
21
22
从 2009-09-09 09:09:09 [av2](https://www.bilibili.com/video/av2) 的发布到 2020-03-28 19:45:02 [av99999999](https://www.bilibili.com/video/av99999999) 的发布B站结束了以投稿时间为顺序的avid发放,改为随机发放avid
22
从 2009-09-09 09:09:09 [av2](https://www.bilibili.com/video/av2) 的发布到 2020-03-28 19:45:02 [av99999999](https://www.bilibili.com/video/av99999999) 的发布B站结束了以投稿时间为顺序的avid发放,改为随机发放avid
23
23
24
24
~~暗示B站东方要完?泪目~~
25
25
26
26
## 算法概述
27
27
28
28
~~算法以及程序主要参考[知乎@mcfx的回答](https://www.zhihu.com/question/381784377/answer/1099438784)~~
29
实际上该算法并不完整,新的算法参考自[【揭秘】av号转bv号的过程](https://www.bilibili.com/video/BV1N741127Tj)
29
~~实际上该算法并不完整,新的算法参考自[【揭秘】av号转bv号的过程](https://www.bilibili.com/video/BV1N741127Tj)~~
30
实际上上面的算法依然不完整,新的算法参考自 <https://github.com/SocialSisterYi/bilibili-API-collect/issues/740>~~来自 B 站某个 JS 文件?~~
31
32
### av->bv算法
33
34
**说明**
35
36
1. 目前的 BV 格式为 BV1XXXXXXXXX,以 BV1 开头,后面包含 9 位有效数据。
37
2. AV 最大值为 2⁵¹。
38
39
**算法**
40
41
- 定义一个包含初始值为 `['B', 'V', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0']` 的长度为 12 的数组`bytes`,用于存储转换后的字符。
42
- 定义变量 `bv_idx` 并初始化为数组 `bytes` 的最后一个索引。
43
- 将输入的"aid"与 avid 最大值(2⁵¹)进行按位或运算,并与常量 `XOR_CODE`(23442827791579)进行异或运算,得到变量 `tmp`。
44
- 当 `tmp` 大于0时,执行以下操作:
45
- 将 `tmp` 除以 58 的余数作为索引,从 `FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf` 码表中取出对应的字符,并将其赋值给 `bytes[bv_idx]`。
46
- 将 `tmp` 与 58 求模赋值给 `tmp`。
47
- 将 `bv_idx` 减1。
48
- 将 `bytes` 数组中索引为 3 和 9 的元素进行交换。
49
- 将 `bytes` 数组中索引为 4 和 7 的元素进行交换。
50
- 将 `bytes` 数组转换为字符串,并返回结果。
51
52
### bv->av算法
53
54
- 将 `bvid` 中索引为 3 和 9 的字符进行交换。
55
- 将 `bvid` 中索引为 4 和 7 的字符进行交换。
56
- 删除 `bvid` 前3个字符(固定为 BV1)。
57
- 定义变量 `tmp` 并初始化为 0。
58
- 遍历 `bvid` 的每个字符,执行以下操作:
59
- 获取当前字符在 `FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf` 码表中的索引,并将其赋值给变量 `idx`。
60
- 将 `tmp` 乘以常量 58,并加上 `idx`。
61
- 将 `tmp` 与常量 2⁵¹ - 1 进行按位与运算,并与常量 `XOR_CODE`(23442827791579) 进行异或运算,得到最终结果。
62
63
## 编程实现
64
65
### JavaScript/TypeScript
66
67
<CodeGroup>
68
<CodeGroupItem title="JavaScript">
69
70
```javascript
71
const XOR_CODE = 23442827791579n;
72
const MASK_CODE = 2251799813685247n;
73
const MAX_AID = 1n << 51n;
74
const BASE = 58n;
75
76
const data = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf';
77
78
function av2bv(aid) {
79
const bytes = ['B', 'V', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0'];
80
let bv_idx = bytes.length - 1;
81
let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
82
while (tmp > 0) {
83
bytes[bv_idx] = data[Number(tmp % BigInt(BASE))];
84
tmp = tmp / BASE;
85
bv_idx -= 1;
86
}
87
[bytes[3], bytes[9]] = [bytes[9], bytes[3]];
88
[bytes[4], bytes[7]] = [bytes[7], bytes[4]];
89
return bytes.join('');
90
}
91
92
function bv2av(bvid) {
93
const bvidArr = Array.from(bvid);
94
[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]];
95
[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]];
96
bvidArr.splice(0, 3);
97
let tmp = 0n;
98
for (let i = 0; i < bvidArr.length; i++) {
99
const idx = data.indexOf(bvidArr[i]);
100
tmp = tmp * BASE + BigInt(idx);
101
}
102
return Number((tmp & MASK_CODE) ^ XOR_CODE);
103
}
104
105
console.log(av2bv(111298867365120));
106
console.log(bv2av('BV1L9Uoa9EUx'));
107
```
108
109
</CodeGroupItem>
110
111
<CodeGroupItem title="TypeScript">
112
113
```typescript
114
const XOR_CODE = 23442827791579n;
115
const MASK_CODE = 2251799813685247n;
116
const MAX_AID = 1n << 51n;
117
const BASE = 58n;
118
119
const data = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf';
120
121
function av2bv(aid: number) {
122
const bytes = ['B', 'V', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0'];
123
let bv_idx = bytes.length - 1;
124
let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
125
while (tmp > 0) {
126
bytes[bv_idx] = data[Number(tmp % BigInt(BASE))];
127
tmp = tmp / BASE;
128
bv_idx -= 1;
129
}
130
[bytes[3], bytes[9]] = [bytes[9], bytes[3]];
131
[bytes[4], bytes[7]] = [bytes[7], bytes[4]];
132
return bytes.join('') as `BV1${string}`;
133
}
134
135
function bv2av(bvid: `BV1${string}`) {
136
const bvidArr = Array.from<string>(bvid);
137
[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]];
138
[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]];
139
bvidArr.splice(0, 3);
140
const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n);
141
return Number((tmp & MASK_CODE) ^ XOR_CODE);
142
}
143
144
console.log(av2bv(111298867365120));
145
console.log(bv2av('BV1L9Uoa9EUx'));
146
```
147
</CodeGroupItem>
148
</CodeGroup>
149
150
### Python
151
152
来自:<https://github.com/SocialSisterYi/bilibili-API-collect/issues/847#issuecomment-1807020675>
153
154
```python
155
XOR_CODE = 23442827791579
156
MASK_CODE = 2251799813685247
157
MAX_AID = 1 << 51
158
159
data = [b'F', b'c', b'w', b'A', b'P', b'N', b'K', b'T', b'M', b'u', b'g', b'3', b'G', b'V', b'5', b'L', b'j', b'7', b'E', b'J', b'n', b'H', b'p', b'W', b's', b'x', b'4', b't', b'b', b'8', b'h', b'a', b'Y', b'e', b'v', b'i', b'q', b'B', b'z', b'6', b'r', b'k', b'C', b'y', b'1', b'2', b'm', b'U', b'S', b'D', b'Q', b'X', b'9', b'R', b'd', b'o', b'Z', b'f']
160
161
BASE = 58
162
BV_LEN = 12
163
PREFIX = "BV1"
164
165
def av2bv(aid):
166
bytes = [b'B', b'V', b'1', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0']
167
bv_idx = BV_LEN - 1
168
tmp = (MAX_AID | aid) ^ XOR_CODE
169
while int(tmp) != 0:
170
bytes[bv_idx] = data[int(tmp % BASE)]
171
tmp /= BASE
172
bv_idx -= 1
173
bytes[3], bytes[9] = bytes[9], bytes[3]
174
bytes[4], bytes[7] = bytes[7], bytes[4]
175
return "".join([i.decode() for i in bytes])
176
177
def bv2av(bvid: str):
178
bvid = list(bvid)
179
bvid[3], bvid[9] = bvid[9], bvid[3]
180
bvid[4], bvid[7] = bvid[7], bvid[4]
181
bvid = bvid[3:]
182
tmp = 0
183
for i in bvid:
184
idx = data.index(i.encode())
185
tmp = tmp * BASE + idx
186
return (tmp & MASK_CODE) ^ XOR_CODE
187
188
print(av2bv(111298867365120))
189
print(bv2av("BV1L9Uoa9EUx"))
190
```
191
192
### Rust
193
194
参考 <https://github.com/Colerar/abv/blob/main/src/lib.rs>
195
196
## 老版算法存档
197
198
算法参考自[【揭秘】av号转bv号的过程](https://www.bilibili.com/video/BV1N741127Tj)
30
199
31
200
### av->bv算法
32
201
@@ -60,24 +229,24 @@
60
229
> 5 -> 4
61
230
>
62
231
> 6 -> 0
63
>
232
>
64
233
> 7 -> 7
65
234
>
66
235
> 8 -> 3
67
236
>
68
237
> 9 -> 5
69
238
70
71
239
### bv->av算法
72
240
73
241
为以上算法的逆运算
74
242
75
## 编程实现
243
### 编程实现
76
244
77
245
使用 [Python](#Python) [C](#C) [TypeScript](#TypeScript) [Java](#Java) [Kotlin](#Kotlin) [Golang](#Golang) [Rust](#Rust) 等语言作为示例,欢迎社区提交更多例程
78
246
79
247
注: 新算法只提供了 [Python](#Python) 和 [Rust](#Rust) 版本
80
### Python
248
249
#### Python
81
250
82
251
```python
83
252
XOR = 177451812
@@ -121,8 +290,7 @@ if __name__ == "__main__":
121
290
main()
122
291
```
123
292
124
125
### C
293
#### C
126
294
127
295
```c
128
296
#include <stdio.h>
@@ -175,7 +343,7 @@ BV17x411w7KC
175
343
170001
176
344
```
177
345
178
### TypeScript
346
#### TypeScript
179
347
180
348
感谢[#417](https://github.com/SocialSisterYi/bilibili-API-collect/issues/417#issuecomment-1186475063)提供
181
349
@@ -187,7 +355,7 @@ export default class BvCode {
187
355
private XOR = 177451812; // 固定异或值
188
356
private ADD = 8728348608; // 固定加法值
189
357
constructor() {
190
// 初始化反查码表
358
// 初始化反查码表
191
359
const len = this.TABEL.length;
192
360
for (let i = 0; i < len; i++) {
193
361
this.TR[this.TABEL[i]] = i;
@@ -223,7 +391,7 @@ BV17x411w7KC
223
391
170001
224
392
```
225
393
226
### Java
394
#### Java
227
395
228
396
```java
229
397
/**
@@ -264,7 +432,8 @@ public class Util {
264
432
}
265
433
```
266
434
267
### Kotlin
435
#### Kotlin
436
268
437
```kotlin
269
438
/**
270
439
* 此程序非完全原创,改编自GH站内某大佬的Java程序,修改了部分代码,且转换为Kotlin
@@ -331,7 +500,8 @@ object VideoUtils {
331
500
}
332
501
```
333
502
334
### Golang
503
#### Golang
504
335
505
```go
336
506
package main
337
507
@@ -380,8 +550,11 @@ func main() {
380
550
BV17x411w7KC
381
551
170001
382
552
```
383
### Rust
553
554
#### Rust
555
384
556
crate: https://github.com/stackinspector/bvid
557
385
558
```rust
386
559
// Copyright (c) 2023 stackinspector. MIT license.
387
560