diff --git a/src/helpers/audio.ts b/src/helpers/audio.ts index 2a6aad3c4..5b9ae5150 100644 --- a/src/helpers/audio.ts +++ b/src/helpers/audio.ts @@ -36,6 +36,7 @@ async function nodejsPlayAudio(stream: NodeJS.ReadableStream | Response | File): return new Promise((resolve, reject) => { try { const ffplay = spawn('ffplay', ['-autoexit', '-nodisp', '-i', 'pipe:0']); + ffplay.on('error', reject); let source: NodeJS.ReadableStream; if (isResponse(stream)) { diff --git a/tests/helpers/audio.test.ts b/tests/helpers/audio.test.ts index 6c2a3be87..c0401fdb9 100644 --- a/tests/helpers/audio.test.ts +++ b/tests/helpers/audio.test.ts @@ -1,6 +1,7 @@ jest.mock('node:child_process', () => ({ spawn: jest.fn() })); import { spawn } from 'node:child_process'; +import { EventEmitter } from 'node:events'; import { Readable, Writable } from 'node:stream'; import { playAudio } from 'openai/helpers/audio'; @@ -72,4 +73,22 @@ describe('playAudio', () => { expect(ffplay.kill).toHaveBeenCalled(); }); + + it('rejects when ffplay cannot be spawned', async () => { + const ffplay = Object.assign(new EventEmitter(), { + stdin: new Writable({ + write(_chunk, _encoding, callback) { + callback(); + }, + }), + kill: jest.fn(), + }); + spawnMock.mockReturnValue(ffplay as any); + + const promise = playAudio(Readable.from(['hello'])); + const error = new Error('spawn ffplay ENOENT'); + + expect(() => ffplay.emit('error', error)).not.toThrow(); + await expect(promise).rejects.toBe(error); + }); });