audiometa-python 0.6.1__tar.gz → 0.8.0__tar.gz
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_python-0.6.1/audiometa_python.egg-info → audiometa_python-0.8.0}/PKG-INFO +1 -1
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/__init__.py +58 -25
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/_audio_file.py +48 -13
- {audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5 → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking}/test_audio_data_corruption.py +3 -2
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_file.py +33 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_invalid_md5.py +35 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_invalid_with_metadata_combinations.py +101 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_state_precedence.py +39 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_unset_with_metadata_combinations.py +101 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_fails_with_id3v1.py +61 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_with_id3v2_only.py +42 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_works_without_id3v1.py +60 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_with_metadata_combinations.py +94 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_unset_md5.py +32 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_delete_original.py +90 -0
- {audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5 → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair}/test_invalid_md5/test_flipped_md5.py +5 -10
- {audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5 → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair}/test_invalid_md5/test_partial_md5.py +3 -10
- {audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5 → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair}/test_invalid_md5/test_random_md5.py +3 -10
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_md5_repair_with_metadata.py +63 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_unset_md5.py +38 -0
- audiometa_python-0.8.0/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +105 -0
- audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
- audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
- audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
- audiometa_python-0.8.0/audiometa/utils/flac_md5_state.py +22 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0/audiometa_python.egg-info}/PKG-INFO +1 -1
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa_python.egg-info/SOURCES.txt +23 -8
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/pyproject.toml +1 -1
- audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_unset_md5.py +0 -56
- audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5/test_valid_md5.py +0 -21
- audiometa_python-0.6.1/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +0 -42
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/LICENSE +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/README.md +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/__main__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/cli.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/exceptions.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_MetadataManager.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/id3v2/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/riff/_RiffManager.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/riff/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/riff/_riff_constants.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/vorbis/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/id3v1/_Id3v1Manager.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/id3v1/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/id3v1/_constants.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/id3v1/id3v1_raw_metadata.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/manager/id3v1/id3v1_raw_metadata_key.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/assets/create_test_files.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/common/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/common/audio_file_creator.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/common/external_tool_runner.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v1/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v1/id3v1_header_verifier.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v2/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v2/id3v2_header_verifier.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/riff/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/riff/riff_header_verifier.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/riff/riff_manual_metadata_creator.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/riff/riff_metadata_deleter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/riff/riff_metadata_getter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/riff/riff_metadata_setter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/scripts/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/technical_info_inspector.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/temp_file_with_metadata.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/vorbis/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/vorbis/vorbis_header_verifier.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/conftest.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/test_basic.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/test_comprehensive.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/test_formats.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/test_metadata_content.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/test_multiple_files.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/test_options.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/read/test_unified.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/test_delete.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/test_formatting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/test_help.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_basic.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_comprehensive.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_force_format.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_integer_fields.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_list_fields.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_rating.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_string_fields.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/cli/write/test_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/workflows/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/workflows/test_core_workflows.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/e2e/workflows/test_rating_workflows.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_with_id3v2_tags.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/conftest.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/delete_all_metadata/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/encoding/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/encoding/test_encoding.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_consistency.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_options.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_performance.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/get_full_metadata/test_structure.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/artists/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/artists/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/comment/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/comment/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/composer/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/composer/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/genre/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/isrc/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/language/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/language/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/language/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/title/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/title/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/title/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/reading/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/reading/test_reading_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/real_audio_files/test_reading.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/real_audio_files/test_writing.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/technical_info/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5 → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/integration/writing → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/integration/writing/writing_strategies → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5 → audiometa_python-0.8.0/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair}/test_non_flac_error.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/technical_info/test_bitrate.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/technical_info/test_channels.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/technical_info/test_sample_rate.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/test_audio_file.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit → audiometa_python-0.8.0/audiometa/test/tests/integration/writing}/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/test_forced_format.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/test_partial_update.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/audio_file → audiometa_python-0.8.0/audiometa/test/tests/integration/writing/writing_strategies}/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/writing_strategies/test_sync_strategy.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/audio_file/technical_info → audiometa_python-0.8.0/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields}/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/cli → audiometa_python-0.8.0/audiometa/test/tests/unit}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers → audiometa_python-0.8.0/audiometa/test/tests/unit/audio_file}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/header_info → audiometa_python-0.8.0/audiometa/test/tests/unit/audio_file/technical_info}/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/test_context_manager.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/test_file_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/test_is_audio_file.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/test_operations.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/audio_file/test_path_handling.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/metadata_field → audiometa_python-0.8.0/audiometa/test/tests/unit/cli}/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/cli/test_expand_file_patterns.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values → audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers}/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/conftest.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading → audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/header_info}/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing → audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/metadata_field}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/metadata_field/rating → audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values}/__init__.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/metadata_field/rating → audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values}/reading/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +0 -0
- {audiometa_python-0.6.1/audiometa/test/tests/unit/metadata_managers/metadata_field/rating → audiometa_python-0.8.0/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values}/writing/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_date_format_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_disc_number_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_format_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_type_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_track_number_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/id3v1_genre_code_map.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/metadata_format.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/metadata_writing_strategy.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/mutagen_exception_handler.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/os_dependencies_checker/__init__.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/os_dependencies_checker/base.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/os_dependencies_checker/config.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/os_dependencies_checker/macos.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/os_dependencies_checker/ubuntu.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/os_dependencies_checker/windows.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/rating_profiles.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/tool_path_resolver.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/types.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa/utils/unified_metadata_key.py +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa_python.egg-info/dependency_links.txt +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa_python.egg-info/entry_points.txt +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa_python.egg-info/requires.txt +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/audiometa_python.egg-info/top_level.txt +0 -0
- {audiometa_python-0.6.1 → audiometa_python-0.8.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: audiometa-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: A comprehensive Python library for reading and writing audio metadata across multiple formats
|
|
5
5
|
Author: AudioMeta Python Contributors
|
|
6
6
|
Author-email: Andreas Garcia <garcia.andreas.1991@gmail.com>
|
|
@@ -30,6 +30,7 @@ from .manager._rating_supporting.id3v2._Id3v2Manager import _Id3v2Manager
|
|
|
30
30
|
from .manager._rating_supporting.riff._RiffManager import _RiffManager
|
|
31
31
|
from .manager._rating_supporting.vorbis._VorbisManager import _VorbisManager
|
|
32
32
|
from .manager.id3v1._Id3v1Manager import _Id3v1Manager
|
|
33
|
+
from .utils.flac_md5_state import FlacMd5State
|
|
33
34
|
from .utils.metadata_format import MetadataFormat
|
|
34
35
|
from .utils.metadata_writing_strategy import MetadataWritingStrategy
|
|
35
36
|
from .utils.types import UnifiedMetadata, UnifiedMetadataValue
|
|
@@ -552,6 +553,7 @@ def update_metadata(
|
|
|
552
553
|
metadata_strategy: MetadataWritingStrategy | None = None,
|
|
553
554
|
metadata_format: MetadataFormat | None = None,
|
|
554
555
|
fail_on_unsupported_field: bool = False,
|
|
556
|
+
warn_on_unsupported_field: bool = True,
|
|
555
557
|
) -> None:
|
|
556
558
|
"""Update metadata in an audio file.
|
|
557
559
|
|
|
@@ -574,6 +576,9 @@ def update_metadata(
|
|
|
574
576
|
If False (default), unsupported fields are filtered out with individual warnings for each field.
|
|
575
577
|
For SYNC strategy, this applies per-format: unsupported fields are skipped for each format that
|
|
576
578
|
doesn't support them, while still syncing all supported fields.
|
|
579
|
+
warn_on_unsupported_field: If True (default), issues warnings when unsupported fields are encountered.
|
|
580
|
+
If False, suppresses warnings about unsupported fields. Automatically set to False when
|
|
581
|
+
fail_on_unsupported_field is True.
|
|
577
582
|
|
|
578
583
|
Returns:
|
|
579
584
|
None
|
|
@@ -599,8 +604,9 @@ def update_metadata(
|
|
|
599
604
|
|
|
600
605
|
When metadata_strategy is used, unsupported metadata fields are handled based on the
|
|
601
606
|
fail_on_unsupported_field parameter: True raises MetadataFieldNotSupportedByMetadataFormatError, False (default)
|
|
602
|
-
filters unsupported fields and warns about each one individually
|
|
603
|
-
are skipped per-format, allowing supported fields to sync while warning
|
|
607
|
+
filters unsupported fields and warns about each one individually (unless warn_on_unsupported_field is False).
|
|
608
|
+
For SYNC strategy, unsupported fields are skipped per-format, allowing supported fields to sync while warning
|
|
609
|
+
about unsupported ones.
|
|
604
610
|
|
|
605
611
|
Data Filtering:
|
|
606
612
|
For list-type metadata fields (e.g., ARTISTS, GENRES), empty strings and None values
|
|
@@ -641,6 +647,9 @@ def update_metadata(
|
|
|
641
647
|
}
|
|
642
648
|
# Results in: ["Artist 1", "Artist 2"] - empty strings and None filtered out
|
|
643
649
|
update_metadata("song.mp3", metadata)
|
|
650
|
+
|
|
651
|
+
# Suppress warnings about unsupported fields
|
|
652
|
+
update_metadata("song.mp3", metadata, warn_on_unsupported_field=False)
|
|
644
653
|
"""
|
|
645
654
|
audio_file = _AudioFile(file)
|
|
646
655
|
|
|
@@ -654,6 +663,11 @@ def update_metadata(
|
|
|
654
663
|
)
|
|
655
664
|
raise MetadataWritingConflictParametersError(msg)
|
|
656
665
|
|
|
666
|
+
# Automatically disable warnings when failing on unsupported fields
|
|
667
|
+
# This provides a more intuitive API where fail takes precedence over warn
|
|
668
|
+
if fail_on_unsupported_field:
|
|
669
|
+
warn_on_unsupported_field = False
|
|
670
|
+
|
|
657
671
|
# Default to SYNC strategy if not specified
|
|
658
672
|
if metadata_strategy is None:
|
|
659
673
|
metadata_strategy = MetadataWritingStrategy.SYNC
|
|
@@ -676,6 +690,7 @@ def update_metadata(
|
|
|
676
690
|
id3v2_version,
|
|
677
691
|
metadata_format,
|
|
678
692
|
fail_on_unsupported_field,
|
|
693
|
+
warn_on_unsupported_field,
|
|
679
694
|
)
|
|
680
695
|
|
|
681
696
|
|
|
@@ -687,6 +702,7 @@ def _handle_metadata_strategy(
|
|
|
687
702
|
id3v2_version: tuple[int, int, int] | None,
|
|
688
703
|
target_format: MetadataFormat | None = None,
|
|
689
704
|
fail_on_unsupported_field: bool = False,
|
|
705
|
+
warn_on_unsupported_field: bool = True,
|
|
690
706
|
) -> None:
|
|
691
707
|
"""Handle metadata strategy-specific behavior for all strategies."""
|
|
692
708
|
|
|
@@ -742,11 +758,12 @@ def _handle_metadata_strategy(
|
|
|
742
758
|
msg = f"Fields not supported by {target_format_actual.value} format: {unsupported_fields}"
|
|
743
759
|
raise MetadataFieldNotSupportedByMetadataFormatError(msg)
|
|
744
760
|
# Warn about each unsupported field individually
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
761
|
+
if warn_on_unsupported_field:
|
|
762
|
+
for unsupported_field in unsupported_fields:
|
|
763
|
+
field_warn_msg = (
|
|
764
|
+
f"Field {unsupported_field} not supported by {target_format_actual.value} format, skipped"
|
|
765
|
+
)
|
|
766
|
+
warnings.warn(field_warn_msg, stacklevel=2)
|
|
750
767
|
# Create filtered metadata without unsupported fields
|
|
751
768
|
filtered_metadata = {k: v for k, v in unified_metadata.items() if k not in unsupported_fields}
|
|
752
769
|
unified_metadata = filtered_metadata
|
|
@@ -783,11 +800,12 @@ def _handle_metadata_strategy(
|
|
|
783
800
|
unsupported_fields.append(field)
|
|
784
801
|
if unsupported_fields:
|
|
785
802
|
# Warn about each unsupported field individually for target format
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
803
|
+
if warn_on_unsupported_field:
|
|
804
|
+
for unsupported_field in unsupported_fields:
|
|
805
|
+
field_warn_msg = (
|
|
806
|
+
f"Field {unsupported_field} not supported by {target_format_actual.value} format, skipped"
|
|
807
|
+
)
|
|
808
|
+
warnings.warn(field_warn_msg, stacklevel=2)
|
|
791
809
|
# Create filtered metadata without unsupported fields
|
|
792
810
|
filtered_metadata = {k: v for k, v in unified_metadata.items() if k not in unsupported_fields}
|
|
793
811
|
unified_metadata = filtered_metadata
|
|
@@ -798,8 +816,9 @@ def _handle_metadata_strategy(
|
|
|
798
816
|
target_manager.update_metadata(unified_metadata)
|
|
799
817
|
except MetadataFieldNotSupportedByMetadataFormatError as e:
|
|
800
818
|
# For SYNC strategy, log warning but continue with other formats
|
|
801
|
-
|
|
802
|
-
|
|
819
|
+
if warn_on_unsupported_field:
|
|
820
|
+
format_warn_msg = f"Format {target_format_actual} doesn't support some metadata fields: {e}"
|
|
821
|
+
warnings.warn(format_warn_msg, stacklevel=2)
|
|
803
822
|
except Exception as e:
|
|
804
823
|
# Re-raise user errors (like InvalidRatingValueError) immediately
|
|
805
824
|
from .exceptions import ConfigurationError, InvalidRatingValueError
|
|
@@ -827,9 +846,10 @@ def _handle_metadata_strategy(
|
|
|
827
846
|
)
|
|
828
847
|
|
|
829
848
|
# Warn about each unsupported field individually for non-target formats
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
849
|
+
if warn_on_unsupported_field:
|
|
850
|
+
for unsupported_field in unsupported_fields:
|
|
851
|
+
field_warn_msg = f"Field {unsupported_field} not supported by {fmt_name} format, skipped"
|
|
852
|
+
warnings.warn(field_warn_msg, stacklevel=2)
|
|
833
853
|
|
|
834
854
|
# Try to update with supported fields only
|
|
835
855
|
if format_metadata: # Only update if there are supported fields
|
|
@@ -867,7 +887,8 @@ def _handle_metadata_strategy(
|
|
|
867
887
|
unsupported_warn_msg = (
|
|
868
888
|
f"Fields not supported by {target_format_actual.value} format will be skipped: {unsupported_fields}"
|
|
869
889
|
)
|
|
870
|
-
|
|
890
|
+
if warn_on_unsupported_field:
|
|
891
|
+
warnings.warn(unsupported_warn_msg, stacklevel=2)
|
|
871
892
|
# Create filtered metadata without unsupported fields
|
|
872
893
|
filtered_metadata = {k: v for k, v in unified_metadata.items() if k not in unsupported_fields}
|
|
873
894
|
unified_metadata = filtered_metadata
|
|
@@ -1106,8 +1127,8 @@ def get_duration_in_sec(file: PublicFileType) -> float:
|
|
|
1106
1127
|
return audio_file.get_duration_in_sec()
|
|
1107
1128
|
|
|
1108
1129
|
|
|
1109
|
-
def is_flac_md5_valid(file: PublicFileType) ->
|
|
1110
|
-
"""Check
|
|
1130
|
+
def is_flac_md5_valid(file: PublicFileType) -> FlacMd5State:
|
|
1131
|
+
"""Check the MD5 checksum validation state of a FLAC file.
|
|
1111
1132
|
|
|
1112
1133
|
This function verifies the integrity of a FLAC file by checking its MD5 signature.
|
|
1113
1134
|
Only works with FLAC files.
|
|
@@ -1116,17 +1137,27 @@ def is_flac_md5_valid(file: PublicFileType) -> bool:
|
|
|
1116
1137
|
file: Audio file path (str or Path; must be FLAC)
|
|
1117
1138
|
|
|
1118
1139
|
Returns:
|
|
1119
|
-
|
|
1140
|
+
FlacMd5State indicating the validation state:
|
|
1141
|
+
- FlacMd5State.VALID: MD5 is set and matches the audio data
|
|
1142
|
+
- FlacMd5State.UNSET: MD5 is all zeros (not set)
|
|
1143
|
+
- FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1: MD5 is set but cannot be validated due to ID3v1 tags
|
|
1144
|
+
- FlacMd5State.INVALID: MD5 is set but doesn't match the audio data (corrupted)
|
|
1120
1145
|
|
|
1121
1146
|
Raises:
|
|
1122
1147
|
FileTypeNotSupportedError: If the file is not a FLAC file
|
|
1123
1148
|
FileNotFoundError: If the file does not exist
|
|
1124
1149
|
|
|
1125
1150
|
Examples:
|
|
1151
|
+
from audiometa import is_flac_md5_valid, FlacMd5State
|
|
1152
|
+
|
|
1126
1153
|
# Check FLAC file integrity
|
|
1127
|
-
|
|
1128
|
-
if
|
|
1154
|
+
state = is_flac_md5_valid("song.flac")
|
|
1155
|
+
if state == FlacMd5State.VALID:
|
|
1129
1156
|
print("FLAC file is intact")
|
|
1157
|
+
elif state == FlacMd5State.UNSET:
|
|
1158
|
+
print("MD5 checksum is not set")
|
|
1159
|
+
elif state == FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1:
|
|
1160
|
+
print("MD5 cannot be validated due to ID3v1 tags")
|
|
1130
1161
|
else:
|
|
1131
1162
|
print("FLAC file may be corrupted")
|
|
1132
1163
|
"""
|
|
@@ -1134,7 +1165,7 @@ def is_flac_md5_valid(file: PublicFileType) -> bool:
|
|
|
1134
1165
|
try:
|
|
1135
1166
|
return audio_file.is_flac_file_md5_valid()
|
|
1136
1167
|
except FileCorruptedError:
|
|
1137
|
-
return
|
|
1168
|
+
return FlacMd5State.INVALID
|
|
1138
1169
|
|
|
1139
1170
|
|
|
1140
1171
|
def fix_md5_checking(file: PublicFileType) -> str:
|
|
@@ -1237,7 +1268,9 @@ def get_full_metadata(
|
|
|
1237
1268
|
"file_extension": audio_file.file_extension,
|
|
1238
1269
|
"audio_format_name": audio_file.get_audio_format_name(),
|
|
1239
1270
|
"is_flac_md5_valid": (
|
|
1240
|
-
audio_file.is_flac_file_md5_valid()
|
|
1271
|
+
audio_file.is_flac_file_md5_valid() == FlacMd5State.VALID
|
|
1272
|
+
if audio_file.file_extension == ".flac"
|
|
1273
|
+
else None
|
|
1241
1274
|
),
|
|
1242
1275
|
}
|
|
1243
1276
|
except Exception:
|
|
@@ -5,6 +5,7 @@ import json
|
|
|
5
5
|
import subprocess
|
|
6
6
|
import tempfile
|
|
7
7
|
import types
|
|
8
|
+
import warnings
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import cast
|
|
10
11
|
|
|
@@ -23,6 +24,7 @@ from .exceptions import (
|
|
|
23
24
|
)
|
|
24
25
|
from .manager._rating_supporting.id3v2._id3v2_constants import ID3V2_HEADER_SIZE
|
|
25
26
|
from .manager._rating_supporting.riff._riff_constants import RIFF_HEADER_SIZE
|
|
27
|
+
from .utils.flac_md5_state import FlacMd5State
|
|
26
28
|
from .utils.metadata_format import MetadataFormat
|
|
27
29
|
from .utils.mutagen_exception_handler import handle_mutagen_exception
|
|
28
30
|
from .utils.tool_path_resolver import get_tool_path
|
|
@@ -280,14 +282,31 @@ class _AudioFile:
|
|
|
280
282
|
except Exception:
|
|
281
283
|
return False
|
|
282
284
|
|
|
283
|
-
def
|
|
285
|
+
def _has_id3v1_tags(self) -> bool:
|
|
286
|
+
"""Check if FLAC file has ID3v1 tags.
|
|
287
|
+
|
|
288
|
+
Only ID3v1 tags cause validation failures, so we only need to check for ID3v1.
|
|
289
|
+
ID3v2 tags do not interfere with flac -t validation.
|
|
290
|
+
"""
|
|
291
|
+
try:
|
|
292
|
+
with Path(self.file_path).open("rb") as f:
|
|
293
|
+
# Check for ID3v1 at the end (last 128 bytes)
|
|
294
|
+
f.seek(-128, 2)
|
|
295
|
+
id3v1_header = f.read(3)
|
|
296
|
+
return id3v1_header == b"TAG"
|
|
297
|
+
except Exception:
|
|
298
|
+
return False
|
|
299
|
+
|
|
300
|
+
def is_flac_file_md5_valid(self) -> FlacMd5State:
|
|
284
301
|
if self.file_extension != ".flac":
|
|
285
302
|
msg = "The file is not a FLAC file"
|
|
286
303
|
raise FileTypeNotSupportedError(msg)
|
|
287
304
|
|
|
305
|
+
# Check if MD5 is unset (all zeros)
|
|
288
306
|
if self._is_md5_unset():
|
|
289
|
-
return
|
|
307
|
+
return FlacMd5State.UNSET
|
|
290
308
|
|
|
309
|
+
# Run flac -t to validate MD5
|
|
291
310
|
result = subprocess.run([get_tool_path("flac"), "-t", self.file_path], capture_output=True, check=False)
|
|
292
311
|
|
|
293
312
|
# Combine stdout and stderr as flac may output to either
|
|
@@ -295,20 +314,26 @@ class _AudioFile:
|
|
|
295
314
|
stderr_output = result.stderr.decode()
|
|
296
315
|
combined_output = stdout_output + stderr_output
|
|
297
316
|
|
|
298
|
-
#
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
317
|
+
# Check for explicit success message
|
|
318
|
+
if result.returncode == 0 and "ok" in combined_output.lower():
|
|
319
|
+
return FlacMd5State.VALID
|
|
320
|
+
|
|
321
|
+
# Check for ID3v1-related errors when ID3v1 tags are present
|
|
322
|
+
has_id3v1 = self._has_id3v1_tags()
|
|
323
|
+
if has_id3v1 and "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC" in combined_output:
|
|
324
|
+
return FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1
|
|
302
325
|
|
|
303
|
-
# Check for explicit
|
|
326
|
+
# Check for explicit MD5 mismatch (corruption)
|
|
304
327
|
if "MD5 signature mismatch" in combined_output:
|
|
305
|
-
return
|
|
306
|
-
if "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC" in combined_output:
|
|
307
|
-
return False
|
|
328
|
+
return FlacMd5State.INVALID
|
|
308
329
|
|
|
309
|
-
#
|
|
310
|
-
if
|
|
311
|
-
return
|
|
330
|
+
# If flac -t failed and we have ID3v1 tags, it's likely due to ID3v1 interference
|
|
331
|
+
if has_id3v1 and result.returncode != 0:
|
|
332
|
+
return FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1
|
|
333
|
+
|
|
334
|
+
# If return code is non-zero and no specific error message, assume invalid
|
|
335
|
+
if result.returncode != 0:
|
|
336
|
+
return FlacMd5State.INVALID
|
|
312
337
|
|
|
313
338
|
# If return code was 0 but no "ok" found, something unexpected happened
|
|
314
339
|
msg = "The Flac file md5 check failed"
|
|
@@ -332,6 +357,16 @@ class _AudioFile:
|
|
|
332
357
|
msg = "The file is not a FLAC file"
|
|
333
358
|
raise FileTypeNotSupportedError(msg)
|
|
334
359
|
|
|
360
|
+
# Warn if ID3v1 tags will be removed during re-encoding
|
|
361
|
+
if self._has_id3v1_tags():
|
|
362
|
+
warnings.warn(
|
|
363
|
+
"ID3v1 tags detected in FLAC file. These tags will be removed during MD5 repair "
|
|
364
|
+
"as they are non-standard in FLAC format and interfere with integrity validation. "
|
|
365
|
+
"Consider backing up ID3v1 metadata before repair if you need to preserve it.",
|
|
366
|
+
UserWarning,
|
|
367
|
+
stacklevel=3,
|
|
368
|
+
)
|
|
369
|
+
|
|
335
370
|
# Create a temporary file to store the corrected FLAC content
|
|
336
371
|
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".flac")
|
|
337
372
|
temp_path = temp_file.name
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
-
from audiometa import is_flac_md5_valid
|
|
3
|
+
from audiometa import FlacMd5State, is_flac_md5_valid
|
|
4
4
|
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
5
5
|
from audiometa.test.tests.integration.technical_info.flac_md5.conftest import corrupt_audio_data, ensure_flac_has_md5
|
|
6
6
|
|
|
@@ -18,4 +18,5 @@ class TestAudioDataCorruption:
|
|
|
18
18
|
# If the decoded PCM is unchanged, the MD5 will still match (correct behavior).
|
|
19
19
|
# Manual MD5 verification works correctly - it will detect when MD5 doesn't match PCM.
|
|
20
20
|
# This test verifies the function handles this scenario gracefully.
|
|
21
|
-
assert isinstance(result,
|
|
21
|
+
assert isinstance(result, FlacMd5State)
|
|
22
|
+
assert result in [FlacMd5State.VALID, FlacMd5State.INVALID]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import FlacMd5State, is_flac_md5_valid
|
|
6
|
+
from audiometa.exceptions import FileTypeNotSupportedError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.integration
|
|
10
|
+
class TestValidFlacMd5:
|
|
11
|
+
def test_is_flac_md5_valid_works_with_path_string(self, sample_flac_file: Path):
|
|
12
|
+
state = is_flac_md5_valid(str(sample_flac_file))
|
|
13
|
+
assert isinstance(state, FlacMd5State)
|
|
14
|
+
assert state in [
|
|
15
|
+
FlacMd5State.VALID,
|
|
16
|
+
FlacMd5State.UNSET,
|
|
17
|
+
FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1,
|
|
18
|
+
FlacMd5State.INVALID,
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def test_is_flac_md5_valid_works_with_pathlib_path(self, sample_flac_file: Path):
|
|
22
|
+
state = is_flac_md5_valid(sample_flac_file)
|
|
23
|
+
assert isinstance(state, FlacMd5State)
|
|
24
|
+
assert state in [
|
|
25
|
+
FlacMd5State.VALID,
|
|
26
|
+
FlacMd5State.UNSET,
|
|
27
|
+
FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1,
|
|
28
|
+
FlacMd5State.INVALID,
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
def test_is_flac_md5_valid_non_flac(self, sample_mp3_file: Path):
|
|
32
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
33
|
+
is_flac_md5_valid(sample_mp3_file)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Test invalid MD5 validation."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import FlacMd5State, is_flac_md5_valid
|
|
6
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
7
|
+
from audiometa.test.tests.integration.technical_info.flac_md5.conftest import corrupt_md5, ensure_flac_has_md5
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.integration
|
|
11
|
+
class TestInvalidMd5:
|
|
12
|
+
def test_is_flac_md5_valid_detects_random_md5_corruption(self):
|
|
13
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
14
|
+
ensure_flac_has_md5(test_file)
|
|
15
|
+
corrupt_md5(test_file, "random")
|
|
16
|
+
|
|
17
|
+
assert (
|
|
18
|
+
is_flac_md5_valid(test_file) == FlacMd5State.INVALID
|
|
19
|
+
), "Random MD5 corruption should be detected as INVALID"
|
|
20
|
+
|
|
21
|
+
def test_is_flac_md5_valid_detects_partial_md5_corruption(self):
|
|
22
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
23
|
+
ensure_flac_has_md5(test_file)
|
|
24
|
+
corrupt_md5(test_file, "partial")
|
|
25
|
+
|
|
26
|
+
assert (
|
|
27
|
+
is_flac_md5_valid(test_file) == FlacMd5State.INVALID
|
|
28
|
+
), "Partially corrupted MD5 should be detected as INVALID"
|
|
29
|
+
|
|
30
|
+
def test_is_flac_md5_valid_detects_flipped_md5(self):
|
|
31
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
32
|
+
ensure_flac_has_md5(test_file)
|
|
33
|
+
corrupt_md5(test_file, "flip_all")
|
|
34
|
+
|
|
35
|
+
assert is_flac_md5_valid(test_file) == FlacMd5State.INVALID, "Flipped MD5 should be detected as INVALID"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Test INVALID MD5 state with metadata format combinations that support validation."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import FlacMd5State, is_flac_md5_valid
|
|
6
|
+
from audiometa.test.helpers.id3v1 import ID3v1MetadataSetter
|
|
7
|
+
from audiometa.test.helpers.id3v2 import ID3v2MetadataSetter
|
|
8
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
9
|
+
from audiometa.test.helpers.vorbis import VorbisMetadataSetter
|
|
10
|
+
from audiometa.test.tests.integration.technical_info.flac_md5.conftest import corrupt_md5, ensure_flac_has_md5
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.integration
|
|
14
|
+
class TestMd5InvalidWithMetadataCombinations:
|
|
15
|
+
"""Test INVALID MD5 state with metadata format combinations that support validation."""
|
|
16
|
+
|
|
17
|
+
def test_invalid_md5_no_metadata(self):
|
|
18
|
+
"""Test INVALID state with no metadata."""
|
|
19
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
20
|
+
ensure_flac_has_md5(test_file)
|
|
21
|
+
corrupt_md5(test_file, "random")
|
|
22
|
+
state = is_flac_md5_valid(test_file)
|
|
23
|
+
assert state == FlacMd5State.INVALID, "Clean FLAC with invalid MD5 should return INVALID"
|
|
24
|
+
|
|
25
|
+
def test_invalid_md5_vorbis_only(self):
|
|
26
|
+
"""Test INVALID state with Vorbis metadata only."""
|
|
27
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
28
|
+
ensure_flac_has_md5(test_file)
|
|
29
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
30
|
+
corrupt_md5(test_file, "random")
|
|
31
|
+
state = is_flac_md5_valid(test_file)
|
|
32
|
+
assert state == FlacMd5State.INVALID, "FLAC with Vorbis metadata and invalid MD5 should return INVALID"
|
|
33
|
+
|
|
34
|
+
def test_invalid_md5_id3v2_only(self):
|
|
35
|
+
"""Test INVALID state with ID3v2 metadata only."""
|
|
36
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
37
|
+
ensure_flac_has_md5(test_file)
|
|
38
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
39
|
+
corrupt_md5(test_file, "random")
|
|
40
|
+
state = is_flac_md5_valid(test_file)
|
|
41
|
+
assert state == FlacMd5State.INVALID, "FLAC with ID3v2 metadata and invalid MD5 should return INVALID"
|
|
42
|
+
|
|
43
|
+
def test_invalid_md5_vorbis_and_id3v2(self):
|
|
44
|
+
"""Test INVALID state with Vorbis and ID3v2 metadata."""
|
|
45
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
46
|
+
ensure_flac_has_md5(test_file)
|
|
47
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
48
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
49
|
+
corrupt_md5(test_file, "random")
|
|
50
|
+
state = is_flac_md5_valid(test_file)
|
|
51
|
+
assert (
|
|
52
|
+
state == FlacMd5State.INVALID
|
|
53
|
+
), "FLAC with Vorbis and ID3v2 metadata and invalid MD5 should return INVALID"
|
|
54
|
+
|
|
55
|
+
def test_invalid_md5_with_id3v1_returns_uncheckable(self):
|
|
56
|
+
"""Test that invalid MD5 with ID3v1 returns UNCHECKABLE_DUE_TO_ID3, not INVALID."""
|
|
57
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
58
|
+
ensure_flac_has_md5(test_file)
|
|
59
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
60
|
+
corrupt_md5(test_file, "random")
|
|
61
|
+
state = is_flac_md5_valid(test_file)
|
|
62
|
+
assert (
|
|
63
|
+
state == FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1
|
|
64
|
+
), "FLAC with ID3v1 should return UNCHECKABLE_DUE_TO_ID3 even if MD5 corrupted (ID3v1 takes precedence)"
|
|
65
|
+
|
|
66
|
+
def test_invalid_md5_vorbis_and_id3v1_returns_uncheckable(self):
|
|
67
|
+
"""Test that invalid MD5 with Vorbis and ID3v1 returns UNCHECKABLE_DUE_TO_ID3."""
|
|
68
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
69
|
+
ensure_flac_has_md5(test_file)
|
|
70
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
71
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
72
|
+
corrupt_md5(test_file, "random")
|
|
73
|
+
state = is_flac_md5_valid(test_file)
|
|
74
|
+
assert (
|
|
75
|
+
state == FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1
|
|
76
|
+
), "FLAC with Vorbis and ID3v1 metadata should return UNCHECKABLE_DUE_TO_ID3 (ID3v1 takes precedence)"
|
|
77
|
+
|
|
78
|
+
def test_invalid_md5_id3v1_and_id3v2_returns_uncheckable(self):
|
|
79
|
+
"""Test that invalid MD5 with ID3v1 and ID3v2 returns UNCHECKABLE_DUE_TO_ID3."""
|
|
80
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
81
|
+
ensure_flac_has_md5(test_file)
|
|
82
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
83
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
84
|
+
corrupt_md5(test_file, "random")
|
|
85
|
+
state = is_flac_md5_valid(test_file)
|
|
86
|
+
assert (
|
|
87
|
+
state == FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1
|
|
88
|
+
), "FLAC with ID3v1 and ID3v2 metadata should return UNCHECKABLE_DUE_TO_ID3 (ID3v1 takes precedence)"
|
|
89
|
+
|
|
90
|
+
def test_invalid_md5_all_formats_returns_uncheckable(self):
|
|
91
|
+
"""Test that invalid MD5 with all metadata formats returns UNCHECKABLE_DUE_TO_ID3."""
|
|
92
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
93
|
+
ensure_flac_has_md5(test_file)
|
|
94
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
95
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
96
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
97
|
+
corrupt_md5(test_file, "random")
|
|
98
|
+
state = is_flac_md5_valid(test_file)
|
|
99
|
+
assert (
|
|
100
|
+
state == FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1
|
|
101
|
+
), "FLAC with all metadata formats should return UNCHECKABLE_DUE_TO_ID3 (ID3v1 takes precedence)"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Test that state detection follows correct precedence rules for MD5 validation."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import FlacMd5State, is_flac_md5_valid
|
|
6
|
+
from audiometa.test.helpers.id3v1 import ID3v1MetadataSetter
|
|
7
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
8
|
+
from audiometa.test.tests.integration.technical_info.flac_md5.conftest import (
|
|
9
|
+
corrupt_md5,
|
|
10
|
+
create_flac_without_md5,
|
|
11
|
+
ensure_flac_has_md5,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.mark.integration
|
|
16
|
+
class TestMd5StatePrecedence:
|
|
17
|
+
"""Test that state detection follows correct precedence rules."""
|
|
18
|
+
|
|
19
|
+
def test_unset_takes_precedence_over_uncheckable(self):
|
|
20
|
+
"""Test that UNSET state takes precedence over UNCHECKABLE_DUE_TO_ID3."""
|
|
21
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
22
|
+
ensure_flac_has_md5(test_file)
|
|
23
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
24
|
+
create_flac_without_md5(test_file)
|
|
25
|
+
state = is_flac_md5_valid(test_file)
|
|
26
|
+
assert (
|
|
27
|
+
state == FlacMd5State.UNSET
|
|
28
|
+
), "UNSET should take precedence over UNCHECKABLE_DUE_TO_ID3 when MD5 is unset"
|
|
29
|
+
|
|
30
|
+
def test_uncheckable_takes_precedence_over_invalid_when_id3v1_present(self):
|
|
31
|
+
"""Test that UNCHECKABLE_DUE_TO_ID3 takes precedence over INVALID when ID3v1 is present."""
|
|
32
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
33
|
+
ensure_flac_has_md5(test_file)
|
|
34
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
35
|
+
corrupt_md5(test_file, "random")
|
|
36
|
+
state = is_flac_md5_valid(test_file)
|
|
37
|
+
assert (
|
|
38
|
+
state == FlacMd5State.UNCHECKABLE_DUE_TO_ID3V1
|
|
39
|
+
), "UNCHECKABLE_DUE_TO_ID3 should take precedence over INVALID when ID3v1 is present"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Test UNSET MD5 state with all metadata format combinations."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import FlacMd5State, is_flac_md5_valid
|
|
6
|
+
from audiometa.test.helpers.id3v1 import ID3v1MetadataSetter
|
|
7
|
+
from audiometa.test.helpers.id3v2 import ID3v2MetadataSetter
|
|
8
|
+
from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
|
|
9
|
+
from audiometa.test.helpers.vorbis import VorbisMetadataSetter
|
|
10
|
+
from audiometa.test.tests.integration.technical_info.flac_md5.conftest import (
|
|
11
|
+
create_flac_without_md5,
|
|
12
|
+
ensure_flac_has_md5,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.mark.integration
|
|
17
|
+
class TestMd5UnsetWithMetadataCombinations:
|
|
18
|
+
"""Test UNSET MD5 state with all metadata format combinations."""
|
|
19
|
+
|
|
20
|
+
def test_unset_md5_no_metadata(self):
|
|
21
|
+
"""Test UNSET state with no metadata."""
|
|
22
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
23
|
+
create_flac_without_md5(test_file)
|
|
24
|
+
state = is_flac_md5_valid(test_file)
|
|
25
|
+
assert state == FlacMd5State.UNSET, "Clean FLAC with unset MD5 should return UNSET"
|
|
26
|
+
|
|
27
|
+
def test_unset_md5_vorbis_only(self):
|
|
28
|
+
"""Test UNSET state with Vorbis metadata only."""
|
|
29
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
30
|
+
ensure_flac_has_md5(test_file)
|
|
31
|
+
create_flac_without_md5(test_file)
|
|
32
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
33
|
+
state = is_flac_md5_valid(test_file)
|
|
34
|
+
assert state == FlacMd5State.UNSET, "FLAC with Vorbis metadata and unset MD5 should return UNSET"
|
|
35
|
+
|
|
36
|
+
def test_unset_md5_id3v2_only(self):
|
|
37
|
+
"""Test UNSET state with ID3v2 metadata only."""
|
|
38
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
39
|
+
ensure_flac_has_md5(test_file)
|
|
40
|
+
create_flac_without_md5(test_file)
|
|
41
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
42
|
+
state = is_flac_md5_valid(test_file)
|
|
43
|
+
assert state == FlacMd5State.UNSET, "FLAC with ID3v2 metadata and unset MD5 should return UNSET"
|
|
44
|
+
|
|
45
|
+
def test_unset_md5_id3v1_only(self):
|
|
46
|
+
"""Test UNSET state with ID3v1 metadata only (should still be UNSET, not UNCHECKABLE)."""
|
|
47
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
48
|
+
ensure_flac_has_md5(test_file)
|
|
49
|
+
create_flac_without_md5(test_file)
|
|
50
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
51
|
+
state = is_flac_md5_valid(test_file)
|
|
52
|
+
assert (
|
|
53
|
+
state == FlacMd5State.UNSET
|
|
54
|
+
), "FLAC with ID3v1 metadata and unset MD5 should return UNSET (unset takes precedence)"
|
|
55
|
+
|
|
56
|
+
def test_unset_md5_vorbis_and_id3v2(self):
|
|
57
|
+
"""Test UNSET state with Vorbis and ID3v2 metadata."""
|
|
58
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
59
|
+
ensure_flac_has_md5(test_file)
|
|
60
|
+
create_flac_without_md5(test_file)
|
|
61
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
62
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
63
|
+
state = is_flac_md5_valid(test_file)
|
|
64
|
+
assert state == FlacMd5State.UNSET, "FLAC with Vorbis and ID3v2 metadata and unset MD5 should return UNSET"
|
|
65
|
+
|
|
66
|
+
def test_unset_md5_vorbis_and_id3v1(self):
|
|
67
|
+
"""Test UNSET state with Vorbis and ID3v1 metadata (unset takes precedence)."""
|
|
68
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
69
|
+
ensure_flac_has_md5(test_file)
|
|
70
|
+
create_flac_without_md5(test_file)
|
|
71
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
72
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
73
|
+
state = is_flac_md5_valid(test_file)
|
|
74
|
+
assert (
|
|
75
|
+
state == FlacMd5State.UNSET
|
|
76
|
+
), "FLAC with Vorbis and ID3v1 metadata and unset MD5 should return UNSET (unset takes precedence)"
|
|
77
|
+
|
|
78
|
+
def test_unset_md5_id3v1_and_id3v2(self):
|
|
79
|
+
"""Test UNSET state with ID3v1 and ID3v2 metadata (unset takes precedence)."""
|
|
80
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
81
|
+
ensure_flac_has_md5(test_file)
|
|
82
|
+
create_flac_without_md5(test_file)
|
|
83
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
84
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
85
|
+
state = is_flac_md5_valid(test_file)
|
|
86
|
+
assert (
|
|
87
|
+
state == FlacMd5State.UNSET
|
|
88
|
+
), "FLAC with ID3v1 and ID3v2 metadata and unset MD5 should return UNSET (unset takes precedence)"
|
|
89
|
+
|
|
90
|
+
def test_unset_md5_all_formats(self):
|
|
91
|
+
"""Test UNSET state with all metadata formats (unset takes precedence)."""
|
|
92
|
+
with temp_file_with_metadata({}, "flac") as test_file:
|
|
93
|
+
ensure_flac_has_md5(test_file)
|
|
94
|
+
create_flac_without_md5(test_file)
|
|
95
|
+
VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
|
|
96
|
+
ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
|
|
97
|
+
ID3v2MetadataSetter.set_metadata(test_file, {"title": "ID3v2 Title"})
|
|
98
|
+
state = is_flac_md5_valid(test_file)
|
|
99
|
+
assert (
|
|
100
|
+
state == FlacMd5State.UNSET
|
|
101
|
+
), "FLAC with all metadata formats and unset MD5 should return UNSET (unset takes precedence)"
|