Cap stream retries with cooldown reset for radio and emit
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// Maintainer-controlled web client version.
|
// Maintainer-controlled web client version.
|
||||||
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
||||||
window.CHGRID_WEB_VERSION = "2026.02.25 R234";
|
window.CHGRID_WEB_VERSION = "2026.02.25 R235";
|
||||||
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
||||||
window.CHGRID_TIME_ZONE = "America/Detroit";
|
window.CHGRID_TIME_ZONE = "America/Detroit";
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ const UNSUBSCRIBE_HYSTERESIS_SQUARES = 8;
|
|||||||
const SPATIAL_RAMP_SECONDS = 0.2;
|
const SPATIAL_RAMP_SECONDS = 0.2;
|
||||||
const SPATIAL_TIME_CONSTANT_SECONDS = SPATIAL_RAMP_SECONDS / 3;
|
const SPATIAL_TIME_CONSTANT_SECONDS = SPATIAL_RAMP_SECONDS / 3;
|
||||||
const STREAM_PLAY_RETRY_MS = 5000;
|
const STREAM_PLAY_RETRY_MS = 5000;
|
||||||
|
const STREAM_PLAY_MAX_RETRIES = 6;
|
||||||
|
const STREAM_PLAY_RESET_COOLDOWN_MS = 60000;
|
||||||
|
|
||||||
/** Maps a 0-100 speed control to playback-rate range used by emitted audio. */
|
/** Maps a 0-100 speed control to playback-rate range used by emitted audio. */
|
||||||
function resolveEmitPlaybackRate(raw: unknown): number {
|
function resolveEmitPlaybackRate(raw: unknown): number {
|
||||||
@@ -67,6 +69,7 @@ export class ItemEmitRuntime {
|
|||||||
private readonly outputs = new Map<string, EmitOutput>();
|
private readonly outputs = new Map<string, EmitOutput>();
|
||||||
private readonly pendingEmitStarts = new Set<string>();
|
private readonly pendingEmitStarts = new Set<string>();
|
||||||
private readonly nextEmitStartAtMs = new Map<string, number>();
|
private readonly nextEmitStartAtMs = new Map<string, number>();
|
||||||
|
private readonly emitStartFailureCount = new Map<string, number>();
|
||||||
private layerEnabled = true;
|
private layerEnabled = true;
|
||||||
private listenerPositions: Array<{ x: number; y: number }> = [];
|
private listenerPositions: Array<{ x: number; y: number }> = [];
|
||||||
|
|
||||||
@@ -89,6 +92,7 @@ export class ItemEmitRuntime {
|
|||||||
this.outputs.delete(itemId);
|
this.outputs.delete(itemId);
|
||||||
this.pendingEmitStarts.delete(itemId);
|
this.pendingEmitStarts.delete(itemId);
|
||||||
this.nextEmitStartAtMs.delete(itemId);
|
this.nextEmitStartAtMs.delete(itemId);
|
||||||
|
this.emitStartFailureCount.delete(itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupAll(): void {
|
cleanupAll(): void {
|
||||||
@@ -278,8 +282,16 @@ export class ItemEmitRuntime {
|
|||||||
.play()
|
.play()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.nextEmitStartAtMs.delete(itemId);
|
this.nextEmitStartAtMs.delete(itemId);
|
||||||
|
this.emitStartFailureCount.delete(itemId);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
const failures = (this.emitStartFailureCount.get(itemId) ?? 0) + 1;
|
||||||
|
if (failures >= STREAM_PLAY_MAX_RETRIES) {
|
||||||
|
this.emitStartFailureCount.set(itemId, 0);
|
||||||
|
this.nextEmitStartAtMs.set(itemId, Date.now() + STREAM_PLAY_RESET_COOLDOWN_MS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.emitStartFailureCount.set(itemId, failures);
|
||||||
this.nextEmitStartAtMs.set(itemId, Date.now() + STREAM_PLAY_RETRY_MS);
|
this.nextEmitStartAtMs.set(itemId, Date.now() + STREAM_PLAY_RETRY_MS);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
@@ -168,12 +168,15 @@ const UNSUBSCRIBE_HYSTERESIS_SQUARES = 8;
|
|||||||
const SPATIAL_RAMP_SECONDS = 0.2;
|
const SPATIAL_RAMP_SECONDS = 0.2;
|
||||||
const SPATIAL_TIME_CONSTANT_SECONDS = SPATIAL_RAMP_SECONDS / 3;
|
const SPATIAL_TIME_CONSTANT_SECONDS = SPATIAL_RAMP_SECONDS / 3;
|
||||||
const STREAM_PLAY_RETRY_MS = 5000;
|
const STREAM_PLAY_RETRY_MS = 5000;
|
||||||
|
const STREAM_PLAY_MAX_RETRIES = 6;
|
||||||
|
const STREAM_PLAY_RESET_COOLDOWN_MS = 60000;
|
||||||
|
|
||||||
export class RadioStationRuntime {
|
export class RadioStationRuntime {
|
||||||
private readonly sharedRadioSources = new Map<string, SharedRadioSource>();
|
private readonly sharedRadioSources = new Map<string, SharedRadioSource>();
|
||||||
private readonly itemRadioOutputs = new Map<string, ItemRadioOutput>();
|
private readonly itemRadioOutputs = new Map<string, ItemRadioOutput>();
|
||||||
private readonly pendingSharedStarts = new Set<string>();
|
private readonly pendingSharedStarts = new Set<string>();
|
||||||
private readonly nextSharedStartAtMs = new Map<string, number>();
|
private readonly nextSharedStartAtMs = new Map<string, number>();
|
||||||
|
private readonly sharedStartFailureCount = new Map<string, number>();
|
||||||
private layerEnabled = true;
|
private layerEnabled = true;
|
||||||
private listenerPositions: Array<{ x: number; y: number }> = [];
|
private listenerPositions: Array<{ x: number; y: number }> = [];
|
||||||
|
|
||||||
@@ -339,6 +342,7 @@ export class RadioStationRuntime {
|
|||||||
this.sharedRadioSources.delete(streamUrl);
|
this.sharedRadioSources.delete(streamUrl);
|
||||||
this.pendingSharedStarts.delete(streamUrl);
|
this.pendingSharedStarts.delete(streamUrl);
|
||||||
this.nextSharedStartAtMs.delete(streamUrl);
|
this.nextSharedStartAtMs.delete(streamUrl);
|
||||||
|
this.sharedStartFailureCount.delete(streamUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrCreateSharedSource(streamUrl: string): SharedRadioSource | null {
|
private getOrCreateSharedSource(streamUrl: string): SharedRadioSource | null {
|
||||||
@@ -390,8 +394,16 @@ export class RadioStationRuntime {
|
|||||||
.play()
|
.play()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.nextSharedStartAtMs.delete(shared.streamUrl);
|
this.nextSharedStartAtMs.delete(shared.streamUrl);
|
||||||
|
this.sharedStartFailureCount.delete(shared.streamUrl);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
const failures = (this.sharedStartFailureCount.get(shared.streamUrl) ?? 0) + 1;
|
||||||
|
if (failures >= STREAM_PLAY_MAX_RETRIES) {
|
||||||
|
this.sharedStartFailureCount.set(shared.streamUrl, 0);
|
||||||
|
this.nextSharedStartAtMs.set(shared.streamUrl, Date.now() + STREAM_PLAY_RESET_COOLDOWN_MS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sharedStartFailureCount.set(shared.streamUrl, failures);
|
||||||
this.nextSharedStartAtMs.set(shared.streamUrl, Date.now() + STREAM_PLAY_RETRY_MS);
|
this.nextSharedStartAtMs.set(shared.streamUrl, Date.now() + STREAM_PLAY_RETRY_MS);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user