diff --git a/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift b/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift index 0fdef1a5..cf8c48cf 100644 --- a/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift +++ b/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift @@ -796,7 +796,7 @@ extension ExpandedPlayerSheet { closeVideo() }, onTogglePiP: { - if let mpvBackend = backend as? MPVBackend { + if let mpvBackend = playerService.currentBackend as? MPVBackend { mpvBackend.togglePiP() } }, @@ -873,9 +873,9 @@ extension ExpandedPlayerSheet { .offset(y: controlsVerticalOffset) } #elseif os(macOS) - if let backend = playerService?.currentBackend, - backend.backendType == .mpv, - let playerState, + // No backend gate: the backend is created only after the video + // details fetch, and controls should be visible while loading. + if let playerState, let playerService, playerState.pipState != .active && !playerState.showDebugOverlay { MacOSPlayerControlsView( @@ -897,7 +897,7 @@ extension ExpandedPlayerSheet { closeVideo() }, onTogglePiP: { - if let mpvBackend = backend as? MPVBackend { + if let mpvBackend = playerService.currentBackend as? MPVBackend { mpvBackend.togglePiP() } }, @@ -1142,9 +1142,9 @@ extension ExpandedPlayerSheet { .frame(width: controlsWidth, height: controlsHeight) } #elseif os(macOS) - if let backend = playerService?.currentBackend, - backend.backendType == .mpv, - let playerState, + // No backend gate: the backend is created only after the video + // details fetch, and controls should be visible while loading. + if let playerState, let playerService, playerState.pipState != .active && !playerState.showDebugOverlay { MacOSPlayerControlsView( @@ -1166,7 +1166,7 @@ extension ExpandedPlayerSheet { closeVideo() }, onTogglePiP: { - if let mpvBackend = backend as? MPVBackend { + if let mpvBackend = playerService.currentBackend as? MPVBackend { mpvBackend.togglePiP() } }, @@ -1447,7 +1447,7 @@ extension ExpandedPlayerSheet { closeVideo() }, onTogglePiP: { - if let mpvBackend = backend as? MPVBackend { + if let mpvBackend = playerService.currentBackend as? MPVBackend { mpvBackend.togglePiP() } }, @@ -1551,9 +1551,9 @@ extension ExpandedPlayerSheet { } } #elseif os(macOS) - if let backend = playerService?.currentBackend, - backend.backendType == .mpv, - let playerState, + // No backend gate: the backend is created only after the video + // details fetch, and controls should be visible while loading. + if let playerState, let playerService, playerState.pipState != .active && !playerState.showDebugOverlay { MacOSPlayerControlsView( @@ -1585,7 +1585,7 @@ extension ExpandedPlayerSheet { closeVideo() }, onTogglePiP: { - if let mpvBackend = backend as? MPVBackend { + if let mpvBackend = playerService.currentBackend as? MPVBackend { mpvBackend.togglePiP() } }, diff --git a/Yattee/Views/Player/macOS/MacOSPlayerControlsView.swift b/Yattee/Views/Player/macOS/MacOSPlayerControlsView.swift index 97104d55..76e2aadb 100644 --- a/Yattee/Views/Player/macOS/MacOSPlayerControlsView.swift +++ b/Yattee/Views/Player/macOS/MacOSPlayerControlsView.swift @@ -89,6 +89,11 @@ struct MacOSPlayerControlsView: View { /// Controls visibility - show when hovering, interacting, or paused private var shouldShowControls: Bool { + // Ended/failed overlays take over the surface; controls stay hidden + // regardless of hover or manual toggles. + if playerState.playbackState == .ended || playerState.isFailed { + return false + } if let showControls { return showControls } @@ -96,11 +101,9 @@ struct MacOSPlayerControlsView: View { if isHovering || isInteracting { return true } - // Show when paused, loading, or failed + // Show when paused or loading return playerState.playbackState == .paused || - playerState.playbackState == .loading || - playerState.playbackState == .ended || - playerState.isFailed + playerState.playbackState == .loading } /// Yattee Server URL used by `ChannelAvatarView` for avatar fallback. @@ -295,14 +298,13 @@ struct MacOSPlayerControlsView: View { if newState == .playing && shouldShowControls { startHideTimer() } - // Hide controls when video ends (ended overlay takes over) - if newState == .ended { - showControls = false - cancelHideTimer() + // A new load resets any manual show/hide override (e.g. hidden + // after ended/failed) so controls are visible while loading. + if newState == .loading { + showControls = nil } - // Hide controls when video fails - if case .failed = newState { - showControls = false + // Ended/failed hide via shouldShowControls; just stop the timer. + if newState == .ended || playerState.isFailed { cancelHideTimer() } }