🚀调整项目结构

This commit is contained in:
SocialSisterYi
2023-02-22 01:00:06 +08:00
parent b85e835ce7
commit 7d89ece2ac
201 changed files with 84964 additions and 0 deletions

84
docs/other/API_sign.md Normal file
View File

@@ -0,0 +1,84 @@
# API 签名与鉴权
部分客户端专用的 rest api 存在基于 sign 的鉴权,需要使用规定的`appkey`及其对应的`appsec`与原始请求参数进行签名计算
不同`appkey`对应不同的 app (如客户端、概念版、必剪、漫画、bililink等)
不同平台同 app 也会存在不同的 `appkey`(如安卓端、ios端、TV端等)
同平台同 app 下不同功能也会存在不同的 `appkey`(如登录专用、取流专用等)
**appkey与appsec一一对应**
- [API签名的计算方式](#API签名的计算方式)
- [已知的APPKey](#已知的APPKey)
---
## API签名的计算方式
首先为参数中添加`appkey`字段,然后按照参数的 key 重新排序,再将重排序后的参数使用 url query 格式序列化拼接与该 appkey 相对应的 appsec (盐值) 进行**md5 hash计算**32位小写该 hash 便是 API 签名
为参数尾部增添`sign`字段,它的值为上一步计算所得的 hash一并作为表单提交
**实例:**
使用 appkey = `1d8b6e7d45233436`, appsec = `560c52ccd288fed045859ed18bffd973` 对如下 `params` 参数进行签名
```python
import hashlib
import urllib.parse
def appsign(params, appkey, appsec):
'为请求参数进行 api 签名'
params.update({'appkey': appkey})
params = dict(sorted(params.items())) # 重排序参数 key
query = urllib.parse.urlencode(params) # 序列化参数
sign = hashlib.md5((query+appsec).encode()).hexdigest() # 计算 api 签名
params.update({'sign':sign})
return params
appkey = '1d8b6e7d45233436'
appsec = '560c52ccd288fed045859ed18bffd973'
params = {
'id':114514,
'str':'1919810',
'test':'いいよ,こいよ',
}
signed_params = appsign(params, appkey, appsec)
query = urllib.parse.urlencode(signed_params)
print(signed_params)
print(query)
```
输出以下内容,分别是进行 api 签名后参数的 dict 以及 url query 格式
```
{'appkey': '1d8b6e7d45233436', 'id': 114514, 'str': '1919810', 'test': 'いいよ,こいよ', 'sign': '01479cf20504d865519ac50f33ba3a7d'}
appkey=1d8b6e7d45233436&id=114514&str=1919810&test=%E3%81%84%E3%81%84%E3%82%88%EF%BC%8C%E3%81%93%E3%81%84%E3%82%88&sign=01479cf20504d865519ac50f33ba3a7d
```
## 已知的APPKey
| appkey | appsecsign盐值 | 平台 | 应用 | 备注 |
|------------------|----------------------------------|-----|----------|------|
| 07da50c9a0bf829f | 25bdede4e1581c836cab73a48790ca6e | 安卓 | 概念版 | |
| 1d8b6e7d45233436 | 560c52ccd288fed045859ed18bffd973 | 安卓 | 客户端 | 一般用途 |
| 178cf125136ca8ea | 34381a26236dd1171185c0beb042e1c6 | 安卓 | 概念版 | |
| 27eb53fc9058f8c3 | c2ed53a74eeefe3cf99fbd01d8c9c375 | ios | 客户端 | 一般用途 |
| 37207f2beaebf8d7 | e988e794d4d4b6dd43bc0e89d6e90c43 | 安卓 | biliLink | |
| 4409e2ce8ffd12b8 | 59b43e04ad6965f34319062b478f83dd | TV | 客户端 | |
| 57263273bc6b67f6 | a0488e488d1567960d3a765e8d129f90 | 安卓 | 客户端 | |
| 8d23902c1688a798 | 710f0212e62bd499b8d3ac6e1db9302a | 安卓 | 车机版 | |
| 5dce947fe22167f9 | | 安卓 | 必剪 | |
| 7d336ec01856996b | a1ce6983bc89e20a36c37f40c4f1a0dd | 安卓 | 概念版 | |
| 85eb6835b0a1034e | 2ad42749773c441109bdc0191257a664 | | | |
| 8e16697a1b4f8121 | f5dd03b752426f2e623d7badb28d190a | 安卓 | 国际版 | |
| aae92bc66f3edfab | af125a0d5279fd576c1b4418a3e8276d | PC | 投稿工具 | |
| ae57252b0c09105d | c75875c596a69eb55bd119e74b07cfe3 | 安卓 | 国际版 | |
| bb3101000e232e27 | 36efcfed79309338ced0380abd824ac1 | 安卓 | 国际版 | |
| bca7e84c2d947ac6 | 60698ba2f68e01ce44738920a0ffe768 | 安卓 | 客户端 | 登录专用 |
| cc578d267072c94d | ffb6bb4c4edae2566584dbcacfc6a6ad | 安卓 | 轻视频 | |
| cc8617fd6961e070 | 3131924b941aac971e45189f265262be | 安卓 | 漫画 | |
| iVGUTjsxvpLeuDCf | aHRmhWMLkdeMuILqORnYZocwMBpMEOdt | 安卓 | 客户端 | 取流专用 |
| YvirImLGlLANCLvM | JNlZNgfNGKZEpaDTkCdPQVXntXhuiJEM | ios | 客户端 | 取流专用 |

389
docs/other/bvid_desc.md Normal file
View File

@@ -0,0 +1,389 @@
# 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)
---
- [概述](#概述)
- [格式](#格式)
- [实质](#实质)
- [avid发号方式的变化](#avid发号方式的变化)
- [算法概述](#算法概述)
- [av->bv算法](#av->bv算法)
- [bv->av算法](#bv->av算法)
- [编程实现](#编程实现)
- [Python](#Python)
- [C](#C)
- [TypeScript](#TypeScript)
- [Java](#Java)
- [Kotlin](#Kotlin)
- [Golang](#Golang)
---
## 概述
### 格式
“bvid”恒为长度为12的字符串前两个字母为大写“BV”后10个为base58计算结果
### 实质
“bvid"为“avid”的base58编码可通过算法进行相互转化
### avid发号方式的变化
从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
~~暗示B站东方要完泪目~~
## 算法概述
算法以及程序主要参考[知乎@mcfx的回答](https://www.zhihu.com/question/381784377/answer/1099438784)
### av->bv算法
本算法及示例程序仅能编码及解码avid<` 29460791296 `无法验证avid>=` 29460791296 `的正确性
1. a=(avid⊕177451812)+8728348608
2. 以i为循环变量循环6次b[i]=(a/58^i)%58
3. 将b[i]中各个数字转换为以下码表中的字符
码表:
> fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF
4. 初始化字符串b[i]=`BV1 4 1 7 `
5. 按照以下字符顺序编码表编码并填充至b[i]
字符顺序编码表:
> 0 -> 11
>
> 1 -> 10
>
> 2 -> 3
>
> 3 -> 8
>
> 4 -> 4
>
> 5 -> 6
### bv->av算法
为以上算法的逆运算
## 编程实现
使用Python、C、TypeScript、Java、Kotlin以及Golang作为示例欢迎社区提交更多例程
### Python
```python
table = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF' # 码表
tr = {} # 反查码表
# 初始化反查码表
for i in range(58):
tr[table[i]] = i
s = [11, 10, 3, 8, 4, 6] # 位置编码表
XOR = 177451812 # 固定异或值
ADD = 8728348608 # 固定加法值
def bv2av(x):
r = 0
for i in range(6):
r += tr[x[s[i]]] * 58 ** i
return (r - ADD) ^ XOR
def av2bv(x):
x = (x ^ XOR) + ADD
r = list('BV1 4 1 7 ')
for i in range(6):
r[s[i]] = table[x // 58 ** i % 58]
return ''. join(r)
print(av2bv(170001))
print(bv2av('BV17x411w7KC'))
```
输出为:
```
BV17x411w7KC
170001
```
### C
```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
```
### TypeScript
感谢[#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() {
// 初始化反查码表
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
```
### Java
```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);
}
}
```
### Kotlin
```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 ""
}
}
}
```
### Golang
```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
```

65
docs/other/errcode.md Normal file
View File

@@ -0,0 +1,65 @@
# 公共错误码
下表为大部分接口返回值中`code`字段值中公共的错误代码
- [权限类](#权限类)
- [请求类](#请求类)
---
## 权限类
| 代码 | 含义 |
| ---- | -------------------------- |
| -1 | 应用程序不存在或已被封禁 |
| -2 | Access Key 错误 |
| -3 | API 校验密匙错误 |
| -4 | 调用方对该 Method 没有权限 |
| -101 | 账号未登录 |
| -102 | 账号被封停 |
| -103 | 积分不足 |
| -104 | 硬币不足 |
| -105 | 验证码错误 |
| -106 | 账号非正式会员或在适应期 |
| -107 | 应用不存在或者被封禁 |
| -108 | 未绑定手机 |
| -110 | 未绑定手机 |
| -111 | csrf 校验失败 |
| -112 | 系统升级中 |
| -113 | 账号尚未实名认证 |
| -114 | 请先绑定手机 |
| -115 | 请先完成实名认证 |
## 请求类
| 代码 | 含义 |
| ---- | --------------------- |
| -304 | 木有改动 |
| -307 | 撞车跳转 |
| -400 | 请求错误 |
| -401 | 未认证 (或非法请求) |
| -403 | 访问权限不足 |
| -404 | 啥都木有 |
| -405 | 不支持该方法 |
| -409 | 冲突 |
| -412 | 请求被拦截 (客户端 ip 被服务端风控) |
| -500 | 服务器错误 |
| -503 | 过载保护,服务暂不可用 |
| -504 | 服务调用超时 |
| -509 | 超出限制 |
| -616 | 上传文件不存在 |
| -617 | 上传文件太大 |
| -625 | 登录失败次数太多 |
| -626 | 用户不存在 |
| -628 | 密码太弱 |
| -629 | 用户名或密码错误 |
| -632 | 操作对象数量限制 |
| -643 | 被锁定 |
| -650 | 用户等级太低 |
| -652 | 重复的用户 |
| -658 | Token 过期 |
| -662 | 密码时间戳过期 |
| -688 | 地理区域限制 |
| -689 | 版权限制 |
| -701 | 扣节操失败 |
|-8888|对不起,服务器开小差了~ (ಥ﹏ಥ)|

44
docs/other/picture.md Normal file
View File

@@ -0,0 +1,44 @@
# 图片格式化
对于\*.hdslb.com/bfs下的图片文件都可以使用以下格式化参数
> \*.hdslb.com/bfs/\*/\*.\[jpg/png/gif\]@{width}w\_{high}h\_{quality}q.{format}
| 可选参数 | 含义 | 备注 |
| -------- | ---------------- | ---------------- |
| width | 图片最大限制宽度 | |
| high | 图片最大限制高度 | |
| quality | 图片质量百分比 | 仅限webp |
| format | 图片格式 | 仅限png/jpg/webp |
**示例:**
原始图片
https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg
![](https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg)
高度限制为100
https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@100h
![](https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@100h)
宽度限制为100
https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@100w
![](https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@100w)
转换格式为webp
https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@.webp
![](https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@.webp)
转换为webp图片质量为1%
https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@1q.webp
![](https://i1.hdslb.com/bfs/archive/e5fff1472bad1c0c6bcb3004205f9be23b58ffc0.jpg@1q.webp)

79
docs/other/time_stamp.md Normal file
View File

@@ -0,0 +1,79 @@
# 获取当前时间戳
- [获取当前时间戳](#获取当前时间戳)
- [获取当前时间戳](#获取当前时间戳-1)
- [获取服务器端UTC时间](#获取服务器端utc时间)
---
## 获取当前时间戳
> https://api.bilibili.com/x/report/click/now
*请求方式GET*
**json回复**
根对象:
| 字段 | 类型 | 内容 | 备注 |
| ------- | ---- | -------- | ------- |
| code | num | 返回值 | 0成功 |
| message | str | 错误信息 | 默认为0 |
| ttl | num | 1 | |
| data | obj | 信息本体 | |
`data`对象:
| 字段 | 类型 | 内容 | 备注 |
| ---- | ---- | ------------ | ---- |
| now | num | 当前的时间戳 | |
**示例:**
```shell
curl 'https://api.bilibili.com/x/report/click/now'
```
<details>
<summary>查看响应示例:</summary>
```json
{
"code": 0,
"message": "0",
"ttl": 1,
"data": {
"now": 1592666471
}
}
```
</details>
## 获取服务器端UTC时间
> https://interface.bilibili.com/serverdate.js
*请求方式GET*
**js回复**
```js
window.serverdate = Date.UTC(YYYY, M, D, h, m, s);
```
**示例:**
```shell
curl 'https://interface.bilibili.com/serverdate.js'
```
<details>
<summary>查看响应示例:</summary>
```js
window.serverdate = Date.UTC(2021, 4, 16, 17, 31, 8);
```
</details>