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,58 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_unified_metadata
|
|
6
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestBase255Proportional:
|
|
11
|
+
@pytest.mark.parametrize(
|
|
12
|
+
("star_rating", "expected_normalized_rating"),
|
|
13
|
+
[
|
|
14
|
+
(1, 20),
|
|
15
|
+
(2, 40),
|
|
16
|
+
(3, 60),
|
|
17
|
+
(4, 80),
|
|
18
|
+
(5, 100),
|
|
19
|
+
],
|
|
20
|
+
)
|
|
21
|
+
def test_vorbis_flac_traktor(self, assets_dir: Path, star_rating, expected_normalized_rating):
|
|
22
|
+
file_path = assets_dir / f"rating_vorbis_traktor={star_rating} star.flac"
|
|
23
|
+
metadata = get_unified_metadata(file_path, normalized_rating_max_value=100)
|
|
24
|
+
rating = metadata.get(UnifiedMetadataKey.RATING)
|
|
25
|
+
assert rating is not None
|
|
26
|
+
assert isinstance(rating, int | float)
|
|
27
|
+
assert rating == expected_normalized_rating
|
|
28
|
+
|
|
29
|
+
def test_none_rating_vorbis_flac_traktor(self, assets_dir: Path):
|
|
30
|
+
file_path = assets_dir / "rating_vorbis_traktor=none.flac"
|
|
31
|
+
metadata = get_unified_metadata(file_path, normalized_rating_max_value=100)
|
|
32
|
+
rating = metadata.get(UnifiedMetadataKey.RATING)
|
|
33
|
+
assert rating is None
|
|
34
|
+
|
|
35
|
+
@pytest.mark.parametrize(
|
|
36
|
+
("star_rating", "expected_normalized_rating"),
|
|
37
|
+
[
|
|
38
|
+
(1, 20),
|
|
39
|
+
(2, 40),
|
|
40
|
+
(3, 60),
|
|
41
|
+
(4, 80),
|
|
42
|
+
(5, 100),
|
|
43
|
+
],
|
|
44
|
+
)
|
|
45
|
+
def test_id3v2_mp3_traktor(self, assets_dir: Path, star_rating, expected_normalized_rating):
|
|
46
|
+
file_path = assets_dir / f"rating_id3v2_tracktor={star_rating} star.mp3"
|
|
47
|
+
metadata = get_unified_metadata(file_path, normalized_rating_max_value=100)
|
|
48
|
+
rating = metadata.get(UnifiedMetadataKey.RATING)
|
|
49
|
+
assert rating is not None
|
|
50
|
+
assert isinstance(rating, int | float)
|
|
51
|
+
assert rating == expected_normalized_rating
|
|
52
|
+
|
|
53
|
+
def test_none_rating_id3v2_mp3_traktor(self, assets_dir: Path):
|
|
54
|
+
file_path = assets_dir / "rating_id3v2_tracktor=none.mp3"
|
|
55
|
+
metadata = get_unified_metadata(file_path, normalized_rating_max_value=100)
|
|
56
|
+
rating = metadata.get(UnifiedMetadataKey.RATING)
|
|
57
|
+
# Traktor "none" may actually be 0, not None
|
|
58
|
+
assert rating is None or rating == 0
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field, 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_format import MetadataFormat
|
|
7
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.integration
|
|
11
|
+
class TestRatingDeleting:
|
|
12
|
+
def test_delete_rating_id3v2(self):
|
|
13
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
14
|
+
update_metadata(
|
|
15
|
+
test_file,
|
|
16
|
+
{UnifiedMetadataKey.RATING: 50},
|
|
17
|
+
metadata_format=MetadataFormat.ID3V2,
|
|
18
|
+
normalized_rating_max_value=100,
|
|
19
|
+
)
|
|
20
|
+
assert (
|
|
21
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100) == 50
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: None}, metadata_format=MetadataFormat.ID3V2)
|
|
25
|
+
assert (
|
|
26
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
27
|
+
is None
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def test_delete_rating_id3v1(self):
|
|
31
|
+
with (
|
|
32
|
+
temp_file_with_metadata({}, "id3v1") as test_file,
|
|
33
|
+
pytest.raises(MetadataFieldNotSupportedByMetadataFormatError),
|
|
34
|
+
):
|
|
35
|
+
update_metadata(
|
|
36
|
+
test_file,
|
|
37
|
+
{UnifiedMetadataKey.RATING: 50},
|
|
38
|
+
metadata_format=MetadataFormat.ID3V1,
|
|
39
|
+
normalized_rating_max_value=100,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def test_delete_rating_riff(self):
|
|
43
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
44
|
+
update_metadata(
|
|
45
|
+
test_file,
|
|
46
|
+
{UnifiedMetadataKey.RATING: 50},
|
|
47
|
+
metadata_format=MetadataFormat.RIFF,
|
|
48
|
+
normalized_rating_max_value=100,
|
|
49
|
+
)
|
|
50
|
+
assert (
|
|
51
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100) == 50
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: None}, metadata_format=MetadataFormat.RIFF)
|
|
55
|
+
assert (
|
|
56
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
57
|
+
is None
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def test_delete_rating_vorbis(self):
|
|
61
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
62
|
+
update_metadata(
|
|
63
|
+
test_file,
|
|
64
|
+
{UnifiedMetadataKey.RATING: 50},
|
|
65
|
+
metadata_format=MetadataFormat.VORBIS,
|
|
66
|
+
normalized_rating_max_value=100,
|
|
67
|
+
)
|
|
68
|
+
assert (
|
|
69
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100) == 50
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: None}, metadata_format=MetadataFormat.VORBIS)
|
|
73
|
+
assert (
|
|
74
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
75
|
+
is None
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def test_delete_rating_preserves_other_fields(self):
|
|
79
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
80
|
+
update_metadata(
|
|
81
|
+
test_file,
|
|
82
|
+
{
|
|
83
|
+
UnifiedMetadataKey.RATING: 70,
|
|
84
|
+
UnifiedMetadataKey.TITLE: "Test Title",
|
|
85
|
+
UnifiedMetadataKey.ARTISTS: ["Test Artist"],
|
|
86
|
+
},
|
|
87
|
+
normalized_rating_max_value=100,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: None})
|
|
91
|
+
|
|
92
|
+
assert (
|
|
93
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
94
|
+
is None
|
|
95
|
+
)
|
|
96
|
+
assert get_unified_metadata_field(test_file, UnifiedMetadataKey.TITLE) == "Test Title"
|
|
97
|
+
assert get_unified_metadata_field(test_file, UnifiedMetadataKey.ARTISTS) == ["Test Artist"]
|
|
98
|
+
|
|
99
|
+
def test_delete_rating_already_none(self):
|
|
100
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
101
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: None})
|
|
102
|
+
assert (
|
|
103
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
104
|
+
is None
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def test_delete_rating_zero(self):
|
|
108
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
109
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: 0}, normalized_rating_max_value=100)
|
|
110
|
+
assert (
|
|
111
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100) == 0
|
|
112
|
+
)
|
|
113
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: None})
|
|
114
|
+
assert (
|
|
115
|
+
get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
116
|
+
is None
|
|
117
|
+
)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field, update_metadata
|
|
4
|
+
from audiometa.exceptions import FileTypeNotSupportedError, InvalidMetadataFieldTypeError, InvalidRatingValueError
|
|
5
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
6
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
7
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.integration
|
|
11
|
+
class TestRatingErrorHandling:
|
|
12
|
+
def test_rating_unsupported_file_type(self):
|
|
13
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
14
|
+
temp_file_path.write_bytes(b"fake audio content")
|
|
15
|
+
temp_txt = temp_file_path.with_suffix(".txt")
|
|
16
|
+
temp_txt.write_bytes(b"fake audio content")
|
|
17
|
+
|
|
18
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
19
|
+
get_unified_metadata_field(str(temp_txt), UnifiedMetadataKey.RATING)
|
|
20
|
+
|
|
21
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
22
|
+
update_metadata(str(temp_txt), {UnifiedMetadataKey.RATING: 85})
|
|
23
|
+
|
|
24
|
+
def test_rating_nonexistent_file(self):
|
|
25
|
+
nonexistent_file = "nonexistent_file.mp3"
|
|
26
|
+
|
|
27
|
+
with pytest.raises(FileNotFoundError):
|
|
28
|
+
get_unified_metadata_field(nonexistent_file, UnifiedMetadataKey.RATING)
|
|
29
|
+
|
|
30
|
+
with pytest.raises(FileNotFoundError):
|
|
31
|
+
update_metadata(nonexistent_file, {UnifiedMetadataKey.RATING: 85})
|
|
32
|
+
|
|
33
|
+
def test_write_fractional_values(self):
|
|
34
|
+
# Fractional values are now supported for half-star ratings (consistent with classic star rating systems)
|
|
35
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
36
|
+
with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
|
|
37
|
+
# 25.5/100 maps to index 3 (1.5 stars), which is valid
|
|
38
|
+
update_metadata(
|
|
39
|
+
test_file,
|
|
40
|
+
{UnifiedMetadataKey.RATING: 25.5},
|
|
41
|
+
normalized_rating_max_value=100,
|
|
42
|
+
metadata_format=MetadataFormat.ID3V2,
|
|
43
|
+
)
|
|
44
|
+
# Verify it was written correctly
|
|
45
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
46
|
+
assert rating == 30 # 1.5 stars = 30 on 100-scale
|
|
47
|
+
|
|
48
|
+
def test_rating_invalid_string_value(self):
|
|
49
|
+
with (
|
|
50
|
+
temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file,
|
|
51
|
+
pytest.raises(
|
|
52
|
+
InvalidMetadataFieldTypeError,
|
|
53
|
+
match="Invalid type for metadata field 'rating': expected Union\\[int, float\\], got str",
|
|
54
|
+
),
|
|
55
|
+
):
|
|
56
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: "invalid"}, normalized_rating_max_value=100)
|
|
57
|
+
|
|
58
|
+
def test_rating_negative_value_rejected_in_normalized_mode(self):
|
|
59
|
+
with (
|
|
60
|
+
temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file,
|
|
61
|
+
pytest.raises(InvalidRatingValueError) as exc_info,
|
|
62
|
+
):
|
|
63
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: -1}, normalized_rating_max_value=100)
|
|
64
|
+
assert "must be non-negative" in str(exc_info.value)
|
|
65
|
+
|
|
66
|
+
def test_rating_over_max_value_rejected_in_normalized_mode(self):
|
|
67
|
+
with temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file:
|
|
68
|
+
with pytest.raises(InvalidRatingValueError) as exc_info:
|
|
69
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: 101}, normalized_rating_max_value=100)
|
|
70
|
+
assert "out of range" in str(exc_info.value)
|
|
71
|
+
assert "must be between 0 and 100" in str(exc_info.value)
|
|
72
|
+
|
|
73
|
+
def test_rating_none_value(self):
|
|
74
|
+
with temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file:
|
|
75
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: None}, normalized_rating_max_value=100)
|
|
76
|
+
metadata = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
77
|
+
assert metadata is None
|
|
78
|
+
|
|
79
|
+
def test_rating_without_max_value_allows_any_non_negative_integer(self):
|
|
80
|
+
with temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file:
|
|
81
|
+
# Any non-negative integer value should be allowed when normalized_rating_max_value is None
|
|
82
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: 128}, metadata_format=MetadataFormat.ID3V2)
|
|
83
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: 75}, metadata_format=MetadataFormat.ID3V2)
|
|
84
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: 0}, metadata_format=MetadataFormat.ID3V2)
|
|
85
|
+
|
|
86
|
+
# Negative values should be rejected
|
|
87
|
+
with pytest.raises(InvalidRatingValueError) as exc_info:
|
|
88
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: -1}, metadata_format=MetadataFormat.ID3V2)
|
|
89
|
+
assert "must be non-negative" in str(exc_info.value)
|
|
90
|
+
|
|
91
|
+
# Valid value in BASE_100_PROPORTIONAL profile (Vorbis uses this)
|
|
92
|
+
with temp_file_with_metadata(
|
|
93
|
+
{"title": "Test Title", "artist": "Test Artist"}, "flac"
|
|
94
|
+
) as test_file_flac_path:
|
|
95
|
+
update_metadata(
|
|
96
|
+
test_file_flac_path, {UnifiedMetadataKey.RATING: 50}, metadata_format=MetadataFormat.VORBIS
|
|
97
|
+
)
|
|
98
|
+
update_metadata(
|
|
99
|
+
test_file_flac_path, {UnifiedMetadataKey.RATING: 128}, metadata_format=MetadataFormat.VORBIS
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def test_rating_with_normalized_max_validates_profile_values(self):
|
|
103
|
+
with temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file:
|
|
104
|
+
# Valid: 50/100 maps to star rating index 5 (2.5 stars), which is valid
|
|
105
|
+
update_metadata(
|
|
106
|
+
test_file,
|
|
107
|
+
{UnifiedMetadataKey.RATING: 50},
|
|
108
|
+
normalized_rating_max_value=100,
|
|
109
|
+
metadata_format=MetadataFormat.ID3V2,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Invalid: 37/100 maps to star rating index 4 (2 stars), which is valid
|
|
113
|
+
# But 110/100 would map to index 11, which is > 10 (invalid)
|
|
114
|
+
with pytest.raises(InvalidRatingValueError) as exc_info:
|
|
115
|
+
update_metadata(
|
|
116
|
+
test_file,
|
|
117
|
+
{UnifiedMetadataKey.RATING: 110},
|
|
118
|
+
normalized_rating_max_value=100,
|
|
119
|
+
metadata_format=MetadataFormat.ID3V2,
|
|
120
|
+
)
|
|
121
|
+
assert "out of range" in str(exc_info.value) or "do not exist in any supported writing profile" in str(
|
|
122
|
+
exc_info.value
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def test_invalid_rating_value_error_non_numeric_string(self):
|
|
126
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
127
|
+
with pytest.raises(InvalidMetadataFieldTypeError) as exc_info:
|
|
128
|
+
update_metadata(test_file, {UnifiedMetadataKey.RATING: "invalid"}, normalized_rating_max_value=100)
|
|
129
|
+
assert "invalid type for metadata field 'rating'" in str(exc_info.value).lower()
|
|
130
|
+
|
|
131
|
+
def test_invalid_rating_value_error_non_numeric_type(self):
|
|
132
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
133
|
+
with pytest.raises(InvalidMetadataFieldTypeError) as exc_info:
|
|
134
|
+
update_metadata(
|
|
135
|
+
test_file, {UnifiedMetadataKey.RATING: {"not": "valid"}}, normalized_rating_max_value=100
|
|
136
|
+
)
|
|
137
|
+
assert "invalid type for metadata field 'rating'" in str(exc_info.value).lower()
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field, update_metadata
|
|
4
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
5
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
6
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestId3v2RatingWriting:
|
|
11
|
+
@pytest.mark.parametrize(
|
|
12
|
+
("value", "expected"),
|
|
13
|
+
[
|
|
14
|
+
(0, 0),
|
|
15
|
+
(0.5, 13),
|
|
16
|
+
(1, 1),
|
|
17
|
+
(1.5, 54),
|
|
18
|
+
(2, 64),
|
|
19
|
+
(2.5, 118),
|
|
20
|
+
(3, 128),
|
|
21
|
+
(3.5, 186),
|
|
22
|
+
(4, 196),
|
|
23
|
+
(4.5, 242),
|
|
24
|
+
(5, 255),
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
def test_write_base_255_non_proportional_values(self, value, expected):
|
|
28
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
29
|
+
|
|
30
|
+
with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
|
|
31
|
+
test_metadata = {UnifiedMetadataKey.RATING: value}
|
|
32
|
+
update_metadata(
|
|
33
|
+
test_file, test_metadata, normalized_rating_max_value=5, metadata_format=MetadataFormat.ID3V2
|
|
34
|
+
)
|
|
35
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING)
|
|
36
|
+
assert rating == expected
|
|
37
|
+
|
|
38
|
+
def test_write_none_removes_rating(self):
|
|
39
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
40
|
+
|
|
41
|
+
with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
|
|
42
|
+
# First write a rating
|
|
43
|
+
test_metadata = {UnifiedMetadataKey.RATING: 80}
|
|
44
|
+
update_metadata(
|
|
45
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.ID3V2
|
|
46
|
+
)
|
|
47
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
48
|
+
assert rating == 80
|
|
49
|
+
|
|
50
|
+
# Then remove it with None - this may not work as expected in all cases
|
|
51
|
+
test_metadata = {UnifiedMetadataKey.RATING: None}
|
|
52
|
+
update_metadata(
|
|
53
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.ID3V2
|
|
54
|
+
)
|
|
55
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
56
|
+
# Rating removal behavior may vary - check if it's None or 0
|
|
57
|
+
assert rating is None or rating == 0
|
|
58
|
+
|
|
59
|
+
def test_write_edge_values(self):
|
|
60
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
61
|
+
|
|
62
|
+
with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
|
|
63
|
+
# Test minimum value
|
|
64
|
+
test_metadata = {UnifiedMetadataKey.RATING: 0}
|
|
65
|
+
update_metadata(
|
|
66
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.ID3V2
|
|
67
|
+
)
|
|
68
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
69
|
+
assert rating == 0
|
|
70
|
+
|
|
71
|
+
# Test maximum value
|
|
72
|
+
test_metadata = {UnifiedMetadataKey.RATING: 100}
|
|
73
|
+
update_metadata(
|
|
74
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.ID3V2
|
|
75
|
+
)
|
|
76
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
77
|
+
assert rating == 100
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field, update_metadata
|
|
4
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
5
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
6
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestRiffRatingWriting:
|
|
11
|
+
@pytest.mark.parametrize(
|
|
12
|
+
("star_rating", "expected_normalized_rating"),
|
|
13
|
+
[
|
|
14
|
+
(0, 0),
|
|
15
|
+
(0.5, 10),
|
|
16
|
+
(1, 20),
|
|
17
|
+
(1.5, 30),
|
|
18
|
+
(2, 40),
|
|
19
|
+
(2.5, 50),
|
|
20
|
+
(3, 60),
|
|
21
|
+
(3.5, 70),
|
|
22
|
+
(4, 80),
|
|
23
|
+
(4.5, 90),
|
|
24
|
+
(5, 100),
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
def test_write_star_rating(self, star_rating, expected_normalized_rating):
|
|
28
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
29
|
+
|
|
30
|
+
with temp_file_with_metadata(basic_metadata, "wav") as test_file:
|
|
31
|
+
test_metadata = {UnifiedMetadataKey.RATING: star_rating}
|
|
32
|
+
update_metadata(
|
|
33
|
+
test_file, test_metadata, normalized_rating_max_value=5, metadata_format=MetadataFormat.RIFF
|
|
34
|
+
)
|
|
35
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
36
|
+
assert rating == expected_normalized_rating
|
|
37
|
+
|
|
38
|
+
def test_write_none_removes_rating(self):
|
|
39
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
40
|
+
|
|
41
|
+
with temp_file_with_metadata(basic_metadata, "wav") as test_file:
|
|
42
|
+
# First write a rating
|
|
43
|
+
test_metadata = {UnifiedMetadataKey.RATING: 80}
|
|
44
|
+
update_metadata(
|
|
45
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.RIFF
|
|
46
|
+
)
|
|
47
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
48
|
+
assert rating == 80
|
|
49
|
+
|
|
50
|
+
# Then remove it with None
|
|
51
|
+
test_metadata = {UnifiedMetadataKey.RATING: None}
|
|
52
|
+
update_metadata(
|
|
53
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.RIFF
|
|
54
|
+
)
|
|
55
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata, get_unified_metadata_field, update_metadata
|
|
4
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
5
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
6
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestVorbisRatingWriting:
|
|
11
|
+
@pytest.mark.parametrize(
|
|
12
|
+
("star_rating", "expected_normalized_rating"),
|
|
13
|
+
[
|
|
14
|
+
(0, 0),
|
|
15
|
+
(0.5, 10),
|
|
16
|
+
(1, 20),
|
|
17
|
+
(1.5, 30),
|
|
18
|
+
(2, 40),
|
|
19
|
+
(2.5, 50),
|
|
20
|
+
(3, 60),
|
|
21
|
+
(3.5, 70),
|
|
22
|
+
(4, 80),
|
|
23
|
+
(4.5, 90),
|
|
24
|
+
(5, 100),
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
def test_write_star_rating(self, star_rating, expected_normalized_rating):
|
|
28
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
29
|
+
|
|
30
|
+
with temp_file_with_metadata(basic_metadata, "flac") as test_file:
|
|
31
|
+
test_metadata = {UnifiedMetadataKey.RATING: star_rating}
|
|
32
|
+
update_metadata(
|
|
33
|
+
test_file, test_metadata, normalized_rating_max_value=5, metadata_format=MetadataFormat.VORBIS
|
|
34
|
+
)
|
|
35
|
+
metadata = get_unified_metadata(test_file)
|
|
36
|
+
rating = metadata.get(UnifiedMetadataKey.RATING)
|
|
37
|
+
assert rating == expected_normalized_rating
|
|
38
|
+
|
|
39
|
+
def test_write_none_removes_rating(self):
|
|
40
|
+
basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
|
|
41
|
+
|
|
42
|
+
with temp_file_with_metadata(basic_metadata, "flac") as test_file:
|
|
43
|
+
# First write a rating
|
|
44
|
+
test_metadata = {UnifiedMetadataKey.RATING: 80}
|
|
45
|
+
update_metadata(
|
|
46
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.VORBIS
|
|
47
|
+
)
|
|
48
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
49
|
+
assert rating == 80
|
|
50
|
+
|
|
51
|
+
# Then remove it with None
|
|
52
|
+
test_metadata = {UnifiedMetadataKey.RATING: None}
|
|
53
|
+
update_metadata(
|
|
54
|
+
test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.VORBIS
|
|
55
|
+
)
|
|
56
|
+
rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
|
|
57
|
+
assert rating is None
|