versioncam 0.1.2 → 0.2.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/CHANGELOG.md +196 -46
- package/README.md +486 -243
- package/bin/versioncam.js +1 -1
- package/dist/app-server.js +2 -2
- package/dist/app-server.js.map +1 -1
- package/dist/cli/commands/doctor.js +41 -4
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.js +26 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/preview.js +10 -1
- package/dist/cli/commands/preview.js.map +1 -1
- package/dist/cli/commands/record.js +3 -2
- package/dist/cli/commands/record.js.map +1 -1
- package/dist/cli/commands/render.d.ts +17 -0
- package/dist/cli/commands/render.js +79 -22
- package/dist/cli/commands/render.js.map +1 -1
- package/dist/cli/commands/stability.js +4 -4
- package/dist/cli/commands/stability.js.map +1 -1
- package/dist/cli/cwd.d.ts +46 -0
- package/dist/cli/cwd.js +102 -0
- package/dist/cli/cwd.js.map +1 -0
- package/dist/cli/main.js +11 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/usage.d.ts +10 -5
- package/dist/cli/usage.js +42 -24
- package/dist/cli/usage.js.map +1 -1
- package/dist/config.d.ts +24 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -1
- package/dist/core/camera-track.d.ts +63 -0
- package/dist/core/camera-track.js +230 -0
- package/dist/core/camera-track.js.map +1 -0
- package/dist/core/camera.d.ts +14 -1
- package/dist/core/camera.js +18 -2
- package/dist/core/camera.js.map +1 -1
- package/dist/core/follow.d.ts +129 -0
- package/dist/core/follow.js +151 -0
- package/dist/core/follow.js.map +1 -0
- package/dist/core/motion-defaults.d.ts +29 -0
- package/dist/core/motion-defaults.js +29 -0
- package/dist/core/motion-defaults.js.map +1 -1
- package/dist/core/timeline.d.ts +32 -2
- package/dist/core/timeline.js +10 -0
- package/dist/core/timeline.js.map +1 -1
- package/dist/driver/clip.d.ts +4 -1
- package/dist/driver/clip.js +9 -1
- package/dist/driver/clip.js.map +1 -1
- package/dist/driver/session.d.ts +35 -1
- package/dist/driver/session.js +81 -12
- package/dist/driver/session.js.map +1 -1
- package/dist/driver/settle.js +2 -2
- package/dist/driver/settle.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect/inspect.js +1 -1
- package/dist/loader.d.ts +7 -1
- package/dist/loader.js +23 -8
- package/dist/loader.js.map +1 -1
- package/dist/page/assets/index-fZtacWyp.js +2 -0
- package/dist/page/index.html +2 -2
- package/dist/render/encode.d.ts +12 -1
- package/dist/render/encode.js +94 -35
- package/dist/render/encode.js.map +1 -1
- package/dist/render/presentation.d.ts +12 -0
- package/dist/render/presentation.js +15 -0
- package/dist/render/presentation.js.map +1 -1
- package/dist/render/render.js +19 -2
- package/dist/render/render.js.map +1 -1
- package/dist/render/sampling.d.ts +13 -0
- package/dist/render/sampling.js +48 -20
- package/dist/render/sampling.js.map +1 -1
- package/dist/render/scenes.d.ts +16 -0
- package/dist/render/scenes.js +108 -0
- package/dist/render/scenes.js.map +1 -0
- package/dist/render/sequence.d.ts +18 -0
- package/dist/render/sequence.js +67 -13
- package/dist/render/sequence.js.map +1 -1
- package/dist/review/review.js +48 -23
- package/dist/review/review.js.map +1 -1
- package/dsl.md +267 -92
- package/package.json +2 -2
- package/plugin/README.md +58 -47
- package/plugin/agents/versioncam-reviewer.md +3 -2
- package/plugin/skills/versioncam/SKILL.md +60 -14
- package/plugin/skills/versioncam/authoring.md +6 -1
- package/plugin/skills/versioncam/onboarding.md +17 -17
- package/dist/page/assets/index-DUom5amc.js +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgBxE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AASjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE7E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAenD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EACL,YAAY,EACZ,WAAW,EACX,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,cAAc,GACf,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/inspect/inspect.js
CHANGED
|
@@ -138,7 +138,7 @@ const MAX_PRINTED_CONTROLS = 60;
|
|
|
138
138
|
/** The inventory as something a person reads in a terminal. */
|
|
139
139
|
export function formatInventory(inventory) {
|
|
140
140
|
const lines = [
|
|
141
|
-
`${inventory.title}
|
|
141
|
+
`${inventory.title} · ${inventory.url}`,
|
|
142
142
|
`viewport ${inventory.viewport.width}x${inventory.viewport.height}`,
|
|
143
143
|
"",
|
|
144
144
|
// The bracket after a name is where it came from; the second is the test
|
package/dist/loader.d.ts
CHANGED
|
@@ -24,5 +24,11 @@ export declare function loadConfig(cwd: string): Promise<ResolvedConfig>;
|
|
|
24
24
|
export declare function loadClips(config: ResolvedConfig, only?: string[]): Promise<ClipDefinition[]>;
|
|
25
25
|
/** The app's scenes, bundled for the browser. Null when it has none. */
|
|
26
26
|
export declare function bundleScenes(config: ResolvedConfig): Promise<string | null>;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Scene ids, without running a browser: needed to list and to filter.
|
|
29
|
+
*
|
|
30
|
+
* Reading them is also the check that the module exports its scenes where
|
|
31
|
+
* the render page will look for them. A module that does not is refused here,
|
|
32
|
+
* before anything renders, rather than left out of the output unremarked.
|
|
33
|
+
*/
|
|
28
34
|
export declare function loadSceneIds(config: ResolvedConfig): Promise<string[]>;
|
package/dist/loader.js
CHANGED
|
@@ -5,6 +5,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
5
5
|
import { basename, dirname, join, resolve } from "node:path";
|
|
6
6
|
import { pathToFileURL } from "node:url";
|
|
7
7
|
import { resolveConfig } from "./config.js";
|
|
8
|
+
import { readScenes } from "./render/scenes.js";
|
|
8
9
|
/**
|
|
9
10
|
* Loads the TypeScript an app writes: its config, its clips, its scenes.
|
|
10
11
|
*
|
|
@@ -123,21 +124,35 @@ export async function loadClips(config, only = []) {
|
|
|
123
124
|
? clips
|
|
124
125
|
: clips.filter((clip) => only.some((needle) => clip.id.includes(needle)));
|
|
125
126
|
}
|
|
126
|
-
/** The
|
|
127
|
-
|
|
127
|
+
/** The config's scenes module, or null when it names none. */
|
|
128
|
+
function scenesFile(config) {
|
|
128
129
|
if (!config.scenes)
|
|
129
130
|
return null;
|
|
130
131
|
if (!existsSync(config.scenes)) {
|
|
131
132
|
throw new Error(`versioncam config: \`scenes\` points at a missing file: ${config.scenes}`);
|
|
132
133
|
}
|
|
133
|
-
return
|
|
134
|
+
return config.scenes;
|
|
135
|
+
}
|
|
136
|
+
/** The app's scenes, bundled for the browser. Null when it has none. */
|
|
137
|
+
export async function bundleScenes(config) {
|
|
138
|
+
const file = scenesFile(config);
|
|
139
|
+
return file ? bundleModule(file, "browser", config.cacheDir) : null;
|
|
134
140
|
}
|
|
135
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Scene ids, without running a browser: needed to list and to filter.
|
|
143
|
+
*
|
|
144
|
+
* Reading them is also the check that the module exports its scenes where
|
|
145
|
+
* the render page will look for them. A module that does not is refused here,
|
|
146
|
+
* before anything renders, rather than left out of the output unremarked.
|
|
147
|
+
*/
|
|
136
148
|
export async function loadSceneIds(config) {
|
|
137
|
-
|
|
149
|
+
const file = scenesFile(config);
|
|
150
|
+
if (!file)
|
|
138
151
|
return [];
|
|
139
|
-
const bundle = await bundleModule(
|
|
140
|
-
const
|
|
141
|
-
|
|
152
|
+
const bundle = await bundleModule(file, "node", config.cacheDir);
|
|
153
|
+
const read = readScenes(await importFresh(bundle), file);
|
|
154
|
+
if ("error" in read)
|
|
155
|
+
throw new Error(read.error);
|
|
156
|
+
return Object.keys(read.scenes);
|
|
142
157
|
}
|
|
143
158
|
//# sourceMappingURL=loader.js.map
|
package/dist/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function e(e){let n=t(e);return n*n*n*(10+n*(-15+6*n))}function t(e){return e<=0?0:e>=1?1:e}function n(e,t,n){return e+(t-e)*n}function r(e,t,r){let i=e.x+e.w/2,a=e.y+e.h/2,o=t.x+t.w/2,s=t.y+t.h/2,c=e.w*(t.w/e.w)**+r,l=e.h*(t.h/e.h)**+r,u=n(i,o,r),d=n(a,s,r);return{x:u-c/2,y:d-l/2,w:c,h:l}}function i(e,t,r){return{x:n(e.x,t.x,r),y:n(e.y,t.y,r),w:n(e.w,t.w,r),h:n(e.h,t.h,r)}}function a(t,n){if(t.length===0)throw Error(`evalCamera called with no keys`);if(n<=t[0].t)return t[0].rect;for(let a=1;a<t.length;a+=1){let o=t[a-1],s=t[a];if(n>=s.t)continue;if(s.ease===`hold`)return o.rect;let c=s.t-o.t,l=c<=0?1:(n-o.t)/c;return s.ease===`linear`?i(o.rect,s.rect,l):r(o.rect,s.rect,e(l))}return t[t.length-1].rect}function o(e,t,n,r){let i=1-r,a=i*i,o=2*i*r,s=r*r;return[a*e[0]+o*t[0]+s*n[0],a*e[1]+o*t[1]+s*n[1]]}function s(t,n){if(t.length===0)return[0,0];let r=t[0];if(n<=r.t0)return r.kind===`move`?r.from:r.at;for(let r of t){if(n>r.t1)continue;if(r.kind===`hold`)return r.at;let t=r.t1-r.t0,i=t<=0?1:(n-r.t0)/t;return o(r.from,r.ctrl,r.to,e(i))}let i=t[t.length-1];return i.kind===`move`?i.to:i.at}function c(e,t,n){let r=n.w/t.w;return[n.x+(e[0]-t.x)*r,n.y+(e[1]-t.y)*r]}function l(e,t,n){let r=n.w/t.w;return{x:n.x+(e.x-t.x)*r,y:n.y+(e.y-t.y)*r,w:e.w*r,h:e.h*r}}function u(e,t){return{x:e.x*t,y:e.y*t,w:e.w*t,h:e.h*t}}function d(e,t,n){let r=Math.min(e.width,e.height)*n,i=e.width-r*2,a=e.height-r*2,o=t.width/t.height,s=i,c=s/o;return c>a&&(c=a,s=c*o),{x:(e.width-s)/2,y:(e.height-c)/2,w:s,h:c}}function f(e,n,r){return r<=0?1:t((e-n+1)/r)}function p(e,n,r,i){return e<=n||e>=r?0:i<=0?1:Math.min(t((e-n)/i),t((r-e)/i))}function ee(e,t){return e/t}function te(e,t,n){let r=n?.padding??0;return!n||r<=0?{x:0,y:0,w:e.width,h:e.height}:d(e,t,r)}function m(e){let t=e?.caption??`auto`;return t===`none`?`none`:t===`below`||t===`overlay`?t:(e?.padding??0)>0?`below`:`overlay`}function h(e,t){let n=e?.captionInset;return n?.right===void 0?{x:n?.left??40,align:`left`}:{x:t-n.right,align:`right`}}var g={radius:16,hairline:`rgba(255, 255, 255, 0.16)`,shadow:`rgba(0, 0, 0, 0.45)`,shadowBlur:60,shadowOffsetY:18,captionFont:`600 30px "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif`,captionColor:`rgba(255, 255, 255, 0.94)`,captionShadow:`rgba(0, 0, 0, 0.5)`,captionScrim:`rgba(0, 0, 0, 0.45)`,captionScrimHeight:86,cursorFill:`#ffffff`,cursorStroke:`rgba(0, 0, 0, 0.55)`,cursorShadow:`rgba(0, 0, 0, 0.35)`,ringWidth:3,ringRadius:10,spotlightDim:.55,spotlightTint:`6, 2, 16`};function ne(e,t){let n=/^#([0-9a-f]{6})$/i.exec(e.trim());if(!n)return e;let r=Number.parseInt(n[1],16);return`rgba(${r>>16&255}, ${r>>8&255}, ${r&255}, ${t})`}function _(e,t,n){let r=Math.min(n,t.w/2,t.h/2);e.beginPath(),e.moveTo(t.x+r,t.y),e.arcTo(t.x+t.w,t.y,t.x+t.w,t.y+t.h,r),e.arcTo(t.x+t.w,t.y+t.h,t.x,t.y+t.h,r),e.arcTo(t.x,t.y+t.h,t.x,t.y,r),e.arcTo(t.x,t.y,t.x+t.w,t.y,r),e.closePath()}function re(e,t,n){if(typeof n==`string`)e.fillStyle=n;else{let r=e.createLinearGradient(0,0,t.width,t.height);r.addColorStop(0,n[0]),r.addColorStop(1,n[1]),e.fillStyle=r}e.fillRect(0,0,t.width,t.height)}function ie(e,t,n){for(let r of e.cuts)if(t>r&&t<=r+n)return{current:e.frames[t],previous:e.frames[r],alpha:f(t,r+1,n)};return{current:e.frames[t],previous:null,alpha:1}}function v(e,t,n,r,i,a,o){let s=u(i,t.viewport.dpr),{current:c,previous:l,alpha:d}=ie(t,r,o.motion.crossfadeFrames),f=o.theme.presentation?.frame??!1;e.save(),f&&(_(e,a,g.radius),e.clip());let p=(t,r)=>{let i=n.images[t];i&&(e.globalAlpha=r,e.drawImage(i,s.x,s.y,s.w,s.h,a.x,a.y,a.w,a.h))};l!==null&&l!==c&&p(l,1),p(c,l===null?1:d),e.globalAlpha=1,e.restore()}function y(e,t){e.save(),e.shadowColor=g.shadow,e.shadowBlur=g.shadowBlur,e.shadowOffsetY=g.shadowOffsetY,e.fillStyle=`rgba(0,0,0,1)`,_(e,t,g.radius),e.fill(),e.restore()}function b(e,t){e.save(),e.strokeStyle=g.hairline,e.lineWidth=1,_(e,t,g.radius),e.stroke(),e.restore()}function x(e,t){return{x:e.x-t,y:e.y-t,w:e.w+t*2,h:e.h+t*2}}function S(e,t,n,r,i,a){let o=a.theme.presentation?.frame??!1;for(let s of t.highlights){let t=p(n,s.t0,s.t1,.25);if(t<=0)continue;let c=l(s.rect,r,i);e.save(),e.globalAlpha=t,s.style===`spotlight`?(e.save(),_(e,i,o?g.radius:0),e.clip(),e.fillStyle=`rgba(${g.spotlightTint}, ${g.spotlightDim})`,e.fillRect(i.x,i.y,i.w,i.h),e.globalCompositeOperation=`destination-out`,_(e,x(c,8),g.ringRadius),e.fill(),e.restore()):(e.strokeStyle=a.theme.accent,e.lineWidth=g.ringWidth,e.shadowColor=ne(a.theme.accent,.35),e.shadowBlur=18,_(e,x(c,6),g.ringRadius),e.stroke()),e.restore()}}function C(e,t,n){let r=!1,i=[],a=0;for(let o of e.pointer){if(o.t>t)break;if(o.kind===`down`)r=!0,a=o.t;else{r=!1;let e=t-o.t;e>=0&&e<=n.motion.rippleMs/1e3&&i.push({at:o.at,age:e})}}let o=r?t-a:0,s=r?Math.min(1,o/(n.motion.pressScaleMs/1e3)):0;return{scale:1-(1-n.motion.pressScale)*s,ripples:i}}function w(e,t,n,r,i){for(let a of t){let t=a.age/(i.motion.rippleMs/1e3),[o,s]=c(a.at,n,r);e.save(),e.globalAlpha=(1-t)*.55,e.strokeStyle=i.theme.accent,e.lineWidth=2.5,e.beginPath(),e.arc(o,s,6+t*i.motion.rippleRadius,0,Math.PI*2),e.stroke(),e.restore()}}function T(e,t,n,r){let i=r*n;e.save(),e.translate(t[0],t[1]),e.scale(i,i),e.shadowColor=g.cursorShadow,e.shadowBlur=6/i,e.shadowOffsetY=2/i,e.beginPath(),e.moveTo(0,0),e.lineTo(0,16.6),e.lineTo(4.1,12.9),e.lineTo(6.9,18.9),e.lineTo(9.6,17.6),e.lineTo(6.9,11.8),e.lineTo(12.1,11.5),e.closePath(),e.fillStyle=g.cursorFill,e.fill(),e.shadowColor=`transparent`,e.strokeStyle=g.cursorStroke,e.lineWidth=1/i,e.stroke(),e.restore()}function E(e,t,n,r,i,a){let o=m(a.theme.presentation);if(o!==`none`)for(let s of t.captions){let t=p(n,s.t0,s.t1,a.motion.captionFadeS);if(t<=0)continue;if(e.save(),e.globalAlpha=t,o===`overlay`){let t=g.captionScrimHeight,n=e.createLinearGradient(0,i.height-t,0,i.height);n.addColorStop(0,`rgba(0, 0, 0, 0)`),n.addColorStop(1,g.captionScrim),e.fillStyle=n,e.fillRect(0,i.height-t,i.width,t)}e.font=g.captionFont,e.fillStyle=g.captionColor,e.textBaseline=`middle`,e.shadowColor=g.captionShadow,e.shadowBlur=8;let c=r.x,l=(r.y+r.h+i.height)/2;if(o===`overlay`){let t=h(a.theme.presentation,i.width);e.textAlign=t.align,c=t.x,l=i.height-g.captionScrimHeight/2+4}e.fillText(s.text,c,l),e.restore()}}function ae(e,t,n,r,i,o){let l=ee(r,t.fps),u=a(t.camera,l),d=o.theme.presentation,f=te(i,t.viewport,d);d?.backdrop&&re(e,i,d.backdrop),d?.frame&&y(e,f),v(e,t,n,r,u,f,o),d?.frame&&b(e,f),S(e,t,l,u,f,o);let{scale:p,ripples:m}=C(t,l,o);w(e,m,u,f,o),T(e,c(s(t.cursor,l),u,f),p,o.theme.cursorScale),E(e,t,l,f,i,o)}var D="versioncam reads scenes from a module's default export: a Record<string, Scene> keyed by scene id, as in `export default { [intro.id]: intro }`.";function O(e){return e==null?String(e):Array.isArray(e)?`an array`:`a ${typeof e}`}function k(e){return typeof e==`string`?JSON.stringify(e):String(e)}function A(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function oe(e,t){if(!A(t))return`"${e}" is ${O(t)}, not a scene`;let{draw:n,durationFrames:r,fps:i}=t;return typeof n==`function`?!Number.isInteger(r)||r<1?`"${e}" has durationFrames ${k(r)}, where a whole number of frames, 1 or more, belongs`:typeof i!=`number`||!Number.isFinite(i)||i<=0?`"${e}" has fps ${k(i)}, where a number above 0 belongs`:null:`"${e}" has no draw(ctx, frame, canvas)`}function j(e){if(!A(e))return O(e);if(typeof e.draw==`function`)return`a single scene rather than a record of them`;for(let[t,n]of Object.entries(e)){let e=oe(t,n);if(e)return`a record in which ${e}`}return null}function M(e,t){if(e.default===void 0){let n=Object.keys(e).filter(e=>e!=="default"),r=[`${t} has no default export.`+(n.length>0?` It exports ${n.join(`, `)}.`:``),D],i=n.filter(t=>j(e[t])===null&&Object.keys(e[t]).length>0);return i.length===1&&r.push(`${i[0]} is one: add \`export default ${i[0]}\`.`),{error:r.join(`
|
|
2
|
+
`)}}let n=j(e.default);return n?{error:`${t} default-exports ${n}.\n${D}`}:{scenes:e.default}}function N(e,t,n){let r=e.map(e=>`"${e}"`).join(`, `),i=`No scene${e.length===1?``:`s`} called ${r}.`;return n===null?`${i} The config names no \`scenes\` module.`:`${i} ${n} defines ${t.length>0?t.join(`, `):`none`}.`}var P=`modulepreload`,F=function(e,t){return new URL(e,t).href},I={},L=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=F(t,n),t=s(t),t in I)return;I[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:P,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},R=new URLSearchParams(location.search),z=R.has(`recording`),B=R.get(`scene`),V=R.get(`headless`)===`1`;V&&document.body.classList.add(`headless`);var H=document.getElementById(`stage`),U=document.getElementById(`scrub`),W=document.getElementById(`status`),G=document.getElementById(`play`),K=document.getElementById(`error`);function q(e){throw K.textContent=e,Error(e)}async function J(e,t){let n=await fetch(e);return n.ok||q(`Could not load ${t} (${e}): ${n.status}`),await n.json()}async function se(e){let t=await fetch(e);return t.ok||q(`Could not load ${e}: ${t.status}`),createImageBitmap(await t.blob())}function Y(e){H.width=e.output.width,H.height=e.output.height,V?(H.style.width=`${e.output.width}px`,H.style.height=`${e.output.height}px`):(H.style.width=`min(100%, 1280px)`,H.style.height=`auto`);let t=H.getContext(`2d`,{alpha:!1});return t||q(`No 2D context`),t.imageSmoothingEnabled=!0,t.imageSmoothingQuality=`high`,t}function X(e,t,n,r){let i=!1;G.addEventListener(`click`,()=>{i=!i,G.textContent=i?`Pause`:`Play`;let a=performance.now(),o=s=>{i&&(s-a>=1e3/e&&(a=s,r((n()+1)%t)),requestAnimationFrame(o))};requestAnimationFrame(o)})}function Z(e,t,n,r){e.durationFrames=t,e.fps=n,e.setFrame=e=>new Promise(t=>{r(e),requestAnimationFrame(()=>requestAnimationFrame(()=>t()))})}async function Q(e,t){let n=await J(`/recording/timeline.json`,`the recording`),r=Y(t),i={images:await Promise.all(n.states.map(e=>se(`/recording/states/${e}`)))};await document.fonts.ready;let a=-1,o=e=>{let o=Math.max(0,Math.min(n.durationFrames-1,e));ae(r,n,i,o,H,t),a=o,V||(U.value=String(o),W.textContent=`${o+1} / ${n.durationFrames} · ${(o/n.fps).toFixed(2)}s · state ${n.frames[o]}`)};U.max=String(n.durationFrames-1),U.addEventListener(`input`,()=>o(Number(U.value))),X(n.fps,n.durationFrames,()=>a,o),o(Number(R.get(`frame`)??0)),Z(e,n.durationFrames,n.fps,o)}async function ce(e,t,n){t.scenes===null&&q(N([n],[],null));let r=M(await L(()=>import(`/scenes.js`),[],import.meta.url),t.scenes);`error`in r&&q(r.error);let i=r.scenes[n];i||q(N([n],Object.keys(r.scenes),t.scenes));let a=Y(t);await document.fonts.ready;let o=-1,s=e=>{let t=Math.max(0,Math.min(i.durationFrames-1,e));i.draw(a,t,H),o=t,V||(U.value=String(t),W.textContent=`${t+1} / ${i.durationFrames} · ${(t/i.fps).toFixed(2)}s`)};U.max=String(i.durationFrames-1),U.addEventListener(`input`,()=>s(Number(U.value))),X(i.fps,i.durationFrames,()=>o,s),s(Number(R.get(`frame`)??0)),Z(e,i.durationFrames,i.fps,s)}async function le(e){let t=await J(`/render-config.json`,`the render config`);if(B)return ce(e,t,B);if(z)return Q(e,t);q(`Pass ?recording or ?scene=<id>.`)}var $={ready:Promise.resolve(),setFrame:async()=>{},durationFrames:0,fps:60};window.__render=$,$.ready=le($);
|
package/dist/page/index.html
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
|
-
<title>versioncam
|
|
5
|
+
<title>versioncam render</title>
|
|
6
6
|
<style>*{box-sizing:border-box;margin:0;padding:0;transition:none!important;animation:none!important}body{color:#e8e2f5;background:#0b0614;flex-direction:column;align-items:center;min-height:100vh;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;display:flex}body.headless{background:0 0;display:block}#stage{display:block}body.headless #stage{position:fixed;inset:0}#controls{align-items:center;gap:14px;width:min(100%,1280px);padding:14px 18px 28px;font-size:13px;display:flex}body.headless #controls{display:none}#scrub{flex:1}#status{font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.75}button{font:inherit;color:#fff;cursor:pointer;background:#2f6fed;border:0;border-radius:6px;padding:6px 14px}#error{color:#ff9a9a;white-space:pre-wrap;padding:24px;font-family:ui-monospace,Menlo,monospace}</style>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-fZtacWyp.js"></script>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<canvas id="stage"></canvas>
|
package/dist/render/encode.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export type EncodeOptions = {
|
|
|
17
17
|
* Walk the render page frame by frame, screenshotting each one into both
|
|
18
18
|
* encoders. The page is a pure function of the frame number, so this loop can
|
|
19
19
|
* be re-run, interrupted or parallelised without changing its output.
|
|
20
|
+
*
|
|
21
|
+
* Nothing the last good render left is touched until this one has finished.
|
|
22
|
+
* The clip, its webm and its poster are written beside them, as
|
|
23
|
+
* `.partial` files, and moved over them only once both encoders have
|
|
24
|
+
* exited cleanly. Written in place, a scene that threw at frame 30 of 60
|
|
25
|
+
* left a playable half-clip, and the failed run's poster, where the last
|
|
26
|
+
* good render had been.
|
|
20
27
|
*/
|
|
21
28
|
export declare function encodeFromPage(page: Page, options: EncodeOptions): Promise<EncodeResult>;
|
|
22
29
|
/** A strip of frames, for reviewing a clip in a pull request. */
|
|
@@ -24,7 +31,11 @@ export declare function contactSheet(page: Page, options: {
|
|
|
24
31
|
id: string;
|
|
25
32
|
durationFrames: number;
|
|
26
33
|
count?: number;
|
|
27
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Exactly which frames to show. Evenly spaced when absent, which suits a
|
|
36
|
+
* scene; a recording's come from `evenFrames`, which knows where its
|
|
37
|
+
* dissolves are.
|
|
38
|
+
*/
|
|
28
39
|
frames?: number[];
|
|
29
40
|
outDir: string;
|
|
30
41
|
}): Promise<string>;
|
package/dist/render/encode.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { ffmpegPath } from "./ffmpeg.js";
|
|
5
5
|
import { uniformFrames } from "./sampling.js";
|
|
6
6
|
/**
|
|
7
|
-
* Pipe PNG frames into ffmpeg twice
|
|
7
|
+
* Pipe PNG frames into ffmpeg twice: once for H.264, once for VP9.
|
|
8
8
|
*
|
|
9
9
|
* Two encoders rather than a transcode because both come from the same
|
|
10
10
|
* lossless frames; transcoding the mp4 into a webm would compound artefacts
|
|
11
11
|
* for no saving worth having on a ten-second clip.
|
|
12
12
|
*/
|
|
13
|
-
function startEncoder(args) {
|
|
13
|
+
function startEncoder(args, what) {
|
|
14
14
|
const child = spawn(ffmpegPath(), args, {
|
|
15
15
|
stdio: ["pipe", "ignore", "pipe"],
|
|
16
16
|
});
|
|
@@ -18,17 +18,32 @@ function startEncoder(args) {
|
|
|
18
18
|
child.stderr?.on("data", (chunk) => {
|
|
19
19
|
stderr += chunk.toString();
|
|
20
20
|
});
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
// A write to an encoder that has exited fails with EPIPE. The write's own
|
|
22
|
+
// callback and `done` both report it; left unhandled here, it would crash.
|
|
23
|
+
child.stdin?.on("error", () => { });
|
|
24
|
+
const done = new Promise((resolveDone, rejectDone) => {
|
|
25
|
+
child.once("error", (error) => rejectDone(new Error(`ffmpeg could not be started (${error.message}). Install it, or add ` +
|
|
26
|
+
"the optional ffmpeg-static dependency.")));
|
|
27
|
+
child.once("close", (code, signal) => {
|
|
28
|
+
if (code === 0)
|
|
29
|
+
return resolveDone();
|
|
30
|
+
const how = signal ? `was stopped (${signal})` : `exited with ${code}`;
|
|
31
|
+
const tail = stderr.split("\n").slice(-25).join("\n");
|
|
32
|
+
rejectDone(new Error(`ffmpeg (${what}) ${how}:\n${tail}`));
|
|
33
|
+
});
|
|
30
34
|
});
|
|
31
|
-
|
|
35
|
+
// Whoever finishes the encode awaits `done`. A render stopped early only
|
|
36
|
+
// waits for it to settle, and must not leave it an unhandled rejection.
|
|
37
|
+
done.catch(() => { });
|
|
38
|
+
return { child, done };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Where `name.ext` is written until it is whole: `name.partial.ext`, the
|
|
42
|
+
* sequence's convention. ffmpeg picks its muxer from the extension, so the
|
|
43
|
+
* extension stays last.
|
|
44
|
+
*/
|
|
45
|
+
function partial(file) {
|
|
46
|
+
return file.replace(/(\.[^./]+)$/, ".partial$1");
|
|
32
47
|
}
|
|
33
48
|
function mp4Args(fps, out) {
|
|
34
49
|
return [
|
|
@@ -93,18 +108,30 @@ function writeAll(stream, buffer) {
|
|
|
93
108
|
stream.once("drain", () => resolveWrite());
|
|
94
109
|
});
|
|
95
110
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
/**
|
|
112
|
+
* One frame into one encoder. An encoder that exits early fails the write
|
|
113
|
+
* with ffmpeg's own account of why, rather than leaving it waiting on a
|
|
114
|
+
* `drain` that will never come.
|
|
115
|
+
*/
|
|
116
|
+
function feed(encoder, buffer) {
|
|
117
|
+
return Promise.race([
|
|
118
|
+
writeAll(encoder.child.stdin, buffer),
|
|
119
|
+
encoder.done.then(() => {
|
|
120
|
+
throw new Error("ffmpeg finished before the last frame was sent");
|
|
121
|
+
}),
|
|
122
|
+
]);
|
|
103
123
|
}
|
|
104
124
|
/**
|
|
105
125
|
* Walk the render page frame by frame, screenshotting each one into both
|
|
106
126
|
* encoders. The page is a pure function of the frame number, so this loop can
|
|
107
127
|
* be re-run, interrupted or parallelised without changing its output.
|
|
128
|
+
*
|
|
129
|
+
* Nothing the last good render left is touched until this one has finished.
|
|
130
|
+
* The clip, its webm and its poster are written beside them, as
|
|
131
|
+
* `.partial` files, and moved over them only once both encoders have
|
|
132
|
+
* exited cleanly. Written in place, a scene that threw at frame 30 of 60
|
|
133
|
+
* left a playable half-clip, and the failed run's poster, where the last
|
|
134
|
+
* good render had been.
|
|
108
135
|
*/
|
|
109
136
|
export async function encodeFromPage(page, options) {
|
|
110
137
|
const outDir = options.outDir;
|
|
@@ -113,21 +140,47 @@ export async function encodeFromPage(page, options) {
|
|
|
113
140
|
const webm = join(outDir, `${options.id}.webm`);
|
|
114
141
|
const poster = join(outDir, `${options.id}-poster.png`);
|
|
115
142
|
const posterFrame = options.posterFrame ?? Math.floor(options.durationFrames / 3);
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
const outputs = [mp4, webm, poster];
|
|
144
|
+
const encoders = [
|
|
145
|
+
startEncoder(mp4Args(options.fps, partial(mp4)), "H.264"),
|
|
146
|
+
startEncoder(webmArgs(options.fps, partial(webm)), "VP9"),
|
|
147
|
+
];
|
|
148
|
+
try {
|
|
149
|
+
const stage = page.locator("#stage");
|
|
150
|
+
for (let frame = 0; frame < options.durationFrames; frame += 1) {
|
|
151
|
+
await page.evaluate((n) => window.__render?.setFrame(n), frame);
|
|
152
|
+
const buffer = await stage.screenshot({
|
|
153
|
+
type: "png",
|
|
154
|
+
animations: "disabled",
|
|
155
|
+
});
|
|
156
|
+
if (frame === posterFrame)
|
|
157
|
+
writeFileSync(partial(poster), buffer);
|
|
158
|
+
for (const encoder of encoders)
|
|
159
|
+
await feed(encoder, buffer);
|
|
160
|
+
}
|
|
161
|
+
for (const encoder of encoders)
|
|
162
|
+
encoder.child.stdin?.end();
|
|
163
|
+
await Promise.all(encoders.map((encoder) => encoder.done));
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
// Both encoders are waiting for frames that will never come. Left
|
|
167
|
+
// running, they held the process open after the error was reported:
|
|
168
|
+
// `render` said what had failed and then never exited, which in CI is a
|
|
169
|
+
// job that hangs until it is killed.
|
|
170
|
+
for (const encoder of encoders) {
|
|
171
|
+
encoder.child.stdin?.destroy();
|
|
172
|
+
encoder.child.kill("SIGKILL");
|
|
173
|
+
}
|
|
174
|
+
await Promise.allSettled(encoders.map((encoder) => encoder.done));
|
|
175
|
+
for (const file of outputs)
|
|
176
|
+
rmSync(partial(file), { force: true });
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
for (const file of outputs) {
|
|
180
|
+
// No poster when the poster frame is past the end: the last one stays.
|
|
181
|
+
if (existsSync(partial(file)))
|
|
182
|
+
renameSync(partial(file), file);
|
|
129
183
|
}
|
|
130
|
-
await Promise.all([closeEncoder(h264), closeEncoder(vp9)]);
|
|
131
184
|
return { frames: options.durationFrames, mp4, webm, poster };
|
|
132
185
|
}
|
|
133
186
|
/** A strip of frames, for reviewing a clip in a pull request. */
|
|
@@ -165,19 +218,25 @@ export async function contactSheet(page, options) {
|
|
|
165
218
|
`scale=960:-1,tile=${columns}x${rows}`,
|
|
166
219
|
"-frames:v",
|
|
167
220
|
"1",
|
|
168
|
-
sheet,
|
|
221
|
+
partial(sheet),
|
|
169
222
|
], { stdio: ["ignore", "ignore", "pipe"] });
|
|
170
223
|
let stderr = "";
|
|
171
224
|
child.stderr.on("data", (c) => {
|
|
172
225
|
stderr += c.toString();
|
|
173
226
|
});
|
|
227
|
+
child.once("error", (error) => rejectSheet(new Error(`ffmpeg could not be started (${error.message})`)));
|
|
174
228
|
child.on("close", (code) => {
|
|
175
229
|
if (code === 0)
|
|
176
230
|
resolveSheet();
|
|
177
231
|
else
|
|
178
232
|
rejectSheet(new Error(`contact sheet failed:\n${stderr.slice(-800)}`));
|
|
179
233
|
});
|
|
234
|
+
}).catch((error) => {
|
|
235
|
+
rmSync(partial(sheet), { force: true });
|
|
236
|
+
throw error;
|
|
180
237
|
});
|
|
238
|
+
// Like the clip: the last good sheet stays until this one is whole.
|
|
239
|
+
renameSync(partial(sheet), sheet);
|
|
181
240
|
return sheet;
|
|
182
241
|
}
|
|
183
242
|
//# sourceMappingURL=encode.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../../src/render/encode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"encode.js","sourceRoot":"","sources":["../../src/render/encode.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,3 +17,15 @@ export declare function frameAreaFor(canvas: {
|
|
|
17
17
|
}, presentation: Presentation | null): Rect;
|
|
18
18
|
/** Whether the caption sits under the app or over it. */
|
|
19
19
|
export declare function captionPlacement(presentation: Presentation | null): "below" | "overlay" | "none";
|
|
20
|
+
/**
|
|
21
|
+
* Where an overlay caption's text is anchored, and which way it runs from
|
|
22
|
+
* there: from `x` rightwards, or leftwards to it.
|
|
23
|
+
*
|
|
24
|
+
* Measured from the video's edge rather than the app's, so the default is
|
|
25
|
+
* exactly where the caption sat before it could be moved, padded or not:
|
|
26
|
+
* 40 pixels in from the left.
|
|
27
|
+
*/
|
|
28
|
+
export declare function overlayCaptionAnchor(presentation: Presentation | null, canvasWidth: number): {
|
|
29
|
+
x: number;
|
|
30
|
+
align: "left" | "right";
|
|
31
|
+
};
|
|
@@ -24,4 +24,19 @@ export function captionPlacement(presentation) {
|
|
|
24
24
|
// "auto": there is only room below the app if something made room.
|
|
25
25
|
return (presentation?.padding ?? 0) > 0 ? "below" : "overlay";
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Where an overlay caption's text is anchored, and which way it runs from
|
|
29
|
+
* there: from `x` rightwards, or leftwards to it.
|
|
30
|
+
*
|
|
31
|
+
* Measured from the video's edge rather than the app's, so the default is
|
|
32
|
+
* exactly where the caption sat before it could be moved, padded or not:
|
|
33
|
+
* 40 pixels in from the left.
|
|
34
|
+
*/
|
|
35
|
+
export function overlayCaptionAnchor(presentation, canvasWidth) {
|
|
36
|
+
const inset = presentation?.captionInset;
|
|
37
|
+
if (inset?.right !== undefined) {
|
|
38
|
+
return { x: canvasWidth - inset.right, align: "right" };
|
|
39
|
+
}
|
|
40
|
+
return { x: inset?.left ?? 40, align: "left" };
|
|
41
|
+
}
|
|
27
42
|
//# sourceMappingURL=presentation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation.js","sourceRoot":"","sources":["../../src/render/presentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAyC,EACzC,QAA2C,EAC3C,YAAiC;IAEjC,MAAM,OAAO,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,YAAY,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAC9B,YAAiC;IAEjC,MAAM,IAAI,GAAG,YAAY,EAAE,OAAO,IAAI,MAAM,CAAC;IAC7C,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACnC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACxD,mEAAmE;IACnE,OAAO,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAChE,CAAC"}
|
|
1
|
+
{"version":3,"file":"presentation.js","sourceRoot":"","sources":["../../src/render/presentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAyC,EACzC,QAA2C,EAC3C,YAAiC;IAEjC,MAAM,OAAO,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,YAAY,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAC9B,YAAiC;IAEjC,MAAM,IAAI,GAAG,YAAY,EAAE,OAAO,IAAI,MAAM,CAAC;IAC7C,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACnC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACxD,mEAAmE;IACnE,OAAO,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAiC,EACjC,WAAmB;IAEnB,MAAM,KAAK,GAAG,YAAY,EAAE,YAAY,CAAC;IACzC,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC"}
|
package/dist/render/render.js
CHANGED
|
@@ -5,6 +5,7 @@ import { launchBrowser } from "../driver/launch.js";
|
|
|
5
5
|
import { renderConfigOf } from "../config.js";
|
|
6
6
|
import { bundleScenes } from "../loader.js";
|
|
7
7
|
import { contactSheet as encodeContactSheet, encodeFromPage, } from "./encode.js";
|
|
8
|
+
import { evenFrames } from "./sampling.js";
|
|
8
9
|
import { distPageDir, installRenderRoutes, RENDER_ORIGIN } from "./serve.js";
|
|
9
10
|
/**
|
|
10
11
|
* Open the render page at the exact output size, at DPR 1.
|
|
@@ -44,7 +45,7 @@ export async function openRenderPage(browser, config, target) {
|
|
|
44
45
|
if (!response || !response.ok()) {
|
|
45
46
|
await context.close();
|
|
46
47
|
throw new Error(`The render page did not load (${response?.status() ?? "no response"}). ` +
|
|
47
|
-
`Expected it at ${distPageDir()}
|
|
48
|
+
`Expected it at ${distPageDir()}. Run \`npm run build\` in the recorder package.`);
|
|
48
49
|
}
|
|
49
50
|
await page.waitForFunction(() => window.__render !== undefined, undefined, {
|
|
50
51
|
timeout: 30_000,
|
|
@@ -61,6 +62,21 @@ export async function openRenderPage(browser, config, target) {
|
|
|
61
62
|
}
|
|
62
63
|
return { page, ...info };
|
|
63
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The frames a sheet shows when nobody chose them.
|
|
67
|
+
*
|
|
68
|
+
* For a recording, evenly spread and moved past the renderer's dissolves,
|
|
69
|
+
* which only the recording's own cuts can say where they are. Read from its
|
|
70
|
+
* directory rather than by id, since a target can be a recording anywhere. A
|
|
71
|
+
* scene draws every frame itself and never dissolves, so its sheet is the
|
|
72
|
+
* plain even spread the encoder falls back on.
|
|
73
|
+
*/
|
|
74
|
+
async function sheetFrames(config, target, durationFrames) {
|
|
75
|
+
if (target.kind !== "recording")
|
|
76
|
+
return undefined;
|
|
77
|
+
const timeline = JSON.parse(await readFile(join(target.dir, "timeline.json"), "utf8"));
|
|
78
|
+
return evenFrames(durationFrames, undefined, config.motion.crossfadeFrames, timeline.cuts);
|
|
79
|
+
}
|
|
64
80
|
async function renderTarget(browser, config, target) {
|
|
65
81
|
const { page, durationFrames, fps } = await openRenderPage(browser, config, target);
|
|
66
82
|
try {
|
|
@@ -73,6 +89,7 @@ async function renderTarget(browser, config, target) {
|
|
|
73
89
|
const sheet = await encodeContactSheet(page, {
|
|
74
90
|
id: target.id,
|
|
75
91
|
durationFrames,
|
|
92
|
+
frames: await sheetFrames(config, target, durationFrames),
|
|
76
93
|
outDir: config.outputDir,
|
|
77
94
|
});
|
|
78
95
|
return { ...encoded, id: target.id, durationFrames, fps, sheet };
|
|
@@ -97,7 +114,7 @@ export async function contactSheet(browser, config, target, options = {}) {
|
|
|
97
114
|
return await encodeContactSheet(page, {
|
|
98
115
|
id: target.id,
|
|
99
116
|
durationFrames,
|
|
100
|
-
frames: options.frames,
|
|
117
|
+
frames: options.frames ?? (await sheetFrames(config, target, durationFrames)),
|
|
101
118
|
outDir: config.outputDir,
|
|
102
119
|
});
|
|
103
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EACL,YAAY,IAAI,kBAAkB,EAClC,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAM7E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAgB,EAChB,MAAsB,EACtB,MAAoB;IAEpB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;QACvC,QAAQ,EAAE,MAAM,CAAC,MAAM;QACvB,iBAAiB,EAAE,CAAC;QACpB,aAAa,EAAE,QAAQ;QACvB,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU;QACnC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;KAC5B,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAErC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,OAAO;YAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,MAAM,mBAAmB,CAAC,IAAI,EAAE;QAC9B,OAAO,EAAE,WAAW,EAAE;QACtB,YAAY,EAAE,cAAc,CAAC,MAAM,CAAC;QACpC,YAAY,EAAE,MAAM,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QAClE,YAAY,EAAE,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,SAAS;KACxD,CAAC,CAAC;IAEH,MAAM,KAAK,GACT,MAAM,CAAC,IAAI,KAAK,WAAW;QACzB,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,aAAa,KAAK,KAAK,aAAa,EAAE;QACxE,SAAS,EAAE,kBAAkB;KAC9B,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;QAChC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,iCAAiC,QAAQ,EAAE,MAAM,EAAE,IAAI,aAAa,KAAK;YACvE,kBAAkB,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,19 @@ import type { Mark } from "../core/timeline.js";
|
|
|
8
8
|
export declare const SHEET_TILES = 8;
|
|
9
9
|
/** Evenly spaced, ends included: the shape of the clip, knowing nothing about it. */
|
|
10
10
|
export declare function uniformFrames(durationFrames: number, count?: number): number[];
|
|
11
|
+
/**
|
|
12
|
+
* Evenly spaced, ends included, and none of them inside a dissolve: the
|
|
13
|
+
* frames a sheet shows when nobody chose them.
|
|
14
|
+
*
|
|
15
|
+
* An even spread knows nothing about the clip, so it lands wherever the
|
|
16
|
+
* spacing puts it, and after a settle that can be the renderer's crossfade: a
|
|
17
|
+
* plain sheet of the example app showed two tiles with one page ghosting
|
|
18
|
+
* through the next, which `--at-marks` had stopped doing for its own tiles
|
|
19
|
+
* long before. A tile inside a dissolve moves to the first frame after it.
|
|
20
|
+
* The first frame is never inside one, the last stays the last, and two
|
|
21
|
+
* tiles that land on the same frame are one.
|
|
22
|
+
*/
|
|
23
|
+
export declare function evenFrames(durationFrames: number, count?: number, crossfadeFrames?: number, cuts?: readonly number[]): number[];
|
|
11
24
|
/**
|
|
12
25
|
* Frame 0, every beat, and the last frame.
|
|
13
26
|
*
|