audiometa-python 0.6.0__tar.gz → 0.6.1__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.0/audiometa_python.egg-info → audiometa_python-0.6.1}/PKG-INFO +35 -37
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/README.md +34 -36
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/__init__.py +42 -20
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/common/external_tool_runner.py +12 -1
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +8 -6
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/test_sync_strategy.py +64 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/tool_path_resolver.py +2 -1
- {audiometa_python-0.6.0 → audiometa_python-0.6.1/audiometa_python.egg-info}/PKG-INFO +35 -37
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/pyproject.toml +1 -1
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/LICENSE +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/__main__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/_audio_file.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/cli.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/exceptions.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_MetadataManager.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/id3v2/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/riff/_RiffManager.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/riff/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/riff/_riff_constants.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/vorbis/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/id3v1/_Id3v1Manager.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/id3v1/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/id3v1/_constants.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/id3v1/id3v1_raw_metadata.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/manager/id3v1/id3v1_raw_metadata_key.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/assets/create_test_files.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/common/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/common/audio_file_creator.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v1/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v1/id3v1_header_verifier.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v2/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v2/id3v2_header_verifier.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/riff/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/riff/riff_header_verifier.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/riff/riff_manual_metadata_creator.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/riff/riff_metadata_deleter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/riff/riff_metadata_getter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/riff/riff_metadata_setter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/scripts/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/technical_info_inspector.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/temp_file_with_metadata.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/vorbis/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/vorbis/vorbis_header_verifier.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/conftest.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/test_basic.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/test_comprehensive.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/test_formats.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/test_metadata_content.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/test_multiple_files.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/test_options.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/read/test_unified.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/test_delete.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/test_formatting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/test_help.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_basic.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_comprehensive.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_force_format.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_integer_fields.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_list_fields.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_rating.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_string_fields.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/cli/write/test_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/workflows/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/workflows/test_core_workflows.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/e2e/workflows/test_rating_workflows.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/wav/test_wav_with_id3v2_tags.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/conftest.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/delete_all_metadata/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/encoding/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/encoding/test_encoding.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_consistency.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_options.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_performance.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/get_full_metadata/test_structure.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/artists/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/artists/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/comment/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/comment/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/composer/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/composer/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/genre/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/isrc/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/language/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/language/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/language/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/title/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/title/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/title/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/reading/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/reading/test_reading_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/real_audio_files/test_reading.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/real_audio_files/test_writing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_audio_data_corruption.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_flipped_md5.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_non_flac_error.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_partial_md5.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_random_md5.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_unset_md5.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/flac_md5/test_valid_md5.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/test_bitrate.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/test_channels.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/technical_info/test_sample_rate.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/test_audio_file.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/test_forced_format.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/test_partial_update.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/test_context_manager.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/test_file_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/test_is_audio_file.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/test_operations.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/audio_file/test_path_handling.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/cli/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/cli/test_expand_file_patterns.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/conftest.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_date_format_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_disc_number_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_format_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_type_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_track_number_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/id3v1_genre_code_map.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/metadata_format.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/metadata_writing_strategy.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/mutagen_exception_handler.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/os_dependencies_checker/__init__.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/os_dependencies_checker/base.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/os_dependencies_checker/config.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/os_dependencies_checker/macos.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/os_dependencies_checker/ubuntu.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/os_dependencies_checker/windows.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/rating_profiles.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/types.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa/utils/unified_metadata_key.py +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa_python.egg-info/SOURCES.txt +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa_python.egg-info/dependency_links.txt +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa_python.egg-info/entry_points.txt +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa_python.egg-info/requires.txt +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/audiometa_python.egg-info/top_level.txt +0 -0
- {audiometa_python-0.6.0 → audiometa_python-0.6.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: audiometa-python
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
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>
|
|
@@ -480,6 +480,8 @@ from audiometa.utils.MetadataFormat import MetadataFormat
|
|
|
480
480
|
update_metadata("song.wav", new_metadata, metadata_format=MetadataFormat.RIFF)
|
|
481
481
|
```
|
|
482
482
|
|
|
483
|
+
**For comprehensive documentation** on writing strategies, format handling, and unsupported field management, see the **[Writing Metadata Guide](docs/WRITING_METADATA.md)**.
|
|
484
|
+
|
|
483
485
|
### Deleting Metadata
|
|
484
486
|
|
|
485
487
|
There are two ways to remove metadata from audio files:
|
|
@@ -936,6 +938,10 @@ validate_metadata_for_update({UnifiedMetadataKey.RELEASE_DATE: "2024/01/01"})
|
|
|
936
938
|
|
|
937
939
|
### Writing Metadata (API Reference)
|
|
938
940
|
|
|
941
|
+
**For comprehensive writing metadata documentation**, including writing strategies, format handling, unsupported field management, and advanced examples, see the dedicated guide:
|
|
942
|
+
|
|
943
|
+
**[Writing Metadata Guide](docs/WRITING_METADATA.md)**
|
|
944
|
+
|
|
939
945
|
For validation before writing, see [Pre-Update Validation (API Reference)](#pre-update-validation-api-reference).
|
|
940
946
|
|
|
941
947
|
#### Metadata Dictionary Structure
|
|
@@ -1057,6 +1063,7 @@ The library provides flexible control over how metadata is written to files that
|
|
|
1057
1063
|
1. **`SYNC` (Default)**: Write to native format and synchronize other metadata formats that are already present
|
|
1058
1064
|
2. **`PRESERVE`**: Write to native format only, preserve existing metadata in other formats
|
|
1059
1065
|
3. **`CLEANUP`**: Write to native format and remove all non-native metadata formats
|
|
1066
|
+
4. **`FORCE`**: Write only to the specified format (when `metadata_format` is provided), fail on unsupported fields
|
|
1060
1067
|
|
|
1061
1068
|
##### Usage Examples
|
|
1062
1069
|
|
|
@@ -1075,35 +1082,17 @@ update_metadata("song.wav", {"title": "New Title"},
|
|
|
1075
1082
|
# PRESERVE strategy - keep other formats unchanged
|
|
1076
1083
|
update_metadata("song.wav", {"title": "New Title"},
|
|
1077
1084
|
metadata_strategy=MetadataWritingStrategy.PRESERVE)
|
|
1085
|
+
|
|
1086
|
+
# FORCE strategy - write only to specified format
|
|
1087
|
+
from audiometa.utils.MetadataFormat import MetadataFormat
|
|
1088
|
+
update_metadata("song.mp3", {"title": "New Title"},
|
|
1089
|
+
metadata_format=MetadataFormat.ID3V2)
|
|
1078
1090
|
```
|
|
1079
1091
|
|
|
1080
1092
|
##### Default Behavior
|
|
1081
1093
|
|
|
1082
1094
|
By default, the library uses the **SYNC strategy** which writes metadata to the native format and synchronizes other metadata formats that are already present. This provides the best user experience by writing metadata where possible and handling unsupported fields gracefully.
|
|
1083
1095
|
|
|
1084
|
-
- **MP3 files**: Writes to ID3v2 and syncs other formats
|
|
1085
|
-
- **FLAC files**: Writes to Vorbis comments and syncs other formats
|
|
1086
|
-
- **WAV files**: Writes to RIFF and syncs other formats
|
|
1087
|
-
|
|
1088
|
-
#### Forced Format Behavior
|
|
1089
|
-
|
|
1090
|
-
When you specify a `metadata_format` parameter, you **cannot** also specify a `metadata_strategy`:
|
|
1091
|
-
|
|
1092
|
-
- **Write only to the specified format**: Other formats are left completely untouched
|
|
1093
|
-
- **Fail fast on unsupported fields**: Raises `MetadataFieldNotSupportedByMetadataFormatError` for any unsupported metadata
|
|
1094
|
-
- **Predictable behavior**: No side effects on other metadata formats
|
|
1095
|
-
|
|
1096
|
-
```python
|
|
1097
|
-
# Correct usage - specify only the format
|
|
1098
|
-
update_metadata("song.mp3", metadata,
|
|
1099
|
-
metadata_format=MetadataFormat.RIFF) # Writes only to RIFF, ignores ID3v2
|
|
1100
|
-
|
|
1101
|
-
# This will raise MetadataWritingConflictParametersError - cannot specify both parameters
|
|
1102
|
-
update_metadata("song.mp3", metadata,
|
|
1103
|
-
metadata_format=MetadataFormat.RIFF,
|
|
1104
|
-
metadata_strategy=MetadataWritingStrategy.CLEANUP) # Raises MetadataWritingConflictParametersError
|
|
1105
|
-
```
|
|
1106
|
-
|
|
1107
1096
|
#### Usage Examples
|
|
1108
1097
|
|
|
1109
1098
|
**Default Behavior (SYNC strategy)**
|
|
@@ -1111,13 +1100,14 @@ update_metadata("song.mp3", metadata,
|
|
|
1111
1100
|
```python
|
|
1112
1101
|
from audiometa import update_metadata
|
|
1113
1102
|
|
|
1114
|
-
# WAV file with existing ID3v1 tags (30-char limit)
|
|
1115
|
-
update_metadata("song.wav", {"title": "This is a Very Long Title That Exceeds ID3v1 Limits"
|
|
1103
|
+
# WAV file with existing ID3v1 tags (30-char limit and no album artist support)
|
|
1104
|
+
update_metadata("song.wav", {"title": "This is a Very Long Title That Exceeds ID3v1 Limits",
|
|
1105
|
+
"album_artist": "Various Artists"})
|
|
1116
1106
|
|
|
1117
1107
|
# Result:
|
|
1118
|
-
# - RIFF tags: Updated with full title (native format)
|
|
1119
|
-
# - ID3v1 tags: Synchronized with truncated title (
|
|
1120
|
-
# - When reading: RIFF title is returned (higher precedence)
|
|
1108
|
+
# - RIFF tags: Updated with full title (native format) and album artist
|
|
1109
|
+
# - ID3v1 tags: Synchronized only with truncated 30-char truncated title and no album artist (not supported)
|
|
1110
|
+
# - When reading: RIFF title is returned (higher precedence) and album artist is available
|
|
1121
1111
|
# Note: ID3v1 title becomes "This is a Very Long Title Th" (truncated)
|
|
1122
1112
|
```
|
|
1123
1113
|
|
|
@@ -1152,11 +1142,15 @@ update_metadata("song.wav", {"title": "New Title"},
|
|
|
1152
1142
|
# Note: SYNC preserves and updates ALL existing metadata formats
|
|
1153
1143
|
```
|
|
1154
1144
|
|
|
1155
|
-
**Format-Specific Writing**
|
|
1145
|
+
**FORCE Strategy - Format-Specific Writing**
|
|
1156
1146
|
|
|
1157
1147
|
```python
|
|
1158
1148
|
from audiometa.utils.MetadataFormat import MetadataFormat
|
|
1159
1149
|
|
|
1150
|
+
# Write specifically to ID3v1 format
|
|
1151
|
+
update_metadata("song.flac", {"title": "New Title"},
|
|
1152
|
+
metadata_format=MetadataFormat.ID3V1)
|
|
1153
|
+
|
|
1160
1154
|
# Write specifically to ID3v2 format (even for WAV files)
|
|
1161
1155
|
update_metadata("song.wav", {"title": "New Title"},
|
|
1162
1156
|
metadata_format=MetadataFormat.ID3V2)
|
|
@@ -1164,6 +1158,10 @@ update_metadata("song.wav", {"title": "New Title"},
|
|
|
1164
1158
|
# Write specifically to RIFF format
|
|
1165
1159
|
update_metadata("song.wav", {"title": "New Title"},
|
|
1166
1160
|
metadata_format=MetadataFormat.RIFF)
|
|
1161
|
+
|
|
1162
|
+
# Write specifically to Vorbis format
|
|
1163
|
+
update_metadata("song.flac", {"title": "New Title"},
|
|
1164
|
+
metadata_format=MetadataFormat.VORBIS)
|
|
1167
1165
|
```
|
|
1168
1166
|
|
|
1169
1167
|
### Deleting Metadata (API Reference)
|
|
@@ -1344,17 +1342,17 @@ For comprehensive documentation on all exceptions that can be raised by the libr
|
|
|
1344
1342
|
The library handles unsupported metadata consistently across all strategies:
|
|
1345
1343
|
|
|
1346
1344
|
- **Forced format** (when `metadata_format` is specified): Always fails fast by raising `MetadataFieldNotSupportedByMetadataFormatError` for any unsupported field. **No writing is performed** - the file remains completely unchanged.
|
|
1347
|
-
- **All strategies (SYNC, PRESERVE, CLEANUP) with `fail_on_unsupported_field=False` (default)**: Handle unsupported fields gracefully by logging warnings and continuing with supported fields
|
|
1345
|
+
- **All strategies (SYNC, PRESERVE, CLEANUP) with `fail_on_unsupported_field=False` (default)**: Handle unsupported fields gracefully by logging individual warnings for each unsupported field and continuing with supported fields. For SYNC strategy, unsupported fields are filtered per-format, allowing all supported fields to sync to each format.
|
|
1348
1346
|
- **All strategies (SYNC, PRESERVE, CLEANUP) with `fail_on_unsupported_field=True`**: Fails fast if any field is not supported by the target format. **No writing is performed** - the file remains completely unchanged (atomic operation).
|
|
1349
1347
|
|
|
1350
1348
|
#### Format-Specific Limitations
|
|
1351
1349
|
|
|
1352
|
-
| Format | Forced Format | All Strategies with `fail_on_unsupported_field=False`
|
|
1353
|
-
| -------------- | --------------------------------- |
|
|
1354
|
-
| **RIFF (WAV)** | Always fails fast, **no writing** | Logs warnings
|
|
1355
|
-
| **ID3v1** | Always fails fast, **no writing** | Logs warnings
|
|
1356
|
-
| **ID3v2** | Always fails fast, **no writing** | All fields supported
|
|
1357
|
-
| **Vorbis** | Always fails fast, **no writing** | All fields supported
|
|
1350
|
+
| Format | Forced Format | All Strategies with `fail_on_unsupported_field=False` | All Strategies with `fail_on_unsupported_field=True` |
|
|
1351
|
+
| -------------- | --------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
1352
|
+
| **RIFF (WAV)** | Always fails fast, **no writing** | Logs individual warnings per unsupported field, writes supported ones | Fails fast for unsupported fields, **no writing** |
|
|
1353
|
+
| **ID3v1** | Always fails fast, **no writing** | Logs individual warnings per unsupported field, writes supported ones | Fails fast for unsupported fields, **no writing** |
|
|
1354
|
+
| **ID3v2** | Always fails fast, **no writing** | All fields supported | All fields supported |
|
|
1355
|
+
| **Vorbis** | Always fails fast, **no writing** | All fields supported | All fields supported |
|
|
1358
1356
|
|
|
1359
1357
|
#### Atomic Write Operations
|
|
1360
1358
|
|
|
@@ -438,6 +438,8 @@ from audiometa.utils.MetadataFormat import MetadataFormat
|
|
|
438
438
|
update_metadata("song.wav", new_metadata, metadata_format=MetadataFormat.RIFF)
|
|
439
439
|
```
|
|
440
440
|
|
|
441
|
+
**For comprehensive documentation** on writing strategies, format handling, and unsupported field management, see the **[Writing Metadata Guide](docs/WRITING_METADATA.md)**.
|
|
442
|
+
|
|
441
443
|
### Deleting Metadata
|
|
442
444
|
|
|
443
445
|
There are two ways to remove metadata from audio files:
|
|
@@ -894,6 +896,10 @@ validate_metadata_for_update({UnifiedMetadataKey.RELEASE_DATE: "2024/01/01"})
|
|
|
894
896
|
|
|
895
897
|
### Writing Metadata (API Reference)
|
|
896
898
|
|
|
899
|
+
**For comprehensive writing metadata documentation**, including writing strategies, format handling, unsupported field management, and advanced examples, see the dedicated guide:
|
|
900
|
+
|
|
901
|
+
**[Writing Metadata Guide](docs/WRITING_METADATA.md)**
|
|
902
|
+
|
|
897
903
|
For validation before writing, see [Pre-Update Validation (API Reference)](#pre-update-validation-api-reference).
|
|
898
904
|
|
|
899
905
|
#### Metadata Dictionary Structure
|
|
@@ -1015,6 +1021,7 @@ The library provides flexible control over how metadata is written to files that
|
|
|
1015
1021
|
1. **`SYNC` (Default)**: Write to native format and synchronize other metadata formats that are already present
|
|
1016
1022
|
2. **`PRESERVE`**: Write to native format only, preserve existing metadata in other formats
|
|
1017
1023
|
3. **`CLEANUP`**: Write to native format and remove all non-native metadata formats
|
|
1024
|
+
4. **`FORCE`**: Write only to the specified format (when `metadata_format` is provided), fail on unsupported fields
|
|
1018
1025
|
|
|
1019
1026
|
##### Usage Examples
|
|
1020
1027
|
|
|
@@ -1033,35 +1040,17 @@ update_metadata("song.wav", {"title": "New Title"},
|
|
|
1033
1040
|
# PRESERVE strategy - keep other formats unchanged
|
|
1034
1041
|
update_metadata("song.wav", {"title": "New Title"},
|
|
1035
1042
|
metadata_strategy=MetadataWritingStrategy.PRESERVE)
|
|
1043
|
+
|
|
1044
|
+
# FORCE strategy - write only to specified format
|
|
1045
|
+
from audiometa.utils.MetadataFormat import MetadataFormat
|
|
1046
|
+
update_metadata("song.mp3", {"title": "New Title"},
|
|
1047
|
+
metadata_format=MetadataFormat.ID3V2)
|
|
1036
1048
|
```
|
|
1037
1049
|
|
|
1038
1050
|
##### Default Behavior
|
|
1039
1051
|
|
|
1040
1052
|
By default, the library uses the **SYNC strategy** which writes metadata to the native format and synchronizes other metadata formats that are already present. This provides the best user experience by writing metadata where possible and handling unsupported fields gracefully.
|
|
1041
1053
|
|
|
1042
|
-
- **MP3 files**: Writes to ID3v2 and syncs other formats
|
|
1043
|
-
- **FLAC files**: Writes to Vorbis comments and syncs other formats
|
|
1044
|
-
- **WAV files**: Writes to RIFF and syncs other formats
|
|
1045
|
-
|
|
1046
|
-
#### Forced Format Behavior
|
|
1047
|
-
|
|
1048
|
-
When you specify a `metadata_format` parameter, you **cannot** also specify a `metadata_strategy`:
|
|
1049
|
-
|
|
1050
|
-
- **Write only to the specified format**: Other formats are left completely untouched
|
|
1051
|
-
- **Fail fast on unsupported fields**: Raises `MetadataFieldNotSupportedByMetadataFormatError` for any unsupported metadata
|
|
1052
|
-
- **Predictable behavior**: No side effects on other metadata formats
|
|
1053
|
-
|
|
1054
|
-
```python
|
|
1055
|
-
# Correct usage - specify only the format
|
|
1056
|
-
update_metadata("song.mp3", metadata,
|
|
1057
|
-
metadata_format=MetadataFormat.RIFF) # Writes only to RIFF, ignores ID3v2
|
|
1058
|
-
|
|
1059
|
-
# This will raise MetadataWritingConflictParametersError - cannot specify both parameters
|
|
1060
|
-
update_metadata("song.mp3", metadata,
|
|
1061
|
-
metadata_format=MetadataFormat.RIFF,
|
|
1062
|
-
metadata_strategy=MetadataWritingStrategy.CLEANUP) # Raises MetadataWritingConflictParametersError
|
|
1063
|
-
```
|
|
1064
|
-
|
|
1065
1054
|
#### Usage Examples
|
|
1066
1055
|
|
|
1067
1056
|
**Default Behavior (SYNC strategy)**
|
|
@@ -1069,13 +1058,14 @@ update_metadata("song.mp3", metadata,
|
|
|
1069
1058
|
```python
|
|
1070
1059
|
from audiometa import update_metadata
|
|
1071
1060
|
|
|
1072
|
-
# WAV file with existing ID3v1 tags (30-char limit)
|
|
1073
|
-
update_metadata("song.wav", {"title": "This is a Very Long Title That Exceeds ID3v1 Limits"
|
|
1061
|
+
# WAV file with existing ID3v1 tags (30-char limit and no album artist support)
|
|
1062
|
+
update_metadata("song.wav", {"title": "This is a Very Long Title That Exceeds ID3v1 Limits",
|
|
1063
|
+
"album_artist": "Various Artists"})
|
|
1074
1064
|
|
|
1075
1065
|
# Result:
|
|
1076
|
-
# - RIFF tags: Updated with full title (native format)
|
|
1077
|
-
# - ID3v1 tags: Synchronized with truncated title (
|
|
1078
|
-
# - When reading: RIFF title is returned (higher precedence)
|
|
1066
|
+
# - RIFF tags: Updated with full title (native format) and album artist
|
|
1067
|
+
# - ID3v1 tags: Synchronized only with truncated 30-char truncated title and no album artist (not supported)
|
|
1068
|
+
# - When reading: RIFF title is returned (higher precedence) and album artist is available
|
|
1079
1069
|
# Note: ID3v1 title becomes "This is a Very Long Title Th" (truncated)
|
|
1080
1070
|
```
|
|
1081
1071
|
|
|
@@ -1110,11 +1100,15 @@ update_metadata("song.wav", {"title": "New Title"},
|
|
|
1110
1100
|
# Note: SYNC preserves and updates ALL existing metadata formats
|
|
1111
1101
|
```
|
|
1112
1102
|
|
|
1113
|
-
**Format-Specific Writing**
|
|
1103
|
+
**FORCE Strategy - Format-Specific Writing**
|
|
1114
1104
|
|
|
1115
1105
|
```python
|
|
1116
1106
|
from audiometa.utils.MetadataFormat import MetadataFormat
|
|
1117
1107
|
|
|
1108
|
+
# Write specifically to ID3v1 format
|
|
1109
|
+
update_metadata("song.flac", {"title": "New Title"},
|
|
1110
|
+
metadata_format=MetadataFormat.ID3V1)
|
|
1111
|
+
|
|
1118
1112
|
# Write specifically to ID3v2 format (even for WAV files)
|
|
1119
1113
|
update_metadata("song.wav", {"title": "New Title"},
|
|
1120
1114
|
metadata_format=MetadataFormat.ID3V2)
|
|
@@ -1122,6 +1116,10 @@ update_metadata("song.wav", {"title": "New Title"},
|
|
|
1122
1116
|
# Write specifically to RIFF format
|
|
1123
1117
|
update_metadata("song.wav", {"title": "New Title"},
|
|
1124
1118
|
metadata_format=MetadataFormat.RIFF)
|
|
1119
|
+
|
|
1120
|
+
# Write specifically to Vorbis format
|
|
1121
|
+
update_metadata("song.flac", {"title": "New Title"},
|
|
1122
|
+
metadata_format=MetadataFormat.VORBIS)
|
|
1125
1123
|
```
|
|
1126
1124
|
|
|
1127
1125
|
### Deleting Metadata (API Reference)
|
|
@@ -1302,17 +1300,17 @@ For comprehensive documentation on all exceptions that can be raised by the libr
|
|
|
1302
1300
|
The library handles unsupported metadata consistently across all strategies:
|
|
1303
1301
|
|
|
1304
1302
|
- **Forced format** (when `metadata_format` is specified): Always fails fast by raising `MetadataFieldNotSupportedByMetadataFormatError` for any unsupported field. **No writing is performed** - the file remains completely unchanged.
|
|
1305
|
-
- **All strategies (SYNC, PRESERVE, CLEANUP) with `fail_on_unsupported_field=False` (default)**: Handle unsupported fields gracefully by logging warnings and continuing with supported fields
|
|
1303
|
+
- **All strategies (SYNC, PRESERVE, CLEANUP) with `fail_on_unsupported_field=False` (default)**: Handle unsupported fields gracefully by logging individual warnings for each unsupported field and continuing with supported fields. For SYNC strategy, unsupported fields are filtered per-format, allowing all supported fields to sync to each format.
|
|
1306
1304
|
- **All strategies (SYNC, PRESERVE, CLEANUP) with `fail_on_unsupported_field=True`**: Fails fast if any field is not supported by the target format. **No writing is performed** - the file remains completely unchanged (atomic operation).
|
|
1307
1305
|
|
|
1308
1306
|
#### Format-Specific Limitations
|
|
1309
1307
|
|
|
1310
|
-
| Format | Forced Format | All Strategies with `fail_on_unsupported_field=False`
|
|
1311
|
-
| -------------- | --------------------------------- |
|
|
1312
|
-
| **RIFF (WAV)** | Always fails fast, **no writing** | Logs warnings
|
|
1313
|
-
| **ID3v1** | Always fails fast, **no writing** | Logs warnings
|
|
1314
|
-
| **ID3v2** | Always fails fast, **no writing** | All fields supported
|
|
1315
|
-
| **Vorbis** | Always fails fast, **no writing** | All fields supported
|
|
1308
|
+
| Format | Forced Format | All Strategies with `fail_on_unsupported_field=False` | All Strategies with `fail_on_unsupported_field=True` |
|
|
1309
|
+
| -------------- | --------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
1310
|
+
| **RIFF (WAV)** | Always fails fast, **no writing** | Logs individual warnings per unsupported field, writes supported ones | Fails fast for unsupported fields, **no writing** |
|
|
1311
|
+
| **ID3v1** | Always fails fast, **no writing** | Logs individual warnings per unsupported field, writes supported ones | Fails fast for unsupported fields, **no writing** |
|
|
1312
|
+
| **ID3v2** | Always fails fast, **no writing** | All fields supported | All fields supported |
|
|
1313
|
+
| **Vorbis** | Always fails fast, **no writing** | All fields supported | All fields supported |
|
|
1316
1314
|
|
|
1317
1315
|
#### Atomic Write Operations
|
|
1318
1316
|
|
|
@@ -571,7 +571,9 @@ def update_metadata(
|
|
|
571
571
|
metadata_format: Specific format to write to. If None, uses the file's native format.
|
|
572
572
|
When specified, strategy is ignored and metadata is written only to this format.
|
|
573
573
|
fail_on_unsupported_field: If True, fails when any metadata field is not supported by the target format.
|
|
574
|
-
|
|
574
|
+
If False (default), unsupported fields are filtered out with individual warnings for each field.
|
|
575
|
+
For SYNC strategy, this applies per-format: unsupported fields are skipped for each format that
|
|
576
|
+
doesn't support them, while still syncing all supported fields.
|
|
575
577
|
|
|
576
578
|
Returns:
|
|
577
579
|
None
|
|
@@ -597,7 +599,8 @@ def update_metadata(
|
|
|
597
599
|
|
|
598
600
|
When metadata_strategy is used, unsupported metadata fields are handled based on the
|
|
599
601
|
fail_on_unsupported_field parameter: True raises MetadataFieldNotSupportedByMetadataFormatError, False (default)
|
|
600
|
-
|
|
602
|
+
filters unsupported fields and warns about each one individually. For SYNC strategy, unsupported fields
|
|
603
|
+
are skipped per-format, allowing supported fields to sync while warning about unsupported ones.
|
|
601
604
|
|
|
602
605
|
Data Filtering:
|
|
603
606
|
For list-type metadata fields (e.g., ARTISTS, GENRES), empty strings and None values
|
|
@@ -738,10 +741,12 @@ def _handle_metadata_strategy(
|
|
|
738
741
|
if fail_on_unsupported_field:
|
|
739
742
|
msg = f"Fields not supported by {target_format_actual.value} format: {unsupported_fields}"
|
|
740
743
|
raise MetadataFieldNotSupportedByMetadataFormatError(msg)
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
744
|
+
# Warn about each unsupported field individually
|
|
745
|
+
for unsupported_field in unsupported_fields:
|
|
746
|
+
field_warn_msg = (
|
|
747
|
+
f"Field {unsupported_field} not supported by {target_format_actual.value} format, skipped"
|
|
748
|
+
)
|
|
749
|
+
warnings.warn(field_warn_msg, stacklevel=2)
|
|
745
750
|
# Create filtered metadata without unsupported fields
|
|
746
751
|
filtered_metadata = {k: v for k, v in unified_metadata.items() if k not in unsupported_fields}
|
|
747
752
|
unified_metadata = filtered_metadata
|
|
@@ -777,10 +782,12 @@ def _handle_metadata_strategy(
|
|
|
777
782
|
) and field not in target_manager.metadata_keys_direct_map_write:
|
|
778
783
|
unsupported_fields.append(field)
|
|
779
784
|
if unsupported_fields:
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
785
|
+
# Warn about each unsupported field individually for target format
|
|
786
|
+
for unsupported_field in unsupported_fields:
|
|
787
|
+
field_warn_msg = (
|
|
788
|
+
f"Field {unsupported_field} not supported by {target_format_actual.value} format, skipped"
|
|
789
|
+
)
|
|
790
|
+
warnings.warn(field_warn_msg, stacklevel=2)
|
|
784
791
|
# Create filtered metadata without unsupported fields
|
|
785
792
|
filtered_metadata = {k: v for k, v in unified_metadata.items() if k not in unsupported_fields}
|
|
786
793
|
unified_metadata = filtered_metadata
|
|
@@ -804,16 +811,31 @@ def _handle_metadata_strategy(
|
|
|
804
811
|
# Then sync all other available formats
|
|
805
812
|
# Note: We need to be careful about the order to avoid conflicts
|
|
806
813
|
for fmt_name, manager in other_managers.items():
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
814
|
+
# For non-target formats, filter out unsupported fields and warn about them
|
|
815
|
+
# This allows syncing supported fields even when some fields are not supported
|
|
816
|
+
unsupported_fields = []
|
|
817
|
+
if hasattr(manager, "metadata_keys_direct_map_write") and manager.metadata_keys_direct_map_write:
|
|
818
|
+
for field in unified_metadata:
|
|
819
|
+
if field not in manager.metadata_keys_direct_map_write:
|
|
820
|
+
unsupported_fields.append(field)
|
|
821
|
+
|
|
822
|
+
# Create filtered metadata with only supported fields
|
|
823
|
+
format_metadata = (
|
|
824
|
+
{k: v for k, v in unified_metadata.items() if k not in unsupported_fields}
|
|
825
|
+
if unsupported_fields
|
|
826
|
+
else unified_metadata
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
# Warn about each unsupported field individually for non-target formats
|
|
830
|
+
for unsupported_field in unsupported_fields:
|
|
831
|
+
field_warn_msg = f"Field {unsupported_field} not supported by {fmt_name} format, skipped"
|
|
832
|
+
warnings.warn(field_warn_msg, stacklevel=2)
|
|
833
|
+
|
|
834
|
+
# Try to update with supported fields only
|
|
835
|
+
if format_metadata: # Only update if there are supported fields
|
|
836
|
+
with contextlib.suppress(Exception):
|
|
837
|
+
# Some managers might fail for other reasons - continue with next format
|
|
838
|
+
manager.update_metadata(format_metadata)
|
|
817
839
|
|
|
818
840
|
elif strategy == MetadataWritingStrategy.PRESERVE:
|
|
819
841
|
# For PRESERVE, we need to save existing metadata from other formats first
|
|
@@ -27,7 +27,18 @@ def run_external_tool(
|
|
|
27
27
|
"""
|
|
28
28
|
try:
|
|
29
29
|
text = not isinstance(input_data, bytes) if input_data is not None else True
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
# Ensure venv's bin directory is in PATH for Python tools like mid3v2
|
|
32
|
+
import os
|
|
33
|
+
import sys
|
|
34
|
+
|
|
35
|
+
env = os.environ.copy()
|
|
36
|
+
if hasattr(sys, "prefix"):
|
|
37
|
+
venv_bin = Path(sys.prefix) / "bin"
|
|
38
|
+
if venv_bin.exists():
|
|
39
|
+
env["PATH"] = f"{venv_bin}:{env.get('PATH', '')}"
|
|
40
|
+
|
|
41
|
+
return subprocess.run(command, capture_output=True, text=text, check=check, input=input_data, env=env)
|
|
31
42
|
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
32
43
|
msg = f"{tool_name} failed: {e}"
|
|
33
44
|
raise ExternalMetadataToolError(msg) from e
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from audiometa.utils.tool_path_resolver import get_tool_path
|
|
7
|
+
|
|
6
8
|
from ..common.external_tool_runner import run_external_tool
|
|
7
9
|
|
|
8
10
|
|
|
@@ -47,7 +49,7 @@ class ID3v2MetadataSetter:
|
|
|
47
49
|
}
|
|
48
50
|
else:
|
|
49
51
|
tool = "mid3v2"
|
|
50
|
-
cmd = ["mid3v2"]
|
|
52
|
+
cmd = [get_tool_path("mid3v2")]
|
|
51
53
|
# Map common metadata keys to mid3v2 tool arguments
|
|
52
54
|
key_mapping = {
|
|
53
55
|
"title": "--song",
|
|
@@ -174,7 +176,7 @@ class ID3v2MetadataSetter:
|
|
|
174
176
|
command = ["id3v2", "--id3v2-only", "--song", title, str(file_path)]
|
|
175
177
|
run_external_tool(command, "id3v2")
|
|
176
178
|
else:
|
|
177
|
-
command = ["mid3v2", "--song", title, str(file_path)]
|
|
179
|
+
command = [get_tool_path("mid3v2"), "--song", title, str(file_path)]
|
|
178
180
|
run_external_tool(command, "mid3v2")
|
|
179
181
|
|
|
180
182
|
@staticmethod
|
|
@@ -200,7 +202,7 @@ class ID3v2MetadataSetter:
|
|
|
200
202
|
run_external_tool(command, "id3v2")
|
|
201
203
|
else:
|
|
202
204
|
# Use mid3v2 for ID3v2.4
|
|
203
|
-
command = ["mid3v2", "--artist", artists, str(file_path)]
|
|
205
|
+
command = [get_tool_path("mid3v2"), "--artist", artists, str(file_path)]
|
|
204
206
|
run_external_tool(command, "mid3v2")
|
|
205
207
|
else:
|
|
206
208
|
# For list input, use multiple values handling
|
|
@@ -214,7 +216,7 @@ class ID3v2MetadataSetter:
|
|
|
214
216
|
command = ["id3v2", "--id3v2-only", "--album", album, str(file_path)]
|
|
215
217
|
run_external_tool(command, "id3v2")
|
|
216
218
|
else:
|
|
217
|
-
command = ["mid3v2", "--album", album, str(file_path)]
|
|
219
|
+
command = [get_tool_path("mid3v2"), "--album", album, str(file_path)]
|
|
218
220
|
run_external_tool(command, "mid3v2")
|
|
219
221
|
|
|
220
222
|
@staticmethod
|
|
@@ -232,7 +234,7 @@ class ID3v2MetadataSetter:
|
|
|
232
234
|
command = ["id3v2", "--id3v2-only", "--USLT", lyrics, str(file_path)]
|
|
233
235
|
run_external_tool(command, "id3v2")
|
|
234
236
|
else:
|
|
235
|
-
command = ["mid3v2", "--USLT", lyrics, str(file_path)]
|
|
237
|
+
command = [get_tool_path("mid3v2"), "--USLT", lyrics, str(file_path)]
|
|
236
238
|
run_external_tool(command, "mid3v2")
|
|
237
239
|
|
|
238
240
|
@staticmethod
|
|
@@ -287,7 +289,7 @@ class ID3v2MetadataSetter:
|
|
|
287
289
|
run_external_tool(command, "id3v2")
|
|
288
290
|
else:
|
|
289
291
|
# ID3v2.4: Use TDRC with full date
|
|
290
|
-
command = ["mid3v2", "--TDRC", date_str, str(file_path)]
|
|
292
|
+
command = [get_tool_path("mid3v2"), "--TDRC", date_str, str(file_path)]
|
|
291
293
|
run_external_tool(command, "mid3v2")
|
|
292
294
|
|
|
293
295
|
@staticmethod
|
|
@@ -213,3 +213,67 @@ class TestSyncStrategy:
|
|
|
213
213
|
|
|
214
214
|
vorbis_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.VORBIS)
|
|
215
215
|
assert vorbis_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
216
|
+
|
|
217
|
+
def test_sync_strategy_mp3_genre_and_album_artist(self):
|
|
218
|
+
"""Test SYNC strategy updates genre and album artist on MP3 file with ID3v1 present.
|
|
219
|
+
|
|
220
|
+
This test verifies that when writing metadata with SYNC strategy:
|
|
221
|
+
1. ID3v2 metadata is written with all fields including genre and album artist
|
|
222
|
+
2. ID3v1 metadata is synced with supported fields (genre) but unsupported fields (album_artists) are skipped
|
|
223
|
+
"""
|
|
224
|
+
with temp_file_with_metadata({}, "mp3") as test_file:
|
|
225
|
+
# Add ID3v1 metadata using external tools (to establish ID3v1 as an existing format)
|
|
226
|
+
ID3v1MetadataSetter.set_metadata(
|
|
227
|
+
test_file,
|
|
228
|
+
{
|
|
229
|
+
"title": "ID3v1 Title",
|
|
230
|
+
"artist": "ID3v1 Artist",
|
|
231
|
+
"album": "ID3v1 Album",
|
|
232
|
+
"genre": "Rock",
|
|
233
|
+
},
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Verify ID3v1 metadata was written
|
|
237
|
+
id3v1_result = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
238
|
+
assert id3v1_result.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
|
|
239
|
+
assert id3v1_result.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
|
|
240
|
+
|
|
241
|
+
# Now write ID3v2 metadata with SYNC strategy including genre and album artist
|
|
242
|
+
# SYNC should write to ID3v2 (native format) and sync to ID3v1 (existing format)
|
|
243
|
+
id3v2_metadata = {
|
|
244
|
+
UnifiedMetadataKey.TITLE: "Synced Title",
|
|
245
|
+
UnifiedMetadataKey.ARTISTS: ["Synced Artist"],
|
|
246
|
+
UnifiedMetadataKey.ALBUM: "Synced Album",
|
|
247
|
+
UnifiedMetadataKey.GENRES_NAMES: ["Jazz", "Blues"],
|
|
248
|
+
UnifiedMetadataKey.ALBUM_ARTISTS: ["Album Artist 1", "Album Artist 2"],
|
|
249
|
+
}
|
|
250
|
+
update_metadata(test_file, id3v2_metadata, metadata_strategy=MetadataWritingStrategy.SYNC)
|
|
251
|
+
|
|
252
|
+
# Verify ID3v2 metadata has all values including multiple genres and album artists
|
|
253
|
+
id3v2_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
|
|
254
|
+
assert id3v2_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
255
|
+
assert id3v2_after.get(UnifiedMetadataKey.ARTISTS) == ["Synced Artist"]
|
|
256
|
+
assert id3v2_after.get(UnifiedMetadataKey.ALBUM) == "Synced Album"
|
|
257
|
+
assert id3v2_after.get(UnifiedMetadataKey.GENRES_NAMES) == ["Jazz", "Blues"]
|
|
258
|
+
assert id3v2_after.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Album Artist 1", "Album Artist 2"]
|
|
259
|
+
|
|
260
|
+
# Verify ID3v1 metadata was synced with supported fields only
|
|
261
|
+
# When SYNC strategy encounters unsupported fields (like ALBUM_ARTISTS in ID3v1),
|
|
262
|
+
# it should sync only the supported fields and skip the unsupported ones.
|
|
263
|
+
# This is the correct behavior for non-target formats in SYNC strategy.
|
|
264
|
+
id3v1_after = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
|
|
265
|
+
|
|
266
|
+
# ID3v1 should be synced with all supported fields
|
|
267
|
+
assert id3v1_after.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
268
|
+
assert id3v1_after.get(UnifiedMetadataKey.ARTISTS) == ["Synced Artist"]
|
|
269
|
+
assert id3v1_after.get(UnifiedMetadataKey.ALBUM) == "Synced Album"
|
|
270
|
+
assert id3v1_after.get(UnifiedMetadataKey.GENRES_NAMES) == ["Jazz"] # Only first genre in ID3v1
|
|
271
|
+
|
|
272
|
+
# Album artists are not supported in ID3v1, should be None (field is skipped)
|
|
273
|
+
assert id3v1_after.get(UnifiedMetadataKey.ALBUM_ARTISTS) is None
|
|
274
|
+
|
|
275
|
+
# Merged metadata should prefer ID3v2 (higher precedence)
|
|
276
|
+
merged = get_unified_metadata(test_file)
|
|
277
|
+
assert merged.get(UnifiedMetadataKey.TITLE) == "Synced Title"
|
|
278
|
+
assert merged.get(UnifiedMetadataKey.GENRES_NAMES) == ["Jazz", "Blues"]
|
|
279
|
+
assert merged.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Album Artist 1", "Album Artist 2"]
|
|
@@ -27,7 +27,7 @@ def get_tool_path(tool_name: str) -> str:
|
|
|
27
27
|
name if pinned version path cannot be resolved (relies on PATH).
|
|
28
28
|
|
|
29
29
|
Args:
|
|
30
|
-
tool_name: Name of the tool (e.g., "flac", "ffprobe", "metaflac")
|
|
30
|
+
tool_name: Name of the tool (e.g., "flac", "ffprobe", "metaflac", "mid3v2")
|
|
31
31
|
|
|
32
32
|
Returns:
|
|
33
33
|
Absolute path to pinned version executable, or tool name if not found
|
|
@@ -48,6 +48,7 @@ def get_tool_path(tool_name: str) -> str:
|
|
|
48
48
|
"metaflac": "flac", # metaflac comes from flac package
|
|
49
49
|
"mediainfo": "media-info",
|
|
50
50
|
"id3v2": "id3v2",
|
|
51
|
+
"mid3v2": "mutagen", # mid3v2 comes from mutagen package
|
|
51
52
|
"bwfmetaedit": "bwfmetaedit",
|
|
52
53
|
"exiftool": "exiftool",
|
|
53
54
|
}
|