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,54 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from audiometa import get_unified_metadata
|
|
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.e2e
|
|
12
|
+
class TestCLIWriteStringFields:
|
|
13
|
+
def test_cli_write_all_string_fields(self):
|
|
14
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
15
|
+
result = subprocess.run(
|
|
16
|
+
[
|
|
17
|
+
sys.executable,
|
|
18
|
+
"-m",
|
|
19
|
+
"audiometa",
|
|
20
|
+
"write",
|
|
21
|
+
str(test_file),
|
|
22
|
+
"--title",
|
|
23
|
+
"Test Title",
|
|
24
|
+
"--language",
|
|
25
|
+
"eng",
|
|
26
|
+
"--publisher",
|
|
27
|
+
"Test Publisher",
|
|
28
|
+
"--copyright",
|
|
29
|
+
"© 2024 Test",
|
|
30
|
+
"--lyrics",
|
|
31
|
+
"Test lyrics text",
|
|
32
|
+
"--comment",
|
|
33
|
+
"Test comment",
|
|
34
|
+
"--replaygain",
|
|
35
|
+
"+2.5 dB",
|
|
36
|
+
"--archival-location",
|
|
37
|
+
"Archive Location",
|
|
38
|
+
],
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
check=False,
|
|
42
|
+
)
|
|
43
|
+
assert result.returncode == 0
|
|
44
|
+
|
|
45
|
+
metadata = get_unified_metadata(test_file)
|
|
46
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == "Test Title"
|
|
47
|
+
assert metadata.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
48
|
+
assert metadata.get(UnifiedMetadataKey.PUBLISHER) == "Test Publisher"
|
|
49
|
+
assert metadata.get(UnifiedMetadataKey.COPYRIGHT) == "© 2024 Test"
|
|
50
|
+
assert metadata.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "Test lyrics text"
|
|
51
|
+
assert metadata.get(UnifiedMetadataKey.COMMENT) == "Test comment"
|
|
52
|
+
assert metadata.get(UnifiedMetadataKey.REPLAYGAIN) == "+2.5 dB"
|
|
53
|
+
# ARCHIVAL_LOCATION is not supported by ID3v2 format (MP3)
|
|
54
|
+
# It's only supported by Vorbis (FLAC)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.e2e
|
|
10
|
+
class TestCLIWriteValidation:
|
|
11
|
+
def test_cli_write_negative_disc_number_error(self):
|
|
12
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
13
|
+
result = subprocess.run(
|
|
14
|
+
[
|
|
15
|
+
sys.executable,
|
|
16
|
+
"-m",
|
|
17
|
+
"audiometa",
|
|
18
|
+
"write",
|
|
19
|
+
str(test_file),
|
|
20
|
+
"--disc-number",
|
|
21
|
+
"-1",
|
|
22
|
+
],
|
|
23
|
+
capture_output=True,
|
|
24
|
+
text=True,
|
|
25
|
+
check=False,
|
|
26
|
+
)
|
|
27
|
+
assert result.returncode != 0
|
|
28
|
+
assert "cannot be negative" in result.stderr.lower()
|
|
29
|
+
|
|
30
|
+
def test_cli_write_negative_disc_total_error(self):
|
|
31
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
32
|
+
result = subprocess.run(
|
|
33
|
+
[
|
|
34
|
+
sys.executable,
|
|
35
|
+
"-m",
|
|
36
|
+
"audiometa",
|
|
37
|
+
"write",
|
|
38
|
+
str(test_file),
|
|
39
|
+
"--disc-total",
|
|
40
|
+
"-1",
|
|
41
|
+
],
|
|
42
|
+
capture_output=True,
|
|
43
|
+
text=True,
|
|
44
|
+
check=False,
|
|
45
|
+
)
|
|
46
|
+
assert result.returncode != 0
|
|
47
|
+
assert "cannot be negative" in result.stderr.lower()
|
|
48
|
+
|
|
49
|
+
def test_cli_write_negative_bpm_error(self):
|
|
50
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
51
|
+
result = subprocess.run(
|
|
52
|
+
[
|
|
53
|
+
sys.executable,
|
|
54
|
+
"-m",
|
|
55
|
+
"audiometa",
|
|
56
|
+
"write",
|
|
57
|
+
str(test_file),
|
|
58
|
+
"--bpm",
|
|
59
|
+
"-1",
|
|
60
|
+
],
|
|
61
|
+
capture_output=True,
|
|
62
|
+
text=True,
|
|
63
|
+
check=False,
|
|
64
|
+
)
|
|
65
|
+
assert result.returncode != 0
|
|
66
|
+
assert "cannot be negative" in result.stderr.lower()
|
|
67
|
+
|
|
68
|
+
def test_cli_write_negative_year_error(self):
|
|
69
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
70
|
+
result = subprocess.run(
|
|
71
|
+
[
|
|
72
|
+
sys.executable,
|
|
73
|
+
"-m",
|
|
74
|
+
"audiometa",
|
|
75
|
+
"write",
|
|
76
|
+
str(test_file),
|
|
77
|
+
"--year",
|
|
78
|
+
"-1",
|
|
79
|
+
],
|
|
80
|
+
capture_output=True,
|
|
81
|
+
text=True,
|
|
82
|
+
check=False,
|
|
83
|
+
)
|
|
84
|
+
assert result.returncode != 0
|
|
85
|
+
assert "cannot be negative" in result.stderr.lower()
|
|
File without changes
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""End-to-end tests for real user scenarios using external scripts.
|
|
2
|
+
|
|
3
|
+
This refactored version uses external scripts to set up test data instead of the app's update functions, preventing
|
|
4
|
+
circular dependencies.
|
|
5
|
+
|
|
6
|
+
These tests simulate how actual users would interact with the library in real-world applications.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from audiometa import delete_all_metadata, get_unified_metadata, update_metadata
|
|
12
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
13
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.mark.e2e
|
|
17
|
+
class TestUserScenarios:
|
|
18
|
+
def test_music_library_organization(self, sample_mp3_file, sample_flac_file, sample_wav_file):
|
|
19
|
+
# Simulate a user organizing their music library
|
|
20
|
+
|
|
21
|
+
sample_files = [(sample_mp3_file, "mp3"), (sample_flac_file, "flac"), (sample_wav_file, "wav")]
|
|
22
|
+
|
|
23
|
+
for i, (_file_path, format_type) in enumerate(sample_files[:3]): # Test with first 3 files
|
|
24
|
+
# Set basic metadata using external script
|
|
25
|
+
basic_metadata = {"title": f"Original Track {i + 1}", "artist": "Original Artist"}
|
|
26
|
+
with temp_file_with_metadata(basic_metadata, format_type) as test_file:
|
|
27
|
+
# Set consistent metadata for organization using app's function (this is what we're testing)
|
|
28
|
+
test_metadata = {
|
|
29
|
+
UnifiedMetadataKey.ALBUM: "My Music Library",
|
|
30
|
+
UnifiedMetadataKey.TITLE: f"Track {i + 1}",
|
|
31
|
+
}
|
|
32
|
+
update_metadata(test_file, test_metadata)
|
|
33
|
+
|
|
34
|
+
# Verify the organization worked
|
|
35
|
+
metadata = get_unified_metadata(test_file)
|
|
36
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM) == "My Music Library"
|
|
37
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == f"Track {i + 1}"
|
|
38
|
+
|
|
39
|
+
def test_metadata_import_export_workflow(self):
|
|
40
|
+
# Simulate a user importing metadata from external source
|
|
41
|
+
# Use external script to set initial metadata
|
|
42
|
+
initial_metadata = {"title": "Original Title", "artist": "Original Artist", "album": "Original Album"}
|
|
43
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
44
|
+
# Export current metadata
|
|
45
|
+
current_metadata = get_unified_metadata(test_file)
|
|
46
|
+
metadata = {
|
|
47
|
+
"title": current_metadata.get(UnifiedMetadataKey.TITLE),
|
|
48
|
+
"artist": current_metadata.get(UnifiedMetadataKey.ARTISTS),
|
|
49
|
+
"album": current_metadata.get(UnifiedMetadataKey.ALBUM),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Simulate external metadata update
|
|
53
|
+
metadata["title"] = "Updated Title"
|
|
54
|
+
metadata["artist"] = ["Updated Artist"]
|
|
55
|
+
|
|
56
|
+
# Apply updated metadata using app's function (this is what we're testing)
|
|
57
|
+
test_metadata = {
|
|
58
|
+
UnifiedMetadataKey.TITLE: metadata["title"],
|
|
59
|
+
UnifiedMetadataKey.ARTISTS: metadata["artist"],
|
|
60
|
+
UnifiedMetadataKey.ALBUM: metadata["album"],
|
|
61
|
+
}
|
|
62
|
+
update_metadata(test_file, test_metadata)
|
|
63
|
+
|
|
64
|
+
# Verify the import worked
|
|
65
|
+
updated_metadata = get_unified_metadata(test_file)
|
|
66
|
+
assert updated_metadata.get(UnifiedMetadataKey.TITLE) == "Updated Title"
|
|
67
|
+
assert updated_metadata.get(UnifiedMetadataKey.ARTISTS) == ["Updated Artist"]
|
|
68
|
+
|
|
69
|
+
def test_metadata_privacy_cleanup_workflow(self):
|
|
70
|
+
# Simulate a user cleaning up metadata for privacy before sharing files
|
|
71
|
+
initial_metadata = {
|
|
72
|
+
"title": "Personal Music Track",
|
|
73
|
+
"artist": "Personal Artist",
|
|
74
|
+
"album": "Personal Album",
|
|
75
|
+
"comment": "Personal comment with sensitive info",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
79
|
+
# 1. Verify initial metadata exists
|
|
80
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
81
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Personal Music Track"
|
|
82
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.COMMENT) == "Personal comment with sensitive info"
|
|
83
|
+
|
|
84
|
+
# 2. User decides to clean up sensitive metadata
|
|
85
|
+
cleanup_metadata = {
|
|
86
|
+
UnifiedMetadataKey.COMMENT: None, # Remove personal comment
|
|
87
|
+
UnifiedMetadataKey.ALBUM: "Generic Album", # Replace personal album
|
|
88
|
+
}
|
|
89
|
+
update_metadata(test_file, cleanup_metadata)
|
|
90
|
+
|
|
91
|
+
# 3. Verify sensitive data was removed
|
|
92
|
+
cleaned_metadata = get_unified_metadata(test_file)
|
|
93
|
+
assert cleaned_metadata.get(UnifiedMetadataKey.COMMENT) is None
|
|
94
|
+
assert cleaned_metadata.get(UnifiedMetadataKey.ALBUM) == "Generic Album"
|
|
95
|
+
assert cleaned_metadata.get(UnifiedMetadataKey.TITLE) == "Personal Music Track" # Title remains
|
|
96
|
+
|
|
97
|
+
# 4. User decides to remove all metadata for complete privacy
|
|
98
|
+
delete_result = delete_all_metadata(test_file)
|
|
99
|
+
assert delete_result is True
|
|
100
|
+
|
|
101
|
+
# 5. Verify all metadata is removed
|
|
102
|
+
final_metadata = get_unified_metadata(test_file)
|
|
103
|
+
assert (
|
|
104
|
+
final_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
105
|
+
or final_metadata.get(UnifiedMetadataKey.TITLE) != "Personal Music Track"
|
|
106
|
+
)
|
|
107
|
+
assert final_metadata.get(UnifiedMetadataKey.COMMENT) is None
|
|
108
|
+
|
|
109
|
+
def test_metadata_correction_workflow(self):
|
|
110
|
+
# Simulate a user correcting incorrect metadata
|
|
111
|
+
initial_metadata = {"title": "Wrong Title", "artist": "Wrong Artist", "album": "Wrong Album"}
|
|
112
|
+
|
|
113
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
114
|
+
# 1. Verify initial incorrect metadata
|
|
115
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
116
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Wrong Title"
|
|
117
|
+
|
|
118
|
+
# 2. User realizes the metadata is wrong and wants to start fresh
|
|
119
|
+
# Delete all metadata first
|
|
120
|
+
delete_result = delete_all_metadata(test_file)
|
|
121
|
+
assert delete_result is True
|
|
122
|
+
|
|
123
|
+
# 3. Verify metadata was deleted
|
|
124
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
125
|
+
assert (
|
|
126
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
127
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "Wrong Title"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# 4. Add correct metadata
|
|
131
|
+
correct_metadata = {
|
|
132
|
+
UnifiedMetadataKey.TITLE: "Correct Title",
|
|
133
|
+
UnifiedMetadataKey.ARTISTS: ["Correct Artist"],
|
|
134
|
+
UnifiedMetadataKey.ALBUM: "Correct Album",
|
|
135
|
+
}
|
|
136
|
+
update_metadata(test_file, correct_metadata)
|
|
137
|
+
|
|
138
|
+
# 5. Verify correct metadata was added
|
|
139
|
+
final_metadata = get_unified_metadata(test_file)
|
|
140
|
+
assert final_metadata.get(UnifiedMetadataKey.TITLE) == "Correct Title"
|
|
141
|
+
assert final_metadata.get(UnifiedMetadataKey.ARTISTS) == ["Correct Artist"]
|
|
142
|
+
assert final_metadata.get(UnifiedMetadataKey.ALBUM) == "Correct Album"
|
|
143
|
+
|
|
144
|
+
def test_cross_format_compatibility(self, sample_mp3_file, sample_flac_file, sample_wav_file):
|
|
145
|
+
# Test that metadata works consistently across MP3, FLAC, etc.
|
|
146
|
+
|
|
147
|
+
test_metadata = {
|
|
148
|
+
UnifiedMetadataKey.TITLE: "Cross Format Test",
|
|
149
|
+
UnifiedMetadataKey.ARTISTS: ["Test Artist"],
|
|
150
|
+
UnifiedMetadataKey.ALBUM: "Test Album",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
sample_files = [(sample_mp3_file, "mp3"), (sample_flac_file, "flac"), (sample_wav_file, "wav")]
|
|
154
|
+
|
|
155
|
+
for _file_path, format_type in sample_files:
|
|
156
|
+
# Set basic metadata using external script
|
|
157
|
+
basic_metadata = {"title": "Original Title", "artist": "Original Artist"}
|
|
158
|
+
with temp_file_with_metadata(basic_metadata, format_type) as test_file:
|
|
159
|
+
# Set metadata using app's function (this is what we're testing)
|
|
160
|
+
update_metadata(test_file, test_metadata)
|
|
161
|
+
|
|
162
|
+
# Verify metadata was set correctly
|
|
163
|
+
metadata = get_unified_metadata(test_file)
|
|
164
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == test_metadata[UnifiedMetadataKey.TITLE]
|
|
165
|
+
assert metadata.get(UnifiedMetadataKey.ARTISTS) == test_metadata[UnifiedMetadataKey.ARTISTS]
|
|
166
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM) == test_metadata[UnifiedMetadataKey.ALBUM]
|
|
File without changes
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""End-to-end tests for core metadata editing workflows.
|
|
2
|
+
|
|
3
|
+
These tests verify the basic functionality of the entire system for real users, including file I/O and complete metadata
|
|
4
|
+
editing workflows.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from audiometa import (
|
|
12
|
+
_AudioFile,
|
|
13
|
+
delete_all_metadata,
|
|
14
|
+
get_bitrate,
|
|
15
|
+
get_duration_in_sec,
|
|
16
|
+
get_unified_metadata,
|
|
17
|
+
update_metadata,
|
|
18
|
+
)
|
|
19
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
20
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.e2e
|
|
24
|
+
class TestCoreWorkflows:
|
|
25
|
+
def test_complete_metadata_editing_workflow(self):
|
|
26
|
+
# This is an e2e test - it tests the entire user journey
|
|
27
|
+
# 1. Load a file
|
|
28
|
+
# 2. Read existing metadata
|
|
29
|
+
# 3. Edit multiple fields
|
|
30
|
+
# 4. Save changes
|
|
31
|
+
# 5. Verify persistence
|
|
32
|
+
|
|
33
|
+
# Use external script to set initial metadata
|
|
34
|
+
initial_metadata = {"title": "Original Title", "artist": "Original Artist", "album": "Original Album"}
|
|
35
|
+
|
|
36
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
37
|
+
# Edit metadata using app's function (this is what we're testing)
|
|
38
|
+
test_metadata = {
|
|
39
|
+
UnifiedMetadataKey.TITLE: "New Title",
|
|
40
|
+
UnifiedMetadataKey.ARTISTS: ["New Artist"],
|
|
41
|
+
UnifiedMetadataKey.ALBUM: "New Album",
|
|
42
|
+
UnifiedMetadataKey.GENRES_NAMES: ["Rock"],
|
|
43
|
+
UnifiedMetadataKey.COMMENT: "Test comment",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Save changes
|
|
47
|
+
update_metadata(test_file, test_metadata)
|
|
48
|
+
|
|
49
|
+
# Verify persistence by reloading
|
|
50
|
+
metadata = get_unified_metadata(test_file)
|
|
51
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == "New Title"
|
|
52
|
+
assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["New Artist"]
|
|
53
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM) == "New Album"
|
|
54
|
+
assert metadata.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
|
|
55
|
+
assert metadata.get(UnifiedMetadataKey.COMMENT) == "Test comment"
|
|
56
|
+
|
|
57
|
+
def test_batch_metadata_processing(self, sample_mp3_file, sample_flac_file, sample_wav_file):
|
|
58
|
+
# E2E test for batch operations
|
|
59
|
+
results = []
|
|
60
|
+
|
|
61
|
+
sample_files = [(sample_mp3_file, "mp3"), (sample_flac_file, "flac"), (sample_wav_file, "wav")]
|
|
62
|
+
|
|
63
|
+
for file_path, format_type in sample_files:
|
|
64
|
+
try:
|
|
65
|
+
# Set initial metadata using external script
|
|
66
|
+
initial_metadata = {"title": "Batch Test Title", "artist": "Batch Test Artist"}
|
|
67
|
+
with temp_file_with_metadata(initial_metadata, format_type) as test_file:
|
|
68
|
+
# Update metadata using functional API (this is what we're testing)
|
|
69
|
+
test_metadata = {
|
|
70
|
+
UnifiedMetadataKey.ALBUM: "Batch Album",
|
|
71
|
+
UnifiedMetadataKey.COMMENT: "Batch processing test",
|
|
72
|
+
}
|
|
73
|
+
update_metadata(test_file, test_metadata)
|
|
74
|
+
results.append(("success", file_path))
|
|
75
|
+
except Exception as e:
|
|
76
|
+
results.append(("error", file_path, str(e)))
|
|
77
|
+
|
|
78
|
+
# Verify all files were processed
|
|
79
|
+
assert len(results) == len(sample_files)
|
|
80
|
+
success_count = sum(1 for result in results if result[0] == "success")
|
|
81
|
+
assert success_count > 0
|
|
82
|
+
|
|
83
|
+
def test_audio_file_context_manager(self, sample_mp3_file: Path):
|
|
84
|
+
with _AudioFile(sample_mp3_file) as audio_file:
|
|
85
|
+
# Test that we can read metadata within context using file path
|
|
86
|
+
metadata = get_unified_metadata(audio_file.file_path)
|
|
87
|
+
assert isinstance(metadata, dict)
|
|
88
|
+
|
|
89
|
+
# Test that we can get technical info within context using file path
|
|
90
|
+
bitrate = get_bitrate(audio_file.file_path)
|
|
91
|
+
duration = get_duration_in_sec(audio_file.file_path)
|
|
92
|
+
assert isinstance(bitrate, int)
|
|
93
|
+
assert isinstance(duration, float)
|
|
94
|
+
|
|
95
|
+
def test_cross_format_deletion_workflow(self, sample_mp3_file, sample_flac_file, sample_wav_file):
|
|
96
|
+
# E2E test for deletion across multiple formats
|
|
97
|
+
test_metadata = {
|
|
98
|
+
UnifiedMetadataKey.TITLE: "Cross Format Deletion",
|
|
99
|
+
UnifiedMetadataKey.ARTISTS: ["Cross Format Artist"],
|
|
100
|
+
UnifiedMetadataKey.ALBUM: "Cross Format Album",
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
sample_files = [(sample_mp3_file, "mp3"), (sample_flac_file, "flac"), (sample_wav_file, "wav")]
|
|
104
|
+
|
|
105
|
+
for _file_path, format_type in sample_files:
|
|
106
|
+
# Set up metadata using external script
|
|
107
|
+
initial_metadata = {"title": "Original Title", "artist": "Original Artist"}
|
|
108
|
+
with temp_file_with_metadata(initial_metadata, format_type) as test_file:
|
|
109
|
+
# Add metadata using app's function
|
|
110
|
+
update_metadata(test_file, test_metadata)
|
|
111
|
+
|
|
112
|
+
# Verify metadata was added
|
|
113
|
+
added_metadata = get_unified_metadata(test_file)
|
|
114
|
+
assert added_metadata.get(UnifiedMetadataKey.TITLE) == "Cross Format Deletion"
|
|
115
|
+
|
|
116
|
+
# Delete all metadata
|
|
117
|
+
delete_result = delete_all_metadata(test_file)
|
|
118
|
+
assert delete_result is True
|
|
119
|
+
|
|
120
|
+
# Verify metadata was deleted
|
|
121
|
+
deleted_metadata = get_unified_metadata(test_file)
|
|
122
|
+
assert (
|
|
123
|
+
deleted_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
124
|
+
or deleted_metadata.get(UnifiedMetadataKey.TITLE) != "Cross Format Deletion"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def test_metadata_cleanup_workflow(self):
|
|
128
|
+
# E2E test for complete metadata cleanup workflow
|
|
129
|
+
initial_metadata = {
|
|
130
|
+
"title": "Cleanup Test Title",
|
|
131
|
+
"artist": "Cleanup Test Artist",
|
|
132
|
+
"album": "Cleanup Test Album",
|
|
133
|
+
"year": "2023",
|
|
134
|
+
"genre": "Electronic",
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
with temp_file_with_metadata(initial_metadata, "mp3") as test_file:
|
|
138
|
+
# 1. Verify initial metadata exists
|
|
139
|
+
initial_metadata_result = get_unified_metadata(test_file)
|
|
140
|
+
assert initial_metadata_result.get(UnifiedMetadataKey.TITLE) == "Cleanup Test Title"
|
|
141
|
+
|
|
142
|
+
# 2. Add more metadata
|
|
143
|
+
additional_metadata = {
|
|
144
|
+
UnifiedMetadataKey.RATING: 80,
|
|
145
|
+
UnifiedMetadataKey.BPM: 128,
|
|
146
|
+
UnifiedMetadataKey.COMMENT: "Cleanup test comment",
|
|
147
|
+
}
|
|
148
|
+
update_metadata(test_file, additional_metadata, normalized_rating_max_value=100)
|
|
149
|
+
|
|
150
|
+
# 3. Verify all metadata exists
|
|
151
|
+
full_metadata = get_unified_metadata(test_file, normalized_rating_max_value=100)
|
|
152
|
+
assert full_metadata.get(UnifiedMetadataKey.RATING) == 80
|
|
153
|
+
assert full_metadata.get(UnifiedMetadataKey.BPM) == 128
|
|
154
|
+
|
|
155
|
+
# 4. Complete cleanup - delete all metadata
|
|
156
|
+
delete_result = delete_all_metadata(test_file)
|
|
157
|
+
assert delete_result is True
|
|
158
|
+
|
|
159
|
+
# 5. Verify complete cleanup
|
|
160
|
+
cleaned_metadata = get_unified_metadata(test_file)
|
|
161
|
+
assert (
|
|
162
|
+
cleaned_metadata.get(UnifiedMetadataKey.TITLE) is None
|
|
163
|
+
or cleaned_metadata.get(UnifiedMetadataKey.TITLE) != "Cleanup Test Title"
|
|
164
|
+
)
|
|
165
|
+
assert cleaned_metadata.get(UnifiedMetadataKey.RATING) is None
|
|
166
|
+
assert cleaned_metadata.get(UnifiedMetadataKey.BPM) is None
|