From 8f00fe012fdaded95638db463bd8ca2c1254ef86 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Sat, 18 Apr 2026 00:29:59 +0200 Subject: [PATCH] Add tvOS setting to close video with Menu button When enabled, the Siri remote Menu button stops playback and clears the queue instead of only collapsing the player, and the explicit top-bar close (X) button is hidden. --- Yattee/Core/Settings/SettingsKey.swift | 1 + .../Settings/SettingsManager+Playback.swift | 14 +++++++++++++ Yattee/Core/SettingsManager.swift | 2 ++ Yattee/Localizable.xcstrings | 20 +++++++++++++++++++ .../Player/tvOS/TVPlayerControlsView.swift | 20 +++++++++++-------- Yattee/Views/Player/tvOS/TVPlayerView.swift | 3 +++ .../Views/Settings/PlaybackSettingsView.swift | 17 +++++++++++++++- 7 files changed, 68 insertions(+), 9 deletions(-) diff --git a/Yattee/Core/Settings/SettingsKey.swift b/Yattee/Core/Settings/SettingsKey.swift index b30aabe2..c65dd229 100644 --- a/Yattee/Core/Settings/SettingsKey.swift +++ b/Yattee/Core/Settings/SettingsKey.swift @@ -24,6 +24,7 @@ enum SettingsKey: String, CaseIterable { case preferredAudioLanguage case preferredSubtitlesLanguage case resumeAction + case tvOSMenuButtonClosesVideo // SponsorBlock case sponsorBlockEnabled diff --git a/Yattee/Core/Settings/SettingsManager+Playback.swift b/Yattee/Core/Settings/SettingsManager+Playback.swift index 1050896a..e40d37ce 100644 --- a/Yattee/Core/Settings/SettingsManager+Playback.swift +++ b/Yattee/Core/Settings/SettingsManager+Playback.swift @@ -48,6 +48,20 @@ extension SettingsManager { } } + /// 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. + var tvOSMenuButtonClosesVideo: Bool { + get { + if let cached = _tvOSMenuButtonClosesVideo { return cached } + return bool(for: .tvOSMenuButtonClosesVideo, default: false) + } + set { + _tvOSMenuButtonClosesVideo = newValue + set(newValue, for: .tvOSMenuButtonClosesVideo) + } + } + /// Whether DASH streams are enabled (MPV only). /// Disabled by default as DASH can be unreliable with some Invidious instances. var dashEnabled: Bool { diff --git a/Yattee/Core/SettingsManager.swift b/Yattee/Core/SettingsManager.swift index ef4e329e..a096a188 100644 --- a/Yattee/Core/SettingsManager.swift +++ b/Yattee/Core/SettingsManager.swift @@ -39,6 +39,7 @@ final class SettingsManager { var _preferredSubtitlesLanguage: String? var _playerVolume: Float? var _resumeAction: ResumeAction? + var _tvOSMenuButtonClosesVideo: Bool? // SponsorBlock var _sponsorBlockEnabled: Bool? @@ -410,6 +411,7 @@ final class SettingsManager { _preferredSubtitlesLanguage = nil _playerVolume = nil _resumeAction = nil + _tvOSMenuButtonClosesVideo = nil _sponsorBlockEnabled = nil _sponsorBlockCategories = nil _sponsorBlockAPIURL = nil diff --git a/Yattee/Localizable.xcstrings b/Yattee/Localizable.xcstrings index 91192006..6f882446 100644 --- a/Yattee/Localizable.xcstrings +++ b/Yattee/Localizable.xcstrings @@ -11845,6 +11845,26 @@ } } }, + "settings.playback.tvOSMenuButtonClosesVideo" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu Button Closes Video" + } + } + } + }, + "settings.playback.tvOSMenuButtonClosesVideo.footer" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "When enabled, the Siri remote Menu button stops playback and clears the queue. The close (X) button is hidden from the player. When disabled, Menu collapses the player and playback continues in the background." + } + } + } + }, "settings.playback.behavior.header" : { "localizations" : { "en" : { diff --git a/Yattee/Views/Player/tvOS/TVPlayerControlsView.swift b/Yattee/Views/Player/tvOS/TVPlayerControlsView.swift index 58f3d2ac..5a9ea092 100644 --- a/Yattee/Views/Player/tvOS/TVPlayerControlsView.swift +++ b/Yattee/Views/Player/tvOS/TVPlayerControlsView.swift @@ -170,15 +170,19 @@ struct TVPlayerControlsView: View { // Close button — stops playback and dismisses. // Menu button only hides the player (keeps background playback), // so an explicit Close is kept here, icon-only in the top bar. - Button { - onClose() - } label: { - Image(systemName: "xmark") - .font(.system(size: 26, weight: .semibold)) + // When `tvOSMenuButtonClosesVideo` is enabled, the Menu button + // takes over this role and the explicit button is hidden. + if appEnvironment?.settingsManager.tvOSMenuButtonClosesVideo != true { + Button { + onClose() + } label: { + Image(systemName: "xmark") + .font(.system(size: 26, weight: .semibold)) + } + .buttonStyle(TVCloseButtonStyle()) + .focused($focusedControl, equals: .closeButton) + .accessibilityLabel(Text("player.controls.close")) } - .buttonStyle(TVCloseButtonStyle()) - .focused($focusedControl, equals: .closeButton) - .accessibilityLabel(Text("player.controls.close")) } } diff --git a/Yattee/Views/Player/tvOS/TVPlayerView.swift b/Yattee/Views/Player/tvOS/TVPlayerView.swift index c93a30d7..2880d498 100644 --- a/Yattee/Views/Player/tvOS/TVPlayerView.swift +++ b/Yattee/Views/Player/tvOS/TVPlayerView.swift @@ -667,6 +667,9 @@ struct TVPlayerView: View { } else if controlsVisible { // Fifth: hide controls hideControls() + } else if appEnvironment?.settingsManager.tvOSMenuButtonClosesVideo == true { + // Sixth (Menu-closes mode): fully close the video like the xmark button + closeVideo() } else { // Sixth: dismiss player (controls already hidden) dismissPlayer() diff --git a/Yattee/Views/Settings/PlaybackSettingsView.swift b/Yattee/Views/Settings/PlaybackSettingsView.swift index 0f56b8e5..676dfbd6 100644 --- a/Yattee/Views/Settings/PlaybackSettingsView.swift +++ b/Yattee/Views/Settings/PlaybackSettingsView.swift @@ -240,7 +240,7 @@ private struct BehaviorSection: View { @Bindable var settings: SettingsManager var body: some View { - Section(String(localized: "settings.playback.behavior.header")) { + Section { PlatformMenuPicker( String(localized: "settings.playback.resumeAction"), selection: $settings.resumeAction @@ -256,6 +256,21 @@ private struct BehaviorSection: View { isOn: $settings.backgroundPlaybackEnabled ) #endif + + #if os(tvOS) + Toggle( + String(localized: "settings.playback.tvOSMenuButtonClosesVideo"), + isOn: $settings.tvOSMenuButtonClosesVideo + ) + #endif + } header: { + Text(String(localized: "settings.playback.behavior.header")) + } footer: { + #if os(tvOS) + Text(String(localized: "settings.playback.tvOSMenuButtonClosesVideo.footer")) + #else + EmptyView() + #endif } } }