audiometa-python 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audiometa/__init__.py +1240 -0
- audiometa/__main__.py +6 -0
- audiometa/_audio_file.py +602 -0
- audiometa/cli.py +347 -0
- audiometa/exceptions.py +167 -0
- audiometa/manager/_MetadataManager.py +665 -0
- audiometa/manager/__init__.py +1 -0
- audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +250 -0
- audiometa/manager/_rating_supporting/__init__.py +1 -0
- audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +945 -0
- audiometa/manager/_rating_supporting/id3v2/__init__.py +25 -0
- audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +11 -0
- audiometa/manager/_rating_supporting/riff/_RiffManager.py +778 -0
- audiometa/manager/_rating_supporting/riff/__init__.py +25 -0
- audiometa/manager/_rating_supporting/riff/_riff_constants.py +10 -0
- audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +525 -0
- audiometa/manager/_rating_supporting/vorbis/__init__.py +17 -0
- audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +6 -0
- audiometa/manager/id3v1/_Id3v1Manager.py +512 -0
- audiometa/manager/id3v1/__init__.py +1 -0
- audiometa/manager/id3v1/_constants.py +8 -0
- audiometa/manager/id3v1/id3v1_raw_metadata.py +242 -0
- audiometa/manager/id3v1/id3v1_raw_metadata_key.py +13 -0
- audiometa/test/__init__.py +1 -0
- audiometa/test/assets/create_test_files.py +72 -0
- audiometa/test/helpers/__init__.py +51 -0
- audiometa/test/helpers/common/__init__.py +6 -0
- audiometa/test/helpers/common/audio_file_creator.py +68 -0
- audiometa/test/helpers/common/external_tool_runner.py +74 -0
- audiometa/test/helpers/id3v1/__init__.py +8 -0
- audiometa/test/helpers/id3v1/id3v1_header_verifier.py +18 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +37 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +61 -0
- audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +82 -0
- audiometa/test/helpers/id3v2/__init__.py +28 -0
- audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +305 -0
- audiometa/test/helpers/id3v2/id3v2_header_verifier.py +38 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +56 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +188 -0
- audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +428 -0
- audiometa/test/helpers/riff/__init__.py +8 -0
- audiometa/test/helpers/riff/riff_header_verifier.py +85 -0
- audiometa/test/helpers/riff/riff_manual_metadata_creator.py +216 -0
- audiometa/test/helpers/riff/riff_metadata_deleter.py +56 -0
- audiometa/test/helpers/riff/riff_metadata_getter.py +118 -0
- audiometa/test/helpers/riff/riff_metadata_setter.py +196 -0
- audiometa/test/helpers/scripts/__init__.py +0 -0
- audiometa/test/helpers/technical_info_inspector.py +115 -0
- audiometa/test/helpers/temp_file_with_metadata.py +82 -0
- audiometa/test/helpers/vorbis/__init__.py +8 -0
- audiometa/test/helpers/vorbis/vorbis_header_verifier.py +31 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +49 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +67 -0
- audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +200 -0
- audiometa/test/tests/__init__.py +0 -0
- audiometa/test/tests/conftest.py +261 -0
- audiometa/test/tests/e2e/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/__init__.py +0 -0
- audiometa/test/tests/e2e/cli/error_handling/__init__.py +1 -0
- audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +77 -0
- audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +130 -0
- audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +118 -0
- audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +172 -0
- audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +49 -0
- audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +160 -0
- audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +90 -0
- audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +51 -0
- audiometa/test/tests/e2e/cli/test_cli_delete.py +20 -0
- audiometa/test/tests/e2e/cli/test_cli_formatting.py +31 -0
- audiometa/test/tests/e2e/cli/test_cli_help.py +40 -0
- audiometa/test/tests/e2e/cli/test_cli_read.py +79 -0
- audiometa/test/tests/e2e/cli/test_cli_unified.py +33 -0
- audiometa/test/tests/e2e/cli/test_cli_write.py +116 -0
- audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
- audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +166 -0
- audiometa/test/tests/e2e/workflows/__init__.py +0 -0
- audiometa/test/tests/e2e/workflows/test_core_workflows.py +166 -0
- audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +318 -0
- audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +165 -0
- audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +129 -0
- audiometa/test/tests/e2e/workflows/test_rating_workflows.py +124 -0
- audiometa/test/tests/integration/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +108 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +61 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +65 -0
- audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +69 -0
- audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +79 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +61 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +67 -0
- audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +60 -0
- audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +87 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +62 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +57 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_with_id3v2_tags.py +83 -0
- audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +62 -0
- audiometa/test/tests/integration/conftest.py +29 -0
- audiometa/test/tests/integration/delete_all_metadata/__init__.py +1 -0
- audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +102 -0
- audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +77 -0
- audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +47 -0
- audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +24 -0
- audiometa/test/tests/integration/encoding/__init__.py +1 -0
- audiometa/test/tests/integration/encoding/test_encoding.py +88 -0
- audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +223 -0
- audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
- audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +250 -0
- audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +297 -0
- audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata_edge_cases.py +231 -0
- audiometa/test/tests/integration/get_full_metadata/test_options.py +207 -0
- audiometa/test/tests/integration/get_full_metadata/test_parsed_fields_keys.py +85 -0
- audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album/test_deleting.py +73 -0
- audiometa/test/tests/integration/metadata_field/album/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/album/test_writing.py +50 -0
- audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +83 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +38 -0
- audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +52 -0
- audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +68 -0
- audiometa/test/tests/integration/metadata_field/artists/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/artists/test_writing.py +46 -0
- audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +32 -0
- audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +56 -0
- audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +68 -0
- audiometa/test/tests/integration/metadata_field/comment/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/comment/test_writing.py +49 -0
- audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/composer/test_reading.py +34 -0
- audiometa/test/tests/integration/metadata_field/composer/test_writing.py +41 -0
- audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +81 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +35 -0
- audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +41 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +56 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +54 -0
- audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +61 -0
- audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +65 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +25 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +58 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +61 -0
- audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +191 -0
- audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +62 -0
- audiometa/test/tests/integration/metadata_field/genre/test_writing.py +64 -0
- audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/language/test_deleting.py +75 -0
- audiometa/test/tests/integration/metadata_field/language/test_reading.py +39 -0
- audiometa/test/tests/integration/metadata_field/language/test_writing.py +43 -0
- audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +129 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +57 -0
- audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +59 -0
- audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +88 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +32 -0
- audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +47 -0
- audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +81 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +33 -0
- audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +58 -0
- audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +117 -0
- audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +137 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +77 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +55 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +57 -0
- audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +192 -0
- audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +74 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +82 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +59 -0
- audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +49 -0
- audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +134 -0
- audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/title/test_deleting.py +73 -0
- audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +47 -0
- audiometa/test/tests/integration/metadata_field/title/test_reading.py +36 -0
- audiometa/test/tests/integration/metadata_field/title/test_writing.py +64 -0
- audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +43 -0
- audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +32 -0
- audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +59 -0
- audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +73 -0
- audiometa/test/tests/integration/multiple_values/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +23 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +92 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +216 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +84 -0
- audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +169 -0
- audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +209 -0
- audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +198 -0
- audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +24 -0
- audiometa/test/tests/integration/multiple_values/writing/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +1 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +62 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +36 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +34 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +32 -0
- audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +54 -0
- audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +42 -0
- audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +98 -0
- audiometa/test/tests/integration/reading/__init__.py +1 -0
- audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +80 -0
- audiometa/test/tests/integration/reading/test_reading_error_handling.py +36 -0
- audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
- audiometa/test/tests/integration/real_audio_files/test_reading.py +146 -0
- audiometa/test/tests/integration/real_audio_files/test_writing.py +198 -0
- audiometa/test/tests/integration/technical_info/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +103 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/__init__.py +0 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_audio_data_corruption.py +21 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_flipped_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_non_flac_error.py +13 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_partial_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_random_md5.py +29 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_unset_md5.py +56 -0
- audiometa/test/tests/integration/technical_info/flac_md5/test_valid_md5.py +21 -0
- audiometa/test/tests/integration/technical_info/test_bitrate.py +38 -0
- audiometa/test/tests/integration/technical_info/test_channels.py +38 -0
- audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +38 -0
- audiometa/test/tests/integration/technical_info/test_sample_rate.py +40 -0
- audiometa/test/tests/integration/test_audio_file.py +23 -0
- audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +95 -0
- audiometa/test/tests/integration/writing/__init__.py +0 -0
- audiometa/test/tests/integration/writing/test_error_handling.py +44 -0
- audiometa/test/tests/integration/writing/test_forced_format.py +224 -0
- audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +223 -0
- audiometa/test/tests/integration/writing/test_partial_update.py +36 -0
- audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +79 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +76 -0
- audiometa/test/tests/integration/writing/writing_strategies/test_sync_strategy.py +215 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +42 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +93 -0
- audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +99 -0
- audiometa/test/tests/unit/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +26 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +31 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +38 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +130 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +51 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +28 -0
- audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +31 -0
- audiometa/test/tests/unit/audio_file/test_context_manager.py +30 -0
- audiometa/test/tests/unit/audio_file/test_file_validation.py +40 -0
- audiometa/test/tests/unit/audio_file/test_operations.py +20 -0
- audiometa/test/tests/unit/audio_file/test_path_handling.py +23 -0
- audiometa/test/tests/unit/cli/__init__.py +0 -0
- audiometa/test/tests/unit/cli/test_expand_file_patterns.py +234 -0
- audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/conftest.py +142 -0
- audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +49 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +66 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +57 -0
- audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +53 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +186 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +142 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +76 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +152 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +23 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +77 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +151 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +61 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_date_format_validation.py +135 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_track_number_validation.py +46 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +22 -0
- audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +83 -0
- audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +74 -0
- audiometa/utils/__init__.py +1 -0
- audiometa/utils/id3v1_genre_code_map.py +205 -0
- audiometa/utils/metadata_format.py +31 -0
- audiometa/utils/metadata_writing_strategy.py +16 -0
- audiometa/utils/os_dependencies_checker/__init__.py +24 -0
- audiometa/utils/os_dependencies_checker/base.py +62 -0
- audiometa/utils/os_dependencies_checker/config.py +51 -0
- audiometa/utils/os_dependencies_checker/macos.py +236 -0
- audiometa/utils/os_dependencies_checker/ubuntu.py +95 -0
- audiometa/utils/os_dependencies_checker/windows.py +227 -0
- audiometa/utils/rating_profiles.py +110 -0
- audiometa/utils/tool_path_resolver.py +135 -0
- audiometa/utils/types.py +82 -0
- audiometa/utils/unified_metadata_key.py +81 -0
- audiometa_python-0.2.2.dist-info/METADATA +1464 -0
- audiometa_python-0.2.2.dist-info/RECORD +318 -0
- audiometa_python-0.2.2.dist-info/WHEEL +5 -0
- audiometa_python-0.2.2.dist-info/entry_points.txt +2 -0
- audiometa_python-0.2.2.dist-info/licenses/LICENSE +202 -0
- audiometa_python-0.2.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import update_metadata
|
|
4
|
+
from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
|
|
5
|
+
from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
|
|
6
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
7
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestMultipleValuesId3v23:
|
|
13
|
+
def test_artists_concatenation(self):
|
|
14
|
+
initial_metadata = {"title": "Test Song"}
|
|
15
|
+
with temp_file_with_metadata(initial_metadata, "id3v2.3") as test_file:
|
|
16
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Artist 1", "Artist 2", "Artist 3"]}
|
|
17
|
+
|
|
18
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 3, 0))
|
|
19
|
+
|
|
20
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
|
|
21
|
+
assert raw_metadata["TPE1"] == ["Artist 1//Artist 2//Artist 3"]
|
|
22
|
+
|
|
23
|
+
def test_with_existing_artists_field(self):
|
|
24
|
+
# Start with an existing artist field
|
|
25
|
+
initial_metadata = {"artist": "Existing Artist"}
|
|
26
|
+
with temp_file_with_metadata(initial_metadata, "id3v2.3") as test_file:
|
|
27
|
+
ID3v2MetadataSetter.set_artists(test_file, "Existing 1; Existing 2", version="2.3")
|
|
28
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
|
|
29
|
+
assert raw_metadata["TPE1"] == ["Existing 1; Existing 2"]
|
|
30
|
+
|
|
31
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Existing 1", "New 2"]}
|
|
32
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 3, 0))
|
|
33
|
+
|
|
34
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
|
|
35
|
+
|
|
36
|
+
assert raw_metadata["TPE1"] == ["Existing 1//New 2"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import update_metadata
|
|
4
|
+
from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
|
|
5
|
+
from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
|
|
6
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
7
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestMultipleValuesId3v24:
|
|
13
|
+
def test_write_multiple_artists(self):
|
|
14
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
15
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", "Artist Two"]}
|
|
16
|
+
|
|
17
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 4, 0))
|
|
18
|
+
|
|
19
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
|
|
20
|
+
|
|
21
|
+
assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two"]
|
|
22
|
+
|
|
23
|
+
def test_write_on_existing_artists_field(self):
|
|
24
|
+
with temp_file_with_metadata({}, "id3v2.4") as test_file:
|
|
25
|
+
ID3v2MetadataSetter.set_artists(test_file, ["Existing A\x00Existing B"], version="2.4")
|
|
26
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
|
|
27
|
+
|
|
28
|
+
assert raw_metadata["TPE1"] == ["Existing A\x00Existing B"]
|
|
29
|
+
|
|
30
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Existing A", "New B"]}
|
|
31
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 4, 0))
|
|
32
|
+
|
|
33
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
|
|
34
|
+
assert raw_metadata["TPE1"] == ["Existing A\x00New B"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import update_metadata
|
|
4
|
+
from audiometa.test.helpers.riff.riff_metadata_getter import RIFFMetadataGetter
|
|
5
|
+
from audiometa.test.helpers.riff.riff_metadata_setter import RIFFMetadataSetter
|
|
6
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
7
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestMultipleValuesRiff:
|
|
13
|
+
def test_artists_concatenation(self):
|
|
14
|
+
initial_metadata = {"title": "Test Song"}
|
|
15
|
+
with temp_file_with_metadata(initial_metadata, "wav") as test_file:
|
|
16
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Artist 1", "Artist 2", "Artist 3"]}
|
|
17
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.RIFF)
|
|
18
|
+
|
|
19
|
+
raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
|
|
20
|
+
assert "TAG:artist=Artist 1//Artist 2//Artist 3" in raw_metadata
|
|
21
|
+
|
|
22
|
+
def test_with_existing_artists_field(self):
|
|
23
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
24
|
+
RIFFMetadataSetter.set_artists(test_file, ["Existing 1;Existing 2"])
|
|
25
|
+
raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
|
|
26
|
+
assert "TAG:artist=Existing 1;Existing 2" in raw_metadata
|
|
27
|
+
|
|
28
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Existing 1", "New 2"]}
|
|
29
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.RIFF)
|
|
30
|
+
|
|
31
|
+
raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
|
|
32
|
+
assert "TAG:artist=Existing 1//New 2" in raw_metadata
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import update_metadata
|
|
4
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
5
|
+
from audiometa.test.helpers.vorbis.vorbis_metadata_getter import VorbisMetadataGetter
|
|
6
|
+
from audiometa.test.helpers.vorbis.vorbis_metadata_setter import VorbisMetadataSetter
|
|
7
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestMultipleValuesVorbis:
|
|
13
|
+
def test_write_multiple_artists(self):
|
|
14
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
15
|
+
# Write multiple artists using update_metadata
|
|
16
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", "Artist Two", "Artist Three"]}
|
|
17
|
+
|
|
18
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
|
|
19
|
+
VorbisMetadataSetter.add_title(test_file, "Test Song")
|
|
20
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
21
|
+
assert "ARTIST=Artist One" in raw_metadata
|
|
22
|
+
assert "ARTIST=Artist Two" in raw_metadata
|
|
23
|
+
assert "ARTIST=Artist Three" in raw_metadata
|
|
24
|
+
|
|
25
|
+
def test_with_existing_artists_fields(self):
|
|
26
|
+
# Start with an existing artist field
|
|
27
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
28
|
+
# create an existing value using setter
|
|
29
|
+
VorbisMetadataSetter.set_artists(test_file, ["Existing 1; Existing 2"])
|
|
30
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
31
|
+
assert "Existing 1; Existing 2" in raw_metadata
|
|
32
|
+
|
|
33
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Existing 1", "New 2"]}
|
|
34
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
|
|
35
|
+
|
|
36
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
37
|
+
assert "Existing 1" in raw_metadata
|
|
38
|
+
assert "New 2" in raw_metadata
|
|
39
|
+
assert "Existing 2" not in raw_metadata
|
|
40
|
+
|
|
41
|
+
def test_with_existing_artists_fields_with_lower_case_key(self):
|
|
42
|
+
# Start with an existing artist field in lower case
|
|
43
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
44
|
+
# create an existing value using setter
|
|
45
|
+
VorbisMetadataSetter.set_artists(test_file, ["ExistingLower 1; ExistingLower 2"], key_lower_case=True)
|
|
46
|
+
raw_output = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
47
|
+
assert "artist=ExistingLower 1; ExistingLower 2" in raw_output
|
|
48
|
+
|
|
49
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["ExistingLower 1", "New 2"]}
|
|
50
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
|
|
51
|
+
|
|
52
|
+
raw_output = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
53
|
+
assert "ARTIST=ExistingLower 1" in raw_output
|
|
54
|
+
assert "ARTIST=New 2" in raw_output
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field, update_metadata
|
|
4
|
+
from audiometa.exceptions import InvalidMetadataFieldTypeError
|
|
5
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
6
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestMultipleValuesErrorHandling:
|
|
11
|
+
def test_write_invalid_data_types_in_list(self):
|
|
12
|
+
# Test with invalid data types in multiple value lists
|
|
13
|
+
with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
|
|
14
|
+
metadata = {UnifiedMetadataKey.ARTISTS: [1, 2, 3]} # Numbers instead of strings
|
|
15
|
+
with pytest.raises(InvalidMetadataFieldTypeError):
|
|
16
|
+
update_metadata(temp_audio_file_path, metadata)
|
|
17
|
+
|
|
18
|
+
def test_write_mixed_data_types_in_list(self):
|
|
19
|
+
# Test with mixed data types in multiple value lists
|
|
20
|
+
with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
|
|
21
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", 123, None, "Artist Two"]}
|
|
22
|
+
with pytest.raises(InvalidMetadataFieldTypeError):
|
|
23
|
+
update_metadata(temp_audio_file_path, metadata)
|
|
24
|
+
|
|
25
|
+
def test_write_list_with_none_values_are_filtered(self):
|
|
26
|
+
# Test that None values in lists are automatically filtered out
|
|
27
|
+
# If all values are None, the field should be removed entirely
|
|
28
|
+
with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
|
|
29
|
+
metadata = {UnifiedMetadataKey.ARTISTS: [None, None]}
|
|
30
|
+
update_metadata(temp_audio_file_path, metadata)
|
|
31
|
+
# Field should be removed (None) since all values were filtered out
|
|
32
|
+
artists = get_unified_metadata_field(temp_audio_file_path, UnifiedMetadataKey.ARTISTS)
|
|
33
|
+
assert artists is None
|
|
34
|
+
|
|
35
|
+
def test_write_list_with_mixed_none_and_valid_values(self):
|
|
36
|
+
# Test that None values are filtered but valid values remain
|
|
37
|
+
with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
|
|
38
|
+
metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", None, "Artist Two", None, "Artist Three"]}
|
|
39
|
+
update_metadata(temp_audio_file_path, metadata)
|
|
40
|
+
# None values should be filtered out, only valid artists remain
|
|
41
|
+
artists = get_unified_metadata_field(temp_audio_file_path, UnifiedMetadataKey.ARTISTS)
|
|
42
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_unified_metadata_field, update_metadata
|
|
6
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
7
|
+
from audiometa.utils.metadata_format import MetadataFormat
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestMultipleValuesBoundaryConditions:
|
|
13
|
+
def test_write_large_number_of_multiple_values_per_field(self):
|
|
14
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
15
|
+
# Test with very large number of values per field
|
|
16
|
+
large_values_number = 1000
|
|
17
|
+
large_artist_list = [f"Artist {i:04d}" for i in range(large_values_number)]
|
|
18
|
+
|
|
19
|
+
metadata = {
|
|
20
|
+
UnifiedMetadataKey.ARTISTS: large_artist_list,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
start_time = time.time()
|
|
24
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
|
|
25
|
+
write_time = time.time() - start_time
|
|
26
|
+
|
|
27
|
+
artists = get_unified_metadata_field(
|
|
28
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
assert isinstance(artists, list)
|
|
32
|
+
assert len(artists) == large_values_number
|
|
33
|
+
|
|
34
|
+
# Performance should be reasonable
|
|
35
|
+
assert write_time < 10.0, f"Write took too long: {write_time:.2f}s"
|
|
36
|
+
|
|
37
|
+
def test_write_extremely_long_individual_values(self):
|
|
38
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
39
|
+
# Test with extremely long individual values
|
|
40
|
+
very_long_string = "A" * 50000 # 50KB string
|
|
41
|
+
metadata = {
|
|
42
|
+
UnifiedMetadataKey.ARTISTS: [very_long_string, "Normal Artist"],
|
|
43
|
+
UnifiedMetadataKey.COMMENT: very_long_string,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
|
|
47
|
+
|
|
48
|
+
artists = get_unified_metadata_field(
|
|
49
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
50
|
+
)
|
|
51
|
+
assert isinstance(artists, list)
|
|
52
|
+
assert len(artists) == 2
|
|
53
|
+
assert very_long_string in artists
|
|
54
|
+
assert "Normal Artist" in artists
|
|
55
|
+
|
|
56
|
+
def test_write_mixed_length_values(self):
|
|
57
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
58
|
+
# Test with mixed length values
|
|
59
|
+
mixed_lengths = [
|
|
60
|
+
"A", # 1 character
|
|
61
|
+
"AB", # 2 characters
|
|
62
|
+
"ABC", # 3 characters
|
|
63
|
+
"A" * 100, # 100 characters
|
|
64
|
+
"A" * 1000, # 1000 characters
|
|
65
|
+
"A" * 10000, # 10000 characters
|
|
66
|
+
]
|
|
67
|
+
metadata = {UnifiedMetadataKey.ARTISTS: mixed_lengths}
|
|
68
|
+
update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
|
|
69
|
+
|
|
70
|
+
artists = get_unified_metadata_field(test_file, UnifiedMetadataKey.ARTISTS)
|
|
71
|
+
|
|
72
|
+
assert isinstance(artists, list)
|
|
73
|
+
assert len(artists) == 6
|
|
74
|
+
for value in mixed_lengths:
|
|
75
|
+
assert value in artists
|
|
76
|
+
|
|
77
|
+
def test_write_very_large_metadata_dict(self):
|
|
78
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
79
|
+
# Test with very large metadata dictionary
|
|
80
|
+
large_metadata = {}
|
|
81
|
+
|
|
82
|
+
# Add multiple values for each supported multiple-value field
|
|
83
|
+
# Actual implementation of Vorbis metadata cannot support more than 50 values per field
|
|
84
|
+
for i in range(50):
|
|
85
|
+
large_metadata[UnifiedMetadataKey.ARTISTS] = [f"Artist {i:04d}" for i in range(50)]
|
|
86
|
+
start_time = time.time()
|
|
87
|
+
update_metadata(test_file, large_metadata, metadata_format=MetadataFormat.VORBIS)
|
|
88
|
+
write_time = time.time() - start_time
|
|
89
|
+
|
|
90
|
+
artists = get_unified_metadata_field(
|
|
91
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
assert isinstance(artists, list)
|
|
95
|
+
assert len(artists) == 50
|
|
96
|
+
|
|
97
|
+
# Performance should be reasonable
|
|
98
|
+
assert write_time < 15.0, f"Write took too long: {write_time:.2f}s"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Integration tests for reading metadata."""
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata
|
|
4
|
+
from audiometa.test.helpers.id3v1 import ID3v1MetadataSetter
|
|
5
|
+
from audiometa.test.helpers.id3v2 import ID3v2MetadataSetter
|
|
6
|
+
from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
|
|
7
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestMultipleMetadata:
|
|
13
|
+
def test_id3v1(self):
|
|
14
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
15
|
+
# First set ID3v1 metadata using external tools
|
|
16
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
17
|
+
ID3v1MetadataSetter.set_artist(test_file, "ID3v1 Artist")
|
|
18
|
+
ID3v1MetadataSetter.set_album(test_file, "ID3v1 Album")
|
|
19
|
+
|
|
20
|
+
merged_metadata = get_unified_metadata(test_file)
|
|
21
|
+
assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
22
|
+
assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["ID3v1 Artist"]
|
|
23
|
+
assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "ID3v1 Album"
|
|
24
|
+
|
|
25
|
+
def test_id3v2_3(self):
|
|
26
|
+
with temp_file_with_metadata({}, "id3v2.3") as test_file:
|
|
27
|
+
# First set ID3v2.3 metadata using external tools
|
|
28
|
+
ID3v2MetadataSetter.set_title(test_file, "ID3v2.3 Title", version="2.3")
|
|
29
|
+
ID3v2MetadataSetter.set_artists(test_file, "ID3v2.3 Artist", version="2.3")
|
|
30
|
+
ID3v2MetadataSetter.set_album(test_file, "ID3v2.3 Album", version="2.3")
|
|
31
|
+
|
|
32
|
+
merged_metadata = get_unified_metadata(test_file)
|
|
33
|
+
assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "ID3v2.3 Title"
|
|
34
|
+
assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["ID3v2.3 Artist"]
|
|
35
|
+
assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "ID3v2.3 Album"
|
|
36
|
+
|
|
37
|
+
def test_id3v2_4(self):
|
|
38
|
+
with temp_file_with_metadata({}, "id3v2.4") as test_file:
|
|
39
|
+
# First set ID3v2.4 metadata using external tools
|
|
40
|
+
ID3v2MetadataSetter.set_title(test_file, "ID3v2.4 Title", version="2.4")
|
|
41
|
+
ID3v2MetadataSetter.set_artists(test_file, "ID3v2.4 Artist", version="2.4")
|
|
42
|
+
ID3v2MetadataSetter.set_album(test_file, "ID3v2.4 Album", version="2.4")
|
|
43
|
+
|
|
44
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
|
|
45
|
+
assert raw_metadata["TIT2"] == ["ID3v2.4 Title"]
|
|
46
|
+
assert raw_metadata["TPE1"] == ["ID3v2.4 Artist"]
|
|
47
|
+
assert raw_metadata["TALB"] == ["ID3v2.4 Album"]
|
|
48
|
+
|
|
49
|
+
merged_metadata = get_unified_metadata(test_file)
|
|
50
|
+
assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "ID3v2.4 Title"
|
|
51
|
+
assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["ID3v2.4 Artist"]
|
|
52
|
+
assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "ID3v2.4 Album"
|
|
53
|
+
|
|
54
|
+
def test_riff(self):
|
|
55
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
56
|
+
# First set RIFF metadata using external tools
|
|
57
|
+
from audiometa.test.helpers.riff import RIFFMetadataSetter
|
|
58
|
+
|
|
59
|
+
RIFFMetadataSetter.set_title(test_file, "RIFF Title")
|
|
60
|
+
RIFFMetadataSetter.set_artist(test_file, "RIFF Artist")
|
|
61
|
+
RIFFMetadataSetter.set_album(test_file, "RIFF Album")
|
|
62
|
+
|
|
63
|
+
merged_metadata = get_unified_metadata(test_file)
|
|
64
|
+
assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
|
|
65
|
+
assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["RIFF Artist"]
|
|
66
|
+
assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "RIFF Album"
|
|
67
|
+
|
|
68
|
+
def test_vorbis(self):
|
|
69
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
70
|
+
# First set Vorbis metadata using external tools
|
|
71
|
+
from audiometa.test.helpers.vorbis import VorbisMetadataSetter
|
|
72
|
+
|
|
73
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
74
|
+
VorbisMetadataSetter.set_artist(test_file, "Vorbis Artist")
|
|
75
|
+
VorbisMetadataSetter.set_album(test_file, "Vorbis Album")
|
|
76
|
+
|
|
77
|
+
merged_metadata = get_unified_metadata(test_file)
|
|
78
|
+
assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "Vorbis Title"
|
|
79
|
+
assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["Vorbis Artist"]
|
|
80
|
+
assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "Vorbis Album"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_unified_metadata, get_unified_metadata_field
|
|
6
|
+
from audiometa.exceptions import FileTypeNotSupportedError
|
|
7
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestReadingErrorHandling:
|
|
13
|
+
def test_unsupported_file_type_raises_error(self):
|
|
14
|
+
# Create a file with unsupported extension
|
|
15
|
+
with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
|
|
16
|
+
temp_audio_file_path.write_bytes(b"fake audio content")
|
|
17
|
+
txt_file_path = temp_audio_file_path.with_suffix(".txt")
|
|
18
|
+
txt_file_path.write_bytes(b"fake audio content")
|
|
19
|
+
|
|
20
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
21
|
+
get_unified_metadata(str(txt_file_path))
|
|
22
|
+
|
|
23
|
+
def test_nonexistent_file_raises_error(self):
|
|
24
|
+
nonexistent_file = "nonexistent_file.mp3"
|
|
25
|
+
|
|
26
|
+
with pytest.raises(FileNotFoundError):
|
|
27
|
+
get_unified_metadata(nonexistent_file)
|
|
28
|
+
|
|
29
|
+
with pytest.raises(FileNotFoundError):
|
|
30
|
+
get_unified_metadata_field(nonexistent_file, UnifiedMetadataKey.TITLE)
|
|
31
|
+
|
|
32
|
+
def test_metadata_key_not_found_returns_none(self, sample_mp3_file: Path):
|
|
33
|
+
# This should not raise an error, but return None when the field is not found
|
|
34
|
+
# Using a valid UnifiedMetadataKey that might not be present in the file
|
|
35
|
+
result = get_unified_metadata_field(sample_mp3_file, UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS)
|
|
36
|
+
assert result is None
|
|
File without changes
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""End-to-end tests using real audio files from data/audio_files."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa import get_unified_metadata
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestRealAudioFilesReading:
|
|
13
|
+
"""Test cases using real audio files for end-to-end validation."""
|
|
14
|
+
|
|
15
|
+
def test_recording_allumerlefeu_metadata(self, assets_dir: Path):
|
|
16
|
+
file_path = assets_dir / "recording=Allumerlefeu_2 matches one with more release groups.mp3"
|
|
17
|
+
|
|
18
|
+
metadata = get_unified_metadata(file_path)
|
|
19
|
+
assert isinstance(metadata, dict)
|
|
20
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "Allumer le feu"
|
|
21
|
+
assert metadata[UnifiedMetadataKey.ARTISTS] == ["Johnny Hallyday"]
|
|
22
|
+
assert metadata[UnifiedMetadataKey.ALBUM] == "Les Années 80-90-2000, C'Etait Mieux Avant"
|
|
23
|
+
assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Variétés Internationales"]
|
|
24
|
+
assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2018-09-28"
|
|
25
|
+
assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "39"
|
|
26
|
+
assert metadata[UnifiedMetadataKey.BPM] == 139
|
|
27
|
+
assert metadata[UnifiedMetadataKey.COMPOSERS] == ["Pascal Obispo", "Pierre Jaconelli"]
|
|
28
|
+
assert metadata[UnifiedMetadataKey.PUBLISHER] == ""
|
|
29
|
+
assert metadata[UnifiedMetadataKey.COPYRIGHT] == "© 2018 Mercury Music Group"
|
|
30
|
+
|
|
31
|
+
def test_recording_celinekin_park_metadata(self, assets_dir: Path):
|
|
32
|
+
file_path = assets_dir / "recording=Celinekin Park - no musicbrainz recording duration.mp3"
|
|
33
|
+
|
|
34
|
+
metadata = get_unified_metadata(file_path)
|
|
35
|
+
assert isinstance(metadata, dict)
|
|
36
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "Celinekin Park (Linkin Park vs. Celine Dion)"
|
|
37
|
+
assert metadata[UnifiedMetadataKey.ARTISTS] == ["The Table"]
|
|
38
|
+
assert metadata[UnifiedMetadataKey.ALBUM] == "Bootie Top 10 – November/December 2018" # noqa: RUF001
|
|
39
|
+
assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["A Plus D"]
|
|
40
|
+
assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2018-11-30"
|
|
41
|
+
assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "9/10"
|
|
42
|
+
assert metadata[UnifiedMetadataKey.BPM] == 99
|
|
43
|
+
assert metadata[UnifiedMetadataKey.PUBLISHER] == "[no label]"
|
|
44
|
+
assert metadata[UnifiedMetadataKey.COMMENT] == "BootieMashup.com"
|
|
45
|
+
|
|
46
|
+
def test_recording_dans_la_legende_metadata(self, assets_dir: Path):
|
|
47
|
+
file_path = assets_dir / "recording=Dans la legende.flac"
|
|
48
|
+
|
|
49
|
+
metadata = get_unified_metadata(file_path)
|
|
50
|
+
assert isinstance(metadata, dict)
|
|
51
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "DA"
|
|
52
|
+
assert metadata[UnifiedMetadataKey.ARTISTS] == ["PNL"]
|
|
53
|
+
assert metadata[UnifiedMetadataKey.ALBUM] == "Dans La Légende"
|
|
54
|
+
assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["PNL"]
|
|
55
|
+
assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["French cloud rap"]
|
|
56
|
+
assert metadata[UnifiedMetadataKey.RATING] == 10
|
|
57
|
+
assert metadata[UnifiedMetadataKey.LANGUAGE] == "French"
|
|
58
|
+
assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2016"
|
|
59
|
+
assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "01"
|
|
60
|
+
|
|
61
|
+
def test_recording_kemar_france_metadata(self, assets_dir: Path):
|
|
62
|
+
file_path = assets_dir / "recording=Kemar - France.mp3"
|
|
63
|
+
|
|
64
|
+
metadata = get_unified_metadata(file_path)
|
|
65
|
+
assert isinstance(metadata, dict)
|
|
66
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "Kemar - France"
|
|
67
|
+
assert metadata[UnifiedMetadataKey.BPM] == 140
|
|
68
|
+
|
|
69
|
+
def test_recording_tokyo_drift_metadata(self, assets_dir: Path):
|
|
70
|
+
file_path = assets_dir / "recording=Tokyo Drift_no mb recording.mp3"
|
|
71
|
+
|
|
72
|
+
metadata = get_unified_metadata(file_path)
|
|
73
|
+
assert isinstance(metadata, dict)
|
|
74
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "Tokyo Drift x Temperature x You Little Beauty (BENNE BOOM Mashup)"
|
|
75
|
+
assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2021"
|
|
76
|
+
assert metadata[UnifiedMetadataKey.BPM] == 128
|
|
77
|
+
|
|
78
|
+
def test_recording_y_do_i_carmina_burana_mp3_metadata(self, assets_dir: Path):
|
|
79
|
+
file_path = assets_dir / "recording=Y do i - Carmina Burana Remix - 7m52.mp3"
|
|
80
|
+
|
|
81
|
+
metadata = get_unified_metadata(file_path)
|
|
82
|
+
assert isinstance(metadata, dict)
|
|
83
|
+
assert len(metadata) == 0 # No metadata
|
|
84
|
+
|
|
85
|
+
def test_recording_y_do_i_carmina_burana_wav_metadata(self, assets_dir: Path):
|
|
86
|
+
file_path = assets_dir / "recording=Y do i - Carmina Burana Remix - 7m52.wav"
|
|
87
|
+
|
|
88
|
+
metadata = get_unified_metadata(file_path)
|
|
89
|
+
assert isinstance(metadata, dict)
|
|
90
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == "Y do i - Carmina Burana Remix (Techno of the Opera)"
|
|
91
|
+
# Additional metadata fields that should be present if added:
|
|
92
|
+
if UnifiedMetadataKey.ARTISTS in metadata:
|
|
93
|
+
assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Y do I"]
|
|
94
|
+
if UnifiedMetadataKey.ALBUM_ARTISTS in metadata:
|
|
95
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Y do I"]
|
|
96
|
+
if UnifiedMetadataKey.COMPOSERS in metadata:
|
|
97
|
+
assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["Carl Orff"]
|
|
98
|
+
if UnifiedMetadataKey.ALBUM in metadata:
|
|
99
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM) == "Remixes"
|
|
100
|
+
|
|
101
|
+
def test_recording_california_gurls_metadata(self, assets_dir: Path):
|
|
102
|
+
file_path = assets_dir / "recording=california gurls_id3v2 tags.flac"
|
|
103
|
+
|
|
104
|
+
metadata = get_unified_metadata(file_path)
|
|
105
|
+
assert isinstance(metadata, dict)
|
|
106
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "California Gurls"
|
|
107
|
+
assert metadata[UnifiedMetadataKey.ARTISTS] == ["Katy Perry feat. Snoop Dogg"]
|
|
108
|
+
assert metadata[UnifiedMetadataKey.ALBUM] == "Now That's What I Call Music! 76"
|
|
109
|
+
assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["Various"]
|
|
110
|
+
assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Pop Rock", "Euro House"]
|
|
111
|
+
assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2010"
|
|
112
|
+
assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "1"
|
|
113
|
+
assert metadata[UnifiedMetadataKey.BPM] == 125
|
|
114
|
+
assert (
|
|
115
|
+
metadata[UnifiedMetadataKey.COPYRIGHT]
|
|
116
|
+
== "© EMI Records Ltd. © Virgin Records Ltd. © Universal Music Operations Ltd."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def test_recording_juan_hansen_drown_flac_metadata(self, assets_dir: Path):
|
|
120
|
+
file_path = assets_dir / "recording=juan hansen oostil - drown (massano remix) - 7m20.flac"
|
|
121
|
+
|
|
122
|
+
metadata = get_unified_metadata(file_path)
|
|
123
|
+
assert isinstance(metadata, dict)
|
|
124
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "Drown (Massano Remix)"
|
|
125
|
+
assert metadata[UnifiedMetadataKey.ARTISTS] == ["Øostil & Juan Hansen"]
|
|
126
|
+
assert metadata[UnifiedMetadataKey.ALBUM] == "In My System EP"
|
|
127
|
+
assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["Massano"]
|
|
128
|
+
assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Techno"]
|
|
129
|
+
assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2022"
|
|
130
|
+
assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "2"
|
|
131
|
+
assert metadata[UnifiedMetadataKey.BPM] == 122
|
|
132
|
+
|
|
133
|
+
def test_recording_juan_hansen_drown_mp3_metadata(self, assets_dir: Path):
|
|
134
|
+
file_path = assets_dir / "recording=juan hansen oostil - drown (massano remix) - 7m21.mp3"
|
|
135
|
+
|
|
136
|
+
metadata = get_unified_metadata(file_path)
|
|
137
|
+
assert isinstance(metadata, dict)
|
|
138
|
+
assert metadata[UnifiedMetadataKey.TITLE] == "Drown (Massano Remix)"
|
|
139
|
+
assert metadata[UnifiedMetadataKey.ARTISTS] == ["Øostil"]
|
|
140
|
+
assert metadata[UnifiedMetadataKey.ALBUM] == "In My System EP"
|
|
141
|
+
assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["Massano"]
|
|
142
|
+
assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Electro"]
|
|
143
|
+
assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2022-04-15"
|
|
144
|
+
assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "2"
|
|
145
|
+
assert metadata[UnifiedMetadataKey.BPM] == 122
|
|
146
|
+
assert metadata[UnifiedMetadataKey.PUBLISHER] == "Afterlife"
|