From a811feaede27b3c92585be27066646d588d07628 Mon Sep 17 00:00:00 2001 From: Julian Lechner Date: Mon, 22 Jun 2026 12:09:21 +0200 Subject: [PATCH] Handle renewal restart failures --- src/audio-manager.ts | 10 +++++++++- tests/audio-manager.test.ts | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/audio-manager.ts b/src/audio-manager.ts index 9eb97f1..a25b1e3 100644 --- a/src/audio-manager.ts +++ b/src/audio-manager.ts @@ -227,7 +227,15 @@ export default class AudioManager { } this.renewTimer = setTimeout(() => { - void this.start(); + void this.start().catch(() => { + this.clearRenewTimer(); + this.stopCurrentPlayback(); + this.audioPlayer.stop(true); + this.connection?.disconnect(); + this.connection?.destroy(); + this.connection = undefined; + this.playbackState = 'stopped'; + }); }, renewIntervalMs); if (typeof this.renewTimer.unref === 'function') { diff --git a/tests/audio-manager.test.ts b/tests/audio-manager.test.ts index 5d3d1d8..0d185e2 100644 --- a/tests/audio-manager.test.ts +++ b/tests/audio-manager.test.ts @@ -298,6 +298,27 @@ describe('AudioManager', () => { expect(startSpy).toHaveBeenCalledTimes(1); }); + it('cleans up when renewal restart fails', async () => { + jest.useFakeTimers(); + + const manager = new AudioManager({ + connection: connectionOptions, + source: liveStreamSource, + renewIntervalMs: 10_000, + }); + const startSpy = jest.spyOn(manager, 'start').mockRejectedValue(new Error('renewal failed')); + + await manager.connect(); + jest.advanceTimersByTime(10_000); + await Promise.resolve(); + + expect(startSpy).toHaveBeenCalledTimes(1); + expect(mockAudioPlayer.stop).toHaveBeenCalledWith(true); + expect(mockConnection.destroy).toHaveBeenCalled(); + expect(manager.state).toBe('stopped'); + expect(jest.getTimerCount()).toBe(0); + }); + it('clears existing renewal timer before reconnecting', async () => { jest.useFakeTimers();