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,250 @@
1
+ """Tests for binary data filtering in get_full_metadata function output."""
2
+
3
+ import pytest
4
+
5
+ from audiometa import get_full_metadata
6
+ from audiometa.manager._rating_supporting.id3v2._Id3v2Manager import _Id3v2Manager as Id3v2Manager
7
+
8
+
9
+ @pytest.mark.integration
10
+ class TestGetFullMetadataBinaryDataFiltering:
11
+ """Test that get_full_metadata function properly filters binary data from raw metadata output."""
12
+
13
+ def test_get_full_metadata_id3v2_binary_frames_filtered(self, sample_mp3_file):
14
+ """Test that get_full_metadata filters ID3v2 binary frames and replaces with size info."""
15
+ result = get_full_metadata(sample_mp3_file)
16
+ raw_metadata = result.get("raw_metadata", {})
17
+ id3v2_frames = raw_metadata.get("id3v2", {}).get("frames", {})
18
+
19
+ # Check that all frames have reasonable text content
20
+ for frame_id, frame_data in id3v2_frames.items():
21
+ text = frame_data.get("text", "")
22
+
23
+ # Text should not contain binary data patterns
24
+ assert not any(
25
+ ord(c) < 32 and c not in "\t\n\r" for c in text
26
+ ), f"Frame {frame_id} contains binary data in text: {text[:50]!r}"
27
+
28
+ # If it's a binary frame type, should have placeholder text
29
+ binary_frame_types = {
30
+ "APIC:",
31
+ "GEOB:",
32
+ "AENC:",
33
+ "RVA2:",
34
+ "RVRB:",
35
+ "EQU2:",
36
+ "PCNT:",
37
+ "POPM:",
38
+ "RBUF:",
39
+ "LINK:",
40
+ "POSS:",
41
+ "SYLT:",
42
+ "USLT:",
43
+ "SYTC:",
44
+ "ETCO:",
45
+ "MLLT:",
46
+ "OWNE:",
47
+ "COMR:",
48
+ "ENCR:",
49
+ "GRID:",
50
+ "PRIV:",
51
+ "SIGN:",
52
+ "SEEK:",
53
+ "ASPI:",
54
+ }
55
+
56
+ if frame_id in binary_frame_types:
57
+ assert text.startswith(
58
+ "<Binary data:"
59
+ ), f"Binary frame {frame_id} should have placeholder text, got: {text}"
60
+ assert text.endswith(" bytes>"), f"Binary frame {frame_id} should end with ' bytes>', got: {text}"
61
+
62
+ def test_id3v2_manager_binary_filtering(self, sample_mp3_file):
63
+ """Test Id3v2Manager directly filters binary frames."""
64
+ from audiometa._audio_file import _AudioFile
65
+
66
+ audio_file = _AudioFile(sample_mp3_file)
67
+ manager = Id3v2Manager(audio_file)
68
+ raw_info = manager.get_raw_metadata_info()
69
+
70
+ frames = raw_info.get("frames", {})
71
+ binary_frame_types = {
72
+ "APIC:",
73
+ "GEOB:",
74
+ "AENC:",
75
+ "RVA2:",
76
+ "RVRB:",
77
+ "EQU2:",
78
+ "PCNT:",
79
+ "POPM:",
80
+ "RBUF:",
81
+ "LINK:",
82
+ "POSS:",
83
+ "SYLT:",
84
+ "USLT:",
85
+ "SYTC:",
86
+ "ETCO:",
87
+ "MLLT:",
88
+ "OWNE:",
89
+ "COMR:",
90
+ "ENCR:",
91
+ "GRID:",
92
+ "PRIV:",
93
+ "SIGN:",
94
+ "SEEK:",
95
+ "ASPI:",
96
+ }
97
+
98
+ for frame_id, frame_data in frames.items():
99
+ text = frame_data.get("text", "")
100
+
101
+ if frame_id in binary_frame_types:
102
+ # Binary frames should have placeholder text
103
+ assert text.startswith("<Binary data:"), f"Binary frame {frame_id} should have placeholder text"
104
+ assert text.endswith(" bytes>"), f"Binary frame {frame_id} should end with ' bytes>'"
105
+ else:
106
+ # Text frames should not contain binary data
107
+ assert not any(
108
+ ord(c) < 32 and c not in "\t\n\r" for c in text
109
+ ), f"Text frame {frame_id} contains binary data: {text[:50]!r}"
110
+
111
+ def test_get_full_metadata_vorbis_no_binary_data(self, sample_flac_file):
112
+ """Test that get_full_metadata Vorbis comments don't contain binary data."""
113
+ result = get_full_metadata(sample_flac_file)
114
+ raw_metadata = result.get("raw_metadata", {})
115
+ vorbis_comments = raw_metadata.get("vorbis", {}).get("comments", {})
116
+
117
+ # Vorbis comments should only contain text
118
+ for key, values in vorbis_comments.items():
119
+ assert isinstance(values, list), f"Vorbis comment {key} should be a list"
120
+ for value in values:
121
+ assert isinstance(value, str), f"Vorbis comment {key} value should be string"
122
+ # Check for binary data patterns
123
+ assert not any(
124
+ ord(c) < 32 and c not in "\t\n\r" for c in value
125
+ ), f"Vorbis comment {key} contains binary data: {value[:50]!r}"
126
+
127
+ def test_get_full_metadata_riff_no_binary_data(self, sample_wav_file):
128
+ """Test that get_full_metadata RIFF metadata doesn't contain binary data."""
129
+ result = get_full_metadata(sample_wav_file)
130
+ raw_metadata = result.get("raw_metadata", {})
131
+ riff_fields = raw_metadata.get("riff", {}).get("parsed_fields", {})
132
+
133
+ # RIFF parsed fields should only contain text
134
+ for key, value in riff_fields.items():
135
+ assert isinstance(value, str), f"RIFF field {key} should be string"
136
+ # Check for binary data patterns
137
+ assert not any(
138
+ ord(c) < 32 and c not in "\t\n\r" for c in value
139
+ ), f"RIFF field {key} contains binary data: {value[:50]!r}"
140
+
141
+ def test_get_full_metadata_id3v1_no_binary_data(self, sample_mp3_file):
142
+ """Test that get_full_metadata ID3v1 metadata doesn't contain binary data."""
143
+ result = get_full_metadata(sample_mp3_file)
144
+ raw_metadata = result.get("raw_metadata", {})
145
+ id3v1_fields = raw_metadata.get("id3v1", {}).get("parsed_fields", {})
146
+
147
+ # ID3v1 parsed fields should only contain text
148
+ for key, value in id3v1_fields.items():
149
+ assert isinstance(value, str), f"ID3v1 field {key} should be string"
150
+ # Check for binary data patterns
151
+ assert not any(
152
+ ord(c) < 32 and c not in "\t\n\r" for c in value
153
+ ), f"ID3v1 field {key} contains binary data: {value[:50]!r}"
154
+
155
+ def test_cli_output_no_binary_data(self, sample_mp3_file):
156
+ """Test that CLI output doesn't contain binary data."""
157
+ import subprocess
158
+ import sys
159
+
160
+ # Test JSON output
161
+ result = subprocess.run(
162
+ [sys.executable, "-m", "audiometa", "read", str(sample_mp3_file), "--format", "json"],
163
+ capture_output=True,
164
+ text=True,
165
+ check=False,
166
+ )
167
+
168
+ assert result.returncode == 0, f"CLI failed: {result.stderr}"
169
+
170
+ # Check for binary data patterns in output
171
+ output = result.stdout
172
+ binary_patterns = ["\\xff", "\\x00", "\\x01", "\\x02", "\\x03"]
173
+
174
+ for pattern in binary_patterns:
175
+ assert pattern not in output, f"CLI output contains binary pattern {pattern}"
176
+
177
+ # Should be valid JSON
178
+ import json
179
+
180
+ data = json.loads(output)
181
+ assert isinstance(data, dict)
182
+
183
+ def test_binary_frame_size_preserved(self, sample_mp3_file):
184
+ """Test that binary frame sizes are still reported correctly."""
185
+ from audiometa._audio_file import _AudioFile
186
+
187
+ audio_file = _AudioFile(sample_mp3_file)
188
+ manager = Id3v2Manager(audio_file)
189
+ raw_info = manager.get_raw_metadata_info()
190
+
191
+ frames = raw_info.get("frames", {})
192
+ binary_frame_types = {
193
+ "APIC:",
194
+ "GEOB:",
195
+ "AENC:",
196
+ "RVA2:",
197
+ "RVRB:",
198
+ "EQU2:",
199
+ "PCNT:",
200
+ "POPM:",
201
+ "RBUF:",
202
+ "LINK:",
203
+ "POSS:",
204
+ "SYLT:",
205
+ "USLT:",
206
+ "SYTC:",
207
+ "ETCO:",
208
+ "MLLT:",
209
+ "OWNE:",
210
+ "COMR:",
211
+ "ENCR:",
212
+ "GRID:",
213
+ "PRIV:",
214
+ "SIGN:",
215
+ "SEEK:",
216
+ "ASPI:",
217
+ }
218
+
219
+ for frame_id, frame_data in frames.items():
220
+ if frame_id in binary_frame_types:
221
+ size = frame_data.get("size")
222
+ flags = frame_data.get("flags")
223
+
224
+ # Size and flags should still be present
225
+ assert isinstance(size, int), f"Binary frame {frame_id} size should be int"
226
+ assert isinstance(flags, int), f"Binary frame {frame_id} flags should be int"
227
+
228
+ # Size should be reasonable (not negative)
229
+ assert size >= 0, f"Binary frame {frame_id} size should be non-negative"
230
+
231
+ def test_text_frames_unchanged(self, sample_mp3_file):
232
+ """Test that text frames are not affected by binary filtering."""
233
+ from audiometa._audio_file import _AudioFile
234
+
235
+ audio_file = _AudioFile(sample_mp3_file)
236
+ manager = Id3v2Manager(audio_file)
237
+ raw_info = manager.get_raw_metadata_info()
238
+
239
+ frames = raw_info.get("frames", {})
240
+ text_frame_types = {"TIT2", "TALB", "TPE1", "TDRC", "COMM", "TENC", "TSSE"}
241
+
242
+ for frame_id, frame_data in frames.items():
243
+ if any(frame_id.startswith(prefix) for prefix in text_frame_types):
244
+ text = frame_data.get("text", "")
245
+
246
+ # Text frames should have actual content, not placeholder
247
+ assert not text.startswith("<Binary data:"), f"Text frame {frame_id} should not have binary placeholder"
248
+
249
+ # Should have reasonable content
250
+ assert len(text) > 0, f"Text frame {frame_id} should have content"
@@ -0,0 +1,67 @@
1
+ """Tests for get_full_metadata function consistency and accuracy."""
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from audiometa import get_full_metadata, get_unified_metadata
8
+ from audiometa._audio_file import _AudioFile
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestGetFullMetadataConsistency:
13
+ def test_get_full_metadata_consistency_with_merged_metadata(self, sample_mp3_file: Path):
14
+ full_result = get_full_metadata(sample_mp3_file)
15
+ merged_result = get_unified_metadata(sample_mp3_file)
16
+
17
+ # Should be identical
18
+ assert full_result["unified_metadata"] == merged_result
19
+
20
+ def test_get_full_metadata_technical_info_accuracy(self, sample_mp3_file: Path):
21
+ audio_file = _AudioFile(sample_mp3_file)
22
+ result = get_full_metadata(sample_mp3_file)
23
+
24
+ tech_info = result["technical_info"]
25
+
26
+ # Compare with direct _AudioFile methods
27
+ assert tech_info["duration_seconds"] == audio_file.get_duration_in_sec()
28
+ assert tech_info["bitrate_bps"] == audio_file.get_bitrate()
29
+ assert tech_info["sample_rate_hz"] == audio_file.get_sample_rate()
30
+ assert tech_info["channels"] == audio_file.get_channels()
31
+ assert tech_info["file_size_bytes"] == audio_file.get_file_size()
32
+ assert tech_info["file_extension"] == audio_file.file_extension
33
+ assert tech_info["audio_format_name"] == audio_file.get_audio_format_name()
34
+
35
+ def test_get_full_metadata_flac_md5_validation(self, sample_flac_file: Path):
36
+ result = get_full_metadata(sample_flac_file)
37
+
38
+ tech_info = result["technical_info"]
39
+ assert "is_flac_md5_valid" in tech_info
40
+ assert isinstance(tech_info["is_flac_md5_valid"], bool)
41
+
42
+ def test_get_full_metadata_structure_consistency(self, sample_mp3_file: Path):
43
+ result1 = get_full_metadata(sample_mp3_file)
44
+ result2 = get_full_metadata(sample_mp3_file)
45
+
46
+ # Structure should be identical
47
+ assert set(result1.keys()) == set(result2.keys())
48
+
49
+ # Each top-level section should have same keys
50
+ for key in result1:
51
+ if key in ["unified_metadata", "technical_info", "metadata_format", "headers", "raw_metadata"]:
52
+ assert set(result1[key].keys()) == set(result2[key].keys())
53
+
54
+ def test_get_full_metadata_format_detection_accuracy(self, sample_mp3_file: Path):
55
+ result = get_full_metadata(sample_mp3_file)
56
+
57
+ # Format priorities should be correct for MP3
58
+ priorities = result["format_priorities"]
59
+ assert priorities["file_extension"] == ".mp3"
60
+ assert "id3v2" in priorities["reading_order"]
61
+ assert "id3v1" in priorities["reading_order"]
62
+ assert priorities["writing_format"] == "id3v2"
63
+
64
+ # Technical info should reflect MP3 format
65
+ tech_info = result["technical_info"]
66
+ assert tech_info["file_extension"] == ".mp3"
67
+ assert tech_info["audio_format_name"] == "MP3"
@@ -0,0 +1,123 @@
1
+ """Edge case tests for get_full_metadata function."""
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from audiometa import get_full_metadata
8
+ from audiometa.test.helpers.temp_file_with_metadata import temp_file_with_metadata
9
+
10
+
11
+ @pytest.mark.integration
12
+ class TestGetFullMetadataEdgeCases:
13
+ def test_get_full_metadata_empty_file(self):
14
+ with temp_file_with_metadata({}, "mp3") as temp_file_path:
15
+ # Should handle gracefully and return structure with minimal data
16
+ result = get_full_metadata(temp_file_path)
17
+
18
+ # Should still return complete structure
19
+ assert "unified_metadata" in result
20
+ assert "technical_info" in result
21
+ assert "metadata_format" in result
22
+ assert "headers" in result
23
+ assert "raw_metadata" in result
24
+ assert "format_priorities" in result
25
+
26
+ def test_get_full_metadata_file_with_only_headers_no_metadata(self):
27
+ with temp_file_with_metadata({}, "mp3") as temp_file_path:
28
+ result = get_full_metadata(temp_file_path)
29
+
30
+ # Should detect headers even if no metadata content
31
+ headers = result["headers"]
32
+
33
+ for _metadata_format_name, header_info in headers.items():
34
+ # Headers might be present even without metadata content
35
+ assert "present" in header_info
36
+ assert isinstance(header_info["present"], bool)
37
+
38
+ def test_get_full_metadata_large_file(self, sample_mp3_file: Path):
39
+ # This test ensures the function can handle larger files
40
+ result = get_full_metadata(sample_mp3_file)
41
+
42
+ # Should complete successfully
43
+ assert "unified_metadata" in result
44
+ assert "technical_info" in result
45
+
46
+ # File size should be reasonable
47
+ tech_info = result["technical_info"]
48
+ assert tech_info["file_size_bytes"] > 0
49
+
50
+ def test_get_full_metadata_file_with_mixed_formats(self, sample_mp3_file: Path):
51
+ result = get_full_metadata(sample_mp3_file)
52
+
53
+ # Should handle multiple formats gracefully
54
+ metadata_format = result["metadata_format"]
55
+ headers = result["headers"]
56
+
57
+ # Each format should have its own section
58
+ for metadata_format_name in ["id3v2", "id3v1"]:
59
+ assert metadata_format_name in metadata_format
60
+ assert metadata_format_name in headers
61
+
62
+ # Each should be a dictionary
63
+ assert isinstance(metadata_format[metadata_format_name], dict)
64
+ assert isinstance(headers[metadata_format_name], dict)
65
+
66
+ def test_get_full_metadata_with_unicode_metadata(self, sample_mp3_file: Path):
67
+ # This test ensures unicode handling works correctly
68
+ result = get_full_metadata(sample_mp3_file)
69
+
70
+ # Should handle unicode in metadata
71
+ unified_metadata = result["unified_metadata"]
72
+
73
+ # Check that string values are properly handled
74
+ for _key, value in unified_metadata.items():
75
+ if isinstance(value, str):
76
+ # Should be able to handle unicode
77
+ assert isinstance(value, str)
78
+ elif isinstance(value, list):
79
+ for item in value:
80
+ if isinstance(item, str):
81
+ assert isinstance(item, str)
82
+
83
+ def test_get_full_metadata_with_minimal_metadata(self):
84
+ with temp_file_with_metadata({}, "mp3") as temp_file_path:
85
+ result = get_full_metadata(temp_file_path)
86
+
87
+ # Should still return complete structure
88
+ assert "unified_metadata" in result
89
+ assert "technical_info" in result
90
+ assert "metadata_format" in result
91
+ assert "headers" in result
92
+ assert "raw_metadata" in result
93
+ assert "format_priorities" in result
94
+
95
+ # Unified metadata might be empty or minimal
96
+ unified_metadata = result["unified_metadata"]
97
+ assert isinstance(unified_metadata, dict)
98
+
99
+ # Technical info should still be present
100
+ tech_info = result["technical_info"]
101
+ assert "file_size_bytes" in tech_info
102
+ assert tech_info["file_size_bytes"] >= 0 # Can be 0 for empty files
103
+
104
+ def test_get_full_metadata_file_with_no_metadata(self):
105
+ with temp_file_with_metadata({}, "mp3") as temp_file_path:
106
+ result = get_full_metadata(temp_file_path)
107
+
108
+ # Should still return complete structure
109
+ assert "unified_metadata" in result
110
+ assert "technical_info" in result
111
+ assert "metadata_format" in result
112
+ assert "headers" in result
113
+ assert "raw_metadata" in result
114
+ assert "format_priorities" in result
115
+
116
+ # Unified metadata should be empty or minimal
117
+ assert isinstance(result["unified_metadata"], dict)
118
+
119
+ # Technical info should still be present
120
+ tech_info = result["technical_info"]
121
+ assert "duration_seconds" in tech_info
122
+ assert "bitrate_bps" in tech_info
123
+ assert "file_size_bytes" in tech_info
@@ -0,0 +1,40 @@
1
+ """Error handling tests for get_full_metadata function."""
2
+
3
+ import tempfile
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+
8
+ from audiometa import get_full_metadata
9
+ from audiometa.exceptions import FileCorruptedError, FileTypeNotSupportedError
10
+
11
+
12
+ @pytest.mark.integration
13
+ class TestGetFullMetadataErrorHandling:
14
+ def test_get_full_metadata_corrupted_file(self):
15
+ with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
16
+ temp_path = temp_file.name
17
+ # Write some garbage data
18
+ temp_file.write(b"This is not a valid audio file")
19
+
20
+ try:
21
+ with pytest.raises(FileCorruptedError):
22
+ get_full_metadata(temp_path)
23
+ finally:
24
+ Path(temp_path).unlink()
25
+
26
+ def test_get_full_metadata_error_recovery(self):
27
+ # Test with non-existent file
28
+ with pytest.raises(FileNotFoundError):
29
+ get_full_metadata("non_existent_file.mp3")
30
+
31
+ # Test with unsupported file type (create the file first)
32
+ with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as temp_file:
33
+ temp_path = temp_file.name
34
+ temp_file.write(b"This is not an audio file")
35
+
36
+ try:
37
+ with pytest.raises(FileTypeNotSupportedError):
38
+ get_full_metadata(temp_path)
39
+ finally:
40
+ Path(temp_path).unlink()
@@ -0,0 +1,43 @@
1
+ """Basic structure tests for get_full_metadata function."""
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 TestGetFullMetadata:
12
+ def test_get_full_metadata_basic_structure(self, sample_mp3_file: Path):
13
+ """Test that get_full_metadata returns the expected basic structure."""
14
+ result = get_full_metadata(sample_mp3_file)
15
+
16
+ # Check all required top-level keys are present
17
+ assert "unified_metadata" in result
18
+ assert "technical_info" in result
19
+ assert "metadata_format" in result
20
+ assert "headers" in result
21
+ assert "raw_metadata" in result
22
+ assert "format_priorities" in result
23
+
24
+ # Check that each section is a dictionary
25
+ assert isinstance(result["unified_metadata"], dict)
26
+ assert isinstance(result["technical_info"], dict)
27
+ assert isinstance(result["metadata_format"], dict)
28
+ assert isinstance(result["headers"], dict)
29
+ assert isinstance(result["raw_metadata"], dict)
30
+ assert isinstance(result["format_priorities"], dict)
31
+
32
+ def test_get_full_metadata_format_priorities_structure(self, sample_mp3_file: Path):
33
+ """Test that format_priorities has the expected structure."""
34
+ result = get_full_metadata(sample_mp3_file)
35
+
36
+ priorities = result["format_priorities"]
37
+ assert "file_extension" in priorities
38
+ assert "reading_order" in priorities
39
+ assert "writing_format" in priorities
40
+
41
+ assert isinstance(priorities["file_extension"], str)
42
+ assert isinstance(priorities["reading_order"], list)
43
+ assert isinstance(priorities["writing_format"], str)