// // Swiftfin is subject to the terms of the Mozilla Public // License, v2.0. If a copy of the MPL was not distributed with this // file, you can obtain one at https://mozilla.org/MPL/2.0/. // // Copyright (c) 2025 Jellyfin & Jellyfin Contributors // import Defaults import Factory import Foundation import JellyfinAPI import OrderedCollections final class XtreamViewModel: ViewModel, Stateful { @Injected(\.sessionManager) private var sessionManager var xtreamSession: XtreamSession? { sessionManager.xtreamSession } // MARK: Action enum Action: Equatable { case error(JellyfinAPIError) case refresh } // MARK: State enum State: Hashable { case content case error(JellyfinAPIError) case initial case refreshing } @Published var xtreamChannels: OrderedSet = [] @Published var backgroundStates: Set = [] @Published var state: State = .initial func respond(to action: Action) -> State { switch action { case let .error(error): return .error(error) case .refresh: cancellables.removeAll() Task { do { try await refresh() await MainActor.run { self.state = .content } } catch { await MainActor.run { self.state = .error(.init(error.localizedDescription)) } } } .store(in: &cancellables) return .refreshing } } private func refresh() async throws { guard let session = xtreamSession else { throw XtreamAPIError.invalidResponse } await MainActor.run { xtreamChannels.removeAll() } let categories = try await session.client.getLiveCategories() // Convert Xtream categories to BaseItemDto for now // TODO: Create proper Xtream data models let items = categories.map { category -> BaseItemDto in var item = BaseItemDto() item.id = category.categoryId item.name = category.categoryName item.type = .channel return item } await MainActor.run { xtreamChannels.elements = items } } func randomItemImageSources(for channel: BaseItemDto) async throws -> [ImageSource] { // TODO: Implement Xtream-specific image fetching [] } }