Add audio-only music mode

Persistent per-platform mode that plays only the audio track to save
bandwidth and speed up loads. When enabled, stream selection picks the
best audio-only stream (preferred language, codec, bitrate) and never
fetches the video URL; toggling mid-video reloads at the current
position using the quality-switch path (live vid=no toggling is avoided
due to A/V desync).

- Toggle row in the player quality/settings sheet on iOS, macOS and
  tvOS, next to lock controls
- New audioMode control button type (red when active) for player
  controls and mini player layouts
- Picking a video quality explicitly turns the mode off; audio track
  picks keep it on and now work while audio-only is playing
- Stream URL refresh preserves audio-only playback instead of
  resurrecting video
- Queue preloads and history entries resolved before a toggle are
  discarded so selection re-runs with the current mode
- tvOS player shows the video thumbnail instead of a black screen
  during audio-only playback
- PiP button hidden for audio-only streams
This commit is contained in:
Arkadiusz Fal
2026-07-13 19:06:01 +02:00
parent 66f7602d08
commit 06087220ef
19 changed files with 352 additions and 85 deletions

View File

@@ -306,7 +306,7 @@ struct PlaybackCommands: Commands {
Text(String(localized: "menu.playback.pip"))
}
.keyboardShortcut("i", modifiers: [.command, .shift])
.disabled(!hasActiveVideo || !state.isPiPPossible)
.disabled(!hasActiveVideo || !state.isPiPPossible || state.currentStream?.isAudioOnly == true)
}
// MARK: - Close video button

View File

@@ -29,6 +29,7 @@ enum SettingsKey: String, CaseIterable {
case resumeAction
case tvOSMenuButtonClosesVideo
case allowSoftwareDecodedFormats
case audioOnlyMode
// SponsorBlock
case sponsorBlockEnabled
@@ -144,7 +145,7 @@ enum SettingsKey: String, CaseIterable {
/// in both UserDefaults and iCloud, so each platform family syncs independently.
var isPlatformSpecific: Bool {
switch self {
case .preferredQuality, .cellularQuality, .allowSoftwareDecodedFormats,
case .preferredQuality, .cellularQuality, .allowSoftwareDecodedFormats, .audioOnlyMode,
.macPlayerSeparateWindow, .macPlayerFloating, .listStyle,
.macControlsBarOffsetX, .macControlsBarOffsetY,
// Home layout different UI paradigms per platform

View File

@@ -81,6 +81,19 @@ extension SettingsManager {
}
}
/// Audio-only ("music") mode: when enabled, only the audio track is loaded
/// for every video until turned off. Persisted per platform.
var audioOnlyModeEnabled: Bool {
get {
if let cached = _audioOnlyModeEnabled { return cached }
return bool(for: .audioOnlyMode, default: false)
}
set {
_audioOnlyModeEnabled = newValue
set(newValue, for: .audioOnlyMode)
}
}
/// Preferred audio language code (e.g., "en", "de", "ja").
/// When set, audio streams in this language will be auto-selected and shown first.
/// nil means no preference (use original/default audio).

View File

@@ -44,6 +44,7 @@ final class SettingsManager {
var _resumeAction: ResumeAction?
var _tvOSMenuButtonClosesVideo: Bool?
var _allowSoftwareDecodedFormats: Bool?
var _audioOnlyModeEnabled: Bool?
// SponsorBlock
var _sponsorBlockEnabled: Bool?
@@ -435,6 +436,7 @@ final class SettingsManager {
_resumeAction = nil
_tvOSMenuButtonClosesVideo = nil
_allowSoftwareDecodedFormats = nil
_audioOnlyModeEnabled = nil
_sponsorBlockEnabled = nil
_sponsorBlockCategories = nil
_sponsorBlockAPIURL = nil