@kvman/kvvideo 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +10 -2
- package/dist/api.d.ts +79 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +2 -0
- package/dist/api.js.map +1 -0
- package/dist/browser/channels.d.ts +6 -0
- package/dist/browser/channels.d.ts.map +1 -0
- package/dist/browser/channels.js +5 -0
- package/dist/browser/channels.js.map +1 -0
- package/dist/browser/launch.d.ts +4 -0
- package/dist/browser/launch.d.ts.map +1 -0
- package/dist/browser/launch.js +8 -0
- package/dist/browser/launch.js.map +1 -0
- package/dist/browser/start-browser.d.ts +5 -0
- package/dist/browser/start-browser.d.ts.map +1 -0
- package/dist/browser/start-browser.js +17 -0
- package/dist/browser/start-browser.js.map +1 -0
- package/dist/compose/ass-file.d.ts +12 -0
- package/dist/compose/ass-file.d.ts.map +1 -0
- package/dist/compose/ass-file.js +36 -0
- package/dist/compose/ass-file.js.map +1 -0
- package/dist/compose/caption-font.d.ts +6 -0
- package/dist/compose/caption-font.d.ts.map +1 -0
- package/dist/compose/caption-font.js +23 -0
- package/dist/compose/caption-font.js.map +1 -0
- package/dist/compose/compose-input.d.ts +48 -0
- package/dist/compose/compose-input.d.ts.map +1 -0
- package/dist/compose/compose-input.js +37 -0
- package/dist/compose/compose-input.js.map +1 -0
- package/dist/compose/player-copies.d.ts +6 -0
- package/dist/compose/player-copies.d.ts.map +1 -0
- package/dist/compose/player-copies.js +31 -0
- package/dist/compose/player-copies.js.map +1 -0
- package/dist/compose/register-compose.d.ts +3 -0
- package/dist/compose/register-compose.d.ts.map +1 -0
- package/dist/compose/register-compose.js +65 -0
- package/dist/compose/register-compose.js.map +1 -0
- package/dist/compose/render.d.ts +16 -0
- package/dist/compose/render.d.ts.map +1 -0
- package/dist/compose/render.js +35 -0
- package/dist/compose/render.js.map +1 -0
- package/dist/compose/scene-args.d.ts +12 -0
- package/dist/compose/scene-args.d.ts.map +1 -0
- package/dist/compose/scene-args.js +42 -0
- package/dist/compose/scene-args.js.map +1 -0
- package/dist/compose/scene-plan.d.ts +28 -0
- package/dist/compose/scene-plan.d.ts.map +1 -0
- package/dist/compose/scene-plan.js +32 -0
- package/dist/compose/scene-plan.js.map +1 -0
- package/dist/connector.d.ts +79 -0
- package/dist/connector.d.ts.map +1 -0
- package/dist/connector.js +44 -0
- package/dist/connector.js.map +1 -0
- package/dist/files/answer-path.d.ts +3 -0
- package/dist/files/answer-path.d.ts.map +1 -0
- package/dist/files/answer-path.js +6 -0
- package/dist/files/answer-path.js.map +1 -0
- package/dist/files/place-output.d.ts +8 -0
- package/dist/files/place-output.d.ts.map +1 -0
- package/dist/files/place-output.js +24 -0
- package/dist/files/place-output.js.map +1 -0
- package/dist/files/workspace-paths.d.ts +7 -0
- package/dist/files/workspace-paths.d.ts.map +1 -0
- package/dist/files/workspace-paths.js +48 -0
- package/dist/files/workspace-paths.js.map +1 -0
- package/dist/fonts/bundled-fonts.d.ts +10 -0
- package/dist/fonts/bundled-fonts.d.ts.map +1 -0
- package/dist/fonts/bundled-fonts.js +10 -0
- package/dist/fonts/bundled-fonts.js.map +1 -0
- package/dist/fonts/font-family.d.ts +2 -0
- package/dist/fonts/font-family.d.ts.map +1 -0
- package/dist/fonts/font-family.js +43 -0
- package/dist/fonts/font-family.js.map +1 -0
- package/dist/fonts/register-fonts.d.ts +3 -0
- package/dist/fonts/register-fonts.d.ts.map +1 -0
- package/dist/fonts/register-fonts.js +12 -0
- package/dist/fonts/register-fonts.js.map +1 -0
- package/dist/guide.d.ts +3 -0
- package/dist/guide.d.ts.map +1 -0
- package/dist/guide.js +6 -0
- package/dist/guide.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/narration/captions.d.ts +10 -0
- package/dist/narration/captions.d.ts.map +1 -0
- package/dist/narration/captions.js +42 -0
- package/dist/narration/captions.js.map +1 -0
- package/dist/narration/piper.d.ts +8 -0
- package/dist/narration/piper.d.ts.map +1 -0
- package/dist/narration/piper.js +19 -0
- package/dist/narration/piper.js.map +1 -0
- package/dist/narration/register-narration.d.ts +17 -0
- package/dist/narration/register-narration.d.ts.map +1 -0
- package/dist/narration/register-narration.js +75 -0
- package/dist/narration/register-narration.js.map +1 -0
- package/dist/narration/speech-services.d.ts +19 -0
- package/dist/narration/speech-services.d.ts.map +1 -0
- package/dist/narration/speech-services.js +50 -0
- package/dist/narration/speech-services.js.map +1 -0
- package/dist/problems.d.ts +12 -0
- package/dist/problems.d.ts.map +1 -0
- package/dist/problems.js +19 -0
- package/dist/problems.js.map +1 -0
- package/dist/programs/kill-tree.d.ts +16 -0
- package/dist/programs/kill-tree.d.ts.map +1 -0
- package/dist/programs/kill-tree.js +28 -0
- package/dist/programs/kill-tree.js.map +1 -0
- package/dist/programs/media-duration.d.ts +10 -0
- package/dist/programs/media-duration.d.ts.map +1 -0
- package/dist/programs/media-duration.js +10 -0
- package/dist/programs/media-duration.js.map +1 -0
- package/dist/programs/piped-program.d.ts +7 -0
- package/dist/programs/piped-program.d.ts.map +1 -0
- package/dist/programs/piped-program.js +45 -0
- package/dist/programs/piped-program.js.map +1 -0
- package/dist/programs/programs.d.ts +6 -0
- package/dist/programs/programs.d.ts.map +1 -0
- package/dist/programs/programs.js +6 -0
- package/dist/programs/programs.js.map +1 -0
- package/dist/programs/run-program.d.ts +22 -0
- package/dist/programs/run-program.d.ts.map +1 -0
- package/dist/programs/run-program.js +46 -0
- package/dist/programs/run-program.js.map +1 -0
- package/dist/record/address.d.ts +5 -0
- package/dist/record/address.d.ts.map +1 -0
- package/dist/record/address.js +28 -0
- package/dist/record/address.js.map +1 -0
- package/dist/record/cursor-script.d.ts +2 -0
- package/dist/record/cursor-script.d.ts.map +1 -0
- package/dist/record/cursor-script.js +21 -0
- package/dist/record/cursor-script.js.map +1 -0
- package/dist/record/encoder-args.d.ts +3 -0
- package/dist/record/encoder-args.d.ts.map +1 -0
- package/dist/record/encoder-args.js +13 -0
- package/dist/record/encoder-args.js.map +1 -0
- package/dist/record/frame-writer.d.ts +8 -0
- package/dist/record/frame-writer.d.ts.map +1 -0
- package/dist/record/frame-writer.js +30 -0
- package/dist/record/frame-writer.js.map +1 -0
- package/dist/record/own-port.d.ts +2 -0
- package/dist/record/own-port.d.ts.map +1 -0
- package/dist/record/own-port.js +23 -0
- package/dist/record/own-port.js.map +1 -0
- package/dist/record/pointer.d.ts +9 -0
- package/dist/record/pointer.d.ts.map +1 -0
- package/dist/record/pointer.js +23 -0
- package/dist/record/pointer.js.map +1 -0
- package/dist/record/recorder.d.ts +24 -0
- package/dist/record/recorder.d.ts.map +1 -0
- package/dist/record/recorder.js +69 -0
- package/dist/record/recorder.js.map +1 -0
- package/dist/record/register-record.d.ts +3 -0
- package/dist/record/register-record.d.ts.map +1 -0
- package/dist/record/register-record.js +49 -0
- package/dist/record/register-record.js.map +1 -0
- package/dist/record/run-step.d.ts +7 -0
- package/dist/record/run-step.d.ts.map +1 -0
- package/dist/record/run-step.js +49 -0
- package/dist/record/run-step.js.map +1 -0
- package/dist/record/screencast.d.ts +9 -0
- package/dist/record/screencast.d.ts.map +1 -0
- package/dist/record/screencast.js +38 -0
- package/dist/record/screencast.js.map +1 -0
- package/dist/record/steps.d.ts +71 -0
- package/dist/record/steps.d.ts.map +1 -0
- package/dist/record/steps.js +29 -0
- package/dist/record/steps.js.map +1 -0
- package/dist/register-with-kvcoder.d.ts +3 -0
- package/dist/register-with-kvcoder.d.ts.map +1 -0
- package/dist/register-with-kvcoder.js +15 -0
- package/dist/register-with-kvcoder.js.map +1 -0
- package/dist/settings.d.ts +29 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +27 -0
- package/dist/settings.js.map +1 -0
- package/dist/setup/browser-readiness.d.ts +14 -0
- package/dist/setup/browser-readiness.d.ts.map +1 -0
- package/dist/setup/browser-readiness.js +16 -0
- package/dist/setup/browser-readiness.js.map +1 -0
- package/dist/setup/ffmpeg-readiness.d.ts +19 -0
- package/dist/setup/ffmpeg-readiness.d.ts.map +1 -0
- package/dist/setup/ffmpeg-readiness.js +33 -0
- package/dist/setup/ffmpeg-readiness.js.map +1 -0
- package/dist/setup/narration-readiness.d.ts +21 -0
- package/dist/setup/narration-readiness.d.ts.map +1 -0
- package/dist/setup/narration-readiness.js +18 -0
- package/dist/setup/narration-readiness.js.map +1 -0
- package/dist/setup/register-setup.d.ts +3 -0
- package/dist/setup/register-setup.d.ts.map +1 -0
- package/dist/setup/register-setup.js +51 -0
- package/dist/setup/register-setup.js.map +1 -0
- package/dist/ui.d.ts +3 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +25 -0
- package/dist/ui.js.map +1 -0
- package/dist/video-start.d.ts +3 -0
- package/dist/video-start.d.ts.map +1 -0
- package/dist/video-start.js +28 -0
- package/dist/video-start.js.map +1 -0
- package/dist/web/components/player.css +2 -0
- package/dist/web/components/player.js +43 -0
- package/docs/guide.md +16 -0
- package/fonts/Amiri-Bold.ttf +0 -0
- package/fonts/IBMPlexSansArabic-Bold.ttf +0 -0
- package/fonts/Lato-Bold.ttf +0 -0
- package/fonts/OFL-Amiri.txt +94 -0
- package/fonts/OFL-IBMPlexSansArabic.txt +93 -0
- package/fonts/OFL-Lato.txt +93 -0
- package/fonts/OFL-Poppins.txt +93 -0
- package/fonts/Poppins-Bold.ttf +0 -0
- package/locales/ar.json +37 -0
- package/locales/en.json +37 -0
- package/package.json +67 -3
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from '@kvman/sdk';
|
|
2
|
+
// A recording's input (plan 16 §16.5): the page, up to 200 steps, the size, and the output.
|
|
3
|
+
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 stepSchema = z.discriminatedUnion('do', [
|
|
6
|
+
z.strictObject({ do: z.literal('goto'), url: address.describe('A localhost address to open.') }),
|
|
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, one character every 50 ms.') }),
|
|
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.') }),
|
|
12
|
+
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 }),
|
|
14
|
+
]);
|
|
15
|
+
const side = z.number().int().min(320).max(1920).refine((pixels) => pixels % 2 === 0, 'A side is an even number of pixels.');
|
|
16
|
+
export const recordInputSchema = z.strictObject({
|
|
17
|
+
url: address.describe('The page to record: http or https on localhost, 127.0.0.1, or [::1].'),
|
|
18
|
+
steps: z.array(stepSchema).max(200).describe('What happens on the page, in order.').exactOptional(),
|
|
19
|
+
file: z.string().min(1).max(1000).describe('The .mp4 file to write, in the workspace.'),
|
|
20
|
+
width: side.describe('The width in pixels, 1280 by default.').exactOptional(),
|
|
21
|
+
height: side.describe('The height in pixels, 720 by default.').exactOptional(),
|
|
22
|
+
replace: z.boolean().describe('Write over an existing file.').exactOptional(),
|
|
23
|
+
});
|
|
24
|
+
export const recordOutputSchema = z.object({
|
|
25
|
+
file: z.string(),
|
|
26
|
+
durationMs: z.number().int().nonnegative(),
|
|
27
|
+
steps: z.array(z.object({ do: z.string(), atMs: z.number().int().nonnegative() })),
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=steps.js.map
|
|
@@ -0,0 +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,4FAA4F;AAE5F,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,2GAA2G,CAAC,CAAC;AAChK,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAE5C,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE;IACnD,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAC,EAAE,CAAC;IAChG,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAClD,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,4CAA4C,CAAC,EAAE,CAAC;IAC3I,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,8CAA8C,CAAC,EAAE,CAAC;IACpI,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAClD,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,6CAA6C,CAAC,EAAE,CAAC;IACtJ,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,6BAA6B,CAAC,EAAE,CAAC;IAC1H,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACrD,CAAC,CAAC;AAKH,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,qCAAqC,CAAC,CAAC;AAE7H,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9C,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,sEAAsE,CAAC;IAC7F,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC,CAAC,aAAa,EAAE;IACnG,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,2CAA2C,CAAC;IACvF,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,uCAAuC,CAAC,CAAC,aAAa,EAAE;IAC7E,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,uCAAuC,CAAC,CAAC,aAAa,EAAE;IAC9E,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC,CAAC,aAAa,EAAE;CAC9E,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC1C,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;CACnF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-with-kvcoder.d.ts","sourceRoot":"","sources":["../src/register-with-kvcoder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAOtC,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAUlD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { videoConnector } from "./connector.js";
|
|
2
|
+
// kvvideo extends kvcoder (plan 16 §16.1): at each start it registers its `optIn` connector and `/video`, since kvcoder
|
|
3
|
+
// clears both at its own start.
|
|
4
|
+
export function registerWithKvcoder(ctx) {
|
|
5
|
+
ctx.registerHandler('kernel.started', {
|
|
6
|
+
description: "Registers kvvideo's connector and its slash command with kvcoder.",
|
|
7
|
+
handle: async () => {
|
|
8
|
+
await ctx.exec('kvcoder.connector.register', videoConnector);
|
|
9
|
+
await ctx.exec('kvcoder.slash.register', {
|
|
10
|
+
commands: [{ name: 'video', description: 'kvvideo.slash.video', command: 'kvvideo.video.start', message: 'kvvideo.slash.video.message' }],
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=register-with-kvcoder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-with-kvcoder.js","sourceRoot":"","sources":["../src/register-with-kvcoder.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,wHAAwH;AACxH,gCAAgC;AAEhC,MAAM,UAAU,mBAAmB,CAAC,GAAQ;IAC1C,GAAG,CAAC,eAAe,CAAC,gBAAgB,EAAE;QACpC,WAAW,EAAE,mEAAmE;QAChF,MAAM,EAAE,KAAK,IAAI,EAAE;YACjB,MAAM,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,cAAc,CAAC,CAAC;YAC7D,MAAM,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE;gBACvC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC;aAC1I,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z, type Ctx } from '@kvman/sdk';
|
|
2
|
+
export declare const narrationServices: readonly ["elevenlabs", "openai", "piper"];
|
|
3
|
+
/** A narration service. */
|
|
4
|
+
export type NarrationService = (typeof narrationServices)[number];
|
|
5
|
+
export declare const browserChoices: readonly ["auto", "chrome", "msedge"];
|
|
6
|
+
/** Which installed browser records. */
|
|
7
|
+
export type BrowserChoice = (typeof browserChoices)[number];
|
|
8
|
+
export declare const programNames: readonly ["ffmpeg", "ffprobe", "piper"];
|
|
9
|
+
/** A program kvvideo runs. */
|
|
10
|
+
export type ProgramName = (typeof programNames)[number];
|
|
11
|
+
export declare const settingSchemas: {
|
|
12
|
+
service: z.ZodNullable<z.ZodEnum<{
|
|
13
|
+
elevenlabs: "elevenlabs";
|
|
14
|
+
openai: "openai";
|
|
15
|
+
piper: "piper";
|
|
16
|
+
}>>;
|
|
17
|
+
voice: z.ZodNullable<z.ZodString>;
|
|
18
|
+
model: z.ZodNullable<z.ZodString>;
|
|
19
|
+
browser: z.ZodEnum<{
|
|
20
|
+
auto: "auto";
|
|
21
|
+
chrome: "chrome";
|
|
22
|
+
msedge: "msedge";
|
|
23
|
+
}>;
|
|
24
|
+
program: z.ZodString;
|
|
25
|
+
};
|
|
26
|
+
/** The setting that names a program. */
|
|
27
|
+
export declare function programSetting(program: ProgramName): `kvvideo.programs.${ProgramName}`;
|
|
28
|
+
export declare function registerSettings(ctx: Ctx): void;
|
|
29
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAKzC,eAAO,MAAM,iBAAiB,4CAA6C,CAAC;AAE5E,2BAA2B;AAC3B,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,cAAc,uCAAwC,CAAC;AAEpE,uCAAuC;AACvC,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,eAAO,MAAM,YAAY,yCAA0C,CAAC;AAEpE,8BAA8B;AAC9B,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;CAM1B,CAAC;AAEF,wCAAwC;AACxC,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,oBAAoB,WAAW,EAAE,CAEtF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAQ/C"}
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from '@kvman/sdk';
|
|
2
|
+
// kvvideo's settings (plan 16 §16.2, ADR 0107, 5 and 20): no narration service until the person chooses one, and the
|
|
3
|
+
// programs as a name on the PATH or a full path.
|
|
4
|
+
export const narrationServices = ['elevenlabs', 'openai', 'piper'];
|
|
5
|
+
export const browserChoices = ['auto', 'chrome', 'msedge'];
|
|
6
|
+
export const programNames = ['ffmpeg', 'ffprobe', 'piper'];
|
|
7
|
+
export const settingSchemas = {
|
|
8
|
+
service: z.enum(narrationServices).nullable(),
|
|
9
|
+
voice: z.string().min(1).max(500).nullable(),
|
|
10
|
+
model: z.string().min(1).max(100).nullable(),
|
|
11
|
+
browser: z.enum(browserChoices),
|
|
12
|
+
program: z.string().min(1).max(1000),
|
|
13
|
+
};
|
|
14
|
+
/** The setting that names a program. */
|
|
15
|
+
export function programSetting(program) {
|
|
16
|
+
return `kvvideo.programs.${program}`;
|
|
17
|
+
}
|
|
18
|
+
export function registerSettings(ctx) {
|
|
19
|
+
ctx.registerSetting('kvvideo.narration.service', { description: 'The text-to-speech service that narrates; null until the person chooses one.', schema: settingSchemas.service, default: null });
|
|
20
|
+
ctx.registerSetting('kvvideo.narration.voice', { description: "The narration voice: a voice id for ElevenLabs or OpenAI, or the path of a Piper voice's .onnx file; null is the service's default.", schema: settingSchemas.voice, default: null });
|
|
21
|
+
ctx.registerSetting('kvvideo.narration.model', { description: "The narration model of ElevenLabs or OpenAI; null is the service's default.", schema: settingSchemas.model, default: null });
|
|
22
|
+
ctx.registerSetting('kvvideo.browser', { description: 'The installed browser that records pages: auto tries Chrome, then Edge.', schema: settingSchemas.browser, default: 'auto', scopes: ['global'] });
|
|
23
|
+
for (const program of programNames) {
|
|
24
|
+
ctx.registerSetting(programSetting(program), { description: `The ${program} program: a name on the PATH, or a full path.`, schema: settingSchemas.program, default: program, scopes: ['global'] });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAY,MAAM,YAAY,CAAC;AAEzC,qHAAqH;AACrH,iDAAiD;AAEjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAK5E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAKpE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAU,CAAC;AAKpE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE;IAC7C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC5C,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;IAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;CACrC,CAAC;AAEF,wCAAwC;AACxC,MAAM,UAAU,cAAc,CAAC,OAAoB;IACjD,OAAO,oBAAoB,OAAO,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAQ;IACvC,GAAG,CAAC,eAAe,CAAC,2BAA2B,EAAE,EAAE,WAAW,EAAE,8EAA8E,EAAE,MAAM,EAAE,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACjM,GAAG,CAAC,eAAe,CAAC,yBAAyB,EAAE,EAAE,WAAW,EAAE,qIAAqI,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACpP,GAAG,CAAC,eAAe,CAAC,yBAAyB,EAAE,EAAE,WAAW,EAAE,6EAA6E,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5L,GAAG,CAAC,eAAe,CAAC,iBAAiB,EAAE,EAAE,WAAW,EAAE,yEAAyE,EAAE,MAAM,EAAE,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxM,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,GAAG,CAAC,eAAe,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,OAAO,+CAA+C,EAAE,MAAM,EAAE,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACrM,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Channel } from '../browser/channels.ts';
|
|
2
|
+
import type { BrowserChoice } from '../settings.ts';
|
|
3
|
+
/** A started browser. */
|
|
4
|
+
export type Started = {
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
/** Starts one channel headless, or rejects when it can't. */
|
|
8
|
+
export type Launch = (channel: Channel) => Promise<Started>;
|
|
9
|
+
export type BrowserReadiness = {
|
|
10
|
+
ready: boolean;
|
|
11
|
+
channel: Channel | null;
|
|
12
|
+
};
|
|
13
|
+
export declare function browserReadiness(choice: BrowserChoice, launch: Launch): Promise<BrowserReadiness>;
|
|
14
|
+
//# sourceMappingURL=browser-readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-readiness.d.ts","sourceRoot":"","sources":["../../src/setup/browser-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAKpD,yBAAyB;AACzB,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAEjD,6DAA6D;AAC7D,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAY3E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAKvG"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { channelsOf } from "../browser/channels.js";
|
|
2
|
+
async function starts(launch, channel) {
|
|
3
|
+
const browser = await launch(channel).then((started) => started, () => undefined);
|
|
4
|
+
if (browser === undefined)
|
|
5
|
+
return false;
|
|
6
|
+
await browser.close();
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
export async function browserReadiness(choice, launch) {
|
|
10
|
+
for (const channel of channelsOf(choice)) {
|
|
11
|
+
if (await starts(launch, channel))
|
|
12
|
+
return { ready: true, channel };
|
|
13
|
+
}
|
|
14
|
+
return { ready: false, channel: null };
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=browser-readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-readiness.js","sourceRoot":"","sources":["../../src/setup/browser-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAgB,MAAM,wBAAwB,CAAC;AAclE,KAAK,UAAU,MAAM,CAAC,MAAc,EAAE,OAAgB;IACpD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CACxC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EACpB,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;IACF,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACtB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAqB,EAAE,MAAc;IAC1E,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,IAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProgramName } from '../settings.ts';
|
|
2
|
+
/** What one program printed with its exit code, or `undefined` when it couldn't run. */
|
|
3
|
+
export type Answer = {
|
|
4
|
+
exitCode: number;
|
|
5
|
+
stdout: string;
|
|
6
|
+
} | undefined;
|
|
7
|
+
/** Runs a program with its arguments, giving it 5 s. */
|
|
8
|
+
export type CheckRun = (program: ProgramName, args: readonly string[]) => Promise<Answer>;
|
|
9
|
+
export type FfmpegReadiness = {
|
|
10
|
+
ready: boolean;
|
|
11
|
+
version: string | null;
|
|
12
|
+
missing: string[];
|
|
13
|
+
};
|
|
14
|
+
/** The names in an `-encoders` or `-filters` listing: each row's second word, after its flags. */
|
|
15
|
+
export declare function listedNames(listing: string): Set<string>;
|
|
16
|
+
/** The version in ffmpeg's first line, `ffmpeg version <version> …`. */
|
|
17
|
+
export declare function versionOf(text: string): string | null;
|
|
18
|
+
export declare function ffmpegReadiness(run: CheckRun): Promise<FfmpegReadiness>;
|
|
19
|
+
//# sourceMappingURL=ffmpeg-readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg-readiness.d.ts","sourceRoot":"","sources":["../../src/setup/ffmpeg-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKlD,wFAAwF;AACxF,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC;AAEtE,wDAAwD;AACxD,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE1F,MAAM,MAAM,eAAe,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAS5F,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAExD;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGrD;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,CAiB7E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const encoders = ['libx264', 'aac', 'libmp3lame'];
|
|
2
|
+
const filters = ['subtitles'];
|
|
3
|
+
function succeeded(answer) {
|
|
4
|
+
return answer !== undefined && answer.exitCode === 0;
|
|
5
|
+
}
|
|
6
|
+
/** The names in an `-encoders` or `-filters` listing: each row's second word, after its flags. */
|
|
7
|
+
export function listedNames(listing) {
|
|
8
|
+
return new Set(listing.split('\n').map((line) => line.trim().split(/\s+/)[1]).filter((name) => name !== undefined));
|
|
9
|
+
}
|
|
10
|
+
/** The version in ffmpeg's first line, `ffmpeg version <version> …`. */
|
|
11
|
+
export function versionOf(text) {
|
|
12
|
+
const words = (text.split('\n')[0] ?? '').trim().split(/\s+/);
|
|
13
|
+
return words[0] === 'ffmpeg' && words[1] === 'version' && words[2] !== undefined ? words[2] : null;
|
|
14
|
+
}
|
|
15
|
+
export async function ffmpegReadiness(run) {
|
|
16
|
+
const [version, probe, encoderList, filterList] = await Promise.all([
|
|
17
|
+
run('ffmpeg', ['-hide_banner', '-version']),
|
|
18
|
+
run('ffprobe', ['-hide_banner', '-version']),
|
|
19
|
+
run('ffmpeg', ['-hide_banner', '-encoders']),
|
|
20
|
+
run('ffmpeg', ['-hide_banner', '-filters']),
|
|
21
|
+
]);
|
|
22
|
+
const ffmpegRuns = succeeded(version);
|
|
23
|
+
const encoderNames = ffmpegRuns && succeeded(encoderList) ? listedNames(encoderList.stdout) : new Set();
|
|
24
|
+
const filterNames = ffmpegRuns && succeeded(filterList) ? listedNames(filterList.stdout) : new Set();
|
|
25
|
+
const missing = [
|
|
26
|
+
...(ffmpegRuns ? [] : ['ffmpeg']),
|
|
27
|
+
...(succeeded(probe) ? [] : ['ffprobe']),
|
|
28
|
+
...encoders.filter((name) => !encoderNames.has(name)),
|
|
29
|
+
...filters.filter((name) => !filterNames.has(name)),
|
|
30
|
+
];
|
|
31
|
+
return { ready: missing.length === 0, version: ffmpegRuns ? versionOf(version.stdout) : null, missing };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=ffmpeg-readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg-readiness.js","sourceRoot":"","sources":["../../src/setup/ffmpeg-readiness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { NarrationService } from '../settings.ts';
|
|
2
|
+
export type NarrationChoice = {
|
|
3
|
+
service: NarrationService | null;
|
|
4
|
+
voice: string | null;
|
|
5
|
+
};
|
|
6
|
+
export type NarrationReadiness = {
|
|
7
|
+
service: NarrationService | null;
|
|
8
|
+
ready: boolean;
|
|
9
|
+
missing: string | null;
|
|
10
|
+
};
|
|
11
|
+
/** The secret a paid service's key is kept in (ADR 0107, 21). */
|
|
12
|
+
export declare const keySecrets: {
|
|
13
|
+
readonly elevenlabs: "elevenlabs.apiKey";
|
|
14
|
+
readonly openai: "openai.apiKey";
|
|
15
|
+
};
|
|
16
|
+
export type NarrationProbe = {
|
|
17
|
+
hasSecret(name: string): Promise<boolean>;
|
|
18
|
+
piperRuns(): Promise<boolean>;
|
|
19
|
+
};
|
|
20
|
+
export declare function narrationReadiness(choice: NarrationChoice, probe: NarrationProbe): Promise<NarrationReadiness>;
|
|
21
|
+
//# sourceMappingURL=narration-readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narration-readiness.d.ts","sourceRoot":"","sources":["../../src/setup/narration-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKvD,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEzF,MAAM,MAAM,kBAAkB,GAAG;IAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE9G,iEAAiE;AACjE,eAAO,MAAM,UAAU;;;CAAwE,CAAC;AAEhG,MAAM,MAAM,cAAc,GAAG;IAAE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAAC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;AAY1G,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAGpH"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** The secret a paid service's key is kept in (ADR 0107, 21). */
|
|
2
|
+
export const keySecrets = { elevenlabs: 'elevenlabs.apiKey', openai: 'openai.apiKey' };
|
|
3
|
+
async function missingOf(choice, probe) {
|
|
4
|
+
if (choice.service === null)
|
|
5
|
+
return 'kvvideo.narration.service';
|
|
6
|
+
if (choice.service !== 'piper') {
|
|
7
|
+
const secret = keySecrets[choice.service];
|
|
8
|
+
return (await probe.hasSecret(secret)) ? null : secret;
|
|
9
|
+
}
|
|
10
|
+
if (!(await probe.piperRuns()))
|
|
11
|
+
return 'piper';
|
|
12
|
+
return choice.voice === null ? 'kvvideo.narration.voice' : null;
|
|
13
|
+
}
|
|
14
|
+
export async function narrationReadiness(choice, probe) {
|
|
15
|
+
const missing = await missingOf(choice, probe);
|
|
16
|
+
return { service: choice.service, ready: missing === null, missing };
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=narration-readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narration-readiness.js","sourceRoot":"","sources":["../../src/setup/narration-readiness.ts"],"names":[],"mappings":"AASA,iEAAiE;AACjE,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAE,eAAe,EAAW,CAAC;AAIhG,KAAK,UAAU,SAAS,CAAC,MAAuB,EAAE,KAAqB;IACrE,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,2BAA2B,CAAC;IAChE,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;IACzD,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IAC/C,OAAO,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC;AAClE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAuB,EAAE,KAAqB;IACrF,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,EAAE,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-setup.d.ts","sourceRoot":"","sources":["../../src/setup/register-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AA+BzC,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAqB5C"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from '@kvman/sdk';
|
|
2
|
+
import { launchChannel } from "../browser/launch.js";
|
|
3
|
+
import { programOf } from "../programs/programs.js";
|
|
4
|
+
import { runProgram } from "../programs/run-program.js";
|
|
5
|
+
import { isKvvideoProblem } from "../problems.js";
|
|
6
|
+
import { narrationServices } from "../settings.js";
|
|
7
|
+
import { browserReadiness } from "./browser-readiness.js";
|
|
8
|
+
import { ffmpegReadiness } from "./ffmpeg-readiness.js";
|
|
9
|
+
import { narrationReadiness } from "./narration-readiness.js";
|
|
10
|
+
// `video check` (plan 16 §16.4): what is ready, and what the person still has to install or set. It fails for none of
|
|
11
|
+
// them: a program that can't start, or doesn't answer in 5 s, is reported missing.
|
|
12
|
+
const checkTimeoutMs = 5_000;
|
|
13
|
+
const setupSchema = z.object({
|
|
14
|
+
ffmpeg: z.object({ ready: z.boolean(), version: z.string().nullable(), missing: z.array(z.string()) }),
|
|
15
|
+
browser: z.object({ ready: z.boolean(), channel: z.enum(['chrome', 'msedge']).nullable() }),
|
|
16
|
+
narration: z.object({ service: z.enum(narrationServices).nullable(), ready: z.boolean(), missing: z.string().nullable() }),
|
|
17
|
+
});
|
|
18
|
+
async function answerOf(ctx, name, args) {
|
|
19
|
+
try {
|
|
20
|
+
const run = await runProgram(await programOf(ctx, name), args, { signal: ctx.job.signal, timeoutMs: checkTimeoutMs });
|
|
21
|
+
return run.timedOut ? undefined : { exitCode: run.exitCode, stdout: run.stdout };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (isKvvideoProblem(error, 'PROGRAM_MISSING'))
|
|
25
|
+
return undefined;
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function registerSetup(ctx) {
|
|
30
|
+
ctx.registerQuery('kvvideo.setup.get', {
|
|
31
|
+
description: 'Tells what is ready to make videos: ffmpeg, the installed browser, and narration, and what is missing.',
|
|
32
|
+
input: z.object({}),
|
|
33
|
+
output: setupSchema,
|
|
34
|
+
public: true,
|
|
35
|
+
timeoutMs: 60_000,
|
|
36
|
+
handle: async () => {
|
|
37
|
+
const run = (name, args) => answerOf(ctx, name, args);
|
|
38
|
+
const choice = { service: await ctx.settings.get('kvvideo.narration.service'), voice: await ctx.settings.get('kvvideo.narration.voice') };
|
|
39
|
+
const [ffmpeg, browser, narration] = await Promise.all([
|
|
40
|
+
ffmpegReadiness(run),
|
|
41
|
+
browserReadiness(await ctx.settings.get('kvvideo.browser'), launchChannel),
|
|
42
|
+
narrationReadiness(choice, {
|
|
43
|
+
hasSecret: async (name) => (await ctx.secrets.get(name)) !== undefined,
|
|
44
|
+
piperRuns: async () => (await run('piper', ['--help']))?.exitCode === 0,
|
|
45
|
+
}),
|
|
46
|
+
]);
|
|
47
|
+
return { ffmpeg, browser, narration };
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=register-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-setup.js","sourceRoot":"","sources":["../../src/setup/register-setup.ts"],"names":[],"mappings":"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"}
|
package/dist/ui.d.ts
ADDED
package/dist/ui.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAoBzC,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAQzC"}
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from '@kvman/sdk';
|
|
2
|
+
// kvvideo's configuration on its extension page (plan 16 §16.2): three cards of setting rows, and nothing else. The
|
|
3
|
+
// keys' secrets are added in the page's own Secrets section (plan 06 §6.6).
|
|
4
|
+
const settings = (keys) => keys.map((key) => ({ type: 'setting', key }));
|
|
5
|
+
const configuration = {
|
|
6
|
+
type: 'stack',
|
|
7
|
+
direction: 'vertical',
|
|
8
|
+
gap: 'md',
|
|
9
|
+
children: [
|
|
10
|
+
{ type: 'card', title: 'kvvideo.config.narration', children: settings(['kvvideo.narration.service', 'kvvideo.narration.voice', 'kvvideo.narration.model']) },
|
|
11
|
+
{ type: 'card', title: 'kvvideo.config.recording', children: settings(['kvvideo.browser']) },
|
|
12
|
+
{ type: 'card', title: 'kvvideo.config.programs', children: settings(['kvvideo.programs.ffmpeg', 'kvvideo.programs.ffprobe', 'kvvideo.programs.piper']) },
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
const contributionsSchema = z.object({ pages: z.array(z.json()), nav: z.array(z.json()), panels: z.array(z.json()), status: z.array(z.json()), configuration: z.json() });
|
|
16
|
+
export function registerUi(ctx) {
|
|
17
|
+
ctx.registerQuery('kvvideo.ui.get', {
|
|
18
|
+
description: "Gives kvvideo's UI: its configuration on its extension page.",
|
|
19
|
+
input: z.object({}),
|
|
20
|
+
output: contributionsSchema,
|
|
21
|
+
public: true,
|
|
22
|
+
handle: () => ({ pages: [], nav: [], panels: [], status: [], configuration }),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAY,MAAM,YAAY,CAAC;AAEzC,oHAAoH;AACpH,4EAA4E;AAE5E,MAAM,QAAQ,GAAG,CAAC,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;AAE5F,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,OAAO;IACb,SAAS,EAAE,UAAU;IACrB,GAAG,EAAE,IAAI;IACT,QAAQ,EAAE;QACR,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,0BAA0B,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,2BAA2B,EAAE,yBAAyB,EAAE,yBAAyB,CAAC,CAAC,EAAE;QAC5J,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,0BAA0B,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,iBAAiB,CAAC,CAAC,EAAE;QAC5F,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,yBAAyB,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,yBAAyB,EAAE,0BAA0B,EAAE,wBAAwB,CAAC,CAAC,EAAE;KAC1J;CACF,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAE1K,MAAM,UAAU,UAAU,CAAC,GAAQ;IACjC,GAAG,CAAC,aAAa,CAAC,gBAAgB,EAAE;QAClC,WAAW,EAAE,8DAA8D;QAC3E,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACnB,MAAM,EAAE,mBAAmB;QAC3B,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC;KAC9E,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-start.d.ts","sourceRoot":"","sources":["../src/video-start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAUzC,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAmBjD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from '@kvman/sdk';
|
|
2
|
+
import { connectorName } from "./connector.js";
|
|
3
|
+
import { readGuide } from "./guide.js";
|
|
4
|
+
// `/video` (plan 16 §16.1): the person starts making videos in a chat. The connector is enabled first, then the guide;
|
|
5
|
+
// the note is added the first time only, known by the guide section being there already.
|
|
6
|
+
const owner = '@kvman/kvvideo';
|
|
7
|
+
export function registerVideoStart(ctx) {
|
|
8
|
+
ctx.registerCommand('kvvideo.video.start', {
|
|
9
|
+
description: "Starts making videos in one chat: gives the chat kvvideo's connector and guide.",
|
|
10
|
+
public: true,
|
|
11
|
+
retries: 0,
|
|
12
|
+
input: z.object({
|
|
13
|
+
sessionId: z.string().min(1).describe('The chat to make videos in.'),
|
|
14
|
+
argument: z.string().describe('The text after /video; the send box sends it as the message.'),
|
|
15
|
+
}),
|
|
16
|
+
output: z.object({}),
|
|
17
|
+
handle: async ({ sessionId }) => {
|
|
18
|
+
await ctx.exec('kvcoder.connector.enable', { sessionId, names: [connectorName] });
|
|
19
|
+
const sections = await ctx.exec('kvcoder.section.list', { sessionId });
|
|
20
|
+
const started = sections.some((section) => section.id === 'guide' && section.owner === owner && section.sessionId === sessionId);
|
|
21
|
+
await ctx.exec('kvcoder.section.set', { id: 'guide', title: 'Making videos', order: 30, sessionId, content: readGuide() });
|
|
22
|
+
if (!started)
|
|
23
|
+
await ctx.exec('kvcoder.note.add', { sessionId, key: 'kvvideo.video.started' });
|
|
24
|
+
return {};
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=video-start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-start.js","sourceRoot":"","sources":["../src/video-start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAY,MAAM,YAAY,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,uHAAuH;AACvH,yFAAyF;AAEzF,MAAM,KAAK,GAAG,gBAAgB,CAAC;AAE/B,MAAM,UAAU,kBAAkB,CAAC,GAAQ;IACzC,GAAG,CAAC,eAAe,CAAC,qBAAqB,EAAE;QACzC,WAAW,EAAE,iFAAiF;QAC9F,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;YACd,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,6BAA6B,CAAC;YACpE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;SAC9F,CAAC;QACF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACpB,MAAM,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YAC9B,MAAM,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAClF,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YACvE,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;YACjI,MAAM,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YAC3H,IAAI,CAAC,OAAO;gBAAE,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC9F,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.kvvideo-player{padding:var(--kv-space-sm);border:1px solid var(--kv-color-border);border-radius:var(--kv-radius);background:var(--kv-color-surface);max-inline-size:640px;margin:0}.kvvideo-player-head{gap:var(--kv-space-sm);color:var(--kv-color-muted);justify-content:space-between;padding-block-end:var(--kv-space-sm);font-size:.85em;display:flex}.kvvideo-player-file{text-overflow:ellipsis;white-space:nowrap;font-family:var(--kv-font-mono);overflow:hidden}.kvvideo-player-video{border-radius:calc(var(--kv-radius) / 2);background:#000;max-block-size:360px;inline-size:100%;display:block}.kvvideo-player-unavailable{color:var(--kv-color-text);margin:0}
|
|
2
|
+
/*$vite$:1*/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { computed as e, createElementBlock as t, createElementVNode as n, defineComponent as r, inject as i, openBlock as a, ref as o, toDisplayString as s, unref as c } from "vue";
|
|
2
|
+
//#region web/components/player.vue?vue&type=script&setup=true&lang.ts
|
|
3
|
+
var l = {
|
|
4
|
+
class: "kvvideo-player",
|
|
5
|
+
"data-test": "player"
|
|
6
|
+
}, u = { class: "kvvideo-player-head" }, d = {
|
|
7
|
+
class: "kvvideo-player-file",
|
|
8
|
+
dir: "auto",
|
|
9
|
+
"data-test": "file"
|
|
10
|
+
}, f = {
|
|
11
|
+
class: "kvvideo-player-length",
|
|
12
|
+
dir: "ltr",
|
|
13
|
+
"data-test": "length"
|
|
14
|
+
}, p = ["src"], m = {
|
|
15
|
+
key: 1,
|
|
16
|
+
class: "kvvideo-player-unavailable",
|
|
17
|
+
"data-test": "unavailable"
|
|
18
|
+
}, h = /* @__PURE__ */ r({
|
|
19
|
+
__name: "player",
|
|
20
|
+
props: {
|
|
21
|
+
payload: {},
|
|
22
|
+
output: {}
|
|
23
|
+
},
|
|
24
|
+
setup(r) {
|
|
25
|
+
let h = r, g = i("kvman");
|
|
26
|
+
if (g === void 0) throw Error("kvvideo components run inside kvwebui, which provides kvman.");
|
|
27
|
+
let { t: _, workspace: v } = g, y = o(!1), b = e(() => typeof h.output.file == "string" ? h.output.file : ""), x = e(() => typeof h.output.fileId == "string" ? h.output.fileId : void 0), S = e(() => x.value === void 0 ? void 0 : `/api/files/${encodeURIComponent(x.value)}?workspaceId=${encodeURIComponent(v.value.id)}`), C = e(() => {
|
|
28
|
+
let e = Math.round((typeof h.output.durationMs == "number" ? h.output.durationMs : 0) / 1e3);
|
|
29
|
+
return `${String(Math.floor(e / 60))}:${String(e % 60).padStart(2, "0")}`;
|
|
30
|
+
});
|
|
31
|
+
return (e, r) => (a(), t("figure", l, [n("figcaption", u, [n("span", d, s(b.value), 1), n("span", f, s(C.value), 1)]), S.value !== void 0 && !y.value ? (a(), t("video", {
|
|
32
|
+
key: 0,
|
|
33
|
+
class: "kvvideo-player-video",
|
|
34
|
+
"data-test": "video",
|
|
35
|
+
src: S.value,
|
|
36
|
+
controls: "",
|
|
37
|
+
preload: "metadata",
|
|
38
|
+
onError: r[0] ||= (e) => y.value = !0
|
|
39
|
+
}, null, 40, p)) : (a(), t("p", m, s(c(_)("kvvideo.player.unavailable", { file: b.value })), 1))]));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
//#endregion
|
|
43
|
+
export { h as default };
|
package/docs/guide.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
You can make videos with the `video` connector: tutorials that show how to use the person's app, demos, and promotional videos. A video is composed from pages of an app running on this computer, recorded in the installed browser, a narration of your script, and captions burned into the picture.
|
|
2
|
+
|
|
3
|
+
How to make one:
|
|
4
|
+
|
|
5
|
+
1. Run `video check` first. When something is missing, tell the person in plain words what to install or set, and stop until it is there: ffmpeg (with libass) from https://ffmpeg.org/download.html; Google Chrome or Microsoft Edge; a narration service chosen on kvvideo's extension page, with its key added there as a secret (`elevenlabs.apiKey` or `openai.apiKey`), or Piper (`pip install piper-tts`) with a voice file.
|
|
6
|
+
2. Make sure the app runs: start its dev server in the background with `shell`, and use its localhost address. Only pages on this computer can be recorded.
|
|
7
|
+
3. Write the script as a Markdown artifact: one section per scene, each with its narration and what is on screen. Keep scenes short, a few sentences each. Have the person agree to it before you narrate, since narration with ElevenLabs or OpenAI costs money.
|
|
8
|
+
4. For each scene, run `narrate` first: its answer has the narration's length and its captions. Then `record` the scene, with `wait` steps so the picture lasts about as long as the narration; each step's `atMs` says when it happened.
|
|
9
|
+
5. Run `compose` last, with each scene's clip, narration, and captions. Tell the person where the file is; the chat shows a player.
|
|
10
|
+
|
|
11
|
+
Rules:
|
|
12
|
+
|
|
13
|
+
- Narrate a scene again only when its text changed.
|
|
14
|
+
- Put the parts of a video in their own folder, such as `videos/<name>/`, and the video beside it.
|
|
15
|
+
- When the person asks for a font, find it on the web, save its .ttf or .otf file in the workspace, and name that file in `compose`'s `captions.font`. `video fonts` lists the bundled ones.
|
|
16
|
+
- A target in `record` is a Playwright selector: prefer `role=button[name="Save"]` or `text=Save` to CSS classes.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|