audiometa-python 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audiometa/__init__.py +1297 -0
- audiometa/__main__.py +6 -0
- audiometa/_audio_file.py +607 -0
- audiometa/cli.py +476 -0
- audiometa/exceptions.py +167 -0
- audiometa/manager/_MetadataManager.py +768 -0
- audiometa/manager/__init__.py +1 -0
- audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +250 -0
- audiometa/manager/_rating_supporting/__init__.py +1 -0
- audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +1032 -0
- audiometa/manager/_rating_supporting/id3v2/__init__.py +25 -0
- audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +11 -0
- audiometa/manager/_rating_supporting/riff/_RiffManager.py +1002 -0
- audiometa/manager/_rating_supporting/riff/__init__.py +25 -0
- audiometa/manager/_rating_supporting/riff/_riff_constants.py +17 -0
- audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +542 -0
- audiometa/manager/_rating_supporting/vorbis/__init__.py +17 -0
- audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +6 -0
- audiometa/manager/id3v1/_Id3v1Manager.py +512 -0
- audiometa/manager/id3v1/__init__.py +1 -0
- audiometa/manager/id3v1/_constants.py +8 -0
- audiometa/manager/id3v1/id3v1_raw_metadata.py +242 -0
- audiometa/manager/id3v1/id3v1_raw_metadata_key.py +13 -0
- audiometa/test/__init__.py +1 -0
- audiometa/test/assets/create_test_files.py +72 -0
- audiometa/test/helpers/__init__.py +51 -0
- audiometa/test/helpers/common/__init__.py +6 -0
- audiometa/test/helpers/common/audio_file_creator.py +68 -0
- audiometa/test/helpers/common/external_tool_runner.py +74 -0
- audiometa/test/helpers/id3v1/__init__.py +8 -0
- audiometa/test/helpers/id3v1/id3v1_header_verifier.py +18 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +37 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +61 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +82 -0
- audiometa/test/helpers/id3v2/__init__.py +28 -0
- audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +349 -0
- audiometa/test/helpers/id3v2/id3v2_header_verifier.py +38 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +56 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +189 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +506 -0
- audiometa/test/helpers/riff/__init__.py +8 -0
- audiometa/test/helpers/riff/riff_header_verifier.py +85 -0
- audiometa/test/helpers/riff/riff_manual_metadata_creator.py +298 -0
- audiometa/test/helpers/riff/riff_metadata_deleter.py +56 -0
- audiometa/test/helpers/riff/riff_metadata_getter.py +219 -0
- audiometa/test/helpers/riff/riff_metadata_setter.py +374 -0
- audiometa/test/helpers/scripts/__init__.py +0 -0
- audiometa/test/helpers/technical_info_inspector.py +115 -0
- audiometa/test/helpers/temp_file_with_metadata.py +82 -0
- audiometa/test/helpers/vorbis/__init__.py +8 -0
- audiometa/test/helpers/vorbis/vorbis_header_verifier.py +31 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +49 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +67 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +221 -0
- audiometa/test/tests/__init__.py +0 -0
- audiometa/test/tests/conftest.py +276 -0
- audiometa/test/tests/e2e/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/error_handling/__init__.py +1 -0
- audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +77 -0
- audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +130 -0
- audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +118 -0
- audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +172 -0
- audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +49 -0
- audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +160 -0
- audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +90 -0
- audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +51 -0
- audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/read/test_basic.py +58 -0
- audiometa/test/tests/e2e/cli/read/test_comprehensive.py +240 -0
- audiometa/test/tests/e2e/cli/read/test_formats.py +55 -0
- audiometa/test/tests/e2e/cli/read/test_metadata_content.py +164 -0
- audiometa/test/tests/e2e/cli/read/test_multiple_files.py +149 -0
- audiometa/test/tests/e2e/cli/read/test_options.py +88 -0
- audiometa/test/tests/e2e/cli/read/test_unified.py +84 -0
- audiometa/test/tests/e2e/cli/test_delete.py +20 -0
- audiometa/test/tests/e2e/cli/test_formatting.py +31 -0
- audiometa/test/tests/e2e/cli/test_help.py +41 -0
- audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/write/test_basic.py +51 -0
- audiometa/test/tests/e2e/cli/write/test_comprehensive.py +210 -0
- audiometa/test/tests/e2e/cli/write/test_force_format.py +336 -0
- audiometa/test/tests/e2e/cli/write/test_integer_fields.py +145 -0
- audiometa/test/tests/e2e/cli/write/test_list_fields.py +107 -0
- audiometa/test/tests/e2e/cli/write/test_rating.py +74 -0
- audiometa/test/tests/e2e/cli/write/test_string_fields.py +54 -0
- audiometa/test/tests/e2e/cli/write/test_validation.py +85 -0
- audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
- audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +166 -0
- audiometa/test/tests/e2e/workflows/__init__.py +0 -0
- audiometa/test/tests/e2e/workflows/test_core_workflows.py +166 -0
- audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +318 -0
- audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +165 -0
- audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +129 -0
- audiometa/test/tests/e2e/workflows/test_rating_workflows.py +124 -0
- audiometa/test/tests/integration/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +108 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +61 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +65 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +69 -0
- audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +79 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +61 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +67 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +60 -0
- audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +87 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +62 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +57 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_with_id3v2_tags.py +83 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +62 -0
- audiometa/test/tests/integration/conftest.py +29 -0
- audiometa/test/tests/integration/delete_all_metadata/__init__.py +1 -0
- audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +102 -0
- audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +77 -0
- audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +47 -0
- audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +24 -0
- audiometa/test/tests/integration/encoding/__init__.py +1 -0
- audiometa/test/tests/integration/encoding/test_encoding.py +88 -0
- audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +223 -0
- audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
- audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +122 -0
- audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +250 -0
- audiometa/test/tests/integration/get_full_metadata/test_consistency.py +67 -0
- audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +123 -0
- audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +40 -0
- audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +43 -0
- audiometa/test/tests/integration/get_full_metadata/test_options.py +207 -0
- audiometa/test/tests/integration/get_full_metadata/test_performance.py +95 -0
- audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +128 -0
- audiometa/test/tests/integration/get_full_metadata/test_structure.py +161 -0
- audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album/test_deleting.py +73 -0
- audiometa/test/tests/integration/metadata_field/album/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/album/test_writing.py +50 -0
- audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +83 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +38 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +52 -0
- audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +68 -0
- audiometa/test/tests/integration/metadata_field/artists/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/artists/test_writing.py +46 -0
- audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +32 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +56 -0
- audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +68 -0
- audiometa/test/tests/integration/metadata_field/comment/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/comment/test_writing.py +49 -0
- audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/composer/test_reading.py +34 -0
- audiometa/test/tests/integration/metadata_field/composer/test_writing.py +41 -0
- audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +81 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +35 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +41 -0
- audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +97 -0
- audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +92 -0
- audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +153 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +56 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +54 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +61 -0
- audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +65 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +25 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +58 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +61 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +191 -0
- audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +62 -0
- audiometa/test/tests/integration/metadata_field/genre/test_writing.py +64 -0
- audiometa/test/tests/integration/metadata_field/isrc/__init__.py +1 -0
- audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +31 -0
- audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +35 -0
- audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +165 -0
- audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/language/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/language/test_reading.py +39 -0
- audiometa/test/tests/integration/metadata_field/language/test_writing.py +43 -0
- audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +129 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +57 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +59 -0
- audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +88 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +32 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +47 -0
- audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +81 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +33 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +58 -0
- audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +117 -0
- audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +137 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +77 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +55 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +57 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +192 -0
- audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +74 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +82 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +59 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +49 -0
- audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +135 -0
- audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/title/test_deleting.py +73 -0
- audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +47 -0
- audiometa/test/tests/integration/metadata_field/title/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/title/test_writing.py +64 -0
- audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +43 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +32 -0
- audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +59 -0
- audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +73 -0
- audiometa/test/tests/integration/multiple_values/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +23 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +92 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +216 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +84 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +169 -0
- audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +209 -0
- audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +198 -0
- audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +24 -0
- audiometa/test/tests/integration/multiple_values/writing/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +62 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +36 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +34 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +32 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +54 -0
- audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +42 -0
- audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +98 -0
- audiometa/test/tests/integration/reading/__init__.py +1 -0
- audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +80 -0
- audiometa/test/tests/integration/reading/test_reading_error_handling.py +36 -0
- audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
- audiometa/test/tests/integration/real_audio_files/test_reading.py +146 -0
- audiometa/test/tests/integration/real_audio_files/test_writing.py +198 -0
- audiometa/test/tests/integration/technical_info/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +103 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_audio_data_corruption.py +21 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_flipped_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_non_flac_error.py +13 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_partial_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_random_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_unset_md5.py +56 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_valid_md5.py +21 -0
- audiometa/test/tests/integration/technical_info/test_bitrate.py +79 -0
- audiometa/test/tests/integration/technical_info/test_channels.py +38 -0
- audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +38 -0
- audiometa/test/tests/integration/technical_info/test_sample_rate.py +40 -0
- audiometa/test/tests/integration/test_audio_file.py +35 -0
- audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +95 -0
- audiometa/test/tests/integration/writing/__init__.py +0 -0
- audiometa/test/tests/integration/writing/test_error_handling.py +44 -0
- audiometa/test/tests/integration/writing/test_forced_format.py +224 -0
- audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +223 -0
- audiometa/test/tests/integration/writing/test_partial_update.py +36 -0
- audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +79 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +76 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_sync_strategy.py +215 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +42 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +93 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +99 -0
- audiometa/test/tests/unit/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +26 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +31 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +38 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +190 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +51 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +28 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +31 -0
- audiometa/test/tests/unit/audio_file/test_context_manager.py +30 -0
- audiometa/test/tests/unit/audio_file/test_file_validation.py +40 -0
- audiometa/test/tests/unit/audio_file/test_is_audio_file.py +49 -0
- audiometa/test/tests/unit/audio_file/test_operations.py +20 -0
- audiometa/test/tests/unit/audio_file/test_path_handling.py +23 -0
- audiometa/test/tests/unit/cli/__init__.py +0 -0
- audiometa/test/tests/unit/cli/test_expand_file_patterns.py +234 -0
- audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/conftest.py +142 -0
- audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +49 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +66 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +343 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +53 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +186 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +142 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +76 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +152 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +23 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +77 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +43 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +151 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +61 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_date_format_validation.py +135 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_disc_number_validation.py +75 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_format_validation.py +121 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_type_validation.py +30 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_track_number_validation.py +46 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +22 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +83 -0
- audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +74 -0
- audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +26 -0
- audiometa/utils/__init__.py +1 -0
- audiometa/utils/id3v1_genre_code_map.py +205 -0
- audiometa/utils/metadata_format.py +31 -0
- audiometa/utils/metadata_writing_strategy.py +16 -0
- audiometa/utils/mutagen_exception_handler.py +24 -0
- audiometa/utils/os_dependencies_checker/__init__.py +24 -0
- audiometa/utils/os_dependencies_checker/base.py +62 -0
- audiometa/utils/os_dependencies_checker/config.py +77 -0
- audiometa/utils/os_dependencies_checker/macos.py +236 -0
- audiometa/utils/os_dependencies_checker/ubuntu.py +95 -0
- audiometa/utils/os_dependencies_checker/windows.py +227 -0
- audiometa/utils/rating_profiles.py +110 -0
- audiometa/utils/tool_path_resolver.py +135 -0
- audiometa/utils/types.py +82 -0
- audiometa/utils/unified_metadata_key.py +87 -0
- audiometa_python-0.6.0.dist-info/METADATA +1593 -0
- audiometa_python-0.6.0.dist-info/RECORD +352 -0
- audiometa_python-0.6.0.dist-info/WHEEL +5 -0
- audiometa_python-0.6.0.dist-info/entry_points.txt +2 -0
- audiometa_python-0.6.0.dist-info/licenses/LICENSE +202 -0
- audiometa_python-0.6.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""Unit tests for RIFF metadata manager header information methods."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa._audio_file import _AudioFile
|
|
8
|
+
from audiometa.manager._rating_supporting.riff._RiffManager import _RiffManager as RiffManager
|
|
9
|
+
from audiometa.test.helpers.riff.riff_metadata_getter import RIFFMetadataGetter
|
|
10
|
+
from audiometa.test.helpers.riff.riff_metadata_setter import RIFFMetadataSetter
|
|
11
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.unit
|
|
15
|
+
class TestRiffHeaderMethods:
|
|
16
|
+
"""Test cases for RIFF metadata manager header information methods."""
|
|
17
|
+
|
|
18
|
+
def test_riff_manager_header_info(self, sample_wav_file: Path):
|
|
19
|
+
"""Test RiffManager header info method."""
|
|
20
|
+
audio_file = _AudioFile(sample_wav_file)
|
|
21
|
+
manager = RiffManager(audio_file)
|
|
22
|
+
|
|
23
|
+
header_info = manager.get_header_info()
|
|
24
|
+
|
|
25
|
+
# Should have RIFF specific structure
|
|
26
|
+
assert "present" in header_info
|
|
27
|
+
assert "chunk_info" in header_info
|
|
28
|
+
|
|
29
|
+
# Should be valid structure
|
|
30
|
+
assert isinstance(header_info["present"], bool)
|
|
31
|
+
assert isinstance(header_info["chunk_info"], dict)
|
|
32
|
+
|
|
33
|
+
# Chunk info should have expected keys
|
|
34
|
+
chunk_info = header_info["chunk_info"]
|
|
35
|
+
if header_info["present"]:
|
|
36
|
+
assert "riff_chunk_size" in chunk_info
|
|
37
|
+
assert "info_chunk_size" in chunk_info
|
|
38
|
+
assert "audio_format" in chunk_info
|
|
39
|
+
assert "subchunk_size" in chunk_info
|
|
40
|
+
|
|
41
|
+
def test_riff_manager_raw_metadata_info(self, sample_wav_file: Path):
|
|
42
|
+
"""Test RiffManager raw metadata info method."""
|
|
43
|
+
audio_file = _AudioFile(sample_wav_file)
|
|
44
|
+
manager = RiffManager(audio_file)
|
|
45
|
+
|
|
46
|
+
raw_info = manager.get_raw_metadata_info()
|
|
47
|
+
|
|
48
|
+
# Should have RIFF specific structure
|
|
49
|
+
assert "raw_data" in raw_info
|
|
50
|
+
assert "parsed_fields" in raw_info
|
|
51
|
+
assert "frames" in raw_info
|
|
52
|
+
assert "comments" in raw_info
|
|
53
|
+
assert "chunk_structure" in raw_info
|
|
54
|
+
|
|
55
|
+
# Should be valid structure
|
|
56
|
+
assert raw_info["raw_data"] is None or isinstance(raw_info["raw_data"], bytes)
|
|
57
|
+
assert isinstance(raw_info["parsed_fields"], dict)
|
|
58
|
+
assert isinstance(raw_info["frames"], dict)
|
|
59
|
+
assert isinstance(raw_info["comments"], dict)
|
|
60
|
+
assert isinstance(raw_info["chunk_structure"], dict)
|
|
61
|
+
|
|
62
|
+
def test_riff_manager_bext_chunk_extraction_with_description(self):
|
|
63
|
+
"""Test RIFF manager bext chunk extraction with Description field."""
|
|
64
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
65
|
+
RIFFMetadataSetter.set_bext_description(test_file, "Test Description")
|
|
66
|
+
|
|
67
|
+
# Verify using external tool
|
|
68
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
69
|
+
assert external_bext.get("Description") == "Test Description"
|
|
70
|
+
|
|
71
|
+
# Verify using our extraction
|
|
72
|
+
audio_file = _AudioFile(test_file)
|
|
73
|
+
manager = RiffManager(audio_file)
|
|
74
|
+
raw_info = manager.get_raw_metadata_info()
|
|
75
|
+
|
|
76
|
+
assert "chunk_structure" in raw_info
|
|
77
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
78
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
79
|
+
assert bext_data["Description"] == "Test Description"
|
|
80
|
+
# Verify our extraction matches external tool
|
|
81
|
+
assert bext_data["Description"] == external_bext.get("Description")
|
|
82
|
+
|
|
83
|
+
def test_riff_manager_bext_chunk_extraction_with_originator(self):
|
|
84
|
+
"""Test RIFF manager bext chunk extraction with Originator field."""
|
|
85
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
86
|
+
RIFFMetadataSetter.set_bext_originator(test_file, "Test Originator")
|
|
87
|
+
|
|
88
|
+
# Verify using external tool
|
|
89
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
90
|
+
assert external_bext.get("Originator") == "Test Originator"
|
|
91
|
+
|
|
92
|
+
# Verify using our extraction
|
|
93
|
+
audio_file = _AudioFile(test_file)
|
|
94
|
+
manager = RiffManager(audio_file)
|
|
95
|
+
raw_info = manager.get_raw_metadata_info()
|
|
96
|
+
|
|
97
|
+
assert "chunk_structure" in raw_info
|
|
98
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
99
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
100
|
+
assert bext_data["Originator"] == "Test Originator"
|
|
101
|
+
# Verify our extraction matches external tool
|
|
102
|
+
assert bext_data["Originator"] == external_bext.get("Originator")
|
|
103
|
+
|
|
104
|
+
def test_riff_manager_bext_chunk_extraction_with_originator_reference(self):
|
|
105
|
+
"""Test RIFF manager bext chunk extraction with OriginatorReference field."""
|
|
106
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
107
|
+
RIFFMetadataSetter.set_bext_originator_reference(test_file, "REF-12345")
|
|
108
|
+
|
|
109
|
+
# Verify using external tool
|
|
110
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
111
|
+
assert external_bext.get("OriginatorReference") == "REF-12345"
|
|
112
|
+
|
|
113
|
+
# Verify using our extraction
|
|
114
|
+
audio_file = _AudioFile(test_file)
|
|
115
|
+
manager = RiffManager(audio_file)
|
|
116
|
+
raw_info = manager.get_raw_metadata_info()
|
|
117
|
+
|
|
118
|
+
assert "chunk_structure" in raw_info
|
|
119
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
120
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
121
|
+
assert bext_data["OriginatorReference"] == "REF-12345"
|
|
122
|
+
assert bext_data["OriginatorReference"] == external_bext.get("OriginatorReference")
|
|
123
|
+
|
|
124
|
+
def test_riff_manager_bext_chunk_extraction_with_origination_date(self):
|
|
125
|
+
"""Test RIFF manager bext chunk extraction with OriginationDate field."""
|
|
126
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
127
|
+
RIFFMetadataSetter.set_bext_origination_date(test_file, "2024-01-15")
|
|
128
|
+
|
|
129
|
+
# Verify using external tool
|
|
130
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
131
|
+
assert external_bext.get("OriginationDate") == "2024-01-15"
|
|
132
|
+
|
|
133
|
+
# Verify using our extraction
|
|
134
|
+
audio_file = _AudioFile(test_file)
|
|
135
|
+
manager = RiffManager(audio_file)
|
|
136
|
+
raw_info = manager.get_raw_metadata_info()
|
|
137
|
+
|
|
138
|
+
assert "chunk_structure" in raw_info
|
|
139
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
140
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
141
|
+
assert bext_data["OriginationDate"] == "2024-01-15"
|
|
142
|
+
assert bext_data["OriginationDate"] == external_bext.get("OriginationDate")
|
|
143
|
+
|
|
144
|
+
def test_riff_manager_bext_chunk_extraction_with_origination_time(self):
|
|
145
|
+
"""Test RIFF manager bext chunk extraction with OriginationTime field."""
|
|
146
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
147
|
+
RIFFMetadataSetter.set_bext_origination_time(test_file, "14:30:00")
|
|
148
|
+
|
|
149
|
+
# Verify using external tool
|
|
150
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
151
|
+
assert external_bext.get("OriginationTime") == "14:30:00"
|
|
152
|
+
|
|
153
|
+
# Verify using our extraction
|
|
154
|
+
audio_file = _AudioFile(test_file)
|
|
155
|
+
manager = RiffManager(audio_file)
|
|
156
|
+
raw_info = manager.get_raw_metadata_info()
|
|
157
|
+
|
|
158
|
+
assert "chunk_structure" in raw_info
|
|
159
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
160
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
161
|
+
assert bext_data["OriginationTime"] == "14:30:00"
|
|
162
|
+
assert bext_data["OriginationTime"] == external_bext.get("OriginationTime")
|
|
163
|
+
|
|
164
|
+
def test_riff_manager_bext_chunk_extraction_with_time_reference(self):
|
|
165
|
+
"""Test RIFF manager bext chunk extraction with TimeReference field."""
|
|
166
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
167
|
+
RIFFMetadataSetter.set_bext_time_reference(test_file, 44100)
|
|
168
|
+
|
|
169
|
+
# Verify using external tool
|
|
170
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
171
|
+
assert external_bext.get("TimeReference") == 44100
|
|
172
|
+
|
|
173
|
+
# Verify using our extraction
|
|
174
|
+
audio_file = _AudioFile(test_file)
|
|
175
|
+
manager = RiffManager(audio_file)
|
|
176
|
+
raw_info = manager.get_raw_metadata_info()
|
|
177
|
+
|
|
178
|
+
assert "chunk_structure" in raw_info
|
|
179
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
180
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
181
|
+
assert bext_data["TimeReference"] == 44100
|
|
182
|
+
assert bext_data["TimeReference"] == external_bext.get("TimeReference")
|
|
183
|
+
|
|
184
|
+
def test_riff_manager_bext_chunk_extraction_with_coding_history(self):
|
|
185
|
+
"""Test RIFF manager bext chunk extraction with CodingHistory field."""
|
|
186
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
187
|
+
RIFFMetadataSetter.set_bext_coding_history(test_file, "A=PCM,F=44100,W=16,M=mono,T=PCM")
|
|
188
|
+
|
|
189
|
+
# Verify using external tool
|
|
190
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
191
|
+
assert external_bext.get("CodingHistory") == "A=PCM,F=44100,W=16,M=mono,T=PCM"
|
|
192
|
+
|
|
193
|
+
# Verify using our extraction
|
|
194
|
+
audio_file = _AudioFile(test_file)
|
|
195
|
+
manager = RiffManager(audio_file)
|
|
196
|
+
raw_info = manager.get_raw_metadata_info()
|
|
197
|
+
|
|
198
|
+
assert "chunk_structure" in raw_info
|
|
199
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
200
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
201
|
+
assert bext_data["CodingHistory"] == "A=PCM,F=44100,W=16,M=mono,T=PCM"
|
|
202
|
+
assert bext_data["CodingHistory"] == external_bext.get("CodingHistory")
|
|
203
|
+
|
|
204
|
+
def test_riff_manager_bext_chunk_extraction_with_multiple_fields(self):
|
|
205
|
+
"""Test RIFF manager bext chunk extraction with multiple fields."""
|
|
206
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
207
|
+
RIFFMetadataSetter.set_bext_metadata(
|
|
208
|
+
test_file,
|
|
209
|
+
{
|
|
210
|
+
"Description": "Test Description",
|
|
211
|
+
"Originator": "Test Originator",
|
|
212
|
+
"OriginatorReference": "REF-12345",
|
|
213
|
+
"OriginationDate": "2024-01-15",
|
|
214
|
+
"OriginationTime": "14:30:00",
|
|
215
|
+
"TimeReference": 44100,
|
|
216
|
+
"CodingHistory": "A=PCM,F=44100,W=16,M=mono,T=PCM",
|
|
217
|
+
},
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Verify using external tool
|
|
221
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
222
|
+
|
|
223
|
+
# Verify using our extraction
|
|
224
|
+
audio_file = _AudioFile(test_file)
|
|
225
|
+
manager = RiffManager(audio_file)
|
|
226
|
+
raw_info = manager.get_raw_metadata_info()
|
|
227
|
+
|
|
228
|
+
assert "chunk_structure" in raw_info
|
|
229
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
230
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
231
|
+
|
|
232
|
+
# Verify each field matches external tool
|
|
233
|
+
assert bext_data["Description"] == "Test Description"
|
|
234
|
+
assert bext_data["Description"] == external_bext.get("Description")
|
|
235
|
+
assert bext_data["Originator"] == "Test Originator"
|
|
236
|
+
assert bext_data["Originator"] == external_bext.get("Originator")
|
|
237
|
+
assert bext_data["OriginatorReference"] == "REF-12345"
|
|
238
|
+
assert bext_data["OriginatorReference"] == external_bext.get("OriginatorReference")
|
|
239
|
+
assert bext_data["OriginationDate"] == "2024-01-15"
|
|
240
|
+
assert bext_data["OriginationDate"] == external_bext.get("OriginationDate")
|
|
241
|
+
assert bext_data["OriginationTime"] == "14:30:00"
|
|
242
|
+
assert bext_data["OriginationTime"] == external_bext.get("OriginationTime")
|
|
243
|
+
assert bext_data["TimeReference"] == 44100
|
|
244
|
+
assert bext_data["TimeReference"] == external_bext.get("TimeReference")
|
|
245
|
+
assert bext_data["CodingHistory"] == "A=PCM,F=44100,W=16,M=mono,T=PCM"
|
|
246
|
+
assert bext_data["CodingHistory"] == external_bext.get("CodingHistory")
|
|
247
|
+
|
|
248
|
+
def test_riff_manager_bext_chunk_extraction_without_bext_chunk(self, sample_wav_file: Path):
|
|
249
|
+
"""Test that regular WAV files without bext chunk return empty chunk_structure."""
|
|
250
|
+
audio_file = _AudioFile(sample_wav_file)
|
|
251
|
+
manager = RiffManager(audio_file)
|
|
252
|
+
raw_info = manager.get_raw_metadata_info()
|
|
253
|
+
|
|
254
|
+
assert "chunk_structure" in raw_info
|
|
255
|
+
# Regular WAV files without bext chunk should not have bext in chunk_structure
|
|
256
|
+
assert "bext" not in raw_info["chunk_structure"]
|
|
257
|
+
|
|
258
|
+
def test_riff_manager_bext_chunk_extraction_without_info_metadata(self):
|
|
259
|
+
"""Test that bext chunk is extracted even when no user-defined RIFF INFO metadata is present.
|
|
260
|
+
|
|
261
|
+
This tests the code path at lines 899-901 in _RiffManager.py that ensures bext chunk
|
|
262
|
+
extraction happens even when raw_clean_metadata is empty (no INFO metadata).
|
|
263
|
+
"""
|
|
264
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
265
|
+
# Add bext metadata but no user-defined INFO metadata
|
|
266
|
+
RIFFMetadataSetter.set_bext_description(test_file, "Test Description")
|
|
267
|
+
RIFFMetadataSetter.set_bext_originator(test_file, "Test Originator")
|
|
268
|
+
|
|
269
|
+
# Verify using external tool
|
|
270
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
271
|
+
assert external_bext.get("Description") == "Test Description"
|
|
272
|
+
assert external_bext.get("Originator") == "Test Originator"
|
|
273
|
+
|
|
274
|
+
# Verify using our extraction
|
|
275
|
+
audio_file = _AudioFile(test_file)
|
|
276
|
+
manager = RiffManager(audio_file)
|
|
277
|
+
raw_info = manager.get_raw_metadata_info()
|
|
278
|
+
|
|
279
|
+
# Should still have bext chunk in chunk_structure regardless of INFO metadata
|
|
280
|
+
assert "chunk_structure" in raw_info
|
|
281
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
282
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
283
|
+
assert bext_data["Description"] == "Test Description"
|
|
284
|
+
assert bext_data["Originator"] == "Test Originator"
|
|
285
|
+
# Verify our extraction matches external tool
|
|
286
|
+
assert bext_data["Description"] == external_bext.get("Description")
|
|
287
|
+
assert bext_data["Originator"] == external_bext.get("Originator")
|
|
288
|
+
|
|
289
|
+
def test_riff_manager_bext_chunk_extraction_with_loudness_metadata(self):
|
|
290
|
+
"""Test RIFF manager bext chunk extraction with BWF v2 loudness metadata fields."""
|
|
291
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
292
|
+
# Set bext metadata including loudness fields (requires BWF v2)
|
|
293
|
+
RIFFMetadataSetter.set_bext_metadata(
|
|
294
|
+
test_file,
|
|
295
|
+
{
|
|
296
|
+
"Description": "Test Description",
|
|
297
|
+
"LoudnessValue": -23.0,
|
|
298
|
+
"LoudnessRange": 7.0,
|
|
299
|
+
"MaxTruePeakLevel": -1.5,
|
|
300
|
+
"MaxMomentaryLoudness": -22.0,
|
|
301
|
+
"MaxShortTermLoudness": -22.5,
|
|
302
|
+
},
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Verify using external tool
|
|
306
|
+
external_bext = RIFFMetadataGetter.get_bext_metadata(test_file)
|
|
307
|
+
assert external_bext.get("Description") == "Test Description"
|
|
308
|
+
assert external_bext.get("LoudnessValue") == pytest.approx(-23.0, abs=0.1)
|
|
309
|
+
assert external_bext.get("LoudnessRange") == pytest.approx(7.0, abs=0.1)
|
|
310
|
+
assert external_bext.get("MaxTruePeakLevel") == pytest.approx(-1.5, abs=0.1)
|
|
311
|
+
assert external_bext.get("MaxMomentaryLoudness") == pytest.approx(-22.0, abs=0.1)
|
|
312
|
+
assert external_bext.get("MaxShortTermLoudness") == pytest.approx(-22.5, abs=0.1)
|
|
313
|
+
|
|
314
|
+
# Verify using our extraction
|
|
315
|
+
audio_file = _AudioFile(test_file)
|
|
316
|
+
manager = RiffManager(audio_file)
|
|
317
|
+
raw_info = manager.get_raw_metadata_info()
|
|
318
|
+
|
|
319
|
+
assert "chunk_structure" in raw_info
|
|
320
|
+
assert "bext" in raw_info["chunk_structure"]
|
|
321
|
+
bext_data = raw_info["chunk_structure"]["bext"]
|
|
322
|
+
|
|
323
|
+
# Verify version is 2 (BWF v2)
|
|
324
|
+
assert bext_data.get("Version") == 2
|
|
325
|
+
|
|
326
|
+
# Verify loudness fields match external tool
|
|
327
|
+
assert bext_data["Description"] == "Test Description"
|
|
328
|
+
assert bext_data.get("LoudnessValue") == pytest.approx(-23.0, abs=0.1)
|
|
329
|
+
assert bext_data.get("LoudnessRange") == pytest.approx(7.0, abs=0.1)
|
|
330
|
+
assert bext_data.get("MaxTruePeakLevel") == pytest.approx(-1.5, abs=0.1)
|
|
331
|
+
assert bext_data.get("MaxMomentaryLoudness") == pytest.approx(-22.0, abs=0.1)
|
|
332
|
+
assert bext_data.get("MaxShortTermLoudness") == pytest.approx(-22.5, abs=0.1)
|
|
333
|
+
|
|
334
|
+
# Verify our extraction matches external tool
|
|
335
|
+
assert bext_data.get("LoudnessValue") == pytest.approx(external_bext.get("LoudnessValue", 0), abs=0.1)
|
|
336
|
+
assert bext_data.get("LoudnessRange") == pytest.approx(external_bext.get("LoudnessRange", 0), abs=0.1)
|
|
337
|
+
assert bext_data.get("MaxTruePeakLevel") == pytest.approx(external_bext.get("MaxTruePeakLevel", 0), abs=0.1)
|
|
338
|
+
assert bext_data.get("MaxMomentaryLoudness") == pytest.approx(
|
|
339
|
+
external_bext.get("MaxMomentaryLoudness", 0), abs=0.1
|
|
340
|
+
)
|
|
341
|
+
assert bext_data.get("MaxShortTermLoudness") == pytest.approx(
|
|
342
|
+
external_bext.get("MaxShortTermLoudness", 0), abs=0.1
|
|
343
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Unit tests for Vorbis metadata manager header information methods."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa._audio_file import _AudioFile
|
|
8
|
+
from audiometa.manager._rating_supporting.vorbis._VorbisManager import _VorbisManager as VorbisManager
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.unit
|
|
12
|
+
class TestVorbisHeaderMethods:
|
|
13
|
+
"""Test cases for Vorbis metadata manager header information methods."""
|
|
14
|
+
|
|
15
|
+
def test_vorbis_manager_header_info(self, sample_flac_file: Path):
|
|
16
|
+
"""Test VorbisManager header info method."""
|
|
17
|
+
audio_file = _AudioFile(sample_flac_file)
|
|
18
|
+
manager = VorbisManager(audio_file)
|
|
19
|
+
|
|
20
|
+
header_info = manager.get_header_info()
|
|
21
|
+
|
|
22
|
+
# Should have Vorbis specific structure
|
|
23
|
+
assert "present" in header_info
|
|
24
|
+
assert "vendor_string" in header_info
|
|
25
|
+
assert "comment_count" in header_info
|
|
26
|
+
assert "block_size" in header_info
|
|
27
|
+
|
|
28
|
+
# Should be valid structure
|
|
29
|
+
assert isinstance(header_info["present"], bool)
|
|
30
|
+
assert header_info["vendor_string"] is None or isinstance(header_info["vendor_string"], str)
|
|
31
|
+
assert isinstance(header_info["comment_count"], int)
|
|
32
|
+
assert isinstance(header_info["block_size"], int)
|
|
33
|
+
|
|
34
|
+
def test_vorbis_manager_raw_metadata_info(self, sample_flac_file: Path):
|
|
35
|
+
"""Test VorbisManager raw metadata info method."""
|
|
36
|
+
audio_file = _AudioFile(sample_flac_file)
|
|
37
|
+
manager = VorbisManager(audio_file)
|
|
38
|
+
|
|
39
|
+
raw_info = manager.get_raw_metadata_info()
|
|
40
|
+
|
|
41
|
+
# Should have Vorbis specific structure
|
|
42
|
+
assert "raw_data" in raw_info
|
|
43
|
+
assert "parsed_fields" in raw_info
|
|
44
|
+
assert "frames" in raw_info
|
|
45
|
+
assert "comments" in raw_info
|
|
46
|
+
assert "chunk_structure" in raw_info
|
|
47
|
+
|
|
48
|
+
# Should be valid structure
|
|
49
|
+
assert raw_info["raw_data"] is None or isinstance(raw_info["raw_data"], bytes)
|
|
50
|
+
assert isinstance(raw_info["parsed_fields"], dict)
|
|
51
|
+
assert isinstance(raw_info["frames"], dict)
|
|
52
|
+
assert isinstance(raw_info["comments"], dict)
|
|
53
|
+
assert isinstance(raw_info["chunk_structure"], dict)
|
|
File without changes
|
|
File without changes
|
audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from unittest.mock import MagicMock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa.manager._MetadataManager import _MetadataManager as MetadataManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.unit
|
|
9
|
+
class TestSmartParsing:
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
("values", "expected_should_parse"),
|
|
12
|
+
[
|
|
13
|
+
([], False),
|
|
14
|
+
([""], False),
|
|
15
|
+
([" "], False),
|
|
16
|
+
(["Artist One;Artist Two"], True),
|
|
17
|
+
(["Artist One", "Artist Two"], False),
|
|
18
|
+
(["Artist One//Artist Two"], True),
|
|
19
|
+
],
|
|
20
|
+
)
|
|
21
|
+
def test_should_apply_smart_parsing(self, values, expected_should_parse):
|
|
22
|
+
audio_file = MagicMock()
|
|
23
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
24
|
+
|
|
25
|
+
result = manager._should_apply_smart_parsing(values)
|
|
26
|
+
assert result == expected_should_parse
|
|
27
|
+
|
|
28
|
+
def test_should_apply_smart_parsing_with_null_separators(self):
|
|
29
|
+
audio_file = MagicMock()
|
|
30
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
31
|
+
|
|
32
|
+
result = manager._should_apply_smart_parsing(["Artist\x00One"])
|
|
33
|
+
assert result is True
|
|
34
|
+
|
|
35
|
+
result = manager._should_apply_smart_parsing(["Artist One", "Artist\x00Two"])
|
|
36
|
+
assert result is True
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize(
|
|
39
|
+
("values", "expected_parsed"),
|
|
40
|
+
[
|
|
41
|
+
(["Artist One;Artist Two"], ["Artist One", "Artist Two"]),
|
|
42
|
+
(["Artist One//Artist Two"], ["Artist One", "Artist Two"]),
|
|
43
|
+
(["Artist One,Artist Two"], ["Artist One", "Artist Two"]),
|
|
44
|
+
(["Artist One\\Artist Two"], ["Artist One", "Artist Two"]),
|
|
45
|
+
(["Artist One/Artist Two"], ["Artist One", "Artist Two"]),
|
|
46
|
+
(["Artist One;Artist;Three"], ["Artist One", "Artist", "Three"]),
|
|
47
|
+
(["Artist One;Artist Two;Artist Three"], ["Artist One", "Artist Two", "Artist Three"]),
|
|
48
|
+
([" Artist One "], ["Artist One"]),
|
|
49
|
+
([""], []),
|
|
50
|
+
([" "], []),
|
|
51
|
+
(["Artist One"], ["Artist One"]),
|
|
52
|
+
],
|
|
53
|
+
)
|
|
54
|
+
def test_apply_smart_parsing(self, values, expected_parsed):
|
|
55
|
+
audio_file = MagicMock()
|
|
56
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
57
|
+
|
|
58
|
+
result = manager._apply_smart_parsing(values)
|
|
59
|
+
assert result == expected_parsed
|
|
60
|
+
|
|
61
|
+
def test_apply_smart_parsing_with_null_separators(self):
|
|
62
|
+
audio_file = MagicMock()
|
|
63
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
64
|
+
|
|
65
|
+
result = manager._apply_smart_parsing(["Artist\x00One\x00Two"])
|
|
66
|
+
assert result == ["Artist", "One", "Two"]
|
|
67
|
+
|
|
68
|
+
def test_apply_smart_parsing_mixed_null_and_regular_separators(self):
|
|
69
|
+
audio_file = MagicMock()
|
|
70
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
71
|
+
|
|
72
|
+
# Null separators take priority - split on null first
|
|
73
|
+
result = manager._apply_smart_parsing(["Artist\x00One;Two"])
|
|
74
|
+
assert result == ["Artist", "One;Two"]
|
|
75
|
+
|
|
76
|
+
def test_apply_smart_parsing_multiple_entries_no_separators(self):
|
|
77
|
+
audio_file = MagicMock()
|
|
78
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
79
|
+
|
|
80
|
+
# Multiple entries without separators - only first non-empty entry is parsed
|
|
81
|
+
# This is because _apply_smart_parsing is designed for single-entry legacy data
|
|
82
|
+
result = manager._apply_smart_parsing(["Artist One", "Artist Two"])
|
|
83
|
+
assert result == ["Artist One"]
|
|
84
|
+
|
|
85
|
+
def test_apply_smart_parsing_separator_priority_semicolon_over_comma(self):
|
|
86
|
+
audio_file = MagicMock()
|
|
87
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
88
|
+
|
|
89
|
+
result = manager._apply_smart_parsing(["Artist One;Artist Two,Artist Three"])
|
|
90
|
+
assert result == ["Artist One", "Artist Two,Artist Three"]
|
|
91
|
+
|
|
92
|
+
def test_apply_smart_parsing_separator_priority_double_slash_over_slash(self):
|
|
93
|
+
audio_file = MagicMock()
|
|
94
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
95
|
+
|
|
96
|
+
result = manager._apply_smart_parsing(["Artist One//Artist Two/Artist Three"])
|
|
97
|
+
assert result == ["Artist One", "Artist Two/Artist Three"]
|
|
98
|
+
|
|
99
|
+
def test_unicode_characters(self):
|
|
100
|
+
audio_file = MagicMock()
|
|
101
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
102
|
+
|
|
103
|
+
# Unicode characters in parsed values
|
|
104
|
+
values = ["Artist Café"]
|
|
105
|
+
result = manager._apply_smart_parsing(values)
|
|
106
|
+
assert "Artist Café" in result
|
|
107
|
+
|
|
108
|
+
def test_empty_values_after_separation(self):
|
|
109
|
+
audio_file = MagicMock()
|
|
110
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
111
|
+
|
|
112
|
+
result = manager._apply_smart_parsing(["Artist One;;Artist Two;"])
|
|
113
|
+
assert result == ["Artist One", "Artist Two"]
|
|
114
|
+
|
|
115
|
+
def test_whitespace_around_separators(self):
|
|
116
|
+
audio_file = MagicMock()
|
|
117
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
118
|
+
|
|
119
|
+
result = manager._apply_smart_parsing(["Artist One ; Artist Two ; Artist Three"])
|
|
120
|
+
assert result == ["Artist One", "Artist Two", "Artist Three"]
|
|
121
|
+
|
|
122
|
+
def test_numeric_entries_in_parsed_value(self):
|
|
123
|
+
audio_file = MagicMock()
|
|
124
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
125
|
+
|
|
126
|
+
values = ["Artist 1;Artist 2;123"]
|
|
127
|
+
result = manager._apply_smart_parsing(values)
|
|
128
|
+
assert "Artist 1" in result
|
|
129
|
+
assert "Artist 2" in result
|
|
130
|
+
assert "123" in result
|
|
131
|
+
|
|
132
|
+
def test_case_sensitivity_in_parsed_value(self):
|
|
133
|
+
audio_file = MagicMock()
|
|
134
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
135
|
+
|
|
136
|
+
values = ["Artist One;ARTIST TWO;artist three;ArTiSt FoUr"]
|
|
137
|
+
result = manager._apply_smart_parsing(values)
|
|
138
|
+
assert "Artist One" in result
|
|
139
|
+
assert "ARTIST TWO" in result
|
|
140
|
+
assert "artist three" in result
|
|
141
|
+
assert "ArTiSt FoUr" in result
|
|
142
|
+
|
|
143
|
+
def test_duplicate_entries_in_parsed_value(self):
|
|
144
|
+
audio_file = MagicMock()
|
|
145
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
146
|
+
|
|
147
|
+
values = ["Artist One;Artist Two;Artist One;Artist Three;Artist Two"]
|
|
148
|
+
result = manager._apply_smart_parsing(values)
|
|
149
|
+
assert result.count("Artist One") == 2
|
|
150
|
+
assert result.count("Artist Two") == 2
|
|
151
|
+
assert result.count("Artist Three") == 1
|
|
152
|
+
|
|
153
|
+
def test_order_preservation_in_parsed_value(self):
|
|
154
|
+
audio_file = MagicMock()
|
|
155
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
156
|
+
|
|
157
|
+
values = ["First Artist;Second Artist;Third Artist;Fourth Artist"]
|
|
158
|
+
result = manager._apply_smart_parsing(values)
|
|
159
|
+
assert result[0] == "First Artist"
|
|
160
|
+
assert result[1] == "Second Artist"
|
|
161
|
+
assert result[2] == "Third Artist"
|
|
162
|
+
assert result[3] == "Fourth Artist"
|
|
163
|
+
|
|
164
|
+
def test_very_long_single_entry(self):
|
|
165
|
+
audio_file = MagicMock()
|
|
166
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
167
|
+
|
|
168
|
+
long_artist = "A" * 10000
|
|
169
|
+
result = manager._apply_smart_parsing([long_artist])
|
|
170
|
+
assert result == [long_artist]
|
|
171
|
+
assert len(result[0]) == 10000
|
|
172
|
+
|
|
173
|
+
def test_mixed_empty_values_with_separators(self):
|
|
174
|
+
audio_file = MagicMock()
|
|
175
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
176
|
+
|
|
177
|
+
result = manager._apply_smart_parsing(["Artist One;;;Artist Two;Artist;"])
|
|
178
|
+
assert result == ["Artist One", "Artist Two", "Artist"]
|
|
179
|
+
|
|
180
|
+
def test_multiple_spaces_within_values_preserved(self):
|
|
181
|
+
audio_file = MagicMock()
|
|
182
|
+
manager = MetadataManager(audio_file, {}, {})
|
|
183
|
+
|
|
184
|
+
values = ["Artist One"]
|
|
185
|
+
result = manager._apply_smart_parsing(values)
|
|
186
|
+
assert "Artist One" in result
|
audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py
ADDED
|
File without changes
|