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,149 @@
|
|
|
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 TestCLIReadMultipleFiles:
|
|
12
|
+
def test_cli_read_multiple_files(self):
|
|
13
|
+
with (
|
|
14
|
+
temp_file_with_metadata({"title": "File One"}, "mp3") as file1,
|
|
15
|
+
temp_file_with_metadata({"title": "File Two"}, "mp3") as file2,
|
|
16
|
+
):
|
|
17
|
+
result = subprocess.run(
|
|
18
|
+
[
|
|
19
|
+
sys.executable,
|
|
20
|
+
"-m",
|
|
21
|
+
"audiometa",
|
|
22
|
+
"read",
|
|
23
|
+
str(file1),
|
|
24
|
+
str(file2),
|
|
25
|
+
"--format",
|
|
26
|
+
"json",
|
|
27
|
+
],
|
|
28
|
+
capture_output=True,
|
|
29
|
+
text=True,
|
|
30
|
+
check=False,
|
|
31
|
+
)
|
|
32
|
+
assert result.returncode == 0
|
|
33
|
+
# When reading multiple files, each file outputs a separate JSON object
|
|
34
|
+
# JSON objects can span multiple lines, so we need to parse them properly
|
|
35
|
+
output = result.stdout.strip()
|
|
36
|
+
if output:
|
|
37
|
+
# Try to parse the first complete JSON object
|
|
38
|
+
# Find the first complete JSON object by counting braces
|
|
39
|
+
brace_count = 0
|
|
40
|
+
start_idx = 0
|
|
41
|
+
for i, char in enumerate(output):
|
|
42
|
+
if char == "{":
|
|
43
|
+
brace_count += 1
|
|
44
|
+
elif char == "}":
|
|
45
|
+
brace_count -= 1
|
|
46
|
+
if brace_count == 0:
|
|
47
|
+
# Found complete JSON object
|
|
48
|
+
first_json_str = output[start_idx : i + 1]
|
|
49
|
+
data = json.loads(first_json_str)
|
|
50
|
+
assert isinstance(data, dict)
|
|
51
|
+
assert "unified_metadata" in data or "metadata_format" in data
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
def test_cli_read_multiple_files_with_continue_on_error(self, tmp_path):
|
|
55
|
+
nonexistent_file = tmp_path / "nonexistent.mp3"
|
|
56
|
+
with temp_file_with_metadata({"title": "Valid File"}, "mp3") as valid_file:
|
|
57
|
+
result = subprocess.run(
|
|
58
|
+
[
|
|
59
|
+
sys.executable,
|
|
60
|
+
"-m",
|
|
61
|
+
"audiometa",
|
|
62
|
+
"read",
|
|
63
|
+
str(nonexistent_file),
|
|
64
|
+
str(valid_file),
|
|
65
|
+
"--continue-on-error",
|
|
66
|
+
"--format",
|
|
67
|
+
"json",
|
|
68
|
+
],
|
|
69
|
+
capture_output=True,
|
|
70
|
+
text=True,
|
|
71
|
+
check=False,
|
|
72
|
+
)
|
|
73
|
+
assert result.returncode == 0
|
|
74
|
+
# When reading multiple files, each file outputs a separate JSON object
|
|
75
|
+
# JSON objects can span multiple lines, so we need to parse them properly
|
|
76
|
+
output = result.stdout.strip()
|
|
77
|
+
if output:
|
|
78
|
+
# Try to parse the first complete JSON object
|
|
79
|
+
brace_count = 0
|
|
80
|
+
start_idx = 0
|
|
81
|
+
for i, char in enumerate(output):
|
|
82
|
+
if char == "{":
|
|
83
|
+
if brace_count == 0:
|
|
84
|
+
start_idx = i
|
|
85
|
+
brace_count += 1
|
|
86
|
+
elif char == "}":
|
|
87
|
+
brace_count -= 1
|
|
88
|
+
if brace_count == 0:
|
|
89
|
+
# Found complete JSON object
|
|
90
|
+
try:
|
|
91
|
+
first_json_str = output[start_idx : i + 1]
|
|
92
|
+
data = json.loads(first_json_str)
|
|
93
|
+
assert isinstance(data, dict)
|
|
94
|
+
break
|
|
95
|
+
except json.JSONDecodeError:
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
def test_cli_read_glob_pattern(self, tmp_path):
|
|
99
|
+
# Use a dedicated temp directory to avoid matching other temp files
|
|
100
|
+
test_dir = tmp_path / "glob_test"
|
|
101
|
+
test_dir.mkdir()
|
|
102
|
+
|
|
103
|
+
with temp_file_with_metadata({"title": "Pattern File 1"}, "mp3") as file1:
|
|
104
|
+
file1_name = file1.name
|
|
105
|
+
test_file1 = test_dir / file1_name
|
|
106
|
+
import shutil
|
|
107
|
+
|
|
108
|
+
shutil.copy2(file1, test_file1)
|
|
109
|
+
with temp_file_with_metadata({"title": "Pattern File 2"}, "mp3") as temp_file2:
|
|
110
|
+
file2_name = "pattern_file_2.mp3"
|
|
111
|
+
test_file2 = test_dir / file2_name
|
|
112
|
+
shutil.copy2(temp_file2, test_file2)
|
|
113
|
+
|
|
114
|
+
result = subprocess.run(
|
|
115
|
+
[
|
|
116
|
+
sys.executable,
|
|
117
|
+
"-m",
|
|
118
|
+
"audiometa",
|
|
119
|
+
"read",
|
|
120
|
+
str(test_dir / "*.mp3"),
|
|
121
|
+
"--format",
|
|
122
|
+
"json",
|
|
123
|
+
"--continue-on-error",
|
|
124
|
+
],
|
|
125
|
+
capture_output=True,
|
|
126
|
+
text=True,
|
|
127
|
+
check=False,
|
|
128
|
+
)
|
|
129
|
+
assert result.returncode == 0
|
|
130
|
+
# When reading multiple files, each file outputs a separate JSON object
|
|
131
|
+
# JSON objects can span multiple lines, so we need to parse them properly
|
|
132
|
+
output = result.stdout.strip()
|
|
133
|
+
if output:
|
|
134
|
+
# Try to parse the first complete JSON object
|
|
135
|
+
brace_count = 0
|
|
136
|
+
start_idx = 0
|
|
137
|
+
for i, char in enumerate(output):
|
|
138
|
+
if char == "{":
|
|
139
|
+
if brace_count == 0:
|
|
140
|
+
start_idx = i
|
|
141
|
+
brace_count += 1
|
|
142
|
+
elif char == "}":
|
|
143
|
+
brace_count -= 1
|
|
144
|
+
if brace_count == 0:
|
|
145
|
+
# Found complete JSON object
|
|
146
|
+
first_json_str = output[start_idx : i + 1]
|
|
147
|
+
data = json.loads(first_json_str)
|
|
148
|
+
assert isinstance(data, dict)
|
|
149
|
+
break
|
|
@@ -0,0 +1,88 @@
|
|
|
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 TestCLIReadOptions:
|
|
12
|
+
def test_cli_read_no_headers(self):
|
|
13
|
+
with temp_file_with_metadata({"title": "Test Title"}, "mp3") as test_file:
|
|
14
|
+
result = subprocess.run(
|
|
15
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--no-headers", "--format", "json"],
|
|
16
|
+
capture_output=True,
|
|
17
|
+
text=True,
|
|
18
|
+
check=False,
|
|
19
|
+
)
|
|
20
|
+
assert result.returncode == 0
|
|
21
|
+
data = json.loads(result.stdout)
|
|
22
|
+
assert "unified_metadata" in data
|
|
23
|
+
# When --no-headers is used, headers should be empty dict, not absent
|
|
24
|
+
assert "headers" in data
|
|
25
|
+
assert data["headers"] == {}
|
|
26
|
+
|
|
27
|
+
def test_cli_read_no_technical(self):
|
|
28
|
+
with temp_file_with_metadata({"title": "Test Title"}, "mp3") as test_file:
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
[sys.executable, "-m", "audiometa", "read", str(test_file), "--no-technical", "--format", "json"],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True,
|
|
33
|
+
check=False,
|
|
34
|
+
)
|
|
35
|
+
assert result.returncode == 0
|
|
36
|
+
data = json.loads(result.stdout)
|
|
37
|
+
assert "unified_metadata" in data
|
|
38
|
+
# When --no-technical is used, technical_info should be empty dict, not absent
|
|
39
|
+
assert "technical_info" in data
|
|
40
|
+
assert data["technical_info"] == {}
|
|
41
|
+
|
|
42
|
+
def test_cli_read_no_headers_no_technical(self):
|
|
43
|
+
with temp_file_with_metadata({"title": "Test Title"}, "mp3") as test_file:
|
|
44
|
+
result = subprocess.run(
|
|
45
|
+
[
|
|
46
|
+
sys.executable,
|
|
47
|
+
"-m",
|
|
48
|
+
"audiometa",
|
|
49
|
+
"read",
|
|
50
|
+
str(test_file),
|
|
51
|
+
"--no-headers",
|
|
52
|
+
"--no-technical",
|
|
53
|
+
"--format",
|
|
54
|
+
"json",
|
|
55
|
+
],
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
check=False,
|
|
59
|
+
)
|
|
60
|
+
assert result.returncode == 0
|
|
61
|
+
data = json.loads(result.stdout)
|
|
62
|
+
assert "unified_metadata" in data
|
|
63
|
+
# When --no-headers and --no-technical are used, these should be empty dicts, not absent
|
|
64
|
+
assert "headers" in data
|
|
65
|
+
assert data["headers"] == {}
|
|
66
|
+
assert "technical_info" in data
|
|
67
|
+
assert data["technical_info"] == {}
|
|
68
|
+
|
|
69
|
+
def test_cli_read_with_all_options(self):
|
|
70
|
+
with temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file:
|
|
71
|
+
result = subprocess.run(
|
|
72
|
+
[
|
|
73
|
+
sys.executable,
|
|
74
|
+
"-m",
|
|
75
|
+
"audiometa",
|
|
76
|
+
"read",
|
|
77
|
+
str(test_file),
|
|
78
|
+
"--no-headers",
|
|
79
|
+
"--no-technical",
|
|
80
|
+
"--format",
|
|
81
|
+
"table",
|
|
82
|
+
],
|
|
83
|
+
capture_output=True,
|
|
84
|
+
text=True,
|
|
85
|
+
check=False,
|
|
86
|
+
)
|
|
87
|
+
assert result.returncode == 0
|
|
88
|
+
assert "UNIFIED METADATA" in result.stdout
|
|
@@ -0,0 +1,84 @@
|
|
|
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 TestCLIReadUnified:
|
|
12
|
+
def test_cli_unified_output(self, sample_mp3_file):
|
|
13
|
+
result = subprocess.run(
|
|
14
|
+
[sys.executable, "-m", "audiometa", "unified", str(sample_mp3_file), "--format", "json"],
|
|
15
|
+
capture_output=True,
|
|
16
|
+
text=True,
|
|
17
|
+
check=False,
|
|
18
|
+
)
|
|
19
|
+
assert result.returncode == 0
|
|
20
|
+
data = json.loads(result.stdout)
|
|
21
|
+
assert isinstance(data, dict)
|
|
22
|
+
assert "unified_metadata" not in data
|
|
23
|
+
assert "headers" not in data
|
|
24
|
+
assert "technical_info" not in data
|
|
25
|
+
|
|
26
|
+
def test_cli_unified_with_metadata(self):
|
|
27
|
+
with temp_file_with_metadata({"title": "Unified Test", "artist": "Unified Artist"}, "mp3") as test_file:
|
|
28
|
+
result = subprocess.run(
|
|
29
|
+
[sys.executable, "-m", "audiometa", "unified", str(test_file), "--format", "json"],
|
|
30
|
+
capture_output=True,
|
|
31
|
+
text=True,
|
|
32
|
+
check=False,
|
|
33
|
+
)
|
|
34
|
+
assert result.returncode == 0
|
|
35
|
+
data = json.loads(result.stdout)
|
|
36
|
+
assert isinstance(data, dict)
|
|
37
|
+
assert data.get("title") == "Unified Test"
|
|
38
|
+
assert data.get("artists") == ["Unified Artist"]
|
|
39
|
+
|
|
40
|
+
def test_cli_unified_table_format(self):
|
|
41
|
+
with temp_file_with_metadata({"title": "Table Test", "artist": "Table Artist"}, "mp3") as test_file:
|
|
42
|
+
result = subprocess.run(
|
|
43
|
+
[sys.executable, "-m", "audiometa", "unified", str(test_file), "--format", "table"],
|
|
44
|
+
capture_output=True,
|
|
45
|
+
text=True,
|
|
46
|
+
check=False,
|
|
47
|
+
)
|
|
48
|
+
assert result.returncode == 0
|
|
49
|
+
assert "title" in result.stdout.lower() or "Table Test" in result.stdout
|
|
50
|
+
|
|
51
|
+
def test_cli_unified_output_to_file(self, tmp_path):
|
|
52
|
+
with temp_file_with_metadata({"title": "File Test"}, "mp3") as test_file:
|
|
53
|
+
output_file = tmp_path / "unified.json"
|
|
54
|
+
result = subprocess.run(
|
|
55
|
+
[
|
|
56
|
+
sys.executable,
|
|
57
|
+
"-m",
|
|
58
|
+
"audiometa",
|
|
59
|
+
"unified",
|
|
60
|
+
str(test_file),
|
|
61
|
+
"--output",
|
|
62
|
+
str(output_file),
|
|
63
|
+
],
|
|
64
|
+
capture_output=True,
|
|
65
|
+
text=True,
|
|
66
|
+
check=False,
|
|
67
|
+
)
|
|
68
|
+
assert result.returncode == 0
|
|
69
|
+
assert output_file.exists()
|
|
70
|
+
with output_file.open() as f:
|
|
71
|
+
data = json.load(f)
|
|
72
|
+
assert isinstance(data, dict)
|
|
73
|
+
|
|
74
|
+
def test_cli_with_spaces_in_filename_unified(self):
|
|
75
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
[sys.executable, "-m", "audiometa", "unified", str(test_file), "--format", "json"],
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
check=False,
|
|
81
|
+
)
|
|
82
|
+
assert result.returncode == 0
|
|
83
|
+
data = json.loads(result.stdout)
|
|
84
|
+
assert isinstance(data, dict)
|
|
@@ -0,0 +1,20 @@
|
|
|
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 TestCLIDelete:
|
|
11
|
+
def test_cli_delete_metadata(self):
|
|
12
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
13
|
+
result = subprocess.run(
|
|
14
|
+
[sys.executable, "-m", "audiometa", "delete", str(test_file)],
|
|
15
|
+
capture_output=True,
|
|
16
|
+
text=True,
|
|
17
|
+
check=False,
|
|
18
|
+
)
|
|
19
|
+
assert result.returncode == 0
|
|
20
|
+
assert "Deleted metadata" in result.stdout or "No metadata found" in result.stderr
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa.cli import format_as_table, format_output
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.e2e
|
|
9
|
+
class TestCLIFormatting:
|
|
10
|
+
def test_format_output_json(self):
|
|
11
|
+
data = {"title": "Test Song", "artist": "Test Artist"}
|
|
12
|
+
result = format_output(data, "json")
|
|
13
|
+
parsed = json.loads(result)
|
|
14
|
+
assert parsed == data
|
|
15
|
+
|
|
16
|
+
def test_format_output_yaml(self):
|
|
17
|
+
data = {"title": "Test Song", "artist": "Test Artist"}
|
|
18
|
+
result = format_output(data, "yaml")
|
|
19
|
+
# Should fall back to JSON if PyYAML not available
|
|
20
|
+
assert "Test Song" in result
|
|
21
|
+
|
|
22
|
+
def test_format_output_table(self):
|
|
23
|
+
data = {
|
|
24
|
+
"unified_metadata": {"title": "Test Song", "artist": "Test Artist"},
|
|
25
|
+
"technical_info": {"duration_seconds": 180, "bitrate_bps": 320000},
|
|
26
|
+
}
|
|
27
|
+
result = format_as_table(data)
|
|
28
|
+
assert "Test Song" in result
|
|
29
|
+
assert "Test Artist" in result
|
|
30
|
+
assert "180" in result
|
|
31
|
+
assert "320" in result
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.e2e
|
|
8
|
+
class TestCLIHelp:
|
|
9
|
+
def test_cli_help(self):
|
|
10
|
+
result = subprocess.run([sys.executable, "-m", "audiometa"], capture_output=True, text=True, check=False)
|
|
11
|
+
assert result.returncode == 1 # Should exit with error
|
|
12
|
+
assert "usage:" in result.stdout.lower() or "help" in result.stdout.lower()
|
|
13
|
+
|
|
14
|
+
def test_cli_read_help(self):
|
|
15
|
+
result = subprocess.run(
|
|
16
|
+
[sys.executable, "-m", "audiometa", "read", "--help"], capture_output=True, text=True, check=False
|
|
17
|
+
)
|
|
18
|
+
assert result.returncode == 0
|
|
19
|
+
assert "read" in result.stdout.lower()
|
|
20
|
+
|
|
21
|
+
def test_cli_unified_help(self):
|
|
22
|
+
result = subprocess.run(
|
|
23
|
+
[sys.executable, "-m", "audiometa", "unified", "--help"], capture_output=True, text=True, check=False
|
|
24
|
+
)
|
|
25
|
+
assert result.returncode == 0
|
|
26
|
+
assert "unified" in result.stdout.lower()
|
|
27
|
+
|
|
28
|
+
def test_cli_write_help(self):
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
[sys.executable, "-m", "audiometa", "write", "--help"], capture_output=True, text=True, check=False
|
|
31
|
+
)
|
|
32
|
+
assert result.returncode == 0
|
|
33
|
+
assert "write" in result.stdout.lower()
|
|
34
|
+
assert "force-format" in result.stdout.lower()
|
|
35
|
+
|
|
36
|
+
def test_cli_delete_help(self):
|
|
37
|
+
result = subprocess.run(
|
|
38
|
+
[sys.executable, "-m", "audiometa", "delete", "--help"], capture_output=True, text=True, check=False
|
|
39
|
+
)
|
|
40
|
+
assert result.returncode == 0
|
|
41
|
+
assert "delete" in result.stdout.lower()
|
|
File without changes
|
|
@@ -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 TestCLIWriteBasic:
|
|
11
|
+
def test_cli_write_no_metadata(self, sample_mp3_file):
|
|
12
|
+
result = subprocess.run(
|
|
13
|
+
[sys.executable, "-m", "audiometa", "write", str(sample_mp3_file)],
|
|
14
|
+
capture_output=True,
|
|
15
|
+
text=True,
|
|
16
|
+
check=False,
|
|
17
|
+
)
|
|
18
|
+
assert result.returncode == 1
|
|
19
|
+
assert "no metadata fields specified" in result.stderr.lower()
|
|
20
|
+
|
|
21
|
+
def test_cli_write_basic_metadata(self):
|
|
22
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
23
|
+
result = subprocess.run(
|
|
24
|
+
[sys.executable, "-m", "audiometa", "write", str(test_file), "--title", "CLI Test Title"],
|
|
25
|
+
capture_output=True,
|
|
26
|
+
text=True,
|
|
27
|
+
check=False,
|
|
28
|
+
)
|
|
29
|
+
assert result.returncode == 0
|
|
30
|
+
assert "Updated metadata" in result.stdout
|
|
31
|
+
|
|
32
|
+
def test_cli_with_spaces_in_filename_write(self):
|
|
33
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
34
|
+
result = subprocess.run(
|
|
35
|
+
[
|
|
36
|
+
sys.executable,
|
|
37
|
+
"-m",
|
|
38
|
+
"audiometa",
|
|
39
|
+
"write",
|
|
40
|
+
str(test_file),
|
|
41
|
+
"--title",
|
|
42
|
+
"Test Title",
|
|
43
|
+
"--artist",
|
|
44
|
+
"Test Artist",
|
|
45
|
+
],
|
|
46
|
+
capture_output=True,
|
|
47
|
+
text=True,
|
|
48
|
+
check=False,
|
|
49
|
+
)
|
|
50
|
+
assert result.returncode == 0
|
|
51
|
+
assert "Updated metadata" in result.stdout
|
|
@@ -0,0 +1,210 @@
|
|
|
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 TestCLIWriteComprehensive:
|
|
13
|
+
def test_cli_write_all_fields_comprehensive(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
|
+
"Comprehensive Test Title",
|
|
24
|
+
"--artist",
|
|
25
|
+
"Artist One",
|
|
26
|
+
"--artist",
|
|
27
|
+
"Artist Two",
|
|
28
|
+
"--album",
|
|
29
|
+
"Test Album",
|
|
30
|
+
"--album-artist",
|
|
31
|
+
"Album Artist",
|
|
32
|
+
"--year",
|
|
33
|
+
"2024",
|
|
34
|
+
"--genre",
|
|
35
|
+
"Rock",
|
|
36
|
+
"--genre",
|
|
37
|
+
"Blues",
|
|
38
|
+
"--track-number",
|
|
39
|
+
"5/12",
|
|
40
|
+
"--disc-number",
|
|
41
|
+
"1",
|
|
42
|
+
"--disc-total",
|
|
43
|
+
"2",
|
|
44
|
+
"--rating",
|
|
45
|
+
"85",
|
|
46
|
+
"--bpm",
|
|
47
|
+
"120",
|
|
48
|
+
"--language",
|
|
49
|
+
"eng",
|
|
50
|
+
"--composer",
|
|
51
|
+
"Composer One",
|
|
52
|
+
"--composer",
|
|
53
|
+
"Composer Two",
|
|
54
|
+
"--publisher",
|
|
55
|
+
"Test Publisher",
|
|
56
|
+
"--copyright",
|
|
57
|
+
"© 2024",
|
|
58
|
+
"--lyrics",
|
|
59
|
+
"Test lyrics",
|
|
60
|
+
"--comment",
|
|
61
|
+
"Test comment",
|
|
62
|
+
"--replaygain",
|
|
63
|
+
"+2.5 dB",
|
|
64
|
+
"--archival-location",
|
|
65
|
+
"Archive",
|
|
66
|
+
"--isrc",
|
|
67
|
+
"USRC17607839",
|
|
68
|
+
],
|
|
69
|
+
capture_output=True,
|
|
70
|
+
text=True,
|
|
71
|
+
check=False,
|
|
72
|
+
)
|
|
73
|
+
assert result.returncode == 0
|
|
74
|
+
|
|
75
|
+
metadata = get_unified_metadata(test_file)
|
|
76
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == "Comprehensive Test Title"
|
|
77
|
+
assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist One", "Artist Two"]
|
|
78
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM) == "Test Album"
|
|
79
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Album Artist"]
|
|
80
|
+
assert metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
|
|
81
|
+
assert metadata.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock", "Blues"]
|
|
82
|
+
assert metadata.get(UnifiedMetadataKey.TRACK_NUMBER) == "5/12"
|
|
83
|
+
assert metadata.get(UnifiedMetadataKey.DISC_NUMBER) == 1
|
|
84
|
+
assert metadata.get(UnifiedMetadataKey.DISC_TOTAL) == 2
|
|
85
|
+
assert metadata.get(UnifiedMetadataKey.RATING) == 85
|
|
86
|
+
assert metadata.get(UnifiedMetadataKey.BPM) == 120
|
|
87
|
+
assert metadata.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
88
|
+
assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["Composer One", "Composer Two"]
|
|
89
|
+
assert metadata.get(UnifiedMetadataKey.PUBLISHER) == "Test Publisher"
|
|
90
|
+
assert metadata.get(UnifiedMetadataKey.COPYRIGHT) == "© 2024"
|
|
91
|
+
assert metadata.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "Test lyrics"
|
|
92
|
+
assert metadata.get(UnifiedMetadataKey.COMMENT) == "Test comment"
|
|
93
|
+
assert metadata.get(UnifiedMetadataKey.REPLAYGAIN) == "+2.5 dB"
|
|
94
|
+
assert metadata.get(UnifiedMetadataKey.ISRC) == "USRC17607839"
|
|
95
|
+
# ARCHIVAL_LOCATION is not supported by ID3v2 format (MP3)
|
|
96
|
+
# It's only supported by Vorbis (FLAC)
|
|
97
|
+
|
|
98
|
+
def test_cli_write_flac_all_fields(self):
|
|
99
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
100
|
+
result = subprocess.run(
|
|
101
|
+
[
|
|
102
|
+
sys.executable,
|
|
103
|
+
"-m",
|
|
104
|
+
"audiometa",
|
|
105
|
+
"write",
|
|
106
|
+
str(test_file),
|
|
107
|
+
"--title",
|
|
108
|
+
"FLAC Test",
|
|
109
|
+
"--artist",
|
|
110
|
+
"FLAC Artist",
|
|
111
|
+
"--album",
|
|
112
|
+
"FLAC Album",
|
|
113
|
+
"--track-number",
|
|
114
|
+
"3/10",
|
|
115
|
+
"--disc-number",
|
|
116
|
+
"1",
|
|
117
|
+
"--disc-total",
|
|
118
|
+
"2",
|
|
119
|
+
"--bpm",
|
|
120
|
+
"140",
|
|
121
|
+
"--language",
|
|
122
|
+
"eng",
|
|
123
|
+
"--composer",
|
|
124
|
+
"FLAC Composer",
|
|
125
|
+
"--publisher",
|
|
126
|
+
"FLAC Publisher",
|
|
127
|
+
"--copyright",
|
|
128
|
+
"© FLAC",
|
|
129
|
+
"--lyrics",
|
|
130
|
+
"FLAC lyrics",
|
|
131
|
+
"--comment",
|
|
132
|
+
"FLAC comment",
|
|
133
|
+
"--isrc",
|
|
134
|
+
"FRXXX1800001",
|
|
135
|
+
],
|
|
136
|
+
capture_output=True,
|
|
137
|
+
text=True,
|
|
138
|
+
check=False,
|
|
139
|
+
)
|
|
140
|
+
assert result.returncode == 0
|
|
141
|
+
|
|
142
|
+
metadata = get_unified_metadata(test_file)
|
|
143
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == "FLAC Test"
|
|
144
|
+
assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["FLAC Artist"]
|
|
145
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM) == "FLAC Album"
|
|
146
|
+
assert metadata.get(UnifiedMetadataKey.TRACK_NUMBER) == "3/10"
|
|
147
|
+
assert metadata.get(UnifiedMetadataKey.DISC_NUMBER) == 1
|
|
148
|
+
assert metadata.get(UnifiedMetadataKey.DISC_TOTAL) == 2
|
|
149
|
+
assert metadata.get(UnifiedMetadataKey.BPM) == 140
|
|
150
|
+
assert metadata.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
151
|
+
assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["FLAC Composer"]
|
|
152
|
+
assert metadata.get(UnifiedMetadataKey.PUBLISHER) == "FLAC Publisher"
|
|
153
|
+
assert metadata.get(UnifiedMetadataKey.COPYRIGHT) == "© FLAC"
|
|
154
|
+
assert metadata.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "FLAC lyrics"
|
|
155
|
+
assert metadata.get(UnifiedMetadataKey.COMMENT) == "FLAC comment"
|
|
156
|
+
assert metadata.get(UnifiedMetadataKey.ISRC) == "FRXXX1800001"
|
|
157
|
+
|
|
158
|
+
def test_cli_write_wav_all_fields(self):
|
|
159
|
+
with temp_file_with_metadata({}, "wav") as test_file:
|
|
160
|
+
result = subprocess.run(
|
|
161
|
+
[
|
|
162
|
+
sys.executable,
|
|
163
|
+
"-m",
|
|
164
|
+
"audiometa",
|
|
165
|
+
"write",
|
|
166
|
+
str(test_file),
|
|
167
|
+
"--title",
|
|
168
|
+
"WAV Test",
|
|
169
|
+
"--artist",
|
|
170
|
+
"WAV Artist",
|
|
171
|
+
"--album",
|
|
172
|
+
"WAV Album",
|
|
173
|
+
"--year",
|
|
174
|
+
"2024",
|
|
175
|
+
"--genre",
|
|
176
|
+
"Rock",
|
|
177
|
+
"--rating",
|
|
178
|
+
"100",
|
|
179
|
+
"--bpm",
|
|
180
|
+
"120",
|
|
181
|
+
"--language",
|
|
182
|
+
"eng",
|
|
183
|
+
"--composer",
|
|
184
|
+
"WAV Composer",
|
|
185
|
+
"--copyright",
|
|
186
|
+
"© WAV",
|
|
187
|
+
"--comment",
|
|
188
|
+
"WAV comment",
|
|
189
|
+
"--isrc",
|
|
190
|
+
"GBUM71505078",
|
|
191
|
+
],
|
|
192
|
+
capture_output=True,
|
|
193
|
+
text=True,
|
|
194
|
+
check=False,
|
|
195
|
+
)
|
|
196
|
+
assert result.returncode == 0
|
|
197
|
+
|
|
198
|
+
metadata = get_unified_metadata(test_file)
|
|
199
|
+
assert metadata.get(UnifiedMetadataKey.TITLE) == "WAV Test"
|
|
200
|
+
assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["WAV Artist"]
|
|
201
|
+
assert metadata.get(UnifiedMetadataKey.ALBUM) == "WAV Album"
|
|
202
|
+
assert metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
|
|
203
|
+
assert metadata.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
|
|
204
|
+
assert metadata.get(UnifiedMetadataKey.RATING) == 100
|
|
205
|
+
assert metadata.get(UnifiedMetadataKey.BPM) == 120
|
|
206
|
+
assert metadata.get(UnifiedMetadataKey.LANGUAGE) == "eng"
|
|
207
|
+
assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["WAV Composer"]
|
|
208
|
+
assert metadata.get(UnifiedMetadataKey.COPYRIGHT) == "© WAV"
|
|
209
|
+
assert metadata.get(UnifiedMetadataKey.COMMENT) == "WAV comment"
|
|
210
|
+
assert metadata.get(UnifiedMetadataKey.ISRC) == "GBUM71505078"
|