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,47 @@
1
+ import pytest
2
+
3
+ from audiometa import delete_all_metadata
4
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
5
+
6
+
7
+ @pytest.mark.integration
8
+ class TestDeleteAllMetadataBasic:
9
+ def test_delete_all_metadata_file_with_no_metadata(self):
10
+ with temp_file_with_metadata({}, "mp3") as test_file:
11
+ # Delete metadata from file that has no metadata
12
+ result = delete_all_metadata(test_file)
13
+ assert result is True
14
+
15
+ def test_delete_all_metadata_id3v2_version_specific(self):
16
+ # First add some metadata using external script
17
+ test_metadata = {"title": "Test Title", "artist": "Test Artist"}
18
+ with temp_file_with_metadata(test_metadata, "mp3") as test_file:
19
+ # Delete metadata with specific ID3v2 version
20
+ result = delete_all_metadata(test_file, id3v2_version=(2, 3, 0))
21
+ assert result is True
22
+
23
+ def test_delete_all_metadata_return_value_success(self):
24
+ # First add some metadata using external script
25
+ test_metadata = {"title": "Test Title", "artist": "Test Artist"}
26
+ with temp_file_with_metadata(test_metadata, "mp3") as test_file:
27
+ result = delete_all_metadata(test_file)
28
+ assert result is True
29
+ assert isinstance(result, bool)
30
+
31
+ def test_delete_all_metadata_preserves_audio_data(self):
32
+ # First add some metadata using external script
33
+ test_metadata = {"title": "Test Title", "artist": "Test Artist"}
34
+ with temp_file_with_metadata(test_metadata, "mp3") as test_file:
35
+ # Get file size with metadata
36
+ with_metadata_size = test_file.stat().st_size
37
+
38
+ # Delete all metadata
39
+ result = delete_all_metadata(test_file)
40
+ assert result is True
41
+
42
+ # Verify file size decreased (metadata headers removed)
43
+ after_deletion_size = test_file.stat().st_size
44
+ assert after_deletion_size < with_metadata_size
45
+
46
+ # Verify the file is still valid (not corrupted)
47
+ assert after_deletion_size > 0
@@ -0,0 +1,24 @@
1
+ import pytest
2
+
3
+ from audiometa import delete_all_metadata
4
+ from audiometa.exceptions import FileTypeNotSupportedError
5
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
6
+
7
+
8
+ @pytest.mark.integration
9
+ class TestDeleteAllMetadataErrorHandling:
10
+ def test_delete_all_metadata_unsupported_file_type(self):
11
+ # Create a file with unsupported extension
12
+ with temp_file_with_metadata({}, "mp3") as temp_audio_file_path:
13
+ temp_audio_file_path.write_bytes(b"fake audio content")
14
+ txt_file_path = temp_audio_file_path.with_suffix(".txt")
15
+ txt_file_path.write_bytes(b"fake audio content")
16
+
17
+ with pytest.raises(FileTypeNotSupportedError):
18
+ delete_all_metadata(str(txt_file_path))
19
+
20
+ def test_delete_all_metadata_nonexistent_file(self):
21
+ nonexistent_file = "nonexistent_file.mp3"
22
+
23
+ with pytest.raises(FileNotFoundError):
24
+ delete_all_metadata(nonexistent_file)
@@ -0,0 +1 @@
1
+ """Integration tests for reading metadata."""
@@ -0,0 +1,88 @@
1
+ import pytest
2
+
3
+ from audiometa import get_unified_metadata, update_metadata
4
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
5
+ from audiometa.utils.metadata_format import MetadataFormat
6
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
7
+
8
+
9
+ @pytest.mark.integration
10
+ class TestEncoding:
11
+ def test_id3v2_3_encoding_support(self):
12
+ with temp_file_with_metadata({}, "id3v2.3") as test_file:
13
+ metadata_dict = {
14
+ UnifiedMetadataKey.TITLE: "Test Title with ASCII",
15
+ UnifiedMetadataKey.ARTISTS: ["Artist Name"],
16
+ UnifiedMetadataKey.ALBUM: "Album Name",
17
+ }
18
+ update_metadata(test_file, metadata_dict, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 3, 0))
19
+
20
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V2)
21
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Test Title with ASCII"
22
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist Name"]
23
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Album Name"
24
+
25
+ def test_id3v2_4_utf8_encoding_support(self):
26
+ with temp_file_with_metadata({}, "id3v2.4") as test_file:
27
+ # Test with Unicode characters that require UTF-8
28
+ unicode_metadata = {
29
+ UnifiedMetadataKey.TITLE: "Test 中文 العربية русский 🎵",
30
+ UnifiedMetadataKey.ARTISTS: ["Artist 日本語 한국어"],
31
+ UnifiedMetadataKey.ALBUM: "Album Ελληνικά ภาษาไทย",
32
+ }
33
+
34
+ update_metadata(test_file, unicode_metadata, metadata_format=MetadataFormat.ID3V2, id3v2_version=(2, 4, 0))
35
+
36
+ # Verify the Unicode characters are preserved
37
+ metadata = get_unified_metadata(test_file)
38
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Test 中文 العربية русский 🎵"
39
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist 日本語 한국어"]
40
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Album Ελληνικά ภาษาไทย"
41
+
42
+ def test_id3v1_encoding_support(self):
43
+ with temp_file_with_metadata({}, "id3v1") as test_file:
44
+ metadata_dict = {
45
+ UnifiedMetadataKey.TITLE: "Test Title with ASCII",
46
+ UnifiedMetadataKey.ARTISTS: ["Artist Name"],
47
+ UnifiedMetadataKey.ALBUM: "Album Name",
48
+ }
49
+ update_metadata(test_file, metadata_dict, metadata_format=MetadataFormat.ID3V1)
50
+
51
+ metadata = get_unified_metadata(test_file, metadata_format=MetadataFormat.ID3V1)
52
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Test Title with ASCII"
53
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist Name"]
54
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Album Name"
55
+
56
+ def test_riff_utf8_encoding_support(self):
57
+ with temp_file_with_metadata({}, "wav") as test_file:
58
+ # Test with Unicode characters that require UTF-8
59
+ unicode_metadata = {
60
+ UnifiedMetadataKey.TITLE: "Test 中文 العربية русский 🎵",
61
+ UnifiedMetadataKey.ARTISTS: ["Artist 日本語 한국어"],
62
+ UnifiedMetadataKey.ALBUM: "Album Ελληνικά ภาษาไทย",
63
+ }
64
+
65
+ update_metadata(test_file, unicode_metadata, metadata_format=MetadataFormat.RIFF)
66
+
67
+ # Verify the Unicode characters are preserved
68
+ metadata = get_unified_metadata(test_file)
69
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Test 中文 العربية русский 🎵"
70
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist 日本語 한국어"]
71
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Album Ελληνικά ภาษาไทย"
72
+
73
+ def test_vorbis_utf8_encoding_support(self):
74
+ with temp_file_with_metadata({}, "flac") as test_file:
75
+ # Test with Unicode characters that require UTF-8
76
+ unicode_metadata = {
77
+ UnifiedMetadataKey.TITLE: "Test 中文 العربية русский 🎵",
78
+ UnifiedMetadataKey.ARTISTS: ["Artist 日本語 한국어"],
79
+ UnifiedMetadataKey.ALBUM: "Album Ελληνικά ภาษาไทย",
80
+ }
81
+
82
+ update_metadata(test_file, unicode_metadata, metadata_format=MetadataFormat.VORBIS)
83
+
84
+ # Verify the Unicode characters are preserved
85
+ metadata = get_unified_metadata(test_file)
86
+ assert metadata.get(UnifiedMetadataKey.TITLE) == "Test 中文 العربية русский 🎵"
87
+ assert metadata.get(UnifiedMetadataKey.ARTISTS) == ["Artist 日本語 한국어"]
88
+ assert metadata.get(UnifiedMetadataKey.ALBUM) == "Album Ελληνικά ภาษาไทย"
@@ -0,0 +1,223 @@
1
+ from pathlib import Path
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.test.helpers.vorbis.vorbis_metadata_getter import VorbisMetadataGetter
9
+ from audiometa.utils.unified_metadata_key import UnifiedMetadataKey
10
+
11
+
12
+ @pytest.mark.integration
13
+ class TestSpecialCharactersEdgeCases:
14
+ def test_read_unicode_characters(self):
15
+ with temp_file_with_metadata({}, "flac") as test_file:
16
+ VorbisMetadataSetter.set_artists(test_file, ["François", "José", "Müller", "北京"])
17
+ VorbisMetadataSetter.add_title(test_file, "Café Music 音乐")
18
+ metadata_title = VorbisMetadataGetter.get_title(test_file)
19
+ assert metadata_title == "Café Music 音乐"
20
+
21
+ unified_metadata = get_unified_metadata(test_file)
22
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
23
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
24
+
25
+ assert isinstance(artists, list)
26
+ assert len(artists) == 4
27
+ assert "François" in artists
28
+ assert "José" in artists
29
+ assert "Müller" in artists
30
+ assert "北京" in artists
31
+
32
+ assert isinstance(title, str)
33
+ assert title == "Café Music 音乐"
34
+
35
+ def test_read_special_punctuation(self):
36
+ with temp_file_with_metadata({}, "flac") as test_file:
37
+ VorbisMetadataSetter.set_artists(test_file, ["Artist & Co.", "Band (feat. Singer)", "Group - The Band"])
38
+ VorbisMetadataSetter.add_title(test_file, "Song (Remix) - Special Edition")
39
+
40
+ unified_metadata = get_unified_metadata(test_file)
41
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
42
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
43
+
44
+ assert isinstance(artists, list)
45
+ assert len(artists) == 3
46
+ assert "Artist & Co." in artists
47
+ assert "Band (feat. Singer)" in artists
48
+ assert "Group - The Band" in artists
49
+
50
+ assert isinstance(title, str)
51
+ assert title == "Song (Remix) - Special Edition"
52
+
53
+ def test_read_quotes_and_apostrophes(self):
54
+ with temp_file_with_metadata({}, "flac") as test_file:
55
+ VorbisMetadataSetter.set_artists(test_file, ["Artist's Band", 'The "Quoted" Band', "It's a Band"])
56
+ VorbisMetadataSetter.add_title(test_file, 'Don\'t Stop "Believing"')
57
+
58
+ unified_metadata = get_unified_metadata(test_file)
59
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
60
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
61
+
62
+ assert isinstance(artists, list)
63
+ assert len(artists) == 3
64
+ assert "Artist's Band" in artists
65
+ assert 'The "Quoted" Band' in artists
66
+ assert "It's a Band" in artists
67
+
68
+ assert isinstance(title, str)
69
+ assert title == 'Don\'t Stop "Believing"'
70
+
71
+ def test_read_control_characters(self):
72
+ with temp_file_with_metadata({}, "flac") as test_file:
73
+ VorbisMetadataSetter.set_artists(
74
+ test_file, ["Artist\twith\ttabs", "Band\nwith\nnewlines", "Group\rwith\rcarriage"]
75
+ )
76
+ VorbisMetadataSetter.add_title(test_file, "Song\twith\ttabs")
77
+
78
+ unified_metadata = get_unified_metadata(test_file)
79
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
80
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
81
+
82
+ assert isinstance(artists, list)
83
+ assert len(artists) == 3
84
+ assert "Artist\twith\ttabs" in artists
85
+ assert "Band\nwith\nnewlines" in artists
86
+ assert "Group\rwith\rcarriage" in artists
87
+
88
+ assert isinstance(title, str)
89
+ assert title == "Song\twith\ttabs"
90
+
91
+ def test_read_very_long_strings(self):
92
+ long_string = "A" * 1000
93
+ with temp_file_with_metadata({}, "flac") as test_file:
94
+ VorbisMetadataSetter.set_artists(test_file, [long_string, "Short Artist"])
95
+ VorbisMetadataSetter.add_title(test_file, "B" * 500)
96
+
97
+ unified_metadata = get_unified_metadata(test_file)
98
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
99
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
100
+
101
+ assert isinstance(artists, list)
102
+ assert len(artists) == 2
103
+ assert long_string in artists
104
+ assert "Short Artist" in artists
105
+
106
+ assert isinstance(title, str)
107
+ assert title == "B" * 500
108
+
109
+ def test_read_mixed_encodings(self):
110
+ with temp_file_with_metadata({}, "flac") as test_file:
111
+ VorbisMetadataSetter.set_artists(test_file, ["ASCII Artist", "Français", "Русский", "العربية", "中文"])
112
+ VorbisMetadataSetter.add_title(test_file, "Mixed 编码 Title")
113
+
114
+ unified_metadata = get_unified_metadata(test_file)
115
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
116
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
117
+
118
+ assert isinstance(artists, list)
119
+ assert len(artists) == 5
120
+ assert "ASCII Artist" in artists
121
+ assert "Français" in artists
122
+ assert "Русский" in artists
123
+ assert "العربية" in artists
124
+ assert "中文" in artists
125
+
126
+ assert isinstance(title, str)
127
+ assert title == "Mixed 编码 Title"
128
+
129
+ def test_read_special_separator_characters(self):
130
+ with temp_file_with_metadata({}, "flac") as test_file:
131
+ VorbisMetadataSetter.set_artists(
132
+ test_file, ["Artist; with; semicolons", "Band, with, commas", "Group|with|pipes"]
133
+ )
134
+ VorbisMetadataSetter.add_title(test_file, "Song; with; separators")
135
+
136
+ unified_metadata = get_unified_metadata(test_file)
137
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
138
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
139
+
140
+ assert isinstance(artists, list)
141
+ assert len(artists) == 3
142
+ assert "Artist; with; semicolons" in artists
143
+ assert "Band, with, commas" in artists
144
+ assert "Group|with|pipes" in artists
145
+
146
+ assert isinstance(title, str)
147
+ assert title == "Song; with; separators"
148
+
149
+ def test_read_html_xml_characters(self):
150
+ with temp_file_with_metadata({}, "flac") as test_file:
151
+ VorbisMetadataSetter.set_artists(test_file, ["Artist <tag>", "Band &amp; Co.", "Group &lt;test&gt;"])
152
+ VorbisMetadataSetter.add_title(test_file, "Song &amp; Title")
153
+
154
+ unified_metadata = get_unified_metadata(test_file)
155
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
156
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
157
+
158
+ assert isinstance(artists, list)
159
+ assert len(artists) == 3
160
+ assert "Artist <tag>" in artists
161
+ assert "Band &amp; Co." in artists
162
+ assert "Group &lt;test&gt;" in artists
163
+
164
+ assert isinstance(title, str)
165
+ assert title == "Song &amp; Title"
166
+
167
+ def test_read_emoji_characters(self):
168
+ with temp_file_with_metadata({}, "flac") as test_file:
169
+ VorbisMetadataSetter.set_artists(test_file, ["Artist 🎵", "Band 🎸", "Group 🎤"])
170
+ VorbisMetadataSetter.add_title(test_file, "Song 🎶 with 🎵 emojis")
171
+
172
+ unified_metadata = get_unified_metadata(test_file)
173
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
174
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
175
+
176
+ assert isinstance(artists, list)
177
+ assert len(artists) == 3
178
+ assert "Artist 🎵" in artists
179
+ assert "Band 🎸" in artists
180
+ assert "Group 🎤" in artists
181
+
182
+ assert isinstance(title, str)
183
+ assert title == "Song 🎶 with 🎵 emojis"
184
+
185
+ def test_read_mixed_special_characters(self):
186
+ with temp_file_with_metadata({}, "flac") as test_file:
187
+ VorbisMetadataSetter.set_artists(
188
+ test_file,
189
+ [
190
+ "François & Co. (feat. Müller) 🎵",
191
+ 'The "Quoted" Band - Special Characters',
192
+ "Artist with\nnewlines\tand\ttabs",
193
+ ],
194
+ )
195
+ VorbisMetadataSetter.add_title(test_file, 'Mixed Special 🎵 Characters & "Quotes"')
196
+
197
+ unified_metadata = get_unified_metadata(test_file)
198
+ artists = unified_metadata.get(UnifiedMetadataKey.ARTISTS)
199
+ title = unified_metadata.get(UnifiedMetadataKey.TITLE)
200
+
201
+ assert isinstance(artists, list)
202
+ assert len(artists) == 3
203
+ assert "François & Co. (feat. Müller) 🎵" in artists
204
+ assert 'The "Quoted" Band - Special Characters' in artists
205
+ assert "Artist with\nnewlines\tand\ttabs" in artists
206
+
207
+ assert isinstance(title, str)
208
+ assert title == 'Mixed Special 🎵 Characters & "Quotes"'
209
+
210
+ def test_read_special_characters_from_existing_file(self, sample_mp3_file: Path):
211
+ # Test reading special characters from a file that already has them
212
+ # This tests the reading functionality without writing first
213
+ unified_metadata = get_unified_metadata(sample_mp3_file)
214
+
215
+ # Should handle any special characters that might be in the sample file
216
+ for _key, value in unified_metadata.items():
217
+ if isinstance(value, str):
218
+ # Should be able to handle unicode and special characters
219
+ assert isinstance(value, str)
220
+ elif isinstance(value, list):
221
+ for item in value:
222
+ if isinstance(item, str):
223
+ assert isinstance(item, str)
@@ -0,0 +1,122 @@
1
+ """Tests for get_full_metadata function with different audio formats."""
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from audiometa import get_full_metadata
8
+
9
+
10
+ @pytest.mark.integration
11
+ class TestGetFullMetadataAudioFormats:
12
+ def test_get_full_metadata_mp3_with_metadata(self, sample_mp3_file: Path):
13
+ result = get_full_metadata(sample_mp3_file)
14
+
15
+ # Check structure
16
+ assert "unified_metadata" in result
17
+ assert "technical_info" in result
18
+ assert "metadata_format" in result
19
+ assert "headers" in result
20
+ assert "raw_metadata" in result
21
+ assert "format_priorities" in result
22
+
23
+ # Check format priorities
24
+ assert result["format_priorities"]["file_extension"] == ".mp3"
25
+ assert "id3v2" in result["format_priorities"]["reading_order"]
26
+ assert "id3v1" in result["format_priorities"]["reading_order"]
27
+ assert result["format_priorities"]["writing_format"] == "id3v2"
28
+
29
+ # Check technical info
30
+ tech_info = result["technical_info"]
31
+ assert "duration_seconds" in tech_info
32
+ assert "bitrate_bps" in tech_info
33
+ assert "sample_rate_hz" in tech_info
34
+ assert "channels" in tech_info
35
+ assert "file_size_bytes" in tech_info
36
+ assert "file_extension" in tech_info
37
+ assert "audio_format_name" in tech_info
38
+ assert tech_info["file_extension"] == ".mp3"
39
+ assert tech_info["audio_format_name"] == "MP3"
40
+ assert tech_info["is_flac_md5_valid"] is None # Not a FLAC file
41
+
42
+ # Check format metadata
43
+ assert "id3v2" in result["metadata_format"]
44
+ assert "id3v1" in result["metadata_format"]
45
+
46
+ # Check headers
47
+ assert "id3v2" in result["headers"]
48
+ assert "id3v1" in result["headers"]
49
+
50
+ # Check raw metadata
51
+ assert "id3v2" in result["raw_metadata"]
52
+ assert "id3v1" in result["raw_metadata"]
53
+
54
+ def test_get_full_metadata_flac_with_metadata(self, sample_flac_file: Path):
55
+ result = get_full_metadata(sample_flac_file)
56
+
57
+ # Check format priorities
58
+ assert result["format_priorities"]["file_extension"] == ".flac"
59
+ assert "vorbis" in result["format_priorities"]["reading_order"]
60
+ assert "id3v2" in result["format_priorities"]["reading_order"]
61
+ assert "id3v1" in result["format_priorities"]["reading_order"]
62
+ assert result["format_priorities"]["writing_format"] == "vorbis"
63
+
64
+ # Check technical info
65
+ tech_info = result["technical_info"]
66
+ assert tech_info["file_extension"] == ".flac"
67
+ assert tech_info["audio_format_name"] == "FLAC"
68
+ assert "is_flac_md5_valid" in tech_info # Should be present for FLAC
69
+
70
+ # Check format metadata
71
+ assert "vorbis" in result["metadata_format"]
72
+ assert "id3v2" in result["metadata_format"]
73
+ assert "id3v1" in result["metadata_format"]
74
+
75
+ # Check headers
76
+ assert "vorbis" in result["headers"]
77
+ assert "id3v2" in result["headers"]
78
+ assert "id3v1" in result["headers"]
79
+
80
+ def test_get_full_metadata_wav_with_metadata(self, sample_wav_file: Path):
81
+ result = get_full_metadata(sample_wav_file)
82
+
83
+ # Check format priorities
84
+ assert result["format_priorities"]["file_extension"] == ".wav"
85
+ assert "riff" in result["format_priorities"]["reading_order"]
86
+ assert "id3v2" in result["format_priorities"]["reading_order"]
87
+ assert "id3v1" in result["format_priorities"]["reading_order"]
88
+ assert result["format_priorities"]["writing_format"] == "riff"
89
+
90
+ # Check technical info
91
+ tech_info = result["technical_info"]
92
+ assert tech_info["file_extension"] == ".wav"
93
+ assert tech_info["audio_format_name"] == "WAV"
94
+ assert tech_info["is_flac_md5_valid"] is None # Not a FLAC file
95
+
96
+ # Check format metadata
97
+ assert "riff" in result["metadata_format"]
98
+ assert "id3v2" in result["metadata_format"]
99
+ assert "id3v1" in result["metadata_format"]
100
+
101
+ # Check headers
102
+ assert "riff" in result["headers"]
103
+ assert "id3v2" in result["headers"]
104
+ assert "id3v1" in result["headers"]
105
+
106
+ def test_get_full_metadata_format_specific_metadata_isolation(self, sample_mp3_file: Path):
107
+ result = get_full_metadata(sample_mp3_file)
108
+
109
+ # Each format should have its own metadata section
110
+ metadata_format = result["metadata_format"]
111
+
112
+ # ID3v2 metadata should be separate from ID3v1
113
+ if "id3v2" in metadata_format and "id3v1" in metadata_format:
114
+ id3v2_metadata = metadata_format["id3v2"]
115
+ id3v1_metadata = metadata_format["id3v1"]
116
+
117
+ # They should be separate dictionaries
118
+ assert isinstance(id3v2_metadata, dict)
119
+ assert isinstance(id3v1_metadata, dict)
120
+
121
+ # They might have different content or structure
122
+ # This is expected and correct behavior