From 169e4c5b899da45969a4924747553c0993888306 Mon Sep 17 00:00:00 2001 From: xjbeta Date: Thu, 29 Jun 2023 23:01:00 +0800 Subject: [PATCH 1/5] fix bangumi UA --- IINA+/Utils/VideoDecoder/Bilibili.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/IINA+/Utils/VideoDecoder/Bilibili.swift b/IINA+/Utils/VideoDecoder/Bilibili.swift index 6e63073b..a980fe1c 100644 --- a/IINA+/Utils/VideoDecoder/Bilibili.swift +++ b/IINA+/Utils/VideoDecoder/Bilibili.swift @@ -16,6 +16,8 @@ class Bilibili: NSObject, SupportSiteProtocol { let bilibiliUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Safari/605.1.15" + let bangumiUA = "Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.2.1" + func liveInfo(_ url: String) -> Promise { if SupportSites(url: url) == .bangumi { return getBilibiliHTMLDatas(url, isBangumi: true).map { @@ -93,7 +95,7 @@ class Bilibili: NSObject, SupportSiteProtocol { func getBilibiliHTMLDatas(_ url: String, isBangumi: Bool = false) -> Promise<((playInfoData: Data, initialStateData: Data, bangumiData: Data))> { let headers = HTTPHeaders( ["Referer": "https://www.bilibili.com/", - "User-Agent": isBangumi ? "Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.2.1" : bilibiliUA]) + "User-Agent": isBangumi ? bangumiUA : bilibiliUA]) return AF.request(url, headers: headers).responseString().map { @@ -209,7 +211,7 @@ class Bilibili: NSObject, SupportSiteProtocol { let headers = HTTPHeaders( ["Referer": "https://www.bilibili.com/", - "User-Agent": bilibiliUA]) + "User-Agent": isBangumi ? bangumiUA : bilibiliUA]) return AF.request(u, headers: headers).responseData().map { @@ -433,7 +435,7 @@ class Bilibili: NSObject, SupportSiteProtocol { func getBangumiList(_ url: String, initialStateData: Data? = nil) -> Promise<(BangumiList)> { - getBilibiliHTMLDatas(url).map { + getBilibiliHTMLDatas(url, isBangumi: true).map { let stateJson: JSONObject = try JSONParser.JSONObjectWithData($0.initialStateData) let state = try BangumiList(object: stateJson) return state From dfb7c6b0b34cd2855479b5483d7f4f157eb30336 Mon Sep 17 00:00:00 2001 From: xjbeta Date: Fri, 30 Jun 2023 00:20:56 +0800 Subject: [PATCH 2/5] load cookies with webview --- IINA+/Utils/VideoDecoder/KuaiShou.swift | 87 ++++++++++++++++++------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/IINA+/Utils/VideoDecoder/KuaiShou.swift b/IINA+/Utils/VideoDecoder/KuaiShou.swift index 64c46737..8f0ca1bd 100644 --- a/IINA+/Utils/VideoDecoder/KuaiShou.swift +++ b/IINA+/Utils/VideoDecoder/KuaiShou.swift @@ -11,6 +11,7 @@ import PromiseKit import Alamofire import PMKAlamofire import Marshal +import WebKit class KuaiShou: NSObject, SupportSiteProtocol { @@ -23,7 +24,9 @@ class KuaiShou: NSObject, SupportSiteProtocol { let initUA = 1000 let reloadTimes = 50 - var cookieStorage = [String: String]() + var cookies = "" + var prepareTask: Promise<()>? + var refererStorage = [String: String]() var uaStorage = [String: Int]() @@ -34,9 +37,18 @@ class KuaiShou: NSObject, SupportSiteProtocol { reinitLimit[eid] = nil } - return getInfo(url).map { - $0 - } + if cookies.count == 0 { + if prepareTask == nil { + prepareTask = prepareCookies().ensure { + self.prepareTask = nil + } + } + return prepareTask!.then { + self.getInfo(url).map { $0 } + } + } else { + return self.getInfo(url).map { $0 } + } } func decodeUrl(_ url: String) -> Promise { @@ -45,6 +57,45 @@ class KuaiShou: NSObject, SupportSiteProtocol { } } + var webView: WKWebView? + var webViewLoadingObserver: NSKeyValueObservation? + + + func prepareCookies() -> Promise<()> { + .init { resolver in + webView = WKWebView() + + + webViewLoadingObserver?.invalidate() + webViewLoadingObserver = webView?.observe(\.isLoading) { webView, _ in + guard !webView.isLoading else { return } + + self.webView = nil + self.webViewLoadingObserver?.invalidate() + self.webViewLoadingObserver = nil + + WKWebsiteDataStore.default().httpCookieStore + .getAllCookies().done { + + + let v = $0.filter { + $0.domain == ".chenzhongtech.com" + } + self.cookies = v.map { + $0.name + "=" + $0.value + }.joined(separator: ";") + + Log("KuaiShou cookies: \(self.cookies)") + resolver.fulfill(()) + }.catch { + resolver.reject($0) + } + } + + webView?.load(.init(url: .init(string: "https://livev.m.chenzhongtech.com/about/")!)) + } + } + func getEid(_ url: String) -> String? { guard let uc = URLComponents(string: url), uc.path.starts(with: "/u/") else { @@ -72,9 +123,9 @@ class KuaiShou: NSObject, SupportSiteProtocol { } let headers: HTTPHeaders = [ - .userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1 EID/\(eid).\(uaStorage[eid]!)"), + .userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"), .init(name: "Origin", value: "https://livev.m.chenzhongtech.com"), - .init(name: "Cookie", value: ""), + .init(name: "Cookie", value: self.cookies), .init(name: "Accept-Encoding", value: "gzip, deflate, br"), .init(name: "Accept-Language", value: "zh-Hans;q=1.0") ] @@ -86,17 +137,16 @@ class KuaiShou: NSObject, SupportSiteProtocol { } return { - guard cookieStorage[eid] != nil && refererStorage[eid] != nil else { + guard refererStorage[eid] != nil else { return loadReferer(eid, headers: headers) } + isInitRequest = true return Promise { resolver in - let cookie = self.cookieStorage[eid]! let ref = self.refererStorage[eid]! var headers = headers - headers.add(name: "Cookie", value: cookie) headers.add(name: "Referer", value: ref) resolver.fulfill(headers) @@ -113,7 +163,6 @@ class KuaiShou: NSObject, SupportSiteProtocol { self.uaStorage[eid] = i if (i % self.reloadTimes) == 0 { - self.cookieStorage[eid] = nil self.refererStorage[eid] = nil } }.then { re in @@ -158,27 +207,13 @@ class KuaiShou: NSObject, SupportSiteProtocol { throw KuaiShouError.nilReferer } - self.saveCookies(response, eid: eid) - var headers = headers - headers.add(name: "Cookie", value: self.cookieStorage[eid] ?? "") - self.refererStorage[eid] = ref headers.add(name: "Referer", value: ref) return headers } } - - func saveCookies(_ response: HTTPURLResponse?, eid: String) { - guard let res = response else { return } - let cookie = HTTPCookie.cookies(withResponseHeaderFields: res.headers.dictionary, for: .init(string: "chenzhongtech.com")!) - .map { - $0.name + "=" + $0.value - }.joined(separator: "; ") - - cookieStorage[eid] = cookie - } } struct KuaiShouInfo: Unmarshaling, LiveInfo { @@ -197,7 +232,9 @@ struct KuaiShouInfo: Unmarshaling, LiveInfo { init(object: Marshal.MarshaledObject) throws { name = try object.value(for: "liveStream.user.user_name") avatar = try object.value(for: "liveStream.user.headurl") - title = try object.value(for: "liveStream.caption") + let t: String? = try object.value(for: "liveStream.caption") + + title = try t ?? object.value(for: "shareInfo.shareSubTitle") cover = try object.value(for: "liveStream.coverUrl") isLiving = try object.value(for: "liveStream.living") playUrls = try object.value(for: "liveStream.multiResolutionHlsPlayUrls") From 47cd061c83b1880e34176d0d3302d258f0f5f5f7 Mon Sep 17 00:00:00 2001 From: xjbeta Date: Fri, 30 Jun 2023 00:42:51 +0800 Subject: [PATCH 3/5] reinit limit --- IINA+/Utils/VideoDecoder/KuaiShou.swift | 48 ++++++------------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/IINA+/Utils/VideoDecoder/KuaiShou.swift b/IINA+/Utils/VideoDecoder/KuaiShou.swift index 8f0ca1bd..7458f8c1 100644 --- a/IINA+/Utils/VideoDecoder/KuaiShou.swift +++ b/IINA+/Utils/VideoDecoder/KuaiShou.swift @@ -21,22 +21,16 @@ class KuaiShou: NSObject, SupportSiteProtocol { case apiLimited } - let initUA = 1000 - let reloadTimes = 50 - var cookies = "" var prepareTask: Promise<()>? + var webView: WKWebView? + var webViewLoadingObserver: NSKeyValueObservation? - var refererStorage = [String: String]() - var uaStorage = [String: Int]() + var refererStorage = [String: String]() var reinitLimit = [String: Int]() func liveInfo(_ url: String) -> Promise { - if let eid = getEid(url) { - reinitLimit[eid] = nil - } - if cookies.count == 0 { if prepareTask == nil { prepareTask = prepareCookies().ensure { @@ -57,10 +51,7 @@ class KuaiShou: NSObject, SupportSiteProtocol { } } - var webView: WKWebView? - var webViewLoadingObserver: NSKeyValueObservation? - - + func prepareCookies() -> Promise<()> { .init { resolver in webView = WKWebView() @@ -116,12 +107,7 @@ class KuaiShou: NSObject, SupportSiteProtocol { "shareMethod": "card", "source": 6 ] - - - if uaStorage[eid] == nil { - uaStorage[eid] = initUA - } - + let headers: HTTPHeaders = [ .userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"), .init(name: "Origin", value: "https://livev.m.chenzhongtech.com"), @@ -131,17 +117,13 @@ class KuaiShou: NSObject, SupportSiteProtocol { ] var isInitRequest = false - - if reinitLimit[eid] == nil { - reinitLimit[eid] = -1 - } - + return { guard refererStorage[eid] != nil else { + isInitRequest = true + self.reinitLimit[eid] = 0 return loadReferer(eid, headers: headers) } - - isInitRequest = true return Promise { resolver in let ref = self.refererStorage[eid]! @@ -158,13 +140,6 @@ class KuaiShou: NSObject, SupportSiteProtocol { parameters: pars, encoding: JSONEncoding.default, headers: $0).responseData() - }.ensure { - let i = (self.uaStorage[eid] ?? self.initUA) + 1 - self.uaStorage[eid] = i - - if (i % self.reloadTimes) == 0 { - self.refererStorage[eid] = nil - } }.then { re in Promise { resolver in let obj = try JSONParser.JSONObjectWithData(re.data) @@ -175,16 +150,13 @@ class KuaiShou: NSObject, SupportSiteProtocol { resolver.fulfill(info) } else if result == 2, isInitRequest, - let limit = self.reinitLimit[eid], - limit < 2 { + self.reinitLimit[eid] == 0 { Log("KuaiShou API Limited, try to reinit \(eid)") - - self.reinitLimit[eid] = limit + 1 + self.reinitLimit[eid] = 1 after(seconds: 1).then { self.getInfo(url) }.done { - self.reinitLimit[eid] = -1 resolver.fulfill($0) }.catch { resolver.reject($0) From 64606adee06ba4883a64a10331ec65800705ce42 Mon Sep 17 00:00:00 2001 From: xjbeta Date: Thu, 6 Jul 2023 14:44:17 +0800 Subject: [PATCH 4/5] Cookies date --- IINA+/Utils/VideoDecoder/KuaiShou.swift | 95 ++++++++++++++++++------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/IINA+/Utils/VideoDecoder/KuaiShou.swift b/IINA+/Utils/VideoDecoder/KuaiShou.swift index 7458f8c1..36e51f59 100644 --- a/IINA+/Utils/VideoDecoder/KuaiShou.swift +++ b/IINA+/Utils/VideoDecoder/KuaiShou.swift @@ -19,9 +19,13 @@ class KuaiShou: NSObject, SupportSiteProtocol { case invalidLink case nilReferer case apiLimited + case unknown } + var cookies = "" + var cookiesDate: Date? + var prepareTask: Promise<()>? var webView: WKWebView? var webViewLoadingObserver: NSKeyValueObservation? @@ -31,7 +35,8 @@ class KuaiShou: NSObject, SupportSiteProtocol { var reinitLimit = [String: Int]() func liveInfo(_ url: String) -> Promise { - if cookies.count == 0 { + if cookies.count == 0, + cookiesDate == nil { if prepareTask == nil { prepareTask = prepareCookies().ensure { self.prepareTask = nil @@ -46,40 +51,42 @@ class KuaiShou: NSObject, SupportSiteProtocol { } func decodeUrl(_ url: String) -> Promise { - getInfo(url).map { - $0.write(to: YouGetJSON(rawUrl: url)) - } + getInfo(url).map { + $0.write(to: YouGetJSON(rawUrl: url)) + } } + func deleteCookies() { + HTTPCookieStorage.shared.cookies?.filter { + $0.domain.contains("kuaishou") + }.forEach(HTTPCookieStorage.shared.deleteCookie) + } func prepareCookies() -> Promise<()> { .init { resolver in webView = WKWebView() - + deleteCookies() webViewLoadingObserver?.invalidate() webViewLoadingObserver = webView?.observe(\.isLoading) { webView, _ in - guard !webView.isLoading else { return } - - self.webView = nil - self.webViewLoadingObserver?.invalidate() - self.webViewLoadingObserver = nil + guard !webView.isLoading, let url = webView.url else { return } - WKWebsiteDataStore.default().httpCookieStore - .getAllCookies().done { - - - let v = $0.filter { - $0.domain == ".chenzhongtech.com" + if url.absoluteString.contains("about") { + webView.load(.init(url: .init(string: "https://live.kuaishou.com")!)) + } else { + self.webView?.evaluateJavaScript("document.cookie").done { + + self.cookies = $0 as! String + self.cookiesDate = Date() + Log("KuaiShou cookies: \(self.cookies)") + + self.webView = nil + self.webViewLoadingObserver?.invalidate() + self.webViewLoadingObserver = nil + resolver.fulfill(()) + }.catch { + resolver.reject($0) } - self.cookies = v.map { - $0.name + "=" + $0.value - }.joined(separator: ";") - - Log("KuaiShou cookies: \(self.cookies)") - resolver.fulfill(()) - }.catch { - resolver.reject($0) } } @@ -145,25 +152,59 @@ class KuaiShou: NSObject, SupportSiteProtocol { let obj = try JSONParser.JSONObjectWithData(re.data) let result: Int = try obj.value(for: "result") + let limit = self.reinitLimit[eid] ?? 0 + let date = self.cookiesDate ?? Date() + if result == 1 { let info = try KuaiShouInfo(object: obj) resolver.fulfill(info) } else if result == 2, isInitRequest, - self.reinitLimit[eid] == 0 { + + limit < 3, + + let date = self.cookiesDate, + date.timeIntervalSinceNow > -30 { Log("KuaiShou API Limited, try to reinit \(eid)") - self.reinitLimit[eid] = 1 + self.reinitLimit[eid] = limit + 1 after(seconds: 1).then { self.getInfo(url) }.done { + self.reinitLimit[eid] = 0 resolver.fulfill($0) }.catch { resolver.reject($0) } - } else { + } else if limit < -15 || (limit <= -2 && + date.timeIntervalSinceNow < -300) { + + Log("KuaiShou API Limited, reload cookies") + self.reinitLimit[eid] = nil + + if self.prepareTask == nil { + self.cookies = "" + self.cookiesDate = nil + + self.prepareTask = self.prepareCookies().ensure { + self.prepareTask = nil + } + } + + self.prepareTask!.then { + self.getInfo(url) + }.done { + resolver.fulfill($0) + }.catch { + resolver.reject($0) + } + } else if result == 2 { + self.reinitLimit[eid] = limit - 1 Log("KuaiShou API Limited, result \(result), \(eid)") resolver.reject(KuaiShouError.apiLimited) + } else { + Log("KuaiShou API failed, result \(result), \(eid)") + resolver.reject(KuaiShouError.unknown) } } } From e5c1714fd053bbe64a126a3541c17fec2fcaabd4 Mon Sep 17 00:00:00 2001 From: xjbeta Date: Thu, 6 Jul 2023 17:52:41 +0800 Subject: [PATCH 5/5] Bump Version to 0.6.27. --- IINA+.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IINA+.xcodeproj/project.pbxproj b/IINA+.xcodeproj/project.pbxproj index e9fbf348..3a109390 100644 --- a/IINA+.xcodeproj/project.pbxproj +++ b/IINA+.xcodeproj/project.pbxproj @@ -985,7 +985,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.6.26; + MARKETING_VERSION = 0.6.27; PRODUCT_BUNDLE_IDENTIFIER = "com.xjbeta.iina-plus"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1010,7 +1010,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.6.26; + MARKETING_VERSION = 0.6.27; PRODUCT_BUNDLE_IDENTIFIER = "com.xjbeta.iina-plus"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "";