Skip to content

Commit 8501f03

Browse files
committed
refactor(synced-lyrics): unify empty line handling
1 parent a151439 commit 8501f03

File tree

4 files changed

+47
-138
lines changed

4 files changed

+47
-138
lines changed

src/plugins/synced-lyrics/parsers/lrc.ts

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import {
2+
ensureLeadingPaddingEmptyLine,
3+
mergeConsecutiveEmptySyncedLines,
4+
} from '../shared/lines';
5+
16
interface LRCTag {
27
tag: string;
38
value: string;
@@ -79,42 +84,11 @@ export const LRC = {
7984
line.timeInMs += offset;
8085
}
8186

82-
const first = lrc.lines.at(0);
83-
if (first && first.timeInMs > 300) {
84-
lrc.lines.unshift({
85-
time: '0:0:0',
86-
timeInMs: 0,
87-
duration: first.timeInMs,
88-
text: '',
89-
});
90-
}
87+
// leading padding line if the first line starts late
88+
lrc.lines = ensureLeadingPaddingEmptyLine(lrc.lines, 300, 'span');
9189

9290
// Merge consecutive empty lines into a single empty line
93-
{
94-
const merged: LRCLine[] = [];
95-
for (const line of lrc.lines) {
96-
const isEmpty = !line.text || !line.text.trim();
97-
if (isEmpty && merged.length > 0) {
98-
const prev = merged[merged.length - 1];
99-
const prevEmpty = !prev.text || !prev.text.trim();
100-
if (prevEmpty) {
101-
const prevEnd = Number.isFinite(prev.duration)
102-
? prev.timeInMs + prev.duration
103-
: Infinity;
104-
const thisEnd = Number.isFinite(line.duration)
105-
? line.timeInMs + line.duration
106-
: Infinity;
107-
const newEnd = Math.max(prevEnd, thisEnd);
108-
prev.duration = Number.isFinite(newEnd)
109-
? newEnd - prev.timeInMs
110-
: Infinity;
111-
continue;
112-
}
113-
}
114-
merged.push(line);
115-
}
116-
lrc.lines = merged;
117-
}
91+
lrc.lines = mergeConsecutiveEmptySyncedLines(lrc.lines);
11892

11993
return lrc;
12094
},

src/plugins/synced-lyrics/providers/LRCLib.ts

Lines changed: 13 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import { jaroWinkler } from '@skyra/jaro-winkler';
22

33
import { config } from '../renderer/renderer';
44
import { LRC } from '../parsers/lrc';
5+
import {
6+
ensureLeadingPaddingEmptyLine,
7+
ensureTrailingEmptyLine,
8+
mergeConsecutiveEmptySyncedLines,
9+
} from '../shared/lines';
510

611
import type { LyricProvider, LyricResult, SearchSongInfo } from '../types';
712

@@ -162,67 +167,18 @@ export class LRCLib implements LyricProvider {
162167
...l,
163168
status: 'upcoming' as const,
164169
}));
165-
166-
// Merge consecutive empty lines into a single empty line
167-
const merged: typeof parsed = [];
168-
for (const line of parsed) {
169-
const isEmpty = !line.text || !line.text.trim();
170-
if (isEmpty && merged.length > 0) {
171-
const prev = merged[merged.length - 1];
172-
const prevEmpty = !prev.text || !prev.text.trim();
173-
if (prevEmpty) {
174-
// extend previous duration to cover this line
175-
const prevEnd = prev.timeInMs + prev.duration;
176-
const thisEnd = line.timeInMs + line.duration;
177-
const newEnd = Math.max(prevEnd, thisEnd);
178-
prev.duration = newEnd - prev.timeInMs;
179-
continue; // skip adding this line
180-
}
181-
}
182-
merged.push(line);
183-
}
184-
185-
// If the final merged line is not empty, append a computed empty line
186-
if (merged.length > 0) {
187-
const last = merged[merged.length - 1];
188-
const lastIsEmpty = !last.text || !last.text.trim();
189-
if (lastIsEmpty) {
190-
// last line already empty, don't append another
191-
} else {
192-
// If duration is infinity (no following line), treat end as start for midpoint calculation
193-
const lastEndCandidate = Number.isFinite(last.duration)
194-
? last.timeInMs + last.duration
195-
: last.timeInMs;
196-
const songEnd = songDuration * 1000;
197-
198-
if (lastEndCandidate < songEnd) {
199-
const midpoint = Math.floor((lastEndCandidate + songEnd) / 2);
200-
201-
// update last duration to end at midpoint
202-
last.duration = midpoint - last.timeInMs;
203-
204-
const minutes = Math.floor(midpoint / 60000);
205-
const seconds = Math.floor((midpoint % 60000) / 1000);
206-
const centiseconds = Math.floor((midpoint % 1000) / 10);
207-
const timeStr = `${minutes.toString().padStart(2, '0')}:${seconds
208-
.toString()
209-
.padStart(2, '0')}.${centiseconds.toString().padStart(2, '0')}`;
210-
211-
merged.push({
212-
timeInMs: midpoint,
213-
time: timeStr,
214-
duration: songEnd - midpoint,
215-
text: '',
216-
status: 'upcoming' as const,
217-
});
218-
}
219-
}
220-
}
170+
const merged = mergeConsecutiveEmptySyncedLines(parsed);
171+
const withLeading = ensureLeadingPaddingEmptyLine(merged, 300, 'span');
172+
const finalLines = ensureTrailingEmptyLine(
173+
withLeading,
174+
'midpoint',
175+
songDuration * 1000,
176+
);
221177

222178
return {
223179
title: closestResult.trackName,
224180
artists: closestResult.artistName.split(/[&,]/g),
225-
lines: merged,
181+
lines: finalLines,
226182
};
227183
} else if (plain) {
228184
// Fallback to plain if no synced

src/plugins/synced-lyrics/providers/MusixMatch.ts

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import * as z from 'zod';
22

33
import { LRC } from '../parsers/lrc';
4+
import {
5+
ensureLeadingPaddingEmptyLine,
6+
ensureTrailingEmptyLine,
7+
mergeConsecutiveEmptySyncedLines,
8+
} from '../shared/lines';
49
import { netFetch } from '../renderer';
510

611
import type { LyricProvider, LyricResult, SearchSongInfo } from '../types';
@@ -47,26 +52,13 @@ export class MusixMatch implements LyricProvider {
4752
(l) => ({ ...l, status: 'upcoming' as const }),
4853
);
4954

50-
// Merge consecutive empty lines into a single empty line
51-
const merged: typeof parsed = [];
52-
for (const line of parsed) {
53-
const isEmpty = !line.text || !line.text.trim();
54-
if (isEmpty && merged.length > 0) {
55-
const prev = merged[merged.length - 1];
56-
const prevEmpty = !prev.text || !prev.text.trim();
57-
if (prevEmpty) {
58-
// extend previous duration to cover this line
59-
const prevEnd = prev.timeInMs + prev.duration;
60-
const thisEnd = line.timeInMs + line.duration;
61-
const newEnd = Math.max(prevEnd, thisEnd);
62-
prev.duration = newEnd - prev.timeInMs;
63-
continue; // skip adding this line
64-
}
65-
}
66-
merged.push(line);
67-
}
68-
69-
return merged;
55+
const merged = mergeConsecutiveEmptySyncedLines(parsed);
56+
const withLeading = ensureLeadingPaddingEmptyLine(
57+
merged,
58+
300,
59+
'span',
60+
);
61+
return ensureTrailingEmptyLine(withLeading, 'lastEnd');
7062
})()
7163
: undefined,
7264
lyrics: lyrics,

src/plugins/synced-lyrics/providers/YTMusic.ts

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import {
2+
ensureLeadingPaddingEmptyLine,
3+
ensureTrailingEmptyLine,
4+
mergeConsecutiveEmptySyncedLines,
5+
} from '../shared/lines';
6+
17
import type { LyricProvider, LyricResult, SearchSongInfo } from '../types';
28
import type { YouTubeMusicAppElement } from '@/types/youtube-music-app-element';
39

@@ -76,39 +82,20 @@ export class YTMusic implements LyricProvider {
7682
return null;
7783
}
7884

79-
if (synced?.length && synced[0].timeInMs > 300) {
80-
synced.unshift({
81-
duration: 0,
82-
text: '',
83-
time: '00:00.00',
84-
timeInMs: 0,
85-
status: 'upcoming' as const,
86-
});
87-
}
88-
89-
// ensure a final empty line exists
90-
if (synced?.length) {
91-
const last = synced[synced.length - 1];
92-
const lastEnd = parseInt(last.timeInMs.toString()) + last.duration;
93-
94-
// youtube sometimes omits trailing silence, add our own
95-
if (last.text !== '') {
96-
synced.push({
97-
duration: 0,
98-
text: '',
99-
time: this.millisToTime(lastEnd),
100-
timeInMs: lastEnd,
101-
status: 'upcoming' as const,
102-
});
103-
}
104-
}
85+
const processed = synced
86+
? (() => {
87+
const merged = mergeConsecutiveEmptySyncedLines(synced);
88+
const withLeading = ensureLeadingPaddingEmptyLine(merged, 300, 'span');
89+
return ensureTrailingEmptyLine(withLeading, 'lastEnd');
90+
})()
91+
: undefined;
10592

10693
return {
10794
title,
10895
artists: [artist],
10996

11097
lyrics: plain,
111-
lines: synced,
98+
lines: processed,
11299
};
113100
}
114101

0 commit comments

Comments
 (0)