audiometa-python 0.2.2__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 (318) hide show
  1. audiometa/__init__.py +1240 -0
  2. audiometa/__main__.py +6 -0
  3. audiometa/_audio_file.py +602 -0
  4. audiometa/cli.py +347 -0
  5. audiometa/exceptions.py +167 -0
  6. audiometa/manager/_MetadataManager.py +665 -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 +945 -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 +778 -0
  14. audiometa/manager/_rating_supporting/riff/__init__.py +25 -0
  15. audiometa/manager/_rating_supporting/riff/_riff_constants.py +10 -0
  16. audiometa/manager/_rating_supporting/vorbis/_VorbisManager.py +525 -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 +305 -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 +188 -0
  40. audiometa/test/helpers/id3v2/id3v2_metadata_setter.py +428 -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 +216 -0
  44. audiometa/test/helpers/riff/riff_metadata_deleter.py +56 -0
  45. audiometa/test/helpers/riff/riff_metadata_getter.py +118 -0
  46. audiometa/test/helpers/riff/riff_metadata_setter.py +196 -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 +200 -0
  55. audiometa/test/tests/__init__.py +0 -0
  56. audiometa/test/tests/conftest.py +261 -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/test_cli_delete.py +20 -0
  69. audiometa/test/tests/e2e/cli/test_cli_formatting.py +31 -0
  70. audiometa/test/tests/e2e/cli/test_cli_help.py +40 -0
  71. audiometa/test/tests/e2e/cli/test_cli_read.py +79 -0
  72. audiometa/test/tests/e2e/cli/test_cli_unified.py +33 -0
  73. audiometa/test/tests/e2e/cli/test_cli_write.py +116 -0
  74. audiometa/test/tests/e2e/scenarios/__init__.py +0 -0
  75. audiometa/test/tests/e2e/scenarios/test_user_scenarios.py +166 -0
  76. audiometa/test/tests/e2e/workflows/__init__.py +0 -0
  77. audiometa/test/tests/e2e/workflows/test_core_workflows.py +166 -0
  78. audiometa/test/tests/e2e/workflows/test_deletion_workflows.py +318 -0
  79. audiometa/test/tests/e2e/workflows/test_error_handling_workflows.py +165 -0
  80. audiometa/test/tests/e2e/workflows/test_format_specific_workflows.py +129 -0
  81. audiometa/test/tests/e2e/workflows/test_rating_workflows.py +124 -0
  82. audiometa/test/tests/integration/__init__.py +0 -0
  83. audiometa/test/tests/integration/audio_format/__init__.py +0 -0
  84. audiometa/test/tests/integration/audio_format/flac/__init__.py +0 -0
  85. audiometa/test/tests/integration/audio_format/flac/test_flac_delete_all.py +108 -0
  86. audiometa/test/tests/integration/audio_format/flac/test_flac_reading_all.py +61 -0
  87. audiometa/test/tests/integration/audio_format/flac/test_flac_reading_field.py +65 -0
  88. audiometa/test/tests/integration/audio_format/flac/test_flac_writing.py +69 -0
  89. audiometa/test/tests/integration/audio_format/mp3/__init__.py +0 -0
  90. audiometa/test/tests/integration/audio_format/mp3/test_mp3_delete_all.py +79 -0
  91. audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_all.py +61 -0
  92. audiometa/test/tests/integration/audio_format/mp3/test_mp3_reading_field.py +67 -0
  93. audiometa/test/tests/integration/audio_format/mp3/test_mp3_writing.py +60 -0
  94. audiometa/test/tests/integration/audio_format/wav/__init__.py +0 -0
  95. audiometa/test/tests/integration/audio_format/wav/test_wav_delete_all.py +87 -0
  96. audiometa/test/tests/integration/audio_format/wav/test_wav_reading_all.py +62 -0
  97. audiometa/test/tests/integration/audio_format/wav/test_wav_reading_field.py +57 -0
  98. audiometa/test/tests/integration/audio_format/wav/test_wav_with_id3v2_tags.py +83 -0
  99. audiometa/test/tests/integration/audio_format/wav/test_wav_writing.py +62 -0
  100. audiometa/test/tests/integration/conftest.py +29 -0
  101. audiometa/test/tests/integration/delete_all_metadata/__init__.py +1 -0
  102. audiometa/test/tests/integration/delete_all_metadata/test_audio_format_all.py +102 -0
  103. audiometa/test/tests/integration/delete_all_metadata/test_audio_format_header_deletion.py +77 -0
  104. audiometa/test/tests/integration/delete_all_metadata/test_basic_functionality.py +47 -0
  105. audiometa/test/tests/integration/delete_all_metadata/test_error_handling.py +24 -0
  106. audiometa/test/tests/integration/encoding/__init__.py +1 -0
  107. audiometa/test/tests/integration/encoding/test_encoding.py +88 -0
  108. audiometa/test/tests/integration/encoding/test_special_characters_edge_cases.py +223 -0
  109. audiometa/test/tests/integration/get_full_metadata/__init__.py +0 -0
  110. audiometa/test/tests/integration/get_full_metadata/test_binary_data_filtering.py +250 -0
  111. audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata.py +297 -0
  112. audiometa/test/tests/integration/get_full_metadata/test_get_full_metadata_edge_cases.py +231 -0
  113. audiometa/test/tests/integration/get_full_metadata/test_options.py +207 -0
  114. audiometa/test/tests/integration/get_full_metadata/test_parsed_fields_keys.py +85 -0
  115. audiometa/test/tests/integration/metadata_field/__init__.py +0 -0
  116. audiometa/test/tests/integration/metadata_field/album/__init__.py +0 -0
  117. audiometa/test/tests/integration/metadata_field/album/test_deleting.py +73 -0
  118. audiometa/test/tests/integration/metadata_field/album/test_reading.py +36 -0
  119. audiometa/test/tests/integration/metadata_field/album/test_writing.py +50 -0
  120. audiometa/test/tests/integration/metadata_field/album_artists/__init__.py +0 -0
  121. audiometa/test/tests/integration/metadata_field/album_artists/test_deleting.py +83 -0
  122. audiometa/test/tests/integration/metadata_field/album_artists/test_reading.py +38 -0
  123. audiometa/test/tests/integration/metadata_field/album_artists/test_writing.py +52 -0
  124. audiometa/test/tests/integration/metadata_field/artists/__init__.py +0 -0
  125. audiometa/test/tests/integration/metadata_field/artists/test_deleting.py +68 -0
  126. audiometa/test/tests/integration/metadata_field/artists/test_reading.py +36 -0
  127. audiometa/test/tests/integration/metadata_field/artists/test_writing.py +46 -0
  128. audiometa/test/tests/integration/metadata_field/bpm/__init__.py +0 -0
  129. audiometa/test/tests/integration/metadata_field/bpm/test_deleting.py +75 -0
  130. audiometa/test/tests/integration/metadata_field/bpm/test_reading.py +32 -0
  131. audiometa/test/tests/integration/metadata_field/bpm/test_writing.py +56 -0
  132. audiometa/test/tests/integration/metadata_field/comment/__init__.py +0 -0
  133. audiometa/test/tests/integration/metadata_field/comment/test_deleting.py +68 -0
  134. audiometa/test/tests/integration/metadata_field/comment/test_reading.py +36 -0
  135. audiometa/test/tests/integration/metadata_field/comment/test_writing.py +49 -0
  136. audiometa/test/tests/integration/metadata_field/composer/__init__.py +0 -0
  137. audiometa/test/tests/integration/metadata_field/composer/test_deleting.py +75 -0
  138. audiometa/test/tests/integration/metadata_field/composer/test_reading.py +34 -0
  139. audiometa/test/tests/integration/metadata_field/composer/test_writing.py +41 -0
  140. audiometa/test/tests/integration/metadata_field/copyright/__init__.py +0 -0
  141. audiometa/test/tests/integration/metadata_field/copyright/test_deleting.py +81 -0
  142. audiometa/test/tests/integration/metadata_field/copyright/test_reading.py +35 -0
  143. audiometa/test/tests/integration/metadata_field/copyright/test_writing.py +41 -0
  144. audiometa/test/tests/integration/metadata_field/field_not_supported/__init__.py +0 -0
  145. audiometa/test/tests/integration/metadata_field/field_not_supported/test_deleting.py +56 -0
  146. audiometa/test/tests/integration/metadata_field/field_not_supported/test_reading.py +54 -0
  147. audiometa/test/tests/integration/metadata_field/field_not_supported/test_writing.py +61 -0
  148. audiometa/test/tests/integration/metadata_field/genre/__init__.py +0 -0
  149. audiometa/test/tests/integration/metadata_field/genre/reading/__init__.py +0 -0
  150. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/__init__.py +0 -0
  151. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v1_reading.py +65 -0
  152. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_id3v2_reading.py +25 -0
  153. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_riff_reading.py +58 -0
  154. audiometa/test/tests/integration/metadata_field/genre/reading/metadata_format/test_vorbis_reading.py +61 -0
  155. audiometa/test/tests/integration/metadata_field/genre/reading/test_smart_reading.py +191 -0
  156. audiometa/test/tests/integration/metadata_field/genre/test_deleting.py +62 -0
  157. audiometa/test/tests/integration/metadata_field/genre/test_writing.py +64 -0
  158. audiometa/test/tests/integration/metadata_field/language/__init__.py +0 -0
  159. audiometa/test/tests/integration/metadata_field/language/test_deleting.py +75 -0
  160. audiometa/test/tests/integration/metadata_field/language/test_reading.py +39 -0
  161. audiometa/test/tests/integration/metadata_field/language/test_writing.py +43 -0
  162. audiometa/test/tests/integration/metadata_field/lyrics/__init__.py +0 -0
  163. audiometa/test/tests/integration/metadata_field/lyrics/test_deleting.py +129 -0
  164. audiometa/test/tests/integration/metadata_field/lyrics/test_reading.py +57 -0
  165. audiometa/test/tests/integration/metadata_field/lyrics/test_writing.py +59 -0
  166. audiometa/test/tests/integration/metadata_field/publisher/__init__.py +0 -0
  167. audiometa/test/tests/integration/metadata_field/publisher/test_deleting.py +88 -0
  168. audiometa/test/tests/integration/metadata_field/publisher/test_reading.py +32 -0
  169. audiometa/test/tests/integration/metadata_field/publisher/test_writing.py +47 -0
  170. audiometa/test/tests/integration/metadata_field/rating/__init__.py +0 -0
  171. audiometa/test/tests/integration/metadata_field/rating/reading/__init__.py +0 -0
  172. audiometa/test/tests/integration/metadata_field/rating/reading/test_base_100_proportional.py +81 -0
  173. audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_non_proportional.py +33 -0
  174. audiometa/test/tests/integration/metadata_field/rating/reading/test_base_255_proportional.py +58 -0
  175. audiometa/test/tests/integration/metadata_field/rating/test_deleting.py +117 -0
  176. audiometa/test/tests/integration/metadata_field/rating/test_error_handling.py +137 -0
  177. audiometa/test/tests/integration/metadata_field/rating/writing/__init__.py +0 -0
  178. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/__init__.py +0 -0
  179. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_id3v2.py +77 -0
  180. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_riff.py +55 -0
  181. audiometa/test/tests/integration/metadata_field/rating/writing/metadata_format/test_vorbis.py +57 -0
  182. audiometa/test/tests/integration/metadata_field/rating/writing/test_comprehensive.py +192 -0
  183. audiometa/test/tests/integration/metadata_field/release_date/__init__.py +0 -0
  184. audiometa/test/tests/integration/metadata_field/release_date/test_deleting.py +74 -0
  185. audiometa/test/tests/integration/metadata_field/release_date/test_error_handling.py +82 -0
  186. audiometa/test/tests/integration/metadata_field/release_date/test_reading.py +59 -0
  187. audiometa/test/tests/integration/metadata_field/release_date/test_writing.py +49 -0
  188. audiometa/test/tests/integration/metadata_field/test_metadata_field_validation.py +134 -0
  189. audiometa/test/tests/integration/metadata_field/title/__init__.py +0 -0
  190. audiometa/test/tests/integration/metadata_field/title/test_deleting.py +73 -0
  191. audiometa/test/tests/integration/metadata_field/title/test_error_handling.py +47 -0
  192. audiometa/test/tests/integration/metadata_field/title/test_reading.py +36 -0
  193. audiometa/test/tests/integration/metadata_field/title/test_writing.py +64 -0
  194. audiometa/test/tests/integration/metadata_field/track_number/__init__.py +0 -0
  195. audiometa/test/tests/integration/metadata_field/track_number/reading/__init__.py +0 -0
  196. audiometa/test/tests/integration/metadata_field/track_number/reading/test_edge_cases.py +43 -0
  197. audiometa/test/tests/integration/metadata_field/track_number/reading/test_metadata_format.py +32 -0
  198. audiometa/test/tests/integration/metadata_field/track_number/test_deleting.py +59 -0
  199. audiometa/test/tests/integration/metadata_field/track_number/test_writing.py +73 -0
  200. audiometa/test/tests/integration/multiple_values/__init__.py +1 -0
  201. audiometa/test/tests/integration/multiple_values/reading/__init__.py +1 -0
  202. audiometa/test/tests/integration/multiple_values/reading/metadata_format/__init__.py +1 -0
  203. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v1.py +23 -0
  204. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_3.py +92 -0
  205. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_id3v2_4.py +216 -0
  206. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_riff.py +84 -0
  207. audiometa/test/tests/integration/multiple_values/reading/metadata_format/test_vorbis.py +169 -0
  208. audiometa/test/tests/integration/multiple_values/reading/test_performance_large_data.py +209 -0
  209. audiometa/test/tests/integration/multiple_values/reading/test_smart_parsing_scenarios.py +198 -0
  210. audiometa/test/tests/integration/multiple_values/reading/test_unicode_handling.py +24 -0
  211. audiometa/test/tests/integration/multiple_values/writing/__init__.py +1 -0
  212. audiometa/test/tests/integration/multiple_values/writing/metadata_format/__init__.py +1 -0
  213. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v1.py +62 -0
  214. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_3.py +36 -0
  215. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_id3v2_4.py +34 -0
  216. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_riff.py +32 -0
  217. audiometa/test/tests/integration/multiple_values/writing/metadata_format/test_vorbis.py +54 -0
  218. audiometa/test/tests/integration/multiple_values/writing/test_error_handling.py +42 -0
  219. audiometa/test/tests/integration/multiple_values/writing/test_large_values.py +98 -0
  220. audiometa/test/tests/integration/reading/__init__.py +1 -0
  221. audiometa/test/tests/integration/reading/test_read_multiple_metadata.py +80 -0
  222. audiometa/test/tests/integration/reading/test_reading_error_handling.py +36 -0
  223. audiometa/test/tests/integration/real_audio_files/__init__.py +0 -0
  224. audiometa/test/tests/integration/real_audio_files/test_reading.py +146 -0
  225. audiometa/test/tests/integration/real_audio_files/test_writing.py +198 -0
  226. audiometa/test/tests/integration/technical_info/__init__.py +0 -0
  227. audiometa/test/tests/integration/technical_info/flac_md5/__init__.py +0 -0
  228. audiometa/test/tests/integration/technical_info/flac_md5/conftest.py +103 -0
  229. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/__init__.py +0 -0
  230. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_audio_data_corruption.py +21 -0
  231. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_flipped_md5.py +29 -0
  232. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_non_flac_error.py +13 -0
  233. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_partial_md5.py +29 -0
  234. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_random_md5.py +29 -0
  235. audiometa/test/tests/integration/technical_info/flac_md5/test_invalid_md5/test_unset_md5.py +56 -0
  236. audiometa/test/tests/integration/technical_info/flac_md5/test_valid_md5.py +21 -0
  237. audiometa/test/tests/integration/technical_info/test_bitrate.py +38 -0
  238. audiometa/test/tests/integration/technical_info/test_channels.py +38 -0
  239. audiometa/test/tests/integration/technical_info/test_duration_in_sec.py +38 -0
  240. audiometa/test/tests/integration/technical_info/test_sample_rate.py +40 -0
  241. audiometa/test/tests/integration/test_audio_file.py +23 -0
  242. audiometa/test/tests/integration/test_audio_format_readable_after_update_all_metadata_formats.py +95 -0
  243. audiometa/test/tests/integration/writing/__init__.py +0 -0
  244. audiometa/test/tests/integration/writing/test_error_handling.py +44 -0
  245. audiometa/test/tests/integration/writing/test_forced_format.py +224 -0
  246. audiometa/test/tests/integration/writing/test_multiple_format_preservation.py +223 -0
  247. audiometa/test/tests/integration/writing/test_partial_update.py +36 -0
  248. audiometa/test/tests/integration/writing/writing_strategies/__init__.py +0 -0
  249. audiometa/test/tests/integration/writing/writing_strategies/test_cleanup_strategy.py +79 -0
  250. audiometa/test/tests/integration/writing/writing_strategies/test_preserve_strategy.py +76 -0
  251. audiometa/test/tests/integration/writing/writing_strategies/test_sync_strategy.py +215 -0
  252. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/__init__.py +0 -0
  253. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_fail_behavior.py +42 -0
  254. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_no_writing_on_failure.py +93 -0
  255. audiometa/test/tests/integration/writing/writing_strategies/unsupported_fields/test_strategy_specific.py +99 -0
  256. audiometa/test/tests/unit/__init__.py +0 -0
  257. audiometa/test/tests/unit/audio_file/__init__.py +0 -0
  258. audiometa/test/tests/unit/audio_file/technical_info/__init__.py +0 -0
  259. audiometa/test/tests/unit/audio_file/technical_info/test_bitrate.py +26 -0
  260. audiometa/test/tests/unit/audio_file/technical_info/test_channels.py +31 -0
  261. audiometa/test/tests/unit/audio_file/technical_info/test_duration_in_sec.py +38 -0
  262. audiometa/test/tests/unit/audio_file/technical_info/test_error_handling.py +130 -0
  263. audiometa/test/tests/unit/audio_file/technical_info/test_file_size.py +51 -0
  264. audiometa/test/tests/unit/audio_file/technical_info/test_format_name.py +28 -0
  265. audiometa/test/tests/unit/audio_file/technical_info/test_sample_rate.py +31 -0
  266. audiometa/test/tests/unit/audio_file/test_context_manager.py +30 -0
  267. audiometa/test/tests/unit/audio_file/test_file_validation.py +40 -0
  268. audiometa/test/tests/unit/audio_file/test_operations.py +20 -0
  269. audiometa/test/tests/unit/audio_file/test_path_handling.py +23 -0
  270. audiometa/test/tests/unit/cli/__init__.py +0 -0
  271. audiometa/test/tests/unit/cli/test_expand_file_patterns.py +234 -0
  272. audiometa/test/tests/unit/metadata_managers/__init__.py +0 -0
  273. audiometa/test/tests/unit/metadata_managers/conftest.py +142 -0
  274. audiometa/test/tests/unit/metadata_managers/header_info/__init__.py +0 -0
  275. audiometa/test/tests/unit/metadata_managers/header_info/test_id3v1.py +49 -0
  276. audiometa/test/tests/unit/metadata_managers/header_info/test_id3v2.py +66 -0
  277. audiometa/test/tests/unit/metadata_managers/header_info/test_riff.py +57 -0
  278. audiometa/test/tests/unit/metadata_managers/header_info/test_vorbis.py +53 -0
  279. audiometa/test/tests/unit/metadata_managers/metadata_field/__init__.py +0 -0
  280. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/__init__.py +0 -0
  281. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/__init__.py +0 -0
  282. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/reading/test_smart_parsing.py +186 -0
  283. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/__init__.py +0 -0
  284. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_separator_selection.py +142 -0
  285. audiometa/test/tests/unit/metadata_managers/metadata_field/multiple_values/writing/test_value_filtering.py +76 -0
  286. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/__init__.py +0 -0
  287. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/__init__.py +0 -0
  288. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_normalization.py +152 -0
  289. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/reading/test_profiles_values.py +23 -0
  290. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/test_rating_validation.py +77 -0
  291. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/__init__.py +0 -0
  292. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_validation.py +151 -0
  293. audiometa/test/tests/unit/metadata_managers/metadata_field/rating/writing/test_writing_profiles.py +61 -0
  294. audiometa/test/tests/unit/metadata_managers/metadata_field/test_date_format_validation.py +135 -0
  295. audiometa/test/tests/unit/metadata_managers/metadata_field/test_track_number_validation.py +46 -0
  296. audiometa/test/tests/unit/metadata_managers/metadata_field/test_type_validation_exception.py +22 -0
  297. audiometa/test/tests/unit/metadata_managers/metadata_field/test_validation.py +83 -0
  298. audiometa/test/tests/unit/metadata_managers/test_metadata_format_managers_write_and_read.py +74 -0
  299. audiometa/utils/__init__.py +1 -0
  300. audiometa/utils/id3v1_genre_code_map.py +205 -0
  301. audiometa/utils/metadata_format.py +31 -0
  302. audiometa/utils/metadata_writing_strategy.py +16 -0
  303. audiometa/utils/os_dependencies_checker/__init__.py +24 -0
  304. audiometa/utils/os_dependencies_checker/base.py +62 -0
  305. audiometa/utils/os_dependencies_checker/config.py +51 -0
  306. audiometa/utils/os_dependencies_checker/macos.py +236 -0
  307. audiometa/utils/os_dependencies_checker/ubuntu.py +95 -0
  308. audiometa/utils/os_dependencies_checker/windows.py +227 -0
  309. audiometa/utils/rating_profiles.py +110 -0
  310. audiometa/utils/tool_path_resolver.py +135 -0
  311. audiometa/utils/types.py +82 -0
  312. audiometa/utils/unified_metadata_key.py +81 -0
  313. audiometa_python-0.2.2.dist-info/METADATA +1464 -0
  314. audiometa_python-0.2.2.dist-info/RECORD +318 -0
  315. audiometa_python-0.2.2.dist-info/WHEEL +5 -0
  316. audiometa_python-0.2.2.dist-info/entry_points.txt +2 -0
  317. audiometa_python-0.2.2.dist-info/licenses/LICENSE +202 -0
  318. audiometa_python-0.2.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,36 @@
1
+ import pytest
2
+
3
+ from audiometa import update_metadata
4
+ from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
5
+ from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
6
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
7
+ from audiometa.utils.metadata_format import MetadataFormat
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestMultipleValuesId3v23:
13
+ def test_artists_concatenation(self):
14
+ initial_metadata = {"title": "Test Song"}
15
+ with temp_file_with_metadata(initial_metadata, "id3v2.3") as test_file:
16
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Artist 1", "Artist 2", "Artist 3"]}
17
+
18
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 3, 0))
19
+
20
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
21
+ assert raw_metadata["TPE1"] == ["Artist 1//Artist 2//Artist 3"]
22
+
23
+ def test_with_existing_artists_field(self):
24
+ # Start with an existing artist field
25
+ initial_metadata = {"artist": "Existing Artist"}
26
+ with temp_file_with_metadata(initial_metadata, "id3v2.3") as test_file:
27
+ ID3v2MetadataSetter.set_artists(test_file, "Existing 1; Existing 2", version="2.3")
28
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
29
+ assert raw_metadata["TPE1"] == ["Existing 1; Existing 2"]
30
+
31
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Existing 1", "New 2"]}
32
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 3, 0))
33
+
34
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.3")
35
+
36
+ assert raw_metadata["TPE1"] == ["Existing 1//New 2"]
@@ -0,0 +1,34 @@
1
+ import pytest
2
+
3
+ from audiometa import update_metadata
4
+ from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
5
+ from audiometa.test.helpers.id3v2.id3v2_metadata_setter import ID3v2MetadataSetter
6
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
7
+ from audiometa.utils.metadata_format import MetadataFormat
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestMultipleValuesId3v24:
13
+ def test_write_multiple_artists(self):
14
+ with temp_file_with_metadata({"title": "Test Song"}, "id3v2.4") as test_file:
15
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", "Artist Two"]}
16
+
17
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 4, 0))
18
+
19
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
20
+
21
+ assert raw_metadata["TPE1"] == ["Artist One\x00Artist Two"]
22
+
23
+ def test_write_on_existing_artists_field(self):
24
+ with temp_file_with_metadata({}, "id3v2.4") as test_file:
25
+ ID3v2MetadataSetter.set_artists(test_file, ["Existing A\x00Existing B"], version="2.4")
26
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
27
+
28
+ assert raw_metadata["TPE1"] == ["Existing A\x00Existing B"]
29
+
30
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Existing A", "New B"]}
31
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 4, 0))
32
+
33
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
34
+ assert raw_metadata["TPE1"] == ["Existing A\x00New B"]
@@ -0,0 +1,32 @@
1
+ import pytest
2
+
3
+ from audiometa import update_metadata
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 TestMultipleValuesRiff:
13
+ def test_artists_concatenation(self):
14
+ initial_metadata = {"title": "Test Song"}
15
+ with temp_file_with_metadata(initial_metadata, "wav") as test_file:
16
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Artist 1", "Artist 2", "Artist 3"]}
17
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.RIFF)
18
+
19
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
20
+ assert "TAG:artist=Artist 1//Artist 2//Artist 3" in raw_metadata
21
+
22
+ def test_with_existing_artists_field(self):
23
+ with temp_file_with_metadata({}, "wav") as test_file:
24
+ RIFFMetadataSetter.set_artists(test_file, ["Existing 1;Existing 2"])
25
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
26
+ assert "TAG:artist=Existing 1;Existing 2" in raw_metadata
27
+
28
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Existing 1", "New 2"]}
29
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.RIFF)
30
+
31
+ raw_metadata = RIFFMetadataGetter.get_raw_metadata(test_file)
32
+ assert "TAG:artist=Existing 1//New 2" in raw_metadata
@@ -0,0 +1,54 @@
1
+ import pytest
2
+
3
+ from audiometa import update_metadata
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 TestMultipleValuesVorbis:
13
+ def test_write_multiple_artists(self):
14
+ with temp_file_with_metadata({}, "flac") as test_file:
15
+ # Write multiple artists using update_metadata
16
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", "Artist Two", "Artist Three"]}
17
+
18
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
19
+ VorbisMetadataSetter.add_title(test_file, "Test Song")
20
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
21
+ assert "ARTIST=Artist One" in raw_metadata
22
+ assert "ARTIST=Artist Two" in raw_metadata
23
+ assert "ARTIST=Artist Three" in raw_metadata
24
+
25
+ def test_with_existing_artists_fields(self):
26
+ # Start with an existing artist field
27
+ with temp_file_with_metadata({}, "flac") as test_file:
28
+ # create an existing value using setter
29
+ VorbisMetadataSetter.set_artists(test_file, ["Existing 1; Existing 2"])
30
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
31
+ assert "Existing 1; Existing 2" in raw_metadata
32
+
33
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Existing 1", "New 2"]}
34
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
35
+
36
+ raw_metadata = VorbisMetadataGetter.get_raw_metadata(test_file)
37
+ assert "Existing 1" in raw_metadata
38
+ assert "New 2" in raw_metadata
39
+ assert "Existing 2" not in raw_metadata
40
+
41
+ def test_with_existing_artists_fields_with_lower_case_key(self):
42
+ # Start with an existing artist field in lower case
43
+ with temp_file_with_metadata({}, "flac") as test_file:
44
+ # create an existing value using setter
45
+ VorbisMetadataSetter.set_artists(test_file, ["ExistingLower 1; ExistingLower 2"], key_lower_case=True)
46
+ raw_output = VorbisMetadataGetter.get_raw_metadata(test_file)
47
+ assert "artist=ExistingLower 1; ExistingLower 2" in raw_output
48
+
49
+ metadata = {UnifiedMetadataKey.ARTISTS: ["ExistingLower 1", "New 2"]}
50
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
51
+
52
+ raw_output = VorbisMetadataGetter.get_raw_metadata(test_file)
53
+ assert "ARTIST=ExistingLower 1" in raw_output
54
+ assert "ARTIST=New 2" in raw_output
@@ -0,0 +1,42 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata_field, update_metadata
4
+ from audiometa.exceptions import InvalidMetadataFieldTypeError
5
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
6
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
7
+
8
+
9
+ @pytest.mark.integration
10
+ class TestMultipleValuesErrorHandling:
11
+ def test_write_invalid_data_types_in_list(self):
12
+ # Test with invalid data types in multiple value lists
13
+ with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
14
+ metadata = {UnifiedMetadataKey.ARTISTS: [1, 2, 3]} # Numbers instead of strings
15
+ with pytest.raises(InvalidMetadataFieldTypeError):
16
+ update_metadata(temp_audio_file_path, metadata)
17
+
18
+ def test_write_mixed_data_types_in_list(self):
19
+ # Test with mixed data types in multiple value lists
20
+ with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
21
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", 123, None, "Artist Two"]}
22
+ with pytest.raises(InvalidMetadataFieldTypeError):
23
+ update_metadata(temp_audio_file_path, metadata)
24
+
25
+ def test_write_list_with_none_values_are_filtered(self):
26
+ # Test that None values in lists are automatically filtered out
27
+ # If all values are None, the field should be removed entirely
28
+ with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
29
+ metadata = {UnifiedMetadataKey.ARTISTS: [None, None]}
30
+ update_metadata(temp_audio_file_path, metadata)
31
+ # Field should be removed (None) since all values were filtered out
32
+ artists = get_unified_metadata_field(temp_audio_file_path, UnifiedMetadataKey.ARTISTS)
33
+ assert artists is None
34
+
35
+ def test_write_list_with_mixed_none_and_valid_values(self):
36
+ # Test that None values are filtered but valid values remain
37
+ with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
38
+ metadata = {UnifiedMetadataKey.ARTISTS: ["Artist One", None, "Artist Two", None, "Artist Three"]}
39
+ update_metadata(temp_audio_file_path, metadata)
40
+ # None values should be filtered out, only valid artists remain
41
+ artists = get_unified_metadata_field(temp_audio_file_path, UnifiedMetadataKey.ARTISTS)
42
+ assert artists == ["Artist One", "Artist Two", "Artist Three"]
@@ -0,0 +1,98 @@
1
+ import time
2
+
3
+ import pytest
4
+
5
+ from audiometa import get_unified_metadata_field, update_metadata
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 TestMultipleValuesBoundaryConditions:
13
+ def test_write_large_number_of_multiple_values_per_field(self):
14
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
15
+ # Test with very large number of values per field
16
+ large_values_number = 1000
17
+ large_artist_list = [f"Artist {i:04d}" for i in range(large_values_number)]
18
+
19
+ metadata = {
20
+ UnifiedMetadataKey.ARTISTS: large_artist_list,
21
+ }
22
+
23
+ start_time = time.time()
24
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
25
+ write_time = time.time() - start_time
26
+
27
+ artists = get_unified_metadata_field(
28
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
29
+ )
30
+
31
+ assert isinstance(artists, list)
32
+ assert len(artists) == large_values_number
33
+
34
+ # Performance should be reasonable
35
+ assert write_time < 10.0, f"Write took too long: {write_time:.2f}s"
36
+
37
+ def test_write_extremely_long_individual_values(self):
38
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
39
+ # Test with extremely long individual values
40
+ very_long_string = "A" * 50000 # 50KB string
41
+ metadata = {
42
+ UnifiedMetadataKey.ARTISTS: [very_long_string, "Normal Artist"],
43
+ UnifiedMetadataKey.COMMENT: very_long_string,
44
+ }
45
+
46
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
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) == 2
53
+ assert very_long_string in artists
54
+ assert "Normal Artist" in artists
55
+
56
+ def test_write_mixed_length_values(self):
57
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
58
+ # Test with mixed length values
59
+ mixed_lengths = [
60
+ "A", # 1 character
61
+ "AB", # 2 characters
62
+ "ABC", # 3 characters
63
+ "A" * 100, # 100 characters
64
+ "A" * 1000, # 1000 characters
65
+ "A" * 10000, # 10000 characters
66
+ ]
67
+ metadata = {UnifiedMetadataKey.ARTISTS: mixed_lengths}
68
+ update_metadata(test_file, metadata, metadata_format=MetadataFormat.VORBIS)
69
+
70
+ artists = get_unified_metadata_field(test_file, UnifiedMetadataKey.ARTISTS)
71
+
72
+ assert isinstance(artists, list)
73
+ assert len(artists) == 6
74
+ for value in mixed_lengths:
75
+ assert value in artists
76
+
77
+ def test_write_very_large_metadata_dict(self):
78
+ with temp_file_with_metadata({"title": "Test Song"}, "flac") as test_file:
79
+ # Test with very large metadata dictionary
80
+ large_metadata = {}
81
+
82
+ # Add multiple values for each supported multiple-value field
83
+ # Actual implementation of Vorbis metadata cannot support more than 50 values per field
84
+ for i in range(50):
85
+ large_metadata[UnifiedMetadataKey.ARTISTS] = [f"Artist {i:04d}" for i in range(50)]
86
+ start_time = time.time()
87
+ update_metadata(test_file, large_metadata, metadata_format=MetadataFormat.VORBIS)
88
+ write_time = time.time() - start_time
89
+
90
+ artists = get_unified_metadata_field(
91
+ test_file, UnifiedMetadataKey.ARTISTS, metadata_format=MetadataFormat.VORBIS
92
+ )
93
+
94
+ assert isinstance(artists, list)
95
+ assert len(artists) == 50
96
+
97
+ # Performance should be reasonable
98
+ assert write_time < 15.0, f"Write took too long: {write_time:.2f}s"
@@ -0,0 +1 @@
1
+ """Integration tests for reading metadata."""
@@ -0,0 +1,80 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata
4
+ from audiometa.test.helpers.id3v1 import ID3v1MetadataSetter
5
+ from audiometa.test.helpers.id3v2 import ID3v2MetadataSetter
6
+ from audiometa.test.helpers.id3v2.id3v2_metadata_getter import ID3v2MetadataGetter
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.integration
12
+ class TestMultipleMetadata:
13
+ def test_id3v1(self):
14
+ with temp_file_with_metadata({}, "mp3") as test_file:
15
+ # First set ID3v1 metadata using external tools
16
+ ID3v1MetadataSetter.set_title(test_file, "ID3v1 Title")
17
+ ID3v1MetadataSetter.set_artist(test_file, "ID3v1 Artist")
18
+ ID3v1MetadataSetter.set_album(test_file, "ID3v1 Album")
19
+
20
+ merged_metadata = get_unified_metadata(test_file)
21
+ assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "ID3v1 Title"
22
+ assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["ID3v1 Artist"]
23
+ assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "ID3v1 Album"
24
+
25
+ def test_id3v2_3(self):
26
+ with temp_file_with_metadata({}, "id3v2.3") as test_file:
27
+ # First set ID3v2.3 metadata using external tools
28
+ ID3v2MetadataSetter.set_title(test_file, "ID3v2.3 Title", version="2.3")
29
+ ID3v2MetadataSetter.set_artists(test_file, "ID3v2.3 Artist", version="2.3")
30
+ ID3v2MetadataSetter.set_album(test_file, "ID3v2.3 Album", version="2.3")
31
+
32
+ merged_metadata = get_unified_metadata(test_file)
33
+ assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "ID3v2.3 Title"
34
+ assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["ID3v2.3 Artist"]
35
+ assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "ID3v2.3 Album"
36
+
37
+ def test_id3v2_4(self):
38
+ with temp_file_with_metadata({}, "id3v2.4") as test_file:
39
+ # First set ID3v2.4 metadata using external tools
40
+ ID3v2MetadataSetter.set_title(test_file, "ID3v2.4 Title", version="2.4")
41
+ ID3v2MetadataSetter.set_artists(test_file, "ID3v2.4 Artist", version="2.4")
42
+ ID3v2MetadataSetter.set_album(test_file, "ID3v2.4 Album", version="2.4")
43
+
44
+ raw_metadata = ID3v2MetadataGetter.get_raw_metadata(test_file, version="2.4")
45
+ assert raw_metadata["TIT2"] == ["ID3v2.4 Title"]
46
+ assert raw_metadata["TPE1"] == ["ID3v2.4 Artist"]
47
+ assert raw_metadata["TALB"] == ["ID3v2.4 Album"]
48
+
49
+ merged_metadata = get_unified_metadata(test_file)
50
+ assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "ID3v2.4 Title"
51
+ assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["ID3v2.4 Artist"]
52
+ assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "ID3v2.4 Album"
53
+
54
+ def test_riff(self):
55
+ with temp_file_with_metadata({}, "wav") as test_file:
56
+ # First set RIFF metadata using external tools
57
+ from audiometa.test.helpers.riff import RIFFMetadataSetter
58
+
59
+ RIFFMetadataSetter.set_title(test_file, "RIFF Title")
60
+ RIFFMetadataSetter.set_artist(test_file, "RIFF Artist")
61
+ RIFFMetadataSetter.set_album(test_file, "RIFF Album")
62
+
63
+ merged_metadata = get_unified_metadata(test_file)
64
+ assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "RIFF Title"
65
+ assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["RIFF Artist"]
66
+ assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "RIFF Album"
67
+
68
+ def test_vorbis(self):
69
+ with temp_file_with_metadata({}, "flac") as test_file:
70
+ # First set Vorbis metadata using external tools
71
+ from audiometa.test.helpers.vorbis import VorbisMetadataSetter
72
+
73
+ VorbisMetadataSetter.add_title(test_file, "Vorbis Title")
74
+ VorbisMetadataSetter.set_artist(test_file, "Vorbis Artist")
75
+ VorbisMetadataSetter.set_album(test_file, "Vorbis Album")
76
+
77
+ merged_metadata = get_unified_metadata(test_file)
78
+ assert merged_metadata.get(UnifiedMetadataKey.TITLE) == "Vorbis Title"
79
+ assert merged_metadata.get(UnifiedMetadataKey.ARTISTS) == ["Vorbis Artist"]
80
+ assert merged_metadata.get(UnifiedMetadataKey.ALBUM) == "Vorbis Album"
@@ -0,0 +1,36 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from audiometa import get_unified_metadata, get_unified_metadata_field
6
+ from audiometa.exceptions import FileTypeNotSupportedError
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.integration
12
+ class TestReadingErrorHandling:
13
+ def test_unsupported_file_type_raises_error(self):
14
+ # Create a file with unsupported extension
15
+ with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
16
+ temp_audio_file_path.write_bytes(b"fake audio content")
17
+ txt_file_path = temp_audio_file_path.with_suffix(".txt")
18
+ txt_file_path.write_bytes(b"fake audio content")
19
+
20
+ with pytest.raises(FileTypeNotSupportedError):
21
+ get_unified_metadata(str(txt_file_path))
22
+
23
+ def test_nonexistent_file_raises_error(self):
24
+ nonexistent_file = "nonexistent_file.mp3"
25
+
26
+ with pytest.raises(FileNotFoundError):
27
+ get_unified_metadata(nonexistent_file)
28
+
29
+ with pytest.raises(FileNotFoundError):
30
+ get_unified_metadata_field(nonexistent_file, UnifiedMetadataKey.TITLE)
31
+
32
+ def test_metadata_key_not_found_returns_none(self, sample_mp3_file: Path):
33
+ # This should not raise an error, but return None when the field is not found
34
+ # Using a valid UnifiedMetadataKey that might not be present in the file
35
+ result = get_unified_metadata_field(sample_mp3_file, UnifiedMetadataKey.UNSYNCHRONIZED_LYRICS)
36
+ assert result is None
@@ -0,0 +1,146 @@
1
+ """End-to-end tests using real audio files from data/audio_files."""
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from audiometa import get_unified_metadata
8
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestRealAudioFilesReading:
13
+ """Test cases using real audio files for end-to-end validation."""
14
+
15
+ def test_recording_allumerlefeu_metadata(self, assets_dir: Path):
16
+ file_path = assets_dir / "recording=Allumerlefeu_2 matches one with more release groups.mp3"
17
+
18
+ metadata = get_unified_metadata(file_path)
19
+ assert isinstance(metadata, dict)
20
+ assert metadata[UnifiedMetadataKey.TITLE] == "Allumer le feu"
21
+ assert metadata[UnifiedMetadataKey.ARTISTS] == ["Johnny Hallyday"]
22
+ assert metadata[UnifiedMetadataKey.ALBUM] == "Les Années 80-90-2000, C'Etait Mieux Avant"
23
+ assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Variétés Internationales"]
24
+ assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2018-09-28"
25
+ assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "39"
26
+ assert metadata[UnifiedMetadataKey.BPM] == 139
27
+ assert metadata[UnifiedMetadataKey.COMPOSERS] == ["Pascal Obispo", "Pierre Jaconelli"]
28
+ assert metadata[UnifiedMetadataKey.PUBLISHER] == ""
29
+ assert metadata[UnifiedMetadataKey.COPYRIGHT] == "© 2018 Mercury Music Group"
30
+
31
+ def test_recording_celinekin_park_metadata(self, assets_dir: Path):
32
+ file_path = assets_dir / "recording=Celinekin Park - no musicbrainz recording duration.mp3"
33
+
34
+ metadata = get_unified_metadata(file_path)
35
+ assert isinstance(metadata, dict)
36
+ assert metadata[UnifiedMetadataKey.TITLE] == "Celinekin Park (Linkin Park vs. Celine Dion)"
37
+ assert metadata[UnifiedMetadataKey.ARTISTS] == ["The Table"]
38
+ assert metadata[UnifiedMetadataKey.ALBUM] == "Bootie Top 10 – November/December 2018" # noqa: RUF001
39
+ assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["A Plus D"]
40
+ assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2018-11-30"
41
+ assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "9/10"
42
+ assert metadata[UnifiedMetadataKey.BPM] == 99
43
+ assert metadata[UnifiedMetadataKey.PUBLISHER] == "[no label]"
44
+ assert metadata[UnifiedMetadataKey.COMMENT] == "BootieMashup.com"
45
+
46
+ def test_recording_dans_la_legende_metadata(self, assets_dir: Path):
47
+ file_path = assets_dir / "recording=Dans la legende.flac"
48
+
49
+ metadata = get_unified_metadata(file_path)
50
+ assert isinstance(metadata, dict)
51
+ assert metadata[UnifiedMetadataKey.TITLE] == "DA"
52
+ assert metadata[UnifiedMetadataKey.ARTISTS] == ["PNL"]
53
+ assert metadata[UnifiedMetadataKey.ALBUM] == "Dans La Légende"
54
+ assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["PNL"]
55
+ assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["French cloud rap"]
56
+ assert metadata[UnifiedMetadataKey.RATING] == 10
57
+ assert metadata[UnifiedMetadataKey.LANGUAGE] == "French"
58
+ assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2016"
59
+ assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "01"
60
+
61
+ def test_recording_kemar_france_metadata(self, assets_dir: Path):
62
+ file_path = assets_dir / "recording=Kemar - France.mp3"
63
+
64
+ metadata = get_unified_metadata(file_path)
65
+ assert isinstance(metadata, dict)
66
+ assert metadata[UnifiedMetadataKey.TITLE] == "Kemar - France"
67
+ assert metadata[UnifiedMetadataKey.BPM] == 140
68
+
69
+ def test_recording_tokyo_drift_metadata(self, assets_dir: Path):
70
+ file_path = assets_dir / "recording=Tokyo Drift_no mb recording.mp3"
71
+
72
+ metadata = get_unified_metadata(file_path)
73
+ assert isinstance(metadata, dict)
74
+ assert metadata[UnifiedMetadataKey.TITLE] == "Tokyo Drift x Temperature x You Little Beauty (BENNE BOOM Mashup)"
75
+ assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2021"
76
+ assert metadata[UnifiedMetadataKey.BPM] == 128
77
+
78
+ def test_recording_y_do_i_carmina_burana_mp3_metadata(self, assets_dir: Path):
79
+ file_path = assets_dir / "recording=Y do i - Carmina Burana Remix - 7m52.mp3"
80
+
81
+ metadata = get_unified_metadata(file_path)
82
+ assert isinstance(metadata, dict)
83
+ assert len(metadata) == 0 # No metadata
84
+
85
+ def test_recording_y_do_i_carmina_burana_wav_metadata(self, assets_dir: Path):
86
+ file_path = assets_dir / "recording=Y do i - Carmina Burana Remix - 7m52.wav"
87
+
88
+ metadata = get_unified_metadata(file_path)
89
+ assert isinstance(metadata, dict)
90
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Y do i - Carmina Burana Remix (Techno of the Opera)"
91
+ # Additional metadata fields that should be present if added:
92
+ if UnifiedMetadataKey.ARTISTS in metadata:
93
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Y do I"]
94
+ if UnifiedMetadataKey.ALBUM_ARTISTS in metadata:
95
+ assert metadata.get(UnifiedMetadataKey.ALBUM_ARTISTS) == ["Y do I"]
96
+ if UnifiedMetadataKey.COMPOSERS in metadata:
97
+ assert metadata.get(UnifiedMetadataKey.COMPOSERS) == ["Carl Orff"]
98
+ if UnifiedMetadataKey.ALBUM in metadata:
99
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Remixes"
100
+
101
+ def test_recording_california_gurls_metadata(self, assets_dir: Path):
102
+ file_path = assets_dir / "recording=california gurls_id3v2 tags.flac"
103
+
104
+ metadata = get_unified_metadata(file_path)
105
+ assert isinstance(metadata, dict)
106
+ assert metadata[UnifiedMetadataKey.TITLE] == "California Gurls"
107
+ assert metadata[UnifiedMetadataKey.ARTISTS] == ["Katy Perry feat. Snoop Dogg"]
108
+ assert metadata[UnifiedMetadataKey.ALBUM] == "Now That's What I Call Music! 76"
109
+ assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["Various"]
110
+ assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Pop Rock", "Euro House"]
111
+ assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2010"
112
+ assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "1"
113
+ assert metadata[UnifiedMetadataKey.BPM] == 125
114
+ assert (
115
+ metadata[UnifiedMetadataKey.COPYRIGHT]
116
+ == "© EMI Records Ltd. © Virgin Records Ltd. © Universal Music Operations Ltd."
117
+ )
118
+
119
+ def test_recording_juan_hansen_drown_flac_metadata(self, assets_dir: Path):
120
+ file_path = assets_dir / "recording=juan hansen oostil - drown (massano remix) - 7m20.flac"
121
+
122
+ metadata = get_unified_metadata(file_path)
123
+ assert isinstance(metadata, dict)
124
+ assert metadata[UnifiedMetadataKey.TITLE] == "Drown (Massano Remix)"
125
+ assert metadata[UnifiedMetadataKey.ARTISTS] == ["Øostil & Juan Hansen"]
126
+ assert metadata[UnifiedMetadataKey.ALBUM] == "In My System EP"
127
+ assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["Massano"]
128
+ assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Techno"]
129
+ assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2022"
130
+ assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "2"
131
+ assert metadata[UnifiedMetadataKey.BPM] == 122
132
+
133
+ def test_recording_juan_hansen_drown_mp3_metadata(self, assets_dir: Path):
134
+ file_path = assets_dir / "recording=juan hansen oostil - drown (massano remix) - 7m21.mp3"
135
+
136
+ metadata = get_unified_metadata(file_path)
137
+ assert isinstance(metadata, dict)
138
+ assert metadata[UnifiedMetadataKey.TITLE] == "Drown (Massano Remix)"
139
+ assert metadata[UnifiedMetadataKey.ARTISTS] == ["Øostil"]
140
+ assert metadata[UnifiedMetadataKey.ALBUM] == "In My System EP"
141
+ assert metadata[UnifiedMetadataKey.ALBUM_ARTISTS] == ["Massano"]
142
+ assert metadata[UnifiedMetadataKey.GENRES_NAMES] == ["Electro"]
143
+ assert metadata[UnifiedMetadataKey.RELEASE_DATE] == "2022-04-15"
144
+ assert metadata[UnifiedMetadataKey.TRACK_NUMBER] == "2"
145
+ assert metadata[UnifiedMetadataKey.BPM] == 122
146
+ assert metadata[UnifiedMetadataKey.PUBLISHER] == "Afterlife"