This commit is contained in:
eevee
2024-10-09 21:38:29 +03:00
parent 657f808387
commit e76b62edbc
54 changed files with 470 additions and 171 deletions
+6 -11
View File
@@ -5,21 +5,17 @@ import SwiftUI
struct DarkPopUps: HookGroup { }
class EncoreLabelHook: ClassHook<UIView> {
typealias Group = DarkPopUps
static let targetName = "SPTEncoreLabel"
func intrinsicContentSize() -> CGSize {
if let viewController = WindowHelper.shared.viewController(for: target),
NSStringFromClass(type(of: viewController)) == "SPTEncorePopUpContainer"
{
let label = Dynamic.convert(target.subviews.first!, to: UILabel.self)
if let viewController = WindowHelper.shared.viewController(for: target) {
if NSStringFromClass(type(of: viewController)) == "SPTEncorePopUpContainer" {
let label = Dynamic.convert(target.subviews.first!, to: UILabel.self)
if !label.hasParent(matching: "Primary") {
label.textColor = .white
}
if !label.hasParent(matching: "Primary") {
label.textColor = .white
}
}
@@ -28,7 +24,6 @@ class EncoreLabelHook: ClassHook<UIView> {
}
class SPTEncorePopUpContainerHook: ClassHook<UIViewController> {
typealias Group = DarkPopUps
static let targetName = "SPTEncorePopUpContainer"
@@ -1,8 +1,7 @@
import UIKit
import Orion
class PopUpHelper {
struct PopUpHelper {
private static var isPopUpShowing = false
static let sharedPresenter = type(
@@ -19,9 +18,7 @@ class PopUpHelper {
onPrimaryClick: (() -> Void)? = nil,
onSecondaryClick: (() -> Void)? = nil
) {
DispatchQueue.main.asyncAfter(deadline: delayed ? .now() + 3.0 : .now()) {
if isPopUpShowing {
return
}
@@ -1,7 +1,6 @@
import UIKit
class URLSessionHelper {
static let shared = URLSessionHelper()
private var requestsMap: [URL:Data]
@@ -1,7 +1,6 @@
import UIKit
class WindowHelper {
struct WindowHelper {
static let shared = WindowHelper()
let window: UIWindow
@@ -17,7 +16,6 @@ class WindowHelper {
}
func findFirstViewController(_ regex: String) -> UIViewController? {
let rootView = self.rootViewController.view!
var result: UIViewController?
@@ -37,6 +35,10 @@ class WindowHelper {
searchViews(rootView)
return result
}
func dismissCurrentViewController() {
rootViewController.dismiss(animated: true)
}
func overrideUserInterfaceStyle(_ style: UIUserInterfaceStyle) {
window.overrideUserInterfaceStyle = style
@@ -185,7 +185,7 @@ private func loadLyricsForCurrentTrack() throws {
PopUpHelper.showPopUp(
delayed: false,
message: "musixmatch_unauthorized_popup".localized,
buttonText: "OK"
buttonText: "OK".uiKitLocalized
)
hasShownUnauthorizedPopUp.toggle()
@@ -197,7 +197,7 @@ private func loadLyricsForCurrentTrack() throws {
PopUpHelper.showPopUp(
delayed: false,
message: "musixmatch_restricted_popup".localized,
buttonText: "OK"
buttonText: "OK".uiKitLocalized
)
hasShownRestrictedPopUp.toggle()
@@ -1,7 +1,6 @@
import Foundation
struct GeniusLyricsRepository: LyricsRepository {
private let jsonDecoder: JSONDecoder
private let apiUrl = "https://api.genius.com"
private let session: URLSession
@@ -2,7 +2,6 @@ import Foundation
import UIKit
class MusixmatchLyricsRepository: LyricsRepository {
private let apiUrl = "https://apic.musixmatch.com"
var selectedLanguage: String
@@ -19,9 +18,7 @@ class MusixmatchLyricsRepository: LyricsRepository {
_ path: String,
query: [String: Any] = [:]
) throws -> Data {
var stringUrl = "\(apiUrl)\(path)"
var finalQuery = query
finalQuery["usertoken"] = UserDefaults.musixmatchToken
@@ -1,4 +1,5 @@
import Foundation
import UIKit
import NaturalLanguage
extension String {
@@ -10,6 +11,11 @@ extension String {
BundleHelper.shared.localizedString(self)
}
var uiKitLocalized: String {
let bundle = Bundle(for: UIApplication.self)
return bundle.localizedString(forKey: self, value: nil, table: nil)
}
func localizeWithFormat(_ arguments: CVarArg...) -> String{
String(format: self.localized, arguments: arguments)
}
-2
View File
@@ -2,9 +2,7 @@ import Orion
import UIKit
class UIOpenURLContextHook: ClassHook<UIOpenURLContext> {
func URL() -> URL {
let url = orig.URL()
if url.isOpenSpotifySafariExtension {
@@ -4,7 +4,7 @@ private func showHavePremiumPopUp() {
PopUpHelper.showPopUp(
delayed: true,
message: "have_premium_popup".localized,
buttonText: "ok".localized
buttonText: "OK".uiKitLocalized
)
}
@@ -8,7 +8,7 @@ class StreamQualitySettingsSectionHook: ClassHook<NSObject> {
func shouldResetSelection() -> Bool {
PopUpHelper.showPopUp(
message: "high_audio_quality_popup".localized,
buttonText: "ok".localized
buttonText: "OK".uiKitLocalized
)
return true
@@ -20,7 +20,7 @@ class StreamQualitySettingsSectionHook: ClassHook<NSObject> {
private func showOfflineModePopUp() {
PopUpHelper.showPopUp(
message: "playlist_downloading_popup".localized,
buttonText: "ok".localized
buttonText: "OK".uiKitLocalized
)
}
@@ -35,6 +35,16 @@ class ContentOffliningUIHelperImplementationHook: ClassHook<NSObject> {
pageIdentifier: String,
pageURI: URL
) -> String {
if pageIdentifier == "spotify:local-files" {
return orig.downloadToggledWithCurrentAvailability(
availability,
addAction: addAction,
removeAction: removeAction,
pageIdentifier: pageIdentifier,
pageURI: pageURI
)
}
showOfflineModePopUp()
return pageIdentifier
}
@@ -3,7 +3,6 @@ import SwiftUI
import UIKit
class ProfileSettingsSectionHook: ClassHook<NSObject> {
static let targetName = "ProfileSettingsSection"
func numberOfRows() -> Int {
@@ -11,9 +10,7 @@ class ProfileSettingsSectionHook: ClassHook<NSObject> {
}
func didSelectRow(_ row: Int) {
if row == 1 {
let rootSettingsController = WindowHelper.shared.findFirstViewController(
"RootSettingsViewController"
)!
@@ -62,9 +59,7 @@ class ProfileSettingsSectionHook: ClassHook<NSObject> {
}
func cellForRow(_ row: Int) -> UITableViewCell {
if row == 1 {
let settingsTableCell = Dynamic.SPTSettingsTableViewCell
.alloc(interface: SPTSettingsTableViewCell.self)
.initWithStyle(3, reuseIdentifier: "EeveeSpotify")
@@ -0,0 +1,43 @@
import Foundation
struct GitHubHelper {
private let apiUrl = "https://api.github.com"
private let decoder = JSONDecoder()
static let shared = GitHubHelper()
init() {
decoder.keyDecodingStrategy = .convertFromSnakeCase
}
private func perform(_ path: String) async throws -> Data {
let url = URL(string: "\(apiUrl)\(path)")!
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
func getLatestRelease() async throws -> GitHubRelease {
let data = try await perform("/repos/whoeevee/EeveeSpotify/releases/latest")
return try decoder.decode(GitHubRelease.self, from: data)
}
func getUser(_ username: String) async throws -> GitHubUser {
let data = try await perform("/users/\(username)")
return try decoder.decode(GitHubUser.self, from: data)
}
func getContributors() async throws -> [GitHubUser] {
let data = try await perform("/repos/whoeevee/EeveeSpotify/contributors")
return try decoder.decode([GitHubUser].self, from: data)
}
func getEeveeContributorSections() async throws -> [EeveeContributorSection] {
let (data, _) = try await URLSession.shared.data(
from: URL(
string: "https://raw.githubusercontent.com/whoeevee/EeveeSpotify/swift/repo.altsource.json"
)!
)
return try decoder.decode([EeveeContributorSection].self, from: data)
}
}
@@ -0,0 +1,4 @@
struct EeveeContributor: Decodable, Equatable {
var username: String
var roles: [String]
}
@@ -0,0 +1,5 @@
struct EeveeContributorSection: Decodable, Equatable {
var title: String
var shuffled: Bool
var contributors: [EeveeContributor]
}
@@ -0,0 +1,3 @@
struct GitHubRelease: Decodable {
var tagName: String
}
@@ -1,5 +0,0 @@
import Foundation
struct GitHubReleaseInfo: Decodable {
var tag_name: String
}
@@ -0,0 +1,5 @@
struct GitHubUser: Decodable, Equatable {
var avatarUrl: String
var htmlUrl: String
var login: String
}
@@ -0,0 +1,51 @@
import SwiftUI
import UIKit
class ImageViewModel: ObservableObject {
@Published var image: UIImage?
private var imageCache: NSCache<NSString, UIImage>?
init(urlString: String?) {
loadImage(urlString: urlString)
}
private func loadImage(urlString: String?) {
guard let urlString = urlString else { return }
if let imageFromCache = getImageFromCache(from: urlString) {
self.image = imageFromCache
return
}
loadImageFromURL(urlString: urlString)
}
private func loadImageFromURL(urlString: String) {
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
DispatchQueue.main.async { [weak self] in
guard let loadedImage = UIImage(data: data) else { return }
self?.image = loadedImage
self?.setImageCache(image: loadedImage, key: urlString)
}
}.resume()
}
private func setImageCache(image: UIImage, key: String) {
imageCache?.setObject(image, forKey: key as NSString)
}
private func getImageFromCache(from key: String) -> UIImage? {
return imageCache?.object(forKey: key as NSString) as? UIImage
}
}
@@ -0,0 +1,33 @@
import SwiftUI
struct EeveeContributorView: View {
var contributor: EeveeContributor
var githubUser: GitHubUser
var body: some View {
VStack {
Link(destination: URL(string: githubUser.htmlUrl)!) {
HStack(spacing: 10) {
ImageView(urlString: githubUser.avatarUrl)
.frame(width: 48, height: 48)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 0) {
Text(contributor.username)
.foregroundColor(.white)
.font(.headline)
ForEach(contributor.roles, id: \.self) { role in
Text(role)
}
.foregroundColor(.gray)
}
Spacer()
ChevronRightView()
}
}
}
}
}
@@ -0,0 +1,57 @@
import SwiftUI
struct EeveeContributorsSheetView: View {
@State private var users: [GitHubUser] = []
@State private var sections: [EeveeContributorSection] = []
var body: some View {
NavigationView {
VStack {
if users.isEmpty && sections.isEmpty {
ProgressView("Loading".uiKitLocalized)
}
else {
List {
ForEach(sections, id: \.title) { section in
Section {
ForEach(
section.shuffled
? section.contributors.shuffled()
: section.contributors,
id: \.username
) { contributor in
if let user = users.first(where: { $0.login == contributor.username }) {
EeveeContributorView(contributor: contributor, githubUser: user)
}
}
} header: {
Text(section.title)
}
}
}
}
}
.navigationTitle("contributors".localized)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
Button {
WindowHelper.shared.dismissCurrentViewController()
} label: {
Text("Done".uiKitLocalized)
.font(.headline)
}
}
.animation(.default, value: users)
.animation(.default, value: sections)
.onAppear {
Task {
users = try await GitHubHelper.shared.getContributors()
sections = try await GitHubHelper.shared.getEeveeContributorSections()
}
}
}
}
}
@@ -0,0 +1,55 @@
import SwiftUI
struct EeveeSettingsVersionView: View {
@State private var latestVersion: String?
@State private var isPresentingContributorsSheet = false
private func loadVersion() async throws {
let release = try await GitHubHelper.shared.getLatestRelease()
latestVersion = String(release.tagName.dropFirst(5)) // swiftX.X
}
private var isUpdateAvailable: Bool {
latestVersion != nil && latestVersion != EeveeSpotify.version
}
var body: some View {
Section {
if isUpdateAvailable {
Link(
"update_available".localized,
destination: URL(string: "https://github.com/whoeevee/EeveeSpotify/releases")!
)
}
} footer: {
VStack(alignment: .leading) {
Text("v\(EeveeSpotify.version)")
if latestVersion == nil {
HStack(spacing: 10) {
ProgressView()
Text("checking_for_update".localized)
}
}
else {
Button("\("contributors".localized)...") {
isPresentingContributorsSheet = true
}
.foregroundColor(.gray)
.font(.subheadline.weight(.semibold))
}
}
}
.sheet(isPresented: $isPresentingContributorsSheet) {
EeveeContributorsSheetView()
}
.animation(.default, value: latestVersion)
.onAppear {
Task {
try await loadVersion()
}
}
}
}
@@ -1,45 +0,0 @@
import SwiftUI
extension EeveeSettingsView {
func loadVersion() async throws {
let (data, _) = try await URLSession.shared.data(
from: URL(string: "https://api.github.com/repos/whoeevee/EeveeSpotify/releases/latest")!
)
let tag = try JSONDecoder().decode(GitHubReleaseInfo.self, from: data).tag_name
latestVersion = String(tag.dropFirst(5))
}
private var isUpdateAvailable: Bool {
guard
let latest = Double(latestVersion),
let current = Double(EeveeSpotify.version)
else {
return false
}
return latest > current
}
@ViewBuilder func VersionSection() -> some View {
Section {
if isUpdateAvailable {
Link(
"update_available".localized,
destination: URL(string: "https://github.com/whoeevee/EeveeSpotify/releases")!
)
}
} footer: {
VStack(alignment: .leading) {
Text("v\(EeveeSpotify.version)")
if latestVersion.isEmpty {
HStack(spacing: 10) {
ProgressView()
Text("checking_for_update".localized)
}
}
}
}
}
}
@@ -4,8 +4,6 @@ import UIKit
struct EeveeSettingsView: View {
let navigationController: UINavigationController
@State var latestVersion = ""
@State private var hasShownCommonIssuesTip = UserDefaults.hasShownCommonIssuesTip
@State private var isClearingData = false
@@ -17,10 +15,17 @@ struct EeveeSettingsView: View {
)
navigationController.pushViewController(viewController, animated: true)
}
init(navigationController: UINavigationController) {
self.navigationController = navigationController
let spotifyAccentColor = UIColor(Color(hex: "#1ed760"))
UIView.appearance().tintColor = spotifyAccentColor
}
var body: some View {
List {
VersionSection()
EeveeSettingsVersionView()
if !hasShownCommonIssuesTip {
CommonIssuesTipView(
@@ -95,19 +100,13 @@ struct EeveeSettingsView: View {
}
}
}
.listStyle(GroupedListStyle())
.animation(.default, value: isClearingData)
.animation(.default, value: latestVersion)
.animation(.default, value: hasShownCommonIssuesTip)
.onAppear {
WindowHelper.shared.overrideUserInterfaceStyle(.dark)
Task {
try await loadVersion()
}
}
}
}
@@ -1,7 +1,6 @@
import SwiftUI
extension EeveeLyricsSettingsView {
func getMusixmatchToken(_ input: String) -> String? {
if let match = input.firstMatch("\\[UserToken\\]: ([a-f0-9]+)"),
let tokenRange = Range(match.range(at: 1), in: input) {
@@ -21,17 +20,15 @@ extension EeveeLyricsSettingsView {
preferredStyle: .alert
)
alert.view.tintColor = UIColor(Color(hex: "#1ed760"))
alert.addTextField() { textField in
textField.placeholder = "---- Debug Info ---- [Device]: iPhone"
}
alert.addAction(UIAlertAction(title: "cancel".localized, style: .cancel) { _ in
alert.addAction(UIAlertAction(title: "Cancel".uiKitLocalized, style: .cancel) { _ in
lyricsSource = oldSource
})
alert.addAction(UIAlertAction(title: "ok".localized, style: .default) { _ in
alert.addAction(UIAlertAction(title: "OK".uiKitLocalized, style: .default) { _ in
let text = alert.textFields!.first!.text!
guard let token = getMusixmatchToken(text) else {
@@ -1,7 +1,6 @@
import SwiftUI
struct EeveeLyricsSettingsView: View {
@State var musixmatchToken = UserDefaults.musixmatchToken
@State var lyricsSource = UserDefaults.lyricsSource
@State var geniusFallback = UserDefaults.geniusFallback
@@ -63,8 +63,8 @@ struct EeveeUISettingsView: View {
.modifier(ListRowSeparatorHidden())
}
}
.listStyle(GroupedListStyle())
.listStyle(GroupedListStyle())
.animation(.default, value: lyricsColors)
}
}
@@ -0,0 +1,9 @@
import SwiftUI
struct ChevronRightView: View {
var body: some View {
Image(systemName: "chevron.right")
.font(.subheadline.weight(.semibold))
.foregroundColor(Color(UIColor.systemGray2))
}
}
@@ -1,7 +1,6 @@
import SwiftUI
struct CommonIssuesTipView: View {
var onDismiss: () -> Void
var body: some View {
@@ -18,7 +17,9 @@ struct CommonIssuesTipView: View {
}
Link(
destination: URL(string: "https://github.com/whoeevee/EeveeSpotify/blob/swift/common_issues.md")!,
destination: URL(
string: "https://github.com/whoeevee/EeveeSpotify/blob/swift/common_issues.md"
)!,
label: {
VStack {
Text("\("common_issues_tip_message".localized) ")
@@ -0,0 +1,14 @@
import SwiftUI
struct ImageView: View {
@ObservedObject private var imageViewModel: ImageViewModel
init(urlString: String?) {
imageViewModel = ImageViewModel(urlString: urlString)
}
var body: some View {
Image(uiImage: imageViewModel.image ?? UIImage())
.resizable()
}
}
@@ -1,7 +1,6 @@
import SwiftUI
struct NavigationSectionView: View {
var color: Color
var title: String
var imageSystemName: String
@@ -23,9 +22,7 @@ struct NavigationSectionView: View {
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(Color(UIColor.systemGray2))
.font(.subheadline.bold())
ChevronRightView()
}
}
}
+1 -2
View File
@@ -11,8 +11,7 @@ func exitApplication() {
struct PremiumPatching: HookGroup { }
struct EeveeSpotify: Tweak {
static let version = "5.4"
static let version = "5.5"
static let isOldSpotifyVersion = NSClassFromString("Lyrics_NPVCommunicatorImpl.LyricsOnlyViewController") == nil
init() {
+136
View File
@@ -0,0 +1,136 @@
[
{
"title": "Developement",
"shuffled": false,
"contributors": [
{
"username": "whoeevee",
"roles": [
"Owner",
"Main Developer"
]
},
{
"username": "asdfzxcvbn",
"roles": [
"Collaborator",
"EeveeRepoUpdater"
]
}
]
},
{
"title": "Localization",
"shuffled": true,
"contributors": [
{
"username": "longopy",
"roles": [
"Spanish"
]
},
{
"username": "xiangfeidexiaohuo",
"roles": [
"Simplified Chinese"
]
},
{
"username": "LivioZ",
"roles": [
"Italian"
]
},
{
"username": "LIKVIDATOR1337",
"roles": [
"Ukrainian"
]
},
{
"username": "gototheskinny",
"roles": [
"Turkish"
]
},
{
"username": "ElliotCHEN37",
"roles": [
"Traditional Chinese"
]
},
{
"username": "schweppes-0x",
"roles": [
"Bulgarian"
]
},
{
"username": "speedyfriend433",
"roles": [
"Korean"
]
},
{
"username": "Richard-NDC",
"roles": [
"Vietnamese"
]
},
{
"username": "wlxxd",
"roles": [
"German"
]
},
{
"username": "Incognito-Coder",
"roles": [
"Persian"
]
},
{
"username": "UnexcitingDean",
"roles": [
"Danish"
]
},
{
"username": "An0n-00",
"roles": [
"Swiss German"
]
},
{
"username": "CukierDev",
"roles": [
"Polish"
]
},
{
"username": "3xynos7",
"roles": [
"Nepalese"
]
},
{
"username": "by3lish",
"roles": [
"Azerbaijani"
]
},
{
"username": "5jd",
"roles": [
"French"
]
},
{
"username": "emal0n",
"roles": [
"Brazil"
]
}
]
}
]
+1 -1
View File
@@ -1,6 +1,6 @@
Package: com.eevee.spotify
Name: EeveeSpotify
Version: 5.4
Version: 5.5
Architecture: iphoneos-arm
Description: A tweak to get Spotify Premium for free, just like Spotilife
Maintainer: Eevee
@@ -15,9 +15,6 @@ reset_data_description = "Keş yaddaşını təmizləyin və tətbiqi yenidən b
checking_for_update = "Yeniləmələr yoxlanılır...";
update_available = "Yeniləmə Mövcuddur";
cancel = "Ləğv et";
ok = "OK";
// Patching
do_not_patch_premium = "Premium üçün yamaqlama";
@@ -15,9 +15,6 @@ reset_data_description = "Изчистете кешираните данни и
checking_for_update = "Проверка за актуализация...";
update_available = "Налична е актуализация";
cancel = "Отказ";
ok = "ОК";
// Patching
do_not_patch_premium = "Не пачвай Premium";
@@ -15,9 +15,6 @@ reset_data_description = "Ryd cachelagrede data og genstart appen.";
checking_for_update = "Tjekker for opdateringer...";
update_available = "Opdatering tilgængelig";
cancel = "Annuller";
ok = "OK";
// Patching
do_not_patch_premium = "Patch ikke Premium";
@@ -15,9 +15,6 @@ reset_data_description = "Lösch zwischegspeicherti Date und starte d'App neu.";
checking_for_update = "lueg nachemene Update...";
update_available = "Es het es Update verfüegbar!";
cancel = "Abbräche";
ok = "OK";
// Patching
do_not_patch_premium = "Duen Premium nid pätche";
@@ -15,9 +15,6 @@ reset_data_description = "Zwischengespeicherte Daten (Cache) löschen und App ne
checking_for_update = "Suche nach Updates...";
update_available = "Update verfügbar";
cancel = "Abbrechen";
ok = "OK";
// Patching
do_not_patch_premium = "Premium nicht patchen";
@@ -15,9 +15,6 @@ reset_data_description = "Clear cached data and restart the app.";
checking_for_update = "Checking for Update...";
update_available = "Update Available";
cancel = "Cancel";
ok = "OK";
// Patching
do_not_patch_premium = "Do Not Patch Premium";
@@ -105,3 +102,5 @@ let_the_music_play = "Let the music play...";
// liked songs title, should match official spotify loc
liked_songs = "Liked songs";
contributors = "Contributors";
@@ -15,9 +15,6 @@ reset_data_description = "Borra los datos almacenados en caché y reinicia la ap
checking_for_update = "Comprobando Actualizaciones...";
update_available = "Actualización Disponible";
cancel = "Cancelar";
ok = "Confirmar";
// Patching
do_not_patch_premium = "No Parchear Premium";
@@ -15,9 +15,6 @@ reset_data_description = "پاک کردن کش و راه اندازی مجدد";
checking_for_update = "درحال بررسی بروزرسانی...";
update_available = "آپدیت موجود است";
cancel = "لغو";
ok = "باشه";
// Patching
do_not_patch_premium = "پچ نکردن حالت ویژه";
@@ -15,9 +15,6 @@ reset_data_description = "Effacez les données en cache et redémarrez l'applica
checking_for_update = "Vérification des mises à jour...";
update_available = "Mise à jour disponible";
cancel = "Annuler";
ok = "OK";
// Patching
do_not_patch_premium = "Ne pas patcher Premium";
@@ -15,9 +15,6 @@ reset_data_description = "Cancella i dati memorizzati nella cache e riavvia l'ap
checking_for_update = "Verifica aggiornamenti...";
update_available = "Aggiornamento disponibile";
cancel = "Annulla";
ok = "OK";
// Patching
do_not_patch_premium = "Non patchare Premium";
@@ -15,9 +15,6 @@ reset_data_description = "캐시된 데이터를 지우고 앱을 다시 시작
checking_for_update = "업데이트 확인 중...";
update_available = "업데이트 가능";
cancel = "취소";
ok = "확인";
// Patching
do_not_patch_premium = "프리미엄으로 패치 안 함";
@@ -100,4 +97,4 @@ unknown_error = "알 수 없는 오류";
// Instrumental Titles
song_is_instrumental = "이 노래는 악기전용 노래입니다.";
let_the_music_play = "음악이 재생되게 놔두세요...";
let_the_music_play = "음악이 재생되게 놔두세요...";
@@ -15,9 +15,6 @@ reset_data_description = "क्यास डाटा खाली गर्न
checking_for_update = "अपडेटको लागि जाँच गर्दै...";
update_available = "अपडेट उपलब्ध छ";
cancel = "रद्द गर्नुहोस्";
ok = "ठीक छ";
// Patching
do_not_patch_premium = "प्रिमियम प्याच नगर्नुहोस्";
@@ -15,9 +15,6 @@ reset_data_description = "Wyczyść dane cache i zrestartuj aplikacje.";
checking_for_update = "Sprawdzanie aktualizacji...";
update_available = "Aktualizacja Dostępna";
cancel = "Anuluj";
ok = "OK";
// Patching
do_not_patch_premium = "Nie używaj łatki premium";
@@ -15,9 +15,6 @@ reset_data_description = "Esta função limpará os Dados em cache e reiniciará
checking_for_update = "Procurando atualizações...";
update_available = "Atualização disponivel";
cancel = "Cancelar";
ok = "Confirmar";
// Patching
do_not_patch_premium = "Não aplicar correção ao premium";
@@ -15,9 +15,6 @@ reset_data_description = "Очистить кэшированные данные
checking_for_update = "Проверка наличия обновления...";
update_available = "Доступно обновление";
cancel = "Отменить";
ok = "OK";
// Patching
do_not_patch_premium = "Не патчить Premium";
@@ -103,3 +100,5 @@ let_the_music_play = "Пусть заиграет музыка...";
// liked songs title, should match official spotify loc
liked_songs = "Тебе понравилось";
contributors = "Участники";
@@ -15,9 +15,6 @@ reset_data_description = "Önbelleğe alınan verileri temizleyin ve uygulamayı
checking_for_update = "Güncelleme Kontrol Ediliyor...";
update_available = "Güncelleme Mevcut";
cancel = "İptal";
ok = "Tamam";
// Patching
do_not_patch_premium = "Premium için olan Yamayı Yapma";
@@ -15,9 +15,6 @@ reset_data_description = "Очистити кешовані дані та пер
checking_for_update = "Перевірка наявності оновлень...";
update_available = "Доступне оновлення";
cancel = "Скасувати";
ok = "ОК";
// Patching
do_not_patch_premium = "Не патчити Premium";
@@ -15,9 +15,6 @@ reset_data_description = "Xóa cache và khởi động lại app.";
checking_for_update = "Đang kiểm tra bản cập nhật...";
update_available = "Đã có bản cập nhật mới";
cancel = "Hủy";
ok = "OK";
// Patching
do_not_patch_premium = "Không vá Premium";
@@ -15,9 +15,6 @@ reset_data_description = "清除缓存数据并重新启动应用程序。";
checking_for_update = "正在检查更新...";
update_available = "发现可用更新";
cancel = "取消";
ok = "好的";
// Patching
do_not_patch_premium = "不启用 Premium 补丁";
@@ -15,9 +15,6 @@ reset_data_description = "清除快取並重新啟動Spotify.";
checking_for_update = "正在檢查更新...";
update_available = "有可用的更新";
cancel = "取消";
ok = "確定";
// 修補方案
do_not_patch_premium = "不修補Premium";