From 0d8b8b6f5d0dde2002d3da831b25dd7e0203809f Mon Sep 17 00:00:00 2001 From: Honey Date: Sun, 3 May 2026 16:47:17 +0200 Subject: [PATCH] fix: Bug fixes Scrub lag Chapter track last chapter Mini bar stays over music Resume ABS doesn't stop music --- src/abs.js | 22 ++++++++++++++++++---- src/main.js | 3 ++- src/player.js | 12 +++++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/abs.js b/src/abs.js index b7a555d..f0375eb 100644 --- a/src/abs.js +++ b/src/abs.js @@ -253,7 +253,12 @@ export async function absPlayItem(item) { export function absTogglePlay() { const audio = _getAudio(); - S.absPlayer.playing ? audio.pause() : audio.play().catch(() => {}); + if (S.absPlayer.playing) { + audio.pause(); + } else { + if (S.audio && !S.audio.paused) S.audio.pause(); + audio.play().catch(() => {}); + } } export function absInterrupt() { @@ -261,6 +266,11 @@ export function absInterrupt() { _getAudio().pause(); // triggers the pause listener which calls _doSync() } +export function absOnMusicPause() { + // When music pauses, re-evaluate mini bar visibility (ABS bar should reappear) + if (S.absPlayer.item) _renderMiniBar(); +} + export function absSeek(globalTime) { const ap = S.absPlayer; globalTime = Math.max(0, Math.min(globalTime, ap.duration || Infinity)); @@ -373,8 +383,11 @@ function _chapterWindow() { const ap = S.absPlayer; if (!_chapterTrackMode || ap.chapterIdx < 0 || !ap.chapters.length) return null; const ch = ap.chapters[ap.chapterIdx]; + if (!ch || ch.start == null) return null; const nextCh = ap.chapters[ap.chapterIdx + 1]; - return { start: ch.start ?? 0, end: nextCh?.start ?? ap.duration }; + const end = nextCh?.start ?? ch.end ?? ap.duration; + if (end <= ch.start) return null; + return { start: ch.start, end }; } // ─── PROGRESS SCRUB ─── @@ -396,7 +409,8 @@ function _renderMiniBar() { const ap = S.absPlayer; const bar = document.getElementById('abs-mini-bar'); if (!bar) return; - if (!ap.item) { bar.classList.remove('visible'); return; } + // Hide when no ABS item loaded, or when music is actively playing + if (!ap.item || (S.audio && !S.audio.paused)) { bar.classList.remove('visible'); return; } bar.classList.add('visible'); const meta = ap.item?.media?.metadata || {}; const cover = absCoverUrl(ap.item?.id); @@ -452,7 +466,7 @@ function _renderPlayer() { function _renderProgress() { const ap = S.absPlayer; const win = _chapterWindow(); - const dispCur = win ? ap.currentTime - win.start : ap.currentTime; + const dispCur = win ? Math.max(0, ap.currentTime - win.start) : ap.currentTime; const dispDur = win ? win.end - win.start : ap.duration; const pct = dispDur > 0 ? Math.min(100, (dispCur / dispDur) * 100) : 0; diff --git a/src/main.js b/src/main.js index c98edf0..34ac137 100644 --- a/src/main.js +++ b/src/main.js @@ -24,7 +24,7 @@ import { absSetSpeed, absSleepIn, absOpenPlayer, absClosePlayer, absToggleChapters, absToggleSpeedPicker, absToggleSleepPicker, absSeekToChapter, absProgDown, absProgMove, absProgUp, loadAbsBooks, absOpenBook, loadAbsBookDetail, absPlayChapter, - absLoadSettingsLibraries, absToggleChapterTrack, absInterrupt, + absLoadSettingsLibraries, absToggleChapterTrack, absInterrupt, absOnMusicPause, } from './abs.js'; import { openNowPlaying, closeNowPlaying, switchNpTab, npGoArtist, npGoAlbum, @@ -135,6 +135,7 @@ window.addEventListener('load', () => { setPlayIcon(false); document.getElementById('player-art').classList.remove('spinning'); if(navigator.mediaSession) navigator.mediaSession.playbackState = 'paused'; + absOnMusicPause(); }); S.previewAudio.addEventListener('timeupdate', updatePreviewProgress); S.previewAudio.addEventListener('ended', stopPreview); diff --git a/src/player.js b/src/player.js index 76ee3e2..ea93969 100644 --- a/src/player.js +++ b/src/player.js @@ -235,7 +235,17 @@ function _initBarScrub(bar, fill, onMove) { function getPos(clientX) { const r = bar.getBoundingClientRect(); return Math.max(0, Math.min(1, (clientX-r.left)/r.width)); } function startScrub(pos) { S._scrubbing=true; fill.style.transition='none'; fill.style.width=(pos*100)+'%'; onMove(pos); } function moveScrub(pos) { fill.style.width=(pos*100)+'%'; onMove(pos); } - function endScrub(pos) { S._scrubbing=false; fill.style.transition=''; if(S.audio.duration) S.audio.currentTime=pos*S.audio.duration; } + function endScrub(pos) { + fill.style.transition=''; + if(S.audio.duration) { + S.audio.currentTime = pos * S.audio.duration; + // Keep _scrubbing=true until seek completes so timeupdate can't snap bar back + S.audio.addEventListener('seeked', () => { S._scrubbing = false; }, { once: true }); + setTimeout(() => { S._scrubbing = false; }, 1500); // fallback if seeked never fires + } else { + S._scrubbing = false; + } + } bar.addEventListener('touchstart', e => { startScrub(getPos(e.touches[0].clientX)); e.stopPropagation(); }, {passive:true}); bar.addEventListener('touchmove', e => { if(S._scrubbing) moveScrub(getPos(e.touches[0].clientX)); }, {passive:true}); bar.addEventListener('touchend', e => { if(S._scrubbing) endScrub(getPos(e.changedTouches[0].clientX)); }, {passive:true});