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,318 @@
|
|
|
1
|
+
"""End-to-end tests for metadata deletion workflows.
|
|
2
|
+
|
|
3
|
+
These tests verify complete deletion workflows that real users would perform, including full metadata deletion, partial
|
|
4
|
+
deletion, and cross-format deletion.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from audiometa import delete_all_metadata, get_unified_metadata, update_metadata
|
|
10
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
11
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
12
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.mark.e2e
|
|
16
|
+
class TestDeletionWorkflows:
|
|
17
|
+
def test_complete_metadata_deletion_workflow_mp3(self):
|
|
18
|
+
# Complete e2e deletion workflow for MP3
|
|
19
|
+
initial_metadata = {
|
|
20
|
+
"title": "Original Title",
|
|
21
|
+
"artist": "Original Artist",
|
|
22
|
+
"album": "Original Album",
|
|
23
|
+
"year": "2023",
|
|
24
|
+
"genre": "Rock",
|
|
25
|
+
"comment": "Original comment",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
29
|
+
# 1. Verify initial metadata exists
|
|
30
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
31
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Original Title"
|
|
32
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.ARTISTS) == ["Original Artist"]
|
|
33
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.ALBUM) == "Original Album"
|
|
34
|
+
|
|
35
|
+
# 2. Add more metadata using app's function
|
|
36
|
+
additional_metadata = {
|
|
37
|
+
UnifiedMetadataKey.RATING: 80,
|
|
38
|
+
UnifiedMetadataKey.BPM: 120,
|
|
39
|
+
UnifiedMetadataKey.COMMENT: "Updated comment",
|
|
40
|
+
}
|
|
41
|
+
update_metadata(test_file, additional_metadata, normalized_rating_max_value=100)
|
|
42
|
+
|
|
43
|
+
# 3. Verify metadata was added
|
|
44
|
+
updated_metadata = get_unified_metadata(test_file, normalized_rating_max_value=100)
|
|
45
|
+
assert updated_metadata.get(UnifiedMetadataKey.RATING) == 80
|
|
46
|
+
assert updated_metadata.get(UnifiedMetadataKey.BPM) == 120
|
|
47
|
+
|
|
48
|
+
# 4. Delete all metadata
|
|
49
|
+
delete_result = delete_all_metadata(test_file)
|
|
50
|
+
assert delete_result is True
|
|
51
|
+
|
|
52
|
+
# 5. Verify all metadata was deleted
|
|
53
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
54
|
+
assert (
|
|
55
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
56
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "Original Title"
|
|
57
|
+
)
|
|
58
|
+
assert deleted_metadata.get(UnifiedMetadataKey.ARTISTS) is None or deleted_metadata.get(
|
|
59
|
+
UnifiedMetadataKey.ARTISTS
|
|
60
|
+
) != ["Original Artist"]
|
|
61
|
+
assert (
|
|
62
|
+
deleted_metadata.get(UnifiedMetadataKey.ALBUM) is None
|
|
63
|
+
or deleted_metadata.get(UnifiedMetadataKey.ALBUM) != "Original Album"
|
|
64
|
+
)
|
|
65
|
+
assert deleted_metadata.get(UnifiedMetadataKey.RATING) is None
|
|
66
|
+
assert deleted_metadata.get(UnifiedMetadataKey.BPM) is None
|
|
67
|
+
|
|
68
|
+
def test_complete_metadata_deletion_workflow_flac(self):
|
|
69
|
+
# Complete e2e deletion workflow for FLAC
|
|
70
|
+
initial_metadata = {
|
|
71
|
+
"title": "FLAC Original Title",
|
|
72
|
+
"artist": "FLAC Original Artist",
|
|
73
|
+
"album": "FLAC Original Album",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
with temp_file_with_metadata(initial_metadata, "flac") as test_file:
|
|
77
|
+
# 1. Verify initial metadata exists
|
|
78
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
79
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "FLAC Original Title"
|
|
80
|
+
|
|
81
|
+
# 2. Add more metadata
|
|
82
|
+
additional_metadata = {
|
|
83
|
+
UnifiedMetadataKey.RATING: 90,
|
|
84
|
+
UnifiedMetadataKey.BPM: 140,
|
|
85
|
+
UnifiedMetadataKey.COMMENT: "FLAC comment",
|
|
86
|
+
}
|
|
87
|
+
update_metadata(test_file, additional_metadata, normalized_rating_max_value=100)
|
|
88
|
+
|
|
89
|
+
# 3. Delete all metadata
|
|
90
|
+
delete_result = delete_all_metadata(test_file)
|
|
91
|
+
assert delete_result is True
|
|
92
|
+
|
|
93
|
+
# 4. Verify all metadata was deleted
|
|
94
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
95
|
+
assert (
|
|
96
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
97
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "FLAC Original Title"
|
|
98
|
+
)
|
|
99
|
+
assert deleted_metadata.get(UnifiedMetadataKey.RATING) is None
|
|
100
|
+
|
|
101
|
+
def test_complete_metadata_deletion_workflow_wav(self):
|
|
102
|
+
# Complete e2e deletion workflow for WAV
|
|
103
|
+
initial_metadata = {
|
|
104
|
+
"title": "WAV Original Title",
|
|
105
|
+
"artist": "WAV Original Artist",
|
|
106
|
+
"album": "WAV Original Album",
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
110
|
+
# 1. Verify initial metadata exists
|
|
111
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
112
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "WAV Original Title"
|
|
113
|
+
|
|
114
|
+
# 2. Add more metadata (WAV doesn't support rating/BPM)
|
|
115
|
+
additional_metadata = {UnifiedMetadataKey.COMMENT: "WAV comment"}
|
|
116
|
+
update_metadata(test_file, additional_metadata)
|
|
117
|
+
|
|
118
|
+
# 3. Delete all metadata
|
|
119
|
+
delete_result = delete_all_metadata(test_file)
|
|
120
|
+
assert delete_result is True
|
|
121
|
+
|
|
122
|
+
# 4. Verify all metadata was deleted
|
|
123
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
124
|
+
assert (
|
|
125
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
126
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "WAV Original Title"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def test_partial_metadata_deletion_workflow(self):
|
|
130
|
+
# E2e test for deleting specific metadata fields
|
|
131
|
+
initial_metadata = {
|
|
132
|
+
"title": "Partial Deletion Title",
|
|
133
|
+
"artist": "Partial Deletion Artist",
|
|
134
|
+
"album": "Partial Deletion Album",
|
|
135
|
+
"year": "2023",
|
|
136
|
+
"genre": "Jazz",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
140
|
+
# 1. Verify initial metadata
|
|
141
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
142
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Partial Deletion Title"
|
|
143
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.ARTISTS) == ["Partial Deletion Artist"]
|
|
144
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.ALBUM) == "Partial Deletion Album"
|
|
145
|
+
|
|
146
|
+
# 2. Delete specific fields by setting them to None
|
|
147
|
+
deletion_metadata = {UnifiedMetadataKey.TITLE: None, UnifiedMetadataKey.ARTISTS: None}
|
|
148
|
+
update_metadata(test_file, deletion_metadata)
|
|
149
|
+
|
|
150
|
+
# 3. Verify specific fields were deleted while others remain
|
|
151
|
+
updated_metadata = get_unified_metadata(test_file)
|
|
152
|
+
assert updated_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
153
|
+
assert updated_metadata.get(UnifiedMetadataKey.ARTISTS) is None
|
|
154
|
+
assert updated_metadata.get(UnifiedMetadataKey.ALBUM) == "Partial Deletion Album" # Should remain
|
|
155
|
+
|
|
156
|
+
# 4. Delete remaining metadata
|
|
157
|
+
remaining_deletion = {
|
|
158
|
+
UnifiedMetadataKey.ALBUM: None,
|
|
159
|
+
UnifiedMetadataKey.RELEASE_DATE: None,
|
|
160
|
+
UnifiedMetadataKey.GENRES_NAMES: None,
|
|
161
|
+
}
|
|
162
|
+
update_metadata(test_file, remaining_deletion)
|
|
163
|
+
|
|
164
|
+
# 5. Verify all metadata is now deleted
|
|
165
|
+
final_metadata = get_unified_metadata(test_file)
|
|
166
|
+
assert final_metadata.get(UnifiedMetadataKey.ALBUM) is None
|
|
167
|
+
assert final_metadata.get(UnifiedMetadataKey.RELEASE_DATE) is None
|
|
168
|
+
assert final_metadata.get(UnifiedMetadataKey.GENRES_NAMES) is None
|
|
169
|
+
|
|
170
|
+
def test_cross_format_deletion_consistency(self, sample_mp3_file, sample_flac_file, sample_wav_file):
|
|
171
|
+
# E2e test for deletion consistency across formats
|
|
172
|
+
test_metadata = {
|
|
173
|
+
UnifiedMetadataKey.TITLE: "Cross Format Deletion Test",
|
|
174
|
+
UnifiedMetadataKey.ARTISTS: ["Test Artist"],
|
|
175
|
+
UnifiedMetadataKey.ALBUM: "Test Album",
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
sample_files = [(sample_mp3_file, "mp3"), (sample_flac_file, "flac"), (sample_wav_file, "wav")]
|
|
179
|
+
|
|
180
|
+
for _file_path, format_type in sample_files:
|
|
181
|
+
# Set up metadata using external script
|
|
182
|
+
initial_metadata = {"title": "Original Title", "artist": "Original Artist"}
|
|
183
|
+
with temp_file_with_metadata(initial_metadata, format_type) as test_file:
|
|
184
|
+
# Add metadata using app's function
|
|
185
|
+
update_metadata(test_file, test_metadata)
|
|
186
|
+
|
|
187
|
+
# Verify metadata was added
|
|
188
|
+
added_metadata = get_unified_metadata(test_file)
|
|
189
|
+
assert added_metadata.get(UnifiedMetadataKey.TITLE) == "Cross Format Deletion Test"
|
|
190
|
+
|
|
191
|
+
# Delete all metadata
|
|
192
|
+
delete_result = delete_all_metadata(test_file)
|
|
193
|
+
assert delete_result is True
|
|
194
|
+
|
|
195
|
+
# Verify metadata was deleted consistently across formats
|
|
196
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
197
|
+
assert (
|
|
198
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
199
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "Cross Format Deletion Test"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
def test_format_specific_deletion_workflow(self):
|
|
203
|
+
# E2e test for deleting specific metadata formats
|
|
204
|
+
initial_metadata = {"title": "Format Specific Deletion", "artist": "Format Specific Artist"}
|
|
205
|
+
|
|
206
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
207
|
+
# 1. Add metadata in different formats
|
|
208
|
+
id3v2_metadata = {UnifiedMetadataKey.TITLE: "ID3v2 Title", UnifiedMetadataKey.ARTISTS: ["ID3v2 Artist"]}
|
|
209
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
210
|
+
|
|
211
|
+
id3v1_metadata = {UnifiedMetadataKey.ALBUM: "ID3v1 Album"}
|
|
212
|
+
update_metadata(test_file, id3v1_metadata, metadata_format=MetadataFormat.ID3V1)
|
|
213
|
+
|
|
214
|
+
# 2. Verify both formats have metadata
|
|
215
|
+
combined_metadata = get_unified_metadata(test_file)
|
|
216
|
+
assert combined_metadata.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
217
|
+
assert combined_metadata.get(UnifiedMetadataKey.ALBUM) == "ID3v1 Album"
|
|
218
|
+
|
|
219
|
+
# 3. Delete only ID3v2 metadata
|
|
220
|
+
id3v2_deletion = {UnifiedMetadataKey.TITLE: None, UnifiedMetadataKey.ARTISTS: None}
|
|
221
|
+
update_metadata(test_file, id3v2_deletion, metadata_format=MetadataFormat.ID3V2)
|
|
222
|
+
|
|
223
|
+
# 4. Verify ID3v2 metadata is deleted but ID3v1 remains
|
|
224
|
+
after_id3v2_deletion = get_unified_metadata(test_file)
|
|
225
|
+
assert (
|
|
226
|
+
after_id3v2_deletion.get(UnifiedMetadataKey.TITLE) is None
|
|
227
|
+
or after_id3v2_deletion.get(UnifiedMetadataKey.TITLE) != "ID3v2 Title"
|
|
228
|
+
)
|
|
229
|
+
assert after_id3v2_deletion.get(UnifiedMetadataKey.ALBUM) == "ID3v1 Album" # Should remain
|
|
230
|
+
|
|
231
|
+
# 5. Delete ID3v1 metadata
|
|
232
|
+
id3v1_deletion = {UnifiedMetadataKey.ALBUM: None}
|
|
233
|
+
update_metadata(test_file, id3v1_deletion, metadata_format=MetadataFormat.ID3V1)
|
|
234
|
+
|
|
235
|
+
# 6. Verify all metadata is now deleted
|
|
236
|
+
final_metadata = get_unified_metadata(test_file)
|
|
237
|
+
assert final_metadata.get(UnifiedMetadataKey.ALBUM) is None
|
|
238
|
+
|
|
239
|
+
def test_deletion_error_handling_workflow(self):
|
|
240
|
+
# E2e test for deletion error scenarios
|
|
241
|
+
# Create a file with unsupported extension
|
|
242
|
+
with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
|
|
243
|
+
test_file = temp_audio_file_path.with_suffix(".txt")
|
|
244
|
+
test_file.write_bytes(b"fake audio content")
|
|
245
|
+
|
|
246
|
+
# All deletion operations should raise appropriate errors
|
|
247
|
+
from audiometa.exceptions import FileTypeNotSupportedError
|
|
248
|
+
|
|
249
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
250
|
+
delete_all_metadata(str(test_file))
|
|
251
|
+
|
|
252
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
253
|
+
update_metadata(str(test_file), {UnifiedMetadataKey.TITLE: None})
|
|
254
|
+
|
|
255
|
+
def test_deletion_with_rating_normalization_workflow(self):
|
|
256
|
+
# E2e test for deletion with rating normalization
|
|
257
|
+
initial_metadata = {"title": "Rating Deletion Test", "artist": "Rating Artist"}
|
|
258
|
+
|
|
259
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
260
|
+
# 1. Add rating with normalization
|
|
261
|
+
rating_metadata = {UnifiedMetadataKey.RATING: 80}
|
|
262
|
+
update_metadata(test_file, rating_metadata, normalized_rating_max_value=100)
|
|
263
|
+
|
|
264
|
+
# 2. Verify rating was added
|
|
265
|
+
metadata_with_rating = get_unified_metadata(test_file, normalized_rating_max_value=100)
|
|
266
|
+
assert metadata_with_rating.get(UnifiedMetadataKey.RATING) == 80
|
|
267
|
+
|
|
268
|
+
# 3. Delete rating
|
|
269
|
+
rating_deletion = {UnifiedMetadataKey.RATING: None}
|
|
270
|
+
update_metadata(test_file, rating_deletion, normalized_rating_max_value=100)
|
|
271
|
+
|
|
272
|
+
# 4. Verify rating was deleted
|
|
273
|
+
metadata_after_deletion = get_unified_metadata(test_file, normalized_rating_max_value=100)
|
|
274
|
+
assert metadata_after_deletion.get(UnifiedMetadataKey.RATING) is None
|
|
275
|
+
|
|
276
|
+
def test_batch_deletion_workflow(self, sample_mp3_file, sample_flac_file, sample_wav_file):
|
|
277
|
+
# E2e test for batch deletion operations
|
|
278
|
+
sample_files = [(sample_mp3_file, "mp3"), (sample_flac_file, "flac"), (sample_wav_file, "wav")]
|
|
279
|
+
|
|
280
|
+
results = []
|
|
281
|
+
|
|
282
|
+
for _file_path, format_type in sample_files:
|
|
283
|
+
try:
|
|
284
|
+
# Set up metadata using external script
|
|
285
|
+
initial_metadata = {"title": f"Batch Deletion Test {format_type.upper()}", "artist": "Batch Artist"}
|
|
286
|
+
with temp_file_with_metadata(initial_metadata, format_type) as test_file:
|
|
287
|
+
# Add more metadata using app's function
|
|
288
|
+
additional_metadata = {
|
|
289
|
+
UnifiedMetadataKey.ALBUM: f"Batch Album {format_type.upper()}",
|
|
290
|
+
UnifiedMetadataKey.COMMENT: f"Batch comment for {format_type}",
|
|
291
|
+
}
|
|
292
|
+
update_metadata(test_file, additional_metadata)
|
|
293
|
+
|
|
294
|
+
# Verify metadata was added
|
|
295
|
+
added_metadata = get_unified_metadata(test_file)
|
|
296
|
+
assert added_metadata.get(UnifiedMetadataKey.TITLE) == f"Batch Deletion Test {format_type.upper()}"
|
|
297
|
+
|
|
298
|
+
# Delete all metadata
|
|
299
|
+
delete_result = delete_all_metadata(test_file)
|
|
300
|
+
assert delete_result is True
|
|
301
|
+
|
|
302
|
+
# Verify deletion worked
|
|
303
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
304
|
+
assert (
|
|
305
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
306
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE)
|
|
307
|
+
!= f"Batch Deletion Test {format_type.upper()}"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
results.append(("success", format_type))
|
|
311
|
+
|
|
312
|
+
except Exception as e:
|
|
313
|
+
results.append(("error", format_type, str(e)))
|
|
314
|
+
|
|
315
|
+
# Verify all files were processed successfully
|
|
316
|
+
assert len(results) == len(sample_files)
|
|
317
|
+
success_count = sum(1 for result in results if result[0] == "success")
|
|
318
|
+
assert success_count == len(sample_files) # All should succeed
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""End-to-end tests for error handling and recovery workflows.
|
|
2
|
+
|
|
3
|
+
These tests verify that the system handles errors gracefully and recovers properly from various error conditions in
|
|
4
|
+
real-world scenarios.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from audiometa import delete_all_metadata, get_bitrate, get_duration_in_sec, get_unified_metadata, update_metadata
|
|
10
|
+
from audiometa.exceptions import InvalidMetadataFieldFormatError, InvalidRatingValueError
|
|
11
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
12
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.mark.e2e
|
|
16
|
+
class TestErrorHandlingWorkflows:
|
|
17
|
+
def test_error_recovery_workflow(self):
|
|
18
|
+
# E2E test for error scenarios and recovery
|
|
19
|
+
initial_metadata = {"title": "Original Title", "artist": "Original Artist"}
|
|
20
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
21
|
+
# 1. Verify initial metadata exists
|
|
22
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
23
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Original Title"
|
|
24
|
+
|
|
25
|
+
# 2. Test invalid rating operation - should raise InvalidRatingValueError
|
|
26
|
+
# Using normalized mode with invalid value (110 is over the max value and results in invalid star rating
|
|
27
|
+
# index > 10)
|
|
28
|
+
with pytest.raises(InvalidRatingValueError) as exc_info:
|
|
29
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: 110}, normalized_rating_max_value=100)
|
|
30
|
+
assert "out of range" in str(exc_info.value) or "invalid star rating index" in str(exc_info.value)
|
|
31
|
+
|
|
32
|
+
# 3. Test recovery after error - verify the file is still usable
|
|
33
|
+
# The file should still have its original metadata intact
|
|
34
|
+
metadata_after_error = get_unified_metadata(test_file)
|
|
35
|
+
assert metadata_after_error.get(UnifiedMetadataKey.TITLE) == "Original Title"
|
|
36
|
+
|
|
37
|
+
# 4. Test successful operation after error - should work normally
|
|
38
|
+
recovery_metadata = {UnifiedMetadataKey.TITLE: "Recovery Test"}
|
|
39
|
+
update_metadata(test_file, recovery_metadata)
|
|
40
|
+
|
|
41
|
+
# 5. Verify recovery worked - file is fully functional
|
|
42
|
+
final_metadata = get_unified_metadata(test_file)
|
|
43
|
+
assert final_metadata.get(UnifiedMetadataKey.TITLE) == "Recovery Test"
|
|
44
|
+
|
|
45
|
+
def test_error_handling_workflow(self):
|
|
46
|
+
# Create a file with unsupported extension
|
|
47
|
+
with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
|
|
48
|
+
temp_audio_file_path.write_bytes(b"fake audio content")
|
|
49
|
+
test_file = temp_audio_file_path.with_suffix(".txt")
|
|
50
|
+
test_file.write_bytes(b"fake audio content")
|
|
51
|
+
|
|
52
|
+
# All operations should raise FileTypeNotSupportedError
|
|
53
|
+
from audiometa.exceptions import FileTypeNotSupportedError
|
|
54
|
+
|
|
55
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
56
|
+
get_unified_metadata(str(test_file))
|
|
57
|
+
|
|
58
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
59
|
+
update_metadata(str(test_file), {UnifiedMetadataKey.TITLE: "Test"})
|
|
60
|
+
|
|
61
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
62
|
+
delete_all_metadata(str(test_file))
|
|
63
|
+
|
|
64
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
65
|
+
get_bitrate(str(test_file))
|
|
66
|
+
|
|
67
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
68
|
+
get_duration_in_sec(str(test_file))
|
|
69
|
+
|
|
70
|
+
def test_deletion_error_recovery_workflow(self):
|
|
71
|
+
# E2E test for deletion error scenarios and recovery
|
|
72
|
+
initial_metadata = {"title": "Deletion Error Test", "artist": "Deletion Error Artist"}
|
|
73
|
+
|
|
74
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
75
|
+
# 1. Verify initial metadata exists
|
|
76
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
77
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Deletion Error Test"
|
|
78
|
+
|
|
79
|
+
# 2. Test deletion on file that doesn't exist
|
|
80
|
+
non_existent_file = test_file.parent / "non_existent.mp3"
|
|
81
|
+
with pytest.raises(FileNotFoundError):
|
|
82
|
+
delete_all_metadata(str(non_existent_file))
|
|
83
|
+
|
|
84
|
+
# 3. Test deletion on directory instead of file
|
|
85
|
+
from audiometa.exceptions import FileTypeNotSupportedError
|
|
86
|
+
|
|
87
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
88
|
+
delete_all_metadata(str(test_file.parent))
|
|
89
|
+
|
|
90
|
+
# 4. Verify original file is still usable after errors
|
|
91
|
+
metadata_after_errors = get_unified_metadata(test_file)
|
|
92
|
+
assert metadata_after_errors.get(UnifiedMetadataKey.TITLE) == "Deletion Error Test"
|
|
93
|
+
|
|
94
|
+
# 5. Successfully delete metadata from original file
|
|
95
|
+
delete_result = delete_all_metadata(test_file)
|
|
96
|
+
assert delete_result is True
|
|
97
|
+
|
|
98
|
+
# 6. Verify deletion worked
|
|
99
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
100
|
+
assert (
|
|
101
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
102
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "Deletion Error Test"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def test_deletion_with_corrupted_metadata_workflow(self):
|
|
106
|
+
# E2E test for deletion when metadata might be corrupted
|
|
107
|
+
initial_metadata = {"title": "Corrupted Metadata Test", "artist": "Corrupted Artist"}
|
|
108
|
+
|
|
109
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
110
|
+
# 1. Verify initial metadata exists
|
|
111
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
112
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Corrupted Metadata Test"
|
|
113
|
+
|
|
114
|
+
# 2. Try to delete metadata - should work even if some metadata is corrupted
|
|
115
|
+
delete_result = delete_all_metadata(test_file)
|
|
116
|
+
assert delete_result is True
|
|
117
|
+
|
|
118
|
+
# 3. Verify deletion worked
|
|
119
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
120
|
+
assert (
|
|
121
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
122
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "Corrupted Metadata Test"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# 4. Verify file is still usable after deletion
|
|
126
|
+
# Try to add new metadata
|
|
127
|
+
new_metadata = {UnifiedMetadataKey.TITLE: "New Title After Deletion"}
|
|
128
|
+
update_metadata(test_file, new_metadata)
|
|
129
|
+
|
|
130
|
+
# 5. Verify new metadata was added successfully
|
|
131
|
+
new_metadata_result = get_unified_metadata(test_file)
|
|
132
|
+
assert new_metadata_result.get(UnifiedMetadataKey.TITLE) == "New Title After Deletion"
|
|
133
|
+
|
|
134
|
+
def test_date_format_validation_workflow(self):
|
|
135
|
+
initial_metadata = {"title": "Date Validation Test", "artist": "Date Test Artist"}
|
|
136
|
+
|
|
137
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
138
|
+
# 1. Verify initial metadata exists
|
|
139
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
140
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Date Validation Test"
|
|
141
|
+
|
|
142
|
+
# 2. Test invalid date formats - should raise InvalidMetadataFieldFormatError
|
|
143
|
+
invalid_dates = [
|
|
144
|
+
"2024/01/01",
|
|
145
|
+
"2024-1-1",
|
|
146
|
+
"not-a-date",
|
|
147
|
+
"24",
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
for invalid_date in invalid_dates:
|
|
151
|
+
with pytest.raises(InvalidMetadataFieldFormatError) as exc_info:
|
|
152
|
+
update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: invalid_date})
|
|
153
|
+
error = exc_info.value
|
|
154
|
+
assert error.field == UnifiedMetadataKey.RELEASE_DATE.value
|
|
155
|
+
assert error.value == invalid_date
|
|
156
|
+
|
|
157
|
+
# 3. Verify file is still usable after validation errors (validation happens before file write)
|
|
158
|
+
metadata_after_errors = get_unified_metadata(test_file)
|
|
159
|
+
assert metadata_after_errors.get(UnifiedMetadataKey.TITLE) == "Date Validation Test"
|
|
160
|
+
|
|
161
|
+
# 4. Test valid date format - should succeed
|
|
162
|
+
# Update with valid YYYY-MM-DD format
|
|
163
|
+
update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: "2024-01-01"})
|
|
164
|
+
updated_metadata = get_unified_metadata(test_file)
|
|
165
|
+
assert updated_metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2024-01-01"
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""End-to-end tests for format-specific metadata workflows.
|
|
2
|
+
|
|
3
|
+
These tests verify that the system works correctly with different audio formats (MP3, FLAC, WAV) and their specific
|
|
4
|
+
capabilities and limitations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from audiometa import delete_all_metadata, get_bitrate, get_duration_in_sec, get_unified_metadata, update_metadata
|
|
10
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
11
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.e2e
|
|
15
|
+
class TestFormatSpecificWorkflows:
|
|
16
|
+
def test_complete_metadata_workflow_mp3(self):
|
|
17
|
+
# Use external script to set initial metadata
|
|
18
|
+
initial_metadata = {"title": "Initial MP3 Title", "artist": "Initial MP3 Artist", "album": "Initial MP3 Album"}
|
|
19
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
20
|
+
# 1. Read initial metadata
|
|
21
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
22
|
+
assert isinstance(initial_metadata_result, dict)
|
|
23
|
+
|
|
24
|
+
# 2. Update metadata using app's function (this is what we're testing)
|
|
25
|
+
test_metadata = {
|
|
26
|
+
UnifiedMetadataKey.TITLE: "Integration Test Title",
|
|
27
|
+
UnifiedMetadataKey.ARTISTS: ["Integration Test Artist"],
|
|
28
|
+
UnifiedMetadataKey.ALBUM: "Integration Test Album",
|
|
29
|
+
UnifiedMetadataKey.RATING: 90,
|
|
30
|
+
UnifiedMetadataKey.BPM: 130,
|
|
31
|
+
}
|
|
32
|
+
update_metadata(test_file, test_metadata, normalized_rating_max_value=100)
|
|
33
|
+
|
|
34
|
+
# 3. Verify metadata was updated
|
|
35
|
+
updated_metadata = get_unified_metadata(test_file, normalized_rating_max_value=100)
|
|
36
|
+
assert updated_metadata.get(UnifiedMetadataKey.TITLE) == "Integration Test Title"
|
|
37
|
+
assert updated_metadata.get(UnifiedMetadataKey.ARTISTS) == ["Integration Test Artist"]
|
|
38
|
+
assert updated_metadata.get(UnifiedMetadataKey.ALBUM) == "Integration Test Album"
|
|
39
|
+
assert updated_metadata.get(UnifiedMetadataKey.RATING) == 90
|
|
40
|
+
assert updated_metadata.get(UnifiedMetadataKey.BPM) == 130
|
|
41
|
+
|
|
42
|
+
# 4. Test technical information
|
|
43
|
+
bitrate = get_bitrate(test_file)
|
|
44
|
+
duration = get_duration_in_sec(test_file)
|
|
45
|
+
assert isinstance(bitrate, int)
|
|
46
|
+
assert isinstance(duration, float)
|
|
47
|
+
assert bitrate > 0
|
|
48
|
+
assert duration > 0
|
|
49
|
+
|
|
50
|
+
# 5. Delete metadata
|
|
51
|
+
delete_result = delete_all_metadata(test_file)
|
|
52
|
+
assert delete_result is True
|
|
53
|
+
|
|
54
|
+
# 6. Verify metadata was deleted
|
|
55
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
56
|
+
# After deletion, metadata should be empty or minimal
|
|
57
|
+
assert (
|
|
58
|
+
UnifiedMetadataKey.TITLE not in deleted_metadata
|
|
59
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "Integration Test Title"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def test_complete_metadata_workflow_flac(self):
|
|
63
|
+
# Use external script to set initial metadata
|
|
64
|
+
initial_metadata = {
|
|
65
|
+
"title": "Initial FLAC Title",
|
|
66
|
+
"artist": "Initial FLAC Artist",
|
|
67
|
+
"album": "Initial FLAC Album",
|
|
68
|
+
}
|
|
69
|
+
with temp_file_with_metadata(initial_metadata, "flac") as test_file:
|
|
70
|
+
# 1. Read initial metadata
|
|
71
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
72
|
+
assert isinstance(initial_metadata_result, dict)
|
|
73
|
+
|
|
74
|
+
# 2. Update metadata using app's function (this is what we're testing)
|
|
75
|
+
test_metadata = {
|
|
76
|
+
UnifiedMetadataKey.TITLE: "FLAC Integration Test Title",
|
|
77
|
+
UnifiedMetadataKey.ARTISTS: ["FLAC Integration Test Artist"],
|
|
78
|
+
UnifiedMetadataKey.ALBUM: "FLAC Integration Test Album",
|
|
79
|
+
UnifiedMetadataKey.RATING: 80,
|
|
80
|
+
UnifiedMetadataKey.BPM: 140,
|
|
81
|
+
}
|
|
82
|
+
update_metadata(test_file, test_metadata, normalized_rating_max_value=100)
|
|
83
|
+
|
|
84
|
+
# 3. Verify metadata was updated
|
|
85
|
+
updated_metadata = get_unified_metadata(test_file, normalized_rating_max_value=100)
|
|
86
|
+
assert updated_metadata.get(UnifiedMetadataKey.TITLE) == "FLAC Integration Test Title"
|
|
87
|
+
assert updated_metadata.get(UnifiedMetadataKey.ARTISTS) == ["FLAC Integration Test Artist"]
|
|
88
|
+
assert updated_metadata.get(UnifiedMetadataKey.ALBUM) == "FLAC Integration Test Album"
|
|
89
|
+
assert updated_metadata.get(UnifiedMetadataKey.RATING) == 80
|
|
90
|
+
assert updated_metadata.get(UnifiedMetadataKey.BPM) == 140
|
|
91
|
+
|
|
92
|
+
# 4. Test technical information
|
|
93
|
+
bitrate = get_bitrate(test_file)
|
|
94
|
+
duration = get_duration_in_sec(test_file)
|
|
95
|
+
assert isinstance(bitrate, int)
|
|
96
|
+
assert isinstance(duration, float)
|
|
97
|
+
assert bitrate > 0
|
|
98
|
+
assert duration > 0
|
|
99
|
+
|
|
100
|
+
def test_complete_metadata_workflow_wav(self):
|
|
101
|
+
# Use external script to set initial metadata
|
|
102
|
+
initial_metadata = {"title": "Initial WAV Title", "artist": "Initial WAV Artist", "album": "Initial WAV Album"}
|
|
103
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
104
|
+
# 1. Read initial metadata
|
|
105
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
106
|
+
assert isinstance(initial_metadata_result, dict)
|
|
107
|
+
|
|
108
|
+
# 2. Update metadata using app's function (this is what we're testing)
|
|
109
|
+
# WAV doesn't support rating or BPM
|
|
110
|
+
test_metadata = {
|
|
111
|
+
UnifiedMetadataKey.TITLE: "WAV Integration Test Title",
|
|
112
|
+
UnifiedMetadataKey.ARTISTS: ["WAV Integration Test Artist"],
|
|
113
|
+
UnifiedMetadataKey.ALBUM: "WAV Integration Test Album",
|
|
114
|
+
}
|
|
115
|
+
update_metadata(test_file, test_metadata)
|
|
116
|
+
|
|
117
|
+
# 3. Verify metadata was updated
|
|
118
|
+
updated_metadata = get_unified_metadata(test_file)
|
|
119
|
+
assert updated_metadata.get(UnifiedMetadataKey.TITLE) == "WAV Integration Test Title"
|
|
120
|
+
assert updated_metadata.get(UnifiedMetadataKey.ARTISTS) == ["WAV Integration Test Artist"]
|
|
121
|
+
assert updated_metadata.get(UnifiedMetadataKey.ALBUM) == "WAV Integration Test Album"
|
|
122
|
+
|
|
123
|
+
# 4. Test technical information
|
|
124
|
+
bitrate = get_bitrate(test_file)
|
|
125
|
+
duration = get_duration_in_sec(test_file)
|
|
126
|
+
assert isinstance(bitrate, int)
|
|
127
|
+
assert isinstance(duration, float)
|
|
128
|
+
assert bitrate > 0
|
|
129
|
+
assert duration > 0
|