@kvman/kvvideo 0.0.0-stage → 0.1.1

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 (345) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/dist/api.d.ts +105 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/api.js +2 -0
  6. package/dist/api.js.map +1 -0
  7. package/dist/brand/brand.d.ts +17 -0
  8. package/dist/brand/brand.d.ts.map +1 -0
  9. package/dist/brand/brand.js +44 -0
  10. package/dist/brand/brand.js.map +1 -0
  11. package/dist/browser/channels.d.ts +6 -0
  12. package/dist/browser/channels.d.ts.map +1 -0
  13. package/dist/browser/channels.js +5 -0
  14. package/dist/browser/channels.js.map +1 -0
  15. package/dist/browser/launch.d.ts +6 -0
  16. package/dist/browser/launch.d.ts.map +1 -0
  17. package/dist/browser/launch.js +14 -0
  18. package/dist/browser/launch.js.map +1 -0
  19. package/dist/browser/start-browser.d.ts +5 -0
  20. package/dist/browser/start-browser.d.ts.map +1 -0
  21. package/dist/browser/start-browser.js +17 -0
  22. package/dist/browser/start-browser.js.map +1 -0
  23. package/dist/compose/ass-file.d.ts +17 -0
  24. package/dist/compose/ass-file.d.ts.map +1 -0
  25. package/dist/compose/ass-file.js +65 -0
  26. package/dist/compose/ass-file.js.map +1 -0
  27. package/dist/compose/background.d.ts +23 -0
  28. package/dist/compose/background.d.ts.map +1 -0
  29. package/dist/compose/background.js +31 -0
  30. package/dist/compose/background.js.map +1 -0
  31. package/dist/compose/camera.d.ts +32 -0
  32. package/dist/compose/camera.d.ts.map +1 -0
  33. package/dist/compose/camera.js +98 -0
  34. package/dist/compose/camera.js.map +1 -0
  35. package/dist/compose/caption-font.d.ts +8 -0
  36. package/dist/compose/caption-font.d.ts.map +1 -0
  37. package/dist/compose/caption-font.js +24 -0
  38. package/dist/compose/caption-font.js.map +1 -0
  39. package/dist/compose/compose-drawing.d.ts +19 -0
  40. package/dist/compose/compose-drawing.d.ts.map +1 -0
  41. package/dist/compose/compose-drawing.js +38 -0
  42. package/dist/compose/compose-drawing.js.map +1 -0
  43. package/dist/compose/compose-input.d.ts +187 -0
  44. package/dist/compose/compose-input.d.ts.map +1 -0
  45. package/dist/compose/compose-input.js +76 -0
  46. package/dist/compose/compose-input.js.map +1 -0
  47. package/dist/compose/compose-plan.d.ts +36 -0
  48. package/dist/compose/compose-plan.d.ts.map +1 -0
  49. package/dist/compose/compose-plan.js +55 -0
  50. package/dist/compose/compose-plan.js.map +1 -0
  51. package/dist/compose/compose-size.d.ts +50 -0
  52. package/dist/compose/compose-size.d.ts.map +1 -0
  53. package/dist/compose/compose-size.js +72 -0
  54. package/dist/compose/compose-size.js.map +1 -0
  55. package/dist/compose/drawn-images.d.ts +19 -0
  56. package/dist/compose/drawn-images.d.ts.map +1 -0
  57. package/dist/compose/drawn-images.js +75 -0
  58. package/dist/compose/drawn-images.js.map +1 -0
  59. package/dist/compose/final-pass.d.ts +34 -0
  60. package/dist/compose/final-pass.d.ts.map +1 -0
  61. package/dist/compose/final-pass.js +106 -0
  62. package/dist/compose/final-pass.js.map +1 -0
  63. package/dist/compose/frame-layout.d.ts +30 -0
  64. package/dist/compose/frame-layout.d.ts.map +1 -0
  65. package/dist/compose/frame-layout.js +33 -0
  66. package/dist/compose/frame-layout.js.map +1 -0
  67. package/dist/compose/overlays.d.ts +20 -0
  68. package/dist/compose/overlays.d.ts.map +1 -0
  69. package/dist/compose/overlays.js +30 -0
  70. package/dist/compose/overlays.js.map +1 -0
  71. package/dist/compose/page-again.d.ts +26 -0
  72. package/dist/compose/page-again.d.ts.map +1 -0
  73. package/dist/compose/page-again.js +50 -0
  74. package/dist/compose/page-again.js.map +1 -0
  75. package/dist/compose/player-copies.d.ts +10 -0
  76. package/dist/compose/player-copies.d.ts.map +1 -0
  77. package/dist/compose/player-copies.js +34 -0
  78. package/dist/compose/player-copies.js.map +1 -0
  79. package/dist/compose/register-compose.d.ts +3 -0
  80. package/dist/compose/register-compose.d.ts.map +1 -0
  81. package/dist/compose/register-compose.js +151 -0
  82. package/dist/compose/register-compose.js.map +1 -0
  83. package/dist/compose/render.d.ts +25 -0
  84. package/dist/compose/render.d.ts.map +1 -0
  85. package/dist/compose/render.js +61 -0
  86. package/dist/compose/render.js.map +1 -0
  87. package/dist/compose/scene-args.d.ts +37 -0
  88. package/dist/compose/scene-args.d.ts.map +1 -0
  89. package/dist/compose/scene-args.js +103 -0
  90. package/dist/compose/scene-args.js.map +1 -0
  91. package/dist/compose/scene-look.d.ts +31 -0
  92. package/dist/compose/scene-look.d.ts.map +1 -0
  93. package/dist/compose/scene-look.js +46 -0
  94. package/dist/compose/scene-look.js.map +1 -0
  95. package/dist/compose/scene-plan.d.ts +26 -0
  96. package/dist/compose/scene-plan.d.ts.map +1 -0
  97. package/dist/compose/scene-plan.js +34 -0
  98. package/dist/compose/scene-plan.js.map +1 -0
  99. package/dist/compose/sound-effects.d.ts +23 -0
  100. package/dist/compose/sound-effects.d.ts.map +1 -0
  101. package/dist/compose/sound-effects.js +40 -0
  102. package/dist/compose/sound-effects.js.map +1 -0
  103. package/dist/compose/srt.d.ts +3 -0
  104. package/dist/compose/srt.d.ts.map +1 -0
  105. package/dist/compose/srt.js +10 -0
  106. package/dist/compose/srt.js.map +1 -0
  107. package/dist/compose/transitions.d.ts +16 -0
  108. package/dist/compose/transitions.d.ts.map +1 -0
  109. package/dist/compose/transitions.js +38 -0
  110. package/dist/compose/transitions.js.map +1 -0
  111. package/dist/connector.d.ts +132 -0
  112. package/dist/connector.d.ts.map +1 -0
  113. package/dist/connector.js +63 -0
  114. package/dist/connector.js.map +1 -0
  115. package/dist/files/answer-path.d.ts +3 -0
  116. package/dist/files/answer-path.d.ts.map +1 -0
  117. package/dist/files/answer-path.js +6 -0
  118. package/dist/files/answer-path.js.map +1 -0
  119. package/dist/files/place-output.d.ts +8 -0
  120. package/dist/files/place-output.d.ts.map +1 -0
  121. package/dist/files/place-output.js +24 -0
  122. package/dist/files/place-output.js.map +1 -0
  123. package/dist/files/sidecar.d.ts +209 -0
  124. package/dist/files/sidecar.d.ts.map +1 -0
  125. package/dist/files/sidecar.js +66 -0
  126. package/dist/files/sidecar.js.map +1 -0
  127. package/dist/files/workspace-paths.d.ts +7 -0
  128. package/dist/files/workspace-paths.d.ts.map +1 -0
  129. package/dist/files/workspace-paths.js +48 -0
  130. package/dist/files/workspace-paths.js.map +1 -0
  131. package/dist/fonts/bundled-fonts.d.ts +10 -0
  132. package/dist/fonts/bundled-fonts.d.ts.map +1 -0
  133. package/dist/fonts/bundled-fonts.js +10 -0
  134. package/dist/fonts/bundled-fonts.js.map +1 -0
  135. package/dist/fonts/font-family.d.ts +2 -0
  136. package/dist/fonts/font-family.d.ts.map +1 -0
  137. package/dist/fonts/font-family.js +43 -0
  138. package/dist/fonts/font-family.js.map +1 -0
  139. package/dist/fonts/register-fonts.d.ts +3 -0
  140. package/dist/fonts/register-fonts.d.ts.map +1 -0
  141. package/dist/fonts/register-fonts.js +12 -0
  142. package/dist/fonts/register-fonts.js.map +1 -0
  143. package/dist/guide.d.ts +3 -0
  144. package/dist/guide.d.ts.map +1 -0
  145. package/dist/guide.js +6 -0
  146. package/dist/guide.js.map +1 -0
  147. package/dist/index.d.ts +5 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +30 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/narration/alignment.d.ts +15 -0
  152. package/dist/narration/alignment.d.ts.map +1 -0
  153. package/dist/narration/alignment.js +21 -0
  154. package/dist/narration/alignment.js.map +1 -0
  155. package/dist/narration/captions.d.ts +28 -0
  156. package/dist/narration/captions.d.ts.map +1 -0
  157. package/dist/narration/captions.js +70 -0
  158. package/dist/narration/captions.js.map +1 -0
  159. package/dist/narration/join-args.d.ts +3 -0
  160. package/dist/narration/join-args.d.ts.map +1 -0
  161. package/dist/narration/join-args.js +19 -0
  162. package/dist/narration/join-args.js.map +1 -0
  163. package/dist/narration/narration-cache.d.ts +25 -0
  164. package/dist/narration/narration-cache.d.ts.map +1 -0
  165. package/dist/narration/narration-cache.js +36 -0
  166. package/dist/narration/narration-cache.js.map +1 -0
  167. package/dist/narration/piper.d.ts +4 -0
  168. package/dist/narration/piper.d.ts.map +1 -0
  169. package/dist/narration/piper.js +13 -0
  170. package/dist/narration/piper.js.map +1 -0
  171. package/dist/narration/pronunciations.d.ts +11 -0
  172. package/dist/narration/pronunciations.d.ts.map +1 -0
  173. package/dist/narration/pronunciations.js +40 -0
  174. package/dist/narration/pronunciations.js.map +1 -0
  175. package/dist/narration/register-narration.d.ts +25 -0
  176. package/dist/narration/register-narration.d.ts.map +1 -0
  177. package/dist/narration/register-narration.js +103 -0
  178. package/dist/narration/register-narration.js.map +1 -0
  179. package/dist/narration/register-voice.d.ts +27 -0
  180. package/dist/narration/register-voice.d.ts.map +1 -0
  181. package/dist/narration/register-voice.js +82 -0
  182. package/dist/narration/register-voice.js.map +1 -0
  183. package/dist/narration/sentence-speaker.d.ts +35 -0
  184. package/dist/narration/sentence-speaker.d.ts.map +1 -0
  185. package/dist/narration/sentence-speaker.js +44 -0
  186. package/dist/narration/sentence-speaker.js.map +1 -0
  187. package/dist/narration/speech-services.d.ts +39 -0
  188. package/dist/narration/speech-services.d.ts.map +1 -0
  189. package/dist/narration/speech-services.js +67 -0
  190. package/dist/narration/speech-services.js.map +1 -0
  191. package/dist/narration/voice-args.d.ts +3 -0
  192. package/dist/narration/voice-args.d.ts.map +1 -0
  193. package/dist/narration/voice-args.js +10 -0
  194. package/dist/narration/voice-args.js.map +1 -0
  195. package/dist/problems.d.ts +12 -0
  196. package/dist/problems.d.ts.map +1 -0
  197. package/dist/problems.js +19 -0
  198. package/dist/problems.js.map +1 -0
  199. package/dist/programs/kill-tree.d.ts +16 -0
  200. package/dist/programs/kill-tree.d.ts.map +1 -0
  201. package/dist/programs/kill-tree.js +28 -0
  202. package/dist/programs/kill-tree.js.map +1 -0
  203. package/dist/programs/media-duration.d.ts +22 -0
  204. package/dist/programs/media-duration.d.ts.map +1 -0
  205. package/dist/programs/media-duration.js +23 -0
  206. package/dist/programs/media-duration.js.map +1 -0
  207. package/dist/programs/piped-program.d.ts +7 -0
  208. package/dist/programs/piped-program.d.ts.map +1 -0
  209. package/dist/programs/piped-program.js +45 -0
  210. package/dist/programs/piped-program.js.map +1 -0
  211. package/dist/programs/programs.d.ts +6 -0
  212. package/dist/programs/programs.d.ts.map +1 -0
  213. package/dist/programs/programs.js +6 -0
  214. package/dist/programs/programs.js.map +1 -0
  215. package/dist/programs/run-program.d.ts +23 -0
  216. package/dist/programs/run-program.d.ts.map +1 -0
  217. package/dist/programs/run-program.js +47 -0
  218. package/dist/programs/run-program.js.map +1 -0
  219. package/dist/record/address.d.ts +5 -0
  220. package/dist/record/address.d.ts.map +1 -0
  221. package/dist/record/address.js +26 -0
  222. package/dist/record/address.js.map +1 -0
  223. package/dist/record/cursor-script.d.ts +3 -0
  224. package/dist/record/cursor-script.d.ts.map +1 -0
  225. package/dist/record/cursor-script.js +59 -0
  226. package/dist/record/cursor-script.js.map +1 -0
  227. package/dist/record/encoder-args.d.ts +3 -0
  228. package/dist/record/encoder-args.d.ts.map +1 -0
  229. package/dist/record/encoder-args.js +13 -0
  230. package/dist/record/encoder-args.js.map +1 -0
  231. package/dist/record/frame-writer.d.ts +8 -0
  232. package/dist/record/frame-writer.d.ts.map +1 -0
  233. package/dist/record/frame-writer.js +30 -0
  234. package/dist/record/frame-writer.js.map +1 -0
  235. package/dist/record/motion.d.ts +19 -0
  236. package/dist/record/motion.d.ts.map +1 -0
  237. package/dist/record/motion.js +59 -0
  238. package/dist/record/motion.js.map +1 -0
  239. package/dist/record/narration-cues.d.ts +14 -0
  240. package/dist/record/narration-cues.d.ts.map +1 -0
  241. package/dist/record/narration-cues.js +12 -0
  242. package/dist/record/narration-cues.js.map +1 -0
  243. package/dist/record/own-port.d.ts +2 -0
  244. package/dist/record/own-port.d.ts.map +1 -0
  245. package/dist/record/own-port.js +23 -0
  246. package/dist/record/own-port.js.map +1 -0
  247. package/dist/record/page-ready.d.ts +4 -0
  248. package/dist/record/page-ready.d.ts.map +1 -0
  249. package/dist/record/page-ready.js +15 -0
  250. package/dist/record/page-ready.js.map +1 -0
  251. package/dist/record/page-route.d.ts +13 -0
  252. package/dist/record/page-route.d.ts.map +1 -0
  253. package/dist/record/page-route.js +64 -0
  254. package/dist/record/page-route.js.map +1 -0
  255. package/dist/record/pointer.d.ts +8 -0
  256. package/dist/record/pointer.d.ts.map +1 -0
  257. package/dist/record/pointer.js +24 -0
  258. package/dist/record/pointer.js.map +1 -0
  259. package/dist/record/recorder.d.ts +34 -0
  260. package/dist/record/recorder.d.ts.map +1 -0
  261. package/dist/record/recorder.js +99 -0
  262. package/dist/record/recorder.js.map +1 -0
  263. package/dist/record/register-record.d.ts +3 -0
  264. package/dist/record/register-record.d.ts.map +1 -0
  265. package/dist/record/register-record.js +83 -0
  266. package/dist/record/register-record.js.map +1 -0
  267. package/dist/record/run-step.d.ts +20 -0
  268. package/dist/record/run-step.d.ts.map +1 -0
  269. package/dist/record/run-step.js +69 -0
  270. package/dist/record/run-step.js.map +1 -0
  271. package/dist/record/screencast.d.ts +9 -0
  272. package/dist/record/screencast.d.ts.map +1 -0
  273. package/dist/record/screencast.js +38 -0
  274. package/dist/record/screencast.js.map +1 -0
  275. package/dist/record/steps.d.ts +124 -0
  276. package/dist/record/steps.d.ts.map +1 -0
  277. package/dist/record/steps.js +47 -0
  278. package/dist/record/steps.js.map +1 -0
  279. package/dist/register-with-kvcoder.d.ts +3 -0
  280. package/dist/register-with-kvcoder.d.ts.map +1 -0
  281. package/dist/register-with-kvcoder.js +15 -0
  282. package/dist/register-with-kvcoder.js.map +1 -0
  283. package/dist/review/analysis.d.ts +19 -0
  284. package/dist/review/analysis.d.ts.map +1 -0
  285. package/dist/review/analysis.js +36 -0
  286. package/dist/review/analysis.js.map +1 -0
  287. package/dist/review/frames.d.ts +9 -0
  288. package/dist/review/frames.d.ts.map +1 -0
  289. package/dist/review/frames.js +28 -0
  290. package/dist/review/frames.js.map +1 -0
  291. package/dist/review/register-review.d.ts +24 -0
  292. package/dist/review/register-review.d.ts.map +1 -0
  293. package/dist/review/register-review.js +81 -0
  294. package/dist/review/register-review.js.map +1 -0
  295. package/dist/review/sheet-args.d.ts +11 -0
  296. package/dist/review/sheet-args.d.ts.map +1 -0
  297. package/dist/review/sheet-args.js +24 -0
  298. package/dist/review/sheet-args.js.map +1 -0
  299. package/dist/settings.d.ts +33 -0
  300. package/dist/settings.d.ts.map +1 -0
  301. package/dist/settings.js +29 -0
  302. package/dist/settings.js.map +1 -0
  303. package/dist/setup/browser-readiness.d.ts +14 -0
  304. package/dist/setup/browser-readiness.d.ts.map +1 -0
  305. package/dist/setup/browser-readiness.js +16 -0
  306. package/dist/setup/browser-readiness.js.map +1 -0
  307. package/dist/setup/ffmpeg-readiness.d.ts +19 -0
  308. package/dist/setup/ffmpeg-readiness.d.ts.map +1 -0
  309. package/dist/setup/ffmpeg-readiness.js +33 -0
  310. package/dist/setup/ffmpeg-readiness.js.map +1 -0
  311. package/dist/setup/narration-readiness.d.ts +21 -0
  312. package/dist/setup/narration-readiness.d.ts.map +1 -0
  313. package/dist/setup/narration-readiness.js +18 -0
  314. package/dist/setup/narration-readiness.js.map +1 -0
  315. package/dist/setup/register-setup.d.ts +3 -0
  316. package/dist/setup/register-setup.d.ts.map +1 -0
  317. package/dist/setup/register-setup.js +51 -0
  318. package/dist/setup/register-setup.js.map +1 -0
  319. package/dist/ui.d.ts +3 -0
  320. package/dist/ui.d.ts.map +1 -0
  321. package/dist/ui.js +26 -0
  322. package/dist/ui.js.map +1 -0
  323. package/dist/video-start.d.ts +3 -0
  324. package/dist/video-start.d.ts.map +1 -0
  325. package/dist/video-start.js +28 -0
  326. package/dist/video-start.js.map +1 -0
  327. package/dist/web/components/player.css +2 -0
  328. package/dist/web/components/player.js +55 -0
  329. package/docs/guide.md +28 -0
  330. package/fonts/Amiri-Bold.ttf +0 -0
  331. package/fonts/IBMPlexSansArabic-Bold.ttf +0 -0
  332. package/fonts/Lato-Bold.ttf +0 -0
  333. package/fonts/OFL-Amiri.txt +94 -0
  334. package/fonts/OFL-IBMPlexSansArabic.txt +93 -0
  335. package/fonts/OFL-Lato.txt +93 -0
  336. package/fonts/OFL-Poppins.txt +93 -0
  337. package/fonts/Poppins-Bold.ttf +0 -0
  338. package/locales/ar.json +48 -0
  339. package/locales/en.json +48 -0
  340. package/package.json +73 -3
  341. package/sounds/SOURCES.md +13 -0
  342. package/sounds/click.wav +0 -0
  343. package/sounds/keys.wav +0 -0
  344. package/sounds/pop.wav +0 -0
  345. package/sounds/whoosh.wav +0 -0
@@ -0,0 +1,44 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { mediaDuration } from "../programs/media-duration.js";
4
+ import { kvvideoProblem } from "../problems.js";
5
+ import { cachedSentence, keepSentence, sentenceKey } from "./narration-cache.js";
6
+ import { piperSentence } from "./piper.js";
7
+ import { resolvedChoice, speak, speechRequest } from "./speech-services.js";
8
+ function failed(service, reason) {
9
+ return kvvideoProblem('NARRATION_FAILED', `${service} couldn't narrate: ${reason}`, { service, reason });
10
+ }
11
+ async function measured(voice, file) {
12
+ const duration = await mediaDuration(voice.programs.ffprobe, file, voice.signal);
13
+ if (!duration.ok)
14
+ throw failed(voice.service, `ffprobe couldn't read the narration: ${duration.reason}`);
15
+ return duration.ms;
16
+ }
17
+ async function spokenNow(voice, text, file) {
18
+ if (voice.service === 'piper') {
19
+ if (voice.choice.voice === null || voice.programs.piper === null)
20
+ throw failed('piper', 'no voice');
21
+ await piperSentence(voice.programs.piper, voice.choice.voice, text.spoken, file, voice.speed, voice.signal);
22
+ return { type: 'audio/wav', durationMs: await measured(voice, file), alignment: null };
23
+ }
24
+ const key = voice.key ?? '';
25
+ const request = speechRequest(voice.service, text.spoken, voice.choice, key, { tone: voice.tone, speed: voice.speed, previous: text.previous, next: text.next });
26
+ const speech = await speak(voice.service, request, text.spoken, key, voice.fetch, voice.signal);
27
+ writeFileSync(file, speech.audio);
28
+ return { type: 'audio/mpeg', durationMs: await measured(voice, file), alignment: speech.alignment };
29
+ }
30
+ export async function saySentence(ctx, voice, text, folder, index) {
31
+ const choice = voice.service === 'piper' ? voice.choice : resolvedChoice(voice.service, voice.choice);
32
+ const key = sentenceKey({ service: voice.service, voice: choice.voice, model: choice.model, tone: voice.tone, speed: voice.speed, spoken: text.spoken });
33
+ const cached = await cachedSentence(ctx, key);
34
+ if (cached !== undefined) {
35
+ const file = path.join(folder, `sentence-${String(index)}.${cached.type === 'audio/wav' ? 'wav' : 'mp3'}`);
36
+ writeFileSync(file, cached.audio);
37
+ return { file, durationMs: cached.durationMs, alignment: cached.alignment, reused: true };
38
+ }
39
+ const file = path.join(folder, `sentence-${String(index)}.${voice.service === 'piper' ? 'wav' : 'mp3'}`);
40
+ const said = await spokenNow(voice, text, file);
41
+ await keepSentence(ctx, key, { audio: readFileSync(file), ...said });
42
+ return { file, durationMs: said.durationMs, alignment: said.alignment, reused: false };
43
+ }
44
+ //# sourceMappingURL=sentence-speaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentence-speaker.js","sourceRoot":"","sources":["../../src/narration/sentence-speaker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { type Alignment } from './alignment.ts';
2
+ export type WebService = 'elevenlabs' | 'openai';
3
+ export type SpeechRequest = {
4
+ url: string;
5
+ headers: Record<string, string>;
6
+ body: string;
7
+ };
8
+ /** How a sentence is said: the tone (OpenAI only), the speed, and its neighbours (ElevenLabs only). */
9
+ export type Direction = {
10
+ tone: string | null;
11
+ speed: number;
12
+ previous: string | null;
13
+ next: string | null;
14
+ };
15
+ export type Speech = {
16
+ audio: Buffer;
17
+ alignment: Alignment | null;
18
+ };
19
+ export type Fetch = (url: string, init: {
20
+ method: 'POST';
21
+ headers: Record<string, string>;
22
+ body: string;
23
+ signal: AbortSignal;
24
+ }) => Promise<Response>;
25
+ /** The voice and model a service uses: the chosen ones, else its defaults. */
26
+ export declare function resolvedChoice(service: WebService, choice: {
27
+ voice: string | null;
28
+ model: string | null;
29
+ }): {
30
+ voice: string;
31
+ model: string;
32
+ };
33
+ export declare function speechRequest(service: WebService, text: string, choice: {
34
+ voice: string | null;
35
+ model: string | null;
36
+ }, key: string, direction: Direction): SpeechRequest;
37
+ /** The speech a service answers for `spoken`, or `kvvideo/NARRATION_FAILED`. */
38
+ export declare function speak(service: WebService, request: SpeechRequest, spoken: string, key: string, fetch: Fetch, signal: AbortSignal): Promise<Speech>;
39
+ //# sourceMappingURL=speech-services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speech-services.d.ts","sourceRoot":"","sources":["../../src/narration/speech-services.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAOxF,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEjD,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,uGAAuG;AACvG,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE7G,MAAM,MAAM,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AASrJ,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAE5I;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,aAAa,CAgBzK;AAoBD,gFAAgF;AAChF,wBAAsB,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBxJ"}
@@ -0,0 +1,67 @@
1
+ import { ProblemError, z } from '@kvman/sdk';
2
+ import { kvvideoProblem } from "../problems.js";
3
+ import { alignmentOf, elevenLabsAlignmentSchema } from "./alignment.js";
4
+ const defaults = {
5
+ elevenlabs: { voice: 'JBFqnCBsd6RMkjVDRZzb', model: 'eleven_multilingual_v2' },
6
+ openai: { voice: 'alloy', model: 'gpt-4o-mini-tts' },
7
+ };
8
+ const requestTimeoutMs = 60_000;
9
+ /** The voice and model a service uses: the chosen ones, else its defaults. */
10
+ export function resolvedChoice(service, choice) {
11
+ return { voice: choice.voice ?? defaults[service].voice, model: choice.model ?? defaults[service].model };
12
+ }
13
+ export function speechRequest(service, text, choice, key, direction) {
14
+ const { voice, model } = resolvedChoice(service, choice);
15
+ const speed = direction.speed === 1 ? {} : { speed: direction.speed };
16
+ if (service === 'elevenlabs') {
17
+ const neighbours = { ...(direction.previous === null ? {} : { previous_text: direction.previous }), ...(direction.next === null ? {} : { next_text: direction.next }) };
18
+ return {
19
+ url: `https://api.elevenlabs.io/v1/text-to-speech/${encodeURIComponent(voice)}/with-timestamps?output_format=mp3_44100_128`,
20
+ headers: { 'xi-api-key': key, 'Content-Type': 'application/json' },
21
+ body: JSON.stringify({ text, model_id: model, ...neighbours, ...(direction.speed === 1 ? {} : { voice_settings: speed }) }),
22
+ };
23
+ }
24
+ return {
25
+ url: 'https://api.openai.com/v1/audio/speech',
26
+ headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
27
+ body: JSON.stringify({ model, voice, input: text, response_format: 'mp3', ...(direction.tone === null ? {} : { instructions: direction.tone }), ...speed }),
28
+ };
29
+ }
30
+ const elevenLabsAnswerSchema = z.object({ audio_base64: z.string(), alignment: elevenLabsAlignmentSchema.nullable() });
31
+ function withoutKey(text, key) {
32
+ return key === '' ? text : text.split(key).join('…');
33
+ }
34
+ const reasonOf = (error) => (error instanceof Error ? error.message : String(error));
35
+ async function speechOf(service, response, spoken) {
36
+ if (service === 'openai')
37
+ return { audio: Buffer.from(await response.arrayBuffer()), alignment: null };
38
+ const answer = elevenLabsAnswerSchema.safeParse(await response.json());
39
+ if (!answer.success) {
40
+ const reason = "ElevenLabs's answer has no audio and character times";
41
+ throw kvvideoProblem('NARRATION_FAILED', reason, { service, reason });
42
+ }
43
+ return { audio: Buffer.from(answer.data.audio_base64, 'base64'), alignment: answer.data.alignment === null ? null : alignmentOf(answer.data.alignment, spoken) };
44
+ }
45
+ /** The speech a service answers for `spoken`, or `kvvideo/NARRATION_FAILED`. */
46
+ export async function speak(service, request, spoken, key, fetch, signal) {
47
+ const timeout = new AbortController();
48
+ const timer = setTimeout(() => timeout.abort(new Error(`no answer in ${String(requestTimeoutMs / 1000)} s`)), requestTimeoutMs);
49
+ try {
50
+ const response = await fetch(request.url, { method: 'POST', headers: request.headers, body: request.body, signal: AbortSignal.any([signal, timeout.signal]) });
51
+ if (!response.ok) {
52
+ const reason = withoutKey((await response.text()).slice(0, 300), key);
53
+ throw kvvideoProblem('NARRATION_FAILED', `${service} answered ${String(response.status)}: ${reason}`, { service, status: response.status, reason });
54
+ }
55
+ return await speechOf(service, response, spoken);
56
+ }
57
+ catch (error) {
58
+ if (signal.aborted || error instanceof ProblemError)
59
+ throw error;
60
+ const reason = withoutKey(timeout.signal.aborted ? reasonOf(timeout.signal.reason) : reasonOf(error), key);
61
+ throw kvvideoProblem('NARRATION_FAILED', `${service} couldn't be reached: ${reason}`, { service, reason });
62
+ }
63
+ finally {
64
+ clearTimeout(timer);
65
+ }
66
+ }
67
+ //# sourceMappingURL=speech-services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speech-services.js","sourceRoot":"","sources":["../../src/narration/speech-services.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const cleanFilter: string;
2
+ export declare function takeArgs(take: string, clean: boolean, output: string): string[];
3
+ //# sourceMappingURL=voice-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice-args.d.ts","sourceRoot":"","sources":["../../src/narration/voice-args.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW,QAAuJ,CAAC;AAEhL,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAE/E"}
@@ -0,0 +1,10 @@
1
+ // ffmpeg's arguments to clean one take of the person's voice (plan 16 §16.7; ADR 0115, 28 and 50): low rumble removed,
2
+ // steady noise reduced, silence below −45 dB trimmed at both ends down to 150 ms (the end trimmed by reversing the
3
+ // sound, so pauses inside the take stay), and the level evened out at −16 LUFS. With `clean` false, it is only
4
+ // converted. Either way it becomes 44.1 kHz mono WAV, ready to join.
5
+ const trimStart = 'silenceremove=start_periods=1:start_threshold=-45dB:start_silence=0.15';
6
+ export const cleanFilter = ['highpass=f=80', 'afftdn=nr=12:nf=-30', trimStart, 'areverse', trimStart, 'areverse', 'loudnorm=I=-16:TP=-1.5:LRA=11', 'aresample=44100'].join(',');
7
+ export function takeArgs(take, clean, output) {
8
+ return ['-hide_banner', '-loglevel', 'error', '-i', take, '-vn', ...(clean ? ['-af', cleanFilter] : []), '-ac', '1', '-ar', '44100', '-c:a', 'pcm_s16le', '-y', output];
9
+ }
10
+ //# sourceMappingURL=voice-args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice-args.js","sourceRoot":"","sources":["../../src/narration/voice-args.ts"],"names":[],"mappings":"AAAA,uHAAuH;AACvH,mHAAmH;AACnH,+GAA+G;AAC/G,qEAAqE;AAErE,MAAM,SAAS,GAAG,wEAAwE,CAAC;AAE3F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,eAAe,EAAE,qBAAqB,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,+BAA+B,EAAE,iBAAiB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEhL,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAc,EAAE,MAAc;IACnE,OAAO,CAAC,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAC1K,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { ProblemError, type Json } from '@kvman/sdk';
2
+ export type KvvideoCode = 'PROGRAM_MISSING' | 'BROWSER_MISSING' | 'STEP_FAILED' | 'NARRATION_NOT_SET' | 'NARRATION_KEY_MISSING' | 'NARRATION_FAILED' | 'RENDER_FAILED';
3
+ export declare const kvvideoCodes: readonly KvvideoCode[];
4
+ /** A `kvvideo/<code>` Problem to throw. */
5
+ export declare function kvvideoProblem(code: KvvideoCode, message: string, params?: Record<string, Json>): ProblemError;
6
+ /** A `VALIDATION_FAILED` Problem to throw. */
7
+ export declare function invalid(message: string, params?: Record<string, Json>): ProblemError;
8
+ /** A `NOT_FOUND` Problem to throw. */
9
+ export declare function notFound(message: string, params?: Record<string, Json>): ProblemError;
10
+ /** Whether an error is the Problem of a kvvideo code. */
11
+ export declare function isKvvideoProblem(error: unknown, code: KvvideoCode): boolean;
12
+ //# sourceMappingURL=problems.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"problems.d.ts","sourceRoot":"","sources":["../src/problems.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAIrD,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,aAAa,GAAG,mBAAmB,GAAG,uBAAuB,GAAG,kBAAkB,GAAG,eAAe,CAAC;AAEvK,eAAO,MAAM,YAAY,EAAE,SAAS,WAAW,EAA6I,CAAC;AAE7L,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM,GAAG,YAAY,CAElH;AAED,8CAA8C;AAC9C,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM,GAAG,YAAY,CAExF;AAED,sCAAsC;AACtC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM,GAAG,YAAY,CAEzF;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAE3E"}
@@ -0,0 +1,19 @@
1
+ import { ProblemError } from '@kvman/sdk';
2
+ export const kvvideoCodes = ['PROGRAM_MISSING', 'BROWSER_MISSING', 'STEP_FAILED', 'NARRATION_NOT_SET', 'NARRATION_KEY_MISSING', 'NARRATION_FAILED', 'RENDER_FAILED'];
3
+ /** A `kvvideo/<code>` Problem to throw. */
4
+ export function kvvideoProblem(code, message, params = {}) {
5
+ return new ProblemError({ code: `kvvideo/${code}`, message, params });
6
+ }
7
+ /** A `VALIDATION_FAILED` Problem to throw. */
8
+ export function invalid(message, params = {}) {
9
+ return new ProblemError({ code: 'VALIDATION_FAILED', message, params });
10
+ }
11
+ /** A `NOT_FOUND` Problem to throw. */
12
+ export function notFound(message, params = {}) {
13
+ return new ProblemError({ code: 'NOT_FOUND', message, params });
14
+ }
15
+ /** Whether an error is the Problem of a kvvideo code. */
16
+ export function isKvvideoProblem(error, code) {
17
+ return error instanceof ProblemError && error.problem.code === `kvvideo/${code}`;
18
+ }
19
+ //# sourceMappingURL=problems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"problems.js","sourceRoot":"","sources":["../src/problems.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAa,MAAM,YAAY,CAAC;AAMrD,MAAM,CAAC,MAAM,YAAY,GAA2B,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,eAAe,CAAC,CAAC;AAE7L,2CAA2C;AAC3C,MAAM,UAAU,cAAc,CAAC,IAAiB,EAAE,OAAe,EAAE,SAA+B,EAAE;IAClG,OAAO,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,OAAO,CAAC,OAAe,EAAE,SAA+B,EAAE;IACxE,OAAO,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,SAA+B,EAAE;IACzE,OAAO,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,IAAiB;IAChE,OAAO,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,EAAE,CAAC;AACnF,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** How a tree is killed on one OS. */
2
+ export type TreeKill = {
3
+ kind: 'group';
4
+ pid: number;
5
+ signal: 'SIGKILL';
6
+ } | {
7
+ kind: 'taskkill';
8
+ command: 'taskkill';
9
+ args: string[];
10
+ };
11
+ export declare function treeKillOf(platform: NodeJS.Platform, pid: number): TreeKill;
12
+ /** Whether a program starts as its own group's leader, so its tree can be killed. */
13
+ export declare function startsOwnGroup(platform: NodeJS.Platform): boolean;
14
+ /** Kills a running program's tree; a tree that is already gone is left alone. */
15
+ export declare function killTree(pid: number | undefined): void;
16
+ //# sourceMappingURL=kill-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill-tree.d.ts","sourceRoot":"","sources":["../../src/programs/kill-tree.ts"],"names":[],"mappings":"AAKA,sCAAsC;AACtC,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAErI,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAG3E;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,OAAO,CAEjE;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAYtD"}
@@ -0,0 +1,28 @@
1
+ import { spawn } from 'node:child_process';
2
+ export function treeKillOf(platform, pid) {
3
+ if (platform === 'win32')
4
+ return { kind: 'taskkill', command: 'taskkill', args: ['/PID', String(pid), '/T', '/F'] };
5
+ return { kind: 'group', pid: -pid, signal: 'SIGKILL' };
6
+ }
7
+ /** Whether a program starts as its own group's leader, so its tree can be killed. */
8
+ export function startsOwnGroup(platform) {
9
+ return platform !== 'win32';
10
+ }
11
+ /** Kills a running program's tree; a tree that is already gone is left alone. */
12
+ export function killTree(pid) {
13
+ if (pid === undefined)
14
+ return;
15
+ const kill = treeKillOf(process.platform, pid);
16
+ if (kill.kind === 'taskkill') {
17
+ spawn(kill.command, kill.args, { stdio: 'ignore', windowsHide: true });
18
+ return;
19
+ }
20
+ try {
21
+ process.kill(kill.pid, kill.signal);
22
+ }
23
+ catch (error) {
24
+ if (!(error instanceof Error && 'code' in error && error.code === 'ESRCH'))
25
+ throw error;
26
+ }
27
+ }
28
+ //# sourceMappingURL=kill-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill-tree.js","sourceRoot":"","sources":["../../src/programs/kill-tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAQ3C,MAAM,UAAU,UAAU,CAAC,QAAyB,EAAE,GAAW;IAC/D,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;IACpH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACzD,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,QAAyB;IACtD,OAAO,QAAQ,KAAK,OAAO,CAAC;AAC9B,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,QAAQ,CAAC,GAAuB;IAC9C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO;IAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;YAAE,MAAM,KAAK,CAAC;IAC1F,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type Program } from './run-program.ts';
2
+ export type Duration = {
3
+ ok: true;
4
+ ms: number;
5
+ } | {
6
+ ok: false;
7
+ reason: string;
8
+ };
9
+ export declare function mediaDuration(ffprobe: Program, file: string, signal: AbortSignal): Promise<Duration>;
10
+ export type MediaSize = {
11
+ ok: true;
12
+ width: number;
13
+ height: number;
14
+ } | {
15
+ ok: false;
16
+ reason: string;
17
+ };
18
+ /** A video's or an image's size in pixels, read with ffprobe from its first video stream. */
19
+ export declare function mediaSize(ffprobe: Program, file: string, signal: AbortSignal): Promise<MediaSize>;
20
+ /** Whether a file has a sound stream, read with ffprobe. */
21
+ export declare function hasSound(ffprobe: Program, file: string, signal: AbortSignal): Promise<boolean>;
22
+ //# sourceMappingURL=media-duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-duration.d.ts","sourceRoot":"","sources":["../../src/programs/media-duration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIvE,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAM1G;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,6FAA6F;AAC7F,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAKvG;AAED,4DAA4D;AAC5D,wBAAsB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAGpG"}
@@ -0,0 +1,23 @@
1
+ import { lastLines, runProgram } from "./run-program.js";
2
+ export async function mediaDuration(ffprobe, file, signal) {
3
+ const run = await runProgram(ffprobe, ['-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', file], { signal });
4
+ const answer = run.stdout.trim();
5
+ const seconds = Number(answer);
6
+ if (run.exitCode !== 0 || answer === '' || !Number.isFinite(seconds))
7
+ return { ok: false, reason: lastLines(run.output) || `ffprobe answered ${answer}` };
8
+ return { ok: true, ms: Math.round(seconds * 1000) };
9
+ }
10
+ /** A video's or an image's size in pixels, read with ffprobe from its first video stream. */
11
+ export async function mediaSize(ffprobe, file, signal) {
12
+ const run = await runProgram(ffprobe, ['-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=width,height', '-of', 'csv=s=x:p=0', file], { signal });
13
+ const match = /^(\d+)x(\d+)/.exec(run.stdout.trim());
14
+ if (run.exitCode !== 0 || match === null)
15
+ return { ok: false, reason: lastLines(run.output) || `ffprobe answered ${run.stdout.trim()}` };
16
+ return { ok: true, width: Number(match[1]), height: Number(match[2]) };
17
+ }
18
+ /** Whether a file has a sound stream, read with ffprobe. */
19
+ export async function hasSound(ffprobe, file, signal) {
20
+ const run = await runProgram(ffprobe, ['-v', 'error', '-select_streams', 'a', '-show_entries', 'stream=index', '-of', 'csv=p=0', file], { signal });
21
+ return run.exitCode === 0 && run.stdout.trim() !== '';
22
+ }
23
+ //# sourceMappingURL=media-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-duration.js","sourceRoot":"","sources":["../../src/programs/media-duration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAC;AAMvE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB,EAAE,IAAY,EAAE,MAAmB;IACrF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/H,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,IAAI,MAAM,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,oBAAoB,MAAM,EAAE,EAAE,CAAC;IAC1J,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;AACtD,CAAC;AAID,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAgB,EAAE,IAAY,EAAE,MAAmB;IACjF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACjK,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACrD,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,oBAAoB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC;IACzI,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAgB,EAAE,IAAY,EAAE,MAAmB;IAChF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,eAAe,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACpJ,OAAO,GAAG,CAAC,QAAQ,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Program, ProgramRun } from './run-program.ts';
2
+ export type PipedProgram = {
3
+ write(chunk: Buffer): Promise<void>;
4
+ end(): Promise<ProgramRun>;
5
+ };
6
+ export declare function startPiped(program: Program, args: readonly string[], signal: AbortSignal): Promise<PipedProgram>;
7
+ //# sourceMappingURL=piped-program.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piped-program.d.ts","sourceRoot":"","sources":["../../src/programs/piped-program.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM5D,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,CAAC;AAI/F,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAqChH"}
@@ -0,0 +1,45 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { kvvideoProblem } from "../problems.js";
3
+ import { programSetting } from "../settings.js";
4
+ import { killTree, startsOwnGroup } from "./kill-tree.js";
5
+ const notStarted = new Set(['ENOENT', 'EACCES', 'ENOTDIR']);
6
+ export function startPiped(program, args, signal) {
7
+ return new Promise((resolve, reject) => {
8
+ const child = spawn(program.command, args, { stdio: ['pipe', 'pipe', 'pipe'], detached: startsOwnGroup(process.platform), windowsHide: true });
9
+ let stdout = '';
10
+ let output = '';
11
+ let pipeError;
12
+ child.stdout.setEncoding('utf8').on('data', (text) => {
13
+ stdout += text;
14
+ output += text;
15
+ });
16
+ child.stderr.setEncoding('utf8').on('data', (text) => (output += text));
17
+ child.stdin.on('error', (error) => (pipeError = error));
18
+ const abort = () => killTree(child.pid);
19
+ signal.addEventListener('abort', abort, { once: true });
20
+ const closed = new Promise((settle) => child.once('close', (code) => settle(code ?? 1)));
21
+ child.once('error', (error) => {
22
+ signal.removeEventListener('abort', abort);
23
+ const missing = 'code' in error && typeof error.code === 'string' && notStarted.has(error.code);
24
+ reject(missing ? kvvideoProblem('PROGRAM_MISSING', `${program.name} can't be started (${program.command}); install it or set ${programSetting(program.name)}.`, { program: program.name, setting: programSetting(program.name) }) : error);
25
+ });
26
+ child.once('spawn', () => {
27
+ resolve({
28
+ write: (chunk) => new Promise((done) => {
29
+ if (pipeError !== undefined || child.stdin.destroyed)
30
+ return done();
31
+ child.stdin.write(chunk, () => done());
32
+ }),
33
+ end: async () => {
34
+ child.stdin.end();
35
+ const exitCode = await closed;
36
+ signal.removeEventListener('abort', abort);
37
+ if (exitCode === 0 && pipeError !== undefined)
38
+ throw pipeError;
39
+ return { exitCode, stdout, output, timedOut: false };
40
+ },
41
+ });
42
+ });
43
+ });
44
+ }
45
+ //# sourceMappingURL=piped-program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piped-program.js","sourceRoot":"","sources":["../../src/programs/piped-program.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Ctx } from '@kvman/sdk';
2
+ import { type ProgramName } from '../settings.ts';
3
+ import type { Program } from './run-program.ts';
4
+ /** A program as its setting names it (plan 16 §16.2, ADR 0107, 20). */
5
+ export declare function programOf(ctx: Ctx, name: ProgramName): Promise<Program>;
6
+ //# sourceMappingURL=programs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../../src/programs/programs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,uEAAuE;AACvE,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7E"}
@@ -0,0 +1,6 @@
1
+ import { programSetting } from "../settings.js";
2
+ /** A program as its setting names it (plan 16 §16.2, ADR 0107, 20). */
3
+ export async function programOf(ctx, name) {
4
+ return { name, command: await ctx.settings.get(programSetting(name)) };
5
+ }
6
+ //# sourceMappingURL=programs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"programs.js","sourceRoot":"","sources":["../../src/programs/programs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAoB,MAAM,gBAAgB,CAAC;AAGlE,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAQ,EAAE,IAAiB;IACzD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type ProgramName } from '../settings.ts';
2
+ /** A finished run. */
3
+ export type ProgramRun = {
4
+ exitCode: number;
5
+ stdout: string;
6
+ output: string;
7
+ timedOut: boolean;
8
+ };
9
+ /** How one program runs: the program kvvideo calls it, and where its executable is. */
10
+ export type Program = {
11
+ name: ProgramName;
12
+ command: string;
13
+ };
14
+ export type RunOptions = {
15
+ signal: AbortSignal;
16
+ cwd?: string;
17
+ timeoutMs?: number;
18
+ onStdout?: (text: string) => void;
19
+ };
20
+ export declare function runProgram(program: Program, args: readonly string[], options: RunOptions): Promise<ProgramRun>;
21
+ /** The last lines of a run's output, for a Problem's reason. */
22
+ export declare function lastLines(output: string, count?: number): string;
23
+ //# sourceMappingURL=run-program.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-program.d.ts","sourceRoot":"","sources":["../../src/programs/run-program.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAOlE,sBAAsB;AACtB,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjG,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CAAC;AAQtH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAgC9G;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAE5D"}
@@ -0,0 +1,47 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { kvvideoProblem } from "../problems.js";
3
+ import { programSetting } from "../settings.js";
4
+ import { killTree, startsOwnGroup } from "./kill-tree.js";
5
+ const notStarted = new Set(['ENOENT', 'EACCES', 'ENOTDIR']);
6
+ function missing(program) {
7
+ return kvvideoProblem('PROGRAM_MISSING', `${program.name} can't be started (${program.command}); install it or set ${programSetting(program.name)}.`, { program: program.name, setting: programSetting(program.name) });
8
+ }
9
+ export function runProgram(program, args, options) {
10
+ return new Promise((resolve, reject) => {
11
+ const child = spawn(program.command, args, { cwd: options.cwd, stdio: ['ignore', 'pipe', 'pipe'], detached: startsOwnGroup(process.platform), windowsHide: true });
12
+ let stdout = '';
13
+ let output = '';
14
+ let timedOut = false;
15
+ child.stdout.setEncoding('utf8').on('data', (text) => {
16
+ stdout += text;
17
+ output += text;
18
+ options.onStdout?.(text);
19
+ });
20
+ child.stderr.setEncoding('utf8').on('data', (text) => (output += text));
21
+ const abort = () => killTree(child.pid);
22
+ const timer = options.timeoutMs === undefined ? undefined : setTimeout(() => {
23
+ timedOut = true;
24
+ abort();
25
+ }, options.timeoutMs);
26
+ const settle = () => {
27
+ clearTimeout(timer);
28
+ options.signal.removeEventListener('abort', abort);
29
+ };
30
+ options.signal.addEventListener('abort', abort, { once: true });
31
+ if (options.signal.aborted)
32
+ abort();
33
+ child.once('error', (error) => {
34
+ settle();
35
+ reject('code' in error && typeof error.code === 'string' && notStarted.has(error.code) ? missing(program) : error);
36
+ });
37
+ child.once('close', (code) => {
38
+ settle();
39
+ resolve({ exitCode: code ?? 1, stdout, output, timedOut });
40
+ });
41
+ });
42
+ }
43
+ /** The last lines of a run's output, for a Problem's reason. */
44
+ export function lastLines(output, count = 20) {
45
+ return output.trimEnd().split('\n').slice(-count).join('\n');
46
+ }
47
+ //# sourceMappingURL=run-program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-program.js","sourceRoot":"","sources":["../../src/programs/run-program.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Whether a request goes to kvman's port, on any host. */
2
+ export declare function isOwnAddress(url: URL, ownPort: number | null): boolean;
3
+ /** The address, when it is one a recording may open. */
4
+ export declare function recordableAddress(address: string, ownPort: number | null): URL;
5
+ //# sourceMappingURL=address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../../src/record/address.ts"],"names":[],"mappings":"AAaA,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAEtE;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,GAAG,CAM9E"}
@@ -0,0 +1,26 @@
1
+ import { invalid } from "../problems.js";
2
+ // The addresses a recording may open (plan 16 §16.5; ADR 0115, 56): any http or https address without credentials,
3
+ // in a fresh browser, and never kvman's own (ADR 0107, 18). The recorded page's requests to kvman's port are aborted
4
+ // whatever their host, since `0.0.0.0`, `127.0.0.2`, or `[::ffff:127.0.0.1]` reach kvman too.
5
+ const localHosts = new Set(['localhost', '127.0.0.1', '[::1]']);
6
+ function portOf(url) {
7
+ if (url.port !== '')
8
+ return Number(url.port);
9
+ return url.protocol === 'https:' ? 443 : 80;
10
+ }
11
+ /** Whether a request goes to kvman's port, on any host. */
12
+ export function isOwnAddress(url, ownPort) {
13
+ return ownPort !== null && portOf(url) === ownPort;
14
+ }
15
+ /** The address, when it is one a recording may open. */
16
+ export function recordableAddress(address, ownPort) {
17
+ const url = URL.canParse(address) ? new URL(address) : undefined;
18
+ if (url === undefined || (url.protocol !== 'http:' && url.protocol !== 'https:'))
19
+ throw invalid(`${address} isn't an http or https address.`, { url: address });
20
+ if (url.username !== '' || url.password !== '')
21
+ throw invalid(`${address} has a user name or password.`, { url: address });
22
+ if (localHosts.has(url.hostname) && isOwnAddress(url, ownPort))
23
+ throw invalid(`${address} is kvman's own address, which can't be recorded.`, { url: address });
24
+ return url;
25
+ }
26
+ //# sourceMappingURL=address.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address.js","sourceRoot":"","sources":["../../src/record/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,mHAAmH;AACnH,qHAAqH;AACrH,8FAA8F;AAE9F,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhE,SAAS,MAAM,CAAC,GAAQ;IACtB,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,YAAY,CAAC,GAAQ,EAAE,OAAsB;IAC3D,OAAO,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC;AACrD,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,OAAsB;IACvE,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;QAAE,MAAM,OAAO,CAAC,GAAG,OAAO,kCAAkC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAChK,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE;QAAE,MAAM,OAAO,CAAC,GAAG,OAAO,+BAA+B,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3H,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC;QAAE,MAAM,OAAO,CAAC,GAAG,OAAO,mDAAmD,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/J,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** The script a page of the device gets. */
2
+ export declare function cursorScript(device: 'desktop' | 'phone'): string;
3
+ //# sourceMappingURL=cursor-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-script.d.ts","sourceRoot":"","sources":["../../src/record/cursor-script.ts"],"names":[],"mappings":"AAyDA,4CAA4C;AAC5C,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAIhE"}