@nebutra/cinema 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/.turbo/turbo-build.log +17 -0
- package/.turbo/turbo-test.log +14 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +36 -0
- package/LICENSE +676 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
- package/src/__tests__/cinema.test.ts +141 -0
- package/src/best-frame.ts +55 -0
- package/src/camera-tree.ts +103 -0
- package/src/director.ts +50 -0
- package/src/errors.ts +13 -0
- package/src/index.ts +38 -0
- package/src/novel-segment.ts +59 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +10 -0
package/src/director.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Film-director composition — the ViMax pipeline form, re-expressed as a
|
|
3
|
+
* thin, fully-injected orchestrator. Every stage (write script, split shots,
|
|
4
|
+
* build cameras, infer parents, render a shot) is supplied by the caller and
|
|
5
|
+
* wired to Sailor primitives (`@nebutra/agents`, `@nebutra/reel`,
|
|
6
|
+
* `@nebutra/cinema` camera tree). This module owns only the sequencing and
|
|
7
|
+
* the cross-shot continuity guarantee — no model, no I/O.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { buildCameraTree, type Camera, type CameraParent, type CameraTree } from "./camera-tree";
|
|
11
|
+
|
|
12
|
+
export interface FilmInput {
|
|
13
|
+
readonly idea: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface FilmSteps {
|
|
17
|
+
readonly writeScript: (idea: string) => Promise<string>;
|
|
18
|
+
readonly splitShots: (script: string) => Promise<readonly string[]>;
|
|
19
|
+
readonly buildCameras: (shots: readonly string[]) => Promise<readonly Camera[]>;
|
|
20
|
+
readonly inferParents: (cameras: readonly Camera[]) => Promise<readonly CameraParent[]>;
|
|
21
|
+
readonly renderShot: (shot: string, index: number) => Promise<{ uri: string }>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface FilmResult {
|
|
25
|
+
readonly script: string;
|
|
26
|
+
readonly shots: readonly string[];
|
|
27
|
+
readonly cameraTree: CameraTree;
|
|
28
|
+
readonly clips: ReadonlyArray<{ shot: string; uri: string }>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Run idea → script → shots → cameras (acyclic continuity tree) → per-shot
|
|
33
|
+
* render → assembled clip list. Shots render in narrative order so a later
|
|
34
|
+
* shot can reuse an earlier one's frames for continuity.
|
|
35
|
+
*/
|
|
36
|
+
export async function runFilmPipeline(input: FilmInput, steps: FilmSteps): Promise<FilmResult> {
|
|
37
|
+
const script = await steps.writeScript(input.idea);
|
|
38
|
+
const shots = await steps.splitShots(script);
|
|
39
|
+
const cameras = await steps.buildCameras(shots);
|
|
40
|
+
const cameraTree = await buildCameraTree(cameras, steps.inferParents);
|
|
41
|
+
|
|
42
|
+
const clips: Array<{ shot: string; uri: string }> = [];
|
|
43
|
+
for (let i = 0; i < shots.length; i++) {
|
|
44
|
+
const shot = shots[i] as string;
|
|
45
|
+
const { uri } = await steps.renderShot(shot, i);
|
|
46
|
+
clips.push({ shot, uri });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { script, shots, cameraTree, clips };
|
|
50
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CapabilityError } from "@nebutra/capability-kit";
|
|
2
|
+
|
|
3
|
+
/** Every cinema failure is a CinemaError (code + actionable suggestion). */
|
|
4
|
+
export class CinemaError extends CapabilityError {
|
|
5
|
+
constructor(message: string, init: { code: string; suggestion: string; cause?: unknown }) {
|
|
6
|
+
super(message, init, {
|
|
7
|
+
name: "CinemaError",
|
|
8
|
+
emptySuggestionFallback:
|
|
9
|
+
"No suggestion was provided. This is a bug in @nebutra/cinema — " +
|
|
10
|
+
"report it with the failing stage.",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nebutra/cinema — agentic film-production IP absorbed from a research
|
|
3
|
+
* video pipeline, re-expressed clean-room as pure, dependency-injected
|
|
4
|
+
* orchestration on top of Sailor primitives.
|
|
5
|
+
*
|
|
6
|
+
* SKIP (reused, not re-implemented): typed media DAG + script→shots split
|
|
7
|
+
* (`@nebutra/reel`), agent loop (`@nebutra/agent-runtime`), LLM + image/video
|
|
8
|
+
* generation registry (`@nebutra/agents`), acyclic guard
|
|
9
|
+
* (`@nebutra/graph-model`), errors/cli (`@nebutra/capability-kit`).
|
|
10
|
+
*
|
|
11
|
+
* PORT (this package — the differentiated IP): camera-continuity tree,
|
|
12
|
+
* consistency-ranked best-frame selection, novel→scene segmentation, and the
|
|
13
|
+
* film-director composition. Every model/IO touchpoint is injected, so this
|
|
14
|
+
* is multi-tenant-agnostic and fully unit-testable.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
type BestFrame,
|
|
19
|
+
type FrameCandidate,
|
|
20
|
+
type RankFrames,
|
|
21
|
+
selectBestFrame,
|
|
22
|
+
} from "./best-frame";
|
|
23
|
+
export {
|
|
24
|
+
buildCameraTree,
|
|
25
|
+
type Camera,
|
|
26
|
+
type CameraParent,
|
|
27
|
+
type CameraTree,
|
|
28
|
+
type InferParents,
|
|
29
|
+
resolveContinuityChain,
|
|
30
|
+
} from "./camera-tree";
|
|
31
|
+
export {
|
|
32
|
+
type FilmInput,
|
|
33
|
+
type FilmResult,
|
|
34
|
+
type FilmSteps,
|
|
35
|
+
runFilmPipeline,
|
|
36
|
+
} from "./director";
|
|
37
|
+
export { CinemaError } from "./errors";
|
|
38
|
+
export { type CompleteFn, compressNovel, extractScenes } from "./novel-segment";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Novel → compressed chunks → scene units — ViMax-distinct IP, re-expressed.
|
|
3
|
+
*
|
|
4
|
+
* Long-form adaptation: compress chapters (preserving plot/character) then
|
|
5
|
+
* segment into filmable scenes. Both steps are model calls — injected via a
|
|
6
|
+
* `CompleteFn` (the same injection shape `@nebutra/reel/storyboard` uses, so
|
|
7
|
+
* callers wire it to `@nebutra/agents` once). RAG retrieval, when used, is
|
|
8
|
+
* supplied by `@nebutra/knowledge-rag` through the same injection — not a
|
|
9
|
+
* hard dependency here.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { CinemaError } from "./errors";
|
|
13
|
+
|
|
14
|
+
/** Injected model call: prompt in, completion text out. */
|
|
15
|
+
export type CompleteFn = (prompt: string) => Promise<string>;
|
|
16
|
+
|
|
17
|
+
/** Compress each chunk independently; order is preserved. */
|
|
18
|
+
export async function compressNovel(
|
|
19
|
+
chunks: readonly string[],
|
|
20
|
+
complete: CompleteFn,
|
|
21
|
+
): Promise<string[]> {
|
|
22
|
+
return Promise.all(
|
|
23
|
+
chunks.map((chunk) =>
|
|
24
|
+
complete(
|
|
25
|
+
`Compress this passage, preserving plot, character and emotional ` +
|
|
26
|
+
`beats; output prose only:\n\n${chunk}`,
|
|
27
|
+
),
|
|
28
|
+
),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Extract ordered scene units from compressed text. The model is asked for a
|
|
34
|
+
* JSON array of scene strings; parsing tolerates surrounding prose but a
|
|
35
|
+
* non-array response is a hard error (a silent empty list would drop story).
|
|
36
|
+
*/
|
|
37
|
+
export async function extractScenes(compressed: string, complete: CompleteFn): Promise<string[]> {
|
|
38
|
+
const raw = await complete(
|
|
39
|
+
`Split the following into a JSON array of self-contained, filmable ` +
|
|
40
|
+
`scene descriptions in narrative order. Output ONLY the array:\n\n${compressed}`,
|
|
41
|
+
);
|
|
42
|
+
const match = raw.match(/\[[\s\S]*\]/);
|
|
43
|
+
if (match) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed: unknown = JSON.parse(match[0]);
|
|
46
|
+
if (Array.isArray(parsed) && parsed.every((s) => typeof s === "string")) {
|
|
47
|
+
return parsed as string[];
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// fall through to the structured error below
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw new CinemaError("Scene extraction did not return a JSON string array.", {
|
|
54
|
+
code: "CINEMA_SCENE_PARSE",
|
|
55
|
+
suggestion:
|
|
56
|
+
"Constrain the model to emit a JSON array of strings (use a schema / " +
|
|
57
|
+
"response-format), or retry — output was not parseable.",
|
|
58
|
+
});
|
|
59
|
+
}
|
package/tsconfig.json
ADDED