Skip to content

Commit

Permalink
Merge fix-douyu into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
xjbeta committed Feb 28, 2019
2 parents 5eb55d9 + 9340fac commit a64017b
Show file tree
Hide file tree
Showing 4 changed files with 6,164 additions and 115 deletions.
4 changes: 4 additions & 0 deletions iina+.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
01683DDB2118905D0016A886 /* Bilibili.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01683DDA2118905D0016A886 /* Bilibili.swift */; };
01683DDE211924160016A886 /* BilibiliViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01683DDD211924160016A886 /* BilibiliViewController.swift */; };
0169098021CF472F008CDF0E /* SideBarImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0169097F21CF472F008CDF0E /* SideBarImageView.swift */; };
01842A4B2226824C00BA77CB /* crypto-js.js in Resources */ = {isa = PBXBuildFile; fileRef = 01842A4A2226824C00BA77CB /* crypto-js.js */; };
0196CB6421214B4200BCD29E /* AdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0196CB6321214B4200BCD29E /* AdvancedViewController.swift */; };
019EB39C21453A0F000FD123 /* devmate.pem in Resources */ = {isa = PBXBuildFile; fileRef = 019EB39B21453A0F000FD123 /* devmate.pem */; };
01AEC8AB20EDFD01001406E8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AEC8AA20EDFD01001406E8 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -200,6 +201,7 @@
01683DDA2118905D0016A886 /* Bilibili.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bilibili.swift; sourceTree = "<group>"; };
01683DDD211924160016A886 /* BilibiliViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BilibiliViewController.swift; sourceTree = "<group>"; };
0169097F21CF472F008CDF0E /* SideBarImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideBarImageView.swift; sourceTree = "<group>"; };
01842A4A2226824C00BA77CB /* crypto-js.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "crypto-js.js"; sourceTree = "<group>"; };
0196CB6321214B4200BCD29E /* AdvancedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedViewController.swift; sourceTree = "<group>"; };
019EB39B21453A0F000FD123 /* devmate.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = devmate.pem; sourceTree = "<group>"; };
01AEC8A720EDFD01001406E8 /* iina+.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iina+.app"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -372,6 +374,7 @@
015C19F5218FC9A8003B2F3A /* Block-List-Plus.xml */,
019EB39B21453A0F000FD123 /* devmate.pem */,
013850F9214EA2AA003817CE /* huya.js */,
01842A4A2226824C00BA77CB /* crypto-js.js */,
);
name = Resources;
sourceTree = "<group>";
Expand Down Expand Up @@ -528,6 +531,7 @@
buildActionMask = 2147483647;
files = (
01AEC8AF20EDFD02001406E8 /* Assets.xcassets in Resources */,
01842A4B2226824C00BA77CB /* crypto-js.js in Resources */,
015C19F7218FC9A8003B2F3A /* Block-List-Plus.xml in Resources */,
019EB39C21453A0F000FD123 /* devmate.pem in Resources */,
016792E7212BDEE5003517A7 /* SelectVideoCollectionViewItem.xib in Resources */,
Expand Down
157 changes: 56 additions & 101 deletions iina+/Utils/Processes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,112 +156,67 @@ class Processes: NSObject {
let pipe = Pipe()
task.standardInput = pipe
var mpvArgs = ["\(MPVOption.Miscellaneous.forceMediaTitle)=\(title)"]
getCookies(for: .douyu).done { cookies in
switch options {
case .douyu:
mpvArgs.append(contentsOf: [MPVOption.Network.cookies,
"\(MPVOption.Network.cookiesFile)=\(cookies)",
"\(MPVOption.ProgramBehavior.ytdl)=no"])
case .bilibili:
mpvArgs.append(contentsOf: ["\(MPVOption.ProgramBehavior.ytdl)=no",
"\(MPVOption.Network.referrer)=https://www.bilibili.com/",
"\(MPVOption.Audio.audioFile)=\(audioUrl)"])
case .withoutYtdl:
mpvArgs.append("\(MPVOption.ProgramBehavior.ytdl)=no")
case .none: break

switch options {
case .douyu:
mpvArgs.append(contentsOf: ["\(MPVOption.ProgramBehavior.ytdl)=no"])
case .bilibili:
mpvArgs.append(contentsOf: ["\(MPVOption.ProgramBehavior.ytdl)=no",
"\(MPVOption.Network.referrer)=https://www.bilibili.com/",
"\(MPVOption.Audio.audioFile)=\(audioUrl)"])
case .withoutYtdl:
mpvArgs.append("\(MPVOption.ProgramBehavior.ytdl)=no")
case .none: break
}

let mergeWithEdl = true
if !mergeWithEdl {
if urls.count > 1 {
mpvArgs.append(MPVOption.ProgramBehavior.mergeFiles)
}
}.ensure {
let mergeWithEdl = true
if !mergeWithEdl {
if urls.count > 1 {
mpvArgs.append(MPVOption.ProgramBehavior.mergeFiles)
}
}

switch Preferences.shared.livePlayer {
case .iina:
task.launchPath = Preferences.shared.livePlayer.rawValue
mpvArgs = mpvArgs.map {
"--mpv-" + $0
}
case .mpv:
task.launchPath = self.which(Preferences.shared.livePlayer.rawValue).first ?? ""
mpvArgs.append(MPVOption.Terminal.reallyQuiet)
mpvArgs = mpvArgs.map {
"--" + $0
}
}
if urls.count == 1 {
mpvArgs.insert(urls.first ?? "", at: 0)
} else if urls.count > 1 {
if mergeWithEdl {
var edlString = urls.reduce(String()) { result, url in
var re = result
re += "%\(url.count)%\(url);"
return re
}
edlString = "edl://" + edlString

switch Preferences.shared.livePlayer {
case .iina:
task.launchPath = Preferences.shared.livePlayer.rawValue
mpvArgs = mpvArgs.map {
"--mpv-" + $0
}
case .mpv:
task.launchPath = self.which(Preferences.shared.livePlayer.rawValue).first ?? ""
mpvArgs.append(MPVOption.Terminal.reallyQuiet)
mpvArgs = mpvArgs.map {
"--" + $0
}
}
if urls.count == 1 {
mpvArgs.insert(urls.first ?? "", at: 0)
} else if urls.count > 1 {
if mergeWithEdl {
var edlString = urls.reduce(String()) { result, url in
var re = result
re += "%\(url.count)%\(url);"
return re
}
edlString = "edl://" + edlString

mpvArgs.insert(edlString, at: 0)
} else {
mpvArgs.insert(contentsOf: urls, at: 0)
}

}
if Preferences.shared.livePlayer == .iina {
if Preferences.shared.enableDanmaku {
mpvArgs.append("--danmaku")
}
if options == .bilibili {
mpvArgs.append("--directly")
}
}
Log("Player arguments: \(mpvArgs)")
task.arguments = mpvArgs
task.launch()
}.catch {
Log("Get video cookies error: \($0)")
mpvArgs.insert(edlString, at: 0)
} else {
mpvArgs.insert(contentsOf: urls, at: 0)
}

}
}

}

private extension Processes {
func getCookies(for website: LiveSupportList) -> Promise<String> {
return Promise { resolver in
switch website {
case .douyu:
let douyuCookie = "https://passport.douyu.com/lapi/did/api/get"
let time = UInt32(NSDate().timeIntervalSinceReferenceDate)
srand48(Int(time))
let random = "\(drand48())"
let parameters = ["client_id": "1",
"callback": ("jsonp_" + random).replacingOccurrences(of: ".", with: "")]
let headers = ["Referer": "http://www.douyu.com"]

HTTP.GET(douyuCookie, parameters: parameters, headers: headers) { response in
if let error = response.error {
resolver.reject(error)
}
do {
var str = response.text
str = str?.subString(from: "(", to: ")")
let json = try JSONParser.JSONObjectWithData(str?.data(using: .utf8) ?? Data())
let didStr: String = try json.value(for: "data.did")
let date = Int(Date().timeIntervalSince1970)
let cookiesString = """
..douyu.com TRUE / FALSE \(date) dy_did \(didStr)
.www.douyu.com TRUE / FALSE \(date) acf_did \(didStr)
"""

resolver.fulfill(cookiesString)
} catch let error {
resolver.reject(error)
}
}
default:
resolver.fulfill("")
if Preferences.shared.livePlayer == .iina {
if Preferences.shared.enableDanmaku {
mpvArgs.append("--danmaku")
}
if options == .bilibili {
mpvArgs.append("--directly")
}
}
Log("Player arguments: \(mpvArgs)")
task.arguments = mpvArgs
task.launch()
}

}
130 changes: 116 additions & 14 deletions iina+/Utils/VideoGet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,29 @@ class VideoGet: NSObject {
resolver.reject($0)
}
case .douyu:
getDouyuRoomIds(url).then {
when(resolved: $0.map { self.getDouyuUrl($0) })
}.done {
var roomIds = [Int]()
var roomTitles = [String]()

getDouyuRoomIds(url).get {
roomIds = $0
}.then { _ in
when(resolved: roomIds.map { self.getDouyuUserInfo($0) })
}.get {
try $0.forEach {
switch $0 {
case .fulfilled(let strings):
yougetJson.streams[strings.0] = Stream(url: strings.1)
case .fulfilled(let info):
roomTitles.append(info.title)
case .rejected(let error):
throw error
}
}
}.then { _ in
when(resolved: roomIds.map { self.getDouyuUrl($0) })
}.done {
try $0.enumerated().forEach {
switch $0.element {
case .fulfilled(let url):
yougetJson.streams[roomTitles[$0.offset]] = Stream(url: url)
case .rejected(let error):
throw error
}
Expand All @@ -79,6 +95,7 @@ class VideoGet: NSObject {
}.catch {
resolver.reject($0)
}

case .panda:
getPandaRoomID(url).then {
when(resolved: $0.map { self.getPandaInfo($0) })
Expand Down Expand Up @@ -463,32 +480,116 @@ extension VideoGet {
}
}

func getDouyuUrl(_ roomID: Int) -> Promise<(String, String)> {
private func getDouyuDid() -> Promise<(String)> {
// 10000000000000000000000000001501


let douyuCookie = "https://passport.douyu.com/lapi/did/api/get"
let time = UInt32(NSDate().timeIntervalSinceReferenceDate)
srand48(Int(time))
let random = "\(drand48())"
let parameters = ["client_id": "1",
"callback": ("jsonp_" + random).replacingOccurrences(of: ".", with: "")]
let headers = ["Referer": "http://www.douyu.com"]

return Promise { resolver in
HTTP.GET(douyuCookie, parameters: parameters, headers: headers) { response in
if let error = response.error {
resolver.reject(error)
}
do {
var str = response.text
str = str?.subString(from: "(", to: ")")
let json = try JSONParser.JSONObjectWithData(str?.data(using: .utf8) ?? Data())
let didStr: String = try json.value(for: "data.did")
resolver.fulfill(didStr)
} catch let error {
resolver.reject(error)
}
}
}
}

private func getDouyuJS(_ roomID: Int) -> Promise<(String)> {
return Promise { resolver in
// https://github.com/soimort/you-get/blob/master/src/you_get/extractors/douyutv.py
let args = "room/\(roomID)?aid=wp&client_sys=wp&time=\(Int(Date().timeIntervalSince1970))"
let auth_md5 = args + "zNzMV1y4EMxOHS6I5WKm"
let auth_str = MD5(auth_md5) ?? ""
HTTP.GET("https://www.douyu.com/swf_api/homeH5Enc?rids=\(roomID)") { response in
if let error = response.error {
resolver.reject(error)
}
do {
let json = try JSONParser.JSONObjectWithData(response.data)
let error: Int = try json.value(for: "error")
guard error == 0 else {
resolver.reject(VideoGetError.douyuSignError)
return
}
let didStr: String = try json.value(for: "data.room\(roomID)")
resolver.fulfill(didStr)
} catch let error {
resolver.reject(error)
}
}
}
}

private func getDouyuRtmpUrl(_ roomID: Int, didStr: String, douyuJS: String) -> Promise<(String)> {
// window[Object(ae.a)(256042, "9f4f419501570ad13334")]
return Promise { resolver in
let time = "\(Int(Date().timeIntervalSince1970))"

let ccryptoJsFilePath = Bundle.main.path(forResource: "crypto-js", ofType: "js")
let jsContext = JSContext()
jsContext?.evaluateScript(try? String(contentsOfFile: ccryptoJsFilePath!))
jsContext?.evaluateScript("var CryptoJS = require('crypto-js');")
jsContext?.evaluateScript(douyuJS)

HTTP.GET("http://www.douyutv.com/api/v1/" + args + "&auth=" + auth_str) { response in
let result = jsContext?.evaluateScript("ub98484234(\(roomID), '\(didStr)', \(time))")
guard let signStr = result?.toString().subString(from: "sign="), signStr.count > 0 else {
resolver.reject(VideoGetError.douyuSignError)
return
}

let pars = ["v": "220120190227",
"did": didStr,
"tt": time,
"sign": signStr,
"cdn": "",
"rate": "0"]

HTTP.POST("https://www.douyu.com/lapi/live/getH5Play/\(roomID)", parameters: pars) { response in
if let error = response.error {
resolver.reject(error)
}
do {
let json: JSONObject = try JSONParser.JSONObjectWithData(response.data)
let title: String = try json.value(for: "data.room_name")
let rtmpUrl: String = try json.value(for: "data.rtmp_url")
let rtmpLive: String = try json.value(for: "data.rtmp_live")

resolver.fulfill((title, rtmpUrl + "/" + rtmpLive))
resolver.fulfill(rtmpUrl + "/" + rtmpLive)
} catch let error {
resolver.reject(error)
}
}
}
}


func getDouyuUrl(_ roomID: Int) -> Promise<(String)> {
return firstly {
when(resolved: [getDouyuDid(), getDouyuJS(roomID)])
}.then { res -> Promise<(String)> in
var reStrs = [String]()
try res.forEach {
switch $0 {
case .fulfilled(let strings):
reStrs.append(strings)
case .rejected(let error):
throw error
}
}
return self.getDouyuRtmpUrl(roomID, didStr: reStrs[0], douyuJS: reStrs[1])
}
}

// MARK: - Panda
func getPandaUserInfo(_ roomId: Int) -> Promise<PandaInfo> {
return Promise { resolver in
Expand Down Expand Up @@ -925,6 +1026,7 @@ extension VideoGet {

enum VideoGetError: Error {
case douyuUrlError
case douyuSignError

case isNotLiving
case notFindUrls
Expand Down
Loading

0 comments on commit a64017b

Please sign in to comment.