fix: instant OS notification update + real audio preload for gapless playback
- Call updateMediaSession immediately on track change so skip buttons and cover art in the OS notification bar update without waiting for audio load - Replace link[rel=prefetch] (no-op for audio streams) with a shadow Audio element (preload=auto) that actually buffers the next track - Start preloading 60s before track end instead of 30s for more buffer margin Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f04d87254b
commit
128437821a
1 file changed
+13
-13
+13
-13
@@ -273,10 +273,10 @@ function _setAudioSession() {
|
|||||||
try { if(navigator.audioSession) navigator.audioSession.type = 'playback'; } catch {}
|
try { if(navigator.audioSession) navigator.audioSession.type = 'playback'; } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── PRELOAD (resource hint keeps connection open through Doze gap) ───────
|
// ─── PRELOAD — shadow Audio element actually buffers the next track ──────────
|
||||||
let _preUrl = '';
|
let _preUrl = '';
|
||||||
let _preId = '';
|
let _preId = '';
|
||||||
let _preLink = null;
|
let _preAudio = null;
|
||||||
|
|
||||||
function _nextIdx() {
|
function _nextIdx() {
|
||||||
if(S.repeat === 'one') return S.qIdx;
|
if(S.repeat === 'one') return S.qIdx;
|
||||||
@@ -286,19 +286,18 @@ function _nextIdx() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _preloadNext() {
|
function _preloadNext() {
|
||||||
if (HAS_BRIDGE) return; // ExoPlayer handles buffering natively
|
if (HAS_BRIDGE) return;
|
||||||
const ni = _nextIdx();
|
const ni = _nextIdx();
|
||||||
if(ni < 0) return;
|
if(ni < 0) return;
|
||||||
const next = S.queue[ni];
|
const next = S.queue[ni];
|
||||||
if(!next || _preId === next.id) return;
|
if(!next || _preId === next.id) return;
|
||||||
_preId = next.id;
|
_preId = next.id;
|
||||||
_preUrl = streamUrl(next.id);
|
_preUrl = streamUrl(next.id);
|
||||||
if(_preLink) _preLink.remove();
|
// Real Audio preload — actually buffers the stream so canplay fires instantly
|
||||||
_preLink = document.createElement('link');
|
if(_preAudio) { _preAudio.src = ''; _preAudio = null; }
|
||||||
_preLink.rel = 'prefetch';
|
_preAudio = new Audio();
|
||||||
_preLink.as = 'fetch';
|
_preAudio.preload = 'auto';
|
||||||
_preLink.href = _preUrl;
|
_preAudio.src = _preUrl;
|
||||||
document.head.appendChild(_preLink);
|
|
||||||
if(next.coverArt) {
|
if(next.coverArt) {
|
||||||
[256, 512, 1024].forEach(size => {
|
[256, 512, 1024].forEach(size => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
@@ -309,7 +308,7 @@ function _preloadNext() {
|
|||||||
|
|
||||||
function _clearPreload() {
|
function _clearPreload() {
|
||||||
_preId = ''; _preUrl = '';
|
_preId = ''; _preUrl = '';
|
||||||
if(_preLink) { _preLink.remove(); _preLink = null; }
|
if(_preAudio) { _preAudio.src = ''; _preAudio = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── PLAY HISTORY (for prev-in-shuffle) ──────────────────────────────────────
|
// ─── PLAY HISTORY (for prev-in-shuffle) ──────────────────────────────────────
|
||||||
@@ -388,6 +387,7 @@ export function playCurrent() {
|
|||||||
_resetProgressUI();
|
_resetProgressUI();
|
||||||
if(navigator.mediaSession) navigator.mediaSession.playbackState = 'playing';
|
if(navigator.mediaSession) navigator.mediaSession.playbackState = 'playing';
|
||||||
updatePlayerUI(s, true);
|
updatePlayerUI(s, true);
|
||||||
|
updateMediaSession(s);
|
||||||
onNpSongChange(s.id);
|
onNpSongChange(s.id);
|
||||||
renderQueue();
|
renderQueue();
|
||||||
_scheduleScrobble(s.id, s.duration);
|
_scheduleScrobble(s.id, s.duration);
|
||||||
@@ -830,7 +830,7 @@ export function updateProgress() {
|
|||||||
if(!S.audio.duration || S._scrubbing) return;
|
if(!S.audio.duration || S._scrubbing) return;
|
||||||
syncMediaSessionPosition();
|
syncMediaSessionPosition();
|
||||||
const rem = S.audio.duration - S.audio.currentTime;
|
const rem = S.audio.duration - S.audio.currentTime;
|
||||||
if(rem > 0 && rem < 30 && !S._trackChanging) _preloadNext();
|
if(rem > 0 && rem < 60 && !S._trackChanging) _preloadNext();
|
||||||
const p = (S.audio.currentTime / S.audio.duration) * 100;
|
const p = (S.audio.currentTime / S.audio.duration) * 100;
|
||||||
document.getElementById('progress-fill').style.width = p + '%';
|
document.getElementById('progress-fill').style.width = p + '%';
|
||||||
document.getElementById('time-cur').textContent = formatDur(S.audio.currentTime);
|
document.getElementById('time-cur').textContent = formatDur(S.audio.currentTime);
|
||||||
|
|||||||
Reference in new issue
Block a user