|
|
@@ -87,7 +87,10 @@ export function createVideoLiveSession(api, update, timers = globalThis) {
|
|
|
// VideoRequestError carries `category`; accepted errors use `code`.
|
|
|
const reason = error?.category || error?.code
|
|
|
return !unrecoverable.has(reason)
|
|
|
- } function scheduleRetry(deviceCode, message) {
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Spends one attempt of the budget and schedules the next attempt after its own delay. */
|
|
|
+ function scheduleRetry(deviceCode, message) {
|
|
|
const attempt = current
|
|
|
// Spend the budget here, before the timer exists, so the last retry cannot schedule another.
|
|
|
retryBudget -= 1
|
|
|
@@ -110,7 +113,13 @@ export function createVideoLiveSession(api, update, timers = globalThis) {
|
|
|
const stopping = stop(), version = generation
|
|
|
const released = await stopping
|
|
|
if (disposed || version !== generation) return
|
|
|
- if (!retryable || !released) { publish({ phase: 'failed' }); return }
|
|
|
+ if (!retryable || !released) {
|
|
|
+ // Running the budget down is actionable information, distinct from a permanent capability error.
|
|
|
+ const exhausted = retryAttempt >= retryDelays.length && !unrecoverable.has(error?.category || error?.code)
|
|
|
+ const reason = exhausted ? `${error.message}(已自动重试 ${retryDelays.length} 次仍失败)` : error.message
|
|
|
+ publish({ phase: 'failed', error: reason, retryAttempt: 0, retryDelayMs: 0, retriesLeft: 0 })
|
|
|
+ return
|
|
|
+ }
|
|
|
generation = version
|
|
|
current = attempt
|
|
|
attempt.version = version
|