返回提交历史
Modified
docs/misc/bvid_desc.md
+59
-0
Modified
docs/misc/sign/wbi.md
+85
-1
XFEstudio/bilibili-API-collect
为avid-bvid转换和Wbi签名添加Swift实现 (#890)
* Update bvid_desc.md * Update wbi.md
27308c2
代码差异
2 个文件
+144
-1
@@ -192,6 +192,65 @@ print(bv2av("BV1L9Uoa9EUx"))
192
192
193
193
参考 <https://github.com/Colerar/abv/blob/main/src/lib.rs>
194
194
195
### Swift
196
197
```swift
198
fileprivate let XOR_CODE: UInt64 = 23442827791579
199
fileprivate let MASK_CODE: UInt64 = 2251799813685247
200
fileprivate let MAX_AID: UInt64 = 1 << 51
201
202
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]
203
204
fileprivate let BASE: UInt64 = 58
205
fileprivate let BV_LEN: Int = 12
206
fileprivate let PREFIX: String = "BV1"
207
208
func av2bv(avid: UInt64) -> String {
209
var bytes: [UInt8] = [66, 86, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48]
210
var bvIdx = BV_LEN - 1
211
var tmp = (MAX_AID | avid) ^ XOR_CODE
212
213
while tmp != 0 {
214
bytes[bvIdx] = data[Int(tmp % BASE)]
215
tmp /= BASE
216
bvIdx -= 1
217
}
218
219
bytes.swapAt(3, 9)
220
bytes.swapAt(4, 7)
221
222
return String(decoding: bytes, as: UTF8.self)
223
}
224
225
func bv2av(bvid: String) -> UInt64 {
226
let fixedBvid: String
227
if bvid.hasPrefix("BV") {
228
fixedBvid = bvid
229
} else {
230
fixedBvid = "BV" + bvid
231
}
232
var bvidArray = Array(fixedBvid.utf8)
233
234
bvidArray.swapAt(3, 9)
235
bvidArray.swapAt(4, 7)
236
237
let trimmedBvid = String(decoding: bvidArray[3...], as: UTF8.self)
238
239
var tmp: UInt64 = 0
240
241
for char in trimmedBvid {
242
if let idx = data.firstIndex(of: char.utf8.first!) {
243
tmp = tmp * BASE + UInt64(idx)
244
}
245
}
246
247
return (tmp & MASK_CODE) ^ XOR_CODE
248
}
249
250
print(av2bv(avid: 111298867365120))
251
print(bv2av(bvid: "BV1L9Uoa9EUx"))
252
```
253
195
254
## 老版算法存档
196
255
197
256
算法参考自[【揭秘】av号转bv号的过程](https://www.bilibili.com/video/BV1N741127Tj)
@@ -115,7 +115,7 @@
115
115
116
116
## Wbi签名算法实现Demo
117
117
118
该 Demo 提供 [Python](#Python)、[JavaScript](#JavaScript)、[Golang](#Golang)、[C#](#CSharp)和[Java](#Java) 语言
118
该 Demo 提供 [Python](#Python)、[JavaScript](#JavaScript)、[Golang](#Golang)、[C#](#CSharp)、[Java](#Java)和[Swift](#Swift) 语言
119
119
120
120
### Python
121
121
@@ -816,3 +816,87 @@ async fn main() {
816
816
}
817
817
```
818
818
819
### Swift
820
821
需要 [Alamofire](https://github.com/Alamofire/Alamofire) 和 [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) 库
822
823
```swift
824
import Foundation
825
import CommonCrypto
826
import Alamofire
827
import SwiftyJSON
828
829
func biliWbiSign(param: String, completion: @escaping (String?) -> Void) {
830
func getMixinKey(orig: String) -> String {
831
return String(mixinKeyEncTab.map { orig[orig.index(orig.startIndex, offsetBy: $0)] }.prefix(32))
832
}
833
834
func encWbi(params: [String: Any], imgKey: String, subKey: String) -> [String: Any] {
835
var params = params
836
let mixinKey = getMixinKey(orig: imgKey + subKey)
837
let currTime = round(Date().timeIntervalSince1970)
838
params["wts"] = currTime
839
params = params.sorted { $0.key < $1.key }.reduce(into: [:]) { $0[$1.key] = $1.value }
840
params = params.mapValues { String(describing: $0).filter { !"!'()*".contains($0) } }
841
let query = params.map { "\($0.key)=\($0.value)" }.joined(separator: "&")
842
let wbiSign = calculateMD5(string: query + mixinKey)
843
params["w_rid"] = wbiSign
844
return params
845
}
846
847
func getWbiKeys(completion: @escaping (Result<(imgKey: String, subKey: String), Error>) -> Void) {
848
AF.request("https://api.bilibili.com/x/web-interface/nav").responseJSON { response in
849
switch response.result {
850
case .success(let value):
851
let json = JSON(value)
852
let imgURL = json["data"]["wbi_img"]["img_url"].string ?? ""
853
let subURL = json["data"]["wbi_img"]["sub_url"].string ?? ""
854
let imgKey = imgURL.components(separatedBy: "/").last?.components(separatedBy: ".").first ?? ""
855
let subKey = subURL.components(separatedBy: "/").last?.components(separatedBy: ".").first ?? ""
856
completion(.success((imgKey, subKey)))
857
case .failure(let error):
858
completion(.failure(error))
859
}
860
}
861
}
862
863
func calculateMD5(string: String) -> String {
864
let data = Data(string.utf8)
865
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
866
_ = data.withUnsafeBytes {
867
CC_MD5($0.baseAddress, CC_LONG(data.count), &digest)
868
}
869
return digest.map { String(format: "%02hhx", $0) }.joined()
870
}
871
872
let mixinKeyEncTab = [
873
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
874
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
875
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
876
36, 20, 34, 44, 52
877
]
878
879
getWbiKeys { result in
880
switch result {
881
case .success(let keys):
882
let spdParam = param.components(separatedBy: "&")
883
var spdDicParam = [String: String]()
884
spdParam.forEach { pair in
885
let components = pair.components(separatedBy: "=")
886
if components.count == 2 {
887
spdDicParam[components[0]] = components[1]
888
}
889
}
890
891
let signedParams = encWbi(params: spdDicParam, imgKey: keys.imgKey, subKey: keys.subKey)
892
let query = signedParams.map { "\($0.key)=\($0.value)" }.joined(separator: "&")
893
completion(query)
894
case .failure(let error):
895
print("Error getting keys: \(error)")
896
completion(nil)
897
}
898
}
899
}
900
901
```
902