Player: eine gewählte Stufe wurde gar nicht umgerechnet
Der Decider rechnet nur um, wenn die Datei die Grenze überschreitet. Für den Normalfall ist das richtig — für eine ausdrückliche Wahl im Player nicht: wer für eine 8-Mbit-Datei „12 Mbit" wählt, bekam DirectPlay, weil die Datei ja darunter liegt. Gemeint ist aber „rechne es mir auf diese Stufe um", nicht „gib mir die Datei, falls sie so vorliegt". Die Richtlinie kann eine Umrechnung jetzt erzwingen, und die Wahl im Player tut das. Dazu verschluckte `select(quality:)` den eigentlichen Grund: lehnte der Server ab und sagte im Klartext warum, wurde diese Meldung danach durch das allgemeine „keine abspielbare Datei" ersetzt — der eine brauchbare Hinweis war damit weg. Jetzt bleibt stehen, was der Server gesagt hat. Und das Menü war zu viel. Vorn steht die kurze Liste — automatisch, Original, je eine brauchbare Stufe pro Auflösung, nur Ton. Wer eine bestimmte Bitrate erzwingen muss, klappt „Alle Stufen" auf; im Alltag will man „eine Stufe kleiner" und nicht die Wahl zwischen zehn und zwölf Megabit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b53fce65b8
commit
f628819765
+2785
-2753
File diff suppressed because it is too large
Load Diff
@@ -119,6 +119,8 @@ struct TechnicalDetails: View {
|
||||
return String(localized: "technical.reason.audioCodec \(codec.uppercased())")
|
||||
case .streamsUnknown:
|
||||
return String(localized: "technical.reason.unanalyzed")
|
||||
case .qualityChosen:
|
||||
return String(localized: "technical.reason.chosen")
|
||||
case .nativelySupported, .bitrateExceedsLimit, .transcodingUnavailable:
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -229,7 +229,13 @@ final class PlayerModel {
|
||||
server: server,
|
||||
startingAt: resumeAt
|
||||
) else {
|
||||
phase = .failed(message: String(localized: "player.noPlayableFile"))
|
||||
// Nur dann eine allgemeine Meldung, wenn `source` keine eigene
|
||||
// hinterlassen hat. Vorher stand hier immer „keine abspielbare
|
||||
// Datei", auch wenn der Server im Klartext gesagt hatte, warum er
|
||||
// ablehnt — und damit war der eine brauchbare Hinweis weg.
|
||||
if case .failed = phase {} else {
|
||||
phase = .failed(message: String(localized: "player.noPlayableFile"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,12 @@ struct QualitySheet: View {
|
||||
@Environment(\.flixyAccent) private var accent
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var showsEveryRung = false
|
||||
|
||||
private var everyday: [PlayerQuality] {
|
||||
PlayerQuality.everyday(forSourceHeight: model.sourceHeight)
|
||||
}
|
||||
|
||||
private var options: [PlayerQuality] {
|
||||
PlayerQuality.available(forSourceHeight: model.sourceHeight)
|
||||
}
|
||||
@@ -24,23 +30,27 @@ struct QualitySheet: View {
|
||||
current
|
||||
|
||||
Section {
|
||||
row(for: .automatic)
|
||||
row(for: .original)
|
||||
}
|
||||
|
||||
ForEach(groupedHeights, id: \.self) { height in
|
||||
Section("\(height)p") {
|
||||
ForEach(rungs(height: height), id: \.self) { rung in
|
||||
row(for: .limited(rung))
|
||||
}
|
||||
ForEach(everyday) { option in
|
||||
row(for: option)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
row(for: .audioOnly)
|
||||
} footer: {
|
||||
Text("quality.audioOnly.hint")
|
||||
}
|
||||
|
||||
// Aufgeklappt nur, wer es wirklich will. Für den Alltag
|
||||
// reicht „eine Stufe kleiner"; die volle Leiter braucht, wer
|
||||
// eine bestimmte Bitrate erzwingen muss.
|
||||
Section {
|
||||
DisclosureGroup(isExpanded: $showsEveryRung) {
|
||||
ForEach(groupedHeights, id: \.self) { height in
|
||||
ForEach(rungs(height: height), id: \.self) { rung in
|
||||
row(for: .limited(rung), detail: "\(height)p")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("quality.everyRung")
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(Text("player.quality"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
@@ -93,7 +103,7 @@ struct QualitySheet: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func row(for option: PlayerQuality) -> some View {
|
||||
private func row(for option: PlayerQuality, detail: String? = nil) -> some View {
|
||||
Button {
|
||||
Task { await model.select(quality: option) }
|
||||
dismiss()
|
||||
@@ -101,6 +111,11 @@ struct QualitySheet: View {
|
||||
HStack {
|
||||
Text(label(for: option))
|
||||
.foregroundStyle(Color.flixyTextPrimary)
|
||||
if let detail {
|
||||
Text(detail)
|
||||
.font(.flixyCaption)
|
||||
.foregroundStyle(Color.flixyTextMuted)
|
||||
}
|
||||
Spacer()
|
||||
if option == model.quality {
|
||||
Image(systemName: "checkmark").foregroundStyle(accent)
|
||||
@@ -116,7 +131,9 @@ struct QualitySheet: View {
|
||||
case .automatic: String(localized: "quality.automatic")
|
||||
case .original: String(localized: "quality.original")
|
||||
case .audioOnly: String(localized: "quality.audioOnly")
|
||||
case .limited(let rung): bitrateText(rung.bitrate)
|
||||
// In der kurzen Liste zählt die Auflösung, nicht die Zahl dahinter:
|
||||
// „720p" sagt mehr als „3 Mbps", wenn man nur kleiner will.
|
||||
case .limited(let rung): "\(rung.height)p · \(bitrateText(rung.bitrate))"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user