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,149 @@
1
+ import json
2
+ import subprocess
3
+ import sys
4
+
5
+ import pytest
6
+
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+
9
+
10
+ @pytest.mark.e2e
11
+ class TestCLIReadMultipleFiles:
12
+ def test_cli_read_multiple_files(self):
13
+ with (
14
+ temp_file_with_metadata({"title": "File One"}, "mp3") as file1,
15
+ temp_file_with_metadata({"title": "File Two"}, "mp3") as file2,
16
+ ):
17
+ result = subprocess.run(
18
+ [
19
+ sys.executable,
20
+ "-m",
21
+ "audiometa",
22
+ "read",
23
+ str(file1),
24
+ str(file2),
25
+ "--format",
26
+ "json",
27
+ ],
28
+ capture_output=True,
29
+ text=True,
30
+ check=False,
31
+ )
32
+ assert result.returncode == 0
33
+ # When reading multiple files, each file outputs a separate JSON object
34
+ # JSON objects can span multiple lines, so we need to parse them properly
35
+ output = result.stdout.strip()
36
+ if output:
37
+ # Try to parse the first complete JSON object
38
+ # Find the first complete JSON object by counting braces
39
+ brace_count = 0
40
+ start_idx = 0
41
+ for i, char in enumerate(output):
42
+ if char == "{":
43
+ brace_count += 1
44
+ elif char == "}":
45
+ brace_count -= 1
46
+ if brace_count == 0:
47
+ # Found complete JSON object
48
+ first_json_str = output[start_idx : i + 1]
49
+ data = json.loads(first_json_str)
50
+ assert isinstance(data, dict)
51
+ assert "unified_metadata" in data or "metadata_format" in data
52
+ break
53
+
54
+ def test_cli_read_multiple_files_with_continue_on_error(self, tmp_path):
55
+ nonexistent_file = tmp_path / "nonexistent.mp3"
56
+ with temp_file_with_metadata({"title": "Valid File"}, "mp3") as valid_file:
57
+ result = subprocess.run(
58
+ [
59
+ sys.executable,
60
+ "-m",
61
+ "audiometa",
62
+ "read",
63
+ str(nonexistent_file),
64
+ str(valid_file),
65
+ "--continue-on-error",
66
+ "--format",
67
+ "json",
68
+ ],
69
+ capture_output=True,
70
+ text=True,
71
+ check=False,
72
+ )
73
+ assert result.returncode == 0
74
+ # When reading multiple files, each file outputs a separate JSON object
75
+ # JSON objects can span multiple lines, so we need to parse them properly
76
+ output = result.stdout.strip()
77
+ if output:
78
+ # Try to parse the first complete JSON object
79
+ brace_count = 0
80
+ start_idx = 0
81
+ for i, char in enumerate(output):
82
+ if char == "{":
83
+ if brace_count == 0:
84
+ start_idx = i
85
+ brace_count += 1
86
+ elif char == "}":
87
+ brace_count -= 1
88
+ if brace_count == 0:
89
+ # Found complete JSON object
90
+ try:
91
+ first_json_str = output[start_idx : i + 1]
92
+ data = json.loads(first_json_str)
93
+ assert isinstance(data, dict)
94
+ break
95
+ except json.JSONDecodeError:
96
+ continue
97
+
98
+ def test_cli_read_glob_pattern(self, tmp_path):
99
+ # Use a dedicated temp directory to avoid matching other temp files
100
+ test_dir = tmp_path / "glob_test"
101
+ test_dir.mkdir()
102
+
103
+ with temp_file_with_metadata({"title": "Pattern File 1"}, "mp3") as file1:
104
+ file1_name = file1.name
105
+ test_file1 = test_dir / file1_name
106
+ import shutil
107
+
108
+ shutil.copy2(file1, test_file1)
109
+ with temp_file_with_metadata({"title": "Pattern File 2"}, "mp3") as temp_file2:
110
+ file2_name = "pattern_file_2.mp3"
111
+ test_file2 = test_dir / file2_name
112
+ shutil.copy2(temp_file2, test_file2)
113
+
114
+ result = subprocess.run(
115
+ [
116
+ sys.executable,
117
+ "-m",
118
+ "audiometa",
119
+ "read",
120
+ str(test_dir / "*.mp3"),
121
+ "--format",
122
+ "json",
123
+ "--continue-on-error",
124
+ ],
125
+ capture_output=True,
126
+ text=True,
127
+ check=False,
128
+ )
129
+ assert result.returncode == 0
130
+ # When reading multiple files, each file outputs a separate JSON object
131
+ # JSON objects can span multiple lines, so we need to parse them properly
132
+ output = result.stdout.strip()
133
+ if output:
134
+ # Try to parse the first complete JSON object
135
+ brace_count = 0
136
+ start_idx = 0
137
+ for i, char in enumerate(output):
138
+ if char == "{":
139
+ if brace_count == 0:
140
+ start_idx = i
141
+ brace_count += 1
142
+ elif char == "}":
143
+ brace_count -= 1
144
+ if brace_count == 0:
145
+ # Found complete JSON object
146
+ first_json_str = output[start_idx : i + 1]
147
+ data = json.loads(first_json_str)
148
+ assert isinstance(data, dict)
149
+ break
@@ -0,0 +1,88 @@
1
+ import json
2
+ import subprocess
3
+ import sys
4
+
5
+ import pytest
6
+
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+
9
+
10
+ @pytest.mark.e2e
11
+ class TestCLIReadOptions:
12
+ def test_cli_read_no_headers(self):
13
+ with temp_file_with_metadata({"title": "Test Title"}, "mp3") as test_file:
14
+ result = subprocess.run(
15
+ [sys.executable, "-m", "audiometa", "read", str(test_file), "--no-headers", "--format", "json"],
16
+ capture_output=True,
17
+ text=True,
18
+ check=False,
19
+ )
20
+ assert result.returncode == 0
21
+ data = json.loads(result.stdout)
22
+ assert "unified_metadata" in data
23
+ # When --no-headers is used, headers should be empty dict, not absent
24
+ assert "headers" in data
25
+ assert data["headers"] == {}
26
+
27
+ def test_cli_read_no_technical(self):
28
+ with temp_file_with_metadata({"title": "Test Title"}, "mp3") as test_file:
29
+ result = subprocess.run(
30
+ [sys.executable, "-m", "audiometa", "read", str(test_file), "--no-technical", "--format", "json"],
31
+ capture_output=True,
32
+ text=True,
33
+ check=False,
34
+ )
35
+ assert result.returncode == 0
36
+ data = json.loads(result.stdout)
37
+ assert "unified_metadata" in data
38
+ # When --no-technical is used, technical_info should be empty dict, not absent
39
+ assert "technical_info" in data
40
+ assert data["technical_info"] == {}
41
+
42
+ def test_cli_read_no_headers_no_technical(self):
43
+ with temp_file_with_metadata({"title": "Test Title"}, "mp3") as test_file:
44
+ result = subprocess.run(
45
+ [
46
+ sys.executable,
47
+ "-m",
48
+ "audiometa",
49
+ "read",
50
+ str(test_file),
51
+ "--no-headers",
52
+ "--no-technical",
53
+ "--format",
54
+ "json",
55
+ ],
56
+ capture_output=True,
57
+ text=True,
58
+ check=False,
59
+ )
60
+ assert result.returncode == 0
61
+ data = json.loads(result.stdout)
62
+ assert "unified_metadata" in data
63
+ # When --no-headers and --no-technical are used, these should be empty dicts, not absent
64
+ assert "headers" in data
65
+ assert data["headers"] == {}
66
+ assert "technical_info" in data
67
+ assert data["technical_info"] == {}
68
+
69
+ def test_cli_read_with_all_options(self):
70
+ with temp_file_with_metadata({"title": "Test Title", "artist": "Test Artist"}, "mp3") as test_file:
71
+ result = subprocess.run(
72
+ [
73
+ sys.executable,
74
+ "-m",
75
+ "audiometa",
76
+ "read",
77
+ str(test_file),
78
+ "--no-headers",
79
+ "--no-technical",
80
+ "--format",
81
+ "table",
82
+ ],
83
+ capture_output=True,
84
+ text=True,
85
+ check=False,
86
+ )
87
+ assert result.returncode == 0
88
+ assert "UNIFIED METADATA" in result.stdout
@@ -0,0 +1,84 @@
1
+ import json
2
+ import subprocess
3
+ import sys
4
+
5
+ import pytest
6
+
7
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
8
+
9
+
10
+ @pytest.mark.e2e
11
+ class TestCLIReadUnified:
12
+ def test_cli_unified_output(self, sample_mp3_file):
13
+ result = subprocess.run(
14
+ [sys.executable, "-m", "audiometa", "unified", str(sample_mp3_file), "--format", "json"],
15
+ capture_output=True,
16
+ text=True,
17
+ check=False,
18
+ )
19
+ assert result.returncode == 0
20
+ data = json.loads(result.stdout)
21
+ assert isinstance(data, dict)
22
+ assert "unified_metadata" not in data
23
+ assert "headers" not in data
24
+ assert "technical_info" not in data
25
+
26
+ def test_cli_unified_with_metadata(self):
27
+ with temp_file_with_metadata({"title": "Unified Test", "artist": "Unified Artist"}, "mp3") as test_file:
28
+ result = subprocess.run(
29
+ [sys.executable, "-m", "audiometa", "unified", str(test_file), "--format", "json"],
30
+ capture_output=True,
31
+ text=True,
32
+ check=False,
33
+ )
34
+ assert result.returncode == 0
35
+ data = json.loads(result.stdout)
36
+ assert isinstance(data, dict)
37
+ assert data.get("title") == "Unified Test"
38
+ assert data.get("artists") == ["Unified Artist"]
39
+
40
+ def test_cli_unified_table_format(self):
41
+ with temp_file_with_metadata({"title": "Table Test", "artist": "Table Artist"}, "mp3") as test_file:
42
+ result = subprocess.run(
43
+ [sys.executable, "-m", "audiometa", "unified", str(test_file), "--format", "table"],
44
+ capture_output=True,
45
+ text=True,
46
+ check=False,
47
+ )
48
+ assert result.returncode == 0
49
+ assert "title" in result.stdout.lower() or "Table Test" in result.stdout
50
+
51
+ def test_cli_unified_output_to_file(self, tmp_path):
52
+ with temp_file_with_metadata({"title": "File Test"}, "mp3") as test_file:
53
+ output_file = tmp_path / "unified.json"
54
+ result = subprocess.run(
55
+ [
56
+ sys.executable,
57
+ "-m",
58
+ "audiometa",
59
+ "unified",
60
+ str(test_file),
61
+ "--output",
62
+ str(output_file),
63
+ ],
64
+ capture_output=True,
65
+ text=True,
66
+ check=False,
67
+ )
68
+ assert result.returncode == 0
69
+ assert output_file.exists()
70
+ with output_file.open() as f:
71
+ data = json.load(f)
72
+ assert isinstance(data, dict)
73
+
74
+ def test_cli_with_spaces_in_filename_unified(self):
75
+ with temp_file_with_metadata({}, "mp3") as test_file:
76
+ result = subprocess.run(
77
+ [sys.executable, "-m", "audiometa", "unified", str(test_file), "--format", "json"],
78
+ capture_output=True,
79
+ text=True,
80
+ check=False,
81
+ )
82
+ assert result.returncode == 0
83
+ data = json.loads(result.stdout)
84
+ assert isinstance(data, dict)
@@ -0,0 +1,20 @@
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 TestCLIDelete:
11
+ def test_cli_delete_metadata(self):
12
+ with temp_file_with_metadata({}, "mp3") as test_file:
13
+ result = subprocess.run(
14
+ [sys.executable, "-m", "audiometa", "delete", str(test_file)],
15
+ capture_output=True,
16
+ text=True,
17
+ check=False,
18
+ )
19
+ assert result.returncode == 0
20
+ assert "Deleted metadata" in result.stdout or "No metadata found" in result.stderr
@@ -0,0 +1,31 @@
1
+ import json
2
+
3
+ import pytest
4
+
5
+ from audiometa.cli import format_as_table, format_output
6
+
7
+
8
+ @pytest.mark.e2e
9
+ class TestCLIFormatting:
10
+ def test_format_output_json(self):
11
+ data = {"title": "Test Song", "artist": "Test Artist"}
12
+ result = format_output(data, "json")
13
+ parsed = json.loads(result)
14
+ assert parsed == data
15
+
16
+ def test_format_output_yaml(self):
17
+ data = {"title": "Test Song", "artist": "Test Artist"}
18
+ result = format_output(data, "yaml")
19
+ # Should fall back to JSON if PyYAML not available
20
+ assert "Test Song" in result
21
+
22
+ def test_format_output_table(self):
23
+ data = {
24
+ "unified_metadata": {"title": "Test Song", "artist": "Test Artist"},
25
+ "technical_info": {"duration_seconds": 180, "bitrate_bps": 320000},
26
+ }
27
+ result = format_as_table(data)
28
+ assert "Test Song" in result
29
+ assert "Test Artist" in result
30
+ assert "180" in result
31
+ assert "320" in result
@@ -0,0 +1,41 @@
1
+ import subprocess
2
+ import sys
3
+
4
+ import pytest
5
+
6
+
7
+ @pytest.mark.e2e
8
+ class TestCLIHelp:
9
+ def test_cli_help(self):
10
+ result = subprocess.run([sys.executable, "-m", "audiometa"], capture_output=True, text=True, check=False)
11
+ assert result.returncode == 1 # Should exit with error
12
+ assert "usage:" in result.stdout.lower() or "help" in result.stdout.lower()
13
+
14
+ def test_cli_read_help(self):
15
+ result = subprocess.run(
16
+ [sys.executable, "-m", "audiometa", "read", "--help"], capture_output=True, text=True, check=False
17
+ )
18
+ assert result.returncode == 0
19
+ assert "read" in result.stdout.lower()
20
+
21
+ def test_cli_unified_help(self):
22
+ result = subprocess.run(
23
+ [sys.executable, "-m", "audiometa", "unified", "--help"], capture_output=True, text=True, check=False
24
+ )
25
+ assert result.returncode == 0
26
+ assert "unified" in result.stdout.lower()
27
+
28
+ def test_cli_write_help(self):
29
+ result = subprocess.run(
30
+ [sys.executable, "-m", "audiometa", "write", "--help"], capture_output=True, text=True, check=False
31
+ )
32
+ assert result.returncode == 0
33
+ assert "write" in result.stdout.lower()
34
+ assert "force-format" in result.stdout.lower()
35
+
36
+ def test_cli_delete_help(self):
37
+ result = subprocess.run(
38
+ [sys.executable, "-m", "audiometa", "delete", "--help"], capture_output=True, text=True, check=False
39
+ )
40
+ assert result.returncode == 0
41
+ assert "delete" in result.stdout.lower()
File without changes
@@ -0,0 +1,51 @@
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 TestCLIWriteBasic:
11
+ def test_cli_write_no_metadata(self, sample_mp3_file):
12
+ result = subprocess.run(
13
+ [sys.executable, "-m", "audiometa", "write", str(sample_mp3_file)],
14
+ capture_output=True,
15
+ text=True,
16
+ check=False,
17
+ )
18
+ assert result.returncode == 1
19
+ assert "no metadata fields specified" in result.stderr.lower()
20
+
21
+ def test_cli_write_basic_metadata(self):
22
+ with temp_file_with_metadata({}, "mp3") as test_file:
23
+ result = subprocess.run(
24
+ [sys.executable, "-m", "audiometa", "write", str(test_file), "--title", "CLI Test Title"],
25
+ capture_output=True,
26
+ text=True,
27
+ check=False,
28
+ )
29
+ assert result.returncode == 0
30
+ assert "Updated metadata" in result.stdout
31
+
32
+ def test_cli_with_spaces_in_filename_write(self):
33
+ with temp_file_with_metadata({}, "mp3") as test_file:
34
+ result = subprocess.run(
35
+ [
36
+ sys.executable,
37
+ "-m",
38
+ "audiometa",
39
+ "write",
40
+ str(test_file),
41
+ "--title",
42
+ "Test Title",
43
+ "--artist",
44
+ "Test Artist",
45
+ ],
46
+ capture_output=True,
47
+ text=True,
48
+ check=False,
49
+ )
50
+ assert result.returncode == 0
51
+ assert "Updated metadata" in result.stdout
@@ -0,0 +1,210 @@
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 TestCLIWriteComprehensive:
13
+ def test_cli_write_all_fields_comprehensive(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
+ "--title",
23
+ "Comprehensive Test Title",
24
+ "--artist",
25
+ "Artist One",
26
+ "--artist",
27
+ "Artist Two",
28
+ "--album",
29
+ "Test Album",
30
+ "--album-artist",
31
+ "Album Artist",
32
+ "--year",
33
+ "2024",
34
+ "--genre",
35
+ "Rock",
36
+ "--genre",
37
+ "Blues",
38
+ "--track-number",
39
+ "5/12",
40
+ "--disc-number",
41
+ "1",
42
+ "--disc-total",
43
+ "2",
44
+ "--rating",
45
+ "85",
46
+ "--bpm",
47
+ "120",
48
+ "--language",
49
+ "eng",
50
+ "--composer",
51
+ "Composer One",
52
+ "--composer",
53
+ "Composer Two",
54
+ "--publisher",
55
+ "Test Publisher",
56
+ "--copyright",
57
+ "© 2024",
58
+ "--lyrics",
59
+ "Test lyrics",
60
+ "--comment",
61
+ "Test comment",
62
+ "--replaygain",
63
+ "+2.5 dB",
64
+ "--archival-location",
65
+ "Archive",
66
+ "--isrc",
67
+ "USRC17607839",
68
+ ],
69
+ capture_output=True,
70
+ text=True,
71
+ check=False,
72
+ )
73
+ assert result.returncode == 0
74
+
75
+ metadata = get_unified_metadata(test_file)
76
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Comprehensive Test Title"
77
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist One", "Artist Two"]
78
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Test Album"
79
+ assert metadata.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Album Artist"]
80
+ assert metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
81
+ assert metadata.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock", "Blues"]
82
+ assert metadata.get(UnifiedMetadataKey.TRACK_NUMBER) == "5/12"
83
+ assert metadata.get(UnifiedMetadataKey.DISC_NUMBER) == 1
84
+ assert metadata.get(UnifiedMetadataKey.DISC_TOTAL) == 2
85
+ assert metadata.get(UnifiedMetadataKey.RATING) == 85
86
+ assert metadata.get(UnifiedMetadataKey.BPM) == 120
87
+ assert metadata.get(UnifiedMetadataKey.LANGUAGE) == "eng"
88
+ assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["Composer One", "Composer Two"]
89
+ assert metadata.get(UnifiedMetadataKey.PUBLISHER) == "Test Publisher"
90
+ assert metadata.get(UnifiedMetadataKey.COPYRIGHT) == "© 2024"
91
+ assert metadata.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "Test lyrics"
92
+ assert metadata.get(UnifiedMetadataKey.COMMENT) == "Test comment"
93
+ assert metadata.get(UnifiedMetadataKey.REPLAYGAIN) == "+2.5 dB"
94
+ assert metadata.get(UnifiedMetadataKey.ISRC) == "USRC17607839"
95
+ # ARCHIVAL_LOCATION is not supported by ID3v2 format (MP3)
96
+ # It's only supported by Vorbis (FLAC)
97
+
98
+ def test_cli_write_flac_all_fields(self):
99
+ with temp_file_with_metadata({}, "flac") as test_file:
100
+ result = subprocess.run(
101
+ [
102
+ sys.executable,
103
+ "-m",
104
+ "audiometa",
105
+ "write",
106
+ str(test_file),
107
+ "--title",
108
+ "FLAC Test",
109
+ "--artist",
110
+ "FLAC Artist",
111
+ "--album",
112
+ "FLAC Album",
113
+ "--track-number",
114
+ "3/10",
115
+ "--disc-number",
116
+ "1",
117
+ "--disc-total",
118
+ "2",
119
+ "--bpm",
120
+ "140",
121
+ "--language",
122
+ "eng",
123
+ "--composer",
124
+ "FLAC Composer",
125
+ "--publisher",
126
+ "FLAC Publisher",
127
+ "--copyright",
128
+ "© FLAC",
129
+ "--lyrics",
130
+ "FLAC lyrics",
131
+ "--comment",
132
+ "FLAC comment",
133
+ "--isrc",
134
+ "FRXXX1800001",
135
+ ],
136
+ capture_output=True,
137
+ text=True,
138
+ check=False,
139
+ )
140
+ assert result.returncode == 0
141
+
142
+ metadata = get_unified_metadata(test_file)
143
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "FLAC Test"
144
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["FLAC Artist"]
145
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "FLAC Album"
146
+ assert metadata.get(UnifiedMetadataKey.TRACK_NUMBER) == "3/10"
147
+ assert metadata.get(UnifiedMetadataKey.DISC_NUMBER) == 1
148
+ assert metadata.get(UnifiedMetadataKey.DISC_TOTAL) == 2
149
+ assert metadata.get(UnifiedMetadataKey.BPM) == 140
150
+ assert metadata.get(UnifiedMetadataKey.LANGUAGE) == "eng"
151
+ assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["FLAC Composer"]
152
+ assert metadata.get(UnifiedMetadataKey.PUBLISHER) == "FLAC Publisher"
153
+ assert metadata.get(UnifiedMetadataKey.COPYRIGHT) == "© FLAC"
154
+ assert metadata.get(UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS) == "FLAC lyrics"
155
+ assert metadata.get(UnifiedMetadataKey.COMMENT) == "FLAC comment"
156
+ assert metadata.get(UnifiedMetadataKey.ISRC) == "FRXXX1800001"
157
+
158
+ def test_cli_write_wav_all_fields(self):
159
+ with temp_file_with_metadata({}, "wav") as test_file:
160
+ result = subprocess.run(
161
+ [
162
+ sys.executable,
163
+ "-m",
164
+ "audiometa",
165
+ "write",
166
+ str(test_file),
167
+ "--title",
168
+ "WAV Test",
169
+ "--artist",
170
+ "WAV Artist",
171
+ "--album",
172
+ "WAV Album",
173
+ "--year",
174
+ "2024",
175
+ "--genre",
176
+ "Rock",
177
+ "--rating",
178
+ "100",
179
+ "--bpm",
180
+ "120",
181
+ "--language",
182
+ "eng",
183
+ "--composer",
184
+ "WAV Composer",
185
+ "--copyright",
186
+ "© WAV",
187
+ "--comment",
188
+ "WAV comment",
189
+ "--isrc",
190
+ "GBUM71505078",
191
+ ],
192
+ capture_output=True,
193
+ text=True,
194
+ check=False,
195
+ )
196
+ assert result.returncode == 0
197
+
198
+ metadata = get_unified_metadata(test_file)
199
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "WAV Test"
200
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["WAV Artist"]
201
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "WAV Album"
202
+ assert metadata.get(UnifiedMetadataKey.RELEASE_DATE) == "2024"
203
+ assert metadata.get(UnifiedMetadataKey.GENRES_NAMES) == ["Rock"]
204
+ assert metadata.get(UnifiedMetadataKey.RATING) == 100
205
+ assert metadata.get(UnifiedMetadataKey.BPM) == 120
206
+ assert metadata.get(UnifiedMetadataKey.LANGUAGE) == "eng"
207
+ assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["WAV Composer"]
208
+ assert metadata.get(UnifiedMetadataKey.COPYRIGHT) == "© WAV"
209
+ assert metadata.get(UnifiedMetadataKey.COMMENT) == "WAV comment"
210
+ assert metadata.get(UnifiedMetadataKey.ISRC) == "GBUM71505078"