audiometa-python 1.3.2__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.
Files changed (424) hide show
  1. {audiometa_python-1.3.2/audiometa_python.egg-info → audiometa_python-1.4.0}/PKG-INFO +44 -4
  2. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/README.md +42 -2
  3. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/__init__.py +53 -1
  4. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_MetadataManager.py +5 -0
  5. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +3 -12
  6. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +48 -0
  7. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +13 -1
  8. audiometa_python-1.4.0/audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +191 -0
  9. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +28 -15
  10. audiometa_python-1.4.0/audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +76 -0
  11. audiometa_python-1.4.0/audiometa/test/tests/unit/test_get_supported_unified_metadata_field_ids.py +48 -0
  12. audiometa_python-1.4.0/audiometa/test/tests/unit/utils/test_disc_number_read.py +66 -0
  13. audiometa_python-1.4.0/audiometa/test/tests/unit/utils/test_unified_metadata_field_schema.py +88 -0
  14. audiometa_python-1.4.0/audiometa/utils/disc_number_read.py +30 -0
  15. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/types.py +12 -0
  16. audiometa_python-1.4.0/audiometa/utils/unified_metadata_field_schema.py +80 -0
  17. {audiometa_python-1.3.2 → audiometa_python-1.4.0/audiometa_python.egg-info}/PKG-INFO +44 -4
  18. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa_python.egg-info/SOURCES.txt +5 -0
  19. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/pyproject.toml +2 -2
  20. audiometa_python-1.3.2/audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +0 -92
  21. audiometa_python-1.3.2/audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +0 -32
  22. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/LICENSE +0 -0
  23. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/__main__.py +0 -0
  24. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/_audio_file.py +0 -0
  25. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/cli.py +0 -0
  26. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/exceptions.py +0 -0
  27. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/__init__.py +0 -0
  28. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +0 -0
  29. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/__init__.py +0 -0
  30. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/__init__.py +0 -0
  31. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v1_preserver.py +0 -0
  32. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +0 -0
  33. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_flac_handler.py +0 -0
  34. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_reader.py +0 -0
  35. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_writer.py +0 -0
  36. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_RiffManager.py +0 -0
  37. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/__init__.py +0 -0
  38. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_bext_chunk.py +0 -0
  39. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_constants.py +0 -0
  40. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_file_structure.py +0 -0
  41. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/riff/_riff_info_chunk.py +0 -0
  42. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/vorbis/__init__.py +0 -0
  43. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +0 -0
  44. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/id3v1/_Id3v1Manager.py +0 -0
  45. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/id3v1/__init__.py +0 -0
  46. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/id3v1/_constants.py +0 -0
  47. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/id3v1/id3v1_raw_metadata.py +0 -0
  48. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/manager/id3v1/id3v1_raw_metadata_key.py +0 -0
  49. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/__init__.py +0 -0
  50. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/assets/create_test_files.py +0 -0
  51. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/__init__.py +0 -0
  52. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/common/__init__.py +0 -0
  53. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/common/audio_file_creator.py +0 -0
  54. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/common/external_tool_runner.py +0 -0
  55. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/__init__.py +0 -0
  56. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_header_verifier.py +0 -0
  57. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +0 -0
  58. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +0 -0
  59. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +0 -0
  60. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/__init__.py +0 -0
  61. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +0 -0
  62. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_header_verifier.py +0 -0
  63. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +0 -0
  64. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +0 -0
  65. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +0 -0
  66. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/__init__.py +0 -0
  67. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_header_verifier.py +0 -0
  68. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_manual_metadata_creator.py +0 -0
  69. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_metadata_deleter.py +0 -0
  70. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_metadata_getter.py +0 -0
  71. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/riff/riff_metadata_setter.py +0 -0
  72. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/scripts/__init__.py +0 -0
  73. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/technical_info_inspector.py +0 -0
  74. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/temp_file_with_metadata.py +0 -0
  75. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/__init__.py +0 -0
  76. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_header_verifier.py +0 -0
  77. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +0 -0
  78. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +0 -0
  79. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +0 -0
  80. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/__init__.py +0 -0
  81. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/conftest.py +0 -0
  82. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/__init__.py +0 -0
  83. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/__init__.py +0 -0
  84. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/__init__.py +0 -0
  85. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +0 -0
  86. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +0 -0
  87. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +0 -0
  88. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +0 -0
  89. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +0 -0
  90. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +0 -0
  91. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +0 -0
  92. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +0 -0
  93. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
  94. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_basic.py +0 -0
  95. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_comprehensive.py +0 -0
  96. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_formats.py +0 -0
  97. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_metadata_content.py +0 -0
  98. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_multiple_files.py +0 -0
  99. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_options.py +0 -0
  100. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/read/test_unified.py +0 -0
  101. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/test_delete.py +0 -0
  102. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/test_formatting.py +0 -0
  103. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/test_help.py +0 -0
  104. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
  105. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_basic.py +0 -0
  106. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_comprehensive.py +0 -0
  107. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_force_format.py +0 -0
  108. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_integer_fields.py +0 -0
  109. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_list_fields.py +0 -0
  110. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_rating.py +0 -0
  111. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_string_fields.py +0 -0
  112. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/cli/write/test_validation.py +0 -0
  113. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
  114. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +0 -0
  115. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/__init__.py +0 -0
  116. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_core_workflows.py +0 -0
  117. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +0 -0
  118. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +0 -0
  119. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +0 -0
  120. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/e2e/workflows/test_rating_workflows.py +0 -0
  121. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/__init__.py +0 -0
  122. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/__init__.py +0 -0
  123. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
  124. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +0 -0
  125. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +0 -0
  126. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +0 -0
  127. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +0 -0
  128. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
  129. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +0 -0
  130. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +0 -0
  131. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +0 -0
  132. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +0 -0
  133. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
  134. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +0 -0
  135. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +0 -0
  136. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +0 -0
  137. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +0 -0
  138. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/conftest.py +0 -0
  139. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/__init__.py +0 -0
  140. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +0 -0
  141. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +0 -0
  142. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +0 -0
  143. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +0 -0
  144. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/encoding/__init__.py +0 -0
  145. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/encoding/test_encoding.py +0 -0
  146. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +0 -0
  147. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
  148. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/options/__init__.py +0 -0
  149. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/options/test_include_headers.py +0 -0
  150. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/options/test_include_technical.py +0 -0
  151. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +0 -0
  152. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +0 -0
  153. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_consistency.py +0 -0
  154. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +0 -0
  155. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +0 -0
  156. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_performance.py +0 -0
  157. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_raw_metadata_includes_unsupported_tags.py +0 -0
  158. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +0 -0
  159. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/get_full_metadata/test_structure.py +0 -0
  160. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
  161. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
  162. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/test_deleting.py +0 -0
  163. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/test_reading.py +0 -0
  164. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album/test_writing.py +0 -0
  165. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
  166. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +0 -0
  167. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +0 -0
  168. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +0 -0
  169. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
  170. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +0 -0
  171. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/test_reading.py +0 -0
  172. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/artists/test_writing.py +0 -0
  173. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
  174. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +0 -0
  175. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +0 -0
  176. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +0 -0
  177. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
  178. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +0 -0
  179. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/test_reading.py +0 -0
  180. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/comment/test_writing.py +0 -0
  181. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
  182. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +0 -0
  183. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/test_reading.py +0 -0
  184. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/composer/test_writing.py +0 -0
  185. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
  186. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +0 -0
  187. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +0 -0
  188. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +0 -0
  189. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/__init__.py +0 -0
  190. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/test_deleting.py +0 -0
  191. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/test_reading.py +0 -0
  192. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/description/test_writing.py +0 -0
  193. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
  194. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +0 -0
  195. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +0 -0
  196. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
  197. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +0 -0
  198. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +0 -0
  199. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +0 -0
  200. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
  201. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
  202. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
  203. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +0 -0
  204. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +0 -0
  205. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +0 -0
  206. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +0 -0
  207. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +0 -0
  208. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +0 -0
  209. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/genre/test_writing.py +0 -0
  210. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/__init__.py +0 -0
  211. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +0 -0
  212. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +0 -0
  213. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +0 -0
  214. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
  215. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/test_deleting.py +0 -0
  216. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/test_reading.py +0 -0
  217. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/language/test_writing.py +0 -0
  218. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
  219. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +0 -0
  220. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +0 -0
  221. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +0 -0
  222. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/__init__.py +0 -0
  223. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_deleting.py +0 -0
  224. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_reading.py +0 -0
  225. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_writing.py +0 -0
  226. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/__init__.py +0 -0
  227. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_deleting.py +0 -0
  228. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_reading.py +0 -0
  229. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_writing.py +0 -0
  230. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/__init__.py +0 -0
  231. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/test_deleting.py +0 -0
  232. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/test_reading.py +0 -0
  233. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/originator/test_writing.py +0 -0
  234. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
  235. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +0 -0
  236. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +0 -0
  237. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +0 -0
  238. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
  239. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
  240. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +0 -0
  241. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +0 -0
  242. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +0 -0
  243. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +0 -0
  244. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +0 -0
  245. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
  246. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
  247. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +0 -0
  248. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +0 -0
  249. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +0 -0
  250. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +0 -0
  251. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
  252. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +0 -0
  253. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +0 -0
  254. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +0 -0
  255. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +0 -0
  256. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +0 -0
  257. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
  258. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_deleting.py +0 -0
  259. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +0 -0
  260. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_reading.py +0 -0
  261. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/title/test_writing.py +0 -0
  262. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
  263. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
  264. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +0 -0
  265. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +0 -0
  266. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/__init__.py +0 -0
  267. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/__init__.py +0 -0
  268. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +0 -0
  269. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +0 -0
  270. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +0 -0
  271. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +0 -0
  272. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +0 -0
  273. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +0 -0
  274. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +0 -0
  275. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +0 -0
  276. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +0 -0
  277. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/__init__.py +0 -0
  278. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +0 -0
  279. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +0 -0
  280. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +0 -0
  281. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +0 -0
  282. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +0 -0
  283. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +0 -0
  284. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +0 -0
  285. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +0 -0
  286. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/reading/__init__.py +0 -0
  287. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +0 -0
  288. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/reading/test_reading_error_handling.py +0 -0
  289. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
  290. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/real_audio_files/test_reading.py +0 -0
  291. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/real_audio_files/test_writing.py +0 -0
  292. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/__init__.py +0 -0
  293. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
  294. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +0 -0
  295. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/__init__.py +0 -0
  296. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_audio_data_corruption.py +0 -0
  297. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_file.py +0 -0
  298. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_invalid_md5.py +0 -0
  299. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/__init__.py +0 -0
  300. {audiometa_python-1.3.2 → 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
  301. {audiometa_python-1.3.2 → 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
  302. {audiometa_python-1.3.2 → 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
  303. {audiometa_python-1.3.2 → 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
  304. {audiometa_python-1.3.2 → 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
  305. {audiometa_python-1.3.2 → 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
  306. {audiometa_python-1.3.2 → 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
  307. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_unset_md5.py +0 -0
  308. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/__init__.py +0 -0
  309. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_delete_original.py +0 -0
  310. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/__init__.py +0 -0
  311. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_flipped_md5.py +0 -0
  312. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_partial_md5.py +0 -0
  313. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_random_md5.py +0 -0
  314. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_md5_repair_with_metadata.py +0 -0
  315. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_non_flac_error.py +0 -0
  316. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_unset_md5.py +0 -0
  317. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_bitrate.py +0 -0
  318. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_channels.py +0 -0
  319. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +0 -0
  320. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/technical_info/test_sample_rate.py +0 -0
  321. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/test_audio_file.py +0 -0
  322. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +0 -0
  323. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/__init__.py +0 -0
  324. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_error_handling.py +0 -0
  325. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_forced_format.py +0 -0
  326. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +0 -0
  327. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/test_partial_update.py +0 -0
  328. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
  329. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/__init__.py +0 -0
  330. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_flac_sync.py +0 -0
  331. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_mp3_sync.py +0 -0
  332. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_wav_sync.py +0 -0
  333. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +0 -0
  334. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +0 -0
  335. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
  336. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +0 -0
  337. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +0 -0
  338. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +0 -0
  339. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/__init__.py +0 -0
  340. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/__init__.py +0 -0
  341. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
  342. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +0 -0
  343. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +0 -0
  344. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +0 -0
  345. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +0 -0
  346. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +0 -0
  347. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +0 -0
  348. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +0 -0
  349. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_context_manager.py +0 -0
  350. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_file_validation.py +0 -0
  351. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_is_audio_file.py +0 -0
  352. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_operations.py +0 -0
  353. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/audio_file/test_path_handling.py +0 -0
  354. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/cli/__init__.py +0 -0
  355. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/cli/test_cli_colorize.py +0 -0
  356. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/cli/test_expand_file_patterns.py +0 -0
  357. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
  358. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/conftest.py +0 -0
  359. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
  360. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +0 -0
  361. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +0 -0
  362. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +0 -0
  363. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff_info_chunk_fourcc.py +0 -0
  364. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +0 -0
  365. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
  366. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/disc_number/__init__.py +0 -0
  367. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/disc_number/test_disc_number_validation.py +0 -0
  368. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/__init__.py +0 -0
  369. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/test_isrc_format_validation.py +0 -0
  370. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/test_isrc_type_validation.py +0 -0
  371. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
  372. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
  373. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +0 -0
  374. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
  375. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +0 -0
  376. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +0 -0
  377. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/__init__.py +0 -0
  378. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/test_musicbrainz_artistid_format_validation.py +0 -0
  379. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/test_musicbrainz_artistid_type_validation.py +0 -0
  380. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/__init__.py +0 -0
  381. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/test_musicbrainz_trackid_format_validation.py +0 -0
  382. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/test_musicbrainz_trackid_type_validation.py +0 -0
  383. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_uuid/__init__.py +0 -0
  384. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_uuid/test_uuid_format_validation.py +0 -0
  385. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
  386. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
  387. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +0 -0
  388. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +0 -0
  389. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +0 -0
  390. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
  391. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +0 -0
  392. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +0 -0
  393. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +0 -0
  394. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/release_date/__init__.py +0 -0
  395. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/release_date/test_date_format_validation.py +0 -0
  396. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +0 -0
  397. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +0 -0
  398. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/track_number/__init__.py +0 -0
  399. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/track_number/test_track_number_validation.py +0 -0
  400. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +0 -0
  401. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +0 -0
  402. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/utils/__init__.py +0 -0
  403. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/test/tests/unit/utils/test_raw_metadata_sanitizer.py +0 -0
  404. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/__init__.py +0 -0
  405. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/flac_md5_state.py +0 -0
  406. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/id3v1_genre_code_map.py +0 -0
  407. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/metadata_format.py +0 -0
  408. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/metadata_writing_strategy.py +0 -0
  409. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/mutagen_exception_handler.py +0 -0
  410. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/__init__.py +0 -0
  411. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/base.py +0 -0
  412. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/config.py +0 -0
  413. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/macos.py +0 -0
  414. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/ubuntu.py +0 -0
  415. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/os_dependencies_checker/windows.py +0 -0
  416. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/rating_profiles.py +0 -0
  417. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/raw_metadata_sanitizer.py +0 -0
  418. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/tool_path_resolver.py +0 -0
  419. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa/utils/unified_metadata_key.py +0 -0
  420. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa_python.egg-info/dependency_links.txt +0 -0
  421. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa_python.egg-info/entry_points.txt +0 -0
  422. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa_python.egg-info/requires.txt +0 -0
  423. {audiometa_python-1.3.2 → audiometa_python-1.4.0}/audiometa_python.egg-info/top_level.txt +0 -0
  424. {audiometa_python-1.3.2 → 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.2
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 :: 3 - Alpha
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, there are three functions to use: `get_unified_metadata` and `get_unified_metadata_field`, and `get_full_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 technical info.
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, there are three functions to use: `get_unified_metadata` and `get_unified_metadata_field`, and `get_full_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 technical info.
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 containing all available metadata and technical information
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],
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, TypeVar, Union, cast
5
5
  from mutagen._file import FileType as MutagenMetadata
6
6
 
7
7
  from audiometa.exceptions import InvalidMetadataFieldFormatError, InvalidMetadataFieldTypeError
8
+ from audiometa.utils.disc_number_read import parse_disc_number_from_combined_str
8
9
  from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
10
 
10
11
  if TYPE_CHECKING:
@@ -698,6 +699,10 @@ class _MetadataManager:
698
699
  return track_str
699
700
  return None
700
701
 
702
+ # DISCNUMBER / TPOS-style "n", "n/m", "n-m" (formats using the base read path)
703
+ if unified_metadata_key == UnifiedMetadataKey.DISC_NUMBER:
704
+ return parse_disc_number_from_combined_str(str(value[0]))
705
+
701
706
  from typing import get_args, get_origin
702
707
 
703
708
  origin = get_origin(unified_metadata_key_optional_type)
@@ -30,6 +30,7 @@ from mutagen.id3._frames import (
30
30
  )
31
31
  from mutagen.id3._util import ID3NoHeaderError
32
32
 
33
+ from audiometa.utils.disc_number_read import parse_disc_number_from_combined_str, parse_disc_total_from_combined_str
33
34
  from audiometa.utils.raw_metadata_sanitizer import sanitize_id3v2_raw_info
34
35
  from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
35
36
 
@@ -415,12 +416,7 @@ class _Id3v2Manager(_RatingSupportingMetadataManager):
415
416
  if tpos_value is None or len(tpos_value) == 0:
416
417
  return None
417
418
  tpos_str = str(tpos_value[0])
418
- import re
419
-
420
- match = re.match(r"^(\d+)(?:/(\d+))?$", tpos_str)
421
- if match:
422
- return int(match.group(1))
423
- return None
419
+ return parse_disc_number_from_combined_str(tpos_str)
424
420
  if unified_metadata_key == UnifiedMetadataKey.DISC_TOTAL:
425
421
  tpos_key = self.Id3TextFrame.DISC_NUMBER
426
422
  if tpos_key not in raw_clean_metadata:
@@ -429,12 +425,7 @@ class _Id3v2Manager(_RatingSupportingMetadataManager):
429
425
  if tpos_value is None or len(tpos_value) == 0:
430
426
  return None
431
427
  tpos_str = str(tpos_value[0])
432
- import re
433
-
434
- match = re.match(r"^(\d+)/(\d+)$", tpos_str)
435
- if match:
436
- return int(match.group(2))
437
- return None
428
+ return parse_disc_total_from_combined_str(tpos_str)
438
429
  msg = f"Metadata key not handled: {unified_metadata_key}"
439
430
  raise MetadataFieldNotSupportedByMetadataFormatError(msg)
440
431
 
@@ -6,6 +6,11 @@ from typing import TYPE_CHECKING, Any, TypeVar, cast
6
6
  if TYPE_CHECKING:
7
7
  from ...._audio_file import _AudioFile
8
8
  from ....exceptions import FileCorruptedError, InvalidRatingValueError, MetadataFieldNotSupportedByMetadataFormatError
9
+ from ....utils.disc_number_read import (
10
+ parse_disc_number_from_combined_str,
11
+ parse_disc_total_from_combined_str,
12
+ parse_explicit_non_negative_disctotal,
13
+ )
9
14
  from ....utils.rating_profiles import RatingWriteProfile
10
15
  from ....utils.raw_metadata_sanitizer import sanitize_vorbis_raw_info
11
16
  from ....utils.tool_path_resolver import get_tool_path
@@ -260,6 +265,49 @@ class _VorbisManager(_RatingSupportingMetadataManager):
260
265
  # Cast to RawMetadataDict since RawMetadataKey is str, Enum and string keys work
261
266
  self.raw_clean_metadata_uppercase_keys = cast(RawMetadataDict, result_dict)
262
267
 
268
+ def get_unified_metadata_field(self, unified_metadata_key: UnifiedMetadataKey) -> UnifiedMetadataValue:
269
+ if unified_metadata_key not in (
270
+ UnifiedMetadataKey.DISC_NUMBER,
271
+ UnifiedMetadataKey.DISC_TOTAL,
272
+ ):
273
+ return super().get_unified_metadata_field(unified_metadata_key)
274
+
275
+ if self.raw_clean_metadata_uppercase_keys is None:
276
+ self._extract_raw_clean_metadata_uppercase_keys_from_file()
277
+ keys = self.raw_clean_metadata_uppercase_keys
278
+ if keys is None:
279
+ return None
280
+
281
+ disc_number_key = self.VorbisKey.DISC_NUMBER
282
+ disc_total_key = self.VorbisKey.DISC_TOTAL
283
+
284
+ def _first_string(raw_key: RawMetadataKey) -> str | None:
285
+ if raw_key not in keys:
286
+ return None
287
+ vals = keys[raw_key]
288
+ if not vals or len(vals) == 0:
289
+ return None
290
+ v = vals[0]
291
+ if v is None or v == "":
292
+ return None
293
+ return str(v)
294
+
295
+ d = _first_string(disc_number_key)
296
+ t = _first_string(disc_total_key)
297
+
298
+ if unified_metadata_key == UnifiedMetadataKey.DISC_NUMBER:
299
+ if d is None:
300
+ return None
301
+ return parse_disc_number_from_combined_str(d)
302
+
303
+ if t is not None:
304
+ explicit = parse_explicit_non_negative_disctotal(t)
305
+ if explicit is not None:
306
+ return explicit
307
+ if d is None:
308
+ return None
309
+ return parse_disc_total_from_combined_str(d)
310
+
263
311
  def _get_raw_rating_by_traktor_or_not(self, raw_clean_metadata: RawMetadataDict) -> tuple[int | None, bool]:
264
312
  if self.VorbisKey.RATING in raw_clean_metadata:
265
313
  rating_list = raw_clean_metadata[self.VorbisKey.RATING]
@@ -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."""
@@ -0,0 +1,191 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field
4
+ from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
5
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
6
+ from audiometa.test.helpers.vorbis.vorbis_metadata_setter import VorbisMetadataSetter
7
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
8
+
9
+
10
+ @pytest.mark.integration
11
+ class TestDiscNumberReading:
12
+ def test_id3v2_with_total(self):
13
+ with temp_file_with_metadata({}, "mp3") as test_file:
14
+ ID3v2MetadataSetter.set_metadata(test_file, {"disc_number": "1/2"})
15
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
16
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
17
+ assert disc_number == 1
18
+ assert disc_total == 2
19
+
20
+ def test_id3v2_without_total(self):
21
+ with temp_file_with_metadata({}, "mp3") as test_file:
22
+ ID3v2MetadataSetter.set_metadata(test_file, {"disc_number": "1"})
23
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
24
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
25
+ assert disc_number == 1
26
+ assert disc_total is None
27
+
28
+ def test_id3v2_max_value(self):
29
+ with temp_file_with_metadata({}, "mp3") as test_file:
30
+ ID3v2MetadataSetter.set_metadata(test_file, {"disc_number": "99/99"})
31
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
32
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
33
+ assert disc_number == 99
34
+ assert disc_total == 99
35
+
36
+ def test_id3v2_hyphen_separator_read(self):
37
+ with temp_file_with_metadata({}, "mp3") as test_file:
38
+ ID3v2MetadataSetter.set_metadata(test_file, {"disc_number": "1-2"})
39
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
40
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
41
+ assert disc_number == 1
42
+ assert disc_total == 2
43
+
44
+ def test_id3v2_invalid_tpos_multiple_slashes_returns_none(self):
45
+ with temp_file_with_metadata({}, "mp3") as test_file:
46
+ ID3v2MetadataSetter.set_metadata(test_file, {"disc_number": "1/2/3"})
47
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
48
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
49
+ assert disc_number is None
50
+ assert disc_total is None
51
+
52
+ def test_id3v2_read_tpos_above_255_not_clamped(self):
53
+ with temp_file_with_metadata({}, "mp3") as test_file:
54
+ ID3v2MetadataSetter.set_metadata(test_file, {"disc_number": "256/300"})
55
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
56
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
57
+ assert disc_number == 256
58
+ assert disc_total == 300
59
+
60
+ def test_vorbis_with_total(self):
61
+ with temp_file_with_metadata({}, "flac") as test_file:
62
+ VorbisMetadataSetter.set_metadata(test_file, {"disc_number": "1", "disc_total": "2"})
63
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
64
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
65
+ assert disc_number == 1
66
+ assert disc_total == 2
67
+
68
+ def test_vorbis_without_total(self):
69
+ with temp_file_with_metadata({}, "flac") as test_file:
70
+ VorbisMetadataSetter.set_metadata(test_file, {"disc_number": "2"})
71
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
72
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
73
+ assert disc_number == 2
74
+ assert disc_total is None
75
+
76
+ def test_vorbis_disc_number_combined_slash_form(self):
77
+ with temp_file_with_metadata({}, "flac") as test_file:
78
+ VorbisMetadataSetter.set_tag(test_file, "DISCNUMBER", "3/5")
79
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
80
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
81
+ assert disc_number == 3
82
+ assert disc_total == 5
83
+
84
+ def test_vorbis_discnumber_combined_slash_via_set_metadata(self):
85
+ with temp_file_with_metadata({}, "flac") as test_file:
86
+ VorbisMetadataSetter.set_metadata(test_file, {"disc_number": "1/2"})
87
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
88
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
89
+ assert disc_number == 1
90
+ assert disc_total == 2
91
+
92
+ def test_vorbis_disc_number_hyphen_form(self):
93
+ with temp_file_with_metadata({}, "flac") as test_file:
94
+ VorbisMetadataSetter.set_tag(test_file, "DISCNUMBER", "1-2")
95
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
96
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
97
+ assert disc_number == 1
98
+ assert disc_total == 2
99
+
100
+ def test_vorbis_disctotal_overrides_embedded_total(self):
101
+ with temp_file_with_metadata({}, "flac") as test_file:
102
+ VorbisMetadataSetter.set_tag(test_file, "DISCNUMBER", "1/3")
103
+ VorbisMetadataSetter.set_tag(test_file, "DISCTOTAL", "2")
104
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
105
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
106
+ assert disc_number == 1
107
+ assert disc_total == 2
108
+
109
+ def test_vorbis_discnumber_slash_with_matching_disctotal(self):
110
+ with temp_file_with_metadata({}, "flac") as test_file:
111
+ VorbisMetadataSetter.set_metadata(test_file, {"disc_number": "1/2", "disc_total": "2"})
112
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
113
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
114
+ assert disc_number == 1
115
+ assert disc_total == 2
116
+
117
+ def test_vorbis_invalid_disctotal_falls_back_to_discnumber_slash(self):
118
+ with temp_file_with_metadata({}, "flac") as test_file:
119
+ VorbisMetadataSetter.set_tag(test_file, "DISCNUMBER", "1/2")
120
+ VorbisMetadataSetter.set_tag(test_file, "DISCTOTAL", "bogus")
121
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
122
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
123
+ assert disc_number == 1
124
+ assert disc_total == 2
125
+
126
+ def test_vorbis_negative_disctotal_falls_back_to_discnumber_slash(self):
127
+ with temp_file_with_metadata({}, "flac") as test_file:
128
+ VorbisMetadataSetter.set_tag(test_file, "DISCNUMBER", "1/2")
129
+ VorbisMetadataSetter.set_tag(test_file, "DISCTOTAL", "-1")
130
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
131
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
132
+ assert disc_number == 1
133
+ assert disc_total == 2
134
+
135
+ def test_vorbis_invalid_combined_discnumber(self):
136
+ with temp_file_with_metadata({}, "flac") as test_file:
137
+ VorbisMetadataSetter.set_tag(test_file, "DISCNUMBER", "1/2/3")
138
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
139
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
140
+ assert disc_number is None
141
+ assert disc_total is None
142
+
143
+ def test_vorbis_disctotal_only(self):
144
+ with temp_file_with_metadata({}, "flac") as test_file:
145
+ VorbisMetadataSetter.set_tag(test_file, "DISCTOTAL", "2")
146
+ disc_number = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_NUMBER)
147
+ disc_total = get_unified_metadata_field(test_file, UnifiedMetadataKey.DISC_TOTAL)
148
+ assert disc_number is None
149
+ assert disc_total == 2
150
+
151
+ def test_id3v1_not_supported(self):
152
+ from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
153
+ from audiometa.utils.metadata_format import MetadataFormat
154
+
155
+ with temp_file_with_metadata({}, "id3v1") as test_file:
156
+ with pytest.raises(
157
+ MetadataFieldNotSupportedByMetadataFormatError,
158
+ match="UnifiedMetadataKey.DISC_NUMBER metadata not supported by ID3v1 format",
159
+ ):
160
+ get_unified_metadata_field(
161
+ test_file, UnifiedMetadataKey.DISC_NUMBER, metadata_format=MetadataFormat.ID3V1
162
+ )
163
+
164
+ with pytest.raises(
165
+ MetadataFieldNotSupportedByMetadataFormatError,
166
+ match="UnifiedMetadataKey.DISC_TOTAL metadata not supported by ID3v1 format",
167
+ ):
168
+ get_unified_metadata_field(
169
+ test_file, UnifiedMetadataKey.DISC_TOTAL, metadata_format=MetadataFormat.ID3V1
170
+ )
171
+
172
+ def test_riff_not_supported(self):
173
+ from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
174
+ from audiometa.utils.metadata_format import MetadataFormat
175
+
176
+ with temp_file_with_metadata({}, "wav") as test_file:
177
+ with pytest.raises(
178
+ MetadataFieldNotSupportedByMetadataFormatError,
179
+ match="UnifiedMetadataKey.DISC_NUMBER metadata not supported by RIFF format",
180
+ ):
181
+ get_unified_metadata_field(
182
+ test_file, UnifiedMetadataKey.DISC_NUMBER, metadata_format=MetadataFormat.RIFF
183
+ )
184
+
185
+ with pytest.raises(
186
+ MetadataFieldNotSupportedByMetadataFormatError,
187
+ match="UnifiedMetadataKey.DISC_TOTAL metadata not supported by RIFF format",
188
+ ):
189
+ get_unified_metadata_field(
190
+ test_file, UnifiedMetadataKey.DISC_TOTAL, metadata_format=MetadataFormat.RIFF
191
+ )