Fix stream stall reconnection failure

- Reset *is-reconnecting* flag before play attempt so error handler can
  catch failures and trigger retry with exponential backoff
- Always reconnect on stall event - ready-state check was unreliable
- Add retry logic with backoff when reconnect fails (up to 5 attempts)
- Store stalled timeout reference for proper cancellation

Fixes issue where stream would show 'Stream stalled - reconnecting...'
but never actually reconnect, requiring manual intervention.
This commit is contained in:
glenneth 2026-01-05 14:24:01 +03:00
parent f476b83cbe
commit 59afb9f01e
2 changed files with 22 additions and 12 deletions

View File

@ -539,10 +539,8 @@
(setf *reconnect-timeout* (setf *reconnect-timeout*
(set-timeout (set-timeout
(lambda () (lambda ()
;; Only reconnect if still stalled ;; Always reconnect on stall - ready-state is unreliable for streams
(if (< (ps:@ audio-element ready-state) 3) (reconnect-stream))
(reconnect-stream)
(setf *is-reconnecting* false)))
5000))))) 5000)))))
;; Ended handler - stream shouldn't end, so reconnect ;; Ended handler - stream shouldn't end, so reconnect

View File

@ -429,6 +429,7 @@
(unless (and container old-audio) (unless (and container old-audio)
(show-status "❌ Could not reconnect - reload page" true) (show-status "❌ Could not reconnect - reload page" true)
(setf *is-reconnecting* false)
(return-from reconnect-stream nil)) (return-from reconnect-stream nil))
;; Save current volume and muted state ;; Save current volume and muted state
@ -469,7 +470,8 @@
;; Re-attach event listeners ;; Re-attach event listeners
(attach-audio-listeners new-audio) (attach-audio-listeners new-audio)
;; Try to play ;; Try to play - reset flag so error handler can catch failures
(setf *is-reconnecting* false)
(set-timeout (set-timeout
(lambda () (lambda ()
(ps:chain new-audio (play) (ps:chain new-audio (play)
@ -496,7 +498,17 @@
600))) 600)))
(catch (lambda (err) (catch (lambda (err)
(ps:chain console (log "Reconnect play failed:" err)) (ps:chain console (log "Reconnect play failed:" err))
(show-status "Click play to start stream" false))))) ;; Retry with exponential backoff
(incf *stream-error-count*)
(if (< *stream-error-count* 5)
(let ((delay (* 2000 *stream-error-count*)))
(show-status (+ "⚠️ Reconnect failed, retrying in " (/ delay 1000) "s...") true)
(setf *is-reconnecting* false)
(setf *reconnect-timeout*
(set-timeout (lambda () (reconnect-stream)) delay)))
(progn
(setf *is-reconnecting* false)
(show-status "❌ Could not reconnect. Click play to try again." true)))))))
300))))) 300)))))
;; Simple reconnect for popout player (just reload and play) ;; Simple reconnect for popout player (just reload and play)
@ -544,12 +556,12 @@
(ps:chain console (log "Audio stalled, will auto-reconnect in 5 seconds...")) (ps:chain console (log "Audio stalled, will auto-reconnect in 5 seconds..."))
(show-status "⚠️ Stream stalled - reconnecting..." true) (show-status "⚠️ Stream stalled - reconnecting..." true)
(setf *is-reconnecting* true) (setf *is-reconnecting* true)
(setf *reconnect-timeout*
(set-timeout (set-timeout
(lambda () (lambda ()
(if (< (ps:@ audio-element ready-state) 3) ;; Always reconnect on stall - ready-state is unreliable for streams
(reconnect-stream) (reconnect-stream))
(setf *is-reconnecting* false))) 5000))))))
5000)))))
;; 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