Owners can list/revoke devices and approve TV codes in the iOS app; TV shows a vmc:// QR, and Films/Series get visible chips, titles, and a non-overlapping A–Z index. Co-authored-by: Cursor <cursoragent@cursor.com>
264 lines
5.7 KiB
Swift
264 lines
5.7 KiB
Swift
import Foundation
|
|
|
|
struct DeviceCodeResponse: Decodable {
|
|
let code: String
|
|
let expiresAt: String?
|
|
let pollIntervalSeconds: Int?
|
|
let linkUrl: String?
|
|
let qrPayload: String?
|
|
}
|
|
|
|
enum PollResult {
|
|
case pending
|
|
case expired
|
|
case alreadyLinked
|
|
case linked(refreshToken: String, viewerName: String)
|
|
}
|
|
|
|
struct HomePayload: Decodable {
|
|
let continueWatching: [MediaItem]
|
|
let spotlight: [MediaItem]?
|
|
let shelves: [ShelfRow]
|
|
let favorites: [MediaItem]?
|
|
}
|
|
|
|
struct ShelfRow: Decodable, Identifiable {
|
|
let id: String
|
|
let title: String
|
|
let name: String?
|
|
let kind: String?
|
|
let slug: String?
|
|
let items: [MediaItem]
|
|
}
|
|
|
|
struct MediaItem: Codable, Identifiable, Hashable {
|
|
let type: String
|
|
let id: String
|
|
let title: String
|
|
let subtitle: String?
|
|
let year: Int?
|
|
let posterUrl: String?
|
|
let backdropUrl: String?
|
|
let logoUrl: String?
|
|
let mediaFileId: String?
|
|
let seriesId: String?
|
|
let positionMs: Int64?
|
|
let durationMs: Int64?
|
|
let progressId: String?
|
|
let badge: String?
|
|
}
|
|
|
|
struct PlayResult: Decodable {
|
|
let streamUrl: String
|
|
let sessionId: String?
|
|
let mediaFileId: String?
|
|
let defaultSubtitle: DefaultSubtitle?
|
|
}
|
|
|
|
struct DefaultSubtitle: Decodable {
|
|
let fileId: Int?
|
|
let source: String?
|
|
let language: String?
|
|
let fileName: String?
|
|
let format: String?
|
|
let contentBase64: String
|
|
let label: String?
|
|
}
|
|
|
|
struct ViewerProfile: Decodable, Identifiable, Hashable {
|
|
let id: String
|
|
let name: String
|
|
let avatarId: String?
|
|
let isOwner: Bool?
|
|
let isKids: Bool?
|
|
let hasPin: Bool?
|
|
}
|
|
|
|
struct ProfilesPayload: Decodable {
|
|
let profiles: [ViewerProfile]
|
|
let activeProfileId: String?
|
|
let canManage: Bool?
|
|
}
|
|
|
|
struct ClientMe: Decodable {
|
|
struct Iptv: Decodable { let enabled: Bool? }
|
|
let iptv: Iptv?
|
|
let profile: ViewerProfile?
|
|
let deviceId: String?
|
|
|
|
var iptvEnabled: Bool { iptv?.enabled == true }
|
|
}
|
|
|
|
struct LinkedDevice: Decodable, Identifiable, Hashable {
|
|
let id: String
|
|
let name: String
|
|
let platform: String?
|
|
let lastSeenAt: String?
|
|
let createdAt: String?
|
|
let current: Bool?
|
|
}
|
|
|
|
struct DevicesPayload: Decodable {
|
|
let devices: [LinkedDevice]
|
|
}
|
|
|
|
struct ApproveDeviceResult: Decodable {
|
|
let ok: Bool?
|
|
let deviceName: String?
|
|
let platform: String?
|
|
}
|
|
|
|
struct MediaFileInfo: Decodable, Identifiable, Hashable {
|
|
let id: String
|
|
let label: String?
|
|
let resolution: String?
|
|
let videoCodec: String?
|
|
let audioCodec: String?
|
|
let hdrType: String?
|
|
let nodeOnline: Bool?
|
|
let nodeName: String?
|
|
}
|
|
|
|
struct MovieDetail: Decodable {
|
|
let id: String
|
|
let title: String
|
|
let overview: String?
|
|
let year: Int?
|
|
let posterUrl: String?
|
|
let backdropUrl: String?
|
|
let logoUrl: String?
|
|
let runtime: Int?
|
|
let genres: [String]?
|
|
let rating: String?
|
|
let cast: [String]?
|
|
let director: [String]?
|
|
let mediaFiles: [MediaFileInfo]?
|
|
|
|
var preferredMediaFileId: String? {
|
|
mediaFiles?.first(where: { $0.nodeOnline != false })?.id ?? mediaFiles?.first?.id
|
|
}
|
|
}
|
|
|
|
struct SeriesDetail: Decodable {
|
|
let id: String
|
|
let title: String
|
|
let overview: String?
|
|
let year: Int?
|
|
let posterUrl: String?
|
|
let backdropUrl: String?
|
|
let logoUrl: String?
|
|
let genres: [String]?
|
|
let rating: String?
|
|
let cast: [String]?
|
|
let director: [String]?
|
|
let seasons: [Season]?
|
|
}
|
|
|
|
struct Season: Decodable, Identifiable {
|
|
var id: Int { seasonNumber }
|
|
let seasonNumber: Int
|
|
let episodes: [Episode]?
|
|
}
|
|
|
|
struct Episode: Decodable, Identifiable {
|
|
let id: String
|
|
let title: String?
|
|
let episodeNumber: Int?
|
|
let seasonNumber: Int?
|
|
let overview: String?
|
|
let stillUrl: String?
|
|
let mediaFileId: String?
|
|
let available: Bool?
|
|
let runtime: Int?
|
|
}
|
|
|
|
struct SearchPayload: Decodable {
|
|
let results: [MediaItem]
|
|
}
|
|
|
|
struct BrowsePayload: Decodable {
|
|
let id: String?
|
|
let title: String?
|
|
let kind: String?
|
|
let total: Int?
|
|
let offset: Int?
|
|
let items: [MediaItem]
|
|
}
|
|
|
|
struct SubtitleHit: Decodable, Identifiable, Hashable {
|
|
var id: String { "\(source ?? "x")-\(fileId)" }
|
|
let fileId: Int
|
|
let language: String?
|
|
let release: String?
|
|
let fileName: String?
|
|
let downloadCount: Int?
|
|
let hearingImpaired: Bool?
|
|
let source: String?
|
|
let uploader: String?
|
|
}
|
|
|
|
struct SubtitleSearchResult: Decodable {
|
|
let configured: Bool?
|
|
let imdbId: String?
|
|
let title: String?
|
|
let results: [SubtitleHit]
|
|
}
|
|
|
|
struct FetchedSubtitle: Decodable {
|
|
let fileId: Int?
|
|
let source: String?
|
|
let fileName: String?
|
|
let format: String?
|
|
let contentBase64: String
|
|
}
|
|
|
|
struct IptvCategory: Decodable, Identifiable, Hashable {
|
|
let id: String
|
|
let name: String
|
|
let channelCount: Int?
|
|
}
|
|
|
|
struct IptvChannel: Decodable, Identifiable, Hashable {
|
|
var id: Int { streamId }
|
|
let streamId: Int
|
|
let name: String
|
|
let logoUrl: String?
|
|
let number: Int?
|
|
let categoryId: String?
|
|
let nowTitle: String?
|
|
}
|
|
|
|
struct IptvPlayInfo: Decodable {
|
|
let streamId: Int
|
|
let name: String
|
|
let logoUrl: String?
|
|
let streamUrl: String
|
|
let format: String?
|
|
let fallbackStreamUrl: String?
|
|
let fallbackFormat: String?
|
|
}
|
|
|
|
struct ShelfPref: Codable, Identifiable, Hashable {
|
|
var id: String { shelfId }
|
|
let shelfId: String
|
|
let name: String
|
|
let slug: String?
|
|
let kind: String?
|
|
var visible: Bool
|
|
var sortOrder: Int
|
|
}
|
|
|
|
struct FavoriteToggleResult: Decodable {
|
|
let favorited: Bool
|
|
let items: [MediaItem]?
|
|
}
|
|
|
|
struct ApiErrorBody: Decodable {
|
|
struct ErrorObj: Decodable { let message: String? }
|
|
let error: ErrorObj?
|
|
}
|
|
|
|
struct PlayerTrack: Identifiable, Hashable {
|
|
let id: Int32
|
|
let name: String
|
|
}
|