diff --git a/Flixy/Session/FlixySession.swift b/Flixy/Session/FlixySession.swift index 7355715..bb58fdb 100644 --- a/Flixy/Session/FlixySession.swift +++ b/Flixy/Session/FlixySession.swift @@ -93,6 +93,18 @@ final class FlixySession { private(set) var pendingCode: String? private(set) var showsManualCode = false + /// Ob der Server in **demselben** Netz steht wie wir. + /// + /// Entscheidet, ob „Auto" gedeckelt anfängt: zuhause trägt die Leitung + /// das Original, unterwegs nicht. Bei Plex steht es im gewählten Weg; bei + /// Jellyfin wird der Server gefragt, weil ein Blick auf die Adresse nicht + /// genügt — wer über einen Reverse Proxy geht, erreicht seinen Server + /// auch von der Couch aus unter einem öffentlichen Namen. + /// + /// Im Zweifel `false`: lieber einmal zu vorsichtig gedeckelt als eine + /// Wiedergabe, die über eine Fernverbindung gar nicht erst anläuft. + private(set) var isLocalConnection = false + private(set) var phase: Phase = .launching /// Welche Serverart gerade bedient wird — `nil`, solange niemand /// angemeldet ist. @@ -476,6 +488,7 @@ final class FlixySession { defaults.set(resource.clientIdentifier, forKey: Key.preferredServer) defaults.set(connection.uri, forKey: Key.cachedConnectionURI) defaults.set(connection.local, forKey: Key.cachedConnectionIsLocal) + isLocalConnection = connection.local defaults.set(resource.name, forKey: Key.cachedServerName) if let token = resource.accessToken { try? tokenStore.store(token, for: Self.plexTokenAccount) @@ -549,6 +562,7 @@ final class FlixySession { kind = .jellyfin jellyfinAccount = account self.backend = backend + isLocalConnection = await backend.isInLocalNetwork() do { libraries = try await backend.libraries().filter(\.isSupported) @@ -594,6 +608,9 @@ final class FlixySession { kind = .jellyfin jellyfinAccount = account self.backend = backend + // Auch hier fragen: dasselbe Konto kann heute zuhause und morgen im + // Zug wiederhergestellt werden, und die Antwort gilt je Sitzung. + isLocalConnection = await backend.isInLocalNetwork() do { libraries = try await backend.libraries().filter(\.isSupported) diff --git a/Flixy/Views/Navigation/RootView.swift b/Flixy/Views/Navigation/RootView.swift index b7e21e9..3325a25 100644 --- a/Flixy/Views/Navigation/RootView.swift +++ b/Flixy/Views/Navigation/RootView.swift @@ -185,7 +185,11 @@ struct RootView: View { diagnostics: diagnostics, policy: settings.policy(for: network), settings: settings, - isLocalConnection: session.server?.connection.local ?? false, + // **Nicht mehr über `server`.** Der ist ein `PlexServer` und bei + // Jellyfin immer `nil` — damit galt dort jede Wiedergabe als + // entfernt, auch zuhause am Kabel, und „Auto" hätte ein + // 67-Mbit-Remux ohne Not heruntergerechnet. + isLocalConnection: session.isLocalConnection, connectionDescription: session.connectionDescription, unavailableMessage: session.playbackUnavailableMessage ) diff --git a/Packages/FlixyKit/Sources/JellyfinKit/JellyfinBackend.swift b/Packages/FlixyKit/Sources/JellyfinKit/JellyfinBackend.swift index 83fff09..eb1eb25 100644 --- a/Packages/FlixyKit/Sources/JellyfinKit/JellyfinBackend.swift +++ b/Packages/FlixyKit/Sources/JellyfinKit/JellyfinBackend.swift @@ -30,6 +30,31 @@ public struct JellyfinBackend: MediaBackend { // MARK: - Blättern + /// Ob der Server diese Verbindung als „im eigenen Netz" ansieht. +/// +/// **Gefragt, nicht geraten.** Ein Blick auf die Adresse taugt nicht: wer +/// über einen Reverse Proxy geht, erreicht seinen Server auch von der +/// Couch aus unter einem öffentlichen Namen. Umgekehrt kann eine private +/// Adresse über ein VPN von weit her kommen. +/// +/// `/System/Endpoint` beantwortet es mit **demselben** Aufruf, mit dem +/// Jellyfin auch selbst entscheidet, ob es eine Bitratenleiter ausliefert. +/// Näher an der Wahrheit kommt man nicht heran. +/// +/// Bei Zweifeln `false`: lieber einmal zu vorsichtig gedeckelt als eine +/// Wiedergabe, die über eine Fernverbindung gar nicht erst anläuft. +public func isInLocalNetwork() async -> Bool { + struct Lage: Decodable { + let isInNetwork: Bool + private enum CodingKeys: String, CodingKey { case isInNetwork = "IsInNetwork" } + } + let lage = try? await client.send( + JellyfinRequest(path: "/System/Endpoint"), + as: Lage.self + ) + return lage?.isInNetwork ?? false + } + public func libraries() async throws(MediaBackendError) -> [MediaLibrary] { let page = try await client.send( JellyfinRequest(path: "/Users/\(account.userID)/Views"),