@poe-platform/safe-bash 0.1.812 → 0.1.813

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 (329) hide show
  1. package/dist/audio-ast/id3-source.d.ts +27 -0
  2. package/dist/audio-ast/index.d.ts +4 -0
  3. package/dist/audio-ast/index.js +996 -596
  4. package/dist/audio-ast/mp3-source.d.ts +18 -0
  5. package/dist/audio-ast/ogg-source.d.ts +25 -0
  6. package/dist/audio-ast/ogg-stream-source.d.ts +22 -0
  7. package/dist/image-ast/index.js +139 -9
  8. package/dist/mp4-ast/containers/adapters.d.ts +7 -0
  9. package/dist/office-xml/retained-xml-document.d.ts +8 -0
  10. package/dist/pdf-ast/ast.d.ts +2 -0
  11. package/dist/pdf-ast/content/dictionary-cursor.d.ts +15 -0
  12. package/dist/pdf-ast/content/range-operator-parser.d.ts +4 -0
  13. package/dist/pdf-ast/content/stored-dictionary.d.ts +10 -0
  14. package/dist/pdf-ast/cos/lexer.d.ts +8 -5
  15. package/dist/pdf-ast/cos/object-reader.d.ts +4 -3
  16. package/dist/pdf-ast/cos/range-parser.d.ts +4 -0
  17. package/dist/pdf-ast/cos/retained-objects.d.ts +1 -0
  18. package/dist/pdf-ast/cos/retained-writer.d.ts +1 -0
  19. package/dist/pdf-ast/cos/value-parser.d.ts +9 -0
  20. package/dist/pdf-ast/edit/retained-append-attachments.d.ts +13 -0
  21. package/dist/pdf-ast/edit/retained-bookmarks.d.ts +13 -0
  22. package/dist/pdf-ast/edit/retained-graph.d.ts +6 -0
  23. package/dist/pdf-ast/edit/retained-save.d.ts +2 -0
  24. package/dist/pdf-ast/edit/retained-stamps.d.ts +9 -0
  25. package/dist/pdf-ast/fonts/resolve.d.ts +3 -1
  26. package/dist/pdf-ast/index.d.ts +3 -1
  27. package/dist/pdf-ast/index.js +1557 -770
  28. package/dist/safe-bash/audio-ast.browser.js +6 -1
  29. package/dist/safe-bash/chunks/{chunk-HHSOUC3Z.js → chunk-HPQE675Q.js} +1 -1
  30. package/dist/safe-bash/chunks/{chunk-BUYRKFW2.js → chunk-I6ECNJMR.js} +1 -1
  31. package/dist/safe-bash/chunks/{pdftk-WRYEP3CA.js → pdftk-Y2TX74LY.js} +2 -1
  32. package/dist/safe-bash/command-chunks/biff-4ALVARRF-7SRX6R6N.js +5192 -0
  33. package/dist/safe-bash/command-chunks/{chunk-EHSM5ZQS.js → chunk-2DGJOOXM.js} +8 -8
  34. package/dist/safe-bash/command-chunks/{chunk-Q6S57NOQ.js → chunk-2E2H7MBH.js} +7 -7
  35. package/dist/safe-bash/command-chunks/{chunk-MMBZTYXO.js → chunk-2PQRZDNR.js} +221 -0
  36. package/dist/safe-bash/command-chunks/{chunk-NARD63DY.js → chunk-2V6FOLXV.js} +22 -66
  37. package/dist/safe-bash/command-chunks/{chunk-56NYSX65.js → chunk-2Z3WRRFT.js} +9 -9
  38. package/dist/safe-bash/command-chunks/chunk-3D24WDND.js +63 -0
  39. package/dist/safe-bash/command-chunks/{chunk-47NEG4UO.js → chunk-3JST2Q7G.js} +219 -43
  40. package/dist/safe-bash/command-chunks/{chunk-WGG6G2KG.js → chunk-3LJAXFH5.js} +7 -7
  41. package/dist/safe-bash/command-chunks/{chunk-AHA6IWAJ.js → chunk-3TYDSGAH.js} +6 -6
  42. package/dist/safe-bash/command-chunks/{chunk-YSMNXMAO.js → chunk-3Y4XZB24.js} +4 -4
  43. package/dist/safe-bash/command-chunks/{chunk-ZIIO6GRY.js → chunk-4PBNMKAT.js} +7 -7
  44. package/dist/safe-bash/command-chunks/{chunk-JFYWVQ4V.js → chunk-4Z6W5EOC.js} +8 -8
  45. package/dist/safe-bash/command-chunks/{chunk-GEM47B2P.js → chunk-57DDYKE5.js} +7 -7
  46. package/dist/safe-bash/command-chunks/{chunk-4CKFNXRN.js → chunk-5IFRAI7Y.js} +1 -1
  47. package/dist/safe-bash/command-chunks/{chunk-RHYJVTJO.js → chunk-5KGEZ3RW.js} +2 -2
  48. package/dist/safe-bash/command-chunks/{chunk-A5FZBR7X.js → chunk-5MFDRSGG.js} +4 -4
  49. package/dist/safe-bash/command-chunks/{chunk-7GKKMNL6.js → chunk-5P6I2T63.js} +8 -8
  50. package/dist/safe-bash/command-chunks/{chunk-6M6TM5Z5.js → chunk-5VQVZPP3.js} +3 -3
  51. package/dist/safe-bash/command-chunks/{chunk-MIRL7HTF.js → chunk-6244HYJY.js} +1 -1
  52. package/dist/safe-bash/command-chunks/chunk-63EMPQ7Z.js +115 -0
  53. package/dist/safe-bash/command-chunks/{chunk-XRVKWGVQ.js → chunk-6CY5BHBS.js} +6 -6
  54. package/dist/safe-bash/command-chunks/{chunk-XVP6V5K3.js → chunk-6IWE7PTK.js} +6 -6
  55. package/dist/safe-bash/command-chunks/{chunk-6QBMND4A.js → chunk-6SRHWVEB.js} +1 -1
  56. package/dist/safe-bash/command-chunks/chunk-6ZHQB453.js +35 -0
  57. package/dist/safe-bash/command-chunks/chunk-73NYFNAS.js +173 -0
  58. package/dist/safe-bash/command-chunks/{chunk-2JSDKQ4J.js → chunk-7NXTEWPR.js} +119 -44
  59. package/dist/safe-bash/command-chunks/{chunk-554Z6IKP.js → chunk-7PRL6RJ6.js} +2 -2
  60. package/dist/safe-bash/command-chunks/{chunk-SDTPO2VV.js → chunk-7PZHWMK6.js} +7 -7
  61. package/dist/safe-bash/command-chunks/{chunk-XZ2FWFNV.js → chunk-7QZ2HP2F.js} +6 -6
  62. package/dist/safe-bash/command-chunks/{chunk-OEJM666F.js → chunk-7UC2Q6Z6.js} +5 -5
  63. package/dist/safe-bash/command-chunks/{chunk-COKYZUBW.js → chunk-AJI55RS7.js} +1 -1
  64. package/dist/safe-bash/command-chunks/{chunk-WXRYDALR.js → chunk-BBAB7LC4.js} +4 -4
  65. package/dist/safe-bash/command-chunks/{chunk-QYMEASQ6.js → chunk-BEHUSUOY.js} +4 -4
  66. package/dist/safe-bash/command-chunks/{chunk-Z2JW6M2D.js → chunk-BK7ISUJL.js} +3 -3
  67. package/dist/safe-bash/command-chunks/{chunk-33YCKZZX.js → chunk-C5KLFZGT.js} +6 -6
  68. package/dist/safe-bash/command-chunks/{chunk-TVH4JORU.js → chunk-C5QDEB3I.js} +3 -3
  69. package/dist/safe-bash/command-chunks/{chunk-N4R2SHSS.js → chunk-CCZZVCHX.js} +2 -2
  70. package/dist/safe-bash/command-chunks/{chunk-2VLAXKTS.js → chunk-CTDC4LJT.js} +121 -85
  71. package/dist/safe-bash/command-chunks/{chunk-S6R3IB36.js → chunk-D55REQA3.js} +1 -1
  72. package/dist/safe-bash/command-chunks/{chunk-Y4EYCZFT.js → chunk-D6WJY6QO.js} +10 -10
  73. package/dist/safe-bash/command-chunks/{chunk-2CORNDGF.js → chunk-DBLHCJJU.js} +2 -2
  74. package/dist/safe-bash/command-chunks/{chunk-SPW4GW4Z.js → chunk-DFI7QF3M.js} +2 -2
  75. package/dist/safe-bash/command-chunks/{chunk-456PHA3L.js → chunk-DMCI6T6N.js} +1 -1
  76. package/dist/safe-bash/command-chunks/{chunk-TW73OXPR.js → chunk-EJTNVXWF.js} +6 -6
  77. package/dist/safe-bash/command-chunks/{chunk-QD7ZRDUN.js → chunk-ET2VPDUA.js} +1 -1
  78. package/dist/safe-bash/command-chunks/{chunk-CJQSAATC.js → chunk-EXOQGXEU.js} +26 -26
  79. package/dist/safe-bash/command-chunks/{chunk-CRV2Q3PB.js → chunk-EZ4NQIPL.js} +1 -1
  80. package/dist/safe-bash/command-chunks/chunk-FWKLBDMK.js +0 -0
  81. package/dist/safe-bash/command-chunks/{chunk-22MOB45U.js → chunk-FWPVQGYK.js} +6 -6
  82. package/dist/safe-bash/command-chunks/{chunk-QC5RMGSA.js → chunk-G3RUXKVY.js} +3 -3
  83. package/dist/safe-bash/command-chunks/{chunk-JYUJAF4L.js → chunk-G7NENPYV.js} +4 -0
  84. package/dist/safe-bash/command-chunks/{chunk-KCOJA7EM.js → chunk-GKILM4HZ.js} +7 -7
  85. package/dist/safe-bash/command-chunks/{chunk-HTVIYLVB.js → chunk-GNXTMSYN.js} +4 -4
  86. package/dist/safe-bash/command-chunks/{chunk-D2ASPM7R.js → chunk-GOBUDKZ3.js} +3 -3
  87. package/dist/safe-bash/command-chunks/{chunk-RNSKXOG3.js → chunk-GVWVRPG6.js} +6 -6
  88. package/dist/safe-bash/command-chunks/{chunk-FDCIPJBQ.js → chunk-HCKYUQ3L.js} +7 -7
  89. package/dist/safe-bash/command-chunks/{chunk-AGLK7OJR.js → chunk-HDSIIQBS.js} +1 -1
  90. package/dist/safe-bash/command-chunks/{chunk-DM2CPMB4.js → chunk-HKOMXGJS.js} +2 -2
  91. package/dist/safe-bash/command-chunks/{chunk-IT7TAC3F.js → chunk-HVW53ARH.js} +16 -16
  92. package/dist/safe-bash/command-chunks/{chunk-M7QE2MN7.js → chunk-I4GMCWAT.js} +6 -6
  93. package/dist/safe-bash/command-chunks/{chunk-HWQ2UHMT.js → chunk-IHSOOSNC.js} +9 -9
  94. package/dist/safe-bash/command-chunks/{chunk-LCZDBW55.js → chunk-JAHJWVGF.js} +5 -5
  95. package/dist/safe-bash/command-chunks/{chunk-MOZD7RUY.js → chunk-JORIVUPM.js} +27 -11
  96. package/dist/safe-bash/command-chunks/{chunk-PMAM4N33.js → chunk-JUJHYPYL.js} +9 -9
  97. package/dist/safe-bash/command-chunks/{chunk-ENT3IV2G.js → chunk-KBUBSMCQ.js} +12 -12
  98. package/dist/safe-bash/command-chunks/{chunk-ZXR6LXV5.js → chunk-KCE5D4Z4.js} +48 -4
  99. package/dist/safe-bash/command-chunks/{chunk-FIIAYCWS.js → chunk-KZ5ODST6.js} +2 -2
  100. package/dist/safe-bash/command-chunks/{chunk-PNGDJA6M.js → chunk-LD7BONZY.js} +2 -2
  101. package/dist/safe-bash/command-chunks/{chunk-KJ4XXZIL.js → chunk-M3Z5PU2M.js} +582 -59
  102. package/dist/safe-bash/command-chunks/{chunk-LXLLA4PM.js → chunk-MKX4JAZ6.js} +17 -17
  103. package/dist/safe-bash/command-chunks/{chunk-RQIQ75W5.js → chunk-MNHBQBF6.js} +5 -5
  104. package/dist/safe-bash/command-chunks/{chunk-A2CLZMQZ.js → chunk-MPKY37QR.js} +1 -1
  105. package/dist/safe-bash/command-chunks/{chunk-K5GB2YMN.js → chunk-MUUWBZIC.js} +10 -10
  106. package/dist/safe-bash/command-chunks/{chunk-JOCFI7TG.js → chunk-N32GSM3J.js} +1 -1
  107. package/dist/safe-bash/command-chunks/{chunk-EUT24Y5Z.js → chunk-NKTULXVD.js} +8 -8
  108. package/dist/safe-bash/command-chunks/{chunk-5HPU7D36.js → chunk-NMPED6L5.js} +4 -4
  109. package/dist/safe-bash/command-chunks/{chunk-QZZ73J2O.js → chunk-NPFHUS4X.js} +6 -6
  110. package/dist/safe-bash/command-chunks/{chunk-X7FA7ZD5.js → chunk-O675URF4.js} +8 -8
  111. package/dist/safe-bash/command-chunks/{chunk-R6EGGK5M.js → chunk-P7EL2FWW.js} +7 -7
  112. package/dist/safe-bash/command-chunks/{chunk-YS25EI54.js → chunk-PCCFLPUQ.js} +2 -2
  113. package/dist/safe-bash/command-chunks/{chunk-RRL3Y562.js → chunk-PI6MOMHA.js} +11 -11
  114. package/dist/safe-bash/command-chunks/{chunk-26VCZHLJ.js → chunk-PIOQBOEK.js} +1 -1
  115. package/dist/safe-bash/command-chunks/chunk-PK36TVXB.js +175 -0
  116. package/dist/safe-bash/command-chunks/{chunk-3Q247UDJ.js → chunk-PKLJRB7W.js} +264 -129
  117. package/dist/safe-bash/command-chunks/{chunk-6CCQI6XZ.js → chunk-PSCOXGCA.js} +12 -12
  118. package/dist/safe-bash/command-chunks/{chunk-XPD636C4.js → chunk-PVGZMLC6.js} +3 -3
  119. package/dist/safe-bash/command-chunks/{chunk-JQVHUSDD.js → chunk-PXWS3Q7L.js} +5 -5
  120. package/dist/safe-bash/command-chunks/{chunk-HDJVJTCK.js → chunk-QFF5KBOM.js} +9 -9
  121. package/dist/safe-bash/command-chunks/{chunk-MPUHH2KR.js → chunk-QM2HZJSJ.js} +3 -3
  122. package/dist/safe-bash/command-chunks/{chunk-UAXSE3XE.js → chunk-R2BMZAHG.js} +5 -5
  123. package/dist/safe-bash/command-chunks/{chunk-KB7DUBVL.js → chunk-RGOQKIG6.js} +5 -5
  124. package/dist/safe-bash/command-chunks/{chunk-D2KZ66W4.js → chunk-RLCQ7BN3.js} +187 -39
  125. package/dist/safe-bash/command-chunks/{chunk-B5LDP7AA.js → chunk-S2N2V5U4.js} +2 -2
  126. package/dist/safe-bash/command-chunks/{chunk-OA75SGN2.js → chunk-SGPCXAG7.js} +7 -7
  127. package/dist/safe-bash/command-chunks/{chunk-QUM3SYAJ.js → chunk-SYSX46IV.js} +3 -3
  128. package/dist/safe-bash/command-chunks/chunk-T3E5RV3U.js +737 -0
  129. package/dist/safe-bash/command-chunks/{chunk-YORJDX4V.js → chunk-T5ERNELL.js} +4 -4
  130. package/dist/safe-bash/command-chunks/{chunk-ZEX53S5I.js → chunk-T6C5QP7T.js} +6368 -6230
  131. package/dist/safe-bash/command-chunks/{chunk-ATTWQVR2.js → chunk-TKRLZIFI.js} +17 -17
  132. package/dist/safe-bash/command-chunks/{chunk-ZMHEGDU2.js → chunk-TODWFDYB.js} +2 -2
  133. package/dist/safe-bash/command-chunks/{chunk-XVLWZTPY.js → chunk-TQEB4DH4.js} +543 -144
  134. package/dist/safe-bash/command-chunks/{chunk-36XGIPOG.js → chunk-U5TWQFZY.js} +1 -1
  135. package/dist/safe-bash/command-chunks/{chunk-APEWTWCC.js → chunk-UAF4IX5H.js} +6 -6
  136. package/dist/safe-bash/command-chunks/{chunk-6VPQ3CDZ.js → chunk-V37D6AHO.js} +5 -5
  137. package/dist/safe-bash/command-chunks/{chunk-RID6VYS3.js → chunk-V6XBEYPC.js} +8 -8
  138. package/dist/safe-bash/command-chunks/{chunk-WJCN2IXI.js → chunk-VQOINPVF.js} +2 -2
  139. package/dist/safe-bash/command-chunks/{chunk-W5YSINXC.js → chunk-VVWXY6IA.js} +6 -6
  140. package/dist/safe-bash/command-chunks/{chunk-HKATEVAP.js → chunk-WPVBUVFK.js} +6 -6
  141. package/dist/safe-bash/command-chunks/{chunk-UYWSIIUX.js → chunk-X53AEG36.js} +7 -7
  142. package/dist/safe-bash/command-chunks/{chunk-L5MCW6CM.js → chunk-X64XGIXZ.js} +9 -155
  143. package/dist/safe-bash/command-chunks/{chunk-Q73EMLLZ.js → chunk-X6RHJ6YN.js} +84 -5
  144. package/dist/safe-bash/command-chunks/{chunk-7PVHO47B.js → chunk-XG6UWQV5.js} +6 -6
  145. package/dist/safe-bash/command-chunks/{chunk-NQDIT667.js → chunk-XIDGHIGB.js} +5 -5
  146. package/dist/safe-bash/command-chunks/{chunk-CV7M2QJU.js → chunk-XLYIVBAR.js} +7 -7
  147. package/dist/safe-bash/command-chunks/{chunk-WVMHRNXZ.js → chunk-YUXTK3XA.js} +3 -3
  148. package/dist/safe-bash/command-chunks/{chunk-SPLOIABU.js → chunk-YXD6VGVG.js} +3 -3
  149. package/dist/safe-bash/command-chunks/{chunk-2ZFLMWOX.js → chunk-Z5BIM5B7.js} +1 -1
  150. package/dist/safe-bash/command-chunks/chunk-ZYLGNIGA.js +156 -0
  151. package/dist/safe-bash/command-chunks/{command-engine-BCUQGJJ4.js → command-engine-X3OLN7N6.js} +150 -67
  152. package/dist/safe-bash/command-chunks/{engine-LAUEWYSQ-DQCEI5HE.js → engine-XUCIFZ7V-LJFGZM6O.js} +1 -1
  153. package/dist/safe-bash/command-chunks/paradox-4FCZE53D-LQPGVMEL.js +365 -0
  154. package/dist/safe-bash/commands/pandoc/index.browser.js +1 -1
  155. package/dist/safe-bash/full-core.browser.js +2 -2
  156. package/dist/safe-bash/plugins/index.browser.js +2 -2
  157. package/dist/safe-bash-command-diff/diff.js +1 -1
  158. package/dist/safe-bash-command-diff/index.js +1 -1
  159. package/dist/safe-bash-command-ffmpeg/index.js +1 -1
  160. package/dist/safe-bash-command-ffprobe/index.js +2 -2
  161. package/dist/safe-bash-command-ffprobe/media.d.ts +3 -1
  162. package/dist/safe-bash-command-ffprobe/media.js +2 -2
  163. package/dist/safe-bash-command-html-to-markdown/budget.js +2 -1
  164. package/dist/safe-bash-command-html-to-markdown/entities.js +4 -3
  165. package/dist/safe-bash-command-html-to-markdown/index.js +564 -110
  166. package/dist/safe-bash-command-html-to-markdown/input.js +6 -5
  167. package/dist/safe-bash-command-html-to-markdown/options.js +2 -1
  168. package/dist/safe-bash-command-html-to-markdown/parser.js +5 -4
  169. package/dist/safe-bash-command-html-to-markdown/render.js +6 -5
  170. package/dist/safe-bash-command-html-to-markdown/text.js +3 -2
  171. package/dist/safe-bash-command-htmlq/index.js +361 -37
  172. package/dist/safe-bash-command-htmlq/tree.d.ts +3 -2
  173. package/dist/safe-bash-command-pandoc/command.js +7 -7
  174. package/dist/safe-bash-command-pandoc/index.js +55 -55
  175. package/dist/safe-bash-command-patch/index.js +6 -6
  176. package/dist/safe-bash-command-patch/patch-envelope.js +53 -3
  177. package/dist/safe-bash-command-patch/patch-formats.d.ts +2 -2
  178. package/dist/safe-bash-command-patch/patch-formats.js +2 -2
  179. package/dist/safe-bash-command-patch/patch-gnu-reject.d.ts +2 -1
  180. package/dist/safe-bash-command-patch/patch-gnu-reject.js +4 -3
  181. package/dist/safe-bash-command-patch/patch-publication.js +2 -1
  182. package/dist/safe-bash-command-patch/patch.js +6 -6
  183. package/dist/safe-bash-command-patch/unified.d.ts +10 -2
  184. package/dist/safe-bash-command-patch/unified.js +1 -1
  185. package/dist/safe-bash-command-pdftk/index.d.ts +6 -2
  186. package/dist/safe-bash-command-pdftk/index.js +830 -271
  187. package/dist/safe-bash-command-pptx/command-engine.d.ts +3 -0
  188. package/dist/safe-bash-command-pptx/engine.js +1 -1
  189. package/dist/safe-bash-command-pptx/index.js +9 -2
  190. package/dist/safe-bash-command-soffice/index.js +1 -1
  191. package/dist/safe-bash-command-ssconvert/index.js +2 -2
  192. package/dist/safe-bash-command-xmllint/index.js +22 -31
  193. package/dist/safe-bash-presentation-engine/accessibility.js +31 -30
  194. package/dist/safe-bash-presentation-engine/animation-editing.js +16 -15
  195. package/dist/safe-bash-presentation-engine/animations.js +16 -15
  196. package/dist/safe-bash-presentation-engine/backgrounds.js +17 -16
  197. package/dist/safe-bash-presentation-engine/bytes.js +3 -3
  198. package/dist/safe-bash-presentation-engine/chart-editing.js +34 -33
  199. package/dist/safe-bash-presentation-engine/chart-object-operations.js +25 -24
  200. package/dist/safe-bash-presentation-engine/charts.js +31 -30
  201. package/dist/safe-bash-presentation-engine/command-placeholder.js +59 -56
  202. package/dist/safe-bash-presentation-engine/comments.js +17 -16
  203. package/dist/safe-bash-presentation-engine/connector-operations.js +32 -31
  204. package/dist/safe-bash-presentation-engine/connectors.js +29 -28
  205. package/dist/safe-bash-presentation-engine/content-types.js +4 -4
  206. package/dist/safe-bash-presentation-engine/creation.js +6 -6
  207. package/dist/safe-bash-presentation-engine/diff.js +32 -31
  208. package/dist/safe-bash-presentation-engine/drawing-enums.d.ts +10 -10
  209. package/dist/safe-bash-presentation-engine/drawing-format.js +20 -19
  210. package/dist/safe-bash-presentation-engine/drawing-operations.js +31 -30
  211. package/dist/safe-bash-presentation-engine/drawing-schema.js +21 -20
  212. package/dist/safe-bash-presentation-engine/equations.js +17 -16
  213. package/dist/safe-bash-presentation-engine/errors.js +2 -2
  214. package/dist/safe-bash-presentation-engine/fields.js +17 -16
  215. package/dist/safe-bash-presentation-engine/font-metrics.js +3 -3
  216. package/dist/safe-bash-presentation-engine/image-extraction.js +20 -19
  217. package/dist/safe-bash-presentation-engine/image-formatting.js +32 -31
  218. package/dist/safe-bash-presentation-engine/image-insertion.js +18 -17
  219. package/dist/safe-bash-presentation-engine/image-replacement.js +21 -20
  220. package/dist/safe-bash-presentation-engine/image-value.js +4 -4
  221. package/dist/safe-bash-presentation-engine/images.js +19 -18
  222. package/dist/safe-bash-presentation-engine/index.d.ts +5 -1
  223. package/dist/safe-bash-presentation-engine/index.js +109 -96
  224. package/dist/safe-bash-presentation-engine/layouts.js +16 -15
  225. package/dist/safe-bash-presentation-engine/length.js +3 -3
  226. package/dist/safe-bash-presentation-engine/links.js +16 -15
  227. package/dist/safe-bash-presentation-engine/masters.js +15 -14
  228. package/dist/safe-bash-presentation-engine/media-editing.js +24 -23
  229. package/dist/safe-bash-presentation-engine/media-extraction.js +17 -16
  230. package/dist/safe-bash-presentation-engine/media.js +16 -15
  231. package/dist/safe-bash-presentation-engine/memberships.js +15 -14
  232. package/dist/safe-bash-presentation-engine/notes.js +16 -15
  233. package/dist/safe-bash-presentation-engine/ole-enum.js +4 -4
  234. package/dist/safe-bash-presentation-engine/ole-insertion.js +20 -19
  235. package/dist/safe-bash-presentation-engine/opaque-objects.js +9 -9
  236. package/dist/safe-bash-presentation-engine/package-reader.js +5 -5
  237. package/dist/safe-bash-presentation-engine/package-tools.d.ts +3 -0
  238. package/dist/safe-bash-presentation-engine/package-tools.js +27 -17
  239. package/dist/safe-bash-presentation-engine/package-uri.js +3 -3
  240. package/dist/safe-bash-presentation-engine/package-writer.js +4 -4
  241. package/dist/safe-bash-presentation-engine/presentation-model.js +58 -55
  242. package/dist/safe-bash-presentation-engine/presentation-settings.js +14 -13
  243. package/dist/safe-bash-presentation-engine/properties.js +16 -15
  244. package/dist/safe-bash-presentation-engine/relationships.js +4 -4
  245. package/dist/safe-bash-presentation-engine/resource-limits.js +1 -1
  246. package/dist/safe-bash-presentation-engine/retained-archive-staging.d.ts +19 -0
  247. package/dist/safe-bash-presentation-engine/retained-extraction-output.d.ts +18 -0
  248. package/dist/safe-bash-presentation-engine/retained-inspection.js +14 -12
  249. package/dist/safe-bash-presentation-engine/retained-package-extraction.d.ts +22 -0
  250. package/dist/safe-bash-presentation-engine/retained-package-packing.d.ts +19 -0
  251. package/dist/safe-bash-presentation-engine/retained-package.d.ts +2 -0
  252. package/dist/safe-bash-presentation-engine/retained-package.js +6 -4
  253. package/dist/safe-bash-presentation-engine/retained-text.js +19 -17
  254. package/dist/safe-bash-presentation-engine/retained-xml-parts.d.ts +5 -1
  255. package/dist/safe-bash-presentation-engine/retained-xml-publication.d.ts +18 -0
  256. package/dist/safe-bash-presentation-engine/retained-xml-replacement.d.ts +13 -0
  257. package/dist/safe-bash-presentation-engine/sanitize.js +17 -16
  258. package/dist/safe-bash-presentation-engine/selectors.js +10 -10
  259. package/dist/safe-bash-presentation-engine/shape-groups.js +31 -30
  260. package/dist/safe-bash-presentation-engine/shape-operations.js +30 -29
  261. package/dist/safe-bash-presentation-engine/shape-path-operations.js +32 -31
  262. package/dist/safe-bash-presentation-engine/shape-paths.js +16 -15
  263. package/dist/safe-bash-presentation-engine/shape-presets.js +3 -3
  264. package/dist/safe-bash-presentation-engine/shape-selection.js +31 -30
  265. package/dist/safe-bash-presentation-engine/shape-transforms.js +18 -17
  266. package/dist/safe-bash-presentation-engine/shapes.d.ts +1 -1
  267. package/dist/safe-bash-presentation-engine/shapes.js +27 -26
  268. package/dist/safe-bash-presentation-engine/slide-copy.js +17 -16
  269. package/dist/safe-bash-presentation-engine/slide-import.js +18 -17
  270. package/dist/safe-bash-presentation-engine/slide-merge-split.js +20 -19
  271. package/dist/safe-bash-presentation-engine/slide-model.js +43 -42
  272. package/dist/safe-bash-presentation-engine/slide-removal.js +18 -17
  273. package/dist/safe-bash-presentation-engine/slide-transfer-budget.js +9 -9
  274. package/dist/safe-bash-presentation-engine/slides.js +19 -16
  275. package/dist/safe-bash-presentation-engine/table-operations.js +33 -32
  276. package/dist/safe-bash-presentation-engine/table-spans.js +32 -31
  277. package/dist/safe-bash-presentation-engine/tables.js +31 -30
  278. package/dist/safe-bash-presentation-engine/tags.js +16 -15
  279. package/dist/safe-bash-presentation-engine/template-bindings.js +33 -32
  280. package/dist/safe-bash-presentation-engine/template-repeat.js +40 -39
  281. package/dist/safe-bash-presentation-engine/text-fitting.js +27 -26
  282. package/dist/safe-bash-presentation-engine/text-frames.js +27 -26
  283. package/dist/safe-bash-presentation-engine/text-paragraphs.js +27 -26
  284. package/dist/safe-bash-presentation-engine/text-reading.js +11 -11
  285. package/dist/safe-bash-presentation-engine/text-replacement.js +17 -16
  286. package/dist/safe-bash-presentation-engine/text-runs.js +20 -19
  287. package/dist/safe-bash-presentation-engine/themes.js +16 -15
  288. package/dist/safe-bash-presentation-engine/transitions.js +16 -15
  289. package/dist/safe-bash-presentation-engine/validation.js +10 -9
  290. package/dist/safe-bash-presentation-engine/xml-parts.d.ts +2 -0
  291. package/dist/safe-bash-presentation-engine/xml-parts.js +23 -15
  292. package/dist/safe-bash-presentation-engine/xml-view.js +3 -3
  293. package/dist/safe-bash-presentation-engine/xml.js +3 -3
  294. package/dist/safe-bash-regex-engine/index.js +1 -1
  295. package/dist/safe-bash-regex-engine/python-glob.js +1 -1
  296. package/dist/safe-bash-regex-engine/text/regex.d.ts +19 -0
  297. package/dist/safe-bash-regex-engine/text/regex.js +1 -1
  298. package/dist/safe-bash-xml-engine/document.js +3 -3
  299. package/dist/safe-bash-xml-engine/evaluate.js +1 -1
  300. package/dist/safe-bash-xml-engine/index.d.ts +1 -0
  301. package/dist/safe-bash-xml-engine/index.js +8 -4
  302. package/dist/safe-bash-xml-engine/query.js +2 -2
  303. package/dist/safe-bash-xml-engine/recovery.d.ts +10 -0
  304. package/dist/safe-bash-xml-engine/stored-document.d.ts +1 -1
  305. package/dist/safe-bash-xml-engine/stored-document.js +1 -1
  306. package/dist/safe-bash-xml-engine/stored-evaluate.js +1 -1
  307. package/dist/spreadsheet-engine/formulas/functions/python-printf.js +56 -33
  308. package/dist/spreadsheet-format-xls/biff-encrypted-properties.d.ts +1 -1
  309. package/dist/spreadsheet-format-xls/biff-encrypted-properties.js +47 -18
  310. package/dist/spreadsheet-format-xls/biff-properties-merge.d.ts +6 -2
  311. package/dist/spreadsheet-format-xls/biff-properties-merge.js +413 -296
  312. package/dist/spreadsheet-format-xls/biff-properties-write.js +40 -23
  313. package/dist/spreadsheet-format-xls/biff-properties.js +3 -68
  314. package/dist/spreadsheet-format-xls/biff-property-index.d.ts +23 -0
  315. package/dist/spreadsheet-format-xls/biff-property-index.js +93 -0
  316. package/dist/spreadsheet-format-xls/biff-property-name.d.ts +4 -0
  317. package/dist/spreadsheet-format-xls/biff-property-name.js +114 -0
  318. package/dist/spreadsheet-format-xls/biff-property-text.d.ts +6 -0
  319. package/dist/spreadsheet-format-xls/biff-property-text.js +68 -0
  320. package/dist/spreadsheet-format-xls/biff-property-transcode.d.ts +5 -0
  321. package/dist/spreadsheet-format-xls/biff-property-transcode.js +45 -0
  322. package/package.json +6 -3
  323. package/dist/safe-bash/command-chunks/biff-YKPDH35C-MVQIWNKV.js +0 -4974
  324. package/dist/safe-bash/command-chunks/chunk-5Q22ZLNC.js +0 -127
  325. package/dist/safe-bash/command-chunks/chunk-JKLOBSMP.js +0 -56
  326. package/dist/safe-bash/command-chunks/chunk-JYTESTBS.js +0 -99
  327. package/dist/safe-bash/command-chunks/chunk-ZEGNRZV2.js +0 -333
  328. package/dist/safe-bash/command-chunks/paradox-ZWWXYY4J-JYOSWSBL.js +0 -362
  329. package/dist/spreadsheet-format-xls/biff-properties-layout.js +0 -56
@@ -74,6 +74,92 @@ function duration(samples, sampleRate) {
74
74
  return samples / sampleRate;
75
75
  }
76
76
 
77
+ // packages/audio-ast/src/ogg-stream.ts
78
+ function oggIdentification(head, size = head.length) {
79
+ const r = new Reader(head);
80
+ if (head.length >= 8 && r.text(0, 8) === "OpusHead") {
81
+ if (size < 19 || r.u8(8) > 15) throw new Error("Invalid Opus identification");
82
+ const channels = r.u8(9), preSkip = r.u16(10, true), mapping = r.u8(18);
83
+ if (mapping === 0 && channels > 2) throw new Error("Invalid Opus channels");
84
+ if (mapping !== 0) r.check(19, 2 + channels);
85
+ return { codec: "opus", sampleRate: 48e3, channels, preSkip };
86
+ }
87
+ if (head.length >= 7 && r.u8(0) === 1 && r.text(1, 6) === "vorbis") {
88
+ if (size !== 30 || r.u32(7, true) !== 0 || !(r.u8(29) & 1)) throw new Error("Invalid Vorbis identification");
89
+ return { codec: "vorbis", sampleRate: r.u32(12, true), channels: r.u8(11), preSkip: 0 };
90
+ }
91
+ throw new Error("Unsupported Ogg codec");
92
+ }
93
+ function oggStream(identification, final, size) {
94
+ const { codec, sampleRate, channels, preSkip } = identification;
95
+ if (!channels || !sampleRate) throw new Error("Invalid Ogg audio stream");
96
+ if (final > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("Ogg granule exceeds safe precision");
97
+ const samples = Math.max(0, Number(final) - preSkip), duration2 = samples / sampleRate;
98
+ return { codec, sampleRate, channels, samples, duration: duration2, bitrate: duration2 ? size * 8 / duration2 : 0 };
99
+ }
100
+
101
+ // packages/audio-ast/src/ogg-stream-source.ts
102
+ async function probeOggStreamSource(input, options = {}) {
103
+ const check = (source, offset2, length) => {
104
+ options.signal?.throwIfAborted();
105
+ if (!Number.isSafeInteger(source.size) || source.size < 0 || offset2 < 0 || length < 0 || length > source.size - offset2)
106
+ throw new Error("Truncated or invalid audio structure");
107
+ };
108
+ const read = async (source, offset2, length) => {
109
+ check(source, offset2, length);
110
+ const bytes = new Uint8Array(length);
111
+ for (let at = 0; at < length; ) {
112
+ const part = await source.read(offset2 + at, length - at);
113
+ options.signal?.throwIfAborted();
114
+ if (!part.length || part.length > length - at) throw new Error("Truncated or invalid audio structure");
115
+ bytes.set(part, at);
116
+ at += part.length;
117
+ }
118
+ await options.checkpoint?.();
119
+ options.signal?.throwIfAborted();
120
+ return new Reader(bytes);
121
+ };
122
+ options.signal?.throwIfAborted();
123
+ if (!Number.isSafeInteger(input.size) || input.size < 0 || input.granule < 0n) throw new RangeError("Invalid Ogg stream size or granule");
124
+ check(input.head, 0, 0);
125
+ const identification = oggIdentification((await read(input.head, 0, Math.min(input.head.size, 276))).bytes, input.head.size);
126
+ const { comments } = input, opus = identification.codec === "opus", prefix = opus ? 8 : 7;
127
+ const missing = opus ? "Missing OpusTags" : "Missing Vorbis comments";
128
+ if (!comments) throw new Error(missing);
129
+ const marker = await read(comments, 0, prefix);
130
+ if (opus ? marker.text(0, 8) !== "OpusTags" : marker.u8(0) !== 3 || marker.text(1, 6) !== "vorbis") throw new Error(missing);
131
+ const vendorLength = (await read(comments, prefix, 4)).u32(0, true);
132
+ check(comments, prefix + 4, vendorLength);
133
+ let offset = prefix + 4 + vendorLength;
134
+ const count = (await read(comments, offset, 4)).u32(0, true);
135
+ offset += 4;
136
+ if (count > (comments.size - offset) / 4) throw new Error("Invalid Vorbis comment count");
137
+ const tags = {};
138
+ for (let i = 0; i < count; i++) {
139
+ const length = (await read(comments, offset, 4)).u32(0, true);
140
+ offset += 4;
141
+ check(comments, offset, length);
142
+ if (options.onComment) {
143
+ await options.onComment({ offset, length });
144
+ options.signal?.throwIfAborted();
145
+ } else {
146
+ const decoder = new TextDecoder();
147
+ let text2 = "";
148
+ for (let at = 0; at < length; at += 16384) text2 += decoder.decode((await read(comments, offset + at, Math.min(16384, length - at))).bytes, { stream: true });
149
+ text2 += decoder.decode();
150
+ const split = text2.indexOf("=");
151
+ if (split > 0) {
152
+ const key = text2.slice(0, split), value = text2.slice(split + 1);
153
+ tags[key] = tags[key] ? `${tags[key]};${value}` : value;
154
+ }
155
+ }
156
+ offset += length;
157
+ }
158
+ if (!opus && (offset === comments.size || !((await read(comments, offset, 1)).u8(0) & 1)))
159
+ throw new Error("Missing Vorbis comment framing bit");
160
+ return { tags, ...oggStream(identification, input.granule, input.size) };
161
+ }
162
+
77
163
  // packages/audio-ast/src/vorbis.ts
78
164
  var names = {
79
165
  TITLE: "title",
@@ -168,94 +254,872 @@ function normalizeVorbisCommentKey(value) {
168
254
  return names[key] ?? key;
169
255
  }
170
256
 
171
- // packages/audio-ast/src/flac.ts
172
- function parseFlac(bytes) {
173
- const r = new Reader(bytes), nodes = [], pictures = [];
174
- const tags = {};
175
- let offset = 4, last = false, stream;
176
- while (!last) {
177
- const header = r.u8(offset), type = header & 127, size = r.u24(offset + 1);
178
- last = !!(header & 128);
179
- const data = r.slice(offset + 4, size), node = {
180
- type: [
181
- "STREAMINFO",
182
- "PADDING",
183
- "APPLICATION",
184
- "SEEKTABLE",
185
- "VORBIS_COMMENT",
186
- "CUESHEET",
187
- "PICTURE"
188
- ][type] ?? `BLOCK_${type}`,
189
- offset,
190
- size: size + 4,
191
- data
192
- };
193
- nodes.push(node);
194
- const block = new Reader(data);
195
- if (type === 0) {
196
- if (stream || offset !== 4 || size !== 34)
197
- throw new Error("FLAC requires first unique STREAMINFO");
198
- stream = flacStream(block, bytes.length);
199
- const { sampleRate, channels, bitsPerSample, samples } = stream;
200
- node.fields = {
201
- minBlockSize: block.u16(0),
202
- maxBlockSize: block.u16(2),
203
- minFrameSize: block.u24(4),
204
- maxFrameSize: block.u24(7),
205
- sampleRate,
206
- channels,
207
- bitsPerSample,
208
- totalSamples: samples,
209
- md5: Array.from(block.slice(18, 16), (b) => b.toString(16).padStart(2, "0")).join("")
210
- };
211
- } else if (type === 4) {
212
- const parsed = parseComments(data);
213
- if (parsed.size !== size) throw new Error("Trailing Vorbis comment bytes");
214
- Object.assign(tags, parsed.tags);
215
- node.fields = { vendor: parsed.vendor, comments: parsed.comments };
216
- } else if (type === 6) pictures.push(parsePicture(data));
217
- else if (type === 3) {
218
- if (size % 18) throw new Error("Invalid FLAC seek table");
219
- const points = [];
220
- for (let pos = 0; pos < size; pos += 18)
221
- points.push({
222
- sample: block.u64(pos),
223
- offset: block.u64(pos + 8),
224
- samples: block.u16(pos + 16)
225
- });
226
- node.fields = { points };
227
- } else if (type === 127) throw new Error("Invalid FLAC metadata block");
228
- offset += 4 + size;
257
+ // packages/audio-ast/src/ogg.ts
258
+ function oggCrc(bytes, crc = 0, pageOffset = 0) {
259
+ for (let i = 0; i < bytes.length; i++) {
260
+ const offset = pageOffset + i;
261
+ crc ^= (offset >= 22 && offset < 26 ? 0 : bytes[i]) << 24;
262
+ for (let bit = 0; bit < 8; bit++) crc = crc << 1 ^ (crc & 2147483648 ? 79764919 : 0);
229
263
  }
230
- if (!stream) throw new Error("Missing FLAC STREAMINFO");
231
- if (offset < bytes.length)
264
+ return crc >>> 0;
265
+ }
266
+ function readOgg(bytes) {
267
+ const r = new Reader(bytes), nodes = [], packets = [], logical = /* @__PURE__ */ new Map();
268
+ for (let offset = 0; offset < bytes.length; ) {
269
+ if (r.text(offset, 4) !== "OggS" || r.u8(offset + 4) !== 0) throw new Error("Invalid Ogg page");
270
+ const flags = r.u8(offset + 5), granule = r.u64(offset + 6, true), serial = r.u32(offset + 14, true), sequence = r.u32(offset + 18, true), count = r.u8(offset + 26), laces = r.slice(offset + 27, count);
271
+ if (flags & ~7) throw new Error("Invalid Ogg page flags");
272
+ const size = 27 + count + laces.reduce((sum, n) => sum + n, 0), page = r.slice(offset, size);
273
+ if (oggCrc(page) !== r.u32(offset + 22, true)) throw new Error("Ogg checksum mismatch");
274
+ let state = logical.get(serial);
275
+ if (!state) {
276
+ if (!(flags & 2) || sequence !== 0) throw new Error("Missing Ogg beginning-of-stream");
277
+ state = { packets: [], pending: [], sequence: -1, ended: false };
278
+ logical.set(serial, state);
279
+ }
280
+ if (state.ended || sequence !== state.sequence + 1 >>> 0 || !!(flags & 1) !== !!state.pending.length)
281
+ throw new Error("Invalid Ogg sequence/continuation");
282
+ state.sequence = sequence;
283
+ const completed = [];
284
+ let pos = offset + 27 + count;
285
+ for (const lace of laces) {
286
+ state.pending.push(r.slice(pos, lace));
287
+ pos += lace;
288
+ if (lace < 255) {
289
+ const packet = {
290
+ data: join(state.pending),
291
+ serial,
292
+ granule: 0xffffffffffffffffn,
293
+ bos: state.packets.length === 0,
294
+ eos: false
295
+ };
296
+ state.pending = [];
297
+ state.packets.push(packet);
298
+ packets.push(packet);
299
+ completed.push(packet);
300
+ }
301
+ }
302
+ if (completed.length) {
303
+ completed[completed.length - 1].granule = granule;
304
+ if (flags & 4) completed[completed.length - 1].eos = true;
305
+ }
306
+ if (flags & 4) {
307
+ if (state.pending.length) throw new Error("Incomplete final Ogg packet");
308
+ state.ended = true;
309
+ }
232
310
  nodes.push({
233
- type: "FRAMES",
311
+ type: "OggS",
234
312
  offset,
235
- size: bytes.length - offset,
236
- data: r.slice(offset, bytes.length - offset)
313
+ size,
314
+ data: page,
315
+ fields: {
316
+ flags,
317
+ granule,
318
+ serial,
319
+ sequence,
320
+ checksum: r.u32(offset + 22, true),
321
+ lacing: laces
322
+ }
237
323
  });
324
+ offset += size;
325
+ }
326
+ if (!logical.size || Array.from(logical.values()).some((s) => s.pending.length || !s.ended))
327
+ throw new Error("Incomplete Ogg stream");
328
+ return { nodes, packets };
329
+ }
330
+ function encodeOgg(packets) {
331
+ const pages = [], sequences = /* @__PURE__ */ new Map(), pending = /* @__PURE__ */ new Map();
332
+ for (const packet of packets) {
333
+ const group = pending.get(packet.serial) ?? [];
334
+ group.push(packet);
335
+ pending.set(packet.serial, group);
336
+ if (packet.granule === 0xffffffffffffffffn && !packet.eos) continue;
337
+ const laces = [], granules = [], boundaries = /* @__PURE__ */ new Set();
338
+ for (const item of group) {
339
+ for (let j = 0; j < Math.floor(item.data.length / 255); j++) {
340
+ laces.push(255);
341
+ granules.push(0xffffffffffffffffn);
342
+ }
343
+ laces.push(item.data.length % 255);
344
+ granules.push(item.granule);
345
+ boundaries.add(laces.length);
346
+ }
347
+ const body = join(group.map((p) => p.data));
348
+ let offset = 0;
349
+ for (let start = 0; start < laces.length; start += 255) {
350
+ const part = laces.slice(start, start + 255), last = start + 255 >= laces.length, size = part.reduce((a, b) => a + b, 0), header = new Uint8Array(27 + part.length), v = new DataView(header.buffer);
351
+ header.set(ascii("OggS"));
352
+ header[5] = (start && !boundaries.has(start) ? 1 : 0) | (group[0].bos && start === 0 ? 2 : 0) | (packet.eos && last ? 4 : 0);
353
+ let granule = 0xffffffffffffffffn;
354
+ for (let i = start; i < start + part.length; i++)
355
+ if (laces[i] < 255) granule = granules[i] === 0xffffffffffffffffn ? 0n : granules[i];
356
+ v.setBigUint64(6, granule, true);
357
+ v.setUint32(14, packet.serial, true);
358
+ const sequence = sequences.get(packet.serial) ?? 0;
359
+ v.setUint32(18, sequence, true);
360
+ sequences.set(packet.serial, sequence + 1);
361
+ header[26] = part.length;
362
+ header.set(part, 27);
363
+ const page = join([header, body.subarray(offset, offset + size)]);
364
+ new DataView(page.buffer).setUint32(22, oggCrc(page), true);
365
+ pages.push(page);
366
+ offset += size;
367
+ }
368
+ pending.delete(packet.serial);
369
+ }
370
+ if (pending.size) throw new Error("Ogg packets lack a final page granule");
371
+ return join(pages);
372
+ }
373
+ function parseOgg(bytes) {
374
+ const { nodes, packets } = readOgg(bytes), streams = [], tags = {}, pictures = [];
375
+ const serials = [...new Set(packets.map((p) => p.serial))];
376
+ for (const serial of serials) {
377
+ const group = packets.filter((p) => p.serial === serial), identification = oggIdentification(group[0].data);
378
+ const comment = group[1]?.data;
379
+ let comments;
380
+ if (identification.codec === "opus") {
381
+ if (!comment || new Reader(comment).text(0, 8) !== "OpusTags") throw new Error("Missing OpusTags");
382
+ const parsed = parseComments(comment.subarray(8));
383
+ Object.assign(tags, parsed.tags);
384
+ comments = parsed.comments;
385
+ } else {
386
+ if (!comment || comment[0] !== 3 || new Reader(comment).text(1, 6) !== "vorbis") throw new Error("Missing Vorbis comments");
387
+ const parsed = parseComments(comment.subarray(7));
388
+ if (!(comment[7 + parsed.size] & 1)) throw new Error("Missing Vorbis comment framing bit");
389
+ Object.assign(tags, parsed.tags);
390
+ comments = parsed.comments;
391
+ }
392
+ const final = group.filter((p) => p.granule !== 0xffffffffffffffffn).at(-1)?.granule ?? 0n;
393
+ const size = nodes.filter((n) => n.fields?.serial === serial).reduce((sum, n) => sum + n.size, 0);
394
+ const streamTags = {};
395
+ for (const comment2 of comments) {
396
+ const split = comment2.indexOf("=");
397
+ if (split <= 0) continue;
398
+ const key = comment2.slice(0, split), value = comment2.slice(split + 1);
399
+ streamTags[key] = streamTags[key] ? `${streamTags[key]};${value}` : value;
400
+ }
401
+ streams.push({ tags: streamTags, ...oggStream(identification, final, size) });
402
+ }
403
+ for (const value of (tags.METADATA_BLOCK_PICTURE ?? "").split("\n").filter(Boolean)) {
404
+ pictures.push(parsePicture(Uint8Array.from(atob(value), (c) => c.charCodeAt(0))));
405
+ }
406
+ let duration2 = 0, groupDuration = 0;
407
+ const active = /* @__PURE__ */ new Set();
408
+ for (const node of nodes) {
409
+ const serial = node.fields.serial, flags = node.fields.flags;
410
+ if (flags & 2) {
411
+ if (!active.size) {
412
+ duration2 += groupDuration;
413
+ groupDuration = 0;
414
+ }
415
+ active.add(serial);
416
+ groupDuration = Math.max(groupDuration, streams[serials.indexOf(serial)].duration);
417
+ }
418
+ if (flags & 4) active.delete(serial);
419
+ }
420
+ duration2 += groupDuration;
238
421
  return {
239
- format: "flac",
422
+ format: "ogg",
240
423
  data: bytes,
241
424
  nodes,
425
+ streams,
242
426
  tags,
243
427
  pictures,
244
- streams: [stream],
245
- duration: stream.duration,
246
- bitrate: stream.bitrate
428
+ duration: duration2,
429
+ bitrate: duration2 ? bytes.length * 8 / duration2 : 0
247
430
  };
248
431
  }
249
- function flacStream(block, byteLength) {
250
- const packed = block.u64(10), sampleRate = Number(packed >> 44n), channels = Number(packed >> 41n & 7n) + 1, bitsPerSample = Number(packed >> 36n & 31n) + 1, samples = Number(packed & 0xfffffffffn), seconds2 = duration(samples, sampleRate);
251
- return { codec: "flac", sampleRate, channels, bitsPerSample, samples, duration: seconds2, bitrate: seconds2 ? byteLength * 8 / seconds2 : 0 };
252
- }
253
432
 
254
- // packages/audio-ast/src/flac-source.ts
255
- async function probeFlacSource(source, options = {}) {
256
- const check = (offset2, length, end = source.size) => {
433
+ // packages/audio-ast/src/ogg-source.ts
434
+ async function* scanOggPages(source, options = {}) {
435
+ options.signal?.throwIfAborted();
436
+ const retained = "size" in source ? source : void 0;
437
+ if (retained && (!Number.isSafeInteger(retained.size) || retained.size < 0)) throw new RangeError("Invalid Ogg source size");
438
+ const iterator = !retained && Symbol.asyncIterator in source ? source[Symbol.asyncIterator]() : void 0;
439
+ let offset = 0, position = 0, done = false;
440
+ let pending = new Uint8Array(0);
441
+ const read = async (length, eof = false) => {
442
+ const out = new Uint8Array(length);
443
+ let used = 0;
444
+ while (used < length) {
445
+ options.signal?.throwIfAborted();
446
+ let chunk;
447
+ if (retained) {
448
+ if (offset === retained.size && eof && used === 0) return out.subarray(0, 0);
449
+ if (length - used > retained.size - offset) throw new Error("Truncated Ogg page");
450
+ chunk = await retained.read(offset, length - used);
451
+ options.signal?.throwIfAborted();
452
+ if (!chunk.length) throw new Error("Truncated Ogg page");
453
+ if (chunk.length > length - used) throw new Error("Ogg source returned more bytes than requested");
454
+ } else {
455
+ while (position === pending.length) {
456
+ const next = await iterator.next();
457
+ options.signal?.throwIfAborted();
458
+ if (next.done) {
459
+ done = true;
460
+ if (eof && used === 0) return out.subarray(0, 0);
461
+ throw new Error("Truncated Ogg page");
462
+ }
463
+ pending = next.value;
464
+ position = 0;
465
+ if (!pending.length) {
466
+ await options.checkpoint?.();
467
+ options.signal?.throwIfAborted();
468
+ }
469
+ }
470
+ const length2 = Math.min(out.length - used, pending.length - position);
471
+ chunk = pending.subarray(position, position + length2);
472
+ position += length2;
473
+ }
474
+ out.set(chunk, used);
475
+ used += chunk.length;
476
+ offset += chunk.length;
477
+ if (!Number.isSafeInteger(offset)) throw new RangeError("Invalid Ogg source size");
478
+ }
479
+ await options.checkpoint?.();
257
480
  options.signal?.throwIfAborted();
258
- if (!Number.isSafeInteger(source.size) || !Number.isSafeInteger(offset2) || !Number.isSafeInteger(length) || offset2 < 0 || length < 0 || length > end - offset2)
481
+ return out;
482
+ };
483
+ let failed = false;
484
+ const retire = async () => {
485
+ if (iterator && !done) {
486
+ try {
487
+ await iterator.return?.();
488
+ } catch (error) {
489
+ if (!failed) throw error;
490
+ }
491
+ }
492
+ };
493
+ try {
494
+ for (; ; ) {
495
+ const start = offset, header = await read(27, true);
496
+ if (!header.length) return;
497
+ const r = new Reader(header);
498
+ if (r.text(0, 4) !== "OggS" || r.u8(4) !== 0) throw new Error("Invalid Ogg page");
499
+ const flags = r.u8(5);
500
+ if (flags & ~7) throw new Error("Invalid Ogg page flags");
501
+ const lacing = await read(r.u8(26)), payloadOffset = offset;
502
+ let crc = oggCrc(header);
503
+ crc = oggCrc(lacing, crc, 27);
504
+ let remaining = 0;
505
+ for (const length of lacing) remaining += length;
506
+ while (remaining) {
507
+ const pageOffset = offset - start, chunk = await read(Math.min(16384, remaining));
508
+ crc = oggCrc(chunk, crc, pageOffset);
509
+ remaining -= chunk.length;
510
+ }
511
+ const checksum = r.u32(22, true);
512
+ if (crc !== checksum) throw new Error("Ogg checksum mismatch");
513
+ yield {
514
+ offset: start,
515
+ size: offset - start,
516
+ payloadOffset,
517
+ flags,
518
+ granule: r.u64(6, true),
519
+ serial: r.u32(14, true),
520
+ sequence: r.u32(18, true),
521
+ checksum,
522
+ lacing
523
+ };
524
+ }
525
+ } catch (error) {
526
+ failed = true;
527
+ throw error;
528
+ } finally {
529
+ await retire();
530
+ }
531
+ }
532
+
533
+ // packages/audio-ast/src/id3.ts
534
+ var names2 = {
535
+ TIT2: "title",
536
+ TPE1: "artist",
537
+ TPE2: "albumArtist",
538
+ TALB: "album",
539
+ TRCK: "track",
540
+ TPOS: "disc",
541
+ TCON: "genre",
542
+ TDRC: "date",
543
+ TYER: "date",
544
+ TCOP: "copyright",
545
+ TSSE: "encoder",
546
+ TT2: "title",
547
+ TP1: "artist",
548
+ TP2: "albumArtist",
549
+ TAL: "album",
550
+ TRK: "track",
551
+ TPA: "disc",
552
+ TCO: "genre",
553
+ TYE: "date",
554
+ TCR: "copyright",
555
+ TEN: "encoder"
556
+ };
557
+ function synchsafe(r, offset) {
558
+ let value = 0;
559
+ for (let i = 0; i < 4; i++) {
560
+ const b = r.u8(offset + i);
561
+ if (b & 128) throw new Error("Invalid ID3 synchsafe size");
562
+ value = value * 128 + b;
563
+ }
564
+ return value;
565
+ }
566
+ function unsync(bytes) {
567
+ const result = [];
568
+ for (let i = 0; i < bytes.length; i++) {
569
+ const b = bytes[i];
570
+ result.push(b);
571
+ if (b === 255 && bytes[i + 1] === 0) i++;
572
+ }
573
+ return Uint8Array.from(result);
574
+ }
575
+ function id3TagName(id) {
576
+ return names2[id] ?? id;
577
+ }
578
+ function id3TextEncoding(encoding, bytes) {
579
+ if (encoding === 0) return "latin1";
580
+ if (encoding === 3) return "utf-8";
581
+ if (encoding === 2) return "utf-16be";
582
+ if (encoding === 1) return bytes[0] === 254 && bytes[1] === 255 ? "utf-16be" : "utf-16le";
583
+ throw new Error("Invalid ID3 text encoding");
584
+ }
585
+ function text(bytes, encoding) {
586
+ return cleanText(new TextDecoder(id3TextEncoding(encoding, bytes)).decode(bytes));
587
+ }
588
+ function terminated(bytes, start, encoding) {
589
+ const width = encoding === 1 || encoding === 2 ? 2 : 1;
590
+ for (let i = start; i + width <= bytes.length; i += width)
591
+ if (bytes[i] === 0 && (width === 1 || bytes[i + 1] === 0)) return i;
592
+ throw new Error("Unterminated ID3 text field");
593
+ }
594
+ function parseId3(bytes) {
595
+ const input = new Reader(bytes), version = input.u8(3), flags = input.u8(5), length = synchsafe(input, 6);
596
+ if (![2, 3, 4].includes(version)) throw new Error("Unsupported ID3 version");
597
+ if (version === 2 && flags & 64) throw new Error("Compressed ID3v2.2 tag unsupported");
598
+ const footer = version === 4 && flags & 16 ? 10 : 0;
599
+ input.check(0, 10 + length + footer);
600
+ const payload = input.slice(10, length), r = new Reader(flags & 128 && version < 4 ? unsync(payload) : payload);
601
+ const nodes = [], tags = {}, pictures = [];
602
+ let pos = 0;
603
+ if (version >= 3 && flags & 64) {
604
+ const extended = version === 4 ? synchsafe(r, 0) : r.u32(0) + 4;
605
+ r.check(0, extended);
606
+ if (extended < (version === 3 ? 10 : 6)) throw new Error("Invalid ID3 extended header");
607
+ pos = extended;
608
+ }
609
+ while (pos < r.bytes.length && r.u8(pos) !== 0) {
610
+ const header = version === 2 ? 6 : 10, id = r.text(pos, version === 2 ? 3 : 4), size = version === 2 ? r.u24(pos + 3) : version === 4 ? synchsafe(r, pos + 4) : r.u32(pos + 4);
611
+ const frameFlags = version === 2 ? 0 : r.u16(pos + 8);
612
+ let data = r.slice(pos + header, size);
613
+ if (version === 4 && (flags & 128 || frameFlags & 2)) data = unsync(data);
614
+ const node = {
615
+ type: id,
616
+ offset: 10 + pos,
617
+ size: header + size,
618
+ data,
619
+ fields: { flags: frameFlags, version }
620
+ };
621
+ nodes.push(node);
622
+ const unsupported = version === 3 ? frameFlags & 192 : frameFlags & 12;
623
+ if (!unsupported && data.length) {
624
+ let prefix = 0;
625
+ if (version === 3 && frameFlags & 32) prefix++;
626
+ if (version === 4 && frameFlags & 64) prefix++;
627
+ if (version === 4 && frameFlags & 1) prefix += 4;
628
+ data = data.subarray(prefix);
629
+ if (id.startsWith("T") && id !== "TXXX" && id !== "TXX")
630
+ tags[id3TagName(id)] = text(data.subarray(1), data[0]);
631
+ else if (id === "COMM" || id === "COM") {
632
+ const encoding = data[0], end = terminated(data, 4, encoding), width = encoding === 1 || encoding === 2 ? 2 : 1;
633
+ tags.comment = text(data.subarray(end + width), encoding);
634
+ } else if (id === "APIC" || id === "PIC") {
635
+ const encoding = data[0], mimeEnd = id === "PIC" ? 4 : terminated(data, 1, 0);
636
+ const mime = id === "PIC" ? `image/${new Reader(data).text(1, 3) === "JPG" ? "jpeg" : new Reader(data).text(1, 3).toLowerCase()}` : text(data.subarray(1, mimeEnd), 0);
637
+ const typeOffset = id === "PIC" ? 4 : mimeEnd + 1, descriptionStart = typeOffset + 1, end = terminated(data, descriptionStart, encoding), width = encoding === 1 || encoding === 2 ? 2 : 1;
638
+ pictures.push({
639
+ type: data[typeOffset],
640
+ mime,
641
+ description: text(data.subarray(descriptionStart, end), encoding),
642
+ data: data.subarray(end + width)
643
+ });
644
+ }
645
+ }
646
+ pos += header + size;
647
+ }
648
+ if (footer && input.text(10 + length, 3) !== "3DI") throw new Error("Missing ID3 footer");
649
+ return { size: 10 + length + footer, nodes, tags, pictures, version };
650
+ }
651
+ function syncBytes(value) {
652
+ if (value > 268435455) throw new Error("ID3 tag too large");
653
+ return Uint8Array.from([
654
+ value >>> 21 & 127,
655
+ value >>> 14 & 127,
656
+ value >>> 7 & 127,
657
+ value & 127
658
+ ]);
659
+ }
660
+ function encodeId3(tags, retained = [], pictures = []) {
661
+ const frames = [];
662
+ function frame(id, data) {
663
+ frames.push(join([ascii(id), syncBytes(data.length), new Uint8Array(2), data]));
664
+ }
665
+ for (const node of retained) {
666
+ const flags = Number(node.fields?.flags ?? 0);
667
+ if (flags & (node.fields?.version === 3 ? 192 : 12)) {
668
+ throw new Error(`Cannot safely rewrite transformed ID3 frame ${node.type}`);
669
+ }
670
+ if (node.type === "APIC" || node.type === "PIC" || names2[node.type] || node.type.startsWith("T") && node.type !== "TXXX" && node.type !== "TXX" || node.type === "COMM" || node.type === "COM")
671
+ continue;
672
+ if (node.type.length === 4 && !node.fields?.flags) frame(node.type, node.data);
673
+ else throw new Error(`Cannot safely rewrite ID3 frame ${node.type}`);
674
+ }
675
+ for (const [name, value] of Object.entries(tags)) {
676
+ const id = Object.keys(names2).find((key) => key.length === 4 && names2[key] === name) ?? name;
677
+ if (name === "comment")
678
+ frame(
679
+ "COMM",
680
+ join([
681
+ new Uint8Array([3]),
682
+ ascii("eng"),
683
+ new Uint8Array(1),
684
+ new TextEncoder().encode(value)
685
+ ])
686
+ );
687
+ else {
688
+ if (id.length !== 4 || !id.startsWith("T")) throw new Error(`Unsupported ID3 tag ${name}`);
689
+ frame(id, join([new Uint8Array([3]), new TextEncoder().encode(value)]));
690
+ }
691
+ }
692
+ for (const picture of pictures)
693
+ frame(
694
+ "APIC",
695
+ join([
696
+ new Uint8Array([3]),
697
+ ascii(picture.mime),
698
+ new Uint8Array([0, picture.type]),
699
+ new TextEncoder().encode(picture.description),
700
+ new Uint8Array(1),
701
+ picture.data
702
+ ])
703
+ );
704
+ const body = join(frames);
705
+ return join([ascii("ID3"), new Uint8Array([4, 0, 0]), syncBytes(body.length), body]);
706
+ }
707
+ function parseId3v1(bytes) {
708
+ const r = new Reader(bytes), tags = {};
709
+ for (const [name, offset, size] of [
710
+ ["title", 3, 30],
711
+ ["artist", 33, 30],
712
+ ["album", 63, 30],
713
+ ["date", 93, 4],
714
+ ["comment", 97, 30]
715
+ ])
716
+ tags[name] = cleanText(r.text(offset, size));
717
+ if (r.u8(125) === 0 && r.u8(126) !== 0) {
718
+ tags.track = String(r.u8(126));
719
+ tags.comment = cleanText(r.text(97, 28));
720
+ }
721
+ tags.genre = String(r.u8(127));
722
+ return tags;
723
+ }
724
+
725
+ // packages/audio-ast/src/id3-source.ts
726
+ var Cursor = class {
727
+ constructor(source, position, end, unsynchronized, options) {
728
+ this.source = source;
729
+ this.position = position;
730
+ this.end = end;
731
+ this.unsynchronized = unsynchronized;
732
+ this.options = options;
733
+ if (!Number.isSafeInteger(source.size) || source.size < 0 || !Number.isSafeInteger(position) || !Number.isSafeInteger(end) || position < 0 || end < position || end > source.size)
734
+ throw new Error("Truncated or invalid audio structure");
735
+ }
736
+ source;
737
+ position;
738
+ end;
739
+ unsynchronized;
740
+ options;
741
+ cache = new Uint8Array();
742
+ cacheOffset = 0;
743
+ async fill() {
744
+ this.options.signal?.throwIfAborted();
745
+ if (this.position >= this.end) throw new Error("Truncated or invalid audio structure");
746
+ if (this.position >= this.cacheOffset && this.position < this.cacheOffset + this.cache.length) return;
747
+ await this.options.checkpoint?.();
748
+ this.options.signal?.throwIfAborted();
749
+ const length = Math.min(16384, this.end - this.position);
750
+ const bytes = await this.source.read(this.position, length);
751
+ this.options.signal?.throwIfAborted();
752
+ if (bytes.length !== length) throw new Error("Truncated or invalid audio structure");
753
+ this.cache = bytes.slice();
754
+ this.cacheOffset = this.position;
755
+ }
756
+ async read(length, partial = false) {
757
+ if (!Number.isSafeInteger(length) || length < 0 || length > 16384) throw new Error("Invalid ID3 header read");
758
+ this.options.signal?.throwIfAborted();
759
+ const result = new Uint8Array(length);
760
+ let count = 0;
761
+ while (count < length && this.position < this.end) {
762
+ await this.fill();
763
+ if (!this.unsynchronized) {
764
+ const size = Math.min(length - count, this.cacheOffset + this.cache.length - this.position);
765
+ result.set(this.cache.subarray(this.position - this.cacheOffset, this.position - this.cacheOffset + size), count);
766
+ count += size;
767
+ this.position += size;
768
+ } else {
769
+ while (count < length && this.position < this.cacheOffset + this.cache.length) {
770
+ const byte = this.cache[this.position++ - this.cacheOffset];
771
+ result[count++] = byte;
772
+ if (byte === 255 && this.position < this.end) {
773
+ if (this.position === this.cacheOffset + this.cache.length) await this.fill();
774
+ if (this.cache[this.position - this.cacheOffset] === 0) this.position++;
775
+ }
776
+ }
777
+ }
778
+ }
779
+ if (!partial && count !== length) throw new Error("Truncated or invalid audio structure");
780
+ return result.subarray(0, count);
781
+ }
782
+ async skip(length, partial = false) {
783
+ this.options.signal?.throwIfAborted();
784
+ if (!Number.isSafeInteger(length) || length < 0) throw new Error("Truncated or invalid audio structure");
785
+ if (!this.unsynchronized) {
786
+ if (!partial && length > this.end - this.position) throw new Error("Truncated or invalid audio structure");
787
+ this.position += Math.min(length, this.end - this.position);
788
+ return;
789
+ }
790
+ while (length && this.position < this.end) {
791
+ const bytes = await this.read(Math.min(length, 16384), partial);
792
+ length -= bytes.length;
793
+ }
794
+ if (length && !partial) throw new Error("Truncated or invalid audio structure");
795
+ }
796
+ async terminated(start, encoding) {
797
+ const width = encoding === 1 || encoding === 2 ? 2 : 1;
798
+ await this.skip(start, true);
799
+ while (this.position < this.end) {
800
+ const bytes = await this.read(width, true);
801
+ if (bytes.length < width) break;
802
+ if (bytes[0] === 0 && (width === 1 || bytes[1] === 0)) return;
803
+ }
804
+ throw new Error("Unterminated ID3 text field");
805
+ }
806
+ };
807
+ async function* readId3Text(source, span, options = {}) {
808
+ const cursor = new Cursor(source, span.offset, span.offset + span.length, span.unsynchronized, options);
809
+ const first = await cursor.read(Math.min(2, span.length), true);
810
+ const decoder = new TextDecoder(id3TextEncoding(span.encoding, first));
811
+ yield decoder.decode(first, { stream: true });
812
+ while (cursor.position < cursor.end) yield decoder.decode(await cursor.read(16384, true), { stream: true });
813
+ options.signal?.throwIfAborted();
814
+ yield decoder.decode();
815
+ }
816
+ async function probeId3Source(source, options = {}) {
817
+ const input = new Cursor(source, 0, source.size, false, options), header = new Reader(await input.read(10));
818
+ const version = header.u8(3), flags = header.u8(5), length = synchsafe(header, 6);
819
+ if (![2, 3, 4].includes(version)) throw new Error("Unsupported ID3 version");
820
+ if (version === 2 && flags & 64) throw new Error("Compressed ID3v2.2 tag unsupported");
821
+ const footer = version === 4 && flags & 16 ? 10 : 0, size = 10 + length + footer;
822
+ if (size > source.size) throw new Error("Truncated or invalid audio structure");
823
+ const cursor = new Cursor(source, 10, 10 + length, !!(flags & 128 && version < 4), options), tags = {};
824
+ if (version >= 3 && flags & 64) {
825
+ const prefix = new Reader(await cursor.read(4)), extended = version === 4 ? synchsafe(prefix, 0) : prefix.u32(0) + 4;
826
+ if (extended >= 4) await cursor.skip(extended - 4);
827
+ if (extended < (version === 3 ? 10 : 6)) throw new Error("Invalid ID3 extended header");
828
+ }
829
+ while (cursor.position < cursor.end) {
830
+ options.signal?.throwIfAborted();
831
+ await options.checkpoint?.();
832
+ const first = await cursor.read(1);
833
+ if (first[0] === 0) break;
834
+ const headerSize = version === 2 ? 6 : 10, bytes = new Uint8Array(headerSize);
835
+ bytes.set(first);
836
+ bytes.set(await cursor.read(headerSize - 1), 1);
837
+ const r = new Reader(bytes), id = r.text(0, version === 2 ? 3 : 4);
838
+ const frameSize = version === 2 ? r.u24(3) : version === 4 ? synchsafe(r, 4) : r.u32(4), frameFlags = version === 2 ? 0 : r.u16(8);
839
+ const offset = cursor.position;
840
+ await cursor.skip(frameSize);
841
+ const frame = new Cursor(source, offset, cursor.position, cursor.unsynchronized || !!(version === 4 && (flags & 128 || frameFlags & 2)), options);
842
+ const unsupported = version === 3 ? frameFlags & 192 : frameFlags & 12;
843
+ if (unsupported || !frameSize) continue;
844
+ let prefix = 0;
845
+ if (version === 3 && frameFlags & 32) prefix++;
846
+ if (version === 4 && frameFlags & 64) prefix++;
847
+ if (version === 4 && frameFlags & 1) prefix += 4;
848
+ await frame.skip(prefix, true);
849
+ let key, encoding;
850
+ if (id.startsWith("T") && id !== "TXXX" && id !== "TXX") {
851
+ key = id3TagName(id);
852
+ encoding = (await frame.read(1, true))[0];
853
+ } else if (id === "COMM" || id === "COM") {
854
+ encoding = (await frame.read(1, true))[0];
855
+ await frame.terminated(3, encoding);
856
+ key = "comment";
857
+ } else if (id === "APIC" || id === "PIC") {
858
+ encoding = (await frame.read(1, true))[0];
859
+ if (id === "PIC") await frame.read(3);
860
+ else await frame.terminated(0, 0);
861
+ await frame.skip(1, true);
862
+ await frame.terminated(0, encoding);
863
+ id3TextEncoding(encoding, new Uint8Array());
864
+ }
865
+ if (key !== void 0) {
866
+ id3TextEncoding(encoding, new Uint8Array());
867
+ const span = { key, offset: frame.position, length: frame.end - frame.position, unsynchronized: frame.unsynchronized, encoding };
868
+ if (options.onTag) {
869
+ await options.onTag(span);
870
+ options.signal?.throwIfAborted();
871
+ } else {
872
+ let value = "";
873
+ for await (const text2 of readId3Text(source, span, options)) value += text2;
874
+ tags[key] = cleanText(value);
875
+ }
876
+ }
877
+ }
878
+ if (footer) {
879
+ const tail = new Cursor(source, 10 + length, size, false, options);
880
+ if (new Reader(await tail.read(3)).text(0, 3) !== "3DI") throw new Error("Missing ID3 footer");
881
+ }
882
+ options.signal?.throwIfAborted();
883
+ return { size, version, tags };
884
+ }
885
+
886
+ // packages/audio-ast/src/mpeg.ts
887
+ function mpegFrame(header, offset, end) {
888
+ const versionBits = header >>> 19 & 3, layer = header >>> 17 & 3, bitrateIndex = header >>> 12 & 15, rateIndex = header >>> 10 & 3;
889
+ if (header >>> 21 !== 2047 || versionBits === 1 || layer !== 1 || !bitrateIndex || bitrateIndex === 15 || rateIndex === 3)
890
+ throw new Error(`Invalid MPEG Layer III frame at ${offset}`);
891
+ const version = versionBits === 3 ? 1 : versionBits === 2 ? 2 : 2.5;
892
+ const rate = [44100, 48e3, 32e3][rateIndex] / (version === 1 ? 1 : version === 2 ? 2 : 4);
893
+ const bitrate = (version === 1 ? [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320] : [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160])[bitrateIndex] * 1e3;
894
+ const count = version === 1 ? 1152 : 576, frameSize = Math.floor((version === 1 ? 144 : 72) * bitrate / rate) + (header >>> 9 & 1), ch = header >>> 6 & 3;
895
+ if (frameSize > end - offset) throw new Error("Truncated MPEG frame");
896
+ const channels = ch === 3 ? 1 : 2;
897
+ const fields = {
898
+ version,
899
+ layer: 3,
900
+ bitrate,
901
+ sampleRate: rate,
902
+ channels,
903
+ samples: count,
904
+ padding: header >>> 9 & 1
905
+ };
906
+ return { version, rate, count, frameSize, channels, fields };
907
+ }
908
+ function mpegVbr(r, offset, frameSize, header, fields) {
909
+ const version = Number(fields.version), channels = Number(fields.channels);
910
+ const side = version === 1 ? channels === 1 ? 17 : 32 : channels === 1 ? 9 : 17, crc = header & 65536 ? 0 : 2;
911
+ const xing = offset + 4 + crc + side;
912
+ if (xing + 8 <= offset + frameSize && ["Xing", "Info"].includes(r.text(xing, 4))) {
913
+ const flags = r.u32(xing + 4);
914
+ let pos = xing + 8;
915
+ const vbr = { type: r.text(xing, 4), flags };
916
+ if (flags & 1) {
917
+ vbr.frames = r.u32(pos);
918
+ pos += 4;
919
+ }
920
+ if (flags & 2) {
921
+ vbr.bytes = r.u32(pos);
922
+ pos += 4;
923
+ }
924
+ if (flags & 4) {
925
+ vbr.toc = r.slice(pos, 100);
926
+ pos += 100;
927
+ }
928
+ if (flags & 8) {
929
+ vbr.quality = r.u32(pos);
930
+ pos += 4;
931
+ }
932
+ if (pos > offset + frameSize) throw new Error("Xing header exceeds frame");
933
+ fields.vbr = vbr;
934
+ if (pos + 24 <= offset + frameSize && ["LAME", "Lavc", "Lavf"].includes(r.text(pos, 4))) {
935
+ const packed = r.u24(pos + 21);
936
+ fields.encoderDelay = packed >>> 12;
937
+ fields.encoderPadding = packed & 4095;
938
+ }
939
+ }
940
+ const vbri = offset + 36;
941
+ if (vbri + 26 <= offset + frameSize && r.text(vbri, 4) === "VBRI")
942
+ fields.vbr = {
943
+ type: "VBRI",
944
+ version: r.u16(vbri + 4),
945
+ delay: r.u16(vbri + 6),
946
+ quality: r.u16(vbri + 8),
947
+ bytes: r.u32(vbri + 10),
948
+ frames: r.u32(vbri + 14),
949
+ entries: r.u16(vbri + 18),
950
+ scale: r.u16(vbri + 20),
951
+ entryBytes: r.u16(vbri + 22),
952
+ framesPerEntry: r.u16(vbri + 24)
953
+ };
954
+ }
955
+ function mpegStream(state, first) {
956
+ let { samples, seconds: seconds2, audioBytes } = state;
957
+ const { sampleRate, channels, frames } = state;
958
+ if (!frames) throw new Error("MP3 contains no MPEG frames");
959
+ const vbr = first.fields?.vbr;
960
+ const declared = vbr?.frames;
961
+ if (typeof declared === "number" && declared > 0) {
962
+ if (declared !== frames && declared !== frames - 1)
963
+ throw new Error("MPEG VBR frame count mismatch");
964
+ const encodedSamples = declared * (sampleRate >= 32e3 ? 1152 : 576);
965
+ const delay = Number(first.fields?.encoderDelay ?? 0), padding = Number(first.fields?.encoderPadding ?? 0);
966
+ if (delay + padding > encodedSamples) throw new Error("Invalid MPEG encoder delay/padding");
967
+ samples = encodedSamples - delay - padding;
968
+ seconds2 = duration(samples, sampleRate);
969
+ if (declared === frames - 1) audioBytes -= first.size;
970
+ first.fields.encodedSamples = encodedSamples;
971
+ }
972
+ const encodedSeconds = duration(Number(first.fields?.encodedSamples ?? samples), sampleRate);
973
+ const bitrate = encodedSeconds ? audioBytes * 8 / encodedSeconds : 0;
974
+ return { codec: "mp3", sampleRate, channels, samples, duration: seconds2, bitrate };
975
+ }
976
+
977
+ // packages/audio-ast/src/mp3-source.ts
978
+ async function probeMp3Source(source, options = {}) {
979
+ const read = async (offset2, length) => {
980
+ options.signal?.throwIfAborted();
981
+ if (!Number.isSafeInteger(source.size) || source.size < 0 || offset2 < 0 || length < 0 || length > source.size - offset2)
982
+ throw new Error("Truncated or invalid audio structure");
983
+ const bytes = await source.read(offset2, length);
984
+ options.signal?.throwIfAborted();
985
+ if (bytes.length !== length) throw new Error("Truncated or invalid audio structure");
986
+ return new Reader(bytes);
987
+ };
988
+ const marker = (await read(0, 3)).text(0, 3);
989
+ let end = source.size, offset = 0;
990
+ const tags = {};
991
+ if (end >= 128) {
992
+ const tail = await read(end - 128, 128);
993
+ if (tail.text(0, 3) === "TAG") {
994
+ const values = parseId3v1(tail.bytes);
995
+ end -= 128;
996
+ if (options.onTag) for (const [key, value] of Object.entries(values)) {
997
+ await options.onTag({ key, value });
998
+ options.signal?.throwIfAborted();
999
+ }
1000
+ else Object.assign(tags, values);
1001
+ }
1002
+ }
1003
+ if (marker === "ID3") {
1004
+ const id3 = await probeId3Source(source, { ...options, ...options.onTag ? { onTag: options.onTag } : {} });
1005
+ offset = id3.size;
1006
+ Object.assign(tags, id3.tags);
1007
+ }
1008
+ let samples = 0, seconds2 = 0, audioBytes = 0, sampleRate = 0, channels = 0, frames = 0;
1009
+ let firstOffset = 0;
1010
+ let first = { size: 0, fields: {} };
1011
+ while (offset < end) {
1012
+ await options.checkpoint?.();
1013
+ options.signal?.throwIfAborted();
1014
+ const header = (await read(offset, 4)).u32(0), frame = mpegFrame(header, offset, end);
1015
+ if (sampleRate && (sampleRate !== frame.rate || channels !== frame.channels)) throw new Error("Inconsistent MPEG stream");
1016
+ sampleRate = frame.rate;
1017
+ channels = frame.channels;
1018
+ if (!frames) {
1019
+ const metadata = await read(offset, Math.min(192, source.size - offset));
1020
+ mpegVbr(new Reader(metadata.bytes.slice()), 0, frame.frameSize, header, frame.fields);
1021
+ first = { size: frame.frameSize, fields: frame.fields };
1022
+ firstOffset = offset;
1023
+ }
1024
+ samples += frame.count;
1025
+ seconds2 += duration(frame.count, frame.rate);
1026
+ audioBytes += frame.frameSize;
1027
+ frames++;
1028
+ offset += frame.frameSize;
1029
+ }
1030
+ options.signal?.throwIfAborted();
1031
+ const stream = mpegStream({ samples, seconds: seconds2, audioBytes, sampleRate, channels, frames }, first);
1032
+ return { format: "mp3", nodes: [{ type: "MPEG", offset: firstOffset, size: first.size, data: new Uint8Array(), fields: first.fields }], streams: [stream], tags, duration: stream.duration, bitrate: stream.duration ? source.size * 8 / stream.duration : 0 };
1033
+ }
1034
+
1035
+ // packages/audio-ast/src/flac.ts
1036
+ function parseFlac(bytes) {
1037
+ const r = new Reader(bytes), nodes = [], pictures = [];
1038
+ const tags = {};
1039
+ let offset = 4, last = false, stream;
1040
+ while (!last) {
1041
+ const header = r.u8(offset), type = header & 127, size = r.u24(offset + 1);
1042
+ last = !!(header & 128);
1043
+ const data = r.slice(offset + 4, size), node = {
1044
+ type: [
1045
+ "STREAMINFO",
1046
+ "PADDING",
1047
+ "APPLICATION",
1048
+ "SEEKTABLE",
1049
+ "VORBIS_COMMENT",
1050
+ "CUESHEET",
1051
+ "PICTURE"
1052
+ ][type] ?? `BLOCK_${type}`,
1053
+ offset,
1054
+ size: size + 4,
1055
+ data
1056
+ };
1057
+ nodes.push(node);
1058
+ const block = new Reader(data);
1059
+ if (type === 0) {
1060
+ if (stream || offset !== 4 || size !== 34)
1061
+ throw new Error("FLAC requires first unique STREAMINFO");
1062
+ stream = flacStream(block, bytes.length);
1063
+ const { sampleRate, channels, bitsPerSample, samples } = stream;
1064
+ node.fields = {
1065
+ minBlockSize: block.u16(0),
1066
+ maxBlockSize: block.u16(2),
1067
+ minFrameSize: block.u24(4),
1068
+ maxFrameSize: block.u24(7),
1069
+ sampleRate,
1070
+ channels,
1071
+ bitsPerSample,
1072
+ totalSamples: samples,
1073
+ md5: Array.from(block.slice(18, 16), (b) => b.toString(16).padStart(2, "0")).join("")
1074
+ };
1075
+ } else if (type === 4) {
1076
+ const parsed = parseComments(data);
1077
+ if (parsed.size !== size) throw new Error("Trailing Vorbis comment bytes");
1078
+ Object.assign(tags, parsed.tags);
1079
+ node.fields = { vendor: parsed.vendor, comments: parsed.comments };
1080
+ } else if (type === 6) pictures.push(parsePicture(data));
1081
+ else if (type === 3) {
1082
+ if (size % 18) throw new Error("Invalid FLAC seek table");
1083
+ const points = [];
1084
+ for (let pos = 0; pos < size; pos += 18)
1085
+ points.push({
1086
+ sample: block.u64(pos),
1087
+ offset: block.u64(pos + 8),
1088
+ samples: block.u16(pos + 16)
1089
+ });
1090
+ node.fields = { points };
1091
+ } else if (type === 127) throw new Error("Invalid FLAC metadata block");
1092
+ offset += 4 + size;
1093
+ }
1094
+ if (!stream) throw new Error("Missing FLAC STREAMINFO");
1095
+ if (offset < bytes.length)
1096
+ nodes.push({
1097
+ type: "FRAMES",
1098
+ offset,
1099
+ size: bytes.length - offset,
1100
+ data: r.slice(offset, bytes.length - offset)
1101
+ });
1102
+ return {
1103
+ format: "flac",
1104
+ data: bytes,
1105
+ nodes,
1106
+ tags,
1107
+ pictures,
1108
+ streams: [stream],
1109
+ duration: stream.duration,
1110
+ bitrate: stream.bitrate
1111
+ };
1112
+ }
1113
+ function flacStream(block, byteLength) {
1114
+ const packed = block.u64(10), sampleRate = Number(packed >> 44n), channels = Number(packed >> 41n & 7n) + 1, bitsPerSample = Number(packed >> 36n & 31n) + 1, samples = Number(packed & 0xfffffffffn), seconds2 = duration(samples, sampleRate);
1115
+ return { codec: "flac", sampleRate, channels, bitsPerSample, samples, duration: seconds2, bitrate: seconds2 ? byteLength * 8 / seconds2 : 0 };
1116
+ }
1117
+
1118
+ // packages/audio-ast/src/flac-source.ts
1119
+ async function probeFlacSource(source, options = {}) {
1120
+ const check = (offset2, length, end = source.size) => {
1121
+ options.signal?.throwIfAborted();
1122
+ if (!Number.isSafeInteger(source.size) || !Number.isSafeInteger(offset2) || !Number.isSafeInteger(length) || offset2 < 0 || length < 0 || length > end - offset2)
259
1123
  throw new Error("Truncated or invalid audio structure");
260
1124
  };
261
1125
  const read = async (offset2, length, end = source.size) => {
@@ -743,239 +1607,50 @@ function resample(pcm, sampleRate, method = "sinc") {
743
1607
  }
744
1608
  let sum = 0, weight = 0;
745
1609
  for (let j = Math.max(0, Math.ceil(position - radius)); j <= Math.min(frames - 1, Math.floor(position + radius)); j++) {
746
- const distance = position - j, x = Math.PI * distance * cutoff;
747
- const w = (x === 0 ? 1 : Math.sin(x) / x) * (0.5 + 0.5 * Math.cos(Math.PI * distance / radius));
748
- sum += ch[j] * w;
749
- weight += w;
750
- }
751
- return weight ? sum / weight : 0;
752
- })
753
- )
754
- };
755
- }
756
- function transformAudio(pcm, effects) {
757
- validatePcm(pcm);
758
- let result = {
759
- sampleRate: pcm.sampleRate,
760
- channels: pcm.channels.map((ch) => ch.slice())
761
- };
762
- for (const effect of effects) {
763
- switch (effect.type) {
764
- case "trim":
765
- result = trim(result, effect.startSec, effect.durationSec);
766
- break;
767
- case "pad":
768
- result = pad(result, effect.leadSec, effect.trailSec);
769
- break;
770
- case "rate":
771
- result = resample(result, effect.sampleRate, effect.method);
772
- break;
773
- case "channels":
774
- result = remix(result, effect.channels, effect.matrix);
775
- break;
776
- case "extract":
777
- result = extractChannels(result, effect.channels);
778
- break;
779
- case "normalize":
780
- result = normalize(result, effect.targetDb);
781
- break;
782
- case "fade":
783
- result = fade(result, effect.inSec, effect.outSec);
784
- break;
785
- default:
786
- throw new Error("Unsupported audio effect");
787
- }
788
- }
789
- return result;
790
- }
791
-
792
- // packages/audio-ast/src/id3.ts
793
- var names2 = {
794
- TIT2: "title",
795
- TPE1: "artist",
796
- TPE2: "albumArtist",
797
- TALB: "album",
798
- TRCK: "track",
799
- TPOS: "disc",
800
- TCON: "genre",
801
- TDRC: "date",
802
- TYER: "date",
803
- TCOP: "copyright",
804
- TSSE: "encoder",
805
- TT2: "title",
806
- TP1: "artist",
807
- TP2: "albumArtist",
808
- TAL: "album",
809
- TRK: "track",
810
- TPA: "disc",
811
- TCO: "genre",
812
- TYE: "date",
813
- TCR: "copyright",
814
- TEN: "encoder"
815
- };
816
- function synchsafe(r, offset) {
817
- let value = 0;
818
- for (let i = 0; i < 4; i++) {
819
- const b = r.u8(offset + i);
820
- if (b & 128) throw new Error("Invalid ID3 synchsafe size");
821
- value = value * 128 + b;
822
- }
823
- return value;
824
- }
825
- function unsync(bytes) {
826
- const result = [];
827
- for (let i = 0; i < bytes.length; i++) {
828
- const b = bytes[i];
829
- result.push(b);
830
- if (b === 255 && bytes[i + 1] === 0) i++;
831
- }
832
- return Uint8Array.from(result);
833
- }
834
- function text(bytes, encoding) {
835
- if (encoding === 0) return cleanText(new TextDecoder("latin1").decode(bytes));
836
- if (encoding === 3) return cleanText(new TextDecoder().decode(bytes));
837
- if (encoding === 2) return cleanText(new TextDecoder("utf-16be").decode(bytes));
838
- if (encoding === 1)
839
- return cleanText(
840
- new TextDecoder(bytes[0] === 254 && bytes[1] === 255 ? "utf-16be" : "utf-16le").decode(bytes)
841
- );
842
- throw new Error("Invalid ID3 text encoding");
843
- }
844
- function terminated(bytes, start, encoding) {
845
- const width = encoding === 1 || encoding === 2 ? 2 : 1;
846
- for (let i = start; i + width <= bytes.length; i += width)
847
- if (bytes[i] === 0 && (width === 1 || bytes[i + 1] === 0)) return i;
848
- throw new Error("Unterminated ID3 text field");
849
- }
850
- function parseId3(bytes) {
851
- const input = new Reader(bytes), version = input.u8(3), flags = input.u8(5), length = synchsafe(input, 6);
852
- if (![2, 3, 4].includes(version)) throw new Error("Unsupported ID3 version");
853
- if (version === 2 && flags & 64) throw new Error("Compressed ID3v2.2 tag unsupported");
854
- const footer = version === 4 && flags & 16 ? 10 : 0;
855
- input.check(0, 10 + length + footer);
856
- const payload = input.slice(10, length), r = new Reader(flags & 128 && version < 4 ? unsync(payload) : payload);
857
- const nodes = [], tags = {}, pictures = [];
858
- let pos = 0;
859
- if (version >= 3 && flags & 64) {
860
- const extended = version === 4 ? synchsafe(r, 0) : r.u32(0) + 4;
861
- r.check(0, extended);
862
- if (extended < (version === 3 ? 10 : 6)) throw new Error("Invalid ID3 extended header");
863
- pos = extended;
864
- }
865
- while (pos < r.bytes.length && r.u8(pos) !== 0) {
866
- const header = version === 2 ? 6 : 10, id = r.text(pos, version === 2 ? 3 : 4), size = version === 2 ? r.u24(pos + 3) : version === 4 ? synchsafe(r, pos + 4) : r.u32(pos + 4);
867
- const frameFlags = version === 2 ? 0 : r.u16(pos + 8);
868
- let data = r.slice(pos + header, size);
869
- if (version === 4 && (flags & 128 || frameFlags & 2)) data = unsync(data);
870
- const node = {
871
- type: id,
872
- offset: 10 + pos,
873
- size: header + size,
874
- data,
875
- fields: { flags: frameFlags, version }
876
- };
877
- nodes.push(node);
878
- const unsupported = version === 3 ? frameFlags & 192 : frameFlags & 12;
879
- if (!unsupported && data.length) {
880
- let prefix = 0;
881
- if (version === 3 && frameFlags & 32) prefix++;
882
- if (version === 4 && frameFlags & 64) prefix++;
883
- if (version === 4 && frameFlags & 1) prefix += 4;
884
- data = data.subarray(prefix);
885
- if (id.startsWith("T") && id !== "TXXX" && id !== "TXX")
886
- tags[names2[id] ?? id] = text(data.subarray(1), data[0]);
887
- else if (id === "COMM" || id === "COM") {
888
- const encoding = data[0], end = terminated(data, 4, encoding), width = encoding === 1 || encoding === 2 ? 2 : 1;
889
- tags.comment = text(data.subarray(end + width), encoding);
890
- } else if (id === "APIC" || id === "PIC") {
891
- const encoding = data[0], mimeEnd = id === "PIC" ? 4 : terminated(data, 1, 0);
892
- const mime = id === "PIC" ? `image/${new Reader(data).text(1, 3) === "JPG" ? "jpeg" : new Reader(data).text(1, 3).toLowerCase()}` : text(data.subarray(1, mimeEnd), 0);
893
- const typeOffset = id === "PIC" ? 4 : mimeEnd + 1, descriptionStart = typeOffset + 1, end = terminated(data, descriptionStart, encoding), width = encoding === 1 || encoding === 2 ? 2 : 1;
894
- pictures.push({
895
- type: data[typeOffset],
896
- mime,
897
- description: text(data.subarray(descriptionStart, end), encoding),
898
- data: data.subarray(end + width)
899
- });
900
- }
901
- }
902
- pos += header + size;
903
- }
904
- if (footer && input.text(10 + length, 3) !== "3DI") throw new Error("Missing ID3 footer");
905
- return { size: 10 + length + footer, nodes, tags, pictures, version };
906
- }
907
- function syncBytes(value) {
908
- if (value > 268435455) throw new Error("ID3 tag too large");
909
- return Uint8Array.from([
910
- value >>> 21 & 127,
911
- value >>> 14 & 127,
912
- value >>> 7 & 127,
913
- value & 127
914
- ]);
915
- }
916
- function encodeId3(tags, retained = [], pictures = []) {
917
- const frames = [];
918
- function frame(id, data) {
919
- frames.push(join([ascii(id), syncBytes(data.length), new Uint8Array(2), data]));
920
- }
921
- for (const node of retained) {
922
- const flags = Number(node.fields?.flags ?? 0);
923
- if (flags & (node.fields?.version === 3 ? 192 : 12)) {
924
- throw new Error(`Cannot safely rewrite transformed ID3 frame ${node.type}`);
925
- }
926
- if (node.type === "APIC" || node.type === "PIC" || names2[node.type] || node.type.startsWith("T") && node.type !== "TXXX" && node.type !== "TXX" || node.type === "COMM" || node.type === "COM")
927
- continue;
928
- if (node.type.length === 4 && !node.fields?.flags) frame(node.type, node.data);
929
- else throw new Error(`Cannot safely rewrite ID3 frame ${node.type}`);
930
- }
931
- for (const [name, value] of Object.entries(tags)) {
932
- const id = Object.keys(names2).find((key) => key.length === 4 && names2[key] === name) ?? name;
933
- if (name === "comment")
934
- frame(
935
- "COMM",
936
- join([
937
- new Uint8Array([3]),
938
- ascii("eng"),
939
- new Uint8Array(1),
940
- new TextEncoder().encode(value)
941
- ])
942
- );
943
- else {
944
- if (id.length !== 4 || !id.startsWith("T")) throw new Error(`Unsupported ID3 tag ${name}`);
945
- frame(id, join([new Uint8Array([3]), new TextEncoder().encode(value)]));
946
- }
947
- }
948
- for (const picture of pictures)
949
- frame(
950
- "APIC",
951
- join([
952
- new Uint8Array([3]),
953
- ascii(picture.mime),
954
- new Uint8Array([0, picture.type]),
955
- new TextEncoder().encode(picture.description),
956
- new Uint8Array(1),
957
- picture.data
958
- ])
959
- );
960
- const body = join(frames);
961
- return join([ascii("ID3"), new Uint8Array([4, 0, 0]), syncBytes(body.length), body]);
1610
+ const distance = position - j, x = Math.PI * distance * cutoff;
1611
+ const w = (x === 0 ? 1 : Math.sin(x) / x) * (0.5 + 0.5 * Math.cos(Math.PI * distance / radius));
1612
+ sum += ch[j] * w;
1613
+ weight += w;
1614
+ }
1615
+ return weight ? sum / weight : 0;
1616
+ })
1617
+ )
1618
+ };
962
1619
  }
963
- function parseId3v1(bytes) {
964
- const r = new Reader(bytes), tags = {};
965
- for (const [name, offset, size] of [
966
- ["title", 3, 30],
967
- ["artist", 33, 30],
968
- ["album", 63, 30],
969
- ["date", 93, 4],
970
- ["comment", 97, 30]
971
- ])
972
- tags[name] = cleanText(r.text(offset, size));
973
- if (r.u8(125) === 0 && r.u8(126) !== 0) {
974
- tags.track = String(r.u8(126));
975
- tags.comment = cleanText(r.text(97, 28));
1620
+ function transformAudio(pcm, effects) {
1621
+ validatePcm(pcm);
1622
+ let result = {
1623
+ sampleRate: pcm.sampleRate,
1624
+ channels: pcm.channels.map((ch) => ch.slice())
1625
+ };
1626
+ for (const effect of effects) {
1627
+ switch (effect.type) {
1628
+ case "trim":
1629
+ result = trim(result, effect.startSec, effect.durationSec);
1630
+ break;
1631
+ case "pad":
1632
+ result = pad(result, effect.leadSec, effect.trailSec);
1633
+ break;
1634
+ case "rate":
1635
+ result = resample(result, effect.sampleRate, effect.method);
1636
+ break;
1637
+ case "channels":
1638
+ result = remix(result, effect.channels, effect.matrix);
1639
+ break;
1640
+ case "extract":
1641
+ result = extractChannels(result, effect.channels);
1642
+ break;
1643
+ case "normalize":
1644
+ result = normalize(result, effect.targetDb);
1645
+ break;
1646
+ case "fade":
1647
+ result = fade(result, effect.inSec, effect.outSec);
1648
+ break;
1649
+ default:
1650
+ throw new Error("Unsupported audio effect");
1651
+ }
976
1652
  }
977
- tags.genre = String(r.u8(127));
978
- return tags;
1653
+ return result;
979
1654
  }
980
1655
 
981
1656
  // packages/audio-ast/src/mp3.ts
@@ -1003,27 +1678,11 @@ function parseMp3(bytes) {
1003
1678
  }
1004
1679
  let samples = 0, seconds2 = 0, audioBytes = 0, sampleRate = 0, channels = 0, frames = 0;
1005
1680
  while (offset < end) {
1006
- const header = r.u32(offset), versionBits = header >>> 19 & 3, layer = header >>> 17 & 3, bitrateIndex = header >>> 12 & 15, rateIndex = header >>> 10 & 3;
1007
- if (header >>> 21 !== 2047 || versionBits === 1 || layer !== 1 || !bitrateIndex || bitrateIndex === 15 || rateIndex === 3)
1008
- throw new Error(`Invalid MPEG Layer III frame at ${offset}`);
1009
- const version = versionBits === 3 ? 1 : versionBits === 2 ? 2 : 2.5;
1010
- const rate = [44100, 48e3, 32e3][rateIndex] / (version === 1 ? 1 : version === 2 ? 2 : 4);
1011
- const bitrate2 = (version === 1 ? [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320] : [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160])[bitrateIndex] * 1e3;
1012
- const count = version === 1 ? 1152 : 576, frameSize = Math.floor((version === 1 ? 144 : 72) * bitrate2 / rate) + (header >>> 9 & 1), ch = header >>> 6 & 3;
1013
- if (frameSize > end - offset) throw new Error("Truncated MPEG frame");
1014
- if (sampleRate && (sampleRate !== rate || channels !== (ch === 3 ? 1 : 2)))
1015
- throw new Error("Inconsistent MPEG stream");
1681
+ const header = r.u32(offset), frame = mpegFrame(header, offset, end);
1682
+ const { rate, count, frameSize, fields } = frame;
1683
+ if (sampleRate && (sampleRate !== rate || channels !== frame.channels)) throw new Error("Inconsistent MPEG stream");
1016
1684
  sampleRate = rate;
1017
- channels = ch === 3 ? 1 : 2;
1018
- const fields = {
1019
- version,
1020
- layer: 3,
1021
- bitrate: bitrate2,
1022
- sampleRate: rate,
1023
- channels,
1024
- samples: count,
1025
- padding: header >>> 9 & 1
1026
- };
1685
+ channels = frame.channels;
1027
1686
  const node = {
1028
1687
  type: "MPEG",
1029
1688
  offset,
@@ -1032,288 +1691,24 @@ function parseMp3(bytes) {
1032
1691
  fields
1033
1692
  };
1034
1693
  nodes.push(node);
1035
- if (!frames) {
1036
- const side = version === 1 ? channels === 1 ? 17 : 32 : channels === 1 ? 9 : 17, crc = header & 65536 ? 0 : 2;
1037
- const xing = offset + 4 + crc + side;
1038
- if (xing + 8 <= offset + frameSize && ["Xing", "Info"].includes(r.text(xing, 4))) {
1039
- const flags = r.u32(xing + 4);
1040
- let pos = xing + 8;
1041
- const vbr2 = { type: r.text(xing, 4), flags };
1042
- if (flags & 1) {
1043
- vbr2.frames = r.u32(pos);
1044
- pos += 4;
1045
- }
1046
- if (flags & 2) {
1047
- vbr2.bytes = r.u32(pos);
1048
- pos += 4;
1049
- }
1050
- if (flags & 4) {
1051
- vbr2.toc = r.slice(pos, 100);
1052
- pos += 100;
1053
- }
1054
- if (flags & 8) {
1055
- vbr2.quality = r.u32(pos);
1056
- pos += 4;
1057
- }
1058
- if (pos > offset + frameSize) throw new Error("Xing header exceeds frame");
1059
- fields.vbr = vbr2;
1060
- if (pos + 24 <= offset + frameSize && ["LAME", "Lavc", "Lavf"].includes(r.text(pos, 4))) {
1061
- const packed = r.u24(pos + 21);
1062
- fields.encoderDelay = packed >>> 12;
1063
- fields.encoderPadding = packed & 4095;
1064
- }
1065
- }
1066
- const vbri = offset + 36;
1067
- if (vbri + 26 <= offset + frameSize && r.text(vbri, 4) === "VBRI")
1068
- fields.vbr = {
1069
- type: "VBRI",
1070
- version: r.u16(vbri + 4),
1071
- delay: r.u16(vbri + 6),
1072
- quality: r.u16(vbri + 8),
1073
- bytes: r.u32(vbri + 10),
1074
- frames: r.u32(vbri + 14),
1075
- entries: r.u16(vbri + 18),
1076
- scale: r.u16(vbri + 20),
1077
- entryBytes: r.u16(vbri + 22),
1078
- framesPerEntry: r.u16(vbri + 24)
1079
- };
1080
- }
1694
+ if (!frames) mpegVbr(r, offset, frameSize, header, fields);
1081
1695
  samples += count;
1082
1696
  seconds2 += duration(count, rate);
1083
1697
  audioBytes += frameSize;
1084
1698
  frames++;
1085
1699
  offset += frameSize;
1086
1700
  }
1087
- if (!frames) throw new Error("MP3 contains no MPEG frames");
1088
- const first = nodes.find((n) => n.type === "MPEG"), vbr = first.fields?.vbr;
1089
- const declared = vbr?.frames;
1090
- if (typeof declared === "number" && declared > 0) {
1091
- if (declared !== frames && declared !== frames - 1)
1092
- throw new Error("MPEG VBR frame count mismatch");
1093
- const encodedSamples = declared * (sampleRate >= 32e3 ? 1152 : 576);
1094
- const delay = Number(first.fields?.encoderDelay ?? 0), padding = Number(first.fields?.encoderPadding ?? 0);
1095
- if (delay + padding > encodedSamples) throw new Error("Invalid MPEG encoder delay/padding");
1096
- samples = encodedSamples - delay - padding;
1097
- seconds2 = duration(samples, sampleRate);
1098
- if (declared === frames - 1) audioBytes -= first.size;
1099
- first.fields.encodedSamples = encodedSamples;
1100
- }
1101
- const encodedSeconds = duration(Number(first.fields?.encodedSamples ?? samples), sampleRate);
1102
- const bitrate = encodedSeconds ? audioBytes * 8 / encodedSeconds : 0;
1701
+ const first = nodes.find((n) => n.type === "MPEG");
1702
+ const stream = mpegStream({ samples, seconds: seconds2, audioBytes, sampleRate, channels, frames }, { size: first?.size ?? 0, fields: first?.fields ?? {} });
1103
1703
  return {
1104
1704
  format: "mp3",
1105
1705
  data: bytes,
1106
1706
  nodes,
1107
1707
  tags,
1108
1708
  pictures,
1109
- streams: [{ codec: "mp3", sampleRate, channels, samples, duration: seconds2, bitrate }],
1110
- duration: seconds2,
1111
- bitrate: seconds2 ? bytes.length * 8 / seconds2 : 0
1112
- };
1113
- }
1114
-
1115
- // packages/audio-ast/src/ogg.ts
1116
- function oggCrc(bytes) {
1117
- let crc = 0;
1118
- for (let i = 0; i < bytes.length; i++) {
1119
- crc ^= (i >= 22 && i < 26 ? 0 : bytes[i]) << 24;
1120
- for (let bit = 0; bit < 8; bit++) crc = crc << 1 ^ (crc & 2147483648 ? 79764919 : 0);
1121
- }
1122
- return crc >>> 0;
1123
- }
1124
- function readOgg(bytes) {
1125
- const r = new Reader(bytes), nodes = [], packets = [], logical = /* @__PURE__ */ new Map();
1126
- for (let offset = 0; offset < bytes.length; ) {
1127
- if (r.text(offset, 4) !== "OggS" || r.u8(offset + 4) !== 0) throw new Error("Invalid Ogg page");
1128
- const flags = r.u8(offset + 5), granule = r.u64(offset + 6, true), serial = r.u32(offset + 14, true), sequence = r.u32(offset + 18, true), count = r.u8(offset + 26), laces = r.slice(offset + 27, count);
1129
- if (flags & ~7) throw new Error("Invalid Ogg page flags");
1130
- const size = 27 + count + laces.reduce((sum, n) => sum + n, 0), page = r.slice(offset, size);
1131
- if (oggCrc(page) !== r.u32(offset + 22, true)) throw new Error("Ogg checksum mismatch");
1132
- let state = logical.get(serial);
1133
- if (!state) {
1134
- if (!(flags & 2) || sequence !== 0) throw new Error("Missing Ogg beginning-of-stream");
1135
- state = { packets: [], pending: [], sequence: -1, ended: false };
1136
- logical.set(serial, state);
1137
- }
1138
- if (state.ended || sequence !== state.sequence + 1 >>> 0 || !!(flags & 1) !== !!state.pending.length)
1139
- throw new Error("Invalid Ogg sequence/continuation");
1140
- state.sequence = sequence;
1141
- const completed = [];
1142
- let pos = offset + 27 + count;
1143
- for (const lace of laces) {
1144
- state.pending.push(r.slice(pos, lace));
1145
- pos += lace;
1146
- if (lace < 255) {
1147
- const packet = {
1148
- data: join(state.pending),
1149
- serial,
1150
- granule: 0xffffffffffffffffn,
1151
- bos: state.packets.length === 0,
1152
- eos: false
1153
- };
1154
- state.pending = [];
1155
- state.packets.push(packet);
1156
- packets.push(packet);
1157
- completed.push(packet);
1158
- }
1159
- }
1160
- if (completed.length) {
1161
- completed[completed.length - 1].granule = granule;
1162
- if (flags & 4) completed[completed.length - 1].eos = true;
1163
- }
1164
- if (flags & 4) {
1165
- if (state.pending.length) throw new Error("Incomplete final Ogg packet");
1166
- state.ended = true;
1167
- }
1168
- nodes.push({
1169
- type: "OggS",
1170
- offset,
1171
- size,
1172
- data: page,
1173
- fields: {
1174
- flags,
1175
- granule,
1176
- serial,
1177
- sequence,
1178
- checksum: r.u32(offset + 22, true),
1179
- lacing: laces
1180
- }
1181
- });
1182
- offset += size;
1183
- }
1184
- if (!logical.size || Array.from(logical.values()).some((s) => s.pending.length || !s.ended))
1185
- throw new Error("Incomplete Ogg stream");
1186
- return { nodes, packets };
1187
- }
1188
- function encodeOgg(packets) {
1189
- const pages = [], sequences = /* @__PURE__ */ new Map(), pending = /* @__PURE__ */ new Map();
1190
- for (const packet of packets) {
1191
- const group = pending.get(packet.serial) ?? [];
1192
- group.push(packet);
1193
- pending.set(packet.serial, group);
1194
- if (packet.granule === 0xffffffffffffffffn && !packet.eos) continue;
1195
- const laces = [], granules = [], boundaries = /* @__PURE__ */ new Set();
1196
- for (const item of group) {
1197
- for (let j = 0; j < Math.floor(item.data.length / 255); j++) {
1198
- laces.push(255);
1199
- granules.push(0xffffffffffffffffn);
1200
- }
1201
- laces.push(item.data.length % 255);
1202
- granules.push(item.granule);
1203
- boundaries.add(laces.length);
1204
- }
1205
- const body = join(group.map((p) => p.data));
1206
- let offset = 0;
1207
- for (let start = 0; start < laces.length; start += 255) {
1208
- const part = laces.slice(start, start + 255), last = start + 255 >= laces.length, size = part.reduce((a, b) => a + b, 0), header = new Uint8Array(27 + part.length), v = new DataView(header.buffer);
1209
- header.set(ascii("OggS"));
1210
- header[5] = (start && !boundaries.has(start) ? 1 : 0) | (group[0].bos && start === 0 ? 2 : 0) | (packet.eos && last ? 4 : 0);
1211
- let granule = 0xffffffffffffffffn;
1212
- for (let i = start; i < start + part.length; i++)
1213
- if (laces[i] < 255) granule = granules[i] === 0xffffffffffffffffn ? 0n : granules[i];
1214
- v.setBigUint64(6, granule, true);
1215
- v.setUint32(14, packet.serial, true);
1216
- const sequence = sequences.get(packet.serial) ?? 0;
1217
- v.setUint32(18, sequence, true);
1218
- sequences.set(packet.serial, sequence + 1);
1219
- header[26] = part.length;
1220
- header.set(part, 27);
1221
- const page = join([header, body.subarray(offset, offset + size)]);
1222
- new DataView(page.buffer).setUint32(22, oggCrc(page), true);
1223
- pages.push(page);
1224
- offset += size;
1225
- }
1226
- pending.delete(packet.serial);
1227
- }
1228
- if (pending.size) throw new Error("Ogg packets lack a final page granule");
1229
- return join(pages);
1230
- }
1231
- function parseOgg(bytes) {
1232
- const { nodes, packets } = readOgg(bytes), streams = [], tags = {}, pictures = [];
1233
- const serials = [...new Set(packets.map((p) => p.serial))];
1234
- for (const serial of serials) {
1235
- const group = packets.filter((p) => p.serial === serial), head = group[0].data, r = new Reader(head);
1236
- let codec, sampleRate, channels, preSkip = 0;
1237
- let comments;
1238
- if (head.length >= 8 && r.text(0, 8) === "OpusHead") {
1239
- if (head.length < 19 || r.u8(8) > 15) throw new Error("Invalid Opus identification");
1240
- codec = "opus";
1241
- sampleRate = 48e3;
1242
- channels = r.u8(9);
1243
- preSkip = r.u16(10, true);
1244
- const mapping = r.u8(18);
1245
- if (mapping === 0 && channels > 2) throw new Error("Invalid Opus channels");
1246
- if (mapping !== 0) r.check(19, 2 + channels);
1247
- const comment = group[1]?.data;
1248
- if (!comment || new Reader(comment).text(0, 8) !== "OpusTags")
1249
- throw new Error("Missing OpusTags");
1250
- const parsed = parseComments(comment.subarray(8));
1251
- Object.assign(tags, parsed.tags);
1252
- comments = parsed.comments;
1253
- } else if (head.length >= 7 && r.u8(0) === 1 && r.text(1, 6) === "vorbis") {
1254
- if (head.length !== 30 || r.u32(7, true) !== 0 || !(r.u8(29) & 1))
1255
- throw new Error("Invalid Vorbis identification");
1256
- codec = "vorbis";
1257
- channels = r.u8(11);
1258
- sampleRate = r.u32(12, true);
1259
- const comment = group[1]?.data;
1260
- if (!comment || comment[0] !== 3 || new Reader(comment).text(1, 6) !== "vorbis")
1261
- throw new Error("Missing Vorbis comments");
1262
- const parsed = parseComments(comment.subarray(7));
1263
- if (!(comment[7 + parsed.size] & 1)) throw new Error("Missing Vorbis comment framing bit");
1264
- Object.assign(tags, parsed.tags);
1265
- comments = parsed.comments;
1266
- } else throw new Error("Unsupported Ogg codec");
1267
- if (!channels || !sampleRate) throw new Error("Invalid Ogg audio stream");
1268
- const final = group.filter((p) => p.granule !== 0xffffffffffffffffn).at(-1)?.granule ?? 0n;
1269
- if (final > BigInt(Number.MAX_SAFE_INTEGER))
1270
- throw new Error("Ogg granule exceeds safe precision");
1271
- const samples = Math.max(0, Number(final) - preSkip), duration3 = samples / sampleRate;
1272
- const size = nodes.filter((n) => n.fields?.serial === serial).reduce((sum, n) => sum + n.size, 0);
1273
- const streamTags = {};
1274
- for (const comment of comments) {
1275
- const split = comment.indexOf("=");
1276
- if (split <= 0) continue;
1277
- const key = comment.slice(0, split), value = comment.slice(split + 1);
1278
- streamTags[key] = streamTags[key] ? `${streamTags[key]};${value}` : value;
1279
- }
1280
- streams.push({
1281
- tags: streamTags,
1282
- codec,
1283
- sampleRate,
1284
- channels,
1285
- samples,
1286
- duration: duration3,
1287
- bitrate: duration3 ? size * 8 / duration3 : 0
1288
- });
1289
- }
1290
- for (const value of (tags.METADATA_BLOCK_PICTURE ?? "").split("\n").filter(Boolean)) {
1291
- pictures.push(parsePicture(Uint8Array.from(atob(value), (c) => c.charCodeAt(0))));
1292
- }
1293
- let duration2 = 0, groupDuration = 0;
1294
- const active = /* @__PURE__ */ new Set();
1295
- for (const node of nodes) {
1296
- const serial = node.fields.serial, flags = node.fields.flags;
1297
- if (flags & 2) {
1298
- if (!active.size) {
1299
- duration2 += groupDuration;
1300
- groupDuration = 0;
1301
- }
1302
- active.add(serial);
1303
- groupDuration = Math.max(groupDuration, streams[serials.indexOf(serial)].duration);
1304
- }
1305
- if (flags & 4) active.delete(serial);
1306
- }
1307
- duration2 += groupDuration;
1308
- return {
1309
- format: "ogg",
1310
- data: bytes,
1311
- nodes,
1312
- streams,
1313
- tags,
1314
- pictures,
1315
- duration: duration2,
1316
- bitrate: duration2 ? bytes.length * 8 / duration2 : 0
1709
+ streams: [stream],
1710
+ duration: stream.duration,
1711
+ bitrate: stream.duration ? bytes.length * 8 / stream.duration : 0
1317
1712
  };
1318
1713
  }
1319
1714
 
@@ -1703,9 +2098,14 @@ export {
1703
2098
  parseAudio,
1704
2099
  probeAudio,
1705
2100
  probeFlacSource,
2101
+ probeId3Source,
2102
+ probeMp3Source,
2103
+ probeOggStreamSource,
1706
2104
  probeWavSource,
2105
+ readId3Text,
1707
2106
  remix,
1708
2107
  resample,
2108
+ scanOggPages,
1709
2109
  stats,
1710
2110
  transformAudio,
1711
2111
  trim,