gooseworks 0.5.8 → 0.5.9-dev.56.eca9486d
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/dist/agents/claude-mcp.d.ts +7 -1
- package/dist/agents/claude-mcp.d.ts.map +1 -1
- package/dist/agents/claude-mcp.js +43 -10
- package/dist/agents/claude-mcp.js.map +1 -1
- package/dist/auth/credentials.d.ts +8 -0
- package/dist/auth/credentials.d.ts.map +1 -1
- package/dist/auth/credentials.js +35 -1
- package/dist/auth/credentials.js.map +1 -1
- package/dist/auth/device-flow.d.ts +4 -1
- package/dist/auth/device-flow.d.ts.map +1 -1
- package/dist/auth/device-flow.js +8 -3
- package/dist/auth/device-flow.js.map +1 -1
- package/dist/auth/oauth-server.d.ts +9 -1
- package/dist/auth/oauth-server.d.ts.map +1 -1
- package/dist/auth/oauth-server.js +19 -11
- package/dist/auth/oauth-server.js.map +1 -1
- package/dist/auth/revoke.d.ts +10 -0
- package/dist/auth/revoke.d.ts.map +1 -0
- package/dist/auth/revoke.js +31 -0
- package/dist/auth/revoke.js.map +1 -0
- package/dist/commands/login.d.ts +15 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +34 -10
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +29 -11
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/video.d.ts +35 -0
- package/dist/commands/video.d.ts.map +1 -1
- package/dist/commands/video.js +475 -39
- package/dist/commands/video.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/kit/core/canonical.d.ts +3 -1
- package/dist/kit/core/canonical.d.ts.map +1 -1
- package/dist/kit/core/canonical.js +1 -1
- package/dist/kit/core/canonical.js.map +1 -1
- package/dist/kit/core/check.d.ts +2 -0
- package/dist/kit/core/check.d.ts.map +1 -1
- package/dist/kit/core/check.js +12 -2
- package/dist/kit/core/check.js.map +1 -1
- package/dist/kit/core/detach.d.ts +11 -0
- package/dist/kit/core/detach.d.ts.map +1 -0
- package/dist/kit/core/detach.js +72 -0
- package/dist/kit/core/detach.js.map +1 -0
- package/dist/kit/core/diff.d.ts +5 -0
- package/dist/kit/core/diff.d.ts.map +1 -0
- package/dist/kit/core/diff.js +132 -0
- package/dist/kit/core/diff.js.map +1 -0
- package/dist/kit/core/errors.d.ts +7 -1
- package/dist/kit/core/errors.d.ts.map +1 -1
- package/dist/kit/core/errors.js +11 -1
- package/dist/kit/core/errors.js.map +1 -1
- package/dist/kit/core/eyes.d.ts +80 -0
- package/dist/kit/core/eyes.d.ts.map +1 -0
- package/dist/kit/core/eyes.js +366 -0
- package/dist/kit/core/eyes.js.map +1 -0
- package/dist/kit/core/files.js +2 -2
- package/dist/kit/core/files.js.map +1 -1
- package/dist/kit/core/lock.d.ts +4 -0
- package/dist/kit/core/lock.d.ts.map +1 -0
- package/dist/kit/core/lock.js +13 -0
- package/dist/kit/core/lock.js.map +1 -0
- package/dist/kit/core/log.d.ts +38 -2
- package/dist/kit/core/log.d.ts.map +1 -1
- package/dist/kit/core/log.js +38 -5
- package/dist/kit/core/log.js.map +1 -1
- package/dist/kit/core/outcome.d.ts +114 -0
- package/dist/kit/core/outcome.d.ts.map +1 -0
- package/dist/kit/core/outcome.js +246 -0
- package/dist/kit/core/outcome.js.map +1 -0
- package/dist/kit/core/patch.d.ts +64 -0
- package/dist/kit/core/patch.d.ts.map +1 -0
- package/dist/kit/core/patch.js +289 -0
- package/dist/kit/core/patch.js.map +1 -0
- package/dist/kit/core/paths.d.ts +17 -0
- package/dist/kit/core/paths.d.ts.map +1 -1
- package/dist/kit/core/paths.js +21 -0
- package/dist/kit/core/paths.js.map +1 -1
- package/dist/kit/core/pieces.d.ts +16 -4
- package/dist/kit/core/pieces.d.ts.map +1 -1
- package/dist/kit/core/pieces.js +84 -27
- package/dist/kit/core/pieces.js.map +1 -1
- package/dist/kit/core/progress-reporter.d.ts +21 -4
- package/dist/kit/core/progress-reporter.d.ts.map +1 -1
- package/dist/kit/core/progress-reporter.js +74 -12
- package/dist/kit/core/progress-reporter.js.map +1 -1
- package/dist/kit/core/progress.d.ts +41 -4
- package/dist/kit/core/progress.d.ts.map +1 -1
- package/dist/kit/core/progress.js +105 -18
- package/dist/kit/core/progress.js.map +1 -1
- package/dist/kit/core/refusal-log.d.ts +7 -0
- package/dist/kit/core/refusal-log.d.ts.map +1 -0
- package/dist/kit/core/refusal-log.js +54 -0
- package/dist/kit/core/refusal-log.js.map +1 -0
- package/dist/kit/core/run.d.ts +39 -5
- package/dist/kit/core/run.d.ts.map +1 -1
- package/dist/kit/core/run.js +514 -83
- package/dist/kit/core/run.js.map +1 -1
- package/dist/kit/core/save.d.ts +7 -3
- package/dist/kit/core/save.d.ts.map +1 -1
- package/dist/kit/core/save.js +57 -20
- package/dist/kit/core/save.js.map +1 -1
- package/dist/kit/core/style.d.ts +1 -0
- package/dist/kit/core/style.d.ts.map +1 -1
- package/dist/kit/core/style.js +1 -1
- package/dist/kit/core/style.js.map +1 -1
- package/dist/kit/line/client.d.ts +14 -2
- package/dist/kit/line/client.d.ts.map +1 -1
- package/dist/kit/line/client.js +40 -20
- package/dist/kit/line/client.js.map +1 -1
- package/dist/kit/line/types.d.ts +33 -1
- package/dist/kit/line/types.d.ts.map +1 -1
- package/dist/kit/maker/cues.d.ts +67 -0
- package/dist/kit/maker/cues.d.ts.map +1 -0
- package/dist/kit/maker/cues.js +204 -0
- package/dist/kit/maker/cues.js.map +1 -0
- package/dist/kit/maker/encode.d.ts +5 -0
- package/dist/kit/maker/encode.d.ts.map +1 -1
- package/dist/kit/maker/encode.js +7 -1
- package/dist/kit/maker/encode.js.map +1 -1
- package/dist/kit/maker/facts.d.ts +46 -0
- package/dist/kit/maker/facts.d.ts.map +1 -0
- package/dist/kit/maker/facts.js +159 -0
- package/dist/kit/maker/facts.js.map +1 -0
- package/dist/kit/maker/inputs.d.ts +48 -3
- package/dist/kit/maker/inputs.d.ts.map +1 -1
- package/dist/kit/maker/inputs.js +151 -22
- package/dist/kit/maker/inputs.js.map +1 -1
- package/dist/kit/maker/make.d.ts +20 -1
- package/dist/kit/maker/make.d.ts.map +1 -1
- package/dist/kit/maker/make.js +129 -21
- package/dist/kit/maker/make.js.map +1 -1
- package/dist/kit/maker/page.d.ts.map +1 -1
- package/dist/kit/maker/page.js +49 -1
- package/dist/kit/maker/page.js.map +1 -1
- package/dist/kit/maker/parts/html-frames/1.2.1/part.json +1083 -0
- package/dist/kit/maker/parts/html-frames/src/part.d.ts.map +1 -1
- package/dist/kit/maker/parts/html-frames/src/part.js +4 -1
- package/dist/kit/maker/parts/html-frames/src/part.js.map +1 -1
- package/dist/kit/maker/runtime.d.ts.map +1 -1
- package/dist/kit/maker/runtime.js +54 -7
- package/dist/kit/maker/runtime.js.map +1 -1
- package/dist/kit/maker/timing.d.ts +74 -0
- package/dist/kit/maker/timing.d.ts.map +1 -0
- package/dist/kit/maker/timing.js +152 -0
- package/dist/kit/maker/timing.js.map +1 -0
- package/dist/kit/maker/under.d.ts +126 -0
- package/dist/kit/maker/under.d.ts.map +1 -0
- package/dist/kit/maker/under.js +299 -0
- package/dist/kit/maker/under.js.map +1 -0
- package/dist/kit/parts/catalog.d.ts +3 -20
- package/dist/kit/parts/catalog.d.ts.map +1 -1
- package/dist/kit/parts/catalog.js +6 -41
- package/dist/kit/parts/catalog.js.map +1 -1
- package/dist/kit/parts/loader.d.ts +7 -2
- package/dist/kit/parts/loader.d.ts.map +1 -1
- package/dist/kit/parts/loader.js +88 -23
- package/dist/kit/parts/loader.js.map +1 -1
- package/dist/skills/master-skill.d.ts +4 -6
- package/dist/skills/master-skill.d.ts.map +1 -1
- package/dist/skills/master-skill.js +28 -28
- package/dist/skills/master-skill.js.map +1 -1
- package/dist/skills/routes.js +5 -5
- package/dist/skills/routes.js.map +1 -1
- package/dist/skills/server-entry-skills.d.ts +6 -6
- package/dist/skills/server-entry-skills.d.ts.map +1 -1
- package/dist/skills/server-entry-skills.js +89 -60
- package/dist/skills/server-entry-skills.js.map +1 -1
- package/dist/utils/browser-preflight.d.ts +3 -0
- package/dist/utils/browser-preflight.d.ts.map +1 -1
- package/dist/utils/browser-preflight.js +27 -6
- package/dist/utils/browser-preflight.js.map +1 -1
- package/dist/utils/exit.d.ts +6 -0
- package/dist/utils/exit.d.ts.map +1 -0
- package/dist/utils/exit.js +37 -0
- package/dist/utils/exit.js.map +1 -0
- package/package.json +14 -3
- package/skills/goose-ads/SKILL.md +8 -8
- package/skills/goose-product-photos/SKILL.md +8 -8
- package/skills/goose-video/SKILL.md +29 -595
- package/skills/goose-video-local/SKILL.md +3 -1572
- package/skills/gooseworks/SKILL.md +10 -12
- package/skills/make-custom-video/SKILL.md +25 -672
- package/skills/manifest.json +7 -7
- package/skills/routes.json +3 -3
- package/dist/kit/maker/parts/html-frames/1.1.0/part.json +0 -199
- package/dist/skills/production-video-skills.json +0 -5
package/dist/kit/maker/encode.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FRAME_PATTERN = exports.SEGMENT_FRAMES = void 0;
|
|
3
|
+
exports.MASTER_RATE_CAP = exports.FRAME_PATTERN = exports.SEGMENT_FRAMES = void 0;
|
|
4
4
|
exports.frameName = frameName;
|
|
5
5
|
exports.segmentArgs = segmentArgs;
|
|
6
6
|
exports.concatList = concatList;
|
|
@@ -9,6 +9,11 @@ exports.pngSize = pngSize;
|
|
|
9
9
|
/** Frames per segment: about 2 seconds at 30 fps. */
|
|
10
10
|
exports.SEGMENT_FRAMES = 60;
|
|
11
11
|
exports.FRAME_PATTERN = 'f%06d.png';
|
|
12
|
+
/**
|
|
13
|
+
* The 1.2.0 size rule: every final H.264 encode caps its rate, appended right
|
|
14
|
+
* after the kit's h264-master settings, so a busy page cannot make a huge file.
|
|
15
|
+
*/
|
|
16
|
+
exports.MASTER_RATE_CAP = ['-maxrate', '16M', '-bufsize', '32M'];
|
|
12
17
|
function frameName(index) {
|
|
13
18
|
return `f${String(index).padStart(6, '0')}.png`;
|
|
14
19
|
}
|
|
@@ -26,6 +31,7 @@ function segmentArgs(opts) {
|
|
|
26
31
|
'-frames:v', String(opts.count),
|
|
27
32
|
'-vf', 'scale=out_color_matrix=bt709:out_range=tv,format=yuv420p',
|
|
28
33
|
...opts.tools.encodeArgs('h264-master'),
|
|
34
|
+
...exports.MASTER_RATE_CAP,
|
|
29
35
|
'-c:v', 'libx264',
|
|
30
36
|
'-pix_fmt', 'yuv420p',
|
|
31
37
|
'-colorspace', 'bt709', '-color_primaries', 'bt709', '-color_trc', 'bt709', '-color_range', 'tv',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../../../src/kit/maker/encode.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../../../src/kit/maker/encode.ts"],"names":[],"mappings":";;;AAgBA,8BAEC;AAOD,kCAwBC;AAGD,gCAEC;AAED,gCASC;AAGD,0BAIC;AAnED,qDAAqD;AACxC,QAAA,cAAc,GAAG,EAAE,CAAC;AAEpB,QAAA,aAAa,GAAG,WAAW,CAAC;AAEzC;;;GAGG;AACU,QAAA,eAAe,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;AAEtE,SAAgB,SAAS,CAAC,KAAa;IACrC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,SAAgB,WAAW,CAAC,IAO3B;IACC,OAAO;QACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI;QACtD,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QAC9B,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QACnC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,qBAAa,EAAE;QAC1C,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/B,KAAK,EAAE,0DAA0D;QACjE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC;QACvC,GAAG,uBAAe;QAClB,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,aAAa,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI;QAChG,KAAK;QACL,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,IAAI,CAAC,GAAG;KACT,CAAC;AACJ,CAAC;AAED,mDAAmD;AACnD,SAAgB,UAAU,CAAC,QAAkB;IAC3C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,SAAgB,UAAU,CAAC,QAAgB,EAAE,GAAW;IACtD,OAAO;QACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI;QACtD,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ;QAC5C,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,YAAY;QACzB,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,IAAI;QAC7C,GAAG;KACJ,CAAC;AACJ,CAAC;AAED,4CAA4C;AAC5C,SAAgB,OAAO,CAAC,IAAgB;IACtC,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAClH,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { KitTools } from '../part-interface';
|
|
2
|
+
import type { PictureGeometry } from './images';
|
|
3
|
+
/** A box as fractions (0..1) of the picture as shown. */
|
|
4
|
+
export interface SubjectBox {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
/** The x-centre of the subject's top half: centre a bottle on this, not on its shadow. */
|
|
10
|
+
top_cx: number;
|
|
11
|
+
}
|
|
12
|
+
export interface PictureFacts {
|
|
13
|
+
/** More than 4% of the picture is see-through: a cutout. */
|
|
14
|
+
alpha: boolean;
|
|
15
|
+
/** The colour of an even, opaque border (a studio backdrop), as #rrggbb; null for a photo or a cutout. */
|
|
16
|
+
plate: string | null;
|
|
17
|
+
/** The most common foreground colour (not the plate, near-white or near-black), as #rrggbb; null when there is none. */
|
|
18
|
+
accent: string | null;
|
|
19
|
+
/** Mean brightness of the opaque pixels, 0 (black) to 1 (white). */
|
|
20
|
+
luminance: number;
|
|
21
|
+
/** Size of the picture as the page loads it, in pixels. */
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
/** Where the subject is, for a cutout or a picture on a plain backdrop; null for a photo. */
|
|
25
|
+
subject: SubjectBox | null;
|
|
26
|
+
}
|
|
27
|
+
/** The measuring copy is this many pixels on each side (stretched to a square: boxes come out as fractions). */
|
|
28
|
+
export declare const FACTS_SIZE = 128;
|
|
29
|
+
/**
|
|
30
|
+
* Facts from a square RGBA copy of the picture (`n` x `n`, as shown), and the
|
|
31
|
+
* size the page loads it at. Pure, so it is tested on hand-made pixels.
|
|
32
|
+
*/
|
|
33
|
+
export declare function pictureFacts(rgba: Uint8Array, n: number, size: {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}): PictureFacts;
|
|
37
|
+
/**
|
|
38
|
+
* Measures one picture from its checked bytes with the kit's ffmpeg. `scratch`
|
|
39
|
+
* is a file path the RGBA copy is written to and removed from. Returns null
|
|
40
|
+
* when ffmpeg cannot read it; the page then has no facts for that picture.
|
|
41
|
+
*/
|
|
42
|
+
export declare function measurePicture(tools: Pick<KitTools, 'exec'>, data: Uint8Array, shape: PictureGeometry, size: {
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}, scratch: string): Promise<PictureFacts | null>;
|
|
46
|
+
//# sourceMappingURL=facts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facts.d.ts","sourceRoot":"","sources":["../../../src/kit/maker/facts.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,0FAA0F;IAC1F,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,KAAK,EAAE,OAAO,CAAC;IACf,0GAA0G;IAC1G,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,wHAAwH;IACxH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;CAC5B;AAED,gHAAgH;AAChH,eAAO,MAAM,UAAU,MAAM,CAAC;AAuB9B;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAuD/G;AAyCD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,EAC7B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACvC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAgB9B"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FACTS_SIZE = void 0;
|
|
4
|
+
exports.pictureFacts = pictureFacts;
|
|
5
|
+
exports.measurePicture = measurePicture;
|
|
6
|
+
// Picture facts (html-frames 1.2.0 `facts: true`): how each picture the page
|
|
7
|
+
// gets should be presented, measured here in Node because a file:// page may
|
|
8
|
+
// not read a picture's pixels (its canvas is tainted). A port of the app's
|
|
9
|
+
// hyperframe imageFacts (video-lab/blocks/hyperframe/images.ts) and its
|
|
10
|
+
// product palette, plus the subject box the old product renderers centred on.
|
|
11
|
+
// The kit's ffmpeg decodes a small RGBA copy, turned by the EXIF orientation as
|
|
12
|
+
// the browser shows it; the sums below run on those bytes. Bundled into the part.
|
|
13
|
+
const promises_1 = require("node:fs/promises");
|
|
14
|
+
/** The measuring copy is this many pixels on each side (stretched to a square: boxes come out as fractions). */
|
|
15
|
+
exports.FACTS_SIZE = 128;
|
|
16
|
+
/** Border width the plate is read from: 3 px of the app's 96, kept as a share. */
|
|
17
|
+
const BORDER = Math.max(1, Math.round((3 / 96) * exports.FACTS_SIZE));
|
|
18
|
+
/** ffmpeg filters that turn stored pixels the way each EXIF orientation shows them (as page.ts stages them). */
|
|
19
|
+
const ORIENT = {
|
|
20
|
+
1: [],
|
|
21
|
+
2: ['hflip'],
|
|
22
|
+
3: ['hflip', 'vflip'],
|
|
23
|
+
4: ['vflip'],
|
|
24
|
+
5: ['transpose=0'],
|
|
25
|
+
6: ['transpose=1'],
|
|
26
|
+
7: ['transpose=3'],
|
|
27
|
+
8: ['transpose=2'],
|
|
28
|
+
};
|
|
29
|
+
function hex(r, g, b) {
|
|
30
|
+
return '#' + [r, g, b].map((v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, '0')).join('');
|
|
31
|
+
}
|
|
32
|
+
const round = (value, places = 4) => Math.round(value * 10 ** places) / 10 ** places;
|
|
33
|
+
/**
|
|
34
|
+
* Facts from a square RGBA copy of the picture (`n` x `n`, as shown), and the
|
|
35
|
+
* size the page loads it at. Pure, so it is tested on hand-made pixels.
|
|
36
|
+
*/
|
|
37
|
+
function pictureFacts(rgba, n, size) {
|
|
38
|
+
const at = (x, y) => (y * n + x) * 4;
|
|
39
|
+
let seeThrough = 0;
|
|
40
|
+
let lumSum = 0;
|
|
41
|
+
let lumCount = 0;
|
|
42
|
+
const border = [];
|
|
43
|
+
for (let y = 0; y < n; y++) {
|
|
44
|
+
for (let x = 0; x < n; x++) {
|
|
45
|
+
const i = at(x, y);
|
|
46
|
+
const a = rgba[i + 3];
|
|
47
|
+
if (a < 200)
|
|
48
|
+
seeThrough++;
|
|
49
|
+
if (a >= 128) {
|
|
50
|
+
lumSum += 0.2126 * rgba[i] + 0.7152 * rgba[i + 1] + 0.0722 * rgba[i + 2];
|
|
51
|
+
lumCount++;
|
|
52
|
+
}
|
|
53
|
+
if (x < BORDER || y < BORDER || x >= n - BORDER || y >= n - BORDER)
|
|
54
|
+
border.push([rgba[i], rgba[i + 1], rgba[i + 2], a]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const alpha = seeThrough / (n * n) > 0.04;
|
|
58
|
+
const opaque = border.filter((p) => p[3] >= 200);
|
|
59
|
+
const avg = [0, 1, 2].map((k) => opaque.reduce((s, p) => s + p[k], 0) / Math.max(1, opaque.length));
|
|
60
|
+
const spread = Math.max(...[0, 1, 2].map((k) => Math.sqrt(opaque.reduce((s, p) => s + (p[k] - avg[k]) ** 2, 0) / Math.max(1, opaque.length))));
|
|
61
|
+
const even = !alpha && opaque.length > border.length * 0.9 && spread < 14;
|
|
62
|
+
const plate = even ? hex(avg[0], avg[1], avg[2]) : null;
|
|
63
|
+
// Foreground: opaque, away from the plate, not a white highlight or black.
|
|
64
|
+
const near = (i) => even && Math.abs(rgba[i] - avg[0]) + Math.abs(rgba[i + 1] - avg[1]) + Math.abs(rgba[i + 2] - avg[2]) < 40;
|
|
65
|
+
const buckets = new Map();
|
|
66
|
+
for (let i = 0; i < rgba.length; i += 4) {
|
|
67
|
+
const [r, g, b, a] = [rgba[i], rgba[i + 1], rgba[i + 2], rgba[i + 3]];
|
|
68
|
+
if (a < 200 || near(i))
|
|
69
|
+
continue;
|
|
70
|
+
const max = Math.max(r, g, b);
|
|
71
|
+
const min = Math.min(r, g, b);
|
|
72
|
+
if ((max > 238 && min > 225) || max < 22)
|
|
73
|
+
continue;
|
|
74
|
+
const key = ((r >> 5) << 6) | ((g >> 5) << 3) | (b >> 5);
|
|
75
|
+
const bucket = buckets.get(key) ?? { n: 0, r: 0, g: 0, b: 0 };
|
|
76
|
+
bucket.n++;
|
|
77
|
+
bucket.r += r;
|
|
78
|
+
bucket.g += g;
|
|
79
|
+
bucket.b += b;
|
|
80
|
+
buckets.set(key, bucket);
|
|
81
|
+
}
|
|
82
|
+
// Ties go to the lower bucket key, so the answer never depends on the order pixels were seen in.
|
|
83
|
+
const top = [...buckets.entries()].sort((a, b) => b[1].n - a[1].n || a[0] - b[0])[0]?.[1];
|
|
84
|
+
const accent = top && top.n >= n * n * 0.005 ? hex(top.r / top.n, top.g / top.n, top.b / top.n) : null;
|
|
85
|
+
return {
|
|
86
|
+
alpha,
|
|
87
|
+
plate,
|
|
88
|
+
accent,
|
|
89
|
+
luminance: round(lumCount ? lumSum / lumCount / 255 : 0, 3),
|
|
90
|
+
width: size.width,
|
|
91
|
+
height: size.height,
|
|
92
|
+
subject: alpha || even ? subjectOf(rgba, n, alpha ? (i) => rgba[i + 3] >= 128 : (i) => rgba[i + 3] >= 128 && !near(i)) : null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** The box of the subject's pixels; a lone pixel (JPEG noise on a backdrop) does not count. */
|
|
96
|
+
function subjectOf(rgba, n, inSubject) {
|
|
97
|
+
const mask = new Uint8Array(n * n);
|
|
98
|
+
for (let p = 0; p < n * n; p++)
|
|
99
|
+
mask[p] = inSubject(p * 4) ? 1 : 0;
|
|
100
|
+
const kept = (x, y) => {
|
|
101
|
+
const p = y * n + x;
|
|
102
|
+
if (!mask[p])
|
|
103
|
+
return false;
|
|
104
|
+
return (x > 0 && mask[p - 1] === 1) || (x < n - 1 && mask[p + 1] === 1) || (y > 0 && mask[p - n] === 1) || (y < n - 1 && mask[p + n] === 1);
|
|
105
|
+
};
|
|
106
|
+
let [x0, y0, x1, y1] = [n, n, -1, -1];
|
|
107
|
+
for (let y = 0; y < n; y++) {
|
|
108
|
+
for (let x = 0; x < n; x++) {
|
|
109
|
+
if (!kept(x, y))
|
|
110
|
+
continue;
|
|
111
|
+
x0 = Math.min(x0, x);
|
|
112
|
+
x1 = Math.max(x1, x);
|
|
113
|
+
y0 = Math.min(y0, y);
|
|
114
|
+
y1 = Math.max(y1, y);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (x1 < 0)
|
|
118
|
+
return null;
|
|
119
|
+
// The top half of the subject's rows: its own left and right edges give the centre.
|
|
120
|
+
const half = y0 + Math.max(1, Math.ceil((y1 - y0 + 1) / 2));
|
|
121
|
+
let [tx0, tx1] = [n, -1];
|
|
122
|
+
for (let y = y0; y < half; y++) {
|
|
123
|
+
for (let x = 0; x < n; x++) {
|
|
124
|
+
if (!kept(x, y))
|
|
125
|
+
continue;
|
|
126
|
+
tx0 = Math.min(tx0, x);
|
|
127
|
+
tx1 = Math.max(tx1, x);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
x: round(x0 / n),
|
|
132
|
+
y: round(y0 / n),
|
|
133
|
+
w: round((x1 + 1 - x0) / n),
|
|
134
|
+
h: round((y1 + 1 - y0) / n),
|
|
135
|
+
top_cx: round(tx1 < 0 ? (x0 + x1 + 1) / 2 / n : (tx0 + tx1 + 1) / 2 / n),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Measures one picture from its checked bytes with the kit's ffmpeg. `scratch`
|
|
140
|
+
* is a file path the RGBA copy is written to and removed from. Returns null
|
|
141
|
+
* when ffmpeg cannot read it; the page then has no facts for that picture.
|
|
142
|
+
*/
|
|
143
|
+
async function measurePicture(tools, data, shape, size, scratch) {
|
|
144
|
+
const filters = [...ORIENT[shape.orientation], 'format=rgba', `scale=${exports.FACTS_SIZE}:${exports.FACTS_SIZE}:flags=area`, 'setsar=1', 'format=rgba'];
|
|
145
|
+
try {
|
|
146
|
+
await tools.exec('ffmpeg', ['-hide_banner', '-y', '-loglevel', 'error', '-noautorotate', '-f', `${shape.format}_pipe`, '-i', 'pipe:0', '-frames:v', '1', '-vf', filters.join(','), '-f', 'rawvideo', '-pix_fmt', 'rgba', '-threads', '1', scratch], { stdin: data });
|
|
147
|
+
const rgba = await (0, promises_1.readFile)(scratch);
|
|
148
|
+
if (rgba.length !== exports.FACTS_SIZE * exports.FACTS_SIZE * 4)
|
|
149
|
+
return null;
|
|
150
|
+
return pictureFacts(rgba, exports.FACTS_SIZE, size);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
await (0, promises_1.rm)(scratch, { force: true }).catch(() => undefined);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=facts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facts.js","sourceRoot":"","sources":["../../../src/kit/maker/facts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { FileRef, PartContext } from '../part-interface';
|
|
2
|
+
import { countWords, type SpeechEntry, type SpokenWord, type Zone } from './timing';
|
|
3
|
+
import { type UnderInfo, type UnderSpec } from './under';
|
|
2
4
|
export type Aspect = '9:16' | '1:1' | '4:5' | '16:9';
|
|
3
5
|
/** Every frame page is laid out at a 1080 px short side (CSS px). */
|
|
4
6
|
export declare const DESIGN_SIZE: Record<Aspect, {
|
|
@@ -27,6 +29,10 @@ export interface SceneSpec {
|
|
|
27
29
|
picture: FileRef | string | null;
|
|
28
30
|
/** A picture the customer uploaded for this scene (a partner's icon, a screen), as a file. */
|
|
29
31
|
image: FileRef | null;
|
|
32
|
+
/** The cast key of who says this scene (1.2.0). */
|
|
33
|
+
speaker: string | null;
|
|
34
|
+
/** The style's own scene fields (1.2.0), passed to the page as they are. */
|
|
35
|
+
extra: Record<string, unknown> | null;
|
|
30
36
|
}
|
|
31
37
|
export interface ProductSpec {
|
|
32
38
|
id: string;
|
|
@@ -46,6 +52,24 @@ export interface BrandSpec {
|
|
|
46
52
|
url: string | null;
|
|
47
53
|
} | null;
|
|
48
54
|
}
|
|
55
|
+
/** An extra picture the page shows by id (1.2.0 `media`): a cast still, a screen. */
|
|
56
|
+
export interface MediaSpec {
|
|
57
|
+
id: string;
|
|
58
|
+
image: FileRef;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* One plan.footage entry, as the page sees it (1.2.0 `footage`): its window and
|
|
62
|
+
* framing only. The file is never put in the page; a frame page cannot play video.
|
|
63
|
+
*/
|
|
64
|
+
export interface FootageSpec {
|
|
65
|
+
index: number;
|
|
66
|
+
scene_id: string | null;
|
|
67
|
+
kind: string;
|
|
68
|
+
start_s: number | null;
|
|
69
|
+
end_s: number | null;
|
|
70
|
+
crop: unknown;
|
|
71
|
+
fit: string | null;
|
|
72
|
+
}
|
|
49
73
|
export interface MakerSpec {
|
|
50
74
|
template: FileRef;
|
|
51
75
|
frames: FileRef[];
|
|
@@ -72,10 +96,31 @@ export interface MakerSpec {
|
|
|
72
96
|
/** Device scale factor: output / design. */
|
|
73
97
|
scale: number;
|
|
74
98
|
values: Record<string, unknown>;
|
|
99
|
+
/** The video the page is drawn over (1.2.0), or null for a video of the page alone. */
|
|
100
|
+
under: UnderSpec | null;
|
|
101
|
+
/** The voice track the scenes were timed by, or the speech of the video drawn over; else empty (1.2.0). */
|
|
102
|
+
speech: SpeechEntry[];
|
|
103
|
+
/** Each scene's spoken words, in video seconds (kit.scenes[i].words). */
|
|
104
|
+
words: SpokenWord[][];
|
|
105
|
+
/** Where captions may go, in the page's CSS pixels, kept to the page (1.2.0). */
|
|
106
|
+
captionZone: Zone | null;
|
|
107
|
+
/** How many of the last scenes are the end card; 0 for none (1.2.0). */
|
|
108
|
+
endCardScenes: number;
|
|
109
|
+
/** skip_blank asked for nothing and no scene has text: the step makes nothing (1.2.0). */
|
|
110
|
+
skip: boolean;
|
|
111
|
+
/** Extra pictures by id (1.2.0), staged as kit.media. */
|
|
112
|
+
media: MediaSpec[];
|
|
113
|
+
/** plan.footage windows (1.2.0), never the files. */
|
|
114
|
+
footage: FootageSpec[];
|
|
115
|
+
/** Measure every picture into kit.facts (1.2.0). */
|
|
116
|
+
facts: boolean;
|
|
117
|
+
}
|
|
118
|
+
/** What the maker measured before reading the inputs: the video to draw over, read with ffprobe. */
|
|
119
|
+
export interface Measured {
|
|
120
|
+
under?: UnderInfo | null;
|
|
75
121
|
}
|
|
76
122
|
export declare function isFileRef(value: unknown): value is FileRef;
|
|
77
|
-
|
|
78
|
-
export declare function countWords(text: string | null): number;
|
|
123
|
+
export { countWords };
|
|
79
124
|
/** Reads the part's inputs into a render plan, or throws bad_input naming the problem. */
|
|
80
|
-
export declare function readInputs(raw: unknown, ctx: Pick<PartContext, 'error'
|
|
125
|
+
export declare function readInputs(raw: unknown, ctx: Pick<PartContext, 'error'>, measured?: Measured): MakerSpec;
|
|
81
126
|
//# sourceMappingURL=inputs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../../src/kit/maker/inputs.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../../src/kit/maker/inputs.ts"],"names":[],"mappings":"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"}
|
package/dist/kit/maker/inputs.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LIMITS = exports.SHORT_SIDES = exports.DESIGN_SIZE = void 0;
|
|
3
|
+
exports.countWords = exports.LIMITS = exports.SHORT_SIDES = exports.DESIGN_SIZE = void 0;
|
|
4
4
|
exports.isFileRef = isFileRef;
|
|
5
|
-
exports.countWords = countWords;
|
|
6
5
|
exports.readInputs = readInputs;
|
|
7
6
|
// The html-frames inputs, read and checked before anything starts. Bundled
|
|
8
7
|
// into the part (part.mjs) with the input schema from its part.json and the
|
|
9
8
|
// kit core's schema checker, so the part refuses exactly what part.json does,
|
|
10
9
|
// however it is called.
|
|
11
10
|
const schema_1 = require("../core/schema");
|
|
12
|
-
const part_json_1 = require("./parts/html-frames/1.1
|
|
11
|
+
const part_json_1 = require("./parts/html-frames/1.2.1/part.json");
|
|
12
|
+
const timing_1 = require("./timing");
|
|
13
|
+
Object.defineProperty(exports, "countWords", { enumerable: true, get: function () { return timing_1.countWords; } });
|
|
14
|
+
const under_1 = require("./under");
|
|
13
15
|
/** Every frame page is laid out at a 1080 px short side (CSS px). */
|
|
14
16
|
exports.DESIGN_SIZE = {
|
|
15
17
|
'9:16': { width: 1080, height: 1920 },
|
|
@@ -30,6 +32,8 @@ exports.LIMITS = {
|
|
|
30
32
|
maxWords: 200,
|
|
31
33
|
textChars: 2000,
|
|
32
34
|
};
|
|
35
|
+
/** The inputs that set the video's length; exactly one is given. */
|
|
36
|
+
const LENGTH_SOURCES = ['duration_s', 'scene_s', 'pace', 'speech', 'under'];
|
|
33
37
|
function isObject(value) {
|
|
34
38
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
35
39
|
}
|
|
@@ -59,12 +63,6 @@ function optionalText(value, at, fail) {
|
|
|
59
63
|
fail(`${at} is longer than ${exports.LIMITS.textChars} characters`);
|
|
60
64
|
return value;
|
|
61
65
|
}
|
|
62
|
-
/** Words as a reader counts them: runs of letters or digits, joined by ' or -. */
|
|
63
|
-
function countWords(text) {
|
|
64
|
-
if (!text)
|
|
65
|
-
return 0;
|
|
66
|
-
return text.split(/\s+/u).filter((token) => /[\p{L}\p{N}]/u.test(token)).length;
|
|
67
|
-
}
|
|
68
66
|
function scenesOf(value, maxWords, fail) {
|
|
69
67
|
if (!Array.isArray(value) || value.length === 0)
|
|
70
68
|
fail('scenes should list at least one scene');
|
|
@@ -84,7 +82,7 @@ function scenesOf(value, maxWords, fail) {
|
|
|
84
82
|
const line = optionalText(raw.line, `${at}.line`, fail);
|
|
85
83
|
const onScreen = optionalText(raw.on_screen, `${at}.on_screen`, fail);
|
|
86
84
|
for (const [field, text] of [['line', line], ['on_screen', onScreen]]) {
|
|
87
|
-
const words = countWords(text);
|
|
85
|
+
const words = (0, timing_1.countWords)(text);
|
|
88
86
|
if (words > maxWords) {
|
|
89
87
|
fail(`scene ${index + 1} (${id}) has ${words} words in ${field}; this style allows ${maxWords} per scene`);
|
|
90
88
|
}
|
|
@@ -95,7 +93,11 @@ function scenesOf(value, maxWords, fail) {
|
|
|
95
93
|
else
|
|
96
94
|
picture = optionalText(raw.picture, `${at}.picture`, fail);
|
|
97
95
|
const image = raw.image === undefined || raw.image === null ? null : fileOf(raw.image, `${at}.image`, ['image'], fail);
|
|
98
|
-
|
|
96
|
+
const speaker = optionalText(raw.speaker, `${at}.speaker`, fail);
|
|
97
|
+
if (raw.extra !== undefined && raw.extra !== null && !isObject(raw.extra))
|
|
98
|
+
fail(`${at}.extra should be an object`);
|
|
99
|
+
const extra = isObject(raw.extra) ? raw.extra : null;
|
|
100
|
+
return { id, index, line, on_screen: onScreen, picture, image, speaker, extra };
|
|
99
101
|
});
|
|
100
102
|
}
|
|
101
103
|
function productsOf(value, fail) {
|
|
@@ -160,8 +162,53 @@ function brandOf(value, fail) {
|
|
|
160
162
|
}
|
|
161
163
|
return { name, logo, colors, fonts, cta };
|
|
162
164
|
}
|
|
165
|
+
function mediaOf(value, fail) {
|
|
166
|
+
if (value === undefined || value === null)
|
|
167
|
+
return [];
|
|
168
|
+
if (!Array.isArray(value))
|
|
169
|
+
fail('media should be a list');
|
|
170
|
+
const ids = new Set();
|
|
171
|
+
return value.map((raw, i) => {
|
|
172
|
+
const at = `media[${i}]`;
|
|
173
|
+
if (!isObject(raw))
|
|
174
|
+
fail(`${at} should be an object`);
|
|
175
|
+
if (typeof raw.id !== 'string' || !raw.id)
|
|
176
|
+
fail(`${at}.id should be text`);
|
|
177
|
+
if (ids.has(raw.id))
|
|
178
|
+
fail(`${at}.id "${raw.id}" is used twice`);
|
|
179
|
+
ids.add(raw.id);
|
|
180
|
+
return { id: raw.id, image: fileOf(raw.image, `${at}.image`, ['image'], fail) };
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function secondsOf(ms) {
|
|
184
|
+
return typeof ms === 'number' && Number.isFinite(ms) ? Math.round(ms) / 1000 : null;
|
|
185
|
+
}
|
|
186
|
+
function footageOf(value, fail) {
|
|
187
|
+
if (value === undefined || value === null)
|
|
188
|
+
return [];
|
|
189
|
+
if (!Array.isArray(value))
|
|
190
|
+
fail('footage should be a list');
|
|
191
|
+
return value.map((raw, index) => {
|
|
192
|
+
const at = `footage[${index}]`;
|
|
193
|
+
if (!isObject(raw))
|
|
194
|
+
fail(`${at} should be an object`);
|
|
195
|
+
const start = secondsOf(raw.start_ms);
|
|
196
|
+
const end = secondsOf(raw.end_ms);
|
|
197
|
+
if (start !== null && end !== null && end < start)
|
|
198
|
+
fail(`${at} ends before it starts`);
|
|
199
|
+
return {
|
|
200
|
+
index,
|
|
201
|
+
scene_id: typeof raw.scene_id === 'string' && raw.scene_id ? raw.scene_id : null,
|
|
202
|
+
kind: typeof raw.kind === 'string' && raw.kind ? raw.kind : 'video',
|
|
203
|
+
start_s: start,
|
|
204
|
+
end_s: end,
|
|
205
|
+
crop: raw.crop ?? null,
|
|
206
|
+
fit: typeof raw.fit === 'string' && raw.fit ? raw.fit : null,
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
}
|
|
163
210
|
/** Reads the part's inputs into a render plan, or throws bad_input naming the problem. */
|
|
164
|
-
function readInputs(raw, ctx) {
|
|
211
|
+
function readInputs(raw, ctx, measured = {}) {
|
|
165
212
|
const fail = (detail) => {
|
|
166
213
|
throw ctx.error('bad_input', detail);
|
|
167
214
|
};
|
|
@@ -188,7 +235,32 @@ function readInputs(raw, ctx) {
|
|
|
188
235
|
if (typeof aspect !== 'string' || !(aspect in exports.DESIGN_SIZE))
|
|
189
236
|
fail(`aspect should be one of ${Object.keys(exports.DESIGN_SIZE).join(', ')}`);
|
|
190
237
|
const design = exports.DESIGN_SIZE[aspect];
|
|
191
|
-
const
|
|
238
|
+
const given = LENGTH_SOURCES.filter((key) => raw[key] !== undefined && raw[key] !== null);
|
|
239
|
+
if (given.length !== 1)
|
|
240
|
+
fail('give exactly one of duration_s (the whole video), scene_s (each scene), pace, speech or under (a video to draw over)');
|
|
241
|
+
const source = given[0];
|
|
242
|
+
let under = null;
|
|
243
|
+
if (source === 'under') {
|
|
244
|
+
const file = fileOf(raw.under, 'under', ['video'], fail);
|
|
245
|
+
if (!isObject(raw.under_timeline))
|
|
246
|
+
fail('under_timeline should be the timeline of the video to draw over');
|
|
247
|
+
const timeline = raw.under_timeline;
|
|
248
|
+
const info = measured.under === undefined ? null : measured.under;
|
|
249
|
+
if (measured.under === null)
|
|
250
|
+
fail('the video to draw over could not be read');
|
|
251
|
+
// Before it is measured (a dry read), the timeline's own length stands in.
|
|
252
|
+
const seconds = info?.seconds ?? (typeof file.duration_s === 'number' ? file.duration_s : timeline.duration_s);
|
|
253
|
+
under = {
|
|
254
|
+
file,
|
|
255
|
+
timeline,
|
|
256
|
+
info: info ?? { seconds, width: timeline.width, height: timeline.height, fps: timeline.fps, hasAudio: false },
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
else if (raw.under_timeline !== undefined && raw.under_timeline !== null)
|
|
260
|
+
fail('under_timeline is given without under');
|
|
261
|
+
// Drawn over a video, the frame rate follows it unless the step sets one.
|
|
262
|
+
const underFps = under?.info.fps ? Math.min(exports.LIMITS.maxFps, Math.max(1, Math.round(under.info.fps))) : null;
|
|
263
|
+
const fps = raw.fps ?? underFps ?? 30;
|
|
192
264
|
if (typeof fps !== 'number' || !Number.isInteger(fps) || fps < 1 || fps > exports.LIMITS.maxFps)
|
|
193
265
|
fail(`fps should be a whole number from 1 to ${exports.LIMITS.maxFps}`);
|
|
194
266
|
const shortSide = raw.short_side ?? 1080;
|
|
@@ -196,25 +268,73 @@ function readInputs(raw, ctx) {
|
|
|
196
268
|
fail(`short_side should be ${exports.SHORT_SIDES.join(' or ')}`);
|
|
197
269
|
const scale = shortSide / 1080;
|
|
198
270
|
const output = { width: Math.round(design.width * scale), height: Math.round(design.height * scale) };
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
271
|
+
// Scenes timed by a voice track or held by their words start where these say.
|
|
272
|
+
const speechIn = source === 'speech' ? raw.speech : null;
|
|
273
|
+
let timed = null;
|
|
274
|
+
if (speechIn) {
|
|
275
|
+
const times = (0, timing_1.speechTimes)(scenes.map((scene) => scene.id), speechIn, typeof raw.tail_s === 'number' ? raw.tail_s : 0);
|
|
276
|
+
if (typeof times === 'string')
|
|
277
|
+
fail(times);
|
|
278
|
+
timed = times;
|
|
279
|
+
}
|
|
280
|
+
else if (source === 'pace') {
|
|
281
|
+
const holds = (0, timing_1.paceHolds)(scenes, raw.pace);
|
|
282
|
+
if (typeof holds === 'string')
|
|
283
|
+
fail(holds);
|
|
284
|
+
timed = (0, timing_1.startsOfHolds)(holds);
|
|
285
|
+
}
|
|
286
|
+
const seconds = under
|
|
287
|
+
? under.info.seconds
|
|
288
|
+
: timed
|
|
289
|
+
? timed.seconds
|
|
290
|
+
: source === 'duration_s'
|
|
291
|
+
? raw.duration_s
|
|
292
|
+
: raw.scene_s * scenes.length;
|
|
204
293
|
if (typeof seconds !== 'number' || !Number.isFinite(seconds) || seconds < exports.LIMITS.minSeconds || seconds > exports.LIMITS.maxSeconds) {
|
|
205
294
|
fail(`the video would be ${String(seconds)} seconds; it must be ${exports.LIMITS.minSeconds} to ${exports.LIMITS.maxSeconds}`);
|
|
206
295
|
}
|
|
207
296
|
const frameCount = Math.round(seconds * fps);
|
|
208
297
|
if (frameCount < scenes.length)
|
|
209
298
|
fail(`${scenes.length} scenes need at least ${scenes.length} frames; the video has ${frameCount}`);
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
299
|
+
let sceneFrames;
|
|
300
|
+
if (timed) {
|
|
301
|
+
const frames = (0, timing_1.framesOfStarts)(timed.starts, fps, frameCount);
|
|
302
|
+
if (!Array.isArray(frames)) {
|
|
303
|
+
const scene = scenes[frames.empty];
|
|
304
|
+
fail(`scene ${frames.empty + 1} (${scene.id}) gets no time at ${fps} fps: ${source === 'speech' ? 'the speech gives it none (a scene with no speech needs a gap, or tail_s at the end)' : 'its pace hold is too short'}`);
|
|
305
|
+
}
|
|
306
|
+
sceneFrames = frames;
|
|
307
|
+
}
|
|
308
|
+
else if (under) {
|
|
309
|
+
// Scenes take the real times of the video drawn over.
|
|
310
|
+
const fromUnder = (0, under_1.underSceneFrames)(scenes.map((scene) => scene.id), under.timeline, fps, frameCount);
|
|
311
|
+
if (!fromUnder) {
|
|
312
|
+
fail(`the plan's ${scenes.length} scenes do not match the ${under.timeline.scenes.length} scenes of the video to draw over (by id or in order)`);
|
|
313
|
+
}
|
|
314
|
+
sceneFrames = fromUnder;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// Scenes share the video evenly, on whole frames, so every scene starts on a frame.
|
|
318
|
+
sceneFrames = scenes.map((_, i) => Math.round((i * frameCount) / scenes.length));
|
|
319
|
+
sceneFrames.push(frameCount);
|
|
320
|
+
}
|
|
213
321
|
const values = raw.values ?? {};
|
|
214
322
|
if (!isObject(values))
|
|
215
323
|
fail('values should be an object');
|
|
216
|
-
if (JSON.stringify(values)
|
|
324
|
+
if (Buffer.byteLength(JSON.stringify(values), 'utf8') > 64 * 1024)
|
|
217
325
|
fail('values is larger than 64 KB');
|
|
326
|
+
let captionZone = null;
|
|
327
|
+
if (raw.caption_zone !== undefined && raw.caption_zone !== null) {
|
|
328
|
+
captionZone = (0, timing_1.clipZone)(raw.caption_zone, design);
|
|
329
|
+
if (!captionZone)
|
|
330
|
+
fail(`caption_zone lies outside the ${design.width}x${design.height} page`);
|
|
331
|
+
if (!(0, timing_1.zoneOnVideo)(captionZone, scale, output))
|
|
332
|
+
fail(`caption_zone is less than a pixel of the ${output.width}x${output.height} video`);
|
|
333
|
+
}
|
|
334
|
+
const endCardScenes = typeof raw.end_card_scenes === 'number' ? raw.end_card_scenes : 0;
|
|
335
|
+
if (endCardScenes > scenes.length)
|
|
336
|
+
fail(`end_card_scenes is ${endCardScenes}, but there are only ${scenes.length} scenes`);
|
|
337
|
+
const speech = speechIn ?? under?.timeline.speech ?? [];
|
|
218
338
|
return {
|
|
219
339
|
template,
|
|
220
340
|
frames,
|
|
@@ -230,6 +350,15 @@ function readInputs(raw, ctx) {
|
|
|
230
350
|
output,
|
|
231
351
|
scale,
|
|
232
352
|
values,
|
|
353
|
+
under,
|
|
354
|
+
speech,
|
|
355
|
+
words: (0, timing_1.wordsByScene)(speech, sceneFrames, fps),
|
|
356
|
+
captionZone,
|
|
357
|
+
endCardScenes,
|
|
358
|
+
skip: raw.skip_blank === true && scenes.every(timing_1.isBlankScene),
|
|
359
|
+
media: mediaOf(raw.media, fail),
|
|
360
|
+
footage: footageOf(raw.footage, fail),
|
|
361
|
+
facts: raw.facts === true,
|
|
233
362
|
};
|
|
234
363
|
}
|
|
235
364
|
//# sourceMappingURL=inputs.js.map
|