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,216 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field
|
|
4
|
+
from audiometa.test.helpers.id3v2 import ID3v2HeaderVerifier, ID3v2MetadataGetter, ID3v2MetadataSetter
|
|
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 TestId3v24Mixed:
|
|
12
|
+
def test_null_separated_artists(self):
|
|
13
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
14
|
+
ID3v2MetadataSetter.set_artists(test_file, ["Artist One", "Artist Two", "Artist Three"], version="2.4")
|
|
15
|
+
|
|
16
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
17
|
+
|
|
18
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
|
|
19
|
+
# raw_output replaces NUL bytes with slashes for display purposes
|
|
20
|
+
assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
|
|
21
|
+
|
|
22
|
+
artists = get_unified_metadata_field(
|
|
23
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
assert isinstance(artists, list)
|
|
27
|
+
assert len(artists) == 3
|
|
28
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
29
|
+
|
|
30
|
+
def test_null_separated_artists_iso_8859_1(self):
|
|
31
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
32
|
+
# Use a single NUL in the Python string; encoding=0 will encode it as a single 0x00
|
|
33
|
+
ID3v2MetadataSetter.write_tpe1_with_encoding(
|
|
34
|
+
test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=0
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
38
|
+
|
|
39
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
40
|
+
assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
|
|
41
|
+
|
|
42
|
+
artists = get_unified_metadata_field(
|
|
43
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
assert isinstance(artists, list)
|
|
47
|
+
assert len(artists) == 3
|
|
48
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
49
|
+
|
|
50
|
+
def test_null_separated_artists_utf16_with_bom(self):
|
|
51
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
52
|
+
# Using a Python string with '\x00' — when encoded as UTF-16 it will become two zero bytes
|
|
53
|
+
ID3v2MetadataSetter.write_tpe1_with_encoding(
|
|
54
|
+
test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=1
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
58
|
+
|
|
59
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
60
|
+
assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
|
|
61
|
+
|
|
62
|
+
artists = get_unified_metadata_field(
|
|
63
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
assert isinstance(artists, list)
|
|
67
|
+
assert len(artists) == 3
|
|
68
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
69
|
+
|
|
70
|
+
def test_null_separated_artists_utf16be(self):
|
|
71
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
72
|
+
# ID3v2.4 with encoding=2 (UTF-16BE without BOM) — separator becomes two NUL bytes after encoding.
|
|
73
|
+
ID3v2MetadataSetter.write_tpe1_with_encoding(
|
|
74
|
+
test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=2
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
78
|
+
|
|
79
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
80
|
+
assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
|
|
81
|
+
|
|
82
|
+
artists = get_unified_metadata_field(
|
|
83
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
assert isinstance(artists, list)
|
|
87
|
+
assert len(artists) == 3
|
|
88
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
89
|
+
|
|
90
|
+
def test_null_separated_artists_utf8(self):
|
|
91
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
92
|
+
# ID3v2.4 with encoding=3 (UTF-8) — separator is a single NUL byte.
|
|
93
|
+
ID3v2MetadataSetter.write_tpe1_with_encoding(
|
|
94
|
+
test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=3
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
98
|
+
|
|
99
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
100
|
+
assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
|
|
101
|
+
|
|
102
|
+
artists = get_unified_metadata_field(
|
|
103
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
assert isinstance(artists, list)
|
|
107
|
+
assert len(artists) == 3
|
|
108
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
109
|
+
|
|
110
|
+
def test_semicolon_separated_artists(self):
|
|
111
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
112
|
+
ID3v2MetadataSetter.set_artists(test_file, "Artist One;Artist Two;Artist Three", version="2.4")
|
|
113
|
+
|
|
114
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
115
|
+
|
|
116
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
117
|
+
assert raw_metadata["TPE1"] == ["Artist One;Artist Two;Artist Three"]
|
|
118
|
+
|
|
119
|
+
artists = get_unified_metadata_field(
|
|
120
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
assert isinstance(artists, list)
|
|
124
|
+
assert len(artists) == 3
|
|
125
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
126
|
+
|
|
127
|
+
def test_multiple_artists_in_multiple_entries_semicolon_separator(self):
|
|
128
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
129
|
+
ID3v2MetadataSetter.set_artists(
|
|
130
|
+
test_file, ["Artist One;Artist Two", "Artist Three"], in_separate_frames=True, version="2.4"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
134
|
+
|
|
135
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
136
|
+
assert raw_metadata["TPE1"] == ["Artist One;Artist Two", "Artist Three"]
|
|
137
|
+
|
|
138
|
+
artists = get_unified_metadata_field(
|
|
139
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
assert isinstance(artists, list)
|
|
143
|
+
assert len(artists) == 2
|
|
144
|
+
assert artists == ["Artist One;Artist Two", "Artist Three"]
|
|
145
|
+
|
|
146
|
+
def test_null_separated_artists_in_multiple_entries(self):
|
|
147
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
148
|
+
ID3v2MetadataSetter.set_artists(
|
|
149
|
+
test_file, ["Artist One\0Artist Two", "Artist Three"], in_separate_frames=True, version="2.4"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
153
|
+
|
|
154
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
155
|
+
assert raw_metadata["TPE1"] == ["Artist One\0Artist Two", "Artist Three"]
|
|
156
|
+
|
|
157
|
+
artists = get_unified_metadata_field(
|
|
158
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
assert isinstance(artists, list)
|
|
162
|
+
assert len(artists) == 3
|
|
163
|
+
assert artists == ["Artist One", "Artist Two", "Artist Three"]
|
|
164
|
+
|
|
165
|
+
def test_null_separated_artists_in_multiple_entries_and_semicolon_separated(self):
|
|
166
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
167
|
+
ID3v2MetadataSetter.set_artists(
|
|
168
|
+
test_file, ["Artist 1\0Artist 2", "Artist 3;Artist 4"], in_separate_frames=True, version="2.4"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
172
|
+
|
|
173
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
174
|
+
assert raw_metadata["TPE1"] == ["Artist 1\0Artist 2", "Artist 3;Artist 4"]
|
|
175
|
+
|
|
176
|
+
artists = get_unified_metadata_field(
|
|
177
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
assert isinstance(artists, list)
|
|
181
|
+
assert len(artists) == 3
|
|
182
|
+
assert artists == ["Artist 1", "Artist 2", "Artist 3;Artist 4"]
|
|
183
|
+
|
|
184
|
+
def test_mixed_separators_semicolon_and_null(self):
|
|
185
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
186
|
+
ID3v2MetadataSetter.set_artists(test_file, ["Artist 1\0Artist 2;Artist 3"], version="2.4")
|
|
187
|
+
|
|
188
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
189
|
+
|
|
190
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
191
|
+
assert raw_metadata["TPE1"] == ["Artist 1\0Artist 2;Artist 3"]
|
|
192
|
+
|
|
193
|
+
artists = get_unified_metadata_field(
|
|
194
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
|
|
195
|
+
)
|
|
196
|
+
assert isinstance(artists, list)
|
|
197
|
+
assert len(artists) == 2
|
|
198
|
+
assert artists == ["Artist 1", "Artist 2;Artist 3"]
|
|
199
|
+
|
|
200
|
+
def test_multiple_title_entries_then_first_one(self):
|
|
201
|
+
with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
|
|
202
|
+
ID3v2MetadataSetter.set_titles(
|
|
203
|
+
test_file, ["Title One", "Title Two", "Title Three"], in_separate_frames=True, version="2.4"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
|
|
207
|
+
|
|
208
|
+
raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
|
|
209
|
+
assert raw_metadata["TIT2"] == ["Title One", "Title Two", "Title Three"]
|
|
210
|
+
|
|
211
|
+
title = get_unified_metadata_field(
|
|
212
|
+
test_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.ID3V2
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
assert isinstance(title, str)
|
|
216
|
+
assert title == "Title One"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field
|
|
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 TestRiff:
|
|
13
|
+
def test_semicolon_separated_artists(self):
|
|
14
|
+
with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
|
|
15
|
+
RIFFMetadataSetter.set_artists(test_file, ["Artist One;Artist Two;Artist Three"], in_separate_frames=False)
|
|
16
|
+
|
|
17
|
+
raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
|
|
18
|
+
assert "TAG:artist=Artist One;Artist Two;Artist Three" in raw_metadata
|
|
19
|
+
|
|
20
|
+
artists = get_unified_metadata_field(
|
|
21
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.RIFF
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
assert isinstance(artists, list)
|
|
25
|
+
assert len(artists) == 3
|
|
26
|
+
assert "Artist One" in artists
|
|
27
|
+
assert "Artist Two" in artists
|
|
28
|
+
assert "Artist Three" in artists
|
|
29
|
+
|
|
30
|
+
def test_multiple_artists_in_multiple_entries(self):
|
|
31
|
+
with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
|
|
32
|
+
RIFFMetadataSetter.set_artists(test_file, ["One", "Two", "Three"], in_separate_frames=True)
|
|
33
|
+
|
|
34
|
+
raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
|
|
35
|
+
# ffprobe only shows the last field when multiple fields with the same tag exist
|
|
36
|
+
assert "TAG:artist=Three" in raw_metadata
|
|
37
|
+
|
|
38
|
+
# Get RIFF metadata specifically to read the artists
|
|
39
|
+
artists = get_unified_metadata_field(
|
|
40
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.RIFF
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
assert isinstance(artists, list)
|
|
44
|
+
assert len(artists) == 3
|
|
45
|
+
assert "One" in artists
|
|
46
|
+
assert "Two" in artists
|
|
47
|
+
assert "Three" in artists
|
|
48
|
+
|
|
49
|
+
def test_mixed_separators_and_multiple_entries(self):
|
|
50
|
+
with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
|
|
51
|
+
RIFFMetadataSetter.set_artists(
|
|
52
|
+
test_file, ["Artist 1;Artist 2", "Artist 3", "Artist 4"], in_separate_frames=True
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
|
|
56
|
+
# ffprobe only shows the last field when multiple fields with the same tag exist
|
|
57
|
+
assert "TAG:artist=Artist 4" in raw_metadata
|
|
58
|
+
|
|
59
|
+
# Get RIFF metadata specifically to read the artists
|
|
60
|
+
artists = get_unified_metadata_field(
|
|
61
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.RIFF
|
|
62
|
+
)
|
|
63
|
+
assert isinstance(artists, list)
|
|
64
|
+
|
|
65
|
+
# We created 3 separate RIFF frames, so we should get 3 entries
|
|
66
|
+
# (separator parsing happens at a higher level, not in RIFF format itself)
|
|
67
|
+
assert len(artists) == 3
|
|
68
|
+
assert "Artist 1;Artist 2" in artists
|
|
69
|
+
assert "Artist 3" in artists
|
|
70
|
+
assert "Artist 4" in artists
|
|
71
|
+
|
|
72
|
+
def test_multiple_title_entries_then_first_one(self):
|
|
73
|
+
with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
|
|
74
|
+
RIFFMetadataSetter.set_multiple_titles(
|
|
75
|
+
test_file, ["Title One", "Title Two", "Title Three"], in_separate_frames=True
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
|
|
79
|
+
# ffprobe only shows the last field when multiple fields with the same tag exist
|
|
80
|
+
assert "TAG:title=Title Three" in raw_metadata
|
|
81
|
+
|
|
82
|
+
title = get_unified_metadata_field(test_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.RIFF)
|
|
83
|
+
assert isinstance(title, str)
|
|
84
|
+
assert title == "Title One"
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from audiometa import get_unified_metadata_field
|
|
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 TestVorbis:
|
|
13
|
+
def test_null_value_separated_artists(self):
|
|
14
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
15
|
+
VorbisMetadataSetter.set_artists(
|
|
16
|
+
test_file, ["Artist One", "Artist Two", "Artist Three"], in_single_entry=True
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata_without_truncating_null_bytes_but_lower_case_keys(
|
|
20
|
+
test_file
|
|
21
|
+
)
|
|
22
|
+
# The key is lower case because that is how mutagen stores it but it is upper case in the file
|
|
23
|
+
assert "artist=Artist One\x00Artist Two\x00Artist Three" in raw_metadata
|
|
24
|
+
|
|
25
|
+
artists = get_unified_metadata_field(
|
|
26
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
27
|
+
)
|
|
28
|
+
assert isinstance(artists, list)
|
|
29
|
+
assert len(artists) == 3
|
|
30
|
+
assert "Artist One" in artists
|
|
31
|
+
assert "Artist Two" in artists
|
|
32
|
+
assert "Artist Three" in artists
|
|
33
|
+
|
|
34
|
+
def test_null_separated_artists_with_additional_entries(self):
|
|
35
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
36
|
+
VorbisMetadataSetter.set_artists(test_file, ["Artist A", "Artist B"], in_single_entry=True)
|
|
37
|
+
VorbisMetadataSetter.set_artists(
|
|
38
|
+
test_file, ["Artist C", "Artist D"], removing_existing=False, in_single_entry=True
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata_without_truncating_null_bytes_but_lower_case_keys(
|
|
42
|
+
test_file
|
|
43
|
+
)
|
|
44
|
+
# The key is lower case because that is how mutagen stores it but it is upper case in the file
|
|
45
|
+
assert "artist=Artist A\x00Artist B" in raw_metadata
|
|
46
|
+
assert "artist=Artist C\x00Artist D" in raw_metadata
|
|
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) == 4
|
|
53
|
+
assert "Artist A" in artists
|
|
54
|
+
assert "Artist B" in artists
|
|
55
|
+
assert "Artist C" in artists
|
|
56
|
+
assert "Artist D" in artists
|
|
57
|
+
|
|
58
|
+
def test_null_separated_artists_in_multiple_entries_with_semicolon(self):
|
|
59
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
60
|
+
VorbisMetadataSetter.set_artists(test_file, ["Artist One\x00Artist Two", "Artist Three;Artist Four"])
|
|
61
|
+
|
|
62
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata_without_truncating_null_bytes_but_lower_case_keys(
|
|
63
|
+
test_file
|
|
64
|
+
)
|
|
65
|
+
assert "artist=Artist One\x00Artist Two" in raw_metadata
|
|
66
|
+
assert "artist=Artist Three;Artist Four" in raw_metadata
|
|
67
|
+
|
|
68
|
+
artists = get_unified_metadata_field(
|
|
69
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
70
|
+
)
|
|
71
|
+
assert isinstance(artists, list)
|
|
72
|
+
assert len(artists) == 3
|
|
73
|
+
assert "Artist One" in artists
|
|
74
|
+
assert "Artist Two" in artists
|
|
75
|
+
assert "Artist Three;Artist Four" in artists
|
|
76
|
+
|
|
77
|
+
def test_semicolon_separated_artists(self):
|
|
78
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
79
|
+
VorbisMetadataSetter.set_artists(test_file, ["Artist One;Artist Two;Artist Three"])
|
|
80
|
+
|
|
81
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
82
|
+
assert "ARTIST=Artist One;Artist Two;Artist Three" in raw_metadata
|
|
83
|
+
|
|
84
|
+
artists = get_unified_metadata_field(
|
|
85
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
86
|
+
)
|
|
87
|
+
assert isinstance(artists, list)
|
|
88
|
+
assert len(artists) == 3
|
|
89
|
+
assert "Artist One" in artists
|
|
90
|
+
assert "Artist Two" in artists
|
|
91
|
+
assert "Artist Three" in artists
|
|
92
|
+
|
|
93
|
+
def test_artists_in_multiple_entries(self):
|
|
94
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
95
|
+
VorbisMetadataSetter.set_artists(test_file, ["One", "Two", "Three"])
|
|
96
|
+
|
|
97
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
98
|
+
assert "ARTIST=One" in raw_metadata
|
|
99
|
+
assert "ARTIST=Two" in raw_metadata
|
|
100
|
+
assert "ARTIST=Three" in raw_metadata
|
|
101
|
+
|
|
102
|
+
artists = get_unified_metadata_field(
|
|
103
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
assert isinstance(artists, list)
|
|
107
|
+
assert len(artists) == 3
|
|
108
|
+
assert "One" in artists
|
|
109
|
+
assert "Two" in artists
|
|
110
|
+
assert "Three" in artists
|
|
111
|
+
|
|
112
|
+
def test_artists_in_multiple_entries_with_different_key_casings(self):
|
|
113
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
114
|
+
VorbisMetadataSetter.set_artists(test_file, ["Artist A", "Artist B"])
|
|
115
|
+
VorbisMetadataSetter.set_artists(
|
|
116
|
+
test_file, ["Artist C", "Artist D"], removing_existing=False, key_lower_case=True
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
120
|
+
assert "artist=Artist A" in raw_metadata
|
|
121
|
+
assert "artist=Artist B" in raw_metadata
|
|
122
|
+
assert "artist=Artist C" in raw_metadata
|
|
123
|
+
assert "artist=Artist D" in raw_metadata
|
|
124
|
+
|
|
125
|
+
artists = get_unified_metadata_field(
|
|
126
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
assert isinstance(artists, list)
|
|
130
|
+
assert len(artists) == 4
|
|
131
|
+
assert "Artist A" in artists
|
|
132
|
+
assert "Artist B" in artists
|
|
133
|
+
assert "Artist C" in artists
|
|
134
|
+
assert "Artist D" in artists
|
|
135
|
+
|
|
136
|
+
def test_mixed_single_and_multiple_entries(self):
|
|
137
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
138
|
+
VorbisMetadataSetter.set_artists(test_file, ["Artist 1;Artist 2", "Artist 3", "Artist 4"])
|
|
139
|
+
|
|
140
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
141
|
+
|
|
142
|
+
assert "ARTIST=Artist 1;Artist" in raw_metadata
|
|
143
|
+
assert "ARTIST=Artist 3" in raw_metadata
|
|
144
|
+
assert "ARTIST=Artist 4" in raw_metadata
|
|
145
|
+
|
|
146
|
+
artists = get_unified_metadata_field(
|
|
147
|
+
test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
|
|
148
|
+
)
|
|
149
|
+
assert isinstance(artists, list)
|
|
150
|
+
assert len(artists) == 3
|
|
151
|
+
assert "Artist 1;Artist 2" in artists
|
|
152
|
+
assert "Artist 3" in artists
|
|
153
|
+
assert "Artist 4" in artists
|
|
154
|
+
|
|
155
|
+
def test_multiple_title_entries_returns_first_value(self):
|
|
156
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
157
|
+
VorbisMetadataSetter.add_title(test_file, "Title One")
|
|
158
|
+
VorbisMetadataSetter.add_title(test_file, "Title Two")
|
|
159
|
+
VorbisMetadataSetter.add_title(test_file, "Title Three")
|
|
160
|
+
|
|
161
|
+
raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
|
|
162
|
+
assert "TITLE=Title One" in raw_metadata
|
|
163
|
+
assert "TITLE=Title Two" in raw_metadata
|
|
164
|
+
assert "TITLE=Title Three" in raw_metadata
|
|
165
|
+
|
|
166
|
+
title = get_unified_metadata_field(
|
|
167
|
+
test_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.VORBIS
|
|
168
|
+
)
|
|
169
|
+
assert title == "Title One"
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import get_unified_metadata
|
|
6
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
7
|
+
from audiometa.test.helpers.vorbis import VorbisMetadataSetter
|
|
8
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.integration
|
|
12
|
+
class TestPerformanceLargeData:
|
|
13
|
+
def test_performance_with_many_entries(self):
|
|
14
|
+
# Create temporary file with basic metadata
|
|
15
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
16
|
+
# Set many artists using temp_file_with_metadata
|
|
17
|
+
artists_list = [f"Artist {i + 1}" for i in range(20)]
|
|
18
|
+
VorbisMetadataSetter.set_artists(test_file, artists_list)
|
|
19
|
+
|
|
20
|
+
for _ in range(5):
|
|
21
|
+
unified_metadata = get_unified_metadata(test_file)
|
|
22
|
+
artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
|
|
23
|
+
|
|
24
|
+
assert isinstance(artists, list)
|
|
25
|
+
assert len(artists) == 20
|
|
26
|
+
|
|
27
|
+
def test_performance_with_large_separated_values(self):
|
|
28
|
+
# Create temporary file with basic metadata
|
|
29
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
30
|
+
# Set many artists using temp_file_with_metadata
|
|
31
|
+
artists_list = [f"Artist {i + 1}" for i in range(50)]
|
|
32
|
+
VorbisMetadataSetter.set_artists(test_file, artists_list)
|
|
33
|
+
|
|
34
|
+
start_time = time.time()
|
|
35
|
+
unified_metadata = get_unified_metadata(test_file)
|
|
36
|
+
end_time = time.time()
|
|
37
|
+
|
|
38
|
+
artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
|
|
39
|
+
|
|
40
|
+
assert isinstance(artists, list)
|
|
41
|
+
assert len(artists) == 50
|
|
42
|
+
|
|
43
|
+
# Performance should be reasonable (less than 1 second for 50 artists)
|
|
44
|
+
assert (end_time - start_time) < 1.0
|
|
45
|
+
|
|
46
|
+
def test_performance_with_mixed_separators_large(self):
|
|
47
|
+
# Create temporary file with basic metadata
|
|
48
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
49
|
+
# Test performance with complex separator scenarios
|
|
50
|
+
complex_artists = [
|
|
51
|
+
"Artist 1",
|
|
52
|
+
"Artist 2",
|
|
53
|
+
"Artist 3",
|
|
54
|
+
"Artist 4",
|
|
55
|
+
"Artist 5",
|
|
56
|
+
"Artist 6",
|
|
57
|
+
"Artist 7",
|
|
58
|
+
"Artist 8",
|
|
59
|
+
"Artist 9",
|
|
60
|
+
"Artist 10",
|
|
61
|
+
"Artist 11",
|
|
62
|
+
"Artist 12",
|
|
63
|
+
"Artist 13",
|
|
64
|
+
"Artist 14",
|
|
65
|
+
"Artist 15",
|
|
66
|
+
"Artist 16",
|
|
67
|
+
"Artist 17",
|
|
68
|
+
"Artist 18",
|
|
69
|
+
"Artist 19",
|
|
70
|
+
"Artist 20",
|
|
71
|
+
"Artist 21",
|
|
72
|
+
"Artist 22",
|
|
73
|
+
"Artist 23",
|
|
74
|
+
"Artist 24",
|
|
75
|
+
"Artist 25",
|
|
76
|
+
"Artist 26",
|
|
77
|
+
"Artist 27",
|
|
78
|
+
"Artist 28",
|
|
79
|
+
"Artist 29",
|
|
80
|
+
"Artist 30",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
VorbisMetadataSetter.set_artists(test_file, complex_artists)
|
|
85
|
+
except RuntimeError:
|
|
86
|
+
pytest.skip("metaflac not available or failed to set complex separated artists")
|
|
87
|
+
|
|
88
|
+
start_time = time.time()
|
|
89
|
+
unified_metadata = get_unified_metadata(test_file)
|
|
90
|
+
end_time = time.time()
|
|
91
|
+
|
|
92
|
+
artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
|
|
93
|
+
|
|
94
|
+
assert isinstance(artists, list)
|
|
95
|
+
# Should have many artists after complex separation
|
|
96
|
+
assert len(artists) >= 30
|
|
97
|
+
|
|
98
|
+
# Performance should be reasonable
|
|
99
|
+
assert (end_time - start_time) < 2.0
|
|
100
|
+
|
|
101
|
+
def test_memory_usage_with_large_values(self):
|
|
102
|
+
# Create temporary file with basic metadata
|
|
103
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
104
|
+
# Test with very long individual values
|
|
105
|
+
long_artist = "A" * 50000 # 50,000 character artist name
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
VorbisMetadataSetter.set_artist(test_file, long_artist)
|
|
109
|
+
except RuntimeError:
|
|
110
|
+
pytest.skip("metaflac not available or failed to set very long artist")
|
|
111
|
+
|
|
112
|
+
unified_metadata = get_unified_metadata(test_file)
|
|
113
|
+
artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
|
|
114
|
+
|
|
115
|
+
assert isinstance(artists, list)
|
|
116
|
+
assert len(artists) == 1
|
|
117
|
+
assert artists[0] == long_artist
|
|
118
|
+
|
|
119
|
+
def test_performance_repeated_reads(self):
|
|
120
|
+
# Create temporary file with basic metadata
|
|
121
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
122
|
+
# Set up test data
|
|
123
|
+
try:
|
|
124
|
+
artists_list = [f"Artist {i + 1}" for i in range(10)]
|
|
125
|
+
VorbisMetadataSetter.set_artists(test_file, artists_list)
|
|
126
|
+
except RuntimeError:
|
|
127
|
+
pytest.skip("metaflac not available or failed to set test artists")
|
|
128
|
+
|
|
129
|
+
# Test repeated reads
|
|
130
|
+
start_time = time.time()
|
|
131
|
+
for _ in range(100):
|
|
132
|
+
unified_metadata = get_unified_metadata(test_file)
|
|
133
|
+
artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
|
|
134
|
+
assert isinstance(artists, list)
|
|
135
|
+
assert len(artists) == 10
|
|
136
|
+
end_time = time.time()
|
|
137
|
+
|
|
138
|
+
# 100 reads should complete in reasonable time
|
|
139
|
+
assert (end_time - start_time) < 5.0
|
|
140
|
+
|
|
141
|
+
def test_performance_with_all_multi_value_fields(self):
|
|
142
|
+
# Create temporary file with basic metadata
|
|
143
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
144
|
+
# Test performance when multiple multi-value fields are populated
|
|
145
|
+
try:
|
|
146
|
+
# Set multiple artists
|
|
147
|
+
artists_list = [f"Artist {i + 1}" for i in range(10)]
|
|
148
|
+
VorbisMetadataSetter.set_artists(test_file, artists_list)
|
|
149
|
+
|
|
150
|
+
# Set multiple genres
|
|
151
|
+
genres_list = [f"Genre {i + 1}" for i in range(5)]
|
|
152
|
+
VorbisMetadataSetter.set_genres(test_file, genres_list)
|
|
153
|
+
|
|
154
|
+
# Set multiple composers
|
|
155
|
+
composers_list = [f"Composer {i + 1}" for i in range(8)]
|
|
156
|
+
VorbisMetadataSetter.set_composers(test_file, composers_list)
|
|
157
|
+
|
|
158
|
+
except RuntimeError:
|
|
159
|
+
pytest.skip("metaflac not available or failed to set multiple multi-value fields")
|
|
160
|
+
|
|
161
|
+
start_time = time.time()
|
|
162
|
+
unified_metadata = get_unified_metadata(test_file)
|
|
163
|
+
end_time = time.time()
|
|
164
|
+
|
|
165
|
+
# Check all multi-value fields
|
|
166
|
+
artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
|
|
167
|
+
assert isinstance(artists, list)
|
|
168
|
+
assert len(artists) == 10
|
|
169
|
+
|
|
170
|
+
genres = unified_metadata.get(UnifiedMetadataKey.GENRES_NAMES)
|
|
171
|
+
assert isinstance(genres, list)
|
|
172
|
+
assert len(genres) == 5
|
|
173
|
+
|
|
174
|
+
composers = unified_metadata.get(UnifiedMetadataKey.COMPOSERS)
|
|
175
|
+
assert isinstance(composers, list)
|
|
176
|
+
assert len(composers) == 8
|
|
177
|
+
|
|
178
|
+
# Performance should be reasonable
|
|
179
|
+
assert (end_time - start_time) < 2.0
|
|
180
|
+
|
|
181
|
+
def test_performance_with_whitespace_heavy_data(self):
|
|
182
|
+
# Create temporary file with basic metadata
|
|
183
|
+
with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
|
|
184
|
+
# Test performance with data that has lots of whitespace processing
|
|
185
|
+
whitespace_heavy_artists = [" Artist One ", " Artist Two ", " Artist Three "]
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
VorbisMetadataSetter.set_artists(test_file, whitespace_heavy_artists)
|
|
189
|
+
except RuntimeError:
|
|
190
|
+
pytest.skip("metaflac not available or failed to set whitespace-heavy artists")
|
|
191
|
+
|
|
192
|
+
start_time = time.time()
|
|
193
|
+
unified_metadata = get_unified_metadata(test_file)
|
|
194
|
+
end_time = time.time()
|
|
195
|
+
|
|
196
|
+
artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
|
|
197
|
+
|
|
198
|
+
assert isinstance(artists, list)
|
|
199
|
+
assert len(artists) == 3
|
|
200
|
+
assert "Artist One" in artists
|
|
201
|
+
assert "Artist Two" in artists
|
|
202
|
+
assert "Artist Three" in artists
|
|
203
|
+
|
|
204
|
+
# Check that whitespace was properly stripped
|
|
205
|
+
for artist in artists:
|
|
206
|
+
assert artist == artist.strip()
|
|
207
|
+
|
|
208
|
+
# Performance should be reasonable
|
|
209
|
+
assert (end_time - start_time) < 1.0
|