mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-07-29 21:19:13 +00:00
Xcode's embedAndSignAppleFrameworkForXcode task for :shared: is intermittently disabled by the Kotlin Gradle Plugin's own IDE/index-build detection (onlyIf 'Task is enabled' is false), silently skipping the framework embed and leaving these types unavailable to Swift. core's framework already embeds reliably for iOS (matching tvOS), so these plain DTOs move there instead; the Compose-dependent DataSource wrappers stay in shared, which already re-exports core.
99 lines
3.3 KiB
Swift
99 lines
3.3 KiB
Swift
import Foundation
|
|
import FluxaCore
|
|
|
|
final class FluxaAppleAddonResourceLoader {
|
|
private let resolver: FluxaAppleAddonCatalogResolver
|
|
private let session: URLSession
|
|
|
|
init(
|
|
resolver: FluxaAppleAddonCatalogResolver = FluxaAppleAddonCatalogResolver(),
|
|
session: URLSession = .shared
|
|
) {
|
|
self.resolver = resolver
|
|
self.session = session
|
|
}
|
|
|
|
func loadMeta(
|
|
transportUrl: String,
|
|
contentType: String,
|
|
id: String
|
|
) async throws -> FluxaAppleJsonValue {
|
|
guard let url = resolver.resourceUrl(
|
|
transportUrl: transportUrl,
|
|
resource: "meta",
|
|
contentType: contentType,
|
|
id: id
|
|
) else {
|
|
throw URLError(.badURL)
|
|
}
|
|
let (data, response) = try await session.data(from: url)
|
|
guard let httpResponse = response as? HTTPURLResponse,
|
|
(200..<300).contains(httpResponse.statusCode) else {
|
|
throw URLError(.badServerResponse)
|
|
}
|
|
let root = try JSONSerialization.jsonObject(with: data)
|
|
guard let object = root as? [String: Any], let meta = object["meta"] else {
|
|
throw URLError(.cannotParseResponse)
|
|
}
|
|
return try FluxaAppleJsonValue(any: meta)
|
|
}
|
|
|
|
func loadDirectStreams(
|
|
transportUrl: String,
|
|
contentType: String,
|
|
id: String
|
|
) async throws -> [AppleDetailStreamSnapshot] {
|
|
guard let url = resolver.resourceUrl(
|
|
transportUrl: transportUrl,
|
|
resource: "stream",
|
|
contentType: contentType,
|
|
id: id
|
|
) else {
|
|
throw URLError(.badURL)
|
|
}
|
|
let (data, response) = try await session.data(from: url)
|
|
guard let httpResponse = response as? HTTPURLResponse,
|
|
(200..<300).contains(httpResponse.statusCode) else {
|
|
throw URLError(.badServerResponse)
|
|
}
|
|
let addonName = URL(string: transportUrl)?.host ?? transportUrl
|
|
guard let streams = FluxaCoreStremio.parseDirectStreams(
|
|
body: String(decoding: data, as: UTF8.self)
|
|
) else {
|
|
throw URLError(.cannotParseResponse)
|
|
}
|
|
return streams.compactMap { stream in
|
|
guard let data = try? JSONSerialization.data(withJSONObject: stream.requestHeaders),
|
|
let requestHeadersJson = String(data: data, encoding: .utf8) else {
|
|
return nil
|
|
}
|
|
return AppleDetailStreamSnapshot(
|
|
addonName: addonName,
|
|
title: stream.title ?? addonName,
|
|
playableUrl: stream.playableUrl,
|
|
requestHeadersJson: requestHeadersJson
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension FluxaAppleJsonValue {
|
|
init(any value: Any) throws {
|
|
switch value {
|
|
case is NSNull:
|
|
self = .null
|
|
case let value as Bool:
|
|
self = .boolean(value)
|
|
case let value as NSNumber:
|
|
self = .number(value.doubleValue)
|
|
case let value as String:
|
|
self = .string(value)
|
|
case let value as [Any]:
|
|
self = .array(try value.map { try FluxaAppleJsonValue(any: $0) })
|
|
case let value as [String: Any]:
|
|
self = .object(try value.mapValues { try FluxaAppleJsonValue(any: $0) })
|
|
default:
|
|
throw URLError(.cannotParseResponse)
|
|
}
|
|
}
|
|
}
|