audiometa-python 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audiometa/__init__.py +1297 -0
- audiometa/__main__.py +6 -0
- audiometa/_audio_file.py +607 -0
- audiometa/cli.py +476 -0
- audiometa/exceptions.py +167 -0
- audiometa/manager/_MetadataManager.py +768 -0
- audiometa/manager/__init__.py +1 -0
- audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +250 -0
- audiometa/manager/_rating_supporting/__init__.py +1 -0
- audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +1032 -0
- audiometa/manager/_rating_supporting/id3v2/__init__.py +25 -0
- audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +11 -0
- audiometa/manager/_rating_supporting/riff/_RiffManager.py +1002 -0
- audiometa/manager/_rating_supporting/riff/__init__.py +25 -0
- audiometa/manager/_rating_supporting/riff/_riff_constants.py +17 -0
- audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +542 -0
- audiometa/manager/_rating_supporting/vorbis/__init__.py +17 -0
- audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +6 -0
- audiometa/manager/id3v1/_Id3v1Manager.py +512 -0
- audiometa/manager/id3v1/__init__.py +1 -0
- audiometa/manager/id3v1/_constants.py +8 -0
- audiometa/manager/id3v1/id3v1_raw_metadata.py +242 -0
- audiometa/manager/id3v1/id3v1_raw_metadata_key.py +13 -0
- audiometa/test/__init__.py +1 -0
- audiometa/test/assets/create_test_files.py +72 -0
- audiometa/test/helpers/__init__.py +51 -0
- audiometa/test/helpers/common/__init__.py +6 -0
- audiometa/test/helpers/common/audio_file_creator.py +68 -0
- audiometa/test/helpers/common/external_tool_runner.py +74 -0
- audiometa/test/helpers/id3v1/__init__.py +8 -0
- audiometa/test/helpers/id3v1/id3v1_header_verifier.py +18 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +37 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +61 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +82 -0
- audiometa/test/helpers/id3v2/__init__.py +28 -0
- audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +349 -0
- audiometa/test/helpers/id3v2/id3v2_header_verifier.py +38 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +56 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +189 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +506 -0
- audiometa/test/helpers/riff/__init__.py +8 -0
- audiometa/test/helpers/riff/riff_header_verifier.py +85 -0
- audiometa/test/helpers/riff/riff_manual_metadata_creator.py +298 -0
- audiometa/test/helpers/riff/riff_metadata_deleter.py +56 -0
- audiometa/test/helpers/riff/riff_metadata_getter.py +219 -0
- audiometa/test/helpers/riff/riff_metadata_setter.py +374 -0
- audiometa/test/helpers/scripts/__init__.py +0 -0
- audiometa/test/helpers/technical_info_inspector.py +115 -0
- audiometa/test/helpers/temp_file_with_metadata.py +82 -0
- audiometa/test/helpers/vorbis/__init__.py +8 -0
- audiometa/test/helpers/vorbis/vorbis_header_verifier.py +31 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +49 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +67 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +221 -0
- audiometa/test/tests/__init__.py +0 -0
- audiometa/test/tests/conftest.py +276 -0
- audiometa/test/tests/e2e/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/error_handling/__init__.py +1 -0
- audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +77 -0
- audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +130 -0
- audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +118 -0
- audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +172 -0
- audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +49 -0
- audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +160 -0
- audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +90 -0
- audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +51 -0
- audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/read/test_basic.py +58 -0
- audiometa/test/tests/e2e/cli/read/test_comprehensive.py +240 -0
- audiometa/test/tests/e2e/cli/read/test_formats.py +55 -0
- audiometa/test/tests/e2e/cli/read/test_metadata_content.py +164 -0
- audiometa/test/tests/e2e/cli/read/test_multiple_files.py +149 -0
- audiometa/test/tests/e2e/cli/read/test_options.py +88 -0
- audiometa/test/tests/e2e/cli/read/test_unified.py +84 -0
- audiometa/test/tests/e2e/cli/test_delete.py +20 -0
- audiometa/test/tests/e2e/cli/test_formatting.py +31 -0
- audiometa/test/tests/e2e/cli/test_help.py +41 -0
- audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/write/test_basic.py +51 -0
- audiometa/test/tests/e2e/cli/write/test_comprehensive.py +210 -0
- audiometa/test/tests/e2e/cli/write/test_force_format.py +336 -0
- audiometa/test/tests/e2e/cli/write/test_integer_fields.py +145 -0
- audiometa/test/tests/e2e/cli/write/test_list_fields.py +107 -0
- audiometa/test/tests/e2e/cli/write/test_rating.py +74 -0
- audiometa/test/tests/e2e/cli/write/test_string_fields.py +54 -0
- audiometa/test/tests/e2e/cli/write/test_validation.py +85 -0
- audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
- audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +166 -0
- audiometa/test/tests/e2e/workflows/__init__.py +0 -0
- audiometa/test/tests/e2e/workflows/test_core_workflows.py +166 -0
- audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +318 -0
- audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +165 -0
- audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +129 -0
- audiometa/test/tests/e2e/workflows/test_rating_workflows.py +124 -0
- audiometa/test/tests/integration/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +108 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +61 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +65 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +69 -0
- audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +79 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +61 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +67 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +60 -0
- audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +87 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +62 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +57 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_with_id3v2_tags.py +83 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +62 -0
- audiometa/test/tests/integration/conftest.py +29 -0
- audiometa/test/tests/integration/delete_all_metadata/__init__.py +1 -0
- audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +102 -0
- audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +77 -0
- audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +47 -0
- audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +24 -0
- audiometa/test/tests/integration/encoding/__init__.py +1 -0
- audiometa/test/tests/integration/encoding/test_encoding.py +88 -0
- audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +223 -0
- audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
- audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +122 -0
- audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +250 -0
- audiometa/test/tests/integration/get_full_metadata/test_consistency.py +67 -0
- audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +123 -0
- audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +40 -0
- audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +43 -0
- audiometa/test/tests/integration/get_full_metadata/test_options.py +207 -0
- audiometa/test/tests/integration/get_full_metadata/test_performance.py +95 -0
- audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +128 -0
- audiometa/test/tests/integration/get_full_metadata/test_structure.py +161 -0
- audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album/test_deleting.py +73 -0
- audiometa/test/tests/integration/metadata_field/album/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/album/test_writing.py +50 -0
- audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +83 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +38 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +52 -0
- audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +68 -0
- audiometa/test/tests/integration/metadata_field/artists/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/artists/test_writing.py +46 -0
- audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +32 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +56 -0
- audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +68 -0
- audiometa/test/tests/integration/metadata_field/comment/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/comment/test_writing.py +49 -0
- audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/composer/test_reading.py +34 -0
- audiometa/test/tests/integration/metadata_field/composer/test_writing.py +41 -0
- audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +81 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +35 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +41 -0
- audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +97 -0
- audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +92 -0
- audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +153 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +56 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +54 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +61 -0
- audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +65 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +25 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +58 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +61 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +191 -0
- audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +62 -0
- audiometa/test/tests/integration/metadata_field/genre/test_writing.py +64 -0
- audiometa/test/tests/integration/metadata_field/isrc/__init__.py +1 -0
- audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +31 -0
- audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +35 -0
- audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +165 -0
- audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/language/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/language/test_reading.py +39 -0
- audiometa/test/tests/integration/metadata_field/language/test_writing.py +43 -0
- audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +129 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +57 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +59 -0
- audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +88 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +32 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +47 -0
- audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +81 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +33 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +58 -0
- audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +117 -0
- audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +137 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +77 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +55 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +57 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +192 -0
- audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +74 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +82 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +59 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +49 -0
- audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +135 -0
- audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/title/test_deleting.py +73 -0
- audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +47 -0
- audiometa/test/tests/integration/metadata_field/title/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/title/test_writing.py +64 -0
- audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +43 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +32 -0
- audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +59 -0
- audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +73 -0
- audiometa/test/tests/integration/multiple_values/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +23 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +92 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +216 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +84 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +169 -0
- audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +209 -0
- audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +198 -0
- audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +24 -0
- audiometa/test/tests/integration/multiple_values/writing/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +62 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +36 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +34 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +32 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +54 -0
- audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +42 -0
- audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +98 -0
- audiometa/test/tests/integration/reading/__init__.py +1 -0
- audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +80 -0
- audiometa/test/tests/integration/reading/test_reading_error_handling.py +36 -0
- audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
- audiometa/test/tests/integration/real_audio_files/test_reading.py +146 -0
- audiometa/test/tests/integration/real_audio_files/test_writing.py +198 -0
- audiometa/test/tests/integration/technical_info/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +103 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_audio_data_corruption.py +21 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_flipped_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_non_flac_error.py +13 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_partial_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_random_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_unset_md5.py +56 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_valid_md5.py +21 -0
- audiometa/test/tests/integration/technical_info/test_bitrate.py +79 -0
- audiometa/test/tests/integration/technical_info/test_channels.py +38 -0
- audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +38 -0
- audiometa/test/tests/integration/technical_info/test_sample_rate.py +40 -0
- audiometa/test/tests/integration/test_audio_file.py +35 -0
- audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +95 -0
- audiometa/test/tests/integration/writing/__init__.py +0 -0
- audiometa/test/tests/integration/writing/test_error_handling.py +44 -0
- audiometa/test/tests/integration/writing/test_forced_format.py +224 -0
- audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +223 -0
- audiometa/test/tests/integration/writing/test_partial_update.py +36 -0
- audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +79 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +76 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_sync_strategy.py +215 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +42 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +93 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +99 -0
- audiometa/test/tests/unit/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +26 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +31 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +38 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +190 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +51 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +28 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +31 -0
- audiometa/test/tests/unit/audio_file/test_context_manager.py +30 -0
- audiometa/test/tests/unit/audio_file/test_file_validation.py +40 -0
- audiometa/test/tests/unit/audio_file/test_is_audio_file.py +49 -0
- audiometa/test/tests/unit/audio_file/test_operations.py +20 -0
- audiometa/test/tests/unit/audio_file/test_path_handling.py +23 -0
- audiometa/test/tests/unit/cli/__init__.py +0 -0
- audiometa/test/tests/unit/cli/test_expand_file_patterns.py +234 -0
- audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/conftest.py +142 -0
- audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +49 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +66 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +343 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +53 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +186 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +142 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +76 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +152 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +23 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +77 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +43 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +151 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +61 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_date_format_validation.py +135 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_disc_number_validation.py +75 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_format_validation.py +121 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_type_validation.py +30 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_track_number_validation.py +46 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +22 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +83 -0
- audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +74 -0
- audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +26 -0
- audiometa/utils/__init__.py +1 -0
- audiometa/utils/id3v1_genre_code_map.py +205 -0
- audiometa/utils/metadata_format.py +31 -0
- audiometa/utils/metadata_writing_strategy.py +16 -0
- audiometa/utils/mutagen_exception_handler.py +24 -0
- audiometa/utils/os_dependencies_checker/__init__.py +24 -0
- audiometa/utils/os_dependencies_checker/base.py +62 -0
- audiometa/utils/os_dependencies_checker/config.py +77 -0
- audiometa/utils/os_dependencies_checker/macos.py +236 -0
- audiometa/utils/os_dependencies_checker/ubuntu.py +95 -0
- audiometa/utils/os_dependencies_checker/windows.py +227 -0
- audiometa/utils/rating_profiles.py +110 -0
- audiometa/utils/tool_path_resolver.py +135 -0
- audiometa/utils/types.py +82 -0
- audiometa/utils/unified_metadata_key.py +87 -0
- audiometa_python-0.6.0.dist-info/METADATA +1593 -0
- audiometa_python-0.6.0.dist-info/RECORD +352 -0
- audiometa_python-0.6.0.dist-info/WHEEL +5 -0
- audiometa_python-0.6.0.dist-info/entry_points.txt +2 -0
- audiometa_python-0.6.0.dist-info/licenses/LICENSE +202 -0
- audiometa_python-0.6.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Tests for SYNC metadata writing strategy.
|
|
2
|
+
|
|
3
|
+
This module tests the SYNC strategy which writes to native format and synchronizes other metadata formats that are
|
|
4
|
+
already present.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from audiometa import get_unified_metadata, update_metadata
|
|
10
|
+
from audiometa.test.helpers.id3v1.id3v1_metadata_setter import ID3v1MetadataSetter
|
|
11
|
+
from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
|
|
12
|
+
from audiometa.test.helpers.riff.riff_metadata_setter import RIFFMetadataSetter
|
|
13
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
14
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
15
|
+
from audiometa.utils.metadata_writing_strategy import MetadataWritingStrategy
|
|
16
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.integration
|
|
20
|
+
class TestSyncStrategy:
|
|
21
|
+
def test_sync_strategy_wav(self):
|
|
22
|
+
# Create WAV file with initial RIFF metadata
|
|
23
|
+
with temp_file_with_metadata(
|
|
24
|
+
{"title": "Initial Title", "artist": "Initial Artist", "album": "Initial Album"}, "wav"
|
|
25
|
+
) as test_file:
|
|
26
|
+
# Verify initial RIFF metadata was written
|
|
27
|
+
riff_initial = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
28
|
+
assert riff_initial.get(UnifiedMetadataKey.TITLE) == "Initial Title"
|
|
29
|
+
|
|
30
|
+
# Now write RIFF metadata with SYNC strategy
|
|
31
|
+
# For WAV files, SYNC strategy should only affect RIFF metadata
|
|
32
|
+
sync_metadata = {
|
|
33
|
+
UnifiedMetadataKey.TITLE: "Synced Title",
|
|
34
|
+
UnifiedMetadataKey.ARTISTS: ["Synced Artist"],
|
|
35
|
+
UnifiedMetadataKey.ALBUM: "Synced Album",
|
|
36
|
+
}
|
|
37
|
+
update_metadata(test_file, sync_metadata, metadata_strategy=MetadataWritingStrategy.SYNC)
|
|
38
|
+
|
|
39
|
+
# Verify RIFF metadata has the synced values
|
|
40
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
41
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
42
|
+
assert riff_after.get(UnifiedMetadataKey.ARTISTS) == ["Synced Artist"]
|
|
43
|
+
assert riff_after.get(UnifiedMetadataKey.ALBUM) == "Synced Album"
|
|
44
|
+
|
|
45
|
+
# Verify merged metadata prefers RIFF (WAV native format)
|
|
46
|
+
merged = get_unified_metadata(test_file)
|
|
47
|
+
assert merged.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
48
|
+
|
|
49
|
+
def test_default_strategy_is_sync(self):
|
|
50
|
+
# Create WAV file with initial RIFF metadata
|
|
51
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
52
|
+
# First, add RIFF metadata using external tools
|
|
53
|
+
RIFFMetadataSetter.set_title(test_file, "Initial RIFF Title")
|
|
54
|
+
RIFFMetadataSetter.set_artists(test_file, ["Initial RIFF Artist"])
|
|
55
|
+
|
|
56
|
+
# Now write RIFF metadata without specifying strategy (should default to SYNC)
|
|
57
|
+
riff_metadata = {UnifiedMetadataKey.TITLE: "RIFF Title", UnifiedMetadataKey.ARTISTS: ["RIFF Artist"]}
|
|
58
|
+
update_metadata(test_file, riff_metadata)
|
|
59
|
+
|
|
60
|
+
# Verify RIFF metadata has the new values (SYNC strategy)
|
|
61
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
62
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
63
|
+
|
|
64
|
+
# Merged metadata should prefer RIFF (WAV native format)
|
|
65
|
+
merged = get_unified_metadata(test_file)
|
|
66
|
+
assert merged.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
67
|
+
|
|
68
|
+
def test_id3v1_not_preserved_with_sync_strategy(self):
|
|
69
|
+
# Create test file with ID3v1 metadata using external tools
|
|
70
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
71
|
+
# Add ID3v1 metadata using external tools
|
|
72
|
+
ID3v1MetadataSetter.set_metadata(
|
|
73
|
+
test_file, {"title": "ID3v1 Title", "artist": "ID3v1 Artist", "album": "ID3v1 Album"}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Verify ID3v1 metadata was written
|
|
77
|
+
id3v1_result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
78
|
+
assert id3v1_result.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
79
|
+
|
|
80
|
+
# Now write ID3v2 metadata with SYNC strategy
|
|
81
|
+
id3v2_metadata = {
|
|
82
|
+
UnifiedMetadataKey.TITLE: "Synced Title",
|
|
83
|
+
UnifiedMetadataKey.ARTISTS: ["Synced Artist"],
|
|
84
|
+
UnifiedMetadataKey.ALBUM: "Synced Album",
|
|
85
|
+
}
|
|
86
|
+
update_metadata(test_file, id3v2_metadata, metadata_strategy=MetadataWritingStrategy.SYNC)
|
|
87
|
+
|
|
88
|
+
# Verify ID3v1 metadata behavior with different strategies
|
|
89
|
+
# When ID3v2 is written, it overwrites the ID3v1 tag
|
|
90
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
91
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "Synced Title" # ID3v1 was overwritten
|
|
92
|
+
|
|
93
|
+
# Verify ID3v2 metadata was written with synced values
|
|
94
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
95
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
96
|
+
|
|
97
|
+
# Merged metadata should prefer ID3v2 (higher precedence)
|
|
98
|
+
merged = get_unified_metadata(test_file)
|
|
99
|
+
assert merged.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
100
|
+
|
|
101
|
+
def test_id3v1_modification_success(self):
|
|
102
|
+
# Create test file with ID3v1 metadata using external tools
|
|
103
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
104
|
+
# Add ID3v1 metadata using external tools
|
|
105
|
+
ID3v1MetadataSetter.set_metadata(
|
|
106
|
+
test_file, {"title": "ID3v1 Title", "artist": "ID3v1 Artist", "album": "ID3v1 Album"}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Verify ID3v1 metadata was written
|
|
110
|
+
id3v1_result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
111
|
+
assert id3v1_result.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
112
|
+
|
|
113
|
+
# Modify ID3v1 metadata directly should succeed
|
|
114
|
+
update_metadata(test_file, {UnifiedMetadataKey.TITLE: "New Title"}, metadata_format=MetadataFormat.ID3V1)
|
|
115
|
+
|
|
116
|
+
# Verify the modification was successful
|
|
117
|
+
updated_id3v1_result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
118
|
+
assert updated_id3v1_result.get(UnifiedMetadataKey.TITLE) == "New Title"
|
|
119
|
+
|
|
120
|
+
def test_sync_strategy_wav_with_field_length_limits(self):
|
|
121
|
+
# Create WAV file with initial RIFF metadata
|
|
122
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
123
|
+
# Add initial RIFF metadata using external tools
|
|
124
|
+
RIFFMetadataSetter.set_title(test_file, "Short Title")
|
|
125
|
+
RIFFMetadataSetter.set_artists(test_file, ["Short Artist"])
|
|
126
|
+
RIFFMetadataSetter.set_album(test_file, "Short Album")
|
|
127
|
+
|
|
128
|
+
# Verify initial RIFF metadata was written
|
|
129
|
+
riff_initial = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
130
|
+
assert riff_initial.get(UnifiedMetadataKey.TITLE) == "Short Title"
|
|
131
|
+
|
|
132
|
+
# Now test SYNC strategy with long title
|
|
133
|
+
long_title = "This is a Very Long Title That Tests RIFF Metadata Handling"
|
|
134
|
+
sync_metadata = {
|
|
135
|
+
UnifiedMetadataKey.TITLE: long_title,
|
|
136
|
+
UnifiedMetadataKey.ARTISTS: ["Long Artist Name That Tests RIFF Handling"],
|
|
137
|
+
UnifiedMetadataKey.ALBUM: "Long Album Name That Tests RIFF Handling",
|
|
138
|
+
}
|
|
139
|
+
update_metadata(test_file, sync_metadata, metadata_strategy=MetadataWritingStrategy.SYNC)
|
|
140
|
+
|
|
141
|
+
# Verify RIFF metadata has full values (RIFF supports longer fields than ID3v1)
|
|
142
|
+
riff_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
|
|
143
|
+
assert riff_after.get(UnifiedMetadataKey.TITLE) == long_title
|
|
144
|
+
assert riff_after.get(UnifiedMetadataKey.ARTISTS) == ["Long Artist Name That Tests RIFF Handling"]
|
|
145
|
+
assert riff_after.get(UnifiedMetadataKey.ALBUM) == "Long Album Name That Tests RIFF Handling"
|
|
146
|
+
|
|
147
|
+
# Merged metadata should prefer RIFF (WAV native format has higher precedence)
|
|
148
|
+
merged = get_unified_metadata(test_file)
|
|
149
|
+
assert merged.get(UnifiedMetadataKey.TITLE) == long_title # Full title from RIFF
|
|
150
|
+
assert merged.get(UnifiedMetadataKey.ARTISTS) == ["Long Artist Name That Tests RIFF Handling"]
|
|
151
|
+
|
|
152
|
+
def test_sync_strategy_mp3(self):
|
|
153
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
154
|
+
# Add ID3v1 metadata using external tools
|
|
155
|
+
ID3v1MetadataSetter.set_metadata(
|
|
156
|
+
test_file, {"title": "ID3v1 Title", "artist": "ID3v1 Artist", "album": "ID3v1 Album"}
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Verify ID3v1 metadata was written
|
|
160
|
+
id3v1_result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
161
|
+
assert id3v1_result.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
162
|
+
|
|
163
|
+
# Now write ID3v2 metadata with SYNC strategy
|
|
164
|
+
id3v2_metadata = {
|
|
165
|
+
UnifiedMetadataKey.TITLE: "Synced Title",
|
|
166
|
+
UnifiedMetadataKey.ARTISTS: ["Synced Artist"],
|
|
167
|
+
UnifiedMetadataKey.ALBUM: "Synced Album",
|
|
168
|
+
}
|
|
169
|
+
update_metadata(test_file, id3v2_metadata, metadata_strategy=MetadataWritingStrategy.SYNC)
|
|
170
|
+
|
|
171
|
+
# Verify ID3v1 metadata behavior with different strategies
|
|
172
|
+
# When ID3v2 is written, it overwrites the ID3v1 tag
|
|
173
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
174
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "Synced Title" # ID3v1 was overwritten
|
|
175
|
+
|
|
176
|
+
# Verify ID3v2 metadata was written with synced values
|
|
177
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
178
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
179
|
+
|
|
180
|
+
def test_sync_strategy_flac(self):
|
|
181
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
182
|
+
ID3v1MetadataSetter.set_metadata(
|
|
183
|
+
test_file, {"title": "ID3v1 Title", "artist": "ID3v1 Artist", "album": "ID3v1 Album"}
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Verify ID3v1 metadata was written
|
|
187
|
+
id3v1_result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
188
|
+
assert id3v1_result.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
189
|
+
|
|
190
|
+
ID3v2MetadataSetter.set_metadata(
|
|
191
|
+
test_file,
|
|
192
|
+
{"title": "ID3v2 Title", "artist": "ID3v2 Artist", "album": "ID3v2 Album"},
|
|
193
|
+
version="2.3",
|
|
194
|
+
)
|
|
195
|
+
id3v2_result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
196
|
+
assert id3v2_result.get(UnifiedMetadataKey.TITLE) == "ID3v2 Title"
|
|
197
|
+
|
|
198
|
+
# Now write Vorbis metadata with SYNC strategy
|
|
199
|
+
vorbis_metadata = {
|
|
200
|
+
UnifiedMetadataKey.TITLE: "Synced Title",
|
|
201
|
+
UnifiedMetadataKey.ARTISTS: ["Synced Artist"],
|
|
202
|
+
UnifiedMetadataKey.ALBUM: "Synced Album",
|
|
203
|
+
}
|
|
204
|
+
update_metadata(test_file, vorbis_metadata, metadata_strategy=MetadataWritingStrategy.SYNC)
|
|
205
|
+
|
|
206
|
+
# Verify ID3v1 metadata behavior with different strategies
|
|
207
|
+
# When Vorbis is written, it overwrites the ID3v1 tag
|
|
208
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
209
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "Synced Title" # ID3v1 was overwritten
|
|
210
|
+
|
|
211
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
212
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
213
|
+
|
|
214
|
+
vorbis_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.VORBIS)
|
|
215
|
+
assert vorbis_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
File without changes
|
audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_unified_metadata, update_metadata
|
|
6
|
+
from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
|
|
7
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestFailBehavior:
|
|
13
|
+
def test_fail_on_unsupported_field_enabled(self):
|
|
14
|
+
with temp_file_with_metadata({"title": "Test"}, "wav") as test_file:
|
|
15
|
+
test_metadata = {
|
|
16
|
+
UnifiedMetadataKey.TITLE: "Test Title",
|
|
17
|
+
UnifiedMetadataKey.REPLAYGAIN: "89 dB", # REPLAYGAIN is not supported by RIFF format
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError) as exc_info:
|
|
21
|
+
update_metadata(test_file, test_metadata, fail_on_unsupported_field=True)
|
|
22
|
+
|
|
23
|
+
assert "Fields not supported by riff format" in str(exc_info.value)
|
|
24
|
+
assert "REPLAYGAIN" in str(exc_info.value)
|
|
25
|
+
|
|
26
|
+
def test_fail_on_unsupported_field_disabled_graceful_default(self):
|
|
27
|
+
with temp_file_with_metadata({"title": "Test"}, "wav") as test_file:
|
|
28
|
+
test_metadata = {
|
|
29
|
+
UnifiedMetadataKey.TITLE: "Test Title",
|
|
30
|
+
UnifiedMetadataKey.REPLAYGAIN: "89 dB", # REPLAYGAIN is not supported by RIFF format
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
with warnings.catch_warnings(record=True) as w:
|
|
34
|
+
warnings.simplefilter("always")
|
|
35
|
+
update_metadata(test_file, test_metadata) # fail_on_unsupported_field=False by default
|
|
36
|
+
|
|
37
|
+
assert len(w) > 0
|
|
38
|
+
warning_messages = [str(warning.message) for warning in w]
|
|
39
|
+
assert any("unsupported" in msg.lower() or "not supported" in msg.lower() for msg in warning_messages)
|
|
40
|
+
|
|
41
|
+
metadata = get_unified_metadata(test_file)
|
|
42
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == "Test Title"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata, update_metadata
|
|
4
|
+
from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
|
|
5
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
6
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestNoWritingOnFailure:
|
|
11
|
+
def test_fail_on_unsupported_field_no_changes_wav_only(self):
|
|
12
|
+
initial_metadata = {"title": "Original Title", "artist": "Original Artist"}
|
|
13
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
14
|
+
initial_read = get_unified_metadata(test_file)
|
|
15
|
+
assert initial_read.get(UnifiedMetadataKey.TITLE) == "Original Title"
|
|
16
|
+
assert initial_read.get(UnifiedMetadataKey.ARTISTS) == ["Original Artist"]
|
|
17
|
+
|
|
18
|
+
test_metadata = {
|
|
19
|
+
UnifiedMetadataKey.TITLE: "New Title", # This should NOT be written
|
|
20
|
+
UnifiedMetadataKey.ARTISTS: ["New Artist"], # This should NOT be written
|
|
21
|
+
UnifiedMetadataKey.REPLAYGAIN: "89 dB", # REPLAYGAIN is not supported by RIFF format
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError):
|
|
25
|
+
update_metadata(test_file, test_metadata, fail_on_unsupported_field=True)
|
|
26
|
+
|
|
27
|
+
final_read = get_unified_metadata(test_file)
|
|
28
|
+
assert final_read.get(UnifiedMetadataKey.TITLE) == "Original Title" # Should be unchanged
|
|
29
|
+
assert final_read.get(UnifiedMetadataKey.ARTISTS) == ["Original Artist"] # Should be unchanged
|
|
30
|
+
assert final_read.get(UnifiedMetadataKey.REPLAYGAIN) is None
|
|
31
|
+
|
|
32
|
+
def test_fail_on_unsupported_field_no_changes_id3v2_only(self):
|
|
33
|
+
initial_metadata = {"title": "Original MP3 Title", "artist": "Original MP3 Artist"}
|
|
34
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
35
|
+
initial_read = get_unified_metadata(test_file)
|
|
36
|
+
assert initial_read.get(UnifiedMetadataKey.TITLE) == "Original MP3 Title"
|
|
37
|
+
assert initial_read.get(UnifiedMetadataKey.ARTISTS) == ["Original MP3 Artist"]
|
|
38
|
+
|
|
39
|
+
test_metadata = {
|
|
40
|
+
UnifiedMetadataKey.TITLE: "New MP3 Title", # Should NOT be written
|
|
41
|
+
UnifiedMetadataKey.ARTISTS: ["New MP3 Artist"], # Should NOT be written
|
|
42
|
+
UnifiedMetadataKey.ARCHIVAL_LOCATION: "some location", # This field is not supported by any format
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError):
|
|
46
|
+
update_metadata(test_file, test_metadata, fail_on_unsupported_field=True)
|
|
47
|
+
|
|
48
|
+
final_read = get_unified_metadata(test_file)
|
|
49
|
+
assert final_read.get(UnifiedMetadataKey.TITLE) == "Original MP3 Title" # Should be unchanged
|
|
50
|
+
assert final_read.get(UnifiedMetadataKey.ARTISTS) == ["Original MP3 Artist"] # Should be unchanged
|
|
51
|
+
assert final_read.get(UnifiedMetadataKey.ARCHIVAL_LOCATION) is None # Should not exist
|
|
52
|
+
|
|
53
|
+
def test_fail_on_unsupported_field_no_changes_vorbis_only(self):
|
|
54
|
+
initial_metadata = {"title": "Original FLAC Title", "artist": "Original FLAC Artist"}
|
|
55
|
+
with temp_file_with_metadata(initial_metadata, "flac") as test_file:
|
|
56
|
+
initial_read = get_unified_metadata(test_file)
|
|
57
|
+
assert initial_read.get(UnifiedMetadataKey.TITLE) == "Original FLAC Title"
|
|
58
|
+
assert initial_read.get(UnifiedMetadataKey.ARTISTS) == ["Original FLAC Artist"]
|
|
59
|
+
|
|
60
|
+
test_metadata = {
|
|
61
|
+
UnifiedMetadataKey.TITLE: "New FLAC Title", # Should NOT be written
|
|
62
|
+
UnifiedMetadataKey.ARTISTS: ["New FLAC Artist"], # Should NOT be written
|
|
63
|
+
UnifiedMetadataKey.ARCHIVAL_LOCATION: "some location", # This field is not supported by any format
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError):
|
|
67
|
+
update_metadata(test_file, test_metadata, fail_on_unsupported_field=True)
|
|
68
|
+
|
|
69
|
+
final_read = get_unified_metadata(test_file)
|
|
70
|
+
assert final_read.get(UnifiedMetadataKey.TITLE) == "Original FLAC Title" # Should be unchanged
|
|
71
|
+
assert final_read.get(UnifiedMetadataKey.ARTISTS) == ["Original FLAC Artist"] # Should be unchanged
|
|
72
|
+
assert final_read.get(UnifiedMetadataKey.ARCHIVAL_LOCATION) is None # Should not exist
|
|
73
|
+
|
|
74
|
+
def test_fail_on_unsupported_field_no_changes_riff_only(self):
|
|
75
|
+
initial_metadata = {"title": "Original WAV Title", "artist": "Original WAV Artist"}
|
|
76
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
77
|
+
initial_read = get_unified_metadata(test_file)
|
|
78
|
+
assert initial_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title"
|
|
79
|
+
assert initial_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"]
|
|
80
|
+
|
|
81
|
+
test_metadata = {
|
|
82
|
+
UnifiedMetadataKey.TITLE: "New WAV Title", # TITLE is supported by RIFF format
|
|
83
|
+
UnifiedMetadataKey.ARTISTS: ["New WAV Artist"], # ARTISTS is supported by RIFF format
|
|
84
|
+
UnifiedMetadataKey.REPLAYGAIN: "89 dB", # REPLAYGAIN is not supported by any format
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError):
|
|
88
|
+
update_metadata(test_file, test_metadata, fail_on_unsupported_field=True)
|
|
89
|
+
|
|
90
|
+
final_read = get_unified_metadata(test_file)
|
|
91
|
+
assert final_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title" # Should be unchanged
|
|
92
|
+
assert final_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"] # Should be unchanged
|
|
93
|
+
assert final_read.get(UnifiedMetadataKey.REPLAYGAIN) is None # Should not exist
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata, update_metadata
|
|
4
|
+
from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
|
|
5
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
6
|
+
from audiometa.utils.metadata_writing_strategy import MetadataWritingStrategy
|
|
7
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.integration
|
|
11
|
+
class TestStrategySpecific:
|
|
12
|
+
def test_fail_on_unsupported_field_preserve_strategy(self):
|
|
13
|
+
initial_metadata = {"title": "Original WAV Title", "artist": "Original WAV Artist"}
|
|
14
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
15
|
+
initial_read = get_unified_metadata(test_file)
|
|
16
|
+
assert initial_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title"
|
|
17
|
+
assert initial_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"]
|
|
18
|
+
|
|
19
|
+
test_metadata = {
|
|
20
|
+
UnifiedMetadataKey.TITLE: "New WAV Title", # Should NOT be written
|
|
21
|
+
UnifiedMetadataKey.ARTISTS: ["New WAV Artist"], # Should NOT be written
|
|
22
|
+
UnifiedMetadataKey.REPLAYGAIN: "89 dB", # Not supported by RIFF format
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError) as exc_info:
|
|
26
|
+
update_metadata(
|
|
27
|
+
test_file,
|
|
28
|
+
test_metadata,
|
|
29
|
+
metadata_strategy=MetadataWritingStrategy.PRESERVE,
|
|
30
|
+
fail_on_unsupported_field=True,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert "Fields not supported by riff format" in str(exc_info.value)
|
|
34
|
+
assert "REPLAYGAIN" in str(exc_info.value)
|
|
35
|
+
|
|
36
|
+
final_read = get_unified_metadata(test_file)
|
|
37
|
+
assert final_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title" # Should be unchanged
|
|
38
|
+
assert final_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"] # Should be unchanged
|
|
39
|
+
assert final_read.get(UnifiedMetadataKey.REPLAYGAIN) is None # Should not exist
|
|
40
|
+
|
|
41
|
+
def test_fail_on_unsupported_field_cleanup_strategy(self):
|
|
42
|
+
initial_metadata = {"title": "Original WAV Title", "artist": "Original WAV Artist"}
|
|
43
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
44
|
+
initial_read = get_unified_metadata(test_file)
|
|
45
|
+
assert initial_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title"
|
|
46
|
+
assert initial_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"]
|
|
47
|
+
|
|
48
|
+
test_metadata = {
|
|
49
|
+
UnifiedMetadataKey.TITLE: "New WAV Title", # Should NOT be written
|
|
50
|
+
UnifiedMetadataKey.ARTISTS: ["New WAV Artist"], # Should NOT be written
|
|
51
|
+
UnifiedMetadataKey.REPLAYGAIN: "89 dB", # Not supported by RIFF format
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError) as exc_info:
|
|
55
|
+
update_metadata(
|
|
56
|
+
test_file,
|
|
57
|
+
test_metadata,
|
|
58
|
+
metadata_strategy=MetadataWritingStrategy.CLEANUP,
|
|
59
|
+
fail_on_unsupported_field=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assert "Fields not supported by riff format" in str(exc_info.value)
|
|
63
|
+
assert "REPLAYGAIN" in str(exc_info.value)
|
|
64
|
+
|
|
65
|
+
final_read = get_unified_metadata(test_file)
|
|
66
|
+
assert final_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title" # Should be unchanged
|
|
67
|
+
assert final_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"] # Should be unchanged
|
|
68
|
+
assert final_read.get(UnifiedMetadataKey.REPLAYGAIN) is None # Should not exist
|
|
69
|
+
|
|
70
|
+
def test_fail_on_unsupported_field_sync_strategy(self):
|
|
71
|
+
initial_metadata = {"title": "Original WAV Title", "artist": "Original WAV Artist"}
|
|
72
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
73
|
+
initial_read = get_unified_metadata(test_file)
|
|
74
|
+
assert initial_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title"
|
|
75
|
+
assert initial_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"]
|
|
76
|
+
|
|
77
|
+
test_metadata = {
|
|
78
|
+
UnifiedMetadataKey.TITLE: "New WAV Title", # Should be written to RIFF (supported)
|
|
79
|
+
UnifiedMetadataKey.ARTISTS: ["New WAV Artist"], # Should be written to RIFF (supported)
|
|
80
|
+
UnifiedMetadataKey.REPLAYGAIN: "89 dB", # Not supported by RIFF format - should cause failure
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError) as exc_info:
|
|
84
|
+
update_metadata(
|
|
85
|
+
test_file,
|
|
86
|
+
test_metadata,
|
|
87
|
+
metadata_strategy=MetadataWritingStrategy.SYNC,
|
|
88
|
+
fail_on_unsupported_field=True,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
assert "Fields not supported by riff format" in str(exc_info.value)
|
|
92
|
+
assert "REPLAYGAIN" in str(exc_info.value)
|
|
93
|
+
|
|
94
|
+
# With fail_on_unsupported_field=True, the operation should be atomic
|
|
95
|
+
# No writing should occur, so file should remain unchanged
|
|
96
|
+
final_read = get_unified_metadata(test_file)
|
|
97
|
+
assert final_read.get(UnifiedMetadataKey.TITLE) == "Original WAV Title" # Should be unchanged
|
|
98
|
+
assert final_read.get(UnifiedMetadataKey.ARTISTS) == ["Original WAV Artist"] # Should be unchanged
|
|
99
|
+
assert final_read.get(UnifiedMetadataKey.REPLAYGAIN) is None # Should not exist
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa._audio_file import _AudioFile
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.unit
|
|
9
|
+
class TestAudioFileBitrate:
|
|
10
|
+
def test_get_bitrate_mp3(self, sample_mp3_file: Path):
|
|
11
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
12
|
+
bitrate = audio_file.get_bitrate()
|
|
13
|
+
assert isinstance(bitrate, int)
|
|
14
|
+
assert bitrate > 0
|
|
15
|
+
|
|
16
|
+
def test_get_bitrate_flac(self, sample_flac_file: Path):
|
|
17
|
+
audio_file = _AudioFile(sample_flac_file)
|
|
18
|
+
bitrate = audio_file.get_bitrate()
|
|
19
|
+
assert isinstance(bitrate, int)
|
|
20
|
+
assert bitrate > 0
|
|
21
|
+
|
|
22
|
+
def test_get_bitrate_wav(self, sample_wav_file: Path):
|
|
23
|
+
audio_file = _AudioFile(sample_wav_file)
|
|
24
|
+
bitrate = audio_file.get_bitrate()
|
|
25
|
+
assert isinstance(bitrate, int)
|
|
26
|
+
assert bitrate > 0
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa._audio_file import _AudioFile
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.unit
|
|
9
|
+
class TestAudioFileChannels:
|
|
10
|
+
def test_get_channels_mp3(self, sample_mp3_file: Path):
|
|
11
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
12
|
+
channels = audio_file.get_channels()
|
|
13
|
+
assert isinstance(channels, int)
|
|
14
|
+
assert channels > 0
|
|
15
|
+
|
|
16
|
+
def test_get_channels_flac(self, sample_flac_file: Path):
|
|
17
|
+
audio_file = _AudioFile(sample_flac_file)
|
|
18
|
+
channels = audio_file.get_channels()
|
|
19
|
+
assert isinstance(channels, int)
|
|
20
|
+
assert channels > 0
|
|
21
|
+
|
|
22
|
+
def test_get_channels_wav(self, sample_wav_file: Path):
|
|
23
|
+
audio_file = _AudioFile(sample_wav_file)
|
|
24
|
+
channels = audio_file.get_channels()
|
|
25
|
+
assert isinstance(channels, int)
|
|
26
|
+
assert channels > 0
|
|
27
|
+
|
|
28
|
+
def test_get_channels_returns_int(self, sample_mp3_file: Path):
|
|
29
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
30
|
+
channels = audio_file.get_channels()
|
|
31
|
+
assert isinstance(channels, int)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa._audio_file import _AudioFile
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.unit
|
|
9
|
+
class TestAudioFileDurationInSec:
|
|
10
|
+
def test_get_duration_in_sec_mp3(self, sample_mp3_file: Path):
|
|
11
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
12
|
+
duration = audio_file.get_duration_in_sec()
|
|
13
|
+
assert isinstance(duration, float)
|
|
14
|
+
assert duration > 0
|
|
15
|
+
assert duration < 1000 # Reasonable bounds check
|
|
16
|
+
|
|
17
|
+
def test_get_duration_in_sec_flac(self, sample_flac_file: Path):
|
|
18
|
+
audio_file = _AudioFile(sample_flac_file)
|
|
19
|
+
duration = audio_file.get_duration_in_sec()
|
|
20
|
+
assert isinstance(duration, float)
|
|
21
|
+
assert duration > 0
|
|
22
|
+
|
|
23
|
+
def test_get_duration_in_sec_wav(self, sample_wav_file: Path):
|
|
24
|
+
audio_file = _AudioFile(sample_wav_file)
|
|
25
|
+
duration = audio_file.get_duration_in_sec()
|
|
26
|
+
assert isinstance(duration, float)
|
|
27
|
+
assert duration > 0
|
|
28
|
+
|
|
29
|
+
def test_get_duration_in_sec_long_file(self, duration_182s_mp3: Path):
|
|
30
|
+
audio_file = _AudioFile(duration_182s_mp3)
|
|
31
|
+
duration = audio_file.get_duration_in_sec()
|
|
32
|
+
assert isinstance(duration, float)
|
|
33
|
+
assert duration > 100
|
|
34
|
+
|
|
35
|
+
def test_get_duration_in_sec_returns_float(self, sample_mp3_file: Path):
|
|
36
|
+
audio_file = _AudioFile(sample_mp3_file)
|
|
37
|
+
duration = audio_file.get_duration_in_sec()
|
|
38
|
+
assert isinstance(duration, float)
|