@kvman/kvvideo 0.1.0 → 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 (242) hide show
  1. package/README.md +1 -1
  2. package/dist/api.d.ts +26 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/brand/brand.d.ts +17 -0
  5. package/dist/brand/brand.d.ts.map +1 -0
  6. package/dist/brand/brand.js +44 -0
  7. package/dist/brand/brand.js.map +1 -0
  8. package/dist/browser/launch.d.ts +2 -0
  9. package/dist/browser/launch.d.ts.map +1 -1
  10. package/dist/browser/launch.js +7 -1
  11. package/dist/browser/launch.js.map +1 -1
  12. package/dist/compose/ass-file.d.ts +5 -0
  13. package/dist/compose/ass-file.d.ts.map +1 -1
  14. package/dist/compose/ass-file.js +32 -3
  15. package/dist/compose/ass-file.js.map +1 -1
  16. package/dist/compose/background.d.ts +23 -0
  17. package/dist/compose/background.d.ts.map +1 -0
  18. package/dist/compose/background.js +31 -0
  19. package/dist/compose/background.js.map +1 -0
  20. package/dist/compose/camera.d.ts +32 -0
  21. package/dist/compose/camera.d.ts.map +1 -0
  22. package/dist/compose/camera.js +98 -0
  23. package/dist/compose/camera.js.map +1 -0
  24. package/dist/compose/caption-font.d.ts +2 -0
  25. package/dist/compose/caption-font.d.ts.map +1 -1
  26. package/dist/compose/caption-font.js +2 -1
  27. package/dist/compose/caption-font.js.map +1 -1
  28. package/dist/compose/compose-drawing.d.ts +19 -0
  29. package/dist/compose/compose-drawing.d.ts.map +1 -0
  30. package/dist/compose/compose-drawing.js +38 -0
  31. package/dist/compose/compose-drawing.js.map +1 -0
  32. package/dist/compose/compose-input.d.ts +139 -0
  33. package/dist/compose/compose-input.d.ts.map +1 -1
  34. package/dist/compose/compose-input.js +48 -9
  35. package/dist/compose/compose-input.js.map +1 -1
  36. package/dist/compose/compose-plan.d.ts +36 -0
  37. package/dist/compose/compose-plan.d.ts.map +1 -0
  38. package/dist/compose/compose-plan.js +55 -0
  39. package/dist/compose/compose-plan.js.map +1 -0
  40. package/dist/compose/compose-size.d.ts +50 -0
  41. package/dist/compose/compose-size.d.ts.map +1 -0
  42. package/dist/compose/compose-size.js +72 -0
  43. package/dist/compose/compose-size.js.map +1 -0
  44. package/dist/compose/drawn-images.d.ts +19 -0
  45. package/dist/compose/drawn-images.d.ts.map +1 -0
  46. package/dist/compose/drawn-images.js +75 -0
  47. package/dist/compose/drawn-images.js.map +1 -0
  48. package/dist/compose/final-pass.d.ts +34 -0
  49. package/dist/compose/final-pass.d.ts.map +1 -0
  50. package/dist/compose/final-pass.js +106 -0
  51. package/dist/compose/final-pass.js.map +1 -0
  52. package/dist/compose/frame-layout.d.ts +30 -0
  53. package/dist/compose/frame-layout.d.ts.map +1 -0
  54. package/dist/compose/frame-layout.js +33 -0
  55. package/dist/compose/frame-layout.js.map +1 -0
  56. package/dist/compose/overlays.d.ts +20 -0
  57. package/dist/compose/overlays.d.ts.map +1 -0
  58. package/dist/compose/overlays.js +30 -0
  59. package/dist/compose/overlays.js.map +1 -0
  60. package/dist/compose/page-again.d.ts +26 -0
  61. package/dist/compose/page-again.d.ts.map +1 -0
  62. package/dist/compose/page-again.js +50 -0
  63. package/dist/compose/page-again.js.map +1 -0
  64. package/dist/compose/player-copies.d.ts +5 -1
  65. package/dist/compose/player-copies.d.ts.map +1 -1
  66. package/dist/compose/player-copies.js +19 -16
  67. package/dist/compose/player-copies.js.map +1 -1
  68. package/dist/compose/register-compose.d.ts.map +1 -1
  69. package/dist/compose/register-compose.js +106 -20
  70. package/dist/compose/register-compose.js.map +1 -1
  71. package/dist/compose/render.d.ts +13 -4
  72. package/dist/compose/render.d.ts.map +1 -1
  73. package/dist/compose/render.js +48 -22
  74. package/dist/compose/render.js.map +1 -1
  75. package/dist/compose/scene-args.d.ts +32 -7
  76. package/dist/compose/scene-args.d.ts.map +1 -1
  77. package/dist/compose/scene-args.js +77 -16
  78. package/dist/compose/scene-args.js.map +1 -1
  79. package/dist/compose/scene-look.d.ts +31 -0
  80. package/dist/compose/scene-look.d.ts.map +1 -0
  81. package/dist/compose/scene-look.js +46 -0
  82. package/dist/compose/scene-look.js.map +1 -0
  83. package/dist/compose/scene-plan.d.ts +0 -2
  84. package/dist/compose/scene-plan.d.ts.map +1 -1
  85. package/dist/compose/scene-plan.js +7 -5
  86. package/dist/compose/scene-plan.js.map +1 -1
  87. package/dist/compose/sound-effects.d.ts +23 -0
  88. package/dist/compose/sound-effects.d.ts.map +1 -0
  89. package/dist/compose/sound-effects.js +40 -0
  90. package/dist/compose/sound-effects.js.map +1 -0
  91. package/dist/compose/srt.d.ts +3 -0
  92. package/dist/compose/srt.d.ts.map +1 -0
  93. package/dist/compose/srt.js +10 -0
  94. package/dist/compose/srt.js.map +1 -0
  95. package/dist/compose/transitions.d.ts +16 -0
  96. package/dist/compose/transitions.d.ts.map +1 -0
  97. package/dist/compose/transitions.js +38 -0
  98. package/dist/compose/transitions.js.map +1 -0
  99. package/dist/connector.d.ts +67 -14
  100. package/dist/connector.d.ts.map +1 -1
  101. package/dist/connector.js +27 -8
  102. package/dist/connector.js.map +1 -1
  103. package/dist/files/sidecar.d.ts +209 -0
  104. package/dist/files/sidecar.d.ts.map +1 -0
  105. package/dist/files/sidecar.js +66 -0
  106. package/dist/files/sidecar.js.map +1 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +7 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/narration/alignment.d.ts +15 -0
  111. package/dist/narration/alignment.d.ts.map +1 -0
  112. package/dist/narration/alignment.js +21 -0
  113. package/dist/narration/alignment.js.map +1 -0
  114. package/dist/narration/captions.d.ts +22 -4
  115. package/dist/narration/captions.d.ts.map +1 -1
  116. package/dist/narration/captions.js +44 -16
  117. package/dist/narration/captions.js.map +1 -1
  118. package/dist/narration/join-args.d.ts +3 -0
  119. package/dist/narration/join-args.d.ts.map +1 -0
  120. package/dist/narration/join-args.js +19 -0
  121. package/dist/narration/join-args.js.map +1 -0
  122. package/dist/narration/narration-cache.d.ts +25 -0
  123. package/dist/narration/narration-cache.d.ts.map +1 -0
  124. package/dist/narration/narration-cache.js +36 -0
  125. package/dist/narration/narration-cache.js.map +1 -0
  126. package/dist/narration/piper.d.ts +2 -6
  127. package/dist/narration/piper.d.ts.map +1 -1
  128. package/dist/narration/piper.js +9 -15
  129. package/dist/narration/piper.js.map +1 -1
  130. package/dist/narration/pronunciations.d.ts +11 -0
  131. package/dist/narration/pronunciations.d.ts.map +1 -0
  132. package/dist/narration/pronunciations.js +40 -0
  133. package/dist/narration/pronunciations.js.map +1 -0
  134. package/dist/narration/register-narration.d.ts +8 -0
  135. package/dist/narration/register-narration.d.ts.map +1 -1
  136. package/dist/narration/register-narration.js +54 -26
  137. package/dist/narration/register-narration.js.map +1 -1
  138. package/dist/narration/register-voice.d.ts +27 -0
  139. package/dist/narration/register-voice.d.ts.map +1 -0
  140. package/dist/narration/register-voice.js +82 -0
  141. package/dist/narration/register-voice.js.map +1 -0
  142. package/dist/narration/sentence-speaker.d.ts +35 -0
  143. package/dist/narration/sentence-speaker.d.ts.map +1 -0
  144. package/dist/narration/sentence-speaker.js +44 -0
  145. package/dist/narration/sentence-speaker.js.map +1 -0
  146. package/dist/narration/speech-services.d.ts +23 -3
  147. package/dist/narration/speech-services.d.ts.map +1 -1
  148. package/dist/narration/speech-services.js +27 -10
  149. package/dist/narration/speech-services.js.map +1 -1
  150. package/dist/narration/voice-args.d.ts +3 -0
  151. package/dist/narration/voice-args.d.ts.map +1 -0
  152. package/dist/narration/voice-args.js +10 -0
  153. package/dist/narration/voice-args.js.map +1 -0
  154. package/dist/programs/media-duration.d.ts +12 -0
  155. package/dist/programs/media-duration.d.ts.map +1 -1
  156. package/dist/programs/media-duration.js +13 -0
  157. package/dist/programs/media-duration.js.map +1 -1
  158. package/dist/programs/run-program.d.ts +1 -0
  159. package/dist/programs/run-program.d.ts.map +1 -1
  160. package/dist/programs/run-program.js +1 -0
  161. package/dist/programs/run-program.js.map +1 -1
  162. package/dist/record/address.d.ts +1 -1
  163. package/dist/record/address.d.ts.map +1 -1
  164. package/dist/record/address.js +5 -7
  165. package/dist/record/address.js.map +1 -1
  166. package/dist/record/cursor-script.d.ts +2 -1
  167. package/dist/record/cursor-script.d.ts.map +1 -1
  168. package/dist/record/cursor-script.js +52 -14
  169. package/dist/record/cursor-script.js.map +1 -1
  170. package/dist/record/encoder-args.js +1 -1
  171. package/dist/record/motion.d.ts +19 -0
  172. package/dist/record/motion.d.ts.map +1 -0
  173. package/dist/record/motion.js +59 -0
  174. package/dist/record/motion.js.map +1 -0
  175. package/dist/record/narration-cues.d.ts +14 -0
  176. package/dist/record/narration-cues.d.ts.map +1 -0
  177. package/dist/record/narration-cues.js +12 -0
  178. package/dist/record/narration-cues.js.map +1 -0
  179. package/dist/record/page-ready.d.ts +4 -0
  180. package/dist/record/page-ready.d.ts.map +1 -0
  181. package/dist/record/page-ready.js +15 -0
  182. package/dist/record/page-ready.js.map +1 -0
  183. package/dist/record/page-route.d.ts +13 -0
  184. package/dist/record/page-route.d.ts.map +1 -0
  185. package/dist/record/page-route.js +64 -0
  186. package/dist/record/page-route.js.map +1 -0
  187. package/dist/record/pointer.d.ts +6 -7
  188. package/dist/record/pointer.d.ts.map +1 -1
  189. package/dist/record/pointer.js +11 -10
  190. package/dist/record/pointer.js.map +1 -1
  191. package/dist/record/recorder.d.ts +17 -7
  192. package/dist/record/recorder.d.ts.map +1 -1
  193. package/dist/record/recorder.js +48 -18
  194. package/dist/record/recorder.js.map +1 -1
  195. package/dist/record/register-record.d.ts.map +1 -1
  196. package/dist/record/register-record.js +47 -13
  197. package/dist/record/register-record.js.map +1 -1
  198. package/dist/record/run-step.d.ts +16 -3
  199. package/dist/record/run-step.d.ts.map +1 -1
  200. package/dist/record/run-step.js +43 -23
  201. package/dist/record/run-step.js.map +1 -1
  202. package/dist/record/screencast.js +1 -1
  203. package/dist/record/steps.d.ts +54 -1
  204. package/dist/record/steps.d.ts.map +1 -1
  205. package/dist/record/steps.js +34 -16
  206. package/dist/record/steps.js.map +1 -1
  207. package/dist/review/analysis.d.ts +19 -0
  208. package/dist/review/analysis.d.ts.map +1 -0
  209. package/dist/review/analysis.js +36 -0
  210. package/dist/review/analysis.js.map +1 -0
  211. package/dist/review/frames.d.ts +9 -0
  212. package/dist/review/frames.d.ts.map +1 -0
  213. package/dist/review/frames.js +28 -0
  214. package/dist/review/frames.js.map +1 -0
  215. package/dist/review/register-review.d.ts +24 -0
  216. package/dist/review/register-review.d.ts.map +1 -0
  217. package/dist/review/register-review.js +81 -0
  218. package/dist/review/register-review.js.map +1 -0
  219. package/dist/review/sheet-args.d.ts +11 -0
  220. package/dist/review/sheet-args.d.ts.map +1 -0
  221. package/dist/review/sheet-args.js +24 -0
  222. package/dist/review/sheet-args.js.map +1 -0
  223. package/dist/settings.d.ts +4 -0
  224. package/dist/settings.d.ts.map +1 -1
  225. package/dist/settings.js +4 -2
  226. package/dist/settings.js.map +1 -1
  227. package/dist/setup/ffmpeg-readiness.js +1 -1
  228. package/dist/setup/ffmpeg-readiness.js.map +1 -1
  229. package/dist/ui.d.ts.map +1 -1
  230. package/dist/ui.js +3 -2
  231. package/dist/ui.js.map +1 -1
  232. package/dist/web/components/player.css +1 -1
  233. package/dist/web/components/player.js +33 -21
  234. package/docs/guide.md +21 -9
  235. package/locales/ar.json +12 -1
  236. package/locales/en.json +12 -1
  237. package/package.json +11 -5
  238. package/sounds/SOURCES.md +13 -0
  239. package/sounds/click.wav +0 -0
  240. package/sounds/keys.wav +0 -0
  241. package/sounds/pop.wav +0 -0
  242. package/sounds/whoosh.wav +0 -0
@@ -1,32 +1,49 @@
1
- import { ProblemError } from '@kvman/sdk';
1
+ import { ProblemError, z } from '@kvman/sdk';
2
2
  import { kvvideoProblem } from "../problems.js";
3
+ import { alignmentOf, elevenLabsAlignmentSchema } from "./alignment.js";
3
4
  const defaults = {
4
5
  elevenlabs: { voice: 'JBFqnCBsd6RMkjVDRZzb', model: 'eleven_multilingual_v2' },
5
6
  openai: { voice: 'alloy', model: 'gpt-4o-mini-tts' },
6
7
  };
7
8
  const requestTimeoutMs = 60_000;
8
- export function speechRequest(service, text, choice, key) {
9
- const voice = choice.voice ?? defaults[service].voice;
10
- const model = choice.model ?? defaults[service].model;
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 };
11
16
  if (service === 'elevenlabs') {
17
+ const neighbours = { ...(direction.previous === null ? {} : { previous_text: direction.previous }), ...(direction.next === null ? {} : { next_text: direction.next }) };
12
18
  return {
13
- url: `https://api.elevenlabs.io/v1/text-to-speech/${encodeURIComponent(voice)}?output_format=mp3_44100_128`,
19
+ url: `https://api.elevenlabs.io/v1/text-to-speech/${encodeURIComponent(voice)}/with-timestamps?output_format=mp3_44100_128`,
14
20
  headers: { 'xi-api-key': key, 'Content-Type': 'application/json' },
15
- body: JSON.stringify({ text, model_id: model }),
21
+ body: JSON.stringify({ text, model_id: model, ...neighbours, ...(direction.speed === 1 ? {} : { voice_settings: speed }) }),
16
22
  };
17
23
  }
18
24
  return {
19
25
  url: 'https://api.openai.com/v1/audio/speech',
20
26
  headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
21
- body: JSON.stringify({ model, voice, input: text, response_format: 'mp3' }),
27
+ body: JSON.stringify({ model, voice, input: text, response_format: 'mp3', ...(direction.tone === null ? {} : { instructions: direction.tone }), ...speed }),
22
28
  };
23
29
  }
30
+ const elevenLabsAnswerSchema = z.object({ audio_base64: z.string(), alignment: elevenLabsAlignmentSchema.nullable() });
24
31
  function withoutKey(text, key) {
25
32
  return key === '' ? text : text.split(key).join('…');
26
33
  }
27
34
  const reasonOf = (error) => (error instanceof Error ? error.message : String(error));
28
- /** The audio a service answers, or `kvvideo/NARRATION_FAILED`. */
29
- export async function speak(service, request, key, fetch, signal) {
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) {
30
47
  const timeout = new AbortController();
31
48
  const timer = setTimeout(() => timeout.abort(new Error(`no answer in ${String(requestTimeoutMs / 1000)} s`)), requestTimeoutMs);
32
49
  try {
@@ -35,7 +52,7 @@ export async function speak(service, request, key, fetch, signal) {
35
52
  const reason = withoutKey((await response.text()).slice(0, 300), key);
36
53
  throw kvvideoProblem('NARRATION_FAILED', `${service} answered ${String(response.status)}: ${reason}`, { service, status: response.status, reason });
37
54
  }
38
- return Buffer.from(await response.arrayBuffer());
55
+ return await speechOf(service, response, spoken);
39
56
  }
40
57
  catch (error) {
41
58
  if (signal.aborted || error instanceof ProblemError)
@@ -1 +1 @@
1
- {"version":3,"file":"speech-services.js","sourceRoot":"","sources":["../../src/narration/speech-services.ts"],"names":[],"mappings":"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"}
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"}
@@ -7,4 +7,16 @@ export type Duration = {
7
7
  reason: string;
8
8
  };
9
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>;
10
22
  //# sourceMappingURL=media-duration.d.ts.map
@@ -1 +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"}
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"}
@@ -7,4 +7,17 @@ export async function mediaDuration(ffprobe, file, signal) {
7
7
  return { ok: false, reason: lastLines(run.output) || `ffprobe answered ${answer}` };
8
8
  return { ok: true, ms: Math.round(seconds * 1000) };
9
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
+ }
10
23
  //# sourceMappingURL=media-duration.js.map
@@ -1 +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"}
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"}
@@ -15,6 +15,7 @@ export type RunOptions = {
15
15
  signal: AbortSignal;
16
16
  cwd?: string;
17
17
  timeoutMs?: number;
18
+ onStdout?: (text: string) => void;
18
19
  };
19
20
  export declare function runProgram(program: Program, args: readonly string[], options: RunOptions): Promise<ProgramRun>;
20
21
  /** The last lines of a run's output, for a Problem's reason. */
@@ -1 +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,CAAA;CAAE,CAAC;AAQnF,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B9G;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAE5D"}
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"}
@@ -15,6 +15,7 @@ export function runProgram(program, args, options) {
15
15
  child.stdout.setEncoding('utf8').on('data', (text) => {
16
16
  stdout += text;
17
17
  output += text;
18
+ options.onStdout?.(text);
18
19
  });
19
20
  child.stderr.setEncoding('utf8').on('data', (text) => (output += text));
20
21
  const abort = () => killTree(child.pid);
@@ -1 +1 @@
1
- {"version":3,"file":"run-program.js","sourceRoot":"","sources":["../../src/programs/run-program.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-program.js","sourceRoot":"","sources":["../../src/programs/run-program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAoB,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAc1D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAE5D,SAAS,OAAO,CAAC,OAAgB;IAC/B,OAAO,cAAc,CAAC,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,sBAAsB,OAAO,CAAC,OAAO,wBAAwB,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC1N,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,IAAuB,EAAE,OAAmB;IACvF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACnK,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YAC3D,MAAM,IAAI,IAAI,CAAC;YACf,MAAM,IAAI,IAAI,CAAC;YACf,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,GAAS,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE;YAC1E,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,EAAE,CAAC;QACV,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACtB,MAAM,MAAM,GAAG,GAAS,EAAE;YACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACrD,CAAC,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO;YAAE,KAAK,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC5B,MAAM,EAAE,CAAC;YACT,MAAM,CAAC,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACrH,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAC3B,MAAM,EAAE,CAAC;YACT,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,KAAK,GAAG,EAAE;IAClD,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /** Whether a request goes to kvman's port, on any host. */
2
2
  export declare function isOwnAddress(url: URL, ownPort: number | null): boolean;
3
3
  /** The address, when it is one a recording may open. */
4
- export declare function localAddress(address: string, ownPort: number | null): URL;
4
+ export declare function recordableAddress(address: string, ownPort: number | null): URL;
5
5
  //# sourceMappingURL=address.d.ts.map
@@ -1 +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,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,GAAG,CAOzE"}
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"}
@@ -1,7 +1,7 @@
1
1
  import { invalid } from "../problems.js";
2
- // The addresses a recording may open (plan 16 §16.5, ADR 0107, 18): http or https on this computer, without
3
- // credentials, and never kvman's own. The recorded page's requests to kvman's port are aborted whatever their host,
4
- // since `0.0.0.0`, `127.0.0.2`, or `[::ffff:127.0.0.1]` reach kvman too.
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
5
  const localHosts = new Set(['localhost', '127.0.0.1', '[::1]']);
6
6
  function portOf(url) {
7
7
  if (url.port !== '')
@@ -13,15 +13,13 @@ export function isOwnAddress(url, ownPort) {
13
13
  return ownPort !== null && portOf(url) === ownPort;
14
14
  }
15
15
  /** The address, when it is one a recording may open. */
16
- export function localAddress(address, ownPort) {
16
+ export function recordableAddress(address, ownPort) {
17
17
  const url = URL.canParse(address) ? new URL(address) : undefined;
18
18
  if (url === undefined || (url.protocol !== 'http:' && url.protocol !== 'https:'))
19
19
  throw invalid(`${address} isn't an http or https address.`, { url: address });
20
- if (!localHosts.has(url.hostname))
21
- throw invalid(`${address} isn't on this computer: use localhost, 127.0.0.1, or [::1].`, { url: address });
22
20
  if (url.username !== '' || url.password !== '')
23
21
  throw invalid(`${address} has a user name or password.`, { url: address });
24
- if (isOwnAddress(url, ownPort))
22
+ if (localHosts.has(url.hostname) && isOwnAddress(url, ownPort))
25
23
  throw invalid(`${address} is kvman's own address, which can't be recorded.`, { url: address });
26
24
  return url;
27
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"address.js","sourceRoot":"","sources":["../../src/record/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,4GAA4G;AAC5G,oHAAoH;AACpH,yEAAyE;AAEzE,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,YAAY,CAAC,OAAe,EAAE,OAAsB;IAClE,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,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,MAAM,OAAO,CAAC,GAAG,OAAO,8DAA8D,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7I,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,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC;QAAE,MAAM,OAAO,CAAC,GAAG,OAAO,mDAAmD,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/H,OAAO,GAAG,CAAC;AACb,CAAC"}
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"}
@@ -1,2 +1,3 @@
1
- export declare const cursorScript = "(() => {\n const draw = () => {\n if (document.getElementById('kvvideo-cursor')) return;\n const dot = document.createElement('div');\n dot.id = 'kvvideo-cursor';\n dot.style.cssText = 'position:fixed;inset-inline-start:0;inset-block-start:0;width:18px;height:18px;margin:-9px 0 0 -9px;border-radius:50%;background:rgba(20,20,20,.75);border:2px solid #fff;box-shadow:0 0 4px rgba(0,0,0,.5);pointer-events:none;z-index:2147483647;transform:translate(-100px,-100px)';\n document.documentElement.appendChild(dot);\n window.addEventListener('mousemove', (event) => { dot.style.transform = 'translate(' + event.clientX + 'px,' + event.clientY + 'px)'; }, true);\n window.addEventListener('mousedown', (event) => {\n const ring = document.createElement('div');\n ring.className = 'kvvideo-ring';\n ring.style.cssText = 'position:fixed;inset-inline-start:' + (event.clientX - 22) + 'px;inset-block-start:' + (event.clientY - 22) + 'px;width:44px;height:44px;border-radius:50%;border:3px solid rgba(255,190,0,.9);pointer-events:none;z-index:2147483646';\n document.documentElement.appendChild(ring);\n ring.animate([{ opacity: 1, transform: 'scale(.4)' }, { opacity: 0, transform: 'scale(1.3)' }], { duration: 500 }).onfinish = () => ring.remove();\n }, true);\n };\n if (document.documentElement) draw(); else document.addEventListener('DOMContentLoaded', draw);\n})();";
1
+ /** The script a page of the device gets. */
2
+ export declare function cursorScript(device: 'desktop' | 'phone'): string;
2
3
  //# sourceMappingURL=cursor-script.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-script.d.ts","sourceRoot":"","sources":["../../src/record/cursor-script.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,24CAiBnB,CAAC"}
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"}
@@ -1,21 +1,59 @@
1
- // The cursor a recording shows (ADR 0107, 19): a browser's recording has no pointer, so the page draws a dot that
2
- // follows the mouse and a ring at each press. It is added to every page before its own scripts run.
3
- export const cursorScript = `(() => {
1
+ // What a recorded page draws (plan 16 §16.5; ADR 0115, 9 and 53): a browser's recording has no pointer, so on a
2
+ // desktop the page draws an arrow that follows the mouse, hidden until the mouse first moves; a press shrinks it for
3
+ // 120 ms and shows a ring. On a phone it draws a round mark at each touch instead. Scroll bars are hidden. The script
4
+ // is added to every page before its own scripts run.
5
+ const arrowPath = 'M3 2 L3 20 L7.6 15.6 L10.6 22.6 L13.8 21.2 L10.9 14.4 L17.2 14.4 Z';
6
+ const shared = `
7
+ const hideScrollBars = () => {
8
+ const style = document.createElement('style');
9
+ style.textContent = 'html { scrollbar-width: none; } ::-webkit-scrollbar { display: none; }';
10
+ document.documentElement.appendChild(style);
11
+ };
12
+ const ring = (x, y, size, color) => {
13
+ const mark = document.createElement('div');
14
+ mark.className = 'kvvideo-ring';
15
+ mark.style.cssText = 'position:fixed;direction:ltr;inset-inline-start:' + (x - size / 2) + 'px;inset-block-start:' + (y - size / 2) + 'px;inline-size:' + size + 'px;block-size:' + size + 'px;border-radius:50%;box-sizing:border-box;border:3px solid ' + color + ';pointer-events:none;z-index:2147483646';
16
+ document.documentElement.appendChild(mark);
17
+ mark.animate([{ opacity: 1, transform: 'scale(.4)' }, { opacity: 0, transform: 'scale(1.3)' }], { duration: 500 }).onfinish = () => mark.remove();
18
+ };`;
19
+ const desktop = `
4
20
  const draw = () => {
5
21
  if (document.getElementById('kvvideo-cursor')) return;
6
- const dot = document.createElement('div');
7
- dot.id = 'kvvideo-cursor';
8
- dot.style.cssText = 'position:fixed;inset-inline-start:0;inset-block-start:0;width:18px;height:18px;margin:-9px 0 0 -9px;border-radius:50%;background:rgba(20,20,20,.75);border:2px solid #fff;box-shadow:0 0 4px rgba(0,0,0,.5);pointer-events:none;z-index:2147483647;transform:translate(-100px,-100px)';
9
- document.documentElement.appendChild(dot);
10
- window.addEventListener('mousemove', (event) => { dot.style.transform = 'translate(' + event.clientX + 'px,' + event.clientY + 'px)'; }, true);
22
+ hideScrollBars();
23
+ const pointer = document.createElement('div');
24
+ pointer.id = 'kvvideo-cursor';
25
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
26
+ for (const [name, value] of [['width', '26'], ['height', '26'], ['viewBox', '0 0 26 26']]) svg.setAttribute(name, value);
27
+ const shape = document.createElementNS('http://www.w3.org/2000/svg', 'path');
28
+ for (const [name, value] of [['d', ${JSON.stringify(arrowPath)}], ['fill', '#fff'], ['stroke', '#111'], ['stroke-width', '1.6'], ['stroke-linejoin', 'round']]) shape.setAttribute(name, value);
29
+ svg.appendChild(shape);
30
+ pointer.appendChild(svg);
31
+ pointer.style.cssText = 'position:fixed;direction:ltr;inset-inline-start:0;inset-block-start:0;inline-size:26px;block-size:26px;margin-block-start:-2px;margin-inline-start:-3px;display:none;transform-origin:3px 2px;pointer-events:none;z-index:2147483647;filter:drop-shadow(0 1px 2px rgba(0,0,0,.4))';
32
+ document.documentElement.appendChild(pointer);
33
+ let x = 0; let y = 0; let scale = 1;
34
+ const place = () => { pointer.style.transform = 'translate(' + x + 'px,' + y + 'px) scale(' + scale + ')'; };
35
+ window.addEventListener('mousemove', (event) => { x = event.clientX; y = event.clientY; pointer.style.display = 'block'; place(); }, true);
11
36
  window.addEventListener('mousedown', (event) => {
12
- const ring = document.createElement('div');
13
- ring.className = 'kvvideo-ring';
14
- ring.style.cssText = 'position:fixed;inset-inline-start:' + (event.clientX - 22) + 'px;inset-block-start:' + (event.clientY - 22) + 'px;width:44px;height:44px;border-radius:50%;border:3px solid rgba(255,190,0,.9);pointer-events:none;z-index:2147483646';
15
- document.documentElement.appendChild(ring);
16
- ring.animate([{ opacity: 1, transform: 'scale(.4)' }, { opacity: 0, transform: 'scale(1.3)' }], { duration: 500 }).onfinish = () => ring.remove();
37
+ scale = 0.85; place();
38
+ setTimeout(() => { scale = 1; place(); }, 120);
39
+ ring(event.clientX, event.clientY, 44, 'rgba(255,190,0,.9)');
17
40
  }, true);
18
- };
41
+ };`;
42
+ const phone = `
43
+ const draw = () => {
44
+ if (document.getElementById('kvvideo-touch')) return;
45
+ hideScrollBars();
46
+ const marker = document.createElement('meta');
47
+ marker.id = 'kvvideo-touch';
48
+ document.documentElement.appendChild(marker);
49
+ window.addEventListener('touchstart', (event) => {
50
+ for (const touch of event.changedTouches) ring(touch.clientX, touch.clientY, 40, 'rgba(255,255,255,.9)');
51
+ }, true);
52
+ };`;
53
+ /** The script a page of the device gets. */
54
+ export function cursorScript(device) {
55
+ return `(() => {${shared}${device === 'phone' ? phone : desktop}
19
56
  if (document.documentElement) draw(); else document.addEventListener('DOMContentLoaded', draw);
20
57
  })();`;
58
+ }
21
59
  //# sourceMappingURL=cursor-script.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-script.js","sourceRoot":"","sources":["../../src/record/cursor-script.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,oGAAoG;AAEpG,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;MAiBtB,CAAC"}
1
+ {"version":3,"file":"cursor-script.js","sourceRoot":"","sources":["../../src/record/cursor-script.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAChH,qHAAqH;AACrH,sHAAsH;AACtH,qDAAqD;AAErD,MAAM,SAAS,GAAG,oEAAoE,CAAC;AAEvF,MAAM,MAAM,GAAG;;;;;;;;;;;;KAYV,CAAC;AAEN,MAAM,OAAO,GAAG;;;;;;;;;yCASyB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;KAa7D,CAAC;AAEN,MAAM,KAAK,GAAG;;;;;;;;;;KAUT,CAAC;AAEN,4CAA4C;AAC5C,MAAM,UAAU,YAAY,CAAC,MAA2B;IACtD,OAAO,WAAW,MAAM,GAAG,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO;;MAE3D,CAAC;AACP,CAAC"}
@@ -6,7 +6,7 @@ export function recordEncoderArgs(width, height, output) {
6
6
  '-hide_banner', '-loglevel', 'error',
7
7
  '-f', 'image2pipe', '-framerate', String(framesPerSecond), '-c:v', 'mjpeg', '-i', '-',
8
8
  '-vf', fit,
9
- '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '18', '-r', String(framesPerSecond),
9
+ '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '14', '-r', String(framesPerSecond),
10
10
  '-an', '-movflags', '+faststart', '-y', output,
11
11
  ];
12
12
  }
@@ -0,0 +1,19 @@
1
+ export declare const frameMs = 16;
2
+ export type Point = {
3
+ x: number;
4
+ y: number;
5
+ };
6
+ /** Cubic easing in and out, from 0 to 1. */
7
+ export declare function easeInOut(share: number): number;
8
+ /** How long the pointer takes to travel `distance` pixels. */
9
+ export declare function glideMs(distance: number): number;
10
+ /** The pointer's positions, one a frame, from `from` (left out) to `to`, along a gentle curve. */
11
+ export declare function glidePath(from: Point, to: Point): Point[];
12
+ /** The wait before each character of `text`: 35 to 110 ms, and 150 ms more after a space or a punctuation mark. */
13
+ export declare function typingDelays(text: string): number[];
14
+ /** An eased scroll of `pixels`: its length and its move in each frame, which add up to `pixels`. */
15
+ export declare function scrollPlan(pixels: number): {
16
+ durationMs: number;
17
+ moves: number[];
18
+ };
19
+ //# sourceMappingURL=motion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/record/motion.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,KAAK,CAAC;AAE1B,MAAM,MAAM,KAAK,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,4CAA4C;AAC5C,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAID,8DAA8D;AAC9D,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,kGAAkG;AAClG,wBAAgB,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,KAAK,EAAE,CAYzD;AAgBD,mHAAmH;AACnH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAInD;AAED,oGAAoG;AACpG,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAWlF"}
@@ -0,0 +1,59 @@
1
+ // How things move in a recording (plan 16 §16.5; ADR 0115, 9): the pointer's eased, gently curved path, a typing
2
+ // pace that varies but is the same for the same text, and an eased scroll. Each is planned in frames of `frameMs`.
3
+ export const frameMs = 16;
4
+ /** Cubic easing in and out, from 0 to 1. */
5
+ export function easeInOut(share) {
6
+ return share < 0.5 ? 4 * share ** 3 : 1 - (-2 * share + 2) ** 3 / 2;
7
+ }
8
+ const frameCount = (durationMs) => Math.max(1, Math.round(durationMs / frameMs));
9
+ /** How long the pointer takes to travel `distance` pixels. */
10
+ export function glideMs(distance) {
11
+ return Math.min(900, Math.max(400, Math.round(300 + 0.6 * distance)));
12
+ }
13
+ /** The pointer's positions, one a frame, from `from` (left out) to `to`, along a gentle curve. */
14
+ export function glidePath(from, to) {
15
+ const dx = to.x - from.x;
16
+ const dy = to.y - from.y;
17
+ const bend = 0.08;
18
+ const control = { x: (from.x + to.x) / 2 - dy * bend, y: (from.y + to.y) / 2 + dx * bend };
19
+ const frames = frameCount(glideMs(Math.hypot(dx, dy)));
20
+ return Array.from({ length: frames }, (_, index) => {
21
+ const share = easeInOut((index + 1) / frames);
22
+ const toControl = 2 * (1 - share) * share;
23
+ const toEnd = share * share;
24
+ return { x: from.x + toControl * (control.x - from.x) + toEnd * dx, y: from.y + toControl * (control.y - from.y) + toEnd * dy };
25
+ });
26
+ }
27
+ // A small seeded generator (mulberry32 over an FNV-1a hash of the text), so the same text types the same way.
28
+ function seededRandom(text) {
29
+ let seed = 0x811c9dc5;
30
+ for (const unit of text)
31
+ seed = Math.imul(seed ^ (unit.codePointAt(0) ?? 0), 0x01000193);
32
+ return () => {
33
+ seed = (seed + 0x6d2b79f5) | 0;
34
+ let mixed = Math.imul(seed ^ (seed >>> 15), seed | 1);
35
+ mixed ^= mixed + Math.imul(mixed ^ (mixed >>> 7), mixed | 61);
36
+ return ((mixed ^ (mixed >>> 14)) >>> 0) / 4_294_967_296;
37
+ };
38
+ }
39
+ const pausesAfter = (character) => /[\s\p{P}]/u.test(character);
40
+ /** The wait before each character of `text`: 35 to 110 ms, and 150 ms more after a space or a punctuation mark. */
41
+ export function typingDelays(text) {
42
+ const random = seededRandom(text);
43
+ const characters = [...text];
44
+ return characters.map((_, index) => 35 + Math.floor(random() * 76) + (index > 0 && pausesAfter(characters[index - 1] ?? '') ? 150 : 0));
45
+ }
46
+ /** An eased scroll of `pixels`: its length and its move in each frame, which add up to `pixels`. */
47
+ export function scrollPlan(pixels) {
48
+ const durationMs = Math.min(3000, Math.round(400 + 0.5 * Math.abs(pixels)));
49
+ const frames = frameCount(durationMs);
50
+ let before = 0;
51
+ const moves = Array.from({ length: frames }, (_, index) => {
52
+ const at = Math.round(pixels * easeInOut((index + 1) / frames));
53
+ const move = at - before;
54
+ before = at;
55
+ return move;
56
+ });
57
+ return { durationMs, moves };
58
+ }
59
+ //# sourceMappingURL=motion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion.js","sourceRoot":"","sources":["../../src/record/motion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export declare const narrationLeadMs = 300;
2
+ export type Cues = {
3
+ lineStartsMs: number[];
4
+ leastLengthMs: number;
5
+ };
6
+ export declare function cuesOf(narration: {
7
+ durationMs: number;
8
+ captions: readonly {
9
+ startMs: number;
10
+ }[];
11
+ }): Cues;
12
+ /** How long to wait, `nowMs` into the recording, until `atMs`; nothing once it has passed. */
13
+ export declare function waitUntilMs(atMs: number, nowMs: number): number;
14
+ //# sourceMappingURL=narration-cues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narration-cues.d.ts","sourceRoot":"","sources":["../../src/record/narration-cues.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,MAAM,MAAM,IAAI,GAAG;IAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,wBAAgB,MAAM,CAAC,SAAS,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,GAAG,IAAI,CAExG;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/D"}
@@ -0,0 +1,12 @@
1
+ // When a recording's steps start, from its narration (plan 16 §16.5; ADR 0115, 16 and 41): the narration starts
2
+ // 300 ms into the scene, so line `n` is heard at 300 ms plus its start, and the recording lasts at least the
3
+ // narration with 300 ms before and after it.
4
+ export const narrationLeadMs = 300;
5
+ export function cuesOf(narration) {
6
+ return { lineStartsMs: narration.captions.map((caption) => narrationLeadMs + caption.startMs), leastLengthMs: narrationLeadMs + narration.durationMs + narrationLeadMs };
7
+ }
8
+ /** How long to wait, `nowMs` into the recording, until `atMs`; nothing once it has passed. */
9
+ export function waitUntilMs(atMs, nowMs) {
10
+ return Math.max(0, Math.round(atMs - nowMs));
11
+ }
12
+ //# sourceMappingURL=narration-cues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narration-cues.js","sourceRoot":"","sources":["../../src/record/narration-cues.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAChH,6GAA6G;AAC7G,6CAA6C;AAE7C,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAInC,MAAM,UAAU,MAAM,CAAC,SAA2E;IAChG,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,eAAe,GAAG,SAAS,CAAC,UAAU,GAAG,eAAe,EAAE,CAAC;AAC3K,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAa;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Page } from 'playwright-core';
2
+ export type ReadyPage = Pick<Page, 'waitForLoadState' | 'evaluate'>;
3
+ export declare function waitUntilReady(page: ReadyPage): Promise<void>;
4
+ //# sourceMappingURL=page-ready.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-ready.d.ts","sourceRoot":"","sources":["../../src/record/page-ready.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAO5C,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,kBAAkB,GAAG,UAAU,CAAC,CAAC;AAIpE,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnE"}
@@ -0,0 +1,15 @@
1
+ // A page is ready to record once its network has been idle for 500 ms, waited for at most 10 s, and all its fonts
2
+ // have loaded (plan 16 §16.5). A network that never goes idle doesn't stop the recording; any other failure does.
3
+ const idleTimeoutMs = 10_000;
4
+ const isTimeout = (error) => error instanceof Error && error.name === 'TimeoutError';
5
+ export async function waitUntilReady(page) {
6
+ try {
7
+ await page.waitForLoadState('networkidle', { timeout: idleTimeoutMs });
8
+ }
9
+ catch (error) {
10
+ if (!isTimeout(error))
11
+ throw error;
12
+ }
13
+ await page.evaluate('document.fonts.ready.then(() => true)');
14
+ }
15
+ //# sourceMappingURL=page-ready.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-ready.js","sourceRoot":"","sources":["../../src/record/page-ready.ts"],"names":[],"mappings":"AAEA,kHAAkH;AAClH,kHAAkH;AAElH,MAAM,aAAa,GAAG,MAAM,CAAC;AAI7B,MAAM,SAAS,GAAG,CAAC,KAAc,EAAW,EAAE,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC;AAEvG,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAe;IAClD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;IACrC,CAAC;IACD,MAAM,IAAI,CAAC,QAAQ,CAAC,uCAAuC,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const pageOrigin = "http://page.kvvideo.invalid";
2
+ export type PageAnswer = {
3
+ status: 200;
4
+ file: string;
5
+ contentType: string;
6
+ } | {
7
+ status: 404;
8
+ };
9
+ /** The address a page file is opened at. */
10
+ export declare function pageAddress(page: string): string;
11
+ /** What a request of the page's origin is answered with, from the page's folder. */
12
+ export declare function answerPageRequest(folder: string, address: string): PageAnswer;
13
+ //# sourceMappingURL=page-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-route.d.ts","sourceRoot":"","sources":["../../src/record/page-route.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,UAAU,gCAAgC,CAAC;AA6BxD,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC;AAE9F,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAiBD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAO7E"}