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.
Files changed (127) hide show
  1. package/lib/actions/movie.d.ts +2 -9
  2. package/lib/actions/movie.js +149 -147
  3. package/lib/agents/image_google_agent.d.ts +5 -0
  4. package/lib/agents/image_google_agent.js +86 -0
  5. package/lib/agents/movie_google_agent.d.ts +9 -0
  6. package/lib/agents/movie_google_agent.js +122 -0
  7. package/lib/agents/mulmo_prompts_agent.d.ts +7 -0
  8. package/lib/agents/mulmo_prompts_agent.js +37 -0
  9. package/lib/agents/prompts_data.d.ts +15 -0
  10. package/lib/agents/prompts_data.js +16 -0
  11. package/lib/agents/tts_nijivoice_agent.d.ts +5 -0
  12. package/lib/agents/tts_nijivoice_agent.js +69 -0
  13. package/lib/agents/validate_mulmo_script_agent.d.ts +17 -0
  14. package/lib/agents/validate_mulmo_script_agent.js +34 -0
  15. package/lib/cli/args.d.ts +15 -0
  16. package/lib/cli/args.js +62 -0
  17. package/lib/cli/bin.js +0 -0
  18. package/lib/cli/cli.d.ts +17 -0
  19. package/lib/cli/cli.js +117 -0
  20. package/lib/cli/run.d.ts +2 -0
  21. package/lib/cli/run.js +3 -0
  22. package/lib/cli/tool-args.d.ts +16 -0
  23. package/lib/cli/tool-args.js +64 -0
  24. package/lib/cli/tool-cli.d.ts +2 -0
  25. package/lib/cli/tool-cli.js +32 -0
  26. package/lib/cli/tool_commands/prompt/builder.d.ts +4 -0
  27. package/lib/cli/tool_commands/prompt/builder.js +11 -0
  28. package/lib/cli/tool_commands/prompt/handler.d.ts +4 -0
  29. package/lib/cli/tool_commands/prompt/handler.js +14 -0
  30. package/lib/cli/tool_commands/prompt/index.d.ts +4 -0
  31. package/lib/cli/tool_commands/prompt/index.js +4 -0
  32. package/lib/cli/tool_commands/schema/builder.d.ts +2 -0
  33. package/lib/cli/tool_commands/schema/builder.js +3 -0
  34. package/lib/cli/tool_commands/schema/handler.d.ts +2 -0
  35. package/lib/cli/tool_commands/schema/handler.js +12 -0
  36. package/lib/cli/tool_commands/schema/index.d.ts +4 -0
  37. package/lib/cli/tool_commands/schema/index.js +4 -0
  38. package/lib/cli/tool_commands/scripting/builder.d.ts +20 -0
  39. package/lib/cli/tool_commands/scripting/builder.js +63 -0
  40. package/lib/cli/tool_commands/scripting/handler.d.ts +12 -0
  41. package/lib/cli/tool_commands/scripting/handler.js +36 -0
  42. package/lib/cli/tool_commands/scripting/index.d.ts +4 -0
  43. package/lib/cli/tool_commands/scripting/index.js +4 -0
  44. package/lib/index.d.ts +8 -0
  45. package/lib/index.js +8 -0
  46. package/lib/methods/mulmo_beat.d.ts +3 -0
  47. package/lib/methods/mulmo_beat.js +17 -0
  48. package/lib/methods/mulmo_beat_node.d.ts +3 -0
  49. package/lib/methods/mulmo_presentation_style.d.ts +5 -1
  50. package/lib/methods/mulmo_presentation_style.js +43 -1
  51. package/lib/methods/mulmo_script_template.d.ts +4 -0
  52. package/lib/methods/mulmo_script_template.js +6 -0
  53. package/lib/methods/mulmo_studio.d.ts +8 -0
  54. package/lib/methods/mulmo_studio.js +25 -0
  55. package/lib/methods/mulmo_studio_context.d.ts +6 -1
  56. package/lib/methods/mulmo_studio_context.js +36 -1
  57. package/lib/slide/blocks.d.ts +13 -0
  58. package/lib/slide/blocks.js +251 -0
  59. package/lib/slide/index.d.ts +6 -0
  60. package/lib/slide/index.js +7 -0
  61. package/lib/slide/layouts/big_quote.d.ts +2 -0
  62. package/lib/slide/layouts/big_quote.js +19 -0
  63. package/lib/slide/layouts/columns.d.ts +2 -0
  64. package/lib/slide/layouts/columns.js +53 -0
  65. package/lib/slide/layouts/comparison.d.ts +2 -0
  66. package/lib/slide/layouts/comparison.js +25 -0
  67. package/lib/slide/layouts/funnel.d.ts +2 -0
  68. package/lib/slide/layouts/funnel.js +25 -0
  69. package/lib/slide/layouts/grid.d.ts +2 -0
  70. package/lib/slide/layouts/grid.js +38 -0
  71. package/lib/slide/layouts/index.d.ts +3 -0
  72. package/lib/slide/layouts/index.js +46 -0
  73. package/lib/slide/layouts/matrix.d.ts +2 -0
  74. package/lib/slide/layouts/matrix.js +53 -0
  75. package/lib/slide/layouts/split.d.ts +2 -0
  76. package/lib/slide/layouts/split.js +51 -0
  77. package/lib/slide/layouts/stats.d.ts +2 -0
  78. package/lib/slide/layouts/stats.js +23 -0
  79. package/lib/slide/layouts/table.d.ts +2 -0
  80. package/lib/slide/layouts/table.js +10 -0
  81. package/lib/slide/layouts/timeline.d.ts +2 -0
  82. package/lib/slide/layouts/timeline.js +27 -0
  83. package/lib/slide/layouts/title.d.ts +2 -0
  84. package/lib/slide/layouts/title.js +19 -0
  85. package/lib/slide/layouts/waterfall.d.ts +2 -0
  86. package/lib/slide/layouts/waterfall.js +63 -0
  87. package/lib/slide/render.d.ts +17 -0
  88. package/lib/slide/render.js +101 -0
  89. package/lib/slide/schema.d.ts +9309 -0
  90. package/lib/slide/schema.js +434 -0
  91. package/lib/slide/utils.d.ts +76 -0
  92. package/lib/slide/utils.js +243 -0
  93. package/lib/tools/prompt.d.ts +1 -0
  94. package/lib/tools/prompt.js +18 -0
  95. package/lib/tools/seed.d.ts +3 -0
  96. package/lib/tools/seed.js +211 -0
  97. package/lib/tools/seed_from_url.d.ts +3 -0
  98. package/lib/tools/seed_from_url.js +188 -0
  99. package/lib/types/slide.d.ts +9309 -0
  100. package/lib/types/slide.js +434 -0
  101. package/lib/utils/assets.d.ts +18 -0
  102. package/lib/utils/assets.js +101 -0
  103. package/lib/utils/cli.d.ts +1 -0
  104. package/lib/utils/cli.js +15 -0
  105. package/lib/utils/const.d.ts +12 -0
  106. package/lib/utils/const.js +12 -0
  107. package/lib/utils/filters.js +11 -2
  108. package/lib/utils/image_preprocess.d.ts +14 -0
  109. package/lib/utils/image_preprocess.js +52 -0
  110. package/lib/utils/markdown.d.ts +3 -0
  111. package/lib/utils/markdown.js +42 -0
  112. package/lib/utils/pdf.d.ts +9 -0
  113. package/lib/utils/pdf.js +77 -0
  114. package/lib/utils/preprocess.d.ts +343 -0
  115. package/lib/utils/preprocess.js +58 -0
  116. package/lib/utils/provider2agent.d.ts +141 -0
  117. package/lib/utils/provider2agent.js +267 -0
  118. package/lib/utils/text_hash.d.ts +1 -0
  119. package/lib/utils/text_hash.js +4 -0
  120. package/package.json +8 -8
  121. package/scripts/test/test_voice_over_transition_no_audio.json +71 -0
  122. package/lib/utils/sdk_timeout.d.ts +0 -3
  123. package/lib/utils/sdk_timeout.js +0 -7
  124. package/scripts/test/test_captions_2.json~ +0 -278
  125. package/scripts/test/test_en_portrait.json~ +0 -31
  126. package/scripts/test/test_slide_image_ref_gemini_en.json~ +0 -289
  127. package/scripts/test/test_vertexai.json~ +0 -21
@@ -0,0 +1,52 @@
1
+ import { MulmoStudioContextMethods } from "../methods/index.js";
2
+ import { getHTMLFile } from "./file.js";
3
+ import { renderMarkdownToImage, renderHTMLToImage, interpolate } from "./markdown.js";
4
+ export const processTextSlide = async (params) => {
5
+ const { beat, imagePath, textSlideStyle, canvasSize } = params;
6
+ if (!beat.image || beat.image.type !== "textSlide")
7
+ return;
8
+ const slide = beat.image.slide;
9
+ const markdown = `# ${slide.title}\n` + slide.bullets.map((text) => `- ${text}`).join("\n");
10
+ await renderMarkdownToImage(markdown, textSlideStyle, imagePath, canvasSize.width, canvasSize.height);
11
+ };
12
+ export const processMarkdown = async (params) => {
13
+ const { beat, imagePath, textSlideStyle, canvasSize } = params;
14
+ if (!beat.image || beat.image.type !== "markdown")
15
+ return;
16
+ const markdown = Array.isArray(beat.image.markdown) ? beat.image.markdown.join("\n") : beat.image.markdown;
17
+ await renderMarkdownToImage(markdown, textSlideStyle, imagePath, canvasSize.width, canvasSize.height);
18
+ };
19
+ export const processImage = (params) => {
20
+ const { beat, context } = params;
21
+ if (!beat.image || beat.image.type !== "image")
22
+ return;
23
+ if (beat.image.source.kind === "url") {
24
+ return beat.image.source.url;
25
+ }
26
+ else if (beat.image.source.kind === "path") {
27
+ return MulmoStudioContextMethods.resolveAssetPath(context, beat.image.source.path);
28
+ }
29
+ };
30
+ export const processChart = async (params) => {
31
+ const { beat, imagePath, canvasSize } = params;
32
+ if (!beat.image || beat.image.type !== "chart")
33
+ return;
34
+ const template = getHTMLFile("chart");
35
+ const htmlData = interpolate(template, {
36
+ title: beat.image.title,
37
+ width: Math.round(canvasSize.width * 0.625).toString(),
38
+ chart_data: JSON.stringify(beat.image.chartData),
39
+ });
40
+ await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height);
41
+ };
42
+ export const processMermaid = async (params) => {
43
+ const { beat, imagePath, canvasSize } = params;
44
+ if (!beat.image || beat.image.type !== "mermaid")
45
+ return;
46
+ const template = getHTMLFile("mermaid");
47
+ const htmlData = interpolate(template, {
48
+ title: beat.image.title,
49
+ diagram_code: beat.image.code,
50
+ });
51
+ await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height);
52
+ };
@@ -0,0 +1,3 @@
1
+ export declare const renderHTMLToImage: (html: string, outputPath: string, width: number, height: number, isMermaid?: boolean, omitBackground?: boolean) => Promise<void>;
2
+ export declare const renderMarkdownToImage: (markdown: string, style: string, outputPath: string, width: number, height: number) => Promise<void>;
3
+ export declare const interpolate: (template: string, data: Record<string, string>) => string;
@@ -0,0 +1,42 @@
1
+ import { marked } from "marked";
2
+ import puppeteer from "puppeteer";
3
+ const isCI = process.env.CI === "true";
4
+ export const renderHTMLToImage = async (html, outputPath, width, height, isMermaid = false, omitBackground = false) => {
5
+ // Use Puppeteer to render HTML to an image
6
+ const browser = await puppeteer.launch({
7
+ args: isCI ? ["--no-sandbox"] : [],
8
+ });
9
+ const page = await browser.newPage();
10
+ // Set the page content to the HTML generated from the Markdown
11
+ await page.setContent(html);
12
+ // Adjust page settings if needed (like width, height, etc.)
13
+ await page.setViewport({ width, height });
14
+ await page.addStyleTag({ content: "html,body{margin:0;padding:0;overflow:hidden}" });
15
+ if (isMermaid) {
16
+ await page.waitForFunction(() => {
17
+ const el = document.querySelector(".mermaid");
18
+ return el && el.dataset.ready === "true";
19
+ }, { timeout: 20000 });
20
+ }
21
+ // Measure the size of the page and scale the page to the width and height
22
+ await page.evaluate(({ vw, vh }) => {
23
+ const de = document.documentElement;
24
+ const sw = Math.max(de.scrollWidth, document.body.scrollWidth || 0);
25
+ const sh = Math.max(de.scrollHeight, document.body.scrollHeight || 0);
26
+ const scale = Math.min(vw / (sw || vw), vh / (sh || vh), 1); // <=1 で縮小のみ
27
+ de.style.overflow = "hidden";
28
+ document.body.style.zoom = String(scale);
29
+ }, { vw: width, vh: height });
30
+ // Step 3: Capture screenshot of the page (which contains the Markdown-rendered HTML)
31
+ await page.screenshot({ path: outputPath, omitBackground });
32
+ await browser.close();
33
+ };
34
+ export const renderMarkdownToImage = async (markdown, style, outputPath, width, height) => {
35
+ const header = `<head><style>${style}</style></head>`;
36
+ const body = await marked(markdown);
37
+ const html = `<html>${header}<body>${body}</body></html>`;
38
+ await renderHTMLToImage(html, outputPath, width, height);
39
+ };
40
+ export const interpolate = (template, data) => {
41
+ return template.replace(/\$\{(.*?)\}/g, (_, key) => data[key.trim()] ?? "");
42
+ };
@@ -0,0 +1,9 @@
1
+ import { PDFFont } from "pdf-lib";
2
+ export declare const fontSize = 12;
3
+ export declare const textMargin = 6;
4
+ export declare const drawSize: (fitWidth: boolean, expectWidth: number, expectHeight: number, origWidth: number, origHeight: number) => {
5
+ containerWidth: number;
6
+ drawWidth: number;
7
+ drawHeight: number;
8
+ };
9
+ export declare const wrapText: (text: string, font: PDFFont, fontSize: number, maxWidth: number) => string[];
@@ -0,0 +1,77 @@
1
+ export const fontSize = 12;
2
+ export const textMargin = 6;
3
+ export const drawSize = (fitWidth, expectWidth, expectHeight, origWidth, origHeight) => {
4
+ if (fitWidth) {
5
+ const drawWidth = expectWidth;
6
+ const scale = drawWidth / origWidth;
7
+ const drawHeight = origHeight * scale;
8
+ return {
9
+ containerWidth: expectWidth,
10
+ drawWidth,
11
+ drawHeight,
12
+ };
13
+ }
14
+ const drawHeight = expectHeight;
15
+ const scale = drawHeight / origHeight;
16
+ const drawWidth = origWidth * scale;
17
+ return {
18
+ containerWidth: expectWidth,
19
+ drawWidth,
20
+ drawHeight,
21
+ };
22
+ };
23
+ const isFullwidth = (char) => {
24
+ const code = char.charCodeAt(0);
25
+ return ((code >= 0x3000 && code <= 0x9fff) || // CJK, punctuation, kana
26
+ (code >= 0xff00 && code <= 0xffef) // Fullwidth ASCII
27
+ );
28
+ };
29
+ export const wrapText = (text, font, fontSize, maxWidth) => {
30
+ const lines = [];
31
+ for (const rawLine of text.split("\n")) {
32
+ let line = "";
33
+ let buffer = "";
34
+ for (let i = 0; i < rawLine.length; i++) {
35
+ const char = rawLine[i];
36
+ buffer += char;
37
+ const width = font.widthOfTextAtSize(line + buffer, fontSize);
38
+ const nextChar = rawLine[i + 1];
39
+ const currentIsFull = isFullwidth(char);
40
+ const nextIsFull = nextChar && isFullwidth(nextChar);
41
+ const isBreakable = currentIsFull || (!currentIsFull && (char === " " || nextChar === " " || nextIsFull));
42
+ if (width > maxWidth && line) {
43
+ lines.push(line);
44
+ line = buffer;
45
+ buffer = "";
46
+ }
47
+ if (isBreakable || i === rawLine.length - 1) {
48
+ line += buffer;
49
+ buffer = "";
50
+ }
51
+ }
52
+ if (line)
53
+ lines.push(line);
54
+ }
55
+ return lines;
56
+ };
57
+ /*
58
+ export const wrapText = (text: string, font: PDFFont, fontSize: number, maxWidth: number) => {
59
+ const words = text.split(" ");
60
+ const lines = [];
61
+ let currentLine = "";
62
+
63
+ for (const word of words) {
64
+ const nextLine = currentLine ? `${currentLine} ${word}` : word;
65
+ // console.log(nextLine);
66
+ const width = font.widthOfTextAtSize(nextLine ?? "", fontSize);
67
+ if (width <= maxWidth) {
68
+ currentLine = nextLine;
69
+ } else {
70
+ lines.push(currentLine);
71
+ currentLine = word;
72
+ }
73
+ }
74
+ if (currentLine) lines.push(currentLine);
75
+ return lines;
76
+ };
77
+ */
@@ -0,0 +1,343 @@
1
+ import { MulmoStudio, MulmoScript, MulmoPresentationStyle } from "../types/index.js";
2
+ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, currentStudio: MulmoStudio | undefined, fileName: string, videoCaptionLang?: string, presentationStyle?: MulmoPresentationStyle | null) => {
3
+ beats: {
4
+ duration?: number | undefined;
5
+ startAt?: number | undefined;
6
+ hash?: string | undefined;
7
+ audioDuration?: number | undefined;
8
+ movieDuration?: number | undefined;
9
+ silenceDuration?: number | undefined;
10
+ hasMovieAudio?: boolean | undefined;
11
+ audioFile?: string | undefined;
12
+ imageFile?: string | undefined;
13
+ movieFile?: string | undefined;
14
+ soundEffectFile?: string | undefined;
15
+ lipSyncFile?: string | undefined;
16
+ captionFile?: string | undefined;
17
+ }[];
18
+ script: {
19
+ lang: string;
20
+ imageParams: {
21
+ provider: string;
22
+ model?: string | undefined;
23
+ style?: string | undefined;
24
+ quality?: string | undefined;
25
+ moderation?: string | undefined;
26
+ images?: Record<string, {
27
+ type: "image";
28
+ source: {
29
+ url: string;
30
+ kind: "url";
31
+ } | {
32
+ kind: "base64";
33
+ data: string;
34
+ } | {
35
+ text: string;
36
+ kind: "text";
37
+ } | {
38
+ path: string;
39
+ kind: "path";
40
+ };
41
+ } | {
42
+ type: "imagePrompt";
43
+ prompt: string;
44
+ }> | undefined;
45
+ };
46
+ audioParams: {
47
+ padding: number;
48
+ introPadding: number;
49
+ closingPadding: number;
50
+ outroPadding: number;
51
+ bgmVolume: number;
52
+ audioVolume: number;
53
+ suppressSpeech: boolean;
54
+ bgm?: {
55
+ url: string;
56
+ kind: "url";
57
+ } | {
58
+ kind: "base64";
59
+ data: string;
60
+ } | {
61
+ text: string;
62
+ kind: "text";
63
+ } | {
64
+ path: string;
65
+ kind: "path";
66
+ } | undefined;
67
+ };
68
+ movieParams: {
69
+ provider?: string | undefined;
70
+ model?: string | undefined;
71
+ fillOption?: {
72
+ style: "aspectFit" | "aspectFill";
73
+ } | undefined;
74
+ transition?: {
75
+ type: "fade" | "slideout_left";
76
+ duration: number;
77
+ } | undefined;
78
+ };
79
+ soundEffectParams: {
80
+ provider?: string | undefined;
81
+ model?: string | undefined;
82
+ };
83
+ $mulmocast: {
84
+ version: "1.1";
85
+ credit?: "closing" | undefined;
86
+ };
87
+ canvasSize: {
88
+ width: number;
89
+ height: number;
90
+ };
91
+ speechParams: {
92
+ speakers: Record<string, {
93
+ voiceId: string;
94
+ lang?: Record<string, {
95
+ voiceId: string;
96
+ displayName?: Record<string, string> | undefined;
97
+ isDefault?: boolean | undefined;
98
+ speechOptions?: {
99
+ speed?: number | undefined;
100
+ instruction?: string | undefined;
101
+ } | undefined;
102
+ provider?: string | undefined;
103
+ model?: string | undefined;
104
+ }> | undefined;
105
+ displayName?: Record<string, string> | undefined;
106
+ isDefault?: boolean | undefined;
107
+ speechOptions?: {
108
+ speed?: number | undefined;
109
+ instruction?: string | undefined;
110
+ } | undefined;
111
+ provider?: string | undefined;
112
+ model?: string | undefined;
113
+ }>;
114
+ };
115
+ beats: {
116
+ text: string;
117
+ image?: {
118
+ type: "markdown";
119
+ markdown: string | string[];
120
+ } | {
121
+ type: "web";
122
+ url: string;
123
+ } | {
124
+ type: "pdf";
125
+ source: {
126
+ url: string;
127
+ kind: "url";
128
+ } | {
129
+ kind: "base64";
130
+ data: string;
131
+ } | {
132
+ text: string;
133
+ kind: "text";
134
+ } | {
135
+ path: string;
136
+ kind: "path";
137
+ };
138
+ } | {
139
+ type: "image";
140
+ source: {
141
+ url: string;
142
+ kind: "url";
143
+ } | {
144
+ kind: "base64";
145
+ data: string;
146
+ } | {
147
+ text: string;
148
+ kind: "text";
149
+ } | {
150
+ path: string;
151
+ kind: "path";
152
+ };
153
+ } | {
154
+ type: "svg";
155
+ source: {
156
+ url: string;
157
+ kind: "url";
158
+ } | {
159
+ kind: "base64";
160
+ data: string;
161
+ } | {
162
+ text: string;
163
+ kind: "text";
164
+ } | {
165
+ path: string;
166
+ kind: "path";
167
+ };
168
+ } | {
169
+ type: "movie";
170
+ source: {
171
+ url: string;
172
+ kind: "url";
173
+ } | {
174
+ kind: "base64";
175
+ data: string;
176
+ } | {
177
+ text: string;
178
+ kind: "text";
179
+ } | {
180
+ path: string;
181
+ kind: "path";
182
+ };
183
+ } | {
184
+ type: "textSlide";
185
+ slide: {
186
+ title: string;
187
+ subtitle?: string | undefined;
188
+ bullets?: string[] | undefined;
189
+ };
190
+ } | {
191
+ type: "chart";
192
+ title: string;
193
+ chartData: Record<string, any>;
194
+ } | {
195
+ code: {
196
+ url: string;
197
+ kind: "url";
198
+ } | {
199
+ kind: "base64";
200
+ data: string;
201
+ } | {
202
+ text: string;
203
+ kind: "text";
204
+ } | {
205
+ path: string;
206
+ kind: "path";
207
+ };
208
+ type: "mermaid";
209
+ title: string;
210
+ appendix?: string[] | undefined;
211
+ } | {
212
+ type: "html_tailwind";
213
+ html: string | string[];
214
+ } | {
215
+ type: "beat";
216
+ id?: string | undefined;
217
+ } | {
218
+ type: "voice_over";
219
+ startAt?: number | undefined;
220
+ } | undefined;
221
+ audio?: {
222
+ type: "audio";
223
+ source: {
224
+ url: string;
225
+ kind: "url";
226
+ } | {
227
+ kind: "base64";
228
+ data: string;
229
+ } | {
230
+ text: string;
231
+ kind: "text";
232
+ } | {
233
+ path: string;
234
+ kind: "path";
235
+ };
236
+ } | {
237
+ type: "midi";
238
+ source: string;
239
+ } | undefined;
240
+ duration?: number | undefined;
241
+ speechOptions?: {
242
+ speed?: number | undefined;
243
+ instruction?: string | undefined;
244
+ } | undefined;
245
+ id?: string | undefined;
246
+ imagePrompt?: string | undefined;
247
+ speaker?: string | undefined;
248
+ description?: string | undefined;
249
+ imageParams?: {
250
+ provider: string;
251
+ model?: string | undefined;
252
+ style?: string | undefined;
253
+ quality?: string | undefined;
254
+ moderation?: string | undefined;
255
+ images?: Record<string, {
256
+ type: "image";
257
+ source: {
258
+ url: string;
259
+ kind: "url";
260
+ } | {
261
+ kind: "base64";
262
+ data: string;
263
+ } | {
264
+ text: string;
265
+ kind: "text";
266
+ } | {
267
+ path: string;
268
+ kind: "path";
269
+ };
270
+ } | {
271
+ type: "imagePrompt";
272
+ prompt: string;
273
+ }> | undefined;
274
+ } | undefined;
275
+ audioParams?: {
276
+ movieVolume: number;
277
+ padding?: number | undefined;
278
+ } | undefined;
279
+ movieParams?: {
280
+ speed?: number | undefined;
281
+ provider?: string | undefined;
282
+ model?: string | undefined;
283
+ fillOption?: {
284
+ style: "aspectFit" | "aspectFill";
285
+ } | undefined;
286
+ } | undefined;
287
+ soundEffectParams?: {
288
+ provider?: string | undefined;
289
+ model?: string | undefined;
290
+ } | undefined;
291
+ lipSyncParams?: {
292
+ provider?: string | undefined;
293
+ model?: string | undefined;
294
+ } | undefined;
295
+ htmlImageParams?: {
296
+ model?: string | undefined;
297
+ } | undefined;
298
+ textSlideParams?: {
299
+ cssStyles: string | string[];
300
+ } | undefined;
301
+ captionParams?: {
302
+ styles: string[];
303
+ lang?: string | undefined;
304
+ } | undefined;
305
+ imageNames?: string[] | undefined;
306
+ moviePrompt?: string | undefined;
307
+ soundEffectPrompt?: string | undefined;
308
+ htmlPrompt?: {
309
+ prompt: string;
310
+ data?: any;
311
+ images?: Record<string, any> | undefined;
312
+ systemPrompt?: string | undefined;
313
+ } | undefined;
314
+ enableLipSync?: boolean | undefined;
315
+ }[];
316
+ title?: string | undefined;
317
+ description?: string | undefined;
318
+ lipSyncParams?: {
319
+ provider?: string | undefined;
320
+ model?: string | undefined;
321
+ } | undefined;
322
+ htmlImageParams?: {
323
+ provider: string;
324
+ model?: string | undefined;
325
+ } | undefined;
326
+ textSlideParams?: {
327
+ cssStyles: string | string[];
328
+ } | undefined;
329
+ captionParams?: {
330
+ styles: string[];
331
+ lang?: string | undefined;
332
+ } | undefined;
333
+ references?: {
334
+ type: string;
335
+ url: string;
336
+ title?: string | undefined;
337
+ description?: string | undefined;
338
+ }[] | undefined;
339
+ imagePath?: string | undefined;
340
+ __test_invalid__?: boolean | undefined;
341
+ };
342
+ filename: string;
343
+ };
@@ -0,0 +1,58 @@
1
+ import { GraphAILogger } from "graphai";
2
+ import { mulmoStudioSchema, mulmoCaptionParamsSchema } from "../types/index.js";
3
+ import { MulmoPresentationStyleMethods, MulmoScriptMethods } from "../methods/index.js";
4
+ const rebuildStudio = (currentStudio, mulmoScript, fileName) => {
5
+ const isTest = process.env.NODE_ENV === "test";
6
+ const parsed = isTest && currentStudio ? { data: mulmoStudioSchema.parse(currentStudio), success: true, error: null } : mulmoStudioSchema.safeParse(currentStudio);
7
+ if (parsed.success) {
8
+ return parsed.data;
9
+ }
10
+ if (currentStudio) {
11
+ GraphAILogger.info("currentStudio is invalid", parsed.error);
12
+ }
13
+ // We need to parse it to fill default values
14
+ return mulmoStudioSchema.parse({
15
+ script: mulmoScript,
16
+ filename: fileName,
17
+ beats: [...Array(mulmoScript.beats.length)].map(() => ({})),
18
+ });
19
+ };
20
+ const mulmoCredit = (speaker) => {
21
+ return {
22
+ speaker,
23
+ text: "",
24
+ image: {
25
+ type: "image",
26
+ source: {
27
+ kind: "url",
28
+ url: "https://github.com/receptron/mulmocast-cli/raw/refs/heads/main/assets/images/mulmocast_credit.png",
29
+ },
30
+ },
31
+ audio: {
32
+ type: "audio",
33
+ source: {
34
+ kind: "url",
35
+ url: "https://github.com/receptron/mulmocast-cli/raw/refs/heads/main/assets/audio/silent300.mp3",
36
+ },
37
+ },
38
+ };
39
+ };
40
+ export const createOrUpdateStudioData = (_mulmoScript, currentStudio, fileName, videoCaptionLang, presentationStyle) => {
41
+ const mulmoScript = _mulmoScript.__test_invalid__ ? _mulmoScript : MulmoScriptMethods.validate(_mulmoScript); // validate and insert default value
42
+ const studio = rebuildStudio(currentStudio, mulmoScript, fileName);
43
+ // TODO: Move this code out of this function later
44
+ // Addition cloing credit
45
+ if (mulmoScript.$mulmocast.credit === "closing") {
46
+ const defaultSpeaker = MulmoPresentationStyleMethods.getDefaultSpeaker(presentationStyle ?? studio.script);
47
+ mulmoScript.beats.push(mulmoCredit(mulmoScript.beats[0].speaker ?? defaultSpeaker)); // First speaker
48
+ }
49
+ studio.script = MulmoScriptMethods.validate(mulmoScript); // update the script
50
+ studio.beats = studio.script.beats.map((_, index) => studio.beats[index] ?? {});
51
+ if (videoCaptionLang) {
52
+ studio.script.captionParams = mulmoCaptionParamsSchema.parse({
53
+ ...(studio.script.captionParams ?? {}),
54
+ lang: videoCaptionLang,
55
+ });
56
+ }
57
+ return studio;
58
+ };