hyperframes 0.8.106 → 0.8.108

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 (177) hide show
  1. package/dist/{add-QRAI4CAF.js → add-OH75E7FX.js} +16 -16
  2. package/dist/{animatedGifPrep-RZVYVAXD.js → animatedGifPrep-ZDAWSJYL.js} +7 -7
  3. package/dist/{asset-WFBQ5VS6.js → asset-BVDO4DEW.js} +2 -2
  4. package/dist/{assetCataloger-BQREHRS2.js → assetCataloger-FXEHOUA3.js} +3 -3
  5. package/dist/{auth-LVD6TWYG.js → auth-K5GUKO5S.js} +2 -2
  6. package/dist/{autoUpdate-HGE2PUZY.js → autoUpdate-77ROWK5G.js} +2 -2
  7. package/dist/{backgroundChecks-63CSE4XR.js → backgroundChecks-3Q3D5WBJ.js} +3 -3
  8. package/dist/backgroundChecksWorker.js +3 -3
  9. package/dist/{batchRender-AQN5PRXM.js → batchRender-ZTHFMHZY.js} +4 -4
  10. package/dist/{beats-OBCN5OSQ.js → beats-HQZPR6KE.js} +19 -13
  11. package/dist/{benchmark-DYHJSR7W.js → benchmark-BQOWARGU.js} +13 -13
  12. package/dist/{browser-JLYV5T7O.js → browser-45CK7HXI.js} +10 -10
  13. package/dist/{bundleWithLocalizedFonts-JKJKZR2S.js → bundleWithLocalizedFonts-3M2LKRHK.js} +2 -2
  14. package/dist/{capture-5BCJWVC7.js → capture-6IVZXM7G.js} +2 -2
  15. package/dist/{capture-QYLHIYKJ.js → capture-VGEUOP7N.js} +8 -8
  16. package/dist/{catalog-6UYNKENR.js → catalog-VHD7B6FD.js} +16 -16
  17. package/dist/{check-YOGAZ6UE.js → check-UPNZZJXV.js} +32 -31
  18. package/dist/{checkBrowser-M73YHPNM.js → checkBrowser-KWTHKOOP.js} +31 -30
  19. package/dist/{chunk-JYRPUDVZ.js → chunk-5N4KKGSZ.js} +1 -1
  20. package/dist/{normalize-audio-H3SI5V63.js → chunk-5ON5GH3X.js} +101 -209
  21. package/dist/{chunk-NRMYDPFQ.js → chunk-5R5UHSXN.js} +9 -9
  22. package/dist/{chunk-OSZJCBZX.js → chunk-AFGDOKRI.js} +7 -7
  23. package/dist/{chunk-6CN2QLB5.js → chunk-AWUZ3AUD.js} +1 -1
  24. package/dist/{chunk-TEXPOC5V.js → chunk-B25H4M4V.js} +2 -2
  25. package/dist/{chunk-JXXE3SWC.js → chunk-C6XQER6H.js} +28 -14
  26. package/dist/{chunk-Y2CZEJPK.js → chunk-CFC3EEDQ.js} +2 -2
  27. package/dist/{chunk-PB2OX2JY.js → chunk-D7SCIX4D.js} +1 -1
  28. package/dist/{chunk-AXWBDWAV.js → chunk-FZGUZIK5.js} +3 -3
  29. package/dist/{chunk-4WAMN4IF.js → chunk-GYYFNAVA.js} +1 -1
  30. package/dist/{chunk-TYA65W4U.js → chunk-HQGJLZB6.js} +2 -2
  31. package/dist/{chunk-LAHK4P7Q.js → chunk-J7XPPADY.js} +3 -3
  32. package/dist/{chunk-I77CAR4W.js → chunk-KVOWTRD5.js} +2 -2
  33. package/dist/{chunk-GVZYTDOD.js → chunk-KZ5OSG4G.js} +1 -1
  34. package/dist/{chunk-IBCGKQB5.js → chunk-KZJ6V7RT.js} +3 -3
  35. package/dist/{chunk-HUHJYNST.js → chunk-LAQEZHOH.js} +1 -1
  36. package/dist/{chunk-HFFR7TN7.js → chunk-LPXM5L6S.js} +2 -1
  37. package/dist/{chunk-2IZZZJDZ.js → chunk-LTYI4E42.js} +4 -4
  38. package/dist/{chunk-J3Q3YC6P.js → chunk-LZPAZZZP.js} +1 -1
  39. package/dist/{chunk-5TBFBLIX.js → chunk-MGTECHI3.js} +2 -2
  40. package/dist/{chunk-4I6AQUWG.js → chunk-NGVH2QYT.js} +8 -8
  41. package/dist/{chunk-M4KS6MJN.js → chunk-NJLFRKHX.js} +4 -4
  42. package/dist/{chunk-6LPWMCPY.js → chunk-NROJESOK.js} +2 -2
  43. package/dist/{chunk-UVUAU3CH.js → chunk-ONY5CJXS.js} +48 -26
  44. package/dist/{chunk-O5TBFZ7X.js → chunk-OVRFBUXU.js} +2 -2
  45. package/dist/{chunk-6F6EEQNB.js → chunk-PSGDNMM6.js} +1 -1
  46. package/dist/{chunk-AZQLJHAO.js → chunk-PZTEY73C.js} +1 -1
  47. package/dist/{chunk-VW5OMJNG.js → chunk-QB6OM2BP.js} +89 -70
  48. package/dist/{chunk-I6M4GXMC.js → chunk-QBHT234I.js} +1 -1
  49. package/dist/{chunk-TMLEIURT.js → chunk-QRXICW5Q.js} +1 -1
  50. package/dist/{chunk-4NJZZHYT.js → chunk-RPMXMOFU.js} +1 -1
  51. package/dist/{chunk-XZSH5GS7.js → chunk-RSL3XFMO.js} +4 -4
  52. package/dist/{chunk-PUB5XZYL.js → chunk-SIVCJJHE.js} +1 -1
  53. package/dist/{chunk-UUE5BZGE.js → chunk-SQ42NHXP.js} +620 -74
  54. package/dist/{chunk-GOI4GCUB.js → chunk-THVNQFYG.js} +1 -1
  55. package/dist/{chunk-IQ4UCWV7.js → chunk-TIZQC44K.js} +1 -1
  56. package/dist/{chunk-FTXU3TQ4.js → chunk-US7SWKGC.js} +1 -1
  57. package/dist/{chunk-TLYU5625.js → chunk-UUDQD62L.js} +6 -6
  58. package/dist/{chunk-2LCIQS3K.js → chunk-VBXJSCHR.js} +3 -3
  59. package/dist/{chunk-DVCYGZ7R.js → chunk-VLHN34SB.js} +2 -2
  60. package/dist/{chunk-4Z5O7BTD.js → chunk-WEATBQTR.js} +10 -1
  61. package/dist/{chunk-TH4APNRP.js → chunk-X763EUBO.js} +2 -2
  62. package/dist/{chunk-32Y3HRS3.js → chunk-XIGK4LNU.js} +2 -2
  63. package/dist/{chunk-R5RF3LGS.js → chunk-XMAL2A7Y.js} +1 -1
  64. package/dist/{chunk-USWOTTSP.js → chunk-XYVFPDNU.js} +1 -1
  65. package/dist/chunk-YLVI7VL5.js +3541 -0
  66. package/dist/{chunk-YVWILZCO.js → chunk-ZFL6FVQZ.js} +5 -5
  67. package/dist/{clean-GRX3GXKR.js → clean-LCRWNIWO.js} +17 -16
  68. package/dist/cli.js +50 -50
  69. package/dist/{cloud-TQCNBM6H.js → cloud-ITRFE5UN.js} +4 -4
  70. package/dist/{cloudrun-ISE6GSUO.js → cloudrun-LVZ4OPPC.js} +5 -5
  71. package/dist/{compare-QAWB3O4U.js → compare-NPVMUILH.js} +26 -25
  72. package/dist/{compiler-QMEZK6CU.js → compiler-YYOV2BJ7.js} +3 -3
  73. package/dist/{component-NXNNSXN7.js → component-HJFMHPNW.js} +3 -3
  74. package/dist/{compositions-I4PIPJKO.js → compositions-I34RN5WD.js} +12 -12
  75. package/dist/{delete-ZC4ZRBBF.js → delete-WN3J74BD.js} +4 -4
  76. package/dist/{deterministicFonts-XS3PGWH5.js → deterministicFonts-A4CNCSAL.js} +2 -2
  77. package/dist/{dist-E44WU6LH.js → dist-WCMFH74H.js} +1 -1
  78. package/dist/{dist-R24TLUYO.js → dist-YUIPB7OX.js} +9 -3
  79. package/dist/docs/data-attributes.md +1 -1
  80. package/dist/{doctor-EFQ4XNT3.js → doctor-MYC2S3J3.js} +11 -11
  81. package/dist/{events-AVPSJNYU.js → events-7JVV4746.js} +10 -10
  82. package/dist/{events-COIPJKSK.js → events-AEUV2JPA.js} +9 -9
  83. package/dist/{feedback-3DYZDMLC.js → feedback-X7DDSZPU.js} +11 -11
  84. package/dist/{figma-7XPHSMNG.js → figma-ISI4PPTF.js} +3 -3
  85. package/dist/fontLocalizeCli.js +10 -10
  86. package/dist/{get-TSBHDMKL.js → get-IYALAE4I.js} +4 -4
  87. package/dist/{gpuPolicy-BQFPO7UA.js → gpuPolicy-KSWTVU5C.js} +7 -7
  88. package/dist/{grade-compare-5Y4LXQFG.js → grade-compare-YWPIWQLK.js} +26 -25
  89. package/dist/{gsapParser-EZGJSSV5.js → gsapParser-KVMPC2SC.js} +5 -1
  90. package/dist/{help-5YBRP3C4.js → help-S2KJRMAJ.js} +1 -1
  91. package/dist/{history-SRNXKL6D.js → history-CQDDCY4J.js} +17 -16
  92. package/dist/hyperframe-runtime.js +54 -54
  93. package/dist/hyperframe.manifest.json +1 -1
  94. package/dist/hyperframe.runtime.iife.js +54 -54
  95. package/dist/hyperframes-player.global.js +3 -3
  96. package/dist/{info-U4BOXV2I.js → info-4KFNUWYN.js} +12 -12
  97. package/dist/{init-T4JYHIBM.js → init-I7GMRHFW.js} +98 -42
  98. package/dist/{inspect-XYLD2BMM.js → inspect-DUCLCHSD.js} +27 -26
  99. package/dist/{installer-UZSVMUKT.js → installer-NBEN3ZIP.js} +5 -5
  100. package/dist/{keyframes-BGPXWP7O.js → keyframes-F3VDEN66.js} +13 -13
  101. package/dist/{lambda-U4WIJQCJ.js → lambda-BFHJMMCG.js} +6 -6
  102. package/dist/{layout-UREZQ37U.js → layout-RNCXJI6C.js} +27 -26
  103. package/dist/{lint-LSTEZZ6T.js → lint-3NKKYCMH.js} +13 -13
  104. package/dist/{lintProject-4P4C6AN2.js → lintProject-F2OTN2IR.js} +1 -1
  105. package/dist/{list-6VICFTAM.js → list-Q7SOE3NF.js} +4 -4
  106. package/dist/{login-MXDIDOOG.js → login-TGRGWS5E.js} +4 -4
  107. package/dist/{media-treatment-ZVCWEACM.js → media-treatment-5TUXWHWE.js} +13 -13
  108. package/dist/{models-LXUIU5DC.js → models-YJUTM6TH.js} +8 -8
  109. package/dist/{motionShot-2LYUQF3T.js → motionShot-5QUMZ7J3.js} +11 -11
  110. package/dist/normalize-audio-63BI5BPL.js +276 -0
  111. package/dist/{pipeline-OFFKTFXS.js → pipeline-4NN5T7Q3.js} +7 -7
  112. package/dist/{play-P56CWNVC.js → play-SRDESSZI.js} +22 -21
  113. package/dist/{preflight-TVCVP5P2.js → preflight-SVN4PSFO.js} +8 -8
  114. package/dist/{present-ATMOWKMH.js → present-TDYR5LRW.js} +18 -17
  115. package/dist/{preview-7U6HYHYE.js → preview-Z5MFRZUM.js} +18 -18
  116. package/dist/{producer-LGG3TOUI.js → producer-RS7CB3XJ.js} +1 -1
  117. package/dist/{projectConfig-V53SEFIS.js → projectConfig-JHEWPAA5.js} +7 -7
  118. package/dist/{publication-7RKKPKLM.js → publication-7XA2H7UB.js} +3 -3
  119. package/dist/{publish-TRRFCEMQ.js → publish-2R22XX77.js} +9 -9
  120. package/dist/{remove-background-HJFT46Z5.js → remove-background-KT2SWYCC.js} +8 -8
  121. package/dist/{render-TPZ5FXHS.js → render-4C3YKVZI.js} +4 -4
  122. package/dist/{render-YB7BARWL.js → render-7GDLEGXT.js} +15 -15
  123. package/dist/{render-2QZBNE2V.js → render-Y2Y7N5WD.js} +22 -22
  124. package/dist/{render-batch-4ZF5C2LN.js → render-batch-AFDBPDMM.js} +4 -4
  125. package/dist/{renderCancellation-4PYGAN5D.js → renderCancellation-RQ3UVNVJ.js} +7 -7
  126. package/dist/renderSetupWorker.js +1 -1
  127. package/dist/{resolver-4JRJPC6S.js → resolver-S72XBTEQ.js} +5 -5
  128. package/dist/{sherpa-MNCWRFD4.js → sherpa-PR3I4KGM.js} +10 -10
  129. package/dist/{skills-ETBA5B46.js → skills-BEYZZWY6.js} +12 -12
  130. package/dist/{skillsUpdateCheck-DQOUIYV5.js → skillsUpdateCheck-3USXDPWV.js} +3 -3
  131. package/dist/{snapshot-W3HTBAYH.js → snapshot-S77MQOV2.js} +27 -26
  132. package/dist/{src-QX5PPANW.js → src-6R4DC2YI.js} +6 -6
  133. package/dist/{src-QF6RYX76.js → src-IOJXK4VC.js} +9 -9
  134. package/dist/{staticProjectServer-WBXW2CA4.js → staticProjectServer-RXAFGDK7.js} +16 -15
  135. package/dist/{status-C3IDZFDG.js → status-CRS7H6FA.js} +7 -7
  136. package/dist/studio/assets/{hyperframes-player-dl6K6ABp.js → hyperframes-player-BakGew45.js} +1 -1
  137. package/dist/studio/assets/{index-ooaZZhKf.js → index-C5dyUHwk.js} +1 -1
  138. package/dist/studio/assets/{index-CMiYVGv5.js → index-C842tcVa.js} +203 -208
  139. package/dist/studio/assets/{index-DKT5NF1Y.js → index-CPe7d_wO.js} +1 -1
  140. package/dist/studio/assets/{index-lrT-gfHa.css → index-ncVJ5IVu.css} +1 -1
  141. package/dist/studio/chunk-6LUNLTHR.js +611 -0
  142. package/dist/studio/chunk-6LUNLTHR.js.map +1 -0
  143. package/dist/studio/{chunk-ICTZJPQQ.js → chunk-AOWVEVEU.js} +3 -494
  144. package/dist/studio/chunk-AOWVEVEU.js.map +1 -0
  145. package/dist/studio/{chunk-46XP7J3B.js → chunk-PQFEBX32.js} +5 -3
  146. package/dist/studio/chunk-PQFEBX32.js.map +1 -0
  147. package/dist/studio/{domEditingLayers-AQBDFNUC.js → domEditingLayers-JQPQGGT7.js} +3 -3
  148. package/dist/studio/index.d.ts +377 -12
  149. package/dist/studio/index.html +2 -2
  150. package/dist/studio/index.js +13445 -11874
  151. package/dist/studio/index.js.map +1 -1
  152. package/dist/studio/player.js +2 -2
  153. package/dist/studio/styles.css +1 -1
  154. package/dist/{studio-api-6IJPJVG6.js → studio-api-3FH7OSSS.js} +10 -9
  155. package/dist/{studioServer-77CY4XPO.js → studioServer-4E5RRPU7.js} +49 -42
  156. package/dist/{system-OAB2LYBV.js → system-YMANAUAM.js} +7 -7
  157. package/dist/{telemetry-XTN5BO5S.js → telemetry-46XSB2VN.js} +10 -10
  158. package/dist/templates/_shared/AGENTS.md +1 -1
  159. package/dist/templates/_shared/CLAUDE.md +1 -1
  160. package/dist/templates/from-file/index.html +3 -2
  161. package/dist/templates/warm-grain/index.html +2 -10
  162. package/dist/{timeline-YKKTGPL7.js → timeline-XKLKHGD2.js} +17 -16
  163. package/dist/{tokens-LU25F3IJ.js → tokens-JCZKPUPT.js} +2 -2
  164. package/dist/{transcribe-O7OL4RRU.js → transcribe-FIFJ3BBB.js} +13 -13
  165. package/dist/{transcribe-AL3E5MBZ.js → transcribe-FQ4YC2AS.js} +10 -8
  166. package/dist/{updateCheck-MZZML4DL.js → updateCheck-PDIH4GZW.js} +2 -2
  167. package/dist/{upgrade-PQRTEJJY.js → upgrade-TTGO6JCF.js} +2 -2
  168. package/dist/{urlDownloader-U3TNOPD5.js → urlDownloader-32JRHOPN.js} +6 -6
  169. package/dist/{validate-KQETGCIR.js → validate-G5F56ONL.js} +16 -16
  170. package/dist/{video-KAHL76HG.js → video-SGBEMEKA.js} +7 -7
  171. package/package.json +1 -1
  172. package/dist/chunk-FOKUJKBN.js +0 -3420
  173. package/dist/studio/chunk-46XP7J3B.js.map +0 -1
  174. package/dist/studio/chunk-DI4LCJHL.js +0 -102
  175. package/dist/studio/chunk-DI4LCJHL.js.map +0 -1
  176. package/dist/studio/chunk-ICTZJPQQ.js.map +0 -1
  177. /package/dist/studio/{domEditingLayers-AQBDFNUC.js.map → domEditingLayers-JQPQGGT7.js.map} +0 -0
@@ -12,12 +12,12 @@ import {
12
12
  iterateFontFamilyDeclarations,
13
13
  normalizeSystemFontPrimaryFamilies,
14
14
  resolveFontFamilyDeclarationFamilies
15
- } from "./chunk-R5RF3LGS.js";
15
+ } from "./chunk-XMAL2A7Y.js";
16
16
  import {
17
17
  EncoderInterruptedError,
18
18
  encoderFailureError,
19
19
  prepareAnimatedGifInputs
20
- } from "./chunk-32Y3HRS3.js";
20
+ } from "./chunk-XIGK4LNU.js";
21
21
  import {
22
22
  createStudioPositionSeekReapplyScript
23
23
  } from "./chunk-KC5XXX3X.js";
@@ -145,7 +145,7 @@ import {
145
145
  validateEngineConfigSnapshot,
146
146
  verifyDiskDrawElementSamples,
147
147
  writeCapturedFrame
148
- } from "./chunk-VW5OMJNG.js";
148
+ } from "./chunk-QB6OM2BP.js";
149
149
  import {
150
150
  HF_VFX_ATTR,
151
151
  MEDIA_START_BASIS_ATTR,
@@ -162,7 +162,7 @@ import {
162
162
  resolveAbsoluteMediaStartSeconds,
163
163
  resolveAuthoredTimingWindow,
164
164
  toFps
165
- } from "./chunk-TEXPOC5V.js";
165
+ } from "./chunk-B25H4M4V.js";
166
166
  import {
167
167
  CANVAS_DIMENSIONS,
168
168
  HTML_BODY_CSS_HEIGHT_FIRST_RE,
@@ -173,7 +173,7 @@ import {
173
173
  isAspectAgnosticResolutionAlias,
174
174
  normalizeResolutionFlag,
175
175
  suggestMatchingPreset
176
- } from "./chunk-TLYU5625.js";
176
+ } from "./chunk-UUDQD62L.js";
177
177
  import {
178
178
  assignBundledRuntimeCompositionIds,
179
179
  buildVariablesByCompScript,
@@ -186,7 +186,7 @@ import {
186
186
  inlineSubCompositions,
187
187
  prepareFlattenedInnerRoot,
188
188
  styleElementsFor
189
- } from "./chunk-TH4APNRP.js";
189
+ } from "./chunk-X763EUBO.js";
190
190
  import {
191
191
  HF_COLOR_GRADING_ATTR,
192
192
  MAX_AUDIO_GAIN,
@@ -207,7 +207,7 @@ import {
207
207
  resolveNaturalMediaTimelineDurationFromValues,
208
208
  shouldClampResolvedMediaDuration,
209
209
  sourceTimeAt
210
- } from "./chunk-FOKUJKBN.js";
210
+ } from "./chunk-YLVI7VL5.js";
211
211
  import {
212
212
  chromeMajorCeiling
213
213
  } from "./chunk-TWWSYAKJ.js";
@@ -216,7 +216,7 @@ import {
216
216
  lintHyperframeHtml,
217
217
  readProjectFile,
218
218
  resolveProjectRelativeSrc
219
- } from "./chunk-UVUAU3CH.js";
219
+ } from "./chunk-ONY5CJXS.js";
220
220
  import {
221
221
  CSS_URL_RE,
222
222
  checkSubCompositionUsability,
@@ -8042,7 +8042,7 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
8042
8042
  for (const ext of extracted) {
8043
8043
  if (!ext.metadata.hasAudio) continue;
8044
8044
  const video = composition.videos.find((v) => v.id === ext.videoId);
8045
- if (!video || !video.hasAudio || existingAudioSrcs.has(video.src)) continue;
8045
+ if (!video || !video.hasAudio || video.hidden || existingAudioSrcs.has(video.src)) continue;
8046
8046
  composition.audios.push({
8047
8047
  id: `${video.id}-audio`,
8048
8048
  src: video.src,
@@ -9850,6 +9850,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
9850
9850
  } finally {
9851
9851
  for (const path of plan2.cleanupPaths) rmSync7(path, { force: true });
9852
9852
  }
9853
+ let loweredDb = 0;
9853
9854
  if (probeTruePeak) {
9854
9855
  const correction = await enforceAacTruePeak({
9855
9856
  audioPath: input.outputPath,
@@ -9869,13 +9870,15 @@ async function padOrTrimAudioToVideoFrameCount(input) {
9869
9870
  failureReason: correction.failureReason
9870
9871
  };
9871
9872
  }
9873
+ loweredDb = correction.loweredDb ?? 0;
9872
9874
  }
9873
9875
  return {
9874
9876
  success: true,
9875
9877
  outputPath: input.outputPath,
9876
9878
  targetDurationSeconds,
9877
9879
  sourceDurationSeconds: audioInfo.durationSeconds,
9878
- operation: plan2.operation
9880
+ operation: plan2.operation,
9881
+ ...loweredDb > 0 ? { audioLoweredDb: loweredDb } : {}
9879
9882
  };
9880
9883
  }
9881
9884
  async function enforceAacTruePeak(input) {
@@ -9896,8 +9899,9 @@ async function enforceAacTruePeak(input) {
9896
9899
  return { success: false, error: "audioPadTrim: FFmpeg reported an invalid true peak" };
9897
9900
  }
9898
9901
  if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
9899
- if (measuredPath === correctedPath) renameSync3(correctedPath, input.audioPath);
9900
- return { success: true };
9902
+ if (measuredPath !== correctedPath) return { success: true };
9903
+ renameSync3(correctedPath, input.audioPath);
9904
+ return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
9901
9905
  }
9902
9906
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
9903
9907
  attenuationDb += correctionTargetDbfs - truePeakDbfs;
@@ -10069,6 +10073,15 @@ async function runFfprobeJson(args, signal) {
10069
10073
  }
10070
10074
 
10071
10075
  // ../producer/src/services/render/stages/assembleStage.ts
10076
+ function recordLimiterAttenuation(job, audioLoweredDb) {
10077
+ if (audioLoweredDb === void 0) return;
10078
+ job.audioLoweredDb = audioLoweredDb;
10079
+ const ceiling = `\u2212${Math.abs(AAC_DELIVERY_TRUE_PEAK_DBFS)} dBTP`;
10080
+ (job.config.logger ?? defaultLogger).info(
10081
+ `Audio lowered by ${audioLoweredDb.toFixed(1)} dB to stay under ${ceiling}`,
10082
+ { audioLoweredDb }
10083
+ );
10084
+ }
10072
10085
  async function runAssembleStage(input) {
10073
10086
  const {
10074
10087
  job,
@@ -10098,6 +10111,7 @@ async function runAssembleStage(input) {
10098
10111
  if (!normalizeResult.success) {
10099
10112
  throw encoderFailureError("Audio duration normalization failed", normalizeResult);
10100
10113
  }
10114
+ recordLimiterAttenuation(job, normalizeResult.audioLoweredDb);
10101
10115
  if (isHls) {
10102
10116
  await runHlsPackaging(input, normalizeResult.outputPath);
10103
10117
  } else {
@@ -14130,7 +14144,7 @@ function startServer(options = {}) {
14130
14144
  });
14131
14145
  async function shutdown(signal) {
14132
14146
  log.info(`Received ${signal}, shutting down`);
14133
- const { drainBrowserPool } = await import("./src-QX5PPANW.js");
14147
+ const { drainBrowserPool } = await import("./src-6R4DC2YI.js");
14134
14148
  await drainBrowserPool().catch(() => {
14135
14149
  });
14136
14150
  const handle = await Promise.race([
@@ -15253,7 +15267,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15253
15267
  if (probeResult.fileServer) closeFileServerSafely(probeResult.fileServer, "plan", log);
15254
15268
  if (probeResult.probeSession) {
15255
15269
  try {
15256
- const { closeCaptureSession: closeCaptureSession2 } = await import("./src-QX5PPANW.js");
15270
+ const { closeCaptureSession: closeCaptureSession2 } = await import("./src-6R4DC2YI.js");
15257
15271
  await closeCaptureSession2(probeResult.probeSession);
15258
15272
  } catch (err) {
15259
15273
  log.warn("[plan] probe session close failed", {
@@ -19,10 +19,10 @@ import {
19
19
  import {
20
20
  formatVariableValidationIssue,
21
21
  validateVariables
22
- } from "./chunk-TEXPOC5V.js";
22
+ } from "./chunk-B25H4M4V.js";
23
23
  import {
24
24
  extractCompositionMetadata
25
- } from "./chunk-TLYU5625.js";
25
+ } from "./chunk-UUDQD62L.js";
26
26
 
27
27
  // src/utils/variables.ts
28
28
  import { readFileSync } from "fs";
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-TVCRUG2S.js";
14
14
  import {
15
15
  getFreeDiskMb
16
- } from "./chunk-PUB5XZYL.js";
16
+ } from "./chunk-SIVCJJHE.js";
17
17
  import {
18
18
  ensureBrowser,
19
19
  findBrowser
@@ -7,7 +7,7 @@ var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  getMimeType,
9
9
  injectMediaCodecMapIntoHtml
10
- } from "./chunk-UUE5BZGE.js";
10
+ } from "./chunk-SQ42NHXP.js";
11
11
  import {
12
12
  PROXY_VARIANT_CONFIG,
13
13
  ProxyCapacityError,
@@ -18,10 +18,10 @@ import {
18
18
  recordProxyRequest,
19
19
  resolveProxy,
20
20
  resolveProxyVariantRequest
21
- } from "./chunk-O5TBFZ7X.js";
21
+ } from "./chunk-OVRFBUXU.js";
22
22
  import {
23
23
  resolveAutoProxy
24
- } from "./chunk-HUHJYNST.js";
24
+ } from "./chunk-LAQEZHOH.js";
25
25
 
26
26
  // src/utils/staticProjectServer.ts
27
27
  import { createServer } from "http";
@@ -13,7 +13,7 @@ function resolveLocalBrowserGpuMode(browserGpuArg, envMode = process.env.PRODUCE
13
13
  return "auto";
14
14
  }
15
15
  async function resolveCaptureBrowserGpuMode(requestedMode, chromePath) {
16
- const { resolveBrowserGpuMode } = await import("./src-QX5PPANW.js");
16
+ const { resolveBrowserGpuMode } = await import("./src-6R4DC2YI.js");
17
17
  return resolveBrowserGpuMode(requestedMode, { chromePath });
18
18
  }
19
19
 
@@ -8,14 +8,14 @@ import {
8
8
  DEFAULT_REGISTRY_URL,
9
9
  fetchItemFile,
10
10
  validRegistryItem
11
- } from "./chunk-6CN2QLB5.js";
11
+ } from "./chunk-AWUZ3AUD.js";
12
12
  import {
13
13
  publishRegistryFile,
14
14
  registryTargetPath
15
15
  } from "./chunk-GMECXFAN.js";
16
16
  import {
17
17
  validateVariables
18
- } from "./chunk-TEXPOC5V.js";
18
+ } from "./chunk-B25H4M4V.js";
19
19
  import {
20
20
  realProjectRoot
21
21
  } from "./chunk-OFQWTKPW.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-ZTZA4UWZ.js";
13
13
  import {
14
14
  getSystemMeta
15
- } from "./chunk-PUB5XZYL.js";
15
+ } from "./chunk-SIVCJJHE.js";
16
16
  import {
17
17
  getInvocationId
18
18
  } from "./chunk-54HJ63Q2.js";
@@ -23,7 +23,7 @@ import {
23
23
  evaluateCanary,
24
24
  findCanary,
25
25
  parseCanaryOverride
26
- } from "./chunk-TEXPOC5V.js";
26
+ } from "./chunk-B25H4M4V.js";
27
27
  import {
28
28
  hostAnswers
29
29
  } from "./chunk-K4WIOZFP.js";
@@ -37,7 +37,7 @@ import {
37
37
  } from "./chunk-XUXGIA7H.js";
38
38
  import {
39
39
  VERSION
40
- } from "./chunk-6LPWMCPY.js";
40
+ } from "./chunk-NROJESOK.js";
41
41
 
42
42
  // src/telemetry/transport.ts
43
43
  import { spawn } from "child_process";
@@ -16,7 +16,7 @@ import {
16
16
  import {
17
17
  DEFAULT_VP9_CPU_USED,
18
18
  renderProvenanceArgs
19
- } from "./chunk-VW5OMJNG.js";
19
+ } from "./chunk-QB6OM2BP.js";
20
20
  import {
21
21
  loadOptionalPackage
22
22
  } from "./chunk-XT5P4QMO.js";
@@ -202,7 +202,7 @@ function inferInputKind(inputPath) {
202
202
  }
203
203
  async function probeMedia(inputPath) {
204
204
  const isImage = inferInputKind(inputPath) === "image";
205
- const engine = await import("./src-QX5PPANW.js");
205
+ const engine = await import("./src-6R4DC2YI.js");
206
206
  const meta = await engine.extractMediaMetadata(inputPath);
207
207
  if (isImage) {
208
208
  return { width: meta.width, height: meta.height, fps: 0, frameCount: 1 };
@@ -6,7 +6,7 @@ var __filename = __hf_fileURLToPath(import.meta.url);
6
6
  var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  updateNoticesSuppressed
9
- } from "./chunk-4NJZZHYT.js";
9
+ } from "./chunk-RPMXMOFU.js";
10
10
  import {
11
11
  hostAnswers
12
12
  } from "./chunk-K4WIOZFP.js";
@@ -7,14 +7,14 @@ var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  trackLintReport,
9
9
  trackLintRuleStreak
10
- } from "./chunk-2LCIQS3K.js";
10
+ } from "./chunk-VBXJSCHR.js";
11
11
  import {
12
12
  shouldTrack
13
- } from "./chunk-LAHK4P7Q.js";
13
+ } from "./chunk-J7XPPADY.js";
14
14
  import {
15
15
  LINT_RULE_COUNT,
16
16
  LINT_RULE_GROUP_COUNTS
17
- } from "./chunk-UVUAU3CH.js";
17
+ } from "./chunk-ONY5CJXS.js";
18
18
 
19
19
  // src/telemetry/lintStreaks.ts
20
20
  import { createHash } from "crypto";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-HVW7ABXD.js";
13
13
  import {
14
14
  DEFAULT_REGISTRY_URL
15
- } from "./chunk-6CN2QLB5.js";
15
+ } from "./chunk-AWUZ3AUD.js";
16
16
 
17
17
  // src/utils/projectConfig.ts
18
18
  import { readFileSync, writeFileSync } from "fs";
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-TVCRUG2S.js";
18
18
  import {
19
19
  stoppedByCancelSignal
20
- } from "./chunk-TMLEIURT.js";
20
+ } from "./chunk-QRXICW5Q.js";
21
21
 
22
22
  // src/whisper/transcribe.ts
23
23
  import { execFileSync } from "child_process";
@@ -392,6 +392,7 @@ export {
392
392
  whisperModelSlowdownFactor,
393
393
  resolveWhisperTimeoutMs,
394
394
  resolveAudioPreparationTimeoutMs,
395
+ getMediaDurationSeconds,
395
396
  getPreparedWavDurationSeconds,
396
397
  detectSpeechOnset,
397
398
  isPcm16kMono,
@@ -7,16 +7,16 @@ var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  parseFeedbackRating,
9
9
  trackRenderFeedback
10
- } from "./chunk-2LCIQS3K.js";
10
+ } from "./chunk-VBXJSCHR.js";
11
11
  import {
12
12
  shouldTrack
13
- } from "./chunk-LAHK4P7Q.js";
13
+ } from "./chunk-J7XPPADY.js";
14
14
  import {
15
15
  c
16
16
  } from "./chunk-ZTZA4UWZ.js";
17
17
  import {
18
18
  detectAgentRuntime
19
- } from "./chunk-PUB5XZYL.js";
19
+ } from "./chunk-SIVCJJHE.js";
20
20
  import {
21
21
  readConfig,
22
22
  writeConfig
@@ -83,7 +83,7 @@ async function maybePromptRenderFeedback(opts) {
83
83
  async function getDoctorSummary() {
84
84
  try {
85
85
  const [{ getSystemMeta }, { findFFmpeg }] = await Promise.all([
86
- import("./system-OAB2LYBV.js"),
86
+ import("./system-YMANAUAM.js"),
87
87
  import("./ffmpeg-QIUFEFTY.js")
88
88
  ]);
89
89
  const sys = getSystemMeta();
@@ -1278,7 +1278,7 @@ async function withFigmaErrors(command, fn) {
1278
1278
  } catch (err) {
1279
1279
  if (err instanceof Error) {
1280
1280
  try {
1281
- const { trackCommandFailure } = await import("./events-AVPSJNYU.js");
1281
+ const { trackCommandFailure } = await import("./events-7JVV4746.js");
1282
1282
  trackCommandFailure(
1283
1283
  command,
1284
1284
  err,
@@ -6,10 +6,10 @@ var __filename = __hf_fileURLToPath(import.meta.url);
6
6
  var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  getMimeType
9
- } from "./chunk-UUE5BZGE.js";
9
+ } from "./chunk-SQ42NHXP.js";
10
10
  import {
11
11
  injectTagsAtHeadStart
12
- } from "./chunk-FOKUJKBN.js";
12
+ } from "./chunk-YLVI7VL5.js";
13
13
 
14
14
  // src/utils/compositionServer.ts
15
15
  import { createHash } from "crypto";
@@ -6,19 +6,19 @@ var __filename = __hf_fileURLToPath(import.meta.url);
6
6
  var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  serveStaticProjectHtml
9
- } from "./chunk-AXWBDWAV.js";
9
+ } from "./chunk-FZGUZIK5.js";
10
10
  import {
11
11
  AUDIT_SEEK_OPTIONS,
12
12
  installPageFunctionGuard,
13
13
  seekCompositionTimeline,
14
14
  waitForCompositionFonts
15
- } from "./chunk-XZSH5GS7.js";
15
+ } from "./chunk-RSL3XFMO.js";
16
16
  import {
17
17
  resolveDiagnosticNavigationTimeoutMs
18
- } from "./chunk-IQ4UCWV7.js";
18
+ } from "./chunk-TIZQC44K.js";
19
19
  import {
20
20
  resolveProject
21
- } from "./chunk-FTXU3TQ4.js";
21
+ } from "./chunk-US7SWKGC.js";
22
22
  import {
23
23
  c
24
24
  } from "./chunk-ZTZA4UWZ.js";
@@ -32,7 +32,7 @@ import {
32
32
  import {
33
33
  printDeprecationNotice,
34
34
  withMeta
35
- } from "./chunk-4NJZZHYT.js";
35
+ } from "./chunk-RPMXMOFU.js";
36
36
  import {
37
37
  normalizeErrorMessage
38
38
  } from "./chunk-KNU5COPB.js";
@@ -665,7 +665,7 @@ async function collectTweenBoundaries(page) {
665
665
  });
666
666
  }
667
667
  async function bundleProjectHtml(projectDir) {
668
- const { bundleToSingleHtml } = await import("./compiler-QMEZK6CU.js");
668
+ const { bundleToSingleHtml } = await import("./compiler-YYOV2BJ7.js");
669
669
  return bundleToSingleHtml(projectDir);
670
670
  }
671
671
  async function alignViewportToComposition(page, url) {
@@ -687,13 +687,13 @@ async function alignViewportToComposition(page, url) {
687
687
  async function runLayoutAudit(projectDir, opts) {
688
688
  const { ensureBrowser } = await import("./manager-NEVLEZK3.js");
689
689
  const puppeteer = await import("puppeteer-core");
690
- const { buildChromeArgs } = await import("./src-QX5PPANW.js");
690
+ const { buildChromeArgs } = await import("./src-6R4DC2YI.js");
691
691
  const {
692
692
  assertWebGpuAdapterAvailable,
693
693
  compositionRequiresWebGpu,
694
694
  resolveCaptureBrowserGpuMode,
695
695
  resolveLocalBrowserGpuMode
696
- } = await import("./gpuPolicy-BQFPO7UA.js");
696
+ } = await import("./gpuPolicy-KSWTVU5C.js");
697
697
  const html = await bundleProjectHtml(projectDir);
698
698
  const server = await serveStaticProjectHtml(
699
699
  projectDir,
@@ -8,19 +8,19 @@ import {
8
8
  isAllowedHtmlAttribute,
9
9
  isSafeAttributeValue,
10
10
  sanitizeRichTextChildren
11
- } from "./chunk-TEXPOC5V.js";
11
+ } from "./chunk-B25H4M4V.js";
12
12
  import {
13
13
  removeElementWithGsapCascade
14
- } from "./chunk-TLYU5625.js";
14
+ } from "./chunk-UUDQD62L.js";
15
15
  import {
16
16
  EXCLUDED_TAGS,
17
17
  ensureHfIds,
18
18
  mintHfId,
19
19
  walkCompositionDescendants
20
- } from "./chunk-TH4APNRP.js";
20
+ } from "./chunk-X763EUBO.js";
21
21
  import {
22
22
  require_dist
23
- } from "./chunk-UVUAU3CH.js";
23
+ } from "./chunk-ONY5CJXS.js";
24
24
  import {
25
25
  readClipTiming,
26
26
  readMediaOffsetSeconds,
@@ -6,8 +6,8 @@ var __filename = __hf_fileURLToPath(import.meta.url);
6
6
  var __dirname = __hf_dirname(__filename);
7
7
 
8
8
  // src/version.ts
9
- var VERSION = true ? "0.8.106" : "0.0.0-dev";
10
- var PRODUCER_VERSION = true ? "0.8.106" : "0.0.0-dev";
9
+ var VERSION = true ? "0.8.108" : "0.0.0-dev";
10
+ var PRODUCER_VERSION = true ? "0.8.108" : "0.0.0-dev";
11
11
 
12
12
  export {
13
13
  VERSION,
@@ -4006,6 +4006,28 @@ function readAttr(tagSource, attr) {
4006
4006
  const match = tagSource.match(new RegExp(`(?<![\\w-])${escaped}\\s*=\\s*["']([^"']+)["']`, "i"));
4007
4007
  return match?.[1] || null;
4008
4008
  }
4009
+ function hasAttrName(tagSource, attr) {
4010
+ const escaped = attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4011
+ const attrs = tagSource.replace(/^<\s*[a-z][\w:-]*/i, "");
4012
+ return new RegExp(`(?:^|\\s)${escaped}(?:\\s*=|\\s|/?>)`, "i").test(attrs);
4013
+ }
4014
+ function isAudibleVideoTag(tagSource) {
4015
+ if (hasAttrName(tagSource, "muted")) return false;
4016
+ if (!hasAttrName(tagSource, "data-has-audio")) return true;
4017
+ const declared = readAttr(tagSource, "data-has-audio");
4018
+ return declared === "true";
4019
+ }
4020
+ function mediaTimeWindow(tagSource) {
4021
+ const start = Number(readAttr(tagSource, "data-start"));
4022
+ const duration = Number(readAttr(tagSource, "data-duration"));
4023
+ if (!readAttr(tagSource, "data-start") || !readAttr(tagSource, "data-duration")) return null;
4024
+ if (!Number.isFinite(start) || !Number.isFinite(duration)) return null;
4025
+ return { start, end: start + duration };
4026
+ }
4027
+ function mediaWindowsOverlap(a, b) {
4028
+ if (!a || !b) return true;
4029
+ return a.start < b.end && b.start < a.end;
4030
+ }
4009
4031
  function readDecodedAttr(tagSource, attr) {
4010
4032
  if (!tagSource) return null;
4011
4033
  let value = null;
@@ -5243,11 +5265,6 @@ function tweensVolumeInSameCall(script, id) {
5243
5265
  function escapeRegExp(value) {
5244
5266
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5245
5267
  }
5246
- function hasAttrName(tagSource, attr) {
5247
- const escaped = escapeRegExp(attr);
5248
- const attrs = tagSource.replace(/^<\s*[a-z][\w:-]*/i, "");
5249
- return new RegExp(`(?:^|\\s)${escaped}(?:\\s*=|\\s|/?>)`, "i").test(attrs);
5250
- }
5251
5268
  var IMAGE_SRC_EXT = /* @__PURE__ */ new Set([
5252
5269
  "jpg",
5253
5270
  "jpeg",
@@ -5648,14 +5665,15 @@ var mediaRules = [
5648
5665
  if (tag.name !== "video") continue;
5649
5666
  const hasMuted = hasAttrName(tag.raw, "muted");
5650
5667
  const hasDeclaredAudio = readAttr(tag.raw, "data-has-audio") === "true";
5651
- if (!hasMuted && !hasDeclaredAudio && readAttr(tag.raw, "data-start")) {
5668
+ const declaresAudioState = hasAttrName(tag.raw, "data-has-audio");
5669
+ if (!hasMuted && !declaresAudioState && readAttr(tag.raw, "data-start")) {
5652
5670
  const elementId = readAttr(tag.raw, "id") || void 0;
5653
5671
  findings.push({
5654
5672
  code: "video_missing_muted",
5655
5673
  severity: "error",
5656
- message: `<video${elementId ? ` id="${elementId}"` : ""}> has data-start but is not muted. Mark audible videos with data-has-audio="true"; otherwise keep video muted and use a separate <audio> element for sound.`,
5674
+ message: `<video${elementId ? ` id="${elementId}"` : ""}> has data-start but declares neither muted nor data-has-audio. If the file has sound, add data-has-audio="true" (the sound stays on this clip). If it is silent, add muted.`,
5657
5675
  elementId,
5658
- fixHint: 'Add the `muted` attribute for silent video, or add data-has-audio="true" when the video track should contribute audio.',
5676
+ fixHint: 'Add data-has-audio="true" when the file has sound (recommended \u2014 keeps picture and sound on one clip), or add `muted` for silent footage.',
5659
5677
  snippet: truncateSnippet(tag.raw)
5660
5678
  });
5661
5679
  }
@@ -5881,31 +5899,31 @@ var mediaRules = [
5881
5899
  // <audio> pointing to the same file, which causes double playback at runtime
5882
5900
  ({ tags }) => {
5883
5901
  const findings = [];
5884
- const videoSources = /* @__PURE__ */ new Map();
5885
- const audioSources = /* @__PURE__ */ new Map();
5902
+ const videos = [];
5903
+ const audios = [];
5886
5904
  for (const tag of tags) {
5887
5905
  if (!readAttr(tag.raw, "data-start")) continue;
5888
5906
  const src = readAttr(tag.raw, "src");
5889
5907
  if (!src) continue;
5890
- const elementId = readAttr(tag.raw, "id") || void 0;
5908
+ const source = { id: readAttr(tag.raw, "id") || void 0, raw: tag.raw };
5891
5909
  if (tag.name === "video") {
5892
- const isMuted = hasAttrName(tag.raw, "muted");
5893
- if (!isMuted) {
5894
- videoSources.set(src, { id: elementId, raw: tag.raw });
5895
- }
5910
+ if (isAudibleVideoTag(tag.raw)) videos.push([src, source]);
5896
5911
  } else if (tag.name === "audio") {
5897
- audioSources.set(src, { id: elementId, raw: tag.raw });
5912
+ audios.push([src, source]);
5898
5913
  }
5899
5914
  }
5900
- for (const [src, audioInfo] of audioSources) {
5901
- const videoInfo = videoSources.get(src);
5902
- if (!videoInfo) continue;
5915
+ for (const [src, audioInfo] of audios) {
5916
+ const match = videos.find(
5917
+ ([videoSrc, video]) => videoSrc === src && mediaWindowsOverlap(mediaTimeWindow(video.raw), mediaTimeWindow(audioInfo.raw))
5918
+ );
5919
+ if (!match) continue;
5920
+ const videoInfo = match[1];
5903
5921
  findings.push({
5904
5922
  code: "video_audio_double_source",
5905
5923
  severity: "error",
5906
- message: `<audio${audioInfo.id ? ` id="${audioInfo.id}"` : ""}> and <video${videoInfo.id ? ` id="${videoInfo.id}"` : ""}> both point to the same source. The unmuted video already provides audio \u2014 the duplicate <audio> will cause double playback and echo.`,
5924
+ message: `<audio${audioInfo.id ? ` id="${audioInfo.id}"` : ""}> and <video${videoInfo.id ? ` id="${videoInfo.id}"` : ""}> both point to the same source at the same time. The unmuted video already provides audio \u2014 the duplicate <audio> will cause double playback and echo.`,
5907
5925
  elementId: audioInfo.id,
5908
- fixHint: "Either mute the video (add `muted` attribute) and keep the separate <audio>, or remove the <audio> element and let the video provide its own audio track.",
5926
+ fixHint: "Remove the <audio> element and let the video carry its own sound (recommended), or mute the video (add `muted`) and keep the separate <audio> when picture and sound must be cut independently.",
5909
5927
  snippet: truncateSnippet(audioInfo.raw)
5910
5928
  });
5911
5929
  }
@@ -6014,7 +6032,7 @@ function findCarveUngroupedSourcesFindings(ctx) {
6014
6032
  var AUDIO_GROUP_TIMING_ATTRS = ["data-start", "data-duration", "data-track-index"];
6015
6033
  function findAudioGroupNoMembersFindings(ctx) {
6016
6034
  const memberGroupIds = new Set(
6017
- ctx.tags.filter((tag) => tag.name === "audio").map((tag) => readAttr(tag.raw, "data-audio-group")).filter((id) => Boolean(id))
6035
+ ctx.tags.filter((tag) => tag.name === "audio" || tag.name === "video" && isAudibleVideoTag(tag.raw)).map((tag) => readAttr(tag.raw, "data-audio-group")).filter((id) => Boolean(id))
6018
6036
  );
6019
6037
  if (memberGroupIds.size === 0) return [];
6020
6038
  const mayHaveCrossFileMembers = ctx.tags.some(
@@ -10404,7 +10422,10 @@ function lintProjectAudioFiles(projectDir, htmlSources) {
10404
10422
  }
10405
10423
  if (audioFiles.length === 0) return findings;
10406
10424
  const hasAudioElement = htmlSources.some(({ html }) => /<audio\b/i.test(html));
10407
- if (!hasAudioElement) {
10425
+ const hasAudibleVideo = htmlSources.some(
10426
+ ({ html }) => (html.match(/<video\b[^>]*>/gi) ?? []).some(isAudibleVideoTag)
10427
+ );
10428
+ if (!hasAudioElement && !hasAudibleVideo) {
10408
10429
  findings.push({
10409
10430
  code: "audio_file_without_element",
10410
10431
  severity: "warning",
@@ -10554,10 +10575,11 @@ function lintDuplicateAudioTracks(htmlSources) {
10554
10575
  const tracks = [];
10555
10576
  const seen = /* @__PURE__ */ new Set();
10556
10577
  for (const { html } of htmlSources) {
10557
- const audioTagRe = /<audio\b[^>]*>/gi;
10578
+ const soundTagRe = /<(?:audio|video)\b[^>]*>/gi;
10558
10579
  let match;
10559
- while ((match = audioTagRe.exec(html)) !== null) {
10580
+ while ((match = soundTagRe.exec(html)) !== null) {
10560
10581
  const tag = match[0];
10582
+ if (/^<video/i.test(tag) && !isAudibleVideoTag(tag)) continue;
10561
10583
  const trackStr = extractAttr(tag, "data-track-index");
10562
10584
  const startStr = extractAttr(tag, "data-start");
10563
10585
  const durStr = extractAttr(tag, "data-duration");
@@ -10581,7 +10603,7 @@ function lintDuplicateAudioTracks(htmlSources) {
10581
10603
  findings.push({
10582
10604
  code: "duplicate_audio_track",
10583
10605
  severity: "warning",
10584
- message: `Multiple <audio> elements on track ${a.trackIndex} overlap (${a.src} at ${a.start}-${Number.isFinite(a.end) ? a.end.toFixed(1) : "end"}s, ${b.src} at ${b.start}-${Number.isFinite(b.end) ? b.end.toFixed(1) : "end"}s). This causes layered audio playback.`,
10606
+ message: `Multiple audible <audio>/<video> elements on track ${a.trackIndex} overlap (${a.src} at ${a.start}-${Number.isFinite(a.end) ? a.end.toFixed(1) : "end"}s, ${b.src} at ${b.start}-${Number.isFinite(b.end) ? b.end.toFixed(1) : "end"}s). This causes layered audio playback.`,
10585
10607
  fixHint: "Use non-overlapping time windows or different track indices."
10586
10608
  });
10587
10609
  }
@@ -8,7 +8,7 @@ import {
8
8
  firstFrameColourArgs,
9
9
  hdrToSdrToneMapFilter,
10
10
  parseFirstFrameColour
11
- } from "./chunk-TEXPOC5V.js";
11
+ } from "./chunk-B25H4M4V.js";
12
12
  import {
13
13
  mkdirWithinProject,
14
14
  realProjectRoot,
@@ -21,7 +21,7 @@ import {
21
21
  isUnresolvedAssetPlaceholder,
22
22
  maskNonScannableRanges,
23
23
  resolveLocalAssetCandidates
24
- } from "./chunk-UVUAU3CH.js";
24
+ } from "./chunk-ONY5CJXS.js";
25
25
  import {
26
26
  findFfBinary
27
27
  } from "./chunk-PJ3HDUCY.js";
@@ -6,7 +6,7 @@ var __filename = __hf_fileURLToPath(import.meta.url);
6
6
  var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  resolveWritePath
9
- } from "./chunk-4Z5O7BTD.js";
9
+ } from "./chunk-WEATBQTR.js";
10
10
 
11
11
  // src/cloud/download.ts
12
12
  import {