Fix stream-player.js reconnect loop (play/pause race, stall backoff)
- Keep *is-reconnecting* true until 'playing' event fires, preventing pause/stalled handlers from triggering new reconnect cycles mid-flight - Add exponential backoff for stall retries (5s, 10s, 20s... max 60s) - Give up auto-reconnect after 10 stall attempts, show manual retry - Add *stall-count* tracking, reset on successful playback - Add *user-paused* guard to muted-tab pause handler - Increase play() delay from 200ms to 500ms after load() for reliability Fixes: AbortError from play()/pause() race, 429 Too Many Requests from aggressive reconnect hammering, infinite reconnect loop on muted tabs.
This commit is contained in:
parent
bcfda2ebb6
commit
f2e60b5648
|
|
@ -664,8 +664,10 @@
|
||||||
|
|
||||||
;; Error retry counter and reconnect state
|
;; Error retry counter and reconnect state
|
||||||
(defvar *stream-error-count* 0)
|
(defvar *stream-error-count* 0)
|
||||||
|
(defvar *stall-count* 0)
|
||||||
(defvar *reconnect-timeout* nil)
|
(defvar *reconnect-timeout* nil)
|
||||||
(defvar *is-reconnecting* false)
|
(defvar *is-reconnecting* false)
|
||||||
|
(defvar *user-paused* false)
|
||||||
|
|
||||||
;; Reconnect stream - reuses existing audio element to preserve user gesture context
|
;; Reconnect stream - reuses existing audio element to preserve user gesture context
|
||||||
(defun reconnect-stream ()
|
(defun reconnect-stream ()
|
||||||
|
|
@ -704,15 +706,16 @@
|
||||||
(setf (ps:@ new-source type) (ps:@ config type))
|
(setf (ps:@ new-source type) (ps:@ config type))
|
||||||
(ps:chain audio (append-child new-source))))
|
(ps:chain audio (append-child new-source))))
|
||||||
|
|
||||||
;; Reload and play
|
;; Reload and play — keep *is-reconnecting* true until 'playing' fires
|
||||||
(ps:chain audio (load))
|
(ps:chain audio (load))
|
||||||
(setf *is-reconnecting* false)
|
|
||||||
(set-timeout
|
(set-timeout
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(ps:chain audio (play)
|
(ps:chain audio (play)
|
||||||
(catch (lambda (error)
|
(catch (lambda (error)
|
||||||
(ps:chain console (log "Reconnect play failed:" error))))))
|
(ps:chain console (log "Reconnect play failed:" error))
|
||||||
200)))
|
;; play() rejected — reset so next stall/error can retry
|
||||||
|
(setf *is-reconnecting* false)))))
|
||||||
|
500)))
|
||||||
|
|
||||||
;; Simple reconnect for popout player (just reload and play)
|
;; Simple reconnect for popout player (just reload and play)
|
||||||
(defun simple-reconnect (audio-element)
|
(defun simple-reconnect (audio-element)
|
||||||
|
|
@ -737,6 +740,7 @@
|
||||||
(start-mini-spectrum)
|
(start-mini-spectrum)
|
||||||
(hide-status)
|
(hide-status)
|
||||||
(setf *stream-error-count* 0)
|
(setf *stream-error-count* 0)
|
||||||
|
(setf *stall-count* 0)
|
||||||
(setf *is-reconnecting* false)
|
(setf *is-reconnecting* false)
|
||||||
(when *reconnect-timeout*
|
(when *reconnect-timeout*
|
||||||
(clear-timeout *reconnect-timeout*)
|
(clear-timeout *reconnect-timeout*)
|
||||||
|
|
@ -756,18 +760,26 @@
|
||||||
(set-timeout (lambda () (reconnect-stream)) delay)))))))
|
(set-timeout (lambda () (reconnect-stream)) delay)))))))
|
||||||
|
|
||||||
(ps:chain audio-element
|
(ps:chain audio-element
|
||||||
(add-event-listener "stalled"
|
(add-event-listener "stalled"
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(unless *is-reconnecting*
|
(unless *is-reconnecting*
|
||||||
(ps:chain console (log "Audio stalled, will auto-reconnect in 5 seconds..."))
|
(setf *stall-count* (+ *stall-count* 1))
|
||||||
(show-status "⚠️ Stream stalled - reconnecting..." true)
|
;; Exponential backoff: 5s, 10s, 20s, max 60s
|
||||||
(setf *is-reconnecting* true)
|
(let ((delay (ps:chain -math (min (* 5000 (ps:chain -math (pow 2 (- *stall-count* 1)))) 60000))))
|
||||||
(setf *reconnect-timeout*
|
(if (> *stall-count* 10)
|
||||||
(set-timeout
|
;; Give up after 10 stall attempts — show manual retry
|
||||||
(lambda ()
|
(progn
|
||||||
;; Always reconnect on stall - ready-state is unreliable for streams
|
(ps:chain console (log "Too many stall retries, giving up auto-reconnect"))
|
||||||
(reconnect-stream))
|
(show-status "⚠️ Stream unavailable - click play to retry" true))
|
||||||
5000))))))
|
(progn
|
||||||
|
(ps:chain console (log (+ "Audio stalled (attempt " *stall-count* "), reconnecting in " (/ delay 1000) "s...")))
|
||||||
|
(show-status (+ "⚠️ Stream stalled - reconnecting (" *stall-count* ")...") true)
|
||||||
|
(setf *is-reconnecting* true)
|
||||||
|
(setf *reconnect-timeout*
|
||||||
|
(set-timeout
|
||||||
|
(lambda ()
|
||||||
|
(reconnect-stream))
|
||||||
|
delay)))))))))
|
||||||
|
|
||||||
;; Handle ended event - stream shouldn't end, so reconnect
|
;; Handle ended event - stream shouldn't end, so reconnect
|
||||||
(ps:chain audio-element
|
(ps:chain audio-element
|
||||||
|
|
@ -785,12 +797,14 @@
|
||||||
(lambda ()
|
(lambda ()
|
||||||
;; Stop mini spectrum when paused
|
;; Stop mini spectrum when paused
|
||||||
(stop-mini-spectrum)
|
(stop-mini-spectrum)
|
||||||
;; If paused while muted and we didn't initiate it, browser may have throttled
|
;; Only treat as throttle if: muted, not reconnecting, and not user-initiated
|
||||||
(when (and (ps:@ audio-element muted) (not *is-reconnecting*))
|
(when (and (ps:@ audio-element muted)
|
||||||
(ps:chain console (log "Stream paused while muted (possible browser throttling), will reconnect in 3 seconds..."))
|
(not *is-reconnecting*)
|
||||||
|
(not *user-paused*))
|
||||||
|
(ps:chain console (log "Stream paused while muted (possible browser throttling), will reconnect in 5 seconds..."))
|
||||||
(show-status "⚠️ Stream paused - reconnecting..." true)
|
(show-status "⚠️ Stream paused - reconnecting..." true)
|
||||||
(setf *is-reconnecting* true)
|
(setf *is-reconnecting* true)
|
||||||
(set-timeout (lambda () (reconnect-stream)) 3000))))))
|
(set-timeout (lambda () (reconnect-stream)) 5000))))))
|
||||||
|
|
||||||
;; Attach simple listeners for popout player
|
;; Attach simple listeners for popout player
|
||||||
(defun attach-popout-listeners (audio-element)
|
(defun attach-popout-listeners (audio-element)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue