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,142 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa.manager._MetadataManager import _MetadataManager as MetadataManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.unit
|
|
7
|
+
class TestSeparatorSelection:
|
|
8
|
+
@pytest.mark.parametrize(
|
|
9
|
+
("values", "expected_separator"),
|
|
10
|
+
[
|
|
11
|
+
(["Artist One", "Artist Two"], "//"),
|
|
12
|
+
(["Artist One", "Artist Two", "Artist Three"], "//"),
|
|
13
|
+
(["Artist;One", "Artist;Two"], "//"),
|
|
14
|
+
(["Artist,One", "Artist,Two"], "//"),
|
|
15
|
+
(["Artist/One", "Artist/Two"], "//"),
|
|
16
|
+
(["Artist\\One", "Artist\\Two"], "//"),
|
|
17
|
+
(["Artist\\One", "Artist;Two"], "//"),
|
|
18
|
+
# Cases where // is present, so pick \\\\
|
|
19
|
+
(["Artist//One", "Artist Two"], "\\\\"),
|
|
20
|
+
(["Artist//One", "Artist//Two"], "\\\\"),
|
|
21
|
+
# Cases where // and \\\\ are present, so pick ;
|
|
22
|
+
(["Artist//One", "Artist\\\\Two"], ";"),
|
|
23
|
+
(["Artist//One", "Artist\\\\Two", "Artist\\Three"], ";"),
|
|
24
|
+
# Cases where //, \\\\, \\, ; are present, so pick ,
|
|
25
|
+
(["Artist//One", "Artist\\\\Two", "Artist\\Three", "Artist;Four"], ","),
|
|
26
|
+
(["Artist//One", "Artist\\\\Two", "Artist\\Three", "Artist;Four", "Artist/Five"], ","),
|
|
27
|
+
# Cases with mixed separators
|
|
28
|
+
(["Artist;One", "Artist,Two", "Artist/Three"], "//"),
|
|
29
|
+
(["Artist\\One", "Artist;Two", "Artist,Three"], "//"),
|
|
30
|
+
(["Artist/One", "Artist,Two"], "//"),
|
|
31
|
+
# Edge cases
|
|
32
|
+
(["Artist//One;Two", "Artist Three"], "\\\\"),
|
|
33
|
+
(["Artist\\\\One\\Two", "Artist;Three"], "//"),
|
|
34
|
+
],
|
|
35
|
+
)
|
|
36
|
+
def test_find_safe_separator(self, values, expected_separator):
|
|
37
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
38
|
+
assert separator == expected_separator
|
|
39
|
+
|
|
40
|
+
for value in values:
|
|
41
|
+
assert separator not in value
|
|
42
|
+
|
|
43
|
+
def test_find_safe_separator_prioritizes_highest_available(self):
|
|
44
|
+
# When clean values, uses highest priority
|
|
45
|
+
values = ["Artist One", "Artist Two"]
|
|
46
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
47
|
+
assert separator == "//"
|
|
48
|
+
|
|
49
|
+
# When // appears, uses next priority
|
|
50
|
+
values = ["Artist//One", "Artist Two"]
|
|
51
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
52
|
+
assert separator == "\\\\" # Double backslash
|
|
53
|
+
|
|
54
|
+
# When both // and \\ appear, uses semicolon
|
|
55
|
+
values = ["Artist//One", "Artist\\\\Two"] # Note: \\\\ in string is \\
|
|
56
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
57
|
+
assert separator not in ["//", "\\"] # Neither // nor single \
|
|
58
|
+
for value in values:
|
|
59
|
+
assert separator not in value
|
|
60
|
+
|
|
61
|
+
def test_find_safe_separator_no_safe_separator(self):
|
|
62
|
+
# Test case where all common separators appear
|
|
63
|
+
# Note: Single backslash is hard to test, so this tests the common separators
|
|
64
|
+
from audiometa.manager._MetadataManager import METADATA_MULTI_VALUE_SEPARATORS_PRIORITIZED
|
|
65
|
+
|
|
66
|
+
values = ["Artist//One", "Artist;Two", "Artist/Three", "Artist,Four"]
|
|
67
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
68
|
+
# Should still find a safe separator (backslash-based ones don't appear)
|
|
69
|
+
assert separator in METADATA_MULTI_VALUE_SEPARATORS_PRIORITIZED
|
|
70
|
+
for value in values:
|
|
71
|
+
assert separator not in value
|
|
72
|
+
|
|
73
|
+
@pytest.mark.parametrize(
|
|
74
|
+
"values",
|
|
75
|
+
[
|
|
76
|
+
(["Artist//One", "Artist\\\\Two"]),
|
|
77
|
+
(["Artist;One", "Artist\\Two", "Artist,Three"]),
|
|
78
|
+
(["Band/Name", "Artist;Separated", "Composer,List"]),
|
|
79
|
+
(["Artist//With\\Slashes", "Another;With\\Semicolon"]),
|
|
80
|
+
],
|
|
81
|
+
)
|
|
82
|
+
def test_real_world_artist_names(self, values):
|
|
83
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
84
|
+
for value in values:
|
|
85
|
+
assert separator not in value
|
|
86
|
+
|
|
87
|
+
def test_values_with_multiple_separators(self):
|
|
88
|
+
values = ["Artist//One/Two", "Artist\\\\Three\\Four", "Artist;Five"]
|
|
89
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
90
|
+
# Should find a separator that's safe (doesn't appear in values)
|
|
91
|
+
# Since all common separators appear, it will pick the last available one
|
|
92
|
+
for value in values:
|
|
93
|
+
assert separator not in value
|
|
94
|
+
|
|
95
|
+
def test_empty_list(self):
|
|
96
|
+
from audiometa.manager._MetadataManager import METADATA_MULTI_VALUE_SEPARATORS_PRIORITIZED
|
|
97
|
+
|
|
98
|
+
separator = MetadataManager.find_safe_separator([])
|
|
99
|
+
assert separator in METADATA_MULTI_VALUE_SEPARATORS_PRIORITIZED
|
|
100
|
+
|
|
101
|
+
def test_single_value(self):
|
|
102
|
+
separator = MetadataManager.find_safe_separator(["Artist One"])
|
|
103
|
+
assert separator == "//"
|
|
104
|
+
|
|
105
|
+
@pytest.mark.parametrize(
|
|
106
|
+
("values", "expected_separator"),
|
|
107
|
+
[
|
|
108
|
+
(["DJ Snake", "The Chainsmokers"], "//"),
|
|
109
|
+
(["Miley Cyrus & Billy Ray Cyrus", "Florida Georgia Line"], "//"),
|
|
110
|
+
(["Hank Williams, Jr", "Hank Williams"], "//"),
|
|
111
|
+
# // and / appear, so skips to \\\\
|
|
112
|
+
(["7//Heaven", "Back/Street"], "\\\\"),
|
|
113
|
+
# ; and \ appear, so skips to //
|
|
114
|
+
(["Artist;With\\Backslash", "Another\\Artist"], "//"),
|
|
115
|
+
],
|
|
116
|
+
)
|
|
117
|
+
def test_complex_artist_names_from_spec_examples(self, values, expected_separator):
|
|
118
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
119
|
+
assert separator == expected_separator
|
|
120
|
+
for value in values:
|
|
121
|
+
assert separator not in value
|
|
122
|
+
|
|
123
|
+
def test_unicode_and_special_characters(self):
|
|
124
|
+
values = ["Artíst", "Artišt", "Arti§t"]
|
|
125
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
126
|
+
assert separator == "//"
|
|
127
|
+
|
|
128
|
+
def test_spaces_and_punctuation(self):
|
|
129
|
+
values = ["Artist One (Feat. Collaborator)", "Artist Two & Friend"]
|
|
130
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
131
|
+
assert separator == "//"
|
|
132
|
+
|
|
133
|
+
def test_find_safe_separator_empty_and_whitespace_values(self):
|
|
134
|
+
"""Test behavior with empty strings and whitespace."""
|
|
135
|
+
values = ["", " ", "Artist One", "Artist Two"]
|
|
136
|
+
separator = MetadataManager.find_safe_separator(values)
|
|
137
|
+
assert separator == "//"
|
|
138
|
+
|
|
139
|
+
# Empty and whitespace shouldn't affect separator choice
|
|
140
|
+
for value in values:
|
|
141
|
+
if value.strip(): # Only check non-empty values
|
|
142
|
+
assert separator not in value
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa.manager._MetadataManager import _MetadataManager as MetadataManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.unit
|
|
7
|
+
class TestValueFiltering:
|
|
8
|
+
@pytest.mark.parametrize(
|
|
9
|
+
("values", "expected_filtered"),
|
|
10
|
+
[
|
|
11
|
+
(["Artist One", "Artist Two"], ["Artist One", "Artist Two"]),
|
|
12
|
+
(["Artist One", ""], ["Artist One"]),
|
|
13
|
+
(["Artist One", " "], ["Artist One", " "]),
|
|
14
|
+
(["", "Artist Two"], ["Artist Two"]),
|
|
15
|
+
([None, "Artist Two"], ["Artist Two"]),
|
|
16
|
+
(["Artist One", None, "Artist Two"], ["Artist One", "Artist Two"]),
|
|
17
|
+
([], []),
|
|
18
|
+
([""], []),
|
|
19
|
+
([" "], [" "]),
|
|
20
|
+
([None], []),
|
|
21
|
+
(["", None, " "], [" "]),
|
|
22
|
+
],
|
|
23
|
+
)
|
|
24
|
+
def test_filter_valid_values(self, values, expected_filtered):
|
|
25
|
+
result = MetadataManager._filter_valid_values(values)
|
|
26
|
+
assert result == expected_filtered
|
|
27
|
+
|
|
28
|
+
def test_filter_valid_values_all_valid(self):
|
|
29
|
+
values = ["Artist One", "Artist Two", "Artist Three"]
|
|
30
|
+
result = MetadataManager._filter_valid_values(values)
|
|
31
|
+
assert result == values
|
|
32
|
+
|
|
33
|
+
def test_filter_valid_values_all_invalid(self):
|
|
34
|
+
values = [None, ""]
|
|
35
|
+
result = MetadataManager._filter_valid_values(values)
|
|
36
|
+
assert result == []
|
|
37
|
+
|
|
38
|
+
def test_mixed_empty_and_valid_entries_filtering(self):
|
|
39
|
+
values = ["Valid Artist 1", "", "Valid Artist 2", "", "Valid Artist 3"]
|
|
40
|
+
result = MetadataManager._filter_valid_values(values)
|
|
41
|
+
assert result == ["Valid Artist 1", "Valid Artist 2", "Valid Artist 3"]
|
|
42
|
+
|
|
43
|
+
def test_filter_valid_values_removes_empty_strings(self):
|
|
44
|
+
values = ["Valid Artist", "", "Another Valid Artist"]
|
|
45
|
+
result = MetadataManager._filter_valid_values(values)
|
|
46
|
+
assert result == ["Valid Artist", "Another Valid Artist"]
|
|
47
|
+
|
|
48
|
+
def test_filter_valid_values_only_whitespace_not_empty(self):
|
|
49
|
+
# filter_valid_values only removes None and empty strings, not whitespace
|
|
50
|
+
values = [" ", "\t", "\n"]
|
|
51
|
+
result = MetadataManager._filter_valid_values(values)
|
|
52
|
+
assert result == values # Whitespace strings are not empty
|
|
53
|
+
|
|
54
|
+
def test_single_empty_value(self):
|
|
55
|
+
values = [""]
|
|
56
|
+
result = MetadataManager._filter_valid_values(values)
|
|
57
|
+
assert result == []
|
|
58
|
+
|
|
59
|
+
def test_none_and_empty_mixed(self):
|
|
60
|
+
values = [None, "Artist", "", "Another Artist", None]
|
|
61
|
+
result = MetadataManager._filter_valid_values(values)
|
|
62
|
+
assert result == ["Artist", "Another Artist"]
|
|
63
|
+
|
|
64
|
+
def test_all_none_values_filtered_to_empty_list(self):
|
|
65
|
+
# Test that [None, None] filters to empty list
|
|
66
|
+
# This matches the integration test scenario where field is removed
|
|
67
|
+
values = [None, None]
|
|
68
|
+
result = MetadataManager._filter_valid_values(values)
|
|
69
|
+
assert result == []
|
|
70
|
+
|
|
71
|
+
def test_mixed_none_and_valid_values_filtering(self):
|
|
72
|
+
# Test that None values are filtered but valid values remain
|
|
73
|
+
# This matches the integration test scenario
|
|
74
|
+
values = ["Artist One", None, "Artist Two", None, "Artist Three"]
|
|
75
|
+
result = MetadataManager._filter_valid_values(values)
|
|
76
|
+
assert result == ["Artist One", "Artist Two", "Artist Three"]
|
|
File without changes
|
|
File without changes
|
audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from unittest.mock import MagicMock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa.manager._rating_supporting.id3v2._Id3v2Manager import _Id3v2Manager as Id3v2Manager
|
|
6
|
+
from audiometa.manager._rating_supporting.riff._RiffManager import _RiffManager as RiffManager
|
|
7
|
+
from audiometa.manager._rating_supporting.vorbis._VorbisManager import _VorbisManager as VorbisManager
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.unit
|
|
12
|
+
class TestNormalization:
|
|
13
|
+
@pytest.mark.parametrize(
|
|
14
|
+
("metadata_rating_read", "expected_normalized_value"),
|
|
15
|
+
[
|
|
16
|
+
(0, 0),
|
|
17
|
+
(13, 1),
|
|
18
|
+
(1, 2),
|
|
19
|
+
(54, 3),
|
|
20
|
+
(64, 4),
|
|
21
|
+
(118, 5),
|
|
22
|
+
(128, 6),
|
|
23
|
+
(186, 7),
|
|
24
|
+
(196, 8),
|
|
25
|
+
(242, 9),
|
|
26
|
+
(255, 10),
|
|
27
|
+
],
|
|
28
|
+
)
|
|
29
|
+
def test_base_255_non_proportional(self, metadata_rating_read, expected_normalized_value):
|
|
30
|
+
id3v2_audio_file = MagicMock()
|
|
31
|
+
id3v2_manager = Id3v2Manager(audio_file=id3v2_audio_file, normalized_rating_max_value=10)
|
|
32
|
+
id3v2_normalized_rating = id3v2_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
33
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
34
|
+
raw_clean_metadata_uppercase_keys={
|
|
35
|
+
Id3v2Manager.Id3TextFrame.RATING: [id3v2_manager.ID3_RATING_APP_EMAIL, metadata_rating_read]
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
assert id3v2_normalized_rating == expected_normalized_value
|
|
39
|
+
|
|
40
|
+
wave_audio_file = MagicMock()
|
|
41
|
+
wave_audio_file.file_extension = ".wav"
|
|
42
|
+
riff_manager = RiffManager(audio_file=wave_audio_file, normalized_rating_max_value=10)
|
|
43
|
+
riff_normalized_rating = riff_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
44
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
45
|
+
raw_clean_metadata_uppercase_keys={RiffManager.RiffTagKey.RATING: [str(metadata_rating_read)]},
|
|
46
|
+
)
|
|
47
|
+
assert riff_normalized_rating == expected_normalized_value
|
|
48
|
+
|
|
49
|
+
flac_audio_file = MagicMock()
|
|
50
|
+
flac_audio_file.file_extension = ".flac"
|
|
51
|
+
vorbis_manager = VorbisManager(audio_file=flac_audio_file, normalized_rating_max_value=10)
|
|
52
|
+
vorbis_normalized_rating = vorbis_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
53
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
54
|
+
raw_clean_metadata_uppercase_keys={VorbisManager.VorbisKey.RATING: [metadata_rating_read]},
|
|
55
|
+
)
|
|
56
|
+
assert vorbis_normalized_rating == expected_normalized_value
|
|
57
|
+
|
|
58
|
+
@pytest.mark.parametrize(
|
|
59
|
+
("metadata_rating_read", "expected_normalized_value"),
|
|
60
|
+
[
|
|
61
|
+
(0, 0),
|
|
62
|
+
(10, 1),
|
|
63
|
+
(20, 2),
|
|
64
|
+
(30, 3),
|
|
65
|
+
(40, 4),
|
|
66
|
+
(50, 5),
|
|
67
|
+
(60, 6),
|
|
68
|
+
(70, 7),
|
|
69
|
+
(80, 8),
|
|
70
|
+
(90, 9),
|
|
71
|
+
(100, 10),
|
|
72
|
+
],
|
|
73
|
+
)
|
|
74
|
+
def test_base_100_proportional(self, metadata_rating_read, expected_normalized_value):
|
|
75
|
+
# ID3v2
|
|
76
|
+
id3v2_manager = Id3v2Manager(audio_file=MagicMock(), normalized_rating_max_value=10)
|
|
77
|
+
id3v2_normalized_rating = id3v2_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
78
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
79
|
+
raw_clean_metadata_uppercase_keys={
|
|
80
|
+
Id3v2Manager.Id3TextFrame.RATING: [id3v2_manager.ID3_RATING_APP_EMAIL, metadata_rating_read]
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
assert id3v2_normalized_rating == expected_normalized_value
|
|
84
|
+
|
|
85
|
+
# RIFF / WAV
|
|
86
|
+
wave_audio_file = MagicMock()
|
|
87
|
+
wave_audio_file.file_extension = ".wav"
|
|
88
|
+
riff_manager = RiffManager(audio_file=wave_audio_file, normalized_rating_max_value=10)
|
|
89
|
+
riff_normalized_rating = riff_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
90
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
91
|
+
raw_clean_metadata_uppercase_keys={RiffManager.RiffTagKey.RATING: [str(metadata_rating_read)]},
|
|
92
|
+
)
|
|
93
|
+
assert riff_normalized_rating == expected_normalized_value
|
|
94
|
+
|
|
95
|
+
# VORBIS / FLAC
|
|
96
|
+
flac_audio_file = MagicMock()
|
|
97
|
+
flac_audio_file.file_extension = ".flac"
|
|
98
|
+
vorbis_manager = VorbisManager(audio_file=flac_audio_file, normalized_rating_max_value=10)
|
|
99
|
+
vorbis_normalized_rating = vorbis_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
100
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
101
|
+
raw_clean_metadata_uppercase_keys={VorbisManager.VorbisKey.RATING: [metadata_rating_read]},
|
|
102
|
+
)
|
|
103
|
+
assert vorbis_normalized_rating == expected_normalized_value
|
|
104
|
+
|
|
105
|
+
@pytest.mark.parametrize(
|
|
106
|
+
("metadata_rating_read", "expected_normalized_value"),
|
|
107
|
+
[
|
|
108
|
+
(51, 1),
|
|
109
|
+
(102, 2),
|
|
110
|
+
(153, 3),
|
|
111
|
+
(204, 4),
|
|
112
|
+
(255, 5),
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
def test_base_255_proportional(self, metadata_rating_read, expected_normalized_value):
|
|
116
|
+
# ID3v2
|
|
117
|
+
id3v2_manager = Id3v2Manager(audio_file=MagicMock(), normalized_rating_max_value=5)
|
|
118
|
+
id3v2_normalized_rating = id3v2_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
119
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
120
|
+
raw_clean_metadata_uppercase_keys={
|
|
121
|
+
Id3v2Manager.Id3TextFrame.RATING: [id3v2_manager.ID3_RATING_APP_EMAIL, metadata_rating_read]
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
assert id3v2_normalized_rating == expected_normalized_value
|
|
125
|
+
|
|
126
|
+
# RIFF / WAV
|
|
127
|
+
wave_audio_file = MagicMock()
|
|
128
|
+
wave_audio_file.file_extension = ".wav"
|
|
129
|
+
riff_manager = RiffManager(audio_file=wave_audio_file, normalized_rating_max_value=5)
|
|
130
|
+
riff_normalized_rating = riff_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
131
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
132
|
+
raw_clean_metadata_uppercase_keys={RiffManager.RiffTagKey.RATING: [str(metadata_rating_read)]},
|
|
133
|
+
)
|
|
134
|
+
assert riff_normalized_rating == expected_normalized_value
|
|
135
|
+
|
|
136
|
+
# VORBIS / FLAC
|
|
137
|
+
flac_audio_file = MagicMock()
|
|
138
|
+
flac_audio_file.file_extension = ".flac"
|
|
139
|
+
vorbis_manager = VorbisManager(audio_file=flac_audio_file, normalized_rating_max_value=5)
|
|
140
|
+
vorbis_normalized_rating = vorbis_manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
141
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
142
|
+
raw_clean_metadata_uppercase_keys={VorbisManager.VorbisKey.RATING: [metadata_rating_read]},
|
|
143
|
+
)
|
|
144
|
+
assert vorbis_normalized_rating == expected_normalized_value
|
|
145
|
+
|
|
146
|
+
def test_invalid_value(self):
|
|
147
|
+
manager = Id3v2Manager(audio_file=MagicMock(), normalized_rating_max_value=10)
|
|
148
|
+
normalized_rating = manager._get_undirectly_mapped_metadata_value_from_raw_clean_metadata(
|
|
149
|
+
unified_metadata_key=UnifiedMetadataKey.RATING,
|
|
150
|
+
raw_clean_metadata_uppercase_keys={Id3v2Manager.Id3TextFrame.RATING: [manager.ID3_RATING_APP_EMAIL, 300]},
|
|
151
|
+
)
|
|
152
|
+
assert normalized_rating is None
|
audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa.utils.rating_profiles import RatingReadProfile
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.unit
|
|
7
|
+
class TestRatingProfileValues:
|
|
8
|
+
@pytest.mark.parametrize(
|
|
9
|
+
("profile_enum", "expected_values"),
|
|
10
|
+
[
|
|
11
|
+
(RatingReadProfile.BASE_255_NON_PROPORTIONAL, (0, 13, 1, 54, 64, 118, 128, 186, 196, 242, 255)),
|
|
12
|
+
(RatingReadProfile.BASE_100_PROPORTIONAL, (0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100)),
|
|
13
|
+
(
|
|
14
|
+
RatingReadProfile.BASE_255_PROPORTIONAL_TRAKTOR,
|
|
15
|
+
(None, None, 51, None, 102, None, 153, None, 204, None, 255),
|
|
16
|
+
),
|
|
17
|
+
],
|
|
18
|
+
)
|
|
19
|
+
def test_profile_values(self, profile_enum, expected_values):
|
|
20
|
+
profile = profile_enum.value
|
|
21
|
+
|
|
22
|
+
assert profile == expected_values
|
|
23
|
+
assert len(profile) == 11 # 0-5 stars (0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import validate_metadata_for_update
|
|
4
|
+
from audiometa.exceptions import InvalidMetadataFieldTypeError, InvalidRatingValueError
|
|
5
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.unit
|
|
9
|
+
class TestRatingValidation:
|
|
10
|
+
"""Test rating field validation in validate_metadata_for_update."""
|
|
11
|
+
|
|
12
|
+
@pytest.mark.parametrize(
|
|
13
|
+
"rating",
|
|
14
|
+
[0, 128, 255],
|
|
15
|
+
)
|
|
16
|
+
def test_valid_rating_raw_mode(self, rating):
|
|
17
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: rating})
|
|
18
|
+
|
|
19
|
+
@pytest.mark.parametrize(
|
|
20
|
+
"rating",
|
|
21
|
+
[1.5, 75.7],
|
|
22
|
+
)
|
|
23
|
+
def test_invalid_rating_float_in_raw_mode_raises_error(self, rating):
|
|
24
|
+
with pytest.raises(InvalidRatingValueError) as exc_info:
|
|
25
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: rating})
|
|
26
|
+
assert "In raw mode, float values must be whole numbers" in str(exc_info.value)
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize(
|
|
29
|
+
"rating",
|
|
30
|
+
[0.0, 196.0, 128.0],
|
|
31
|
+
)
|
|
32
|
+
def test_valid_rating_whole_number_float_in_raw_mode(self, rating):
|
|
33
|
+
# Whole-number floats like 0.0, 196.0 are accepted and converted to int
|
|
34
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: rating})
|
|
35
|
+
|
|
36
|
+
@pytest.mark.parametrize(
|
|
37
|
+
"rating",
|
|
38
|
+
[50, 0, 100, 1.5, 7.5, 50.0],
|
|
39
|
+
)
|
|
40
|
+
def test_valid_rating_normalized_mode(self, rating):
|
|
41
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: rating}, normalized_rating_max_value=100)
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize(
|
|
44
|
+
"rating",
|
|
45
|
+
[-1, -100, -0.5, -1.5],
|
|
46
|
+
)
|
|
47
|
+
def test_invalid_rating_negative_raises_error(self, rating):
|
|
48
|
+
with pytest.raises(InvalidRatingValueError):
|
|
49
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: rating})
|
|
50
|
+
|
|
51
|
+
@pytest.mark.parametrize(
|
|
52
|
+
"rating",
|
|
53
|
+
[101, 100.1, 101.5],
|
|
54
|
+
)
|
|
55
|
+
def test_invalid_rating_over_max_raises_error(self, rating):
|
|
56
|
+
with pytest.raises(InvalidRatingValueError):
|
|
57
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: rating}, normalized_rating_max_value=100)
|
|
58
|
+
|
|
59
|
+
@pytest.mark.parametrize(
|
|
60
|
+
"rating",
|
|
61
|
+
[110, 110.5],
|
|
62
|
+
)
|
|
63
|
+
def test_invalid_rating_profile_value_raises_error(self, rating):
|
|
64
|
+
# Values > max that would map to invalid star rating indices (> 10)
|
|
65
|
+
with pytest.raises(InvalidRatingValueError):
|
|
66
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: rating}, normalized_rating_max_value=100)
|
|
67
|
+
|
|
68
|
+
@pytest.mark.parametrize(
|
|
69
|
+
"invalid_value",
|
|
70
|
+
["invalid", {}],
|
|
71
|
+
)
|
|
72
|
+
def test_invalid_rating_type_raises_error(self, invalid_value):
|
|
73
|
+
with pytest.raises(InvalidMetadataFieldTypeError):
|
|
74
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: invalid_value})
|
|
75
|
+
|
|
76
|
+
def test_rating_none_is_allowed(self):
|
|
77
|
+
validate_metadata_for_update({UnifiedMetadataKey.RATING: None})
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from unittest.mock import MagicMock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa.exceptions import ConfigurationError
|
|
6
|
+
from audiometa.manager._rating_supporting.id3v2._Id3v2Manager import _Id3v2Manager as Id3v2Manager
|
|
7
|
+
from audiometa.manager._rating_supporting.riff._RiffManager import _RiffManager as RiffManager
|
|
8
|
+
from audiometa.manager._rating_supporting.vorbis._VorbisManager import _VorbisManager as VorbisManager
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.unit
|
|
12
|
+
class TestConfigurationError:
|
|
13
|
+
def test_convert_normalized_rating_raises_config_error_when_max_value_none(
|
|
14
|
+
self,
|
|
15
|
+
):
|
|
16
|
+
id3v2_audio_file = MagicMock()
|
|
17
|
+
id3v2_manager = Id3v2Manager(audio_file=id3v2_audio_file, normalized_rating_max_value=None)
|
|
18
|
+
|
|
19
|
+
with pytest.raises(ConfigurationError) as exc_info:
|
|
20
|
+
id3v2_manager._convert_normalized_rating_to_file_rating(normalized_rating=5.0)
|
|
21
|
+
assert "normalized_rating_max_value must be set" in str(exc_info.value)
|
|
22
|
+
|
|
23
|
+
def test_convert_normalized_rating_to_file_rating_raises_configuration_error_for_riff_manager(
|
|
24
|
+
self,
|
|
25
|
+
):
|
|
26
|
+
wave_audio_file = MagicMock()
|
|
27
|
+
wave_audio_file.file_extension = ".wav"
|
|
28
|
+
riff_manager = RiffManager(audio_file=wave_audio_file, normalized_rating_max_value=None)
|
|
29
|
+
|
|
30
|
+
with pytest.raises(ConfigurationError) as exc_info:
|
|
31
|
+
riff_manager._convert_normalized_rating_to_file_rating(normalized_rating=5.0)
|
|
32
|
+
assert "normalized_rating_max_value must be set" in str(exc_info.value)
|
|
33
|
+
|
|
34
|
+
def test_convert_normalized_rating_to_file_rating_raises_configuration_error_for_vorbis_manager(
|
|
35
|
+
self,
|
|
36
|
+
):
|
|
37
|
+
flac_audio_file = MagicMock()
|
|
38
|
+
flac_audio_file.file_extension = ".flac"
|
|
39
|
+
vorbis_manager = VorbisManager(audio_file=flac_audio_file, normalized_rating_max_value=None)
|
|
40
|
+
|
|
41
|
+
with pytest.raises(ConfigurationError) as exc_info:
|
|
42
|
+
vorbis_manager._convert_normalized_rating_to_file_rating(normalized_rating=5.0)
|
|
43
|
+
assert "normalized_rating_max_value must be set" in str(exc_info.value)
|