mirror of
https://github.com/FluxaMedia/fluxa.git
synced 2026-08-09 00:17:35 +00:00
141 lines
5.3 KiB
Swift
141 lines
5.3 KiB
Swift
import Foundation
|
|
|
|
final class FluxaAppleHomeEffectHandler: FluxaApplePlatformEffectHandler {
|
|
private let configurationStore: FluxaAppleAddonConfigurationStore
|
|
private let catalogBootstrap: FluxaAppleCatalogBootstrap
|
|
private let addonResourceLoader: FluxaAppleAddonResourceLoader
|
|
|
|
init(
|
|
configurationStore: FluxaAppleAddonConfigurationStore,
|
|
catalogBootstrap: FluxaAppleCatalogBootstrap,
|
|
addonResourceLoader: FluxaAppleAddonResourceLoader = FluxaAppleAddonResourceLoader()
|
|
) {
|
|
self.configurationStore = configurationStore
|
|
self.catalogBootstrap = catalogBootstrap
|
|
self.addonResourceLoader = addonResourceLoader
|
|
}
|
|
|
|
func execute(effect: FluxaAppleHeadlessEffect) async throws -> FluxaAppleJsonValue {
|
|
switch effect.type {
|
|
case "readHomeBootstrap":
|
|
let rows = try await catalogBootstrap.loadRows(
|
|
localAddonUrls: configurationStore.localAddonUrls()
|
|
)
|
|
return .object([
|
|
"categories": .array(rows.map(homeCategory)),
|
|
"continueWatching": .array([]),
|
|
"watchlist": .array([]),
|
|
"userAddons": .array(configurationStore.localAddonUrls().map { .string($0) }),
|
|
"metadataFeeds": .array([]),
|
|
"billboard": rows.first?.items.first.map(homeMeta) ?? .null
|
|
])
|
|
case "refreshContinueWatching":
|
|
return .object(["continueWatching": .array([])])
|
|
case "fetchMetaDetail":
|
|
return try await loadMeta(effect: effect)
|
|
case "runSearch":
|
|
return try await runSearch(effect: effect)
|
|
case "runDiscover":
|
|
return try await runDiscover(effect: effect)
|
|
case "readPlaybackProgress":
|
|
return .null
|
|
default:
|
|
throw NSError(domain: "FluxaAppleUnsupportedEffect", code: 1)
|
|
}
|
|
}
|
|
|
|
private func homeCategory(_ row: FluxaAppleCatalogRow) -> FluxaAppleJsonValue {
|
|
.object([
|
|
"id": .string(row.id),
|
|
"name": .string(row.title),
|
|
"semanticName": .string(row.title),
|
|
"type": .string(row.items.first?.type ?? ""),
|
|
"catalogId": .string(row.id),
|
|
"items": .array(row.items.map(homeMeta))
|
|
])
|
|
}
|
|
|
|
private func homeMeta(_ item: FluxaAppleCatalogItem) -> FluxaAppleJsonValue {
|
|
.object([
|
|
"id": .string(item.id),
|
|
"type": .string(item.type),
|
|
"name": .string(item.title),
|
|
"poster": optionalString(item.artworkUrl),
|
|
"logo": optionalString(item.logoUrl),
|
|
"releaseInfo": .string(item.subtitle),
|
|
"addonTransportUrl": optionalString(item.addonTransportUrl),
|
|
"catalogType": optionalString(item.catalogType)
|
|
])
|
|
}
|
|
|
|
private func optionalString(_ value: String?) -> FluxaAppleJsonValue {
|
|
value.map(FluxaAppleJsonValue.string) ?? .null
|
|
}
|
|
|
|
private func string(_ value: FluxaAppleJsonValue?) -> String? {
|
|
guard case .string(let text)? = value else {
|
|
return nil
|
|
}
|
|
return text
|
|
}
|
|
|
|
private func loadMeta(effect: FluxaAppleHeadlessEffect) async throws -> FluxaAppleJsonValue {
|
|
guard case .object(let payload) = effect.payload,
|
|
let contentType = string(payload["contentType"]),
|
|
let id = string(payload["id"]) else {
|
|
throw URLError(.cannotParseResponse)
|
|
}
|
|
let transportUrl = string(payload["sourceAddonTransportUrl"])
|
|
?? configurationStore.localAddonUrls().first
|
|
guard let transportUrl else {
|
|
throw URLError(.fileDoesNotExist)
|
|
}
|
|
return try await addonResourceLoader.loadMeta(
|
|
transportUrl: transportUrl,
|
|
contentType: contentType,
|
|
id: id
|
|
)
|
|
}
|
|
|
|
private func runSearch(effect: FluxaAppleHeadlessEffect) async throws -> FluxaAppleJsonValue {
|
|
guard case .object(let payload) = effect.payload,
|
|
let query = string(payload["query"]) else {
|
|
throw URLError(.cannotParseResponse)
|
|
}
|
|
let items = try await catalogBootstrap.loadSearchItems(
|
|
localAddonUrls: configurationStore.localAddonUrls(),
|
|
query: query
|
|
)
|
|
return .object(["results": .array(items.map(homeMeta))])
|
|
}
|
|
|
|
private func runDiscover(effect: FluxaAppleHeadlessEffect) async throws -> FluxaAppleJsonValue {
|
|
guard case .object(let payload) = effect.payload,
|
|
let contentType = string(payload["contentType"]) else {
|
|
throw URLError(.cannotParseResponse)
|
|
}
|
|
let genre: String?
|
|
if case .object(let filters)? = payload["filters"],
|
|
case .string(let value)? = filters["genre"] {
|
|
genre = value
|
|
} else {
|
|
genre = nil
|
|
}
|
|
let rows = try await catalogBootstrap.loadRows(
|
|
localAddonUrls: configurationStore.localAddonUrls()
|
|
)
|
|
let items = rows.flatMap(\.items).filter { item in
|
|
guard item.type == contentType else {
|
|
return false
|
|
}
|
|
guard let genre else {
|
|
return true
|
|
}
|
|
return item.subtitle.localizedCaseInsensitiveContains(genre)
|
|
}
|
|
return .object([
|
|
"results": .array(items.map(homeMeta)),
|
|
"resultSources": .object([:])
|
|
])
|
|
}
|
|
}
|