@@ -2,29 +2,52 @@ import Foundation
22import GRDB
33
44struct LyricsLoader {
5- /// Load lyrics for a track, checking external files first, then embedded lyrics
5+ /// Load lyrics for a track, checking external files first, then embedded lyrics, then online
66 /// - Parameters:
77 /// - track: The track to load lyrics for
88 /// - dbQueue: Database queue for fetching embedded lyrics
9+ /// - databaseManager: Database manager for online lyrics storage (optional)
910 /// - Returns: Tuple containing lyrics text and source type
1011 static func loadLyrics(
1112 for track: Track ,
12- using dbQueue: DatabaseQueue
13+ using dbQueue: DatabaseQueue ,
14+ databaseManager: DatabaseManager ? = nil
1315 ) async throws -> ( lyrics: String , source: LyricsSource ) {
16+ var rawLyrics : String ?
17+ var source : LyricsSource = . none
18+
1419 // First, check for external LRC/SRT files
1520 if let externalLyrics = try ? loadExternalLyrics ( for: track) {
16- return externalLyrics
21+ rawLyrics = externalLyrics. lyrics
22+ source = externalLyrics. source
1723 }
1824
19- // Fall back to embedded lyrics
20- if let fullTrack = try ? await track. fullTrack ( using: dbQueue) ,
25+ // Second, check for embedded lyrics (stored in db during library scan)
26+ let fullTrack = try ? await track. fullTrack ( using: dbQueue)
27+ if rawLyrics == nil ,
28+ let fullTrack = fullTrack,
2129 let embeddedLyrics = fullTrack. extendedMetadata? . lyrics,
2230 !embeddedLyrics. isEmpty {
23- return ( embeddedLyrics, . embedded)
31+ rawLyrics = embeddedLyrics
32+ source = . embedded
33+ }
34+
35+ // Finally, try fetching from online source
36+ if rawLyrics == nil ,
37+ let fullTrack = fullTrack,
38+ let databaseManager = databaseManager,
39+ let onlineLyrics = await LyricsManager . shared. fetchLyrics ( for: fullTrack, using: databaseManager) {
40+ rawLyrics = onlineLyrics
41+ source = . online
2442 }
2543
26- // No lyrics found
27- return ( " " , . none)
44+ // Strip timestamps for display
45+ guard let lyrics = rawLyrics else {
46+ return ( " " , . none)
47+ }
48+
49+ let displayLyrics = stripTimestamps ( lyrics)
50+ return ( displayLyrics, source)
2851 }
2952
3053 /// Check for and load external lyrics files (.lrc or .srt)
@@ -68,54 +91,87 @@ struct LyricsLoader {
6891 return nil
6992 }
7093
94+ // MARK: - Timestamp Stripping
95+
96+ /// Strip LRC-style timestamps from lyrics for display
97+ private static func stripTimestamps( _ content: String ) -> String {
98+ let lines = content. components ( separatedBy: . newlines)
99+ var strippedLines : [ String ] = [ ]
100+
101+ for line in lines {
102+ var currentLine = line
103+
104+ // Remove all timestamp tags [mm:ss.xx] from the line
105+ while currentLine. hasPrefix ( " [ " ) {
106+ if let endBracket = currentLine. firstIndex ( of: " ] " ) {
107+ let tag = String ( currentLine [ currentLine. index ( after: currentLine. startIndex) ..< endBracket] )
108+ // Check if it's a timestamp (contains digits and colons/periods)
109+ let isTimestamp = tag. contains ( " : " ) && tag. rangeOfCharacter ( from: . decimalDigits) != nil
110+
111+ if isTimestamp {
112+ currentLine = String ( currentLine [ currentLine. index ( after: endBracket) ... ] )
113+ } else {
114+ break
115+ }
116+ } else {
117+ break
118+ }
119+ }
120+
121+ let trimmed = currentLine. trimmingCharacters ( in: . whitespaces)
122+ if !trimmed. isEmpty {
123+ strippedLines. append ( trimmed)
124+ }
125+ }
126+
127+ return strippedLines. joined ( separator: " \n " )
128+ }
129+
130+ // MARK: - Format Parsing
131+
71132 /// Parse LRC file format and extract lyrics text
72133 private static func parseLRC( _ content: String ) -> String {
73134 let lines = content. components ( separatedBy: . newlines)
74135 var lyricsLines : [ String ] = [ ]
75136
76137 for line in lines {
77- // LRC format: [mm:ss.xx]lyrics text
78- // Remove timestamp and metadata tags for now
79138 if line. hasPrefix ( " [ " ) {
80139 if let endBracket = line. firstIndex ( of: " ] " ) {
81- let afterBracket = line. index ( after: endBracket)
82- if afterBracket < line. endIndex {
83- let lyricsText = String ( line [ afterBracket... ] ) . trimmingCharacters ( in: . whitespaces)
84- if !lyricsText. isEmpty {
85- // Skip metadata lines (ar:, ti:, al:, etc.)
86- let tag = String ( line [ line. index ( after: line. startIndex) ..< endBracket] )
87- if !tag. contains ( " : " ) || tag. contains ( " . " ) {
88- lyricsLines. append ( lyricsText)
89- }
90- }
140+ let tag = String ( line [ line. index ( after: line. startIndex) ..< endBracket] )
141+
142+ // Skip metadata lines (ar:, ti:, al:, etc.) but not timestamps
143+ let isMetadata = tag. contains ( " : " ) && tag. rangeOfCharacter ( from: . decimalDigits) == nil
144+ if isMetadata {
145+ continue
91146 }
147+
148+ // Keep the full line (with timestamps) for now
149+ lyricsLines. append ( line)
92150 }
93151 } else if !line. trimmingCharacters ( in: . whitespaces) . isEmpty {
94152 lyricsLines. append ( line)
95153 }
96154 }
97155
98- return lyricsLines. joined ( separator: " \n " )
156+ // Strip timestamps at the end
157+ return stripTimestamps ( lyricsLines. joined ( separator: " \n " ) )
99158 }
100159
101- /// Parse SRT file format and extract lyrics text (ignoring timestamps for now)
160+ /// Parse SRT file format and extract lyrics text
102161 private static func parseSRT( _ content: String ) -> String {
103162 let lines = content. components ( separatedBy: . newlines)
104163 var lyricsLines : [ String ] = [ ]
105- var skipNext = false
106164
107165 for line in lines {
108166 let trimmed = line. trimmingCharacters ( in: . whitespaces)
109167
110- // Skip empty lines and sequence numbers
168+ // Skip empty lines
111169 if trimmed. isEmpty {
112- skipNext = false
113170 continue
114171 }
115172
116173 // Skip timestamp lines (format: 00:00:00,000 --> 00:00:00,000)
117174 if trimmed. contains ( " --> " ) {
118- skipNext = true
119175 continue
120176 }
121177
@@ -124,12 +180,6 @@ struct LyricsLoader {
124180 continue
125181 }
126182
127- // Skip the line immediately after timestamp
128- if skipNext {
129- skipNext = false
130- }
131-
132- // This is lyrics text
133183 lyricsLines. append ( trimmed)
134184 }
135185
@@ -141,5 +191,6 @@ enum LyricsSource {
141191 case lrc
142192 case srt
143193 case embedded
194+ case online
144195 case none
145196}
0 commit comments