From f0346d82ad2687eb51240758474df5628ba3ce0f Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 24 Jan 2026 14:16:50 +0000 Subject: [PATCH] fix timeline --- static/audioplayer.js | 89 ++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/static/audioplayer.js b/static/audioplayer.js index 5dab0aa..e1193a4 100644 --- a/static/audioplayer.js +++ b/static/audioplayer.js @@ -23,7 +23,6 @@ class SimpleAudioPlayer { this.isSeeking = false; this.rafId = null; this.abortCtrl = null; - this._posInterval = null; // Pre-compute icons once const fill = getComputedStyle(document.documentElement) @@ -53,6 +52,32 @@ class SimpleAudioPlayer { this._initMediaSession(); } + /** + * Push the current position to MediaSession so lockscreen / BT UIs stay in sync. + * Keeps values strict but avoids over-clamping that can confuse some platforms. + */ + _pushPositionState(force = false) { + if (!navigator.mediaSession?.setPositionState) return; + + const duration = this.audio.duration; + const position = this.audio.currentTime; + if (!Number.isFinite(duration) || duration <= 0) return; + if (!Number.isFinite(position) || position < 0) return; + + const playbackRate = this.audio.paused + ? 0 + : (Number.isFinite(this.audio.playbackRate) ? this.audio.playbackRate : 1); + + try { + if ('playbackState' in navigator.mediaSession) { + navigator.mediaSession.playbackState = this.audio.paused ? 'paused' : 'playing'; + } + navigator.mediaSession.setPositionState({ duration, playbackRate, position }); + } catch (err) { + if (force) console.warn('setPositionState failed:', err); + } + } + // Minimal SVG helper svg(pathD, fill) { return ` @@ -71,36 +96,40 @@ class SimpleAudioPlayer { this.timeline.max = this.audio.duration; this.timeline.value = 0; this.timeline.style.backgroundSize = '0% 100%'; + this._pushPositionState(true); }); - this.audio.addEventListener('play', () => this._updatePlayState()); - this.audio.addEventListener('pause', () => this._updatePlayState()); - this.audio.addEventListener('ended', () => this.playBtn.innerHTML = this.icons.play); + this.audio.addEventListener('play', () => { + this._updatePlayState(); + this._pushPositionState(true); + }); + + this.audio.addEventListener('playing', () => this._pushPositionState(true)); + + this.audio.addEventListener('pause', () => { + this._updatePlayState(); + this._pushPositionState(true); + }); + + this.audio.addEventListener('ratechange', () => this._pushPositionState(true)); + this.audio.addEventListener('ended', () => { + this.playBtn.innerHTML = this.icons.play; + this._pushPositionState(true); + if ('mediaSession' in navigator) navigator.mediaSession.playbackState = 'paused'; + }); // Native timeupdate => update timeline this.audio.addEventListener('timeupdate', () => this.updateTimeline()); - // Fallback interval for throttled mobile - this._interval = setInterval(() => { - if (!this.audio.paused && !this.isSeeking) { - this.updateTimeline(); - } - }, 500); + // Keep position in sync when page visibility changes + document.addEventListener('visibilitychange', () => this._pushPositionState(true)); // Unified seek input this.timeline.addEventListener('input', () => { this.isSeeking = true; this.audio.currentTime = this.timeline.value; - // immediate Android sync - if (navigator.mediaSession?.setPositionState && Number.isFinite(this.audio.duration)) { - navigator.mediaSession.setPositionState({ - duration: this.audio.duration, - playbackRate: this.audio.playbackRate, - position: this.audio.currentTime - }); - } - + this._pushPositionState(true); // immediate Android sync this.updateTimeline(); this.isSeeking = false; }); @@ -112,9 +141,7 @@ class SimpleAudioPlayer { _updatePlayState() { this.playBtn.innerHTML = this.audio.paused ? this.icons.play : this.icons.pause; - if ('mediaSession' in navigator) { - navigator.mediaSession.playbackState = this.audio.paused ? 'paused' : 'playing'; - } + this._pushPositionState(true); // lock‑screen refresh on play/pause toggle } _formatTime(sec) { @@ -134,13 +161,7 @@ class SimpleAudioPlayer { this.timeInfo.textContent = `${this._formatTime(this.audio.currentTime)} / ${this._formatTime(this.audio.duration)}`; // 4) Push to Android widget - if (navigator.mediaSession?.setPositionState && Number.isFinite(this.audio.duration)) { - navigator.mediaSession.setPositionState({ - duration: this.audio.duration, - playbackRate: this.audio.playbackRate, - position: this.audio.currentTime - }); - } + this._pushPositionState(); } toggleCollapse() { @@ -267,16 +288,6 @@ class SimpleAudioPlayer { } })); - // Heartbeat for widget - this._posInterval = setInterval(() => { - if (!this.audio.paused && navigator.mediaSession?.setPositionState && Number.isFinite(this.audio.duration)) { - navigator.mediaSession.setPositionState({ - duration: this.audio.duration, - playbackRate: this.audio.playbackRate, - position: this.audio.currentTime - }); - } - }, 1000); } }