mulmocast 1.2.4 → 1.2.6

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.
@@ -0,0 +1,29 @@
1
+ {
2
+ "title": "Multi-character Story",
3
+ "description": "Template for Multi-character Story.",
4
+ "systemPrompt": "Break the story into multiple beats, and put the story text in 'text' field. Generate image prompt for each character in the imageParams.images. Another AI will generate image for each beat based on its imagePrompt and specified characters in 'imageNames'. You don't need to repeat the image style in those image prompts. Use the JSON below as a template.",
5
+ "presentationStyle": {
6
+ "$mulmocast": {
7
+ "version": "1.1",
8
+ "credit": "closing"
9
+ },
10
+ "canvasSize": {
11
+ "width": 1536,
12
+ "height": 1024
13
+ },
14
+ "imageParams": {
15
+ "style": "<style>A dreamy, hyper-detailed anime style that blends photorealistic backgrounds with vibrant, saturated colors. The skies are often filled with luminous clouds, dazzling sunsets, or star-filled nights, rendered with a glowing, almost ethereal quality. Urban landscapes and rural scenery are meticulously illustrated, with attention to tiny details like reflections in puddles, neon lights, or the texture of grass swaying in the wind. Characters are drawn with soft, expressive features, standing out against the breathtaking environments, creating a sense of emotional depth and lyrical atmosphere. The overall mood is cinematic, romantic, and filled with a sense of fleeting beauty and longing.</style>",
16
+ "images": {
17
+ "[CHARACTER_1_ID]": {
18
+ "type": "imagePrompt",
19
+ "prompt": "[IMAGE PROMPT FOR THIS CHARACTER]"
20
+ },
21
+ "[CHARACTER_2_ID]": {
22
+ "type": "imagePrompt",
23
+ "prompt": "[IMAGE PROMPT FOR THIS CHARACTER]"
24
+ }
25
+ }
26
+ }
27
+ },
28
+ "scriptName": "story_with_characters.json"
29
+ }
@@ -1,6 +1,5 @@
1
1
  import "dotenv/config";
2
- import type { CallbackFunction } from "graphai";
3
- import { MulmoStudioContext, MulmoBeat } from "../types/index.js";
2
+ import { MulmoStudioContext, MulmoBeat, PublicAPIArgs } from "../types/index.js";
4
3
  export declare const getBeatAudioPath: (text: string, context: MulmoStudioContext, beat: MulmoBeat, lang?: string) => string | undefined;
5
- export declare const generateBeatAudio: (index: number, context: MulmoStudioContext, settings?: Record<string, string>, callbacks?: CallbackFunction[]) => Promise<void>;
6
- export declare const audio: (context: MulmoStudioContext, settings?: Record<string, string>, callbacks?: CallbackFunction[]) => Promise<MulmoStudioContext>;
4
+ export declare const generateBeatAudio: (index: number, context: MulmoStudioContext, args?: PublicAPIArgs) => Promise<void>;
5
+ export declare const audio: (context: MulmoStudioContext, args?: PublicAPIArgs) => Promise<MulmoStudioContext>;
@@ -186,7 +186,8 @@ const audioAgents = {
186
186
  addBGMAgent,
187
187
  combineAudioFilesAgent,
188
188
  };
189
- export const generateBeatAudio = async (index, context, settings, callbacks) => {
189
+ export const generateBeatAudio = async (index, context, args) => {
190
+ const { settings, callbacks } = args ?? {};
190
191
  try {
191
192
  MulmoStudioContextMethods.setSessionState(context, "audio", true);
192
193
  const fileName = MulmoStudioContextMethods.getFileName(context);
@@ -214,7 +215,8 @@ export const generateBeatAudio = async (index, context, settings, callbacks) =>
214
215
  MulmoStudioContextMethods.setSessionState(context, "audio", false);
215
216
  }
216
217
  };
217
- export const audio = async (context, settings, callbacks) => {
218
+ export const audio = async (context, args) => {
219
+ const { settings, callbacks } = args ?? {};
218
220
  try {
219
221
  MulmoStudioContextMethods.setSessionState(context, "audio", true);
220
222
  const fileName = MulmoStudioContextMethods.getFileName(context);
@@ -1,3 +1,2 @@
1
- import { MulmoStudioContext } from "../types/index.js";
2
- import type { CallbackFunction } from "graphai";
3
- export declare const captions: (context: MulmoStudioContext, callbacks?: CallbackFunction[]) => Promise<MulmoStudioContext>;
1
+ import { MulmoStudioContext, PublicAPIArgs } from "../types/index.js";
2
+ export declare const captions: (context: MulmoStudioContext, args?: PublicAPIArgs) => Promise<MulmoStudioContext>;
@@ -69,7 +69,8 @@ const graph_data = {
69
69
  },
70
70
  },
71
71
  };
72
- export const captions = async (context, callbacks) => {
72
+ export const captions = async (context, args) => {
73
+ const { callbacks } = args ?? {};
73
74
  if (MulmoStudioContextMethods.getCaption(context)) {
74
75
  try {
75
76
  MulmoStudioContextMethods.setSessionState(context, "caption", true);
@@ -7,6 +7,7 @@ export declare const imagePreprocessAgent: (namedInputs: {
7
7
  }) => Promise<{
8
8
  imagePath: string;
9
9
  htmlPrompt: string | undefined;
10
+ htmlImageFile: string;
10
11
  htmlPath: string;
11
12
  htmlImageSystemPrompt: string;
12
13
  } | {
@@ -27,6 +28,7 @@ export declare const imagePreprocessAgent: (namedInputs: {
27
28
  audioFile?: string;
28
29
  beatDuration?: number;
29
30
  htmlPrompt?: undefined;
31
+ htmlImageFile?: undefined;
30
32
  htmlPath?: undefined;
31
33
  htmlImageSystemPrompt?: undefined;
32
34
  } | {
@@ -62,6 +64,7 @@ export declare const imagePreprocessAgent: (namedInputs: {
62
64
  audioFile?: string;
63
65
  beatDuration?: number;
64
66
  htmlPrompt?: undefined;
67
+ htmlImageFile?: undefined;
65
68
  htmlPath?: undefined;
66
69
  htmlImageSystemPrompt?: undefined;
67
70
  } | {
@@ -100,6 +103,7 @@ export declare const imagePreprocessAgent: (namedInputs: {
100
103
  audioFile?: string;
101
104
  beatDuration?: number;
102
105
  htmlPrompt?: undefined;
106
+ htmlImageFile?: undefined;
103
107
  htmlPath?: undefined;
104
108
  htmlImageSystemPrompt?: undefined;
105
109
  }>;
@@ -12,11 +12,11 @@ const htmlStyle = (context, beat) => {
12
12
  export const imagePreprocessAgent = async (namedInputs) => {
13
13
  const { context, beat, index, imageRefs } = namedInputs;
14
14
  const studioBeat = context.studio.beats[index];
15
- const imagePath = getBeatPngImagePath(context, index);
15
+ const { imagePath, htmlImageFile } = getBeatPngImagePath(context, index);
16
16
  if (beat.htmlPrompt) {
17
17
  const htmlPrompt = MulmoBeatMethods.getHtmlPrompt(beat);
18
18
  const htmlPath = imagePath.replace(/\.[^/.]+$/, ".html");
19
- return { imagePath, htmlPrompt, htmlPath, htmlImageSystemPrompt: htmlImageSystemPrompt(context.presentationStyle.canvasSize) };
19
+ return { imagePath, htmlPrompt, htmlImageFile, htmlPath, htmlImageSystemPrompt: htmlImageSystemPrompt(context.presentationStyle.canvasSize) };
20
20
  }
21
21
  const imageAgentInfo = MulmoPresentationStyleMethods.getImageAgentInfo(context.presentationStyle, beat);
22
22
  const moviePaths = getBeatMoviePaths(context, index);
@@ -61,7 +61,7 @@ export const imagePreprocessAgent = async (namedInputs) => {
61
61
  };
62
62
  export const imagePluginAgent = async (namedInputs) => {
63
63
  const { context, beat, index } = namedInputs;
64
- const imagePath = getBeatPngImagePath(context, index);
64
+ const { imagePath } = getBeatPngImagePath(context, index);
65
65
  const plugin = MulmoBeatMethods.getPlugin(beat);
66
66
  try {
67
67
  MulmoStudioContextMethods.setBeatSessionState(context, "image", index, beat.id, true);
@@ -1,20 +1,18 @@
1
- import type { GraphOptions, CallbackFunction } from "graphai";
2
- import { MulmoStudioContext } from "../types/index.js";
1
+ import type { GraphOptions } from "graphai";
2
+ import { MulmoStudioContext, PublicAPIArgs } from "../types/index.js";
3
3
  export declare const graphOption: (context: MulmoStudioContext, settings?: Record<string, string>) => Promise<GraphOptions>;
4
4
  type ImageOptions = {
5
5
  imageAgents: Record<string, unknown>;
6
6
  };
7
- export declare const images: (context: MulmoStudioContext, args?: {
8
- settings?: Record<string, string>;
9
- callbacks?: CallbackFunction[];
7
+ export declare const images: (context: MulmoStudioContext, args?: PublicAPIArgs & {
10
8
  options?: ImageOptions;
11
9
  }) => Promise<MulmoStudioContext>;
12
10
  export declare const generateBeatImage: (inputs: {
13
11
  index: number;
14
12
  context: MulmoStudioContext;
15
- settings?: Record<string, string>;
16
- callbacks?: CallbackFunction[];
17
- forceMovie?: boolean;
18
- forceImage?: boolean;
13
+ args?: PublicAPIArgs & {
14
+ forceMovie?: boolean;
15
+ forceImage?: boolean;
16
+ };
19
17
  }) => Promise<void>;
20
18
  export {};
@@ -116,7 +116,7 @@ const beat_graph_data = {
116
116
  inputs: {
117
117
  htmlText: ":htmlReader.htmlText",
118
118
  canvasSize: ":context.presentationStyle.canvasSize",
119
- file: ":preprocessor.imagePath",
119
+ file: ":preprocessor.htmlImageFile",
120
120
  },
121
121
  },
122
122
  imageGenerator: {
@@ -188,7 +188,7 @@ const beat_graph_data = {
188
188
  return { hasMovieAudio: true };
189
189
  }
190
190
  const sourceFile = namedInputs.movieFile || namedInputs.imageFile;
191
- if (!sourceFile) {
191
+ if (!sourceFile || !fs.existsSync(sourceFile)) {
192
192
  return { hasMovieAudio: false };
193
193
  }
194
194
  const { hasAudio } = await ffmpegGetMediaDuration(sourceFile);
@@ -257,6 +257,7 @@ const beat_graph_data = {
257
257
  soundEffectFile: ":preprocessor.soundEffectFile",
258
258
  lipSyncFile: ":preprocessor.lipSyncFile",
259
259
  hasMovieAudio: ":audioChecker.hasMovieAudio",
260
+ htmlImageFile: ":preprocessor.htmlImageFile",
260
261
  },
261
262
  output: {
262
263
  imageFile: ".imageFile",
@@ -264,6 +265,7 @@ const beat_graph_data = {
264
265
  soundEffectFile: ".soundEffectFile",
265
266
  lipSyncFile: ".lipSyncFile",
266
267
  hasMovieAudio: ".hasMovieAudio",
268
+ htmlImageFile: ".htmlImageFile",
267
269
  },
268
270
  isResult: true,
269
271
  },
@@ -346,8 +348,8 @@ export const graphOption = async (context, settings) => {
346
348
  },
347
349
  ],
348
350
  taskManager: new TaskManager(MulmoPresentationStyleMethods.getConcurrency(context.presentationStyle)),
351
+ config: settings2GraphAIConfig(settings, process.env),
349
352
  };
350
- options.config = settings2GraphAIConfig(settings, process.env);
351
353
  return options;
352
354
  };
353
355
  const prepareGenerateImages = async (context) => {
@@ -367,7 +369,8 @@ const prepareGenerateImages = async (context) => {
367
369
  };
368
370
  return injections;
369
371
  };
370
- const generateImages = async (context, settings, callbacks, options) => {
372
+ const generateImages = async (context, args) => {
373
+ const { settings, callbacks, options } = args ?? {};
371
374
  const optionImageAgents = options?.imageAgents ?? {};
372
375
  const injections = await prepareGenerateImages(context);
373
376
  const graphaiAgent = {
@@ -388,10 +391,9 @@ const generateImages = async (context, settings, callbacks, options) => {
388
391
  };
389
392
  // public api
390
393
  export const images = async (context, args) => {
391
- const { settings, callbacks, options } = args ?? {};
392
394
  try {
393
395
  MulmoStudioContextMethods.setSessionState(context, "image", true);
394
- const newContext = await generateImages(context, settings, callbacks, options);
396
+ const newContext = await generateImages(context, args);
395
397
  MulmoStudioContextMethods.setSessionState(context, "image", false);
396
398
  return newContext;
397
399
  }
@@ -402,7 +404,8 @@ export const images = async (context, args) => {
402
404
  };
403
405
  // public api
404
406
  export const generateBeatImage = async (inputs) => {
405
- const { index, context, settings, callbacks, forceMovie, forceImage } = inputs;
407
+ const { index, context, args } = inputs;
408
+ const { settings, callbacks, forceMovie, forceImage } = args ?? {};
406
409
  const options = await graphOption(context, settings);
407
410
  const injections = await prepareGenerateImages(context);
408
411
  const graph = new GraphAI(beat_graph_data, defaultAgents, options);
@@ -162,7 +162,7 @@ const createVideo = async (audioArtifactFilePath, outputVideoPath, context) => {
162
162
  beatTimestamps.push(timestamp);
163
163
  return timestamp; // Skip voice-over beats.
164
164
  }
165
- const sourceFile = studioBeat.lipSyncFile ?? studioBeat.soundEffectFile ?? studioBeat.movieFile ?? studioBeat.imageFile;
165
+ const sourceFile = studioBeat.lipSyncFile ?? studioBeat.soundEffectFile ?? studioBeat.movieFile ?? studioBeat.htmlImageFile ?? studioBeat.imageFile;
166
166
  assert(!!sourceFile, `studioBeat.imageFile or studioBeat.movieFile is not set: index=${index}`);
167
167
  assert(!!studioBeat.duration, `studioBeat.duration is not set: index=${index}`);
168
168
  const extraPadding = (() => {
@@ -1,6 +1,5 @@
1
1
  import "dotenv/config";
2
- import type { CallbackFunction } from "graphai";
3
- import type { LANG, MulmoStudioContext } from "../types/index.js";
2
+ import type { LANG, MulmoStudioContext, PublicAPIArgs } from "../types/index.js";
4
3
  export declare const translateTextGraph: {
5
4
  version: number;
6
5
  nodes: {
@@ -50,11 +49,5 @@ export declare const getOutputMultilingualFilePathAndMkdir: (context: MulmoStudi
50
49
  outputMultilingualFilePath: string;
51
50
  outDirPath: string;
52
51
  };
53
- export declare const translateBeat: (index: number, context: MulmoStudioContext, targetLangs: string[], args?: {
54
- settings?: Record<string, string>;
55
- callbacks?: CallbackFunction[];
56
- }) => Promise<void>;
57
- export declare const translate: (context: MulmoStudioContext, args?: {
58
- callbacks?: CallbackFunction[];
59
- settings?: Record<string, string>;
60
- }) => Promise<MulmoStudioContext>;
52
+ export declare const translateBeat: (index: number, context: MulmoStudioContext, targetLangs: string[], args?: PublicAPIArgs) => Promise<void>;
53
+ export declare const translate: (context: MulmoStudioContext, args?: PublicAPIArgs) => Promise<MulmoStudioContext>;
@@ -1,4 +1,4 @@
1
- import { readFileSync } from "fs";
1
+ import { readFileSync, existsSync } from "fs";
2
2
  import { GraphAILogger } from "graphai";
3
3
  import Replicate from "replicate";
4
4
  import { provider2LipSyncAgent } from "../utils/provider2agent.js";
@@ -12,11 +12,17 @@ export const lipSyncReplicateAgent = async ({ namedInputs, params, config, }) =>
12
12
  const replicate = new Replicate({
13
13
  auth: apiKey,
14
14
  });
15
- const videoBuffer = movieFile ? readFileSync(movieFile) : undefined;
15
+ if (!audioFile || !existsSync(audioFile)) {
16
+ throw new Error(`lipSyncReplicateAgent audioFile not exist: ${audioFile}`);
17
+ }
16
18
  const audioBuffer = readFileSync(audioFile);
19
+ const videoBuffer = movieFile ? readFileSync(movieFile) : undefined;
17
20
  const imageBuffer = imageFile ? readFileSync(imageFile) : undefined;
18
- const videoUri = videoBuffer ? `data:video/quicktime;base64,${videoBuffer.toString("base64")}` : undefined;
21
+ if (!videoBuffer && !imageBuffer) {
22
+ throw new Error("lipSyncReplicateAgent Either movieFile or imageFile is required");
23
+ }
19
24
  const audioUri = `data:audio/wav;base64,${audioBuffer.toString("base64")}`;
25
+ const videoUri = videoBuffer ? `data:video/quicktime;base64,${videoBuffer.toString("base64")}` : undefined;
20
26
  const imageUri = imageBuffer ? `data:image/png;base64,${imageBuffer.toString("base64")}` : undefined;
21
27
  const input = {
22
28
  video: undefined,
@@ -2,7 +2,8 @@ import * as scriptingCmd from "./scripting/index.js";
2
2
  import * as promptCmd from "./prompt/index.js";
3
3
  import * as schemaCmd from "./schema/index.js";
4
4
  import * as storyToScriptCmd from "./story_to_script/index.js";
5
+ import * as whisperCmd from "./whisper/index.js";
5
6
  export const command = "tool <command>";
6
7
  export const desc = "Generate Mulmo script and other tools";
7
- export const builder = (y) => y.command(scriptingCmd).command(promptCmd).command(schemaCmd).command(storyToScriptCmd).demandCommand().strict();
8
+ export const builder = (y) => y.command(scriptingCmd).command(promptCmd).command(schemaCmd).command(storyToScriptCmd).command(whisperCmd).demandCommand().strict();
8
9
  export const handler = (__argv) => { };
@@ -0,0 +1,4 @@
1
+ import { Argv } from "yargs";
2
+ export declare const builder: (yargs: Argv) => Argv<{
3
+ file: string;
4
+ }>;
@@ -0,0 +1,7 @@
1
+ export const builder = (yargs) => {
2
+ return yargs.positional("file", {
3
+ describe: "File path to process",
4
+ type: "string",
5
+ demandOption: true,
6
+ });
7
+ };
@@ -0,0 +1,5 @@
1
+ import "dotenv/config";
2
+ import { ToolCliArgs } from "../../../../types/cli_types.js";
3
+ export declare const handler: (argv: ToolCliArgs<{
4
+ file: string;
5
+ }>) => Promise<void>;
@@ -0,0 +1,100 @@
1
+ import "dotenv/config";
2
+ import { existsSync, createReadStream, writeFileSync, mkdirSync } from "fs";
3
+ import { resolve, basename, extname, join } from "path";
4
+ import OpenAI from "openai";
5
+ import { mulmoScriptSchema } from "../../../../types/index.js";
6
+ import { ffmpegGetMediaDuration } from "../../../../utils/ffmpeg_utils.js";
7
+ import { GraphAILogger } from "graphai";
8
+ const createMulmoScript = (fullPath, beats) => {
9
+ return mulmoScriptSchema.parse({
10
+ $mulmocast: {
11
+ version: "1.1",
12
+ credit: "closing",
13
+ },
14
+ canvasSize: {
15
+ width: 1536,
16
+ height: 1024,
17
+ },
18
+ lang: "en",
19
+ title: "Music Video",
20
+ captionParams: {
21
+ lang: "en",
22
+ styles: ["font-size: 64px", "width: 90%", "padding-left: 5%", "padding-right: 5%"],
23
+ },
24
+ beats,
25
+ audioParams: {
26
+ bgm: {
27
+ kind: "path",
28
+ path: fullPath,
29
+ },
30
+ padding: 0.0,
31
+ introPadding: 0.0,
32
+ closingPadding: 0.0,
33
+ outroPadding: 0.0,
34
+ bgmVolume: 1.0,
35
+ audioVolume: 0.0,
36
+ suppressSpeech: true,
37
+ },
38
+ });
39
+ };
40
+ export const handler = async (argv) => {
41
+ const { file } = argv;
42
+ const fullPath = resolve(file);
43
+ const filename = basename(file, extname(file));
44
+ if (!existsSync(fullPath)) {
45
+ GraphAILogger.error(`Error: File '${fullPath}' does not exist.`);
46
+ process.exit(1);
47
+ }
48
+ const apiKey = process.env.OPENAI_API_KEY;
49
+ if (!apiKey) {
50
+ GraphAILogger.error("Error: OPENAI_API_KEY environment variable is required");
51
+ process.exit(1);
52
+ }
53
+ try {
54
+ // Get audio duration using FFmpeg
55
+ const { duration: audioDuration } = await ffmpegGetMediaDuration(fullPath);
56
+ GraphAILogger.info(`Audio duration: ${audioDuration.toFixed(2)} seconds`);
57
+ const openai = new OpenAI({ apiKey });
58
+ const transcription = await openai.audio.transcriptions.create({
59
+ file: createReadStream(fullPath),
60
+ model: "whisper-1",
61
+ response_format: "verbose_json",
62
+ timestamp_granularities: ["word", "segment"],
63
+ });
64
+ if (transcription.segments) {
65
+ const starts = transcription.segments.map((segment) => segment.start);
66
+ starts[0] = 0;
67
+ starts.push(audioDuration);
68
+ // Create beats from transcription segments
69
+ const beats = transcription.segments.map((segment, index) => {
70
+ const duration = Math.round((starts[index + 1] - starts[index]) * 100) / 100;
71
+ return {
72
+ text: segment.text,
73
+ duration,
74
+ /*
75
+ image: {
76
+ type: "textSlide",
77
+ slide: {
78
+ title: "Place Holder",
79
+ },
80
+ },
81
+ */
82
+ };
83
+ });
84
+ // Create the script with the processed beats
85
+ const script = createMulmoScript(fullPath, beats);
86
+ // Save script to output directory
87
+ const outputDir = "output";
88
+ if (!existsSync(outputDir)) {
89
+ mkdirSync(outputDir, { recursive: true });
90
+ }
91
+ const outputPath = join(outputDir, `${filename}.json`);
92
+ writeFileSync(outputPath, JSON.stringify(script, null, 2));
93
+ GraphAILogger.info(`Script saved to: ${outputPath}`);
94
+ }
95
+ }
96
+ catch (error) {
97
+ GraphAILogger.error("Error transcribing audio:", error);
98
+ process.exit(1);
99
+ }
100
+ };
@@ -0,0 +1,4 @@
1
+ export declare const command = "whisper <file>";
2
+ export declare const desc = "Process file with whisper";
3
+ export { builder } from "./builder.js";
4
+ export { handler } from "./handler.js";
@@ -0,0 +1,4 @@
1
+ export const command = "whisper <file>";
2
+ export const desc = "Process file with whisper";
3
+ export { builder } from "./builder.js";
4
+ export { handler } from "./handler.js";
@@ -32,6 +32,8 @@ export declare const promptTemplates: ({
32
32
  ani?: undefined;
33
33
  presenter?: undefined;
34
34
  optimus?: undefined;
35
+ "[CHARACTER_1_ID]"?: undefined;
36
+ "[CHARACTER_2_ID]"?: undefined;
35
37
  };
36
38
  style: string;
37
39
  provider?: undefined;
@@ -99,6 +101,8 @@ export declare const promptTemplates: ({
99
101
  girl?: undefined;
100
102
  presenter?: undefined;
101
103
  optimus?: undefined;
104
+ "[CHARACTER_1_ID]"?: undefined;
105
+ "[CHARACTER_2_ID]"?: undefined;
102
106
  };
103
107
  style: string;
104
108
  provider?: undefined;
@@ -169,6 +173,8 @@ export declare const promptTemplates: ({
169
173
  ani?: undefined;
170
174
  presenter?: undefined;
171
175
  optimus?: undefined;
176
+ "[CHARACTER_1_ID]"?: undefined;
177
+ "[CHARACTER_2_ID]"?: undefined;
172
178
  };
173
179
  provider: string;
174
180
  style?: undefined;
@@ -286,6 +292,8 @@ export declare const promptTemplates: ({
286
292
  girl?: undefined;
287
293
  ani?: undefined;
288
294
  optimus?: undefined;
295
+ "[CHARACTER_1_ID]"?: undefined;
296
+ "[CHARACTER_2_ID]"?: undefined;
289
297
  };
290
298
  style: string;
291
299
  provider?: undefined;
@@ -356,6 +364,8 @@ export declare const promptTemplates: ({
356
364
  };
357
365
  girl?: undefined;
358
366
  ani?: undefined;
367
+ "[CHARACTER_1_ID]"?: undefined;
368
+ "[CHARACTER_2_ID]"?: undefined;
359
369
  };
360
370
  style: string;
361
371
  provider?: undefined;
@@ -503,6 +513,72 @@ export declare const promptTemplates: ({
503
513
  scriptName: string;
504
514
  systemPrompt: string;
505
515
  title: string;
516
+ } | {
517
+ description: string;
518
+ filename: string;
519
+ presentationStyle: {
520
+ $mulmocast: {
521
+ credit: string;
522
+ version: string;
523
+ };
524
+ audioParams: {
525
+ audioVolume: number;
526
+ bgmVolume: number;
527
+ closingPadding: number;
528
+ introPadding: number;
529
+ outroPadding: number;
530
+ padding: number;
531
+ suppressSpeech: boolean;
532
+ bgm?: undefined;
533
+ };
534
+ canvasSize: {
535
+ height: number;
536
+ width: number;
537
+ };
538
+ imageParams: {
539
+ images: {
540
+ "[CHARACTER_1_ID]": {
541
+ prompt: string;
542
+ type: string;
543
+ };
544
+ "[CHARACTER_2_ID]": {
545
+ prompt: string;
546
+ type: string;
547
+ };
548
+ girl?: undefined;
549
+ ani?: undefined;
550
+ presenter?: undefined;
551
+ optimus?: undefined;
552
+ };
553
+ style: string;
554
+ provider?: undefined;
555
+ };
556
+ movieParams: {
557
+ provider: string;
558
+ model?: undefined;
559
+ };
560
+ soundEffectParams: {
561
+ provider: string;
562
+ };
563
+ speechParams: {
564
+ speakers: {
565
+ Presenter: {
566
+ displayName: {
567
+ en: string;
568
+ };
569
+ voiceId: string;
570
+ lang?: undefined;
571
+ speechOptions?: undefined;
572
+ };
573
+ Announcer?: undefined;
574
+ Student?: undefined;
575
+ Teacher?: undefined;
576
+ };
577
+ };
578
+ };
579
+ scriptName: string;
580
+ systemPrompt: string;
581
+ title: string;
506
582
  } | {
507
583
  description: string;
508
584
  filename: string;
@@ -749,6 +749,61 @@ export const promptTemplates = [
749
749
  systemPrompt: "This script is for YouTube shorts. The first beat should be a hook, which describes the topic. Another AI will generate images for each beat based on the image prompt of that beat. Movie prompts must be written in English.",
750
750
  title: "Short movie template",
751
751
  },
752
+ {
753
+ description: "Template for Multi-character Story.",
754
+ filename: "sifi_story",
755
+ presentationStyle: {
756
+ $mulmocast: {
757
+ credit: "closing",
758
+ version: "1.1",
759
+ },
760
+ audioParams: {
761
+ audioVolume: 1,
762
+ bgmVolume: 0.2,
763
+ closingPadding: 0.8,
764
+ introPadding: 1,
765
+ outroPadding: 1,
766
+ padding: 0.3,
767
+ suppressSpeech: false,
768
+ },
769
+ canvasSize: {
770
+ height: 1024,
771
+ width: 1536,
772
+ },
773
+ imageParams: {
774
+ images: {
775
+ "[CHARACTER_1_ID]": {
776
+ prompt: "[IMAGE PROMPT FOR THIS CHARACTER]",
777
+ type: "imagePrompt",
778
+ },
779
+ "[CHARACTER_2_ID]": {
780
+ prompt: "[IMAGE PROMPT FOR THIS CHARACTER]",
781
+ type: "imagePrompt",
782
+ },
783
+ },
784
+ style: "<style>A dreamy, hyper-detailed anime style that blends photorealistic backgrounds with vibrant, saturated colors. The skies are often filled with luminous clouds, dazzling sunsets, or star-filled nights, rendered with a glowing, almost ethereal quality. Urban landscapes and rural scenery are meticulously illustrated, with attention to tiny details like reflections in puddles, neon lights, or the texture of grass swaying in the wind. Characters are drawn with soft, expressive features, standing out against the breathtaking environments, creating a sense of emotional depth and lyrical atmosphere. The overall mood is cinematic, romantic, and filled with a sense of fleeting beauty and longing.</style>",
785
+ },
786
+ movieParams: {
787
+ provider: "replicate",
788
+ },
789
+ soundEffectParams: {
790
+ provider: "replicate",
791
+ },
792
+ speechParams: {
793
+ speakers: {
794
+ Presenter: {
795
+ displayName: {
796
+ en: "Presenter",
797
+ },
798
+ voiceId: "shimmer",
799
+ },
800
+ },
801
+ },
802
+ },
803
+ scriptName: "story_with_characters.json",
804
+ systemPrompt: "Break the story into multiple beats, and put the story text in 'text' field. Generate image prompt for each character in the imageParams.images. Another AI will generate image for each beat based on its imagePrompt and specified characters in 'imageNames'. You don't need to repeat the image style in those image prompts. Use the JSON below as a template.",
805
+ title: "Multi-character Story",
806
+ },
752
807
  {
753
808
  description: "Template for A Movie Trailer.",
754
809
  filename: "trailer",
@@ -594,6 +594,26 @@ export declare const scriptTemplates: ({
594
594
  description?: undefined;
595
595
  canvasSize?: undefined;
596
596
  captionParams?: undefined;
597
+ } | {
598
+ $mulmocast: {
599
+ credit: string;
600
+ version: string;
601
+ };
602
+ beats: {
603
+ imageNames: string[];
604
+ imagePrompt: string;
605
+ text: string;
606
+ }[];
607
+ filename: string;
608
+ lang: string;
609
+ title: string;
610
+ references?: undefined;
611
+ htmlImageParams?: undefined;
612
+ imageParams?: undefined;
613
+ movieParams?: undefined;
614
+ description?: undefined;
615
+ canvasSize?: undefined;
616
+ captionParams?: undefined;
597
617
  } | {
598
618
  $mulmocast: {
599
619
  credit: string;
@@ -1001,6 +1001,32 @@ export const scriptTemplates = [
1001
1001
  },
1002
1002
  title: "[TITLE: Brief, engaging title for the topic]",
1003
1003
  },
1004
+ {
1005
+ $mulmocast: {
1006
+ credit: "closing",
1007
+ version: "1.1",
1008
+ },
1009
+ beats: [
1010
+ {
1011
+ imageNames: ["[CHARACTER_ID_1]", "[CHARACTER_ID_2]"],
1012
+ imagePrompt: "[IMAGE_PROMPT FOR THIS BEAT with both characters]",
1013
+ text: "[STORY TEXT FOR THIS BEAT FOR THIS BEAT]",
1014
+ },
1015
+ {
1016
+ imageNames: ["[CHARACTER_ID_1]"],
1017
+ imagePrompt: "[IMAGE_PROMPT FOR THIS BEAT with a single character]",
1018
+ text: "[STORY TEXT FOR THIS BEAT FOR THIS BEAT]",
1019
+ },
1020
+ {
1021
+ imageNames: ["[CHARACTER_ID_2]"],
1022
+ imagePrompt: "[IMAGE_PROMPT FOR THIS BEAT with another character]",
1023
+ text: "[STORY TEXT FOR THIS BEAT FOR THIS BEAT]",
1024
+ },
1025
+ ],
1026
+ filename: "story_with_characters",
1027
+ lang: "en",
1028
+ title: "[TITLE: Brief, engaging title for the topic]",
1029
+ },
1004
1030
  {
1005
1031
  $mulmocast: {
1006
1032
  credit: "closing",
@@ -15,5 +15,6 @@ export declare const templateDataSet: {
15
15
  realistic_movie: string;
16
16
  sensei_and_taro: string;
17
17
  shorts: string;
18
+ sifi_story: string;
18
19
  trailer: string;
19
20
  };
@@ -63,6 +63,10 @@ export const templateDataSet = {
63
63
  "```JSON\n" +
64
64
  `{"$mulmocast":{"version":"1.1"},"title":"[TITLE: Brief, engaging title for the topic]","lang":"en","references":[{"url":"[SOURCE_URL: URL of the source material]","title":"[SOURCE_TITLE: Title of the referenced article, or paper]","type":"[SOURCE_TYPE: article, paper]"}],"movieParams":{"provider":"google"},"beats":[{"text":"[OPENING_BEAT: Introduce the topic with a hook. Reference the source material and set up why this topic matters. Usually 2-3 sentences that grab attention and provide context.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"text":"[MAIN_CONCEPT: Define or explain the core concept/idea. This should be the central focus of your narrative. Keep it clear and accessible.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"text":"[SUPPORTING_DETAIL_1: Additional context, examples, or elaboration that helps illustrate the main concept. This could include how it works, why it's important, or real-world applications.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"text":"[SUPPORTING_DETAIL_2: Continue with more examples, deeper explanation, or different aspects of the topic if needed.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"text":"[ADDITIONAL_BEATS: Add more beats as necessary to fully explore the topic. Complex topics may require 6-10+ beats to cover adequately. Each beat should advance the narrative or provide valuable information.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"text":"[CONCLUSION/IMPACT: Wrap up with the significance, implications, or key takeaway. Help the audience understand why this matters to them.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"}],"canvasSize":{"width":720,"height":1280},"imageParams":{"style":"<style>Photo realistic, cinematic.</style>"}}\n` +
65
65
  "```",
66
+ sifi_story: "Break the story into multiple beats, and put the story text in 'text' field. Generate image prompt for each character in the imageParams.images. Another AI will generate image for each beat based on its imagePrompt and specified characters in 'imageNames'. You don't need to repeat the image style in those image prompts. Use the JSON below as a template.\n" +
67
+ "```JSON\n" +
68
+ '{"$mulmocast":{"version":"1.1","credit":"closing"},"title":"[TITLE: Brief, engaging title for the topic]","lang":"en","beats":[{"text":"[STORY TEXT FOR THIS BEAT FOR THIS BEAT]","imagePrompt":"[IMAGE_PROMPT FOR THIS BEAT with both characters]","imageNames":["[CHARACTER_ID_1]","[CHARACTER_ID_2]"]},{"text":"[STORY TEXT FOR THIS BEAT FOR THIS BEAT]","imagePrompt":"[IMAGE_PROMPT FOR THIS BEAT with a single character]","imageNames":["[CHARACTER_ID_1]"]},{"text":"[STORY TEXT FOR THIS BEAT FOR THIS BEAT]","imagePrompt":"[IMAGE_PROMPT FOR THIS BEAT with another character]","imageNames":["[CHARACTER_ID_2]"]}],"canvasSize":{"width":1536,"height":1024},"imageParams":{"style":"<style>A dreamy, hyper-detailed anime style that blends photorealistic backgrounds with vibrant, saturated colors. The skies are often filled with luminous clouds, dazzling sunsets, or star-filled nights, rendered with a glowing, almost ethereal quality. Urban landscapes and rural scenery are meticulously illustrated, with attention to tiny details like reflections in puddles, neon lights, or the texture of grass swaying in the wind. Characters are drawn with soft, expressive features, standing out against the breathtaking environments, creating a sense of emotional depth and lyrical atmosphere. The overall mood is cinematic, romantic, and filled with a sense of fleeting beauty and longing.</style>","images":{"[CHARACTER_1_ID]":{"type":"imagePrompt","prompt":"[IMAGE PROMPT FOR THIS CHARACTER]"},"[CHARACTER_2_ID]":{"type":"imagePrompt","prompt":"[IMAGE PROMPT FOR THIS CHARACTER]"}}}}\n' +
69
+ "```",
66
70
  trailer: "This script is for a movie trailer. Another AI will generate images for each beat based on the image prompt of that beat. Movie prompts must be written in English.\n" +
67
71
  "```JSON\n" +
68
72
  '{"$mulmocast":{"version":"1.1"},"title":"[TITLE: Brief, engaging title for the topic]","lang":"en","references":[{"url":"[SOURCE_URL: URL of the source material]","title":"[SOURCE_TITLE: Title of the referenced article, or paper]","type":"[SOURCE_TYPE: article, paper]"}],"movieParams":{"provider":"google"},"beats":[{"duration":5,"imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"duration":5,"imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"duration":5,"imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"duration":5,"imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"duration":5,"imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"},{"duration":5,"imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]","moviePrompt":"[MOVIE_PROMPT: A movie prompt for that image.]"}],"canvasSize":{"width":1280,"height":720},"imageParams":{"style":"<style>Photo realistic, cinematic.</style>"},"audioParams":{"padding":0,"introPadding":0,"closingPadding":0,"outroPadding":2.5,"bgm":{"kind":"url","url":"https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/bgms/trailer_dramatic.mp3"}}}\n' +
@@ -5702,6 +5702,7 @@ export declare const mulmoStudioBeatSchema: z.ZodObject<{
5702
5702
  soundEffectFile: z.ZodOptional<z.ZodString>;
5703
5703
  lipSyncFile: z.ZodOptional<z.ZodString>;
5704
5704
  captionFile: z.ZodOptional<z.ZodString>;
5705
+ htmlImageFile: z.ZodOptional<z.ZodString>;
5705
5706
  }, "strict", z.ZodTypeAny, {
5706
5707
  duration?: number | undefined;
5707
5708
  startAt?: number | undefined;
@@ -5716,6 +5717,7 @@ export declare const mulmoStudioBeatSchema: z.ZodObject<{
5716
5717
  soundEffectFile?: string | undefined;
5717
5718
  lipSyncFile?: string | undefined;
5718
5719
  captionFile?: string | undefined;
5720
+ htmlImageFile?: string | undefined;
5719
5721
  }, {
5720
5722
  duration?: number | undefined;
5721
5723
  startAt?: number | undefined;
@@ -5730,6 +5732,7 @@ export declare const mulmoStudioBeatSchema: z.ZodObject<{
5730
5732
  soundEffectFile?: string | undefined;
5731
5733
  lipSyncFile?: string | undefined;
5732
5734
  captionFile?: string | undefined;
5735
+ htmlImageFile?: string | undefined;
5733
5736
  }>;
5734
5737
  export declare const mulmoStudioMultiLingualDataSchema: z.ZodObject<{
5735
5738
  multiLingualTexts: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -8325,6 +8328,7 @@ export declare const mulmoStudioSchema: z.ZodObject<{
8325
8328
  soundEffectFile: z.ZodOptional<z.ZodString>;
8326
8329
  lipSyncFile: z.ZodOptional<z.ZodString>;
8327
8330
  captionFile: z.ZodOptional<z.ZodString>;
8331
+ htmlImageFile: z.ZodOptional<z.ZodString>;
8328
8332
  }, "strict", z.ZodTypeAny, {
8329
8333
  duration?: number | undefined;
8330
8334
  startAt?: number | undefined;
@@ -8339,6 +8343,7 @@ export declare const mulmoStudioSchema: z.ZodObject<{
8339
8343
  soundEffectFile?: string | undefined;
8340
8344
  lipSyncFile?: string | undefined;
8341
8345
  captionFile?: string | undefined;
8346
+ htmlImageFile?: string | undefined;
8342
8347
  }, {
8343
8348
  duration?: number | undefined;
8344
8349
  startAt?: number | undefined;
@@ -8353,6 +8358,7 @@ export declare const mulmoStudioSchema: z.ZodObject<{
8353
8358
  soundEffectFile?: string | undefined;
8354
8359
  lipSyncFile?: string | undefined;
8355
8360
  captionFile?: string | undefined;
8361
+ htmlImageFile?: string | undefined;
8356
8362
  }>, "many">;
8357
8363
  }, "strict", z.ZodTypeAny, {
8358
8364
  beats: {
@@ -8369,6 +8375,7 @@ export declare const mulmoStudioSchema: z.ZodObject<{
8369
8375
  soundEffectFile?: string | undefined;
8370
8376
  lipSyncFile?: string | undefined;
8371
8377
  captionFile?: string | undefined;
8378
+ htmlImageFile?: string | undefined;
8372
8379
  }[];
8373
8380
  script: {
8374
8381
  lang: string;
@@ -8710,6 +8717,7 @@ export declare const mulmoStudioSchema: z.ZodObject<{
8710
8717
  soundEffectFile?: string | undefined;
8711
8718
  lipSyncFile?: string | undefined;
8712
8719
  captionFile?: string | undefined;
8720
+ htmlImageFile?: string | undefined;
8713
8721
  }[];
8714
8722
  script: {
8715
8723
  lang: string;
@@ -396,6 +396,7 @@ export const mulmoStudioBeatSchema = z
396
396
  soundEffectFile: z.string().optional(), // path to the sound effect file
397
397
  lipSyncFile: z.string().optional(), // path to the lip sync file
398
398
  captionFile: z.string().optional(), // path to the caption image
399
+ htmlImageFile: z.string().optional(), // path to the html image
399
400
  })
400
401
  .strict();
401
402
  export const mulmoStudioMultiLingualDataSchema = z.object({
@@ -1,3 +1,4 @@
1
+ import { type CallbackFunction } from "graphai";
1
2
  import { langSchema, localizedTextSchema, mulmoBeatSchema, mulmoScriptSchema, mulmoStudioSchema, mulmoStudioBeatSchema, mulmoStoryboardSchema, mulmoStoryboardSceneSchema, mulmoStudioMultiLingualSchema, mulmoStudioMultiLingualDataSchema, mulmoStudioMultiLingualFileSchema, speakerDictionarySchema, mulmoImageParamsSchema, mulmoImageParamsImagesSchema, mulmoFillOptionSchema, mulmoMovieParamsSchema, textSlideParamsSchema, speechOptionsSchema, speakerDataSchema, mulmoCanvasDimensionSchema, mulmoPromptTemplateSchema, mulmoPromptTemplateFileSchema, text2ImageProviderSchema, text2HtmlImageProviderSchema, text2MovieProviderSchema, text2SpeechProviderSchema, mulmoPresentationStyleSchema, multiLingualTextsSchema, mulmoImageAssetSchema, mulmoMermaidMediaSchema, mulmoTextSlideMediaSchema, mulmoMarkdownMediaSchema, mulmoImageMediaSchema, mulmoChartMediaSchema, mediaSourceSchema, mulmoSessionStateSchema, mulmoOpenAIImageModelSchema, mulmoGoogleImageModelSchema, mulmoGoogleMovieModelSchema, mulmoReplicateMovieModelSchema, mulmoImagePromptMediaSchema } from "./schema.js";
2
3
  import { pdf_modes, pdf_sizes, storyToScriptGenerateMode } from "../utils/const.js";
3
4
  import type { LLM } from "../utils/provider2agent.js";
@@ -128,3 +129,7 @@ export type InitOptions = {
128
129
  c?: string;
129
130
  p?: string;
130
131
  };
132
+ export type PublicAPIArgs = {
133
+ settings?: Record<string, string>;
134
+ callbacks?: CallbackFunction[];
135
+ };
@@ -14,6 +14,7 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
14
14
  soundEffectFile?: string | undefined;
15
15
  lipSyncFile?: string | undefined;
16
16
  captionFile?: string | undefined;
17
+ htmlImageFile?: string | undefined;
17
18
  }[];
18
19
  script: {
19
20
  lang: string;
@@ -360,6 +361,7 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
360
361
  soundEffectFile?: string | undefined;
361
362
  lipSyncFile?: string | undefined;
362
363
  captionFile?: string | undefined;
364
+ htmlImageFile?: string | undefined;
363
365
  }[];
364
366
  script: {
365
367
  lang: string;
@@ -24,7 +24,10 @@ export declare const getAudioFilePath: (audioDirPath: string, dirName: string, f
24
24
  export declare const getAudioArtifactFilePath: (context: MulmoStudioContext) => string;
25
25
  export declare const getOutputVideoFilePath: (outDirPath: string, fileName: string, lang?: string, caption?: string) => string;
26
26
  export declare const imageSuffix = "p";
27
- export declare const getBeatPngImagePath: (context: MulmoStudioContext, index: number) => string;
27
+ export declare const getBeatPngImagePath: (context: MulmoStudioContext, index: number) => {
28
+ imagePath: string;
29
+ htmlImageFile: string;
30
+ };
28
31
  export declare const getBeatMoviePaths: (context: MulmoStudioContext, index: number) => {
29
32
  movieFile: string;
30
33
  soundEffectFile: string;
package/lib/utils/file.js CHANGED
@@ -89,10 +89,10 @@ export const imageSuffix = "p";
89
89
  export const getBeatPngImagePath = (context, index) => {
90
90
  const imageProjectDirPath = MulmoStudioContextMethods.getImageProjectDirPath(context);
91
91
  const beat = context.studio.script.beats[index]; // beat could be undefined only in a test case.
92
- if (beat?.id) {
93
- return `${imageProjectDirPath}/${beat.id}.png`;
94
- }
95
- return `${imageProjectDirPath}/${index}${imageSuffix}.png`;
92
+ const filename = beat?.id ? `${beat.id}` : `${index}${imageSuffix}`;
93
+ const imagePath = `${imageProjectDirPath}/${filename}.png`;
94
+ const htmlImageFile = `${imageProjectDirPath}/${filename}_html.png`;
95
+ return { imagePath, htmlImageFile };
96
96
  };
97
97
  export const getBeatMoviePaths = (context, index) => {
98
98
  const imageProjectDirPath = MulmoStudioContextMethods.getImageProjectDirPath(context);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mulmocast",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.node.js",
@@ -50,6 +50,7 @@
50
50
  "prompt": "npx tsx ./src/cli/bin.ts tool prompt",
51
51
  "schema": "npx tsx ./src/cli/bin.ts tool schema",
52
52
  "story_to_script": "npx tsx ./src/cli/bin.ts tool story_to_script",
53
+ "whisper": "npx tsx ./src/cli/bin.ts tool whisper",
53
54
  "latest": "yarn upgrade-interactive --latest",
54
55
  "format": "prettier --write '{src,scripts,assets/templates,assets/styles,draft,ideason,scripts_mag2,proto,test,batch,graphai,output,docs/scripts}/**/*.{ts,json,yaml}'",
55
56
  "deep_research": "npx tsx ./src/tools/deep_research.ts",
@@ -66,29 +67,29 @@
66
67
  "homepage": "https://github.com/receptron/mulmocast-cli#readme",
67
68
  "dependencies": {
68
69
  "@google-cloud/text-to-speech": "^6.2.0",
69
- "@google/genai": "^1.13.0",
70
- "@graphai/anthropic_agent": "^2.0.9",
70
+ "@google/genai": "^1.15.0",
71
+ "@graphai/anthropic_agent": "^2.0.11",
71
72
  "@graphai/browserless_agent": "^2.0.1",
72
73
  "@graphai/gemini_agent": "^2.0.1",
73
74
  "@graphai/groq_agent": "^2.0.2",
74
75
  "@graphai/input_agents": "^1.0.2",
75
- "@graphai/openai_agent": "^2.0.4",
76
+ "@graphai/openai_agent": "^2.0.7",
76
77
  "@graphai/stream_agent_filter": "^2.0.2",
77
- "@graphai/vanilla": "^2.0.10",
78
- "@graphai/vanilla_node_agents": "^2.0.1",
78
+ "@graphai/vanilla": "^2.0.12",
79
+ "@graphai/vanilla_node_agents": "^2.0.4",
79
80
  "@inquirer/input": "^4.2.1",
80
81
  "@inquirer/select": "^4.3.1",
81
- "@modelcontextprotocol/sdk": "^1.15.1",
82
- "@tavily/core": "^0.5.9",
83
- "canvas": "^3.1.2",
82
+ "@modelcontextprotocol/sdk": "^1.17.3",
83
+ "@tavily/core": "^0.5.11",
84
+ "canvas": "^3.2.0",
84
85
  "clipboardy": "^4.0.0",
85
86
  "dotenv": "^17.2.1",
86
87
  "fluent-ffmpeg": "^2.1.3",
87
88
  "graphai": "^2.0.14",
88
- "marked": "^16.1.2",
89
+ "marked": "^16.2.0",
89
90
  "ora": "^8.2.0",
90
- "puppeteer": "^24.16.2",
91
- "replicate": "^1.0.1",
91
+ "puppeteer": "^24.17.0",
92
+ "replicate": "^1.1.0",
92
93
  "yaml": "^2.8.1",
93
94
  "yargs": "^18.0.0",
94
95
  "zod": "^3.25.76",
@@ -97,7 +98,7 @@
97
98
  "devDependencies": {
98
99
  "@anatine/zod-mock": "^3.14.0",
99
100
  "@faker-js/faker": "^9.9.0",
100
- "@receptron/test_utils": "^2.0.1",
101
+ "@receptron/test_utils": "^2.0.3",
101
102
  "@types/fluent-ffmpeg": "^2.1.26",
102
103
  "@types/yargs": "^17.0.33",
103
104
  "eslint": "^9.33.0",
@@ -108,7 +109,7 @@
108
109
  "ts-node": "^10.9.2",
109
110
  "tsx": "^4.20.4",
110
111
  "typescript": "^5.9.2",
111
- "typescript-eslint": "^8.39.1"
112
+ "typescript-eslint": "^8.40.0"
112
113
  },
113
114
  "engines": {
114
115
  "node": ">=18.0.0"
@@ -0,0 +1,25 @@
1
+ {
2
+ "$mulmocast": {
3
+ "version": "1.1",
4
+ "credit": "closing"
5
+ },
6
+ "title": "[TITLE: Brief, engaging title for the topic]",
7
+ "lang": "en",
8
+ "beats": [
9
+ {
10
+ "text": "[STORY TEXT FOR THIS BEAT FOR THIS BEAT]",
11
+ "imagePrompt": "[IMAGE_PROMPT FOR THIS BEAT with both characters]",
12
+ "imageNames": ["[CHARACTER_ID_1]", "[CHARACTER_ID_2]"]
13
+ },
14
+ {
15
+ "text": "[STORY TEXT FOR THIS BEAT FOR THIS BEAT]",
16
+ "imagePrompt": "[IMAGE_PROMPT FOR THIS BEAT with a single character]",
17
+ "imageNames": ["[CHARACTER_ID_1]"]
18
+ },
19
+ {
20
+ "text": "[STORY TEXT FOR THIS BEAT FOR THIS BEAT]",
21
+ "imagePrompt": "[IMAGE_PROMPT FOR THIS BEAT with another character]",
22
+ "imageNames": ["[CHARACTER_ID_2]"]
23
+ }
24
+ ]
25
+ }