import Foundation #if canImport(UIKit) import UIKit #endif /// Wer wir gegenüber Plex sind. /// /// Plex verlangt bei jeder Anfrage eine Handvoll `X-Plex-*`-Kopfzeilen. Die /// wichtigste ist `X-Plex-Client-Identifier`: darüber erkennt der Server das /// Gerät wieder. Ändert sie sich, gilt die App als neues Gerät — die /// Anmeldung ist futsch und in der Geräteliste des Kontos sammelt sich Müll /// an. Deshalb wird sie einmal erzeugt und dann festgehalten. public struct PlexClientIdentity: Sendable, Equatable { public let product: String public let version: String public let clientIdentifier: String public let platform: String public let platformVersion: String public let device: String public let deviceName: String public init( product: String, version: String, clientIdentifier: String, platform: String, platformVersion: String, device: String, deviceName: String ) { self.product = product self.version = version self.clientIdentifier = clientIdentifier self.platform = platform self.platformVersion = platformVersion self.device = device self.deviceName = deviceName } /// Die Kopfzeilen für eine Anfrage — ohne Token, das kommt je nach Ziel /// (plex.tv oder Server) aus einer anderen Quelle. public var headers: [String: String] { [ "X-Plex-Product": product, "X-Plex-Version": version, "X-Plex-Client-Identifier": clientIdentifier, "X-Plex-Platform": platform, "X-Plex-Platform-Version": platformVersion, "X-Plex-Device": device, "X-Plex-Device-Name": deviceName, // Ohne diese Zeile antwortet Plex in XML. Die Modelle hier sind auf // JSON ausgelegt, also fragen wir es ausdrücklich an. "Accept": "application/json", ] } } extension PlexClientIdentity { private static let storageKey = "flixy.plex.clientIdentifier" /// Baut die Kennung für das laufende Gerät und merkt sich die Client-ID. /// /// `@MainActor`, weil `UIDevice.current` dort beheimatet ist. Das mit /// `assumeIsolated` zu umgehen wäre ein Absturz mit Anlauf — die Kennung /// wird ohnehin einmal beim Start gebaut. @MainActor public static func current( product: String, version: String, defaults: UserDefaults = .standard ) -> PlexClientIdentity { let identifier: String if let stored = defaults.string(forKey: storageKey) { identifier = stored } else { identifier = UUID().uuidString defaults.set(identifier, forKey: storageKey) } #if canImport(UIKit) let systemVersion = UIDevice.current.systemVersion let model = UIDevice.current.model let name = UIDevice.current.name #else let systemVersion = ProcessInfo.processInfo.operatingSystemVersionString let model = "Unknown" let name = ProcessInfo.processInfo.hostName #endif return PlexClientIdentity( product: product, version: version, clientIdentifier: identifier, platform: "iOS", platformVersion: systemVersion, device: model, deviceName: name ) } }