mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
improved conditional proxying
We don't test every single stream anymore. If we get an 200, 206 or 403 we immediately stop testing streams. Unknown streams are also skipped. This speeds up starting playback, since we don'T have to wait for the network anymore.
This commit is contained in:
parent
d6cfadab9a
commit
30cdaf88e1
@ -56,82 +56,101 @@ extension PlayerModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func streamsWithInstance(instance _: Instance, streams: [Stream], completion: @escaping ([Stream]) -> Void) {
|
func streamsWithInstance(instance: Instance, streams: [Stream], completion: @escaping ([Stream]) -> Void) {
|
||||||
// Queue for stream processing
|
// Queue for stream processing
|
||||||
let streamProcessingQueue = DispatchQueue(label: "stream.yattee.streamProcessing.Queue", qos: .userInitiated)
|
let streamProcessingQueue = DispatchQueue(label: "stream.yattee.streamProcessing.Queue")
|
||||||
// Queue for accessing the processedStreams array
|
// Queue for accessing the processedStreams array
|
||||||
let processedStreamsQueue = DispatchQueue(label: "stream.yattee.processedStreams.Queue")
|
let processedStreamsQueue = DispatchQueue(label: "stream.yattee.processedStreams.Queue")
|
||||||
// DispatchGroup for managing multiple tasks
|
// DispatchGroup for managing multiple tasks
|
||||||
let streamProcessingGroup = DispatchGroup()
|
let streamProcessingGroup = DispatchGroup()
|
||||||
|
|
||||||
var processedStreams = [Stream]()
|
var processedStreams = [Stream]()
|
||||||
|
let instance = instance
|
||||||
|
|
||||||
|
var hasForbiddenAsset = false
|
||||||
|
var hasAllowedAsset = false
|
||||||
|
|
||||||
for stream in streams {
|
for stream in streams {
|
||||||
streamProcessingQueue.async(group: streamProcessingGroup) {
|
streamProcessingQueue.async(group: streamProcessingGroup) {
|
||||||
let forbiddenAssetTestGroup = DispatchGroup()
|
let forbiddenAssetTestGroup = DispatchGroup()
|
||||||
var hasForbiddenAsset = false
|
if !hasAllowedAsset, !hasForbiddenAsset, !instance.proxiesVideos, stream.format != Stream.Format.unknown {
|
||||||
|
let (nonHLSAssets, hlsURLs) = self.getAssets(from: [stream])
|
||||||
|
if let firstStream = nonHLSAssets.first {
|
||||||
|
let asset = firstStream.0
|
||||||
|
let url = firstStream.1
|
||||||
|
let requestRange = firstStream.2
|
||||||
|
|
||||||
let (nonHLSAssets, hlsURLs) = self.getAssets(from: [stream])
|
|
||||||
|
|
||||||
if let randomStream = nonHLSAssets.randomElement() {
|
|
||||||
let instance = randomStream.0
|
|
||||||
let asset = randomStream.1
|
|
||||||
let url = randomStream.2
|
|
||||||
let requestRange = randomStream.3
|
|
||||||
|
|
||||||
// swiftlint:disable:next shorthand_optional_binding
|
|
||||||
if let asset = asset, let instance = instance, !instance.proxiesVideos {
|
|
||||||
if instance.app == .invidious {
|
if instance.app == .invidious {
|
||||||
self.testAsset(url: url, range: requestRange, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { isForbidden in
|
self.testAsset(url: url, range: requestRange, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { status in
|
||||||
hasForbiddenAsset = isForbidden
|
switch status {
|
||||||
|
case HTTPStatus.Forbidden:
|
||||||
|
hasForbiddenAsset = true
|
||||||
|
case HTTPStatus.PartialContent:
|
||||||
|
hasAllowedAsset = true
|
||||||
|
case HTTPStatus.OK:
|
||||||
|
hasAllowedAsset = true
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if instance.app == .piped {
|
} else if instance.app == .piped {
|
||||||
self.testPipedAssets(asset: asset, requestRange: requestRange, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { isForbidden in
|
self.testPipedAssets(asset: asset!, requestRange: requestRange, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { status in
|
||||||
hasForbiddenAsset = isForbidden
|
switch status {
|
||||||
|
case HTTPStatus.Forbidden:
|
||||||
|
hasForbiddenAsset = true
|
||||||
|
case HTTPStatus.PartialContent:
|
||||||
|
hasAllowedAsset = true
|
||||||
|
case HTTPStatus.OK:
|
||||||
|
hasAllowedAsset = true
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if let firstHLS = hlsURLs.first {
|
||||||
} else if let randomHLS = hlsURLs.randomElement() {
|
let asset = AVURLAsset(url: firstHLS)
|
||||||
let instance = randomHLS.0
|
if instance.app == .piped {
|
||||||
let asset = AVURLAsset(url: randomHLS.1)
|
self.testPipedAssets(asset: asset, requestRange: nil, isHLS: true, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { status in
|
||||||
|
switch status {
|
||||||
if instance?.app == .piped {
|
case HTTPStatus.Forbidden:
|
||||||
self.testPipedAssets(asset: asset, requestRange: nil, isHLS: true, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { isForbidden in
|
hasForbiddenAsset = true
|
||||||
hasForbiddenAsset = isForbidden
|
case HTTPStatus.PartialContent:
|
||||||
|
hasAllowedAsset = true
|
||||||
|
case HTTPStatus.OK:
|
||||||
|
hasAllowedAsset = true
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forbiddenAssetTestGroup.wait()
|
forbiddenAssetTestGroup.wait()
|
||||||
|
|
||||||
// Post-processing code
|
// Post-processing code
|
||||||
if let instance = stream.instance {
|
if instance.app == .invidious, hasForbiddenAsset || instance.proxiesVideos {
|
||||||
if instance.app == .invidious {
|
if let audio = stream.audioAsset {
|
||||||
if hasForbiddenAsset || instance.proxiesVideos {
|
stream.audioAsset = InvidiousAPI.proxiedAsset(instance: instance, asset: audio)
|
||||||
if let audio = stream.audioAsset {
|
}
|
||||||
stream.audioAsset = InvidiousAPI.proxiedAsset(instance: instance, asset: audio)
|
if let video = stream.videoAsset {
|
||||||
}
|
stream.videoAsset = InvidiousAPI.proxiedAsset(instance: instance, asset: video)
|
||||||
if let video = stream.videoAsset {
|
}
|
||||||
stream.videoAsset = InvidiousAPI.proxiedAsset(instance: instance, asset: video)
|
} else if instance.app == .piped, !instance.proxiesVideos, !hasForbiddenAsset {
|
||||||
|
if let hlsURL = stream.hlsURL {
|
||||||
|
PipedAPI.nonProxiedAsset(url: hlsURL) { possibleNonProxiedURL in
|
||||||
|
if let nonProxiedURL = possibleNonProxiedURL {
|
||||||
|
stream.hlsURL = nonProxiedURL.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if instance.app == .piped, !instance.proxiesVideos, !hasForbiddenAsset {
|
} else {
|
||||||
if let hlsURL = stream.hlsURL {
|
if let audio = stream.audioAsset {
|
||||||
PipedAPI.nonProxiedAsset(url: hlsURL) { possibleNonProxiedURL in
|
PipedAPI.nonProxiedAsset(asset: audio) { nonProxiedAudioAsset in
|
||||||
if let nonProxiedURL = possibleNonProxiedURL {
|
stream.audioAsset = nonProxiedAudioAsset
|
||||||
stream.hlsURL = nonProxiedURL.url
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if let audio = stream.audioAsset {
|
if let video = stream.videoAsset {
|
||||||
PipedAPI.nonProxiedAsset(asset: audio) { nonProxiedAudioAsset in
|
PipedAPI.nonProxiedAsset(asset: video) { nonProxiedVideoAsset in
|
||||||
stream.audioAsset = nonProxiedAudioAsset
|
stream.videoAsset = nonProxiedVideoAsset
|
||||||
}
|
|
||||||
}
|
|
||||||
if let video = stream.videoAsset {
|
|
||||||
PipedAPI.nonProxiedAsset(asset: video) { nonProxiedVideoAsset in
|
|
||||||
stream.videoAsset = nonProxiedVideoAsset
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,21 +171,21 @@ extension PlayerModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getAssets(from streams: [Stream]) -> (nonHLSAssets: [(Instance?, AVURLAsset?, URL, String?)], hlsURLs: [(Instance?, URL)]) {
|
private func getAssets(from streams: [Stream]) -> (nonHLSAssets: [(AVURLAsset?, URL, String?)], hlsURLs: [URL]) {
|
||||||
var nonHLSAssets = [(Instance?, AVURLAsset?, URL, String?)]()
|
var nonHLSAssets = [(AVURLAsset?, URL, String?)]()
|
||||||
var hlsURLs = [(Instance?, URL)]()
|
var hlsURLs = [URL]()
|
||||||
|
|
||||||
for stream in streams {
|
for stream in streams {
|
||||||
if stream.isHLS {
|
if stream.isHLS {
|
||||||
if let url = stream.hlsURL?.url {
|
if let url = stream.hlsURL?.url {
|
||||||
hlsURLs.append((stream.instance, url))
|
hlsURLs.append(url)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let asset = stream.audioAsset {
|
if let asset = stream.audioAsset {
|
||||||
nonHLSAssets.append((stream.instance, asset, asset.url, stream.requestRange))
|
nonHLSAssets.append((asset, asset.url, stream.requestRange))
|
||||||
}
|
}
|
||||||
if let asset = stream.videoAsset {
|
if let asset = stream.videoAsset {
|
||||||
nonHLSAssets.append((stream.instance, asset, asset.url, stream.requestRange))
|
nonHLSAssets.append((asset, asset.url, stream.requestRange))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,24 +193,24 @@ extension PlayerModel {
|
|||||||
return (nonHLSAssets, hlsURLs)
|
return (nonHLSAssets, hlsURLs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func testAsset(url: URL, range: String?, isHLS: Bool, forbiddenAssetTestGroup: DispatchGroup, completion: @escaping (Bool) -> Void) {
|
private func testAsset(url: URL, range: String?, isHLS: Bool, forbiddenAssetTestGroup: DispatchGroup, completion: @escaping (Int) -> Void) {
|
||||||
// In case the range is nil, generate a random one.
|
// In case the range is nil, generate a random one.
|
||||||
let randomEnd = Int.random(in: 200 ... 800)
|
let randomEnd = Int.random(in: 200 ... 800)
|
||||||
let requestRange = range ?? "0-\(randomEnd)"
|
let requestRange = range ?? "0-\(randomEnd)"
|
||||||
|
|
||||||
forbiddenAssetTestGroup.enter()
|
forbiddenAssetTestGroup.enter()
|
||||||
URLTester.testURLResponse(url: url, range: requestRange, isHLS: isHLS) { statusCode in
|
URLTester.testURLResponse(url: url, range: requestRange, isHLS: isHLS) { statusCode in
|
||||||
completion(statusCode == HTTPStatus.Forbidden)
|
completion(statusCode)
|
||||||
forbiddenAssetTestGroup.leave()
|
forbiddenAssetTestGroup.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func testPipedAssets(asset: AVURLAsset, requestRange: String?, isHLS: Bool, forbiddenAssetTestGroup: DispatchGroup, completion: @escaping (Bool) -> Void) {
|
private func testPipedAssets(asset: AVURLAsset, requestRange: String?, isHLS: Bool, forbiddenAssetTestGroup: DispatchGroup, completion: @escaping (Int) -> Void) {
|
||||||
PipedAPI.nonProxiedAsset(asset: asset) { possibleNonProxiedAsset in
|
PipedAPI.nonProxiedAsset(asset: asset) { possibleNonProxiedAsset in
|
||||||
if let nonProxiedAsset = possibleNonProxiedAsset {
|
if let nonProxiedAsset = possibleNonProxiedAsset {
|
||||||
self.testAsset(url: nonProxiedAsset.url, range: requestRange, isHLS: isHLS, forbiddenAssetTestGroup: forbiddenAssetTestGroup, completion: completion)
|
self.testAsset(url: nonProxiedAsset.url, range: requestRange, isHLS: isHLS, forbiddenAssetTestGroup: forbiddenAssetTestGroup, completion: completion)
|
||||||
} else {
|
} else {
|
||||||
completion(false)
|
completion(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user