From e0fe39d71760f2504aff816c8a58f81bcd214e7a Mon Sep 17 00:00:00 2001 From: eevee Date: Thu, 10 Oct 2024 17:55:25 +0300 Subject: [PATCH] actual fix for #437 and Do Not Replace Lyrics option --- .../DataLoaderServiceHooks.x.swift | 9 +- .../EeveeSpotify/Helpers/PopUpHelper.swift | 1 - .../CustomLyrics+AllTracksLyrics.x.swift | 27 ++++++ .../EeveeSpotify/Lyrics/CustomLyrics.x.swift | 34 ++----- .../Lyrics/Models/LyricsError.swift | 2 + .../Lyrics/Models/Settings/LyricsSource.swift | 14 +++ .../Extensions/UserDefaults+Extension.swift | 7 +- .../DynamicPremium+ModifyBootstrap.x.swift | 2 +- .../Premium/LikedSongsEnabler.x.swift | 2 +- .../Premium/ServerSidedReminder.x.swift | 50 +++++----- .../Premium/TrackRowsEnabler.x.swift | 2 +- .../Settings/Views/EeveeSettingsView.swift | 5 +- ...ricsSettingsView+LyricsSourceSection.swift | 79 +++++++++------- .../Sections/EeveeLyricsSettingsView.swift | 91 ++++++++++--------- .../Sections/EeveePatchingSettingsView.swift | 9 +- .../Views/Sections/EeveeUISettingsView.swift | 70 +++++++------- .../Views/Shared/CommonIssuesTipView.swift | 2 +- Sources/EeveeSpotify/Tweak.x.swift | 8 +- .../en.lproj/Localizable.strings | 10 +- .../ru.lproj/Localizable.strings | 12 ++- 20 files changed, 248 insertions(+), 188 deletions(-) create mode 100644 Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift diff --git a/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift b/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift index f02958f..eec67dc 100644 --- a/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift +++ b/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift @@ -6,8 +6,11 @@ class SPTDataLoaderServiceHook: ClassHook, SpotifySessionDelegate { // orion:new func shouldModify(_ url: URL) -> Bool { - let isModifyingCustomizeResponse = UserDefaults.patchType == .requests - return url.isLyrics || (url.isCustomize && isModifyingCustomizeResponse) + let isModifyingCustomizeResponse = PremiumPatchingGroup.isActive + let isModifyingLyrics = LyricsGroup.isActive + + return (url.isLyrics && isModifyingLyrics) + || (url.isCustomize && isModifyingCustomizeResponse) } func URLSession( @@ -82,7 +85,7 @@ class SPTDataLoaderServiceHook: ClassHook, SpotifySessionDelegate { return } - if url.isLyrics, response.statusCode != 200 { + if shouldModify(url), url.isLyrics, response.statusCode != 200 { let okResponse = HTTPURLResponse( url: url, statusCode: 200, diff --git a/Sources/EeveeSpotify/Helpers/PopUpHelper.swift b/Sources/EeveeSpotify/Helpers/PopUpHelper.swift index c587858..25036a3 100644 --- a/Sources/EeveeSpotify/Helpers/PopUpHelper.swift +++ b/Sources/EeveeSpotify/Helpers/PopUpHelper.swift @@ -39,7 +39,6 @@ struct PopUpHelper { dialog.update(model) dialog.setEventHandler({ state in - switch (state) { case .primary: onPrimaryClick?() diff --git a/Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift b/Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift new file mode 100644 index 0000000..6f8be07 --- /dev/null +++ b/Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift @@ -0,0 +1,27 @@ +import Orion + +class SPTPlayerTrackHook: ClassHook { + typealias Group = LyricsGroup + static let targetName = "SPTPlayerTrack" + + func metadata() -> [String:String] { + var meta = orig.metadata() + + meta["has_lyrics"] = "true" + return meta + } +} + +class LyricsScrollProviderHook: ClassHook { + typealias Group = LyricsGroup + + static var targetName: String { + return EeveeSpotify.isOldSpotifyVersion + ? "Lyrics_CoreImpl.ScrollProvider" + : "Lyrics_NPVCommunicatorImpl.ScrollProvider" + } + + func isEnabledForTrack(_ track: SPTPlayerTrack) -> Bool { + return true + } +} diff --git a/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift b/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift index 731cad7..30f9f24 100644 --- a/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift +++ b/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift @@ -1,32 +1,15 @@ import Orion import SwiftUI -class SPTPlayerTrackHook: ClassHook { - static let targetName = "SPTPlayerTrack" +// - func metadata() -> [String:String] { - var meta = orig.metadata() +struct LyricsGroup: HookGroup { } - meta["has_lyrics"] = "true" - return meta - } -} - -class LyricsScrollProviderHook: ClassHook { - - static var targetName: String { - return EeveeSpotify.isOldSpotifyVersion - ? "Lyrics_CoreImpl.ScrollProvider" - : "Lyrics_NPVCommunicatorImpl.ScrollProvider" - } - - func isEnabledForTrack(_ track: SPTPlayerTrack) -> Bool { - return true - } -} +// class LyricsFullscreenViewControllerHook: ClassHook { - + typealias Group = LyricsGroup + static var targetName: String { return EeveeSpotify.isOldSpotifyVersion ? "Lyrics_CoreImpl.FullscreenViewController" @@ -62,6 +45,7 @@ private var hasShownUnauthorizedPopUp = false // class LyricsOnlyViewControllerHook: ClassHook { + typealias Group = LyricsGroup static var targetName: String { return EeveeSpotify.isOldSpotifyVersion @@ -70,7 +54,6 @@ class LyricsOnlyViewControllerHook: ClassHook { } func viewDidLoad() { - orig.viewDidLoad() guard @@ -162,6 +145,7 @@ private func loadLyricsForCurrentTrack() throws { case .lrclib: LrcLibLyricsRepository() case .musixmatch: MusixmatchLyricsRepository.shared case .petit: PetitLyricsRepository() + case .notReplaced: throw LyricsError.InvalidSource } let lyricsDto: LyricsDto @@ -180,7 +164,6 @@ private func loadLyricsForCurrentTrack() throws { switch error { case .InvalidMusixmatchToken: - if !hasShownUnauthorizedPopUp { PopUpHelper.showPopUp( delayed: false, @@ -192,7 +175,6 @@ private func loadLyricsForCurrentTrack() throws { } case .MusixmatchRestricted: - if !hasShownRestrictedPopUp { PopUpHelper.showPopUp( delayed: false, @@ -226,7 +208,7 @@ private func loadLyricsForCurrentTrack() throws { lastLyricsState.areEmpty = lyricsDto.lines.isEmpty lastLyricsState.wasRomanized = lyricsDto.romanization == .romanized - || (lyricsDto.romanization == .canBeRomanized && UserDefaults.lyricsOptions.romanization) + || (lyricsDto.romanization == .canBeRomanized && UserDefaults.lyricsOptions.romanization) lastLyricsState.loadedSuccessfully = true diff --git a/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift b/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift index 47a18f1..70ffcea 100644 --- a/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift +++ b/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift @@ -7,6 +7,7 @@ enum LyricsError: Error, CustomStringConvertible { case DecodingError case NoSuchSong case UnknownError + case InvalidSource var description: String { switch self { @@ -16,6 +17,7 @@ enum LyricsError: Error, CustomStringConvertible { case .DecodingError: "decoding_error".localized case .NoCurrentTrack: "no_current_track".localized case .UnknownError: "unknown_error".localized + case .InvalidSource: "" } } } diff --git a/Sources/EeveeSpotify/Lyrics/Models/Settings/LyricsSource.swift b/Sources/EeveeSpotify/Lyrics/Models/Settings/LyricsSource.swift index 08bb047..d6ed810 100644 --- a/Sources/EeveeSpotify/Lyrics/Models/Settings/LyricsSource.swift +++ b/Sources/EeveeSpotify/Lyrics/Models/Settings/LyricsSource.swift @@ -5,6 +5,11 @@ enum LyricsSource: Int, CaseIterable, CustomStringConvertible { case lrclib case musixmatch case petit + case notReplaced + + static var allCases: [LyricsSource] { + return [.genius, .lrclib, .musixmatch, .petit] + } var description: String { switch self { @@ -12,6 +17,15 @@ enum LyricsSource: Int, CaseIterable, CustomStringConvertible { case .lrclib: "LRCLIB" case .musixmatch: "Musixmatch" case .petit: "PetitLyrics" + case .notReplaced: "Spotify" } } + + var isReplacing: Bool { self != .notReplaced } + + static var defaultSource: LyricsSource { + Locale.isInRegion("JP", orHasLanguage: "ja") + ? .petit + : .lrclib + } } diff --git a/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift b/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift index 5b62f52..9604450 100644 --- a/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift +++ b/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift @@ -1,7 +1,6 @@ import Foundation extension UserDefaults { - private static let defaults = UserDefaults.standard private static let lyricsSourceKey = "lyricsSource" @@ -21,11 +20,7 @@ extension UserDefaults { return LyricsSource(rawValue: rawValue)! } - if Locale.isInRegion("JP", orHasLanguage: "ja") { - return .petit - } - - return .lrclib + return LyricsSource.defaultSource } set (newSource) { defaults.set(newSource.rawValue, forKey: lyricsSourceKey) diff --git a/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift b/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift index 22c1655..7d6cbe2 100644 --- a/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift +++ b/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift @@ -69,7 +69,7 @@ class SpotifySessionDelegateBootstrapHook: ClassHook, SpotifySessionDe } else { UserDefaults.patchType = .requests - PremiumPatching().activate() + PremiumPatchingGroup().activate() } NSLog("[EeveeSpotify] Fetched bootstrap, \(UserDefaults.patchType) was set") diff --git a/Sources/EeveeSpotify/Premium/LikedSongsEnabler.x.swift b/Sources/EeveeSpotify/Premium/LikedSongsEnabler.x.swift index 7179c38..661f8e9 100644 --- a/Sources/EeveeSpotify/Premium/LikedSongsEnabler.x.swift +++ b/Sources/EeveeSpotify/Premium/LikedSongsEnabler.x.swift @@ -6,7 +6,7 @@ private let likedTracksRow: [String: Any] = [ ] class HUBViewModelBuilderImplementationHook: ClassHook { - typealias Group = PremiumPatching + typealias Group = PremiumPatchingGroup static let targetName: String = "HUBViewModelBuilderImplementation" func addJSONDictionary(_ dictionary: NSDictionary?) { diff --git a/Sources/EeveeSpotify/Premium/ServerSidedReminder.x.swift b/Sources/EeveeSpotify/Premium/ServerSidedReminder.x.swift index 92922d0..2130212 100644 --- a/Sources/EeveeSpotify/Premium/ServerSidedReminder.x.swift +++ b/Sources/EeveeSpotify/Premium/ServerSidedReminder.x.swift @@ -2,7 +2,7 @@ import Orion import UIKit class StreamQualitySettingsSectionHook: ClassHook { - typealias Group = PremiumPatching + typealias Group = PremiumPatchingGroup static let targetName = "StreamQualitySettingsSection" func shouldResetSelection() -> Bool { @@ -15,17 +15,8 @@ class StreamQualitySettingsSectionHook: ClassHook { } } -// - -private func showOfflineModePopUp() { - PopUpHelper.showPopUp( - message: "playlist_downloading_popup".localized, - buttonText: "OK".uiKitLocalized - ) -} - class ContentOffliningUIHelperImplementationHook: ClassHook { - typealias Group = PremiumPatching + typealias Group = PremiumPatchingGroup static let targetName = "Offline_ContentOffliningUIImpl.ContentOffliningUIHelperImplementation" func downloadToggledWithCurrentAvailability( @@ -34,18 +25,31 @@ class ContentOffliningUIHelperImplementationHook: ClassHook { removeAction: NSObject, pageIdentifier: String, pageURI: URL - ) -> String { - if pageIdentifier == "spotify:local-files" { - return orig.downloadToggledWithCurrentAvailability( - availability, - addAction: addAction, - removeAction: removeAction, - pageIdentifier: pageIdentifier, - pageURI: pageURI - ) - } + ) -> String? { + let isPlaylist = [ + "free-tier-playlist", + "playlist/ondemand" + ].contains(pageIdentifier) - showOfflineModePopUp() - return pageIdentifier + PopUpHelper.showPopUp( + message: "playlist_downloading_popup".localized, + buttonText: "OK".uiKitLocalized, + secondButtonText: isPlaylist + ? "download_local_playlist".localized + : nil, + onSecondaryClick: isPlaylist + ? { + _ = self.orig.downloadToggledWithCurrentAvailability( + availability, + addAction: addAction, + removeAction: removeAction, + pageIdentifier: pageIdentifier, + pageURI: pageURI + ) + } + : nil + ) + + return nil } } diff --git a/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift b/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift index 73ab869..9287907 100644 --- a/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift +++ b/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift @@ -1,7 +1,7 @@ import Orion class SPTFreeTierArtistHubRemoteURLResolverHook: ClassHook { - typealias Group = PremiumPatching + typealias Group = PremiumPatchingGroup static let targetName = "SPTFreeTierArtistHubRemoteURLResolver" func initWithViewURI( diff --git a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift index 529f325..f036ca0 100644 --- a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift +++ b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift @@ -3,6 +3,7 @@ import UIKit struct EeveeSettingsView: View { let navigationController: UINavigationController + static let spotifyAccentColor = Color(hex: "#1ed760") @State private var hasShownCommonIssuesTip = UserDefaults.hasShownCommonIssuesTip @State private var isClearingData = false @@ -18,9 +19,7 @@ struct EeveeSettingsView: View { init(navigationController: UINavigationController) { self.navigationController = navigationController - - let spotifyAccentColor = UIColor(Color(hex: "#1ed760")) - UIView.appearance().tintColor = spotifyAccentColor + UIView.appearance().tintColor = UIColor(EeveeSettingsView.spotifyAccentColor) } var body: some View { diff --git a/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView+LyricsSourceSection.swift b/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView+LyricsSourceSection.swift index 6c0ff42..f0deff0 100644 --- a/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView+LyricsSourceSection.swift +++ b/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView+LyricsSourceSection.swift @@ -14,41 +14,15 @@ extension EeveeLyricsSettingsView { } @ViewBuilder func LyricsSourceSection() -> some View { - Section(footer: lyricsSourceFooter()) { - Picker( - "lyrics_source".localized, - selection: $lyricsSource - ) { - ForEach(LyricsSource.allCases, id: \.self) { lyricsSource in - Text(lyricsSource.description).tag(lyricsSource) - } - } - - if lyricsSource == .musixmatch { - VStack(alignment: .leading, spacing: 5) { - Text("musixmatch_user_token".localized) - - TextField("user_token_placeholder".localized, text: $musixmatchToken) - .foregroundColor(.gray) - } - .frame(maxWidth: .infinity, alignment: .leading) - } + Section(footer: Text("restart_is_required_description".localized)) { + Toggle( + "do_not_replace_lyrics".localized, + isOn: Binding( + get: { lyricsSource == .notReplaced }, + set: { lyricsSource = $0 ? .notReplaced : LyricsSource.defaultSource } + ) + ) } - - .onChange(of: musixmatchToken) { input in - if input.isEmpty { - return - } - - if let token = getMusixmatchToken(input) { - UserDefaults.musixmatchToken = token - self.musixmatchToken = token - } - else { - self.musixmatchToken = "" - } - } - .onChange(of: lyricsSource) { [lyricsSource] newSource in if newSource == .musixmatch && musixmatchToken.isEmpty { showMusixmatchTokenAlert(lyricsSource) @@ -57,6 +31,41 @@ extension EeveeLyricsSettingsView { UserDefaults.lyricsSource = newSource } + + if lyricsSource.isReplacing { + Section(footer: lyricsSourceFooter()) { + Picker( + "lyrics_source".localized, + selection: $lyricsSource + ) { + ForEach(LyricsSource.allCases, id: \.self) { lyricsSource in + Text(lyricsSource.description).tag(lyricsSource) + } + } + + if lyricsSource == .musixmatch { + VStack(alignment: .leading, spacing: 5) { + Text("musixmatch_user_token".localized) + + TextField("user_token_placeholder".localized, text: $musixmatchToken) + .foregroundColor(.gray) + } + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .onChange(of: musixmatchToken) { input in + if input.isEmpty { + return + } + + if let token = getMusixmatchToken(input) { + UserDefaults.musixmatchToken = token + self.musixmatchToken = token + } + else { + self.musixmatchToken = "" + } + } + } } } - diff --git a/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView.swift b/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView.swift index 3bc0b87..5066505 100644 --- a/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView.swift +++ b/Sources/EeveeSpotify/Settings/Views/Sections/EeveeLyricsSettingsView.swift @@ -11,56 +11,58 @@ struct EeveeLyricsSettingsView: View { var body: some View { List { LyricsSourceSection() - - if lyricsSource != .genius { - Section( - footer: Text("genius_fallback_description".localizeWithFormat(lyricsSource.description)) - ) { - Toggle( - "genius_fallback".localized, - isOn: $geniusFallback - ) - - if geniusFallback { + + if lyricsSource != .notReplaced { + if lyricsSource != .genius { + Section( + footer: Text("genius_fallback_description".localizeWithFormat(lyricsSource.description)) + ) { Toggle( - "show_fallback_reasons".localized, - isOn: Binding( - get: { UserDefaults.fallbackReasons }, - set: { UserDefaults.fallbackReasons = $0 } - ) + "genius_fallback".localized, + isOn: $geniusFallback ) + + if geniusFallback { + Toggle( + "show_fallback_reasons".localized, + isOn: Binding( + get: { UserDefaults.fallbackReasons }, + set: { UserDefaults.fallbackReasons = $0 } + ) + ) + } } } - } - - // - - Section(footer: Text("romanized_lyrics_description".localized)) { - Toggle( - "romanized_lyrics".localized, - isOn: $lyricsOptions.romanization - ) - } - - if lyricsSource == .musixmatch { - Section { - HStack { - if showLanguageWarning { - Image(systemName: "exclamationmark.triangle") - .font(.title3) - .foregroundColor(.yellow) + + // + + Section(footer: Text("romanized_lyrics_description".localized)) { + Toggle( + "romanized_lyrics".localized, + isOn: $lyricsOptions.romanization + ) + } + + if lyricsSource == .musixmatch { + Section { + HStack { + if showLanguageWarning { + Image(systemName: "exclamationmark.triangle") + .font(.title3) + .foregroundColor(.yellow) + } + + Text("musixmatch_language".localized) + + Spacer() + + TextField("en", text: $lyricsOptions.musixmatchLanguage) + .frame(maxWidth: 20) + .foregroundColor(.gray) } - - Text("musixmatch_language".localized) - - Spacer() - - TextField("en", text: $lyricsOptions.musixmatchLanguage) - .frame(maxWidth: 20) - .foregroundColor(.gray) + } footer: { + Text("musixmatch_language_description".localized) } - } footer: { - Text("musixmatch_language_description".localized) } } @@ -71,7 +73,6 @@ struct EeveeLyricsSettingsView: View { .modifier(ListRowSeparatorHidden()) } } - .listStyle(GroupedListStyle()) .animation(.default, value: lyricsSource) diff --git a/Sources/EeveeSpotify/Settings/Views/Sections/EeveePatchingSettingsView.swift b/Sources/EeveeSpotify/Settings/Views/Sections/EeveePatchingSettingsView.swift index f7889ad..340ca2f 100644 --- a/Sources/EeveeSpotify/Settings/Views/Sections/EeveePatchingSettingsView.swift +++ b/Sources/EeveeSpotify/Settings/Views/Sections/EeveePatchingSettingsView.swift @@ -7,7 +7,14 @@ struct EeveePatchingSettingsView: View { var body: some View { List { - Section(footer: patchType == .disabled ? nil : Text("patching_description".localized)) { + Section( + footer: patchType == .disabled + ? nil + : Text( + "patching_description" + .localizeWithFormat("restart_is_required_description".localized) + ) + ) { Toggle( "do_not_patch_premium".localized, isOn: Binding( diff --git a/Sources/EeveeSpotify/Settings/Views/Sections/EeveeUISettingsView.swift b/Sources/EeveeSpotify/Settings/Views/Sections/EeveeUISettingsView.swift index 78c06bd..ce4404e 100644 --- a/Sources/EeveeSpotify/Settings/Views/Sections/EeveeUISettingsView.swift +++ b/Sources/EeveeSpotify/Settings/Views/Sections/EeveeUISettingsView.swift @@ -6,44 +6,46 @@ struct EeveeUISettingsView: View { var body: some View { List { - Section( - header: Text("lyrics_background_color_section".localized), - footer: Text("lyrics_background_color_section_description".localized)) { - Toggle( - "display_original_colors".localized, - isOn: $lyricsColors.displayOriginalColors - ) - - Toggle( - "use_static_color".localized, - isOn: $lyricsColors.useStaticColor - ) - - if lyricsColors.useStaticColor { - ColorPicker( - "static_color".localized, - selection: Binding( - get: { Color(hex: lyricsColors.staticColor) }, - set: { lyricsColors.staticColor = $0.hexString } - ), - supportsOpacity: false + if UserDefaults.lyricsSource.isReplacing { + Section( + header: Text("lyrics_background_color_section".localized), + footer: Text("lyrics_background_color_section_description".localized) + ) { + Toggle( + "display_original_colors".localized, + isOn: $lyricsColors.displayOriginalColors ) - } - else { - VStack(alignment: .leading, spacing: 5) { - Text("color_normalization_factor".localized) - - Slider( - value: $lyricsColors.normalizationFactor, - in: 0.2...0.8, - step: 0.1 + + Toggle( + "use_static_color".localized, + isOn: $lyricsColors.useStaticColor + ) + + if lyricsColors.useStaticColor { + ColorPicker( + "static_color".localized, + selection: Binding( + get: { Color(hex: lyricsColors.staticColor) }, + set: { lyricsColors.staticColor = $0.hexString } + ), + supportsOpacity: false ) } + else { + VStack(alignment: .leading, spacing: 5) { + Text("color_normalization_factor".localized) + + Slider( + value: $lyricsColors.normalizationFactor, + in: 0.2...0.8, + step: 0.1 + ) + } + } + } + .onChange(of: lyricsColors) { lyricsColors in + UserDefaults.lyricsColors = lyricsColors } - } - - .onChange(of: lyricsColors) { lyricsColors in - UserDefaults.lyricsColors = lyricsColors } Section { diff --git a/Sources/EeveeSpotify/Settings/Views/Shared/CommonIssuesTipView.swift b/Sources/EeveeSpotify/Settings/Views/Shared/CommonIssuesTipView.swift index 720b14c..37b2c47 100644 --- a/Sources/EeveeSpotify/Settings/Views/Shared/CommonIssuesTipView.swift +++ b/Sources/EeveeSpotify/Settings/Views/Shared/CommonIssuesTipView.swift @@ -26,7 +26,7 @@ struct CommonIssuesTipView: View { .foregroundColor(.white) + Text("common_issues_tip_button".localized) - .foregroundColor(.blue) + .foregroundColor(EeveeSettingsView.spotifyAccentColor) + Text(".") .foregroundColor(.white) diff --git a/Sources/EeveeSpotify/Tweak.x.swift b/Sources/EeveeSpotify/Tweak.x.swift index 5393d36..18407b7 100644 --- a/Sources/EeveeSpotify/Tweak.x.swift +++ b/Sources/EeveeSpotify/Tweak.x.swift @@ -8,7 +8,7 @@ func exitApplication() { } } -struct PremiumPatching: HookGroup { } +struct PremiumPatchingGroup: HookGroup { } struct EeveeSpotify: Tweak { static let version = "5.5" @@ -20,7 +20,11 @@ struct EeveeSpotify: Tweak { } if UserDefaults.patchType.isPatching { - PremiumPatching().activate() + PremiumPatchingGroup().activate() + } + + if UserDefaults.lyricsSource.isReplacing { + LyricsGroup().activate() } } } diff --git a/layout/Library/Application Support/EeveeSpotify.bundle/en.lproj/Localizable.strings b/layout/Library/Application Support/EeveeSpotify.bundle/en.lproj/Localizable.strings index 681c5d6..3f96383 100644 --- a/layout/Library/Application Support/EeveeSpotify.bundle/en.lproj/Localizable.strings +++ b/layout/Library/Application Support/EeveeSpotify.bundle/en.lproj/Localizable.strings @@ -15,18 +15,23 @@ reset_data_description = "Clear cached data and restart the app."; checking_for_update = "Checking for Update..."; update_available = "Update Available"; +restart_is_required_description = "App restart is required after changing."; + // Patching do_not_patch_premium = "Do Not Patch Premium"; patching_description = "EeveeSpotify intercepts requests to load user data, deserializes it, and modifies the parameters in real-time. -If you have an active Premium subscription, you can turn on Do Not Patch Premium. The tweak won't patch the data or restrict the use of Premium server-sided features. App restart is required after changing."; +If you have an active Premium subscription, you can turn on Do Not Patch Premium. The tweak won't patch the data or restrict the use of Premium server-sided features. + +%@"; overwrite_configuration = "Overwrite Configuration"; overwrite_configuration_description = "Replace remote configuration with the dumped Premium one. This configuration defines most UI/UX parameters and may be helpful, although it could cause issues."; // Lyrics +do_not_replace_lyrics = "Do Not Replace Lyrics"; lyrics_source = "Lyrics Source"; lyrics_source_description = "You can select the lyrics source you prefer. @@ -75,7 +80,8 @@ dark_popups = "Dark PopUps"; have_premium_popup = "It looks like you have an active Premium subscription, so the tweak won't patch the data or restrict the use of Premium server-sided features. You can manage this in the EeveeSpotify settings."; high_audio_quality_popup = "Very high audio quality is server-sided and is not available with this tweak."; -playlist_downloading_popup = "Native playlist downloading is server-sided and is not available with this tweak. You can download podcast episodes though."; +playlist_downloading_popup = "Native playlist downloading is server-sided and is not available with this tweak. You can download podcast episodes and local playlists though."; +download_local_playlist = "Download local playlist"; /* MARK: Lyrics */ diff --git a/layout/Library/Application Support/EeveeSpotify.bundle/ru.lproj/Localizable.strings b/layout/Library/Application Support/EeveeSpotify.bundle/ru.lproj/Localizable.strings index 7cc8adf..084fe81 100644 --- a/layout/Library/Application Support/EeveeSpotify.bundle/ru.lproj/Localizable.strings +++ b/layout/Library/Application Support/EeveeSpotify.bundle/ru.lproj/Localizable.strings @@ -15,18 +15,23 @@ reset_data_description = "Очистить кэшированные данные checking_for_update = "Проверка наличия обновления..."; update_available = "Доступно обновление"; +restart_is_required_description = "Требуется перезапуск для применения."; + // Patching do_not_patch_premium = "Не патчить Premium"; patching_description = "EeveeSpotify перехватывает запросы на получение данных о пользователе, обрабатывает их и заменяет параметры в реальном времени. -Если у вас есть Premium, вы можете включить Не патчить Premium. Твик не будет изменять данные и ограничивать использование серверных функций. Требуется перезапуск для применения."; +Если у вас есть Premium, вы можете включить Не патчить Premium. Твик не будет изменять данные и ограничивать использование серверных функций. + +%@"; overwrite_configuration = "Заменять конфигурацию"; overwrite_configuration_description = "Заменять удаленную конфигурацию на сдампленную конфигурацию Premium. Она определяет многие UI/UX параметры и может быть полезной, хотя может вызвать проблемы."; // Lyrics +do_not_replace_lyrics = "Не заменять тексты"; lyrics_source = "Источник текстов"; lyrics_source_description = "Вы можете выбрать источник текстов, который Вам больше нравится. @@ -57,7 +62,7 @@ musixmatch_language_description = "Вы можете ввести двузнач // UI lyrics_background_color_section = "Цвет фона текстов"; -lyrics_background_color_section_description = "При включении \"Отображать оригинальные цвета\" тексты будут отображаться в оригинальных цветах Spotify у песен, для которых они есть. +lyrics_background_color_section_description = "При включении Отображать оригинальные цвета тексты будут отображаться в оригинальных цветах Spotify у песен, для которых они есть. Вы можете выбрать статический цвет или уровень нормализации на основе цвета обложки песни. Он определяет, насколько темные цвета осветлены, а светлые — затемнены. В основном чем выше уровень нормализации, тем цвета светлее."; @@ -73,7 +78,8 @@ dark_popups = "Темные сообщения"; have_premium_popup = "Похоже, у Вас есть активная подписка Premium. Твик не будет изменять данные и ограничивать использование серверных функций. Вы можете управлять этим в настройках EeveeSpotify."; high_audio_quality_popup = "Очень высокое качество недоступно с твиком, так как предоставляется на стороне сервера."; -playlist_downloading_popup = "Встроенная загрузка плейлистов осуществляется из сервера и недоступна с твиком. Однако вы можете загружать эпизоды подкастов."; +playlist_downloading_popup = "Встроенная загрузка плейлистов осуществляется из сервера и недоступна с твиком. Однако вы можете загружать эпизоды подкастов и локальные плейлисты."; +download_local_playlist = "Загрузить локальный плейлист"; /* MARK: Lyrics */