@@ -207,6 +207,79 @@ describe("FFmpegService", () => {
207207 } ) ;
208208 } ) ;
209209
210+ describe ( "hasAudibleAudio" , ( ) => {
211+ it ( "should resolve false when file has no audio stream" , async ( ) => {
212+ const service = new FFmpegService ( "/mock/ffmpeg" , mockFileSystem , mockProcessSpawner ) ;
213+
214+ const resultPromise = service . hasAudibleAudio ( "/input/video.mp4" ) ;
215+
216+ setImmediate ( ( ) => {
217+ mockChildProcess . stderr ?. emit (
218+ "data" ,
219+ Buffer . from ( "Stream map '0:a:0' matches no streams.\n" ) ,
220+ ) ;
221+ mockChildProcess . emit ( "close" , 1 ) ;
222+ } ) ;
223+
224+ await expect ( resultPromise ) . resolves . toBe ( false ) ;
225+ } ) ;
226+
227+ it ( "should resolve false when max_volume is -inf dB" , async ( ) => {
228+ const service = new FFmpegService ( "/mock/ffmpeg" , mockFileSystem , mockProcessSpawner ) ;
229+
230+ const resultPromise = service . hasAudibleAudio ( "/input/video.mp4" ) ;
231+
232+ setImmediate ( ( ) => {
233+ mockChildProcess . stderr ?. emit ( "data" , Buffer . from ( "max_volume: -inf dB\n" ) ) ;
234+ mockChildProcess . emit ( "close" , 0 ) ;
235+ } ) ;
236+
237+ await expect ( resultPromise ) . resolves . toBe ( false ) ;
238+ } ) ;
239+
240+ it ( "should resolve false when max_volume is below threshold" , async ( ) => {
241+ const service = new FFmpegService ( "/mock/ffmpeg" , mockFileSystem , mockProcessSpawner ) ;
242+
243+ const resultPromise = service . hasAudibleAudio ( "/input/video.mp4" , - 50 ) ;
244+
245+ setImmediate ( ( ) => {
246+ mockChildProcess . stderr ?. emit ( "data" , Buffer . from ( "max_volume: -80.0 dB\n" ) ) ;
247+ mockChildProcess . emit ( "close" , 0 ) ;
248+ } ) ;
249+
250+ await expect ( resultPromise ) . resolves . toBe ( false ) ;
251+ } ) ;
252+
253+ it ( "should resolve true when max_volume meets threshold" , async ( ) => {
254+ const service = new FFmpegService ( "/mock/ffmpeg" , mockFileSystem , mockProcessSpawner ) ;
255+
256+ const resultPromise = service . hasAudibleAudio ( "/input/video.mp4" , - 50 ) ;
257+
258+ setImmediate ( ( ) => {
259+ mockChildProcess . stderr ?. emit ( "data" , Buffer . from ( "max_volume: -10.0 dB\n" ) ) ;
260+ mockChildProcess . emit ( "close" , 0 ) ;
261+ } ) ;
262+
263+ await expect ( resultPromise ) . resolves . toBe ( true ) ;
264+ } ) ;
265+
266+ it ( "should reject when ffmpeg audible-audio check fails for other reasons" , async ( ) => {
267+ const service = new FFmpegService ( "/mock/ffmpeg" , mockFileSystem , mockProcessSpawner ) ;
268+
269+ const resultPromise = service . hasAudibleAudio ( "/input/video.mp4" ) ;
270+
271+ setImmediate ( ( ) => {
272+ mockChildProcess . stderr ?. emit (
273+ "data" ,
274+ Buffer . from ( "Invalid data found when processing input\n" ) ,
275+ ) ;
276+ mockChildProcess . emit ( "close" , 1 ) ;
277+ } ) ;
278+
279+ await expect ( resultPromise ) . rejects . toThrow ( "FFmpeg audible-audio check failed with code 1" ) ;
280+ } ) ;
281+ } ) ;
282+
210283 describe ( "getInstance" , ( ) => {
211284 it ( "should return the same instance (singleton)" , ( ) => {
212285 const instance1 = FFmpegService . getInstance ( ) ;
0 commit comments