Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
xjbeta committed Sep 5, 2023
2 parents 4cc9bef + 6acaff5 commit a9a7d6a
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 13 deletions.
4 changes: 2 additions & 2 deletions IINA+.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 0.7.9;
MARKETING_VERSION = 0.7.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.xjbeta.iina-plus";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -1032,7 +1032,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 0.7.9;
MARKETING_VERSION = 0.7.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.xjbeta.iina-plus";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
25 changes: 23 additions & 2 deletions IINA+/Utils/Danmaku/Danmaku.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,14 @@ class Danmaku: NSObject {
douyinDM = nil
}

func loadDM() {
DispatchQueue.main.async {
self.loadDanmaku()
}
}


func loadDM() {
func loadDanmaku() {
guard let url = URL(string: self.url) else { return }
let roomID = url.lastPathComponent
let videoDecoder = Processes.shared.videoDecoder
Expand Down Expand Up @@ -220,7 +226,7 @@ class Danmaku: NSObject {
return
}

let interval: DispatchTimeInterval = liveSite == .douyin ? .seconds(10) : .seconds(30)
let interval: DispatchTimeInterval = liveSite == .douyin ? .seconds(15) : .seconds(30)

timer.schedule(deadline: .now(), repeating: interval)
timer.setEventHandler {
Expand Down Expand Up @@ -326,6 +332,21 @@ new Uint8Array(sendRegisterGroups(["live:\(id)", "chat:\(id)"]));
}
delegate?.send(.init(method: .liveDMServer, text: "error"), sender: self)
}

func webSocket(_ webSocket: SRWebSocket, didReceivePong pongData: Data?) {
switch liveSite {
case .douyin:
guard let data = pongData,
let str = String(data: data, encoding: .utf8),
str.hasSuffix("hb") else {
return
}

heartBeatCount = 0
default:
break
}
}

func webSocket(_ webSocket: SRWebSocket, didReceiveMessageWith data: Data) {
switch liveSite {
Expand Down
6 changes: 5 additions & 1 deletion IINA+/Utils/Danmaku/DouYinDM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ class DouYinDM: NSObject {
let dy = proc.videoDecoder.douyin
return dy.liveInfo(url).done {
self.cookies = dy.cookies
self.roomId = ($0 as! DouYinInfo).roomId
if let rid = ($0 as? DouYinEnterData.DouYinLiveInfo)?.roomId {
self.roomId = rid
} else {
self.roomId = ($0 as! DouYinInfo).roomId
}
}
}
}
Expand Down
125 changes: 121 additions & 4 deletions IINA+/Utils/VideoDecoder/DouYin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,64 @@ class DouYin: NSObject, SupportSiteProtocol {
}
}
return prepareTask!.then {
self.getContent(url)
self.getEnterContent(url)
}
} else {
return self.getContent(url)
return self.getEnterContent(url)
}
}

func decodeUrl(_ url: String) -> Promise<YouGetJSON> {
liveInfo(url).compactMap {
($0 as? DouYinInfo)?.write(to: YouGetJSON(rawUrl: url))
var json = YouGetJSON(rawUrl: url)

if let info = $0 as? DouYinEnterData.DouYinLiveInfo {
json = info.write(to: json)
} else if let info = $0 as? DouYinInfo {
json = info.write(to: json)
} else {
return nil
}

return json
}
}


func getEnterContent(_ url: String) -> Promise<LiveInfo> {
let cookieString = cookies.map {
"\($0.key)=\($0.value)"
}.joined(separator: ";")

let headers = HTTPHeaders([
"User-Agent": douyinUA,
"referer": url,
"Cookie": cookieString
])

guard let pc = NSURL(string: url)?.pathComponents,
pc.count >= 2,
pc[0] == "/" else {
return .init(error: VideoGetError.invalidLink)
}

let rid = pc[1]

let u = "https://live.douyin.com/webcast/room/web/enter/?aid=6383&app_name=douyin_web&live_id=1&device_platform=web&language=en-US&cookie_enabled=true&browser_language=en-US&browser_platform=Mac&browser_name=Safari&browser_version=16&web_rid=\(rid)&enter_source=&is_need_double_stream=true"


return AF.request(u, headers: headers).responseData().map {
let jsonObj: JSONObject = try JSONParser.JSONObjectWithData($0.data)
let enterData = try DouYinEnterData(object: jsonObj)

if let info = enterData.infos.first {
return info
} else {
throw VideoGetError.notFountData
}
}
}


func getContent(_ url: String) -> Promise<LiveInfo> {
let cookieString = cookies.map {
Expand Down Expand Up @@ -270,7 +315,7 @@ class DouYin: NSObject, SupportSiteProtocol {
}.get {
self.storageDic = $0
}.then { _ in
self.getContent(self.douyinEmptyURL.absoluteString)
self.getEnterContent(self.douyinEmptyURL.absoluteString)
}.done { info in
Log("Douyin test info \(info.title)")
Log("Douyin deinit webview")
Expand Down Expand Up @@ -383,6 +428,78 @@ struct DouYinInfo: Unmarshaling, LiveInfo {
}
}

struct DouYinEnterData: Unmarshaling {
var infos: [DouYinLiveInfo]

struct DouYinLiveInfo: Unmarshaling, LiveInfo {
var title: String
var name: String
var avatar: String
var cover: String
var isLiving: Bool
var site = SupportSites.douyin

var urls: [String: String]
var roomId: String

init(object: MarshaledObject) throws {
title = try object.value(for: "title")

name = (try? object.value(for: "owner.nickname")) ?? ""
let avatars: [String] = (try? object.value(for: "owner.avatar_thumb.url_list")) ?? []
avatar = avatars.first ?? ""
let covers: [String] = (try? object.value(for: "cover.url_list")) ?? []
cover = covers.first ?? ""


let status: Int = try object.value(for: "status")
isLiving = status == 2

roomId = try object.value(for: "id_str")

urls = (try? object.value(for: "stream_url.flv_pull_url")) ?? [:]
// let hlsUrls: [String: String] = try object.value(for: "stream_url.hls_pull_url_map")
}

func write(to yougetJson: YouGetJSON) -> YouGetJSON {
var json = yougetJson
json.title = title


urls.map {
($0.key, $0.value.replacingOccurrences(of: "http://", with: "https://"))
}.sorted { v0, v1 in
v0.0 < v1.0
}.enumerated().forEach {
var stream = Stream(url: $0.element.1)
stream.quality = 999 - $0.offset
json.streams[$0.element.0] = stream
}

return json
}
}


init(object: MarshaledObject) throws {
infos = try object.value(for: "data.data")

let name: String = try object.value(for: "data.user.nickname")
let avatars: [String] = try object.value(for: "data.user.avatar_thumb.url_list")
let avatar = avatars.first ?? ""

self.infos = infos.map {
var info = $0
if !info.isLiving {
info.name = name
info.avatar = avatar
}
return info
}

}
}


class DouYinURLProtocol: URLProtocol, URLSessionDelegate {
override class func canInit(with request: URLRequest) -> Bool {
Expand Down
20 changes: 16 additions & 4 deletions IINA+/Utils/VideoDecoder/KuaiShou.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ class KuaiShou: NSObject, SupportSiteProtocol {
$0.domain.contains("kuaishou")
}.forEach(HTTPCookieStorage.shared.deleteCookie)
}

func getAllWKCookies() -> Promise<[HTTPCookie]> {
Promise { resolver in
WKWebsiteDataStore.default().httpCookieStore.getAllCookies {
let cookies = $0.filter({ $0.domain.contains("kuaishou") })
resolver.fulfill(cookies)
}
}
}

func prepareCookies() -> Promise<()> {
.init { resolver in
Expand All @@ -81,17 +90,20 @@ class KuaiShou: NSObject, SupportSiteProtocol {
if url.absoluteString.contains("about") {
webView.load(.init(url: .init(string: "https://live.kuaishou.com/match")!))
} else {
self.webView?.evaluateJavaScript("document.cookie").done {

self.cookies = $0 as! String
self.getAllWKCookies().done {
self.cookies = $0.map {
$0.name + "=" + $0.value
}.joined(separator: "; ")
self.cookiesDate = Date()
self.saveToPrefs()


Log("KuaiShou cookies: \(self.cookies)")

self.webView = nil
self.webViewLoadingObserver?.invalidate()
self.webViewLoadingObserver = nil

resolver.fulfill(())
}.catch {
resolver.reject($0)
Expand Down

0 comments on commit a9a7d6a

Please sign in to comment.