@hraness/slopcamera 3.2.7 → 3.3.1
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/NOTICE.md +1 -7
- package/PRIVACY.md +16 -7
- package/README.md +65 -20
- package/apps/desktop/README.md +14 -130
- package/apps/desktop/application/context.ts +6 -9
- package/apps/desktop/application/default-registry.ts +26 -10
- package/apps/desktop/application/operation.ts +14 -5
- package/apps/desktop/application/operations/index.ts +4 -1
- package/apps/desktop/application/operations/spatial-behavior.ts +151 -0
- package/apps/desktop/application/operations/spatial-direction.ts +222 -0
- package/apps/desktop/application/operations/spatial-rendered-audit.ts +174 -0
- package/apps/desktop/application/operations/spatial-review.ts +194 -0
- package/apps/desktop/application/operations/spatial-scene.ts +25 -1
- package/apps/desktop/application/registry.ts +2 -2
- package/apps/desktop/application/spatial-asset-admission.ts +201 -0
- package/apps/desktop/application/spatial-assets.ts +160 -31
- package/apps/desktop/application/spatial-render.ts +217 -20
- package/apps/desktop/application/spatial-rendered-audit.ts +273 -0
- package/apps/desktop/application/spatial-review.ts +361 -0
- package/apps/desktop/application/spatial-spz.ts +48 -3
- package/apps/desktop/application/spatial-world-import.ts +26 -5
- package/apps/desktop/application/spatial-world-metadata.ts +105 -0
- package/apps/desktop/cli/args.ts +528 -97
- package/apps/desktop/cli/capability-manifest.ts +374 -0
- package/apps/desktop/cli/cinema-renderer.ts +608 -0
- package/apps/desktop/cli/command-host-resources.ts +31 -2
- package/apps/desktop/cli/commands.ts +1215 -70
- package/apps/desktop/cli/gateway-review-provider.ts +334 -0
- package/apps/desktop/cli/help.ts +183 -23
- package/apps/desktop/cli/html-scene.ts +139 -28
- package/apps/desktop/cli/io.ts +2 -1
- package/apps/desktop/cli/main.ts +26 -50
- package/apps/desktop/cli/menubar.ts +244 -0
- package/apps/desktop/cli/music-analysis-service.ts +61 -0
- package/apps/desktop/cli/portable-surface.ts +18 -5
- package/apps/desktop/cli/spatial-asset-service.ts +124 -0
- package/apps/desktop/cli/spatial-design-service.ts +237 -0
- package/apps/desktop/cli/spatial-generate-service.ts +370 -0
- package/apps/desktop/cli/spatial-scene-service.ts +268 -4
- package/apps/desktop/cli/workflow-code.ts +1 -1
- package/apps/desktop/code/application-node-planner.ts +0 -47
- package/apps/desktop/code/host-source-layout.ts +46 -0
- package/apps/desktop/code/public.ts +0 -1
- package/apps/desktop/code/runtime-identity.ts +50 -8
- package/apps/desktop/code/semantic-builder.ts +24 -43
- package/apps/desktop/code/source-bundle.ts +23 -6
- package/apps/desktop/code/source-typecheck.ts +131 -43
- package/apps/desktop/code/worker-client.ts +14 -2
- package/apps/desktop/contracts/cinema.ts +736 -0
- package/apps/desktop/contracts/index.ts +1 -0
- package/apps/desktop/contracts/spatial-asset.ts +16 -0
- package/apps/desktop/contracts/spatial-world.ts +26 -3
- package/apps/desktop/core/cinema-plan.ts +1320 -0
- package/apps/desktop/core/index.ts +1 -0
- package/apps/desktop/core/music-analysis.ts +124 -1
- package/apps/desktop/core/storage.ts +75 -0
- package/apps/desktop/dist/cli/main.js +737 -291
- package/apps/desktop/html-overlay/audio-reactivity.ts +127 -0
- package/apps/desktop/html-overlay/index.ts +9 -0
- package/apps/desktop/html-overlay/runtime.ts +2 -0
- package/apps/desktop/html-overlay/scene.ts +29 -0
- package/apps/desktop/html-overlay/spatial.ts +619 -57
- package/apps/desktop/workflows/cinematic-world.ts +127 -0
- package/apps/desktop/workflows/index.ts +6 -0
- package/dist/cli.js +32 -434
- package/dist/code/advanced.js +1 -269
- package/dist/code/index.js +16 -2239
- package/dist/generate.js +1 -23
- package/dist/host-resources.js +1 -25
- package/dist/index-0bbesn4m.js +35 -0
- package/dist/index-296qhpf2.js +3 -0
- package/dist/index-3cxf2c58.js +3 -0
- package/dist/index-77fjfg9f.js +3 -0
- package/dist/index-80a36bc0.js +3 -0
- package/dist/index-a3vvs3rf.js +3 -0
- package/dist/index-b3geqczn.js +5 -0
- package/dist/index-cgpkyjqa.js +3 -0
- package/dist/index-f0ptv8dm.js +7 -0
- package/dist/index-grk47tgs.js +3 -0
- package/dist/index-qry58nj2.js +10 -0
- package/dist/index.js +1 -240
- package/dist/operations.js +1 -28
- package/dist/skill-install-gx7afxsd.js +2 -0
- package/dist/vectorize/worker.js +1 -141
- package/dist/workflow.js +1 -15
- package/docs/README.md +5 -1
- package/docs/studio.md +1 -1
- package/package.json +11 -24
- package/skills/slopcamera/SKILL.md +14 -1
- package/skills/slopcamera/references/diagrams.md +5 -14
- package/skills/slopcamera/references/directed-scenes.md +22 -2
- package/skills/slopcamera/references/gateway-media.md +15 -1
- package/skills/slopcamera/references/image-galleries.md +166 -0
- package/skills/slopcamera/references/install.md +2 -2
- package/skills/slopcamera/references/music-video.md +23 -3
- package/skills/slopcamera/references/parametric-design.md +14 -0
- package/skills/slopcamera/references/reference-led-3d.md +1 -1
- package/skills/slopcamera/references/scene-building.md +32 -0
- package/skills/slopcamera/references/social-collage-banners.md +204 -0
- package/skills/slopcamera/references/support.md +25 -0
- package/skills/slopcamera/references/web-media-excerpts.md +120 -0
- package/skills/slopcamera/references/workflows-sdk.md +3 -3
- package/skills/slopcamera/scripts/compose-social-collage-banner.ts +728 -0
- package/src/capability-manifest.ts +361 -0
- package/src/cli.ts +250 -102
- package/src/code/canonical-json.ts +9 -0
- package/src/generate.ts +2 -2
- package/src/icon.ts +1111 -0
- package/src/image-gallery.ts +1049 -0
- package/src/index.ts +11 -23
- package/src/mcp/boundary.ts +11 -11
- package/src/mcp/index.ts +3 -0
- package/src/mcp/server.ts +2 -1
- package/src/mcp/tools.ts +1642 -130
- package/src/mcp/types.ts +19 -0
- package/src/operations.ts +412 -0
- package/src/portable-capability-manifest.ts +67 -0
- package/src/scene-gallery.ts +184 -0
- package/src/spatial-scene/asset-admission.ts +183 -0
- package/src/spatial-scene/audit-rendered.ts +692 -0
- package/src/spatial-scene/audit.ts +617 -0
- package/src/spatial-scene/behavior-audit.ts +252 -0
- package/src/spatial-scene/behavior-bake.ts +233 -0
- package/src/spatial-scene/behavior-fns.ts +585 -0
- package/src/spatial-scene/behavior-gallery.ts +109 -0
- package/src/spatial-scene/behavior-stdlib.ts +323 -0
- package/src/spatial-scene/behavior-trace.ts +325 -0
- package/src/spatial-scene/behavior.ts +618 -0
- package/src/spatial-scene/build.ts +632 -0
- package/src/spatial-scene/camera-rig.ts +125 -0
- package/src/spatial-scene/camera-track.ts +4 -2
- package/src/spatial-scene/character.ts +124 -0
- package/src/spatial-scene/contracts.ts +75 -7
- package/src/spatial-scene/design-templates.ts +351 -0
- package/src/spatial-scene/design.ts +327 -0
- package/src/spatial-scene/direction-compile.ts +584 -0
- package/src/spatial-scene/direction.ts +122 -0
- package/src/spatial-scene/effects.ts +149 -0
- package/src/spatial-scene/evaluate.ts +109 -29
- package/src/spatial-scene/gallery.ts +277 -0
- package/src/spatial-scene/generate.ts +239 -0
- package/src/spatial-scene/geometry-native.ts +165 -0
- package/src/spatial-scene/geometry.ts +1216 -0
- package/src/spatial-scene/gltf.ts +600 -77
- package/src/spatial-scene/identity.ts +55 -2
- package/src/spatial-scene/index.ts +36 -0
- package/src/spatial-scene/inspect.ts +35 -6
- package/src/spatial-scene/material-lighting.ts +609 -0
- package/src/spatial-scene/math.ts +21 -4
- package/src/spatial-scene/motion-evidence.ts +88 -0
- package/src/spatial-scene/parametric.ts +765 -0
- package/src/spatial-scene/particle-preparation.ts +183 -0
- package/src/spatial-scene/particle.ts +195 -0
- package/src/spatial-scene/patch.ts +109 -15
- package/src/spatial-scene/performance.ts +1613 -0
- package/src/spatial-scene/probe.ts +137 -0
- package/src/spatial-scene/recipe-pack.ts +86 -0
- package/src/spatial-scene/render-effects.ts +240 -0
- package/src/spatial-scene/review.ts +639 -0
- package/src/spatial-scene/simulation-bake.ts +206 -0
- package/src/spatial-scene/simulation.ts +187 -0
- package/src/spatial-scene/solve.ts +366 -0
- package/src/spatial-scene/temporal-audit.ts +354 -0
- package/src/support-completion.ts +16 -0
- package/src/support.ts +42 -0
- package/src/version.ts +1 -1
- package/apps/desktop/application/operations/recording/index.ts +0 -5
- package/apps/desktop/application/operations/recording/pause.ts +0 -34
- package/apps/desktop/application/operations/recording/resume.ts +0 -34
- package/apps/desktop/application/operations/recording/shared.ts +0 -199
- package/apps/desktop/application/operations/recording/start.ts +0 -169
- package/apps/desktop/application/operations/recording/stop.ts +0 -34
- package/apps/desktop/capture/protocol.ts +0 -689
- package/apps/desktop/cli/capture-bundle.ts +0 -1117
- package/apps/desktop/cli/recording-controller.ts +0 -1567
- package/apps/desktop/cli/recording-daemon.ts +0 -931
- package/dist/index-42zsesc1.js +0 -2257
- package/dist/index-7308egqr.js +0 -48
- package/dist/index-8txs6fkn.js +0 -712
- package/dist/index-mcy8z0br.js +0 -192
- package/dist/index-p63wavx0.js +0 -2029
- package/dist/index-r7gdhmsp.js +0 -424
- package/dist/index-rvghzt7b.js +0 -2013
- package/dist/index-sh6xbav6.js +0 -1219
- package/dist/index-z1w83f81.js +0 -4
- package/dist/index-z7239b4h.js +0 -2363
- package/dist/skill-install-dh0ntqba.js +0 -10
- package/src/desktop.ts +0 -253
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import type { AudioReactivityBands } from "../core/music-analysis";
|
|
4
|
+
|
|
5
|
+
export const HTML_AUDIO_REACTIVITY_RESOURCE_NAME = "audio-reactivity";
|
|
6
|
+
export const HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH = "slopcamera/audio-reactivity.json";
|
|
7
|
+
export const HTML_AUDIO_REACTIVITY_MAX_BYTES = 4 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
export type HtmlOverlayAudioReactivity = Readonly<{
|
|
10
|
+
kind: "slopcamera.audio-reactivity";
|
|
11
|
+
schemaVersion: 1;
|
|
12
|
+
profile: "bands-v1";
|
|
13
|
+
source: Readonly<{ bytes: number; sha256: string }>;
|
|
14
|
+
pcm: Readonly<{ sampleCount: number; sha256: string }>;
|
|
15
|
+
durationUs: number;
|
|
16
|
+
rateHz: 60;
|
|
17
|
+
bands: AudioReactivityBands;
|
|
18
|
+
}>;
|
|
19
|
+
|
|
20
|
+
export type HtmlOverlayAudioSample = Readonly<{
|
|
21
|
+
bass: number; mid: number; treble: number; energy: number;
|
|
22
|
+
}>;
|
|
23
|
+
export type PreparedHtmlOverlayAudioReactivity = Readonly<{
|
|
24
|
+
durationUs: number;
|
|
25
|
+
sample: (timeUs: number) => HtmlOverlayAudioSample;
|
|
26
|
+
}>;
|
|
27
|
+
|
|
28
|
+
const LIMITS = Object.freeze({ maxSamples: 14_400_000, maxSourceBytes: 512 * 1024 * 1024,
|
|
29
|
+
sampleRateHz: 24_000, hopSize: 400, rateHz: 60, maxTimeUs: 3_600_000_000 });
|
|
30
|
+
|
|
31
|
+
/** One implementation validates the SDK and browser resource, before frame sampling. */
|
|
32
|
+
function createAudioReactivityRuntime(limits: typeof LIMITS) {
|
|
33
|
+
const object = (value: unknown, keys: readonly string[]): Record<string, unknown> => {
|
|
34
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
35
|
+
throw new TypeError("Audio reactivity requires a strict JSON object.");
|
|
36
|
+
}
|
|
37
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
38
|
+
if (Reflect.ownKeys(value).length !== keys.length
|
|
39
|
+
|| keys.some(key => descriptors[key] === undefined || !("value" in descriptors[key]!))) {
|
|
40
|
+
throw new TypeError("Audio reactivity contains missing, unknown, or accessor fields.");
|
|
41
|
+
}
|
|
42
|
+
return value as Record<string, unknown>;
|
|
43
|
+
};
|
|
44
|
+
const integer = (value: unknown, minimum: number, maximum: number): number => {
|
|
45
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
46
|
+
throw new RangeError("Audio reactivity integer is outside its bound.");
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
};
|
|
50
|
+
const digest = (value: unknown): string => {
|
|
51
|
+
if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) {
|
|
52
|
+
throw new TypeError("Audio reactivity requires a SHA-256 digest.");
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
};
|
|
56
|
+
const parse = (value: unknown): HtmlOverlayAudioReactivity => {
|
|
57
|
+
const input = object(value, ["kind", "schemaVersion", "profile", "source", "pcm", "durationUs", "rateHz", "bands"]);
|
|
58
|
+
if (input.kind !== "slopcamera.audio-reactivity" || input.schemaVersion !== 1
|
|
59
|
+
|| input.profile !== "bands-v1" || input.rateHz !== limits.rateHz) {
|
|
60
|
+
throw new TypeError("Unsupported audio reactivity profile or version.");
|
|
61
|
+
}
|
|
62
|
+
const source = object(input.source, ["bytes", "sha256"]);
|
|
63
|
+
const pcm = object(input.pcm, ["sampleCount", "sha256"]);
|
|
64
|
+
const sampleCount = integer(pcm.sampleCount, 1, limits.maxSamples);
|
|
65
|
+
const durationUs = Math.round(sampleCount * 1_000_000 / limits.sampleRateHz);
|
|
66
|
+
if (input.durationUs !== durationUs) throw new RangeError("Audio reactivity duration differs from its PCM sample count.");
|
|
67
|
+
const length = Math.ceil(sampleCount / limits.hopSize) + 1;
|
|
68
|
+
const bands = object(input.bands, ["bass", "mid", "treble", "energy"]);
|
|
69
|
+
const channel = (value: unknown): readonly number[] => {
|
|
70
|
+
const keys = value !== null && typeof value === "object" ? Reflect.ownKeys(value) : [];
|
|
71
|
+
if (!Array.isArray(value) || value.length !== length || keys.length !== length + 1
|
|
72
|
+
|| keys.some(key => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key)))) {
|
|
73
|
+
throw new RangeError("Audio reactivity bands must be dense arrays covering the declared duration.");
|
|
74
|
+
}
|
|
75
|
+
const copy: number[] = [];
|
|
76
|
+
for (let index = 0; index < length; index++) {
|
|
77
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
78
|
+
const sample: unknown = descriptor !== undefined && "value" in descriptor ? descriptor.value : undefined;
|
|
79
|
+
if (typeof sample !== "number" || !Number.isFinite(sample) || sample < 0 || sample > 1) {
|
|
80
|
+
throw new RangeError("Audio reactivity band values must be finite numbers in [0, 1].");
|
|
81
|
+
}
|
|
82
|
+
copy.push(sample === 0 ? 0 : sample);
|
|
83
|
+
}
|
|
84
|
+
if (copy.at(-1) !== 0) throw new RangeError("Audio reactivity requires a zero endpoint at the exact duration.");
|
|
85
|
+
return Object.freeze(copy);
|
|
86
|
+
};
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
kind: "slopcamera.audio-reactivity", schemaVersion: 1, profile: "bands-v1", rateHz: 60, durationUs,
|
|
89
|
+
source: Object.freeze({ bytes: integer(source.bytes, 1, limits.maxSourceBytes), sha256: digest(source.sha256) }),
|
|
90
|
+
pcm: Object.freeze({ sampleCount, sha256: digest(pcm.sha256) }),
|
|
91
|
+
bands: Object.freeze({ bass: channel(bands.bass), mid: channel(bands.mid), treble: channel(bands.treble), energy: channel(bands.energy) }),
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
const prepareAudioReactivity = (input: unknown): PreparedHtmlOverlayAudioReactivity => {
|
|
95
|
+
const data = parse(input);
|
|
96
|
+
const zero = Object.freeze({ bass: 0, mid: 0, treble: 0, energy: 0 });
|
|
97
|
+
return Object.freeze({ durationUs: data.durationUs, sample(timeUs: number): HtmlOverlayAudioSample {
|
|
98
|
+
integer(timeUs, -limits.maxTimeUs, limits.maxTimeUs);
|
|
99
|
+
if (timeUs < 0 || timeUs >= data.durationUs) return zero;
|
|
100
|
+
const index = Math.min(data.bands.bass.length - 2, Math.floor(timeUs * limits.rateHz / 1_000_000));
|
|
101
|
+
const startUs = index * 1_000_000 / limits.rateHz;
|
|
102
|
+
const endUs = Math.min(data.durationUs, (index + 1) * 1_000_000 / limits.rateHz);
|
|
103
|
+
const mix = Math.max(0, Math.min(1, (timeUs - startUs) / (endUs - startUs)));
|
|
104
|
+
const interpolate = (band: readonly number[]) => band[index]! * (1 - mix) + band[index + 1]! * mix;
|
|
105
|
+
return Object.freeze({ bass: interpolate(data.bands.bass), mid: interpolate(data.bands.mid),
|
|
106
|
+
treble: interpolate(data.bands.treble), energy: interpolate(data.bands.energy) });
|
|
107
|
+
} });
|
|
108
|
+
};
|
|
109
|
+
return { parse, prepareAudioReactivity };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const runtime = createAudioReactivityRuntime(LIMITS);
|
|
113
|
+
export const HtmlOverlayAudioReactivitySchema = z.unknown().transform((input, context): HtmlOverlayAudioReactivity => {
|
|
114
|
+
try { return runtime.parse(input); } catch (error) {
|
|
115
|
+
context.addIssue({ code: "custom", message: error instanceof Error ? error.message : "Invalid audio reactivity." });
|
|
116
|
+
return z.NEVER;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/** Validate and copy once. Samples depend only on absolute source time, in integer microseconds. */
|
|
121
|
+
export function prepareHtmlOverlayAudioReactivity(input: unknown): PreparedHtmlOverlayAudioReactivity {
|
|
122
|
+
return runtime.prepareAudioReactivity(input);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function createHtmlOverlayAudioReactivityRuntimeSource(): string {
|
|
126
|
+
return `(() => { const runtime = (${createAudioReactivityRuntime.toString()})(${JSON.stringify(LIMITS)}); return { prepareAudioReactivity: runtime.prepareAudioReactivity }; })()`;
|
|
127
|
+
}
|
|
@@ -33,6 +33,15 @@ export {
|
|
|
33
33
|
type HtmlOverlayTiming,
|
|
34
34
|
} from "./contracts";
|
|
35
35
|
export { HtmlSceneInputSchema, type HtmlSceneInput } from "./scene";
|
|
36
|
+
export {
|
|
37
|
+
HTML_AUDIO_REACTIVITY_RESOURCE_NAME,
|
|
38
|
+
HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH,
|
|
39
|
+
HtmlOverlayAudioReactivitySchema,
|
|
40
|
+
prepareHtmlOverlayAudioReactivity,
|
|
41
|
+
type HtmlOverlayAudioReactivity,
|
|
42
|
+
type HtmlOverlayAudioSample,
|
|
43
|
+
type PreparedHtmlOverlayAudioReactivity,
|
|
44
|
+
} from "./audio-reactivity";
|
|
36
45
|
export {
|
|
37
46
|
HTML_OVERLAY_SCAFFOLD_CLOCK_INTEGRATIONS,
|
|
38
47
|
HTML_OVERLAY_SCAFFOLD_KINDS,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { htmlOverlayAssetLocalUrl } from "./libraries";
|
|
12
12
|
import { HTML_OVERLAY_RANDOM_ALGORITHM } from "./random";
|
|
13
13
|
import { createHtmlOverlayMusicClockRuntimeSource } from "./music-clock";
|
|
14
|
+
import { createHtmlOverlayAudioReactivityRuntimeSource } from "./audio-reactivity";
|
|
14
15
|
|
|
15
16
|
const Uint32Schema = z.number().int().safe().min(0).max(0xffff_ffff);
|
|
16
17
|
|
|
@@ -713,6 +714,7 @@ export function createHtmlOverlayBrowserRuntimeSource(
|
|
|
713
714
|
};
|
|
714
715
|
},
|
|
715
716
|
...${createHtmlOverlayMusicClockRuntimeSource()},
|
|
717
|
+
...${createHtmlOverlayAudioReactivityRuntimeSource()},
|
|
716
718
|
parameters: deepFreeze(config.parameters),
|
|
717
719
|
random,
|
|
718
720
|
randomFor,
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
} from "./contracts";
|
|
7
7
|
import { HtmlOverlayExecutionProfileSchema } from "./execution-profile";
|
|
8
8
|
import { HtmlOverlayLibrarySelectionSchema } from "./libraries";
|
|
9
|
+
import { AUDIO_REACTIVITY_MAX_DURATION_US } from "../core/music-analysis";
|
|
10
|
+
import { HTML_AUDIO_REACTIVITY_RESOURCE_NAME, HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH } from "./audio-reactivity";
|
|
9
11
|
|
|
10
12
|
const ResourceSchema = HtmlOverlayDeclaredResourceSchema.omit({ bytes: true, sha256: true }).extend({
|
|
11
13
|
path: RepositoryRelativePathSchema,
|
|
@@ -27,8 +29,35 @@ export const HtmlSceneInputSchema = z.strictObject({
|
|
|
27
29
|
executionProfile: HtmlOverlayExecutionProfileSchema.optional(),
|
|
28
30
|
audio: z.strictObject({
|
|
29
31
|
path: z.string().min(1).max(4096).refine(path => !/[\0\r\n]/u.test(path), "Audio path contains a control character."),
|
|
32
|
+
reactivity: z.strictObject({
|
|
33
|
+
profile: z.literal("bands-v1"),
|
|
34
|
+
resource: z.literal(HTML_AUDIO_REACTIVITY_RESOURCE_NAME).optional(),
|
|
35
|
+
sha256: z.string().regex(/^[a-f0-9]{64}$/u).optional(),
|
|
36
|
+
}).optional(),
|
|
30
37
|
}).optional(),
|
|
31
38
|
}).superRefine((input, context) => {
|
|
39
|
+
if (input.audio?.reactivity !== undefined && input.timing.durationUs > AUDIO_REACTIVITY_MAX_DURATION_US) {
|
|
40
|
+
context.addIssue({ code: "custom", message: "Audio reactivity is limited to ten minutes per scene." });
|
|
41
|
+
}
|
|
42
|
+
if (input.audio?.reactivity?.resource === undefined && input.audio?.reactivity !== undefined && (
|
|
43
|
+
input.resources.length >= 64
|
|
44
|
+
|| input.resources.some(resource => resource.name === HTML_AUDIO_REACTIVITY_RESOURCE_NAME
|
|
45
|
+
|| resource.urlPath === HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH)
|
|
46
|
+
)) {
|
|
47
|
+
context.addIssue({ code: "custom", message: "Audio reactivity requires one free resource slot and an unused audio-reactivity name and slopcamera/audio-reactivity.json URL path." });
|
|
48
|
+
}
|
|
49
|
+
if (input.audio?.reactivity?.resource === undefined && input.audio?.reactivity?.sha256 !== undefined) {
|
|
50
|
+
context.addIssue({ code: "custom", message: "An audio-reactivity sidecar digest requires its retained resource name." });
|
|
51
|
+
}
|
|
52
|
+
if (input.audio?.reactivity?.resource !== undefined && !input.resources.some(resource => (
|
|
53
|
+
resource.name === HTML_AUDIO_REACTIVITY_RESOURCE_NAME && resource.urlPath === HTML_AUDIO_REACTIVITY_RESOURCE_URL_PATH
|
|
54
|
+
&& resource.mediaType === "application/json" && resource.transport === "fetch"
|
|
55
|
+
))) {
|
|
56
|
+
context.addIssue({ code: "custom", message: "A retained audio-reactivity source must declare its fixed fetched JSON resource." });
|
|
57
|
+
}
|
|
58
|
+
if (input.audio?.reactivity?.resource !== undefined && input.audio.reactivity.sha256 === undefined) {
|
|
59
|
+
context.addIssue({ code: "custom", message: "A retained audio-reactivity source must record its sidecar SHA-256 digest." });
|
|
60
|
+
}
|
|
32
61
|
const authoring = HtmlOverlayAuthoringInputSchema.safeParse({
|
|
33
62
|
kind: "slopcamera.html-overlay", schemaVersion: 1, canvas: input.canvas, timing: input.timing,
|
|
34
63
|
libraries: input.libraries, parameters: input.parameters, seed: input.seed,
|