Das letzte offene Stück aus dem Plan. Du wolltest die WLAN-Voreinstellung nicht verschoben haben, sondern einen Hinweis — und einen, der sich auf etwas stützt. Ohne Grenze kommt die Entscheidung oft bei direkter Wiedergabe heraus, und dann gibt es keine Stufe, auf die irgendetwas ausweichen könnte. Beim Testserver heisst „Original" konkret: 67 Mbit, 4K, HEVC. Über eine Fernverbindung trägt das keine Leitung. Der Hinweis erscheint deshalb nur, wenn beides zutrifft: die Einstellung steht auf „ohne Grenze" **und** die letzte Wiedergabe hat tatsächlich gehakt — gemessen am gleitenden Fenster, nicht geraten. Eine Warnung, die immer dasteht, liest nach dem zweiten Mal niemand mehr. Der Befund überlebt die Wiedergabe, weil der Player längst zu ist, wenn jemand in die Einstellungen schaut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
136 lines
5.4 KiB
Swift
136 lines
5.4 KiB
Swift
import SwiftUI
|
||
import FlixyDesign
|
||
import FlixyPlayback
|
||
|
||
/// Alles, was die Wiedergabe betrifft.
|
||
///
|
||
/// Vorher stand das verstreut in der langen Liste der Einstellungen, zwischen
|
||
/// Serverangaben und Downloads. Zusammen ergibt es einen Bildschirm, den man
|
||
/// einmal durchgeht und dann in Ruhe lässt.
|
||
struct PlaybackSettingsView: View {
|
||
@Environment(PlaybackSettings.self) private var settings
|
||
@Environment(\.flixyAccent) private var accent
|
||
|
||
var body: some View {
|
||
@Bindable var settings = settings
|
||
|
||
Form {
|
||
SettingsHeader(
|
||
symbol: "play.rectangle",
|
||
colour: SettingsPalette.playback,
|
||
title: "settings.playback",
|
||
explanation: "settings.playbackExplanation"
|
||
)
|
||
|
||
Section {
|
||
Picker("settings.qualityWifi", selection: $settings.wifiQuality) {
|
||
ForEach(PlaybackSettings.Quality.allCases) { quality in
|
||
Text(quality.label).tag(quality)
|
||
}
|
||
}
|
||
Picker("settings.qualityCellular", selection: $settings.cellularQuality) {
|
||
ForEach(PlaybackSettings.Quality.allCases) { quality in
|
||
Text(quality.label).tag(quality)
|
||
}
|
||
}
|
||
} header: {
|
||
Text("settings.quality")
|
||
} footer: {
|
||
VStack(alignment: .leading, spacing: FlixySpacing.snug) {
|
||
Text("settings.qualityExplanation")
|
||
|
||
// **Nur mit Beleg.** Eine Warnung, die immer dasteht,
|
||
// liest nach dem zweiten Mal niemand mehr. Sie erscheint,
|
||
// wenn „ohne Grenze" eingestellt ist **und** die letzte
|
||
// Wiedergabe tatsächlich gehakt hat.
|
||
//
|
||
// Ohne Grenze kommt die Entscheidung oft bei direkter
|
||
// Wiedergabe heraus — und dann gibt es keine Stufe, auf
|
||
// die irgendetwas ausweichen könnte. Die Voreinstellung
|
||
// bleibt trotzdem, wie sie ist: zuhause am Kabel ist sie
|
||
// die richtige, und still umstellen wäre nicht in Ordnung.
|
||
if settings.wifiQuality == .original, settings.lastPlaybackStruggled {
|
||
Label {
|
||
Text("settings.qualityUnlimitedHint")
|
||
} icon: {
|
||
Image(systemName: "exclamationmark.triangle")
|
||
}
|
||
.font(.flixyCaption)
|
||
.foregroundStyle(Color.flixyTextSecondary)
|
||
}
|
||
}
|
||
}
|
||
|
||
Section {
|
||
Toggle("settings.rememberQuality", isOn: $settings.remembersQuality)
|
||
Toggle("settings.showBitrate", isOn: $settings.showsBitrate)
|
||
} footer: {
|
||
Text("settings.rememberQualityExplanation")
|
||
}
|
||
|
||
Section {
|
||
Picker("settings.rewind", selection: $settings.rewindSeconds) {
|
||
ForEach(PlaybackSettings.skipChoices, id: \.self) { seconds in
|
||
Text("settings.seconds \(seconds)").tag(seconds)
|
||
}
|
||
}
|
||
Picker("settings.forward", selection: $settings.forwardSeconds) {
|
||
ForEach(PlaybackSettings.skipChoices, id: \.self) { seconds in
|
||
Text("settings.seconds \(seconds)").tag(seconds)
|
||
}
|
||
}
|
||
Picker("settings.defaultRate", selection: $settings.defaultRate) {
|
||
ForEach(PlaybackSettings.rateChoices, id: \.self) { rate in
|
||
Text(rateLabel(rate)).tag(rate)
|
||
}
|
||
}
|
||
} header: {
|
||
Text("settings.controls")
|
||
} footer: {
|
||
// Zurück und vor getrennt, weil sie verschiedene Zwecke haben:
|
||
// zurück, weil man etwas nicht verstanden hat; vor, weil man
|
||
// etwas überspringen will.
|
||
Text("settings.controlsExplanation")
|
||
}
|
||
|
||
Section {
|
||
Toggle("settings.lockLandscape", isOn: $settings.locksLandscape)
|
||
} footer: {
|
||
Text("settings.lockLandscapeExplanation")
|
||
}
|
||
|
||
Section {
|
||
Toggle("settings.autoSkipIntro", isOn: $settings.autoSkipIntro)
|
||
Toggle("settings.autoSkipCredits", isOn: $settings.autoSkipCredits)
|
||
Toggle("settings.autoPlayNext", isOn: $settings.autoPlayNext)
|
||
} header: {
|
||
Text("settings.episodes")
|
||
}
|
||
}
|
||
.tint(accent)
|
||
.flixyForm()
|
||
.scrollContentBackground(.hidden)
|
||
.background(Color.flixyCanvas)
|
||
.navigationTitle(Text("settings.playback"))
|
||
.flixyInlineTitle()
|
||
}
|
||
|
||
private func rateLabel(_ rate: Double) -> String {
|
||
rate == 1
|
||
? String(localized: "settings.rateNormal")
|
||
: rate.formatted(.number.precision(.fractionLength(0...2))) + "×"
|
||
}
|
||
}
|
||
|
||
extension PlaybackSettings.Quality {
|
||
var label: LocalizedStringKey {
|
||
switch self {
|
||
case .original: "quality.original"
|
||
case .high: "quality.high"
|
||
case .medium: "quality.medium"
|
||
case .low: "quality.low"
|
||
case .minimal: "quality.minimal"
|
||
}
|
||
}
|
||
}
|