mulmocast 2.9.0 → 2.9.2
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/lib/actions/movie.d.ts +2 -9
- package/lib/actions/movie.js +149 -147
- package/lib/agents/image_google_agent.d.ts +5 -0
- package/lib/agents/image_google_agent.js +86 -0
- package/lib/agents/movie_google_agent.d.ts +9 -0
- package/lib/agents/movie_google_agent.js +122 -0
- package/lib/agents/mulmo_prompts_agent.d.ts +7 -0
- package/lib/agents/mulmo_prompts_agent.js +37 -0
- package/lib/agents/prompts_data.d.ts +15 -0
- package/lib/agents/prompts_data.js +16 -0
- package/lib/agents/tts_nijivoice_agent.d.ts +5 -0
- package/lib/agents/tts_nijivoice_agent.js +69 -0
- package/lib/agents/validate_mulmo_script_agent.d.ts +17 -0
- package/lib/agents/validate_mulmo_script_agent.js +34 -0
- package/lib/cli/args.d.ts +15 -0
- package/lib/cli/args.js +62 -0
- package/lib/cli/bin.js +0 -0
- package/lib/cli/cli.d.ts +17 -0
- package/lib/cli/cli.js +117 -0
- package/lib/cli/run.d.ts +2 -0
- package/lib/cli/run.js +3 -0
- package/lib/cli/tool-args.d.ts +16 -0
- package/lib/cli/tool-args.js +64 -0
- package/lib/cli/tool-cli.d.ts +2 -0
- package/lib/cli/tool-cli.js +32 -0
- package/lib/cli/tool_commands/prompt/builder.d.ts +4 -0
- package/lib/cli/tool_commands/prompt/builder.js +11 -0
- package/lib/cli/tool_commands/prompt/handler.d.ts +4 -0
- package/lib/cli/tool_commands/prompt/handler.js +14 -0
- package/lib/cli/tool_commands/prompt/index.d.ts +4 -0
- package/lib/cli/tool_commands/prompt/index.js +4 -0
- package/lib/cli/tool_commands/schema/builder.d.ts +2 -0
- package/lib/cli/tool_commands/schema/builder.js +3 -0
- package/lib/cli/tool_commands/schema/handler.d.ts +2 -0
- package/lib/cli/tool_commands/schema/handler.js +12 -0
- package/lib/cli/tool_commands/schema/index.d.ts +4 -0
- package/lib/cli/tool_commands/schema/index.js +4 -0
- package/lib/cli/tool_commands/scripting/builder.d.ts +20 -0
- package/lib/cli/tool_commands/scripting/builder.js +63 -0
- package/lib/cli/tool_commands/scripting/handler.d.ts +12 -0
- package/lib/cli/tool_commands/scripting/handler.js +36 -0
- package/lib/cli/tool_commands/scripting/index.d.ts +4 -0
- package/lib/cli/tool_commands/scripting/index.js +4 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +8 -0
- package/lib/methods/mulmo_beat.d.ts +3 -0
- package/lib/methods/mulmo_beat.js +17 -0
- package/lib/methods/mulmo_beat_node.d.ts +3 -0
- package/lib/methods/mulmo_presentation_style.d.ts +5 -1
- package/lib/methods/mulmo_presentation_style.js +43 -1
- package/lib/methods/mulmo_script_template.d.ts +4 -0
- package/lib/methods/mulmo_script_template.js +6 -0
- package/lib/methods/mulmo_studio.d.ts +8 -0
- package/lib/methods/mulmo_studio.js +25 -0
- package/lib/methods/mulmo_studio_context.d.ts +6 -1
- package/lib/methods/mulmo_studio_context.js +36 -1
- package/lib/slide/blocks.d.ts +13 -0
- package/lib/slide/blocks.js +251 -0
- package/lib/slide/index.d.ts +6 -0
- package/lib/slide/index.js +7 -0
- package/lib/slide/layouts/big_quote.d.ts +2 -0
- package/lib/slide/layouts/big_quote.js +19 -0
- package/lib/slide/layouts/columns.d.ts +2 -0
- package/lib/slide/layouts/columns.js +53 -0
- package/lib/slide/layouts/comparison.d.ts +2 -0
- package/lib/slide/layouts/comparison.js +25 -0
- package/lib/slide/layouts/funnel.d.ts +2 -0
- package/lib/slide/layouts/funnel.js +25 -0
- package/lib/slide/layouts/grid.d.ts +2 -0
- package/lib/slide/layouts/grid.js +38 -0
- package/lib/slide/layouts/index.d.ts +3 -0
- package/lib/slide/layouts/index.js +46 -0
- package/lib/slide/layouts/matrix.d.ts +2 -0
- package/lib/slide/layouts/matrix.js +53 -0
- package/lib/slide/layouts/split.d.ts +2 -0
- package/lib/slide/layouts/split.js +51 -0
- package/lib/slide/layouts/stats.d.ts +2 -0
- package/lib/slide/layouts/stats.js +23 -0
- package/lib/slide/layouts/table.d.ts +2 -0
- package/lib/slide/layouts/table.js +10 -0
- package/lib/slide/layouts/timeline.d.ts +2 -0
- package/lib/slide/layouts/timeline.js +27 -0
- package/lib/slide/layouts/title.d.ts +2 -0
- package/lib/slide/layouts/title.js +19 -0
- package/lib/slide/layouts/waterfall.d.ts +2 -0
- package/lib/slide/layouts/waterfall.js +63 -0
- package/lib/slide/render.d.ts +17 -0
- package/lib/slide/render.js +101 -0
- package/lib/slide/schema.d.ts +9309 -0
- package/lib/slide/schema.js +434 -0
- package/lib/slide/utils.d.ts +76 -0
- package/lib/slide/utils.js +243 -0
- package/lib/tools/prompt.d.ts +1 -0
- package/lib/tools/prompt.js +18 -0
- package/lib/tools/seed.d.ts +3 -0
- package/lib/tools/seed.js +211 -0
- package/lib/tools/seed_from_url.d.ts +3 -0
- package/lib/tools/seed_from_url.js +188 -0
- package/lib/types/slide.d.ts +9309 -0
- package/lib/types/slide.js +434 -0
- package/lib/utils/assets.d.ts +18 -0
- package/lib/utils/assets.js +101 -0
- package/lib/utils/cli.d.ts +1 -0
- package/lib/utils/cli.js +15 -0
- package/lib/utils/const.d.ts +12 -0
- package/lib/utils/const.js +12 -0
- package/lib/utils/filters.js +11 -2
- package/lib/utils/image_preprocess.d.ts +14 -0
- package/lib/utils/image_preprocess.js +52 -0
- package/lib/utils/markdown.d.ts +3 -0
- package/lib/utils/markdown.js +42 -0
- package/lib/utils/pdf.d.ts +9 -0
- package/lib/utils/pdf.js +77 -0
- package/lib/utils/preprocess.d.ts +343 -0
- package/lib/utils/preprocess.js +58 -0
- package/lib/utils/provider2agent.d.ts +141 -0
- package/lib/utils/provider2agent.js +267 -0
- package/lib/utils/text_hash.d.ts +1 -0
- package/lib/utils/text_hash.js +4 -0
- package/package.json +8 -8
- package/scripts/test/test_voice_over_transition_no_audio.json +71 -0
- package/lib/utils/sdk_timeout.d.ts +0 -3
- package/lib/utils/sdk_timeout.js +0 -7
- package/scripts/test/test_captions_2.json~ +0 -278
- package/scripts/test/test_en_portrait.json~ +0 -31
- package/scripts/test/test_slide_image_ref_gemini_en.json~ +0 -289
- package/scripts/test/test_vertexai.json~ +0 -21
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import yargs from "yargs";
|
|
2
|
+
import { hideBin } from "yargs/helpers";
|
|
3
|
+
import { commonOptions } from "./common.js";
|
|
4
|
+
import { getAvailableTemplates } from "../utils/file.js";
|
|
5
|
+
import { llmAgents } from "../utils/utils.js";
|
|
6
|
+
const availableTemplateNames = getAvailableTemplates().map((template) => template.filename);
|
|
7
|
+
export const args = commonOptions(yargs(hideBin(process.argv)))
|
|
8
|
+
.scriptName("mulmo-tool")
|
|
9
|
+
.option("u", {
|
|
10
|
+
alias: "url",
|
|
11
|
+
description: "URLs to reference (required when not in interactive mode)",
|
|
12
|
+
demandOption: false,
|
|
13
|
+
default: [],
|
|
14
|
+
type: "array",
|
|
15
|
+
string: true,
|
|
16
|
+
})
|
|
17
|
+
.option("i", {
|
|
18
|
+
alias: "interactive",
|
|
19
|
+
description: "Generate script in interactive mode with user prompts",
|
|
20
|
+
demandOption: false,
|
|
21
|
+
type: "boolean",
|
|
22
|
+
})
|
|
23
|
+
.option("t", {
|
|
24
|
+
alias: "template",
|
|
25
|
+
description: "Template name to use",
|
|
26
|
+
demandOption: false,
|
|
27
|
+
choices: availableTemplateNames,
|
|
28
|
+
type: "string",
|
|
29
|
+
})
|
|
30
|
+
.option("c", {
|
|
31
|
+
alias: "cache",
|
|
32
|
+
description: "cache dir",
|
|
33
|
+
demandOption: false,
|
|
34
|
+
type: "string",
|
|
35
|
+
})
|
|
36
|
+
.option("s", {
|
|
37
|
+
alias: "script",
|
|
38
|
+
description: "script filename",
|
|
39
|
+
demandOption: false,
|
|
40
|
+
default: "script",
|
|
41
|
+
type: "string",
|
|
42
|
+
})
|
|
43
|
+
.option("llm_agent", {
|
|
44
|
+
description: "llm agent",
|
|
45
|
+
demandOption: false,
|
|
46
|
+
choices: llmAgents,
|
|
47
|
+
type: "string",
|
|
48
|
+
})
|
|
49
|
+
.option("llm_model", {
|
|
50
|
+
description: "llm model",
|
|
51
|
+
demandOption: false,
|
|
52
|
+
type: "string",
|
|
53
|
+
})
|
|
54
|
+
.command("$0 <action>", "Run mulmocast tool", (yargs) => {
|
|
55
|
+
return yargs.positional("action", {
|
|
56
|
+
describe: "action to perform",
|
|
57
|
+
choices: ["scripting", "prompt", "schema"],
|
|
58
|
+
type: "string",
|
|
59
|
+
});
|
|
60
|
+
})
|
|
61
|
+
.strict()
|
|
62
|
+
.help()
|
|
63
|
+
.alias("help", "h")
|
|
64
|
+
.parseSync();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import yargs from "yargs/yargs";
|
|
4
|
+
import { hideBin } from "yargs/helpers";
|
|
5
|
+
import { GraphAILogger } from "graphai";
|
|
6
|
+
import * as scriptingCmd from "./tool_commands/scripting/index.js";
|
|
7
|
+
import * as promptCmd from "./tool_commands/prompt/index.js";
|
|
8
|
+
import * as schemaCmd from "./tool_commands/schema/index.js";
|
|
9
|
+
const cli = yargs(hideBin(process.argv))
|
|
10
|
+
.scriptName("mulmo-tool")
|
|
11
|
+
.usage("$0 <command> [args]")
|
|
12
|
+
.option("v", {
|
|
13
|
+
alias: "verbose",
|
|
14
|
+
describe: "verbose log",
|
|
15
|
+
demandOption: true,
|
|
16
|
+
default: false,
|
|
17
|
+
type: "boolean",
|
|
18
|
+
})
|
|
19
|
+
.command(scriptingCmd)
|
|
20
|
+
.command(promptCmd)
|
|
21
|
+
.command(schemaCmd)
|
|
22
|
+
.demandCommand()
|
|
23
|
+
.strict()
|
|
24
|
+
.help()
|
|
25
|
+
.alias("help", "h");
|
|
26
|
+
const main = async () => {
|
|
27
|
+
await cli.parseAsync();
|
|
28
|
+
};
|
|
29
|
+
main().catch((error) => {
|
|
30
|
+
GraphAILogger.info("An unexpected error occurred:", error);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getAvailableTemplates } from "@/src/utils/file.js";
|
|
2
|
+
const availableTemplateNames = getAvailableTemplates().map((template) => template.filename);
|
|
3
|
+
export const builder = (yargs) => {
|
|
4
|
+
return yargs.option("t", {
|
|
5
|
+
alias: "template",
|
|
6
|
+
describe: "Template name to use",
|
|
7
|
+
demandOption: false,
|
|
8
|
+
choices: availableTemplateNames,
|
|
9
|
+
type: "string",
|
|
10
|
+
});
|
|
11
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { selectTemplate } from "@/src/utils/inquirer.js";
|
|
2
|
+
import { dumpPromptFromTemplate } from "../../../tools/dump_prompt.js";
|
|
3
|
+
import { setGraphAILogger } from "@/src/utils/cli.js";
|
|
4
|
+
export const handler = async (argv) => {
|
|
5
|
+
let { template } = argv;
|
|
6
|
+
const { v: verbose } = argv;
|
|
7
|
+
if (!template) {
|
|
8
|
+
template = await selectTemplate();
|
|
9
|
+
}
|
|
10
|
+
setGraphAILogger(verbose, {
|
|
11
|
+
template,
|
|
12
|
+
});
|
|
13
|
+
await dumpPromptFromTemplate({ templateName: template });
|
|
14
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { mulmoScriptSchema } from "@/src/types/schema.js";
|
|
2
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3
|
+
import { GraphAILogger } from "graphai";
|
|
4
|
+
import { setGraphAILogger } from "@/src/utils/cli.js";
|
|
5
|
+
export const handler = async (argv) => {
|
|
6
|
+
const { v: verbose } = argv;
|
|
7
|
+
setGraphAILogger(verbose);
|
|
8
|
+
const defaultSchema = zodToJsonSchema(mulmoScriptSchema, {
|
|
9
|
+
strictUnions: true,
|
|
10
|
+
});
|
|
11
|
+
GraphAILogger.info(JSON.stringify(defaultSchema, null, 2));
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Argv } from "yargs";
|
|
2
|
+
export declare const builder: (yargs: Argv) => Argv<{
|
|
3
|
+
o: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
b: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
u: string[] | never[];
|
|
8
|
+
} & {
|
|
9
|
+
i: boolean | undefined;
|
|
10
|
+
} & {
|
|
11
|
+
t: string | undefined;
|
|
12
|
+
} & {
|
|
13
|
+
c: string | undefined;
|
|
14
|
+
} & {
|
|
15
|
+
s: string;
|
|
16
|
+
} & {
|
|
17
|
+
llm_agent: string | undefined;
|
|
18
|
+
} & {
|
|
19
|
+
llm_model: string | undefined;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { llmAgents } from "../../../utils/utils.js";
|
|
2
|
+
import { getAvailableTemplates } from "../../../utils/file.js";
|
|
3
|
+
const availableTemplateNames = getAvailableTemplates().map((template) => template.filename);
|
|
4
|
+
export const builder = (yargs) => {
|
|
5
|
+
return yargs
|
|
6
|
+
.option("o", {
|
|
7
|
+
alias: "outdir",
|
|
8
|
+
description: "output dir",
|
|
9
|
+
demandOption: false,
|
|
10
|
+
type: "string",
|
|
11
|
+
})
|
|
12
|
+
.option("b", {
|
|
13
|
+
alias: "basedir",
|
|
14
|
+
description: "base dir",
|
|
15
|
+
demandOption: false,
|
|
16
|
+
type: "string",
|
|
17
|
+
})
|
|
18
|
+
.option("u", {
|
|
19
|
+
alias: "url",
|
|
20
|
+
description: "URLs to reference (required when not in interactive mode)",
|
|
21
|
+
demandOption: false,
|
|
22
|
+
default: [],
|
|
23
|
+
type: "array",
|
|
24
|
+
string: true,
|
|
25
|
+
})
|
|
26
|
+
.option("i", {
|
|
27
|
+
alias: "interactive",
|
|
28
|
+
description: "Generate script in interactive mode with user prompts",
|
|
29
|
+
demandOption: false,
|
|
30
|
+
type: "boolean",
|
|
31
|
+
})
|
|
32
|
+
.option("t", {
|
|
33
|
+
alias: "template",
|
|
34
|
+
description: "Template name to use",
|
|
35
|
+
demandOption: false,
|
|
36
|
+
choices: availableTemplateNames,
|
|
37
|
+
type: "string",
|
|
38
|
+
})
|
|
39
|
+
.option("c", {
|
|
40
|
+
alias: "cache",
|
|
41
|
+
description: "cache dir",
|
|
42
|
+
demandOption: false,
|
|
43
|
+
type: "string",
|
|
44
|
+
})
|
|
45
|
+
.option("s", {
|
|
46
|
+
alias: "script",
|
|
47
|
+
description: "script filename",
|
|
48
|
+
demandOption: false,
|
|
49
|
+
default: "script",
|
|
50
|
+
type: "string",
|
|
51
|
+
})
|
|
52
|
+
.option("llm_agent", {
|
|
53
|
+
description: "llm agent",
|
|
54
|
+
demandOption: false,
|
|
55
|
+
choices: llmAgents,
|
|
56
|
+
type: "string",
|
|
57
|
+
})
|
|
58
|
+
.option("llm_model", {
|
|
59
|
+
description: "llm model",
|
|
60
|
+
demandOption: false,
|
|
61
|
+
type: "string",
|
|
62
|
+
});
|
|
63
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ToolCliArgs } from "@/src/types/cli_types.js";
|
|
2
|
+
export declare const handler: (argv: ToolCliArgs<{
|
|
3
|
+
o?: string;
|
|
4
|
+
b?: string;
|
|
5
|
+
u?: string[];
|
|
6
|
+
i?: boolean;
|
|
7
|
+
t?: string;
|
|
8
|
+
c?: string;
|
|
9
|
+
s?: string;
|
|
10
|
+
llm_agent?: string;
|
|
11
|
+
llm_model?: string;
|
|
12
|
+
}>) => Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getBaseDirPath, getFullPath } from "@/src/utils/file.js";
|
|
2
|
+
import { outDirName, cacheDirName } from "../../../utils/const.js";
|
|
3
|
+
import { getUrlsIfNeeded, selectTemplate } from "@/src/utils/inquirer.js";
|
|
4
|
+
import { createMulmoScriptFromUrl } from "../../../tools/create_mulmo_script_from_url.js";
|
|
5
|
+
import { createMulmoScriptInteractively } from "../../../tools/create_mulmo_script_interactively.js";
|
|
6
|
+
import { setGraphAILogger } from "@/src/utils/cli.js";
|
|
7
|
+
export const handler = async (argv) => {
|
|
8
|
+
const { o: outdir, b: basedir, v: verbose, i: interactive, c: cache, s: filename, llm_agent, llm_model } = argv;
|
|
9
|
+
let { t: template } = argv;
|
|
10
|
+
const urls = argv.u || [];
|
|
11
|
+
const baseDirPath = getBaseDirPath(basedir);
|
|
12
|
+
const outDirPath = getFullPath(baseDirPath, outdir ?? outDirName);
|
|
13
|
+
const cacheDirPath = getFullPath(outDirPath, cache ?? cacheDirName);
|
|
14
|
+
if (!template) {
|
|
15
|
+
template = await selectTemplate();
|
|
16
|
+
}
|
|
17
|
+
setGraphAILogger(verbose, {
|
|
18
|
+
baseDirPath,
|
|
19
|
+
outDirPath,
|
|
20
|
+
cacheDirPath,
|
|
21
|
+
template,
|
|
22
|
+
urls,
|
|
23
|
+
interactive,
|
|
24
|
+
filename,
|
|
25
|
+
llm_agent,
|
|
26
|
+
llm_model,
|
|
27
|
+
});
|
|
28
|
+
const context = { outDirPath, templateName: template, urls, filename: filename, cacheDirPath, llm_model, llm_agent };
|
|
29
|
+
if (interactive) {
|
|
30
|
+
await createMulmoScriptInteractively(context);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
context.urls = await getUrlsIfNeeded(urls);
|
|
34
|
+
await createMulmoScriptFromUrl(context);
|
|
35
|
+
}
|
|
36
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./actions/index.js";
|
|
2
|
+
export * from "./cli/helpers.js";
|
|
3
|
+
export * from "./utils/file.js";
|
|
4
|
+
export * from "./utils/ffmpeg_utils.js";
|
|
5
|
+
export * from "./utils/provider2agent.js";
|
|
6
|
+
export * from "./methods/index.js";
|
|
7
|
+
export * from "./agents/index.js";
|
|
8
|
+
export * from "./types/index.js";
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./actions/index.js";
|
|
2
|
+
export * from "./cli/helpers.js";
|
|
3
|
+
export * from "./utils/file.js";
|
|
4
|
+
export * from "./utils/ffmpeg_utils.js";
|
|
5
|
+
export * from "./utils/provider2agent.js";
|
|
6
|
+
export * from "./methods/index.js";
|
|
7
|
+
export * from "./agents/index.js";
|
|
8
|
+
export * from "./types/index.js";
|
|
@@ -8,6 +8,9 @@ export declare const MulmoBeatMethods: {
|
|
|
8
8
|
isAnimationObject: (animation: unknown) => animation is AnimationConfig;
|
|
9
9
|
isAnimatedHtmlTailwind: (beat: MulmoBeat) => boolean;
|
|
10
10
|
isMovieMode: (animation: unknown) => boolean;
|
|
11
|
+
isVoiceOver: (beat?: MulmoBeat) => boolean;
|
|
12
|
+
getPrevRenderedBeatIndex(beats: MulmoBeat[], index: number): number;
|
|
13
|
+
getNextRenderedBeatIndex(beats: MulmoBeat[], index: number): number;
|
|
11
14
|
getHtmlPrompt(beat: MulmoBeat): string | undefined;
|
|
12
15
|
getImageReferenceForImageGenerator(beat: MulmoBeat, imageRefs: Record<string, string>): string[];
|
|
13
16
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ImageMediaType } from "../types/index.js";
|
|
1
2
|
/** Type guard: checks if animation value is an object config like { fps: 30 } */
|
|
2
3
|
const isAnimationObject = (animation) => {
|
|
3
4
|
return typeof animation === "object" && animation !== null && !Array.isArray(animation);
|
|
@@ -17,11 +18,27 @@ const isAnimatedHtmlTailwind = (beat) => {
|
|
|
17
18
|
const animation = beat.image.animation;
|
|
18
19
|
return isAnimationEnabled(animation);
|
|
19
20
|
};
|
|
21
|
+
// voice_over beats share the preceding beat's shot, and contribute no video segment of their own.
|
|
22
|
+
const isVoiceOver = (beat) => beat?.image?.type === ImageMediaType.VoiceOver;
|
|
20
23
|
export const MulmoBeatMethods = {
|
|
21
24
|
isAnimationEnabled,
|
|
22
25
|
isAnimationObject,
|
|
23
26
|
isAnimatedHtmlTailwind,
|
|
24
27
|
isMovieMode,
|
|
28
|
+
isVoiceOver,
|
|
29
|
+
// Index of the closest preceding beat which is actually rendered as a video segment (-1 if none).
|
|
30
|
+
getPrevRenderedBeatIndex(beats, index) {
|
|
31
|
+
const offset = beats
|
|
32
|
+
.slice(0, Math.max(index, 0))
|
|
33
|
+
.reverse()
|
|
34
|
+
.findIndex((beat) => !isVoiceOver(beat));
|
|
35
|
+
return offset < 0 ? -1 : index - 1 - offset;
|
|
36
|
+
},
|
|
37
|
+
// Index of the closest following beat which is actually rendered as a video segment (-1 if none).
|
|
38
|
+
getNextRenderedBeatIndex(beats, index) {
|
|
39
|
+
const offset = beats.slice(index + 1).findIndex((beat) => !isVoiceOver(beat));
|
|
40
|
+
return offset < 0 ? -1 : index + 1 + offset;
|
|
41
|
+
},
|
|
25
42
|
getHtmlPrompt(beat) {
|
|
26
43
|
if (beat?.htmlPrompt?.data) {
|
|
27
44
|
return beat.htmlPrompt.prompt + "\n\n[data]\n" + JSON.stringify(beat.htmlPrompt.data, null, 2);
|
|
@@ -17,6 +17,9 @@ export declare const MulmoBeatMethods: {
|
|
|
17
17
|
};
|
|
18
18
|
isAnimatedHtmlTailwind: (beat: MulmoBeat) => boolean;
|
|
19
19
|
isMovieMode: (animation: unknown) => boolean;
|
|
20
|
+
isVoiceOver: (beat?: MulmoBeat) => boolean;
|
|
21
|
+
getPrevRenderedBeatIndex(beats: MulmoBeat[], index: number): number;
|
|
22
|
+
getNextRenderedBeatIndex(beats: MulmoBeat[], index: number): number;
|
|
20
23
|
getHtmlPrompt(beat: MulmoBeat): string | undefined;
|
|
21
24
|
getImageReferenceForImageGenerator(beat: MulmoBeat, imageRefs: Record<string, string>): string[];
|
|
22
25
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Works in both Node.js and modern browsers.
|
|
5
5
|
*/
|
|
6
6
|
import type { SlideTheme } from "@mulmocast/deck";
|
|
7
|
-
import { MulmoCanvasDimension, MulmoBeat, Text2SpeechProvider, Text2ImageAgentInfo, Text2HtmlAgentInfo, BeatMediaType, MulmoPresentationStyle, SpeakerData, Text2ImageProvider, MulmoStudioContext, MulmoTransition } from "../types/index.js";
|
|
7
|
+
import { MulmoCanvasDimension, MulmoBeat, Text2SpeechProvider, Text2ImageAgentInfo, Text2HtmlAgentInfo, BeatMediaType, MulmoPresentationStyle, SpeakerData, Text2ImageProvider, MulmoStudioContext, MulmoTransition, MulmoFillOption } from "../types/index.js";
|
|
8
8
|
export declare const MulmoPresentationStyleMethods: {
|
|
9
9
|
getCanvasSize(presentationStyle: MulmoPresentationStyle): MulmoCanvasDimension;
|
|
10
10
|
getAllSpeechProviders(presentationStyle: MulmoPresentationStyle): Set<Text2SpeechProvider>;
|
|
@@ -30,6 +30,10 @@ export declare const MulmoPresentationStyleMethods: {
|
|
|
30
30
|
getSpeakerData(presentationStyle: MulmoPresentationStyle, beat: MulmoBeat, lang: string | undefined): SpeakerData;
|
|
31
31
|
getSpeaker(context: MulmoStudioContext, beat: MulmoBeat, targetLang: string | undefined): SpeakerData;
|
|
32
32
|
getMovieTransition(context: MulmoStudioContext, beat: MulmoBeat): MulmoTransition | null;
|
|
33
|
+
getNeedFirstFrame(context: MulmoStudioContext): boolean[];
|
|
34
|
+
getNeedLastFrame(context: MulmoStudioContext): boolean[];
|
|
35
|
+
getFillOption(context: MulmoStudioContext, beat: MulmoBeat): MulmoFillOption;
|
|
36
|
+
getMovieVolume(context: MulmoStudioContext, beat: MulmoBeat): number;
|
|
33
37
|
getText2ImageProvider(provider: Text2ImageProvider | undefined): Text2ImageProvider;
|
|
34
38
|
getImageAgentInfo(presentationStyle: MulmoPresentationStyle, beat?: MulmoBeat): Text2ImageAgentInfo;
|
|
35
39
|
getMovieAgentInfo(presentationStyle: MulmoPresentationStyle, beat?: MulmoBeat): {
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import { isNull } from "graphai";
|
|
7
7
|
import { userAssert } from "../utils/utils.js";
|
|
8
8
|
import { slideThemes } from "../data/slideThemes.js";
|
|
9
|
-
import { text2ImageProviderSchema, text2HtmlImageProviderSchema, text2MovieProviderSchema, text2SpeechProviderSchema, mulmoCanvasDimensionSchema, mulmoTransitionSchema, } from "../types/schema.js";
|
|
9
|
+
import { text2ImageProviderSchema, text2HtmlImageProviderSchema, text2MovieProviderSchema, text2SpeechProviderSchema, mulmoCanvasDimensionSchema, mulmoTransitionSchema, mulmoFillOptionSchema, } from "../types/schema.js";
|
|
10
|
+
import { MulmoBeatMethods } from "./mulmo_beat.js";
|
|
10
11
|
import { provider2ImageAgent, provider2MovieAgent, provider2LLMAgent, provider2TTSAgent, provider2SoundEffectAgent, provider2LipSyncAgent, defaultProviders, getModelAudio, AUDIO_MODE_ALWAYS, } from "../types/provider2agent.js";
|
|
11
12
|
const defaultTextSlideStyles = [
|
|
12
13
|
'*,*::before,*::after{box-sizing:border-box}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin:0}ul[role="list"],ol[role="list"]{list-style:none}html:focus-within{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}@media(prefers-reduced-motion:reduce){html:focus-within{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}}',
|
|
@@ -22,6 +23,13 @@ const defaultTextSlideStyles = [
|
|
|
22
23
|
"td, th { padding: 8px }",
|
|
23
24
|
"tr:nth-child(even) { background-color: #eee }",
|
|
24
25
|
];
|
|
26
|
+
const DEFAULT_DUCKING_RATIO = 0.3;
|
|
27
|
+
// Both frame requirements answer the same question for each *rendered* beat (voice_over beats have
|
|
28
|
+
// no video segment, so they never supply a frame): does a transition need a still from this beat?
|
|
29
|
+
const mapRenderedBeats = (context, needsFrame) => {
|
|
30
|
+
const beats = context.studio.script.beats;
|
|
31
|
+
return beats.map((beat, index) => (MulmoBeatMethods.isVoiceOver(beat) ? false : needsFrame(beats, index)));
|
|
32
|
+
};
|
|
25
33
|
export const MulmoPresentationStyleMethods = {
|
|
26
34
|
getCanvasSize(presentationStyle) {
|
|
27
35
|
return mulmoCanvasDimensionSchema.parse(presentationStyle.canvasSize);
|
|
@@ -95,6 +103,40 @@ export const MulmoPresentationStyleMethods = {
|
|
|
95
103
|
return null;
|
|
96
104
|
return mulmoTransitionSchema.parse(transitionData);
|
|
97
105
|
},
|
|
106
|
+
getNeedFirstFrame(context) {
|
|
107
|
+
// This beat slides/wipes in over the previous shot, so it supplies its own first frame.
|
|
108
|
+
return mapRenderedBeats(context, (beats, index) => {
|
|
109
|
+
if (MulmoBeatMethods.getPrevRenderedBeatIndex(beats, index) < 0)
|
|
110
|
+
return false; // No previous shot: the transition is skipped
|
|
111
|
+
const transition = MulmoPresentationStyleMethods.getMovieTransition(context, beats[index]);
|
|
112
|
+
return (transition?.type.startsWith("slidein_") || transition?.type.startsWith("wipe")) ?? false;
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
getNeedLastFrame(context) {
|
|
116
|
+
// Any transition consumes the last frame of the previous rendered beat, skipping voice_over beats.
|
|
117
|
+
return mapRenderedBeats(context, (beats, index) => {
|
|
118
|
+
const nextIndex = MulmoBeatMethods.getNextRenderedBeatIndex(beats, index);
|
|
119
|
+
if (nextIndex < 0)
|
|
120
|
+
return false; // Last rendered beat doesn't need _last
|
|
121
|
+
return MulmoPresentationStyleMethods.getMovieTransition(context, beats[nextIndex]) !== null;
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
getFillOption(context, beat) {
|
|
125
|
+
const globalFillOption = context.presentationStyle.movieParams?.fillOption;
|
|
126
|
+
const beatFillOption = beat.movieParams?.fillOption;
|
|
127
|
+
const defaultFillOption = mulmoFillOptionSchema.parse({}); // let the schema infer the default value
|
|
128
|
+
return { ...defaultFillOption, ...globalFillOption, ...beatFillOption };
|
|
129
|
+
},
|
|
130
|
+
getMovieVolume(context, beat) {
|
|
131
|
+
const baseMovieVolume = beat.audioParams?.movieVolume ?? context.presentationStyle.audioParams.movieVolume ?? 1.0;
|
|
132
|
+
const ducking = context.presentationStyle.audioParams.ducking;
|
|
133
|
+
const hasSpeech = !!beat.text && !context.presentationStyle.audioParams.suppressSpeech;
|
|
134
|
+
if (ducking && hasSpeech) {
|
|
135
|
+
const ratio = ducking.ratio ?? DEFAULT_DUCKING_RATIO;
|
|
136
|
+
return baseMovieVolume * ratio;
|
|
137
|
+
}
|
|
138
|
+
return baseMovieVolume;
|
|
139
|
+
},
|
|
98
140
|
/* NOTE: This method is not used.
|
|
99
141
|
getTTSModel(context: MulmoStudioContext, beat: MulmoBeat): string | undefined {
|
|
100
142
|
const speaker = MulmoPresentationStyleMethods.getSpeaker(context, beat);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { MulmoStudio } from "../types/index.js";
|
|
2
|
+
type SessionType = "audio" | "image" | "video" | "multiLingual" | "caption" | "pdf";
|
|
3
|
+
type BeatSessionType = "audio" | "image" | "multiLingual" | "caption" | "movie";
|
|
4
|
+
export declare const MulmoStudioMethods: {
|
|
5
|
+
setSessionState(studio: MulmoStudio, sessionType: SessionType, value: boolean): void;
|
|
6
|
+
setBeatSessionState(studio: MulmoStudio, sessionType: BeatSessionType, index: number, value: boolean): void;
|
|
7
|
+
};
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { GraphAILogger } from "graphai";
|
|
2
|
+
const notifyStateChange = (studio, sessionType) => {
|
|
3
|
+
const prefix = studio.state.inSession[sessionType] ? "<" : " >";
|
|
4
|
+
GraphAILogger.info(`${prefix} ${sessionType}`);
|
|
5
|
+
};
|
|
6
|
+
const notifyBeatStateChange = (studio, sessionType, index) => {
|
|
7
|
+
const prefix = studio.state.inBeatSession[sessionType][index] ? "{" : " }";
|
|
8
|
+
GraphAILogger.info(`${prefix} ${sessionType} ${index}`);
|
|
9
|
+
};
|
|
10
|
+
export const MulmoStudioMethods = {
|
|
11
|
+
setSessionState(studio, sessionType, value) {
|
|
12
|
+
studio.state.inSession[sessionType] = value;
|
|
13
|
+
notifyStateChange(studio, sessionType);
|
|
14
|
+
},
|
|
15
|
+
setBeatSessionState(studio, sessionType, index, value) {
|
|
16
|
+
if (value) {
|
|
17
|
+
studio.state.inBeatSession[sessionType][index] = true;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
// NOTE: Setting to false causes the parse error in rebuildStudio in preprocess.ts
|
|
21
|
+
delete studio.state.inBeatSession[sessionType][index];
|
|
22
|
+
}
|
|
23
|
+
notifyBeatStateChange(studio, sessionType, index);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* (No Node.js built-ins like fs, path, dotenv, etc.)
|
|
4
4
|
* Works in both Node.js and modern browsers.
|
|
5
5
|
*/
|
|
6
|
-
import { BeatSessionType, MulmoStudioContext, SessionProgressCallback, SessionType, MulmoBeat, SpeechOptions } from "../types/index.js";
|
|
6
|
+
import { BeatSessionType, MulmoStudioContext, SessionProgressCallback, SessionType, MulmoBeat, MulmoStudioBeat, SpeechOptions } from "../types/index.js";
|
|
7
7
|
import { provider2TTSAgent } from "../types/provider2agent.js";
|
|
8
8
|
export declare const addSessionProgressCallback: (cb: SessionProgressCallback) => void;
|
|
9
9
|
export declare const removeSessionProgressCallback: (cb: SessionProgressCallback) => void;
|
|
@@ -18,6 +18,11 @@ export declare const MulmoStudioContextMethods: {
|
|
|
18
18
|
setBeatSessionState(context: MulmoStudioContext, sessionType: BeatSessionType | undefined, index: number, id: string | undefined, value: boolean): void;
|
|
19
19
|
needTranslate(context: MulmoStudioContext, includeCaption?: boolean): boolean | "" | undefined;
|
|
20
20
|
getIntroPadding(context: MulmoStudioContext): number;
|
|
21
|
+
getExtraPadding(context: MulmoStudioContext, index: number): number;
|
|
22
|
+
getBeatDuration(context: MulmoStudioContext, index: number): number;
|
|
23
|
+
getVoiceOverGroupDuration(context: MulmoStudioContext, index: number): number;
|
|
24
|
+
isMovieBeat(context: MulmoStudioContext, studioBeat: MulmoStudioBeat, beat: MulmoBeat): boolean;
|
|
25
|
+
getSegmentDuration(context: MulmoStudioContext, index: number): number;
|
|
21
26
|
getAudioParam(context: MulmoStudioContext, beat: MulmoBeat, lang?: string): {
|
|
22
27
|
provider: keyof typeof provider2TTSAgent;
|
|
23
28
|
voiceId: string;
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* (No Node.js built-ins like fs, path, dotenv, etc.)
|
|
4
4
|
* Works in both Node.js and modern browsers.
|
|
5
5
|
*/
|
|
6
|
-
import { text2SpeechProviderSchema, } from "../types/index.js";
|
|
6
|
+
import { ImageMediaType, text2SpeechProviderSchema, } from "../types/index.js";
|
|
7
7
|
import { beatId } from "../utils/utils.js";
|
|
8
8
|
import { GraphAILogger } from "graphai";
|
|
9
9
|
import { MulmoPresentationStyleMethods } from "./mulmo_presentation_style.js";
|
|
10
|
+
import { MulmoBeatMethods } from "./mulmo_beat.js";
|
|
10
11
|
const sessionProgressCallbacks = new Set();
|
|
11
12
|
export const addSessionProgressCallback = (cb) => {
|
|
12
13
|
sessionProgressCallbacks.add(cb);
|
|
@@ -92,6 +93,40 @@ export const MulmoStudioContextMethods = {
|
|
|
92
93
|
}
|
|
93
94
|
return context.presentationStyle.audioParams.introPadding;
|
|
94
95
|
},
|
|
96
|
+
// Intro/outro padding is the only padding not already folded into studio.beats[].duration.
|
|
97
|
+
getExtraPadding(context, index) {
|
|
98
|
+
if (index === 0) {
|
|
99
|
+
return MulmoStudioContextMethods.getIntroPadding(context);
|
|
100
|
+
}
|
|
101
|
+
else if (index === context.studio.beats.length - 1) {
|
|
102
|
+
return context.presentationStyle.audioParams.outroPadding;
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
},
|
|
106
|
+
// The duration this beat occupies on the audio timeline.
|
|
107
|
+
getBeatDuration(context, index) {
|
|
108
|
+
return (context.studio.beats[index]?.duration ?? 0) + MulmoStudioContextMethods.getExtraPadding(context, index);
|
|
109
|
+
},
|
|
110
|
+
// The total duration of the voice_over beats which follow (and share the shot of) this beat.
|
|
111
|
+
getVoiceOverGroupDuration(context, index) {
|
|
112
|
+
const trailingBeats = context.studio.script.beats.slice(index + 1);
|
|
113
|
+
const groupEnd = trailingBeats.findIndex((beat) => !MulmoBeatMethods.isVoiceOver(beat));
|
|
114
|
+
const groupSize = groupEnd < 0 ? trailingBeats.length : groupEnd;
|
|
115
|
+
return trailingBeats.slice(0, groupSize).reduce((total, _, offset) => total + MulmoStudioContextMethods.getBeatDuration(context, index + 1 + offset), 0);
|
|
116
|
+
},
|
|
117
|
+
// Whether this beat's source is a movie (as opposed to a still which has to be looped).
|
|
118
|
+
isMovieBeat(context, studioBeat, beat) {
|
|
119
|
+
return !!(studioBeat.lipSyncFile ||
|
|
120
|
+
studioBeat.movieFile ||
|
|
121
|
+
MulmoPresentationStyleMethods.getImageType(context.presentationStyle, beat) === ImageMediaType.Movie);
|
|
122
|
+
},
|
|
123
|
+
// How long this beat's video segment is: its own duration plus the trailing voice_over beats which
|
|
124
|
+
// share its shot (they have no video segment of their own), never shorter than its movie. This is
|
|
125
|
+
// the single rule for a segment's length -- createVideo builds from it and transitions clamp against it.
|
|
126
|
+
getSegmentDuration(context, index) {
|
|
127
|
+
const ownDuration = MulmoStudioContextMethods.getBeatDuration(context, index) + MulmoStudioContextMethods.getVoiceOverGroupDuration(context, index);
|
|
128
|
+
return Math.max(ownDuration, context.studio.beats[index]?.movieDuration ?? 0);
|
|
129
|
+
},
|
|
95
130
|
getAudioParam(context, beat, lang) {
|
|
96
131
|
const speaker = MulmoPresentationStyleMethods.getSpeaker(context, beat, lang);
|
|
97
132
|
const speechOptions = { ...speaker.speechOptions, ...beat.speechOptions };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ContentBlock, TableCellValue } from "./schema.js";
|
|
2
|
+
export declare const resolveCellColor: (cellObj: {
|
|
3
|
+
color?: string;
|
|
4
|
+
}, isRowHeader: boolean) => string;
|
|
5
|
+
export declare const renderBadge: (text: string, color: string) => string;
|
|
6
|
+
export declare const renderCellValue: (cell: TableCellValue, isRowHeader: boolean) => string;
|
|
7
|
+
export declare const renderTableCore: (headers: string[] | undefined, rows: TableCellValue[][], rowHeaders?: boolean, striped?: boolean) => string;
|
|
8
|
+
/** Render a single content block to HTML */
|
|
9
|
+
export declare const renderContentBlock: (block: ContentBlock) => string;
|
|
10
|
+
/** Render an array of content blocks to HTML */
|
|
11
|
+
export declare const renderContentBlocks: (blocks: ContentBlock[]) => string;
|
|
12
|
+
/** Render content blocks with fixed aspect-ratio container for image blocks (used in card layouts) */
|
|
13
|
+
export declare const renderCardContentBlocks: (blocks: ContentBlock[]) => string;
|