diff --git a/Model/QualityProfile.swift b/Model/QualityProfile.swift index a2c1a1d7..7dfba1a5 100644 --- a/Model/QualityProfile.swift +++ b/Model/QualityProfile.swift @@ -4,7 +4,6 @@ import Foundation struct QualityProfile: Hashable, Identifiable, Defaults.Serializable { static var bridge = QualityProfileBridge() static var defaultProfile = Self(id: "default", backend: .mpv, resolution: .hd720p60, formats: [.stream]) - static var highQualityProfile = Self(id: "highQuality", backend: .mpv, resolution: .hd2160p60, formats: [.webm, .mp4, .av1, .avc1]) enum Format: String, CaseIterable, Identifiable, Defaults.Serializable { case hls diff --git a/Model/QualityProfilesModel.swift b/Model/QualityProfilesModel.swift index fa60e455..aa8b4f1d 100644 --- a/Model/QualityProfilesModel.swift +++ b/Model/QualityProfilesModel.swift @@ -15,11 +15,7 @@ struct QualityProfilesModel { #endif func find(_ id: QualityProfile.ID) -> QualityProfile? { - if id == "default" { - return QualityProfile.defaultProfile - } else if id == "highQuality" { - return QualityProfile.highQualityProfile - } + guard id != "default" else { return QualityProfile.defaultProfile } return Defaults[.qualityProfiles].first { $0.id == id } } @@ -47,6 +43,14 @@ struct QualityProfilesModel { Defaults[.chargingNonCellularProfile] = qualityProfile.id } + func reset() { + Defaults.reset(.qualityProfiles) + Defaults.reset(.batteryCellularProfile) + Defaults.reset(.batteryNonCellularProfile) + Defaults.reset(.chargingCellularProfile) + Defaults.reset(.chargingNonCellularProfile) + } + #if os(iOS) private func findCurrentConnection() -> Reachability.Connection? { do { diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index 6dd7438c..07f2719a 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -38,11 +38,55 @@ extension Defaults.Keys { static let captionsLanguageCode = Key("captionsLanguageCode") static let activeBackend = Key("activeBackend", default: .mpv) - static let qualityProfiles = Key<[QualityProfile]>("qualityProfiles", default: [QualityProfile.defaultProfile, QualityProfile.highQualityProfile]) - static let batteryCellularProfile = Key("batteryCellularProfile", default: QualityProfile.defaultProfile.id) - static let batteryNonCellularProfile = Key("batteryNonCellularProfile", default: QualityProfile.defaultProfile.id) - static let chargingCellularProfile = Key("chargingCellularProfile", default: QualityProfile.defaultProfile.id) - static let chargingNonCellularProfile = Key("chargingNonCellularProfile", default: QualityProfile.defaultProfile.id) + + static let hd2160pMPVProfile = QualityProfile(backend: .mpv, resolution: .hd2160p60, formats: QualityProfile.Format.allCases) + static let hd1080pMPVProfile = QualityProfile(backend: .mpv, resolution: .hd1080p60, formats: QualityProfile.Format.allCases) + static let hd720pMPVProfile = QualityProfile(backend: .mpv, resolution: .hd720p60, formats: QualityProfile.Format.allCases) + static let hd720pAVPlayerProfile = QualityProfile(backend: .appleAVPlayer, resolution: .hd720p60, formats: [.hls, .stream]) + static let sd360pAVPlayerProfile = QualityProfile(backend: .appleAVPlayer, resolution: .sd360p30, formats: [.hls, .stream]) + + #if os(iOS) + static let qualityProfilesDefault = UIDevice.current.userInterfaceIdiom == .pad ? [ + hd2160pMPVProfile, + hd1080pMPVProfile, + hd720pAVPlayerProfile, + sd360pAVPlayerProfile + ] : [ + hd1080pMPVProfile, + hd720pAVPlayerProfile, + sd360pAVPlayerProfile + ] + + static let batteryCellularProfileDefault = hd720pAVPlayerProfile.id + static let batteryNonCellularProfileDefault = hd720pAVPlayerProfile.id + static let chargingCellularProfileDefault = hd720pAVPlayerProfile.id + static let chargingNonCellularProfileDefault = hd1080pMPVProfile.id + #elseif os(tvOS) + static let qualityProfilesDefault = [ + hd2160pMPVProfile, + hd1080pMPVProfile, + hd720pMPVProfile + ] + static let batteryCellularProfileDefault = hd1080pMPVProfile.id + static let batteryNonCellularProfileDefault = hd1080pMPVProfile.id + static let chargingCellularProfileDefault = hd1080pMPVProfile.id + static let chargingNonCellularProfileDefault = hd1080pMPVProfile.id + #else + static let qualityProfilesDefault = [ + hd2160pMPVProfile, + hd1080pMPVProfile, + hd720pAVPlayerProfile + ] + static let batteryCellularProfileDefault = hd1080pMPVProfile.id + static let batteryNonCellularProfileDefault = hd1080pMPVProfile.id + static let chargingCellularProfileDefault = hd1080pMPVProfile.id + static let chargingNonCellularProfileDefault = hd1080pMPVProfile.id + #endif + static let qualityProfiles = Key<[QualityProfile]>("qualityProfiles", default: qualityProfilesDefault) + static let batteryCellularProfile = Key("batteryCellularProfile", default: batteryCellularProfileDefault) + static let batteryNonCellularProfile = Key("batteryNonCellularProfile", default: batteryNonCellularProfileDefault) + static let chargingCellularProfile = Key("chargingCellularProfile", default: chargingCellularProfileDefault) + static let chargingNonCellularProfile = Key("chargingNonCellularProfile", default: chargingNonCellularProfileDefault) static let playerSidebar = Key("playerSidebar", default: PlayerSidebarSetting.defaultValue) static let playerInstanceID = Key("playerInstance") diff --git a/Shared/Settings/LocationsSettings.swift b/Shared/Settings/LocationsSettings.swift index 5d647727..77ebe818 100644 --- a/Shared/Settings/LocationsSettings.swift +++ b/Shared/Settings/LocationsSettings.swift @@ -68,6 +68,7 @@ struct LocationsSettings: View { Section(header: SettingsHeader(text: "Custom Locations")) { #if os(macOS) InstancesSettings() + .environmentObject(model) #else ForEach(instances) { instance in AccountsNavigationLink(instance: instance) diff --git a/Shared/Settings/QualityProfileForm.swift b/Shared/Settings/QualityProfileForm.swift index bae10afe..a8319c3a 100644 --- a/Shared/Settings/QualityProfileForm.swift +++ b/Shared/Settings/QualityProfileForm.swift @@ -1,3 +1,4 @@ +import Defaults import SwiftUI struct QualityProfileForm: View { @@ -14,6 +15,8 @@ struct QualityProfileForm: View { @State private var resolution = ResolutionSetting.hd1080p60 @State private var formats = [QualityProfile.Format]() + @Default(.qualityProfiles) private var qualityProfiles + var qualityProfile: QualityProfile! { if let id = qualityProfileID { return QualityProfilesModel.shared.find(id) @@ -131,7 +134,7 @@ struct QualityProfileForm: View { .modifier(SettingsPickerModifier()) #if os(iOS) - return HStack { + HStack { Text("Resolution") Spacer() Menu { @@ -175,7 +178,7 @@ struct QualityProfileForm: View { } .modifier(SettingsPickerModifier()) #if os(iOS) - return HStack { + HStack { Text("Backend") Spacer() Menu { @@ -321,7 +324,12 @@ struct QualityProfileForm: View { if editing { QualityProfilesModel.shared.update(qualityProfile, formProfile) } else { + let wasEmpty = qualityProfiles.isEmpty QualityProfilesModel.shared.add(formProfile) + + if wasEmpty { + QualityProfilesModel.shared.applyToAll(formProfile) + } } presentationMode.wrappedValue.dismiss() diff --git a/Shared/Settings/QualitySettings.swift b/Shared/Settings/QualitySettings.swift index 3ee4287e..014d785f 100644 --- a/Shared/Settings/QualitySettings.swift +++ b/Shared/Settings/QualitySettings.swift @@ -5,8 +5,9 @@ struct QualitySettings: View { @State private var presentingProfileForm = false @State private var editedProfileID: QualityProfile.ID? - @Default(.qualityProfiles) private var qualityProfiles + @EnvironmentObject private var settings + @Default(.qualityProfiles) private var qualityProfiles @Default(.batteryCellularProfile) private var batteryCellularProfile @Default(.batteryNonCellularProfile) private var batteryNonCellularProfile @Default(.chargingCellularProfile) private var chargingCellularProfile @@ -75,6 +76,25 @@ struct QualitySettings: View { } } .frame(maxWidth: .infinity, alignment: .leading) + + HStack { + Button { + settings.presentAlert( + Alert( + title: Text("Are you sure you want to restore default quality profiles?"), + message: Text("This will remove all your custom profiles and return their default values. This cannot be reverted."), + primaryButton: .destructive(Text("Reset")) { + QualityProfilesModel.shared.reset() + }, + secondaryButton: .cancel() + ) + ) + } label: { + Text("Restore default profiles...") + .foregroundColor(.red) + } + Spacer() + } } }