audiometa-python 0.6.0__tar.gz → 0.6.1__tar.gz

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