mirror of
https://github.com/whoeevee/EeveeSpotifyReborn.git
synced 2026-01-09 00:23:20 +01:00
what i've done so far
This commit is contained in:
@@ -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<UIView> {
|
||||
typealias Group = DarkPopUps
|
||||
|
||||
@@ -34,7 +34,7 @@ class SPTDataLoaderServiceHook: ClassHook<NSObject>, SpotifySessionDelegate {
|
||||
orig.URLSession(
|
||||
session,
|
||||
dataTask: task,
|
||||
didReceiveData: try getLyricsForCurrentTrack(
|
||||
didReceiveData: try getLyricsDataForCurrentTrack(
|
||||
originalLyrics: try? Lyrics(serializedBytes: buffer)
|
||||
)
|
||||
)
|
||||
@@ -94,7 +94,7 @@ class SPTDataLoaderServiceHook: ClassHook<NSObject>, SpotifySessionDelegate {
|
||||
)!
|
||||
|
||||
do {
|
||||
let lyricsData = try getLyricsForCurrentTrack()
|
||||
let lyricsData = try getLyricsDataForCurrentTrack()
|
||||
|
||||
orig.URLSession(
|
||||
session,
|
||||
@@ -109,9 +109,7 @@ class SPTDataLoaderServiceHook: ClassHook<NSObject>, SpotifySessionDelegate {
|
||||
return
|
||||
}
|
||||
catch {
|
||||
NSLog("[EeveeSpotify] Unable to load lyrics: \(error)")
|
||||
orig.URLSession(session, task: task, didCompleteWithError: error)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<NSObject> {
|
||||
static let targetName = "SPTNowPlayingModel"
|
||||
|
||||
func currentTrack() -> SPTPlayerTrack? {
|
||||
if let track = orig.currentTrack() {
|
||||
HookedInstances.currentTrack = track
|
||||
return track
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
class SPTNowPlayingMetadataBackgroundViewModelHook: ClassHook<NSObject> {
|
||||
static let targetName = "SPTNowPlayingMetadataBackgroundViewModel"
|
||||
|
||||
func color() -> UIColor {
|
||||
HookedInstances.nowPlayingMetaBackgroundModel = Dynamic.convert(
|
||||
target,
|
||||
to: SPTNowPlayingMetadataBackgroundViewModel.self
|
||||
)
|
||||
return orig.color()
|
||||
}
|
||||
}
|
||||
|
||||
class SPTCoreProductStateInstanceHook: ClassHook<NSObject> {
|
||||
static let targetName = "SPTCoreProductState"
|
||||
|
||||
func stringForKey(_ key: String) -> NSString {
|
||||
HookedInstances.productState = Dynamic.convert(
|
||||
target,
|
||||
to: SPTCoreProductState.self
|
||||
)
|
||||
return orig.stringForKey(key)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import Orion
|
||||
import UIKit
|
||||
|
||||
class SPTPlayerTrackHook: ClassHook<NSObject> {
|
||||
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<NSObject> {
|
||||
|
||||
class LyricsScrollProviderHook: ClassHook<NSObject> {
|
||||
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<NSObject> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import Orion
|
||||
import UIKit
|
||||
|
||||
class LyricsFullscreenViewControllerHook: ClassHook<UIViewController> {
|
||||
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<UIView>(target.view).headerView
|
||||
|
||||
if let reportButton = headerView.subviews(matching: "EncoreButton")[1] as? UIButton {
|
||||
reportButton.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import Orion
|
||||
import UIKit
|
||||
|
||||
class LyricsOnlyViewControllerHook: ClassHook<UIViewController> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -5,163 +5,24 @@ import SwiftUI
|
||||
|
||||
struct LyricsGroup: HookGroup { }
|
||||
|
||||
//
|
||||
var lyricsState = LyricsLoadingState()
|
||||
|
||||
class LyricsFullscreenViewControllerHook: ClassHook<UIViewController> {
|
||||
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<UIView>(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<UIViewController> {
|
||||
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()
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import Orion
|
||||
|
||||
extension NowPlayingScrollViewController {
|
||||
private var nowPlayingScrollViewModel: NSObject {
|
||||
get {
|
||||
Ivars<NSObject>(self).scrollViewModel
|
||||
}
|
||||
}
|
||||
|
||||
var scrollEnabled: Bool {
|
||||
get {
|
||||
Ivars<Bool>(nowPlayingScrollViewModel).scrollEnabled
|
||||
}
|
||||
set {
|
||||
Ivars<Bool>(nowPlayingScrollViewModel).scrollEnabled = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var loadedTrack: SPTPlayerTrack {
|
||||
get {
|
||||
Ivars<SPTPlayerTrack>(nowPlayingScrollViewModel).loadedTrack
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var backgroundViewController: SPTNowPlayingBackgroundViewController {
|
||||
get {
|
||||
Ivars<SPTNowPlayingBackgroundViewController>(self).backgroundViewController
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import UIKit
|
||||
|
||||
@objc protocol SPTNowPlayingBackgroundViewController {
|
||||
func color() -> UIColor
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,7 @@ import Foundation
|
||||
|
||||
struct LyricsLoadingState {
|
||||
var wasRomanized = false
|
||||
var areEmpty = false
|
||||
var isEmpty = false
|
||||
var fallbackError: LyricsError? = nil
|
||||
var loadedSuccessfully = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import Orion
|
||||
import UIKit
|
||||
|
||||
var nowPlayingScrollViewController: NowPlayingScrollViewController?
|
||||
|
||||
class NowPlayingScrollViewControllerInstanceHook: ClassHook<UIViewController> {
|
||||
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!
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
@objc protocol SPTCoreProductState {
|
||||
func stringForKey(_ key: String) -> String
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
@objc protocol SPTNowPlayingMetadataBackgroundViewModel {
|
||||
func color() -> UIColor
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -10,9 +10,10 @@ private func showHavePremiumPopUp() {
|
||||
|
||||
class SpotifySessionDelegateBootstrapHook: ClassHook<NSObject>, SpotifySessionDelegate {
|
||||
static var targetName: String {
|
||||
EeveeSpotify.isOldSpotifyVersion
|
||||
? "SPTCoreURLSessionDataDelegate"
|
||||
: "SPTDataLoaderService"
|
||||
switch EeveeSpotify.hookTarget {
|
||||
case .lastAvailableiOS14: return "SPTCoreURLSessionDataDelegate"
|
||||
default: return "SPTDataLoaderService"
|
||||
}
|
||||
}
|
||||
|
||||
func URLSession(
|
||||
|
||||
@@ -2,6 +2,8 @@ import Orion
|
||||
import Intents
|
||||
|
||||
class INMediaItemHook: ClassHook<INMediaItem> {
|
||||
typealias Group = PremiumPatchingGroup
|
||||
|
||||
func identifier() -> String {
|
||||
var identifier = orig.identifier()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class SPTFreeTierArtistHubRemoteURLResolverHook: ClassHook<NSObject> {
|
||||
onDemandSet: Any,
|
||||
onDemandTrialService: Any,
|
||||
trackRowsEnabled: Bool,
|
||||
productState: SPTCoreProductState
|
||||
productState: NSObject
|
||||
) -> Target {
|
||||
return orig.initWithViewURI(
|
||||
uri,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
enum VersionHookTarget {
|
||||
case latest
|
||||
case lastAvailableiOS15
|
||||
case lastAvailableiOS14
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user