Revert "feat(web): gapless audio handoff to fix desktop media-tile flicker"
This reverts commit 98d7c84269.
This commit is contained in:
1 parent
98d7c84269
commit
420c84d191
4 files changed
+48
-92
No files matched your search
+35
-44
@@ -122,9 +122,6 @@ window.addEventListener('load', () => {
|
||||
|
||||
// Wire up audio elements from DOM
|
||||
S.audio = document.getElementById('audio-element');
|
||||
const _audioB = document.getElementById('audio-element-b');
|
||||
// Two fixed audio elements for gapless handoff — S.audio always points at the active one.
|
||||
S._audioEls = _audioB ? [S.audio, _audioB] : [S.audio];
|
||||
S.previewAudio = document.getElementById('preview-audio');
|
||||
|
||||
const saved = localStorage.getItem('ns_cfg');
|
||||
@@ -137,50 +134,44 @@ window.addEventListener('load', () => {
|
||||
if(window.NS_SERVER_URL?.trim()) { const el=document.getElementById('srv-url'); if(el&&!el.value) el.value=window.NS_SERVER_URL.trim(); }
|
||||
|
||||
document.getElementById('srv-pass').addEventListener('keydown', e => { if(e.key==='Enter') doConnect(); });
|
||||
// Listeners are bound to BOTH audio elements so a gapless handoff needs no rebinding.
|
||||
// Handlers that only make sense for the playing element guard with `el !== S.audio`.
|
||||
let _errRetries = 0, _errTimer = null;
|
||||
function bindAudioEl(el) {
|
||||
el.addEventListener('timeupdate', updateProgress);
|
||||
el.addEventListener('ended', onEnded);
|
||||
el.addEventListener('play', () => {
|
||||
S._trackChanging = false;
|
||||
absInterrupt();
|
||||
if (S.previewCurrent) stopPreview(false);
|
||||
setPlayIcon(true);
|
||||
document.getElementById('player-art').classList.add('spinning');
|
||||
if(navigator.mediaSession) navigator.mediaSession.playbackState = 'playing';
|
||||
});
|
||||
el.addEventListener('pause', () => {
|
||||
if (el !== S.audio) return; // ignore the idle standby element during a handoff
|
||||
setPlayIcon(false);
|
||||
document.getElementById('player-art').classList.remove('spinning');
|
||||
// Suppress during track change — src swap fires pause before the new track starts
|
||||
if(!S._trackChanging && navigator.mediaSession) navigator.mediaSession.playbackState = 'paused';
|
||||
});
|
||||
// stalled = browser buffer ran out briefly; let the browser retry naturally.
|
||||
el.addEventListener('play', () => { _errRetries = 0; clearTimeout(_errTimer); }, { capture: true });
|
||||
el.addEventListener('error', () => {
|
||||
if (el !== S.audio) return; // standby/preload errors must not disrupt active playback
|
||||
// Skip if playCurrent() is already handling recovery (_trackChanging = true means
|
||||
// we're mid-transition or waiting for canplay — don't conflict with that)
|
||||
if(!S.queue[S.qIdx] || S._trackChanging) return;
|
||||
_errRetries++;
|
||||
const delay = Math.min(1000 * Math.pow(2, _errRetries - 1), 8000); // 1s, 2s, 4s, 8s
|
||||
clearTimeout(_errTimer);
|
||||
_errTimer = setTimeout(() => {
|
||||
if(!S.queue[S.qIdx] || S._trackChanging) return;
|
||||
S._trackChanging = true;
|
||||
S.audio.load();
|
||||
S.audio.play().catch(() => { S._trackChanging = false; });
|
||||
}, delay);
|
||||
});
|
||||
}
|
||||
S._audioEls.forEach(bindAudioEl);
|
||||
|
||||
S.audio.addEventListener('timeupdate', updateProgress);
|
||||
S.audio.addEventListener('ended', onEnded);
|
||||
S.audio.addEventListener('play', () => {
|
||||
S._trackChanging = false;
|
||||
absInterrupt();
|
||||
if (S.previewCurrent) stopPreview(false);
|
||||
setPlayIcon(true);
|
||||
document.getElementById('player-art').classList.add('spinning');
|
||||
if(navigator.mediaSession) navigator.mediaSession.playbackState = 'playing';
|
||||
});
|
||||
S.audio.addEventListener('pause', () => {
|
||||
setPlayIcon(false);
|
||||
document.getElementById('player-art').classList.remove('spinning');
|
||||
// Suppress during track change — src swap fires pause before the new track starts
|
||||
if(!S._trackChanging && navigator.mediaSession) navigator.mediaSession.playbackState = 'paused';
|
||||
});
|
||||
S.previewAudio.addEventListener('timeupdate', updatePreviewProgress);
|
||||
S.previewAudio.addEventListener('ended', stopPreview);
|
||||
|
||||
// stalled = browser buffer ran out briefly; let the browser retry naturally.
|
||||
// The watchdog below catches genuine long freezes.
|
||||
let _errRetries = 0, _errTimer = null;
|
||||
S.audio.addEventListener('play', () => { _errRetries = 0; clearTimeout(_errTimer); }, { capture: true });
|
||||
S.audio.addEventListener('error', () => {
|
||||
// Skip if playCurrent() is already handling recovery (_trackChanging = true means
|
||||
// we're mid-transition or waiting for canplay — don't conflict with that)
|
||||
if(!S.queue[S.qIdx] || S._trackChanging) return;
|
||||
_errRetries++;
|
||||
const delay = Math.min(1000 * Math.pow(2, _errRetries - 1), 8000); // 1s, 2s, 4s, 8s
|
||||
clearTimeout(_errTimer);
|
||||
_errTimer = setTimeout(() => {
|
||||
if(!S.queue[S.qIdx] || S._trackChanging) return;
|
||||
S._trackChanging = true;
|
||||
S.audio.load();
|
||||
S.audio.play().catch(() => { S._trackChanging = false; });
|
||||
}, delay);
|
||||
});
|
||||
|
||||
const _savedVol = parseInt(localStorage.getItem('ns_volume') ?? '', 10);
|
||||
setVolume(isNaN(_savedVol) ? 55 : _savedVol);
|
||||
|
||||
|
||||
Reference in new issue
Block a user