11import { get , set } from 'idb-keyval'
22import { httpClient } from '@/api/httpClient'
33import { usePlayerStore } from '@/store/player.store'
4- import { LyricsResponse } from '@/types/responses/song'
4+ import {
5+ ILyric ,
6+ IStructuredLyric ,
7+ LyricsResponse ,
8+ StructuredLyricsResponse ,
9+ } from '@/types/responses/song'
510import { lrclibClient } from '@/utils/appName'
6- import { checkServerType } from '@/utils/servers'
11+ import { checkServerType , getServerExtensions } from '@/utils/servers'
712
813interface GetLyricsData {
14+ id : string
915 artist : string
1016 title : string
1117 album ?: string
@@ -22,6 +28,7 @@ interface LRCLibResponse {
2228
2329async function getLyrics ( getLyricsData : GetLyricsData ) {
2430 const { preferSyncedLyrics } = usePlayerStore . getState ( ) . settings . lyrics
31+ const { songLyricsEnabled } = getServerExtensions ( )
2532
2633 const cacheKey = getLyricsCacheKey ( getLyricsData , preferSyncedLyrics )
2734
@@ -31,9 +38,47 @@ async function getLyrics(getLyricsData: GetLyricsData) {
3138 return cachedLyrics
3239 }
3340
34- // If the user prefers synced lyrics, attempt to fetch them from the LrcLib first.
35- // If lyrics are found, return them immediately.
36- // If not, proceed with the default flow.
41+ // First attempt to retrieve lyrics from the server.
42+ // If we know it supports the OpenSubsonic songLyrics extension with timing info, use that.
43+ // If the server does not support the extension or the lyrics returned from the server did
44+ // not include timing information, fetch them from the LrcLib
45+
46+ let osUnsyncedLyricsFound : ILyric | undefined
47+ if ( songLyricsEnabled ) {
48+ const response = await httpClient < StructuredLyricsResponse > (
49+ '/getLyricsBySongId' ,
50+ {
51+ method : 'GET' ,
52+ query : {
53+ id : getLyricsData . id ,
54+ } ,
55+ } ,
56+ )
57+
58+ if ( preferSyncedLyrics ) {
59+ if (
60+ response ?. data . lyricsList . structuredLyrics &&
61+ response . data . lyricsList . structuredLyrics . length > 0
62+ ) {
63+ const syncedLyrics = response ?. data . lyricsList . structuredLyrics . find (
64+ ( lyrics ) => lyrics . synced ,
65+ )
66+
67+ if ( syncedLyrics ) {
68+ return osStructuredLyricsToILyric ( syncedLyrics )
69+ }
70+ // save the plain lyrics from this call
71+ osUnsyncedLyricsFound = osStructuredLyricsToILyric (
72+ response . data . lyricsList . structuredLyrics [ 0 ] ,
73+ )
74+ }
75+ // save the plain lyrics retrieved from the server
76+ osUnsyncedLyricsFound = osStructuredLyricsToILyric (
77+ response . data . lyricsList . structuredLyrics [ 0 ] ,
78+ )
79+ }
80+ }
81+
3782 if ( preferSyncedLyrics ) {
3883 const lyrics = await getLyricsFromLRCLib ( getLyricsData )
3984
@@ -44,6 +89,12 @@ async function getLyrics(getLyricsData: GetLyricsData) {
4489 }
4590 }
4691
92+ // if the server supported the songLyrics extension and lrc did not have lyrics, we don't need to query the server and lrc again.
93+ // so return the plain lyrics if we found them
94+ if ( osUnsyncedLyricsFound ) {
95+ return osUnsyncedLyricsFound
96+ }
97+
4798 const response = await httpClient < LyricsResponse > ( '/getLyrics' , {
4899 method : 'GET' ,
49100 query : {
@@ -157,6 +208,31 @@ function getLyricsCacheKey(
157208 return `lyrics:${ artist } :${ title } :${ type } `
158209}
159210
211+ function osStructuredLyricsToILyric ( lyrics : IStructuredLyric ) : ILyric {
212+ return {
213+ artist : lyrics . displayArtist ,
214+ title : lyrics . displayTitle ,
215+ value : formatLyrics (
216+ lyrics . line
217+ . map ( ( l ) => {
218+ if ( l . start != undefined ) {
219+ // l.start may have actual value 0 (falsy)
220+ return `[${ osStartMsToSongTimestamp ( l . start ) } ] ${ l . value } `
221+ }
222+ return l . value
223+ } )
224+ . join ( '\n' ) ,
225+ ) ,
226+ }
227+ }
228+
229+ function osStartMsToSongTimestamp ( startTime : number ) : string {
230+ // Date() isoString is formatted as:
231+ // YYYY-MM-DDTHH:mm:ss.sssZ -> mm:ss.ss
232+ // 2011-10-05T14:48:00.000Z -> 48:00.00
233+ return new Date ( startTime ) . toISOString ( ) . slice ( 14 , - 2 )
234+ }
235+
160236export const lyrics = {
161237 getLyrics,
162238 getLyricsFromLRCLib,
0 commit comments