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,216 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field
4
+ from audiometa.test.helpers.id3v2 import ID3v2HeaderVerifier, ID3v2MetadataGetter, ID3v2MetadataSetter
5
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
6
+ from audiometa.utils.metadata_format import MetadataFormat
7
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
8
+
9
+
10
+ @pytest.mark.integration
11
+ class TestId3v24Mixed:
12
+ def test_null_separated_artists(self):
13
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
14
+ ID3v2MetadataSetter.set_artists(test_file, ["Artist One", "Artist Two", "Artist Three"], version="2.4")
15
+
16
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
17
+
18
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
19
+ # raw_output replaces NUL bytes with slashes for display purposes
20
+ assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
21
+
22
+ artists = get_unified_metadata_field(
23
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
24
+ )
25
+
26
+ assert isinstance(artists, list)
27
+ assert len(artists) == 3
28
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
29
+
30
+ def test_null_separated_artists_iso_8859_1(self):
31
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
32
+ # Use a single NUL in the Python string; encoding=0 will encode it as a single 0x00
33
+ ID3v2MetadataSetter.write_tpe1_with_encoding(
34
+ test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=0
35
+ )
36
+
37
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
38
+
39
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
40
+ assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
41
+
42
+ artists = get_unified_metadata_field(
43
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
44
+ )
45
+
46
+ assert isinstance(artists, list)
47
+ assert len(artists) == 3
48
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
49
+
50
+ def test_null_separated_artists_utf16_with_bom(self):
51
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
52
+ # Using a Python string with '\x00' — when encoded as UTF-16 it will become two zero bytes
53
+ ID3v2MetadataSetter.write_tpe1_with_encoding(
54
+ test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=1
55
+ )
56
+
57
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
58
+
59
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
60
+ assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
61
+
62
+ artists = get_unified_metadata_field(
63
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
64
+ )
65
+
66
+ assert isinstance(artists, list)
67
+ assert len(artists) == 3
68
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
69
+
70
+ def test_null_separated_artists_utf16be(self):
71
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
72
+ # ID3v2.4 with encoding=2 (UTF-16BE without BOM) — separator becomes two NUL bytes after encoding.
73
+ ID3v2MetadataSetter.write_tpe1_with_encoding(
74
+ test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=2
75
+ )
76
+
77
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
78
+
79
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
80
+ assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
81
+
82
+ artists = get_unified_metadata_field(
83
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
84
+ )
85
+
86
+ assert isinstance(artists, list)
87
+ assert len(artists) == 3
88
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
89
+
90
+ def test_null_separated_artists_utf8(self):
91
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
92
+ # ID3v2.4 with encoding=3 (UTF-8) — separator is a single NUL byte.
93
+ ID3v2MetadataSetter.write_tpe1_with_encoding(
94
+ test_file, "Artist One\x00Artist Two\x00Artist Three", encoding=3
95
+ )
96
+
97
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
98
+
99
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
100
+ assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two\x00Artist Three"]
101
+
102
+ artists = get_unified_metadata_field(
103
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
104
+ )
105
+
106
+ assert isinstance(artists, list)
107
+ assert len(artists) == 3
108
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
109
+
110
+ def test_semicolon_separated_artists(self):
111
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
112
+ ID3v2MetadataSetter.set_artists(test_file, "Artist One;Artist Two;Artist Three", version="2.4")
113
+
114
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
115
+
116
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
117
+ assert raw_metadata["TPE1"] == ["Artist One;Artist Two;Artist Three"]
118
+
119
+ artists = get_unified_metadata_field(
120
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
121
+ )
122
+
123
+ assert isinstance(artists, list)
124
+ assert len(artists) == 3
125
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
126
+
127
+ def test_multiple_artists_in_multiple_entries_semicolon_separator(self):
128
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
129
+ ID3v2MetadataSetter.set_artists(
130
+ test_file, ["Artist One;Artist Two", "Artist Three"], in_separate_frames=True, version="2.4"
131
+ )
132
+
133
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
134
+
135
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
136
+ assert raw_metadata["TPE1"] == ["Artist One;Artist Two", "Artist Three"]
137
+
138
+ artists = get_unified_metadata_field(
139
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
140
+ )
141
+
142
+ assert isinstance(artists, list)
143
+ assert len(artists) == 2
144
+ assert artists == ["Artist One;Artist Two", "Artist Three"]
145
+
146
+ def test_null_separated_artists_in_multiple_entries(self):
147
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
148
+ ID3v2MetadataSetter.set_artists(
149
+ test_file, ["Artist One\0Artist Two", "Artist Three"], in_separate_frames=True, version="2.4"
150
+ )
151
+
152
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
153
+
154
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
155
+ assert raw_metadata["TPE1"] == ["Artist One\0Artist Two", "Artist Three"]
156
+
157
+ artists = get_unified_metadata_field(
158
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
159
+ )
160
+
161
+ assert isinstance(artists, list)
162
+ assert len(artists) == 3
163
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
164
+
165
+ def test_null_separated_artists_in_multiple_entries_and_semicolon_separated(self):
166
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
167
+ ID3v2MetadataSetter.set_artists(
168
+ test_file, ["Artist 1\0Artist 2", "Artist 3;Artist 4"], in_separate_frames=True, version="2.4"
169
+ )
170
+
171
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
172
+
173
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
174
+ assert raw_metadata["TPE1"] == ["Artist 1\0Artist 2", "Artist 3;Artist 4"]
175
+
176
+ artists = get_unified_metadata_field(
177
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
178
+ )
179
+
180
+ assert isinstance(artists, list)
181
+ assert len(artists) == 3
182
+ assert artists == ["Artist 1", "Artist 2", "Artist 3;Artist 4"]
183
+
184
+ def test_mixed_separators_semicolon_and_null(self):
185
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
186
+ ID3v2MetadataSetter.set_artists(test_file, ["Artist 1\0Artist 2;Artist 3"], version="2.4")
187
+
188
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
189
+
190
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
191
+ assert raw_metadata["TPE1"] == ["Artist 1\0Artist 2;Artist 3"]
192
+
193
+ artists = get_unified_metadata_field(
194
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.ID3V2
195
+ )
196
+ assert isinstance(artists, list)
197
+ assert len(artists) == 2
198
+ assert artists == ["Artist 1", "Artist 2;Artist 3"]
199
+
200
+ def test_multiple_title_entries_then_first_one(self):
201
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
202
+ ID3v2MetadataSetter.set_titles(
203
+ test_file, ["Title One", "Title Two", "Title Three"], in_separate_frames=True, version="2.4"
204
+ )
205
+
206
+ assert ID3v2HeaderVerifier.get_id3v2_version(test_file) == (2, 4, 0)
207
+
208
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file)
209
+ assert raw_metadata["TIT2"] == ["Title One", "Title Two", "Title Three"]
210
+
211
+ title = get_unified_metadata_field(
212
+ test_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.ID3V2
213
+ )
214
+
215
+ assert isinstance(title, str)
216
+ assert title == "Title One"
@@ -0,0 +1,84 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field
4
+ from audiometa.test.helpers.riff.riff_metadata_getter import RIFFMetadataGetter
5
+ from audiometa.test.helpers.riff.riff_metadata_setter import RIFFMetadataSetter
6
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
7
+ from audiometa.utils.metadata_format import MetadataFormat
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestRiff:
13
+ def test_semicolon_separated_artists(self):
14
+ with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
15
+ RIFFMetadataSetter.set_artists(test_file, ["Artist One;Artist Two;Artist Three"], in_separate_frames=False)
16
+
17
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
18
+ assert "TAG:artist=Artist One;Artist Two;Artist Three" in raw_metadata
19
+
20
+ artists = get_unified_metadata_field(
21
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.RIFF
22
+ )
23
+
24
+ assert isinstance(artists, list)
25
+ assert len(artists) == 3
26
+ assert "Artist One" in artists
27
+ assert "Artist Two" in artists
28
+ assert "Artist Three" in artists
29
+
30
+ def test_multiple_artists_in_multiple_entries(self):
31
+ with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
32
+ RIFFMetadataSetter.set_artists(test_file, ["One", "Two", "Three"], in_separate_frames=True)
33
+
34
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
35
+ # ffprobe only shows the last field when multiple fields with the same tag exist
36
+ assert "TAG:artist=Three" in raw_metadata
37
+
38
+ # Get RIFF metadata specifically to read the artists
39
+ artists = get_unified_metadata_field(
40
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.RIFF
41
+ )
42
+
43
+ assert isinstance(artists, list)
44
+ assert len(artists) == 3
45
+ assert "One" in artists
46
+ assert "Two" in artists
47
+ assert "Three" in artists
48
+
49
+ def test_mixed_separators_and_multiple_entries(self):
50
+ with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
51
+ RIFFMetadataSetter.set_artists(
52
+ test_file, ["Artist 1;Artist 2", "Artist 3", "Artist 4"], in_separate_frames=True
53
+ )
54
+
55
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
56
+ # ffprobe only shows the last field when multiple fields with the same tag exist
57
+ assert "TAG:artist=Artist 4" in raw_metadata
58
+
59
+ # Get RIFF metadata specifically to read the artists
60
+ artists = get_unified_metadata_field(
61
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.RIFF
62
+ )
63
+ assert isinstance(artists, list)
64
+
65
+ # We created 3 separate RIFF frames, so we should get 3 entries
66
+ # (separator parsing happens at a higher level, not in RIFF format itself)
67
+ assert len(artists) == 3
68
+ assert "Artist 1;Artist 2" in artists
69
+ assert "Artist 3" in artists
70
+ assert "Artist 4" in artists
71
+
72
+ def test_multiple_title_entries_then_first_one(self):
73
+ with temp_file_with_metadata({"title": "Test Song"}, "wav") as test_file:
74
+ RIFFMetadataSetter.set_multiple_titles(
75
+ test_file, ["Title One", "Title Two", "Title Three"], in_separate_frames=True
76
+ )
77
+
78
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
79
+ # ffprobe only shows the last field when multiple fields with the same tag exist
80
+ assert "TAG:title=Title Three" in raw_metadata
81
+
82
+ title = get_unified_metadata_field(test_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.RIFF)
83
+ assert isinstance(title, str)
84
+ assert title == "Title One"
@@ -0,0 +1,169 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field
4
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
5
+ from audiometa.test.helpers.vorbis.vorbis_metadata_getter import VorbisMetadataGetter
6
+ from audiometa.test.helpers.vorbis.vorbis_metadata_setter import VorbisMetadataSetter
7
+ from audiometa.utils.metadata_format import MetadataFormat
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestVorbis:
13
+ def test_null_value_separated_artists(self):
14
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
15
+ VorbisMetadataSetter.set_artists(
16
+ test_file, ["Artist One", "Artist Two", "Artist Three"], in_single_entry=True
17
+ )
18
+
19
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata_without_truncating_null_bytes_but_lower_case_keys(
20
+ test_file
21
+ )
22
+ # The key is lower case because that is how mutagen stores it but it is upper case in the file
23
+ assert "artist=Artist One\x00Artist Two\x00Artist Three" in raw_metadata
24
+
25
+ artists = get_unified_metadata_field(
26
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
27
+ )
28
+ assert isinstance(artists, list)
29
+ assert len(artists) == 3
30
+ assert "Artist One" in artists
31
+ assert "Artist Two" in artists
32
+ assert "Artist Three" in artists
33
+
34
+ def test_null_separated_artists_with_additional_entries(self):
35
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
36
+ VorbisMetadataSetter.set_artists(test_file, ["Artist A", "Artist B"], in_single_entry=True)
37
+ VorbisMetadataSetter.set_artists(
38
+ test_file, ["Artist C", "Artist D"], removing_existing=False, in_single_entry=True
39
+ )
40
+
41
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata_without_truncating_null_bytes_but_lower_case_keys(
42
+ test_file
43
+ )
44
+ # The key is lower case because that is how mutagen stores it but it is upper case in the file
45
+ assert "artist=Artist A\x00Artist B" in raw_metadata
46
+ assert "artist=Artist C\x00Artist D" in raw_metadata
47
+
48
+ artists = get_unified_metadata_field(
49
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
50
+ )
51
+ assert isinstance(artists, list)
52
+ assert len(artists) == 4
53
+ assert "Artist A" in artists
54
+ assert "Artist B" in artists
55
+ assert "Artist C" in artists
56
+ assert "Artist D" in artists
57
+
58
+ def test_null_separated_artists_in_multiple_entries_with_semicolon(self):
59
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
60
+ VorbisMetadataSetter.set_artists(test_file, ["Artist One\x00Artist Two", "Artist Three;Artist Four"])
61
+
62
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata_without_truncating_null_bytes_but_lower_case_keys(
63
+ test_file
64
+ )
65
+ assert "artist=Artist One\x00Artist Two" in raw_metadata
66
+ assert "artist=Artist Three;Artist Four" in raw_metadata
67
+
68
+ artists = get_unified_metadata_field(
69
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
70
+ )
71
+ assert isinstance(artists, list)
72
+ assert len(artists) == 3
73
+ assert "Artist One" in artists
74
+ assert "Artist Two" in artists
75
+ assert "Artist Three;Artist Four" in artists
76
+
77
+ def test_semicolon_separated_artists(self):
78
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
79
+ VorbisMetadataSetter.set_artists(test_file, ["Artist One;Artist Two;Artist Three"])
80
+
81
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
82
+ assert "ARTIST=Artist One;Artist Two;Artist Three" in raw_metadata
83
+
84
+ artists = get_unified_metadata_field(
85
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
86
+ )
87
+ assert isinstance(artists, list)
88
+ assert len(artists) == 3
89
+ assert "Artist One" in artists
90
+ assert "Artist Two" in artists
91
+ assert "Artist Three" in artists
92
+
93
+ def test_artists_in_multiple_entries(self):
94
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
95
+ VorbisMetadataSetter.set_artists(test_file, ["One", "Two", "Three"])
96
+
97
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
98
+ assert "ARTIST=One" in raw_metadata
99
+ assert "ARTIST=Two" in raw_metadata
100
+ assert "ARTIST=Three" in raw_metadata
101
+
102
+ artists = get_unified_metadata_field(
103
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
104
+ )
105
+
106
+ assert isinstance(artists, list)
107
+ assert len(artists) == 3
108
+ assert "One" in artists
109
+ assert "Two" in artists
110
+ assert "Three" in artists
111
+
112
+ def test_artists_in_multiple_entries_with_different_key_casings(self):
113
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
114
+ VorbisMetadataSetter.set_artists(test_file, ["Artist A", "Artist B"])
115
+ VorbisMetadataSetter.set_artists(
116
+ test_file, ["Artist C", "Artist D"], removing_existing=False, key_lower_case=True
117
+ )
118
+
119
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
120
+ assert "artist=Artist A" in raw_metadata
121
+ assert "artist=Artist B" in raw_metadata
122
+ assert "artist=Artist C" in raw_metadata
123
+ assert "artist=Artist D" in raw_metadata
124
+
125
+ artists = get_unified_metadata_field(
126
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
127
+ )
128
+
129
+ assert isinstance(artists, list)
130
+ assert len(artists) == 4
131
+ assert "Artist A" in artists
132
+ assert "Artist B" in artists
133
+ assert "Artist C" in artists
134
+ assert "Artist D" in artists
135
+
136
+ def test_mixed_single_and_multiple_entries(self):
137
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
138
+ VorbisMetadataSetter.set_artists(test_file, ["Artist 1;Artist 2", "Artist 3", "Artist 4"])
139
+
140
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
141
+
142
+ assert "ARTIST=Artist 1;Artist" in raw_metadata
143
+ assert "ARTIST=Artist 3" in raw_metadata
144
+ assert "ARTIST=Artist 4" in raw_metadata
145
+
146
+ artists = get_unified_metadata_field(
147
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
148
+ )
149
+ assert isinstance(artists, list)
150
+ assert len(artists) == 3
151
+ assert "Artist 1;Artist 2" in artists
152
+ assert "Artist 3" in artists
153
+ assert "Artist 4" in artists
154
+
155
+ def test_multiple_title_entries_returns_first_value(self):
156
+ with temp_file_with_metadata({}, "flac") as test_file:
157
+ VorbisMetadataSetter.add_title(test_file, "Title One")
158
+ VorbisMetadataSetter.add_title(test_file, "Title Two")
159
+ VorbisMetadataSetter.add_title(test_file, "Title Three")
160
+
161
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
162
+ assert "TITLE=Title One" in raw_metadata
163
+ assert "TITLE=Title Two" in raw_metadata
164
+ assert "TITLE=Title Three" in raw_metadata
165
+
166
+ title = get_unified_metadata_field(
167
+ test_file, UnifiedMetadataKey.TITLE, metadata_format=MetadataFormat.VORBIS
168
+ )
169
+ assert title == "Title One"
@@ -0,0 +1,209 @@
1
+ import time
2
+
3
+ import pytest
4
+
5
+ from audiometa import get_unified_metadata
6
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
7
+ from audiometa.test.helpers.vorbis import VorbisMetadataSetter
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestPerformanceLargeData:
13
+ def test_performance_with_many_entries(self):
14
+ # Create temporary file with basic metadata
15
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
16
+ # Set many artists using temp_file_with_metadata
17
+ artists_list = [f"Artist {i + 1}" for i in range(20)]
18
+ VorbisMetadataSetter.set_artists(test_file, artists_list)
19
+
20
+ for _ in range(5):
21
+ unified_metadata = get_unified_metadata(test_file)
22
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
23
+
24
+ assert isinstance(artists, list)
25
+ assert len(artists) == 20
26
+
27
+ def test_performance_with_large_separated_values(self):
28
+ # Create temporary file with basic metadata
29
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
30
+ # Set many artists using temp_file_with_metadata
31
+ artists_list = [f"Artist {i + 1}" for i in range(50)]
32
+ VorbisMetadataSetter.set_artists(test_file, artists_list)
33
+
34
+ start_time = time.time()
35
+ unified_metadata = get_unified_metadata(test_file)
36
+ end_time = time.time()
37
+
38
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
39
+
40
+ assert isinstance(artists, list)
41
+ assert len(artists) == 50
42
+
43
+ # Performance should be reasonable (less than 1 second for 50 artists)
44
+ assert (end_time - start_time) < 1.0
45
+
46
+ def test_performance_with_mixed_separators_large(self):
47
+ # Create temporary file with basic metadata
48
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
49
+ # Test performance with complex separator scenarios
50
+ complex_artists = [
51
+ "Artist 1",
52
+ "Artist 2",
53
+ "Artist 3",
54
+ "Artist 4",
55
+ "Artist 5",
56
+ "Artist 6",
57
+ "Artist 7",
58
+ "Artist 8",
59
+ "Artist 9",
60
+ "Artist 10",
61
+ "Artist 11",
62
+ "Artist 12",
63
+ "Artist 13",
64
+ "Artist 14",
65
+ "Artist 15",
66
+ "Artist 16",
67
+ "Artist 17",
68
+ "Artist 18",
69
+ "Artist 19",
70
+ "Artist 20",
71
+ "Artist 21",
72
+ "Artist 22",
73
+ "Artist 23",
74
+ "Artist 24",
75
+ "Artist 25",
76
+ "Artist 26",
77
+ "Artist 27",
78
+ "Artist 28",
79
+ "Artist 29",
80
+ "Artist 30",
81
+ ]
82
+
83
+ try:
84
+ VorbisMetadataSetter.set_artists(test_file, complex_artists)
85
+ except RuntimeError:
86
+ pytest.skip("metaflac not available or failed to set complex separated artists")
87
+
88
+ start_time = time.time()
89
+ unified_metadata = get_unified_metadata(test_file)
90
+ end_time = time.time()
91
+
92
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
93
+
94
+ assert isinstance(artists, list)
95
+ # Should have many artists after complex separation
96
+ assert len(artists) >= 30
97
+
98
+ # Performance should be reasonable
99
+ assert (end_time - start_time) < 2.0
100
+
101
+ def test_memory_usage_with_large_values(self):
102
+ # Create temporary file with basic metadata
103
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
104
+ # Test with very long individual values
105
+ long_artist = "A" * 50000 # 50,000 character artist name
106
+
107
+ try:
108
+ VorbisMetadataSetter.set_artist(test_file, long_artist)
109
+ except RuntimeError:
110
+ pytest.skip("metaflac not available or failed to set very long artist")
111
+
112
+ unified_metadata = get_unified_metadata(test_file)
113
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
114
+
115
+ assert isinstance(artists, list)
116
+ assert len(artists) == 1
117
+ assert artists[0] == long_artist
118
+
119
+ def test_performance_repeated_reads(self):
120
+ # Create temporary file with basic metadata
121
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
122
+ # Set up test data
123
+ try:
124
+ artists_list = [f"Artist {i + 1}" for i in range(10)]
125
+ VorbisMetadataSetter.set_artists(test_file, artists_list)
126
+ except RuntimeError:
127
+ pytest.skip("metaflac not available or failed to set test artists")
128
+
129
+ # Test repeated reads
130
+ start_time = time.time()
131
+ for _ in range(100):
132
+ unified_metadata = get_unified_metadata(test_file)
133
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
134
+ assert isinstance(artists, list)
135
+ assert len(artists) == 10
136
+ end_time = time.time()
137
+
138
+ # 100 reads should complete in reasonable time
139
+ assert (end_time - start_time) < 5.0
140
+
141
+ def test_performance_with_all_multi_value_fields(self):
142
+ # Create temporary file with basic metadata
143
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
144
+ # Test performance when multiple multi-value fields are populated
145
+ try:
146
+ # Set multiple artists
147
+ artists_list = [f"Artist {i + 1}" for i in range(10)]
148
+ VorbisMetadataSetter.set_artists(test_file, artists_list)
149
+
150
+ # Set multiple genres
151
+ genres_list = [f"Genre {i + 1}" for i in range(5)]
152
+ VorbisMetadataSetter.set_genres(test_file, genres_list)
153
+
154
+ # Set multiple composers
155
+ composers_list = [f"Composer {i + 1}" for i in range(8)]
156
+ VorbisMetadataSetter.set_composers(test_file, composers_list)
157
+
158
+ except RuntimeError:
159
+ pytest.skip("metaflac not available or failed to set multiple multi-value fields")
160
+
161
+ start_time = time.time()
162
+ unified_metadata = get_unified_metadata(test_file)
163
+ end_time = time.time()
164
+
165
+ # Check all multi-value fields
166
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
167
+ assert isinstance(artists, list)
168
+ assert len(artists) == 10
169
+
170
+ genres = unified_metadata.get(UnifiedMetadataKey.GENRES_NAMES)
171
+ assert isinstance(genres, list)
172
+ assert len(genres) == 5
173
+
174
+ composers = unified_metadata.get(UnifiedMetadataKey.COMPOSERS)
175
+ assert isinstance(composers, list)
176
+ assert len(composers) == 8
177
+
178
+ # Performance should be reasonable
179
+ assert (end_time - start_time) < 2.0
180
+
181
+ def test_performance_with_whitespace_heavy_data(self):
182
+ # Create temporary file with basic metadata
183
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
184
+ # Test performance with data that has lots of whitespace processing
185
+ whitespace_heavy_artists = [" Artist One ", " Artist Two ", " Artist Three "]
186
+
187
+ try:
188
+ VorbisMetadataSetter.set_artists(test_file, whitespace_heavy_artists)
189
+ except RuntimeError:
190
+ pytest.skip("metaflac not available or failed to set whitespace-heavy artists")
191
+
192
+ start_time = time.time()
193
+ unified_metadata = get_unified_metadata(test_file)
194
+ end_time = time.time()
195
+
196
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
197
+
198
+ assert isinstance(artists, list)
199
+ assert len(artists) == 3
200
+ assert "Artist One" in artists
201
+ assert "Artist Two" in artists
202
+ assert "Artist Three" in artists
203
+
204
+ # Check that whitespace was properly stripped
205
+ for artist in artists:
206
+ assert artist == artist.strip()
207
+
208
+ # Performance should be reasonable
209
+ assert (end_time - start_time) < 1.0