audiometa-python 0.6.0__py3-none-any.whl

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 (352) hide show
  1. audiometa/__init__.py +1297 -0
  2. audiometa/__main__.py +6 -0
  3. audiometa/_audio_file.py +607 -0
  4. audiometa/cli.py +476 -0
  5. audiometa/exceptions.py +167 -0
  6. audiometa/manager/_MetadataManager.py +768 -0
  7. audiometa/manager/__init__.py +1 -0
  8. audiometa/manager/_rating_supporting/_RatingSupportingMetadataManager.py +250 -0
  9. audiometa/manager/_rating_supporting/__init__.py +1 -0
  10. audiometa/manager/_rating_supporting/id3v2/_Id3v2Manager.py +1032 -0
  11. audiometa/manager/_rating_supporting/id3v2/__init__.py +25 -0
  12. audiometa/manager/_rating_supporting/id3v2/_id3v2_constants.py +11 -0
  13. audiometa/manager/_rating_supporting/riff/_RiffManager.py +1002 -0
  14. audiometa/manager/_rating_supporting/riff/__init__.py +25 -0
  15. audiometa/manager/_rating_supporting/riff/_riff_constants.py +17 -0
  16. audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +542 -0
  17. audiometa/manager/_rating_supporting/vorbis/__init__.py +17 -0
  18. audiometa/manager/_rating_supporting/vorbis/_vorbis_constants.py +6 -0
  19. audiometa/manager/id3v1/_Id3v1Manager.py +512 -0
  20. audiometa/manager/id3v1/__init__.py +1 -0
  21. audiometa/manager/id3v1/_constants.py +8 -0
  22. audiometa/manager/id3v1/id3v1_raw_metadata.py +242 -0
  23. audiometa/manager/id3v1/id3v1_raw_metadata_key.py +13 -0
  24. audiometa/test/__init__.py +1 -0
  25. audiometa/test/assets/create_test_files.py +72 -0
  26. audiometa/test/helpers/__init__.py +51 -0
  27. audiometa/test/helpers/common/__init__.py +6 -0
  28. audiometa/test/helpers/common/audio_file_creator.py +68 -0
  29. audiometa/test/helpers/common/external_tool_runner.py +74 -0
  30. audiometa/test/helpers/id3v1/__init__.py +8 -0
  31. audiometa/test/helpers/id3v1/id3v1_header_verifier.py +18 -0
  32. audiometa/test/helpers/id3v1/id3v1_metadata_deleter.py +37 -0
  33. audiometa/test/helpers/id3v1/id3v1_metadata_getter.py +61 -0
  34. audiometa/test/helpers/id3v1/id3v1_metadata_setter.py +82 -0
  35. audiometa/test/helpers/id3v2/__init__.py +28 -0
  36. audiometa/test/helpers/id3v2/id3v2_frame_manual_creator.py +349 -0
  37. audiometa/test/helpers/id3v2/id3v2_header_verifier.py +38 -0
  38. audiometa/test/helpers/id3v2/id3v2_metadata_deleter.py +56 -0
  39. audiometa/test/helpers/id3v2/id3v2_metadata_getter.py +189 -0
  40. audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +506 -0
  41. audiometa/test/helpers/riff/__init__.py +8 -0
  42. audiometa/test/helpers/riff/riff_header_verifier.py +85 -0
  43. audiometa/test/helpers/riff/riff_manual_metadata_creator.py +298 -0
  44. audiometa/test/helpers/riff/riff_metadata_deleter.py +56 -0
  45. audiometa/test/helpers/riff/riff_metadata_getter.py +219 -0
  46. audiometa/test/helpers/riff/riff_metadata_setter.py +374 -0
  47. audiometa/test/helpers/scripts/__init__.py +0 -0
  48. audiometa/test/helpers/technical_info_inspector.py +115 -0
  49. audiometa/test/helpers/temp_file_with_metadata.py +82 -0
  50. audiometa/test/helpers/vorbis/__init__.py +8 -0
  51. audiometa/test/helpers/vorbis/vorbis_header_verifier.py +31 -0
  52. audiometa/test/helpers/vorbis/vorbis_metadata_deleter.py +49 -0
  53. audiometa/test/helpers/vorbis/vorbis_metadata_getter.py +67 -0
  54. audiometa/test/helpers/vorbis/vorbis_metadata_setter.py +221 -0
  55. audiometa/test/tests/__init__.py +0 -0
  56. audiometa/test/tests/conftest.py +276 -0
  57. audiometa/test/tests/e2e/__init__.py +0 -0
  58. audiometa/test/tests/e2e/cli/__init__.py +0 -0
  59. audiometa/test/tests/e2e/cli/error_handling/__init__.py +1 -0
  60. audiometa/test/tests/e2e/cli/error_handling/test_command_structure_errors.py +77 -0
  61. audiometa/test/tests/e2e/cli/error_handling/test_file_access_errors.py +130 -0
  62. audiometa/test/tests/e2e/cli/error_handling/test_format_output_errors.py +118 -0
  63. audiometa/test/tests/e2e/cli/error_handling/test_input_validation_errors.py +172 -0
  64. audiometa/test/tests/e2e/cli/error_handling/test_missing_fields_validation.py +49 -0
  65. audiometa/test/tests/e2e/cli/error_handling/test_multiple_files_errors.py +160 -0
  66. audiometa/test/tests/e2e/cli/error_handling/test_rating_validation.py +90 -0
  67. audiometa/test/tests/e2e/cli/error_handling/test_year_validation.py +51 -0
  68. audiometa/test/tests/e2e/cli/read/__init__.py +0 -0
  69. audiometa/test/tests/e2e/cli/read/test_basic.py +58 -0
  70. audiometa/test/tests/e2e/cli/read/test_comprehensive.py +240 -0
  71. audiometa/test/tests/e2e/cli/read/test_formats.py +55 -0
  72. audiometa/test/tests/e2e/cli/read/test_metadata_content.py +164 -0
  73. audiometa/test/tests/e2e/cli/read/test_multiple_files.py +149 -0
  74. audiometa/test/tests/e2e/cli/read/test_options.py +88 -0
  75. audiometa/test/tests/e2e/cli/read/test_unified.py +84 -0
  76. audiometa/test/tests/e2e/cli/test_delete.py +20 -0
  77. audiometa/test/tests/e2e/cli/test_formatting.py +31 -0
  78. audiometa/test/tests/e2e/cli/test_help.py +41 -0
  79. audiometa/test/tests/e2e/cli/write/__init__.py +0 -0
  80. audiometa/test/tests/e2e/cli/write/test_basic.py +51 -0
  81. audiometa/test/tests/e2e/cli/write/test_comprehensive.py +210 -0
  82. audiometa/test/tests/e2e/cli/write/test_force_format.py +336 -0
  83. audiometa/test/tests/e2e/cli/write/test_integer_fields.py +145 -0
  84. audiometa/test/tests/e2e/cli/write/test_list_fields.py +107 -0
  85. audiometa/test/tests/e2e/cli/write/test_rating.py +74 -0
  86. audiometa/test/tests/e2e/cli/write/test_string_fields.py +54 -0
  87. audiometa/test/tests/e2e/cli/write/test_validation.py +85 -0
  88. audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
  89. audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +166 -0
  90. audiometa/test/tests/e2e/workflows/__init__.py +0 -0
  91. audiometa/test/tests/e2e/workflows/test_core_workflows.py +166 -0
  92. audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +318 -0
  93. audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +165 -0
  94. audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +129 -0
  95. audiometa/test/tests/e2e/workflows/test_rating_workflows.py +124 -0
  96. audiometa/test/tests/integration/__init__.py +0 -0
  97. audiometa/test/tests/integration/audio_format/__init__.py +0 -0
  98. audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
  99. audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +108 -0
  100. audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +61 -0
  101. audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +65 -0
  102. audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +69 -0
  103. audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
  104. audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +79 -0
  105. audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +61 -0
  106. audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +67 -0
  107. audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +60 -0
  108. audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
  109. audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +87 -0
  110. audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +62 -0
  111. audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +57 -0
  112. audiometa/test/tests/integration/audio_format/wav/test_wav_with_id3v2_tags.py +83 -0
  113. audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +62 -0
  114. audiometa/test/tests/integration/conftest.py +29 -0
  115. audiometa/test/tests/integration/delete_all_metadata/__init__.py +1 -0
  116. audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +102 -0
  117. audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +77 -0
  118. audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +47 -0
  119. audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +24 -0
  120. audiometa/test/tests/integration/encoding/__init__.py +1 -0
  121. audiometa/test/tests/integration/encoding/test_encoding.py +88 -0
  122. audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +223 -0
  123. audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
  124. audiometa/test/tests/integration/get_full_metadata/test_audio_formats.py +122 -0
  125. audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +250 -0
  126. audiometa/test/tests/integration/get_full_metadata/test_consistency.py +67 -0
  127. audiometa/test/tests/integration/get_full_metadata/test_edge_cases.py +123 -0
  128. audiometa/test/tests/integration/get_full_metadata/test_error_handling.py +40 -0
  129. audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +43 -0
  130. audiometa/test/tests/integration/get_full_metadata/test_options.py +207 -0
  131. audiometa/test/tests/integration/get_full_metadata/test_performance.py +95 -0
  132. audiometa/test/tests/integration/get_full_metadata/test_riff_bext.py +128 -0
  133. audiometa/test/tests/integration/get_full_metadata/test_structure.py +161 -0
  134. audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
  135. audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
  136. audiometa/test/tests/integration/metadata_field/album/test_deleting.py +73 -0
  137. audiometa/test/tests/integration/metadata_field/album/test_reading.py +36 -0
  138. audiometa/test/tests/integration/metadata_field/album/test_writing.py +50 -0
  139. audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
  140. audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +83 -0
  141. audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +38 -0
  142. audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +52 -0
  143. audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
  144. audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +68 -0
  145. audiometa/test/tests/integration/metadata_field/artists/test_reading.py +36 -0
  146. audiometa/test/tests/integration/metadata_field/artists/test_writing.py +46 -0
  147. audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
  148. audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +75 -0
  149. audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +32 -0
  150. audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +56 -0
  151. audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
  152. audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +68 -0
  153. audiometa/test/tests/integration/metadata_field/comment/test_reading.py +36 -0
  154. audiometa/test/tests/integration/metadata_field/comment/test_writing.py +49 -0
  155. audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
  156. audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +75 -0
  157. audiometa/test/tests/integration/metadata_field/composer/test_reading.py +34 -0
  158. audiometa/test/tests/integration/metadata_field/composer/test_writing.py +41 -0
  159. audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
  160. audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +81 -0
  161. audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +35 -0
  162. audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +41 -0
  163. audiometa/test/tests/integration/metadata_field/disc_number/__init__.py +0 -0
  164. audiometa/test/tests/integration/metadata_field/disc_number/test_deleting.py +97 -0
  165. audiometa/test/tests/integration/metadata_field/disc_number/test_reading.py +92 -0
  166. audiometa/test/tests/integration/metadata_field/disc_number/test_writing.py +153 -0
  167. audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
  168. audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +56 -0
  169. audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +54 -0
  170. audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +61 -0
  171. audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
  172. audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
  173. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
  174. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +65 -0
  175. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +25 -0
  176. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +58 -0
  177. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +61 -0
  178. audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +191 -0
  179. audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +62 -0
  180. audiometa/test/tests/integration/metadata_field/genre/test_writing.py +64 -0
  181. audiometa/test/tests/integration/metadata_field/isrc/__init__.py +1 -0
  182. audiometa/test/tests/integration/metadata_field/isrc/test_deleting.py +31 -0
  183. audiometa/test/tests/integration/metadata_field/isrc/test_reading.py +35 -0
  184. audiometa/test/tests/integration/metadata_field/isrc/test_writing.py +165 -0
  185. audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
  186. audiometa/test/tests/integration/metadata_field/language/test_deleting.py +75 -0
  187. audiometa/test/tests/integration/metadata_field/language/test_reading.py +39 -0
  188. audiometa/test/tests/integration/metadata_field/language/test_writing.py +43 -0
  189. audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
  190. audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +129 -0
  191. audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +57 -0
  192. audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +59 -0
  193. audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
  194. audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +88 -0
  195. audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +32 -0
  196. audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +47 -0
  197. audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
  198. audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
  199. audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +81 -0
  200. audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +33 -0
  201. audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +58 -0
  202. audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +117 -0
  203. audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +137 -0
  204. audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
  205. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
  206. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +77 -0
  207. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +55 -0
  208. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +57 -0
  209. audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +192 -0
  210. audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
  211. audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +74 -0
  212. audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +82 -0
  213. audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +59 -0
  214. audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +49 -0
  215. audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +135 -0
  216. audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
  217. audiometa/test/tests/integration/metadata_field/title/test_deleting.py +73 -0
  218. audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +47 -0
  219. audiometa/test/tests/integration/metadata_field/title/test_reading.py +36 -0
  220. audiometa/test/tests/integration/metadata_field/title/test_writing.py +64 -0
  221. audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
  222. audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
  223. audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +43 -0
  224. audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +32 -0
  225. audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +59 -0
  226. audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +73 -0
  227. audiometa/test/tests/integration/multiple_values/__init__.py +1 -0
  228. audiometa/test/tests/integration/multiple_values/reading/__init__.py +1 -0
  229. audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +1 -0
  230. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +23 -0
  231. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +92 -0
  232. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +216 -0
  233. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +84 -0
  234. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +169 -0
  235. audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +209 -0
  236. audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +198 -0
  237. audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +24 -0
  238. audiometa/test/tests/integration/multiple_values/writing/__init__.py +1 -0
  239. audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +1 -0
  240. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +62 -0
  241. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +36 -0
  242. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +34 -0
  243. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +32 -0
  244. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +54 -0
  245. audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +42 -0
  246. audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +98 -0
  247. audiometa/test/tests/integration/reading/__init__.py +1 -0
  248. audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +80 -0
  249. audiometa/test/tests/integration/reading/test_reading_error_handling.py +36 -0
  250. audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
  251. audiometa/test/tests/integration/real_audio_files/test_reading.py +146 -0
  252. audiometa/test/tests/integration/real_audio_files/test_writing.py +198 -0
  253. audiometa/test/tests/integration/technical_info/__init__.py +0 -0
  254. audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
  255. audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +103 -0
  256. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/__init__.py +0 -0
  257. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_audio_data_corruption.py +21 -0
  258. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_flipped_md5.py +29 -0
  259. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_non_flac_error.py +13 -0
  260. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_partial_md5.py +29 -0
  261. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_random_md5.py +29 -0
  262. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_unset_md5.py +56 -0
  263. audiometa/test/tests/integration/technical_info/flac_md5/test_valid_md5.py +21 -0
  264. audiometa/test/tests/integration/technical_info/test_bitrate.py +79 -0
  265. audiometa/test/tests/integration/technical_info/test_channels.py +38 -0
  266. audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +38 -0
  267. audiometa/test/tests/integration/technical_info/test_sample_rate.py +40 -0
  268. audiometa/test/tests/integration/test_audio_file.py +35 -0
  269. audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +95 -0
  270. audiometa/test/tests/integration/writing/__init__.py +0 -0
  271. audiometa/test/tests/integration/writing/test_error_handling.py +44 -0
  272. audiometa/test/tests/integration/writing/test_forced_format.py +224 -0
  273. audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +223 -0
  274. audiometa/test/tests/integration/writing/test_partial_update.py +36 -0
  275. audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
  276. audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +79 -0
  277. audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +76 -0
  278. audiometa/test/tests/integration/writing/writing_strategies/test_sync_strategy.py +215 -0
  279. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
  280. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +42 -0
  281. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +93 -0
  282. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +99 -0
  283. audiometa/test/tests/unit/__init__.py +0 -0
  284. audiometa/test/tests/unit/audio_file/__init__.py +0 -0
  285. audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
  286. audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +26 -0
  287. audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +31 -0
  288. audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +38 -0
  289. audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +190 -0
  290. audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +51 -0
  291. audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +28 -0
  292. audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +31 -0
  293. audiometa/test/tests/unit/audio_file/test_context_manager.py +30 -0
  294. audiometa/test/tests/unit/audio_file/test_file_validation.py +40 -0
  295. audiometa/test/tests/unit/audio_file/test_is_audio_file.py +49 -0
  296. audiometa/test/tests/unit/audio_file/test_operations.py +20 -0
  297. audiometa/test/tests/unit/audio_file/test_path_handling.py +23 -0
  298. audiometa/test/tests/unit/cli/__init__.py +0 -0
  299. audiometa/test/tests/unit/cli/test_expand_file_patterns.py +234 -0
  300. audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
  301. audiometa/test/tests/unit/metadata_managers/conftest.py +142 -0
  302. audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
  303. audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +49 -0
  304. audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +66 -0
  305. audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +343 -0
  306. audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +53 -0
  307. audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
  308. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
  309. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
  310. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +186 -0
  311. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
  312. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +142 -0
  313. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +76 -0
  314. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
  315. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
  316. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +152 -0
  317. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +23 -0
  318. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +77 -0
  319. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
  320. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_configuration_error.py +43 -0
  321. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +151 -0
  322. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +61 -0
  323. audiometa/test/tests/unit/metadata_managers/metadata_field/test_date_format_validation.py +135 -0
  324. audiometa/test/tests/unit/metadata_managers/metadata_field/test_disc_number_validation.py +75 -0
  325. audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_format_validation.py +121 -0
  326. audiometa/test/tests/unit/metadata_managers/metadata_field/test_isrc_type_validation.py +30 -0
  327. audiometa/test/tests/unit/metadata_managers/metadata_field/test_track_number_validation.py +46 -0
  328. audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +22 -0
  329. audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +83 -0
  330. audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +74 -0
  331. audiometa/test/tests/unit/metadata_managers/test_riff_configuration_error.py +26 -0
  332. audiometa/utils/__init__.py +1 -0
  333. audiometa/utils/id3v1_genre_code_map.py +205 -0
  334. audiometa/utils/metadata_format.py +31 -0
  335. audiometa/utils/metadata_writing_strategy.py +16 -0
  336. audiometa/utils/mutagen_exception_handler.py +24 -0
  337. audiometa/utils/os_dependencies_checker/__init__.py +24 -0
  338. audiometa/utils/os_dependencies_checker/base.py +62 -0
  339. audiometa/utils/os_dependencies_checker/config.py +77 -0
  340. audiometa/utils/os_dependencies_checker/macos.py +236 -0
  341. audiometa/utils/os_dependencies_checker/ubuntu.py +95 -0
  342. audiometa/utils/os_dependencies_checker/windows.py +227 -0
  343. audiometa/utils/rating_profiles.py +110 -0
  344. audiometa/utils/tool_path_resolver.py +135 -0
  345. audiometa/utils/types.py +82 -0
  346. audiometa/utils/unified_metadata_key.py +87 -0
  347. audiometa_python-0.6.0.dist-info/METADATA +1593 -0
  348. audiometa_python-0.6.0.dist-info/RECORD +352 -0
  349. audiometa_python-0.6.0.dist-info/WHEEL +5 -0
  350. audiometa_python-0.6.0.dist-info/entry_points.txt +2 -0
  351. audiometa_python-0.6.0.dist-info/licenses/LICENSE +202 -0
  352. audiometa_python-0.6.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,192 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field, update_metadata
4
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
5
+ from audiometa.utils.metadata_format import MetadataFormat
6
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
7
+
8
+
9
+ @pytest.mark.integration
10
+ class TestComprehensiveRatingWriting:
11
+ def test_write_read_with_different_max_values(self):
12
+ basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
13
+
14
+ with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
15
+ # Write with base 100
16
+ test_metadata = {UnifiedMetadataKey.RATING: 50} # 2.5 stars
17
+ update_metadata(
18
+ test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.ID3V2
19
+ )
20
+
21
+ # Read back with base 100
22
+ rating_100 = get_unified_metadata_field(
23
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100
24
+ )
25
+ assert rating_100 == 50
26
+
27
+ # Read back with base 255 (should be normalized)
28
+ rating_255 = get_unified_metadata_field(
29
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=255
30
+ )
31
+ assert rating_255 is not None
32
+ assert isinstance(rating_255, int | float)
33
+ assert rating_255 > 0
34
+
35
+ def test_cross_metadata_format_rating_consistency(self):
36
+ basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
37
+ test_rating = 70
38
+
39
+ # Test ID3v2 metadata format
40
+ with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
41
+ test_metadata = {UnifiedMetadataKey.RATING: test_rating}
42
+ update_metadata(
43
+ test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.ID3V2
44
+ )
45
+ rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
46
+ assert rating is not None
47
+ assert isinstance(rating, int | float)
48
+ assert rating > 0
49
+
50
+ # Test RIFF metadata format
51
+ with temp_file_with_metadata(basic_metadata, "wav") as test_file:
52
+ test_metadata = {UnifiedMetadataKey.RATING: test_rating}
53
+ update_metadata(
54
+ test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.RIFF
55
+ )
56
+ rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
57
+ assert rating is not None
58
+ assert isinstance(rating, int | float)
59
+ assert rating > 0
60
+
61
+ # Test Vorbis metadata format
62
+ with temp_file_with_metadata(basic_metadata, "flac") as test_file:
63
+ test_metadata = {UnifiedMetadataKey.RATING: test_rating}
64
+ update_metadata(
65
+ test_file, test_metadata, normalized_rating_max_value=100, metadata_format=MetadataFormat.VORBIS
66
+ )
67
+ rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100)
68
+ assert rating is not None
69
+ assert isinstance(rating, int | float)
70
+ assert rating > 0
71
+
72
+ def test_metadata_format_specific_rating_profiles(self):
73
+ basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
74
+
75
+ # Test ID3v2 with base 255 non-proportional values
76
+ with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
77
+ test_values = [0, 1, 64, 128, 196, 255]
78
+ for value in test_values:
79
+ test_metadata = {UnifiedMetadataKey.RATING: value}
80
+ update_metadata(
81
+ test_file, test_metadata, normalized_rating_max_value=255, metadata_format=MetadataFormat.ID3V2
82
+ )
83
+ rating = get_unified_metadata_field(
84
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=255
85
+ )
86
+ assert rating is not None
87
+ assert isinstance(rating, int | float)
88
+ assert 0 <= rating <= 255
89
+
90
+ # Test Vorbis with base 100 proportional values
91
+ with temp_file_with_metadata(basic_metadata, "flac") as test_file:
92
+ test_values = [0, 20, 40, 60, 80, 100]
93
+ for value in test_values:
94
+ test_metadata = {UnifiedMetadataKey.RATING: value}
95
+ update_metadata(
96
+ test_file,
97
+ test_metadata,
98
+ normalized_rating_max_value=100,
99
+ metadata_format=MetadataFormat.VORBIS,
100
+ )
101
+ rating = get_unified_metadata_field(
102
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100
103
+ )
104
+ assert rating is not None
105
+ assert rating == value
106
+
107
+ def test_rating_removal_consistency_across_formats(self):
108
+ basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
109
+
110
+ # Test rating removal behavior across all formats
111
+ for format_type, file_ext in [
112
+ (MetadataFormat.ID3V2, "mp3"),
113
+ (MetadataFormat.RIFF, "wav"),
114
+ (MetadataFormat.VORBIS, "flac"),
115
+ ]:
116
+ with temp_file_with_metadata(basic_metadata, file_ext) as test_file:
117
+ # First write a rating
118
+ test_metadata = {UnifiedMetadataKey.RATING: 80}
119
+ update_metadata(test_file, test_metadata, normalized_rating_max_value=100, metadata_format=format_type)
120
+ rating = get_unified_metadata_field(
121
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100
122
+ )
123
+ assert rating == 80
124
+
125
+ # Then remove it with None
126
+ test_metadata = {UnifiedMetadataKey.RATING: None}
127
+ update_metadata(test_file, test_metadata, normalized_rating_max_value=100, metadata_format=format_type)
128
+ rating = get_unified_metadata_field(
129
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100
130
+ )
131
+ # Rating removal behavior may vary - check if it's None or 0
132
+ assert rating is None or rating == 0
133
+
134
+ def test_write_float_rating_values_normalized_mode(self):
135
+ basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
136
+
137
+ # Test float values with max=10 (half-star ratings)
138
+ with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
139
+ test_values = [1.5, 2.5, 3.5, 4.5, 7.5]
140
+ for value in test_values:
141
+ test_metadata = {UnifiedMetadataKey.RATING: value}
142
+ update_metadata(
143
+ test_file, test_metadata, normalized_rating_max_value=10, metadata_format=MetadataFormat.ID3V2
144
+ )
145
+ rating = get_unified_metadata_field(
146
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=10
147
+ )
148
+ assert rating is not None
149
+ assert isinstance(rating, int | float)
150
+ assert rating > 0
151
+
152
+ # Test float values with max=100
153
+ with temp_file_with_metadata(basic_metadata, "flac") as test_file:
154
+ test_values = [1.5, 7.5, 15.0, 25.5, 50.0]
155
+ for value in test_values:
156
+ test_metadata = {UnifiedMetadataKey.RATING: value}
157
+ update_metadata(
158
+ test_file,
159
+ test_metadata,
160
+ normalized_rating_max_value=100,
161
+ metadata_format=MetadataFormat.VORBIS,
162
+ )
163
+ rating = get_unified_metadata_field(
164
+ test_file, UnifiedMetadataKey.RATING, normalized_rating_max_value=100
165
+ )
166
+ assert rating is not None
167
+ assert isinstance(rating, int | float)
168
+ assert rating >= 0
169
+
170
+ @pytest.mark.parametrize("rating_value", [1.5, 75.7, 128.5])
171
+ def test_write_float_rating_values_raw_mode(self, rating_value):
172
+ basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
173
+
174
+ # Test fractional float values in raw mode (no normalization) - should raise error
175
+ from audiometa.exceptions import InvalidRatingValueError
176
+
177
+ with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
178
+ test_metadata = {UnifiedMetadataKey.RATING: rating_value}
179
+ with pytest.raises(InvalidRatingValueError) as exc_info:
180
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.ID3V2)
181
+ assert "In raw mode, float values must be whole numbers" in str(exc_info.value)
182
+
183
+ @pytest.mark.parametrize("rating_value", [0.0, 128.0, 196.0, 255.0])
184
+ def test_write_whole_number_float_rating_values_raw_mode(self, rating_value):
185
+ basic_metadata = {"title": "Test Title", "artist": "Test Artist"}
186
+
187
+ # Test whole-number float values in raw mode (no normalization) - should be accepted and converted to int
188
+ with temp_file_with_metadata(basic_metadata, "mp3") as test_file:
189
+ test_metadata = {UnifiedMetadataKey.RATING: rating_value}
190
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.ID3V2)
191
+ rating = get_unified_metadata_field(test_file, UnifiedMetadataKey.RATING)
192
+ assert rating == int(rating_value)
@@ -0,0 +1,74 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field, update_metadata
4
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
5
+ from audiometa.utils.metadata_format import MetadataFormat
6
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
7
+
8
+
9
+ @pytest.mark.integration
10
+ class TestReleaseDateDeleting:
11
+ def test_delete_release_date_id3v2(self):
12
+ with temp_file_with_metadata({}, "mp3") as test_file:
13
+ update_metadata(
14
+ test_file, {UnifiedMetadataKey.RELEASE_DATE: "2023-01-01"}, metadata_format=MetadataFormat.ID3V2
15
+ )
16
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) == "2023-01-01"
17
+
18
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: None}, metadata_format=MetadataFormat.ID3V2)
19
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) is None
20
+
21
+ def test_delete_release_date_id3v1(self):
22
+ with temp_file_with_metadata({}, "mp3") as test_file:
23
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: "2023"}, metadata_format=MetadataFormat.ID3V1)
24
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) == "2023"
25
+
26
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: None}, metadata_format=MetadataFormat.ID3V1)
27
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) is None
28
+
29
+ def test_delete_release_date_riff(self):
30
+ with temp_file_with_metadata({}, "wav") as test_file:
31
+ update_metadata(
32
+ test_file, {UnifiedMetadataKey.RELEASE_DATE: "2023-01-01"}, metadata_format=MetadataFormat.RIFF
33
+ )
34
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) == "2023-01-01"
35
+
36
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: None}, metadata_format=MetadataFormat.RIFF)
37
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) is None
38
+
39
+ def test_delete_release_date_vorbis(self):
40
+ with temp_file_with_metadata({}, "flac") as test_file:
41
+ update_metadata(
42
+ test_file, {UnifiedMetadataKey.RELEASE_DATE: "2023-01-01"}, metadata_format=MetadataFormat.VORBIS
43
+ )
44
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) == "2023-01-01"
45
+
46
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: None}, metadata_format=MetadataFormat.VORBIS)
47
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) is None
48
+
49
+ def test_delete_release_date_preserves_other_fields(self):
50
+ with temp_file_with_metadata({}, "mp3") as test_file:
51
+ update_metadata(
52
+ test_file,
53
+ {
54
+ UnifiedMetadataKey.RELEASE_DATE: "2023-01-01",
55
+ UnifiedMetadataKey.TITLE: "Test Title",
56
+ UnifiedMetadataKey.ARTISTS: ["Test Artist"],
57
+ },
58
+ )
59
+
60
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: None})
61
+
62
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) is None
63
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.TITLE) == "Test Title"
64
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.ARTISTS) == ["Test Artist"]
65
+
66
+ def test_delete_release_date_already_none(self):
67
+ with temp_file_with_metadata({}, "mp3") as test_file:
68
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: None})
69
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) is None
70
+
71
+ def test_delete_release_date_empty_string(self):
72
+ with temp_file_with_metadata({}, "mp3") as test_file:
73
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: ""}, metadata_format=MetadataFormat.ID3V2)
74
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE) is None
@@ -0,0 +1,82 @@
1
+ import pytest
2
+
3
+ from audiometa import update_metadata
4
+ from audiometa.exceptions import InvalidMetadataFieldFormatError
5
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
6
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
7
+
8
+
9
+ @pytest.mark.integration
10
+ class TestReleaseDateErrorHandling:
11
+ def test_invalid_format_wrong_separator_mp3(self):
12
+ with temp_file_with_metadata({}, "mp3") as test_file:
13
+ invalid_dates = [
14
+ "2024/01/01",
15
+ "2024.01.01",
16
+ "2024 01 01",
17
+ ]
18
+ for invalid_date in invalid_dates:
19
+ with pytest.raises(InvalidMetadataFieldFormatError) as exc_info:
20
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: invalid_date})
21
+ error = exc_info.value
22
+ assert error.field == UnifiedMetadataKey.RELEASE_DATE.value
23
+ assert error.value == invalid_date
24
+
25
+ def test_invalid_format_single_digit_mp3(self):
26
+ with temp_file_with_metadata({}, "mp3") as test_file:
27
+ invalid_dates = [
28
+ "2024-1-1",
29
+ "2024-1-01",
30
+ "2024-01-1",
31
+ ]
32
+ for invalid_date in invalid_dates:
33
+ with pytest.raises(InvalidMetadataFieldFormatError):
34
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: invalid_date})
35
+
36
+ def test_invalid_format_short_year_mp3(self):
37
+ with temp_file_with_metadata({}, "mp3") as test_file:
38
+ invalid_dates = ["24", "024", "999"]
39
+ for invalid_date in invalid_dates:
40
+ with pytest.raises(InvalidMetadataFieldFormatError):
41
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: invalid_date})
42
+
43
+ def test_invalid_format_non_numeric_mp3(self):
44
+ with temp_file_with_metadata({}, "mp3") as test_file:
45
+ invalid_dates = [
46
+ "not-a-date",
47
+ "2024-abc-01",
48
+ "abcd-01-01",
49
+ ]
50
+ for invalid_date in invalid_dates:
51
+ with pytest.raises(InvalidMetadataFieldFormatError):
52
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: invalid_date})
53
+
54
+ def test_invalid_format_wrong_separator_flac(self):
55
+ with temp_file_with_metadata({}, "flac") as test_file, pytest.raises(InvalidMetadataFieldFormatError):
56
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: "2024/01/01"})
57
+
58
+ def test_invalid_format_wrong_separator_wav(self):
59
+ with temp_file_with_metadata({}, "wav") as test_file, pytest.raises(InvalidMetadataFieldFormatError):
60
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: "2024/01/01"})
61
+
62
+ def test_valid_format_yyyy_passes(self):
63
+ with temp_file_with_metadata({}, "mp3") as test_file:
64
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: "2024"})
65
+
66
+ def test_valid_format_yyyy_mm_dd_passes(self):
67
+ with temp_file_with_metadata({}, "mp3") as test_file:
68
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: "2024-01-01"})
69
+
70
+ def test_none_value_allowed(self):
71
+ with temp_file_with_metadata({}, "mp3") as test_file:
72
+ update_metadata(test_file, {UnifiedMetadataKey.RELEASE_DATE: None})
73
+
74
+ def test_invalid_format_with_multiple_fields(self):
75
+ with temp_file_with_metadata({}, "mp3") as test_file, pytest.raises(InvalidMetadataFieldFormatError):
76
+ update_metadata(
77
+ test_file,
78
+ {
79
+ UnifiedMetadataKey.TITLE: "Valid Title",
80
+ UnifiedMetadataKey.RELEASE_DATE: "2024/01/01",
81
+ },
82
+ )
@@ -0,0 +1,59 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field
4
+ from audiometa.test.helpers.id3v1.id3v1_metadata_setter import ID3v1MetadataSetter
5
+ from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
6
+ from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
7
+ from audiometa.test.helpers.riff.riff_metadata_getter import RIFFMetadataGetter
8
+ from audiometa.test.helpers.riff.riff_metadata_setter import RIFFMetadataSetter
9
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
10
+ from audiometa.test.helpers.vorbis.vorbis_metadata_getter import VorbisMetadataGetter
11
+ from audiometa.test.helpers.vorbis.vorbis_metadata_setter import VorbisMetadataSetter
12
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
13
+
14
+
15
+ @pytest.mark.integration
16
+ class TestReleaseDateReading:
17
+ def test_id3v1(self):
18
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v1") as test_file:
19
+ ID3v1MetadataSetter.set_max_metadata(test_file)
20
+
21
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
22
+ assert release_date == "9999"
23
+
24
+ def test_id3v2_3(self):
25
+ with temp_file_with_metadata({"title": "Test Song"}, "mp3") as test_file:
26
+ ID3v2MetadataSetter.set_release_date(test_file, "9999-12-31", version="2.3")
27
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
28
+ assert raw_metadata["TDAT"] == ["3112"]
29
+ assert raw_metadata["TYER"] == ["9999"]
30
+
31
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
32
+ assert release_date == "9999-12-31"
33
+
34
+ def test_id3v2_4(self):
35
+ with temp_file_with_metadata({"title": "Test Song"}, "mp3") as test_file:
36
+ ID3v2MetadataSetter.set_release_date(test_file, "9999-12-31", version="2.4")
37
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
38
+ assert raw_metadata["TDRC"] == ["9999-12-31"]
39
+
40
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
41
+ assert release_date == "9999-12-31"
42
+
43
+ def test_vorbis(self):
44
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
45
+ VorbisMetadataSetter.set_release_date(test_file, "9999-12-31")
46
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
47
+ assert "DATE=9999-12-31" in raw_metadata
48
+
49
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
50
+ assert release_date == "9999-12-31"
51
+
52
+ def test_riff(self):
53
+ with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
54
+ RIFFMetadataSetter.set_release_date(test_file, "9999-12-31")
55
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
56
+ assert "TAG:date=9999-12-31" in raw_metadata
57
+
58
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
59
+ assert release_date == "9999-12-31"
@@ -0,0 +1,49 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field, update_metadata
4
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
5
+ from audiometa.utils.metadata_format import MetadataFormat
6
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
7
+
8
+
9
+ @pytest.mark.integration
10
+ class TestReleaseDateWriting:
11
+ def test_id3v2(self):
12
+ with temp_file_with_metadata({}, "mp3") as test_file:
13
+ test_release_date = "2024-01-01"
14
+ test_metadata = {UnifiedMetadataKey.RELEASE_DATE: test_release_date}
15
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.ID3V2)
16
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
17
+ assert release_date == test_release_date
18
+
19
+ def test_riff(self):
20
+ with temp_file_with_metadata({}, "wav") as test_file:
21
+ test_release_date = "2024-02-01"
22
+ test_metadata = {UnifiedMetadataKey.RELEASE_DATE: test_release_date}
23
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.RIFF)
24
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
25
+ assert release_date == test_release_date
26
+
27
+ def test_vorbis(self):
28
+ with temp_file_with_metadata({}, "flac") as test_file:
29
+ test_release_date = "2024-03-01"
30
+ test_metadata = {UnifiedMetadataKey.RELEASE_DATE: test_release_date}
31
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.VORBIS)
32
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
33
+ assert release_date == test_release_date
34
+
35
+ def test_id3v1(self):
36
+ with temp_file_with_metadata({}, "mp3") as test_file:
37
+ test_release_date = "2024"
38
+ test_metadata = {UnifiedMetadataKey.RELEASE_DATE: test_release_date}
39
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.ID3V1)
40
+ release_date = get_unified_metadata_field(test_file, UnifiedMetadataKey.RELEASE_DATE)
41
+ assert release_date == test_release_date
42
+
43
+ def test_invalid_type_raises(self):
44
+ from audiometa.exceptions import InvalidMetadataFieldTypeError
45
+
46
+ with temp_file_with_metadata({}, "mp3") as test_file:
47
+ bad_metadata = {UnifiedMetadataKey.RELEASE_DATE: 20240101}
48
+ with pytest.raises(InvalidMetadataFieldTypeError):
49
+ update_metadata(test_file, bad_metadata)
@@ -0,0 +1,135 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from audiometa import UnifiedMetadataKey, get_unified_metadata_field, update_metadata
6
+ from audiometa.exceptions import (
7
+ InvalidMetadataFieldTypeError,
8
+ MetadataFieldNotSupportedByLibError,
9
+ MetadataFieldNotSupportedByMetadataFormatError,
10
+ )
11
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
12
+ from audiometa.utils.metadata_format import MetadataFormat
13
+
14
+
15
+ @pytest.mark.integration
16
+ class TestMetadataFieldValidation:
17
+ """Test that get_unified_metadata_field raises MetadataFieldNotSupportedByMetadataFormatError when a field is not
18
+ supported by the specified format.
19
+ """
20
+
21
+ def test_replaygain_not_supported_by_riff(self, sample_wav_file: Path):
22
+ with pytest.raises(
23
+ MetadataFieldNotSupportedByMetadataFormatError,
24
+ match="UnifiedMetadataKey.REPLAYGAIN metadata not supported by RIFF format",
25
+ ):
26
+ get_unified_metadata_field(
27
+ sample_wav_file, UnifiedMetadataKey.REPLAYGAIN, metadata_format=MetadataFormat.RIFF
28
+ )
29
+
30
+ def test_replaygain_not_supported_by_id3v1(self, sample_mp3_file: Path):
31
+ with pytest.raises(
32
+ MetadataFieldNotSupportedByMetadataFormatError,
33
+ match="UnifiedMetadataKey.REPLAYGAIN metadata not supported by ID3v1 format",
34
+ ):
35
+ get_unified_metadata_field(
36
+ sample_mp3_file, UnifiedMetadataKey.REPLAYGAIN, metadata_format=MetadataFormat.ID3V1
37
+ )
38
+
39
+ def test_album_artists_not_supported_by_id3v1(self, sample_mp3_file: Path):
40
+ with pytest.raises(
41
+ MetadataFieldNotSupportedByMetadataFormatError,
42
+ match="UnifiedMetadataKey.ALBUM_ARTISTS metadata not supported by ID3v1 format",
43
+ ):
44
+ get_unified_metadata_field(
45
+ sample_mp3_file, UnifiedMetadataKey.ALBUM_ARTISTS, metadata_format=MetadataFormat.ID3V1
46
+ )
47
+
48
+ def test_supported_field_works_with_riff(self, sample_wav_file: Path):
49
+ title = get_unified_metadata_field(
50
+ sample_wav_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.RIFF
51
+ )
52
+ assert title is None or isinstance(title, str)
53
+
54
+ def test_supported_field_works_with_id3v1(self, sample_mp3_file: Path):
55
+ title = get_unified_metadata_field(
56
+ sample_mp3_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.ID3V1
57
+ )
58
+ assert title is None or isinstance(title, str)
59
+
60
+ def test_unsupported_field_without_format_specification(self, sample_wav_file: Path):
61
+ bpm = get_unified_metadata_field(sample_wav_file, UnifiedMetadataKey.BPM)
62
+ assert bpm is None or isinstance(bpm, int)
63
+
64
+ def test_rating_supported_by_riff_indirectly(self, sample_wav_file: Path):
65
+ rating = get_unified_metadata_field(
66
+ sample_wav_file, UnifiedMetadataKey.RATING, metadata_format=MetadataFormat.RIFF
67
+ )
68
+ assert rating is None or isinstance(rating, int)
69
+
70
+ def test_field_not_supported_by_lib_exception_exists(self):
71
+ msg = "Test field not supported by library"
72
+ with pytest.raises(MetadataFieldNotSupportedByLibError, match="Test field not supported by library"):
73
+ raise MetadataFieldNotSupportedByLibError(msg)
74
+
75
+ def test_field_not_supported_by_lib_concept(self):
76
+ with (
77
+ temp_file_with_metadata({}, "wav") as test_file,
78
+ pytest.raises(MetadataFieldNotSupportedByLibError, match="Test field not supported by library"),
79
+ ):
80
+ get_unified_metadata_field(test_file, "Test field not supported by library")
81
+
82
+ def test_valid_string_key_works(self, sample_mp3_file: Path):
83
+ """Test that passing a string matching a UnifiedMetadataKey enum value works."""
84
+ # Test various string values that match enum values
85
+ title = get_unified_metadata_field(sample_mp3_file, "title")
86
+ assert title is None or isinstance(title, str)
87
+
88
+ artists = get_unified_metadata_field(sample_mp3_file, "artists")
89
+ assert artists is None or isinstance(artists, list)
90
+
91
+ rating = get_unified_metadata_field(sample_mp3_file, "rating")
92
+ assert rating is None or isinstance(rating, int)
93
+
94
+ # Verify it works the same as enum
95
+ title_enum = get_unified_metadata_field(sample_mp3_file, UnifiedMetadataKey.TITLE)
96
+ assert title == title_enum
97
+
98
+ def test_invalid_string_key_raises_error(self, sample_mp3_file: Path):
99
+ """Test that passing an invalid string raises MetadataFieldNotSupportedByLibError."""
100
+ with pytest.raises(
101
+ MetadataFieldNotSupportedByLibError, match="invalid_field metadata not supported by the library"
102
+ ):
103
+ get_unified_metadata_field(sample_mp3_file, "invalid_field")
104
+
105
+ def test_all_valid_enum_values_work(self, sample_mp3_file: Path):
106
+ """Test that all valid UnifiedMetadataKey enum values are accepted (not rejected as invalid enum values)."""
107
+ for key in UnifiedMetadataKey:
108
+ # Should not raise MetadataFieldNotSupportedByLibError for invalid enum value
109
+ # (it's okay if it raises for "not supported by any format" - that's different)
110
+ try:
111
+ result = get_unified_metadata_field(sample_mp3_file, key)
112
+ # Result can be None if field is not present, which is valid
113
+ assert result is None or isinstance(result, str | int | float | list)
114
+ except MetadataFieldNotSupportedByLibError as e:
115
+ # Only fail if the error message indicates invalid enum value
116
+ # It's okay if it raises for "not supported by any format" - that's different
117
+ if "not supported by any format" not in str(e):
118
+ raise
119
+
120
+ def test_invalid_metadata_field_type_error_wrong_type_for_list_field(self):
121
+ with temp_file_with_metadata({}, "mp3") as test_file:
122
+ with pytest.raises(InvalidMetadataFieldTypeError) as exc_info:
123
+ update_metadata(test_file, {UnifiedMetadataKey.ARTISTS: "should be list"})
124
+ error = exc_info.value
125
+ assert error.field == UnifiedMetadataKey.ARTISTS.value
126
+ assert "list" in error.expected_type.lower()
127
+ assert error.value == "should be list"
128
+
129
+ def test_invalid_metadata_field_type_error_wrong_type_for_string_field(self):
130
+ with temp_file_with_metadata({}, "mp3") as test_file:
131
+ with pytest.raises(InvalidMetadataFieldTypeError) as exc_info:
132
+ update_metadata(test_file, {UnifiedMetadataKey.TITLE: 12345})
133
+ error = exc_info.value
134
+ assert error.field == UnifiedMetadataKey.TITLE.value
135
+ assert error.value == 12345