diff --git a/Sources/EeveeSpotify/DarkPopUps.x.swift b/Sources/EeveeSpotify/DarkPopUps.x.swift index 239621a..89d3412 100644 --- a/Sources/EeveeSpotify/DarkPopUps.x.swift +++ b/Sources/EeveeSpotify/DarkPopUps.x.swift @@ -4,9 +4,12 @@ import SwiftUI struct DarkPopUps: HookGroup { } -private let popUpContainerViewController = EeveeSpotify.isOldSpotifyVersion - ? "SPTEncorePopUpContainer" - : "EncoreConsumerMobile_Wrappers.PopUpPresentableContainer" +private var popUpContainerViewController: String { + switch EeveeSpotify.hookTarget { + case .lastAvailableiOS14: return "SPTEncorePopUpContainer" + default: return "EncoreConsumerMobile_Wrappers.PopUpPresentableContainer" + } +} class EncoreLabelHook: ClassHook { typealias Group = DarkPopUps diff --git a/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift b/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift index eec67dc..cc7b4df 100644 --- a/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift +++ b/Sources/EeveeSpotify/DataLoaderServiceHooks.x.swift @@ -34,7 +34,7 @@ class SPTDataLoaderServiceHook: ClassHook, SpotifySessionDelegate { orig.URLSession( session, dataTask: task, - didReceiveData: try getLyricsForCurrentTrack( + didReceiveData: try getLyricsDataForCurrentTrack( originalLyrics: try? Lyrics(serializedBytes: buffer) ) ) @@ -94,7 +94,7 @@ class SPTDataLoaderServiceHook: ClassHook, SpotifySessionDelegate { )! do { - let lyricsData = try getLyricsForCurrentTrack() + let lyricsData = try getLyricsDataForCurrentTrack() orig.URLSession( session, @@ -109,9 +109,7 @@ class SPTDataLoaderServiceHook: ClassHook, SpotifySessionDelegate { return } catch { - NSLog("[EeveeSpotify] Unable to load lyrics: \(error)") orig.URLSession(session, task: task, didCompleteWithError: error) - return } } diff --git a/Sources/EeveeSpotify/HookedInstances.x.swift b/Sources/EeveeSpotify/HookedInstances.x.swift deleted file mode 100644 index 4099903..0000000 --- a/Sources/EeveeSpotify/HookedInstances.x.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Orion -import UIKit - -class HookedInstances { - static var productState: SPTCoreProductState? - static var currentTrack: SPTPlayerTrack? - static var nowPlayingMetaBackgroundModel: SPTNowPlayingMetadataBackgroundViewModel? -} - -class SPTNowPlayingModelHook: ClassHook { - static let targetName = "SPTNowPlayingModel" - - func currentTrack() -> SPTPlayerTrack? { - if let track = orig.currentTrack() { - HookedInstances.currentTrack = track - return track - } - - return nil - } -} - -class SPTNowPlayingMetadataBackgroundViewModelHook: ClassHook { - static let targetName = "SPTNowPlayingMetadataBackgroundViewModel" - - func color() -> UIColor { - HookedInstances.nowPlayingMetaBackgroundModel = Dynamic.convert( - target, - to: SPTNowPlayingMetadataBackgroundViewModel.self - ) - return orig.color() - } -} - -class SPTCoreProductStateInstanceHook: ClassHook { - static let targetName = "SPTCoreProductState" - - func stringForKey(_ key: String) -> NSString { - HookedInstances.productState = Dynamic.convert( - target, - to: SPTCoreProductState.self - ) - return orig.stringForKey(key) - } -} diff --git a/Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift b/Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift index 6f8be07..412873c 100644 --- a/Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift +++ b/Sources/EeveeSpotify/Lyrics/CustomLyrics+AllTracksLyrics.x.swift @@ -1,10 +1,11 @@ import Orion +import UIKit class SPTPlayerTrackHook: ClassHook { typealias Group = LyricsGroup static let targetName = "SPTPlayerTrack" - func metadata() -> [String:String] { + func metadata() -> [String: String] { var meta = orig.metadata() meta["has_lyrics"] = "true" @@ -14,14 +15,33 @@ class SPTPlayerTrackHook: ClassHook { class LyricsScrollProviderHook: ClassHook { typealias Group = LyricsGroup - - static var targetName: String { - return EeveeSpotify.isOldSpotifyVersion - ? "Lyrics_CoreImpl.ScrollProvider" - : "Lyrics_NPVCommunicatorImpl.ScrollProvider" - } + static var targetName = HookTargetNameHelper.lyricsScrollProvider func isEnabledForTrack(_ track: SPTPlayerTrack) -> Bool { return true } } + +class NowPlayingScrollViewControllerHook: ClassHook { + typealias Group = LyricsGroup + static var targetName = "NowPlaying_ScrollImpl.NowPlayingScrollViewController" + + func nowPlayingScrollViewModelWithDidLoadComponentsFor( + _ track: SPTPlayerTrack, + withDifferentProviders: Bool, + scrollEnabledValueChanged: Bool + ) -> NowPlayingScrollViewController { + var controller = orig.nowPlayingScrollViewModelWithDidLoadComponentsFor( + track, + withDifferentProviders: withDifferentProviders, + scrollEnabledValueChanged: scrollEnabledValueChanged + ) + + if !scrollEnabledValueChanged { + controller.scrollEnabled = true + controller.nowPlayingScrollViewModelDidChangeScrollEnabledValue() + } + + return controller + } +} diff --git a/Sources/EeveeSpotify/Lyrics/CustomLyrics+DisableReportButton.x.swift b/Sources/EeveeSpotify/Lyrics/CustomLyrics+DisableReportButton.x.swift new file mode 100644 index 0000000..b3cad13 --- /dev/null +++ b/Sources/EeveeSpotify/Lyrics/CustomLyrics+DisableReportButton.x.swift @@ -0,0 +1,30 @@ +import Orion +import UIKit + +class LyricsFullscreenViewControllerHook: ClassHook { + typealias Group = LyricsGroup + + static var targetName: String { + switch EeveeSpotify.hookTarget { + case .lastAvailableiOS14: return "Lyrics_CoreImpl.FullscreenViewController" + default: return "Lyrics_FullscreenPageImpl.FullscreenViewController" + } + } + + func viewDidLoad() { + orig.viewDidLoad() + + if UserDefaults.lyricsSource == .musixmatch + && lyricsState.fallbackError == nil + && !lyricsState.wasRomanized + && !lyricsState.isEmpty { + return + } + + let headerView = Ivars(target.view).headerView + + if let reportButton = headerView.subviews(matching: "EncoreButton")[1] as? UIButton { + reportButton.isEnabled = false + } + } +} diff --git a/Sources/EeveeSpotify/Lyrics/CustomLyrics+ShowAttributes.x.swift b/Sources/EeveeSpotify/Lyrics/CustomLyrics+ShowAttributes.x.swift new file mode 100644 index 0000000..2d56d9a --- /dev/null +++ b/Sources/EeveeSpotify/Lyrics/CustomLyrics+ShowAttributes.x.swift @@ -0,0 +1,100 @@ +import Orion +import UIKit + +class LyricsOnlyViewControllerHook: ClassHook { + typealias Group = LyricsGroup + + static var targetName: String { + switch EeveeSpotify.hookTarget { + case .lastAvailableiOS14: return "Lyrics_CoreImpl.LyricsOnlyViewController" + default: return "Lyrics_NPVCommunicatorImpl.LyricsOnlyViewController" + } + } + + func viewDidLoad() { + orig.viewDidLoad() + + guard + let lyricsHeaderViewController = target.parent?.children.first + else { + return + } + + guard let lyricsLabel = WindowHelper.shared.findFirstSubview( + "SPTEncoreLabel", + in: lyricsHeaderViewController.view + ) else { + return + } + + let encoreLabel = Dynamic.convert(lyricsLabel, to: SPTEncoreLabel.self) + + var text = [ + encoreLabel.text().firstObject + ] + + let attributes = Dynamic.SPTEncoreAttributes + .alloc(interface: SPTEncoreAttributes.self) + .`init`({ attributes in + attributes.setForegroundColor(.white.withAlphaComponent(0.5)) + }) + + let typeStyle = type( + of: Dynamic[ + dynamicMember: EeveeSpotify.hookTarget == .lastAvailableiOS14 + ? "SPTEncoreTypeStyle" + : "SPTEncoreTextStyle" + ].alloc(interface: SPTEncoreTypeStyle.self) + ).bodyMediumBold() + + // + + if UserDefaults.lyricsOptions.showFallbackReasons, + let description = lyricsState.fallbackError?.description + { + let attributedString = Dynamic.SPTEncoreAttributedString.alloc( + interface: SPTEncoreAttributedString.self + ) + + text.append( + EeveeSpotify.hookTarget == .lastAvailableiOS14 + ? attributedString.initWithString( + "\n\("fallback_attribute".localized): \(description)", + typeStyle: typeStyle, + attributes: attributes + ) + : attributedString.initWithString( + "\n\("fallback_attribute".localized): \(description)", + textStyle: typeStyle, + attributes: attributes + ) + ) + } + + if lyricsState.wasRomanized { + let attributedString = Dynamic.SPTEncoreAttributedString.alloc( + interface: SPTEncoreAttributedString.self + ) + + text.append( + EeveeSpotify.hookTarget == .lastAvailableiOS14 + ? attributedString.initWithString( + "\n\("romanized_attribute".localized)", + typeStyle: typeStyle, + attributes: attributes + ) + : attributedString.initWithString( + "\n\("romanized_attribute".localized)", + textStyle: typeStyle, + attributes: attributes + ) + ) + } + + if EeveeSpotify.hookTarget == .lastAvailableiOS14 { + encoreLabel.setNumberOfLines(text.count) + } + + encoreLabel.setText(text as NSArray) + } +} diff --git a/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift b/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift index c1ee671..8930548 100644 --- a/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift +++ b/Sources/EeveeSpotify/Lyrics/CustomLyrics.x.swift @@ -5,163 +5,24 @@ import SwiftUI struct LyricsGroup: HookGroup { } -// +var lyricsState = LyricsLoadingState() -class LyricsFullscreenViewControllerHook: ClassHook { - typealias Group = LyricsGroup - - static var targetName: String { - return EeveeSpotify.isOldSpotifyVersion - ? "Lyrics_CoreImpl.FullscreenViewController" - : "Lyrics_FullscreenPageImpl.FullscreenViewController" - } - - func viewDidLoad() { - orig.viewDidLoad() - - if UserDefaults.lyricsSource == .musixmatch - && lastLyricsState.fallbackError == nil - && !lastLyricsState.wasRomanized - && !lastLyricsState.areEmpty { - return - } - - let headerView = Ivars(target.view).headerView - - if let reportButton = headerView.subviews(matching: "EncoreButton")[1] as? UIButton { - reportButton.isEnabled = false - } - } -} - -// - -private var preloadedLyrics: Lyrics? = nil -private var lastLyricsState = LyricsLoadingState() - -private var hasShownRestrictedPopUp = false -private var hasShownUnauthorizedPopUp = false - -// - -class LyricsOnlyViewControllerHook: ClassHook { - typealias Group = LyricsGroup - - static var targetName: String { - return EeveeSpotify.isOldSpotifyVersion - ? "Lyrics_CoreImpl.LyricsOnlyViewController" - : "Lyrics_NPVCommunicatorImpl.LyricsOnlyViewController" - } - - func viewDidLoad() { - orig.viewDidLoad() - - guard - let lyricsHeaderViewController = target.parent?.children.first - else { - return - } - - // - - guard let lyricsLabel = WindowHelper.shared.findFirstSubview( - "SPTEncoreLabel", - in: lyricsHeaderViewController.view - ) else { - return - } - - // - - let encoreLabel = Dynamic.convert(lyricsLabel, to: SPTEncoreLabel.self) - - var text = [ - encoreLabel.text().firstObject - ] - - let attributes = Dynamic.SPTEncoreAttributes - .alloc(interface: SPTEncoreAttributes.self) - .`init`({ attributes in - attributes.setForegroundColor(.white.withAlphaComponent(0.5)) - }) - - let typeStyle = type( - of: Dynamic[ - dynamicMember: EeveeSpotify.isOldSpotifyVersion - ? "SPTEncoreTypeStyle" - : "SPTEncoreTextStyle" - ].alloc(interface: SPTEncoreTypeStyle.self) - ).bodyMediumBold() - - // - - if UserDefaults.lyricsOptions.showFallbackReasons, - let description = lastLyricsState.fallbackError?.description - { - let attributedString = Dynamic.SPTEncoreAttributedString.alloc( - interface: SPTEncoreAttributedString.self - ) - - text.append( - EeveeSpotify.isOldSpotifyVersion - ? attributedString.initWithString( - "\n\("fallback_attribute".localized): \(description)", - typeStyle: typeStyle, - attributes: attributes - ) - : attributedString.initWithString( - "\n\("fallback_attribute".localized): \(description)", - textStyle: typeStyle, - attributes: attributes - ) - ) - } - - if lastLyricsState.wasRomanized { - let attributedString = Dynamic.SPTEncoreAttributedString.alloc( - interface: SPTEncoreAttributedString.self - ) - - text.append( - EeveeSpotify.isOldSpotifyVersion - ? attributedString.initWithString( - "\n\("romanized_attribute".localized)", - typeStyle: typeStyle, - attributes: attributes - ) - : attributedString.initWithString( - "\n\("romanized_attribute".localized)", - textStyle: typeStyle, - attributes: attributes - ) - ) - } - - if EeveeSpotify.isOldSpotifyVersion { - encoreLabel.setNumberOfLines(text.count) - } - - encoreLabel.setText(text as NSArray) - } -} - -// +var hasShownRestrictedPopUp = false +var hasShownUnauthorizedPopUp = false private let geniusLyricsRepository = GeniusLyricsRepository() private let petitLyricsRepository = PetitLyricsRepository() // -private func loadLyricsForCurrentTrack() throws { - guard let track = HookedInstances.currentTrack else { +private func loadCustomLyricsForCurrentTrack() throws -> Lyrics { + guard let track = nowPlayingScrollViewController?.loadedTrack else { throw LyricsError.noCurrentTrack } - // - let searchQuery = LyricsSearchQuery( title: track.trackTitle(), - primaryArtist: EeveeSpotify.isOldSpotifyVersion + primaryArtist: EeveeSpotify.hookTarget == .lastAvailableiOS14 ? track.artistTitle() : track.artistName(), spotifyTrackId: track.URI().spt_trackIdentifier() @@ -185,20 +46,17 @@ private func loadLyricsForCurrentTrack() throws { case .notReplaced: throw LyricsError.invalidSource } - let lyricsDto: LyricsDto - // - - lastLyricsState = LyricsLoadingState() + lyricsState = LyricsLoadingState() do { lyricsDto = try repository.getLyrics(searchQuery, options: options) } catch let error { if let error = error as? LyricsError { - lastLyricsState.fallbackError = error + lyricsState.fallbackError = error switch error { @@ -229,50 +87,39 @@ private func loadLyricsForCurrentTrack() throws { } } else { - lastLyricsState.fallbackError = .unknownError + lyricsState.fallbackError = .unknownError } if source == .genius || !UserDefaults.lyricsOptions.geniusFallback { throw error } - NSLog("[EeveeSpotify] Unable to load lyrics from \(source): \(error), trying Genius as fallback") - source = .genius repository = GeniusLyricsRepository() lyricsDto = try repository.getLyrics(searchQuery, options: options) } - lastLyricsState.areEmpty = lyricsDto.lines.isEmpty + lyricsState.isEmpty = lyricsDto.lines.isEmpty - lastLyricsState.wasRomanized = lyricsDto.romanization == .romanized + lyricsState.wasRomanized = lyricsDto.romanization == .romanized || (lyricsDto.romanization == .canBeRomanized && UserDefaults.lyricsOptions.romanization) - lastLyricsState.loadedSuccessfully = true + lyricsState.loadedSuccessfully = true let lyrics = Lyrics.with { - $0.data = lyricsDto.toLyricsData(source: source.description) + $0.data = lyricsDto.toSpotifyLyricsData(source: source.description) } - preloadedLyrics = lyrics + return lyrics } -func getLyricsForCurrentTrack(originalLyrics: Lyrics? = nil) throws -> Data { - guard let track = HookedInstances.currentTrack else { +func getLyricsDataForCurrentTrack(originalLyrics: Lyrics? = nil) throws -> Data { + guard let track = nowPlayingScrollViewController?.loadedTrack else { throw LyricsError.noCurrentTrack } - var lyrics = preloadedLyrics - - if lyrics == nil { - try loadLyricsForCurrentTrack() - lyrics = preloadedLyrics - } - - guard var lyrics = lyrics else { - throw LyricsError.unknownError - } + var lyrics = try loadCustomLyricsForCurrentTrack() let lyricsColorsSettings = UserDefaults.lyricsColors @@ -280,30 +127,36 @@ func getLyricsForCurrentTrack(originalLyrics: Lyrics? = nil) throws -> Data { lyrics.colors = originalLyrics.colors } else { - var color: Color? + let extractedColor = switch EeveeSpotify.hookTarget { + case .lastAvailableiOS14: + track.extractedColorHex() + default: + track.metadata()["extracted_color"] + } - let extractedColor = EeveeSpotify.isOldSpotifyVersion - ? track.extractedColorHex() - : track.metadata()["extracted_color"] + var color: Color - if let extractedColor = extractedColor { + if lyricsColorsSettings.useStaticColor { + color = Color(hex: lyricsColorsSettings.staticColor) + } + else if let extractedColor = extractedColor { color = Color(hex: extractedColor) + .normalized(lyricsColorsSettings.normalizationFactor) } - else if let uiColor = HookedInstances.nowPlayingMetaBackgroundModel?.color() { + else if let uiColor = nowPlayingScrollViewController?.backgroundViewController.color() { color = Color(uiColor) + .normalized(lyricsColorsSettings.normalizationFactor) + } + else { + color = Color.gray } - - color = color?.normalized(lyricsColorsSettings.normalizationFactor) lyrics.colors = LyricsColors.with { - $0.backgroundColor = lyricsColorsSettings.useStaticColor - ? Color(hex: lyricsColorsSettings.staticColor).uInt32 - : color?.uInt32 ?? Color.gray.uInt32 + $0.backgroundColor = color.uInt32 $0.lineColor = Color.black.uInt32 $0.activeLineColor = Color.white.uInt32 } } - preloadedLyrics = nil return try lyrics.serializedBytes() } diff --git a/Sources/EeveeSpotify/Lyrics/Models/Extensions/NowPlayingScrollViewController+Extension.swift b/Sources/EeveeSpotify/Lyrics/Models/Extensions/NowPlayingScrollViewController+Extension.swift new file mode 100644 index 0000000..1c24718 --- /dev/null +++ b/Sources/EeveeSpotify/Lyrics/Models/Extensions/NowPlayingScrollViewController+Extension.swift @@ -0,0 +1,32 @@ +import Orion + +extension NowPlayingScrollViewController { + private var nowPlayingScrollViewModel: NSObject { + get { + Ivars(self).scrollViewModel + } + } + + var scrollEnabled: Bool { + get { + Ivars(nowPlayingScrollViewModel).scrollEnabled + } + set { + Ivars(nowPlayingScrollViewModel).scrollEnabled = newValue + } + } + + var loadedTrack: SPTPlayerTrack { + get { + Ivars(nowPlayingScrollViewModel).loadedTrack + } + } + + // + + var backgroundViewController: SPTNowPlayingBackgroundViewController { + get { + Ivars(self).backgroundViewController + } + } +} diff --git a/Sources/EeveeSpotify/Lyrics/Models/Headers/SPTNowPlayingBackgroundViewController.swift b/Sources/EeveeSpotify/Lyrics/Models/Headers/SPTNowPlayingBackgroundViewController.swift new file mode 100644 index 0000000..f5e5246 --- /dev/null +++ b/Sources/EeveeSpotify/Lyrics/Models/Headers/SPTNowPlayingBackgroundViewController.swift @@ -0,0 +1,5 @@ +import UIKit + +@objc protocol SPTNowPlayingBackgroundViewController { + func color() -> UIColor +} diff --git a/Sources/EeveeSpotify/Lyrics/Models/LyricsDto.swift b/Sources/EeveeSpotify/Lyrics/Models/LyricsDto.swift index acc080b..b30e153 100644 --- a/Sources/EeveeSpotify/Lyrics/Models/LyricsDto.swift +++ b/Sources/EeveeSpotify/Lyrics/Models/LyricsDto.swift @@ -6,7 +6,7 @@ struct LyricsDto { var romanization: LyricsRomanizationStatus var translation: LyricsTranslationDto? - func toLyricsData(source: String) -> LyricsData { + func toSpotifyLyricsData(source: String) -> LyricsData { var lyricsData = LyricsData.with { $0.timeSynchronized = timeSynced $0.restriction = .unrestricted diff --git a/Sources/EeveeSpotify/Lyrics/Models/LyricsLoadingState.swift b/Sources/EeveeSpotify/Lyrics/Models/LyricsLoadingState.swift index 8fb4c65..02b1abc 100644 --- a/Sources/EeveeSpotify/Lyrics/Models/LyricsLoadingState.swift +++ b/Sources/EeveeSpotify/Lyrics/Models/LyricsLoadingState.swift @@ -2,7 +2,7 @@ import Foundation struct LyricsLoadingState { var wasRomanized = false - var areEmpty = false + var isEmpty = false var fallbackError: LyricsError? = nil var loadedSuccessfully = false } diff --git a/Sources/EeveeSpotify/Lyrics/NowPlayingScrollViewControllerInstanceHook.x.swift b/Sources/EeveeSpotify/Lyrics/NowPlayingScrollViewControllerInstanceHook.x.swift new file mode 100644 index 0000000..da0846e --- /dev/null +++ b/Sources/EeveeSpotify/Lyrics/NowPlayingScrollViewControllerInstanceHook.x.swift @@ -0,0 +1,23 @@ +import Orion +import UIKit + +var nowPlayingScrollViewController: NowPlayingScrollViewController? + +class NowPlayingScrollViewControllerInstanceHook: ClassHook { + typealias Group = LyricsGroup + static let targetName = "NowPlaying_ScrollImpl.NowPlayingScrollViewController" + + func nowPlayingScrollViewModelWithDidMoveToRelativeTrack( + _ track: SPTPlayerTrack, + withDifferentProviders: Bool, + scrollEnabledValueChanged: Bool + ) -> NowPlayingScrollViewController { + nowPlayingScrollViewController = orig.nowPlayingScrollViewModelWithDidMoveToRelativeTrack( + track, + withDifferentProviders: withDifferentProviders, + scrollEnabledValueChanged: scrollEnabledValueChanged + ) + + return nowPlayingScrollViewController! + } +} diff --git a/Sources/EeveeSpotify/Models/Headers/SPTCoreProductState.swift b/Sources/EeveeSpotify/Models/Headers/SPTCoreProductState.swift deleted file mode 100644 index 9271637..0000000 --- a/Sources/EeveeSpotify/Models/Headers/SPTCoreProductState.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Foundation - -@objc protocol SPTCoreProductState { - func stringForKey(_ key: String) -> String -} \ No newline at end of file diff --git a/Sources/EeveeSpotify/Models/Headers/SPTNowPlayingMetadataBackgroundViewModel.swift b/Sources/EeveeSpotify/Models/Headers/SPTNowPlayingMetadataBackgroundViewModel.swift deleted file mode 100644 index 2b5fbe5..0000000 --- a/Sources/EeveeSpotify/Models/Headers/SPTNowPlayingMetadataBackgroundViewModel.swift +++ /dev/null @@ -1,5 +0,0 @@ -import UIKit - -@objc protocol SPTNowPlayingMetadataBackgroundViewModel { - func color() -> UIColor -} diff --git a/Sources/EeveeSpotify/Models/Headers/SPTPlayerTrack.swift b/Sources/EeveeSpotify/Models/Headers/SPTPlayerTrack.swift deleted file mode 100644 index 6f2feb2..0000000 --- a/Sources/EeveeSpotify/Models/Headers/SPTPlayerTrack.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -@objc protocol SPTPlayerTrack { - func setMetadata(_ metadata: [String:String]) - func metadata() -> [String:String] - func extractedColorHex() -> String? - func trackTitle() -> String - func artistTitle() -> String - func artistName() -> String - func URI() -> SPTURL -} diff --git a/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift b/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift index 7d6cbe2..6a6d9a0 100644 --- a/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift +++ b/Sources/EeveeSpotify/Premium/DynamicPremium+ModifyBootstrap.x.swift @@ -10,9 +10,10 @@ private func showHavePremiumPopUp() { class SpotifySessionDelegateBootstrapHook: ClassHook, SpotifySessionDelegate { static var targetName: String { - EeveeSpotify.isOldSpotifyVersion - ? "SPTCoreURLSessionDataDelegate" - : "SPTDataLoaderService" + switch EeveeSpotify.hookTarget { + case .lastAvailableiOS14: return "SPTCoreURLSessionDataDelegate" + default: return "SPTDataLoaderService" + } } func URLSession( diff --git a/Sources/EeveeSpotify/Premium/SiriNoPlayAsRadio.x.swift b/Sources/EeveeSpotify/Premium/SiriNoPlayAsRadio.x.swift index 0639d0f..67984dc 100644 --- a/Sources/EeveeSpotify/Premium/SiriNoPlayAsRadio.x.swift +++ b/Sources/EeveeSpotify/Premium/SiriNoPlayAsRadio.x.swift @@ -2,6 +2,8 @@ import Orion import Intents class INMediaItemHook: ClassHook { + typealias Group = PremiumPatchingGroup + func identifier() -> String { var identifier = orig.identifier() diff --git a/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift b/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift index 9287907..f56c05a 100644 --- a/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift +++ b/Sources/EeveeSpotify/Premium/TrackRowsEnabler.x.swift @@ -9,7 +9,7 @@ class SPTFreeTierArtistHubRemoteURLResolverHook: ClassHook { onDemandSet: Any, onDemandTrialService: Any, trackRowsEnabled: Bool, - productState: SPTCoreProductState + productState: NSObject ) -> Target { return orig.initWithViewURI( uri, diff --git a/Sources/EeveeSpotify/Models/Headers/SPTDisclosureAccessoryView.swift b/Sources/EeveeSpotify/Settings/Models/Headers/SPTDisclosureAccessoryView.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTDisclosureAccessoryView.swift rename to Sources/EeveeSpotify/Settings/Models/Headers/SPTDisclosureAccessoryView.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTSettingsTableViewCell.swift b/Sources/EeveeSpotify/Settings/Models/Headers/SPTSettingsTableViewCell.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTSettingsTableViewCell.swift rename to Sources/EeveeSpotify/Settings/Models/Headers/SPTSettingsTableViewCell.swift diff --git a/Sources/EeveeSpotify/Shared/Helpers/HookTargetHelper.swift b/Sources/EeveeSpotify/Shared/Helpers/HookTargetHelper.swift new file mode 100644 index 0000000..75b66f0 --- /dev/null +++ b/Sources/EeveeSpotify/Shared/Helpers/HookTargetHelper.swift @@ -0,0 +1,11 @@ +import UIKit +import Orion + +struct HookTargetNameHelper { + static var lyricsScrollProvider: String { + switch EeveeSpotify.hookTarget { + case .lastAvailableiOS14: return "Lyrics_CoreImpl.ScrollProvider" + default: return "Lyrics_NPVCommunicatorImpl.ScrollProvider" + } + } +} diff --git a/Sources/EeveeSpotify/Helpers/PopUpHelper.swift b/Sources/EeveeSpotify/Shared/Helpers/PopUpHelper.swift similarity index 100% rename from Sources/EeveeSpotify/Helpers/PopUpHelper.swift rename to Sources/EeveeSpotify/Shared/Helpers/PopUpHelper.swift diff --git a/Sources/EeveeSpotify/Helpers/URLSessionHelper.swift b/Sources/EeveeSpotify/Shared/Helpers/URLSessionHelper.swift similarity index 100% rename from Sources/EeveeSpotify/Helpers/URLSessionHelper.swift rename to Sources/EeveeSpotify/Shared/Helpers/URLSessionHelper.swift diff --git a/Sources/EeveeSpotify/Helpers/WindowHelper.swift b/Sources/EeveeSpotify/Shared/Helpers/WindowHelper.swift similarity index 100% rename from Sources/EeveeSpotify/Helpers/WindowHelper.swift rename to Sources/EeveeSpotify/Shared/Helpers/WindowHelper.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/CharacterSet+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/CharacterSet+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/CharacterSet+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/CharacterSet+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/Collection+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/Collection+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/Collection+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/Collection+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/Color+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/Color+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/Color+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/Color+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/Data+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/Data+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/Data+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/Data+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/Dictionary+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/Dictionary+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/Dictionary+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/Dictionary+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/Locale+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/Locale+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/Locale+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/Locale+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/String+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/String+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/String+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/String+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/StringArray+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/StringArray+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/StringArray+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/StringArray+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/UIColor+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/UIColor+Extension.swift similarity index 99% rename from Sources/EeveeSpotify/Models/Extensions/UIColor+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/UIColor+Extension.swift index 653035d..4c62707 100644 --- a/Sources/EeveeSpotify/Models/Extensions/UIColor+Extension.swift +++ b/Sources/EeveeSpotify/Shared/Models/Extensions/UIColor+Extension.swift @@ -2,7 +2,6 @@ import SwiftUI import UIKit extension UIColor { - func mix(with target: UIColor, amount: CGFloat) -> Self { var r1: CGFloat = 0, g1: CGFloat = 0, b1: CGFloat = 0, a1: CGFloat = 0 var r2: CGFloat = 0, g2: CGFloat = 0, b2: CGFloat = 0, a2: CGFloat = 0 diff --git a/Sources/EeveeSpotify/Models/Extensions/UIDevice+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/UIDevice+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/UIDevice+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/UIDevice+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/UIView+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/UIView+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/UIView+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/UIView+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/URL+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/URL+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/URL+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/URL+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift b/Sources/EeveeSpotify/Shared/Models/Extensions/UserDefaults+Extension.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Extensions/UserDefaults+Extension.swift rename to Sources/EeveeSpotify/Shared/Models/Extensions/UserDefaults+Extension.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTEncoreAttributedString.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreAttributedString.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTEncoreAttributedString.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreAttributedString.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTEncoreAttributes.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreAttributes.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTEncoreAttributes.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreAttributes.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTEncoreLabel.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreLabel.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTEncoreLabel.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreLabel.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTEncorePopUpDialog.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTEncorePopUpDialog.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTEncorePopUpDialog.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTEncorePopUpDialog.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTEncorePopUpDialogModel.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTEncorePopUpDialogModel.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTEncorePopUpDialogModel.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTEncorePopUpDialogModel.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTEncorePopUpPresenter.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTEncorePopUpPresenter.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTEncorePopUpPresenter.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTEncorePopUpPresenter.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTEncoreTypeStyle.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreTypeStyle.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTEncoreTypeStyle.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTEncoreTypeStyle.swift diff --git a/Sources/EeveeSpotify/Models/Headers/SPTURL.swift b/Sources/EeveeSpotify/Shared/Models/Headers/SPTURL.swift similarity index 100% rename from Sources/EeveeSpotify/Models/Headers/SPTURL.swift rename to Sources/EeveeSpotify/Shared/Models/Headers/SPTURL.swift diff --git a/Sources/EeveeSpotify/Shared/Models/VersionHookTarget.swift b/Sources/EeveeSpotify/Shared/Models/VersionHookTarget.swift new file mode 100644 index 0000000..897bec8 --- /dev/null +++ b/Sources/EeveeSpotify/Shared/Models/VersionHookTarget.swift @@ -0,0 +1,5 @@ +enum VersionHookTarget { + case latest + case lastAvailableiOS15 + case lastAvailableiOS14 +} diff --git a/Sources/EeveeSpotify/Tweak.x.swift b/Sources/EeveeSpotify/Tweak.x.swift index 3c58315..0af7c14 100644 --- a/Sources/EeveeSpotify/Tweak.x.swift +++ b/Sources/EeveeSpotify/Tweak.x.swift @@ -12,8 +12,19 @@ struct PremiumPatchingGroup: HookGroup { } struct EeveeSpotify: Tweak { static let version = "5.9.9" - static let isOldSpotifyVersion = - NSClassFromString("Lyrics_NPVCommunicatorImpl.LyricsOnlyViewController") == nil + + static var hookTarget: VersionHookTarget { + let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String + + switch version { + case "9.0.48": + return .lastAvailableiOS15 + case "8.9.8": + return .lastAvailableiOS14 + default: + return .latest + } + } init() { if UserDefaults.darkPopUps {