mirror of
https://github.com/whoeevee/EeveeSpotifyReborn.git
synced 2026-01-08 23:23:20 +00:00
fix: improve lyrics fetching and query encoding for special characters (#594)
* chore: gitignore .vscode * fix(lrclib): use 'get' API, return empty lines when instrumental * build: add workflow_dispatch trigger * fix: encode query strings with stricter character set
This commit is contained in:
1
.github/workflows/build-swift.yml
vendored
1
.github/workflows/build-swift.yml
vendored
@@ -5,6 +5,7 @@ on:
|
||||
branches: [ "swift" ]
|
||||
pull_request:
|
||||
branches: [ "swift" ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ xcuserdata/
|
||||
.theos/
|
||||
packages/
|
||||
.DS_Store
|
||||
.vscode/
|
||||
|
||||
@@ -4,4 +4,5 @@ struct LrclibSong: Decodable {
|
||||
var name: String
|
||||
var plainLyrics: String?
|
||||
var syncedLyrics: String?
|
||||
var instrumental: Bool
|
||||
}
|
||||
@@ -26,9 +26,7 @@ struct GeniusLyricsRepository: LyricsRepository {
|
||||
var stringUrl = "\(apiUrl)\(path)"
|
||||
|
||||
if !query.isEmpty {
|
||||
let queryString = query.queryString.addingPercentEncoding(
|
||||
withAllowedCharacters: .urlHostAllowed
|
||||
)!
|
||||
let queryString = query.queryString
|
||||
|
||||
stringUrl += "?\(queryString)"
|
||||
}
|
||||
|
||||
@@ -20,9 +20,7 @@ struct LrcLibLyricsRepository: LyricsRepository {
|
||||
var stringUrl = "\(apiUrl)\(path)"
|
||||
|
||||
if !query.isEmpty {
|
||||
let queryString = query.queryString.addingPercentEncoding(
|
||||
withAllowedCharacters: .urlHostAllowed
|
||||
)!
|
||||
let queryString = query.queryString
|
||||
|
||||
stringUrl += "?\(queryString)"
|
||||
}
|
||||
@@ -49,23 +47,18 @@ struct LrcLibLyricsRepository: LyricsRepository {
|
||||
return data!
|
||||
}
|
||||
|
||||
private func searchSong(_ query: String) throws -> [LrclibSong] {
|
||||
let data = try perform("/search", query: ["q": query])
|
||||
return try JSONDecoder().decode([LrclibSong].self, from: data)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
private func mostRelevantSong(songs: [LrclibSong], strippedTitle: String) -> LrclibSong? {
|
||||
return songs.first(
|
||||
where: { $0.name.containsInsensitive(strippedTitle) }
|
||||
) ?? songs.first
|
||||
private func getSong(trackName: String, artistName: String) throws -> LrclibSong {
|
||||
let data: Data = try perform("/get", query: [
|
||||
"track_name": trackName,
|
||||
"artist_name": artistName
|
||||
])
|
||||
return try JSONDecoder().decode(LrclibSong.self, from: data)
|
||||
}
|
||||
|
||||
private func mapSyncedLyricsLines(_ lines: [String]) -> [LyricsLineDto] {
|
||||
return lines.compactMap { line in
|
||||
guard let match = line.firstMatch(
|
||||
"\\[(?<minute>\\d*):(?<seconds>\\d*\\.?\\d*)\\] ?(?<content>.*)"
|
||||
"\\[(?<minute>\\d*):(?<seconds>\\d+\\.\\d+|\\d+)\\] ?(?<content>.*)"
|
||||
) else {
|
||||
return nil
|
||||
}
|
||||
@@ -93,13 +86,27 @@ struct LrcLibLyricsRepository: LyricsRepository {
|
||||
}
|
||||
|
||||
func getLyrics(_ query: LyricsSearchQuery, options: LyricsOptions) throws -> LyricsDto {
|
||||
let strippedTitle = query.title.strippedTrackTitle
|
||||
let songs = try searchSong("\(strippedTitle) \(query.primaryArtist)")
|
||||
|
||||
guard let song = mostRelevantSong(songs: songs, strippedTitle: strippedTitle) else {
|
||||
throw LyricsError.noSuchSong
|
||||
let song: LrclibSong
|
||||
|
||||
do {
|
||||
song = try getSong(trackName: query.title, artistName: query.primaryArtist)
|
||||
} catch {
|
||||
let strippedTitle = query.title.strippedTrackTitle
|
||||
do {
|
||||
song = try getSong(trackName: strippedTitle, artistName: query.primaryArtist)
|
||||
} catch {
|
||||
throw LyricsError.noSuchSong
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if song.instrumental {
|
||||
return LyricsDto(
|
||||
lines: [],
|
||||
timeSynced: false,
|
||||
romanization: .original
|
||||
)
|
||||
}
|
||||
|
||||
if let syncedLyrics = song.syncedLyrics {
|
||||
let lines = Array(syncedLyrics.components(separatedBy: "\n").dropLast())
|
||||
return LyricsDto(
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
extension CharacterSet {
|
||||
static var urlQueryAllowedStrict: CharacterSet {
|
||||
var allowed = CharacterSet.urlQueryAllowed
|
||||
allowed.remove(charactersIn: "/?@&=+$")
|
||||
return allowed
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,13 @@ import Foundation
|
||||
extension Dictionary {
|
||||
var queryString: String {
|
||||
return self
|
||||
.compactMap({ (key, value) -> String in
|
||||
return "\(key)=\(value)"
|
||||
.compactMap({ (key, value) -> String? in
|
||||
guard let keyString = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowedStrict),
|
||||
let valueString = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowedStrict) else {
|
||||
return nil
|
||||
}
|
||||
return "\(keyString)=\(valueString)"
|
||||
})
|
||||
.joined(separator: "&")
|
||||
.joined(separator: "&")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user