Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions Sources/LyricsService/Lyrics+Quality.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import LyricsCore

private let translationBonus = 0.1
private let inlineTimeTagBonus = 0.1
private let matchedAlbumFactor = 1.0
private let matchedArtistFactor = 1.3
private let matchedTitleFactor = 1.5
private let noAlbumFactor = 0.8
private let noArtistFactor = 0.8
private let noTitleFactor = 0.8
private let noDurationFactor = 0.8
Expand All @@ -26,7 +28,7 @@ extension Lyrics {
if let quality = metadata.quality {
return quality
}
var quality = 1 - pow((qualityMixBound - artistQuality) * (qualityMixBound - titleQuality) * (qualityMixBound - durationQuality), 0.3333)
var quality = 1 - pow((qualityMixBound - albumQuality) * (qualityMixBound - artistQuality) * (qualityMixBound - titleQuality) * (qualityMixBound - durationQuality), 0.3333)
if metadata.hasTranslation {
quality += translationBonus
}
Expand All @@ -39,25 +41,49 @@ extension Lyrics {

public func isMatched() -> Bool {
guard let artist = idTags[.artist],
let title = idTags[.title] else {
let title = idTags[.title],
let album = idTags[.album] else {
return false
}
switch metadata.searchRequest?.searchTerm {
case let .info(searchTitle, searchArtist)?:
return title.isCaseInsensitiveSimilar(to: searchTitle)
case let .info(searchTitle, searchArtist, searchAlbum)?:
let caseInsensitiveMatch = title.isCaseInsensitiveSimilar(to: searchTitle)
&& artist.isCaseInsensitiveSimilar(to: searchArtist)
if let searchAlbum = searchAlbum {
return caseInsensitiveMatch && album.isCaseInsensitiveSimilar(to: searchAlbum)
} else {
return caseInsensitiveMatch
}
case let .keyword(keyword)?:
return title.isCaseInsensitiveSimilar(to: keyword)
&& artist.isCaseInsensitiveSimilar(to: keyword)
case nil:
return false
}
}

private var albumQuality: Double {
guard let album = idTags[.album] else { return noAlbumFactor }
switch metadata.searchRequest?.searchTerm {
case let .info(_, _, searchAlbum)?:
if album == searchAlbum { return matchedAlbumFactor }
if let searchAlbum = searchAlbum {
return similarity(s1: album, s2: searchAlbum)
} else {
return qualityMixBound - 1
}
case let .keyword(keyword)?:
if keyword.contains(album) { return matchedAlbumFactor }
return similarity(s1: album, in: keyword)
case nil:
return noAlbumFactor
}
}

private var artistQuality: Double {
guard let artist = idTags[.artist] else { return noArtistFactor }
switch metadata.searchRequest?.searchTerm {
case let .info(_, searchArtist)?:
case let .info(_, searchArtist, _)?:
if artist == searchArtist { return matchedArtistFactor }
return similarity(s1: artist, s2: searchArtist)
case let .keyword(keyword)?:
Expand All @@ -71,7 +97,7 @@ extension Lyrics {
private var titleQuality: Double {
guard let title = idTags[.title] else { return noTitleFactor }
switch metadata.searchRequest?.searchTerm {
case let .info(searchTitle, _)?:
case let .info(searchTitle, _, _)?:
if title == searchTitle { return matchedTitleFactor }
return similarity(s1: title, s2: searchTitle)
case let .keyword(keyword)?:
Expand Down
6 changes: 3 additions & 3 deletions Sources/LyricsService/LyricsSearchRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public struct LyricsSearchRequest: Equatable {

public enum SearchTerm: Equatable {
case keyword(String)
case info(title: String, artist: String)
case info(title: String, artist: String, album: String? = nil)
}

public init(searchTerm: SearchTerm, duration: TimeInterval, limit: Int = 6, userInfo: [String: String] = [:]) {
Expand All @@ -35,8 +35,8 @@ extension LyricsSearchRequest.SearchTerm: CustomStringConvertible {
switch self {
case let .keyword(keyword):
return keyword
case let .info(title: title, artist: artist):
return title + " " + artist
case let .info(title: title, artist: artist, album: album):
return title + " " + artist + (album != nil ? " \(album!)" : "")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ extension LyricsProviders.ViewLyrics: _LyricsProvider {
}

public func lyricsSearchPublisher(request: LyricsSearchRequest) -> AnyPublisher<LyricsToken, Never> {
guard case let .info(title, artist) = request.searchTerm else {
guard case let .info(title, artist, _) = request.searchTerm else {
// cannot search by keyword
return Empty().eraseToAnyPublisher()
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/LyricsService/Provider/Gecimi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension LyricsProviders.Gecimi: _LyricsProvider {
public static let service: LyricsProviders.Service? = .gecimi

public func lyricsSearchPublisher(request: LyricsSearchRequest) -> AnyPublisher<LyricsToken, Never> {
guard case let .info(title, artist) = request.searchTerm else {
guard case let .info(title, artist, _) = request.searchTerm else {
// cannot search by keyword
return Empty().eraseToAnyPublisher()
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/LyricsService/Provider/Syair.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ extension LyricsProviders.Syair: _LyricsProvider {
public func lyricsSearchPublisher(request: LyricsSearchRequest) -> AnyPublisher<String, Never> {
var parameter: [String: Any] = ["page": 1]
switch request.searchTerm {
case let .info(title: title, artist: artist):
case let .info(title: title, artist: artist, album: album):
parameter["artist"] = artist
parameter["title"] = title
if let album = album {
parameter["album"] = album
}
case let .keyword(keyword):
parameter["q"] = keyword
}
Expand Down
15 changes: 15 additions & 0 deletions Tests/LyricsKitTests/LyricsKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,19 @@ final class LyricsKitTests: XCTestCase {
waitForExpectations(timeout: 10)
cancelable.cancel()
}

func testNetEaseAlbumSearch() {
let searchResultEx = expectation(description: "search succeed")
let provider = LyricsProviders.NetEase()
let searchTerm = LyricsSearchRequest.SearchTerm.info(title: "Cocoa Hooves", artist: "Glass Animals", album: "ZABA")
let publisher = provider.lyricsPublisher(request: .init(searchTerm: searchTerm, duration: 271))
let cancelable = publisher.sink { lyrics in
guard case let .info(title, artist, album) = searchTerm else { return }
if lyrics.idTags[.title] == title && lyrics.idTags[.artist] == artist && lyrics.idTags[.album] == album {
searchResultEx.fulfill()
}
}
waitForExpectations(timeout: 10)
cancelable.cancel()
}
}