Mini-Player: die Wiedergabe überlebt das Zurückgehen
Wer im Fernsehen etwas sieht und nebenher nachsehen will, was sonst läuft, musste bisher abbrechen. Plex' eigene Handy-Apps können das auch nicht — kein Grund, es hier genauso zu machen. Der eigentliche Umbau ist nicht die Kachel, sondern der Besitz. Die Wiedergabe hing an der Sichtbarkeit der Ansicht, und zwar dreifach: das Modell war `@State` im Player, `onDisappear` hat den Motor abgeräumt, und die Bildebene entstand in `makeUIView` neu. Zurückgehen hieß deshalb: Motor tot, Transcoder aus, beim Live-TV der Tuner freigegeben. Jetzt gehört das Modell der Wurzelansicht, die immer da ist. Verkleinern und Vergrößern wechseln nur noch die Darstellung; beendet wird die Wiedergabe an genau einer Stelle, und zwar weil jemand es will. Die Bildebene hält das Modell selbst — dasselbe Muster, das VLC seit jeher verwendet. Dieselbe `UIView` wandert zwischen Kachel und Vollbild, statt neu zu entstehen; sonst setzte das Bild bei jedem Wechsel neu an. Bild-in-Bild wandert mit und bekommt endlich einen Delegaten: bisher war es eine Einbahnstraße, aus der man nicht in die App zurückkam. Die Kachel schwebt und lässt sich in jede Ecke ziehen, statt als Leiste unten zu kleben — eine Leiste nähme jeder Liste Platz weg und müsste in jedem Bildschirm bedacht werden. Die Eckenberechnung steht in MiniPlayerPlacement und ist geprüft: in einer View wäre eine Kachel, die halb aus dem Bild ragt, erst auf dem Gerät aufgefallen. Der Pfeil oben verkleinert, ein neuer Knopf daneben beendet wirklich. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d28df346f2
commit
26c909a2e2
@@ -36,19 +36,78 @@ struct PlaybackRequest: Identifiable, Hashable {
|
||||
@MainActor
|
||||
@Observable
|
||||
final class PlaybackCoordinator {
|
||||
var request: PlaybackRequest?
|
||||
/// Wie der Player gerade zu sehen ist.
|
||||
///
|
||||
/// Der Unterschied zwischen „klein" und „gar nicht" ist der Kern der
|
||||
/// Sache: verkleinern darf die Wiedergabe **nicht** anhalten. Vorher gab
|
||||
/// es diese Unterscheidung nicht — der Player war da oder weg, und mit
|
||||
/// ihm der Motor, der Transcoder und beim Live-TV der belegte Tuner.
|
||||
enum Presentation: Equatable {
|
||||
case hidden
|
||||
case mini
|
||||
case full
|
||||
}
|
||||
|
||||
private(set) var request: PlaybackRequest?
|
||||
private(set) var presentation: Presentation = .hidden
|
||||
|
||||
func play(_ item: PlexItem, mediaIndex: Int = 0, resuming: Bool = true) {
|
||||
request = PlaybackRequest(
|
||||
play(PlaybackRequest(
|
||||
ratingKey: item.ratingKey,
|
||||
title: item.playbackTitle,
|
||||
mediaIndex: mediaIndex,
|
||||
startTime: resuming ? item.resumeTime : .zero
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
/// Schaltet einen laufenden Sender ein.
|
||||
///
|
||||
/// Eigener Weg, weil ein Sender nicht in der Mediathek liegt: Adresse und
|
||||
/// Tuner-Belegung stehen schon fest und dürfen nicht neu ermittelt werden.
|
||||
func playLive(
|
||||
channelID: String,
|
||||
title: String,
|
||||
stream: URL,
|
||||
subscriptionKey: String?
|
||||
) {
|
||||
play(PlaybackRequest(
|
||||
ratingKey: channelID,
|
||||
title: title,
|
||||
mediaIndex: 0,
|
||||
startTime: .zero,
|
||||
streamURL: stream,
|
||||
liveSubscriptionKey: subscriptionKey
|
||||
))
|
||||
}
|
||||
|
||||
/// Übernimmt einen neuen Titel.
|
||||
///
|
||||
/// Läuft bereits die Kachel, bleibt es dabei: wer nebenher stöbert, will
|
||||
/// beim Antippen nicht ins Vollbild gerissen werden. Sonst Vollbild, wie
|
||||
/// man es von einem Antippen erwartet.
|
||||
func play(_ next: PlaybackRequest) {
|
||||
presentation = presentation == .mini ? .mini : .full
|
||||
request = next
|
||||
}
|
||||
|
||||
/// Verkleinert den Player zur schwebenden Kachel. Die Wiedergabe läuft.
|
||||
func minimize() {
|
||||
guard request != nil else { return }
|
||||
presentation = .mini
|
||||
}
|
||||
|
||||
func expand() {
|
||||
guard request != nil else { return }
|
||||
presentation = .full
|
||||
}
|
||||
|
||||
/// Beendet die Wiedergabe wirklich.
|
||||
///
|
||||
/// Die einzige Stelle, an der das passiert. Alles andere — verkleinern,
|
||||
/// vergrößern, Bild-in-Bild — lässt den Motor am Leben.
|
||||
func close() {
|
||||
request = nil
|
||||
presentation = .hidden
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user