diff --git a/Yattee/Core/Settings/SettingsKey.swift b/Yattee/Core/Settings/SettingsKey.swift index b5e8ec09..1a9ac0de 100644 --- a/Yattee/Core/Settings/SettingsKey.swift +++ b/Yattee/Core/Settings/SettingsKey.swift @@ -20,7 +20,6 @@ enum SettingsKey: String, CaseIterable { case cellularQuality case autoplay case backgroundPlayback - case dashEnabled case preferredAudioLanguage case preferredSubtitlesLanguage case resumeAction diff --git a/Yattee/Core/Settings/SettingsManager+Playback.swift b/Yattee/Core/Settings/SettingsManager+Playback.swift index fe21e5db..318a0657 100644 --- a/Yattee/Core/Settings/SettingsManager+Playback.swift +++ b/Yattee/Core/Settings/SettingsManager+Playback.swift @@ -70,19 +70,6 @@ extension SettingsManager { } } - /// Whether DASH streams are enabled (MPV only). - /// Disabled by default as DASH can be unreliable with some Invidious instances. - var dashEnabled: Bool { - get { - if let cached = _dashEnabled { return cached } - return bool(for: .dashEnabled, default: false) - } - set { - _dashEnabled = newValue - set(newValue, for: .dashEnabled) - } - } - var allowSoftwareDecodedFormats: Bool { get { if let cached = _allowSoftwareDecodedFormats { return cached } diff --git a/Yattee/Core/SettingsManager.swift b/Yattee/Core/SettingsManager.swift index e3832b75..56e43382 100644 --- a/Yattee/Core/SettingsManager.swift +++ b/Yattee/Core/SettingsManager.swift @@ -34,7 +34,6 @@ final class SettingsManager { var _preferredQuality: VideoQuality? var _cellularQuality: VideoQuality? var _backgroundPlaybackEnabled: Bool? - var _dashEnabled: Bool? var _preferredAudioLanguage: String? var _preferredSubtitlesLanguage: String? var _playerVolume: Float? @@ -415,7 +414,6 @@ final class SettingsManager { _preferredQuality = nil _cellularQuality = nil _backgroundPlaybackEnabled = nil - _dashEnabled = nil _preferredAudioLanguage = nil _preferredSubtitlesLanguage = nil _playerVolume = nil diff --git a/Yattee/Localizable.xcstrings b/Yattee/Localizable.xcstrings index 9c8eb914..ea0a6cfe 100644 --- a/Yattee/Localizable.xcstrings +++ b/Yattee/Localizable.xcstrings @@ -12181,16 +12181,6 @@ } } }, - "settings.playback.dash" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enable DASH" - } - } - } - }, "settings.playback.macOS.autoResizePlayer" : { "comment" : "Toggle for automatically resizing player window to match video aspect ratio", "localizations" : { diff --git a/Yattee/Services/Player/BackendSwitcher.swift b/Yattee/Services/Player/BackendSwitcher.swift index b7764435..36918d13 100644 --- a/Yattee/Services/Player/BackendSwitcher.swift +++ b/Yattee/Services/Player/BackendSwitcher.swift @@ -306,16 +306,13 @@ final class BackendSwitcher { filteredMuxed = muxedStreams } - // Sort: prefer non-HLS/DASH (progressive) formats, then by resolution + // Sort: prefer progressive formats, then HLS, and only fall back to DASH as a last + // resort (DASH should never be chosen when any other format is available). let sortedMuxed = filteredMuxed.sorted { s1, s2 in - let format1 = StreamFormat.detect(from: s1) - let format2 = StreamFormat.detect(from: s2) - let isAdaptive1 = format1 == .hls || format1 == .dash - let isAdaptive2 = format2 == .hls || format2 == .dash - - // Prefer progressive formats for non-live content - if isAdaptive1 != isAdaptive2 { - return !isAdaptive1 // non-adaptive (false) comes first + let rank1 = muxedFormatRank(StreamFormat.detect(from: s1)) + let rank2 = muxedFormatRank(StreamFormat.detect(from: s2)) + if rank1 != rank2 { + return rank1 < rank2 } return (s1.resolution ?? .p360) > (s2.resolution ?? .p360) } @@ -332,6 +329,17 @@ final class BackendSwitcher { return nil } + /// Ranks muxed formats for fallback selection (lower = preferred). + /// Progressive formats win, then HLS; DASH is the last resort and is only + /// selected when it is the only format available. + private func muxedFormatRank(_ format: StreamFormat) -> Int { + switch format { + case .dash: return 2 + case .hls: return 1 + default: return 0 // progressive + } + } + /// Returns codec priority for video streams (higher = better for MPV). /// AV1 > VP9 > H.264/AVC private func videoCodecPriority(_ codec: String?) -> Int { diff --git a/Yattee/Services/Player/PlayerService.swift b/Yattee/Services/Player/PlayerService.swift index 54665eee..fadeef90 100644 --- a/Yattee/Services/Player/PlayerService.swift +++ b/Yattee/Services/Player/PlayerService.swift @@ -1901,7 +1901,6 @@ final class PlayerService { private func selectStreams(for backendType: PlayerBackendType, from streams: [Stream]) -> (video: Stream, audio: Stream?)? { let supportedFormats = backendType.supportedFormats - let dashEnabled = settingsManager?.dashEnabled ?? false // Get user's original quality preference (before network adjustments) let userPreferredQuality = settingsManager?.preferredQuality ?? .auto @@ -1932,7 +1931,6 @@ final class PlayerService { let muxedStreams = streams.filter { stream in let format = StreamFormat.detect(from: stream) guard supportedFormats.contains(format) else { return false } - if format == .dash && !dashEnabled { return false } // Only include HLS/DASH if they have video (audio-only HLS/DASH should be treated as audio streams) if format == .hls || format == .dash { return !stream.isAudioOnly @@ -1972,7 +1970,7 @@ final class PlayerService { LoggingService.shared.debug("Stream selection: Using HLS for live stream", category: .player) return (hlsStream, nil) } - if dashEnabled, let dashStream = muxedStreams.first(where: { StreamFormat.detect(from: $0) == .dash }) { + if let dashStream = muxedStreams.first(where: { StreamFormat.detect(from: $0) == .dash }) { LoggingService.shared.debug("Stream selection: Using DASH for live stream", category: .player) return (dashStream, nil) } @@ -2065,16 +2063,13 @@ final class PlayerService { filteredMuxed = muxedStreams } - // Sort: prefer non-HLS/DASH (progressive) formats, then by resolution + // Sort: prefer progressive formats, then HLS, and only fall back to DASH as a last + // resort (DASH should never be chosen when any other format is available). let sortedMuxed = filteredMuxed.sorted { s1, s2 in - let format1 = StreamFormat.detect(from: s1) - let format2 = StreamFormat.detect(from: s2) - let isAdaptive1 = format1 == .hls || format1 == .dash - let isAdaptive2 = format2 == .hls || format2 == .dash - - // Prefer progressive formats for non-live content - if isAdaptive1 != isAdaptive2 { - return !isAdaptive1 // non-adaptive (false) comes first + let rank1 = muxedFormatRank(StreamFormat.detect(from: s1)) + let rank2 = muxedFormatRank(StreamFormat.detect(from: s2)) + if rank1 != rank2 { + return rank1 < rank2 } return (s1.resolution ?? .p360) > (s2.resolution ?? .p360) } @@ -2091,6 +2086,17 @@ final class PlayerService { return nil } + /// Ranks muxed formats for fallback selection (lower = preferred). + /// Progressive formats win, then HLS; DASH is the last resort and is only + /// selected when it is the only format available. + private func muxedFormatRank(_ format: StreamFormat) -> Int { + switch format { + case .dash: return 2 + case .hls: return 1 + default: return 0 // progressive + } + } + /// Returns codec priority for video streams (higher = better). /// Prefers hardware-decodable codecs for battery efficiency. private func videoCodecPriority(_ codec: String?) -> Int { diff --git a/Yattee/Views/Player/ExpandedPlayerSheet.swift b/Yattee/Views/Player/ExpandedPlayerSheet.swift index 61c0139c..f911eb3d 100644 --- a/Yattee/Views/Player/ExpandedPlayerSheet.swift +++ b/Yattee/Views/Player/ExpandedPlayerSheet.swift @@ -667,12 +667,11 @@ private struct PlayerSheetsModifier: ViewModifier { private var qualitySelectorSheet: some View { if let playerService { let supportedFormats = playerService.currentBackendType.supportedFormats - let dashEnabled = appEnvironment?.settingsManager.dashEnabled ?? false QualitySelectorView( streams: playerService.availableStreams.filter { stream in let format = StreamFormat.detect(from: stream) - // Filter out DASH streams if disabled in settings - if format == .dash && !dashEnabled { + // DASH is never offered for manual selection + if format == .dash { return false } return supportedFormats.contains(format) diff --git a/Yattee/Views/Player/tvOS/TVPlayerView.swift b/Yattee/Views/Player/tvOS/TVPlayerView.swift index 8459071a..8c0e6f86 100644 --- a/Yattee/Views/Player/tvOS/TVPlayerView.swift +++ b/Yattee/Views/Player/tvOS/TVPlayerView.swift @@ -143,13 +143,13 @@ struct TVPlayerView: View { @ViewBuilder private var qualityPanelContent: some View { if let playerService { - let dashEnabled = appEnvironment?.settingsManager.dashEnabled ?? false let supportedFormats = playerService.currentBackendType.supportedFormats QualitySelectorView( streams: playerService.availableStreams.filter { stream in let format = StreamFormat.detect(from: stream) - if format == .dash && !dashEnabled { + // DASH is never offered for manual selection + if format == .dash { return false } return supportedFormats.contains(format) diff --git a/Yattee/Views/Settings/AdvancedSettingsView.swift b/Yattee/Views/Settings/AdvancedSettingsView.swift index b2762655..fff0d182 100644 --- a/Yattee/Views/Settings/AdvancedSettingsView.swift +++ b/Yattee/Views/Settings/AdvancedSettingsView.swift @@ -206,13 +206,6 @@ struct AdvancedSettingsView: View { Label(String(localized: "settings.advanced.mpv.edl"), systemImage: "arrow.trianglehead.merge") } - Toggle(isOn: Binding( - get: { settingsManager.dashEnabled }, - set: { settingsManager.dashEnabled = $0 } - )) { - Label(String(localized: "settings.playback.dash"), systemImage: "bolt.horizontal") - } - #if os(tvOS) NavigationLink { TVSidebarDetailContainer(