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,191 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field
4
+ from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
5
+ from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
6
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
7
+ from audiometa.test.helpers.vorbis.vorbis_metadata_getter import VorbisMetadataGetter
8
+ from audiometa.test.helpers.vorbis.vorbis_metadata_setter import VorbisMetadataSetter
9
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
10
+
11
+
12
+ @pytest.mark.integration
13
+ class TestGenreSmartReading:
14
+ def test_single_entry_text_with_codes_and_separators_and_without_parentheses(self):
15
+ """Test single genre entry with text with codes and separators without parentheses:
16
+
17
+ '17/6' -> ['Rock', 'Grunge']
18
+ """
19
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
20
+ # Set genre with text with codes and separators without parentheses
21
+ ID3v2MetadataSetter.set_genres(test_file, ["17/6"], version="2.4")
22
+
23
+ # Read via API
24
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
25
+ assert genres == ["Rock", "Grunge"]
26
+
27
+ def test_single_entry_codes_without_separators_id3v2(self):
28
+ """Test single genre entry with codes without separators: '(17)(6)' -> ['Rock', 'Grunge']."""
29
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
30
+ # Set genre with codes without separators
31
+ ID3v2MetadataSetter.set_genres(test_file, ["(17)(6)"], version="2.4")
32
+
33
+ # Validate raw metadata
34
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
35
+ assert "(17)(6)" in raw_metadata["TCON"]
36
+
37
+ # Read via API
38
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
39
+
40
+ assert genres == ["Rock", "Grunge"]
41
+
42
+ def test_single_entry_codes_without_separators_vorbis(self):
43
+ """Test single genre entry with codes without separators in Vorbis: '(17)(6)' -> ['Rock', 'Grunge']."""
44
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
45
+ # Set genre with codes without separators
46
+ VorbisMetadataSetter.set_genres(test_file, ["(17)(6)"])
47
+
48
+ # Validate raw metadata
49
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
50
+ assert "(17)(6)" in raw_metadata
51
+
52
+ # Read via API
53
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
54
+ assert genres == ["Rock", "Grunge"]
55
+
56
+ def test_code_text_then_text_part_even_if_different(self):
57
+ """Test code+text: '(17)Rock' -> 'Rock' (text part only)."""
58
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
59
+ # Set genre with code+text
60
+ ID3v2MetadataSetter.set_genres(test_file, ["(17)Rock"], version="2.4")
61
+
62
+ # Read via API
63
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
64
+
65
+ assert genres == ["Rock"]
66
+
67
+ def test_single_entry_code_text_without_separators(self):
68
+ """Test single genre entry with code+text without separators: '(17)Rock(6)Blues' -> ['Rock', 'Grunge']."""
69
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
70
+ # Set genre with code+text without separators
71
+ ID3v2MetadataSetter.set_genres(test_file, ["(17)Rock(6)Blues"], version="2.4")
72
+
73
+ # Validate raw metadata
74
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
75
+ assert "(17)Rock(6)Blues" in raw_metadata["TCON"]
76
+
77
+ # assert no null separators in raw metadata genre
78
+ assert not any("\x00" in s for s in raw_metadata["TCON"])
79
+
80
+ # Read via API
81
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
82
+
83
+ assert genres == ["Rock", "Blues"]
84
+
85
+ def test_one_code_and_one_code_text_in_single_entry(self):
86
+ """Test one code and one code+text: '(17)', '(6)Grunge' -> ['Rock', 'Grunge']."""
87
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
88
+ # Set genre with one code and one code+text
89
+ ID3v2MetadataSetter.set_genres(test_file, ["(17)(6)Grunge"], version="2.4")
90
+
91
+ # Read via API
92
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
93
+
94
+ assert genres == ["Rock", "Grunge"]
95
+
96
+ def test_one_code_and_one_code_text_in_multi_entries(self):
97
+ """Test one code and one code+text: '(17)', '(6)Grunge' -> ['Rock', 'Grunge']."""
98
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
99
+ # Set genre with one code and one code+text
100
+ ID3v2MetadataSetter.set_genres(test_file, ["(17)", "(6)Grunge"], in_separate_frames=True, version="2.4")
101
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
102
+
103
+ assert "(17)" in raw_metadata["TCON"]
104
+ assert "(6)Grunge" in raw_metadata["TCON"]
105
+
106
+ # Read via API
107
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
108
+
109
+ assert genres == ["Rock", "Grunge"]
110
+
111
+ def test_single_entry_text_with_slash_separators(self):
112
+ """Test single genre entry with text with slash separators: 'Rock/Blues' -> ['Rock', 'Blues']."""
113
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
114
+ # Set genre with text with separators
115
+ ID3v2MetadataSetter.set_genres(test_file, ["Rock/Blues"], version="2.4")
116
+
117
+ # Validate raw metadata
118
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
119
+ assert "Rock/Blues" in raw_metadata["TCON"]
120
+
121
+ # Read via API
122
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
123
+
124
+ assert genres == ["Rock", "Blues"]
125
+
126
+ def test_single_entry_text_with_semicolon_separators(self):
127
+ """Test single genre entry with text with semicolon separators:
128
+
129
+ 'Rock; Alternative' -> ['Rock', 'Alternative']
130
+ """
131
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
132
+ # Set genre with text with separators
133
+ ID3v2MetadataSetter.set_genres(test_file, ["Rock; Alternative"], version="2.4")
134
+
135
+ # Validate raw metadata
136
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
137
+ assert "Rock; Alternative" in raw_metadata["TCON"]
138
+
139
+ # Read via API
140
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
141
+
142
+ assert genres == ["Rock", "Alternative"]
143
+
144
+ def test_multi_codes_with_text_with_separators(self):
145
+ """Test single genre entry with mixed separators: '(17)Rock/(6)Blues' -> ['Rock', 'Blues']."""
146
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
147
+ # Set genre with mixed separators
148
+ ID3v2MetadataSetter.set_genres(test_file, ["(17)Rock/(6)Blues"], version="2.4")
149
+
150
+ # Validate raw metadata
151
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
152
+ assert "(17)Rock/(6)Blues" in raw_metadata["TCON"]
153
+
154
+ # Read via API
155
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
156
+
157
+ assert genres == ["Rock", "Blues"]
158
+
159
+ def test_multiple_entries_return_as_is(self):
160
+ """Test multiple genre entries: separate TCON frames, return as-is without parsing."""
161
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
162
+ # Set multiple genres in separate frames
163
+ ID3v2MetadataSetter.set_genres(test_file, ["Rock/Grunge", "Blues"], in_separate_frames=True, version="2.4")
164
+
165
+ # Read via API
166
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
167
+
168
+ # Should return as-is without parsing
169
+ assert set(genres) == {"Rock/Grunge", "Blues"}
170
+
171
+ def test_code_to_name_conversion_with_unknown_code(self):
172
+ """Test code conversion: '(17)' -> 'Rock', (unknown code) -> unknown code in parentheses should be ignored."""
173
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
174
+ # Set genre with known and unknown codes
175
+ ID3v2MetadataSetter.set_genres(test_file, ["(17)", "(999)"], version="2.4")
176
+
177
+ # Read via API
178
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
179
+
180
+ assert genres == ["Rock"]
181
+
182
+ def test_code_text_uses_text_part(self):
183
+ """Test code+text: '(199)Rock' -> 'Rock' (text part only)."""
184
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
185
+ # Set genre with code+text
186
+ ID3v2MetadataSetter.set_genres(test_file, ["(199)Rock"], version="2.4")
187
+
188
+ # Read via API
189
+ genres = get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES)
190
+
191
+ assert genres == ["Rock"]
@@ -0,0 +1,62 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field, update_metadata
4
+ from audiometa.test.helpers.id3v1 import ID3v1MetadataSetter
5
+ from audiometa.test.helpers.id3v2 import ID3v2MetadataSetter
6
+ from audiometa.test.helpers.riff import RIFFMetadataSetter
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+ from audiometa.test.helpers.vorbis import VorbisMetadataSetter
9
+ from audiometa.utils.metadata_format import MetadataFormat
10
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
11
+
12
+
13
+ @pytest.mark.integration
14
+ class TestGenreDeleting:
15
+ def test_delete_genre_id3v2(self):
16
+ with temp_file_with_metadata({}, "mp3") as test_file:
17
+ ID3v2MetadataSetter.set_genre(test_file, "Rock")
18
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
19
+
20
+ update_metadata(test_file, {UnifiedMetadataKey.GENRES_NAMES: None}, metadata_format=MetadataFormat.ID3V2)
21
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) is None
22
+
23
+ def test_delete_genre_id3v1(self):
24
+ with temp_file_with_metadata({}, "id3v1") as test_file:
25
+ ID3v1MetadataSetter.set_genre(test_file, "Rock")
26
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
27
+
28
+ update_metadata(test_file, {UnifiedMetadataKey.GENRES_NAMES: None}, metadata_format=MetadataFormat.ID3V1)
29
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) is None
30
+
31
+ def test_delete_genre_riff(self):
32
+ with temp_file_with_metadata({}, "wav") as test_file:
33
+ RIFFMetadataSetter.set_genres(test_file, ["Rock"])
34
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
35
+
36
+ update_metadata(test_file, {UnifiedMetadataKey.GENRES_NAMES: None}, metadata_format=MetadataFormat.RIFF)
37
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) is None
38
+
39
+ def test_delete_genre_vorbis(self):
40
+ with temp_file_with_metadata({}, "flac") as test_file:
41
+ VorbisMetadataSetter.set_genre(test_file, "Rock")
42
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
43
+
44
+ update_metadata(test_file, {UnifiedMetadataKey.GENRES_NAMES: None}, metadata_format=MetadataFormat.VORBIS)
45
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) is None
46
+
47
+ def test_delete_genre_preserves_other_fields(self):
48
+ with temp_file_with_metadata({}, "mp3") as test_file:
49
+ ID3v2MetadataSetter.set_genre(test_file, "Rock")
50
+ ID3v2MetadataSetter.set_title(test_file, "Test Title")
51
+ ID3v2MetadataSetter.set_artists(test_file, "Test Artist")
52
+
53
+ update_metadata(test_file, {UnifiedMetadataKey.GENRES_NAMES: None}, metadata_format=MetadataFormat.ID3V2)
54
+
55
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) is None
56
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.TITLE) == "Test Title"
57
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.ARTISTS) == ["Test Artist"]
58
+
59
+ def test_delete_genre_already_none(self):
60
+ with temp_file_with_metadata({}, "mp3") as test_file:
61
+ update_metadata(test_file, {UnifiedMetadataKey.GENRES_NAMES: None}, metadata_format=MetadataFormat.ID3V2)
62
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.GENRES_NAMES) is None
@@ -0,0 +1,64 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata, update_metadata
4
+ from audiometa.test.helpers.id3v1.id3v1_metadata_getter import ID3v1MetadataGetter
5
+ from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
6
+ from audiometa.test.helpers.riff.riff_metadata_getter import RIFFMetadataGetter
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+ from audiometa.utils.metadata_format import MetadataFormat
9
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
10
+
11
+
12
+ @pytest.mark.integration
13
+ class TestGenreWriting:
14
+ def test_id3v1(self):
15
+ with temp_file_with_metadata({}, "mp3") as test_file:
16
+ test_genre = "Rock"
17
+ test_metadata = {UnifiedMetadataKey.GENRES_NAMES: [test_genre]}
18
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.ID3V1)
19
+
20
+ raw_metadata = ID3v1MetadataGetter.get_raw_metadata(test_file)
21
+ assert raw_metadata["genre"] == 17
22
+
23
+ def test_id3v2_3(self):
24
+ with temp_file_with_metadata({}, "mp3") as test_file:
25
+ test_genre = "Test Genre ID3v2"
26
+ test_metadata = {UnifiedMetadataKey.GENRES_NAMES: [test_genre]}
27
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 3, 0))
28
+
29
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
30
+ assert raw_metadata["TCON"] == [test_genre]
31
+
32
+ def test_id3v2_4(self):
33
+ with temp_file_with_metadata({}, "mp3") as test_file:
34
+ test_genre = "Test Genre ID3v2"
35
+ test_metadata = {UnifiedMetadataKey.GENRES_NAMES: [test_genre]}
36
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 4, 0))
37
+
38
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
39
+ assert raw_metadata["TCON"] == [test_genre]
40
+
41
+ def test_riff(self):
42
+ with temp_file_with_metadata({}, "wav") as test_file:
43
+ test_genre = "Rock"
44
+ test_metadata = {UnifiedMetadataKey.GENRES_NAMES: [test_genre]}
45
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.RIFF)
46
+
47
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
48
+ assert "TAG:genre=Rock" in raw_metadata
49
+
50
+ def test_vorbis(self):
51
+ with temp_file_with_metadata({}, "flac") as test_file:
52
+ test_genre = "Test Genre Vorbis"
53
+ test_metadata = {UnifiedMetadataKey.GENRES_NAMES: [test_genre]}
54
+ update_metadata(test_file, test_metadata, metadata_format=MetadataFormat.VORBIS)
55
+ metadata = get_unified_metadata(test_file)
56
+ assert metadata.get(UnifiedMetadataKey.GENRES_NAMES) == [test_genre]
57
+
58
+ def test_invalid_type_raises(self):
59
+ from audiometa.exceptions import InvalidMetadataFieldTypeError
60
+
61
+ with temp_file_with_metadata({}, "mp3") as test_file:
62
+ bad_metadata = {UnifiedMetadataKey.GENRES_NAMES: 123}
63
+ with pytest.raises(InvalidMetadataFieldTypeError):
64
+ update_metadata(test_file, bad_metadata)
@@ -0,0 +1 @@
1
+ """Integration tests for ISRC metadata field."""
@@ -0,0 +1,31 @@
1
+ """Tests for deleting ISRC metadata field."""
2
+
3
+ import pytest
4
+
5
+ from audiometa import delete_all_metadata, get_unified_metadata_field
6
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
7
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
8
+
9
+
10
+ @pytest.mark.integration
11
+ class TestISRCDeleting:
12
+ def test_id3v2_isrc_deleted_with_all_metadata(self):
13
+ """Test that ISRC is deleted when deleting all metadata from MP3."""
14
+ with temp_file_with_metadata({"isrc": "USRC17607839"}, "mp3") as test_file:
15
+ delete_all_metadata(test_file)
16
+ isrc = get_unified_metadata_field(test_file, UnifiedMetadataKey.ISRC)
17
+ assert isrc is None
18
+
19
+ def test_vorbis_isrc_deleted_with_all_metadata(self):
20
+ """Test that ISRC is deleted when deleting all metadata from FLAC."""
21
+ with temp_file_with_metadata({"isrc": "GBUM71505078"}, "flac") as test_file:
22
+ delete_all_metadata(test_file)
23
+ isrc = get_unified_metadata_field(test_file, UnifiedMetadataKey.ISRC)
24
+ assert isrc is None
25
+
26
+ def test_riff_isrc_deleted_with_all_metadata(self):
27
+ """Test that ISRC is deleted when deleting all metadata from WAV."""
28
+ with temp_file_with_metadata({"isrc": "FRXM01500014"}, "wav") as test_file:
29
+ delete_all_metadata(test_file)
30
+ isrc = get_unified_metadata_field(test_file, UnifiedMetadataKey.ISRC)
31
+ assert isrc is None
@@ -0,0 +1,35 @@
1
+ """Tests for reading ISRC metadata field across different formats."""
2
+
3
+ import pytest
4
+
5
+ from audiometa import get_unified_metadata_field
6
+ from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
7
+ from audiometa.test.helpers.riff import RIFFMetadataSetter
8
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
9
+ from audiometa.test.helpers.vorbis import VorbisMetadataSetter
10
+ from audiometa.utils.metadata_format import MetadataFormat
11
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
12
+
13
+
14
+ @pytest.mark.integration
15
+ class TestISRCReading:
16
+ def test_id3v1_isrc_not_supported(self, sample_mp3_file):
17
+ """Test that ISRC is not supported by ID3v1 format when explicitly requesting ID3v1."""
18
+ with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError):
19
+ get_unified_metadata_field(sample_mp3_file, UnifiedMetadataKey.ISRC, metadata_format=MetadataFormat.ID3V1)
20
+
21
+ def test_vorbis_max_isrc(self):
22
+ with temp_file_with_metadata({}, "flac") as test_file:
23
+ VorbisMetadataSetter.set_max_metadata(test_file)
24
+ isrc = get_unified_metadata_field(test_file, UnifiedMetadataKey.ISRC)
25
+ # Max ISRC set by set_max_metadata is "USXXX9999999" (12 chars, standard format)
26
+ assert isrc == "USXXX9999999"
27
+ assert len(isrc) == 12
28
+
29
+ def test_riff_max_isrc(self):
30
+ with temp_file_with_metadata({}, "wav") as test_file:
31
+ RIFFMetadataSetter.set_max_metadata(test_file)
32
+ isrc = get_unified_metadata_field(test_file, UnifiedMetadataKey.ISRC)
33
+ # Max ISRC set by set_max_metadata is "USXXX9999999" (12 chars, standard format)
34
+ assert isrc == "USXXX9999999"
35
+ assert len(isrc) == 12
@@ -0,0 +1,165 @@
1
+ """Tests for writing ISRC metadata field across different formats."""
2
+
3
+ import pytest
4
+
5
+ from audiometa import get_unified_metadata, get_unified_metadata_field, update_metadata
6
+ from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+ from audiometa.utils.metadata_format import MetadataFormat
9
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
10
+
11
+
12
+ @pytest.mark.integration
13
+ class TestISRCWritingByMetadataFormat:
14
+ """Test ISRC writing explicitly by metadata format (ID3v2, Vorbis, RIFF)."""
15
+
16
+ # Test 12-character format against each metadata format
17
+ @pytest.mark.parametrize(
18
+ "isrc",
19
+ [
20
+ "USRC17607839", # Standard 12-char format
21
+ "GBUM71505078", # UK code
22
+ ],
23
+ )
24
+ def test_id3v2_12_char_format(self, isrc):
25
+ """Test 12-character ISRC format with ID3v2."""
26
+ with temp_file_with_metadata({}, "mp3") as test_file:
27
+ update_metadata(
28
+ test_file,
29
+ {UnifiedMetadataKey.ISRC: isrc},
30
+ metadata_format=MetadataFormat.ID3V2,
31
+ )
32
+ result = get_unified_metadata_field(
33
+ test_file, UnifiedMetadataKey.ISRC, metadata_format=MetadataFormat.ID3V2
34
+ )
35
+ assert result == isrc
36
+
37
+ @pytest.mark.parametrize(
38
+ "isrc",
39
+ [
40
+ "USRC17607839", # Standard 12-char format
41
+ "GBUM71505078", # UK code
42
+ ],
43
+ )
44
+ def test_vorbis_12_char_format(self, isrc):
45
+ """Test 12-character ISRC format with Vorbis."""
46
+ with temp_file_with_metadata({}, "flac") as test_file:
47
+ update_metadata(
48
+ test_file,
49
+ {UnifiedMetadataKey.ISRC: isrc},
50
+ metadata_format=MetadataFormat.VORBIS,
51
+ )
52
+ result = get_unified_metadata_field(
53
+ test_file, UnifiedMetadataKey.ISRC, metadata_format=MetadataFormat.VORBIS
54
+ )
55
+ assert result == isrc
56
+
57
+ @pytest.mark.parametrize(
58
+ "isrc",
59
+ [
60
+ "USRC17607839", # Standard 12-char format
61
+ "GBUM71505078", # UK code
62
+ ],
63
+ )
64
+ def test_riff_12_char_format(self, isrc):
65
+ """Test 12-character ISRC format with RIFF."""
66
+ with temp_file_with_metadata({}, "wav") as test_file:
67
+ update_metadata(
68
+ test_file,
69
+ {UnifiedMetadataKey.ISRC: isrc},
70
+ metadata_format=MetadataFormat.RIFF,
71
+ )
72
+ result = get_unified_metadata_field(test_file, UnifiedMetadataKey.ISRC, metadata_format=MetadataFormat.RIFF)
73
+ assert result == isrc
74
+
75
+ # Test hyphenated format against each metadata format
76
+ @pytest.mark.parametrize(
77
+ "isrc",
78
+ [
79
+ "US-RC1-76-07839", # Standard hyphenated format
80
+ "GB-UM7-15-05078", # UK code with hyphens
81
+ ],
82
+ )
83
+ def test_id3v2_hyphenated_format(self, isrc):
84
+ """Test hyphenated ISRC format with ID3v2."""
85
+ with temp_file_with_metadata({}, "mp3") as test_file:
86
+ update_metadata(
87
+ test_file,
88
+ {UnifiedMetadataKey.ISRC: isrc},
89
+ metadata_format=MetadataFormat.ID3V2,
90
+ )
91
+ result = get_unified_metadata_field(
92
+ test_file, UnifiedMetadataKey.ISRC, metadata_format=MetadataFormat.ID3V2
93
+ )
94
+ assert result == isrc
95
+
96
+ @pytest.mark.parametrize(
97
+ "isrc",
98
+ [
99
+ "US-RC1-76-07839", # Standard hyphenated format
100
+ "GB-UM7-15-05078", # UK code with hyphens
101
+ ],
102
+ )
103
+ def test_vorbis_hyphenated_format(self, isrc):
104
+ """Test hyphenated ISRC format with Vorbis."""
105
+ with temp_file_with_metadata({}, "flac") as test_file:
106
+ update_metadata(
107
+ test_file,
108
+ {UnifiedMetadataKey.ISRC: isrc},
109
+ metadata_format=MetadataFormat.VORBIS,
110
+ )
111
+ result = get_unified_metadata_field(
112
+ test_file, UnifiedMetadataKey.ISRC, metadata_format=MetadataFormat.VORBIS
113
+ )
114
+ assert result == isrc
115
+
116
+ @pytest.mark.parametrize(
117
+ "isrc",
118
+ [
119
+ "US-RC1-76-07839", # Standard hyphenated format
120
+ "GB-UM7-15-05078", # UK code with hyphens
121
+ ],
122
+ )
123
+ def test_riff_hyphenated_format(self, isrc):
124
+ """Test hyphenated ISRC format with RIFF."""
125
+ with temp_file_with_metadata({}, "wav") as test_file:
126
+ update_metadata(
127
+ test_file,
128
+ {UnifiedMetadataKey.ISRC: isrc},
129
+ metadata_format=MetadataFormat.RIFF,
130
+ )
131
+ result = get_unified_metadata_field(test_file, UnifiedMetadataKey.ISRC, metadata_format=MetadataFormat.RIFF)
132
+ assert result == isrc
133
+
134
+ def test_id3v1_isrc_not_supported_on_write(self, sample_mp3_file):
135
+ """Test that ISRC is not supported by ID3v1 format when explicitly targeting ID3v1."""
136
+ test_metadata = {UnifiedMetadataKey.ISRC: "USRC17607839"}
137
+
138
+ with pytest.raises(MetadataFieldNotSupportedByMetadataFormatError):
139
+ update_metadata(
140
+ sample_mp3_file,
141
+ test_metadata,
142
+ metadata_format=MetadataFormat.ID3V1,
143
+ fail_on_unsupported_field=True,
144
+ )
145
+
146
+
147
+ @pytest.mark.integration
148
+ class TestISRCWritingRoundtrip:
149
+ """Test ISRC writing roundtrip by file format."""
150
+
151
+ def test_isrc_with_other_fields(self):
152
+ test_metadata = {
153
+ UnifiedMetadataKey.TITLE: "Test Song",
154
+ UnifiedMetadataKey.ARTISTS: ["Test Artist"],
155
+ UnifiedMetadataKey.ALBUM: "Test Album",
156
+ UnifiedMetadataKey.ISRC: "USRC17607839",
157
+ }
158
+
159
+ with temp_file_with_metadata({}, "mp3") as test_file:
160
+ update_metadata(test_file, test_metadata)
161
+ metadata = get_unified_metadata(test_file)
162
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Test Song"
163
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Test Artist"]
164
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Test Album"
165
+ assert metadata.get(UnifiedMetadataKey.ISRC) == "USRC17607839"
@@ -0,0 +1,75 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field, update_metadata
4
+ from audiometa.test.helpers.id3v2 import ID3v2MetadataSetter
5
+ from audiometa.test.helpers.riff import RIFFMetadataSetter
6
+ from audiometa.test.helpers.riff.riff_metadata_getter import RIFFMetadataGetter
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+ from audiometa.test.helpers.vorbis import VorbisMetadataSetter
9
+ from audiometa.utils.metadata_format import MetadataFormat
10
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
11
+
12
+
13
+ @pytest.mark.integration
14
+ class TestLanguageDeleting:
15
+ def test_delete_language_id3v2(self):
16
+ with temp_file_with_metadata({}, "mp3") as test_file:
17
+ ID3v2MetadataSetter.set_language(test_file, "en")
18
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) == "en"
19
+
20
+ # Delete metadata using library API
21
+ update_metadata(test_file, {UnifiedMetadataKey.LANGUAGE: None}, metadata_format=MetadataFormat.ID3V2)
22
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) is None
23
+
24
+ def test_delete_language_id3v1(self):
25
+ from audiometa.exceptions import MetadataFieldNotSupportedByMetadataFormatError
26
+
27
+ with (
28
+ temp_file_with_metadata({}, "id3v1") as test_file,
29
+ pytest.raises(MetadataFieldNotSupportedByMetadataFormatError),
30
+ ):
31
+ # Deleting should fail since the field isn't supported
32
+ update_metadata(test_file, {UnifiedMetadataKey.LANGUAGE: None}, metadata_format=MetadataFormat.ID3V1)
33
+
34
+ def test_delete_language_riff(self):
35
+ with temp_file_with_metadata({}, "wav") as test_file:
36
+ RIFFMetadataSetter.set_language(test_file, "en")
37
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
38
+ assert "TAG:language=en" in raw_metadata
39
+
40
+ # Delete metadata using library API
41
+ update_metadata(test_file, {UnifiedMetadataKey.LANGUAGE: None}, metadata_format=MetadataFormat.RIFF)
42
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) is None
43
+
44
+ def test_delete_language_vorbis(self):
45
+ with temp_file_with_metadata({}, "flac") as test_file:
46
+ VorbisMetadataSetter.set_language(test_file, "en")
47
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) == "en"
48
+
49
+ # Delete metadata using library API
50
+ update_metadata(test_file, {UnifiedMetadataKey.LANGUAGE: None}, metadata_format=MetadataFormat.VORBIS)
51
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) is None
52
+
53
+ def test_delete_language_preserves_other_fields(self):
54
+ with temp_file_with_metadata({}, "mp3") as test_file:
55
+ ID3v2MetadataSetter.set_language(test_file, "en")
56
+ ID3v2MetadataSetter.set_title(test_file, "Test Title")
57
+ ID3v2MetadataSetter.set_artists(test_file, "Test Artist")
58
+
59
+ # Delete only language using library API
60
+ update_metadata(test_file, {UnifiedMetadataKey.LANGUAGE: None}, metadata_format=MetadataFormat.ID3V2)
61
+
62
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) 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_language_already_none(self):
67
+ with temp_file_with_metadata({}, "mp3") as test_file:
68
+ # Try to delete language that doesn't exist
69
+ update_metadata(test_file, {UnifiedMetadataKey.LANGUAGE: None}, metadata_format=MetadataFormat.ID3V2)
70
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) is None
71
+
72
+ def test_delete_language_empty_string(self):
73
+ with temp_file_with_metadata({}, "mp3") as test_file:
74
+ update_metadata(test_file, {UnifiedMetadataKey.LANGUAGE: ""}, metadata_format=MetadataFormat.ID3V2)
75
+ assert get_unified_metadata_field(test_file, UnifiedMetadataKey.LANGUAGE) is None