From 3c5509ea53d3155ca22a11972fc3a1ca008e4329 Mon Sep 17 00:00:00 2001 From: eevee Date: Wed, 5 Jun 2024 01:17:16 +0300 Subject: [PATCH] Musixmatch Invalid Token Alert, better token input, Overwrite Configuration Option --- .../EeveeSpotify/Lyrics/CustomLyrics.x.swift | 37 +++++++---- .../MusixmatchLyricsDataSource.swift | 5 ++ .../Lyrics/Models/LyricsError.swift | 3 +- .../Models/Extensions/String+Extension.swift | 15 +++++ .../Extensions/UserDefaults+Extension.swift | 10 +++ .../DynamicPremium+ModifyFunctions.swift | 5 ++ .../Premium/DynamicPremium.x.swift | 28 ++++---- .../Premium/Helpers/BundleHelper.swift | 11 ++++ ...> EeveeSettingsView+PremiumSections.swift} | 15 ++++- .../Settings/Views/EeveeSettingsView.swift | 62 +++++++++++++----- ...ettingsViewController+LyricsSections.swift | 2 +- .../resolveconfiguration.bnk | Bin 0 -> 51138 bytes 12 files changed, 145 insertions(+), 48 deletions(-) rename Sources/EeveeSpotify/Settings/Views/{EeveeSettingsView+PremiumSection.swift => EeveeSettingsView+PremiumSections.swift} (72%) create mode 100644 layout/Library/Application Support/EeveeSpotify.bundle/resolveconfiguration.bnk diff --git a/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift b/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift index b3531b7..498c51a 100644 --- a/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift +++ b/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift @@ -47,23 +47,36 @@ func getCurrentTrackLyricsData(originalLyrics: Lyrics? = nil) throws -> Data { ) } - catch { - - if source != .genius && UserDefaults.geniusFallback { + catch let error as LyricsError { + + switch error { - NSLog("[EeveeSpotify] Unable to load lyrics from \(source): \(error), trying Genius as fallback") - source = .genius - - plainLyrics = try LyricsRepository.getLyrics( - title: track.trackTitle(), - artist: track.artistTitle(), - spotifyTrackId: track.URI().spt_trackIdentifier(), - source: source + case .InvalidMusixmatchToken: + + PopUpHelper.showPopUp( + delayed: false, + message: "The tweak is unable to load lyrics from Musixmatch due to Unauthorized error. Please check or update your Musixmatch token.", + buttonText: "OK" ) + break + + default: + break } - else { + + if source == .genius || !UserDefaults.geniusFallback { throw error } + + NSLog("[EeveeSpotify] Unable to load lyrics from \(source): \(error), trying Genius as fallback") + source = .genius + + plainLyrics = try LyricsRepository.getLyrics( + title: track.trackTitle(), + artist: track.artistTitle(), + spotifyTrackId: track.URI().spt_trackIdentifier(), + source: source + ) } let lyrics = try Lyrics.with { diff --git a/Sources/EeveeSpotify/Lyrics/DataSources/MusixmatchLyricsDataSource.swift b/Sources/EeveeSpotify/Lyrics/DataSources/MusixmatchLyricsDataSource.swift index b134b30..c20c78a 100644 --- a/Sources/EeveeSpotify/Lyrics/DataSources/MusixmatchLyricsDataSource.swift +++ b/Sources/EeveeSpotify/Lyrics/DataSources/MusixmatchLyricsDataSource.swift @@ -65,6 +65,11 @@ struct MusixmatchLyricsDataSource { else { throw LyricsError.DecodingError } + + if let header = message["header"] as? [String: Any], + header["status_code"] as? Int == 401 { + throw LyricsError.InvalidMusixmatchToken + } if let trackSubtitlesGet = macroCalls["track.subtitles.get"] as? [String: Any], let subtitlesMessage = trackSubtitlesGet["message"] as? [String: Any], diff --git a/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift b/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift index ef1f6ce..d8a8401 100644 --- a/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift +++ b/Sources/EeveeSpotify/Lyrics/Models/LyricsError.swift @@ -2,6 +2,7 @@ import Foundation enum LyricsError: Swift.Error { case NoCurrentTrack + case InvalidMusixmatchToken case DecodingError case NoSuchSong -} \ No newline at end of file +} diff --git a/Sources/EeveeSpotify/Models/Extensions/String+Extension.swift b/Sources/EeveeSpotify/Models/Extensions/String+Extension.swift index 55e47f3..88572b9 100644 --- a/Sources/EeveeSpotify/Models/Extensions/String+Extension.swift +++ b/Sources/EeveeSpotify/Models/Extensions/String+Extension.swift @@ -44,4 +44,19 @@ extension String { withTemplate: "" ) } + + var hexadecimal: Data? { + var data = Data(capacity: count / 2) + + let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) + regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in + let byteString = (self as NSString).substring(with: match!.range) + let num = UInt8(byteString, radix: 16)! + data.append(num) + } + + guard data.count > 0 else { return nil } + + return data + } } diff --git a/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift b/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift index 6d9630b..04b17c5 100644 --- a/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift +++ b/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift @@ -9,6 +9,7 @@ extension UserDefaults { private static let geniusFallbackKey = "geniusFallback" private static let darkPopUpsKey = "darkPopUps" private static let patchTypeKey = "patchType" + private static let overwriteConfigurationKey = "overwriteConfiguration" static var lyricsSource: LyricsSource { get { @@ -62,4 +63,13 @@ extension UserDefaults { defaults.set(patchType.rawValue, forKey: patchTypeKey) } } + + static var overwriteConfiguration: Bool { + get { + defaults.bool(forKey: overwriteConfigurationKey) + } + set (overwriteConfiguration) { + defaults.set(overwriteConfiguration, forKey: overwriteConfigurationKey) + } + } } diff --git a/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyFunctions.swift b/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyFunctions.swift index 785bf0d..d86e1a3 100644 --- a/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyFunctions.swift +++ b/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyFunctions.swift @@ -1,6 +1,11 @@ import Foundation func modifyRemoteConfiguration(_ configuration: inout UcsResponse) { + + if UserDefaults.overwriteConfiguration { + configuration.resolve.configuration = try! BundleHelper.shared.resolveConfiguration() + } + modifyAttributes(&configuration.attributes.accountAttributes) } diff --git a/Sources/EeveeSpotify/Premium/DynamicPremium.x.swift b/Sources/EeveeSpotify/Premium/DynamicPremium.x.swift index 4314381..ffea8c4 100644 --- a/Sources/EeveeSpotify/Premium/DynamicPremium.x.swift +++ b/Sources/EeveeSpotify/Premium/DynamicPremium.x.swift @@ -62,6 +62,19 @@ class SPTCoreURLSessionDataDelegateHook: ClassHook { var bootstrapMessage = try BootstrapMessage(serializedData: buffer) + if UserDefaults.patchType == .notSet { + + if bootstrapMessage.attributes["type"]?.stringValue == "premium" { + UserDefaults.patchType = .disabled + showHavePremiumPopUp() + } + else { + UserDefaults.patchType = .requests + } + + NSLog("[EeveeSpotify] Fetched bootstrap, \(UserDefaults.patchType) was set") + } + if UserDefaults.patchType == .requests { modifyRemoteConfiguration(&bootstrapMessage.ucsResponse) @@ -75,21 +88,6 @@ class SPTCoreURLSessionDataDelegateHook: ClassHook { NSLog("[EeveeSpotify] Modified bootstrap data") } else { - - if UserDefaults.patchType == .notSet { - - if bootstrapMessage.attributes["type"]?.stringValue == "premium" { - UserDefaults.patchType = .disabled - showHavePremiumPopUp() - } - else { - UserDefaults.patchType = .offlineBnk - showOfflineBnkMethodSetPopUp() - } - - NSLog("[EeveeSpotify] Fetched bootstrap, \(UserDefaults.patchType) was set") - } - orig.URLSession(session, dataTask: task, didReceiveData: buffer) } diff --git a/Sources/EeveeSpotify/Premium/Helpers/BundleHelper.swift b/Sources/EeveeSpotify/Premium/Helpers/BundleHelper.swift index 2e28531..1d43e60 100755 --- a/Sources/EeveeSpotify/Premium/Helpers/BundleHelper.swift +++ b/Sources/EeveeSpotify/Premium/Helpers/BundleHelper.swift @@ -26,4 +26,15 @@ class BundleHelper { )! ) } + + func resolveConfiguration() throws -> ResolveConfiguration { + return try ResolveConfiguration( + serializedData: try Data( + contentsOf: self.bundle.url( + forResource: "resolveconfiguration", + withExtension: "bnk" + )! + ) + ) + } } diff --git a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView+PremiumSection.swift b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView+PremiumSections.swift similarity index 72% rename from Sources/EeveeSpotify/Settings/Views/EeveeSettingsView+PremiumSection.swift rename to Sources/EeveeSpotify/Settings/Views/EeveeSettingsView+PremiumSections.swift index ffdd3a1..f298ce0 100644 --- a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView+PremiumSection.swift +++ b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView+PremiumSections.swift @@ -2,7 +2,7 @@ import SwiftUI extension EeveeSettingsView { - @ViewBuilder func PremiumSection() -> some View { + @ViewBuilder func PremiumSections() -> some View { Section(footer: patchType == .disabled ? nil : Text(""" You can select the Premium patching method you prefer. App restart is required after changing. @@ -17,7 +17,7 @@ If you have an active Premium subscription, you can turn on Do Not Patch Premium "Do Not Patch Premium", isOn: Binding( get: { patchType == .disabled }, - set: { patchType = $0 ? .disabled : .offlineBnk } + set: { patchType = $0 ? .disabled : .requests } ) ) @@ -31,5 +31,16 @@ If you have an active Premium subscription, you can turn on Do Not Patch Premium } } } + + if patchType == .requests { + Section( + footer: Text("Replace remote configuration with the dumped Premium one. It might fix some issues, such as appearing ads, but it's not guaranteed.") + ) { + Toggle( + "Overwrite Configuration", + isOn: $overwriteConfiguration + ) + } + } } } diff --git a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift index b74d71a..e04028b 100644 --- a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift +++ b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsView.swift @@ -6,6 +6,20 @@ struct EeveeSettingsView: View { @State var musixmatchToken = UserDefaults.musixmatchToken @State var patchType = UserDefaults.patchType @State var lyricsSource = UserDefaults.lyricsSource + @State var overwriteConfiguration = UserDefaults.overwriteConfiguration + + private func getMusixmatchToken(_ input: String) -> String? { + + if let match = input.firstMatch("\\[UserToken\\]: ([a-f0-9]+)"), + let tokenRange = Range(match.range(at: 1), in: input) { + return String(input[tokenRange]) + } + else if input ~= "^[a-f0-9]+$" { + return input + } + + return nil + } private func showMusixmatchTokenAlert(_ oldSource: LyricsSource) { @@ -25,16 +39,8 @@ struct EeveeSettingsView: View { alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in let text = alert.textFields!.first!.text! - let token: String - - if let match = text.firstMatch("\\[UserToken\\]: ([a-f0-9]+)"), - let tokenRange = Range(match.range(at: 1), in: text) { - token = String(text[tokenRange]) - } - else if text ~= "^[a-f0-9]+$" { - token = text - } - else { + + guard let token = getMusixmatchToken(text) else { lyricsSource = oldSource return } @@ -50,7 +56,7 @@ struct EeveeSettingsView: View { List { - PremiumSection() + PremiumSections() LyricsSections() @@ -73,14 +79,26 @@ struct EeveeSettingsView: View { } } } - - .padding(.bottom, 45) + + .listStyle(GroupedListStyle()) + + .padding(.bottom, 60) + .ignoresSafeArea(.keyboard) .animation(.default, value: lyricsSource) .animation(.default, value: patchType) - .onChange(of: musixmatchToken) { token in - UserDefaults.musixmatchToken = token + .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 @@ -104,8 +122,18 @@ struct EeveeSettingsView: View { NSLog("Unable to reset offline.bnk: \(error)") } } - - .listStyle(GroupedListStyle()) + + .onChange(of: overwriteConfiguration) { overwriteConfiguration in + + UserDefaults.overwriteConfiguration = overwriteConfiguration + + do { + try OfflineHelper.resetOfflineBnk() + } + catch { + NSLog("Unable to reset offline.bnk: \(error)") + } + } .onAppear { UIView.appearance( diff --git a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsViewController+LyricsSections.swift b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsViewController+LyricsSections.swift index 4678b20..5070322 100644 --- a/Sources/EeveeSpotify/Settings/Views/EeveeSettingsViewController+LyricsSections.swift +++ b/Sources/EeveeSpotify/Settings/Views/EeveeSettingsViewController+LyricsSections.swift @@ -30,7 +30,7 @@ If the tweak is unable to find a song or process the lyrics, you'll see a "Could Text("Musixmatch User Token") - TextField("Enter User Token", text: $musixmatchToken) + TextField("Enter User Token or Paste Debug Info", text: $musixmatchToken) .foregroundColor(.gray) } .frame(maxWidth: .infinity, alignment: .leading) diff --git a/layout/Library/Application Support/EeveeSpotify.bundle/resolveconfiguration.bnk b/layout/Library/Application Support/EeveeSpotify.bundle/resolveconfiguration.bnk new file mode 100644 index 0000000000000000000000000000000000000000..3bf289ee8fa6c548b0ddb2862ce650327749403f GIT binary patch literal 51138 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