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

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