2023-11-19 11:35:10 +08:00
|
|
|
|
|
2023-02-22 01:00:06 +08:00
|
|
|
|
# bvid说明
|
|
|
|
|
|
|
|
|
|
2020-03-23 B站推出了全新的稿件视频id`bvid`来接替之前的`avid`,其意义与之相同
|
|
|
|
|
|
|
|
|
|
详见:
|
|
|
|
|
|
|
|
|
|
1. [【升级公告】AV号全面升级至BV号(专栏)](https://www.bilibili.com/read/cv5167957)
|
|
|
|
|
2. [【升级公告】AV号全面升级至BV号](https://www.bilibili.com/blackboard/activity-BV-PC.html)
|
|
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
|
|
|
|
### 格式
|
|
|
|
|
|
2024-01-09 07:03:46 +08:00
|
|
|
|
“bvid”恒为长度为 12 的字符串,前两个固定为“BV1”,后 9 个为 base58 计算结果(不包含数字 `0` 和大写字母 `I`、 `O` 以及小写字母 `l`)
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
### 实质
|
|
|
|
|
|
|
|
|
|
“bvid"为“avid”的base58编码,可通过算法进行相互转化
|
|
|
|
|
|
|
|
|
|
### avid发号方式的变化
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
从 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
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
~~暗示B站东方要完?泪目~~
|
|
|
|
|
|
|
|
|
|
## 算法概述
|
|
|
|
|
|
2024-02-08 18:14:17 +08:00
|
|
|
|
~~算法以及程序主要参考[知乎@mcfx的回答](https://www.zhihu.com/question/381784377/answer/1099438784)~~
|
|
|
|
|
~~实际上该算法并不完整,新的算法参考自[【揭秘】av号转bv号的过程](https://www.bilibili.com/video/BV1N741127Tj)~~
|
|
|
|
|
实际上上面的算法依然不完整,新的算法参考自 [SocialSisterYi#740](https://github.com/SocialSisterYi/bilibili-API-collect/issues/740)~~来自 B 站某个 JS 文件?~~
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
|
|
|
|
### av->bv算法
|
|
|
|
|
|
|
|
|
|
**说明**
|
|
|
|
|
|
|
|
|
|
1. 目前的 BV 格式为 BV1XXXXXXXXX,以 BV1 开头,后面包含 9 位有效数据。
|
|
|
|
|
2. AV 最大值为 2⁵¹。
|
|
|
|
|
|
|
|
|
|
**算法**
|
|
|
|
|
|
|
|
|
|
- 定义一个包含初始值为 `['B', 'V', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0']` 的长度为 12 的数组`bytes`,用于存储转换后的字符。
|
|
|
|
|
- 定义变量 `bv_idx` 并初始化为数组 `bytes` 的最后一个索引。
|
2023-11-19 11:35:10 +08:00
|
|
|
|
- 将输入的 `aid` 与 avid 最大值(2⁵¹)进行按位或运算,其结果与常量 `XOR_CODE`(23442827791579)进行异或运算,得到变量 `tmp`。
|
|
|
|
|
- 当 `tmp` 大于0时,循环执行以下操作直到小于0:
|
|
|
|
|
- 将 `tmp` 除以 58(码表的长度) 的余数作为索引,从 `FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf` 码表中取出对应的字符,并将其赋值给 `bytes[bv_idx]`。
|
2023-11-16 09:35:32 +08:00
|
|
|
|
- 将 `tmp` 与 58 求模赋值给 `tmp`。
|
|
|
|
|
- 将 `bv_idx` 减1。
|
|
|
|
|
- 将 `bytes` 数组中索引为 3 和 9 的元素进行交换。
|
|
|
|
|
- 将 `bytes` 数组中索引为 4 和 7 的元素进行交换。
|
|
|
|
|
- 将 `bytes` 数组转换为字符串,并返回结果。
|
|
|
|
|
|
|
|
|
|
### bv->av算法
|
|
|
|
|
|
2023-11-19 11:35:10 +08:00
|
|
|
|
是 #av->bv算法 的逆向
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
- 将 `bvid` 中索引为 3 和 9 的字符进行交换。
|
|
|
|
|
- 将 `bvid` 中索引为 4 和 7 的字符进行交换。
|
|
|
|
|
- 删除 `bvid` 前3个字符(固定为 BV1)。
|
|
|
|
|
- 定义变量 `tmp` 并初始化为 0。
|
|
|
|
|
- 遍历 `bvid` 的每个字符,执行以下操作:
|
|
|
|
|
- 获取当前字符在 `FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf` 码表中的索引,并将其赋值给变量 `idx`。
|
2023-11-19 11:35:10 +08:00
|
|
|
|
- 将 `tmp` 乘以常量 58,并加上 `idx`,最后赋值给 `tmp`。
|
|
|
|
|
- 将 `tmp` 与常量 2⁵¹ - 1 进行按位与运算,其结果与常量 `XOR_CODE`(23442827791579) 进行异或运算,得到最终结果。
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
|
|
|
|
## 编程实现
|
|
|
|
|
|
|
|
|
|
### JavaScript/TypeScript
|
|
|
|
|
|
|
|
|
|
<CodeGroup>
|
|
|
|
|
<CodeGroupItem title="JavaScript">
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
```javascript
|
|
|
|
|
const XOR_CODE = 23442827791579n;
|
|
|
|
|
const MASK_CODE = 2251799813685247n;
|
|
|
|
|
const MAX_AID = 1n << 51n;
|
|
|
|
|
const BASE = 58n;
|
|
|
|
|
|
|
|
|
|
const data = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf';
|
|
|
|
|
|
|
|
|
|
function av2bv(aid) {
|
|
|
|
|
const bytes = ['B', 'V', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0'];
|
2023-11-19 11:35:10 +08:00
|
|
|
|
let bvIndex = bytes.length - 1;
|
2023-11-16 09:35:32 +08:00
|
|
|
|
let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
|
|
|
|
|
while (tmp > 0) {
|
2023-11-19 11:35:10 +08:00
|
|
|
|
bytes[bvIndex] = data[Number(tmp % BigInt(BASE))];
|
2023-11-16 09:35:32 +08:00
|
|
|
|
tmp = tmp / BASE;
|
2023-11-19 11:35:10 +08:00
|
|
|
|
bvIndex -= 1;
|
2023-11-16 09:35:32 +08:00
|
|
|
|
}
|
|
|
|
|
[bytes[3], bytes[9]] = [bytes[9], bytes[3]];
|
|
|
|
|
[bytes[4], bytes[7]] = [bytes[7], bytes[4]];
|
|
|
|
|
return bytes.join('');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bv2av(bvid) {
|
|
|
|
|
const bvidArr = Array.from(bvid);
|
|
|
|
|
[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]];
|
|
|
|
|
[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]];
|
|
|
|
|
bvidArr.splice(0, 3);
|
2023-11-19 11:35:10 +08:00
|
|
|
|
const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n);
|
2023-11-16 09:35:32 +08:00
|
|
|
|
return Number((tmp & MASK_CODE) ^ XOR_CODE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(av2bv(111298867365120));
|
|
|
|
|
console.log(bv2av('BV1L9Uoa9EUx'));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
</CodeGroupItem>
|
|
|
|
|
|
|
|
|
|
<CodeGroupItem title="TypeScript">
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
const XOR_CODE = 23442827791579n;
|
|
|
|
|
const MASK_CODE = 2251799813685247n;
|
|
|
|
|
const MAX_AID = 1n << 51n;
|
|
|
|
|
const BASE = 58n;
|
|
|
|
|
|
|
|
|
|
const data = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf';
|
|
|
|
|
|
|
|
|
|
function av2bv(aid: number) {
|
|
|
|
|
const bytes = ['B', 'V', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0'];
|
2023-11-19 11:35:10 +08:00
|
|
|
|
let bvIndex = bytes.length - 1;
|
2023-11-16 09:35:32 +08:00
|
|
|
|
let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
|
|
|
|
|
while (tmp > 0) {
|
2023-11-19 11:35:10 +08:00
|
|
|
|
bytes[bvIndex] = data[Number(tmp % BigInt(BASE))];
|
2023-11-16 09:35:32 +08:00
|
|
|
|
tmp = tmp / BASE;
|
2023-11-19 11:35:10 +08:00
|
|
|
|
bvIndex -= 1;
|
2023-11-16 09:35:32 +08:00
|
|
|
|
}
|
|
|
|
|
[bytes[3], bytes[9]] = [bytes[9], bytes[3]];
|
|
|
|
|
[bytes[4], bytes[7]] = [bytes[7], bytes[4]];
|
|
|
|
|
return bytes.join('') as `BV1${string}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bv2av(bvid: `BV1${string}`) {
|
|
|
|
|
const bvidArr = Array.from<string>(bvid);
|
|
|
|
|
[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]];
|
|
|
|
|
[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]];
|
|
|
|
|
bvidArr.splice(0, 3);
|
|
|
|
|
const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n);
|
|
|
|
|
return Number((tmp & MASK_CODE) ^ XOR_CODE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(av2bv(111298867365120));
|
|
|
|
|
console.log(bv2av('BV1L9Uoa9EUx'));
|
|
|
|
|
```
|
|
|
|
|
</CodeGroupItem>
|
|
|
|
|
</CodeGroup>
|
|
|
|
|
|
|
|
|
|
### Python
|
|
|
|
|
|
2024-02-04 18:17:59 +08:00
|
|
|
|
来自:[#847](https://github.com/SocialSisterYi/bilibili-API-collect/issues/847#issuecomment-1807020675)
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
XOR_CODE = 23442827791579
|
|
|
|
|
MASK_CODE = 2251799813685247
|
|
|
|
|
MAX_AID = 1 << 51
|
2024-02-04 18:17:59 +08:00
|
|
|
|
ALPHABET = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf"
|
|
|
|
|
ENCODE_MAP = 8, 7, 0, 5, 1, 3, 2, 4, 6
|
|
|
|
|
DECODE_MAP = tuple(reversed(ENCODE_MAP))
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
2024-02-04 18:17:59 +08:00
|
|
|
|
BASE = len(ALPHABET)
|
2023-11-16 09:35:32 +08:00
|
|
|
|
PREFIX = "BV1"
|
2024-02-04 18:17:59 +08:00
|
|
|
|
PREFIX_LEN = len(PREFIX)
|
|
|
|
|
CODE_LEN = len(ENCODE_MAP)
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
2024-02-01 14:13:56 +08:00
|
|
|
|
def av2bv(aid: int) -> str:
|
2024-02-04 18:17:59 +08:00
|
|
|
|
bvid = [""] * 9
|
2023-11-16 09:35:32 +08:00
|
|
|
|
tmp = (MAX_AID | aid) ^ XOR_CODE
|
2024-02-04 18:17:59 +08:00
|
|
|
|
for i in range(CODE_LEN):
|
2024-02-08 18:17:04 +08:00
|
|
|
|
bvid[ENCODE_MAP[i]] = ALPHABET[tmp % BASE]
|
|
|
|
|
tmp //= BASE
|
2024-02-04 18:17:59 +08:00
|
|
|
|
return PREFIX + "".join(bvid)
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
2024-02-01 14:13:56 +08:00
|
|
|
|
def bv2av(bvid: str) -> int:
|
2024-02-04 18:17:59 +08:00
|
|
|
|
assert bvid[:3] == PREFIX
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2024-02-08 18:23:55 +08:00
|
|
|
|
bvid = bvid[3:]
|
2023-11-16 09:35:32 +08:00
|
|
|
|
tmp = 0
|
2024-02-04 18:17:59 +08:00
|
|
|
|
for i in range(CODE_LEN):
|
|
|
|
|
idx = ALPHABET.index(bvid[DECODE_MAP[i]])
|
2023-11-16 09:35:32 +08:00
|
|
|
|
tmp = tmp * BASE + idx
|
|
|
|
|
return (tmp & MASK_CODE) ^ XOR_CODE
|
|
|
|
|
|
2024-02-04 18:17:59 +08:00
|
|
|
|
assert av2bv(111298867365120) == "BV1L9Uoa9EUx"
|
|
|
|
|
assert bv2av("BV1L9Uoa9EUx") == 111298867365120
|
2023-11-16 09:35:32 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Rust
|
|
|
|
|
|
|
|
|
|
参考 <https://github.com/Colerar/abv/blob/main/src/lib.rs>
|
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
### Swift
|
|
|
|
|
|
|
|
|
|
```swift
|
|
|
|
|
fileprivate let XOR_CODE: UInt64 = 23442827791579
|
|
|
|
|
fileprivate let MASK_CODE: UInt64 = 2251799813685247
|
|
|
|
|
fileprivate let MAX_AID: UInt64 = 1 << 51
|
|
|
|
|
|
|
|
|
|
fileprivate let data: [UInt8] = [70, 99, 119, 65, 80, 78, 75, 84, 77, 117, 103, 51, 71, 86, 53, 76, 106, 55, 69, 74, 110, 72, 112, 87, 115, 120, 52, 116, 98, 56, 104, 97, 89, 101, 118, 105, 113, 66, 122, 54, 114, 107, 67, 121, 49, 50, 109, 85, 83, 68, 81, 88, 57, 82, 100, 111, 90, 102]
|
|
|
|
|
|
|
|
|
|
fileprivate let BASE: UInt64 = 58
|
|
|
|
|
fileprivate let BV_LEN: Int = 12
|
|
|
|
|
fileprivate let PREFIX: String = "BV1"
|
|
|
|
|
|
|
|
|
|
func av2bv(avid: UInt64) -> String {
|
|
|
|
|
var bytes: [UInt8] = [66, 86, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48]
|
|
|
|
|
var bvIdx = BV_LEN - 1
|
|
|
|
|
var tmp = (MAX_AID | avid) ^ XOR_CODE
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
while tmp != 0 {
|
|
|
|
|
bytes[bvIdx] = data[Int(tmp % BASE)]
|
|
|
|
|
tmp /= BASE
|
|
|
|
|
bvIdx -= 1
|
|
|
|
|
}
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
bytes.swapAt(3, 9)
|
|
|
|
|
bytes.swapAt(4, 7)
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
return String(decoding: bytes, as: UTF8.self)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func bv2av(bvid: String) -> UInt64 {
|
|
|
|
|
let fixedBvid: String
|
|
|
|
|
if bvid.hasPrefix("BV") {
|
|
|
|
|
fixedBvid = bvid
|
|
|
|
|
} else {
|
|
|
|
|
fixedBvid = "BV" + bvid
|
|
|
|
|
}
|
|
|
|
|
var bvidArray = Array(fixedBvid.utf8)
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
bvidArray.swapAt(3, 9)
|
|
|
|
|
bvidArray.swapAt(4, 7)
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
let trimmedBvid = String(decoding: bvidArray[3...], as: UTF8.self)
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
var tmp: UInt64 = 0
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
for char in trimmedBvid {
|
|
|
|
|
if let idx = data.firstIndex(of: char.utf8.first!) {
|
|
|
|
|
tmp = tmp * BASE + UInt64(idx)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-08 18:14:17 +08:00
|
|
|
|
|
2023-12-10 10:03:53 +08:00
|
|
|
|
return (tmp & MASK_CODE) ^ XOR_CODE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
print(av2bv(avid: 111298867365120))
|
|
|
|
|
print(bv2av(bvid: "BV1L9Uoa9EUx"))
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
## 老版算法存档
|
|
|
|
|
|
2024-02-04 18:17:59 +08:00
|
|
|
|
**以下算法已失效**,编解码函数值域有限,不推荐使用,在此仅作为存档
|
|
|
|
|
|
|
|
|
|
<details>
|
|
|
|
|
<summary>查看折叠内容:</summary>
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
算法参考自[【揭秘】av号转bv号的过程](https://www.bilibili.com/video/BV1N741127Tj)
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
### av->bv算法
|
|
|
|
|
|
2023-02-23 11:48:58 +08:00
|
|
|
|
注:本算法及示例程序仅能编解码`avid < 29460791296`,且暂无法验证`avid >= 29460791296`的正确性
|
2023-05-15 02:25:58 +08:00
|
|
|
|
再注:本人不清楚新算法能否编解码`avid >= 29460791296`
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
2023-05-15 02:25:58 +08:00
|
|
|
|
1. a = (avid ⊕ 177451812) + 100618342136696320
|
2023-02-23 11:48:58 +08:00
|
|
|
|
2. 以 i 为循环变量循环 6 次 b[i] = (a / 58 ^ i) % 58
|
|
|
|
|
3. 将 b[i] 中各个数字转换为以下码表中的字符
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
码表:
|
|
|
|
|
|
|
|
|
|
> fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF
|
|
|
|
|
|
2023-05-15 02:25:58 +08:00
|
|
|
|
4. 初始化字符串 b[i]=` `
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
2023-02-23 11:48:58 +08:00
|
|
|
|
5. 按照以下字符顺序编码表编码并填充至 b[i]
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
字符顺序编码表:
|
|
|
|
|
|
2023-05-15 02:25:58 +08:00
|
|
|
|
> 0 -> 9
|
2023-02-22 01:00:06 +08:00
|
|
|
|
>
|
2023-05-15 02:25:58 +08:00
|
|
|
|
> 1 -> 8
|
2023-02-22 01:00:06 +08:00
|
|
|
|
>
|
2023-05-15 02:25:58 +08:00
|
|
|
|
> 2 -> 1
|
2023-02-22 01:00:06 +08:00
|
|
|
|
>
|
2023-05-15 02:25:58 +08:00
|
|
|
|
> 3 -> 6
|
2023-02-22 01:00:06 +08:00
|
|
|
|
>
|
2023-05-15 02:25:58 +08:00
|
|
|
|
> 4 -> 2
|
2023-02-22 01:00:06 +08:00
|
|
|
|
>
|
2023-05-15 02:25:58 +08:00
|
|
|
|
> 5 -> 4
|
|
|
|
|
>
|
|
|
|
|
> 6 -> 0
|
2023-11-16 09:35:32 +08:00
|
|
|
|
>
|
2023-05-15 02:25:58 +08:00
|
|
|
|
> 7 -> 7
|
|
|
|
|
>
|
|
|
|
|
> 8 -> 3
|
|
|
|
|
>
|
|
|
|
|
> 9 -> 5
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
### bv->av算法
|
|
|
|
|
|
|
|
|
|
为以上算法的逆运算
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
### 编程实现
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
2023-05-16 02:56:18 +08:00
|
|
|
|
使用 [Python](#Python) [C](#C) [TypeScript](#TypeScript) [Java](#Java) [Kotlin](#Kotlin) [Golang](#Golang) [Rust](#Rust) 等语言作为示例,欢迎社区提交更多例程
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
2023-05-16 02:56:18 +08:00
|
|
|
|
注: 新算法只提供了 [Python](#Python) 和 [Rust](#Rust) 版本
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
|
|
|
|
#### Python
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
```python
|
2023-05-15 02:25:58 +08:00
|
|
|
|
XOR = 177451812
|
|
|
|
|
ADD = 100618342136696320
|
|
|
|
|
TABLE = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"
|
2023-09-26 06:24:46 +08:00
|
|
|
|
MAP = 9, 8, 1, 6, 2, 4, 0, 7, 3, 5
|
|
|
|
|
|
|
|
|
|
|
2023-10-04 12:05:55 +08:00
|
|
|
|
def av2bv(av: int) -> str:
|
2023-05-15 02:25:58 +08:00
|
|
|
|
av = (av ^ XOR) + ADD
|
2023-09-26 06:24:46 +08:00
|
|
|
|
bv = [""] * 10
|
2023-05-15 02:25:58 +08:00
|
|
|
|
for i in range(10):
|
2023-09-26 06:24:46 +08:00
|
|
|
|
bv[MAP[i]] = TABLE[(av // 58**i) % 58]
|
2023-10-04 12:05:55 +08:00
|
|
|
|
return "".join(bv)
|
2023-05-15 02:25:58 +08:00
|
|
|
|
|
2023-09-26 06:24:46 +08:00
|
|
|
|
|
|
|
|
|
def bv2av(bv: int) -> int:
|
|
|
|
|
av = [""] * 10
|
2023-05-15 02:25:58 +08:00
|
|
|
|
s = 0
|
|
|
|
|
for i in range(10):
|
2023-09-26 06:24:46 +08:00
|
|
|
|
s += TABLE.find(bv[MAP[i]]) * 58**i
|
|
|
|
|
av = (s - ADD) ^ XOR
|
|
|
|
|
|
|
|
|
|
return av
|
2023-05-15 02:25:58 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2023-09-26 06:24:46 +08:00
|
|
|
|
while 1:
|
|
|
|
|
mode = input("1. AV to BV\n2. BV to AV\n3. Exit\n你的选择:")
|
|
|
|
|
if mode == "1":
|
|
|
|
|
print(f"BV号是:BV {av2bv(int(input('AV号是:')))}")
|
|
|
|
|
elif mode == "2":
|
|
|
|
|
print(f"AV号是:AV {bv2av(input('BV号是:'))}")
|
|
|
|
|
elif mode == "3":
|
2023-05-15 02:25:58 +08:00
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
print("输入错误请重新输入")
|
|
|
|
|
|
2023-09-26 06:24:46 +08:00
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|
2023-02-22 01:00:06 +08:00
|
|
|
|
```
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
#### C
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
const char table[] = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"; // 码表
|
|
|
|
|
char tr[124]; // 反查码表
|
|
|
|
|
const unsigned long long XOR = 177451812; // 固定异或值
|
|
|
|
|
const unsigned long long ADD = 8728348608; // 固定加法值
|
|
|
|
|
const int s[] = {11, 10, 3, 8, 4, 6}; // 位置编码表
|
|
|
|
|
|
|
|
|
|
// 初始化反查码表
|
|
|
|
|
void tr_init() {
|
|
|
|
|
for (int i = 0; i < 58; i++)
|
|
|
|
|
tr[table[i]] = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned long long bv2av(char bv[]) {
|
|
|
|
|
unsigned long long r = 0;
|
|
|
|
|
unsigned long long av;
|
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
|
|
|
r += tr[bv[s[i]]] * (unsigned long long)pow(58, i);
|
|
|
|
|
av = (r - ADD) ^ XOR;
|
|
|
|
|
return av;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *av2bv(unsigned long long av) {
|
|
|
|
|
char *result = (char*)malloc(13);
|
|
|
|
|
strcpy(result,"BV1 4 1 7 ");
|
|
|
|
|
av = (av ^ XOR) + ADD;
|
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
|
|
|
result[s[i]] = table[(unsigned long long)(av / (unsigned long long)pow(58, i)) % 58];
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
tr_init();
|
|
|
|
|
printf("%s\n", av2bv(170001));
|
|
|
|
|
printf("%u\n", bv2av("BV17x411w7KC"));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
输出为:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
BV17x411w7KC
|
|
|
|
|
170001
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
#### TypeScript
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
感谢[#417](https://github.com/SocialSisterYi/bilibili-API-collect/issues/417#issuecomment-1186475063)提供
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
export default class BvCode {
|
|
|
|
|
private TABEL = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'; // 码表
|
|
|
|
|
private TR: Record<string, number> = {}; // 反查码表
|
|
|
|
|
private S = [11, 10, 3, 8, 4, 6]; // 位置编码表
|
|
|
|
|
private XOR = 177451812; // 固定异或值
|
|
|
|
|
private ADD = 8728348608; // 固定加法值
|
|
|
|
|
constructor() {
|
2023-11-16 09:35:32 +08:00
|
|
|
|
// 初始化反查码表
|
2023-02-22 01:00:06 +08:00
|
|
|
|
const len = this.TABEL.length;
|
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
|
this.TR[this.TABEL[i]] = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
av2bv(av: number): string {
|
|
|
|
|
const x_ = (av ^ this.XOR) + this.ADD;
|
|
|
|
|
const r = ['B', 'V', '1', , , '4', , '1', , '7'];
|
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
|
|
|
r[this.S[i]] = this.TABEL[Math.floor(x_ / 58 ** i) % 58];
|
|
|
|
|
}
|
|
|
|
|
return r.join('');
|
|
|
|
|
}
|
|
|
|
|
bv2av(bv: string): number {
|
|
|
|
|
let r = 0;
|
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
|
|
|
r += this.TR[bv[this.S[i]]] * 58 ** i;
|
|
|
|
|
}
|
|
|
|
|
return (r - this.ADD) ^ this.XOR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bvcode = new BvCode();
|
|
|
|
|
|
|
|
|
|
console.log(bvcode.av2bv(170001));
|
|
|
|
|
console.log(bvcode.bv2av('BV17x411w7KC'));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
输出为:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
BV17x411w7KC
|
|
|
|
|
170001
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
#### Java
|
2023-02-22 01:00:06 +08:00
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
/**
|
|
|
|
|
* 算法来自:https://www.zhihu.com/question/381784377/answer/1099438784
|
|
|
|
|
*/
|
|
|
|
|
public class Util {
|
|
|
|
|
private static final String TABLE = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF";
|
|
|
|
|
private static final int[] S = new int[]{11, 10, 3, 8, 4, 6};
|
|
|
|
|
private static final int XOR = 177451812;
|
|
|
|
|
private static final long ADD = 8728348608L;
|
|
|
|
|
private static final Map<Character, Integer> MAP = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
|
for (int i = 0; i < 58; i++) {
|
|
|
|
|
MAP.put(TABLE.charAt(i), i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String aidToBvid(int aid) {
|
|
|
|
|
long x = (aid ^ XOR) + ADD;
|
|
|
|
|
char[] chars = new char[]{'B', 'V', '1', ' ', ' ', '4', ' ', '1', ' ', '7', ' ', ' '};
|
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
|
|
|
int pow = (int) Math.pow(58, i);
|
|
|
|
|
long i1 = x / pow;
|
|
|
|
|
int index = (int) (i1 % 58);
|
|
|
|
|
chars[S[i]] = TABLE.charAt(index);
|
|
|
|
|
}
|
|
|
|
|
return String.valueOf(chars);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int bvidToAid(String bvid) {
|
|
|
|
|
long r = 0;
|
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
|
|
|
r += MAP.get(bvid.charAt(S[i])) * Math.pow(58, i);
|
|
|
|
|
}
|
|
|
|
|
return (int) ((r - ADD) ^ XOR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
#### Kotlin
|
|
|
|
|
|
2023-02-22 01:00:06 +08:00
|
|
|
|
```kotlin
|
|
|
|
|
/**
|
|
|
|
|
* 此程序非完全原创,改编自GH站内某大佬的Java程序,修改了部分代码,且转换为Kotlin
|
|
|
|
|
* 算法来源同上
|
|
|
|
|
*/
|
|
|
|
|
object VideoUtils {
|
|
|
|
|
//这里是由知乎大佬不知道用什么方法得出的转换用数字
|
|
|
|
|
var ss = intArrayOf(11, 10, 3, 8, 4, 6, 2, 9, 5, 7)
|
|
|
|
|
var xor: Long = 177451812 //二进制时加减数1
|
|
|
|
|
|
|
|
|
|
var add = 8728348608L //十进制时加减数2
|
|
|
|
|
|
|
|
|
|
//变量初始化工作,加载哈希表
|
|
|
|
|
private const val table = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"
|
|
|
|
|
private val mp = HashMap<String, Int>()
|
|
|
|
|
private val mp2 = HashMap<Int, String>()
|
|
|
|
|
|
|
|
|
|
//现在,定义av号和bv号互转的方法
|
|
|
|
|
//定义一个power乘方方法,这是转换进制必要的
|
|
|
|
|
fun power(a: Int, b: Int): Long {
|
|
|
|
|
var power: Long = 1
|
|
|
|
|
for (c in 0 until b) power *= a.toLong()
|
|
|
|
|
return power
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//bv转av方法
|
|
|
|
|
fun bv2av(s: String): String {
|
|
|
|
|
var r: Long = 0
|
|
|
|
|
//58进制转换
|
|
|
|
|
for (i in 0..57) {
|
|
|
|
|
val s1 = table.substring(i, i + 1)
|
|
|
|
|
mp[s1] = i
|
|
|
|
|
}
|
|
|
|
|
for (i in 0..5) {
|
|
|
|
|
r += mp[s.substring(ss[i], ss[i] + 1)]!! * power(58, i)
|
|
|
|
|
}
|
|
|
|
|
//转换完成后,需要处理,带上两个随机数
|
|
|
|
|
return (r - add xor xor).toString()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//av转bv方法
|
|
|
|
|
fun av2bv(st: String): String {
|
|
|
|
|
try {
|
|
|
|
|
var s = java.lang.Long.valueOf(st.split("av".toRegex()).dropLastWhile { it.isEmpty() }
|
|
|
|
|
.toTypedArray()[1])
|
|
|
|
|
val sb = StringBuffer("BV1 4 1 7 ")
|
|
|
|
|
//逆向思路,先将随机数还原
|
|
|
|
|
s = (s xor xor) + add
|
|
|
|
|
//58进制转回
|
|
|
|
|
for (i in 0..57) {
|
|
|
|
|
val s1 = table.substring(i, i + 1)
|
|
|
|
|
mp2[i] = s1
|
|
|
|
|
}
|
|
|
|
|
for (i in 0..5) {
|
|
|
|
|
val r = mp2[(s / power(58, i) % 58).toInt()]
|
|
|
|
|
sb.replace(ss[i], ss[i] + 1, r!!)
|
|
|
|
|
}
|
|
|
|
|
return sb.toString()
|
|
|
|
|
} catch (e: ArrayIndexOutOfBoundsException) {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2023-11-16 09:35:32 +08:00
|
|
|
|
#### Golang
|
|
|
|
|
|
2023-02-22 01:00:06 +08:00
|
|
|
|
```go
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import "math"
|
|
|
|
|
|
|
|
|
|
const TABLE = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"
|
|
|
|
|
var S = [11]uint{11, 10, 3, 8, 4, 6}
|
|
|
|
|
const XOR = 177451812
|
|
|
|
|
const ADD = 8728348608
|
|
|
|
|
|
|
|
|
|
var TR = map[string]int64{}
|
|
|
|
|
|
|
|
|
|
// 初始化 TR
|
|
|
|
|
func init() {
|
|
|
|
|
for i := 0; i < 58; i++ {
|
|
|
|
|
TR[TABLE[i:i+1]] = int64(i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func BV2AV(bv string) int64 {
|
|
|
|
|
r := int64(0)
|
|
|
|
|
for i := 0; i < 6; i++ {
|
|
|
|
|
r += TR[bv[S[i]:S[i]+1]] * int64(math.Pow(58, float64(i)))
|
|
|
|
|
}
|
|
|
|
|
return (r - ADD) ^ XOR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AV2BV(av int64) string {
|
|
|
|
|
x := (av ^ XOR) + ADD
|
|
|
|
|
r := []rune("BV1 4 1 7 ")
|
|
|
|
|
for i := 0; i < 6; i++ {
|
|
|
|
|
r[S[i]] = rune(TABLE[x/int64(math.Pow(58, float64(i)))%58])
|
|
|
|
|
}
|
|
|
|
|
return string(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
println(AV2BV(170001))
|
|
|
|
|
println(BV2AV("BV17x411w7KC"))
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
输出为:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
BV17x411w7KC
|
|
|
|
|
170001
|
|
|
|
|
```
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
|
|
|
|
#### Rust
|
|
|
|
|
|
2023-05-16 02:56:18 +08:00
|
|
|
|
crate: https://github.com/stackinspector/bvid
|
2023-11-16 09:35:32 +08:00
|
|
|
|
|
2023-05-16 02:56:18 +08:00
|
|
|
|
```rust
|
|
|
|
|
// Copyright (c) 2023 stackinspector. MIT license.
|
|
|
|
|
|
|
|
|
|
const XORN: u64 = 177451812;
|
|
|
|
|
const ADDN: u64 = 100618342136696320;
|
|
|
|
|
const TABLE: [u8; 58] = *b"fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF";
|
|
|
|
|
const MAP: [usize; 10] = [9, 8, 1, 6, 2, 4, 0, 7, 3, 5];
|
|
|
|
|
const REV_TABLE: [u8; 74] = [
|
|
|
|
|
13, 12, 46, 31, 43, 18, 40, 28, 5, 0, 0, 0, 0, 0, 0, 0, 54, 20, 15, 8,
|
|
|
|
|
39, 57, 45, 36, 0, 38, 51, 42, 49, 52, 0, 53, 7, 4, 9, 50, 10, 44, 34, 6,
|
|
|
|
|
25, 1, 0, 0, 0, 0, 0, 0, 26, 29, 56, 3, 24, 0, 47, 27, 22, 41, 16, 0,
|
|
|
|
|
11, 37, 2, 35, 21, 17, 33, 30, 48, 23, 55, 32, 14, 19,
|
|
|
|
|
];
|
|
|
|
|
const POW58: [u64; 10] = [
|
|
|
|
|
1, 58, 3364, 195112, 11316496, 656356768, 38068692544,
|
|
|
|
|
2207984167552, 128063081718016, 7427658739644928,
|
|
|
|
|
];
|
|
|
|
|
|
2023-05-16 03:38:27 +08:00
|
|
|
|
fn av2bv(avid: u64) -> [u8; 10] {
|
2023-05-16 02:56:18 +08:00
|
|
|
|
let a = (avid ^ XORN) + ADDN;
|
|
|
|
|
let mut bvid = [0; 10];
|
|
|
|
|
for i in 0..10 {
|
|
|
|
|
bvid[MAP[i]] = TABLE[(a / POW58[i]) as usize % 58];
|
|
|
|
|
}
|
|
|
|
|
bvid
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 03:38:27 +08:00
|
|
|
|
fn bv2av(bvid: [u8; 10]) -> u64 {
|
2023-05-16 02:56:18 +08:00
|
|
|
|
let mut a = 0;
|
|
|
|
|
for i in 0..10 {
|
|
|
|
|
a += REV_TABLE[bvid[MAP[i]] as usize - 49] as u64 * POW58[i];
|
|
|
|
|
}
|
|
|
|
|
(a - ADDN) ^ XORN
|
|
|
|
|
}
|
2023-05-16 03:38:27 +08:00
|
|
|
|
|
|
|
|
|
// assert_eq!(*b"17x411w7KC", av2bv(170001));
|
|
|
|
|
// assert_eq!(170001, bv2av(*b"17x411w7KC"));
|
2023-05-16 02:56:18 +08:00
|
|
|
|
```
|
2024-02-04 18:17:59 +08:00
|
|
|
|
|
|
|
|
|
</details>
|