@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,42 @@
|
|
|
1
|
+
// A narration's captions (plan 16 §16.6, ADR 0107, 7): lines of whole words, each shown for the share of the
|
|
2
|
+
// narration's length that its characters have, one after another from 0 with no gap.
|
|
3
|
+
const maximumLine = 42;
|
|
4
|
+
const length = (text) => [...text].length;
|
|
5
|
+
function sentencesOf(line) {
|
|
6
|
+
return line.split(/(?<=[.!?؟…])\s+/u).filter((sentence) => sentence !== '');
|
|
7
|
+
}
|
|
8
|
+
function linesOf(sentence) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
let current = '';
|
|
11
|
+
for (const word of sentence.split(' ')) {
|
|
12
|
+
const joined = current === '' ? word : `${current} ${word}`;
|
|
13
|
+
if (current !== '' && length(joined) > maximumLine) {
|
|
14
|
+
lines.push(current);
|
|
15
|
+
current = word;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
current = joined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (current !== '')
|
|
22
|
+
lines.push(current);
|
|
23
|
+
return lines;
|
|
24
|
+
}
|
|
25
|
+
/** The caption lines of a text, in order. */
|
|
26
|
+
export function captionLines(text) {
|
|
27
|
+
const textLines = text.split(/\r?\n/u).map((line) => line.replace(/\s+/gu, ' ').trim());
|
|
28
|
+
return textLines.flatMap(sentencesOf).flatMap(linesOf);
|
|
29
|
+
}
|
|
30
|
+
/** The text's captions over a narration of `durationMs`. */
|
|
31
|
+
export function timedCaptions(text, durationMs) {
|
|
32
|
+
const lines = captionLines(text);
|
|
33
|
+
const total = lines.reduce((sum, line) => sum + length(line), 0);
|
|
34
|
+
let before = 0;
|
|
35
|
+
return lines.map((line, index) => {
|
|
36
|
+
const startMs = Math.floor((durationMs * before) / total);
|
|
37
|
+
before += length(line);
|
|
38
|
+
const endMs = index === lines.length - 1 ? durationMs : Math.floor((durationMs * before) / total);
|
|
39
|
+
return { text: line, startMs, endMs };
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=captions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captions.js","sourceRoot":"","sources":["../../src/narration/captions.ts"],"names":[],"mappings":"AAAA,6GAA6G;AAC7G,qFAAqF;AAIrF,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB,MAAM,MAAM,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC;AAE1D,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,OAAO,CAAC,QAAgB;IAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;QAC5D,IAAI,OAAO,KAAK,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;YACnD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,MAAM,CAAC;QACnB,CAAC;IACH,CAAC;IACD,IAAI,OAAO,KAAK,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACxF,OAAO,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACzD,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,UAAkB;IAC5D,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACjE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC;QAC1D,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,KAAK,GAAG,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC;QAClG,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Program } from '../programs/run-program.ts';
|
|
2
|
+
export type PiperPrograms = {
|
|
3
|
+
piper: Program;
|
|
4
|
+
ffmpeg: Program;
|
|
5
|
+
};
|
|
6
|
+
/** Narrates `text` with the voice into `<folder>/narration.mp3`, and answers that file. */
|
|
7
|
+
export declare function piperNarration(programs: PiperPrograms, voice: string, text: string, folder: string, signal: AbortSignal): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=piper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piper.d.ts","sourceRoot":"","sources":["../../src/narration/piper.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAKjF,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAMhE,2FAA2F;AAC3F,wBAAsB,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ/I"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { lastLines, runProgram } from "../programs/run-program.js";
|
|
3
|
+
import { kvvideoProblem } from "../problems.js";
|
|
4
|
+
function failed(reason) {
|
|
5
|
+
return kvvideoProblem('NARRATION_FAILED', `piper couldn't narrate: ${reason}`, { service: 'piper', reason });
|
|
6
|
+
}
|
|
7
|
+
/** Narrates `text` with the voice into `<folder>/narration.mp3`, and answers that file. */
|
|
8
|
+
export async function piperNarration(programs, voice, text, folder, signal) {
|
|
9
|
+
const wav = path.join(folder, 'narration.wav');
|
|
10
|
+
const mp3 = path.join(folder, 'narration.mp3');
|
|
11
|
+
const spoken = await runProgram(programs.piper, ['-m', voice, '-f', wav, '--', text], { signal });
|
|
12
|
+
if (spoken.exitCode !== 0)
|
|
13
|
+
throw failed(lastLines(spoken.output));
|
|
14
|
+
const encoded = await runProgram(programs.ffmpeg, ['-hide_banner', '-loglevel', 'error', '-i', wav, '-codec:a', 'libmp3lame', '-b:a', '128k', '-y', mp3], { signal });
|
|
15
|
+
if (encoded.exitCode !== 0)
|
|
16
|
+
throw failed(lastLines(encoded.output));
|
|
17
|
+
return mp3;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=piper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piper.js","sourceRoot":"","sources":["../../src/narration/piper.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAgB,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMhD,SAAS,MAAM,CAAC,MAAc;IAC5B,OAAO,cAAc,CAAC,kBAAkB,EAAE,2BAA2B,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAC/G,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAuB,EAAE,KAAa,EAAE,IAAY,EAAE,MAAc,EAAE,MAAmB;IAC5H,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAClG,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACtK,IAAI,OAAO,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACpE,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z, type Ctx } from '@kvman/sdk';
|
|
2
|
+
export declare const narrateInputSchema: z.ZodObject<{
|
|
3
|
+
text: z.ZodString;
|
|
4
|
+
file: z.ZodString;
|
|
5
|
+
replace: z.ZodExactOptional<z.ZodBoolean>;
|
|
6
|
+
}, z.core.$strict>;
|
|
7
|
+
export declare const narrateOutputSchema: z.ZodObject<{
|
|
8
|
+
file: z.ZodString;
|
|
9
|
+
durationMs: z.ZodNumber;
|
|
10
|
+
captions: z.ZodArray<z.ZodObject<{
|
|
11
|
+
text: z.ZodString;
|
|
12
|
+
startMs: z.ZodNumber;
|
|
13
|
+
endMs: z.ZodNumber;
|
|
14
|
+
}, z.core.$strip>>;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
export declare function registerNarration(ctx: Ctx): void;
|
|
17
|
+
//# sourceMappingURL=register-narration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-narration.d.ts","sourceRoot":"","sources":["../../src/narration/register-narration.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AAgBzC,eAAO,MAAM,kBAAkB;;;;kBAI7B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;iBAI9B,CAAC;AAuBH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAwBhD"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { z } from '@kvman/sdk';
|
|
4
|
+
import { answerPath } from "../files/answer-path.js";
|
|
5
|
+
import { placeOutput, workFolder } from "../files/place-output.js";
|
|
6
|
+
import { outputPath } from "../files/workspace-paths.js";
|
|
7
|
+
import { mediaDuration } from "../programs/media-duration.js";
|
|
8
|
+
import { programOf } from "../programs/programs.js";
|
|
9
|
+
import { kvvideoProblem } from "../problems.js";
|
|
10
|
+
import { keySecrets } from "../setup/narration-readiness.js";
|
|
11
|
+
import { timedCaptions } from "./captions.js";
|
|
12
|
+
import { piperNarration } from "./piper.js";
|
|
13
|
+
import { speak, speechRequest } from "./speech-services.js";
|
|
14
|
+
// `video narrate` (plan 16 §16.6): the chosen service speaks the text into an MP3 in the workspace, and the answer has
|
|
15
|
+
// its length and its captions. The output is checked before anything runs or is paid for.
|
|
16
|
+
export const narrateInputSchema = z.strictObject({
|
|
17
|
+
text: z.string().min(1).max(4000).describe('What the narrator says: one scene, a few sentences.'),
|
|
18
|
+
file: z.string().min(1).max(1000).describe('The .mp3 file to write, in the workspace.'),
|
|
19
|
+
replace: z.boolean().describe('Write over an existing file.').exactOptional(),
|
|
20
|
+
});
|
|
21
|
+
export const narrateOutputSchema = z.object({
|
|
22
|
+
file: z.string(),
|
|
23
|
+
durationMs: z.number().int().nonnegative(),
|
|
24
|
+
captions: z.array(z.object({ text: z.string(), startMs: z.number().int(), endMs: z.number().int() })),
|
|
25
|
+
});
|
|
26
|
+
async function chosenService(ctx) {
|
|
27
|
+
const service = await ctx.settings.get('kvvideo.narration.service');
|
|
28
|
+
if (service === null)
|
|
29
|
+
throw kvvideoProblem('NARRATION_NOT_SET', 'No narration service is chosen; set kvvideo.narration.service.', { setting: 'kvvideo.narration.service' });
|
|
30
|
+
return service;
|
|
31
|
+
}
|
|
32
|
+
async function narrateInto(ctx, service, text, folder) {
|
|
33
|
+
const choice = { voice: await ctx.settings.get('kvvideo.narration.voice'), model: await ctx.settings.get('kvvideo.narration.model') };
|
|
34
|
+
if (service === 'piper') {
|
|
35
|
+
if (choice.voice === null)
|
|
36
|
+
throw kvvideoProblem('NARRATION_NOT_SET', "Piper has no voice; set kvvideo.narration.voice to a voice's .onnx file.", { setting: 'kvvideo.narration.voice' });
|
|
37
|
+
return piperNarration({ piper: await programOf(ctx, 'piper'), ffmpeg: await programOf(ctx, 'ffmpeg') }, choice.voice, text, folder, ctx.job.signal);
|
|
38
|
+
}
|
|
39
|
+
const secret = keySecrets[service];
|
|
40
|
+
const key = await ctx.secrets.get(secret);
|
|
41
|
+
if (key === undefined)
|
|
42
|
+
throw kvvideoProblem('NARRATION_KEY_MISSING', `The ${service} key is missing; add the secret ${secret} on kvvideo's extension page.`, { service, secret });
|
|
43
|
+
const audio = await speak(service, speechRequest(service, text, choice, key), key, fetch, ctx.job.signal);
|
|
44
|
+
const file = path.join(folder, 'narration.mp3');
|
|
45
|
+
writeFileSync(file, audio);
|
|
46
|
+
return file;
|
|
47
|
+
}
|
|
48
|
+
export function registerNarration(ctx) {
|
|
49
|
+
ctx.registerCommand('kvvideo.narration.make', {
|
|
50
|
+
description: 'Narrates text with the chosen text-to-speech service into an MP3 file in the workspace, with its length and captions.',
|
|
51
|
+
input: narrateInputSchema,
|
|
52
|
+
output: narrateOutputSchema,
|
|
53
|
+
public: true,
|
|
54
|
+
retries: 0,
|
|
55
|
+
timeoutMs: 120_000,
|
|
56
|
+
handle: async (input) => {
|
|
57
|
+
const root = ctx.job.workspace.path;
|
|
58
|
+
const output = outputPath(root, input.file, '.mp3', input.replace === true);
|
|
59
|
+
const service = await chosenService(ctx);
|
|
60
|
+
const work = workFolder();
|
|
61
|
+
try {
|
|
62
|
+
const made = await narrateInto(ctx, service, input.text, work.folder);
|
|
63
|
+
const duration = await mediaDuration(await programOf(ctx, 'ffprobe'), made, ctx.job.signal);
|
|
64
|
+
if (!duration.ok)
|
|
65
|
+
throw kvvideoProblem('NARRATION_FAILED', `ffprobe couldn't read the narration: ${duration.reason}`, { service, reason: duration.reason });
|
|
66
|
+
placeOutput(made, output);
|
|
67
|
+
return { file: answerPath(root, output), durationMs: duration.ms, captions: timedCaptions(input.text, duration.ms) };
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
work.remove();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=register-narration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-narration.js","sourceRoot":"","sources":["../../src/narration/register-narration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type WebService = 'elevenlabs' | 'openai';
|
|
2
|
+
export type SpeechRequest = {
|
|
3
|
+
url: string;
|
|
4
|
+
headers: Record<string, string>;
|
|
5
|
+
body: string;
|
|
6
|
+
};
|
|
7
|
+
export type Fetch = (url: string, init: {
|
|
8
|
+
method: 'POST';
|
|
9
|
+
headers: Record<string, string>;
|
|
10
|
+
body: string;
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
}) => Promise<Response>;
|
|
13
|
+
export declare function speechRequest(service: WebService, text: string, choice: {
|
|
14
|
+
voice: string | null;
|
|
15
|
+
model: string | null;
|
|
16
|
+
}, key: string): SpeechRequest;
|
|
17
|
+
/** The audio a service answers, or `kvvideo/NARRATION_FAILED`. */
|
|
18
|
+
export declare function speak(service: WebService, request: SpeechRequest, key: string, fetch: Fetch, signal: AbortSignal): Promise<Buffer>;
|
|
19
|
+
//# sourceMappingURL=speech-services.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech-services.d.ts","sourceRoot":"","sources":["../../src/narration/speech-services.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEjD,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,MAAM,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AASrJ,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAenJ;AAQD,kEAAkE;AAClE,wBAAsB,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBxI"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ProblemError } from '@kvman/sdk';
|
|
2
|
+
import { kvvideoProblem } from "../problems.js";
|
|
3
|
+
const defaults = {
|
|
4
|
+
elevenlabs: { voice: 'JBFqnCBsd6RMkjVDRZzb', model: 'eleven_multilingual_v2' },
|
|
5
|
+
openai: { voice: 'alloy', model: 'gpt-4o-mini-tts' },
|
|
6
|
+
};
|
|
7
|
+
const requestTimeoutMs = 60_000;
|
|
8
|
+
export function speechRequest(service, text, choice, key) {
|
|
9
|
+
const voice = choice.voice ?? defaults[service].voice;
|
|
10
|
+
const model = choice.model ?? defaults[service].model;
|
|
11
|
+
if (service === 'elevenlabs') {
|
|
12
|
+
return {
|
|
13
|
+
url: `https://api.elevenlabs.io/v1/text-to-speech/${encodeURIComponent(voice)}?output_format=mp3_44100_128`,
|
|
14
|
+
headers: { 'xi-api-key': key, 'Content-Type': 'application/json' },
|
|
15
|
+
body: JSON.stringify({ text, model_id: model }),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
url: 'https://api.openai.com/v1/audio/speech',
|
|
20
|
+
headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
|
|
21
|
+
body: JSON.stringify({ model, voice, input: text, response_format: 'mp3' }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function withoutKey(text, key) {
|
|
25
|
+
return key === '' ? text : text.split(key).join('…');
|
|
26
|
+
}
|
|
27
|
+
const reasonOf = (error) => (error instanceof Error ? error.message : String(error));
|
|
28
|
+
/** The audio a service answers, or `kvvideo/NARRATION_FAILED`. */
|
|
29
|
+
export async function speak(service, request, key, fetch, signal) {
|
|
30
|
+
const timeout = new AbortController();
|
|
31
|
+
const timer = setTimeout(() => timeout.abort(new Error(`no answer in ${String(requestTimeoutMs / 1000)} s`)), requestTimeoutMs);
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(request.url, { method: 'POST', headers: request.headers, body: request.body, signal: AbortSignal.any([signal, timeout.signal]) });
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
const reason = withoutKey((await response.text()).slice(0, 300), key);
|
|
36
|
+
throw kvvideoProblem('NARRATION_FAILED', `${service} answered ${String(response.status)}: ${reason}`, { service, status: response.status, reason });
|
|
37
|
+
}
|
|
38
|
+
return Buffer.from(await response.arrayBuffer());
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (signal.aborted || error instanceof ProblemError)
|
|
42
|
+
throw error;
|
|
43
|
+
const reason = withoutKey(timeout.signal.aborted ? reasonOf(timeout.signal.reason) : reasonOf(error), key);
|
|
44
|
+
throw kvvideoProblem('NARRATION_FAILED', `${service} couldn't be reached: ${reason}`, { service, reason });
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=speech-services.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech-services.js","sourceRoot":"","sources":["../../src/narration/speech-services.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAWhD,MAAM,QAAQ,GAAG;IACf,UAAU,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,wBAAwB,EAAE;IAC9E,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE;CAC5C,CAAC;AAEX,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC,MAAM,UAAU,aAAa,CAAC,OAAmB,EAAE,IAAY,EAAE,MAAsD,EAAE,GAAW;IAClI,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;IACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;IACtD,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC7B,OAAO;YACL,GAAG,EAAE,+CAA+C,kBAAkB,CAAC,KAAK,CAAC,8BAA8B;YAC3G,OAAO,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAClE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;SAChD,CAAC;IACJ,CAAC;IACD,OAAO;QACL,GAAG,EAAE,wCAAwC;QAC7C,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;KAC5E,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,GAAW;IAC3C,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAU,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAEtG,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAmB,EAAE,OAAsB,EAAE,GAAW,EAAE,KAAY,EAAE,MAAmB;IACrH,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,gBAAgB,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAChI,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/J,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;YACtE,MAAM,cAAc,CAAC,kBAAkB,EAAE,GAAG,OAAO,aAAa,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtJ,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,OAAO,IAAI,KAAK,YAAY,YAAY;YAAE,MAAM,KAAK,CAAC;QACjE,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QAC3G,MAAM,cAAc,CAAC,kBAAkB,EAAE,GAAG,OAAO,yBAAyB,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7G,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ProblemError, type Json } from '@kvman/sdk';
|
|
2
|
+
export type KvvideoCode = 'PROGRAM_MISSING' | 'BROWSER_MISSING' | 'STEP_FAILED' | 'NARRATION_NOT_SET' | 'NARRATION_KEY_MISSING' | 'NARRATION_FAILED' | 'RENDER_FAILED';
|
|
3
|
+
export declare const kvvideoCodes: readonly KvvideoCode[];
|
|
4
|
+
/** A `kvvideo/<code>` Problem to throw. */
|
|
5
|
+
export declare function kvvideoProblem(code: KvvideoCode, message: string, params?: Record<string, Json>): ProblemError;
|
|
6
|
+
/** A `VALIDATION_FAILED` Problem to throw. */
|
|
7
|
+
export declare function invalid(message: string, params?: Record<string, Json>): ProblemError;
|
|
8
|
+
/** A `NOT_FOUND` Problem to throw. */
|
|
9
|
+
export declare function notFound(message: string, params?: Record<string, Json>): ProblemError;
|
|
10
|
+
/** Whether an error is the Problem of a kvvideo code. */
|
|
11
|
+
export declare function isKvvideoProblem(error: unknown, code: KvvideoCode): boolean;
|
|
12
|
+
//# sourceMappingURL=problems.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"problems.d.ts","sourceRoot":"","sources":["../src/problems.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAIrD,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,aAAa,GAAG,mBAAmB,GAAG,uBAAuB,GAAG,kBAAkB,GAAG,eAAe,CAAC;AAEvK,eAAO,MAAM,YAAY,EAAE,SAAS,WAAW,EAA6I,CAAC;AAE7L,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM,GAAG,YAAY,CAElH;AAED,8CAA8C;AAC9C,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM,GAAG,YAAY,CAExF;AAED,sCAAsC;AACtC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM,GAAG,YAAY,CAEzF;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAE3E"}
|
package/dist/problems.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ProblemError } from '@kvman/sdk';
|
|
2
|
+
export const kvvideoCodes = ['PROGRAM_MISSING', 'BROWSER_MISSING', 'STEP_FAILED', 'NARRATION_NOT_SET', 'NARRATION_KEY_MISSING', 'NARRATION_FAILED', 'RENDER_FAILED'];
|
|
3
|
+
/** A `kvvideo/<code>` Problem to throw. */
|
|
4
|
+
export function kvvideoProblem(code, message, params = {}) {
|
|
5
|
+
return new ProblemError({ code: `kvvideo/${code}`, message, params });
|
|
6
|
+
}
|
|
7
|
+
/** A `VALIDATION_FAILED` Problem to throw. */
|
|
8
|
+
export function invalid(message, params = {}) {
|
|
9
|
+
return new ProblemError({ code: 'VALIDATION_FAILED', message, params });
|
|
10
|
+
}
|
|
11
|
+
/** A `NOT_FOUND` Problem to throw. */
|
|
12
|
+
export function notFound(message, params = {}) {
|
|
13
|
+
return new ProblemError({ code: 'NOT_FOUND', message, params });
|
|
14
|
+
}
|
|
15
|
+
/** Whether an error is the Problem of a kvvideo code. */
|
|
16
|
+
export function isKvvideoProblem(error, code) {
|
|
17
|
+
return error instanceof ProblemError && error.problem.code === `kvvideo/${code}`;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=problems.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"problems.js","sourceRoot":"","sources":["../src/problems.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAa,MAAM,YAAY,CAAC;AAMrD,MAAM,CAAC,MAAM,YAAY,GAA2B,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,eAAe,CAAC,CAAC;AAE7L,2CAA2C;AAC3C,MAAM,UAAU,cAAc,CAAC,IAAiB,EAAE,OAAe,EAAE,SAA+B,EAAE;IAClG,OAAO,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,OAAO,CAAC,OAAe,EAAE,SAA+B,EAAE;IACxE,OAAO,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,SAA+B,EAAE;IACzE,OAAO,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,IAAiB;IAChE,OAAO,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,EAAE,CAAC;AACnF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** How a tree is killed on one OS. */
|
|
2
|
+
export type TreeKill = {
|
|
3
|
+
kind: 'group';
|
|
4
|
+
pid: number;
|
|
5
|
+
signal: 'SIGKILL';
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'taskkill';
|
|
8
|
+
command: 'taskkill';
|
|
9
|
+
args: string[];
|
|
10
|
+
};
|
|
11
|
+
export declare function treeKillOf(platform: NodeJS.Platform, pid: number): TreeKill;
|
|
12
|
+
/** Whether a program starts as its own group's leader, so its tree can be killed. */
|
|
13
|
+
export declare function startsOwnGroup(platform: NodeJS.Platform): boolean;
|
|
14
|
+
/** Kills a running program's tree; a tree that is already gone is left alone. */
|
|
15
|
+
export declare function killTree(pid: number | undefined): void;
|
|
16
|
+
//# sourceMappingURL=kill-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-tree.d.ts","sourceRoot":"","sources":["../../src/programs/kill-tree.ts"],"names":[],"mappings":"AAKA,sCAAsC;AACtC,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAErI,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAG3E;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,OAAO,CAEjE;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAYtD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
export function treeKillOf(platform, pid) {
|
|
3
|
+
if (platform === 'win32')
|
|
4
|
+
return { kind: 'taskkill', command: 'taskkill', args: ['/PID', String(pid), '/T', '/F'] };
|
|
5
|
+
return { kind: 'group', pid: -pid, signal: 'SIGKILL' };
|
|
6
|
+
}
|
|
7
|
+
/** Whether a program starts as its own group's leader, so its tree can be killed. */
|
|
8
|
+
export function startsOwnGroup(platform) {
|
|
9
|
+
return platform !== 'win32';
|
|
10
|
+
}
|
|
11
|
+
/** Kills a running program's tree; a tree that is already gone is left alone. */
|
|
12
|
+
export function killTree(pid) {
|
|
13
|
+
if (pid === undefined)
|
|
14
|
+
return;
|
|
15
|
+
const kill = treeKillOf(process.platform, pid);
|
|
16
|
+
if (kill.kind === 'taskkill') {
|
|
17
|
+
spawn(kill.command, kill.args, { stdio: 'ignore', windowsHide: true });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
process.kill(kill.pid, kill.signal);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'ESRCH'))
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=kill-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-tree.js","sourceRoot":"","sources":["../../src/programs/kill-tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAQ3C,MAAM,UAAU,UAAU,CAAC,QAAyB,EAAE,GAAW;IAC/D,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;IACpH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACzD,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,QAAyB;IACtD,OAAO,QAAQ,KAAK,OAAO,CAAC;AAC9B,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,QAAQ,CAAC,GAAuB;IAC9C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO;IAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;YAAE,MAAM,KAAK,CAAC;IAC1F,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Program } from './run-program.ts';
|
|
2
|
+
export type Duration = {
|
|
3
|
+
ok: true;
|
|
4
|
+
ms: number;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
reason: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function mediaDuration(ffprobe: Program, file: string, signal: AbortSignal): Promise<Duration>;
|
|
10
|
+
//# sourceMappingURL=media-duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-duration.d.ts","sourceRoot":"","sources":["../../src/programs/media-duration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIvE,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,wBAAsB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAM1G"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { lastLines, runProgram } from "./run-program.js";
|
|
2
|
+
export async function mediaDuration(ffprobe, file, signal) {
|
|
3
|
+
const run = await runProgram(ffprobe, ['-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', file], { signal });
|
|
4
|
+
const answer = run.stdout.trim();
|
|
5
|
+
const seconds = Number(answer);
|
|
6
|
+
if (run.exitCode !== 0 || answer === '' || !Number.isFinite(seconds))
|
|
7
|
+
return { ok: false, reason: lastLines(run.output) || `ffprobe answered ${answer}` };
|
|
8
|
+
return { ok: true, ms: Math.round(seconds * 1000) };
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=media-duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-duration.js","sourceRoot":"","sources":["../../src/programs/media-duration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAC;AAMvE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB,EAAE,IAAY,EAAE,MAAmB;IACrF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/H,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,IAAI,MAAM,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,oBAAoB,MAAM,EAAE,EAAE,CAAC;IAC1J,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Program, ProgramRun } from './run-program.ts';
|
|
2
|
+
export type PipedProgram = {
|
|
3
|
+
write(chunk: Buffer): Promise<void>;
|
|
4
|
+
end(): Promise<ProgramRun>;
|
|
5
|
+
};
|
|
6
|
+
export declare function startPiped(program: Program, args: readonly string[], signal: AbortSignal): Promise<PipedProgram>;
|
|
7
|
+
//# sourceMappingURL=piped-program.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piped-program.d.ts","sourceRoot":"","sources":["../../src/programs/piped-program.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM5D,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,CAAC;AAI/F,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAqChH"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { kvvideoProblem } from "../problems.js";
|
|
3
|
+
import { programSetting } from "../settings.js";
|
|
4
|
+
import { killTree, startsOwnGroup } from "./kill-tree.js";
|
|
5
|
+
const notStarted = new Set(['ENOENT', 'EACCES', 'ENOTDIR']);
|
|
6
|
+
export function startPiped(program, args, signal) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const child = spawn(program.command, args, { stdio: ['pipe', 'pipe', 'pipe'], detached: startsOwnGroup(process.platform), windowsHide: true });
|
|
9
|
+
let stdout = '';
|
|
10
|
+
let output = '';
|
|
11
|
+
let pipeError;
|
|
12
|
+
child.stdout.setEncoding('utf8').on('data', (text) => {
|
|
13
|
+
stdout += text;
|
|
14
|
+
output += text;
|
|
15
|
+
});
|
|
16
|
+
child.stderr.setEncoding('utf8').on('data', (text) => (output += text));
|
|
17
|
+
child.stdin.on('error', (error) => (pipeError = error));
|
|
18
|
+
const abort = () => killTree(child.pid);
|
|
19
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
20
|
+
const closed = new Promise((settle) => child.once('close', (code) => settle(code ?? 1)));
|
|
21
|
+
child.once('error', (error) => {
|
|
22
|
+
signal.removeEventListener('abort', abort);
|
|
23
|
+
const missing = 'code' in error && typeof error.code === 'string' && notStarted.has(error.code);
|
|
24
|
+
reject(missing ? kvvideoProblem('PROGRAM_MISSING', `${program.name} can't be started (${program.command}); install it or set ${programSetting(program.name)}.`, { program: program.name, setting: programSetting(program.name) }) : error);
|
|
25
|
+
});
|
|
26
|
+
child.once('spawn', () => {
|
|
27
|
+
resolve({
|
|
28
|
+
write: (chunk) => new Promise((done) => {
|
|
29
|
+
if (pipeError !== undefined || child.stdin.destroyed)
|
|
30
|
+
return done();
|
|
31
|
+
child.stdin.write(chunk, () => done());
|
|
32
|
+
}),
|
|
33
|
+
end: async () => {
|
|
34
|
+
child.stdin.end();
|
|
35
|
+
const exitCode = await closed;
|
|
36
|
+
signal.removeEventListener('abort', abort);
|
|
37
|
+
if (exitCode === 0 && pipeError !== undefined)
|
|
38
|
+
throw pipeError;
|
|
39
|
+
return { exitCode, stdout, output, timedOut: false };
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=piped-program.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piped-program.js","sourceRoot":"","sources":["../../src/programs/piped-program.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Ctx } from '@kvman/sdk';
|
|
2
|
+
import { type ProgramName } from '../settings.ts';
|
|
3
|
+
import type { Program } from './run-program.ts';
|
|
4
|
+
/** A program as its setting names it (plan 16 §16.2, ADR 0107, 20). */
|
|
5
|
+
export declare function programOf(ctx: Ctx, name: ProgramName): Promise<Program>;
|
|
6
|
+
//# sourceMappingURL=programs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../../src/programs/programs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,uEAAuE;AACvE,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7E"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { programSetting } from "../settings.js";
|
|
2
|
+
/** A program as its setting names it (plan 16 §16.2, ADR 0107, 20). */
|
|
3
|
+
export async function programOf(ctx, name) {
|
|
4
|
+
return { name, command: await ctx.settings.get(programSetting(name)) };
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=programs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"programs.js","sourceRoot":"","sources":["../../src/programs/programs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAoB,MAAM,gBAAgB,CAAC;AAGlE,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAQ,EAAE,IAAiB;IACzD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ProgramName } from '../settings.ts';
|
|
2
|
+
/** A finished run. */
|
|
3
|
+
export type ProgramRun = {
|
|
4
|
+
exitCode: number;
|
|
5
|
+
stdout: string;
|
|
6
|
+
output: string;
|
|
7
|
+
timedOut: boolean;
|
|
8
|
+
};
|
|
9
|
+
/** How one program runs: the program kvvideo calls it, and where its executable is. */
|
|
10
|
+
export type Program = {
|
|
11
|
+
name: ProgramName;
|
|
12
|
+
command: string;
|
|
13
|
+
};
|
|
14
|
+
export type RunOptions = {
|
|
15
|
+
signal: AbortSignal;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function runProgram(program: Program, args: readonly string[], options: RunOptions): Promise<ProgramRun>;
|
|
20
|
+
/** The last lines of a run's output, for a Problem's reason. */
|
|
21
|
+
export declare function lastLines(output: string, count?: number): string;
|
|
22
|
+
//# sourceMappingURL=run-program.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-program.d.ts","sourceRoot":"","sources":["../../src/programs/run-program.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAOlE,sBAAsB;AACtB,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjG,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAQnF,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B9G;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { kvvideoProblem } from "../problems.js";
|
|
3
|
+
import { programSetting } from "../settings.js";
|
|
4
|
+
import { killTree, startsOwnGroup } from "./kill-tree.js";
|
|
5
|
+
const notStarted = new Set(['ENOENT', 'EACCES', 'ENOTDIR']);
|
|
6
|
+
function missing(program) {
|
|
7
|
+
return kvvideoProblem('PROGRAM_MISSING', `${program.name} can't be started (${program.command}); install it or set ${programSetting(program.name)}.`, { program: program.name, setting: programSetting(program.name) });
|
|
8
|
+
}
|
|
9
|
+
export function runProgram(program, args, options) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const child = spawn(program.command, args, { cwd: options.cwd, stdio: ['ignore', 'pipe', 'pipe'], detached: startsOwnGroup(process.platform), windowsHide: true });
|
|
12
|
+
let stdout = '';
|
|
13
|
+
let output = '';
|
|
14
|
+
let timedOut = false;
|
|
15
|
+
child.stdout.setEncoding('utf8').on('data', (text) => {
|
|
16
|
+
stdout += text;
|
|
17
|
+
output += text;
|
|
18
|
+
});
|
|
19
|
+
child.stderr.setEncoding('utf8').on('data', (text) => (output += text));
|
|
20
|
+
const abort = () => killTree(child.pid);
|
|
21
|
+
const timer = options.timeoutMs === undefined ? undefined : setTimeout(() => {
|
|
22
|
+
timedOut = true;
|
|
23
|
+
abort();
|
|
24
|
+
}, options.timeoutMs);
|
|
25
|
+
const settle = () => {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
options.signal.removeEventListener('abort', abort);
|
|
28
|
+
};
|
|
29
|
+
options.signal.addEventListener('abort', abort, { once: true });
|
|
30
|
+
if (options.signal.aborted)
|
|
31
|
+
abort();
|
|
32
|
+
child.once('error', (error) => {
|
|
33
|
+
settle();
|
|
34
|
+
reject('code' in error && typeof error.code === 'string' && notStarted.has(error.code) ? missing(program) : error);
|
|
35
|
+
});
|
|
36
|
+
child.once('close', (code) => {
|
|
37
|
+
settle();
|
|
38
|
+
resolve({ exitCode: code ?? 1, stdout, output, timedOut });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** The last lines of a run's output, for a Problem's reason. */
|
|
43
|
+
export function lastLines(output, count = 20) {
|
|
44
|
+
return output.trimEnd().split('\n').slice(-count).join('\n');
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=run-program.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-program.js","sourceRoot":"","sources":["../../src/programs/run-program.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Whether a request goes to kvman's port, on any host. */
|
|
2
|
+
export declare function isOwnAddress(url: URL, ownPort: number | null): boolean;
|
|
3
|
+
/** The address, when it is one a recording may open. */
|
|
4
|
+
export declare function localAddress(address: string, ownPort: number | null): URL;
|
|
5
|
+
//# sourceMappingURL=address.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../../src/record/address.ts"],"names":[],"mappings":"AAaA,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAEtE;AAED,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,GAAG,CAOzE"}
|