import AVKit import UIKit import FlixyPlayback /// Baut aus unseren Spurlisten die Menüs für Apples Transportleiste. /// /// Die reine Abbildung steht in `TrackMenu` und ist dort geprüft; hier geht es /// nur noch um UIKit. @MainActor enum TVTrackMenuBuilder { static func items( for aufnahme: TVPlayerSnapshot, onAudio: @escaping (Int?) -> Void, onSubtitle: @escaping (Int?) -> Void ) -> [UIMenuElement] { var menüs: [UIMenuElement] = [] if aufnahme.audio.count > 1 { menüs.append(menü( titel: String(localized: "player.audioTracks"), symbol: "waveform", einträge: aufnahme.audio, gesperrt: aufnahme.isRebuilding, aktion: onAudio )) } if aufnahme.subtitles.count > 1 { menüs.append(menü( titel: String(localized: "player.subtitleTracks"), symbol: "captions.bubble", einträge: aufnahme.subtitles, gesperrt: aufnahme.isRebuilding, aktion: onSubtitle )) } return menüs } private static func menü( titel: String, symbol: String, einträge: [TrackMenu.Entry], gesperrt: Bool, aktion: @escaping (Int?) -> Void ) -> UIMenu { UIMenu( title: titel, image: UIImage(systemName: symbol), options: [.singleSelection], children: einträge.map { eintrag in UIAction( title: eintrag.title, // **Vorher ansagen, nicht hinterher erklären.** Steht hier // nichts, ist die Sekundenpause danach eine Panne; steht // es hier, ist sie eine angekündigte Folge. Das ist der // billigste Teil dieser ganzen Sache und der wirksamste. subtitle: eintrag.rebuilds ? String(localized: "player.rebuilds") : nil, // **Keine zweite Bestellung, solange die erste läuft.** // Sonst räumt `restart()` eine Engine ab, die eine andere // gerade baut. attributes: gesperrt ? [.disabled] : [], state: eintrag.isSelected ? .on : .off ) { auslöser in // **Sofort quittieren.** Das Menü schliesst sich beim // Drücken, und darin ist kein Platz für einen Kreisel. // Der Haken wandert, bevor sich am Bild etwas tut. auslöser.state = .on aktion(eintrag.id) } } ) } /// „Intro überspringen" unten rechts, solange eine Marke läuft. /// /// Die tvOS-Entsprechung zu dem Knopf, den der iOS-Player selbst zeichnet. static func contextualActions( for aufnahme: TVPlayerSnapshot, onSkip: @escaping () -> Void ) -> [UIAction] { guard let beschriftung = aufnahme.markerLabel else { return [] } return [UIAction(title: beschriftung) { _ in onSkip() }] } }