audiometa-python 0.2.2__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 +1240 -0
- audiometa/__main__.py +6 -0
- audiometa/_audio_file.py +602 -0
- audiometa/cli.py +347 -0
- audiometa/exceptions.py +167 -0
- audiometa/manager/_MetadataManager.py +665 -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 +945 -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 +778 -0
- audiometa/manager/_rating_supporting/riff/__init__.py +25 -0
- audiometa/manager/_rating_supporting/riff/_riff_constants.py +10 -0
- audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +525 -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 +305 -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 +188 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +428 -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 +216 -0
- audiometa/test/helpers/riff/riff_metadata_deleter.py +56 -0
- audiometa/test/helpers/riff/riff_metadata_getter.py +118 -0
- audiometa/test/helpers/riff/riff_metadata_setter.py +196 -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 +200 -0
- audiometa/test/tests/__init__.py +0 -0
- audiometa/test/tests/conftest.py +261 -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/test_cli_delete.py +20 -0
- audiometa/test/tests/e2e/cli/test_cli_formatting.py +31 -0
- audiometa/test/tests/e2e/cli/test_cli_help.py +40 -0
- audiometa/test/tests/e2e/cli/test_cli_read.py +79 -0
- audiometa/test/tests/e2e/cli/test_cli_unified.py +33 -0
- audiometa/test/tests/e2e/cli/test_cli_write.py +116 -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_binary_data_filtering.py +250 -0
- audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +297 -0
- audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata_edge_cases.py +231 -0
- audiometa/test/tests/integration/get_full_metadata/test_options.py +207 -0
- audiometa/test/tests/integration/get_full_metadata/test_parsed_fields_keys.py +85 -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/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/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 +134 -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 +38 -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 +23 -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 +130 -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_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 +57 -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_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_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/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/os_dependencies_checker/__init__.py +24 -0
- audiometa/utils/os_dependencies_checker/base.py +62 -0
- audiometa/utils/os_dependencies_checker/config.py +51 -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 +81 -0
- audiometa_python-0.2.2.dist-info/METADATA +1464 -0
- audiometa_python-0.2.2.dist-info/RECORD +318 -0
- audiometa_python-0.2.2.dist-info/WHEEL +5 -0
- audiometa_python-0.2.2.dist-info/entry_points.txt +2 -0
- audiometa_python-0.2.2.dist-info/licenses/LICENSE +202 -0
- audiometa_python-0.2.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""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._audio_file import _AudioFile
|
|
9
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.integration
|
|
13
|
+
class TestGetFullMetadata:
|
|
14
|
+
def test_get_full_metadata_mp3_with_metadata(self, sample_mp3_file: Path):
|
|
15
|
+
result = get_full_metadata(sample_mp3_file)
|
|
16
|
+
|
|
17
|
+
# Check structure
|
|
18
|
+
assert "unified_metadata" in result
|
|
19
|
+
assert "technical_info" in result
|
|
20
|
+
assert "metadata_format" in result
|
|
21
|
+
assert "headers" in result
|
|
22
|
+
assert "raw_metadata" in result
|
|
23
|
+
assert "format_priorities" in result
|
|
24
|
+
|
|
25
|
+
# Check format priorities
|
|
26
|
+
assert result["format_priorities"]["file_extension"] == ".mp3"
|
|
27
|
+
assert "id3v2" in result["format_priorities"]["reading_order"]
|
|
28
|
+
assert "id3v1" in result["format_priorities"]["reading_order"]
|
|
29
|
+
assert result["format_priorities"]["writing_format"] == "id3v2"
|
|
30
|
+
|
|
31
|
+
# Check technical info
|
|
32
|
+
tech_info = result["technical_info"]
|
|
33
|
+
assert "duration_seconds" in tech_info
|
|
34
|
+
assert "bitrate_kbps" in tech_info
|
|
35
|
+
assert "sample_rate_hz" in tech_info
|
|
36
|
+
assert "channels" in tech_info
|
|
37
|
+
assert "file_size_bytes" in tech_info
|
|
38
|
+
assert "file_extension" in tech_info
|
|
39
|
+
assert "audio_format_name" in tech_info
|
|
40
|
+
assert tech_info["file_extension"] == ".mp3"
|
|
41
|
+
assert tech_info["audio_format_name"] == "MP3"
|
|
42
|
+
assert tech_info["is_flac_md5_valid"] is None # Not a FLAC file
|
|
43
|
+
|
|
44
|
+
# Check format metadata
|
|
45
|
+
assert "id3v2" in result["metadata_format"]
|
|
46
|
+
assert "id3v1" in result["metadata_format"]
|
|
47
|
+
|
|
48
|
+
# Check headers
|
|
49
|
+
assert "id3v2" in result["headers"]
|
|
50
|
+
assert "id3v1" in result["headers"]
|
|
51
|
+
|
|
52
|
+
# Check raw metadata
|
|
53
|
+
assert "id3v2" in result["raw_metadata"]
|
|
54
|
+
assert "id3v1" in result["raw_metadata"]
|
|
55
|
+
|
|
56
|
+
def test_get_full_metadata_flac_with_metadata(self, sample_flac_file: Path):
|
|
57
|
+
result = get_full_metadata(sample_flac_file)
|
|
58
|
+
|
|
59
|
+
# Check format priorities
|
|
60
|
+
assert result["format_priorities"]["file_extension"] == ".flac"
|
|
61
|
+
assert "vorbis" in result["format_priorities"]["reading_order"]
|
|
62
|
+
assert "id3v2" in result["format_priorities"]["reading_order"]
|
|
63
|
+
assert "id3v1" in result["format_priorities"]["reading_order"]
|
|
64
|
+
assert result["format_priorities"]["writing_format"] == "vorbis"
|
|
65
|
+
|
|
66
|
+
# Check technical info
|
|
67
|
+
tech_info = result["technical_info"]
|
|
68
|
+
assert tech_info["file_extension"] == ".flac"
|
|
69
|
+
assert tech_info["audio_format_name"] == "FLAC"
|
|
70
|
+
assert "is_flac_md5_valid" in tech_info # Should be present for FLAC
|
|
71
|
+
|
|
72
|
+
# Check format metadata
|
|
73
|
+
assert "vorbis" in result["metadata_format"]
|
|
74
|
+
assert "id3v2" in result["metadata_format"]
|
|
75
|
+
assert "id3v1" in result["metadata_format"]
|
|
76
|
+
|
|
77
|
+
# Check headers
|
|
78
|
+
assert "vorbis" in result["headers"]
|
|
79
|
+
assert "id3v2" in result["headers"]
|
|
80
|
+
assert "id3v1" in result["headers"]
|
|
81
|
+
|
|
82
|
+
def test_get_full_metadata_wav_with_metadata(self, sample_wav_file: Path):
|
|
83
|
+
result = get_full_metadata(sample_wav_file)
|
|
84
|
+
|
|
85
|
+
# Check format priorities
|
|
86
|
+
assert result["format_priorities"]["file_extension"] == ".wav"
|
|
87
|
+
assert "riff" in result["format_priorities"]["reading_order"]
|
|
88
|
+
assert "id3v2" in result["format_priorities"]["reading_order"]
|
|
89
|
+
assert "id3v1" in result["format_priorities"]["reading_order"]
|
|
90
|
+
assert result["format_priorities"]["writing_format"] == "riff"
|
|
91
|
+
|
|
92
|
+
# Check technical info
|
|
93
|
+
tech_info = result["technical_info"]
|
|
94
|
+
assert tech_info["file_extension"] == ".wav"
|
|
95
|
+
assert tech_info["audio_format_name"] == "WAV"
|
|
96
|
+
assert tech_info["is_flac_md5_valid"] is None # Not a FLAC file
|
|
97
|
+
|
|
98
|
+
# Check format metadata
|
|
99
|
+
assert "riff" in result["metadata_format"]
|
|
100
|
+
assert "id3v2" in result["metadata_format"]
|
|
101
|
+
assert "id3v1" in result["metadata_format"]
|
|
102
|
+
|
|
103
|
+
# Check headers
|
|
104
|
+
assert "riff" in result["headers"]
|
|
105
|
+
assert "id3v2" in result["headers"]
|
|
106
|
+
assert "id3v1" in result["headers"]
|
|
107
|
+
|
|
108
|
+
def test_get_full_metadata_without_headers(self, sample_mp3_file: Path):
|
|
109
|
+
result = get_full_metadata(sample_mp3_file, include_headers=False)
|
|
110
|
+
|
|
111
|
+
# Should still have basic structure
|
|
112
|
+
assert "unified_metadata" in result
|
|
113
|
+
assert "technical_info" in result
|
|
114
|
+
assert "metadata_format" in result
|
|
115
|
+
assert "format_priorities" in result
|
|
116
|
+
|
|
117
|
+
# Headers and raw metadata should be empty or minimal
|
|
118
|
+
assert "headers" in result
|
|
119
|
+
assert "raw_metadata" in result
|
|
120
|
+
|
|
121
|
+
def test_get_full_metadata_without_technical(self, sample_mp3_file: Path):
|
|
122
|
+
result = get_full_metadata(sample_mp3_file, include_technical=False)
|
|
123
|
+
|
|
124
|
+
# Should still have basic structure
|
|
125
|
+
assert "unified_metadata" in result
|
|
126
|
+
assert "metadata_format" in result
|
|
127
|
+
assert "headers" in result
|
|
128
|
+
assert "raw_metadata" in result
|
|
129
|
+
assert "format_priorities" in result
|
|
130
|
+
|
|
131
|
+
# Technical info should be empty or minimal
|
|
132
|
+
assert "technical_info" in result
|
|
133
|
+
|
|
134
|
+
def test_get_full_metadata_file_with_no_metadata(self):
|
|
135
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
136
|
+
result = get_full_metadata(temp_file_path)
|
|
137
|
+
|
|
138
|
+
# Should still return complete structure
|
|
139
|
+
assert "unified_metadata" in result
|
|
140
|
+
assert "technical_info" in result
|
|
141
|
+
assert "metadata_format" in result
|
|
142
|
+
assert "headers" in result
|
|
143
|
+
assert "raw_metadata" in result
|
|
144
|
+
assert "format_priorities" in result
|
|
145
|
+
|
|
146
|
+
# Unified metadata should be empty or minimal
|
|
147
|
+
assert isinstance(result["unified_metadata"], dict)
|
|
148
|
+
|
|
149
|
+
# Technical info should still be present
|
|
150
|
+
tech_info = result["technical_info"]
|
|
151
|
+
assert "duration_seconds" in tech_info
|
|
152
|
+
assert "bitrate_kbps" in tech_info
|
|
153
|
+
assert "file_size_bytes" in tech_info
|
|
154
|
+
|
|
155
|
+
def test_get_full_metadata_headers_present_flags(self, sample_mp3_file: Path):
|
|
156
|
+
result = get_full_metadata(sample_mp3_file)
|
|
157
|
+
|
|
158
|
+
# Check ID3v2 headers
|
|
159
|
+
id3v2_headers = result["headers"]["id3v2"]
|
|
160
|
+
assert "present" in id3v2_headers
|
|
161
|
+
assert "version" in id3v2_headers
|
|
162
|
+
assert "header_size_bytes" in id3v2_headers
|
|
163
|
+
assert "flags" in id3v2_headers
|
|
164
|
+
assert "extended_header" in id3v2_headers
|
|
165
|
+
|
|
166
|
+
# Check ID3v1 headers
|
|
167
|
+
id3v1_headers = result["headers"]["id3v1"]
|
|
168
|
+
assert "present" in id3v1_headers
|
|
169
|
+
assert "position" in id3v1_headers
|
|
170
|
+
assert "size_bytes" in id3v1_headers
|
|
171
|
+
assert "version" in id3v1_headers
|
|
172
|
+
assert "has_track_number" in id3v1_headers
|
|
173
|
+
|
|
174
|
+
def test_get_full_metadata_raw_metadata_structure(self, sample_mp3_file: Path):
|
|
175
|
+
result = get_full_metadata(sample_mp3_file)
|
|
176
|
+
|
|
177
|
+
# Check ID3v2 raw metadata
|
|
178
|
+
id3v2_raw = result["raw_metadata"]["id3v2"]
|
|
179
|
+
assert "raw_data" in id3v2_raw
|
|
180
|
+
assert "parsed_fields" in id3v2_raw
|
|
181
|
+
assert "frames" in id3v2_raw
|
|
182
|
+
assert "comments" in id3v2_raw
|
|
183
|
+
assert "chunk_structure" in id3v2_raw
|
|
184
|
+
|
|
185
|
+
# Check ID3v1 raw metadata
|
|
186
|
+
id3v1_raw = result["raw_metadata"]["id3v1"]
|
|
187
|
+
assert "raw_data" in id3v1_raw
|
|
188
|
+
assert "parsed_fields" in id3v1_raw
|
|
189
|
+
assert "frames" in id3v1_raw
|
|
190
|
+
assert "comments" in id3v1_raw
|
|
191
|
+
assert "chunk_structure" in id3v1_raw
|
|
192
|
+
|
|
193
|
+
def test_get_full_metadata_consistency_with_merged_metadata(self, sample_mp3_file: Path):
|
|
194
|
+
from audiometa import get_unified_metadata
|
|
195
|
+
|
|
196
|
+
full_result = get_full_metadata(sample_mp3_file)
|
|
197
|
+
merged_result = get_unified_metadata(sample_mp3_file)
|
|
198
|
+
|
|
199
|
+
# Should be identical
|
|
200
|
+
assert full_result["unified_metadata"] == merged_result
|
|
201
|
+
|
|
202
|
+
def test_get_full_metadata_technical_info_accuracy(self, sample_mp3_file: Path):
|
|
203
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
204
|
+
result = get_full_metadata(sample_mp3_file)
|
|
205
|
+
|
|
206
|
+
tech_info = result["technical_info"]
|
|
207
|
+
|
|
208
|
+
# Compare with direct _AudioFile methods
|
|
209
|
+
assert tech_info["duration_seconds"] == audio_file.get_duration_in_sec()
|
|
210
|
+
assert tech_info["bitrate_kbps"] == audio_file.get_bitrate()
|
|
211
|
+
assert tech_info["sample_rate_hz"] == audio_file.get_sample_rate()
|
|
212
|
+
assert tech_info["channels"] == audio_file.get_channels()
|
|
213
|
+
assert tech_info["file_size_bytes"] == audio_file.get_file_size()
|
|
214
|
+
assert tech_info["file_extension"] == audio_file.file_extension
|
|
215
|
+
assert tech_info["audio_format_name"] == audio_file.get_audio_format_name()
|
|
216
|
+
|
|
217
|
+
def test_get_full_metadata_flac_md5_validation(self, sample_flac_file: Path):
|
|
218
|
+
result = get_full_metadata(sample_flac_file)
|
|
219
|
+
|
|
220
|
+
tech_info = result["technical_info"]
|
|
221
|
+
assert "is_flac_md5_valid" in tech_info
|
|
222
|
+
assert isinstance(tech_info["is_flac_md5_valid"], bool)
|
|
223
|
+
|
|
224
|
+
def test_get_full_metadata_error_handling(self):
|
|
225
|
+
# Test with non-existent file
|
|
226
|
+
with pytest.raises(FileNotFoundError):
|
|
227
|
+
get_full_metadata("non_existent_file.mp3")
|
|
228
|
+
|
|
229
|
+
# Test with unsupported file type
|
|
230
|
+
import tempfile
|
|
231
|
+
|
|
232
|
+
from audiometa.exceptions import FileTypeNotSupportedError
|
|
233
|
+
|
|
234
|
+
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp_file:
|
|
235
|
+
tmp_path = tmp_file.name
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
239
|
+
get_full_metadata(tmp_path)
|
|
240
|
+
finally:
|
|
241
|
+
Path(tmp_path).unlink()
|
|
242
|
+
|
|
243
|
+
def test_get_full_metadata_performance_optimization(self, sample_mp3_file: Path):
|
|
244
|
+
"""Test that performance optimization flags work correctly."""
|
|
245
|
+
# Test with minimal data
|
|
246
|
+
result_minimal = get_full_metadata(sample_mp3_file, include_headers=False, include_technical=False)
|
|
247
|
+
|
|
248
|
+
# Should still have basic structure
|
|
249
|
+
assert "unified_metadata" in result_minimal
|
|
250
|
+
assert "metadata_format" in result_minimal
|
|
251
|
+
assert "format_priorities" in result_minimal
|
|
252
|
+
|
|
253
|
+
# Headers and technical info should be minimal
|
|
254
|
+
assert "headers" in result_minimal
|
|
255
|
+
assert "technical_info" in result_minimal
|
|
256
|
+
|
|
257
|
+
def test_get_full_metadata_format_specific_metadata_isolation(self, sample_mp3_file: Path):
|
|
258
|
+
result = get_full_metadata(sample_mp3_file)
|
|
259
|
+
|
|
260
|
+
# Each format should have its own metadata section
|
|
261
|
+
metadata_format = result["metadata_format"]
|
|
262
|
+
|
|
263
|
+
# ID3v2 metadata should be separate from ID3v1
|
|
264
|
+
if "id3v2" in metadata_format and "id3v1" in metadata_format:
|
|
265
|
+
id3v2_metadata = metadata_format["id3v2"]
|
|
266
|
+
id3v1_metadata = metadata_format["id3v1"]
|
|
267
|
+
|
|
268
|
+
# They should be separate dictionaries
|
|
269
|
+
assert isinstance(id3v2_metadata, dict)
|
|
270
|
+
assert isinstance(id3v1_metadata, dict)
|
|
271
|
+
|
|
272
|
+
# They might have different content or structure
|
|
273
|
+
# This is expected and correct behavior
|
|
274
|
+
|
|
275
|
+
def test_get_full_metadata_header_detection_accuracy(self, sample_mp3_file: Path):
|
|
276
|
+
result = get_full_metadata(sample_mp3_file)
|
|
277
|
+
|
|
278
|
+
# Check that headers are detected correctly
|
|
279
|
+
headers = result["headers"]
|
|
280
|
+
|
|
281
|
+
for metadata_format_name, header_info in headers.items():
|
|
282
|
+
assert "present" in header_info
|
|
283
|
+
assert isinstance(header_info["present"], bool)
|
|
284
|
+
|
|
285
|
+
if header_info["present"]:
|
|
286
|
+
# If header is present, should have additional info
|
|
287
|
+
if metadata_format_name == "id3v2":
|
|
288
|
+
assert "version" in header_info
|
|
289
|
+
assert "header_size_bytes" in header_info
|
|
290
|
+
elif metadata_format_name == "id3v1":
|
|
291
|
+
assert "position" in header_info
|
|
292
|
+
assert "size_bytes" in header_info
|
|
293
|
+
elif metadata_format_name == "vorbis":
|
|
294
|
+
assert "vendor_string" in header_info
|
|
295
|
+
assert "comment_count" in header_info
|
|
296
|
+
elif metadata_format_name == "riff":
|
|
297
|
+
assert "chunk_info" in header_info
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Edge case 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
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.integration
|
|
14
|
+
class TestGetFullMetadataEdgeCases:
|
|
15
|
+
def test_get_full_metadata_empty_file(self):
|
|
16
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
17
|
+
# Should handle gracefully and return structure with minimal data
|
|
18
|
+
result = get_full_metadata(temp_file_path)
|
|
19
|
+
|
|
20
|
+
# Should still return complete structure
|
|
21
|
+
assert "unified_metadata" in result
|
|
22
|
+
assert "technical_info" in result
|
|
23
|
+
assert "metadata_format" in result
|
|
24
|
+
assert "headers" in result
|
|
25
|
+
assert "raw_metadata" in result
|
|
26
|
+
assert "format_priorities" in result
|
|
27
|
+
|
|
28
|
+
def test_get_full_metadata_corrupted_file(self):
|
|
29
|
+
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
|
|
30
|
+
temp_path = temp_file.name
|
|
31
|
+
# Write some garbage data
|
|
32
|
+
temp_file.write(b"This is not a valid audio file")
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
with pytest.raises(FileCorruptedError):
|
|
36
|
+
get_full_metadata(temp_path)
|
|
37
|
+
finally:
|
|
38
|
+
Path(temp_path).unlink()
|
|
39
|
+
|
|
40
|
+
def test_get_full_metadata_file_with_only_headers_no_metadata(self):
|
|
41
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
42
|
+
result = get_full_metadata(temp_file_path)
|
|
43
|
+
|
|
44
|
+
# Should detect headers even if no metadata content
|
|
45
|
+
headers = result["headers"]
|
|
46
|
+
|
|
47
|
+
for _metadata_format_name, header_info in headers.items():
|
|
48
|
+
# Headers might be present even without metadata content
|
|
49
|
+
assert "present" in header_info
|
|
50
|
+
assert isinstance(header_info["present"], bool)
|
|
51
|
+
|
|
52
|
+
def test_get_full_metadata_large_file(self, sample_mp3_file: Path):
|
|
53
|
+
# This test ensures the function can handle larger files
|
|
54
|
+
result = get_full_metadata(sample_mp3_file)
|
|
55
|
+
|
|
56
|
+
# Should complete successfully
|
|
57
|
+
assert "unified_metadata" in result
|
|
58
|
+
assert "technical_info" in result
|
|
59
|
+
|
|
60
|
+
# File size should be reasonable
|
|
61
|
+
tech_info = result["technical_info"]
|
|
62
|
+
assert tech_info["file_size_bytes"] > 0
|
|
63
|
+
|
|
64
|
+
def test_get_full_metadata_file_with_mixed_formats(self, sample_mp3_file: Path):
|
|
65
|
+
result = get_full_metadata(sample_mp3_file)
|
|
66
|
+
|
|
67
|
+
# Should handle multiple formats gracefully
|
|
68
|
+
metadata_format = result["metadata_format"]
|
|
69
|
+
headers = result["headers"]
|
|
70
|
+
|
|
71
|
+
# Each format should have its own section
|
|
72
|
+
for metadata_format_name in ["id3v2", "id3v1"]:
|
|
73
|
+
assert metadata_format_name in metadata_format
|
|
74
|
+
assert metadata_format_name in headers
|
|
75
|
+
|
|
76
|
+
# Each should be a dictionary
|
|
77
|
+
assert isinstance(metadata_format[metadata_format_name], dict)
|
|
78
|
+
assert isinstance(headers[metadata_format_name], dict)
|
|
79
|
+
|
|
80
|
+
def test_get_full_metadata_with_unicode_metadata(self, sample_mp3_file: Path):
|
|
81
|
+
# This test ensures unicode handling works correctly
|
|
82
|
+
result = get_full_metadata(sample_mp3_file)
|
|
83
|
+
|
|
84
|
+
# Should handle unicode in metadata
|
|
85
|
+
unified_metadata = result["unified_metadata"]
|
|
86
|
+
|
|
87
|
+
# Check that string values are properly handled
|
|
88
|
+
for _key, value in unified_metadata.items():
|
|
89
|
+
if isinstance(value, str):
|
|
90
|
+
# Should be able to handle unicode
|
|
91
|
+
assert isinstance(value, str)
|
|
92
|
+
elif isinstance(value, list):
|
|
93
|
+
for item in value:
|
|
94
|
+
if isinstance(item, str):
|
|
95
|
+
assert isinstance(item, str)
|
|
96
|
+
|
|
97
|
+
def test_get_full_metadata_performance_with_headers_disabled(self, sample_mp3_file: Path):
|
|
98
|
+
result = get_full_metadata(sample_mp3_file, include_headers=False)
|
|
99
|
+
|
|
100
|
+
# Should still work correctly
|
|
101
|
+
assert "unified_metadata" in result
|
|
102
|
+
assert "technical_info" in result
|
|
103
|
+
assert "metadata_format" in result
|
|
104
|
+
|
|
105
|
+
# Headers should be minimal
|
|
106
|
+
headers = result["headers"]
|
|
107
|
+
for _metadata_format_name, header_info in headers.items():
|
|
108
|
+
# Should have basic structure but minimal data
|
|
109
|
+
assert "present" in header_info
|
|
110
|
+
|
|
111
|
+
def test_get_full_metadata_performance_with_technical_disabled(self, sample_mp3_file: Path):
|
|
112
|
+
result = get_full_metadata(sample_mp3_file, include_technical=False)
|
|
113
|
+
|
|
114
|
+
# Should still work correctly
|
|
115
|
+
assert "unified_metadata" in result
|
|
116
|
+
assert "metadata_format" in result
|
|
117
|
+
assert "headers" in result
|
|
118
|
+
|
|
119
|
+
# Technical info should be minimal
|
|
120
|
+
tech_info = result["technical_info"]
|
|
121
|
+
assert isinstance(tech_info, dict)
|
|
122
|
+
|
|
123
|
+
def test_get_full_metadata_memory_usage(self, sample_mp3_file: Path):
|
|
124
|
+
# This is more of a smoke test to ensure no obvious memory leaks
|
|
125
|
+
for _ in range(10):
|
|
126
|
+
result = get_full_metadata(sample_mp3_file)
|
|
127
|
+
|
|
128
|
+
# Should complete successfully each time
|
|
129
|
+
assert "unified_metadata" in result
|
|
130
|
+
assert "technical_info" in result
|
|
131
|
+
|
|
132
|
+
# Clear result to help with memory management
|
|
133
|
+
del result
|
|
134
|
+
|
|
135
|
+
def test_get_full_metadata_concurrent_access(self, sample_mp3_file: Path):
|
|
136
|
+
import threading
|
|
137
|
+
|
|
138
|
+
results = []
|
|
139
|
+
errors = []
|
|
140
|
+
|
|
141
|
+
def get_metadata():
|
|
142
|
+
try:
|
|
143
|
+
result = get_full_metadata(sample_mp3_file)
|
|
144
|
+
results.append(result)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
errors.append(e)
|
|
147
|
+
|
|
148
|
+
# Create multiple threads accessing the same file
|
|
149
|
+
threads = []
|
|
150
|
+
for _ in range(5):
|
|
151
|
+
thread = threading.Thread(target=get_metadata)
|
|
152
|
+
threads.append(thread)
|
|
153
|
+
thread.start()
|
|
154
|
+
|
|
155
|
+
# Wait for all threads to complete
|
|
156
|
+
for thread in threads:
|
|
157
|
+
thread.join()
|
|
158
|
+
|
|
159
|
+
# Should have 5 successful results
|
|
160
|
+
assert len(results) == 5
|
|
161
|
+
assert len(errors) == 0
|
|
162
|
+
|
|
163
|
+
# All results should be identical
|
|
164
|
+
first_result = results[0]
|
|
165
|
+
for result in results[1:]:
|
|
166
|
+
assert result["format_priorities"] == first_result["format_priorities"]
|
|
167
|
+
assert result["technical_info"]["file_size_bytes"] == first_result["technical_info"]["file_size_bytes"]
|
|
168
|
+
|
|
169
|
+
def test_get_full_metadata_error_recovery(self):
|
|
170
|
+
# Test with non-existent file
|
|
171
|
+
with pytest.raises(FileNotFoundError):
|
|
172
|
+
get_full_metadata("non_existent_file.mp3")
|
|
173
|
+
|
|
174
|
+
# Test with unsupported file type (create the file first)
|
|
175
|
+
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as temp_file:
|
|
176
|
+
temp_path = temp_file.name
|
|
177
|
+
temp_file.write(b"This is not an audio file")
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
181
|
+
get_full_metadata(temp_path)
|
|
182
|
+
finally:
|
|
183
|
+
Path(temp_path).unlink()
|
|
184
|
+
|
|
185
|
+
def test_get_full_metadata_structure_consistency(self, sample_mp3_file: Path):
|
|
186
|
+
result1 = get_full_metadata(sample_mp3_file)
|
|
187
|
+
result2 = get_full_metadata(sample_mp3_file)
|
|
188
|
+
|
|
189
|
+
# Structure should be identical
|
|
190
|
+
assert set(result1.keys()) == set(result2.keys())
|
|
191
|
+
|
|
192
|
+
# Each top-level section should have same keys
|
|
193
|
+
for key in result1:
|
|
194
|
+
if key in ["unified_metadata", "technical_info", "metadata_format", "headers", "raw_metadata"]:
|
|
195
|
+
assert set(result1[key].keys()) == set(result2[key].keys())
|
|
196
|
+
|
|
197
|
+
def test_get_full_metadata_with_minimal_metadata(self):
|
|
198
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
199
|
+
result = get_full_metadata(temp_file_path)
|
|
200
|
+
|
|
201
|
+
# Should still return complete structure
|
|
202
|
+
assert "unified_metadata" in result
|
|
203
|
+
assert "technical_info" in result
|
|
204
|
+
assert "metadata_format" in result
|
|
205
|
+
assert "headers" in result
|
|
206
|
+
assert "raw_metadata" in result
|
|
207
|
+
assert "format_priorities" in result
|
|
208
|
+
|
|
209
|
+
# Unified metadata might be empty or minimal
|
|
210
|
+
unified_metadata = result["unified_metadata"]
|
|
211
|
+
assert isinstance(unified_metadata, dict)
|
|
212
|
+
|
|
213
|
+
# Technical info should still be present
|
|
214
|
+
tech_info = result["technical_info"]
|
|
215
|
+
assert "file_size_bytes" in tech_info
|
|
216
|
+
assert tech_info["file_size_bytes"] >= 0 # Can be 0 for empty files
|
|
217
|
+
|
|
218
|
+
def test_get_full_metadata_format_detection_accuracy(self, sample_mp3_file: Path):
|
|
219
|
+
result = get_full_metadata(sample_mp3_file)
|
|
220
|
+
|
|
221
|
+
# Format priorities should be correct for MP3
|
|
222
|
+
priorities = result["format_priorities"]
|
|
223
|
+
assert priorities["file_extension"] == ".mp3"
|
|
224
|
+
assert "id3v2" in priorities["reading_order"]
|
|
225
|
+
assert "id3v1" in priorities["reading_order"]
|
|
226
|
+
assert priorities["writing_format"] == "id3v2"
|
|
227
|
+
|
|
228
|
+
# Technical info should reflect MP3 format
|
|
229
|
+
tech_info = result["technical_info"]
|
|
230
|
+
assert tech_info["file_extension"] == ".mp3"
|
|
231
|
+
assert tech_info["audio_format_name"] == "MP3"
|