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,234 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from audiometa.cli import expand_file_patterns
6
+
7
+
8
+ @pytest.mark.unit
9
+ class TestExpandFilePatterns:
10
+ def test_single_file_exists(self, tmp_path):
11
+ test_file = tmp_path / "test.mp3"
12
+ test_file.write_text("fake mp3 content")
13
+
14
+ result = expand_file_patterns([str(test_file)])
15
+ assert len(result) == 1
16
+ assert result[0] == test_file
17
+
18
+ def test_single_file_nonexistent(self, tmp_path):
19
+ nonexistent_file = tmp_path / "nonexistent.mp3"
20
+
21
+ result = expand_file_patterns([str(nonexistent_file)], continue_on_error=True)
22
+ assert result == []
23
+
24
+ def test_glob_pattern_matching_files(self, tmp_path):
25
+ # Create test files
26
+ (tmp_path / "song1.mp3").write_text("content1")
27
+ (tmp_path / "song2.mp3").write_text("content2")
28
+ (tmp_path / "other.txt").write_text("content3")
29
+
30
+ pattern = str(tmp_path / "*.mp3")
31
+ result = expand_file_patterns([pattern])
32
+
33
+ assert len(result) == 2
34
+ assert all(path.suffix == ".mp3" for path in result)
35
+ assert all(path.parent == tmp_path for path in result)
36
+
37
+ def test_glob_pattern_no_matches(self, tmp_path):
38
+ pattern = str(tmp_path / "*.wav")
39
+ result = expand_file_patterns([pattern], continue_on_error=True)
40
+ assert result == []
41
+
42
+ def test_directory_non_recursive(self, tmp_path):
43
+ subdir = tmp_path / "subdir"
44
+ subdir.mkdir()
45
+ (subdir / "song.mp3").write_text("content")
46
+
47
+ result = expand_file_patterns([str(subdir)], continue_on_error=True)
48
+ assert result == []
49
+
50
+ def test_directory_recursive(self, tmp_path):
51
+ subdir = tmp_path / "music"
52
+ subdir.mkdir()
53
+ nested_dir = subdir / "album1"
54
+ nested_dir.mkdir()
55
+
56
+ # Create various audio files
57
+ (subdir / "song1.mp3").write_text("content1")
58
+ (nested_dir / "song2.flac").write_text("content2")
59
+ (nested_dir / "song3.wav").write_text("content3")
60
+ (nested_dir / "notes.txt").write_text("content4") # Non-audio file
61
+
62
+ result = expand_file_patterns([str(subdir)], recursive=True)
63
+
64
+ assert len(result) == 3
65
+ assert all(path.suffix in [".mp3", ".flac", ".wav"] for path in result)
66
+ assert all(subdir in path.parents for path in result)
67
+
68
+ def test_directory_recursive_nested_directories(self, tmp_path):
69
+ # Create nested structure: music/artist/album/songs
70
+ music_dir = tmp_path / "music"
71
+ artist_dir = music_dir / "artist"
72
+ album_dir = artist_dir / "album"
73
+ album_dir.mkdir(parents=True)
74
+
75
+ # Create files at different levels
76
+ (music_dir / "root.mp3").write_text("root")
77
+ (artist_dir / "artist.mp3").write_text("artist")
78
+ (album_dir / "song1.mp3").write_text("song1")
79
+ (album_dir / "song2.flac").write_text("song2")
80
+
81
+ result = expand_file_patterns([str(music_dir)], recursive=True)
82
+
83
+ assert len(result) == 4
84
+ assert all(path.suffix in [".mp3", ".flac"] for path in result)
85
+
86
+ def test_mixed_patterns(self, tmp_path):
87
+ # Create files and directories
88
+ single_file = tmp_path / "single.mp3"
89
+ single_file.write_text("single")
90
+
91
+ music_dir = tmp_path / "music"
92
+ music_dir.mkdir()
93
+ (music_dir / "album.mp3").write_text("album")
94
+
95
+ # Create glob-matching files
96
+ (tmp_path / "glob1.mp3").write_text("glob1")
97
+ (tmp_path / "glob2.mp3").write_text("glob2")
98
+
99
+ patterns = [
100
+ str(single_file), # Single file
101
+ str(music_dir), # Directory (recursive)
102
+ str(tmp_path / "glob*.mp3"), # Glob pattern
103
+ ]
104
+
105
+ result = expand_file_patterns(patterns, recursive=True)
106
+
107
+ assert len(result) == 4 # single + album + glob1 + glob2
108
+ assert single_file in result
109
+ assert music_dir / "album.mp3" in result
110
+
111
+ def test_unsupported_audio_extensions_ignored(self, tmp_path):
112
+ music_dir = tmp_path / "music"
113
+ music_dir.mkdir()
114
+
115
+ # Create files with supported and unsupported extensions
116
+ (music_dir / "song.mp3").write_text("mp3")
117
+ (music_dir / "song.flac").write_text("flac")
118
+ (music_dir / "song.wav").write_text("wav")
119
+ (music_dir / "song.ogg").write_text("ogg") # Not supported
120
+ (music_dir / "song.m4a").write_text("m4a") # Not supported
121
+
122
+ result = expand_file_patterns([str(music_dir)], recursive=True)
123
+
124
+ assert len(result) == 3
125
+ assert all(path.suffix in [".mp3", ".flac", ".wav"] for path in result)
126
+
127
+ def test_no_files_found_continue_on_error_true(self, tmp_path, capsys):
128
+ nonexistent_pattern = str(tmp_path / "nonexistent.mp3")
129
+
130
+ result = expand_file_patterns([nonexistent_pattern], continue_on_error=True)
131
+
132
+ assert result == []
133
+ captured = capsys.readouterr()
134
+ assert "Warning: No valid audio files found" in captured.err
135
+
136
+ def test_no_files_found_continue_on_error_false(self, tmp_path, capsys):
137
+ nonexistent_pattern = str(tmp_path / "nonexistent.mp3")
138
+
139
+ with pytest.raises(SystemExit) as exc_info:
140
+ expand_file_patterns([nonexistent_pattern], continue_on_error=False)
141
+
142
+ assert exc_info.value.code == 1
143
+ captured = capsys.readouterr()
144
+ assert "Error: No valid audio files found" in captured.err
145
+
146
+ def test_complex_glob_patterns(self, tmp_path):
147
+ # Create test files with different patterns
148
+ (tmp_path / "track01.mp3").write_text("track01")
149
+ (tmp_path / "track02.mp3").write_text("track02")
150
+ (tmp_path / "song_a.mp3").write_text("song_a")
151
+ (tmp_path / "song_b.mp3").write_text("song_b")
152
+ (tmp_path / "other.wav").write_text("other")
153
+
154
+ # Test character class glob
155
+ pattern1 = str(tmp_path / "track[0-9][0-9].mp3")
156
+ result1 = expand_file_patterns([pattern1])
157
+ assert len(result1) == 2
158
+
159
+ # Test wildcard glob
160
+ pattern2 = str(tmp_path / "song_?.mp3")
161
+ result2 = expand_file_patterns([pattern2])
162
+ assert len(result2) == 2
163
+
164
+ def test_relative_paths(self, tmp_path, monkeypatch):
165
+ # Change to tmp_path directory
166
+ monkeypatch.chdir(tmp_path)
167
+
168
+ # Create relative file
169
+ rel_file = Path("relative.mp3")
170
+ rel_file.write_text("relative")
171
+
172
+ result = expand_file_patterns(["relative.mp3"])
173
+ assert len(result) == 1
174
+ assert result[0].name == "relative.mp3"
175
+
176
+ def test_hidden_files_ignored(self, tmp_path):
177
+ (tmp_path / "visible.mp3").write_text("visible")
178
+ (tmp_path / ".hidden.mp3").write_text("hidden")
179
+
180
+ pattern = str(tmp_path / "*.mp3")
181
+ result = expand_file_patterns([pattern])
182
+
183
+ assert len(result) == 1
184
+ assert result[0].name == "visible.mp3"
185
+
186
+ def test_case_sensitive_extensions(self, tmp_path):
187
+ # On case-insensitive filesystems, we can't create both .mp3 and .MP3 files
188
+ # So we'll test with different extensions instead
189
+ (tmp_path / "song.mp3").write_text("lowercase")
190
+ (tmp_path / "song.wav").write_text("wav")
191
+
192
+ pattern_mp3 = str(tmp_path / "*.mp3")
193
+ pattern_wav = str(tmp_path / "*.wav")
194
+
195
+ result_mp3 = expand_file_patterns([pattern_mp3], continue_on_error=True)
196
+ result_wav = expand_file_patterns([pattern_wav], continue_on_error=True)
197
+
198
+ # Each pattern should match exactly one file
199
+ assert len(result_mp3) == 1
200
+ assert result_mp3[0].name == "song.mp3"
201
+ assert len(result_wav) == 1
202
+ assert result_wav[0].name == "song.wav"
203
+
204
+ def test_empty_pattern_list(self):
205
+ result = expand_file_patterns([], continue_on_error=True)
206
+ assert result == []
207
+
208
+ def test_directory_with_no_audio_files_recursive(self, tmp_path):
209
+ empty_dir = tmp_path / "empty"
210
+ empty_dir.mkdir()
211
+ (empty_dir / "text.txt").write_text("text")
212
+
213
+ result = expand_file_patterns([str(empty_dir)], recursive=True, continue_on_error=True)
214
+ assert result == []
215
+
216
+ def test_file_vs_directory_same_name(self, tmp_path):
217
+ # Create a file named "music"
218
+ music_file = tmp_path / "music"
219
+ music_file.write_text("not audio")
220
+
221
+ # Create a directory named "music_dir" with audio files
222
+ music_dir = tmp_path / "music_dir"
223
+ music_dir.mkdir()
224
+ (music_dir / "song.mp3").write_text("audio")
225
+
226
+ # Test file takes precedence when it exists
227
+ result1 = expand_file_patterns([str(music_file)])
228
+ assert len(result1) == 1
229
+ assert result1[0] == music_file
230
+
231
+ # Test directory expansion
232
+ result2 = expand_file_patterns([str(music_dir)], recursive=True)
233
+ assert len(result2) == 1
234
+ assert result2[0] == music_dir / "song.mp3"
@@ -0,0 +1,142 @@
1
+ from unittest.mock import MagicMock
2
+
3
+ import pytest
4
+
5
+ from audiometa._audio_file import _AudioFile
6
+
7
+
8
+ @pytest.fixture
9
+ def mock_audio_file_mp3():
10
+ mock_audio_file = MagicMock(spec=_AudioFile)
11
+ mock_audio_file.file_path = "/path/to/test.mp3"
12
+ mock_audio_file.file_extension = ".mp3"
13
+ return mock_audio_file
14
+
15
+
16
+ @pytest.fixture
17
+ def mock_audio_file_wav():
18
+ mock_audio_file = MagicMock(spec=_AudioFile)
19
+ mock_audio_file.file_path = "/path/to/test.wav"
20
+ mock_audio_file.file_extension = ".wav"
21
+ return mock_audio_file
22
+
23
+
24
+ @pytest.fixture
25
+ def mock_audio_file_flac():
26
+ mock_audio_file = MagicMock(spec=_AudioFile)
27
+ mock_audio_file.file_path = "/path/to/test.flac"
28
+ mock_audio_file.file_extension = ".flac"
29
+ return mock_audio_file
30
+
31
+
32
+ @pytest.fixture
33
+ def mock_id3_empty():
34
+ mock_id3 = MagicMock()
35
+ mock_id3.version = (2, 3, 0)
36
+ mock_id3.size = 0
37
+ mock_id3.flags = 0
38
+ mock_id3.__contains__ = lambda _self, _key: False
39
+ mock_id3.items.return_value = []
40
+ mock_id3.extended_header = None
41
+ return mock_id3
42
+
43
+
44
+ @pytest.fixture
45
+ def mock_id3_with_metadata():
46
+ mock_id3 = MagicMock()
47
+ mock_id3.version = (2, 3, 0)
48
+ mock_id3.size = 2048
49
+ mock_id3.flags = 0x40
50
+ mock_id3.extended_header = None
51
+
52
+ mock_title = MagicMock()
53
+ mock_title.text = ["Test Title"]
54
+ mock_artists = MagicMock()
55
+ mock_artists.text = ["Test Artist"]
56
+ mock_album = MagicMock()
57
+ mock_album.text = ["Test Album"]
58
+
59
+ frame_dict = {
60
+ "TIT2": mock_title,
61
+ "TPE1": mock_artists,
62
+ "TALB": mock_album,
63
+ }
64
+
65
+ mock_id3.__contains__ = lambda _self, key: key in frame_dict
66
+ mock_id3.__getitem__ = lambda _self, key: frame_dict.get(key)
67
+ mock_id3.items.return_value = []
68
+
69
+ return mock_id3
70
+
71
+
72
+ @pytest.fixture
73
+ def mock_id3_updatable():
74
+ """Mock ID3 object that supports add/delall operations for update_metadata testing."""
75
+ mock_id3 = MagicMock()
76
+ mock_id3.version = (2, 3, 0)
77
+ mock_id3.size = 0
78
+ mock_id3.flags = 0
79
+ mock_id3.extended_header = None
80
+
81
+ # Storage for frames
82
+ frames = {}
83
+
84
+ def mock_delall(key):
85
+ """Remove all frames with the given key."""
86
+ keys_to_remove = [k for k in frames if k.startswith(key)]
87
+ for k in keys_to_remove:
88
+ del frames[k]
89
+
90
+ def mock_add(frame):
91
+ """Add a frame to the mock."""
92
+ frame_id = frame.FrameID
93
+ if hasattr(frame, "email"): # POPM frame
94
+ frame_id = f"{frame_id}:{frame.email}"
95
+
96
+ frames[frame_id] = frame
97
+
98
+ def mock_items():
99
+ """Return list of (key, frame) tuples."""
100
+ return list(frames.items())
101
+
102
+ mock_id3.delall = mock_delall
103
+ mock_id3.add = mock_add
104
+ mock_id3.items = mock_items
105
+ mock_id3.__contains__ = lambda _self, key: key in frames
106
+ mock_id3.__getitem__ = lambda _self, key: frames[key]
107
+
108
+ return mock_id3
109
+
110
+
111
+ @pytest.fixture
112
+ def mock_wave_empty():
113
+ mock_wave = MagicMock()
114
+ mock_wave.info = {}
115
+ return mock_wave
116
+
117
+
118
+ @pytest.fixture
119
+ def mock_wave_with_metadata():
120
+ mock_wave = MagicMock()
121
+ mock_wave.info = {
122
+ "INAM": ["Test Title"],
123
+ "IART": ["Test Artist"],
124
+ "IPRD": ["Test Album"],
125
+ }
126
+ return mock_wave
127
+
128
+
129
+ @pytest.fixture
130
+ def mock_vorbis_metadata_empty():
131
+ """Mock Vorbis metadata dict with no metadata."""
132
+ return {}
133
+
134
+
135
+ @pytest.fixture
136
+ def mock_vorbis_metadata_with_data():
137
+ """Mock Vorbis metadata dict with sample metadata."""
138
+ return {
139
+ "TITLE": ["Test Title"],
140
+ "ARTIST": ["Test Artist"],
141
+ "ALBUM": ["Test Album"],
142
+ }
@@ -0,0 +1,49 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from audiometa._audio_file import _AudioFile
6
+ from audiometa.manager.id3v1._Id3v1Manager import _Id3v1Manager as Id3v1Manager
7
+
8
+
9
+ @pytest.mark.unit
10
+ class TestId3v1HeaderMethods:
11
+ def test_id3v1_manager_header_info(self, sample_mp3_file: Path):
12
+ audio_file = _AudioFile(sample_mp3_file)
13
+ manager = Id3v1Manager(audio_file)
14
+
15
+ header_info = manager.get_header_info()
16
+
17
+ # Should have ID3v1 specific structure
18
+ assert "present" in header_info
19
+ assert "position" in header_info
20
+ assert "size_bytes" in header_info
21
+ assert "version" in header_info
22
+ assert "has_track_number" in header_info
23
+
24
+ # Should be valid structure
25
+ assert isinstance(header_info["present"], bool)
26
+ assert header_info["position"] is None or isinstance(header_info["position"], str)
27
+ assert isinstance(header_info["size_bytes"], int)
28
+ assert header_info["version"] is None or isinstance(header_info["version"], str)
29
+ assert isinstance(header_info["has_track_number"], bool)
30
+
31
+ def test_id3v1_manager_raw_metadata_info(self, sample_mp3_file: Path):
32
+ audio_file = _AudioFile(sample_mp3_file)
33
+ manager = Id3v1Manager(audio_file)
34
+
35
+ raw_info = manager.get_raw_metadata_info()
36
+
37
+ # Should have ID3v1 specific structure
38
+ assert "raw_data" in raw_info
39
+ assert "parsed_fields" in raw_info
40
+ assert "frames" in raw_info
41
+ assert "comments" in raw_info
42
+ assert "chunk_structure" in raw_info
43
+
44
+ # Should be valid structure
45
+ assert raw_info["raw_data"] is None or isinstance(raw_info["raw_data"], bytes)
46
+ assert isinstance(raw_info["parsed_fields"], dict)
47
+ assert isinstance(raw_info["frames"], dict)
48
+ assert isinstance(raw_info["comments"], dict)
49
+ assert isinstance(raw_info["chunk_structure"], dict)
@@ -0,0 +1,66 @@
1
+ from unittest.mock import patch
2
+
3
+ import pytest
4
+
5
+ from audiometa.manager._rating_supporting.id3v2._Id3v2Manager import _Id3v2Manager as Id3v2Manager
6
+
7
+
8
+ @pytest.mark.unit
9
+ class TestId3v2HeaderMethods:
10
+ @patch("audiometa.manager._rating_supporting.id3v2._Id3v2Manager.ID3")
11
+ def test_id3v2_manager_header_info(self, mock_id3_class, mock_audio_file_mp3, mock_id3_with_metadata):
12
+ mock_id3_class.return_value = mock_id3_with_metadata
13
+
14
+ manager = Id3v2Manager(mock_audio_file_mp3)
15
+ header_info = manager.get_header_info()
16
+
17
+ assert "present" in header_info
18
+ assert "version" in header_info
19
+ assert "header_size_bytes" in header_info
20
+ assert "flags" in header_info
21
+ assert "extended_header" in header_info
22
+
23
+ assert isinstance(header_info["present"], bool)
24
+ assert isinstance(header_info["version"], str)
25
+ assert header_info["version"] == "2.3.0"
26
+ assert isinstance(header_info["header_size_bytes"], int)
27
+ assert isinstance(header_info["flags"], dict)
28
+ assert isinstance(header_info["extended_header"], dict)
29
+
30
+ @patch("audiometa.manager._rating_supporting.id3v2._Id3v2Manager.ID3")
31
+ def test_id3v2_manager_header_info_empty(self, mock_id3_class, mock_audio_file_mp3, mock_id3_empty):
32
+ mock_id3_class.return_value = mock_id3_empty
33
+
34
+ manager = Id3v2Manager(mock_audio_file_mp3)
35
+ header_info = manager.get_header_info()
36
+
37
+ assert "present" in header_info
38
+ assert "version" in header_info
39
+ assert "header_size_bytes" in header_info
40
+ assert "flags" in header_info
41
+ assert "extended_header" in header_info
42
+
43
+ assert isinstance(header_info["present"], bool)
44
+ assert isinstance(header_info["version"], str)
45
+ assert isinstance(header_info["header_size_bytes"], int)
46
+ assert isinstance(header_info["flags"], dict)
47
+ assert isinstance(header_info["extended_header"], dict)
48
+
49
+ @patch("audiometa.manager._rating_supporting.id3v2._Id3v2Manager.ID3")
50
+ def test_id3v2_manager_raw_metadata_info(self, mock_id3_class, mock_audio_file_mp3, mock_id3_empty):
51
+ mock_id3_class.return_value = mock_id3_empty
52
+
53
+ manager = Id3v2Manager(mock_audio_file_mp3)
54
+ raw_info = manager.get_raw_metadata_info()
55
+
56
+ assert "raw_data" in raw_info
57
+ assert "parsed_fields" in raw_info
58
+ assert "frames" in raw_info
59
+ assert "comments" in raw_info
60
+ assert "chunk_structure" in raw_info
61
+
62
+ assert raw_info["raw_data"] is None or isinstance(raw_info["raw_data"], bytes)
63
+ assert isinstance(raw_info["parsed_fields"], dict)
64
+ assert isinstance(raw_info["frames"], dict)
65
+ assert isinstance(raw_info["comments"], dict)
66
+ assert isinstance(raw_info["chunk_structure"], dict)