audiometa-python 1.2.0__tar.gz → 1.3.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 (417) hide show
  1. {audiometa_python-1.2.0/audiometa_python.egg-info → audiometa_python-1.3.0}/PKG-INFO +9 -4
  2. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/README.md +5 -0
  3. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/cli.py +110 -9
  4. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/test_comprehensive.py +80 -74
  5. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/test_options.py +25 -0
  6. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/test_unified.py +20 -0
  7. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/test_formatting.py +21 -0
  8. audiometa_python-1.3.0/audiometa/test/tests/unit/cli/test_cli_colorize.py +90 -0
  9. {audiometa_python-1.2.0 → audiometa_python-1.3.0/audiometa_python.egg-info}/PKG-INFO +9 -4
  10. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa_python.egg-info/SOURCES.txt +1 -0
  11. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/pyproject.toml +7 -4
  12. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/LICENSE +0 -0
  13. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/__init__.py +0 -0
  14. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/__main__.py +0 -0
  15. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/_audio_file.py +0 -0
  16. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/exceptions.py +0 -0
  17. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_MetadataManager.py +0 -0
  18. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/__init__.py +0 -0
  19. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +0 -0
  20. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/__init__.py +0 -0
  21. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +0 -0
  22. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/id3v2/__init__.py +0 -0
  23. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/id3v2/_id3v1_preserver.py +0 -0
  24. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +0 -0
  25. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_flac_handler.py +0 -0
  26. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_reader.py +0 -0
  27. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/id3v2/_id3v2_writer.py +0 -0
  28. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/riff/_RiffManager.py +0 -0
  29. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/riff/__init__.py +0 -0
  30. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/riff/_riff_bext_chunk.py +0 -0
  31. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/riff/_riff_constants.py +0 -0
  32. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/riff/_riff_file_structure.py +0 -0
  33. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/riff/_riff_info_chunk.py +0 -0
  34. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +0 -0
  35. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/vorbis/__init__.py +0 -0
  36. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +0 -0
  37. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/id3v1/_Id3v1Manager.py +0 -0
  38. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/id3v1/__init__.py +0 -0
  39. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/id3v1/_constants.py +0 -0
  40. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/id3v1/id3v1_raw_metadata.py +0 -0
  41. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/manager/id3v1/id3v1_raw_metadata_key.py +0 -0
  42. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/__init__.py +0 -0
  43. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/assets/create_test_files.py +0 -0
  44. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/__init__.py +0 -0
  45. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/common/__init__.py +0 -0
  46. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/common/audio_file_creator.py +0 -0
  47. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/common/external_tool_runner.py +0 -0
  48. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v1/__init__.py +0 -0
  49. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v1/id3v1_header_verifier.py +0 -0
  50. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +0 -0
  51. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +0 -0
  52. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +0 -0
  53. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v2/__init__.py +0 -0
  54. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +0 -0
  55. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v2/id3v2_header_verifier.py +0 -0
  56. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +0 -0
  57. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +0 -0
  58. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +0 -0
  59. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/riff/__init__.py +0 -0
  60. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/riff/riff_header_verifier.py +0 -0
  61. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/riff/riff_manual_metadata_creator.py +0 -0
  62. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/riff/riff_metadata_deleter.py +0 -0
  63. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/riff/riff_metadata_getter.py +0 -0
  64. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/riff/riff_metadata_setter.py +0 -0
  65. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/scripts/__init__.py +0 -0
  66. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/technical_info_inspector.py +0 -0
  67. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/temp_file_with_metadata.py +0 -0
  68. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/vorbis/__init__.py +0 -0
  69. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/vorbis/vorbis_header_verifier.py +0 -0
  70. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +0 -0
  71. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +0 -0
  72. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +0 -0
  73. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/__init__.py +0 -0
  74. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/conftest.py +0 -0
  75. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/__init__.py +0 -0
  76. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/__init__.py +0 -0
  77. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/__init__.py +0 -0
  78. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +0 -0
  79. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +0 -0
  80. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +0 -0
  81. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +0 -0
  82. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +0 -0
  83. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +0 -0
  84. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +0 -0
  85. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +0 -0
  86. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
  87. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/test_basic.py +0 -0
  88. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/test_formats.py +0 -0
  89. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/test_metadata_content.py +0 -0
  90. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/read/test_multiple_files.py +0 -0
  91. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/test_delete.py +0 -0
  92. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/test_help.py +0 -0
  93. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
  94. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_basic.py +0 -0
  95. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_comprehensive.py +0 -0
  96. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_force_format.py +0 -0
  97. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_integer_fields.py +0 -0
  98. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_list_fields.py +0 -0
  99. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_rating.py +0 -0
  100. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_string_fields.py +0 -0
  101. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/cli/write/test_validation.py +0 -0
  102. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
  103. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +0 -0
  104. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/workflows/__init__.py +0 -0
  105. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/workflows/test_core_workflows.py +0 -0
  106. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +0 -0
  107. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +0 -0
  108. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +0 -0
  109. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/e2e/workflows/test_rating_workflows.py +0 -0
  110. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/__init__.py +0 -0
  111. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/__init__.py +0 -0
  112. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
  113. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +0 -0
  114. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +0 -0
  115. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +0 -0
  116. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +0 -0
  117. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
  118. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +0 -0
  119. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +0 -0
  120. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +0 -0
  121. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +0 -0
  122. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
  123. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +0 -0
  124. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +0 -0
  125. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +0 -0
  126. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +0 -0
  127. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/conftest.py +0 -0
  128. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/delete_all_metadata/__init__.py +0 -0
  129. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +0 -0
  130. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +0 -0
  131. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +0 -0
  132. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +0 -0
  133. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/encoding/__init__.py +0 -0
  134. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/encoding/test_encoding.py +0 -0
  135. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +0 -0
  136. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
  137. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/options/__init__.py +0 -0
  138. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/options/test_include_headers.py +0 -0
  139. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/options/test_include_technical.py +0 -0
  140. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +0 -0
  141. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +0 -0
  142. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_consistency.py +0 -0
  143. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +0 -0
  144. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +0 -0
  145. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +0 -0
  146. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_performance.py +0 -0
  147. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_raw_metadata_includes_unsupported_tags.py +0 -0
  148. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +0 -0
  149. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/get_full_metadata/test_structure.py +0 -0
  150. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
  151. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
  152. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album/test_deleting.py +0 -0
  153. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album/test_reading.py +0 -0
  154. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album/test_writing.py +0 -0
  155. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
  156. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +0 -0
  157. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +0 -0
  158. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +0 -0
  159. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
  160. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +0 -0
  161. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/artists/test_reading.py +0 -0
  162. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/artists/test_writing.py +0 -0
  163. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
  164. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +0 -0
  165. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +0 -0
  166. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +0 -0
  167. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
  168. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +0 -0
  169. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/comment/test_reading.py +0 -0
  170. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/comment/test_writing.py +0 -0
  171. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
  172. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +0 -0
  173. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/composer/test_reading.py +0 -0
  174. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/composer/test_writing.py +0 -0
  175. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
  176. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +0 -0
  177. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +0 -0
  178. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +0 -0
  179. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/description/__init__.py +0 -0
  180. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/description/test_deleting.py +0 -0
  181. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/description/test_reading.py +0 -0
  182. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/description/test_writing.py +0 -0
  183. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
  184. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +0 -0
  185. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +0 -0
  186. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +0 -0
  187. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
  188. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +0 -0
  189. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +0 -0
  190. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +0 -0
  191. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
  192. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
  193. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
  194. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +0 -0
  195. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +0 -0
  196. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +0 -0
  197. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +0 -0
  198. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +0 -0
  199. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +0 -0
  200. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/genre/test_writing.py +0 -0
  201. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/isrc/__init__.py +0 -0
  202. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +0 -0
  203. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +0 -0
  204. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +0 -0
  205. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
  206. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/language/test_deleting.py +0 -0
  207. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/language/test_reading.py +0 -0
  208. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/language/test_writing.py +0 -0
  209. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
  210. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +0 -0
  211. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +0 -0
  212. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +0 -0
  213. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/__init__.py +0 -0
  214. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_deleting.py +0 -0
  215. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_reading.py +0 -0
  216. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_artistid/test_writing.py +0 -0
  217. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/__init__.py +0 -0
  218. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_deleting.py +0 -0
  219. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_reading.py +0 -0
  220. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/musicbrainz_trackid/test_writing.py +0 -0
  221. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/originator/__init__.py +0 -0
  222. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/originator/test_deleting.py +0 -0
  223. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/originator/test_reading.py +0 -0
  224. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/originator/test_writing.py +0 -0
  225. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
  226. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +0 -0
  227. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +0 -0
  228. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +0 -0
  229. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
  230. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
  231. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +0 -0
  232. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +0 -0
  233. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +0 -0
  234. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +0 -0
  235. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +0 -0
  236. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
  237. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
  238. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +0 -0
  239. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +0 -0
  240. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +0 -0
  241. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +0 -0
  242. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
  243. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +0 -0
  244. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +0 -0
  245. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +0 -0
  246. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +0 -0
  247. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +0 -0
  248. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
  249. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/title/test_deleting.py +0 -0
  250. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +0 -0
  251. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/title/test_reading.py +0 -0
  252. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/title/test_writing.py +0 -0
  253. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
  254. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
  255. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +0 -0
  256. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +0 -0
  257. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +0 -0
  258. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +0 -0
  259. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/__init__.py +0 -0
  260. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/__init__.py +0 -0
  261. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +0 -0
  262. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +0 -0
  263. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +0 -0
  264. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +0 -0
  265. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +0 -0
  266. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +0 -0
  267. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +0 -0
  268. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +0 -0
  269. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +0 -0
  270. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/__init__.py +0 -0
  271. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +0 -0
  272. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +0 -0
  273. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +0 -0
  274. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +0 -0
  275. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +0 -0
  276. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +0 -0
  277. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +0 -0
  278. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +0 -0
  279. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/reading/__init__.py +0 -0
  280. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +0 -0
  281. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/reading/test_reading_error_handling.py +0 -0
  282. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
  283. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/real_audio_files/test_reading.py +0 -0
  284. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/real_audio_files/test_writing.py +0 -0
  285. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/__init__.py +0 -0
  286. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
  287. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +0 -0
  288. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/__init__.py +0 -0
  289. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_audio_data_corruption.py +0 -0
  290. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_file.py +0 -0
  291. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_invalid_md5.py +0 -0
  292. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/__init__.py +0 -0
  293. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_invalid_with_metadata_combinations.py +0 -0
  294. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_state_precedence.py +0 -0
  295. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_unset_with_metadata_combinations.py +0 -0
  296. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_fails_with_id3v1.py +0 -0
  297. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_with_id3v2_only.py +0 -0
  298. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_validation_works_without_id3v1.py +0 -0
  299. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_metadata_combinations/test_md5_with_metadata_combinations.py +0 -0
  300. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_checking/test_unset_md5.py +0 -0
  301. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/__init__.py +0 -0
  302. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_delete_original.py +0 -0
  303. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/__init__.py +0 -0
  304. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_flipped_md5.py +0 -0
  305. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_partial_md5.py +0 -0
  306. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_invalid_md5/test_random_md5.py +0 -0
  307. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_md5_repair_with_metadata.py +0 -0
  308. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_non_flac_error.py +0 -0
  309. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/flac_md5/md5_repair/test_unset_md5.py +0 -0
  310. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/test_bitrate.py +0 -0
  311. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/test_channels.py +0 -0
  312. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +0 -0
  313. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/technical_info/test_sample_rate.py +0 -0
  314. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/test_audio_file.py +0 -0
  315. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +0 -0
  316. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/__init__.py +0 -0
  317. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/test_error_handling.py +0 -0
  318. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/test_forced_format.py +0 -0
  319. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +0 -0
  320. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/test_partial_update.py +0 -0
  321. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
  322. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/__init__.py +0 -0
  323. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_flac_sync.py +0 -0
  324. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_mp3_sync.py +0 -0
  325. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/sync_strategy/test_wav_sync.py +0 -0
  326. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +0 -0
  327. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +0 -0
  328. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
  329. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +0 -0
  330. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +0 -0
  331. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +0 -0
  332. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/__init__.py +0 -0
  333. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/__init__.py +0 -0
  334. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
  335. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +0 -0
  336. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +0 -0
  337. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +0 -0
  338. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +0 -0
  339. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +0 -0
  340. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +0 -0
  341. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +0 -0
  342. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/test_context_manager.py +0 -0
  343. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/test_file_validation.py +0 -0
  344. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/test_is_audio_file.py +0 -0
  345. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/test_operations.py +0 -0
  346. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/audio_file/test_path_handling.py +0 -0
  347. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/cli/__init__.py +0 -0
  348. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/cli/test_expand_file_patterns.py +0 -0
  349. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
  350. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/conftest.py +0 -0
  351. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
  352. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +0 -0
  353. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +0 -0
  354. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +0 -0
  355. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_riff_info_chunk_fourcc.py +0 -0
  356. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +0 -0
  357. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
  358. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/disc_number/__init__.py +0 -0
  359. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/disc_number/test_disc_number_validation.py +0 -0
  360. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/__init__.py +0 -0
  361. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/test_isrc_format_validation.py +0 -0
  362. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/isrc/test_isrc_type_validation.py +0 -0
  363. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
  364. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
  365. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +0 -0
  366. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
  367. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +0 -0
  368. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +0 -0
  369. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/__init__.py +0 -0
  370. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/test_musicbrainz_artistid_format_validation.py +0 -0
  371. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_artistid/test_musicbrainz_artistid_type_validation.py +0 -0
  372. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/__init__.py +0 -0
  373. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/test_musicbrainz_trackid_format_validation.py +0 -0
  374. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_trackid/test_musicbrainz_trackid_type_validation.py +0 -0
  375. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_uuid/__init__.py +0 -0
  376. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/musicbrainz_uuid/test_uuid_format_validation.py +0 -0
  377. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
  378. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
  379. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +0 -0
  380. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +0 -0
  381. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +0 -0
  382. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
  383. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +0 -0
  384. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +0 -0
  385. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +0 -0
  386. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/release_date/__init__.py +0 -0
  387. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/release_date/test_date_format_validation.py +0 -0
  388. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +0 -0
  389. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +0 -0
  390. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/track_number/__init__.py +0 -0
  391. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/metadata_field/track_number/test_track_number_validation.py +0 -0
  392. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +0 -0
  393. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +0 -0
  394. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/utils/__init__.py +0 -0
  395. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/test/tests/unit/utils/test_raw_metadata_sanitizer.py +0 -0
  396. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/__init__.py +0 -0
  397. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/flac_md5_state.py +0 -0
  398. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/id3v1_genre_code_map.py +0 -0
  399. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/metadata_format.py +0 -0
  400. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/metadata_writing_strategy.py +0 -0
  401. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/mutagen_exception_handler.py +0 -0
  402. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/os_dependencies_checker/__init__.py +0 -0
  403. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/os_dependencies_checker/base.py +0 -0
  404. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/os_dependencies_checker/config.py +0 -0
  405. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/os_dependencies_checker/macos.py +0 -0
  406. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/os_dependencies_checker/ubuntu.py +0 -0
  407. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/os_dependencies_checker/windows.py +0 -0
  408. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/rating_profiles.py +0 -0
  409. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/raw_metadata_sanitizer.py +0 -0
  410. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/tool_path_resolver.py +0 -0
  411. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/types.py +0 -0
  412. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa/utils/unified_metadata_key.py +0 -0
  413. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa_python.egg-info/dependency_links.txt +0 -0
  414. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa_python.egg-info/entry_points.txt +0 -0
  415. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa_python.egg-info/requires.txt +0 -0
  416. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/audiometa_python.egg-info/top_level.txt +0 -0
  417. {audiometa_python-1.2.0 → audiometa_python-1.3.0}/setup.cfg +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: audiometa-python
3
- Version: 1.2.0
3
+ Version: 1.3.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>
7
7
  License: Apache-2.0
8
- Project-URL: Homepage, https://github.com/your-username/audiometa-python
9
- Project-URL: Repository, https://github.com/your-username/audiometa-python
10
- Project-URL: Issues, https://github.com/your-username/audiometa-python/issues
8
+ Project-URL: Homepage, https://github.com/BehindTheMusicTree/audiometa
9
+ Project-URL: Repository, https://github.com/BehindTheMusicTree/audiometa
10
+ Project-URL: Issues, https://github.com/BehindTheMusicTree/audiometa/issues
11
11
  Keywords: audio,metadata,mp3,flac,wav,id3,vorbis,riff
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
@@ -1518,6 +1518,9 @@ audiometa read song.mp3 --include-raw-binary-data
1518
1518
 
1519
1519
  # Save to file
1520
1520
  audiometa read song.mp3 --output metadata.json
1521
+
1522
+ # Colorize output (headers, keys, values) for terminal or demos
1523
+ audiometa read song.mp3 --format table --color
1521
1524
  ```
1522
1525
 
1523
1526
  #### Error Handling {#cli-error-handling}
@@ -1543,6 +1546,8 @@ audiometa read "**/*.mp3" --recursive
1543
1546
  - **YAML**: Human-readable structured format (requires PyYAML)
1544
1547
  - **Table**: Simple text table format
1545
1548
 
1549
+ Use `--color` to colorize headers, keys, and values for terminal or demo videos (plain output by default; disabled when writing to a file). Respects the `NO_COLOR` environment variable.
1550
+
1546
1551
  ### Examples
1547
1552
 
1548
1553
  ```bash
@@ -1476,6 +1476,9 @@ audiometa read song.mp3 --include-raw-binary-data
1476
1476
 
1477
1477
  # Save to file
1478
1478
  audiometa read song.mp3 --output metadata.json
1479
+
1480
+ # Colorize output (headers, keys, values) for terminal or demos
1481
+ audiometa read song.mp3 --format table --color
1479
1482
  ```
1480
1483
 
1481
1484
  #### Error Handling {#cli-error-handling}
@@ -1501,6 +1504,8 @@ audiometa read "**/*.mp3" --recursive
1501
1504
  - **YAML**: Human-readable structured format (requires PyYAML)
1502
1505
  - **Table**: Simple text table format
1503
1506
 
1507
+ Use `--color` to colorize headers, keys, and values for terminal or demo videos (plain output by default; disabled when writing to a file). Respects the `NO_COLOR` environment variable.
1508
+
1504
1509
  ### Examples
1505
1510
 
1506
1511
  ```bash
@@ -3,6 +3,8 @@
3
3
 
4
4
  import argparse
5
5
  import json
6
+ import os
7
+ import re
6
8
  import sys
7
9
  from pathlib import Path
8
10
  from typing import Any
@@ -23,20 +25,95 @@ from audiometa.exceptions import (
23
25
  from audiometa.utils.metadata_format import MetadataFormat
24
26
  from audiometa.utils.types import UnifiedMetadata
25
27
 
28
+ _ANSI_RESET = "\033[0m"
29
+ _ANSI_KEY = "\033[32m" # green
30
+ _ANSI_STR = "\033[33m" # yellow
31
+ _ANSI_NUM = "\033[36m" # cyan
32
+ _ANSI_LITERAL = "\033[2m" # dim
33
+ _ANSI_SECTION = "\033[1;36m" # bold cyan
34
+
35
+
36
+ def _data_for_serialization(data: Any) -> Any:
37
+ """Convert data to a form suitable for YAML/JSON (UnifiedMetadataKey keys as strings)."""
38
+ if isinstance(data, dict):
39
+ return {
40
+ (k.value if isinstance(k, UnifiedMetadataKey) else k): _data_for_serialization(v) for k, v in data.items()
41
+ }
42
+ if isinstance(data, list):
43
+ return [_data_for_serialization(item) for item in data]
44
+ return data
45
+
46
+
47
+ def _colorize_json(raw: str) -> str:
48
+ """Add ANSI colors to JSON: keys green, string values yellow, numbers cyan, literals dim."""
49
+ raw = re.sub(r'("[^"]*")\s*:', rf"{_ANSI_KEY}\1{_ANSI_RESET}:", raw)
50
+ raw = re.sub(r':\s*("(?:[^"\\]|\\.)*")', rf": {_ANSI_STR}\1{_ANSI_RESET}", raw)
51
+ raw = re.sub(r":\s*(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)", rf": {_ANSI_NUM}\1{_ANSI_RESET}", raw)
52
+ return re.sub(r"\b(true|false|null)\b", rf"{_ANSI_LITERAL}\1{_ANSI_RESET}", raw)
53
+
54
+
55
+ def _colorize_table(raw: str) -> str:
56
+ """Colorize table: section headers bold cyan, keys green, values yellow (incl. multiline)."""
57
+ lines = raw.split("\n")
58
+ out = []
59
+ key_val_re = re.compile(r"^(\s*)(.*?)\s*:\s*(.*)$")
60
+ for line in lines:
61
+ if line.strip().startswith("===") and line.strip().endswith("==="):
62
+ out.append(f"{_ANSI_SECTION}{line}{_ANSI_RESET}")
63
+ elif ":" in line and not line.strip().startswith("=="):
64
+ match = key_val_re.match(line)
65
+ if match:
66
+ pre, key_part, val_part = match.groups()
67
+ out.append(f"{pre}{_ANSI_KEY}{key_part}{_ANSI_RESET}: {_ANSI_STR}{val_part}{_ANSI_RESET}")
68
+ else:
69
+ out.append(line)
70
+ elif line.strip():
71
+ # Continuation line (e.g. multiline lyrics in table), color as value
72
+ indent = line[: len(line) - len(line.lstrip())]
73
+ rest = line.lstrip()
74
+ out.append(f"{indent}{_ANSI_STR}{rest}{_ANSI_RESET}")
75
+ else:
76
+ out.append(line)
77
+ return "\n".join(out)
78
+
79
+
80
+ def _colorize_yaml(raw: str) -> str:
81
+ """Colorize YAML: keys green, values yellow (including multiline continuation lines)."""
82
+ lines = raw.split("\n")
83
+ out = []
84
+ key_line_re = re.compile(r"^(\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$")
85
+ for line in lines:
86
+ match = key_line_re.match(line)
87
+ if match:
88
+ pre, key, val = match.groups()
89
+ if val.strip():
90
+ out.append(f"{pre}{_ANSI_KEY}{key}{_ANSI_RESET}: {_ANSI_STR}{val}{_ANSI_RESET}")
91
+ else:
92
+ out.append(f"{pre}{_ANSI_KEY}{key}{_ANSI_RESET}:{val}")
93
+ elif line.strip():
94
+ # Continuation line (e.g. multiline lyrics); not a key line, color as value
95
+ indent = line[: len(line) - len(line.lstrip())]
96
+ rest = line.lstrip()
97
+ out.append(f"{indent}{_ANSI_STR}{rest}{_ANSI_RESET}")
98
+ else:
99
+ out.append(line)
100
+ return "\n".join(out)
101
+
26
102
 
27
103
  def format_output(data: Any, output_format: str) -> str:
28
104
  """Format output data according to specified format."""
105
+ serializable = _data_for_serialization(data)
29
106
  if output_format == "json":
30
- return json.dumps(data, indent=2)
107
+ return json.dumps(serializable, indent=2)
31
108
  if output_format == "yaml":
32
109
  try:
33
110
  import yaml # type: ignore[import-untyped]
34
111
 
35
- result = yaml.dump(data, default_flow_style=False)
112
+ result = yaml.dump(serializable, default_flow_style=False)
36
113
  return str(result) if result is not None else ""
37
114
  except ImportError:
38
115
  sys.stderr.write("Warning: PyYAML not installed, falling back to JSON\n")
39
- return json.dumps(data, indent=2)
116
+ return json.dumps(serializable, indent=2)
40
117
  elif output_format == "table":
41
118
  return format_as_table(data)
42
119
  else:
@@ -107,7 +184,8 @@ def format_as_table(data: dict[str, Any]) -> str:
107
184
  lines.append("=== UNIFIED METADATA ===")
108
185
  for key, value in data["unified_metadata"].items():
109
186
  if value is not None:
110
- lines.append(f"{key:20}: {value}")
187
+ label = key.value if isinstance(key, UnifiedMetadataKey) else str(key)
188
+ lines.append(f"{label:20}: {value}")
111
189
  lines.append("")
112
190
 
113
191
  if "technical_info" in data:
@@ -124,7 +202,8 @@ def format_as_table(data: dict[str, Any]) -> str:
124
202
  lines.append(f"\n{metadata_format_name.upper()}:")
125
203
  for key, value in format_data.items():
126
204
  if value is not None:
127
- lines.append(f" {key:18}: {value}")
205
+ label = key.value if isinstance(key, UnifiedMetadataKey) else str(key)
206
+ lines.append(f" {label:18}: {value}")
128
207
 
129
208
  return "\n".join(lines)
130
209
 
@@ -150,17 +229,27 @@ def _read_metadata(args: argparse.Namespace) -> None:
150
229
  include_raw_binary_data=getattr(args, "include_raw_binary_data", False),
151
230
  )
152
231
 
153
- output = format_output(metadata, args.output_format)
232
+ plain = format_output(metadata, args.output_format)
233
+ use_color = getattr(args, "color", False) and not args.output
234
+ if os.environ.get("NO_COLOR"):
235
+ use_color = False
236
+ out = plain
237
+ if use_color and args.output_format == "json":
238
+ out = _colorize_json(plain)
239
+ elif use_color and args.output_format == "table":
240
+ out = _colorize_table(plain)
241
+ elif use_color and args.output_format == "yaml":
242
+ out = _colorize_yaml(plain)
154
243
 
155
244
  if args.output:
156
245
  try:
157
246
  with Path(args.output).open("w") as f:
158
- f.write(output)
247
+ f.write(plain)
159
248
  except (PermissionError, OSError) as e:
160
249
  _handle_file_operation_error(e, args.output, args.continue_on_error)
161
250
  else:
162
- sys.stdout.write(output)
163
- if not output.endswith("\n"):
251
+ sys.stdout.write(out)
252
+ if not out.endswith("\n"):
164
253
  sys.stdout.write("\n")
165
254
 
166
255
  except (FileTypeNotSupportedError, FileNotFoundError, PermissionError, OSError, Exception) as e:
@@ -409,6 +498,12 @@ Examples:
409
498
  read_parser.add_argument(
410
499
  "--continue-on-error", action="store_true", help="Continue processing other files on error"
411
500
  )
501
+ read_parser.add_argument(
502
+ "--color",
503
+ action="store_true",
504
+ dest="color",
505
+ help="Colorize output (headers, keys, values) for JSON, YAML, and table.",
506
+ )
412
507
  read_parser.set_defaults(func=_read_metadata)
413
508
 
414
509
  # Unified command
@@ -426,6 +521,12 @@ Examples:
426
521
  unified_parser.add_argument(
427
522
  "--continue-on-error", action="store_true", help="Continue processing other files on error"
428
523
  )
524
+ unified_parser.add_argument(
525
+ "--color",
526
+ action="store_true",
527
+ dest="color",
528
+ help="Colorize output (headers, keys, values) for JSON, YAML, and table.",
529
+ )
429
530
  unified_parser.set_defaults(func=_read_metadata, format_type="unified")
430
531
 
431
532
  # Write command
@@ -49,26 +49,26 @@ class TestCLIReadComprehensive:
49
49
  data = json.loads(result.stdout)
50
50
  unified = data.get("unified_metadata", {})
51
51
 
52
- assert unified.get(UnifiedMetadataKey.TITLE) == "Comprehensive Test Title"
53
- assert unified.get(UnifiedMetadataKey.ARTISTS) == ["Artist One", "Artist Two"]
54
- assert unified.get(UnifiedMetadataKey.ALBUM) == "Test Album"
55
- assert unified.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Album Artist"]
56
- assert unified.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
57
- assert unified.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock", "Blues"]
58
- assert unified.get(UnifiedMetadataKey.TRACK_NUMBER) == "5/12"
59
- assert unified.get(UnifiedMetadataKey.DISC_NUMBER) == 1
60
- assert unified.get(UnifiedMetadataKey.DISC_TOTAL) == 2
61
- assert unified.get(UnifiedMetadataKey.RATING) == 85
62
- assert unified.get(UnifiedMetadataKey.BPM) == 120
63
- assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
64
- assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["Composer One", "Composer Two"]
65
- assert unified.get(UnifiedMetadataKey.PUBLISHER) == "Test Publisher"
66
- assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© 2024"
67
- assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "Test lyrics"
68
- assert unified.get(UnifiedMetadataKey.COMMENT) == "Test comment"
69
- assert unified.get(UnifiedMetadataKey.ISRC) == "USRC17607839"
70
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID) == "9d6f6f7c-9d52-4c76-8f9e-01d18d8f8ec6"
71
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS) == [
52
+ assert unified.get(UnifiedMetadataKey.TITLE.value) == "Comprehensive Test Title"
53
+ assert unified.get(UnifiedMetadataKey.ARTISTS.value) == ["Artist One", "Artist Two"]
54
+ assert unified.get(UnifiedMetadataKey.ALBUM.value) == "Test Album"
55
+ assert unified.get(UnifiedMetadataKey.ALBUM_ARTISTS.value) == ["Album Artist"]
56
+ assert unified.get(UnifiedMetadataKey.RELEASE_DATE.value) == "2024"
57
+ assert unified.get(UnifiedMetadataKey.GENRES_NAMES.value) == ["Rock", "Blues"]
58
+ assert unified.get(UnifiedMetadataKey.TRACK_NUMBER.value) == "5/12"
59
+ assert unified.get(UnifiedMetadataKey.DISC_NUMBER.value) == 1
60
+ assert unified.get(UnifiedMetadataKey.DISC_TOTAL.value) == 2
61
+ assert unified.get(UnifiedMetadataKey.RATING.value) == 85
62
+ assert unified.get(UnifiedMetadataKey.BPM.value) == 120
63
+ assert unified.get(UnifiedMetadataKey.LANGUAGE.value) == "eng"
64
+ assert unified.get(UnifiedMetadataKey.COMPOSERS.value) == ["Composer One", "Composer Two"]
65
+ assert unified.get(UnifiedMetadataKey.PUBLISHER.value) == "Test Publisher"
66
+ assert unified.get(UnifiedMetadataKey.COPYRIGHT.value) == "© 2024"
67
+ assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS.value) == "Test lyrics"
68
+ assert unified.get(UnifiedMetadataKey.COMMENT.value) == "Test comment"
69
+ assert unified.get(UnifiedMetadataKey.ISRC.value) == "USRC17607839"
70
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID.value) == "9d6f6f7c-9d52-4c76-8f9e-01d18d8f8ec6"
71
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS.value) == [
72
72
  "9d6f6f7c-9d52-4c76-8f9e-01d18d8f8ec6",
73
73
  "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
74
74
  ]
@@ -108,24 +108,26 @@ class TestCLIReadComprehensive:
108
108
  data = json.loads(result.stdout)
109
109
  unified = data.get("unified_metadata", {})
110
110
 
111
- assert unified.get(UnifiedMetadataKey.TITLE) == "FLAC Comprehensive Test"
112
- assert unified.get(UnifiedMetadataKey.ARTISTS) == ["FLAC Artist"]
113
- assert unified.get(UnifiedMetadataKey.ALBUM) == "FLAC Album"
114
- assert unified.get(UnifiedMetadataKey.TRACK_NUMBER) == "3/10"
115
- assert unified.get(UnifiedMetadataKey.DISC_NUMBER) == 1
116
- assert unified.get(UnifiedMetadataKey.DISC_TOTAL) == 2
117
- assert unified.get(UnifiedMetadataKey.BPM) == 140
118
- assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
119
- assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["FLAC Composer"]
120
- assert unified.get(UnifiedMetadataKey.PUBLISHER) == "FLAC Publisher"
121
- assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© FLAC"
122
- assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "FLAC lyrics"
123
- assert unified.get(UnifiedMetadataKey.COMMENT) == "FLAC comment"
124
- assert unified.get(UnifiedMetadataKey.DESCRIPTION) == "FLAC description"
125
- assert unified.get(UnifiedMetadataKey.REPLAYGAIN) == "+2.5 dB"
126
- assert unified.get(UnifiedMetadataKey.ISRC) == "FRXXX1800001"
127
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID) == "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
128
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS) == ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
111
+ assert unified.get(UnifiedMetadataKey.TITLE.value) == "FLAC Comprehensive Test"
112
+ assert unified.get(UnifiedMetadataKey.ARTISTS.value) == ["FLAC Artist"]
113
+ assert unified.get(UnifiedMetadataKey.ALBUM.value) == "FLAC Album"
114
+ assert unified.get(UnifiedMetadataKey.TRACK_NUMBER.value) == "3/10"
115
+ assert unified.get(UnifiedMetadataKey.DISC_NUMBER.value) == 1
116
+ assert unified.get(UnifiedMetadataKey.DISC_TOTAL.value) == 2
117
+ assert unified.get(UnifiedMetadataKey.BPM.value) == 140
118
+ assert unified.get(UnifiedMetadataKey.LANGUAGE.value) == "eng"
119
+ assert unified.get(UnifiedMetadataKey.COMPOSERS.value) == ["FLAC Composer"]
120
+ assert unified.get(UnifiedMetadataKey.PUBLISHER.value) == "FLAC Publisher"
121
+ assert unified.get(UnifiedMetadataKey.COPYRIGHT.value) == "© FLAC"
122
+ assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS.value) == "FLAC lyrics"
123
+ assert unified.get(UnifiedMetadataKey.COMMENT.value) == "FLAC comment"
124
+ assert unified.get(UnifiedMetadataKey.DESCRIPTION.value) == "FLAC description"
125
+ assert unified.get(UnifiedMetadataKey.REPLAYGAIN.value) == "+2.5 dB"
126
+ assert unified.get(UnifiedMetadataKey.ISRC.value) == "FRXXX1800001"
127
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID.value) == "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
128
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS.value) == [
129
+ "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
130
+ ]
129
131
  # ARCHIVAL_LOCATION is not supported by Vorbis format (FLAC)
130
132
 
131
133
  def test_cli_read_all_fields_comprehensive_wav(self):
@@ -160,22 +162,24 @@ class TestCLIReadComprehensive:
160
162
  data = json.loads(result.stdout)
161
163
  unified = data.get("unified_metadata", {})
162
164
 
163
- assert unified.get(UnifiedMetadataKey.TITLE) == "WAV Comprehensive Test"
164
- assert unified.get(UnifiedMetadataKey.ARTISTS) == ["WAV Artist"]
165
- assert unified.get(UnifiedMetadataKey.ALBUM) == "WAV Album"
166
- assert unified.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
167
- assert unified.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
168
- assert unified.get(UnifiedMetadataKey.RATING) == 100
169
- assert unified.get(UnifiedMetadataKey.BPM) == 120
170
- assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
171
- assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["WAV Composer"]
172
- assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© WAV"
173
- assert unified.get(UnifiedMetadataKey.COMMENT) == "WAV comment"
174
- assert unified.get(UnifiedMetadataKey.DESCRIPTION) == "WAV description"
175
- assert unified.get(UnifiedMetadataKey.ORIGINATOR) == "WAV originator"
176
- assert unified.get(UnifiedMetadataKey.ISRC) == "GBUM71505078"
177
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID) == "12345678-1234-5678-9abc-def123456789"
178
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS) == ["12345678-1234-5678-9abc-def123456789"]
165
+ assert unified.get(UnifiedMetadataKey.TITLE.value) == "WAV Comprehensive Test"
166
+ assert unified.get(UnifiedMetadataKey.ARTISTS.value) == ["WAV Artist"]
167
+ assert unified.get(UnifiedMetadataKey.ALBUM.value) == "WAV Album"
168
+ assert unified.get(UnifiedMetadataKey.RELEASE_DATE.value) == "2024"
169
+ assert unified.get(UnifiedMetadataKey.GENRES_NAMES.value) == ["Rock"]
170
+ assert unified.get(UnifiedMetadataKey.RATING.value) == 100
171
+ assert unified.get(UnifiedMetadataKey.BPM.value) == 120
172
+ assert unified.get(UnifiedMetadataKey.LANGUAGE.value) == "eng"
173
+ assert unified.get(UnifiedMetadataKey.COMPOSERS.value) == ["WAV Composer"]
174
+ assert unified.get(UnifiedMetadataKey.COPYRIGHT.value) == "© WAV"
175
+ assert unified.get(UnifiedMetadataKey.COMMENT.value) == "WAV comment"
176
+ assert unified.get(UnifiedMetadataKey.DESCRIPTION.value) == "WAV description"
177
+ assert unified.get(UnifiedMetadataKey.ORIGINATOR.value) == "WAV originator"
178
+ assert unified.get(UnifiedMetadataKey.ISRC.value) == "GBUM71505078"
179
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID.value) == "12345678-1234-5678-9abc-def123456789"
180
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS.value) == [
181
+ "12345678-1234-5678-9abc-def123456789"
182
+ ]
179
183
 
180
184
  def test_cli_read_comprehensive_roundtrip(self):
181
185
  """Test that we can write all fields via CLI and read them back correctly."""
@@ -248,23 +252,25 @@ class TestCLIReadComprehensive:
248
252
  data = json.loads(read_result.stdout)
249
253
  unified = data.get("unified_metadata", {})
250
254
 
251
- assert unified.get(UnifiedMetadataKey.TITLE) == "Roundtrip Test"
252
- assert unified.get(UnifiedMetadataKey.ARTISTS) == ["Roundtrip Artist One", "Roundtrip Artist Two"]
253
- assert unified.get(UnifiedMetadataKey.ALBUM) == "Roundtrip Album"
254
- assert unified.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Roundtrip Album Artist"]
255
- assert unified.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
256
- assert unified.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock", "Blues"]
257
- assert unified.get(UnifiedMetadataKey.TRACK_NUMBER) == "5/12"
258
- assert unified.get(UnifiedMetadataKey.DISC_NUMBER) == 1
259
- assert unified.get(UnifiedMetadataKey.DISC_TOTAL) == 2
260
- assert unified.get(UnifiedMetadataKey.RATING) == 85
261
- assert unified.get(UnifiedMetadataKey.BPM) == 120
262
- assert unified.get(UnifiedMetadataKey.LANGUAGE) == "eng"
263
- assert unified.get(UnifiedMetadataKey.COMPOSERS) == ["Roundtrip Composer"]
264
- assert unified.get(UnifiedMetadataKey.PUBLISHER) == "Roundtrip Publisher"
265
- assert unified.get(UnifiedMetadataKey.COPYRIGHT) == "© Roundtrip"
266
- assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "Roundtrip lyrics"
267
- assert unified.get(UnifiedMetadataKey.COMMENT) == "Roundtrip comment"
268
- assert unified.get(UnifiedMetadataKey.ISRC) == "USRC17607839"
269
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID) == "9d6f6f7c-9d52-4c76-8f9e-01d18d8f8ec6"
270
- assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS) == ["9d6f6f7c-9d52-4c76-8f9e-01d18d8f8ec6"]
255
+ assert unified.get(UnifiedMetadataKey.TITLE.value) == "Roundtrip Test"
256
+ assert unified.get(UnifiedMetadataKey.ARTISTS.value) == ["Roundtrip Artist One", "Roundtrip Artist Two"]
257
+ assert unified.get(UnifiedMetadataKey.ALBUM.value) == "Roundtrip Album"
258
+ assert unified.get(UnifiedMetadataKey.ALBUM_ARTISTS.value) == ["Roundtrip Album Artist"]
259
+ assert unified.get(UnifiedMetadataKey.RELEASE_DATE.value) == "2024"
260
+ assert unified.get(UnifiedMetadataKey.GENRES_NAMES.value) == ["Rock", "Blues"]
261
+ assert unified.get(UnifiedMetadataKey.TRACK_NUMBER.value) == "5/12"
262
+ assert unified.get(UnifiedMetadataKey.DISC_NUMBER.value) == 1
263
+ assert unified.get(UnifiedMetadataKey.DISC_TOTAL.value) == 2
264
+ assert unified.get(UnifiedMetadataKey.RATING.value) == 85
265
+ assert unified.get(UnifiedMetadataKey.BPM.value) == 120
266
+ assert unified.get(UnifiedMetadataKey.LANGUAGE.value) == "eng"
267
+ assert unified.get(UnifiedMetadataKey.COMPOSERS.value) == ["Roundtrip Composer"]
268
+ assert unified.get(UnifiedMetadataKey.PUBLISHER.value) == "Roundtrip Publisher"
269
+ assert unified.get(UnifiedMetadataKey.COPYRIGHT.value) == "© Roundtrip"
270
+ assert unified.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS.value) == "Roundtrip lyrics"
271
+ assert unified.get(UnifiedMetadataKey.COMMENT.value) == "Roundtrip comment"
272
+ assert unified.get(UnifiedMetadataKey.ISRC.value) == "USRC17607839"
273
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_TRACKID.value) == "9d6f6f7c-9d52-4c76-8f9e-01d18d8f8ec6"
274
+ assert unified.get(UnifiedMetadataKey.MUSICBRAINZ_ARTISTIDS.value) == [
275
+ "9d6f6f7c-9d52-4c76-8f9e-01d18d8f8ec6"
276
+ ]
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import os
2
3
  import subprocess
3
4
  import sys
4
5
 
@@ -86,3 +87,27 @@ class TestCLIReadOptions:
86
87
  )
87
88
  assert result.returncode == 0
88
89
  assert "UNIFIED METADATA" in result.stdout
90
+
91
+ def test_cli_read_with_color_produces_ansi(self):
92
+ env = {k: v for k, v in os.environ.items() if k != "NO_COLOR"}
93
+ with temp_file_with_metadata({"title": "Test"}, "mp3") as test_file:
94
+ result = subprocess.run(
95
+ [sys.executable, "-m", "audiometa", "read", str(test_file), "--color", "--format", "json"],
96
+ capture_output=True,
97
+ text=True,
98
+ check=False,
99
+ env=env,
100
+ )
101
+ assert result.returncode == 0
102
+ assert "\033[" in result.stdout
103
+
104
+ def test_cli_read_without_color_no_ansi(self):
105
+ with temp_file_with_metadata({"title": "Test"}, "mp3") as test_file:
106
+ result = subprocess.run(
107
+ [sys.executable, "-m", "audiometa", "read", str(test_file), "--format", "json"],
108
+ capture_output=True,
109
+ text=True,
110
+ check=False,
111
+ )
112
+ assert result.returncode == 0
113
+ assert "\033[" not in result.stdout
@@ -37,6 +37,26 @@ class TestCLIReadUnified:
37
37
  assert data.get("title") == "Unified Test"
38
38
  assert data.get("artists") == ["Unified Artist"]
39
39
 
40
+ def test_cli_unified_yaml_uses_plain_string_keys(self):
41
+ with temp_file_with_metadata({"title": "YAML Key Test", "artist": "YAML Artist"}, "mp3") as test_file:
42
+ result = subprocess.run(
43
+ [sys.executable, "-m", "audiometa", "unified", str(test_file), "--format", "yaml"],
44
+ capture_output=True,
45
+ text=True,
46
+ check=False,
47
+ )
48
+ assert result.returncode == 0
49
+ assert "!!python" not in result.stdout
50
+ try:
51
+ import yaml
52
+
53
+ data = yaml.safe_load(result.stdout)
54
+ assert isinstance(data, dict)
55
+ assert data.get("title") == "YAML Key Test"
56
+ assert data.get("artists") == ["YAML Artist"]
57
+ except ImportError:
58
+ pass
59
+
40
60
  def test_cli_unified_table_format(self):
41
61
  with temp_file_with_metadata({"title": "Table Test", "artist": "Table Artist"}, "mp3") as test_file:
42
62
  result = subprocess.run(
@@ -2,6 +2,7 @@ import json
2
2
 
3
3
  import pytest
4
4
 
5
+ from audiometa import UnifiedMetadataKey
5
6
  from audiometa.cli import format_as_table, format_output
6
7
 
7
8
 
@@ -29,3 +30,23 @@ class TestCLIFormatting:
29
30
  assert "Test Artist" in result
30
31
  assert "180" in result
31
32
  assert "320" in result
33
+
34
+ def test_format_output_table_displays_key_labels_not_enum_repr(self):
35
+ data = {
36
+ "unified_metadata": {
37
+ UnifiedMetadataKey.TITLE: "Song Title",
38
+ UnifiedMetadataKey.ALBUM_ARTISTS: ["Album Artist"],
39
+ },
40
+ "metadata_format": {
41
+ "id3v2": {
42
+ UnifiedMetadataKey.TITLE: "Song Title",
43
+ UnifiedMetadataKey.ALBUM_ARTISTS: ["Album Artist"],
44
+ },
45
+ },
46
+ }
47
+ result = format_as_table(data)
48
+ assert "UnifiedMetadataKey." not in result
49
+ assert "title" in result
50
+ assert "album_artists" in result
51
+ assert "Song Title" in result
52
+ assert "Album Artist" in result or "['Album Artist']" in result
@@ -0,0 +1,90 @@
1
+ import re
2
+
3
+ import pytest
4
+
5
+ from audiometa.cli import _colorize_json, _colorize_table, _colorize_yaml
6
+
7
+
8
+ def _strip_ansi(text: str) -> str:
9
+ return re.sub(r"\033\[[0-9;]*m", "", text)
10
+
11
+
12
+ @pytest.mark.unit
13
+ class TestColorizeJson:
14
+ def test_keys_green(self):
15
+ raw = '"title": "Hello"'
16
+ out = _colorize_json(raw)
17
+ assert "\033[32m" in out
18
+ assert "title" in out
19
+ assert _strip_ansi(out) == raw
20
+
21
+ def test_string_values_yellow(self):
22
+ raw = '"title": "Hello"'
23
+ out = _colorize_json(raw)
24
+ assert "\033[33m" in out
25
+ assert "Hello" in out
26
+
27
+ def test_numbers_cyan(self):
28
+ raw = '"duration": 180'
29
+ out = _colorize_json(raw)
30
+ assert "\033[36m" in out
31
+ assert "180" in out
32
+
33
+ def test_literals_dim(self):
34
+ raw = '"enabled": true'
35
+ out = _colorize_json(raw)
36
+ assert "\033[2m" in out
37
+ assert "true" in out
38
+
39
+ def test_reset_present(self):
40
+ raw = '{"key": "val"}'
41
+ out = _colorize_json(raw)
42
+ assert "\033[0m" in out
43
+
44
+
45
+ @pytest.mark.unit
46
+ class TestColorizeTable:
47
+ def test_section_headers_bold_cyan(self):
48
+ raw = "=== UNIFIED METADATA ==="
49
+ out = _colorize_table(raw)
50
+ assert "\033[1;36m" in out
51
+ assert "UNIFIED METADATA" in out
52
+
53
+ def test_key_value_line(self):
54
+ raw = "title : My Song"
55
+ out = _colorize_table(raw)
56
+ assert "\033[32m" in out
57
+ assert "\033[33m" in out
58
+ assert "title" in out
59
+ assert "My Song" in out
60
+
61
+ def test_continuation_line_colored_as_value(self):
62
+ raw = "title : Line one\nLine two"
63
+ out = _colorize_table(raw)
64
+ assert "\033[33m" in out
65
+ assert "Line one" in out
66
+ assert "Line two" in out
67
+
68
+
69
+ @pytest.mark.unit
70
+ class TestColorizeYaml:
71
+ def test_key_green_value_yellow(self):
72
+ raw = "title: My Album"
73
+ out = _colorize_yaml(raw)
74
+ assert "\033[32m" in out
75
+ assert "\033[33m" in out
76
+ assert "title" in out
77
+ assert "My Album" in out
78
+
79
+ def test_continuation_line_colored(self):
80
+ raw = "lyrics: First line\nSecond line"
81
+ out = _colorize_yaml(raw)
82
+ assert "\033[33m" in out
83
+ assert "First line" in out
84
+ assert "Second line" in out
85
+
86
+ def test_empty_value_key_only_colored(self):
87
+ raw = "empty:"
88
+ out = _colorize_yaml(raw)
89
+ assert "\033[32m" in out
90
+ assert "empty" in out
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: audiometa-python
3
- Version: 1.2.0
3
+ Version: 1.3.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>
7
7
  License: Apache-2.0
8
- Project-URL: Homepage, https://github.com/your-username/audiometa-python
9
- Project-URL: Repository, https://github.com/your-username/audiometa-python
10
- Project-URL: Issues, https://github.com/your-username/audiometa-python/issues
8
+ Project-URL: Homepage, https://github.com/BehindTheMusicTree/audiometa
9
+ Project-URL: Repository, https://github.com/BehindTheMusicTree/audiometa
10
+ Project-URL: Issues, https://github.com/BehindTheMusicTree/audiometa/issues
11
11
  Keywords: audio,metadata,mp3,flac,wav,id3,vorbis,riff
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
@@ -1518,6 +1518,9 @@ audiometa read song.mp3 --include-raw-binary-data
1518
1518
 
1519
1519
  # Save to file
1520
1520
  audiometa read song.mp3 --output metadata.json
1521
+
1522
+ # Colorize output (headers, keys, values) for terminal or demos
1523
+ audiometa read song.mp3 --format table --color
1521
1524
  ```
1522
1525
 
1523
1526
  #### Error Handling {#cli-error-handling}
@@ -1543,6 +1546,8 @@ audiometa read "**/*.mp3" --recursive
1543
1546
  - **YAML**: Human-readable structured format (requires PyYAML)
1544
1547
  - **Table**: Simple text table format
1545
1548
 
1549
+ Use `--color` to colorize headers, keys, and values for terminal or demo videos (plain output by default; disabled when writing to a file). Respects the `NO_COLOR` environment variable.
1550
+
1546
1551
  ### Examples
1547
1552
 
1548
1553
  ```bash
@@ -341,6 +341,7 @@ audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py
341
341
  audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py
342
342
  audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py
343
343
  audiometa/test/tests/unit/cli/__init__.py
344
+ audiometa/test/tests/unit/cli/test_cli_colorize.py
344
345
  audiometa/test/tests/unit/cli/test_expand_file_patterns.py
345
346
  audiometa/test/tests/unit/metadata_managers/__init__.py
346
347
  audiometa/test/tests/unit/metadata_managers/conftest.py