audiometa-python 1.3.3__tar.gz → 1.4.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-1.3.3/audiometa_python.egg-info → audiometa_python-1.4.0}/PKG-INFO +44 -4
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/README.md +42 -2
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/__init__.py +53 -1
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +13 -1
- audiometa_python-1.4.0/audiometa/test/tests/unit/test_get_supported_unified_metadata_field_ids.py +48 -0
- audiometa_python-1.4.0/audiometa/test/tests/unit/utils/test_unified_metadata_field_schema.py +88 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/types.py +12 -0
- audiometa_python-1.4.0/audiometa/utils/unified_metadata_field_schema.py +80 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0/audiometa_python.egg-info}/PKG-INFO +44 -4
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa_python.egg-info/SOURCES.txt +3 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/pyproject.toml +2 -2
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/LICENSE +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/__main__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/_audio_file.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/cli.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/exceptions.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_MetadataManager.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v1_preserver.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_flac_handler.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_reader.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_writer.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_RiffManager.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_bext_chunk.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_constants.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_file_structure.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_info_chunk.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/vorbis/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/id3v1/_Id3v1Manager.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/id3v1/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/id3v1/_constants.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/id3v1/id3v1_raw_metadata.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/manager/id3v1/id3v1_raw_metadata_key.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/assets/create_test_files.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/common/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/common/audio_file_creator.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/common/external_tool_runner.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_header_verifier.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_header_verifier.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_header_verifier.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_manual_metadata_creator.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_metadata_deleter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_metadata_getter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_metadata_setter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/scripts/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/technical_info_inspector.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/temp_file_with_metadata.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_header_verifier.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/conftest.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_basic.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_comprehensive.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_formats.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_metadata_content.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_multiple_files.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_options.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_unified.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/test_delete.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/test_formatting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/test_help.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_basic.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_comprehensive.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_force_format.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_integer_fields.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_list_fields.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_rating.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_string_fields.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_core_workflows.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_rating_workflows.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/conftest.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/encoding/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/encoding/test_encoding.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/options/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/options/test_include_headers.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/options/test_include_technical.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_consistency.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_performance.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_raw_metadata_includes_unsupported_tags.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_structure.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/reading/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/reading/test_reading_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/real_audio_files/test_reading.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/real_audio_files/test_writing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_audio_data_corruption.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_file.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_invalid_md5.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_invalid_with_metadata_combinations.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_state_precedence.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_unset_with_metadata_combinations.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_fails_with_id3v1.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_with_id3v2_only.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_works_without_id3v1.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_with_metadata_combinations.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_unset_md5.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_delete_original.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_flipped_md5.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_partial_md5.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_random_md5.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_md5_repair_with_metadata.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_non_flac_error.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_unset_md5.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_bitrate.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_channels.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_sample_rate.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/test_audio_file.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_forced_format.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_partial_update.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_flac_sync.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_mp3_sync.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_wav_sync.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_context_manager.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_file_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_is_audio_file.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_operations.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_path_handling.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/cli/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/cli/test_cli_colorize.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/cli/test_expand_file_patterns.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/conftest.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff_info_chunk_fourcc.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/disc_number/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/disc_number/test_disc_number_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/test_isrc_format_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/test_isrc_type_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/test_musicbrainz_artistid_format_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/test_musicbrainz_artistid_type_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/test_musicbrainz_trackid_format_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/test_musicbrainz_trackid_type_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_uuid/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_uuid/test_uuid_format_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/release_date/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/release_date/test_date_format_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/track_number/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/track_number/test_track_number_validation.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/utils/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/utils/test_disc_number_read.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/test/tests/unit/utils/test_raw_metadata_sanitizer.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/disc_number_read.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/flac_md5_state.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/id3v1_genre_code_map.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/metadata_format.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/metadata_writing_strategy.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/mutagen_exception_handler.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/__init__.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/base.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/config.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/macos.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/ubuntu.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/windows.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/rating_profiles.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/raw_metadata_sanitizer.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/tool_path_resolver.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa/utils/unified_metadata_key.py +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa_python.egg-info/dependency_links.txt +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa_python.egg-info/entry_points.txt +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa_python.egg-info/requires.txt +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/audiometa_python.egg-info/top_level.txt +0 -0
- {audiometa_python-1.3.3 → audiometa_python-1.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: audiometa-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.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>
|
|
@@ -9,7 +9,7 @@ Project-URL: Homepage, https://github.com/BehindTheMusicTree/audiometa
|
|
|
9
9
|
Project-URL: Repository, https://github.com/BehindTheMusicTree/audiometa
|
|
10
10
|
Project-URL: Issues, https://github.com/BehindTheMusicTree/audiometa/issues
|
|
11
11
|
Keywords: audio,metadata,mp3,flac,wav,id3,vorbis,riff
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
@@ -321,11 +321,13 @@ AudioMeta uses a combination of Python libraries and external command-line tools
|
|
|
321
321
|
|
|
322
322
|
### Reading Metadata
|
|
323
323
|
|
|
324
|
-
When reading metadata,
|
|
324
|
+
When reading metadata, the main entry points are `get_unified_metadata`, `get_unified_metadata_field`, and `get_full_metadata`. For tooling and UIs, `get_unified_metadata_field_schema` and `get_supported_unified_metadata_field_ids` describe the full unified vocabulary and which fields are writable for a given file’s primary format.
|
|
325
325
|
|
|
326
326
|
- `get_unified_metadata`: Reads all metadata from a file and returns a unified dictionary.
|
|
327
327
|
- `get_unified_metadata_field`: Reads a specific metadata field from a file.
|
|
328
|
-
- `get_full_metadata`: Reads all metadata from a file and returns a dictionary including headers and
|
|
328
|
+
- `get_full_metadata`: Reads all metadata from a file and returns a dictionary including headers, technical info, raw tags, **unified field schema**, and **per-file supported unified field ids**.
|
|
329
|
+
- `get_unified_metadata_field_schema`: Returns wire-oriented descriptors (`id`, `label`, `multiple`, `value_type`, `optional_value`) for every `UnifiedMetadataKey` (no file required).
|
|
330
|
+
- `get_supported_unified_metadata_field_ids`: Returns sorted unified field ids that have a write mapping for the file’s primary metadata format.
|
|
329
331
|
|
|
330
332
|
#### Reading from a specific metadata format
|
|
331
333
|
|
|
@@ -413,6 +415,21 @@ except MetadataFieldNotSupportedByMetadataFormatError as e:
|
|
|
413
415
|
print(f"Error: {e}")
|
|
414
416
|
```
|
|
415
417
|
|
|
418
|
+
#### Unified field schema and writable field ids
|
|
419
|
+
|
|
420
|
+
**`get_unified_metadata_field_schema()`** returns one descriptor per `UnifiedMetadataKey`: stable string **`id`** (the enum value), English **`label`**, **`multiple`**, JSON-oriented **`value_type`**, and **`optional_value`**. Use it for forms, validation, or API docs without hard-coding the enum.
|
|
421
|
+
|
|
422
|
+
**`get_supported_unified_metadata_field_ids(file)`** returns sorted **`UnifiedMetadataKey.value`** strings for fields that have a non-`None` write mapping in the file’s **primary** (native) metadata format.
|
|
423
|
+
|
|
424
|
+
```python
|
|
425
|
+
from audiometa import get_unified_metadata_field_schema, get_supported_unified_metadata_field_ids
|
|
426
|
+
|
|
427
|
+
schema = get_unified_metadata_field_schema()
|
|
428
|
+
writable_on_this_file = get_supported_unified_metadata_field_ids("song.mp3")
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
`get_full_metadata` always includes **`unified_metadata_field_schema`** (same list as above) and **`supported_unified_metadata_field_ids`** for that path. The **`audiometa read`** command surfaces these keys in **JSON** and **YAML** output; **table** output only prints unified, technical, and format metadata sections.
|
|
432
|
+
|
|
416
433
|
#### Reading Full Metadata From All Formats Including Headers and Technical Info
|
|
417
434
|
|
|
418
435
|
**`get_full_metadata(file_path, include_headers=True, include_technical=True, include_raw_binary_data=False)`**
|
|
@@ -425,6 +442,8 @@ Gets comprehensive metadata including all available information from a file, inc
|
|
|
425
442
|
from audiometa import get_full_metadata
|
|
426
443
|
|
|
427
444
|
full_metadata = get_full_metadata("song.mp3")
|
|
445
|
+
# full_metadata["unified_metadata_field_schema"] — all unified keys
|
|
446
|
+
# full_metadata["supported_unified_metadata_field_ids"] — writable via primary format
|
|
428
447
|
```
|
|
429
448
|
|
|
430
449
|
### Validate Metadata Before Update
|
|
@@ -702,6 +721,7 @@ This function provides the most complete view of an audio file by combining:
|
|
|
702
721
|
- Technical information (duration, bitrate, sample rate, channels, file size)
|
|
703
722
|
- Format-specific headers and structure information
|
|
704
723
|
- Raw metadata details from each format (when include_raw_binary_data is False, binary/opaque content such as APIC, PRIV, TRAKTOR4 is summarized as size placeholders)
|
|
724
|
+
- **Unified field schema** (`unified_metadata_field_schema`) and **per-file writable unified ids** (`supported_unified_metadata_field_ids`); see `get_unified_metadata_field_schema` and `get_supported_unified_metadata_field_ids`
|
|
705
725
|
|
|
706
726
|
```python
|
|
707
727
|
from audiometa import get_full_metadata, UnifiedMetadataKey
|
|
@@ -713,6 +733,10 @@ full_metadata = get_full_metadata("song.mp3")
|
|
|
713
733
|
print(f"Title: {full_metadata['unified_metadata'][UnifiedMetadataKey.TITLE]}")
|
|
714
734
|
print(f"Artists: {full_metadata['unified_metadata'][UnifiedMetadataKey.ARTISTS]}")
|
|
715
735
|
|
|
736
|
+
# Field vocabulary and ids writable for this file's primary format
|
|
737
|
+
print(full_metadata["unified_metadata_field_schema"][0])
|
|
738
|
+
print(full_metadata["supported_unified_metadata_field_ids"])
|
|
739
|
+
|
|
716
740
|
# Access technical information
|
|
717
741
|
print(f"Duration: {full_metadata['technical_info']['duration_seconds']} seconds")
|
|
718
742
|
print(f"Bitrate: {full_metadata['technical_info']['bitrate_bps']} bps ({full_metadata['technical_info']['bitrate_bps'] // 1000} kbps)")
|
|
@@ -839,6 +863,22 @@ A comprehensive dictionary containing:
|
|
|
839
863
|
'chunk_structure': {...}
|
|
840
864
|
}
|
|
841
865
|
},
|
|
866
|
+
'unified_metadata_field_schema': [
|
|
867
|
+
{
|
|
868
|
+
'id': 'title',
|
|
869
|
+
'label': 'Title',
|
|
870
|
+
'multiple': False,
|
|
871
|
+
'value_type': 'string',
|
|
872
|
+
'optional_value': False,
|
|
873
|
+
},
|
|
874
|
+
# ... one entry per UnifiedMetadataKey
|
|
875
|
+
],
|
|
876
|
+
'supported_unified_metadata_field_ids': [
|
|
877
|
+
'album',
|
|
878
|
+
'artists',
|
|
879
|
+
'title',
|
|
880
|
+
# ... ids with a write mapping for this file's primary format
|
|
881
|
+
],
|
|
842
882
|
'format_priorities': {
|
|
843
883
|
'file_extension': '.mp3',
|
|
844
884
|
'reading_order': ['id3v2', 'id3v1'],
|
|
@@ -279,11 +279,13 @@ AudioMeta uses a combination of Python libraries and external command-line tools
|
|
|
279
279
|
|
|
280
280
|
### Reading Metadata
|
|
281
281
|
|
|
282
|
-
When reading metadata,
|
|
282
|
+
When reading metadata, the main entry points are `get_unified_metadata`, `get_unified_metadata_field`, and `get_full_metadata`. For tooling and UIs, `get_unified_metadata_field_schema` and `get_supported_unified_metadata_field_ids` describe the full unified vocabulary and which fields are writable for a given file’s primary format.
|
|
283
283
|
|
|
284
284
|
- `get_unified_metadata`: Reads all metadata from a file and returns a unified dictionary.
|
|
285
285
|
- `get_unified_metadata_field`: Reads a specific metadata field from a file.
|
|
286
|
-
- `get_full_metadata`: Reads all metadata from a file and returns a dictionary including headers and
|
|
286
|
+
- `get_full_metadata`: Reads all metadata from a file and returns a dictionary including headers, technical info, raw tags, **unified field schema**, and **per-file supported unified field ids**.
|
|
287
|
+
- `get_unified_metadata_field_schema`: Returns wire-oriented descriptors (`id`, `label`, `multiple`, `value_type`, `optional_value`) for every `UnifiedMetadataKey` (no file required).
|
|
288
|
+
- `get_supported_unified_metadata_field_ids`: Returns sorted unified field ids that have a write mapping for the file’s primary metadata format.
|
|
287
289
|
|
|
288
290
|
#### Reading from a specific metadata format
|
|
289
291
|
|
|
@@ -371,6 +373,21 @@ except MetadataFieldNotSupportedByMetadataFormatError as e:
|
|
|
371
373
|
print(f"Error: {e}")
|
|
372
374
|
```
|
|
373
375
|
|
|
376
|
+
#### Unified field schema and writable field ids
|
|
377
|
+
|
|
378
|
+
**`get_unified_metadata_field_schema()`** returns one descriptor per `UnifiedMetadataKey`: stable string **`id`** (the enum value), English **`label`**, **`multiple`**, JSON-oriented **`value_type`**, and **`optional_value`**. Use it for forms, validation, or API docs without hard-coding the enum.
|
|
379
|
+
|
|
380
|
+
**`get_supported_unified_metadata_field_ids(file)`** returns sorted **`UnifiedMetadataKey.value`** strings for fields that have a non-`None` write mapping in the file’s **primary** (native) metadata format.
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
from audiometa import get_unified_metadata_field_schema, get_supported_unified_metadata_field_ids
|
|
384
|
+
|
|
385
|
+
schema = get_unified_metadata_field_schema()
|
|
386
|
+
writable_on_this_file = get_supported_unified_metadata_field_ids("song.mp3")
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`get_full_metadata` always includes **`unified_metadata_field_schema`** (same list as above) and **`supported_unified_metadata_field_ids`** for that path. The **`audiometa read`** command surfaces these keys in **JSON** and **YAML** output; **table** output only prints unified, technical, and format metadata sections.
|
|
390
|
+
|
|
374
391
|
#### Reading Full Metadata From All Formats Including Headers and Technical Info
|
|
375
392
|
|
|
376
393
|
**`get_full_metadata(file_path, include_headers=True, include_technical=True, include_raw_binary_data=False)`**
|
|
@@ -383,6 +400,8 @@ Gets comprehensive metadata including all available information from a file, inc
|
|
|
383
400
|
from audiometa import get_full_metadata
|
|
384
401
|
|
|
385
402
|
full_metadata = get_full_metadata("song.mp3")
|
|
403
|
+
# full_metadata["unified_metadata_field_schema"] — all unified keys
|
|
404
|
+
# full_metadata["supported_unified_metadata_field_ids"] — writable via primary format
|
|
386
405
|
```
|
|
387
406
|
|
|
388
407
|
### Validate Metadata Before Update
|
|
@@ -660,6 +679,7 @@ This function provides the most complete view of an audio file by combining:
|
|
|
660
679
|
- Technical information (duration, bitrate, sample rate, channels, file size)
|
|
661
680
|
- Format-specific headers and structure information
|
|
662
681
|
- Raw metadata details from each format (when include_raw_binary_data is False, binary/opaque content such as APIC, PRIV, TRAKTOR4 is summarized as size placeholders)
|
|
682
|
+
- **Unified field schema** (`unified_metadata_field_schema`) and **per-file writable unified ids** (`supported_unified_metadata_field_ids`); see `get_unified_metadata_field_schema` and `get_supported_unified_metadata_field_ids`
|
|
663
683
|
|
|
664
684
|
```python
|
|
665
685
|
from audiometa import get_full_metadata, UnifiedMetadataKey
|
|
@@ -671,6 +691,10 @@ full_metadata = get_full_metadata("song.mp3")
|
|
|
671
691
|
print(f"Title: {full_metadata['unified_metadata'][UnifiedMetadataKey.TITLE]}")
|
|
672
692
|
print(f"Artists: {full_metadata['unified_metadata'][UnifiedMetadataKey.ARTISTS]}")
|
|
673
693
|
|
|
694
|
+
# Field vocabulary and ids writable for this file's primary format
|
|
695
|
+
print(full_metadata["unified_metadata_field_schema"][0])
|
|
696
|
+
print(full_metadata["supported_unified_metadata_field_ids"])
|
|
697
|
+
|
|
674
698
|
# Access technical information
|
|
675
699
|
print(f"Duration: {full_metadata['technical_info']['duration_seconds']} seconds")
|
|
676
700
|
print(f"Bitrate: {full_metadata['technical_info']['bitrate_bps']} bps ({full_metadata['technical_info']['bitrate_bps'] // 1000} kbps)")
|
|
@@ -797,6 +821,22 @@ A comprehensive dictionary containing:
|
|
|
797
821
|
'chunk_structure': {...}
|
|
798
822
|
}
|
|
799
823
|
},
|
|
824
|
+
'unified_metadata_field_schema': [
|
|
825
|
+
{
|
|
826
|
+
'id': 'title',
|
|
827
|
+
'label': 'Title',
|
|
828
|
+
'multiple': False,
|
|
829
|
+
'value_type': 'string',
|
|
830
|
+
'optional_value': False,
|
|
831
|
+
},
|
|
832
|
+
# ... one entry per UnifiedMetadataKey
|
|
833
|
+
],
|
|
834
|
+
'supported_unified_metadata_field_ids': [
|
|
835
|
+
'album',
|
|
836
|
+
'artists',
|
|
837
|
+
'title',
|
|
838
|
+
# ... ids with a write mapping for this file's primary format
|
|
839
|
+
],
|
|
800
840
|
'format_priorities': {
|
|
801
841
|
'file_extension': '.mp3',
|
|
802
842
|
'reading_order': ['id3v2', 'id3v1'],
|
|
@@ -34,10 +34,13 @@ from .utils.flac_md5_state import FlacMd5State
|
|
|
34
34
|
from .utils.metadata_format import MetadataFormat
|
|
35
35
|
from .utils.metadata_writing_strategy import MetadataWritingStrategy
|
|
36
36
|
from .utils.types import UnifiedMetadata, UnifiedMetadataValue
|
|
37
|
+
from .utils.unified_metadata_field_schema import get_unified_metadata_field_schema
|
|
37
38
|
from .utils.unified_metadata_key import UnifiedMetadataKey
|
|
38
39
|
|
|
39
40
|
__all__ = [
|
|
40
41
|
"UnifiedMetadataKey",
|
|
42
|
+
"get_unified_metadata_field_schema",
|
|
43
|
+
"get_supported_unified_metadata_field_ids",
|
|
41
44
|
"FlacMd5State",
|
|
42
45
|
"MetadataFormat",
|
|
43
46
|
"MetadataWritingStrategy",
|
|
@@ -79,6 +82,35 @@ METADATA_FORMAT_MANAGER_CLASS_MAP: dict[MetadataFormat, type] = {
|
|
|
79
82
|
type PublicFileType = str | Path
|
|
80
83
|
|
|
81
84
|
|
|
85
|
+
def _supported_unified_metadata_field_ids_from_manager(manager: _MetadataManager | None) -> list[str]:
|
|
86
|
+
if manager is None:
|
|
87
|
+
return []
|
|
88
|
+
wmap = getattr(manager, "metadata_keys_direct_map_write", None)
|
|
89
|
+
if not wmap:
|
|
90
|
+
return []
|
|
91
|
+
return sorted(ukey.value for ukey, raw_key in wmap.items() if raw_key is not None)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_supported_unified_metadata_field_ids(file: PublicFileType) -> list[str]:
|
|
95
|
+
"""Return unified field ids writable to the file's primary (native) metadata format.
|
|
96
|
+
|
|
97
|
+
Keys are :attr:`UnifiedMetadataKey.value` strings. Fields with no write mapping
|
|
98
|
+
for that format (``None`` in the manager's write map) are omitted.
|
|
99
|
+
"""
|
|
100
|
+
audio_file = _AudioFile(file)
|
|
101
|
+
available_formats = MetadataFormat.get_priorities().get(audio_file.file_extension, [])
|
|
102
|
+
if not available_formats:
|
|
103
|
+
return []
|
|
104
|
+
target_format = available_formats[0]
|
|
105
|
+
manager = _get_metadata_manager(
|
|
106
|
+
audio_file=audio_file,
|
|
107
|
+
metadata_format=target_format,
|
|
108
|
+
normalized_rating_max_value=None,
|
|
109
|
+
id3v2_version=None,
|
|
110
|
+
)
|
|
111
|
+
return _supported_unified_metadata_field_ids_from_manager(manager)
|
|
112
|
+
|
|
113
|
+
|
|
82
114
|
def _get_metadata_manager(
|
|
83
115
|
audio_file: _AudioFile,
|
|
84
116
|
metadata_format: MetadataFormat | None = None,
|
|
@@ -1290,7 +1322,18 @@ def get_full_metadata(
|
|
|
1290
1322
|
TRAKTOR4). If False (default), such content is replaced by size placeholders.
|
|
1291
1323
|
|
|
1292
1324
|
Returns:
|
|
1293
|
-
Comprehensive dictionary
|
|
1325
|
+
Comprehensive dictionary. Top-level keys always include:
|
|
1326
|
+
|
|
1327
|
+
- ``unified_metadata``: Same merged view as :func:`get_unified_metadata`
|
|
1328
|
+
- ``technical_info``, ``metadata_format``, ``headers``, ``raw_metadata``: As documented below;
|
|
1329
|
+
``technical_info`` / ``headers`` may be empty dicts when disabled via parameters
|
|
1330
|
+
- ``unified_metadata_field_schema``: List of field descriptors (``id``, ``label``, ``multiple``,
|
|
1331
|
+
``value_type``, ``optional_value``) for every
|
|
1332
|
+
:class:`~audiometa.utils.unified_metadata_key.UnifiedMetadataKey`; same data as
|
|
1333
|
+
:func:`get_unified_metadata_field_schema`
|
|
1334
|
+
- ``supported_unified_metadata_field_ids``: Sorted :attr:`UnifiedMetadataKey.value` strings writable
|
|
1335
|
+
for this file's primary metadata format (see :func:`get_supported_unified_metadata_field_ids`)
|
|
1336
|
+
- ``format_priorities``: Extension, reading order, and primary writing format
|
|
1294
1337
|
|
|
1295
1338
|
Raises:
|
|
1296
1339
|
FileTypeNotSupportedError: If the file format is not supported
|
|
@@ -1315,6 +1358,10 @@ def get_full_metadata(
|
|
|
1315
1358
|
# Access header information
|
|
1316
1359
|
print(f"ID3v2 Version: {full_metadata['headers']['id3v2']['version']}")
|
|
1317
1360
|
print(f"Has ID3v1 Header: {full_metadata['headers']['id3v1']['present']}")
|
|
1361
|
+
|
|
1362
|
+
# Full unified key schema; writable field ids for this file's primary format
|
|
1363
|
+
print(len(full_metadata['unified_metadata_field_schema']))
|
|
1364
|
+
print(full_metadata['supported_unified_metadata_field_ids'])
|
|
1318
1365
|
"""
|
|
1319
1366
|
audio_file = _AudioFile(file)
|
|
1320
1367
|
|
|
@@ -1324,6 +1371,9 @@ def get_full_metadata(
|
|
|
1324
1371
|
# Get file-specific format priorities
|
|
1325
1372
|
available_formats = MetadataFormat.get_priorities().get(audio_file.file_extension, [])
|
|
1326
1373
|
|
|
1374
|
+
primary_format = available_formats[0] if available_formats else None
|
|
1375
|
+
primary_manager = all_managers.get(primary_format) if primary_format is not None else None
|
|
1376
|
+
|
|
1327
1377
|
# Initialize result structure
|
|
1328
1378
|
result: dict[str, Any] = {
|
|
1329
1379
|
"unified_metadata": {},
|
|
@@ -1331,6 +1381,8 @@ def get_full_metadata(
|
|
|
1331
1381
|
"metadata_format": {},
|
|
1332
1382
|
"headers": {},
|
|
1333
1383
|
"raw_metadata": {},
|
|
1384
|
+
"unified_metadata_field_schema": get_unified_metadata_field_schema(),
|
|
1385
|
+
"supported_unified_metadata_field_ids": _supported_unified_metadata_field_ids_from_manager(primary_manager),
|
|
1334
1386
|
"format_priorities": {
|
|
1335
1387
|
"file_extension": audio_file.file_extension,
|
|
1336
1388
|
"reading_order": [fmt.value for fmt in available_formats],
|
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from audiometa import get_full_metadata
|
|
7
|
+
from audiometa import UnifiedMetadataKey, get_full_metadata, get_unified_metadata_field_schema
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@pytest.mark.integration
|
|
@@ -20,6 +20,8 @@ class TestGetFullMetadata:
|
|
|
20
20
|
assert "headers" in result
|
|
21
21
|
assert "raw_metadata" in result
|
|
22
22
|
assert "format_priorities" in result
|
|
23
|
+
assert "unified_metadata_field_schema" in result
|
|
24
|
+
assert "supported_unified_metadata_field_ids" in result
|
|
23
25
|
|
|
24
26
|
# Check that each section is a dictionary
|
|
25
27
|
assert isinstance(result["unified_metadata"], dict)
|
|
@@ -28,6 +30,16 @@ class TestGetFullMetadata:
|
|
|
28
30
|
assert isinstance(result["headers"], dict)
|
|
29
31
|
assert isinstance(result["raw_metadata"], dict)
|
|
30
32
|
assert isinstance(result["format_priorities"], dict)
|
|
33
|
+
schema = result["unified_metadata_field_schema"]
|
|
34
|
+
assert isinstance(schema, list)
|
|
35
|
+
assert len(schema) == len(UnifiedMetadataKey)
|
|
36
|
+
assert {item["id"] for item in schema} == {k.value for k in UnifiedMetadataKey}
|
|
37
|
+
supported = result["supported_unified_metadata_field_ids"]
|
|
38
|
+
assert isinstance(supported, list)
|
|
39
|
+
assert all(isinstance(x, str) for x in supported)
|
|
40
|
+
assert supported == sorted(supported)
|
|
41
|
+
assert set(supported) <= {k.value for k in UnifiedMetadataKey}
|
|
42
|
+
assert schema == get_unified_metadata_field_schema()
|
|
31
43
|
|
|
32
44
|
def test_get_full_metadata_format_priorities_structure(self, sample_mp3_file: Path):
|
|
33
45
|
"""Test that format_priorities has the expected structure."""
|
audiometa_python-1.4.0/audiometa/test/tests/unit/test_get_supported_unified_metadata_field_ids.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Tests for get_supported_unified_metadata_field_ids."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from audiometa import FileTypeNotSupportedError, get_full_metadata, get_supported_unified_metadata_field_ids
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.unit
|
|
11
|
+
class TestGetSupportedUnifiedMetadataFieldIds:
|
|
12
|
+
@pytest.mark.parametrize(
|
|
13
|
+
"fixture_name",
|
|
14
|
+
[
|
|
15
|
+
pytest.param("sample_mp3_file", id="mp3"),
|
|
16
|
+
pytest.param("sample_flac_file", id="flac"),
|
|
17
|
+
pytest.param("sample_wav_file", id="wav"),
|
|
18
|
+
],
|
|
19
|
+
)
|
|
20
|
+
def test_returns_sorted_ids(self, fixture_name: str, request: pytest.FixtureRequest) -> None:
|
|
21
|
+
path: Path = request.getfixturevalue(fixture_name)
|
|
22
|
+
supported = get_supported_unified_metadata_field_ids(path)
|
|
23
|
+
assert supported == sorted(supported)
|
|
24
|
+
|
|
25
|
+
@pytest.mark.parametrize(
|
|
26
|
+
"fixture_name",
|
|
27
|
+
[
|
|
28
|
+
pytest.param("sample_mp3_file", id="mp3"),
|
|
29
|
+
pytest.param("sample_flac_file", id="flac"),
|
|
30
|
+
pytest.param("sample_wav_file", id="wav"),
|
|
31
|
+
],
|
|
32
|
+
)
|
|
33
|
+
def test_matches_get_full_metadata(self, fixture_name: str, request: pytest.FixtureRequest) -> None:
|
|
34
|
+
path: Path = request.getfixturevalue(fixture_name)
|
|
35
|
+
direct = get_supported_unified_metadata_field_ids(path)
|
|
36
|
+
from_full = get_full_metadata(path)["supported_unified_metadata_field_ids"]
|
|
37
|
+
assert direct == from_full
|
|
38
|
+
|
|
39
|
+
def test_accepts_str_path(self, sample_mp3_file: Path) -> None:
|
|
40
|
+
a = get_supported_unified_metadata_field_ids(str(sample_mp3_file))
|
|
41
|
+
b = get_supported_unified_metadata_field_ids(sample_mp3_file)
|
|
42
|
+
assert a == b
|
|
43
|
+
|
|
44
|
+
def test_unsupported_extension_raises(self, tmp_path: Path) -> None:
|
|
45
|
+
p = tmp_path / "x.txt"
|
|
46
|
+
p.write_bytes(b"not audio")
|
|
47
|
+
with pytest.raises(FileTypeNotSupportedError):
|
|
48
|
+
get_supported_unified_metadata_field_ids(p)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Tests for unified metadata field schema helpers."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from audiometa import UnifiedMetadataKey, get_unified_metadata_field_schema
|
|
6
|
+
from audiometa.utils.unified_metadata_field_schema import describe_unified_metadata_field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _expected_value_shape(key: UnifiedMetadataKey) -> tuple[str, bool, bool]:
|
|
10
|
+
if key.can_semantically_have_multiple_values():
|
|
11
|
+
return ("strings", True, False)
|
|
12
|
+
if key == UnifiedMetadataKey.TRACK_NUMBER:
|
|
13
|
+
return ("string_or_integer", False, False)
|
|
14
|
+
if key == UnifiedMetadataKey.DISC_TOTAL:
|
|
15
|
+
return ("integer", False, True)
|
|
16
|
+
if key == UnifiedMetadataKey.RATING:
|
|
17
|
+
return ("number", False, False)
|
|
18
|
+
opt = key.get_optional_type()
|
|
19
|
+
if opt is str:
|
|
20
|
+
return ("string", False, False)
|
|
21
|
+
if opt is int:
|
|
22
|
+
return ("integer", False, False)
|
|
23
|
+
return ("string", False, False)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.unit
|
|
27
|
+
class TestUnifiedMetadataFieldSchema:
|
|
28
|
+
def test_schema_covers_all_enum_members(self) -> None:
|
|
29
|
+
schema = get_unified_metadata_field_schema()
|
|
30
|
+
ids = {entry["id"] for entry in schema}
|
|
31
|
+
assert ids == {k.value for k in UnifiedMetadataKey}
|
|
32
|
+
|
|
33
|
+
def test_each_descriptor_keys_and_id_match_enum(self) -> None:
|
|
34
|
+
required = {"id", "label", "multiple", "value_type", "optional_value"}
|
|
35
|
+
for key in UnifiedMetadataKey:
|
|
36
|
+
d = describe_unified_metadata_field(key)
|
|
37
|
+
assert set(d.keys()) == required
|
|
38
|
+
assert d["id"] == key.value
|
|
39
|
+
assert isinstance(d["label"], str)
|
|
40
|
+
assert d["label"]
|
|
41
|
+
assert isinstance(d["multiple"], bool)
|
|
42
|
+
assert isinstance(d["value_type"], str)
|
|
43
|
+
assert d["value_type"]
|
|
44
|
+
assert isinstance(d["optional_value"], bool)
|
|
45
|
+
|
|
46
|
+
def test_each_descriptor_value_shape_matches_contract(self) -> None:
|
|
47
|
+
for key in UnifiedMetadataKey:
|
|
48
|
+
d = describe_unified_metadata_field(key)
|
|
49
|
+
vt, mult, optv = _expected_value_shape(key)
|
|
50
|
+
assert d["value_type"] == vt
|
|
51
|
+
assert d["multiple"] is mult
|
|
52
|
+
assert d["optional_value"] is optv
|
|
53
|
+
|
|
54
|
+
def test_describe_title_scalar_string(self) -> None:
|
|
55
|
+
d = describe_unified_metadata_field(UnifiedMetadataKey.TITLE)
|
|
56
|
+
assert d["id"] == "title"
|
|
57
|
+
assert d["label"] == "Title"
|
|
58
|
+
assert d["multiple"] is False
|
|
59
|
+
assert d["value_type"] == "string"
|
|
60
|
+
assert d["optional_value"] is False
|
|
61
|
+
|
|
62
|
+
def test_describe_artists_multi_strings(self) -> None:
|
|
63
|
+
d = describe_unified_metadata_field(UnifiedMetadataKey.ARTISTS)
|
|
64
|
+
assert d["multiple"] is True
|
|
65
|
+
assert d["value_type"] == "strings"
|
|
66
|
+
|
|
67
|
+
def test_describe_track_number_string_or_integer(self) -> None:
|
|
68
|
+
d = describe_unified_metadata_field(UnifiedMetadataKey.TRACK_NUMBER)
|
|
69
|
+
assert d["value_type"] == "string_or_integer"
|
|
70
|
+
assert d["multiple"] is False
|
|
71
|
+
assert d["optional_value"] is False
|
|
72
|
+
|
|
73
|
+
def test_describe_disc_total_integer_optional_value(self) -> None:
|
|
74
|
+
d = describe_unified_metadata_field(UnifiedMetadataKey.DISC_TOTAL)
|
|
75
|
+
assert d["value_type"] == "integer"
|
|
76
|
+
assert d["optional_value"] is True
|
|
77
|
+
assert d["multiple"] is False
|
|
78
|
+
|
|
79
|
+
def test_describe_rating_number(self) -> None:
|
|
80
|
+
d = describe_unified_metadata_field(UnifiedMetadataKey.RATING)
|
|
81
|
+
assert d["value_type"] == "number"
|
|
82
|
+
assert d["multiple"] is False
|
|
83
|
+
assert d["optional_value"] is False
|
|
84
|
+
|
|
85
|
+
def test_describe_bpm_integer_scalar(self) -> None:
|
|
86
|
+
d = describe_unified_metadata_field(UnifiedMetadataKey.BPM)
|
|
87
|
+
assert d["value_type"] == "integer"
|
|
88
|
+
assert d["optional_value"] is False
|
|
@@ -71,6 +71,16 @@ class RawMetadataInfo(TypedDict):
|
|
|
71
71
|
chunk_structure: dict[str, Any]
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
class UnifiedMetadataFieldDescriptor(TypedDict):
|
|
75
|
+
"""Single entry from get_unified_metadata_field_schema()."""
|
|
76
|
+
|
|
77
|
+
id: str
|
|
78
|
+
label: str
|
|
79
|
+
multiple: bool
|
|
80
|
+
value_type: str
|
|
81
|
+
optional_value: bool
|
|
82
|
+
|
|
83
|
+
|
|
74
84
|
class FullMetadata(TypedDict):
|
|
75
85
|
"""Type for comprehensive metadata returned by get_full_metadata."""
|
|
76
86
|
|
|
@@ -79,4 +89,6 @@ class FullMetadata(TypedDict):
|
|
|
79
89
|
metadata_format: dict[str, UnifiedMetadata]
|
|
80
90
|
headers: dict[str, HeaderInfo]
|
|
81
91
|
raw_metadata: dict[str, RawMetadataInfo]
|
|
92
|
+
unified_metadata_field_schema: list[UnifiedMetadataFieldDescriptor]
|
|
93
|
+
supported_unified_metadata_field_ids: list[str]
|
|
82
94
|
format_priorities: FormatPriorities
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Wire-oriented schema for unified metadata fields (API / UI).
|
|
2
|
+
|
|
3
|
+
Describes every :class:`UnifiedMetadataKey` with stable ids (enum values), human labels,
|
|
4
|
+
and value shape hints. Format-specific mapping stays in metadata managers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
|
|
12
|
+
|
|
13
|
+
# Stable English labels for clients that do not localize.
|
|
14
|
+
_UNIFIED_METADATA_LABELS: dict[UnifiedMetadataKey, str] = {
|
|
15
|
+
UnifiedMetadataKey.TITLE: "Title",
|
|
16
|
+
UnifiedMetadataKey.ARTISTS: "Artists",
|
|
17
|
+
UnifiedMetadataKey.ALBUM: "Album",
|
|
18
|
+
UnifiedMetadataKey.ALBUM_ARTISTS: "Album artists",
|
|
19
|
+
UnifiedMetadataKey.GENRES_NAMES: "Genres",
|
|
20
|
+
UnifiedMetadataKey.RATING: "Rating",
|
|
21
|
+
UnifiedMetadataKey.LANGUAGE: "Language",
|
|
22
|
+
UnifiedMetadataKey.RELEASE_DATE: "Release date",
|
|
23
|
+
UnifiedMetadataKey.TRACK_NUMBER: "Track number",
|
|
24
|
+
UnifiedMetadataKey.DISC_NUMBER: "Disc number",
|
|
25
|
+
UnifiedMetadataKey.DISC_TOTAL: "Total discs",
|
|
26
|
+
UnifiedMetadataKey.BPM: "BPM",
|
|
27
|
+
UnifiedMetadataKey.COMPOSERS: "Composers",
|
|
28
|
+
UnifiedMetadataKey.PUBLISHER: "Publisher",
|
|
29
|
+
UnifiedMetadataKey.COPYRIGHT: "Copyright",
|
|
30
|
+
UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS: "Lyrics (unsynchronized)",
|
|
31
|
+
UnifiedMetadataKey.COMMENT: "Comment",
|
|
32
|
+
UnifiedMetadataKey.REPLAYGAIN: "ReplayGain",
|
|
33
|
+
UnifiedMetadataKey.ARCHIVAL_LOCATION: "Archival location",
|
|
34
|
+
UnifiedMetadataKey.ISRC: "ISRC",
|
|
35
|
+
UnifiedMetadataKey.MUSICBRAINZ_TRACKID: "MusicBrainz recording ID",
|
|
36
|
+
UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS: "MusicBrainz artist IDs",
|
|
37
|
+
UnifiedMetadataKey.DESCRIPTION: "Description",
|
|
38
|
+
UnifiedMetadataKey.ORIGINATOR: "Originator",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# value_type is a coarse JSON-oriented hint for forms.
|
|
42
|
+
# - strings: list of strings
|
|
43
|
+
# - string, integer, number: scalar
|
|
44
|
+
# - string_or_integer: track number (library accepts int or str on write)
|
|
45
|
+
# integer with optional_value=True: may be null (e.g. disc total)
|
|
46
|
+
_VALUE_SHAPE: dict[UnifiedMetadataKey, tuple[str, bool, bool]] = {}
|
|
47
|
+
for _k in UnifiedMetadataKey:
|
|
48
|
+
if _k.can_semantically_have_multiple_values():
|
|
49
|
+
_VALUE_SHAPE[_k] = ("strings", True, False)
|
|
50
|
+
elif _k == UnifiedMetadataKey.TRACK_NUMBER:
|
|
51
|
+
_VALUE_SHAPE[_k] = ("string_or_integer", False, False)
|
|
52
|
+
elif _k == UnifiedMetadataKey.DISC_TOTAL:
|
|
53
|
+
_VALUE_SHAPE[_k] = ("integer", False, True)
|
|
54
|
+
elif _k == UnifiedMetadataKey.RATING:
|
|
55
|
+
_VALUE_SHAPE[_k] = ("number", False, False)
|
|
56
|
+
else:
|
|
57
|
+
_opt = _k.get_optional_type()
|
|
58
|
+
if _opt is str:
|
|
59
|
+
_VALUE_SHAPE[_k] = ("string", False, False)
|
|
60
|
+
elif _opt is int:
|
|
61
|
+
_VALUE_SHAPE[_k] = ("integer", False, False)
|
|
62
|
+
else:
|
|
63
|
+
_VALUE_SHAPE[_k] = ("string", False, False)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def describe_unified_metadata_field(key: UnifiedMetadataKey) -> dict[str, Any]:
|
|
67
|
+
"""Single field descriptor for APIs."""
|
|
68
|
+
value_type, multiple, optional_value = _VALUE_SHAPE[key]
|
|
69
|
+
return {
|
|
70
|
+
"id": key.value,
|
|
71
|
+
"label": _UNIFIED_METADATA_LABELS[key],
|
|
72
|
+
"multiple": multiple,
|
|
73
|
+
"value_type": value_type,
|
|
74
|
+
"optional_value": optional_value,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_unified_metadata_field_schema() -> list[dict[str, Any]]:
|
|
79
|
+
"""Return descriptors for all unified metadata keys (library vocabulary)."""
|
|
80
|
+
return [describe_unified_metadata_field(k) for k in UnifiedMetadataKey]
|