yodaluca23 has confirmed andrea Filice ban prs forever

This commit is contained in:
Luca
2025-11-02 16:47:16 -05:00
parent 7ff296342d
commit 8f8ead182e
2 changed files with 97 additions and 60 deletions
@@ -1,6 +1,6 @@
import Foundation
struct LyricsSearchQuery {
struct LyricsSearchQuery: Hashable {
var title: String
var primaryArtist: String
var spotifyTrackId: String
@@ -3,17 +3,35 @@ import UIKit
class MusixmatchLyricsRepository: LyricsRepository {
private let apiUrl = "https://apic.musixmatch.com"
var selectedLanguage: String
static let shared = MusixmatchLyricsRepository(
language: UserDefaults.lyricsOptions.musixmatchLanguage
)
private init(language: String) {
selectedLanguage = language
}
//
private class CachedLyrics {
let dto: LyricsDto
init(dto: LyricsDto) {
self.dto = dto
}
}
private let lyricsCache = NSCache<NSString, CachedLyrics>()
private func getCacheKey(for query: LyricsSearchQuery) -> String {
return "\(query.hashValue)_\(selectedLanguage)"
}
//
private func perform(
_ path: String,
query: [String: Any] = [:]
@@ -26,7 +44,7 @@ class MusixmatchLyricsRepository: LyricsRepository {
let queryString = finalQuery.queryString
stringUrl += "?\(queryString)"
let request = URLRequest(url: URL(string: stringUrl)!)
let semaphore = DispatchSemaphore(value: 0)
@@ -48,9 +66,9 @@ class MusixmatchLyricsRepository: LyricsRepository {
return data!
}
//
private func getMacroCalls(_ data: Data) throws -> [String: Any] {
guard
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
@@ -62,15 +80,16 @@ class MusixmatchLyricsRepository: LyricsRepository {
}
if let header = message["header"] as? [String: Any],
header["status_code"] as? Int == 401 {
header["status_code"] as? Int == 401
{
throw LyricsError.invalidMusixmatchToken
}
return macroCalls
}
private func getFirstSubtitle(_ subtitlesMessage: [String: Any]) throws -> [String: Any] {
guard
guard
let subtitlesBody = subtitlesMessage["body"] as? [String: Any],
let subtitleList = subtitlesBody["subtitle_list"] as? [[String: Any]],
let firstSubtitle = subtitleList.first,
@@ -78,25 +97,27 @@ class MusixmatchLyricsRepository: LyricsRepository {
else {
throw LyricsError.decodingError
}
if let restricted = subtitle["restricted"] as? Bool, restricted {
throw LyricsError.musixmatchRestricted
}
return subtitle
}
//
private func getTranslations(_ spotifyTrackId: String, selectedLanguage: String) throws -> [String: String] {
private func getTranslations(_ spotifyTrackId: String, selectedLanguage: String) throws
-> [String: String]
{
let data = try perform(
"/ws/1.1/crowd.track.translations.get",
query: [
"track_spotify_id": spotifyTrackId,
"selected_language": selectedLanguage
"selected_language": selectedLanguage,
]
)
guard
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let message = json["message"] as? [String: Any],
@@ -109,27 +130,34 @@ class MusixmatchLyricsRepository: LyricsRepository {
let translations = translationsList.compactMap {
$0["translation"] as? [String: Any]
}
return translations.reduce(into: [:]) { dictionary, translation in
dictionary[translation["subtitle_matched_line"] as! String] = translation["description"] as? String
dictionary[translation["subtitle_matched_line"] as! String] =
translation["description"] as? String
}
}
//
func getLyrics(_ query: LyricsSearchQuery, options: LyricsOptions) throws -> LyricsDto {
let cacheKey = getCacheKey(for: query)
if let cached = lyricsCache.object(forKey: cacheKey as NSString) {
return cached.dto
}
var musixmatchQuery = [
"track_spotify_id": query.spotifyTrackId,
"subtitle_format": "mxm",
"q_track": query.title,
"q_artist": query.primaryArtist
"q_artist": query.primaryArtist,
]
if !selectedLanguage.isEmpty {
musixmatchQuery["selected_language"] = selectedLanguage
musixmatchQuery["part"] = "subtitle_translated"
}
let data = try perform(
"/ws/1.1/macro.subtitles.get",
query: musixmatchQuery
@@ -139,7 +167,7 @@ class MusixmatchLyricsRepository: LyricsRepository {
var romanized = false
var translation: LyricsTranslationDto? = nil
let macroCalls = try getMacroCalls(data)
if let trackSubtitlesGet = macroCalls["track.subtitles.get"] as? [String: Any],
@@ -149,55 +177,55 @@ class MusixmatchLyricsRepository: LyricsRepository {
let subtitleBody = subtitle["subtitle_body"] as? String,
let subtitles = try? JSONDecoder().decode(
[MusixmatchSubtitle].self, from: subtitleBody.data(using: .utf8)!
) {
)
{
let romanizationLanguage = "r\(subtitleLanguage.prefix(1))"
var lyricsLines = subtitles.dropLast().map { subtitle in
LyricsLineDto(
content: subtitle.text.lyricsNoteIfEmpty,
offsetMs: Int(subtitle.time.total * 1000)
)
}
lyricsLines.append(
LyricsLineDto(
content: "",
offsetMs: Int(subtitles.last!.time.total * 1000)
)
)
if selectedLanguage != subtitleLanguage,
let subtitleTranslated = subtitle["subtitle_translated"] as? [String: Any],
let subtitleTranslatedBody = subtitleTranslated["subtitle_body"] as? String,
let subtitlesTranslated = try? JSONDecoder().decode(
let subtitleTranslated = subtitle["subtitle_translated"] as? [String: Any],
let subtitleTranslatedBody = subtitleTranslated["subtitle_body"] as? String,
let subtitlesTranslated = try? JSONDecoder().decode(
[MusixmatchSubtitle].self, from: subtitleTranslatedBody.data(using: .utf8)!
)
)
{
if selectedLanguage == romanizationLanguage {
romanized = true
for (index, subtitleTranslated) in subtitlesTranslated.enumerated() {
if !subtitleTranslated.text.isEmpty {
lyricsLines[index].content = subtitleTranslated.text
}
}
}
else {
} else {
translation = LyricsTranslationDto(
languageCode: selectedLanguage,
lines: subtitlesTranslated.map { $0.text }
)
}
}
if options.romanization && selectedLanguage != romanizationLanguage {
if let translations = try? getTranslations(
query.spotifyTrackId,
selectedLanguage: romanizationLanguage
) {
romanized = true
for (original, translation) in translations {
for i in 0..<lyricsLines.count {
if lyricsLines[i].content == original {
@@ -207,50 +235,59 @@ class MusixmatchLyricsRepository: LyricsRepository {
}
}
}
var romanization = LyricsRomanizationStatus.original
if romanized {
romanization = .romanized
}
else if subtitleLanguage.isCanBeRomanizedLanguage {
} else if subtitleLanguage.isCanBeRomanizedLanguage {
romanization = .canBeRomanized
}
return LyricsDto(
let lyricsDto = LyricsDto(
lines: lyricsLines,
timeSynced: true,
romanization: romanization,
translation: translation
)
lyricsCache.setObject(CachedLyrics(dto: lyricsDto), forKey: cacheKey as NSString)
return lyricsDto
}
if let trackLyricsGet = macroCalls["track.lyrics.get"] as? [String: Any],
let lyricsMessage = trackLyricsGet["message"] as? [String: Any],
let lyricsHeader = lyricsMessage["header"] as? [String: Any],
let lyricsStatusCode = lyricsHeader["status_code"] as? Int {
let lyricsMessage = trackLyricsGet["message"] as? [String: Any],
let lyricsHeader = lyricsMessage["header"] as? [String: Any],
let lyricsStatusCode = lyricsHeader["status_code"] as? Int
{
if lyricsStatusCode == 404 {
throw LyricsError.noSuchSong
}
if let lyricsBody = lyricsMessage["body"] as? [String: Any],
let lyrics = lyricsBody["lyrics"] as? [String: Any],
let lyricsLanguage = lyrics["lyrics_language"] as? String,
let plainLyrics = lyrics["lyrics_body"] as? String {
let lyrics = lyricsBody["lyrics"] as? [String: Any],
let lyricsLanguage = lyrics["lyrics_language"] as? String,
let plainLyrics = lyrics["lyrics_body"] as? String
{
if let restricted = lyrics["restricted"] as? Bool, restricted {
throw LyricsError.musixmatchRestricted
}
return LyricsDto(
lines: plainLyrics
let lyricsDto = LyricsDto(
lines:
plainLyrics
.components(separatedBy: "\n")
.dropLast()
.map { LyricsLineDto(content: $0.lyricsNoteIfEmpty) },
timeSynced: false,
romanization: lyricsLanguage.isCanBeRomanizedLanguage ? .canBeRomanized : .original
romanization: lyricsLanguage.isCanBeRomanizedLanguage
? .canBeRomanized : .original
)
lyricsCache.setObject(CachedLyrics(dto: lyricsDto), forKey: cacheKey as NSString)
return lyricsDto
}
}