@kvman/kvvideo 0.1.0 → 0.1.2
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.
- package/README.md +1 -1
- package/dist/api.d.ts +31 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/brand/brand.d.ts +17 -0
- package/dist/brand/brand.d.ts.map +1 -0
- package/dist/brand/brand.js +44 -0
- package/dist/brand/brand.js.map +1 -0
- package/dist/browser/launch.d.ts +2 -0
- package/dist/browser/launch.d.ts.map +1 -1
- package/dist/browser/launch.js +7 -1
- package/dist/browser/launch.js.map +1 -1
- package/dist/compose/ass-file.d.ts +5 -0
- package/dist/compose/ass-file.d.ts.map +1 -1
- package/dist/compose/ass-file.js +32 -3
- package/dist/compose/ass-file.js.map +1 -1
- package/dist/compose/background.d.ts +23 -0
- package/dist/compose/background.d.ts.map +1 -0
- package/dist/compose/background.js +31 -0
- package/dist/compose/background.js.map +1 -0
- package/dist/compose/camera.d.ts +32 -0
- package/dist/compose/camera.d.ts.map +1 -0
- package/dist/compose/camera.js +98 -0
- package/dist/compose/camera.js.map +1 -0
- package/dist/compose/caption-font.d.ts +2 -0
- package/dist/compose/caption-font.d.ts.map +1 -1
- package/dist/compose/caption-font.js +2 -1
- package/dist/compose/caption-font.js.map +1 -1
- package/dist/compose/compose-drawing.d.ts +19 -0
- package/dist/compose/compose-drawing.d.ts.map +1 -0
- package/dist/compose/compose-drawing.js +38 -0
- package/dist/compose/compose-drawing.js.map +1 -0
- package/dist/compose/compose-input.d.ts +139 -0
- package/dist/compose/compose-input.d.ts.map +1 -1
- package/dist/compose/compose-input.js +48 -9
- package/dist/compose/compose-input.js.map +1 -1
- package/dist/compose/compose-plan.d.ts +36 -0
- package/dist/compose/compose-plan.d.ts.map +1 -0
- package/dist/compose/compose-plan.js +55 -0
- package/dist/compose/compose-plan.js.map +1 -0
- package/dist/compose/compose-size.d.ts +49 -0
- package/dist/compose/compose-size.d.ts.map +1 -0
- package/dist/compose/compose-size.js +67 -0
- package/dist/compose/compose-size.js.map +1 -0
- package/dist/compose/drawn-images.d.ts +19 -0
- package/dist/compose/drawn-images.d.ts.map +1 -0
- package/dist/compose/drawn-images.js +75 -0
- package/dist/compose/drawn-images.js.map +1 -0
- package/dist/compose/final-pass.d.ts +34 -0
- package/dist/compose/final-pass.d.ts.map +1 -0
- package/dist/compose/final-pass.js +106 -0
- package/dist/compose/final-pass.js.map +1 -0
- package/dist/compose/frame-layout.d.ts +30 -0
- package/dist/compose/frame-layout.d.ts.map +1 -0
- package/dist/compose/frame-layout.js +33 -0
- package/dist/compose/frame-layout.js.map +1 -0
- package/dist/compose/overlays.d.ts +20 -0
- package/dist/compose/overlays.d.ts.map +1 -0
- package/dist/compose/overlays.js +30 -0
- package/dist/compose/overlays.js.map +1 -0
- package/dist/compose/page-again.d.ts +26 -0
- package/dist/compose/page-again.d.ts.map +1 -0
- package/dist/compose/page-again.js +50 -0
- package/dist/compose/page-again.js.map +1 -0
- package/dist/compose/player-copies.d.ts +5 -1
- package/dist/compose/player-copies.d.ts.map +1 -1
- package/dist/compose/player-copies.js +19 -16
- package/dist/compose/player-copies.js.map +1 -1
- package/dist/compose/register-compose.d.ts.map +1 -1
- package/dist/compose/register-compose.js +104 -20
- package/dist/compose/register-compose.js.map +1 -1
- package/dist/compose/render.d.ts +13 -4
- package/dist/compose/render.d.ts.map +1 -1
- package/dist/compose/render.js +48 -22
- package/dist/compose/render.js.map +1 -1
- package/dist/compose/scene-args.d.ts +32 -7
- package/dist/compose/scene-args.d.ts.map +1 -1
- package/dist/compose/scene-args.js +77 -16
- package/dist/compose/scene-args.js.map +1 -1
- package/dist/compose/scene-look.d.ts +31 -0
- package/dist/compose/scene-look.d.ts.map +1 -0
- package/dist/compose/scene-look.js +46 -0
- package/dist/compose/scene-look.js.map +1 -0
- package/dist/compose/scene-plan.d.ts +0 -2
- package/dist/compose/scene-plan.d.ts.map +1 -1
- package/dist/compose/scene-plan.js +7 -5
- package/dist/compose/scene-plan.js.map +1 -1
- package/dist/compose/sound-effects.d.ts +23 -0
- package/dist/compose/sound-effects.d.ts.map +1 -0
- package/dist/compose/sound-effects.js +40 -0
- package/dist/compose/sound-effects.js.map +1 -0
- package/dist/compose/transitions.d.ts +16 -0
- package/dist/compose/transitions.d.ts.map +1 -0
- package/dist/compose/transitions.js +38 -0
- package/dist/compose/transitions.js.map +1 -0
- package/dist/connector.d.ts +79 -14
- package/dist/connector.d.ts.map +1 -1
- package/dist/connector.js +33 -8
- package/dist/connector.js.map +1 -1
- package/dist/files/sidecar.d.ts +209 -0
- package/dist/files/sidecar.d.ts.map +1 -0
- package/dist/files/sidecar.js +66 -0
- package/dist/files/sidecar.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/look/look-at.d.ts +15 -0
- package/dist/look/look-at.d.ts.map +1 -0
- package/dist/look/look-at.js +20 -0
- package/dist/look/look-at.js.map +1 -0
- package/dist/look/look-input.d.ts +22 -0
- package/dist/look/look-input.d.ts.map +1 -0
- package/dist/look/look-input.js +25 -0
- package/dist/look/look-input.js.map +1 -0
- package/dist/look/register-look.d.ts +3 -0
- package/dist/look/register-look.d.ts.map +1 -0
- package/dist/look/register-look.js +52 -0
- package/dist/look/register-look.js.map +1 -0
- package/dist/narration/alignment.d.ts +15 -0
- package/dist/narration/alignment.d.ts.map +1 -0
- package/dist/narration/alignment.js +21 -0
- package/dist/narration/alignment.js.map +1 -0
- package/dist/narration/captions.d.ts +22 -4
- package/dist/narration/captions.d.ts.map +1 -1
- package/dist/narration/captions.js +44 -16
- package/dist/narration/captions.js.map +1 -1
- package/dist/narration/join-args.d.ts +3 -0
- package/dist/narration/join-args.d.ts.map +1 -0
- package/dist/narration/join-args.js +19 -0
- package/dist/narration/join-args.js.map +1 -0
- package/dist/narration/narration-cache.d.ts +25 -0
- package/dist/narration/narration-cache.d.ts.map +1 -0
- package/dist/narration/narration-cache.js +36 -0
- package/dist/narration/narration-cache.js.map +1 -0
- package/dist/narration/piper.d.ts +2 -6
- package/dist/narration/piper.d.ts.map +1 -1
- package/dist/narration/piper.js +9 -15
- package/dist/narration/piper.js.map +1 -1
- package/dist/narration/pronunciations.d.ts +11 -0
- package/dist/narration/pronunciations.d.ts.map +1 -0
- package/dist/narration/pronunciations.js +40 -0
- package/dist/narration/pronunciations.js.map +1 -0
- package/dist/narration/register-narration.d.ts +8 -0
- package/dist/narration/register-narration.d.ts.map +1 -1
- package/dist/narration/register-narration.js +54 -26
- package/dist/narration/register-narration.js.map +1 -1
- package/dist/narration/register-voice.d.ts +27 -0
- package/dist/narration/register-voice.d.ts.map +1 -0
- package/dist/narration/register-voice.js +82 -0
- package/dist/narration/register-voice.js.map +1 -0
- package/dist/narration/sentence-speaker.d.ts +35 -0
- package/dist/narration/sentence-speaker.d.ts.map +1 -0
- package/dist/narration/sentence-speaker.js +44 -0
- package/dist/narration/sentence-speaker.js.map +1 -0
- package/dist/narration/speech-services.d.ts +23 -3
- package/dist/narration/speech-services.d.ts.map +1 -1
- package/dist/narration/speech-services.js +27 -10
- package/dist/narration/speech-services.js.map +1 -1
- package/dist/narration/voice-args.d.ts +3 -0
- package/dist/narration/voice-args.d.ts.map +1 -0
- package/dist/narration/voice-args.js +10 -0
- package/dist/narration/voice-args.js.map +1 -0
- package/dist/programs/media-duration.d.ts +12 -0
- package/dist/programs/media-duration.d.ts.map +1 -1
- package/dist/programs/media-duration.js +13 -0
- package/dist/programs/media-duration.js.map +1 -1
- package/dist/programs/run-program.d.ts +1 -0
- package/dist/programs/run-program.d.ts.map +1 -1
- package/dist/programs/run-program.js +1 -0
- package/dist/programs/run-program.js.map +1 -1
- package/dist/record/address.d.ts +1 -1
- package/dist/record/address.d.ts.map +1 -1
- package/dist/record/address.js +5 -7
- package/dist/record/address.js.map +1 -1
- package/dist/record/cursor-script.d.ts +2 -1
- package/dist/record/cursor-script.d.ts.map +1 -1
- package/dist/record/cursor-script.js +52 -14
- package/dist/record/cursor-script.js.map +1 -1
- package/dist/record/encoder-args.js +1 -1
- package/dist/record/motion.d.ts +19 -0
- package/dist/record/motion.d.ts.map +1 -0
- package/dist/record/motion.js +59 -0
- package/dist/record/motion.js.map +1 -0
- package/dist/record/narration-cues.d.ts +14 -0
- package/dist/record/narration-cues.d.ts.map +1 -0
- package/dist/record/narration-cues.js +12 -0
- package/dist/record/narration-cues.js.map +1 -0
- package/dist/record/page-clock.d.ts +7 -0
- package/dist/record/page-clock.d.ts.map +1 -0
- package/dist/record/page-clock.js +13 -0
- package/dist/record/page-clock.js.map +1 -0
- package/dist/record/page-ready.d.ts +4 -0
- package/dist/record/page-ready.d.ts.map +1 -0
- package/dist/record/page-ready.js +15 -0
- package/dist/record/page-ready.js.map +1 -0
- package/dist/record/page-route.d.ts +13 -0
- package/dist/record/page-route.d.ts.map +1 -0
- package/dist/record/page-route.js +64 -0
- package/dist/record/page-route.js.map +1 -0
- package/dist/record/pointer.d.ts +6 -7
- package/dist/record/pointer.d.ts.map +1 -1
- package/dist/record/pointer.js +11 -10
- package/dist/record/pointer.js.map +1 -1
- package/dist/record/record-source.d.ts +16 -0
- package/dist/record/record-source.d.ts.map +1 -0
- package/dist/record/record-source.js +11 -0
- package/dist/record/record-source.js.map +1 -0
- package/dist/record/recorder.d.ts +19 -7
- package/dist/record/recorder.d.ts.map +1 -1
- package/dist/record/recorder.js +52 -18
- package/dist/record/recorder.js.map +1 -1
- package/dist/record/register-record.d.ts.map +1 -1
- package/dist/record/register-record.js +45 -13
- package/dist/record/register-record.js.map +1 -1
- package/dist/record/run-step.d.ts +16 -3
- package/dist/record/run-step.d.ts.map +1 -1
- package/dist/record/run-step.js +43 -23
- package/dist/record/run-step.js.map +1 -1
- package/dist/record/screencast.js +1 -1
- package/dist/record/steps.d.ts +56 -1
- package/dist/record/steps.d.ts.map +1 -1
- package/dist/record/steps.js +36 -18
- package/dist/record/steps.js.map +1 -1
- package/dist/review/analysis.d.ts +19 -0
- package/dist/review/analysis.d.ts.map +1 -0
- package/dist/review/analysis.js +36 -0
- package/dist/review/analysis.js.map +1 -0
- package/dist/review/frames.d.ts +9 -0
- package/dist/review/frames.d.ts.map +1 -0
- package/dist/review/frames.js +28 -0
- package/dist/review/frames.js.map +1 -0
- package/dist/review/register-review.d.ts +24 -0
- package/dist/review/register-review.d.ts.map +1 -0
- package/dist/review/register-review.js +81 -0
- package/dist/review/register-review.js.map +1 -0
- package/dist/review/sheet-args.d.ts +11 -0
- package/dist/review/sheet-args.d.ts.map +1 -0
- package/dist/review/sheet-args.js +24 -0
- package/dist/review/sheet-args.js.map +1 -0
- package/dist/settings.d.ts +4 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +4 -2
- package/dist/settings.js.map +1 -1
- package/dist/setup/ffmpeg-readiness.js +1 -1
- package/dist/setup/ffmpeg-readiness.js.map +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +3 -2
- package/dist/ui.js.map +1 -1
- package/dist/web/components/player.css +1 -1
- package/dist/web/components/player.js +33 -21
- package/docs/guide.md +39 -10
- package/locales/ar.json +12 -1
- package/locales/en.json +12 -1
- package/package.json +11 -5
- package/sounds/SOURCES.md +13 -0
- package/sounds/click.wav +0 -0
- package/sounds/keys.wav +0 -0
- package/sounds/pop.wav +0 -0
- package/sounds/whoosh.wav +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-record.d.ts","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"register-record.d.ts","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAqCtC,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAwC7C"}
|
|
@@ -3,17 +3,42 @@ import { launchChannel } from "../browser/launch.js";
|
|
|
3
3
|
import { startBrowser } from "../browser/start-browser.js";
|
|
4
4
|
import { answerPath } from "../files/answer-path.js";
|
|
5
5
|
import { placeOutput, workFolder } from "../files/place-output.js";
|
|
6
|
-
import {
|
|
6
|
+
import { readSidecar, sidecarOutputPath, writeSidecar } from "../files/sidecar.js";
|
|
7
|
+
import { inputPath, outputPath } from "../files/workspace-paths.js";
|
|
7
8
|
import { programOf } from "../programs/programs.js";
|
|
8
|
-
import {
|
|
9
|
+
import { invalid } from "../problems.js";
|
|
10
|
+
import { audioExtensions } from "../compose/scene-plan.js";
|
|
11
|
+
import { recordableAddress } from "./address.js";
|
|
12
|
+
import { cuesOf } from "./narration-cues.js";
|
|
9
13
|
import { ownPortOf } from "./own-port.js";
|
|
10
14
|
import { record } from "./recorder.js";
|
|
15
|
+
import { defaultSize, sourceOf } from "./record-source.js";
|
|
11
16
|
import { recordInputSchema, recordOutputSchema } from "./steps.js";
|
|
12
|
-
// `video record` (plan 16 §16.5):
|
|
13
|
-
// closed when the call ends, fails, or is cancelled, and the work folder removed.
|
|
17
|
+
// `video record` (plan 16 §16.5): the addresses, the page, the narration, and the outputs are checked before a browser
|
|
18
|
+
// starts; the browser is closed when the call ends, fails, or is cancelled, and the work folder removed. The
|
|
19
|
+
// recording's sidecar is placed beside it (ADR 0115, 31).
|
|
20
|
+
function recordSourceOf(root, input, ownPort) {
|
|
21
|
+
for (const step of input.steps ?? [])
|
|
22
|
+
if (step.do === 'goto')
|
|
23
|
+
recordableAddress(step.url, ownPort);
|
|
24
|
+
return sourceOf(root, input, ownPort);
|
|
25
|
+
}
|
|
26
|
+
function cuesFor(root, input) {
|
|
27
|
+
if (input.narration === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
const narration = readSidecar(inputPath(root, input.narration, audioExtensions));
|
|
30
|
+
if (narration?.kind !== 'narration')
|
|
31
|
+
throw invalid(`${input.narration} has no captions beside it; make it with narrate or voice.`, { path: input.narration });
|
|
32
|
+
const lines = narration.captions.length;
|
|
33
|
+
for (const step of input.steps ?? []) {
|
|
34
|
+
if (step.do !== 'wait' && step.atLine !== undefined && step.atLine > lines)
|
|
35
|
+
throw invalid(`atLine ${String(step.atLine)} is past the narration's ${String(lines)} caption lines.`, { atLine: step.atLine, lines });
|
|
36
|
+
}
|
|
37
|
+
return cuesOf(narration);
|
|
38
|
+
}
|
|
14
39
|
export function registerRecord(ctx) {
|
|
15
40
|
ctx.registerCommand('kvvideo.clip.record', {
|
|
16
|
-
description: 'Records a
|
|
41
|
+
description: 'Records a website or an HTML page of the workspace, with its steps, as an MP4 file in the workspace.',
|
|
17
42
|
input: recordInputSchema,
|
|
18
43
|
output: recordOutputSchema,
|
|
19
44
|
public: true,
|
|
@@ -22,21 +47,28 @@ export function registerRecord(ctx) {
|
|
|
22
47
|
handle: async (input) => {
|
|
23
48
|
const root = ctx.job.workspace.path;
|
|
24
49
|
const ownPort = ownPortOf(process.env['KVMAN_HOME']);
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
50
|
+
const source = recordSourceOf(root, input, ownPort);
|
|
51
|
+
const cues = cuesFor(root, input);
|
|
52
|
+
const replace = input.replace === true;
|
|
53
|
+
const output = outputPath(root, input.file, '.mp4', replace);
|
|
54
|
+
const sidecar = sidecarOutputPath(output, replace, input.file);
|
|
55
|
+
const device = input.device ?? 'desktop';
|
|
56
|
+
const width = input.width ?? defaultSize[device].width;
|
|
57
|
+
const height = input.height ?? defaultSize[device].height;
|
|
30
58
|
const ffmpeg = await programOf(ctx, 'ffmpeg');
|
|
31
59
|
const browser = await startBrowser(await ctx.settings.get('kvvideo.browser'), launchChannel);
|
|
32
60
|
const work = workFolder();
|
|
33
61
|
const stop = () => void browser.close();
|
|
34
62
|
ctx.job.signal.addEventListener('abort', stop, { once: true });
|
|
35
63
|
try {
|
|
36
|
-
const
|
|
37
|
-
const recording = await record(browser,
|
|
64
|
+
const stepStarted = (n, of) => ctx.job.progress({ key: 'kvvideo.progress.step', params: { n, of } });
|
|
65
|
+
const recording = await record(browser, { source, steps: input.steps ?? [], width, height, device, cues, folder: work.folder, ownPort, ffmpeg, signal: ctx.job.signal, now: () => performance.now(), stepStarted });
|
|
66
|
+
const steps = input.steps ?? [];
|
|
67
|
+
const sourceName = source.kind === 'site' ? source.url : answerPath(root, source.file);
|
|
68
|
+
const made = writeSidecar(work.folder, { kind: source.kind, source: sourceName, width, height, device, scale: 2, durationMs: recording.durationMs, steps: [...steps], actions: recording.actions });
|
|
38
69
|
placeOutput(recording.video, output);
|
|
39
|
-
|
|
70
|
+
placeOutput(made, sidecar);
|
|
71
|
+
return { file: answerPath(root, output), durationMs: recording.durationMs, steps: recording.actions };
|
|
40
72
|
}
|
|
41
73
|
finally {
|
|
42
74
|
ctx.job.signal.removeEventListener('abort', stop);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-record.js","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"register-record.js","sourceRoot":"","sources":["../../src/record/register-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import type { Page } from 'playwright-core';
|
|
2
|
-
import { type
|
|
3
|
-
import type { Step } from './steps.ts';
|
|
4
|
-
export
|
|
2
|
+
import { type Point } from './motion.ts';
|
|
3
|
+
import type { Box, Step } from './steps.ts';
|
|
4
|
+
export type StepState = {
|
|
5
|
+
page: Page;
|
|
6
|
+
device: 'desktop' | 'phone';
|
|
7
|
+
pointer: Point;
|
|
8
|
+
elapsed(): number;
|
|
9
|
+
};
|
|
10
|
+
export type StepResult = {
|
|
11
|
+
pointer: Point;
|
|
12
|
+
act?: {
|
|
13
|
+
atMs: number;
|
|
14
|
+
box: Box;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare function runStep(state: StepState, step: Step): Promise<StepResult>;
|
|
5
18
|
/** Whether the page is held a moment after the step, so the viewer sees what it did. */
|
|
6
19
|
export declare function pausesAfter(step: Step): boolean;
|
|
7
20
|
//# sourceMappingURL=run-step.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-step.d.ts","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,
|
|
1
|
+
{"version":3,"file":"run-step.d.ts","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAqC,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAK5C,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,IAAI,MAAM,CAAA;CAAE,CAAC;AAEvG,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,GAAG,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,GAAG,CAAA;KAAE,CAAA;CAAE,CAAC;AA+B9E,wBAAsB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAgC/E;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE/C"}
|
package/dist/record/run-step.js
CHANGED
|
@@ -1,45 +1,65 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { frameMs, scrollPlan, typingDelays } from "./motion.js";
|
|
2
|
+
import { centerOf, glideTo, targetBox } from "./pointer.js";
|
|
3
|
+
async function typeText(page, text) {
|
|
4
|
+
const delays = typingDelays(text);
|
|
5
|
+
for (const [index, character] of [...text].entries()) {
|
|
6
|
+
await page.waitForTimeout(delays[index] ?? 0);
|
|
7
|
+
await page.keyboard.type(character);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
4
10
|
async function scroll(page, pixels) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
await page.
|
|
9
|
-
await page.waitForTimeout(30);
|
|
10
|
-
left -= part;
|
|
11
|
+
for (const move of scrollPlan(pixels).moves) {
|
|
12
|
+
if (move !== 0)
|
|
13
|
+
await page.mouse.wheel(0, move);
|
|
14
|
+
await page.waitForTimeout(frameMs);
|
|
11
15
|
}
|
|
12
16
|
}
|
|
13
|
-
|
|
17
|
+
// Brings the pointer or the finger to the target, and answers where it is and when it arrived.
|
|
18
|
+
async function reach(state, target) {
|
|
19
|
+
const box = await targetBox(state.page, target);
|
|
20
|
+
const center = centerOf(box);
|
|
21
|
+
if (state.device === 'phone')
|
|
22
|
+
return { pointer: center, act: { atMs: state.elapsed(), box } };
|
|
23
|
+
const pointer = await glideTo(state.page, state.pointer, center);
|
|
24
|
+
return { pointer, act: { atMs: state.elapsed(), box } };
|
|
25
|
+
}
|
|
26
|
+
async function press(state, at) {
|
|
27
|
+
if (state.device === 'phone')
|
|
28
|
+
await state.page.touchscreen.tap(at.x, at.y);
|
|
29
|
+
else
|
|
30
|
+
await state.page.mouse.click(at.x, at.y);
|
|
31
|
+
}
|
|
32
|
+
export async function runStep(state, step) {
|
|
33
|
+
const { page } = state;
|
|
14
34
|
switch (step.do) {
|
|
15
35
|
case 'goto':
|
|
16
36
|
await page.goto(step.url, { waitUntil: 'load', timeout: 30_000 });
|
|
17
|
-
return pointer;
|
|
37
|
+
return { pointer: state.pointer };
|
|
18
38
|
case 'click': {
|
|
19
|
-
const
|
|
20
|
-
await
|
|
21
|
-
return
|
|
39
|
+
const reached = await reach(state, step.target);
|
|
40
|
+
await press(state, reached.pointer);
|
|
41
|
+
return reached;
|
|
22
42
|
}
|
|
23
43
|
case 'type': {
|
|
24
|
-
const
|
|
25
|
-
await
|
|
26
|
-
await page
|
|
27
|
-
return
|
|
44
|
+
const reached = await reach(state, step.target);
|
|
45
|
+
await press(state, reached.pointer);
|
|
46
|
+
await typeText(page, step.text);
|
|
47
|
+
return reached;
|
|
28
48
|
}
|
|
29
49
|
case 'press':
|
|
30
50
|
await page.keyboard.press(step.key);
|
|
31
|
-
return pointer;
|
|
51
|
+
return { pointer: state.pointer };
|
|
32
52
|
case 'hover':
|
|
33
|
-
return
|
|
53
|
+
return reach(state, step.target);
|
|
34
54
|
case 'scroll':
|
|
35
55
|
await scroll(page, step.pixels);
|
|
36
|
-
return pointer;
|
|
56
|
+
return { pointer: state.pointer };
|
|
37
57
|
case 'wait':
|
|
38
58
|
await page.waitForTimeout(step.ms);
|
|
39
|
-
return pointer;
|
|
59
|
+
return { pointer: state.pointer };
|
|
40
60
|
case 'waitFor':
|
|
41
61
|
await page.locator(step.target).first().waitFor({ state: 'visible', timeout: 10_000 });
|
|
42
|
-
return pointer;
|
|
62
|
+
return { pointer: state.pointer };
|
|
43
63
|
}
|
|
44
64
|
}
|
|
45
65
|
/** Whether the page is held a moment after the step, so the viewer sees what it did. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-step.js","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"run-step.js","sourceRoot":"","sources":["../../src/record/run-step.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ export async function startScreencast(page, size, onFrame, now) {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
|
-
await session.send('Page.startScreencast', { format: 'jpeg', quality:
|
|
26
|
+
await session.send('Page.startScreencast', { format: 'jpeg', quality: 95, maxWidth: size.width, maxHeight: size.height, everyNthFrame: 1 });
|
|
27
27
|
return {
|
|
28
28
|
stop: async () => {
|
|
29
29
|
stopped = true;
|
package/dist/record/steps.d.ts
CHANGED
|
@@ -1,71 +1,126 @@
|
|
|
1
1
|
import { z } from '@kvman/sdk';
|
|
2
|
+
export declare const address: z.ZodString;
|
|
2
3
|
export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
3
4
|
do: z.ZodLiteral<"goto">;
|
|
4
5
|
url: z.ZodString;
|
|
6
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
5
7
|
}, z.core.$strict>, z.ZodObject<{
|
|
6
8
|
do: z.ZodLiteral<"click">;
|
|
7
9
|
target: z.ZodString;
|
|
10
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
8
11
|
}, z.core.$strict>, z.ZodObject<{
|
|
9
12
|
do: z.ZodLiteral<"type">;
|
|
10
13
|
target: z.ZodString;
|
|
11
14
|
text: z.ZodString;
|
|
15
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
12
16
|
}, z.core.$strict>, z.ZodObject<{
|
|
13
17
|
do: z.ZodLiteral<"press">;
|
|
14
18
|
key: z.ZodString;
|
|
19
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
15
20
|
}, z.core.$strict>, z.ZodObject<{
|
|
16
21
|
do: z.ZodLiteral<"hover">;
|
|
17
22
|
target: z.ZodString;
|
|
23
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
18
24
|
}, z.core.$strict>, z.ZodObject<{
|
|
19
25
|
do: z.ZodLiteral<"scroll">;
|
|
20
26
|
pixels: z.ZodNumber;
|
|
27
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
21
28
|
}, z.core.$strict>, z.ZodObject<{
|
|
22
29
|
do: z.ZodLiteral<"wait">;
|
|
23
30
|
ms: z.ZodNumber;
|
|
24
31
|
}, z.core.$strict>, z.ZodObject<{
|
|
25
32
|
do: z.ZodLiteral<"waitFor">;
|
|
26
33
|
target: z.ZodString;
|
|
34
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
27
35
|
}, z.core.$strict>], "do">;
|
|
28
36
|
/** One step of a recording. */
|
|
29
37
|
export type Step = z.output<typeof stepSchema>;
|
|
38
|
+
export declare const side: z.ZodNumber;
|
|
30
39
|
export declare const recordInputSchema: z.ZodObject<{
|
|
31
|
-
url: z.ZodString
|
|
40
|
+
url: z.ZodExactOptional<z.ZodString>;
|
|
41
|
+
page: z.ZodExactOptional<z.ZodString>;
|
|
32
42
|
steps: z.ZodExactOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
33
43
|
do: z.ZodLiteral<"goto">;
|
|
34
44
|
url: z.ZodString;
|
|
45
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
35
46
|
}, z.core.$strict>, z.ZodObject<{
|
|
36
47
|
do: z.ZodLiteral<"click">;
|
|
37
48
|
target: z.ZodString;
|
|
49
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
38
50
|
}, z.core.$strict>, z.ZodObject<{
|
|
39
51
|
do: z.ZodLiteral<"type">;
|
|
40
52
|
target: z.ZodString;
|
|
41
53
|
text: z.ZodString;
|
|
54
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
42
55
|
}, z.core.$strict>, z.ZodObject<{
|
|
43
56
|
do: z.ZodLiteral<"press">;
|
|
44
57
|
key: z.ZodString;
|
|
58
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
45
59
|
}, z.core.$strict>, z.ZodObject<{
|
|
46
60
|
do: z.ZodLiteral<"hover">;
|
|
47
61
|
target: z.ZodString;
|
|
62
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
48
63
|
}, z.core.$strict>, z.ZodObject<{
|
|
49
64
|
do: z.ZodLiteral<"scroll">;
|
|
50
65
|
pixels: z.ZodNumber;
|
|
66
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
51
67
|
}, z.core.$strict>, z.ZodObject<{
|
|
52
68
|
do: z.ZodLiteral<"wait">;
|
|
53
69
|
ms: z.ZodNumber;
|
|
54
70
|
}, z.core.$strict>, z.ZodObject<{
|
|
55
71
|
do: z.ZodLiteral<"waitFor">;
|
|
56
72
|
target: z.ZodString;
|
|
73
|
+
atLine: z.ZodExactOptional<z.ZodNumber>;
|
|
57
74
|
}, z.core.$strict>], "do">>>;
|
|
58
75
|
file: z.ZodString;
|
|
59
76
|
width: z.ZodExactOptional<z.ZodNumber>;
|
|
60
77
|
height: z.ZodExactOptional<z.ZodNumber>;
|
|
78
|
+
device: z.ZodExactOptional<z.ZodEnum<{
|
|
79
|
+
phone: "phone";
|
|
80
|
+
desktop: "desktop";
|
|
81
|
+
}>>;
|
|
82
|
+
narration: z.ZodExactOptional<z.ZodString>;
|
|
61
83
|
replace: z.ZodExactOptional<z.ZodBoolean>;
|
|
62
84
|
}, z.core.$strict>;
|
|
85
|
+
/** A recording's input. */
|
|
86
|
+
export type RecordInput = z.output<typeof recordInputSchema>;
|
|
87
|
+
declare const boxSchema: z.ZodObject<{
|
|
88
|
+
x: z.ZodNumber;
|
|
89
|
+
y: z.ZodNumber;
|
|
90
|
+
width: z.ZodNumber;
|
|
91
|
+
height: z.ZodNumber;
|
|
92
|
+
}, z.core.$strip>;
|
|
93
|
+
/** A target's box, in CSS pixels of the viewport. */
|
|
94
|
+
export type Box = z.output<typeof boxSchema>;
|
|
95
|
+
export declare const actionSchema: z.ZodObject<{
|
|
96
|
+
do: z.ZodString;
|
|
97
|
+
atMs: z.ZodNumber;
|
|
98
|
+
actMs: z.ZodOptional<z.ZodNumber>;
|
|
99
|
+
endMs: z.ZodNumber;
|
|
100
|
+
box: z.ZodOptional<z.ZodObject<{
|
|
101
|
+
x: z.ZodNumber;
|
|
102
|
+
y: z.ZodNumber;
|
|
103
|
+
width: z.ZodNumber;
|
|
104
|
+
height: z.ZodNumber;
|
|
105
|
+
}, z.core.$strip>>;
|
|
106
|
+
}, z.core.$strip>;
|
|
107
|
+
/** What a step did, and when, from the recording's start. */
|
|
108
|
+
export type Action = z.output<typeof actionSchema>;
|
|
63
109
|
export declare const recordOutputSchema: z.ZodObject<{
|
|
64
110
|
file: z.ZodString;
|
|
65
111
|
durationMs: z.ZodNumber;
|
|
66
112
|
steps: z.ZodArray<z.ZodObject<{
|
|
67
113
|
do: z.ZodString;
|
|
68
114
|
atMs: z.ZodNumber;
|
|
115
|
+
actMs: z.ZodOptional<z.ZodNumber>;
|
|
116
|
+
endMs: z.ZodNumber;
|
|
117
|
+
box: z.ZodOptional<z.ZodObject<{
|
|
118
|
+
x: z.ZodNumber;
|
|
119
|
+
y: z.ZodNumber;
|
|
120
|
+
width: z.ZodNumber;
|
|
121
|
+
height: z.ZodNumber;
|
|
122
|
+
}, z.core.$strip>>;
|
|
69
123
|
}, z.core.$strip>>;
|
|
70
124
|
}, z.core.$strip>;
|
|
125
|
+
export {};
|
|
71
126
|
//# sourceMappingURL=steps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../src/record/steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../src/record/steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAM/B,eAAO,MAAM,OAAO,aAA8B,CAAC;AAGnD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BASrB,CAAC;AAEH,+BAA+B;AAC/B,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAE/C,eAAO,MAAM,IAAI,aAAkH,CAAC;AAcpI,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAK5B,CAAC;AAEH,2BAA2B;AAC3B,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE7D,QAAA,MAAM,SAAS;;;;;iBAAwI,CAAC;AAExJ,qDAAqD;AACrD,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,SAAS,CAAC,CAAC;AAE7C,eAAO,MAAM,YAAY;;;;;;;;;;;iBAMvB,CAAC;AAEH,6DAA6D;AAC7D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAEnD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;iBAA2G,CAAC"}
|
package/dist/record/steps.js
CHANGED
|
@@ -1,29 +1,47 @@
|
|
|
1
1
|
import { z } from '@kvman/sdk';
|
|
2
|
-
// A recording's input (plan 16 §16.5): the
|
|
2
|
+
// A recording's input and answer (plan 16 §16.5): a website or a page of the workspace, up to 200 steps, the size and
|
|
3
|
+
// device, the narration whose caption lines the steps may wait for, and the output.
|
|
3
4
|
const target = z.string().min(1).max(500).describe('A Playwright selector: CSS, text=Save, or role=button[name="Save"]; its first match is used once visible.');
|
|
4
|
-
const address = z.string().min(1).max(2000);
|
|
5
|
+
export const address = z.string().min(1).max(2000);
|
|
6
|
+
const atLine = z.number().int().min(1).max(1000).describe('Start no earlier than this caption line (from 1) of the narration is heard.').exactOptional();
|
|
5
7
|
export const stepSchema = z.discriminatedUnion('do', [
|
|
6
|
-
z.strictObject({ do: z.literal('goto'), url: address.describe('
|
|
7
|
-
z.strictObject({ do: z.literal('click'), target }),
|
|
8
|
-
z.strictObject({ do: z.literal('type'), target, text: z.string().min(1).max(2000).describe('The text typed,
|
|
9
|
-
z.strictObject({ do: z.literal('press'), key: z.string().min(1).max(100).describe('A key or combination, as Enter or Control+A.') }),
|
|
10
|
-
z.strictObject({ do: z.literal('hover'), target }),
|
|
11
|
-
z.strictObject({ do: z.literal('scroll'), pixels: z.number().int().min(-20_000).max(20_000).describe('Pixels to scroll down; negative scrolls up.') }),
|
|
8
|
+
z.strictObject({ do: z.literal('goto'), url: address.describe('An http or https address to open.'), atLine }),
|
|
9
|
+
z.strictObject({ do: z.literal('click'), target, atLine }),
|
|
10
|
+
z.strictObject({ do: z.literal('type'), target, text: z.string().min(1).max(2000).describe('The text typed, at a natural pace.'), atLine }),
|
|
11
|
+
z.strictObject({ do: z.literal('press'), key: z.string().min(1).max(100).describe('A key or combination, as Enter or Control+A.'), atLine }),
|
|
12
|
+
z.strictObject({ do: z.literal('hover'), target, atLine }),
|
|
13
|
+
z.strictObject({ do: z.literal('scroll'), pixels: z.number().int().min(-20_000).max(20_000).describe('Pixels to scroll down; negative scrolls up.'), atLine }),
|
|
12
14
|
z.strictObject({ do: z.literal('wait'), ms: z.number().int().min(0).max(60_000).describe('How long the picture holds.') }),
|
|
13
|
-
z.strictObject({ do: z.literal('waitFor'), target }),
|
|
15
|
+
z.strictObject({ do: z.literal('waitFor'), target, atLine }),
|
|
14
16
|
]);
|
|
15
|
-
const side = z.number().int().min(320).max(1920).refine((pixels) => pixels % 2 === 0, 'A side is an even number of pixels.');
|
|
16
|
-
|
|
17
|
-
url: address.describe('
|
|
17
|
+
export const side = z.number().int().min(320).max(1920).refine((pixels) => pixels % 2 === 0, 'A side is an even number of pixels.');
|
|
18
|
+
const recordFields = z.strictObject({
|
|
19
|
+
url: address.describe('A website to record: any http or https address.').exactOptional(),
|
|
20
|
+
page: z.string().min(1).max(1000).describe('An .html file of the workspace to record, with the files beside it.').exactOptional(),
|
|
18
21
|
steps: z.array(stepSchema).max(200).describe('What happens on the page, in order.').exactOptional(),
|
|
19
22
|
file: z.string().min(1).max(1000).describe('The .mp4 file to write, in the workspace.'),
|
|
20
|
-
width: side.describe('The width in pixels
|
|
21
|
-
height: side.describe('The height in pixels
|
|
23
|
+
width: side.describe('The page width in CSS pixels: 1280 by default, 390 on a phone. The file is twice as wide.').exactOptional(),
|
|
24
|
+
height: side.describe('The page height in CSS pixels: 720 by default, 844 on a phone. The file is twice as high.').exactOptional(),
|
|
25
|
+
device: z.enum(['desktop', 'phone']).describe('desktop (the default), or phone: a touch screen with taps.').exactOptional(),
|
|
26
|
+
narration: z.string().min(1).max(1000).describe("The scene's narration (.mp3, .wav, .m4a), as narrate or voice made it; steps may wait for its lines with atLine.").exactOptional(),
|
|
22
27
|
replace: z.boolean().describe('Write over an existing file.').exactOptional(),
|
|
23
28
|
});
|
|
24
|
-
export const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
steps
|
|
29
|
+
export const recordInputSchema = recordFields.superRefine((input, context) => {
|
|
30
|
+
if ((input.url === undefined) === (input.page === undefined))
|
|
31
|
+
context.addIssue({ code: 'custom', path: ['url'], message: 'Give exactly one of url and page.' });
|
|
32
|
+
const steps = input.steps ?? [];
|
|
33
|
+
if (input.page !== undefined && steps.some((step) => step.do === 'goto'))
|
|
34
|
+
context.addIssue({ code: 'custom', path: ['steps'], message: 'A page has no goto steps.' });
|
|
35
|
+
if (input.narration === undefined && steps.some((step) => step.do !== 'wait' && step.atLine !== undefined))
|
|
36
|
+
context.addIssue({ code: 'custom', path: ['narration'], message: 'atLine needs a narration.' });
|
|
28
37
|
});
|
|
38
|
+
const boxSchema = z.object({ x: z.number().int(), y: z.number().int(), width: z.number().int().nonnegative(), height: z.number().int().nonnegative() });
|
|
39
|
+
export const actionSchema = z.object({
|
|
40
|
+
do: z.string(),
|
|
41
|
+
atMs: z.number().int().nonnegative(),
|
|
42
|
+
actMs: z.number().int().nonnegative().optional(),
|
|
43
|
+
endMs: z.number().int().nonnegative(),
|
|
44
|
+
box: boxSchema.optional(),
|
|
45
|
+
});
|
|
46
|
+
export const recordOutputSchema = z.object({ file: z.string(), durationMs: z.number().int().nonnegative(), steps: z.array(actionSchema) });
|
|
29
47
|
//# sourceMappingURL=steps.js.map
|
package/dist/record/steps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../../src/record/steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,
|
|
1
|
+
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../../src/record/steps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ProblemKind = 'silence' | 'black' | 'frozen' | 'loudness';
|
|
2
|
+
export type Problem = {
|
|
3
|
+
kind: ProblemKind;
|
|
4
|
+
atMs: number;
|
|
5
|
+
lengthMs: number;
|
|
6
|
+
};
|
|
7
|
+
export type Loudness = {
|
|
8
|
+
integratedLufs: number;
|
|
9
|
+
truePeakDb: number;
|
|
10
|
+
};
|
|
11
|
+
export type Analysis = {
|
|
12
|
+
loudness: Loudness | null;
|
|
13
|
+
problems: Problem[];
|
|
14
|
+
};
|
|
15
|
+
/** ffmpeg's arguments to analyse `video`, with its sound when it has one. */
|
|
16
|
+
export declare function analysisArgs(video: string, hasSound: boolean): string[];
|
|
17
|
+
/** The analysis in ffmpeg's output, over a video of `durationMs`. */
|
|
18
|
+
export declare function analysisOf(output: string, durationMs: number, hasSound: boolean): Analysis;
|
|
19
|
+
//# sourceMappingURL=analysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../../src/review/analysis.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEtE,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,MAAM,QAAQ,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC;AAE1E,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,CAIvE;AAoBD,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAQ1F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// What `review` measures in one ffmpeg run (plan 16 §16.10; ADR 0115, 20 and 48): black pictures of 0.5 s or more,
|
|
2
|
+
// pictures unchanged for 6 s or more, sound below −50 dB for 3 s or more, and the loudness, read from what ffmpeg's
|
|
3
|
+
// filters print. A video without sound is measured for its picture only.
|
|
4
|
+
/** ffmpeg's arguments to analyse `video`, with its sound when it has one. */
|
|
5
|
+
export function analysisArgs(video, hasSound) {
|
|
6
|
+
const picture = '[0:v]blackdetect=d=0.5:pix_th=0.10,freezedetect=n=-60dB:d=6[v]';
|
|
7
|
+
const sound = hasSound ? ';[0:a]silencedetect=n=-50dB:d=3,ebur128=peak=true[a]' : '';
|
|
8
|
+
return ['-hide_banner', '-nostats', '-loglevel', 'info', '-i', video, '-filter_complex', `${picture}${sound}`, '-map', '[v]', ...(hasSound ? ['-map', '[a]'] : []), '-f', 'null', '-'];
|
|
9
|
+
}
|
|
10
|
+
const ms = (seconds) => Math.round(Number(seconds ?? 0) * 1000);
|
|
11
|
+
// Spans that start on one line and end on a later one; one that never ends lasts to the video's end.
|
|
12
|
+
function spans(output, start, end, durationMs) {
|
|
13
|
+
const starts = [...output.matchAll(start)].map((match) => ms(match[1]));
|
|
14
|
+
const ends = [...output.matchAll(end)].map((match) => ms(match[1]));
|
|
15
|
+
return starts.map((atMs, index) => ({ atMs, lengthMs: Math.max(0, (ends[index] ?? durationMs) - atMs) }));
|
|
16
|
+
}
|
|
17
|
+
function loudnessOf(output) {
|
|
18
|
+
const summary = output.slice(output.lastIndexOf('Summary:'));
|
|
19
|
+
const integrated = /I:\s+(-?[\d.]+|-inf) LUFS/.exec(summary)?.[1];
|
|
20
|
+
const peak = /Peak:\s+(-?[\d.]+|-inf) dBFS/.exec(summary)?.[1];
|
|
21
|
+
if (integrated === undefined || peak === undefined)
|
|
22
|
+
return null;
|
|
23
|
+
// ebur128 floors its loudness at −70 LUFS; a peak of no sound at all, -inf, is read at that floor too.
|
|
24
|
+
return { integratedLufs: integrated === '-inf' ? -70 : Number(integrated), truePeakDb: peak === '-inf' ? -70 : Number(peak) };
|
|
25
|
+
}
|
|
26
|
+
/** The analysis in ffmpeg's output, over a video of `durationMs`. */
|
|
27
|
+
export function analysisOf(output, durationMs, hasSound) {
|
|
28
|
+
const black = [...output.matchAll(/black_start:([\d.]+) black_end:([\d.]+)/g)].map((match) => ({ kind: 'black', atMs: ms(match[1]), lengthMs: ms(match[2]) - ms(match[1]) }));
|
|
29
|
+
const frozen = spans(output, /freeze_start: ([\d.]+)/g, /freeze_end: ([\d.]+)/g, durationMs).map((span) => ({ kind: 'frozen', ...span }));
|
|
30
|
+
const silence = hasSound ? spans(output, /silence_start: (-?[\d.]+)/g, /silence_end: ([\d.]+)/g, durationMs).map((span) => ({ kind: 'silence', atMs: Math.max(0, span.atMs), lengthMs: span.lengthMs })) : [];
|
|
31
|
+
const loudness = hasSound ? loudnessOf(output) : null;
|
|
32
|
+
const off = loudness !== null && (loudness.integratedLufs < -15 || loudness.integratedLufs > -13 || loudness.truePeakDb > -1);
|
|
33
|
+
const problems = [...(off ? [{ kind: 'loudness', atMs: 0, lengthMs: durationMs }] : []), ...silence, ...black, ...frozen];
|
|
34
|
+
return { loudness, problems: problems.sort((a, b) => a.atMs - b.atMs) };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=analysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis.js","sourceRoot":"","sources":["../../src/review/analysis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { z } from '@kvman/sdk';
|
|
2
|
+
import type { videoSidecarSchema } from '../files/sidecar.ts';
|
|
3
|
+
export type VideoSidecar = z.output<typeof videoSidecarSchema>;
|
|
4
|
+
export declare const perSheet = 12;
|
|
5
|
+
/** The frames' times in ms, in order. */
|
|
6
|
+
export declare function frameTimes(sidecar: VideoSidecar | undefined, durationMs: number): number[];
|
|
7
|
+
/** The frames in sheets of 12. */
|
|
8
|
+
export declare function sheetsOf(times: readonly number[]): number[][];
|
|
9
|
+
//# sourceMappingURL=frames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/review/frames.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAM9D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI/D,eAAO,MAAM,QAAQ,KAAK,CAAC;AAU3B,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1F;AAED,kCAAkC;AAClC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,CAE7D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const closest = 500;
|
|
2
|
+
const most = 96;
|
|
3
|
+
export const perSheet = 12;
|
|
4
|
+
function wanted(sidecar, durationMs) {
|
|
5
|
+
if (sidecar === undefined)
|
|
6
|
+
return Array.from({ length: Math.max(1, Math.ceil(durationMs / 5000)) }, (_, index) => index * 5000);
|
|
7
|
+
const middles = sidecar.scenes.map((scene) => Math.round((scene.startMs + scene.endMs) / 2));
|
|
8
|
+
const afterTransitions = sidecar.scenes.slice(1).map((scene) => scene.startMs + 400);
|
|
9
|
+
const captions = sidecar.captions.map((caption) => Math.round((caption.startMs + caption.endMs) / 2));
|
|
10
|
+
return [...middles, ...afterTransitions, ...captions];
|
|
11
|
+
}
|
|
12
|
+
/** The frames' times in ms, in order. */
|
|
13
|
+
export function frameTimes(sidecar, durationMs) {
|
|
14
|
+
const kept = [];
|
|
15
|
+
for (const time of wanted(sidecar, durationMs).filter((at) => at >= 0 && at < durationMs).sort((a, b) => a - b)) {
|
|
16
|
+
const last = kept.at(-1);
|
|
17
|
+
if (last === undefined || time - last >= closest)
|
|
18
|
+
kept.push(time);
|
|
19
|
+
}
|
|
20
|
+
if (kept.length <= most)
|
|
21
|
+
return kept;
|
|
22
|
+
return Array.from({ length: most }, (_, index) => kept[Math.round((index * (kept.length - 1)) / (most - 1))] ?? 0);
|
|
23
|
+
}
|
|
24
|
+
/** The frames in sheets of 12. */
|
|
25
|
+
export function sheetsOf(times) {
|
|
26
|
+
return Array.from({ length: Math.ceil(times.length / perSheet) }, (_, index) => times.slice(index * perSheet, (index + 1) * perSheet));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=frames.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frames.js","sourceRoot":"","sources":["../../src/review/frames.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,GAAG,GAAG,CAAC;AACpB,MAAM,IAAI,GAAG,EAAE,CAAC;AAChB,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC;AAE3B,SAAS,MAAM,CAAC,OAAiC,EAAE,UAAkB;IACnE,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAChI,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACtG,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,gBAAgB,EAAE,GAAG,QAAQ,CAAC,CAAC;AACxD,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,UAAU,CAAC,OAAiC,EAAE,UAAkB;IAC9E,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAChH,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,GAAG,IAAI,IAAI,OAAO;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACrH,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,QAAQ,CAAC,KAAwB;IAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,QAAQ,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;AACzI,CAAC"}
|