Skip to content

Commit

Permalink
huya mp api
Browse files Browse the repository at this point in the history
  • Loading branch information
xjbeta committed Jun 5, 2024
1 parent 6546a56 commit 000354e
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 54 deletions.
104 changes: 95 additions & 9 deletions IINA+/Utils/VideoDecoder/Huya.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,15 @@ class Huya: NSObject, SupportSiteProtocol {
private let huyaUid = (Int(Date().timeIntervalSince1970 * 1000) % Int(1e10) * Int(1e3) + Int.random(in: Int(1e2)..<Int(1e3))) % 4294967295

func liveInfo(_ url: String) -> Promise<LiveInfo> {
getHuyaInfo(url).map {
getHuyaInfoMP(url).map {
$0
}
}

func decodeUrl(_ url: String) -> Promise<YouGetJSON> {
getHuyaVideos(url)
/*
getHuyaInfoM(url).map {
var yougetJson = YouGetJSON(rawUrl: url)
yougetJson.title = $0.title
return $0.write(to: yougetJson, uid: self.huyaUid)
}
*/
getHuyaInfoMP(url).map {
$0.videos(url, uid: self.huyaUid)
}
}

// MARK: - Huya
Expand Down Expand Up @@ -138,6 +133,19 @@ class Huya: NSObject, SupportSiteProtocol {
return info
}
}

func getHuyaInfoMP(_ url: String) -> Promise<HuyaInfoMP> {
let ucs = url.pathComponents
guard ucs.count >= 3 else {
return .init(error: VideoGetError.invalidLink)
}
let rid = ucs[2]

return pSession.request("https://mp.huya.com/cache.php?m=Live&do=profileRoom&roomid=\(rid)").responseData().map {
let jsonObj: JSONObject = try JSONParser.JSONObjectWithData($0.data)
return try HuyaInfoMP(object: jsonObj)
}
}
}

/*
Expand Down Expand Up @@ -433,6 +441,84 @@ struct HuyaInfoM: Unmarshaling, LiveInfo {
}
}

struct HuyaInfoMP: Unmarshaling, LiveInfo {

var title: String
var name: String
var avatar: String
var cover: String
var isLiving: Bool
var site: SupportSites = .huya

var streamInfos: [HuyaInfoM.StreamInfo]
var bitRateInfos: [HuyaInfoM.BitRateInfo]

init(object: any Marshal.MarshaledObject) throws {
let name1: String = try object.value(for: "data.liveData.roomName")
let name2: String = try object.value(for: "data.liveData.introduction")

title = name1 == "" ? name2 : name1

name = try object.value(for: "data.liveData.nick")
avatar = try object.value(for: "data.liveData.avatar180")
avatar = avatar.https()
cover = try object.value(for: "data.liveData.screenshot")
cover = cover.https()

let liveStatus: String = try object.value(for: "data.liveStatus")
isLiving = liveStatus == "ON"

streamInfos = try object.value(for: "data.stream.baseSteamInfoList")

let bitRateInfoString: String = try object.value(for: "data.liveData.bitRateInfo")
guard let data = bitRateInfoString.data(using: .utf8) else {
throw VideoGetError.notFountData
}
let jsonObj: [JSONObject] = try JSONParser.JSONArrayWithData(data)
bitRateInfos = try jsonObj.map(HuyaInfoM.BitRateInfo.init)
}


func videos(_ url: String, uid: Int) -> YouGetJSON {
var yougetJson = YouGetJSON(rawUrl: url)
yougetJson.title = title

let urls = streamInfos
// .sorted { i1, i2 -> Bool in
// i1.sCdnType == defaultCDN
// }
.sorted { i1, i2 -> Bool in
!i1.sFlvUrl.contains("txdirect.flv.huya.com")
}.compactMap {
HuyaUrl.format(
uid,
sStreamName: $0.sStreamName,
sFlvUrl: $0.sFlvUrl,
sFlvUrlSuffix: $0.sFlvUrlSuffix,
sFlvAntiCode: $0.sFlvAntiCode)
}

guard urls.count > 0 else {
return yougetJson
}

bitRateInfos.map {
($0.sDisplayName, $0.iBitRate)
}.forEach { (name, rate) in
var us = urls.map {
$0.replacingOccurrences(of: "&ratio=0", with: "&ratio=\(rate)")
}
var s = Stream(url: us.removeFirst())
s.src = us
s.quality = rate == 0 ? 9999999 : rate

yougetJson.streams[name] = s
}

return yougetJson
}
}


struct HuyaVideoSelector: VideoSelector {
var id: String
Expand Down
85 changes: 40 additions & 45 deletions IINA+/Utils/VideoDecoder/HuyaUrl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,27 @@ class HuyaUrl: NSObject {

let sid = now()

var parameters = [String: String]()
var antiCodes = antiCodeDic(sFlvAntiCode)

sFlvAntiCode.split(separator: "&").map {
$0.split(separator: "=", maxSplits: 1).map(String.init)
}.filter {
$0.count == 2
}.forEach {
parameters[$0[0]] = $0[1]
}

// (seqid - uid) > sid
let seqid = uid + now()

guard let convertUid = rotUid(uid),
let wsSecret = wsSecret(sFlvAntiCode, convertUid: convertUid, seqid: seqid, streamName: sStreamName) else { return "" }
let wsSecret = wsSecret(antiCodes, convertUid: convertUid, seqid: seqid, streamName: sStreamName) else { return "" }

parameters["u"] = "\(convertUid)"
parameters["wsSecret"] = wsSecret
antiCodes["u"] = "\(convertUid)"
antiCodes["wsSecret"] = wsSecret

// parameters["fm"] = nil
parameters["seqid"] = "\(seqid)"
parameters["sdk_sid"] = "\(sid)"
parameters["sv"] = "2405220949"
// antiCodes["fm"] = nil
antiCodes["seqid"] = "\(seqid)"
antiCodes["sdk_sid"] = "\(sid)"
antiCodes["sv"] = "2405220949"

parameters["sdkPcdn"] = "1_1"
parameters["t"] = "100"
parameters["a_block"] = "0"
parameters["ver"] = "1"
parameters["ratio"] = "0"
parameters["dMod"] = "mseh-32"
antiCodes["sdkPcdn"] = "1_1"
// parameters["t"] = "100"
antiCodes["a_block"] = "0"
antiCodes["ver"] = "1"
antiCodes["ratio"] = "0"
antiCodes["dMod"] = "mseh-32"

let example = "https://hw.flv.huya.com/src/1394575534-1394575534-5989656310331736064-2789274524-10057-A-0-1.flv?wsSecret=4b1ac7c8b5b3792b756f419bd6db09f8&wsTime=665aeff5&seqid=1750435781966&ctype=huya_webh5&ver=1&txyp=o%3An4%3B&fs=bgct&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=huya&sphd=264_*-265_*&exsphd=264_500,264_2000,264_4000,264_6000,264_8000,&ratio=2000&dMod=mseh-32&sdkPcdn=1_1&u=33818100666&t=100&sv=2405220949&sdk_sid=1717235700093&a_block=0"

Expand All @@ -63,7 +54,7 @@ class HuyaUrl: NSObject {

let pars = URLComponents(string: example)!.queryItems!.compactMap {
let key = $0.name
if let value = parameters[key] {
if let value = antiCodes[key] {
return key + "=" + value
} else {
Log("Huya parameters missing key, \($0.description)")
Expand Down Expand Up @@ -101,31 +92,35 @@ class HuyaUrl: NSObject {
return Int(a + n, radix: 2)
}

private static func wsSecret(_ antiCode: String,
convertUid: Int,
seqid: Int,
streamName: String) -> String? {

let d = antiCode.components(separatedBy: "&").reduce([String: String]()) { (re, str) -> [String: String] in
var r = re
let kv = str.components(separatedBy: "=")
guard kv.count == 2 else { return r }
r[kv[0]] = kv[1]
return r
}

guard var u = d["fm"]?.removingPercentEncoding?.base64Decode(),
let l = d["wsTime"],
let ctype = d["ctype"] else { return nil }

let s = "\(seqid)|\(ctype)|100".md5()

// let o = this[Mt].replace(Xt, r).replace($t, this[Kt]).replace(Zt, s).replace(te, this[Bt]);
private static func wsSecret(_ antiCodes: [String: String],
convertUid: Int,
seqid: Int,
streamName: String) -> String? {
guard var u = antiCodes["fm"]?.removingPercentEncoding?.base64Decode(),
let l = antiCodes["wsTime"],
let ctype = antiCodes["ctype"] else { return nil }
let t = antiCodes["t"] ?? "100"
let s = "\(seqid)|\(ctype)|\(t)".md5()

// let o = this[Mt].replace(Xt, r).replace($t, this[Kt]).replace(Zt, s).replace(te, this[Bt]);
u = u.replacingOccurrences(of: "$0", with: "\(convertUid)")
u = u.replacingOccurrences(of: "$1", with: streamName)
u = u.replacingOccurrences(of: "$2", with: s)
u = u.replacingOccurrences(of: "$3", with: l)

return u.md5()
}

private static func antiCodeDic(_ antiCode: String) -> [String: String] {
var dic = [String: String]()

antiCode.split(separator: "&").map {
$0.split(separator: "=", maxSplits: 1).map(String.init)
}.filter {
$0.count == 2
}.forEach {
dic[$0[0]] = $0[1]
}
return dic
}
}

0 comments on commit 000354e

Please sign in to comment.