feat(lyrics): implement multi-backend lyrics provider support#275
Open
yeonfish6040 wants to merge 10 commits into
Open
feat(lyrics): implement multi-backend lyrics provider support#275yeonfish6040 wants to merge 10 commits into
yeonfish6040 wants to merge 10 commits into
Conversation
# Conflicts: # Sources/Kaset/Services/SettingsManager.swift
- preserve the current lyrics result when a cycled provider returns unavailable\n- keep provider switching from replacing synced lyrics with an empty state\n- parse pear-style YTMusic proxy payloads and cueRange timestamps\n- broaden LRCLib and MusixMatch parsing to match the current backend payloads\n- remove LyricsGenius and update provider settings, tests, and fixtures\n- keep prefetch cache behavior and provider selection covered by tests
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR introduces a MusixMatch lyrics provider, a "pear-style" browse proxy for synced YTMusic lyrics, a user-configurable default lyrics provider setting, the ability to cycle through providers in the lyrics view, and lyrics prefetching for adjacent queue tracks. The LyricsParser is updated to handle additional timed-lyrics payload shapes, and LRCLibProvider gains artist similarity filtering and album-based search.
Changes:
- Adds
MusixMatchProviderwith macro API and HTML scraping, a configurable lyrics provider preference (SettingsManager.LyricsProviderPreference), and provider cycling UI inLyricsView. - Refactors
SyncedLyricsServiceto support provider-specific fetching/caching, auto vs. manual mode, and adjacent-track prefetching driven byMainWindow. - Enhances
LyricsParserto parse alternate timed-lyrics payload keys (e.g.,cueRange,lyricLine) and routes browse requests through a proxy endpoint inYTMusicClient.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| Sources/Kaset/Services/Lyrics/Providers/MusixMatchProvider.swift | New MusixMatch lyrics provider with API and HTML extraction |
| Sources/Kaset/Services/Lyrics/Providers/HTMLLyricsExtractor.swift | New HTML parsing utility for lyrics extraction |
| Sources/Kaset/Services/Lyrics/Providers/LRCLibProvider.swift | Adds artist similarity filtering, album query, and refactored duration matching |
| Sources/Kaset/Services/Lyrics/SyncedLyricsService.swift | Adds provider-specific fetching, prefetching, caching, and auto/manual mode |
| Sources/Kaset/Services/API/Parsers/LyricsParser.swift | Handles alternate timed-lyrics payload formats and hardcodes "YTMusic" source |
| Sources/Kaset/Services/API/YTMusicClient.swift | Adds pear-style proxy endpoint for lyrics browse requests |
| Sources/Kaset/Services/SettingsManager.swift | Adds LyricsProviderPreference enum and defaultLyricsProvider setting |
| Sources/Kaset/Views/LyricsView.swift | Provider cycling UI, inline loading overlay, reloadLyrics refactor |
| Sources/Kaset/Views/MainWindow.swift | Adjacent-track lyrics prefetching |
| Sources/Kaset/Views/GeneralSettingsView.swift | Settings UI for default lyrics provider picker |
| Sources/Kaset/KasetApp.swift | Registers MusixMatchProvider |
| Sources/Kaset/Services/API/Parsers/LyricsParser.swift | Removes footer source extraction, hardcodes "YTMusic" |
| Tests/KasetTests/SyncedLyricsTests.swift | Tests for provider preference, cycling, prefetching, pruning |
| Tests/KasetTests/LyricsParserTests.swift | Tests for alternate payload keys and proxy fixture |
| Tests/KasetTests/Fixtures/lyrics_proxy_zzfYBupjwIc.json | JSON fixture for proxy lyrics parsing test |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| if let statusCode = Self.statusCode(in: json), statusCode == 401 { | ||
| self.token = nil | ||
| self.tokenExpiresAt = nil |
Comment on lines
+50
to
+51
| guard let track = Self.track(from: macroCalls), | ||
| track.trackId != 115_264_642 |
Comment on lines
+333
to
+336
| private var lyricsPrefetchKey: String { | ||
| let queueKey = self.playerService.queueEntryIDs.map(\.uuidString).joined(separator: ",") | ||
| let currentVideoId = self.playerService.currentTrack?.videoId ?? "" | ||
| return "\(currentVideoId)|\(self.playerService.currentIndex)|\(queueKey)" |
| } | ||
|
|
||
| self.token = try await self.fetchToken() | ||
| self.tokenExpiresAt = Date().addingTimeInterval(60) |
| private static func normalizeSearchText(_ text: String) -> String { | ||
| text.lowercased() | ||
| .replacingOccurrences(of: #"(?i)\s*[\(\[].*?[\)\]]"#, with: "", options: .regularExpression) | ||
| .replacingOccurrences(of: #"[^a-z0-9가-힣&]+"#, with: " ", options: .regularExpression) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a multi-backend lyrics flow that supports automatic provider selection across multiple lyrics sources, while preserving manual provider selection for forcing a single backend. Also aligns the backend with pear-style YTMusic timed-lyrics payloads
and keeps provider switching from overwriting active lyrics with an unavailable state.
AI Prompt (Optional)
🤖 AI Prompt Used
N/A - Manual implementation
AI Tool: OpenAI Codex
Type of Change
Related Issues
Changes Made
startTimeMsandcueRange.startTimeMilliseconds.LyricsGeniusfrom the supported provider set.Testing
xcodebuild test -only-testing:KasetTests)Checklist
swiftlint --strict && swiftformat .Screenshots
Additional Notes