import Foundation /// Was abgespielt werden soll und wie. /// /// Die Entscheidung *ob* umgerechnet wird, fällt vorher — im /// `PlaybackDecider`, aus Profil und Leitung. Hier steht nur noch, was der /// Server daraufhin bereitstellen soll. Das ist die Trennung, die es vorher /// nicht gab: der Player hat Plex' Transcoder unmittelbar angesprochen und /// konnte deshalb mit keinem anderen Server reden. public struct PlaybackIntent: Sendable, Equatable { public let itemID: String /// Welche Fassung — ein Film kann mehrfach vorliegen. public let versionIndex: Int /// Die Datei innerhalb der Fassung. Plex adressiert Dateien, Jellyfin /// Quellen; beide finden sich hierüber wieder. public let file: MediaFile /// `nil` heißt: unverändert übertragen. public let transcode: TranscodeSettings? public let startTime: Duration /// Ob der Server im selben Netz steht. Plex rechnet dann anders. public let isLocalConnection: Bool /// Welche Tonspur gespielt werden soll. /// /// **Ohne diese Angabe wählt der Server.** Bei einem umgerechneten Stream /// gibt es danach nur noch *eine* Tonspur — was hier nicht gesagt wird, /// lässt sich später nicht mehr umstellen, weil im Player schlicht keine /// zweite ankommt. Genau daran scheiterte die Sprachwahl bisher: sie lief /// auf einer Liste mit einem Eintrag. public let audioStreamIndex: Int? /// Welcher Untertitel eingebrannt oder mitgeliefert werden soll. /// /// Dieselbe Lage wie beim Ton: rechnet der Server um, liefert er genau /// den einen aus, für den er sich entschieden hat. Wer erst im Player /// wählen will, hat dann nichts mehr zur Auswahl. /// /// **`-1` heisst ausdrücklich „keine".** Nicht dasselbe wie `nil`: das /// hiesse „such dir was aus", und der Server sucht sich dann durchaus /// etwas aus. public let subtitleStreamIndex: Int? public init( itemID: String, versionIndex: Int, file: MediaFile, transcode: TranscodeSettings? = nil, startTime: Duration = .zero, isLocalConnection: Bool = false, audioStreamIndex: Int? = nil, subtitleStreamIndex: Int? = nil ) { self.itemID = itemID self.versionIndex = versionIndex self.file = file self.transcode = transcode self.startTime = startTime self.isLocalConnection = isLocalConnection self.audioStreamIndex = audioStreamIndex self.subtitleStreamIndex = subtitleStreamIndex } } /// Wie weit heruntergerechnet werden soll. public struct TranscodeSettings: Sendable, Equatable { /// In Kilobit je Sekunde. `nil` heißt „so gut wie möglich". public let maximumBitrate: Int? /// Etwa „1280x720". `nil` lässt die Auflösung, wie sie ist. public let resolution: String? /// Ob der Server eine **Leiter** liefern soll statt einer festen Stufe. /// /// Dann enthält die Playlist mehrere Varianten, und der Player wechselt /// zwischen ihnen, ohne dass der Stream neu aufgebaut werden muss. Die /// angegebene Bitrate bleibt dabei die **oberste** Sprosse; was darunter /// liegt, erfindet der Server. /// /// Nur bei `automatic` gesetzt. Wer im Sheet ausdrücklich „720p · 3 Mbps" /// antippt, soll das bekommen und nicht „höchstens 3, meistens weniger" /// unter unverändertem Etikett. public let prefersAdaptive: Bool public init(maximumBitrate: Int?, resolution: String?, prefersAdaptive: Bool = false) { self.maximumBitrate = maximumBitrate self.resolution = resolution self.prefersAdaptive = prefersAdaptive } } /// Was der Server für diese Wiedergabe bereitgestellt hat. public struct PlaybackTarget: Sendable, Equatable { public let url: URL /// Die Sitzung, die der Server dafür angelegt hat. /// /// Zum Aufräumen: ohne dieses Kennzeichen rechnet der Transcoder weiter, /// wenn längst niemand mehr zusieht. public let sessionID: String? /// Welche Fassung gespielt wird. /// /// Jellyfin will sie bei jeder Fortschrittsmeldung zurückhaben, sonst /// ordnet es die Meldung keiner Wiedergabe zu. public let versionID: String? public init(url: URL, sessionID: String? = nil, versionID: String? = nil) { self.url = url self.sessionID = sessionID self.versionID = versionID } } /// In welchem Zustand sich die Wiedergabe befindet. public enum PlaybackReportState: String, Sendable { /// Der erste Bericht einer Wiedergabe. /// /// Eigener Fall, weil Jellyfin dafür einen eigenen Endpunkt hat: ohne ihn /// erscheint der Titel nirgends als „läuft gerade", und die Sitzung, unter /// der die weiteren Meldungen laufen, ist dem Server unbekannt. Plex /// unterscheidet das nicht und behandelt ihn wie „spielt". case started case playing case paused /// Beim Verlassen des Players. Erst dieser Wert nimmt den Titel auf dem /// Server aus „wird gerade abgespielt" heraus. case stopped case buffering } /// Wo man gerade steht. public struct PlaybackReport: Sendable, Equatable { public let itemID: String public let sessionID: String? public let versionID: String? public let state: PlaybackReportState public let time: Duration public let duration: Duration public init( itemID: String, sessionID: String? = nil, versionID: String? = nil, state: PlaybackReportState, time: Duration, duration: Duration ) { self.itemID = itemID self.sessionID = sessionID self.versionID = versionID self.state = state self.time = time self.duration = duration } } extension PlaybackReportState: Equatable {}