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,40 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_sample_rate
|
|
6
|
+
from audiometa.test.helpers.technical_info_inspector import TechnicalInfoInspector
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestGetSampleRate:
|
|
11
|
+
def test_get_sample_rate_works_with_path_string(self, sample_mp3_file: Path):
|
|
12
|
+
sample_rate = get_sample_rate(str(sample_mp3_file))
|
|
13
|
+
assert isinstance(sample_rate, int)
|
|
14
|
+
assert sample_rate > 0
|
|
15
|
+
|
|
16
|
+
def test_get_sample_rate_works_with_pathlib_path(self, sample_mp3_file: Path):
|
|
17
|
+
sample_rate = get_sample_rate(sample_mp3_file)
|
|
18
|
+
assert isinstance(sample_rate, int)
|
|
19
|
+
assert sample_rate > 0
|
|
20
|
+
|
|
21
|
+
def test_get_sample_rate_matches_external_tool(self, sample_mp3_file: Path):
|
|
22
|
+
external_sample_rate = TechnicalInfoInspector.get_sample_rate(sample_mp3_file)
|
|
23
|
+
assert external_sample_rate is not None
|
|
24
|
+
assert isinstance(external_sample_rate, int)
|
|
25
|
+
assert external_sample_rate == 44100
|
|
26
|
+
|
|
27
|
+
sample_rate = get_sample_rate(sample_mp3_file)
|
|
28
|
+
assert sample_rate == external_sample_rate
|
|
29
|
+
|
|
30
|
+
def test_get_sample_rate_supports_all_formats(
|
|
31
|
+
self, sample_mp3_file: Path, sample_flac_file: Path, sample_wav_file: Path
|
|
32
|
+
):
|
|
33
|
+
mp3_sample_rate = get_sample_rate(sample_mp3_file)
|
|
34
|
+
flac_sample_rate = get_sample_rate(sample_flac_file)
|
|
35
|
+
wav_sample_rate = get_sample_rate(sample_wav_file)
|
|
36
|
+
|
|
37
|
+
assert isinstance(mp3_sample_rate, int)
|
|
38
|
+
assert isinstance(flac_sample_rate, int)
|
|
39
|
+
assert isinstance(wav_sample_rate, int)
|
|
40
|
+
assert all(r > 0 for r in [mp3_sample_rate, flac_sample_rate, wav_sample_rate])
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_unified_metadata, get_unified_metadata_field
|
|
6
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
7
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.integration
|
|
11
|
+
class TestAudioFileIntegration:
|
|
12
|
+
def test_functional_api_with_file_path(self, sample_mp3_file: Path):
|
|
13
|
+
# Test that functional APIs work with file paths (string)
|
|
14
|
+
metadata = get_unified_metadata(str(sample_mp3_file))
|
|
15
|
+
assert isinstance(metadata, dict)
|
|
16
|
+
|
|
17
|
+
# Test single format with file path
|
|
18
|
+
id3v2_metadata = get_unified_metadata(str(sample_mp3_file), metadata_format=MetadataFormat.ID3V2)
|
|
19
|
+
assert isinstance(id3v2_metadata, dict)
|
|
20
|
+
|
|
21
|
+
# Test specific metadata with file path
|
|
22
|
+
title = get_unified_metadata_field(str(sample_mp3_file), UnifiedMetadataKey.TITLE)
|
|
23
|
+
assert title is None or isinstance(title, str)
|
audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import update_metadata
|
|
4
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
5
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
6
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestAudioFormatReadableAfterUpdate:
|
|
11
|
+
def test_mp3_playable_after_id3v1_and_id3v2_updates(self):
|
|
12
|
+
"""Test that MP3 file remains playable after updating with ID3v1 and ID3v2 tags."""
|
|
13
|
+
sf = pytest.importorskip("soundfile")
|
|
14
|
+
|
|
15
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
16
|
+
# Update with ID3v2 metadata first
|
|
17
|
+
id3v2_metadata = {
|
|
18
|
+
UnifiedMetadataKey.TITLE: "ID3v2 Test Title",
|
|
19
|
+
UnifiedMetadataKey.ARTISTS: ["ID3v2 Test Artist"],
|
|
20
|
+
UnifiedMetadataKey.ALBUM: "ID3v2 Test Album",
|
|
21
|
+
UnifiedMetadataKey.RATING: 80,
|
|
22
|
+
}
|
|
23
|
+
update_metadata(
|
|
24
|
+
test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2, normalized_rating_max_value=100
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Update with ID3v1 metadata
|
|
28
|
+
id3v1_metadata = {
|
|
29
|
+
UnifiedMetadataKey.TITLE: "ID3v1 Test Title",
|
|
30
|
+
UnifiedMetadataKey.ARTISTS: ["ID3v1 Test Artist"],
|
|
31
|
+
UnifiedMetadataKey.ALBUM: "ID3v1 Test Album",
|
|
32
|
+
}
|
|
33
|
+
update_metadata(test_file, id3v1_metadata, metadata_format=MetadataFormat.ID3V1)
|
|
34
|
+
|
|
35
|
+
# Verify the track is still playable by trying to read audio frames
|
|
36
|
+
try:
|
|
37
|
+
with sf.SoundFile(str(test_file)) as f:
|
|
38
|
+
# Just try reading a few frames
|
|
39
|
+
frames = f.read(frames=1024)
|
|
40
|
+
assert f.samplerate > 0
|
|
41
|
+
assert f.channels > 0
|
|
42
|
+
assert len(frames) > 0
|
|
43
|
+
except RuntimeError as e:
|
|
44
|
+
pytest.fail(f"Audio file {test_file} could not be opened or decoded: {e}")
|
|
45
|
+
|
|
46
|
+
def test_flac_playable_after_vorbis_updates(self):
|
|
47
|
+
"""Test that FLAC file remains playable after updating with Vorbis tags."""
|
|
48
|
+
sf = pytest.importorskip("soundfile")
|
|
49
|
+
|
|
50
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
51
|
+
# Update with Vorbis metadata
|
|
52
|
+
vorbis_metadata = {
|
|
53
|
+
UnifiedMetadataKey.TITLE: "Vorbis Test Title",
|
|
54
|
+
UnifiedMetadataKey.ARTISTS: ["Vorbis Test Artist"],
|
|
55
|
+
UnifiedMetadataKey.ALBUM: "Vorbis Test Album",
|
|
56
|
+
UnifiedMetadataKey.RATING: 80,
|
|
57
|
+
}
|
|
58
|
+
update_metadata(
|
|
59
|
+
test_file, vorbis_metadata, metadata_format=MetadataFormat.VORBIS, normalized_rating_max_value=100
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Verify the track is still playable by trying to read audio frames
|
|
63
|
+
try:
|
|
64
|
+
with sf.SoundFile(str(test_file)) as f:
|
|
65
|
+
# Just try reading a few frames
|
|
66
|
+
frames = f.read(frames=1024)
|
|
67
|
+
assert f.samplerate > 0
|
|
68
|
+
assert f.channels > 0
|
|
69
|
+
assert len(frames) > 0
|
|
70
|
+
except RuntimeError as e:
|
|
71
|
+
pytest.fail(f"Audio file {test_file} could not be opened or decoded: {e}")
|
|
72
|
+
|
|
73
|
+
def test_wav_playable_after_riff_updates(self):
|
|
74
|
+
"""Test that WAV file remains playable after updating with RIFF tags."""
|
|
75
|
+
sf = pytest.importorskip("soundfile")
|
|
76
|
+
|
|
77
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
78
|
+
# Update with RIFF metadata
|
|
79
|
+
riff_metadata = {
|
|
80
|
+
UnifiedMetadataKey.TITLE: "RIFF Test Title",
|
|
81
|
+
UnifiedMetadataKey.ARTISTS: ["RIFF Test Artist"],
|
|
82
|
+
UnifiedMetadataKey.ALBUM: "RIFF Test Album",
|
|
83
|
+
}
|
|
84
|
+
update_metadata(test_file, riff_metadata, metadata_format=MetadataFormat.RIFF)
|
|
85
|
+
|
|
86
|
+
# Verify the track is still playable by trying to read audio frames
|
|
87
|
+
try:
|
|
88
|
+
with sf.SoundFile(str(test_file)) as f:
|
|
89
|
+
# Just try reading a few frames
|
|
90
|
+
frames = f.read(frames=1024)
|
|
91
|
+
assert f.samplerate > 0
|
|
92
|
+
assert f.channels > 0
|
|
93
|
+
assert len(frames) > 0
|
|
94
|
+
except RuntimeError as e:
|
|
95
|
+
pytest.fail(f"Audio file {test_file} could not be opened or decoded: {e}")
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import delete_all_metadata, update_metadata
|
|
4
|
+
from audiometa.exceptions import FileTypeNotSupportedError, MetadataWritingConflictParametersError
|
|
5
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
6
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
7
|
+
from audiometa.utils.metadata_writing_strategy import MetadataWritingStrategy
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestWritingErrorHandling:
|
|
13
|
+
def test_unsupported_file_type_raises_error(self):
|
|
14
|
+
# Create a file with unsupported extension using temp_file_with_metadata
|
|
15
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
16
|
+
temp_file_path.write_bytes(b"fake audio content")
|
|
17
|
+
temp_txt = temp_file_path.with_suffix(".txt")
|
|
18
|
+
temp_txt.write_bytes(b"fake audio content")
|
|
19
|
+
|
|
20
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
21
|
+
update_metadata(str(temp_txt), {UnifiedMetadataKey.TITLE: "Test"})
|
|
22
|
+
|
|
23
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
24
|
+
delete_all_metadata(str(temp_txt))
|
|
25
|
+
|
|
26
|
+
def test_nonexistent_file_raises_error(self):
|
|
27
|
+
nonexistent_file = "nonexistent_file.mp3"
|
|
28
|
+
|
|
29
|
+
with pytest.raises(FileNotFoundError):
|
|
30
|
+
update_metadata(nonexistent_file, {UnifiedMetadataKey.TITLE: "Test"})
|
|
31
|
+
|
|
32
|
+
# Note: delete_all_metadata error handling tests have been moved to test_delete_all_metadata.py
|
|
33
|
+
|
|
34
|
+
def test_metadata_writing_conflict_parameters_error_both_strategy_and_format(self):
|
|
35
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
36
|
+
with pytest.raises(MetadataWritingConflictParametersError) as exc_info:
|
|
37
|
+
update_metadata(
|
|
38
|
+
test_file,
|
|
39
|
+
{UnifiedMetadataKey.TITLE: "Test"},
|
|
40
|
+
metadata_strategy=MetadataWritingStrategy.SYNC,
|
|
41
|
+
metadata_format=MetadataFormat.ID3V2,
|
|
42
|
+
)
|
|
43
|
+
assert "metadata_strategy" in str(exc_info.value).lower()
|
|
44
|
+
assert "metadata_format" in str(exc_info.value).lower()
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Tests for forced format parameter functionality.
|
|
2
|
+
|
|
3
|
+
This module tests the behavior when a specific metadata format is forced using the metadata_format parameter in
|
|
4
|
+
update_metadata.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from audiometa import get_unified_metadata, update_metadata
|
|
10
|
+
from audiometa.exceptions import (
|
|
11
|
+
MetadataFieldNotSupportedByMetadataFormatError,
|
|
12
|
+
MetadataFormatNotSupportedByAudioFormatError,
|
|
13
|
+
MetadataWritingConflictParametersError,
|
|
14
|
+
)
|
|
15
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
16
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
17
|
+
from audiometa.utils.metadata_writing_strategy import MetadataWritingStrategy
|
|
18
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.integration
|
|
22
|
+
class TestForcedFormat:
|
|
23
|
+
def test_forced_format_writes_only_to_specified_format(self):
|
|
24
|
+
# Create WAV file with initial RIFF metadata
|
|
25
|
+
initial_metadata = {"title": "Original RIFF Title", "artist": "Original RIFF Artist"}
|
|
26
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
27
|
+
# Add ID3v2 metadata using the library directly
|
|
28
|
+
id3v2_metadata = {
|
|
29
|
+
UnifiedMetadataKey.TITLE: "Original ID3v2 Title",
|
|
30
|
+
UnifiedMetadataKey.ARTISTS: ["Original ID3v2 Artist"],
|
|
31
|
+
}
|
|
32
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
33
|
+
|
|
34
|
+
# Verify both formats have their respective metadata
|
|
35
|
+
riff_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
36
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
37
|
+
assert riff_before.get(UnifiedMetadataKey.TITLE) == "Original RIFF Title"
|
|
38
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) == "Original ID3v2 Title"
|
|
39
|
+
|
|
40
|
+
# Force ID3v2 format (should write only to ID3v2, leave RIFF untouched)
|
|
41
|
+
new_metadata = {
|
|
42
|
+
UnifiedMetadataKey.TITLE: "New ID3v2 Title",
|
|
43
|
+
UnifiedMetadataKey.ARTISTS: ["New ID3v2 Artist"],
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# This should write only to ID3v2 format, leaving RIFF unchanged
|
|
47
|
+
update_metadata(test_file, new_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
48
|
+
|
|
49
|
+
# Verify ID3v2 has new metadata
|
|
50
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
51
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "New ID3v2 Title"
|
|
52
|
+
assert id3v2_after.get(UnifiedMetadataKey.ARTISTS) == ["New ID3v2 Artist"]
|
|
53
|
+
|
|
54
|
+
# Verify RIFF still has original metadata (forced format doesn't affect other formats)
|
|
55
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
56
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "Original RIFF Title"
|
|
57
|
+
assert riff_after.get(UnifiedMetadataKey.ARTISTS) == ["Original RIFF Artist"]
|
|
58
|
+
|
|
59
|
+
def test_forced_format_fails_fast_on_unsupported_fields(self):
|
|
60
|
+
# Create WAV file with basic metadata
|
|
61
|
+
initial_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
62
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
63
|
+
# Try to write ARCHIVAL_LOCATION to RIFF format (not supported)
|
|
64
|
+
unsupported_metadata = {
|
|
65
|
+
UnifiedMetadataKey.TITLE: "Test Title",
|
|
66
|
+
UnifiedMetadataKey.ARCHIVAL_LOCATION: "some location", # ARCHIVAL_LOCATION not supported by RIFF format
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# This should raise MetadataFieldNotSupportedByMetadataFormatError because format is forced
|
|
70
|
+
with pytest.raises(
|
|
71
|
+
MetadataFieldNotSupportedByMetadataFormatError,
|
|
72
|
+
match="UnifiedMetadataKey.ARCHIVAL_LOCATION metadata not supported by RIFF format",
|
|
73
|
+
):
|
|
74
|
+
update_metadata(test_file, unsupported_metadata, metadata_format=MetadataFormat.RIFF)
|
|
75
|
+
|
|
76
|
+
def test_forced_format_succeeds_with_supported_fields(self):
|
|
77
|
+
# Create WAV file with basic metadata
|
|
78
|
+
initial_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
79
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
80
|
+
# Write supported fields to RIFF format
|
|
81
|
+
supported_metadata = {
|
|
82
|
+
UnifiedMetadataKey.TITLE: "New RIFF Title",
|
|
83
|
+
UnifiedMetadataKey.ARTISTS: ["New RIFF Artist"],
|
|
84
|
+
UnifiedMetadataKey.ALBUM: "New RIFF Album",
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# This should succeed because all fields are supported by RIFF
|
|
88
|
+
update_metadata(test_file, supported_metadata, metadata_format=MetadataFormat.RIFF)
|
|
89
|
+
|
|
90
|
+
# Verify RIFF has new metadata
|
|
91
|
+
riff_metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
92
|
+
assert riff_metadata.get(UnifiedMetadataKey.TITLE) == "New RIFF Title"
|
|
93
|
+
assert riff_metadata.get(UnifiedMetadataKey.ARTISTS) == ["New RIFF Artist"]
|
|
94
|
+
assert riff_metadata.get(UnifiedMetadataKey.ALBUM) == "New RIFF Album"
|
|
95
|
+
|
|
96
|
+
def test_forced_format_parameter_conflict_error(self):
|
|
97
|
+
# Create WAV file with basic metadata
|
|
98
|
+
initial_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
99
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
100
|
+
metadata = {UnifiedMetadataKey.TITLE: "Test Title", UnifiedMetadataKey.ARTISTS: ["Test Artist"]}
|
|
101
|
+
|
|
102
|
+
# This should raise error because both parameters are specified
|
|
103
|
+
with pytest.raises(
|
|
104
|
+
MetadataWritingConflictParametersError,
|
|
105
|
+
match="Cannot specify both metadata_strategy and metadata_format",
|
|
106
|
+
):
|
|
107
|
+
update_metadata(
|
|
108
|
+
test_file,
|
|
109
|
+
metadata,
|
|
110
|
+
metadata_format=MetadataFormat.RIFF,
|
|
111
|
+
metadata_strategy=MetadataWritingStrategy.SYNC,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def test_forced_format_with_unsupported_file_type(self):
|
|
115
|
+
# Create MP3 file
|
|
116
|
+
initial_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
117
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
118
|
+
metadata = {UnifiedMetadataKey.TITLE: "Test Title", UnifiedMetadataKey.ARTISTS: ["Test Artist"]}
|
|
119
|
+
|
|
120
|
+
# Try to force Vorbis format on MP3 file (not supported)
|
|
121
|
+
with pytest.raises(
|
|
122
|
+
MetadataFormatNotSupportedByAudioFormatError,
|
|
123
|
+
match="Tag format MetadataFormat.VORBIS not supported for file extension .mp3",
|
|
124
|
+
):
|
|
125
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
|
|
126
|
+
|
|
127
|
+
def test_forced_format_id3v1_writing_support(self):
|
|
128
|
+
# Create MP3 file with basic metadata
|
|
129
|
+
initial_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
130
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
131
|
+
metadata = {UnifiedMetadataKey.TITLE: "New ID3v1 Title", UnifiedMetadataKey.ARTISTS: ["New ID3v1 Artist"]}
|
|
132
|
+
|
|
133
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V1)
|
|
134
|
+
|
|
135
|
+
# Verify the metadata was written
|
|
136
|
+
from audiometa import get_unified_metadata
|
|
137
|
+
|
|
138
|
+
result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
139
|
+
assert result.get(UnifiedMetadataKey.TITLE) == "New ID3v1 Title"
|
|
140
|
+
assert result.get(UnifiedMetadataKey.ARTISTS) == ["New ID3v1 Artist"]
|
|
141
|
+
|
|
142
|
+
def test_forced_format_multiple_formats_present(self):
|
|
143
|
+
# Create WAV file with both RIFF and ID3v2 metadata
|
|
144
|
+
initial_metadata = {"title": "Original RIFF Title", "artist": "Original RIFF Artist"}
|
|
145
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
146
|
+
# Add ID3v2 metadata using the library directly
|
|
147
|
+
id3v2_metadata = {
|
|
148
|
+
UnifiedMetadataKey.TITLE: "Original ID3v2 Title",
|
|
149
|
+
UnifiedMetadataKey.ARTISTS: ["Original ID3v2 Artist"],
|
|
150
|
+
}
|
|
151
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
152
|
+
|
|
153
|
+
# Verify both formats have original metadata
|
|
154
|
+
riff_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
155
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
156
|
+
assert riff_before.get(UnifiedMetadataKey.TITLE) == "Original RIFF Title"
|
|
157
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) == "Original ID3v2 Title"
|
|
158
|
+
|
|
159
|
+
# Force ID3v2 format - should only update ID3v2
|
|
160
|
+
new_metadata = {
|
|
161
|
+
UnifiedMetadataKey.TITLE: "New ID3v2 Title",
|
|
162
|
+
UnifiedMetadataKey.ARTISTS: ["New ID3v2 Artist"],
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
update_metadata(test_file, new_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
166
|
+
|
|
167
|
+
# Verify ID3v2 was updated
|
|
168
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
169
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "New ID3v2 Title"
|
|
170
|
+
assert id3v2_after.get(UnifiedMetadataKey.ARTISTS) == ["New ID3v2 Artist"]
|
|
171
|
+
|
|
172
|
+
# Verify RIFF was NOT updated (forced format only affects specified format)
|
|
173
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
174
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "Original RIFF Title"
|
|
175
|
+
assert riff_after.get(UnifiedMetadataKey.ARTISTS) == ["Original RIFF Artist"]
|
|
176
|
+
|
|
177
|
+
def test_forced_format_with_rating_field(self):
|
|
178
|
+
# Create MP3 file with basic metadata
|
|
179
|
+
initial_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
180
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
181
|
+
# Write rating to ID3v2 format with proper configuration
|
|
182
|
+
metadata_with_rating = {UnifiedMetadataKey.TITLE: "Test Title", UnifiedMetadataKey.RATING: 80}
|
|
183
|
+
|
|
184
|
+
# This should succeed with proper configuration
|
|
185
|
+
update_metadata(
|
|
186
|
+
test_file,
|
|
187
|
+
metadata_with_rating,
|
|
188
|
+
metadata_format=MetadataFormat.ID3V2,
|
|
189
|
+
normalized_rating_max_value=100,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Verify rating was written (converted from 0-100 to 0-255 scale)
|
|
193
|
+
id3v2_metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
194
|
+
assert id3v2_metadata.get(UnifiedMetadataKey.TITLE) == "Test Title"
|
|
195
|
+
# Rating 85 on 0-100 scale becomes 196 on 0-255 scale (85 * 255 / 100 = 216.75, rounded to 196)
|
|
196
|
+
assert id3v2_metadata.get(UnifiedMetadataKey.RATING) == 196
|
|
197
|
+
|
|
198
|
+
# Note: delete_all_metadata tests have been moved to test_delete_all_metadata.py
|
|
199
|
+
|
|
200
|
+
def test_forced_format_validation_before_writing(self):
|
|
201
|
+
# Create WAV file with initial metadata
|
|
202
|
+
initial_metadata = {"title": "Original Title", "artist": "Original Artist"}
|
|
203
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
204
|
+
# Try to write mixed supported/unsupported fields to RIFF format
|
|
205
|
+
mixed_metadata = {
|
|
206
|
+
UnifiedMetadataKey.TITLE: "New Title", # Supported
|
|
207
|
+
UnifiedMetadataKey.ARTISTS: ["New Artist"], # Supported
|
|
208
|
+
UnifiedMetadataKey.ARCHIVAL_LOCATION: "some location", # NOT supported by RIFF
|
|
209
|
+
UnifiedMetadataKey.ALBUM: "New Album", # Supported
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# This should fail because ARCHIVAL_LOCATION is not supported by RIFF
|
|
213
|
+
with pytest.raises(
|
|
214
|
+
MetadataFieldNotSupportedByMetadataFormatError,
|
|
215
|
+
match="UnifiedMetadataKey.ARCHIVAL_LOCATION metadata not supported by RIFF format",
|
|
216
|
+
):
|
|
217
|
+
update_metadata(test_file, mixed_metadata, metadata_format=MetadataFormat.RIFF)
|
|
218
|
+
|
|
219
|
+
# Verify NO changes were made to the file (validation happens before writing)
|
|
220
|
+
final_metadata = get_unified_metadata(test_file)
|
|
221
|
+
assert final_metadata.get(UnifiedMetadataKey.TITLE) == "Original Title"
|
|
222
|
+
assert final_metadata.get(UnifiedMetadataKey.ARTISTS) == ["Original Artist"]
|
|
223
|
+
assert final_metadata.get(UnifiedMetadataKey.BPM) is None
|
|
224
|
+
assert final_metadata.get(UnifiedMetadataKey.ALBUM) is None
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Tests for multiple format preservation when updating metadata.
|
|
2
|
+
|
|
3
|
+
This module tests that updating fields in one format with the PRESERVE strategy does not affect other formats. It covers
|
|
4
|
+
the main working combinations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from audiometa import get_unified_metadata, update_metadata
|
|
10
|
+
from audiometa.test.helpers.id3v1 import ID3v1MetadataSetter
|
|
11
|
+
from audiometa.test.helpers.id3v2 import ID3v2MetadataSetter
|
|
12
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
13
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
14
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.mark.integration
|
|
18
|
+
class TestMultipleFormatPreservation:
|
|
19
|
+
def test_id3v1_preserves_id3v2(self):
|
|
20
|
+
with temp_file_with_metadata({"title": "ID3v2 Title", "artist": "ID3v2 Artist"}, "mp3") as test_file:
|
|
21
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
22
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"artist": "ID3v2 Artist"})
|
|
23
|
+
|
|
24
|
+
# Verify initial state
|
|
25
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
26
|
+
id3v1_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
27
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
28
|
+
assert id3v1_before.get(UnifiedMetadataKey.TITLE) is None
|
|
29
|
+
|
|
30
|
+
# Update ID3v1 with PRESERVE strategy (specify ID3v1 format)
|
|
31
|
+
id3v1_metadata = {UnifiedMetadataKey.TITLE: "ID3v1 Title", UnifiedMetadataKey.ARTISTS: ["ID3v1 Artist"]}
|
|
32
|
+
update_metadata(test_file, id3v1_metadata, metadata_format=MetadataFormat.ID3V1)
|
|
33
|
+
|
|
34
|
+
# Verify ID3v1 was updated and ID3v2 was preserved
|
|
35
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
36
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
37
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
38
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
39
|
+
|
|
40
|
+
def test_id3v2_preserves_id3v1(self):
|
|
41
|
+
with temp_file_with_metadata({"title": "ID3v1 Title", "artist": "ID3v1 Artist"}, "id3v1") as test_file:
|
|
42
|
+
# Verify initial state
|
|
43
|
+
id3v1_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
44
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
45
|
+
assert id3v1_before.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
46
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) is None
|
|
47
|
+
|
|
48
|
+
id3v2_metadata = {UnifiedMetadataKey.TITLE: "ID3v2 Title", UnifiedMetadataKey.ARTISTS: ["ID3v2 Artist"]}
|
|
49
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
50
|
+
|
|
51
|
+
# Verify ID3v2 was updated and ID3v1 was preserved
|
|
52
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
53
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
54
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
55
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
56
|
+
|
|
57
|
+
def test_riff_preserves_id3v1(self):
|
|
58
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
59
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
60
|
+
ID3v1MetadataSetter.set_artist(test_file, "ID3v1 Artist")
|
|
61
|
+
|
|
62
|
+
# Verify initial state
|
|
63
|
+
id3v1_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
64
|
+
riff_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
65
|
+
assert id3v1_before.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
66
|
+
assert riff_before.get(UnifiedMetadataKey.TITLE) is None
|
|
67
|
+
|
|
68
|
+
riff_metadata = {UnifiedMetadataKey.TITLE: "RIFF Title", UnifiedMetadataKey.ARTISTS: ["RIFF Artist"]}
|
|
69
|
+
update_metadata(test_file, riff_metadata, metadata_format=MetadataFormat.RIFF)
|
|
70
|
+
|
|
71
|
+
# Verify RIFF was updated and ID3v1 was preserved
|
|
72
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
73
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
74
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
75
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
76
|
+
|
|
77
|
+
def test_id3v1_preserves_riff(self):
|
|
78
|
+
with temp_file_with_metadata({"title": "RIFF Title", "artist": "RIFF Artist"}, "wav") as test_file:
|
|
79
|
+
# Verify initial state
|
|
80
|
+
riff_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
81
|
+
id3v1_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
82
|
+
assert riff_before.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
83
|
+
assert id3v1_before.get(UnifiedMetadataKey.TITLE) is None
|
|
84
|
+
|
|
85
|
+
# Update ID3v1 with PRESERVE strategy (specify ID3v1 format)
|
|
86
|
+
id3v1_metadata = {UnifiedMetadataKey.TITLE: "ID3v1 Title", UnifiedMetadataKey.ARTISTS: ["ID3v1 Artist"]}
|
|
87
|
+
update_metadata(test_file, id3v1_metadata, metadata_format=MetadataFormat.ID3V1)
|
|
88
|
+
|
|
89
|
+
# Verify ID3v1 was updated and RIFF was preserved
|
|
90
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
91
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
92
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
93
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
94
|
+
|
|
95
|
+
def test_riff_preserves_id3v2(self):
|
|
96
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
97
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
98
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"artist": "ID3v2 Artist"})
|
|
99
|
+
|
|
100
|
+
# Verify initial state
|
|
101
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
102
|
+
riff_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
103
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
104
|
+
assert riff_before.get(UnifiedMetadataKey.TITLE) is None
|
|
105
|
+
|
|
106
|
+
# Update RIFF with PRESERVE strategy (specify RIFF format)
|
|
107
|
+
riff_metadata = {UnifiedMetadataKey.TITLE: "RIFF Title", UnifiedMetadataKey.ARTISTS: ["RIFF Artist"]}
|
|
108
|
+
update_metadata(test_file, riff_metadata, metadata_format=MetadataFormat.RIFF)
|
|
109
|
+
|
|
110
|
+
# Verify RIFF was updated and ID3v2 was preserved
|
|
111
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
112
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
113
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
114
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
115
|
+
|
|
116
|
+
def test_id3v2_preserves_riff(self):
|
|
117
|
+
with temp_file_with_metadata({"title": "RIFF Title", "artist": "RIFF Artist"}, "wav") as test_file:
|
|
118
|
+
# Verify initial state
|
|
119
|
+
riff_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
120
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
121
|
+
assert riff_before.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
122
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) is None
|
|
123
|
+
|
|
124
|
+
# Update ID3v2 with PRESERVE strategy (specify ID3v2 format)
|
|
125
|
+
id3v2_metadata = {UnifiedMetadataKey.TITLE: "ID3v2 Title", UnifiedMetadataKey.ARTISTS: ["ID3v2 Artist"]}
|
|
126
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
127
|
+
|
|
128
|
+
# Verify ID3v2 was updated and RIFF was preserved
|
|
129
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
130
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
131
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
132
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
133
|
+
|
|
134
|
+
def test_vorbis_preserves_id3v2(self):
|
|
135
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
136
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
137
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"artist": "ID3v2 Artist"})
|
|
138
|
+
|
|
139
|
+
# Verify initial state
|
|
140
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
141
|
+
vorbis_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.VORBIS)
|
|
142
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
143
|
+
assert vorbis_before.get(UnifiedMetadataKey.TITLE) is None
|
|
144
|
+
|
|
145
|
+
# Update Vorbis with PRESERVE strategy (specify Vorbis format)
|
|
146
|
+
vorbis_metadata = {UnifiedMetadataKey.TITLE: "Vorbis Title", UnifiedMetadataKey.ARTISTS: ["Vorbis Artist"]}
|
|
147
|
+
update_metadata(test_file, vorbis_metadata, metadata_format=MetadataFormat.VORBIS)
|
|
148
|
+
|
|
149
|
+
# Verify Vorbis was updated and ID3v2 was preserved
|
|
150
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
151
|
+
vorbis_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.VORBIS)
|
|
152
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
153
|
+
assert vorbis_after.get(UnifiedMetadataKey.TITLE) == "Vorbis Title"
|
|
154
|
+
|
|
155
|
+
def test_id3v2_preserves_vorbis(self):
|
|
156
|
+
with temp_file_with_metadata({"title": "Vorbis Title", "artist": "Vorbis Artist"}, "flac") as test_file:
|
|
157
|
+
# Verify initial state
|
|
158
|
+
vorbis_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.VORBIS)
|
|
159
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
160
|
+
assert vorbis_before.get(UnifiedMetadataKey.TITLE) == "Vorbis Title"
|
|
161
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) is None
|
|
162
|
+
|
|
163
|
+
# Update ID3v2 with PRESERVE strategy (specify ID3v2 format)
|
|
164
|
+
id3v2_metadata = {UnifiedMetadataKey.TITLE: "ID3v2 Title", UnifiedMetadataKey.ARTISTS: ["ID3v2 Artist"]}
|
|
165
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
166
|
+
|
|
167
|
+
# Verify ID3v2 was updated and Vorbis was preserved
|
|
168
|
+
vorbis_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.VORBIS)
|
|
169
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
170
|
+
assert vorbis_after.get(UnifiedMetadataKey.TITLE) == "Vorbis Title"
|
|
171
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
172
|
+
|
|
173
|
+
def test_multiple_fields_preservation(self):
|
|
174
|
+
with temp_file_with_metadata({"title": "Original Title", "artist": "Original Artist"}, "mp3") as test_file:
|
|
175
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
176
|
+
ID3v1MetadataSetter.set_artist(test_file, "ID3v1 Artist")
|
|
177
|
+
# Use ID3v2.3 instead of ID3v2.4 to preserve ID3v1 metadata
|
|
178
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"}, version="2.3")
|
|
179
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"artist": "ID3v2 Artist"}, version="2.3")
|
|
180
|
+
|
|
181
|
+
# Verify initial state
|
|
182
|
+
id3v1_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
183
|
+
id3v2_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
184
|
+
assert id3v1_before.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
185
|
+
assert id3v2_before.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
186
|
+
|
|
187
|
+
# Update only ID3v2 with multiple fields
|
|
188
|
+
id3v2_metadata = {
|
|
189
|
+
UnifiedMetadataKey.TITLE: "Updated ID3v2 Title",
|
|
190
|
+
UnifiedMetadataKey.ARTISTS: ["Updated ID3v2 Artist"],
|
|
191
|
+
UnifiedMetadataKey.ALBUM: "ID3v2 Album",
|
|
192
|
+
}
|
|
193
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
194
|
+
|
|
195
|
+
# Verify ID3v2 was updated and ID3v1 was preserved
|
|
196
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
197
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
198
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
199
|
+
assert id3v1_after.get(UnifiedMetadataKey.ARTISTS) == ["ID3v1 Artist"]
|
|
200
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "Updated ID3v2 Title"
|
|
201
|
+
assert id3v2_after.get(UnifiedMetadataKey.ARTISTS) == ["Updated ID3v2 Artist"]
|
|
202
|
+
assert id3v2_after.get(UnifiedMetadataKey.ALBUM) == "ID3v2 Album"
|
|
203
|
+
|
|
204
|
+
def test_preserve_strategy_with_none_values(self):
|
|
205
|
+
with temp_file_with_metadata({"title": "Original Title", "artist": "Original Artist"}, "mp3") as test_file:
|
|
206
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
207
|
+
ID3v1MetadataSetter.set_artist(test_file, "ID3v1 Artist")
|
|
208
|
+
|
|
209
|
+
# Verify initial state
|
|
210
|
+
id3v1_before = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
211
|
+
assert id3v1_before.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
212
|
+
|
|
213
|
+
# Update ID3v2 with None values (should not affect ID3v1)
|
|
214
|
+
id3v2_metadata = {
|
|
215
|
+
UnifiedMetadataKey.TITLE: None, # This should not affect ID3v1
|
|
216
|
+
UnifiedMetadataKey.ARTISTS: ["ID3v2 Artist"],
|
|
217
|
+
}
|
|
218
|
+
update_metadata(test_file, id3v2_metadata, metadata_format=MetadataFormat.ID3V2)
|
|
219
|
+
|
|
220
|
+
# Verify ID3v1 was preserved
|
|
221
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
222
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
223
|
+
assert id3v1_after.get(UnifiedMetadataKey.ARTISTS) == ["ID3v1 Artist"]
|