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:
Mapleshade
2024-12-30 22:37:24 +08:00
committed by GitHub
parent ba407d7ed7
commit 85a002b9b3
7 changed files with 48 additions and 27 deletions

View File

@@ -5,6 +5,7 @@ on:
branches: [ "swift" ]
pull_request:
branches: [ "swift" ]
workflow_dispatch:
jobs:
build:

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ xcuserdata/
.theos/
packages/
.DS_Store
.vscode/

View File

@@ -4,4 +4,5 @@ struct LrclibSong: Decodable {
var name: String
var plainLyrics: String?
var syncedLyrics: String?
var instrumental: Bool
}

View File

@@ -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)"
}

View File

@@ -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(

View File

@@ -0,0 +1,9 @@
import Foundation
extension CharacterSet {
static var urlQueryAllowedStrict: CharacterSet {
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "/?@&=+$")
return allowed
}
}

View File

@@ -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: "&")
}
}