fluxa/appleApp/AppleCore/FluxaAppleAddonResourceLoader.swift
KhooLy a0db322fed fix: move Apple catalog/detail/discover/search snapshot DTOs from shared to core
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.
2026-07-28 19:04:13 +03:00

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)
}
}
}