From aabf5313fac649fa626792953fe9e587d630f6e2 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Sat, 9 May 2026 14:50:38 +0200 Subject: [PATCH] Expose Background Playback toggle on tvOS, default off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surfaces the existing iOS/macOS Background Playback setting in the tvOS Playback settings, defaulting to off so audio stops when the user leaves the app via the TV button. Pauses playback on .background/.inactive when the toggle is off, regardless of audio route — the user's setting wins over AirPlay/HomePod handoff. Also auto-shows the tvOS player controls when returning to foreground so the paused state is immediately visible and actionable. --- Yattee/Core/Settings/SettingsManager+Playback.swift | 10 +++++++++- Yattee/Services/Player/PlayerService.swift | 9 +++++++++ Yattee/Views/Player/tvOS/TVPlayerView.swift | 8 ++++++++ Yattee/Views/Settings/PlaybackSettingsView.swift | 2 -- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Yattee/Core/Settings/SettingsManager+Playback.swift b/Yattee/Core/Settings/SettingsManager+Playback.swift index 11c5e735..fe21e5db 100644 --- a/Yattee/Core/Settings/SettingsManager+Playback.swift +++ b/Yattee/Core/Settings/SettingsManager+Playback.swift @@ -40,7 +40,7 @@ extension SettingsManager { var backgroundPlaybackEnabled: Bool { get { if let cached = _backgroundPlaybackEnabled { return cached } - return bool(for: .backgroundPlayback, default: true) + return bool(for: .backgroundPlayback, default: backgroundPlaybackDefault) } set { _backgroundPlaybackEnabled = newValue @@ -48,6 +48,14 @@ extension SettingsManager { } } + private var backgroundPlaybackDefault: Bool { + #if os(tvOS) + return false + #else + return true + #endif + } + /// tvOS only: when enabled, the Siri remote Menu button closes the video /// (clears queue, stops playback) instead of only collapsing the player. /// When enabled, the explicit top-bar close button is hidden. diff --git a/Yattee/Services/Player/PlayerService.swift b/Yattee/Services/Player/PlayerService.swift index c706568b..ecaedd3d 100644 --- a/Yattee/Services/Player/PlayerService.swift +++ b/Yattee/Services/Player/PlayerService.swift @@ -650,6 +650,15 @@ final class PlayerService { #else let isPiPActive = false #endif + + #if os(tvOS) + LoggingService.shared.debug("PlayerService[tvOS-bg]: phase=\(phase) bgEnabled=\(backgroundEnabled) playbackState=\(state.playbackState)", category: .player) + if (phase == .background || phase == .inactive), !backgroundEnabled, state.playbackState == .playing { + LoggingService.shared.debug("PlayerService[tvOS-bg]: pausing playback (phase=\(phase), backgroundPlaybackEnabled=false)", category: .player) + pause() + } + #endif + currentBackend?.handleScenePhase(phase, backgroundEnabled: backgroundEnabled, isPiPActive: isPiPActive) } diff --git a/Yattee/Views/Player/tvOS/TVPlayerView.swift b/Yattee/Views/Player/tvOS/TVPlayerView.swift index 6b9ee76b..4e9b3d20 100644 --- a/Yattee/Views/Player/tvOS/TVPlayerView.swift +++ b/Yattee/Views/Player/tvOS/TVPlayerView.swift @@ -31,6 +31,7 @@ enum TVPlayerFocusTarget: Hashable { struct TVPlayerView: View { @Environment(\.appEnvironment) private var appEnvironment @Environment(\.dismiss) private var dismiss + @Environment(\.scenePhase) private var scenePhase // MARK: - State @@ -397,6 +398,13 @@ struct TVPlayerView: View { showControls() } } + // When app returns to foreground (e.g. after auto-pause from background), + // surface the controls so the user can immediately resume or navigate. + .onChange(of: scenePhase) { oldPhase, newPhase in + if newPhase == .active && oldPhase != .active { + showControls() + } + } } // MARK: - Failure Overlays diff --git a/Yattee/Views/Settings/PlaybackSettingsView.swift b/Yattee/Views/Settings/PlaybackSettingsView.swift index 176c6626..19c7a8d2 100644 --- a/Yattee/Views/Settings/PlaybackSettingsView.swift +++ b/Yattee/Views/Settings/PlaybackSettingsView.swift @@ -271,12 +271,10 @@ private struct BehaviorSection: View { } } - #if os(iOS) || os(macOS) Toggle( String(localized: "settings.playback.backgroundPlayback"), isOn: $settings.backgroundPlaybackEnabled ) - #endif #if os(tvOS) Toggle(