diff --git a/src/media/musicPlayer.ts b/src/media/musicPlayer.ts index 70848d4..ce8e40b 100644 --- a/src/media/musicPlayer.ts +++ b/src/media/musicPlayer.ts @@ -21,16 +21,23 @@ export function createMusicPlayer( return { play(source: ResolvedMediaSource): MusicPlayback { + if (!audioPlayer.isConnected()) { + throw new Error("Discord audio player is not connected"); + } + const proc = spawn("ffmpeg", buildFfmpegArgs(source.source), { stdio: ["ignore", "pipe", "pipe"], }) as unknown as ChildProcessWithoutNullStreams; + proc.stderr.resume(); audioPlayer.playStream(proc.stdout); + let stopped = false; const done = new Promise((resolve, reject) => { proc.on("error", reject); + proc.stdout.on("error", reject); proc.on("close", (code) => { - if (code === 0) { + if (code === 0 || stopped) { resolve(); return; } @@ -41,6 +48,8 @@ export function createMusicPlayer( return { done, stop() { + if (stopped) return; + stopped = true; proc.kill("SIGTERM"); audioPlayer.stop(); }, diff --git a/tests/media/musicPlayer.test.ts b/tests/media/musicPlayer.test.ts index 7bb12bc..dc46936 100644 --- a/tests/media/musicPlayer.test.ts +++ b/tests/media/musicPlayer.test.ts @@ -54,7 +54,22 @@ describe("createMusicPlayer", () => { expect(discordPlayer.playStream).toHaveBeenCalledWith(proc.stdout); }); - it("kills ffmpeg and stops Discord playback", () => { + it("rejects playback when Discord is not connected", () => { + const spawn = vi.fn(() => new FakeProcess()); + const discordPlayer: DiscordAudioPlayer = { + isConnected: () => false, + playStream: vi.fn(), + stop: vi.fn(), + }; + const player = createMusicPlayer({ spawn, discordPlayer }); + + expect(() => + player.play({ source: "/tmp/song.ogg", title: "song.ogg", kind: "local" }), + ).toThrow("Discord audio player is not connected"); + expect(spawn).not.toHaveBeenCalled(); + }); + + it("kills ffmpeg and stops Discord playback once", () => { const proc = new FakeProcess(); const discordPlayer: DiscordAudioPlayer = { isConnected: () => true, @@ -65,8 +80,10 @@ describe("createMusicPlayer", () => { const playback = player.play({ source: "/tmp/song.ogg", title: "song.ogg", kind: "local" }); playback.stop(); + playback.stop(); + expect(proc.kill).toHaveBeenCalledTimes(1); expect(proc.kill).toHaveBeenCalledWith("SIGTERM"); - expect(discordPlayer.stop).toHaveBeenCalled(); + expect(discordPlayer.stop).toHaveBeenCalledTimes(1); }); }); \ No newline at end of file