mirror of
https://github.com/whoeevee/EeveeSpotifyReborn.git
synced 2026-01-09 00:23:20 +01:00
actual fix for #437 and Do Not Replace Lyrics option
This commit is contained in:
@@ -6,8 +6,11 @@ class SPTDataLoaderServiceHook: ClassHook<NSObject>, 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<NSObject>, SpotifySessionDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
if url.isLyrics, response.statusCode != 200 {
|
||||
if shouldModify(url), url.isLyrics, response.statusCode != 200 {
|
||||
let okResponse = HTTPURLResponse(
|
||||
url: url,
|
||||
statusCode: 200,
|
||||
|
||||
@@ -39,7 +39,6 @@ struct PopUpHelper {
|
||||
|
||||
dialog.update(model)
|
||||
dialog.setEventHandler({ state in
|
||||
|
||||
switch (state) {
|
||||
|
||||
case .primary: onPrimaryClick?()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import Orion
|
||||
|
||||
class SPTPlayerTrackHook: ClassHook<NSObject> {
|
||||
typealias Group = LyricsGroup
|
||||
static let targetName = "SPTPlayerTrack"
|
||||
|
||||
func metadata() -> [String:String] {
|
||||
var meta = orig.metadata()
|
||||
|
||||
meta["has_lyrics"] = "true"
|
||||
return meta
|
||||
}
|
||||
}
|
||||
|
||||
class LyricsScrollProviderHook: ClassHook<NSObject> {
|
||||
typealias Group = LyricsGroup
|
||||
|
||||
static var targetName: String {
|
||||
return EeveeSpotify.isOldSpotifyVersion
|
||||
? "Lyrics_CoreImpl.ScrollProvider"
|
||||
: "Lyrics_NPVCommunicatorImpl.ScrollProvider"
|
||||
}
|
||||
|
||||
func isEnabledForTrack(_ track: SPTPlayerTrack) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,15 @@
|
||||
import Orion
|
||||
import SwiftUI
|
||||
|
||||
class SPTPlayerTrackHook: ClassHook<NSObject> {
|
||||
static let targetName = "SPTPlayerTrack"
|
||||
//
|
||||
|
||||
func metadata() -> [String:String] {
|
||||
var meta = orig.metadata()
|
||||
struct LyricsGroup: HookGroup { }
|
||||
|
||||
meta["has_lyrics"] = "true"
|
||||
return meta
|
||||
}
|
||||
}
|
||||
|
||||
class LyricsScrollProviderHook: ClassHook<NSObject> {
|
||||
|
||||
static var targetName: String {
|
||||
return EeveeSpotify.isOldSpotifyVersion
|
||||
? "Lyrics_CoreImpl.ScrollProvider"
|
||||
: "Lyrics_NPVCommunicatorImpl.ScrollProvider"
|
||||
}
|
||||
|
||||
func isEnabledForTrack(_ track: SPTPlayerTrack) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
class LyricsFullscreenViewControllerHook: ClassHook<UIViewController> {
|
||||
|
||||
typealias Group = LyricsGroup
|
||||
|
||||
static var targetName: String {
|
||||
return EeveeSpotify.isOldSpotifyVersion
|
||||
? "Lyrics_CoreImpl.FullscreenViewController"
|
||||
@@ -62,6 +45,7 @@ private var hasShownUnauthorizedPopUp = false
|
||||
//
|
||||
|
||||
class LyricsOnlyViewControllerHook: ClassHook<UIViewController> {
|
||||
typealias Group = LyricsGroup
|
||||
|
||||
static var targetName: String {
|
||||
return EeveeSpotify.isOldSpotifyVersion
|
||||
@@ -70,7 +54,6 @@ class LyricsOnlyViewControllerHook: ClassHook<UIViewController> {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -69,7 +69,7 @@ class SpotifySessionDelegateBootstrapHook: ClassHook<NSObject>, SpotifySessionDe
|
||||
}
|
||||
else {
|
||||
UserDefaults.patchType = .requests
|
||||
PremiumPatching().activate()
|
||||
PremiumPatchingGroup().activate()
|
||||
}
|
||||
|
||||
NSLog("[EeveeSpotify] Fetched bootstrap, \(UserDefaults.patchType) was set")
|
||||
|
||||
@@ -6,7 +6,7 @@ private let likedTracksRow: [String: Any] = [
|
||||
]
|
||||
|
||||
class HUBViewModelBuilderImplementationHook: ClassHook<NSObject> {
|
||||
typealias Group = PremiumPatching
|
||||
typealias Group = PremiumPatchingGroup
|
||||
static let targetName: String = "HUBViewModelBuilderImplementation"
|
||||
|
||||
func addJSONDictionary(_ dictionary: NSDictionary?) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import Orion
|
||||
import UIKit
|
||||
|
||||
class StreamQualitySettingsSectionHook: ClassHook<NSObject> {
|
||||
typealias Group = PremiumPatching
|
||||
typealias Group = PremiumPatchingGroup
|
||||
static let targetName = "StreamQualitySettingsSection"
|
||||
|
||||
func shouldResetSelection() -> Bool {
|
||||
@@ -15,17 +15,8 @@ class StreamQualitySettingsSectionHook: ClassHook<NSObject> {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
private func showOfflineModePopUp() {
|
||||
PopUpHelper.showPopUp(
|
||||
message: "playlist_downloading_popup".localized,
|
||||
buttonText: "OK".uiKitLocalized
|
||||
)
|
||||
}
|
||||
|
||||
class ContentOffliningUIHelperImplementationHook: ClassHook<NSObject> {
|
||||
typealias Group = PremiumPatching
|
||||
typealias Group = PremiumPatchingGroup
|
||||
static let targetName = "Offline_ContentOffliningUIImpl.ContentOffliningUIHelperImplementation"
|
||||
|
||||
func downloadToggledWithCurrentAvailability(
|
||||
@@ -34,18 +25,31 @@ class ContentOffliningUIHelperImplementationHook: ClassHook<NSObject> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Orion
|
||||
|
||||
class SPTFreeTierArtistHubRemoteURLResolverHook: ClassHook<NSObject> {
|
||||
typealias Group = PremiumPatching
|
||||
typealias Group = PremiumPatchingGroup
|
||||
static let targetName = "SPTFreeTierArtistHubRemoteURLResolver"
|
||||
|
||||
func initWithViewURI(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+44
-35
@@ -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<Bool>(
|
||||
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 = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Bool>(
|
||||
get: { UserDefaults.fallbackReasons },
|
||||
set: { UserDefaults.fallbackReasons = $0 }
|
||||
)
|
||||
"genius_fallback".localized,
|
||||
isOn: $geniusFallback
|
||||
)
|
||||
|
||||
if geniusFallback {
|
||||
Toggle(
|
||||
"show_fallback_reasons".localized,
|
||||
isOn: Binding<Bool>(
|
||||
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)
|
||||
|
||||
@@ -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<Bool>(
|
||||
|
||||
@@ -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<Color>(
|
||||
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<Color>(
|
||||
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 {
|
||||
|
||||
@@ -26,7 +26,7 @@ struct CommonIssuesTipView: View {
|
||||
.foregroundColor(.white)
|
||||
|
||||
+ Text("common_issues_tip_button".localized)
|
||||
.foregroundColor(.blue)
|
||||
.foregroundColor(EeveeSettingsView.spotifyAccentColor)
|
||||
|
||||
+ Text(".")
|
||||
.foregroundColor(.white)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user