mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 05:23:41 +00:00
Add HTTP Response StatusCode List and fix potential race condition
This commit is contained in:
parent
6eba2a45c8
commit
c3e4c074d6
@ -57,41 +57,54 @@ extension PlayerModel {
|
||||
}
|
||||
|
||||
func streamsWithInstance(instance _: Instance, streams: [Stream], completion: @escaping ([Stream]) -> Void) {
|
||||
let forbiddenAssetTestGroup = DispatchGroup()
|
||||
var hasForbiddenAsset = false
|
||||
// Queue for stream processing
|
||||
let streamProcessingQueue = DispatchQueue(label: "stream.yattee.streamProcessing.Queue", qos: .userInitiated)
|
||||
// Queue for accessing the processedStreams array
|
||||
let processedStreamsQueue = DispatchQueue(label: "stream.yattee.processedStreams.Queue")
|
||||
// DispatchGroup for managing multiple tasks
|
||||
let streamProcessingGroup = DispatchGroup()
|
||||
|
||||
let (nonHLSAssets, hlsURLs) = getAssets(from: streams)
|
||||
var processedStreams = [Stream]()
|
||||
|
||||
if let randomStream = nonHLSAssets.randomElement() {
|
||||
let instance = randomStream.0
|
||||
let asset = randomStream.1
|
||||
let url = randomStream.2
|
||||
let requestRange = randomStream.3
|
||||
for stream in streams {
|
||||
streamProcessingQueue.async(group: streamProcessingGroup) {
|
||||
let forbiddenAssetTestGroup = DispatchGroup()
|
||||
var hasForbiddenAsset = false
|
||||
|
||||
if let asset = asset, let instance = instance, !instance.proxiesVideos {
|
||||
if instance.app == .invidious {
|
||||
testAsset(url: url, range: requestRange, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { isForbidden in
|
||||
hasForbiddenAsset = isForbidden
|
||||
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 {
|
||||
self.testAsset(url: url, range: requestRange, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { isForbidden in
|
||||
hasForbiddenAsset = isForbidden
|
||||
}
|
||||
} else if instance.app == .piped {
|
||||
self.testPipedAssets(asset: asset, requestRange: requestRange, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { isForbidden in
|
||||
hasForbiddenAsset = isForbidden
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let randomHLS = hlsURLs.randomElement() {
|
||||
let instance = randomHLS.0
|
||||
let asset = AVURLAsset(url: randomHLS.1)
|
||||
|
||||
if instance?.app == .piped {
|
||||
self.testPipedAssets(asset: asset, requestRange: nil, isHLS: true, forbiddenAssetTestGroup: forbiddenAssetTestGroup) { isForbidden in
|
||||
hasForbiddenAsset = isForbidden
|
||||
}
|
||||
}
|
||||
} else if instance.app == .piped {
|
||||
testPipedAssets(asset: asset, requestRange: requestRange!, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup, completion: { isForbidden in
|
||||
hasForbiddenAsset = isForbidden
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if let randomHLS = hlsURLs.randomElement() {
|
||||
let instance = randomHLS.0
|
||||
let asset = AVURLAsset(url: randomHLS.1)
|
||||
|
||||
if instance?.app == .piped {
|
||||
testPipedAssets(asset: asset, requestRange: nil, isHLS: false, forbiddenAssetTestGroup: forbiddenAssetTestGroup, completion: { isForbidden in
|
||||
hasForbiddenAsset = isForbidden
|
||||
})
|
||||
}
|
||||
}
|
||||
forbiddenAssetTestGroup.wait()
|
||||
|
||||
forbiddenAssetTestGroup.notify(queue: .main) {
|
||||
let processedStreams = streams.map { stream -> Stream in
|
||||
// Post-processing code
|
||||
if let instance = stream.instance {
|
||||
if instance.app == .invidious {
|
||||
if hasForbiddenAsset || instance.proxiesVideos {
|
||||
@ -104,35 +117,36 @@ extension PlayerModel {
|
||||
}
|
||||
} else if instance.app == .piped, !instance.proxiesVideos, !hasForbiddenAsset {
|
||||
if let hlsURL = stream.hlsURL {
|
||||
forbiddenAssetTestGroup.enter()
|
||||
PipedAPI.nonProxiedAsset(url: hlsURL) { nonProxiedURL in
|
||||
if let nonProxiedURL = nonProxiedURL {
|
||||
PipedAPI.nonProxiedAsset(url: hlsURL) { possibleNonProxiedURL in
|
||||
if let nonProxiedURL = possibleNonProxiedURL {
|
||||
stream.hlsURL = nonProxiedURL.url
|
||||
}
|
||||
forbiddenAssetTestGroup.leave()
|
||||
}
|
||||
} else {
|
||||
if let audio = stream.audioAsset {
|
||||
forbiddenAssetTestGroup.enter()
|
||||
PipedAPI.nonProxiedAsset(asset: audio) { nonProxiedAudioAsset in
|
||||
stream.audioAsset = nonProxiedAudioAsset
|
||||
forbiddenAssetTestGroup.leave()
|
||||
}
|
||||
}
|
||||
if let video = stream.videoAsset {
|
||||
forbiddenAssetTestGroup.enter()
|
||||
PipedAPI.nonProxiedAsset(asset: video) { nonProxiedVideoAsset in
|
||||
stream.videoAsset = nonProxiedVideoAsset
|
||||
forbiddenAssetTestGroup.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stream
|
||||
}
|
||||
|
||||
forbiddenAssetTestGroup.notify(queue: .main) {
|
||||
// Append to processedStreams within the processedStreamsQueue
|
||||
processedStreamsQueue.sync {
|
||||
processedStreams.append(stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
streamProcessingGroup.notify(queue: .main) {
|
||||
// Access and pass processedStreams within the processedStreamsQueue block
|
||||
processedStreamsQueue.sync {
|
||||
completion(processedStreams)
|
||||
}
|
||||
}
|
||||
@ -161,21 +175,23 @@ extension PlayerModel {
|
||||
}
|
||||
|
||||
private func testAsset(url: URL, range: String?, isHLS: Bool, forbiddenAssetTestGroup: DispatchGroup, completion: @escaping (Bool) -> Void) {
|
||||
// In case the range is nil, generate a random one.
|
||||
let randomEnd = Int.random(in: 200 ... 800)
|
||||
let requestRange = range ?? "0-\(randomEnd)"
|
||||
let HTTPStatusForbidden = 403
|
||||
|
||||
forbiddenAssetTestGroup.enter()
|
||||
URLTester.testURLResponse(url: url, range: requestRange, isHLS: isHLS) { statusCode in
|
||||
completion(statusCode == HTTPStatusForbidden)
|
||||
completion(statusCode == HTTPStatus.Forbidden)
|
||||
forbiddenAssetTestGroup.leave()
|
||||
}
|
||||
}
|
||||
|
||||
private func testPipedAssets(asset: AVURLAsset, requestRange: String?, isHLS: Bool, forbiddenAssetTestGroup: DispatchGroup, completion: @escaping (Bool) -> Void) {
|
||||
PipedAPI.nonProxiedAsset(asset: asset) { nonProxiedAsset in
|
||||
if let nonProxiedAsset = nonProxiedAsset {
|
||||
PipedAPI.nonProxiedAsset(asset: asset) { possibleNonProxiedAsset in
|
||||
if let nonProxiedAsset = possibleNonProxiedAsset {
|
||||
self.testAsset(url: nonProxiedAsset.url, range: requestRange, isHLS: isHLS, forbiddenAssetTestGroup: forbiddenAssetTestGroup, completion: completion)
|
||||
} else {
|
||||
completion(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
81
Shared/HTTPStatus.swift
Normal file
81
Shared/HTTPStatus.swift
Normal file
@ -0,0 +1,81 @@
|
||||
// HTTP response status codes
|
||||
|
||||
enum HTTPStatus {
|
||||
// Informational responses (100 - 199)
|
||||
|
||||
static let Continue = 100
|
||||
static let SwitchingProtocols = 101
|
||||
static let Processing = 102
|
||||
static let EarlyHints = 103
|
||||
|
||||
// Successful responses (200 - 299)
|
||||
|
||||
static let OK = 200
|
||||
static let Created = 201
|
||||
static let Accepted = 202
|
||||
static let NonAuthoritativeInformation = 203
|
||||
static let NoContent = 204
|
||||
static let ResetContent = 205
|
||||
static let PartialContent = 206
|
||||
static let MultiStatus = 207
|
||||
static let AlreadyReported = 208
|
||||
static let IMUsed = 226
|
||||
|
||||
// Redirection messages (300 - 399)
|
||||
|
||||
static let MultipleChoices = 300
|
||||
static let MovedPermanently = 301
|
||||
static let Found = 302
|
||||
static let SeeOther = 303
|
||||
static let NotModified = 304
|
||||
static let UseProxy = 305
|
||||
static let SwitchProxy = 306
|
||||
static let TemporaryRedirect = 307
|
||||
static let PermanentRedirect = 308
|
||||
|
||||
// Client error responses (400 - 499)
|
||||
|
||||
static let BadRequest = 400
|
||||
static let Unauthorized = 401
|
||||
static let PaymentRequired = 402
|
||||
static let Forbidden = 403
|
||||
static let NotFound = 404
|
||||
static let MethodNotAllowed = 405
|
||||
static let NotAcceptable = 406
|
||||
static let ProxyAuthenticationRequired = 407
|
||||
static let RequestTimeout = 408
|
||||
static let Conflict = 409
|
||||
static let Gone = 410
|
||||
static let LengthRequired = 411
|
||||
static let PreconditionFailed = 412
|
||||
static let PayloadTooLarge = 413
|
||||
static let URITooLong = 414
|
||||
static let UnsupportedMediaType = 415
|
||||
static let RangeNotSatisfiable = 416
|
||||
static let ExpectationFailed = 417
|
||||
static let IAmATeapot = 418
|
||||
static let MisdirectedRequest = 421
|
||||
static let UnprocessableEntity = 422
|
||||
static let Locked = 423
|
||||
static let FailedDependency = 424
|
||||
static let TooEarly = 425
|
||||
static let UpgradeRequired = 426
|
||||
static let PreconditionRequired = 428
|
||||
static let TooManyRequests = 429
|
||||
static let RequestHeaderFieldsTooLarge = 431
|
||||
static let UnavailableForLegalReasons = 451
|
||||
|
||||
// Server error responses (500 - 599)
|
||||
|
||||
static let InternalServerError = 500
|
||||
static let NotImplemented = 501
|
||||
static let BadGateway = 502
|
||||
static let ServiceUnavailable = 503
|
||||
static let GatewayTimeout = 504
|
||||
static let HTTPVersionNotSupported = 505
|
||||
static let VariantAlsoNegotiates = 506
|
||||
static let InsufficientStorage = 507
|
||||
static let LoopDetected = 508
|
||||
static let NotExtended = 510
|
||||
static let NetworkAuthenticationRequired = 511
|
||||
}
|
@ -5,7 +5,6 @@ enum URLTester {
|
||||
private static let hlsMediaPrefix = "#EXT-X-MEDIA:"
|
||||
private static let hlsInfPrefix = "#EXTINF:"
|
||||
private static let uriRegex = "(?<=URI=\")(.*?)(?=\")"
|
||||
private static let HTTPStatusForbidden = 403
|
||||
|
||||
static func testURLResponse(url: URL, range: String, isHLS: Bool, completion: @escaping (Int) -> Void) {
|
||||
if isHLS {
|
||||
@ -24,7 +23,7 @@ enum URLTester {
|
||||
|
||||
var dataTask: URLSessionDataTask?
|
||||
dataTask = URLSession.shared.dataTask(with: request) { _, response, _ in
|
||||
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? HTTPStatusForbidden
|
||||
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? HTTPStatus.Forbidden
|
||||
Logger(label: "stream.yattee.httpRequest").info("URL: \(url) | Status Code: \(statusCode)")
|
||||
completion(statusCode, dataTask)
|
||||
}
|
||||
@ -37,6 +36,8 @@ enum URLTester {
|
||||
httpRequest(url: url, range: range) { statusCode, _ in
|
||||
completion(statusCode)
|
||||
}
|
||||
} else {
|
||||
completion(HTTPStatus.NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,10 +65,15 @@ enum URLTester {
|
||||
|
||||
private static func parseHLSManifest(manifestUrl: URL, completion: @escaping ([URL]) -> Void) {
|
||||
URLSession.shared.dataTask(with: manifestUrl) { data, _, _ in
|
||||
guard let data = data,
|
||||
let manifest = String(data: data, encoding: .utf8),
|
||||
!manifest.isEmpty
|
||||
else {
|
||||
// swiftlint:disable:next shorthand_optional_binding
|
||||
guard let data = data else {
|
||||
Logger(label: "stream.yattee.httpRequest").error("Data is nil")
|
||||
completion([])
|
||||
return
|
||||
}
|
||||
|
||||
// swiftlint:disable:next non_optional_string_data_conversion
|
||||
guard let manifest = String(data: data, encoding: .utf8), !manifest.isEmpty else {
|
||||
Logger(label: "stream.yattee.httpRequest").error("Cannot read or empty HLS manifest")
|
||||
completion([])
|
||||
return
|
||||
|
@ -1070,6 +1070,9 @@
|
||||
37FFC440272734C3009FFD26 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FFC43F272734C3009FFD26 /* Throttle.swift */; };
|
||||
37FFC441272734C3009FFD26 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FFC43F272734C3009FFD26 /* Throttle.swift */; };
|
||||
37FFC442272734C3009FFD26 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FFC43F272734C3009FFD26 /* Throttle.swift */; };
|
||||
E25028B02BF790F5002CB9FC /* HTTPStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */; };
|
||||
E25028B12BF790F5002CB9FC /* HTTPStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */; };
|
||||
E25028B22BF790F5002CB9FC /* HTTPStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */; };
|
||||
E258F38A2BF61BD2005B8C28 /* URLTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = E258F3892BF61BD2005B8C28 /* URLTester.swift */; };
|
||||
E258F38B2BF61BD2005B8C28 /* URLTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = E258F3892BF61BD2005B8C28 /* URLTester.swift */; };
|
||||
E258F38C2BF61BD2005B8C28 /* URLTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = E258F3892BF61BD2005B8C28 /* URLTester.swift */; };
|
||||
@ -1542,6 +1545,7 @@
|
||||
3DA101AD287C30F50027D920 /* DEVELOPMENT_TEAM.template.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DEVELOPMENT_TEAM.template.xcconfig; sourceTree = "<group>"; };
|
||||
3DA101AE287C30F50027D920 /* DEVELOPMENT_TEAM.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DEVELOPMENT_TEAM.xcconfig; sourceTree = "<group>"; };
|
||||
3DA101AF287C30F50027D920 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
|
||||
E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStatus.swift; sourceTree = "<group>"; };
|
||||
E258F3892BF61BD2005B8C28 /* URLTester.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLTester.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -2290,6 +2294,7 @@
|
||||
372915E52687E3B900F5A35B /* Defaults.swift */,
|
||||
37D2E0D328B67EFC00F64D52 /* Delay.swift */,
|
||||
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */,
|
||||
E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */,
|
||||
375B537828DF6CBB004C1D19 /* Localizable.strings */,
|
||||
3729037D2739E47400EA99F6 /* MenuCommands.swift */,
|
||||
37B7958F2771DAE0001CF27B /* OpenURLHandler.swift */,
|
||||
@ -3120,6 +3125,7 @@
|
||||
37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */,
|
||||
37F7D82C289EB05F00E2B3D0 /* SettingsPickerModifier.swift in Sources */,
|
||||
375EC95D289EEEE000751258 /* QualityProfile.swift in Sources */,
|
||||
E25028B02BF790F5002CB9FC /* HTTPStatus.swift in Sources */,
|
||||
3773B8102ADC076800B5FEF3 /* ScrollViewMatcher.swift in Sources */,
|
||||
371B7E662759786B00D21217 /* Comment+Fixtures.swift in Sources */,
|
||||
37BE0BD326A1D4780092E2DB /* AppleAVPlayerView.swift in Sources */,
|
||||
@ -3593,6 +3599,7 @@
|
||||
37D4B19826717E1500C925CA /* Video.swift in Sources */,
|
||||
371B7E5D27596B8400D21217 /* Comment.swift in Sources */,
|
||||
37EF5C232739D37B00B03725 /* MenuModel.swift in Sources */,
|
||||
E25028B12BF790F5002CB9FC /* HTTPStatus.swift in Sources */,
|
||||
37A7D71C2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift in Sources */,
|
||||
37BC50A92778A84700510953 /* HistorySettings.swift in Sources */,
|
||||
374DE88128BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */,
|
||||
@ -3873,6 +3880,7 @@
|
||||
37130A61277657300033018A /* PersistenceController.swift in Sources */,
|
||||
37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */,
|
||||
3717407F2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */,
|
||||
E25028B22BF790F5002CB9FC /* HTTPStatus.swift in Sources */,
|
||||
370F4FAA27CC163B001B35DC /* PlayerBackend.swift in Sources */,
|
||||
376A33E62720CB35000C1D6B /* Account.swift in Sources */,
|
||||
3763C98B290C7A50004D3B5F /* OpenVideosView.swift in Sources */,
|
||||
|
Loading…
Reference in New Issue
Block a user