// Wire models for the tv-anarchy-bridge HTTP API. These mirror the JSON the // bridge emits (src/http.ts in tv-anarchy-mcp) one-for-one. The iOS app is a // pure bridge client — it never touches the filesystem or SSH — so these are the // only "library" types it knows. import Foundation struct BridgeShow: Codable, Identifiable, Hashable { let id: String let name: String let episodeCount: Int let seasons: [Int] let episodes: [BridgeEpisode] } struct BridgeEpisode: Codable, Identifiable, Hashable { /// Opaque, server-issued stream id (base64url of the file path on black/plum). let id: String let season: Int let episode: Int let label: String let ext: String /// On-disk size; optional so the app tolerates an older bridge. let bytes: Int64? /// e.g. "S01E02" — compact badge for the list row. Movies travel as S00E00 /// and show no badge. var code: String { season == 0 && episode == 0 ? "" : String(format: "S%02dE%02d", season, episode) } } struct ShowsResponse: Codable { let shows: [BridgeShow] } /// A film (possibly one of a boxed collection). Same id space as episodes — /// streaming, artwork, downloads and progress all reuse the episode plumbing. struct BridgeMovie: Codable, Identifiable, Hashable { let id: String let title: String let collection: String? let year: Int? let ext: String let bytes: Int64 /// Adapter into the (show:nil, episode:) playback/download path. var asEpisode: BridgeEpisode { BridgeEpisode(id: id, season: 0, episode: 0, label: title, ext: ext, bytes: bytes) } } struct MoviesResponse: Codable { let movies: [BridgeMovie] } // MARK: - Continue watching / prefetch struct ResumePoint: Codable, Hashable { let episodeId: String let season: Int let episode: Int let label: String let positionSeconds: Double let durationSeconds: Double? var code: String { String(format: "S%02dE%02d", season, episode) } } struct NextEpisode: Codable, Hashable { let episodeId: String let season: Int let episode: Int let label: String } struct ContinueItem: Codable, Identifiable, Hashable { let show: String let showId: String let resume: ResumePoint? let next: NextEpisode? let lastWatched: String var id: String { showId } } struct ContinueResponse: Codable { let items: [ContinueItem] } struct ResumeResponse: Codable { let positionSeconds: Double } // MARK: - Remote transport /// A controllable device from the bridge's registry (`/remote/targets`). struct RemoteTarget: Codable, Identifiable, Hashable { let id: String let name: String let kind: String let capabilities: [String] let reachable: Bool func can(_ capability: String) -> Bool { capabilities.contains(capability) } } struct TargetsResponse: Codable { let targets: [RemoteTarget] } struct RemoteStatus: Codable, Hashable { let playing: Bool let paused: Bool? let title: String? let volume: Double? let position: Double? let duration: Double? } // MARK: - Downloads struct Torrent: Codable, Identifiable, Hashable { let id: Int let name: String let percentDone: Double let status: Int let rateDownload: Double let rateUpload: Double let eta: Double let sizeWhenDone: Double let haveValid: Double var statusLabel: String { switch status { case 0: return "Stopped" case 1, 2: return "Checking" case 3, 5: return "Queued" case 4: return "Downloading" case 6: return "Seeding" default: return "—" } } } struct TorrentsResponse: Codable { let torrents: [Torrent] } struct SearchResult: Codable, Identifiable, Hashable { let filename: String let source: String let size: String let seeders: Int let leechers: Int let magnet: String? var id: String { (magnet ?? filename) + source } } struct SearchResponse: Codable { let results: [SearchResult] }