Skip to content

Commit

Permalink
feat:播放信息获取采用wbi接口
Browse files Browse the repository at this point in the history
  • Loading branch information
yichengchen committed Dec 1, 2024
1 parent 559a972 commit f4a851d
Showing 1 changed file with 156 additions and 15 deletions.
171 changes: 156 additions & 15 deletions BilibiliLive/Request/WebRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Alamofire
import CommonCrypto
import Foundation
import SwiftProtobuf
import SwiftyJSON
Expand Down Expand Up @@ -38,8 +39,8 @@ enum WebRequest {
static let like = "https://api.bilibili.com/x/web-interface/archive/like"
static let likeStatus = "https://api.bilibili.com/x/web-interface/archive/has/like"
static let coin = "https://api.bilibili.com/x/web-interface/coin/add"
static let playerInfo = "https://api.bilibili.com/x/player/v2"
static let playUrl = "https://api.bilibili.com/x/player/playurl"
static let playerInfo = "https://api.bilibili.com/x/player/wbi/v2"
static let playUrl = "https://api.bilibili.com/x/player/wbi/playurl"
static let pcgPlayUrl = "https://api.bilibili.com/pgc/player/web/playurl"
static let bangumiSeason = "https://bangumi.bilibili.com/view/web_api/season"
static let userEpisodeInfo = "https://api.bilibili.com/pgc/season/episode/web/info"
Expand Down Expand Up @@ -83,21 +84,63 @@ enum WebRequest {
}
session.sessionConfiguration.timeoutIntervalForResource = 10
session.sessionConfiguration.timeoutIntervalForRequest = 10
session.request(url,
method: method,
parameters: parameters,
encoding: URLEncoding.default,
headers: afheaders,
interceptor: nil)
.responseData { response in
switch response.result {
case let .success(data):
complete?(.success(data))
case let .failure(err):
print(err)
complete?(.failure(.networkFail))

let completionHandler: (AFDataResponse<Data>) -> Void = { response in
switch response.result {
case let .success(data):
complete?(.success(data))
case let .failure(err):
print(err)
complete?(.failure(.networkFail))
}
}

addWbiSign(method: method, url: url, parameters: parameters) { wbiSign in
if let wbiSign {
session.request(wbiSign,
method: method,
encoding: URLEncoding.default,
headers: afheaders)
.responseData(completionHandler: completionHandler)
} else {
session.request(url,
method: method,
parameters: parameters,
encoding: URLEncoding.default,
headers: afheaders)
.responseData(completionHandler: completionHandler)
}
}
}

private static func addWbiSign(method: HTTPMethod = .get,
url: URLConvertible,
parameters: Parameters = [:],
onComplete: @escaping (String?) -> Void)
{
do {
let urlObj = try url.asURL()
if urlObj.absoluteString.contains("/wbi/") == true, method == .get {
var request = URLRequest(url: urlObj)
request.method = .get
request = try URLEncoding.queryString.encode(request, with: parameters)
if let query = request.url?.query(percentEncoded: true) {
biliWbiSign(param: query) { res in
if let res {
let urlString = urlObj.absoluteString + "?" + res
onComplete(urlString)
return
} else {
onComplete(nil)
}
}
return
}
}
onComplete(nil)
} catch {
onComplete(nil)
}
}

static func requestJSON(method: HTTPMethod = .get,
Expand Down Expand Up @@ -507,6 +550,104 @@ extension WebRequest {
}
}

// MARK: - Wbi

extension WebRequest {
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/misc/sign/wbi.md#Swift
static func biliWbiSign(param: String, completion: @escaping (String?) -> Void) {
func getMixinKey(orig: String) -> String {
return String(mixinKeyEncTab.map { orig[orig.index(orig.startIndex, offsetBy: $0)] }.prefix(32))
}

func encWbi(params: [String: Any], imgKey: String, subKey: String) -> [String: Any] {
var params = params
let mixinKey = getMixinKey(orig: imgKey + subKey)
let currTime = round(Date().timeIntervalSince1970)
params["wts"] = currTime
params = params.sorted { $0.key < $1.key }.reduce(into: [:]) { $0[$1.key] = $1.value }
params = params.mapValues { String(describing: $0).filter { !"!'()*".contains($0) } }
let query = params.map { "\($0.key)=\($0.value)" }.joined(separator: "&")
let wbiSign = calculateMD5(string: query + mixinKey)
params["w_rid"] = wbiSign
return params
}

func getWbiKeys(completion: @escaping (Result<(imgKey: String, subKey: String), Error>) -> Void) {
class Cache {
var imgKey: String?
var subKey: String?
var lastUpdate: Date?

static let shared = Cache()
}

if let imgKey = Cache.shared.imgKey, let subKey = Cache.shared.subKey, let lastUpdate = Cache.shared.lastUpdate, Date().timeIntervalSince(lastUpdate) < 60 * 60 * 12 {
completion(.success((imgKey, subKey)))
return
}

let headers: HTTPHeaders = [
"User-Agent": Keys.userAgent,
"Referer": Keys.referer,
]

AF.request("https://api.bilibili.com/x/web-interface/nav", headers: headers).responseData { response in
switch response.result {
case let .success(value):
let json = JSON(value)
let imgURL = json["data"]["wbi_img"]["img_url"].string ?? ""
let subURL = json["data"]["wbi_img"]["sub_url"].string ?? ""
let imgKey = imgURL.components(separatedBy: "/").last?.components(separatedBy: ".").first ?? ""
let subKey = subURL.components(separatedBy: "/").last?.components(separatedBy: ".").first ?? ""
Cache.shared.imgKey = imgKey
Cache.shared.subKey = subKey
Cache.shared.lastUpdate = Date()
completion(.success((imgKey, subKey)))
case let .failure(error):
completion(.failure(error))
}
}
}

func calculateMD5(string: String) -> String {
let data = Data(string.utf8)
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
_ = data.withUnsafeBytes {
CC_MD5($0.baseAddress, CC_LONG(data.count), &digest)
}
return digest.map { String(format: "%02hhx", $0) }.joined()
}

let mixinKeyEncTab = [
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
36, 20, 34, 44, 52,
]

getWbiKeys { result in
switch result {
case let .success(keys):
let spdParam = param.components(separatedBy: "&")
var spdDicParam = [String: String]()
spdParam.forEach { pair in
let components = pair.components(separatedBy: "=")
if components.count == 2 {
spdDicParam[components[0]] = components[1]
}
}

let signedParams = encWbi(params: spdDicParam, imgKey: keys.imgKey, subKey: keys.subKey)
let query = signedParams.map { "\($0.key)=\($0.value)" }.joined(separator: "&")
completion(query)
case let .failure(error):
print("Error getting keys: \(error)")
completion(nil)
}
}
}
}

struct HistoryData: DisplayData, Codable {
struct HistoryPage: Codable, Hashable {
let cid: Int
Expand Down

0 comments on commit f4a851d

Please sign in to comment.