Skip to content

Commit 9e3cc05

Browse files
committed
fix: enhance lyrics processing and synchronization in Player class
1 parent 01609ab commit 9e3cc05

2 files changed

Lines changed: 60 additions & 28 deletions

File tree

src/lyrics/youtube.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,24 @@ export default class YouTubeLyrics {
101101
const lines = lyrics.events
102102
.map((event) => {
103103
const text = event.segs?.map((seg) => seg.utf8).join('') || ''
104+
const words =
105+
event.segs?.map((seg) => ({
106+
text: seg.utf8
107+
.replace(/'/g, "'")
108+
.replace(/"/g, '"')
109+
.replace(/&/g, '&'),
110+
timestamp: event.tStartMs + (seg.tOffsetMs || 0),
111+
duration: 0
112+
})) || []
113+
104114
return {
105115
text: text
106116
.replace(/'/g, "'")
107117
.replace(/"/g, '"')
108118
.replace(/&/g, '&'),
109119
time: event.tStartMs,
110-
duration: event.dDurationMs || 0
120+
duration: event.dDurationMs || 0,
121+
words
111122
}
112123
})
113124
.filter((line) => line.text.trim().length > 0)

src/playback/player.js

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,6 @@ export class Player {
320320
this.isPaused = false
321321

322322
if (!wasResuming && !this._isRestoring) {
323-
this._lyricsBasePackets =
324-
this.connection?.statistics?.packetsExpected ?? 0
325323
this._fading('trackStart')
326324
this._emitTrackStart()
327325
}
@@ -771,6 +769,8 @@ export class Player {
771769
resource.setVolume(this.volumePercent / 100)
772770
}
773771
this._lyricsBasePosition = startTime
772+
this._lyricsBasePackets =
773+
this.connection?.statistics?.packetsExpected ?? 0
774774
this._fading('trackStartArm', { resource })
775775
this._fading('trackEndSchedule', { startPosition: startTime || 0 })
776776

@@ -957,7 +957,7 @@ export class Player {
957957
clearTimeout(this._lyricsMarkerTimer)
958958
this._lyricsMarkerTimer = null
959959
}
960-
if (this.isLyricsSubscribed) this._recalculateLyricsIndex()
960+
if (this.isLyricsSubscribed) this._recalculateLyricsIndex(undefined, undefined, true)
961961
this._fading('seek')
962962
this._fading('trackEndSchedule', { startPosition: this.position })
963963
}
@@ -1047,6 +1047,9 @@ export class Player {
10471047
'Player',
10481048
`Playing resource for guild ${this.guildId} after source seek`
10491049
)
1050+
this._lyricsBasePosition = position
1051+
this._lyricsBasePackets =
1052+
this.connection?.statistics?.packetsExpected ?? 0
10501053
this.connection.play(resource)
10511054
await this.waitEvent('playerStateChange', (s) => s.status === 'playing')
10521055

@@ -1099,6 +1102,10 @@ export class Player {
10991102
this._fading('seekPrepare', { resource })
11001103
resource.setFilters(this.filters)
11011104

1105+
this._lyricsBasePosition = position
1106+
this._lyricsBasePackets =
1107+
this.connection?.statistics?.packetsExpected ?? 0
1108+
11021109
const oldStream = this.connection.play(resource)
11031110
await this.waitEvent('playerStateChange', (s) => s.status === 'playing')
11041111
if (oldStream) {
@@ -1214,6 +1221,9 @@ export class Player {
12141221
'Player',
12151222
`Playing resource for guild ${this.guildId} after legacy seek`
12161223
)
1224+
this._lyricsBasePosition = position
1225+
this._lyricsBasePackets =
1226+
this.connection?.statistics?.packetsExpected ?? 0
12171227
this.connection.play(resource)
12181228
await this.waitEvent('playerStateChange', (s) => s.status === 'playing')
12191229

@@ -1603,16 +1613,25 @@ export class Player {
16031613
)
16041614

16051615
if (lyricsData && lyricsData.loadType === 'lyrics') {
1616+
const lines = lyricsData.data.lines.map((line) => ({
1617+
timestamp: line.time,
1618+
duration: line.duration || 0,
1619+
line: line.text,
1620+
words: line.words || [],
1621+
plugin: {}
1622+
}))
1623+
1624+
for (let i = 0; i < lines.length - 1; i++) {
1625+
if (lines[i].duration === 0) {
1626+
lines[i].duration = lines[i + 1].timestamp - lines[i].timestamp
1627+
}
1628+
}
1629+
16061630
const payload = {
16071631
sourceName: this.track.info.sourceName,
16081632
provider: lyricsData.data.provider,
16091633
text: lyricsData.data.lines.map((l) => l.text).join('\n'),
1610-
lines: lyricsData.data.lines.map((line) => ({
1611-
timestamp: line.time,
1612-
duration: line.duration,
1613-
line: line.text,
1614-
plugin: {}
1615-
})),
1634+
lines,
16161635
plugin: {}
16171636
}
16181637

@@ -1623,7 +1642,7 @@ export class Player {
16231642
clearTimeout(this._lyricsMarkerTimer)
16241643
this._lyricsMarkerTimer = null
16251644
}
1626-
this._recalculateLyricsIndex()
1645+
this._recalculateLyricsIndex(undefined, undefined, true)
16271646
this._syncLyrics(true)
16281647
} else {
16291648
this.currentLyrics = null
@@ -1635,16 +1654,12 @@ export class Player {
16351654
if (!this.isLyricsSubscribed || !this.currentLyrics || !this.currentLyrics.lines) return
16361655
if (this._lyricsMarkerTimer && !force) return
16371656

1638-
const stats = this.connection?.statistics
1639-
if (!stats) return
16401657
const timescale = this.filters.filters?.timescale || {
16411658
speed: 1.0,
16421659
rate: 1.0
16431660
}
16441661
const playbackSpeed = (timescale.speed || 1.0) * (timescale.rate || 1.0)
1645-
const packets = stats.packetsExpected ?? this._lyricsBasePackets
1646-
const deltaPackets = Math.max(0, packets - this._lyricsBasePackets)
1647-
const position = this._lyricsBasePosition + deltaPackets * 20 * playbackSpeed
1662+
const position = this._getLyricsPosition(playbackSpeed)
16481663
const lines = this.currentLyrics.lines
16491664
this._recalculateLyricsIndex(position, lines)
16501665

@@ -1657,19 +1672,18 @@ export class Player {
16571672
this._lyricsMarkerTimer = setTimeout(() => {
16581673
this._lyricsMarkerTimer = null
16591674
if (!this.isLyricsSubscribed || !this.currentLyrics || !this.currentLyrics.lines) return
1660-
const nowStats = this.connection?.statistics
1661-
if (!nowStats) return
1662-
const nowPackets = nowStats.packetsExpected ?? this._lyricsBasePackets
1663-
const nowDeltaPackets = Math.max(0, nowPackets - this._lyricsBasePackets)
1664-
const nowPosition =
1665-
this._lyricsBasePosition + nowDeltaPackets * 20 * playbackSpeed
1675+
const nowPosition = this._getLyricsPosition(playbackSpeed)
16661676
const drift = nowPosition - nextTimestamp
16671677

16681678
if (drift < -15) {
16691679
this._syncLyrics(true)
16701680
return
16711681
}
16721682

1683+
if (Math.abs(drift) > 100) {
1684+
this._lyricsBasePosition -= drift * 0.25
1685+
}
1686+
16731687
this.lyricsLineIndex = nextIndex
16741688
this.emitEvent('LyricsLineEvent', {
16751689
lineIndex: nextIndex,
@@ -1680,23 +1694,26 @@ export class Player {
16801694
}, delayMs)
16811695
}
16821696

1683-
_recalculateLyricsIndex(positionOverride, linesOverride) {
1697+
_getLyricsPosition(playbackSpeed) {
1698+
const stats = this.connection?.statistics
1699+
const packets = stats?.packetsExpected ?? this._lyricsBasePackets
1700+
const deltaPackets = Math.max(0, packets - this._lyricsBasePackets)
1701+
1702+
return this._lyricsBasePosition + deltaPackets * 20 * playbackSpeed
1703+
}
1704+
_recalculateLyricsIndex(positionOverride, linesOverride, allowBackward = false) {
16841705
if (!this.currentLyrics || !this.currentLyrics.lines) return
16851706

16861707
const lines = linesOverride || this.currentLyrics.lines
16871708
let position = positionOverride
16881709

16891710
if (position === undefined) {
1690-
const stats = this.connection?.statistics
1691-
if (!stats) return
16921711
const timescale = this.filters.filters?.timescale || {
16931712
speed: 1.0,
16941713
rate: 1.0
16951714
}
16961715
const playbackSpeed = (timescale.speed || 1.0) * (timescale.rate || 1.0)
1697-
const packets = stats.packetsExpected ?? this._lyricsBasePackets
1698-
const deltaPackets = Math.max(0, packets - this._lyricsBasePackets)
1699-
position = this._lyricsBasePosition + deltaPackets * 20 * playbackSpeed
1716+
position = this._getLyricsPosition(playbackSpeed)
17001717
}
17011718

17021719
let foundIndex = -1
@@ -1709,6 +1726,10 @@ export class Player {
17091726
}
17101727
}
17111728

1729+
if (!allowBackward && foundIndex < this.lyricsLineIndex) {
1730+
return
1731+
}
1732+
17121733
if (foundIndex !== this.lyricsLineIndex) {
17131734
const skipped = foundIndex > this.lyricsLineIndex + 1
17141735
this.lyricsLineIndex = foundIndex

0 commit comments

Comments
 (0)