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,90 @@
|
|
|
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 TestCLIRatingValidation:
|
|
11
|
+
def test_cli_invalid_rating_value_negative(self):
|
|
12
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
13
|
+
result = subprocess.run(
|
|
14
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--rating", "-5"],
|
|
15
|
+
capture_output=True,
|
|
16
|
+
text=True,
|
|
17
|
+
check=False,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Should fail due to negative rating - CLI validates explicitly
|
|
21
|
+
assert result.returncode != 0
|
|
22
|
+
stderr_output = result.stderr.lower()
|
|
23
|
+
assert "error" in stderr_output
|
|
24
|
+
assert "rating" in stderr_output
|
|
25
|
+
|
|
26
|
+
def test_cli_rating_value_allowed_without_normalization(self):
|
|
27
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
28
|
+
# Any integer rating value should be allowed when normalized_rating_max_value is not provided
|
|
29
|
+
# Using a valid ID3v2 profile value (196 = 4 stars in BASE_255_NON_PROPORTIONAL profile)
|
|
30
|
+
result = subprocess.run(
|
|
31
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--rating", "196"],
|
|
32
|
+
capture_output=True,
|
|
33
|
+
text=True,
|
|
34
|
+
check=False,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Should succeed - no write profile validation when normalized_rating_max_value is None
|
|
38
|
+
assert result.returncode == 0
|
|
39
|
+
|
|
40
|
+
def test_cli_rating_whole_number_float_allowed(self):
|
|
41
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
42
|
+
# Whole-number floats like 196.0 should be accepted and converted to integers
|
|
43
|
+
result = subprocess.run(
|
|
44
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--rating", "196.0"],
|
|
45
|
+
capture_output=True,
|
|
46
|
+
text=True,
|
|
47
|
+
check=False,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Should succeed - whole-number floats are converted to integers in raw mode
|
|
51
|
+
assert result.returncode == 0
|
|
52
|
+
|
|
53
|
+
def test_cli_rating_value_non_multiple_of_10_allowed(self):
|
|
54
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
55
|
+
result = subprocess.run(
|
|
56
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--rating", "37"],
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
check=False,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Should succeed - no write profile validation when normalized_rating_max_value is None
|
|
63
|
+
assert result.returncode == 0
|
|
64
|
+
|
|
65
|
+
def test_cli_invalid_rating_value_non_numeric(self):
|
|
66
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
67
|
+
result = subprocess.run(
|
|
68
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--rating", "not-a-number"],
|
|
69
|
+
capture_output=True,
|
|
70
|
+
text=True,
|
|
71
|
+
check=False,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Should fail due to non-numeric rating
|
|
75
|
+
assert result.returncode != 0
|
|
76
|
+
stderr_output = result.stderr.lower()
|
|
77
|
+
assert "invalid" in stderr_output.lower() or "error" in stderr_output
|
|
78
|
+
|
|
79
|
+
def test_cli_valid_rating_multiple_of_10(self):
|
|
80
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
81
|
+
result = subprocess.run(
|
|
82
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--rating", "128"],
|
|
83
|
+
capture_output=True,
|
|
84
|
+
text=True,
|
|
85
|
+
check=False,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Should succeed - any integer rating value is allowed
|
|
89
|
+
assert result.returncode == 0
|
|
90
|
+
assert "updated metadata" in result.stdout.lower()
|
|
@@ -0,0 +1,51 @@
|
|
|
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 TestCLIYearValidation:
|
|
11
|
+
def test_cli_invalid_year_value_non_numeric(self):
|
|
12
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
13
|
+
result = subprocess.run(
|
|
14
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--year", "not-a-year"],
|
|
15
|
+
capture_output=True,
|
|
16
|
+
text=True,
|
|
17
|
+
check=False,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Should fail due to non-numeric year
|
|
21
|
+
assert result.returncode != 0
|
|
22
|
+
stderr_output = result.stderr.lower()
|
|
23
|
+
assert "invalid" in stderr_output.lower() or "error" in stderr_output
|
|
24
|
+
|
|
25
|
+
def test_cli_invalid_year_value_negative(self):
|
|
26
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--year", "-2023"],
|
|
29
|
+
capture_output=True,
|
|
30
|
+
text=True,
|
|
31
|
+
check=False,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Should fail due to invalid date format (negative year doesn't match YYYY format)
|
|
35
|
+
assert result.returncode != 0
|
|
36
|
+
stderr_output = result.stderr.lower()
|
|
37
|
+
assert "error" in stderr_output or "invalid" in stderr_output
|
|
38
|
+
|
|
39
|
+
def test_cli_valid_year_value_future(self):
|
|
40
|
+
with temp_file_with_metadata({}, "mp3") as temp_file_path:
|
|
41
|
+
future_year = str(2030 + 1) # Future year is valid
|
|
42
|
+
result = subprocess.run(
|
|
43
|
+
[sys.executable, "-m", "audiometa", "write", str(temp_file_path), "--year", future_year],
|
|
44
|
+
capture_output=True,
|
|
45
|
+
text=True,
|
|
46
|
+
check=False,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Should succeed - future years are allowed
|
|
50
|
+
assert result.returncode == 0
|
|
51
|
+
assert "updated metadata" in result.stdout.lower()
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.e2e
|
|
11
|
+
class TestCLIReadBasic:
|
|
12
|
+
def test_cli_read_nonexistent_file(self, tmp_path):
|
|
13
|
+
nonexistent_file = tmp_path / "nonexistent.mp3"
|
|
14
|
+
result = subprocess.run(
|
|
15
|
+
[sys.executable, "-m", "audiometa", "read", str(nonexistent_file)],
|
|
16
|
+
capture_output=True,
|
|
17
|
+
text=True,
|
|
18
|
+
check=False,
|
|
19
|
+
)
|
|
20
|
+
assert result.returncode == 1
|
|
21
|
+
assert "error" in result.stderr.lower()
|
|
22
|
+
|
|
23
|
+
def test_cli_read_with_continue_on_error(self, tmp_path):
|
|
24
|
+
nonexistent_file = tmp_path / "nonexistent.mp3"
|
|
25
|
+
result = subprocess.run(
|
|
26
|
+
[sys.executable, "-m", "audiometa", "read", str(nonexistent_file), "--continue-on-error"],
|
|
27
|
+
capture_output=True,
|
|
28
|
+
text=True,
|
|
29
|
+
check=False,
|
|
30
|
+
)
|
|
31
|
+
assert result.returncode == 0
|
|
32
|
+
|
|
33
|
+
def test_cli_with_spaces_in_filename(self):
|
|
34
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
35
|
+
result = subprocess.run(
|
|
36
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
37
|
+
capture_output=True,
|
|
38
|
+
text=True,
|
|
39
|
+
check=False,
|
|
40
|
+
)
|
|
41
|
+
assert result.returncode == 0
|
|
42
|
+
data = json.loads(result.stdout)
|
|
43
|
+
assert isinstance(data, dict)
|
|
44
|
+
assert "unified_metadata" in data
|
|
45
|
+
|
|
46
|
+
def test_cli_read_basic_metadata(self):
|
|
47
|
+
with temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file:
|
|
48
|
+
result = subprocess.run(
|
|
49
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
50
|
+
capture_output=True,
|
|
51
|
+
text=True,
|
|
52
|
+
check=False,
|
|
53
|
+
)
|
|
54
|
+
assert result.returncode == 0
|
|
55
|
+
data = json.loads(result.stdout)
|
|
56
|
+
assert "unified_metadata" in data
|
|
57
|
+
assert data["unified_metadata"].get("title") == "Test Title"
|
|
58
|
+
assert data["unified_metadata"].get("artists") == ["Test Artist"]
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
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 TestCLIReadComprehensive:
|
|
13
|
+
def test_cli_read_all_fields_comprehensive_mp3(self):
|
|
14
|
+
with temp_file_with_metadata(
|
|
15
|
+
{
|
|
16
|
+
"title": "Comprehensive Test Title",
|
|
17
|
+
"artist": ["Artist One", "Artist Two"],
|
|
18
|
+
"album": "Test Album",
|
|
19
|
+
"album_artist": ["Album Artist"],
|
|
20
|
+
"year": "2024",
|
|
21
|
+
"genre": ["Rock", "Blues"],
|
|
22
|
+
"track": "5/12",
|
|
23
|
+
"disc_number": 1,
|
|
24
|
+
"disc_total": 2,
|
|
25
|
+
"rating": 85,
|
|
26
|
+
"bpm": 120,
|
|
27
|
+
"language": "eng",
|
|
28
|
+
"composer": ["Composer One", "Composer Two"],
|
|
29
|
+
"publisher": "Test Publisher",
|
|
30
|
+
"copyright": "© 2024",
|
|
31
|
+
"lyrics": "Test lyrics",
|
|
32
|
+
"comment": "Test comment",
|
|
33
|
+
"isrc": "USRC17607839",
|
|
34
|
+
},
|
|
35
|
+
"mp3",
|
|
36
|
+
) as test_file:
|
|
37
|
+
result = subprocess.run(
|
|
38
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
check=False,
|
|
42
|
+
)
|
|
43
|
+
assert result.returncode == 0
|
|
44
|
+
data = json.loads(result.stdout)
|
|
45
|
+
unified = data.get("unified_metadata", {})
|
|
46
|
+
|
|
47
|
+
assert unified.get(UnifiedMetadataKey.TITLE) == "Comprehensive Test Title"
|
|
48
|
+
assert unified.get(UnifiedMetadataKey.ARTISTS) == ["Artist One", "Artist Two"]
|
|
49
|
+
assert unified.get(UnifiedMetadataKey.ALBUM) == "Test Album"
|
|
50
|
+
assert unified.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Album Artist"]
|
|
51
|
+
assert unified.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
|
|
52
|
+
assert unified.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock", "Blues"]
|
|
53
|
+
assert unified.get(UnifiedMetadataKey.TRACK_NUMBER) == "5/12"
|
|
54
|
+
assert unified.get(UnifiedMetadataKey.DISC_NUMBER) == 1
|
|
55
|
+
assert unified.get(UnifiedMetadataKey.DISC_TOTAL) == 2
|
|
56
|
+
assert unified.get(UnifiedMetadataKey.RATING) == 85
|
|
57
|
+
assert unified.get(UnifiedMetadataKey.BPM) == 120
|
|
58
|
+
assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
59
|
+
assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["Composer One", "Composer Two"]
|
|
60
|
+
assert unified.get(UnifiedMetadataKey.PUBLISHER) == "Test Publisher"
|
|
61
|
+
assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© 2024"
|
|
62
|
+
assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "Test lyrics"
|
|
63
|
+
assert unified.get(UnifiedMetadataKey.COMMENT) == "Test comment"
|
|
64
|
+
assert unified.get(UnifiedMetadataKey.ISRC) == "USRC17607839"
|
|
65
|
+
# REPLAYGAIN and ARCHIVAL_LOCATION are not supported by ID3v2 format (MP3)
|
|
66
|
+
|
|
67
|
+
def test_cli_read_all_fields_comprehensive_flac(self):
|
|
68
|
+
with temp_file_with_metadata(
|
|
69
|
+
{
|
|
70
|
+
"title": "FLAC Comprehensive Test",
|
|
71
|
+
"artist": ["FLAC Artist"],
|
|
72
|
+
"album": "FLAC Album",
|
|
73
|
+
"track_number": "3/10",
|
|
74
|
+
"disc_number": 1,
|
|
75
|
+
"disc_total": 2,
|
|
76
|
+
"bpm": 140,
|
|
77
|
+
"language": "eng",
|
|
78
|
+
"composer": ["FLAC Composer"],
|
|
79
|
+
"publisher": "FLAC Publisher",
|
|
80
|
+
"copyright": "© FLAC",
|
|
81
|
+
"lyrics": "FLAC lyrics",
|
|
82
|
+
"comment": "FLAC comment",
|
|
83
|
+
"replaygain": "+2.5 dB",
|
|
84
|
+
"isrc": "FRXXX1800001",
|
|
85
|
+
},
|
|
86
|
+
"flac",
|
|
87
|
+
) as test_file:
|
|
88
|
+
result = subprocess.run(
|
|
89
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
90
|
+
capture_output=True,
|
|
91
|
+
text=True,
|
|
92
|
+
check=False,
|
|
93
|
+
)
|
|
94
|
+
assert result.returncode == 0
|
|
95
|
+
data = json.loads(result.stdout)
|
|
96
|
+
unified = data.get("unified_metadata", {})
|
|
97
|
+
|
|
98
|
+
assert unified.get(UnifiedMetadataKey.TITLE) == "FLAC Comprehensive Test"
|
|
99
|
+
assert unified.get(UnifiedMetadataKey.ARTISTS) == ["FLAC Artist"]
|
|
100
|
+
assert unified.get(UnifiedMetadataKey.ALBUM) == "FLAC Album"
|
|
101
|
+
assert unified.get(UnifiedMetadataKey.TRACK_NUMBER) == "3/10"
|
|
102
|
+
assert unified.get(UnifiedMetadataKey.DISC_NUMBER) == 1
|
|
103
|
+
assert unified.get(UnifiedMetadataKey.DISC_TOTAL) == 2
|
|
104
|
+
assert unified.get(UnifiedMetadataKey.BPM) == 140
|
|
105
|
+
assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
106
|
+
assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["FLAC Composer"]
|
|
107
|
+
assert unified.get(UnifiedMetadataKey.PUBLISHER) == "FLAC Publisher"
|
|
108
|
+
assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© FLAC"
|
|
109
|
+
assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "FLAC lyrics"
|
|
110
|
+
assert unified.get(UnifiedMetadataKey.COMMENT) == "FLAC comment"
|
|
111
|
+
assert unified.get(UnifiedMetadataKey.REPLAYGAIN) == "+2.5 dB"
|
|
112
|
+
assert unified.get(UnifiedMetadataKey.ISRC) == "FRXXX1800001"
|
|
113
|
+
# ARCHIVAL_LOCATION is not supported by Vorbis format (FLAC)
|
|
114
|
+
|
|
115
|
+
def test_cli_read_all_fields_comprehensive_wav(self):
|
|
116
|
+
with temp_file_with_metadata(
|
|
117
|
+
{
|
|
118
|
+
"title": "WAV Comprehensive Test",
|
|
119
|
+
"artist": ["WAV Artist"],
|
|
120
|
+
"album": "WAV Album",
|
|
121
|
+
"year": "2024",
|
|
122
|
+
"genre": ["Rock"],
|
|
123
|
+
"rating": 100,
|
|
124
|
+
"bpm": 120,
|
|
125
|
+
"language": "eng",
|
|
126
|
+
"composer": ["WAV Composer"],
|
|
127
|
+
"copyright": "© WAV",
|
|
128
|
+
"comment": "WAV comment",
|
|
129
|
+
"isrc": "GBUM71505078",
|
|
130
|
+
},
|
|
131
|
+
"wav",
|
|
132
|
+
) as test_file:
|
|
133
|
+
result = subprocess.run(
|
|
134
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
135
|
+
capture_output=True,
|
|
136
|
+
text=True,
|
|
137
|
+
check=False,
|
|
138
|
+
)
|
|
139
|
+
assert result.returncode == 0
|
|
140
|
+
data = json.loads(result.stdout)
|
|
141
|
+
unified = data.get("unified_metadata", {})
|
|
142
|
+
|
|
143
|
+
assert unified.get(UnifiedMetadataKey.TITLE) == "WAV Comprehensive Test"
|
|
144
|
+
assert unified.get(UnifiedMetadataKey.ARTISTS) == ["WAV Artist"]
|
|
145
|
+
assert unified.get(UnifiedMetadataKey.ALBUM) == "WAV Album"
|
|
146
|
+
assert unified.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
|
|
147
|
+
assert unified.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
|
|
148
|
+
assert unified.get(UnifiedMetadataKey.RATING) == 100
|
|
149
|
+
assert unified.get(UnifiedMetadataKey.BPM) == 120
|
|
150
|
+
assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
151
|
+
assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["WAV Composer"]
|
|
152
|
+
assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© WAV"
|
|
153
|
+
assert unified.get(UnifiedMetadataKey.COMMENT) == "WAV comment"
|
|
154
|
+
assert unified.get(UnifiedMetadataKey.ISRC) == "GBUM71505078"
|
|
155
|
+
|
|
156
|
+
def test_cli_read_comprehensive_roundtrip(self):
|
|
157
|
+
"""Test that we can write all fields via CLI and read them back correctly."""
|
|
158
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
159
|
+
write_result = subprocess.run(
|
|
160
|
+
[
|
|
161
|
+
sys.executable,
|
|
162
|
+
"-m",
|
|
163
|
+
"audiometa",
|
|
164
|
+
"write",
|
|
165
|
+
str(test_file),
|
|
166
|
+
"--title",
|
|
167
|
+
"Roundtrip Test",
|
|
168
|
+
"--artist",
|
|
169
|
+
"Roundtrip Artist One",
|
|
170
|
+
"--artist",
|
|
171
|
+
"Roundtrip Artist Two",
|
|
172
|
+
"--album",
|
|
173
|
+
"Roundtrip Album",
|
|
174
|
+
"--album-artist",
|
|
175
|
+
"Roundtrip Album Artist",
|
|
176
|
+
"--year",
|
|
177
|
+
"2024",
|
|
178
|
+
"--genre",
|
|
179
|
+
"Rock",
|
|
180
|
+
"--genre",
|
|
181
|
+
"Blues",
|
|
182
|
+
"--track-number",
|
|
183
|
+
"5/12",
|
|
184
|
+
"--disc-number",
|
|
185
|
+
"1",
|
|
186
|
+
"--disc-total",
|
|
187
|
+
"2",
|
|
188
|
+
"--rating",
|
|
189
|
+
"85",
|
|
190
|
+
"--bpm",
|
|
191
|
+
"120",
|
|
192
|
+
"--language",
|
|
193
|
+
"eng",
|
|
194
|
+
"--composer",
|
|
195
|
+
"Roundtrip Composer",
|
|
196
|
+
"--publisher",
|
|
197
|
+
"Roundtrip Publisher",
|
|
198
|
+
"--copyright",
|
|
199
|
+
"© Roundtrip",
|
|
200
|
+
"--lyrics",
|
|
201
|
+
"Roundtrip lyrics",
|
|
202
|
+
"--comment",
|
|
203
|
+
"Roundtrip comment",
|
|
204
|
+
"--isrc",
|
|
205
|
+
"USRC17607839",
|
|
206
|
+
],
|
|
207
|
+
capture_output=True,
|
|
208
|
+
text=True,
|
|
209
|
+
check=False,
|
|
210
|
+
)
|
|
211
|
+
assert write_result.returncode == 0
|
|
212
|
+
|
|
213
|
+
read_result = subprocess.run(
|
|
214
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
215
|
+
capture_output=True,
|
|
216
|
+
text=True,
|
|
217
|
+
check=False,
|
|
218
|
+
)
|
|
219
|
+
assert read_result.returncode == 0
|
|
220
|
+
data = json.loads(read_result.stdout)
|
|
221
|
+
unified = data.get("unified_metadata", {})
|
|
222
|
+
|
|
223
|
+
assert unified.get(UnifiedMetadataKey.TITLE) == "Roundtrip Test"
|
|
224
|
+
assert unified.get(UnifiedMetadataKey.ARTISTS) == ["Roundtrip Artist One", "Roundtrip Artist Two"]
|
|
225
|
+
assert unified.get(UnifiedMetadataKey.ALBUM) == "Roundtrip Album"
|
|
226
|
+
assert unified.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Roundtrip Album Artist"]
|
|
227
|
+
assert unified.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
|
|
228
|
+
assert unified.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock", "Blues"]
|
|
229
|
+
assert unified.get(UnifiedMetadataKey.TRACK_NUMBER) == "5/12"
|
|
230
|
+
assert unified.get(UnifiedMetadataKey.DISC_NUMBER) == 1
|
|
231
|
+
assert unified.get(UnifiedMetadataKey.DISC_TOTAL) == 2
|
|
232
|
+
assert unified.get(UnifiedMetadataKey.RATING) == 85
|
|
233
|
+
assert unified.get(UnifiedMetadataKey.BPM) == 120
|
|
234
|
+
assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
235
|
+
assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["Roundtrip Composer"]
|
|
236
|
+
assert unified.get(UnifiedMetadataKey.PUBLISHER) == "Roundtrip Publisher"
|
|
237
|
+
assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© Roundtrip"
|
|
238
|
+
assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "Roundtrip lyrics"
|
|
239
|
+
assert unified.get(UnifiedMetadataKey.COMMENT) == "Roundtrip comment"
|
|
240
|
+
assert unified.get(UnifiedMetadataKey.ISRC) == "USRC17607839"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.e2e
|
|
9
|
+
class TestCLIReadFormats:
|
|
10
|
+
def test_cli_read_output_formats_json(self, sample_mp3_file):
|
|
11
|
+
result = subprocess.run(
|
|
12
|
+
[sys.executable, "-m", "audiometa", "read", str(sample_mp3_file), "--format", "json"],
|
|
13
|
+
capture_output=True,
|
|
14
|
+
text=True,
|
|
15
|
+
check=False,
|
|
16
|
+
)
|
|
17
|
+
assert result.returncode == 0
|
|
18
|
+
data = json.loads(result.stdout)
|
|
19
|
+
assert "unified_metadata" in data
|
|
20
|
+
|
|
21
|
+
def test_cli_read_output_formats_table(self, sample_mp3_file):
|
|
22
|
+
result = subprocess.run(
|
|
23
|
+
[sys.executable, "-m", "audiometa", "read", str(sample_mp3_file), "--format", "table"],
|
|
24
|
+
capture_output=True,
|
|
25
|
+
text=True,
|
|
26
|
+
check=False,
|
|
27
|
+
)
|
|
28
|
+
assert result.returncode == 0
|
|
29
|
+
assert "UNIFIED METADATA" in result.stdout or "TECHNICAL INFO" in result.stdout
|
|
30
|
+
|
|
31
|
+
def test_cli_read_output_formats_yaml(self, sample_mp3_file):
|
|
32
|
+
result = subprocess.run(
|
|
33
|
+
[sys.executable, "-m", "audiometa", "read", str(sample_mp3_file), "--format", "yaml"],
|
|
34
|
+
capture_output=True,
|
|
35
|
+
text=True,
|
|
36
|
+
check=False,
|
|
37
|
+
)
|
|
38
|
+
assert result.returncode == 0
|
|
39
|
+
assert len(result.stdout.strip()) > 0
|
|
40
|
+
|
|
41
|
+
def test_cli_output_to_file(self, sample_mp3_file, tmp_path):
|
|
42
|
+
output_file = tmp_path / "metadata.json"
|
|
43
|
+
result = subprocess.run(
|
|
44
|
+
[sys.executable, "-m", "audiometa", "read", str(sample_mp3_file), "--output", str(output_file)],
|
|
45
|
+
capture_output=True,
|
|
46
|
+
text=True,
|
|
47
|
+
check=False,
|
|
48
|
+
)
|
|
49
|
+
assert result.returncode == 0
|
|
50
|
+
assert output_file.exists()
|
|
51
|
+
|
|
52
|
+
with output_file.open() as f:
|
|
53
|
+
data = json.load(f)
|
|
54
|
+
assert isinstance(data, dict)
|
|
55
|
+
assert "unified_metadata" in data
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.e2e
|
|
11
|
+
class TestCLIReadMetadataContent:
|
|
12
|
+
def test_cli_read_all_metadata_fields_mp3(self):
|
|
13
|
+
with temp_file_with_metadata(
|
|
14
|
+
{
|
|
15
|
+
"title": "Test Title",
|
|
16
|
+
"artist": "Test Artist",
|
|
17
|
+
"album": "Test Album",
|
|
18
|
+
"album_artist": "Test Album Artist",
|
|
19
|
+
"genre": "Rock",
|
|
20
|
+
"year": "2024",
|
|
21
|
+
"track": "5/12",
|
|
22
|
+
"disc_number": 1,
|
|
23
|
+
"disc_total": 2,
|
|
24
|
+
"rating": 85,
|
|
25
|
+
"bpm": 120,
|
|
26
|
+
"language": "eng",
|
|
27
|
+
"composer": "Test Composer",
|
|
28
|
+
"publisher": "Test Publisher",
|
|
29
|
+
"copyright": "© 2024",
|
|
30
|
+
"comment": "Test comment",
|
|
31
|
+
},
|
|
32
|
+
"mp3",
|
|
33
|
+
) as test_file:
|
|
34
|
+
result = subprocess.run(
|
|
35
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
36
|
+
capture_output=True,
|
|
37
|
+
text=True,
|
|
38
|
+
check=False,
|
|
39
|
+
)
|
|
40
|
+
assert result.returncode == 0
|
|
41
|
+
data = json.loads(result.stdout)
|
|
42
|
+
unified = data.get("unified_metadata", {})
|
|
43
|
+
assert unified.get("title") == "Test Title"
|
|
44
|
+
assert unified.get("artists") == ["Test Artist"]
|
|
45
|
+
assert unified.get("album") == "Test Album"
|
|
46
|
+
assert unified.get("album_artists") == ["Test Album Artist"]
|
|
47
|
+
assert unified.get("genres_names") == ["Rock"]
|
|
48
|
+
assert unified.get("release_date") == "2024"
|
|
49
|
+
assert unified.get("track_number") == "5/12"
|
|
50
|
+
assert unified.get("disc_number") == 1
|
|
51
|
+
assert unified.get("disc_total") == 2
|
|
52
|
+
assert unified.get("rating") == 85
|
|
53
|
+
assert unified.get("bpm") == 120
|
|
54
|
+
assert unified.get("language") == "eng"
|
|
55
|
+
assert unified.get("composer") == ["Test Composer"]
|
|
56
|
+
assert unified.get("publisher") == "Test Publisher"
|
|
57
|
+
assert unified.get("copyright") == "© 2024"
|
|
58
|
+
assert unified.get("comment") == "Test comment"
|
|
59
|
+
|
|
60
|
+
def test_cli_read_all_metadata_fields_flac(self):
|
|
61
|
+
with temp_file_with_metadata(
|
|
62
|
+
{
|
|
63
|
+
"title": "FLAC Title",
|
|
64
|
+
"artist": "FLAC Artist",
|
|
65
|
+
"album": "FLAC Album",
|
|
66
|
+
"track_number": "3/10",
|
|
67
|
+
"disc_number": 1,
|
|
68
|
+
"disc_total": 2,
|
|
69
|
+
"bpm": 140,
|
|
70
|
+
"language": "eng",
|
|
71
|
+
"composer": "FLAC Composer",
|
|
72
|
+
"publisher": "FLAC Publisher",
|
|
73
|
+
"copyright": "© FLAC",
|
|
74
|
+
"lyrics": "FLAC lyrics",
|
|
75
|
+
"comment": "FLAC comment",
|
|
76
|
+
},
|
|
77
|
+
"flac",
|
|
78
|
+
) as test_file:
|
|
79
|
+
result = subprocess.run(
|
|
80
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
81
|
+
capture_output=True,
|
|
82
|
+
text=True,
|
|
83
|
+
check=False,
|
|
84
|
+
)
|
|
85
|
+
assert result.returncode == 0
|
|
86
|
+
data = json.loads(result.stdout)
|
|
87
|
+
unified = data.get("unified_metadata", {})
|
|
88
|
+
assert unified.get("title") == "FLAC Title"
|
|
89
|
+
assert unified.get("artists") == ["FLAC Artist"]
|
|
90
|
+
assert unified.get("album") == "FLAC Album"
|
|
91
|
+
assert unified.get("track_number") == "3/10"
|
|
92
|
+
assert unified.get("disc_number") == 1
|
|
93
|
+
assert unified.get("disc_total") == 2
|
|
94
|
+
assert unified.get("bpm") == 140
|
|
95
|
+
assert unified.get("language") == "eng"
|
|
96
|
+
assert unified.get("composer") == ["FLAC Composer"]
|
|
97
|
+
assert unified.get("publisher") == "FLAC Publisher"
|
|
98
|
+
assert unified.get("copyright") == "© FLAC"
|
|
99
|
+
assert unified.get("unsynchronized_lyrics") == "FLAC lyrics"
|
|
100
|
+
assert unified.get("comment") == "FLAC comment"
|
|
101
|
+
|
|
102
|
+
def test_cli_read_all_metadata_fields_wav(self):
|
|
103
|
+
with temp_file_with_metadata(
|
|
104
|
+
{
|
|
105
|
+
"title": "WAV Title",
|
|
106
|
+
"artist": "WAV Artist",
|
|
107
|
+
"album": "WAV Album",
|
|
108
|
+
"year": "2024",
|
|
109
|
+
"genre": "Rock",
|
|
110
|
+
"rating": 100,
|
|
111
|
+
"bpm": 120,
|
|
112
|
+
"language": "eng",
|
|
113
|
+
"composer": "WAV Composer",
|
|
114
|
+
"copyright": "© WAV",
|
|
115
|
+
"comment": "WAV comment",
|
|
116
|
+
},
|
|
117
|
+
"wav",
|
|
118
|
+
) as test_file:
|
|
119
|
+
result = subprocess.run(
|
|
120
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
121
|
+
capture_output=True,
|
|
122
|
+
text=True,
|
|
123
|
+
check=False,
|
|
124
|
+
)
|
|
125
|
+
assert result.returncode == 0
|
|
126
|
+
data = json.loads(result.stdout)
|
|
127
|
+
unified = data.get("unified_metadata", {})
|
|
128
|
+
assert unified.get("title") == "WAV Title"
|
|
129
|
+
assert unified.get("artists") == ["WAV Artist"]
|
|
130
|
+
assert unified.get("album") == "WAV Album"
|
|
131
|
+
assert unified.get("release_date") == "2024"
|
|
132
|
+
assert unified.get("genres_names") == ["Rock"]
|
|
133
|
+
assert unified.get("rating") == 100
|
|
134
|
+
assert unified.get("bpm") == 120
|
|
135
|
+
assert unified.get("language") == "eng"
|
|
136
|
+
assert unified.get("composer") == ["WAV Composer"]
|
|
137
|
+
assert unified.get("copyright") == "© WAV"
|
|
138
|
+
assert unified.get("comment") == "WAV comment"
|
|
139
|
+
|
|
140
|
+
def test_cli_read_multiple_artists(self):
|
|
141
|
+
with temp_file_with_metadata({"artist": ["Artist One", "Artist Two", "Artist Three"]}, "mp3") as test_file:
|
|
142
|
+
result = subprocess.run(
|
|
143
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
144
|
+
capture_output=True,
|
|
145
|
+
text=True,
|
|
146
|
+
check=False,
|
|
147
|
+
)
|
|
148
|
+
assert result.returncode == 0
|
|
149
|
+
data = json.loads(result.stdout)
|
|
150
|
+
unified = data.get("unified_metadata", {})
|
|
151
|
+
assert unified.get("artists") == ["Artist One", "Artist Two", "Artist Three"]
|
|
152
|
+
|
|
153
|
+
def test_cli_read_multiple_genres(self):
|
|
154
|
+
with temp_file_with_metadata({"genre": ["Rock", "Blues", "Jazz"]}, "mp3") as test_file:
|
|
155
|
+
result = subprocess.run(
|
|
156
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
|
|
157
|
+
capture_output=True,
|
|
158
|
+
text=True,
|
|
159
|
+
check=False,
|
|
160
|
+
)
|
|
161
|
+
assert result.returncode == 0
|
|
162
|
+
data = json.loads(result.stdout)
|
|
163
|
+
unified = data.get("unified_metadata", {})
|
|
164
|
+
assert unified.get("genres_names") == ["Rock", "Blues", "Jazz"]
|