@@ -96,7 +96,7 @@ class LyricManager {
9696 if ( lyricsData . length && otherLyrics . length ) {
9797 lyricsData . forEach ( ( v : LyricLine ) => {
9898 otherLyrics . forEach ( ( x : LyricLine ) => {
99- if ( v . startTime === x . startTime || Math . abs ( v . startTime - x . startTime ) < 600 ) {
99+ if ( v . startTime === x . startTime || Math . abs ( v . startTime - x . startTime ) < 300 ) {
100100 v [ key ] = x . words . map ( ( word ) => word . word ) . join ( "" ) ;
101101 }
102102 } ) ;
@@ -244,67 +244,100 @@ class LyricManager {
244244 * 解析 QQ 音乐 QRC 格式歌词
245245 * @param qrcContent QRC 原始内容
246246 * @param trans 翻译歌词
247- * @param roma 罗马音歌词
247+ * @param roma 罗马音歌词(QRC 格式)
248248 * @returns LyricLine 数组
249249 */
250250 private parseQRCLyric ( qrcContent : string , trans ?: string , roma ?: string ) : LyricLine [ ] {
251- const lines : LyricLine [ ] = [ ] ;
252-
253- // 从 XML 中提取歌词内容
254- const contentMatch = / < L y r i c _ 1 [ ^ > ] * L y r i c C o n t e n t = " ( [ ^ " ] * ) " [ ^ > ] * \/ > / . exec ( qrcContent ) ;
255- const content = contentMatch ? contentMatch [ 1 ] : qrcContent ;
256-
257251 // 行匹配: [开始时间,持续时间]内容
258252 const linePattern = / ^ \[ ( \d + ) , ( \d + ) \] ( .* ) $ / ;
259253 // 逐字匹配: 文字(开始时间,持续时间)
260254 const wordPattern = / ( [ ^ ( ] * ) \( ( \d + ) , ( \d + ) \) / g;
255+ /**
256+ * 解析 QRC 内容为行数据
257+ */
258+ const parseQRCContent = (
259+ rawContent : string ,
260+ ) : Array < {
261+ startTime : number ;
262+ endTime : number ;
263+ words : Array < { word : string ; startTime : number ; endTime : number } > ;
264+ } > => {
265+ // 从 XML 中提取歌词内容
266+ const contentMatch = / < L y r i c _ 1 [ ^ > ] * L y r i c C o n t e n t = " ( [ ^ " ] * ) " [ ^ > ] * \/ > / . exec ( rawContent ) ;
267+ const content = contentMatch ? contentMatch [ 1 ] : rawContent ;
268+
269+ const result : Array < {
270+ startTime : number ;
271+ endTime : number ;
272+ words : Array < { word : string ; startTime : number ; endTime : number } > ;
273+ } > = [ ] ;
274+
275+ for ( const rawLine of content . split ( "\n" ) ) {
276+ const line = rawLine . trim ( ) ;
277+ if ( ! line ) continue ;
278+
279+ // 跳过元数据标签 [ti:xxx] [ar:xxx] 等
280+ if ( / ^ \[ [ a - z ] + : / i. test ( line ) ) continue ;
281+
282+ const lineMatch = linePattern . exec ( line ) ;
283+ if ( ! lineMatch ) continue ;
284+
285+ const lineStart = parseInt ( lineMatch [ 1 ] , 10 ) ;
286+ const lineDuration = parseInt ( lineMatch [ 2 ] , 10 ) ;
287+ const lineContent = lineMatch [ 3 ] ;
288+
289+ // 解析逐字
290+ const words : Array < { word : string ; startTime : number ; endTime : number } > = [ ] ;
291+ let wordMatch : RegExpExecArray | null ;
292+ const wordRegex = new RegExp ( wordPattern . source , "g" ) ;
293+
294+ while ( ( wordMatch = wordRegex . exec ( lineContent ) ) !== null ) {
295+ const wordText = wordMatch [ 1 ] ;
296+ const wordStart = parseInt ( wordMatch [ 2 ] , 10 ) ;
297+ const wordDuration = parseInt ( wordMatch [ 3 ] , 10 ) ;
298+
299+ if ( wordText ) {
300+ words . push ( {
301+ word : wordText ,
302+ startTime : wordStart ,
303+ endTime : wordStart + wordDuration ,
304+ } ) ;
305+ }
306+ }
261307
262- for ( const rawLine of content . split ( "\n" ) ) {
263- const line = rawLine . trim ( ) ;
264- if ( ! line ) continue ;
265-
266- // 跳过元数据标签 [ti:xxx] [ar:xxx] 等
267- if ( / ^ \[ [ a - z ] + : / i. test ( line ) ) continue ;
268-
269- const lineMatch = linePattern . exec ( line ) ;
270- if ( ! lineMatch ) continue ;
271-
272- const lineStart = parseInt ( lineMatch [ 1 ] , 10 ) ;
273- const lineDuration = parseInt ( lineMatch [ 2 ] , 10 ) ;
274- const lineContent = lineMatch [ 3 ] ;
275-
276- // 解析逐字
277- const words : Array < { word : string ; startTime : number ; endTime : number } > = [ ] ;
278- let wordMatch : RegExpExecArray | null ;
279- const wordRegex = new RegExp ( wordPattern . source , "g" ) ;
280-
281- while ( ( wordMatch = wordRegex . exec ( lineContent ) ) !== null ) {
282- const wordText = wordMatch [ 1 ] ;
283- const wordStart = parseInt ( wordMatch [ 2 ] , 10 ) ;
284- const wordDuration = parseInt ( wordMatch [ 3 ] , 10 ) ;
285-
286- if ( wordText ) {
287- words . push ( {
288- word : wordText ,
289- startTime : wordStart ,
290- endTime : wordStart + wordDuration ,
308+ if ( words . length > 0 ) {
309+ result . push ( {
310+ startTime : lineStart ,
311+ endTime : lineStart + lineDuration ,
312+ words,
291313 } ) ;
292314 }
293315 }
294-
295- if ( words . length > 0 ) {
296- lines . push ( {
297- words : words . map ( ( w ) => ( { ...w , romanWord : "" } ) ) ,
298- startTime : lineStart ,
299- endTime : lineStart + lineDuration ,
300- translatedLyric : "" ,
301- romanLyric : "" ,
302- isBG : false ,
303- isDuet : false ,
304- } ) ;
305- }
306- }
307-
316+ return result ;
317+ } ;
318+ // 解析主歌词
319+ const qrcLines = parseQRCContent ( qrcContent ) ;
320+ // 解析罗马音(如果有)
321+ const romaLines = roma ? parseQRCContent ( roma ) : [ ] ;
322+ // 构建 LyricLine 数组,同时填充 romanWord
323+ const lines : LyricLine [ ] = qrcLines . map ( ( qrcLine , lineIndex ) => {
324+ // 找到对应的罗马音行
325+ const romaLine = romaLines [ lineIndex ] ;
326+ // 按索引填充 romanWord
327+ const words = qrcLine . words . map ( ( w , wordIndex ) => ( {
328+ ...w ,
329+ romanWord : romaLine ?. words [ wordIndex ] ?. word || "" ,
330+ } ) ) ;
331+ return {
332+ words,
333+ startTime : qrcLine . startTime ,
334+ endTime : qrcLine . endTime ,
335+ translatedLyric : "" ,
336+ romanLyric : romaLine ?. words . map ( ( w ) => w . word ) . join ( "" ) || "" ,
337+ isBG : false ,
338+ isDuet : false ,
339+ } ;
340+ } ) ;
308341 // 处理翻译
309342 let result = lines ;
310343 if ( trans ) {
@@ -313,13 +346,6 @@ class LyricManager {
313346 result = this . alignLyrics ( result , transLines , "translatedLyric" ) ;
314347 }
315348 }
316- // 处理罗马音
317- if ( roma ) {
318- const romaLines = parseLrc ( roma ) ;
319- if ( romaLines ?. length ) {
320- result = this . alignLyrics ( result , romaLines , "romanLyric" ) ;
321- }
322- }
323349 return result ;
324350 }
325351
0 commit comments