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,250 @@
|
|
|
1
|
+
"""Tests for binary data filtering in get_full_metadata function output."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_full_metadata
|
|
6
|
+
from audiometa.manager._rating_supporting.id3v2._Id3v2Manager import _Id3v2Manager as Id3v2Manager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestGetFullMetadataBinaryDataFiltering:
|
|
11
|
+
"""Test that get_full_metadata function properly filters binary data from raw metadata output."""
|
|
12
|
+
|
|
13
|
+
def test_get_full_metadata_id3v2_binary_frames_filtered(self, sample_mp3_file):
|
|
14
|
+
"""Test that get_full_metadata filters ID3v2 binary frames and replaces with size info."""
|
|
15
|
+
result = get_full_metadata(sample_mp3_file)
|
|
16
|
+
raw_metadata = result.get("raw_metadata", {})
|
|
17
|
+
id3v2_frames = raw_metadata.get("id3v2", {}).get("frames", {})
|
|
18
|
+
|
|
19
|
+
# Check that all frames have reasonable text content
|
|
20
|
+
for frame_id, frame_data in id3v2_frames.items():
|
|
21
|
+
text = frame_data.get("text", "")
|
|
22
|
+
|
|
23
|
+
# Text should not contain binary data patterns
|
|
24
|
+
assert not any(
|
|
25
|
+
ord(c) < 32 and c not in "\t\n\r" for c in text
|
|
26
|
+
), f"Frame {frame_id} contains binary data in text: {text[:50]!r}"
|
|
27
|
+
|
|
28
|
+
# If it's a binary frame type, should have placeholder text
|
|
29
|
+
binary_frame_types = {
|
|
30
|
+
"APIC:",
|
|
31
|
+
"GEOB:",
|
|
32
|
+
"AENC:",
|
|
33
|
+
"RVA2:",
|
|
34
|
+
"RVRB:",
|
|
35
|
+
"EQU2:",
|
|
36
|
+
"PCNT:",
|
|
37
|
+
"POPM:",
|
|
38
|
+
"RBUF:",
|
|
39
|
+
"LINK:",
|
|
40
|
+
"POSS:",
|
|
41
|
+
"SYLT:",
|
|
42
|
+
"USLT:",
|
|
43
|
+
"SYTC:",
|
|
44
|
+
"ETCO:",
|
|
45
|
+
"MLLT:",
|
|
46
|
+
"OWNE:",
|
|
47
|
+
"COMR:",
|
|
48
|
+
"ENCR:",
|
|
49
|
+
"GRID:",
|
|
50
|
+
"PRIV:",
|
|
51
|
+
"SIGN:",
|
|
52
|
+
"SEEK:",
|
|
53
|
+
"ASPI:",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if frame_id in binary_frame_types:
|
|
57
|
+
assert text.startswith(
|
|
58
|
+
"<Binary data:"
|
|
59
|
+
), f"Binary frame {frame_id} should have placeholder text, got: {text}"
|
|
60
|
+
assert text.endswith(" bytes>"), f"Binary frame {frame_id} should end with ' bytes>', got: {text}"
|
|
61
|
+
|
|
62
|
+
def test_id3v2_manager_binary_filtering(self, sample_mp3_file):
|
|
63
|
+
"""Test Id3v2Manager directly filters binary frames."""
|
|
64
|
+
from audiometa._audio_file import _AudioFile
|
|
65
|
+
|
|
66
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
67
|
+
manager = Id3v2Manager(audio_file)
|
|
68
|
+
raw_info = manager.get_raw_metadata_info()
|
|
69
|
+
|
|
70
|
+
frames = raw_info.get("frames", {})
|
|
71
|
+
binary_frame_types = {
|
|
72
|
+
"APIC:",
|
|
73
|
+
"GEOB:",
|
|
74
|
+
"AENC:",
|
|
75
|
+
"RVA2:",
|
|
76
|
+
"RVRB:",
|
|
77
|
+
"EQU2:",
|
|
78
|
+
"PCNT:",
|
|
79
|
+
"POPM:",
|
|
80
|
+
"RBUF:",
|
|
81
|
+
"LINK:",
|
|
82
|
+
"POSS:",
|
|
83
|
+
"SYLT:",
|
|
84
|
+
"USLT:",
|
|
85
|
+
"SYTC:",
|
|
86
|
+
"ETCO:",
|
|
87
|
+
"MLLT:",
|
|
88
|
+
"OWNE:",
|
|
89
|
+
"COMR:",
|
|
90
|
+
"ENCR:",
|
|
91
|
+
"GRID:",
|
|
92
|
+
"PRIV:",
|
|
93
|
+
"SIGN:",
|
|
94
|
+
"SEEK:",
|
|
95
|
+
"ASPI:",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for frame_id, frame_data in frames.items():
|
|
99
|
+
text = frame_data.get("text", "")
|
|
100
|
+
|
|
101
|
+
if frame_id in binary_frame_types:
|
|
102
|
+
# Binary frames should have placeholder text
|
|
103
|
+
assert text.startswith("<Binary data:"), f"Binary frame {frame_id} should have placeholder text"
|
|
104
|
+
assert text.endswith(" bytes>"), f"Binary frame {frame_id} should end with ' bytes>'"
|
|
105
|
+
else:
|
|
106
|
+
# Text frames should not contain binary data
|
|
107
|
+
assert not any(
|
|
108
|
+
ord(c) < 32 and c not in "\t\n\r" for c in text
|
|
109
|
+
), f"Text frame {frame_id} contains binary data: {text[:50]!r}"
|
|
110
|
+
|
|
111
|
+
def test_get_full_metadata_vorbis_no_binary_data(self, sample_flac_file):
|
|
112
|
+
"""Test that get_full_metadata Vorbis comments don't contain binary data."""
|
|
113
|
+
result = get_full_metadata(sample_flac_file)
|
|
114
|
+
raw_metadata = result.get("raw_metadata", {})
|
|
115
|
+
vorbis_comments = raw_metadata.get("vorbis", {}).get("comments", {})
|
|
116
|
+
|
|
117
|
+
# Vorbis comments should only contain text
|
|
118
|
+
for key, values in vorbis_comments.items():
|
|
119
|
+
assert isinstance(values, list), f"Vorbis comment {key} should be a list"
|
|
120
|
+
for value in values:
|
|
121
|
+
assert isinstance(value, str), f"Vorbis comment {key} value should be string"
|
|
122
|
+
# Check for binary data patterns
|
|
123
|
+
assert not any(
|
|
124
|
+
ord(c) < 32 and c not in "\t\n\r" for c in value
|
|
125
|
+
), f"Vorbis comment {key} contains binary data: {value[:50]!r}"
|
|
126
|
+
|
|
127
|
+
def test_get_full_metadata_riff_no_binary_data(self, sample_wav_file):
|
|
128
|
+
"""Test that get_full_metadata RIFF metadata doesn't contain binary data."""
|
|
129
|
+
result = get_full_metadata(sample_wav_file)
|
|
130
|
+
raw_metadata = result.get("raw_metadata", {})
|
|
131
|
+
riff_fields = raw_metadata.get("riff", {}).get("parsed_fields", {})
|
|
132
|
+
|
|
133
|
+
# RIFF parsed fields should only contain text
|
|
134
|
+
for key, value in riff_fields.items():
|
|
135
|
+
assert isinstance(value, str), f"RIFF field {key} should be string"
|
|
136
|
+
# Check for binary data patterns
|
|
137
|
+
assert not any(
|
|
138
|
+
ord(c) < 32 and c not in "\t\n\r" for c in value
|
|
139
|
+
), f"RIFF field {key} contains binary data: {value[:50]!r}"
|
|
140
|
+
|
|
141
|
+
def test_get_full_metadata_id3v1_no_binary_data(self, sample_mp3_file):
|
|
142
|
+
"""Test that get_full_metadata ID3v1 metadata doesn't contain binary data."""
|
|
143
|
+
result = get_full_metadata(sample_mp3_file)
|
|
144
|
+
raw_metadata = result.get("raw_metadata", {})
|
|
145
|
+
id3v1_fields = raw_metadata.get("id3v1", {}).get("parsed_fields", {})
|
|
146
|
+
|
|
147
|
+
# ID3v1 parsed fields should only contain text
|
|
148
|
+
for key, value in id3v1_fields.items():
|
|
149
|
+
assert isinstance(value, str), f"ID3v1 field {key} should be string"
|
|
150
|
+
# Check for binary data patterns
|
|
151
|
+
assert not any(
|
|
152
|
+
ord(c) < 32 and c not in "\t\n\r" for c in value
|
|
153
|
+
), f"ID3v1 field {key} contains binary data: {value[:50]!r}"
|
|
154
|
+
|
|
155
|
+
def test_cli_output_no_binary_data(self, sample_mp3_file):
|
|
156
|
+
"""Test that CLI output doesn't contain binary data."""
|
|
157
|
+
import subprocess
|
|
158
|
+
import sys
|
|
159
|
+
|
|
160
|
+
# Test JSON output
|
|
161
|
+
result = subprocess.run(
|
|
162
|
+
[sys.executable, "-m", "audiometa", "read", str(sample_mp3_file), "--format", "json"],
|
|
163
|
+
capture_output=True,
|
|
164
|
+
text=True,
|
|
165
|
+
check=False,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
assert result.returncode == 0, f"CLI failed: {result.stderr}"
|
|
169
|
+
|
|
170
|
+
# Check for binary data patterns in output
|
|
171
|
+
output = result.stdout
|
|
172
|
+
binary_patterns = ["\\xff", "\\x00", "\\x01", "\\x02", "\\x03"]
|
|
173
|
+
|
|
174
|
+
for pattern in binary_patterns:
|
|
175
|
+
assert pattern not in output, f"CLI output contains binary pattern {pattern}"
|
|
176
|
+
|
|
177
|
+
# Should be valid JSON
|
|
178
|
+
import json
|
|
179
|
+
|
|
180
|
+
data = json.loads(output)
|
|
181
|
+
assert isinstance(data, dict)
|
|
182
|
+
|
|
183
|
+
def test_binary_frame_size_preserved(self, sample_mp3_file):
|
|
184
|
+
"""Test that binary frame sizes are still reported correctly."""
|
|
185
|
+
from audiometa._audio_file import _AudioFile
|
|
186
|
+
|
|
187
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
188
|
+
manager = Id3v2Manager(audio_file)
|
|
189
|
+
raw_info = manager.get_raw_metadata_info()
|
|
190
|
+
|
|
191
|
+
frames = raw_info.get("frames", {})
|
|
192
|
+
binary_frame_types = {
|
|
193
|
+
"APIC:",
|
|
194
|
+
"GEOB:",
|
|
195
|
+
"AENC:",
|
|
196
|
+
"RVA2:",
|
|
197
|
+
"RVRB:",
|
|
198
|
+
"EQU2:",
|
|
199
|
+
"PCNT:",
|
|
200
|
+
"POPM:",
|
|
201
|
+
"RBUF:",
|
|
202
|
+
"LINK:",
|
|
203
|
+
"POSS:",
|
|
204
|
+
"SYLT:",
|
|
205
|
+
"USLT:",
|
|
206
|
+
"SYTC:",
|
|
207
|
+
"ETCO:",
|
|
208
|
+
"MLLT:",
|
|
209
|
+
"OWNE:",
|
|
210
|
+
"COMR:",
|
|
211
|
+
"ENCR:",
|
|
212
|
+
"GRID:",
|
|
213
|
+
"PRIV:",
|
|
214
|
+
"SIGN:",
|
|
215
|
+
"SEEK:",
|
|
216
|
+
"ASPI:",
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for frame_id, frame_data in frames.items():
|
|
220
|
+
if frame_id in binary_frame_types:
|
|
221
|
+
size = frame_data.get("size")
|
|
222
|
+
flags = frame_data.get("flags")
|
|
223
|
+
|
|
224
|
+
# Size and flags should still be present
|
|
225
|
+
assert isinstance(size, int), f"Binary frame {frame_id} size should be int"
|
|
226
|
+
assert isinstance(flags, int), f"Binary frame {frame_id} flags should be int"
|
|
227
|
+
|
|
228
|
+
# Size should be reasonable (not negative)
|
|
229
|
+
assert size >= 0, f"Binary frame {frame_id} size should be non-negative"
|
|
230
|
+
|
|
231
|
+
def test_text_frames_unchanged(self, sample_mp3_file):
|
|
232
|
+
"""Test that text frames are not affected by binary filtering."""
|
|
233
|
+
from audiometa._audio_file import _AudioFile
|
|
234
|
+
|
|
235
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
236
|
+
manager = Id3v2Manager(audio_file)
|
|
237
|
+
raw_info = manager.get_raw_metadata_info()
|
|
238
|
+
|
|
239
|
+
frames = raw_info.get("frames", {})
|
|
240
|
+
text_frame_types = {"TIT2", "TALB", "TPE1", "TDRC", "COMM", "TENC", "TSSE"}
|
|
241
|
+
|
|
242
|
+
for frame_id, frame_data in frames.items():
|
|
243
|
+
if any(frame_id.startswith(prefix) for prefix in text_frame_types):
|
|
244
|
+
text = frame_data.get("text", "")
|
|
245
|
+
|
|
246
|
+
# Text frames should have actual content, not placeholder
|
|
247
|
+
assert not text.startswith("<Binary data:"), f"Text frame {frame_id} should not have binary placeholder"
|
|
248
|
+
|
|
249
|
+
# Should have reasonable content
|
|
250
|
+
assert len(text) > 0, f"Text frame {frame_id} should have content"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Tests for get_full_metadata function consistency and accuracy."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa import get_full_metadata, get_unified_metadata
|
|
8
|
+
from audiometa._audio_file import _AudioFile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestGetFullMetadataConsistency:
|
|
13
|
+
def test_get_full_metadata_consistency_with_merged_metadata(self, sample_mp3_file: Path):
|
|
14
|
+
full_result = get_full_metadata(sample_mp3_file)
|
|
15
|
+
merged_result = get_unified_metadata(sample_mp3_file)
|
|
16
|
+
|
|
17
|
+
# Should be identical
|
|
18
|
+
assert full_result["unified_metadata"] == merged_result
|
|
19
|
+
|
|
20
|
+
def test_get_full_metadata_technical_info_accuracy(self, sample_mp3_file: Path):
|
|
21
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
22
|
+
result = get_full_metadata(sample_mp3_file)
|
|
23
|
+
|
|
24
|
+
tech_info = result["technical_info"]
|
|
25
|
+
|
|
26
|
+
# Compare with direct _AudioFile methods
|
|
27
|
+
assert tech_info["duration_seconds"] == audio_file.get_duration_in_sec()
|
|
28
|
+
assert tech_info["bitrate_bps"] == audio_file.get_bitrate()
|
|
29
|
+
assert tech_info["sample_rate_hz"] == audio_file.get_sample_rate()
|
|
30
|
+
assert tech_info["channels"] == audio_file.get_channels()
|
|
31
|
+
assert tech_info["file_size_bytes"] == audio_file.get_file_size()
|
|
32
|
+
assert tech_info["file_extension"] == audio_file.file_extension
|
|
33
|
+
assert tech_info["audio_format_name"] == audio_file.get_audio_format_name()
|
|
34
|
+
|
|
35
|
+
def test_get_full_metadata_flac_md5_validation(self, sample_flac_file: Path):
|
|
36
|
+
result = get_full_metadata(sample_flac_file)
|
|
37
|
+
|
|
38
|
+
tech_info = result["technical_info"]
|
|
39
|
+
assert "is_flac_md5_valid" in tech_info
|
|
40
|
+
assert isinstance(tech_info["is_flac_md5_valid"], bool)
|
|
41
|
+
|
|
42
|
+
def test_get_full_metadata_structure_consistency(self, sample_mp3_file: Path):
|
|
43
|
+
result1 = get_full_metadata(sample_mp3_file)
|
|
44
|
+
result2 = get_full_metadata(sample_mp3_file)
|
|
45
|
+
|
|
46
|
+
# Structure should be identical
|
|
47
|
+
assert set(result1.keys()) == set(result2.keys())
|
|
48
|
+
|
|
49
|
+
# Each top-level section should have same keys
|
|
50
|
+
for key in result1:
|
|
51
|
+
if key in ["unified_metadata", "technical_info", "metadata_format", "headers", "raw_metadata"]:
|
|
52
|
+
assert set(result1[key].keys()) == set(result2[key].keys())
|
|
53
|
+
|
|
54
|
+
def test_get_full_metadata_format_detection_accuracy(self, sample_mp3_file: Path):
|
|
55
|
+
result = get_full_metadata(sample_mp3_file)
|
|
56
|
+
|
|
57
|
+
# Format priorities should be correct for MP3
|
|
58
|
+
priorities = result["format_priorities"]
|
|
59
|
+
assert priorities["file_extension"] == ".mp3"
|
|
60
|
+
assert "id3v2" in priorities["reading_order"]
|
|
61
|
+
assert "id3v1" in priorities["reading_order"]
|
|
62
|
+
assert priorities["writing_format"] == "id3v2"
|
|
63
|
+
|
|
64
|
+
# Technical info should reflect MP3 format
|
|
65
|
+
tech_info = result["technical_info"]
|
|
66
|
+
assert tech_info["file_extension"] == ".mp3"
|
|
67
|
+
assert tech_info["audio_format_name"] == "MP3"
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Edge case tests for get_full_metadata function."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa import get_full_metadata
|
|
8
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestGetFullMetadataEdgeCases:
|
|
13
|
+
def test_get_full_metadata_empty_file(self):
|
|
14
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
15
|
+
# Should handle gracefully and return structure with minimal data
|
|
16
|
+
result = get_full_metadata(temp_file_path)
|
|
17
|
+
|
|
18
|
+
# Should still return complete structure
|
|
19
|
+
assert "unified_metadata" in result
|
|
20
|
+
assert "technical_info" in result
|
|
21
|
+
assert "metadata_format" in result
|
|
22
|
+
assert "headers" in result
|
|
23
|
+
assert "raw_metadata" in result
|
|
24
|
+
assert "format_priorities" in result
|
|
25
|
+
|
|
26
|
+
def test_get_full_metadata_file_with_only_headers_no_metadata(self):
|
|
27
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
28
|
+
result = get_full_metadata(temp_file_path)
|
|
29
|
+
|
|
30
|
+
# Should detect headers even if no metadata content
|
|
31
|
+
headers = result["headers"]
|
|
32
|
+
|
|
33
|
+
for _metadata_format_name, header_info in headers.items():
|
|
34
|
+
# Headers might be present even without metadata content
|
|
35
|
+
assert "present" in header_info
|
|
36
|
+
assert isinstance(header_info["present"], bool)
|
|
37
|
+
|
|
38
|
+
def test_get_full_metadata_large_file(self, sample_mp3_file: Path):
|
|
39
|
+
# This test ensures the function can handle larger files
|
|
40
|
+
result = get_full_metadata(sample_mp3_file)
|
|
41
|
+
|
|
42
|
+
# Should complete successfully
|
|
43
|
+
assert "unified_metadata" in result
|
|
44
|
+
assert "technical_info" in result
|
|
45
|
+
|
|
46
|
+
# File size should be reasonable
|
|
47
|
+
tech_info = result["technical_info"]
|
|
48
|
+
assert tech_info["file_size_bytes"] > 0
|
|
49
|
+
|
|
50
|
+
def test_get_full_metadata_file_with_mixed_formats(self, sample_mp3_file: Path):
|
|
51
|
+
result = get_full_metadata(sample_mp3_file)
|
|
52
|
+
|
|
53
|
+
# Should handle multiple formats gracefully
|
|
54
|
+
metadata_format = result["metadata_format"]
|
|
55
|
+
headers = result["headers"]
|
|
56
|
+
|
|
57
|
+
# Each format should have its own section
|
|
58
|
+
for metadata_format_name in ["id3v2", "id3v1"]:
|
|
59
|
+
assert metadata_format_name in metadata_format
|
|
60
|
+
assert metadata_format_name in headers
|
|
61
|
+
|
|
62
|
+
# Each should be a dictionary
|
|
63
|
+
assert isinstance(metadata_format[metadata_format_name], dict)
|
|
64
|
+
assert isinstance(headers[metadata_format_name], dict)
|
|
65
|
+
|
|
66
|
+
def test_get_full_metadata_with_unicode_metadata(self, sample_mp3_file: Path):
|
|
67
|
+
# This test ensures unicode handling works correctly
|
|
68
|
+
result = get_full_metadata(sample_mp3_file)
|
|
69
|
+
|
|
70
|
+
# Should handle unicode in metadata
|
|
71
|
+
unified_metadata = result["unified_metadata"]
|
|
72
|
+
|
|
73
|
+
# Check that string values are properly handled
|
|
74
|
+
for _key, value in unified_metadata.items():
|
|
75
|
+
if isinstance(value, str):
|
|
76
|
+
# Should be able to handle unicode
|
|
77
|
+
assert isinstance(value, str)
|
|
78
|
+
elif isinstance(value, list):
|
|
79
|
+
for item in value:
|
|
80
|
+
if isinstance(item, str):
|
|
81
|
+
assert isinstance(item, str)
|
|
82
|
+
|
|
83
|
+
def test_get_full_metadata_with_minimal_metadata(self):
|
|
84
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
85
|
+
result = get_full_metadata(temp_file_path)
|
|
86
|
+
|
|
87
|
+
# Should still return complete structure
|
|
88
|
+
assert "unified_metadata" in result
|
|
89
|
+
assert "technical_info" in result
|
|
90
|
+
assert "metadata_format" in result
|
|
91
|
+
assert "headers" in result
|
|
92
|
+
assert "raw_metadata" in result
|
|
93
|
+
assert "format_priorities" in result
|
|
94
|
+
|
|
95
|
+
# Unified metadata might be empty or minimal
|
|
96
|
+
unified_metadata = result["unified_metadata"]
|
|
97
|
+
assert isinstance(unified_metadata, dict)
|
|
98
|
+
|
|
99
|
+
# Technical info should still be present
|
|
100
|
+
tech_info = result["technical_info"]
|
|
101
|
+
assert "file_size_bytes" in tech_info
|
|
102
|
+
assert tech_info["file_size_bytes"] >= 0 # Can be 0 for empty files
|
|
103
|
+
|
|
104
|
+
def test_get_full_metadata_file_with_no_metadata(self):
|
|
105
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
106
|
+
result = get_full_metadata(temp_file_path)
|
|
107
|
+
|
|
108
|
+
# Should still return complete structure
|
|
109
|
+
assert "unified_metadata" in result
|
|
110
|
+
assert "technical_info" in result
|
|
111
|
+
assert "metadata_format" in result
|
|
112
|
+
assert "headers" in result
|
|
113
|
+
assert "raw_metadata" in result
|
|
114
|
+
assert "format_priorities" in result
|
|
115
|
+
|
|
116
|
+
# Unified metadata should be empty or minimal
|
|
117
|
+
assert isinstance(result["unified_metadata"], dict)
|
|
118
|
+
|
|
119
|
+
# Technical info should still be present
|
|
120
|
+
tech_info = result["technical_info"]
|
|
121
|
+
assert "duration_seconds" in tech_info
|
|
122
|
+
assert "bitrate_bps" in tech_info
|
|
123
|
+
assert "file_size_bytes" in tech_info
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Error handling tests for get_full_metadata function."""
|
|
2
|
+
|
|
3
|
+
import tempfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from audiometa import get_full_metadata
|
|
9
|
+
from audiometa.exceptions import FileCorruptedError, FileTypeNotSupportedError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.integration
|
|
13
|
+
class TestGetFullMetadataErrorHandling:
|
|
14
|
+
def test_get_full_metadata_corrupted_file(self):
|
|
15
|
+
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
|
|
16
|
+
temp_path = temp_file.name
|
|
17
|
+
# Write some garbage data
|
|
18
|
+
temp_file.write(b"This is not a valid audio file")
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
with pytest.raises(FileCorruptedError):
|
|
22
|
+
get_full_metadata(temp_path)
|
|
23
|
+
finally:
|
|
24
|
+
Path(temp_path).unlink()
|
|
25
|
+
|
|
26
|
+
def test_get_full_metadata_error_recovery(self):
|
|
27
|
+
# Test with non-existent file
|
|
28
|
+
with pytest.raises(FileNotFoundError):
|
|
29
|
+
get_full_metadata("non_existent_file.mp3")
|
|
30
|
+
|
|
31
|
+
# Test with unsupported file type (create the file first)
|
|
32
|
+
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as temp_file:
|
|
33
|
+
temp_path = temp_file.name
|
|
34
|
+
temp_file.write(b"This is not an audio file")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
38
|
+
get_full_metadata(temp_path)
|
|
39
|
+
finally:
|
|
40
|
+
Path(temp_path).unlink()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Basic structure tests for get_full_metadata function."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa import get_full_metadata
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.integration
|
|
11
|
+
class TestGetFullMetadata:
|
|
12
|
+
def test_get_full_metadata_basic_structure(self, sample_mp3_file: Path):
|
|
13
|
+
"""Test that get_full_metadata returns the expected basic structure."""
|
|
14
|
+
result = get_full_metadata(sample_mp3_file)
|
|
15
|
+
|
|
16
|
+
# Check all required top-level keys are present
|
|
17
|
+
assert "unified_metadata" in result
|
|
18
|
+
assert "technical_info" in result
|
|
19
|
+
assert "metadata_format" in result
|
|
20
|
+
assert "headers" in result
|
|
21
|
+
assert "raw_metadata" in result
|
|
22
|
+
assert "format_priorities" in result
|
|
23
|
+
|
|
24
|
+
# Check that each section is a dictionary
|
|
25
|
+
assert isinstance(result["unified_metadata"], dict)
|
|
26
|
+
assert isinstance(result["technical_info"], dict)
|
|
27
|
+
assert isinstance(result["metadata_format"], dict)
|
|
28
|
+
assert isinstance(result["headers"], dict)
|
|
29
|
+
assert isinstance(result["raw_metadata"], dict)
|
|
30
|
+
assert isinstance(result["format_priorities"], dict)
|
|
31
|
+
|
|
32
|
+
def test_get_full_metadata_format_priorities_structure(self, sample_mp3_file: Path):
|
|
33
|
+
"""Test that format_priorities has the expected structure."""
|
|
34
|
+
result = get_full_metadata(sample_mp3_file)
|
|
35
|
+
|
|
36
|
+
priorities = result["format_priorities"]
|
|
37
|
+
assert "file_extension" in priorities
|
|
38
|
+
assert "reading_order" in priorities
|
|
39
|
+
assert "writing_format" in priorities
|
|
40
|
+
|
|
41
|
+
assert isinstance(priorities["file_extension"], str)
|
|
42
|
+
assert isinstance(priorities["reading_order"], list)
|
|
43
|
+
assert isinstance(priorities["writing_format"], str)
|