@@ -422,3 +422,136 @@ def test_api_smoke_test(self, test_files):
422422
423423 # Test dry run (no actual processing)
424424 normalizer .run_normalization () # Should complete quickly with dry_run=True
425+
426+ @pytest .mark .slow
427+ def test_ebu_lower_only_skips_quiet_files (self , test_files , temp_output_dir ):
428+ """Test that EBU normalization with --lower-only skips files already below target."""
429+ # Use a very high target that the input file will be below
430+ target_level = - 5.0 # Very high target - most audio will be below this
431+ test_file = test_files [0 ]
432+
433+ output_file = temp_output_dir / f"lower_only_ebu_{ test_file .name } "
434+
435+ # Create normalizer with lower_only enabled
436+ normalizer = FFmpegNormalize (
437+ normalization_type = "ebu" ,
438+ target_level = target_level ,
439+ lower_only = True ,
440+ print_stats = False ,
441+ audio_codec = "aac" ,
442+ )
443+
444+ normalizer .add_media_file (str (test_file ), str (output_file ))
445+ normalizer .run_normalization ()
446+
447+ # Get the first pass statistics to check if the input was below target
448+ stats = list (normalizer .media_files [0 ].get_stats ())[0 ]
449+ input_i = stats ["ebu_pass1" ]["input_i" ] if stats ["ebu_pass1" ] else None
450+
451+ # Verify that the file was processed and output file exists
452+ assert output_file .exists (), (
453+ "Output file should be created even when normalization is skipped"
454+ )
455+
456+ # If input was below target, normalization should have been skipped
457+ # (ebu_pass2 stats won't be available because acopy was used instead of loudnorm)
458+ if input_i is not None and input_i < target_level :
459+ # When normalization is skipped, second pass stats might not be available
460+ # because no loudnorm filter was run (acopy was used instead)
461+ # This is expected behavior with --lower-only
462+ pass
463+ else :
464+ # If input was above target, normalization should have occurred
465+ assert stats ["ebu_pass2" ] is not None , (
466+ "Second pass stats should be available when normalization occurs"
467+ )
468+
469+ @pytest .mark .slow
470+ def test_peak_lower_only_skips_quiet_files (self , test_files , temp_output_dir ):
471+ """Test that peak normalization with --lower-only skips files already below target."""
472+ # Use a very high target that the input file will be below
473+ target_level = - 1.0 # Very high target - most audio will be below this
474+ test_file = test_files [0 ]
475+
476+ output_file = temp_output_dir / f"lower_only_peak_{ test_file .name } "
477+
478+ # Create normalizer with lower_only enabled
479+ normalizer = FFmpegNormalize (
480+ normalization_type = "peak" ,
481+ target_level = target_level ,
482+ lower_only = True ,
483+ print_stats = False ,
484+ audio_codec = "aac" ,
485+ )
486+
487+ normalizer .add_media_file (str (test_file ), str (output_file ))
488+
489+ # Get first pass statistics (before normalization)
490+ media_file = normalizer .media_files [0 ]
491+ # Run first pass to get stats
492+ media_file ._first_pass ()
493+ stats_before = list (media_file .get_stats ())[0 ]
494+ input_peak_before = stats_before ["max" ]
495+
496+ # Now run the full normalization
497+ normalizer .run_normalization ()
498+
499+ # Verify that the file was processed and output file exists
500+ assert output_file .exists (), (
501+ "Output file should be created even when normalization is skipped"
502+ )
503+
504+ # Verify behavior: if input was below target, it should have been skipped
505+ assert input_peak_before is not None
506+ if input_peak_before < target_level :
507+ # Normalization should have been skipped
508+ # The output file should exist but audio should not be lifted to target
509+ pass
510+ else :
511+ # If input was above target, normalization should have occurred
512+ pass
513+
514+ @pytest .mark .slow
515+ def test_rms_lower_only_skips_quiet_files (self , test_files , temp_output_dir ):
516+ """Test that RMS normalization with --lower-only skips files already below target."""
517+ # Use a very high target that the input file will be below
518+ target_level = - 10.0 # Very high target - most audio will be below this
519+ test_file = test_files [0 ]
520+
521+ output_file = temp_output_dir / f"lower_only_rms_{ test_file .name } "
522+
523+ # Create normalizer with lower_only enabled
524+ normalizer = FFmpegNormalize (
525+ normalization_type = "rms" ,
526+ target_level = target_level ,
527+ lower_only = True ,
528+ print_stats = False ,
529+ audio_codec = "aac" ,
530+ )
531+
532+ normalizer .add_media_file (str (test_file ), str (output_file ))
533+
534+ # Get first pass statistics (before normalization)
535+ media_file = normalizer .media_files [0 ]
536+ # Run first pass to get stats
537+ media_file ._first_pass ()
538+ stats_before = list (media_file .get_stats ())[0 ]
539+ input_rms_before = stats_before ["mean" ]
540+
541+ # Now run the full normalization
542+ normalizer .run_normalization ()
543+
544+ # Verify that the file was processed and output file exists
545+ assert output_file .exists (), (
546+ "Output file should be created even when normalization is skipped"
547+ )
548+
549+ # Verify behavior: if input was below target, it should have been skipped
550+ assert input_rms_before is not None
551+ if input_rms_before < target_level :
552+ # Normalization should have been skipped
553+ # The output file should exist but audio should not be lifted to target
554+ pass
555+ else :
556+ # If input was above target, normalization should have occurred
557+ pass
0 commit comments