XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/bilibili-API-collect

为avid-bvid转换和Wbi签名添加Swift实现 (#890)

* Update bvid_desc.md * Update wbi.md

27308c2
Mark Chan <45706356+WindowsMEMZ@users.noreply.github.com>
提交于

代码差异

2 个文件 +144 -1
Modified docs/misc/bvid_desc.md +59 -0
@@ -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)
Modified docs/misc/sign/wbi.md +85 -1
@@ -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