fix: make buffered time visible in both player modes
This commit is contained in:
+32
-11
@@ -12,6 +12,7 @@ final class PlayerModel: ObservableObject {
|
||||
@Published private(set) var pendingRevision: UInt64?
|
||||
@Published private(set) var resolvingYouTube = false
|
||||
@Published private(set) var isLiveStream = false
|
||||
@Published private(set) var isNetworkMedia = false
|
||||
@Published private(set) var youtubeHeights: [Int] = []
|
||||
@Published private(set) var youtubeHeight: Int?
|
||||
@Published private(set) var youtubeQuality: Int?
|
||||
@@ -246,6 +247,7 @@ final class PlayerModel: ObservableObject {
|
||||
fileName = title
|
||||
fileIcon = icon
|
||||
isLiveStream = live
|
||||
isNetworkMedia = !url.isFileURL
|
||||
youtubeHeights = []
|
||||
youtubeHeight = nil
|
||||
youtubeQuality = nil
|
||||
@@ -325,6 +327,7 @@ final class PlayerModel: ObservableObject {
|
||||
fileName = nil
|
||||
fileIcon = nil
|
||||
isLiveStream = false
|
||||
isNetworkMedia = false
|
||||
youtubeHeights = []
|
||||
youtubeHeight = nil
|
||||
youtubeQuality = nil
|
||||
@@ -750,6 +753,20 @@ private struct PlayerContent: View {
|
||||
@State private var controlOffset = CGSize.zero
|
||||
@State private var dragOrigin = CGSize.zero
|
||||
|
||||
private var controlHeight: CGFloat { model.isNetworkMedia ? 88 : 68 }
|
||||
private var compactControlHeight: CGFloat { model.isNetworkMedia ? 52 : 40 }
|
||||
|
||||
private var bufferReadout: some View {
|
||||
Text(model.bufferedRange.map {
|
||||
"Buffered: \(String(format: "%.1f", $0.upperBound - $0.lowerBound))s ahead"
|
||||
} ?? "Buffered: —")
|
||||
.font(.system(size: 10).monospacedDigit())
|
||||
.foregroundStyle(.white.opacity(0.75))
|
||||
.lineLimit(1)
|
||||
.frame(height: 12)
|
||||
.help("Conservative playable buffer ahead; — means the estimate is unavailable.")
|
||||
}
|
||||
|
||||
private func time(_ seconds: Double) -> String {
|
||||
let value = Int(min(max(0, seconds.isFinite ? seconds : 0), Double(Int.max / 2)))
|
||||
let hours = value / 3600
|
||||
@@ -834,16 +851,19 @@ private struct PlayerContent: View {
|
||||
}
|
||||
|
||||
private var compactControls: some View {
|
||||
HStack(spacing: 16) {
|
||||
iconButton(model.state.status == PLAYER_PLAYING ? "pause.fill" : "play.fill",
|
||||
model.state.status == PLAYER_PLAYING ? "Pause" : "Play", action: model.togglePlayback)
|
||||
.disabled(!model.canTogglePlayback)
|
||||
iconButton("pip.exit", "Exit Compact Mode", action: model.toggleCompact)
|
||||
.disabled(model.compactWindow.isTransitioning)
|
||||
actionsMenu
|
||||
VStack(spacing: 4) {
|
||||
HStack(spacing: 16) {
|
||||
iconButton(model.state.status == PLAYER_PLAYING ? "pause.fill" : "play.fill",
|
||||
model.state.status == PLAYER_PLAYING ? "Pause" : "Play", action: model.togglePlayback)
|
||||
.disabled(!model.canTogglePlayback)
|
||||
iconButton("pip.exit", "Exit Compact Mode", action: model.toggleCompact)
|
||||
.disabled(model.compactWindow.isTransitioning)
|
||||
actionsMenu
|
||||
}
|
||||
if model.isNetworkMedia { bufferReadout }
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 8)
|
||||
.padding(.vertical, model.isNetworkMedia ? 6 : 8)
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
.background(PlayerGlass().clipShape(RoundedRectangle(cornerRadius: 10)))
|
||||
.onHover(perform: model.setControlsHovered)
|
||||
@@ -924,6 +944,7 @@ private struct PlayerContent: View {
|
||||
}
|
||||
.font(.system(size: 11).monospacedDigit())
|
||||
.padding(.horizontal, 16)
|
||||
if model.isNetworkMedia { bufferReadout }
|
||||
}
|
||||
.padding(.vertical, 11)
|
||||
// Optical centering accounts for symbol insets and the native seek thumb.
|
||||
@@ -1015,14 +1036,14 @@ private struct PlayerContent: View {
|
||||
.allowsHitTesting(false)
|
||||
.accessibilityHidden(!model.controlsVisible)
|
||||
compactControls
|
||||
.position(x: geometry.size.width / 2, y: geometry.size.height - 28)
|
||||
.position(x: geometry.size.width / 2, y: geometry.size.height - compactControlHeight / 2 - 8)
|
||||
} else {
|
||||
titlebar
|
||||
}
|
||||
|
||||
if model.canTogglePlayback && !model.isCompact {
|
||||
playbackControls
|
||||
.frame(width: 440, height: 68)
|
||||
.frame(width: 440, height: controlHeight)
|
||||
.background {
|
||||
PlayerGlass()
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
@@ -1040,7 +1061,7 @@ private struct PlayerContent: View {
|
||||
}
|
||||
.scaleEffect(min(1, (geometry.size.width - 32) / 440))
|
||||
.frame(width: min(440, geometry.size.width - 32),
|
||||
height: 68 * min(1, (geometry.size.width - 32) / 440))
|
||||
height: controlHeight * min(1, (geometry.size.width - 32) / 440))
|
||||
.onGeometryChange(for: CGSize.self) { $0.size } action: { controlSize = $0 }
|
||||
.position(controllerPosition(in: geometry.size))
|
||||
.opacity(model.controlsVisible ? 1 : 0)
|
||||
|
||||
Reference in New Issue
Block a user