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,336 @@
1
+ import platform
2
+ import subprocess
3
+ import sys
4
+
5
+ import pytest
6
+
7
+ from audiometa import get_unified_metadata
8
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
9
+ from audiometa.utils.metadata_format import MetadataFormat
10
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
11
+
12
+
13
+ @pytest.mark.e2e
14
+ class TestCLIWriteForceFormat:
15
+ def test_cli_write_force_format_id3v2_mp3(self):
16
+ with temp_file_with_metadata({}, "mp3") as test_file:
17
+ result = subprocess.run(
18
+ [
19
+ sys.executable,
20
+ "-m",
21
+ "audiometa",
22
+ "write",
23
+ str(test_file),
24
+ "--title",
25
+ "CLI Force ID3v2 Title",
26
+ "--force-format",
27
+ "id3v2",
28
+ ],
29
+ capture_output=True,
30
+ text=True,
31
+ check=False,
32
+ )
33
+ assert result.returncode == 0
34
+ assert "Updated metadata" in result.stdout
35
+
36
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
37
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "CLI Force ID3v2 Title"
38
+
39
+ def test_cli_write_force_format_id3v1_mp3(self):
40
+ with temp_file_with_metadata({}, "mp3") as test_file:
41
+ result = subprocess.run(
42
+ [
43
+ sys.executable,
44
+ "-m",
45
+ "audiometa",
46
+ "write",
47
+ str(test_file),
48
+ "--title",
49
+ "CLI Force ID3v1 Title",
50
+ "--force-format",
51
+ "id3v1",
52
+ ],
53
+ capture_output=True,
54
+ text=True,
55
+ check=False,
56
+ )
57
+ assert result.returncode == 0
58
+ assert "Updated metadata" in result.stdout
59
+
60
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
61
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "CLI Force ID3v1 Title"
62
+
63
+ def test_cli_write_force_format_vorbis_flac(self):
64
+ with temp_file_with_metadata({}, "flac") as test_file:
65
+ result = subprocess.run(
66
+ [
67
+ sys.executable,
68
+ "-m",
69
+ "audiometa",
70
+ "write",
71
+ str(test_file),
72
+ "--title",
73
+ "CLI Force Vorbis Title",
74
+ "--force-format",
75
+ "vorbis",
76
+ ],
77
+ capture_output=True,
78
+ text=True,
79
+ check=False,
80
+ )
81
+ assert result.returncode == 0
82
+ assert "Updated metadata" in result.stdout
83
+
84
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.VORBIS)
85
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "CLI Force Vorbis Title"
86
+
87
+ def test_cli_write_force_format_riff_wav(self):
88
+ with temp_file_with_metadata({}, "wav") as test_file:
89
+ result = subprocess.run(
90
+ [
91
+ sys.executable,
92
+ "-m",
93
+ "audiometa",
94
+ "write",
95
+ str(test_file),
96
+ "--title",
97
+ "CLI Force RIFF Title",
98
+ "--force-format",
99
+ "riff",
100
+ ],
101
+ capture_output=True,
102
+ text=True,
103
+ check=False,
104
+ )
105
+ assert result.returncode == 0
106
+ assert "Updated metadata" in result.stdout
107
+
108
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
109
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "CLI Force RIFF Title"
110
+
111
+ def test_cli_write_force_format_unsupported_format_mp3(self):
112
+ with temp_file_with_metadata({}, "mp3") as test_file:
113
+ result = subprocess.run(
114
+ [
115
+ sys.executable,
116
+ "-m",
117
+ "audiometa",
118
+ "write",
119
+ str(test_file),
120
+ "--title",
121
+ "Test Title",
122
+ "--force-format",
123
+ "vorbis",
124
+ ],
125
+ capture_output=True,
126
+ text=True,
127
+ check=False,
128
+ )
129
+ assert result.returncode != 0
130
+ stderr_output = result.stderr.lower()
131
+ assert "not supported" in stderr_output or "error" in stderr_output
132
+
133
+ def test_cli_write_force_format_writes_only_to_specified_format(self):
134
+ with temp_file_with_metadata({"title": "Original RIFF Title"}, "wav") as test_file:
135
+ result = subprocess.run(
136
+ [
137
+ sys.executable,
138
+ "-m",
139
+ "audiometa",
140
+ "write",
141
+ str(test_file),
142
+ "--title",
143
+ "New ID3v2 Title",
144
+ "--force-format",
145
+ "id3v2",
146
+ ],
147
+ capture_output=True,
148
+ text=True,
149
+ check=False,
150
+ )
151
+ assert result.returncode == 0
152
+
153
+ riff_metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.RIFF)
154
+ id3v2_metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
155
+
156
+ assert riff_metadata.get(UnifiedMetadataKey.TITLE) == "Original RIFF Title"
157
+ assert id3v2_metadata.get(UnifiedMetadataKey.TITLE) == "New ID3v2 Title"
158
+
159
+ @pytest.mark.skipif(platform.system() == "Windows", reason="id3v2 tool requires WSL on Windows")
160
+ def test_cli_write_force_format_id3v2_flac(self):
161
+ """Test forcing ID3v2 format on FLAC files.
162
+
163
+ Note: This test is skipped on Windows because writing ID3v2 tags to FLAC files
164
+ requires the external 'id3v2' tool (mutagen corrupts FLAC structure). On Windows,
165
+ the 'id3v2' tool is not available as a native binary and requires WSL (Windows
166
+ Subsystem for Linux), which is not set up in Windows CI environments.
167
+ """
168
+ with temp_file_with_metadata({}, "flac") as test_file:
169
+ result = subprocess.run(
170
+ [
171
+ sys.executable,
172
+ "-m",
173
+ "audiometa",
174
+ "write",
175
+ str(test_file),
176
+ "--title",
177
+ "CLI Force ID3v2 FLAC Title",
178
+ "--force-format",
179
+ "id3v2",
180
+ ],
181
+ capture_output=True,
182
+ text=True,
183
+ check=False,
184
+ )
185
+ assert result.returncode == 0
186
+ assert "Updated metadata" in result.stdout
187
+
188
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
189
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "CLI Force ID3v2 FLAC Title"
190
+
191
+ def test_cli_write_force_format_id3v1_flac(self):
192
+ with temp_file_with_metadata({}, "flac") as test_file:
193
+ result = subprocess.run(
194
+ [
195
+ sys.executable,
196
+ "-m",
197
+ "audiometa",
198
+ "write",
199
+ str(test_file),
200
+ "--title",
201
+ "CLI Force ID3v1 FLAC Title",
202
+ "--force-format",
203
+ "id3v1",
204
+ ],
205
+ capture_output=True,
206
+ text=True,
207
+ check=False,
208
+ )
209
+ assert result.returncode == 0
210
+ assert "Updated metadata" in result.stdout
211
+
212
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
213
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "CLI Force ID3v1 FLAC Title"
214
+
215
+ def test_cli_write_force_format_id3v1_wav(self):
216
+ with temp_file_with_metadata({}, "wav") as test_file:
217
+ result = subprocess.run(
218
+ [
219
+ sys.executable,
220
+ "-m",
221
+ "audiometa",
222
+ "write",
223
+ str(test_file),
224
+ "--title",
225
+ "CLI Force ID3v1 WAV Title",
226
+ "--force-format",
227
+ "id3v1",
228
+ ],
229
+ capture_output=True,
230
+ text=True,
231
+ check=False,
232
+ )
233
+ assert result.returncode == 0
234
+ assert "Updated metadata" in result.stdout
235
+
236
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
237
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "CLI Force ID3v1 WAV Title"
238
+
239
+ def test_cli_write_force_format_with_multiple_fields(self):
240
+ with temp_file_with_metadata({}, "mp3") as test_file:
241
+ result = subprocess.run(
242
+ [
243
+ sys.executable,
244
+ "-m",
245
+ "audiometa",
246
+ "write",
247
+ str(test_file),
248
+ "--title",
249
+ "Multi Field Title",
250
+ "--artist",
251
+ "Multi Field Artist",
252
+ "--album",
253
+ "Multi Field Album",
254
+ "--force-format",
255
+ "id3v2",
256
+ ],
257
+ capture_output=True,
258
+ text=True,
259
+ check=False,
260
+ )
261
+ assert result.returncode == 0
262
+
263
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
264
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Multi Field Title"
265
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Multi Field Artist"]
266
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Multi Field Album"
267
+
268
+ def test_cli_write_force_format_with_rating(self):
269
+ with temp_file_with_metadata({}, "mp3") as test_file:
270
+ result = subprocess.run(
271
+ [
272
+ sys.executable,
273
+ "-m",
274
+ "audiometa",
275
+ "write",
276
+ str(test_file),
277
+ "--title",
278
+ "Rating Test Title",
279
+ "--rating",
280
+ "85",
281
+ "--force-format",
282
+ "id3v2",
283
+ ],
284
+ capture_output=True,
285
+ text=True,
286
+ check=False,
287
+ )
288
+ assert result.returncode == 0
289
+
290
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
291
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Rating Test Title"
292
+ assert metadata.get(UnifiedMetadataKey.RATING) is not None
293
+
294
+ def test_cli_write_force_format_unsupported_flac_riff(self):
295
+ with temp_file_with_metadata({}, "flac") as test_file:
296
+ result = subprocess.run(
297
+ [
298
+ sys.executable,
299
+ "-m",
300
+ "audiometa",
301
+ "write",
302
+ str(test_file),
303
+ "--title",
304
+ "Test Title",
305
+ "--force-format",
306
+ "riff",
307
+ ],
308
+ capture_output=True,
309
+ text=True,
310
+ check=False,
311
+ )
312
+ assert result.returncode != 0
313
+ stderr_output = result.stderr.lower()
314
+ assert "not supported" in stderr_output or "error" in stderr_output
315
+
316
+ def test_cli_write_force_format_unsupported_wav_vorbis(self):
317
+ with temp_file_with_metadata({}, "wav") as test_file:
318
+ result = subprocess.run(
319
+ [
320
+ sys.executable,
321
+ "-m",
322
+ "audiometa",
323
+ "write",
324
+ str(test_file),
325
+ "--title",
326
+ "Test Title",
327
+ "--force-format",
328
+ "vorbis",
329
+ ],
330
+ capture_output=True,
331
+ text=True,
332
+ check=False,
333
+ )
334
+ assert result.returncode != 0
335
+ stderr_output = result.stderr.lower()
336
+ assert "not supported" in stderr_output or "error" in stderr_output
@@ -0,0 +1,145 @@
1
+ import subprocess
2
+ import sys
3
+
4
+ import pytest
5
+
6
+ from audiometa import get_unified_metadata
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.e2e
12
+ class TestCLIWriteIntegerFields:
13
+ def test_cli_write_integer_fields(self):
14
+ with temp_file_with_metadata({}, "mp3") as test_file:
15
+ result = subprocess.run(
16
+ [
17
+ sys.executable,
18
+ "-m",
19
+ "audiometa",
20
+ "write",
21
+ str(test_file),
22
+ "--disc-number",
23
+ "2",
24
+ "--disc-total",
25
+ "3",
26
+ "--bpm",
27
+ "120",
28
+ ],
29
+ capture_output=True,
30
+ text=True,
31
+ check=False,
32
+ )
33
+ assert result.returncode == 0
34
+
35
+ metadata = get_unified_metadata(test_file)
36
+ assert metadata.get(UnifiedMetadataKey.DISC_NUMBER) == 2
37
+ assert metadata.get(UnifiedMetadataKey.DISC_TOTAL) == 3
38
+ assert metadata.get(UnifiedMetadataKey.BPM) == 120
39
+
40
+ def test_cli_write_track_number_simple(self):
41
+ with temp_file_with_metadata({}, "mp3") as test_file:
42
+ result = subprocess.run(
43
+ [
44
+ sys.executable,
45
+ "-m",
46
+ "audiometa",
47
+ "write",
48
+ str(test_file),
49
+ "--track-number",
50
+ "5",
51
+ ],
52
+ capture_output=True,
53
+ text=True,
54
+ check=False,
55
+ )
56
+ assert result.returncode == 0
57
+
58
+ metadata = get_unified_metadata(test_file)
59
+ assert metadata.get(UnifiedMetadataKey.TRACK_NUMBER) == "5"
60
+
61
+ def test_cli_write_track_number_with_total(self):
62
+ with temp_file_with_metadata({}, "mp3") as test_file:
63
+ result = subprocess.run(
64
+ [
65
+ sys.executable,
66
+ "-m",
67
+ "audiometa",
68
+ "write",
69
+ str(test_file),
70
+ "--track-number",
71
+ "5/12",
72
+ ],
73
+ capture_output=True,
74
+ text=True,
75
+ check=False,
76
+ )
77
+ assert result.returncode == 0
78
+
79
+ metadata = get_unified_metadata(test_file)
80
+ assert metadata.get(UnifiedMetadataKey.TRACK_NUMBER) == "5/12"
81
+
82
+ def test_cli_write_release_date_year(self):
83
+ with temp_file_with_metadata({}, "mp3") as test_file:
84
+ result = subprocess.run(
85
+ [
86
+ sys.executable,
87
+ "-m",
88
+ "audiometa",
89
+ "write",
90
+ str(test_file),
91
+ "--year",
92
+ "2024",
93
+ ],
94
+ capture_output=True,
95
+ text=True,
96
+ check=False,
97
+ )
98
+ assert result.returncode == 0
99
+
100
+ metadata = get_unified_metadata(test_file)
101
+ assert metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
102
+
103
+ def test_cli_write_release_date_full(self):
104
+ with temp_file_with_metadata({}, "mp3") as test_file:
105
+ result = subprocess.run(
106
+ [
107
+ sys.executable,
108
+ "-m",
109
+ "audiometa",
110
+ "write",
111
+ str(test_file),
112
+ "--release-date",
113
+ "2024-01-15",
114
+ ],
115
+ capture_output=True,
116
+ text=True,
117
+ check=False,
118
+ )
119
+ assert result.returncode == 0
120
+
121
+ metadata = get_unified_metadata(test_file)
122
+ assert metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2024-01-15"
123
+
124
+ def test_cli_write_year_takes_precedence_over_release_date(self):
125
+ with temp_file_with_metadata({}, "mp3") as test_file:
126
+ result = subprocess.run(
127
+ [
128
+ sys.executable,
129
+ "-m",
130
+ "audiometa",
131
+ "write",
132
+ str(test_file),
133
+ "--year",
134
+ "2023",
135
+ "--release-date",
136
+ "2024-01-15",
137
+ ],
138
+ capture_output=True,
139
+ text=True,
140
+ check=False,
141
+ )
142
+ assert result.returncode == 0
143
+
144
+ metadata = get_unified_metadata(test_file)
145
+ assert metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2023"
@@ -0,0 +1,107 @@
1
+ import subprocess
2
+ import sys
3
+
4
+ import pytest
5
+
6
+ from audiometa import get_unified_metadata
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.e2e
12
+ class TestCLIWriteListFields:
13
+ def test_cli_write_multiple_artists(self):
14
+ with temp_file_with_metadata({}, "mp3") as test_file:
15
+ result = subprocess.run(
16
+ [
17
+ sys.executable,
18
+ "-m",
19
+ "audiometa",
20
+ "write",
21
+ str(test_file),
22
+ "--artist",
23
+ "Artist One",
24
+ "--artist",
25
+ "Artist Two",
26
+ "--artist",
27
+ "Artist Three",
28
+ ],
29
+ capture_output=True,
30
+ text=True,
31
+ check=False,
32
+ )
33
+ assert result.returncode == 0
34
+
35
+ metadata = get_unified_metadata(test_file)
36
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist One", "Artist Two", "Artist Three"]
37
+
38
+ def test_cli_write_multiple_album_artists(self):
39
+ with temp_file_with_metadata({}, "mp3") as test_file:
40
+ result = subprocess.run(
41
+ [
42
+ sys.executable,
43
+ "-m",
44
+ "audiometa",
45
+ "write",
46
+ str(test_file),
47
+ "--album-artist",
48
+ "Album Artist One",
49
+ "--album-artist",
50
+ "Album Artist Two",
51
+ ],
52
+ capture_output=True,
53
+ text=True,
54
+ check=False,
55
+ )
56
+ assert result.returncode == 0
57
+
58
+ metadata = get_unified_metadata(test_file)
59
+ assert metadata.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Album Artist One", "Album Artist Two"]
60
+
61
+ def test_cli_write_multiple_genres(self):
62
+ with temp_file_with_metadata({}, "mp3") as test_file:
63
+ result = subprocess.run(
64
+ [
65
+ sys.executable,
66
+ "-m",
67
+ "audiometa",
68
+ "write",
69
+ str(test_file),
70
+ "--genre",
71
+ "Rock",
72
+ "--genre",
73
+ "Blues",
74
+ "--genre",
75
+ "Jazz",
76
+ ],
77
+ capture_output=True,
78
+ text=True,
79
+ check=False,
80
+ )
81
+ assert result.returncode == 0
82
+
83
+ metadata = get_unified_metadata(test_file)
84
+ assert metadata.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock", "Blues", "Jazz"]
85
+
86
+ def test_cli_write_multiple_composers(self):
87
+ with temp_file_with_metadata({}, "mp3") as test_file:
88
+ result = subprocess.run(
89
+ [
90
+ sys.executable,
91
+ "-m",
92
+ "audiometa",
93
+ "write",
94
+ str(test_file),
95
+ "--composer",
96
+ "Composer One",
97
+ "--composer",
98
+ "Composer Two",
99
+ ],
100
+ capture_output=True,
101
+ text=True,
102
+ check=False,
103
+ )
104
+ assert result.returncode == 0
105
+
106
+ metadata = get_unified_metadata(test_file)
107
+ assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["Composer One", "Composer Two"]
@@ -0,0 +1,74 @@
1
+ import subprocess
2
+ import sys
3
+
4
+ import pytest
5
+
6
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
7
+
8
+
9
+ @pytest.mark.e2e
10
+ class TestCLIWriteRating:
11
+ def test_cli_write_rating_integer(self):
12
+ with temp_file_with_metadata({}, "mp3") as test_file:
13
+ result = subprocess.run(
14
+ [
15
+ sys.executable,
16
+ "-m",
17
+ "audiometa",
18
+ "write",
19
+ str(test_file),
20
+ "--rating",
21
+ "50",
22
+ ],
23
+ capture_output=True,
24
+ text=True,
25
+ check=False,
26
+ )
27
+ assert result.returncode == 0
28
+ assert "Updated metadata" in result.stdout
29
+
30
+ def test_cli_invalid_rating_value_fractional_float(self):
31
+ with temp_file_with_metadata({}, "mp3") as test_file:
32
+ result = subprocess.run(
33
+ [
34
+ sys.executable,
35
+ "-m",
36
+ "audiometa",
37
+ "write",
38
+ str(test_file),
39
+ "--rating",
40
+ "1.5",
41
+ ],
42
+ capture_output=True,
43
+ text=True,
44
+ check=False,
45
+ )
46
+ assert result.returncode != 0
47
+ stderr_output = result.stderr.lower()
48
+ assert "invalid" in stderr_output or "error" in stderr_output
49
+ assert "raw mode" in stderr_output or "normalization" in stderr_output
50
+
51
+ def test_cli_invalid_rating_value_fractional_float_with_other_metadata(self):
52
+ with temp_file_with_metadata({}, "mp3") as test_file:
53
+ result = subprocess.run(
54
+ [
55
+ sys.executable,
56
+ "-m",
57
+ "audiometa",
58
+ "write",
59
+ str(test_file),
60
+ "--title",
61
+ "Test Song",
62
+ "--artist",
63
+ "Test Artist",
64
+ "--rating",
65
+ "7.5",
66
+ ],
67
+ capture_output=True,
68
+ text=True,
69
+ check=False,
70
+ )
71
+ assert result.returncode != 0
72
+ stderr_output = result.stderr.lower()
73
+ assert "invalid" in stderr_output or "error" in stderr_output
74
+ assert "raw mode" in stderr_output or "normalization" in stderr_output