mulmocast 0.0.25 → 0.0.27

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,414 @@
1
+ import type { MulmoScript, MulmoPresentationStyle, MulmoStudioMultiLingual } from "../types/type.js";
2
+ import { FileObject } from "../types/index.js";
3
+ export declare const fetchScript: (isHttpPath: boolean, mulmoFilePath: string, fileOrUrl: string) => Promise<MulmoScript | null>;
4
+ export declare const getMultiLingual: (multilingualFilePath: string, studioBeatsLength: number) => MulmoStudioMultiLingual;
5
+ export declare const getPresentationStyle: (presentationStylePath: string | undefined) => MulmoPresentationStyle | null;
6
+ export declare const initializeContextFromFiles: (files: FileObject, raiseError: boolean, force?: boolean, caption?: string, lang?: string) => Promise<{
7
+ studio: {
8
+ beats: {
9
+ duration?: number | undefined;
10
+ hash?: string | undefined;
11
+ audioFile?: string | undefined;
12
+ imageFile?: string | undefined;
13
+ movieFile?: string | undefined;
14
+ captionFile?: string | undefined;
15
+ }[];
16
+ script: {
17
+ audioParams: {
18
+ padding: number;
19
+ introPadding: number;
20
+ closingPadding: number;
21
+ outroPadding: number;
22
+ bgmVolume: number;
23
+ audioVolume: number;
24
+ suppressSpeech: boolean;
25
+ bgm?: {
26
+ url: string;
27
+ kind: "url";
28
+ } | {
29
+ kind: "base64";
30
+ data: string;
31
+ } | {
32
+ text: string;
33
+ kind: "text";
34
+ } | {
35
+ path: string;
36
+ kind: "path";
37
+ } | undefined;
38
+ };
39
+ $mulmocast: {
40
+ version: "1.0";
41
+ credit?: "closing" | undefined;
42
+ };
43
+ canvasSize: {
44
+ width: number;
45
+ height: number;
46
+ };
47
+ speechParams: {
48
+ provider: "openai" | "nijivoice" | "google" | "elevenlabs";
49
+ speakers: Record<string, {
50
+ voiceId: string;
51
+ displayName?: Record<string, string> | undefined;
52
+ speechOptions?: {
53
+ speed?: number | undefined;
54
+ instruction?: string | undefined;
55
+ } | undefined;
56
+ provider?: "openai" | "nijivoice" | "google" | "elevenlabs" | undefined;
57
+ }>;
58
+ };
59
+ beats: {
60
+ text: string;
61
+ speaker: string;
62
+ duration?: number | undefined;
63
+ speechOptions?: {
64
+ speed?: number | undefined;
65
+ instruction?: string | undefined;
66
+ } | undefined;
67
+ image?: {
68
+ type: "markdown";
69
+ markdown: string | string[];
70
+ } | {
71
+ type: "web";
72
+ url: string;
73
+ } | {
74
+ type: "pdf";
75
+ source: {
76
+ url: string;
77
+ kind: "url";
78
+ } | {
79
+ kind: "base64";
80
+ data: string;
81
+ } | {
82
+ text: string;
83
+ kind: "text";
84
+ } | {
85
+ path: string;
86
+ kind: "path";
87
+ };
88
+ } | {
89
+ type: "image";
90
+ source: {
91
+ url: string;
92
+ kind: "url";
93
+ } | {
94
+ kind: "base64";
95
+ data: string;
96
+ } | {
97
+ text: string;
98
+ kind: "text";
99
+ } | {
100
+ path: string;
101
+ kind: "path";
102
+ };
103
+ } | {
104
+ type: "svg";
105
+ source: {
106
+ url: string;
107
+ kind: "url";
108
+ } | {
109
+ kind: "base64";
110
+ data: string;
111
+ } | {
112
+ text: string;
113
+ kind: "text";
114
+ } | {
115
+ path: string;
116
+ kind: "path";
117
+ };
118
+ } | {
119
+ type: "textSlide";
120
+ slide: {
121
+ title: string;
122
+ subtitle?: string | undefined;
123
+ bullets?: string[] | undefined;
124
+ };
125
+ } | {
126
+ type: "chart";
127
+ title: string;
128
+ chartData: Record<string, any>;
129
+ } | {
130
+ code: {
131
+ url: string;
132
+ kind: "url";
133
+ } | {
134
+ kind: "base64";
135
+ data: string;
136
+ } | {
137
+ text: string;
138
+ kind: "text";
139
+ } | {
140
+ path: string;
141
+ kind: "path";
142
+ };
143
+ type: "mermaid";
144
+ title: string;
145
+ appendix?: string[] | undefined;
146
+ } | {
147
+ type: "html_tailwind";
148
+ html: string | string[];
149
+ } | {
150
+ type: "beat";
151
+ id?: string | undefined;
152
+ } | {
153
+ type: "movie";
154
+ source: {
155
+ url: string;
156
+ kind: "url";
157
+ } | {
158
+ kind: "base64";
159
+ data: string;
160
+ } | {
161
+ text: string;
162
+ kind: "text";
163
+ } | {
164
+ path: string;
165
+ kind: "path";
166
+ };
167
+ mixAudio: number;
168
+ } | undefined;
169
+ id?: string | undefined;
170
+ audio?: {
171
+ type: "audio";
172
+ source: {
173
+ url: string;
174
+ kind: "url";
175
+ } | {
176
+ kind: "base64";
177
+ data: string;
178
+ } | {
179
+ text: string;
180
+ kind: "text";
181
+ } | {
182
+ path: string;
183
+ kind: "path";
184
+ };
185
+ } | {
186
+ type: "midi";
187
+ source: string;
188
+ } | undefined;
189
+ description?: string | undefined;
190
+ imageParams?: {
191
+ style?: string | undefined;
192
+ model?: string | undefined;
193
+ moderation?: string | undefined;
194
+ images?: Record<string, {
195
+ type: "image";
196
+ source: {
197
+ url: string;
198
+ kind: "url";
199
+ } | {
200
+ kind: "base64";
201
+ data: string;
202
+ } | {
203
+ text: string;
204
+ kind: "text";
205
+ } | {
206
+ path: string;
207
+ kind: "path";
208
+ };
209
+ }> | undefined;
210
+ } | undefined;
211
+ audioParams?: {
212
+ padding?: number | undefined;
213
+ } | undefined;
214
+ movieParams?: {
215
+ fillOption: {
216
+ style: "aspectFit" | "aspectFill";
217
+ };
218
+ } | undefined;
219
+ htmlImageParams?: {
220
+ model?: string | undefined;
221
+ } | undefined;
222
+ textSlideParams?: {
223
+ cssStyles: string | string[];
224
+ } | undefined;
225
+ captionParams?: {
226
+ styles: string[];
227
+ lang?: string | undefined;
228
+ } | undefined;
229
+ imageNames?: string[] | undefined;
230
+ imagePrompt?: string | undefined;
231
+ moviePrompt?: string | undefined;
232
+ htmlPrompt?: {
233
+ prompt: string;
234
+ data?: any;
235
+ images?: Record<string, any> | undefined;
236
+ systemPrompt?: string | undefined;
237
+ } | undefined;
238
+ }[];
239
+ lang?: string | undefined;
240
+ title?: string | undefined;
241
+ description?: string | undefined;
242
+ imageParams?: {
243
+ provider: "openai" | "google";
244
+ style?: string | undefined;
245
+ model?: string | undefined;
246
+ moderation?: string | undefined;
247
+ images?: Record<string, {
248
+ type: "image";
249
+ source: {
250
+ url: string;
251
+ kind: "url";
252
+ } | {
253
+ kind: "base64";
254
+ data: string;
255
+ } | {
256
+ text: string;
257
+ kind: "text";
258
+ } | {
259
+ path: string;
260
+ kind: "path";
261
+ };
262
+ }> | undefined;
263
+ } | undefined;
264
+ movieParams?: {
265
+ provider?: "openai" | "google" | "replicate" | undefined;
266
+ model?: string | undefined;
267
+ fillOption?: {
268
+ style: "aspectFit" | "aspectFill";
269
+ } | undefined;
270
+ transition?: {
271
+ type: "fade" | "slideout_left";
272
+ duration: number;
273
+ } | undefined;
274
+ } | undefined;
275
+ htmlImageParams?: {
276
+ provider: "openai" | "anthropic";
277
+ model?: string | undefined;
278
+ } | undefined;
279
+ textSlideParams?: {
280
+ cssStyles: string | string[];
281
+ } | undefined;
282
+ captionParams?: {
283
+ styles: string[];
284
+ lang?: string | undefined;
285
+ } | undefined;
286
+ references?: {
287
+ type: "image" | "audio" | "article" | "paper" | "video";
288
+ url: string;
289
+ title?: string | undefined;
290
+ description?: string | undefined;
291
+ }[] | undefined;
292
+ imagePath?: string | undefined;
293
+ __test_invalid__?: boolean | undefined;
294
+ };
295
+ filename: string;
296
+ };
297
+ fileDirs: FileObject;
298
+ force: boolean;
299
+ lang: string | undefined;
300
+ sessionState: {
301
+ inSession: {
302
+ audio: boolean;
303
+ image: boolean;
304
+ video: boolean;
305
+ multiLingual: boolean;
306
+ caption: boolean;
307
+ pdf: boolean;
308
+ };
309
+ inBeatSession: {
310
+ audio: {};
311
+ image: {};
312
+ movie: {};
313
+ multiLingual: {};
314
+ caption: {};
315
+ };
316
+ };
317
+ presentationStyle: {
318
+ audioParams: {
319
+ padding: number;
320
+ introPadding: number;
321
+ closingPadding: number;
322
+ outroPadding: number;
323
+ bgmVolume: number;
324
+ audioVolume: number;
325
+ suppressSpeech: boolean;
326
+ bgm?: {
327
+ url: string;
328
+ kind: "url";
329
+ } | {
330
+ kind: "base64";
331
+ data: string;
332
+ } | {
333
+ text: string;
334
+ kind: "text";
335
+ } | {
336
+ path: string;
337
+ kind: "path";
338
+ } | undefined;
339
+ };
340
+ $mulmocast: {
341
+ version: "1.0";
342
+ credit?: "closing" | undefined;
343
+ };
344
+ canvasSize: {
345
+ width: number;
346
+ height: number;
347
+ };
348
+ speechParams: {
349
+ provider: "openai" | "nijivoice" | "google" | "elevenlabs";
350
+ speakers: Record<string, {
351
+ voiceId: string;
352
+ displayName?: Record<string, string> | undefined;
353
+ speechOptions?: {
354
+ speed?: number | undefined;
355
+ instruction?: string | undefined;
356
+ } | undefined;
357
+ provider?: "openai" | "nijivoice" | "google" | "elevenlabs" | undefined;
358
+ }>;
359
+ };
360
+ imageParams?: {
361
+ provider: "openai" | "google";
362
+ style?: string | undefined;
363
+ model?: string | undefined;
364
+ moderation?: string | undefined;
365
+ images?: Record<string, {
366
+ type: "image";
367
+ source: {
368
+ url: string;
369
+ kind: "url";
370
+ } | {
371
+ kind: "base64";
372
+ data: string;
373
+ } | {
374
+ text: string;
375
+ kind: "text";
376
+ } | {
377
+ path: string;
378
+ kind: "path";
379
+ };
380
+ }> | undefined;
381
+ } | undefined;
382
+ movieParams?: {
383
+ provider?: "openai" | "google" | "replicate" | undefined;
384
+ model?: string | undefined;
385
+ fillOption?: {
386
+ style: "aspectFit" | "aspectFill";
387
+ } | undefined;
388
+ transition?: {
389
+ type: "fade" | "slideout_left";
390
+ duration: number;
391
+ } | undefined;
392
+ } | undefined;
393
+ htmlImageParams?: {
394
+ provider: "openai" | "anthropic";
395
+ model?: string | undefined;
396
+ } | undefined;
397
+ textSlideParams?: {
398
+ cssStyles: string | string[];
399
+ } | undefined;
400
+ captionParams?: {
401
+ styles: string[];
402
+ lang?: string | undefined;
403
+ } | undefined;
404
+ };
405
+ multiLingual: {
406
+ multiLingualTexts: Record<string, {
407
+ text: string;
408
+ lang: string;
409
+ texts?: string[] | undefined;
410
+ ttsTexts?: string[] | undefined;
411
+ duration?: number | undefined;
412
+ }>;
413
+ }[];
414
+ } | null>;
@@ -0,0 +1,96 @@
1
+ import { GraphAILogger } from "graphai";
2
+ import fs from "fs";
3
+ import { readMulmoScriptFile, fetchMulmoScriptFile } from "./file.js";
4
+ import { createOrUpdateStudioData } from "./preprocess.js";
5
+ import { mulmoPresentationStyleSchema, mulmoStudioMultiLingualSchema } from "../types/index.js";
6
+ export const fetchScript = async (isHttpPath, mulmoFilePath, fileOrUrl) => {
7
+ if (isHttpPath) {
8
+ const res = await fetchMulmoScriptFile(fileOrUrl);
9
+ if (!res.result || !res.script) {
10
+ GraphAILogger.info(`ERROR: HTTP error! ${res.status} ${fileOrUrl}`);
11
+ return null;
12
+ }
13
+ return res.script;
14
+ }
15
+ if (!fs.existsSync(mulmoFilePath)) {
16
+ GraphAILogger.info(`ERROR: File not exists ${mulmoFilePath}`);
17
+ return null;
18
+ }
19
+ return readMulmoScriptFile(mulmoFilePath, "ERROR: File does not exist " + mulmoFilePath)?.mulmoData ?? null;
20
+ };
21
+ export const getMultiLingual = (multilingualFilePath, studioBeatsLength) => {
22
+ if (fs.existsSync(multilingualFilePath)) {
23
+ const jsonData = readMulmoScriptFile(multilingualFilePath, "ERROR: File does not exist " + multilingualFilePath)?.mulmoData ?? null;
24
+ const dataSet = mulmoStudioMultiLingualSchema.parse(jsonData);
25
+ while (dataSet.length < studioBeatsLength) {
26
+ dataSet.push({ multiLingualTexts: {} });
27
+ }
28
+ dataSet.length = studioBeatsLength;
29
+ return dataSet;
30
+ }
31
+ return [...Array(studioBeatsLength)].map(() => ({ multiLingualTexts: {} }));
32
+ };
33
+ export const getPresentationStyle = (presentationStylePath) => {
34
+ if (presentationStylePath) {
35
+ if (!fs.existsSync(presentationStylePath)) {
36
+ throw new Error(`ERROR: File not exists ${presentationStylePath}`);
37
+ }
38
+ const jsonData = readMulmoScriptFile(presentationStylePath, "ERROR: File does not exist " + presentationStylePath)?.mulmoData ?? null;
39
+ return mulmoPresentationStyleSchema.parse(jsonData);
40
+ }
41
+ return null;
42
+ };
43
+ const initSessionState = () => {
44
+ return {
45
+ inSession: {
46
+ audio: false,
47
+ image: false,
48
+ video: false,
49
+ multiLingual: false,
50
+ caption: false,
51
+ pdf: false,
52
+ },
53
+ inBeatSession: {
54
+ audio: {},
55
+ image: {},
56
+ movie: {},
57
+ multiLingual: {},
58
+ caption: {},
59
+ },
60
+ };
61
+ };
62
+ const buildContext = (studio, files, presentationStyle, multiLingual, force, lang) => {
63
+ return {
64
+ studio,
65
+ fileDirs: files,
66
+ force: Boolean(force),
67
+ lang,
68
+ sessionState: initSessionState(),
69
+ presentationStyle: presentationStyle ?? studio.script,
70
+ multiLingual,
71
+ };
72
+ };
73
+ export const initializeContextFromFiles = async (files, raiseError, force, caption, lang) => {
74
+ const { fileName, isHttpPath, fileOrUrl, mulmoFilePath, outputStudioFilePath, presentationStylePath, outputMultilingualFilePath } = files;
75
+ // read mulmoScript, presentationStyle, currentStudio from files
76
+ const mulmoScript = await fetchScript(isHttpPath, mulmoFilePath, fileOrUrl);
77
+ if (!mulmoScript) {
78
+ return null;
79
+ }
80
+ const presentationStyle = getPresentationStyle(presentationStylePath);
81
+ // Create or update MulmoStudio file with MulmoScript
82
+ const currentStudio = readMulmoScriptFile(outputStudioFilePath);
83
+ try {
84
+ // validate mulmoStudioSchema. skip if __test_invalid__ is true
85
+ const studio = createOrUpdateStudioData(mulmoScript, currentStudio?.mulmoData, fileName, caption);
86
+ const multiLingual = getMultiLingual(outputMultilingualFilePath, studio.beats.length);
87
+ return buildContext(studio, files, presentationStyle, multiLingual, force, lang);
88
+ }
89
+ catch (error) {
90
+ GraphAILogger.info(`Error: invalid MulmoScript Schema: ${isHttpPath ? fileOrUrl : mulmoFilePath} \n ${error}`);
91
+ if (raiseError) {
92
+ throw error;
93
+ }
94
+ return null;
95
+ }
96
+ };
@@ -4,6 +4,7 @@ export type FfmpegContext = {
4
4
  inputCount: number;
5
5
  filterComplex: string[];
6
6
  };
7
+ export declare const setFfmpegPath: (ffmpegPath: string) => void;
7
8
  export declare const FfmpegContextInit: () => FfmpegContext;
8
9
  export declare const FfmpegContextAddInput: (context: FfmpegContext, input: string, inputOptions?: string[]) => number;
9
10
  export declare const FfmpegContextPushFormattedAudio: (context: FfmpegContext, sourceId: string, outputId: string, duration?: number | undefined) => void;
@@ -1,5 +1,8 @@
1
1
  import ffmpeg from "fluent-ffmpeg";
2
2
  import { GraphAILogger } from "graphai";
3
+ export const setFfmpegPath = (ffmpegPath) => {
4
+ ffmpeg.setFfmpegPath(ffmpegPath);
5
+ };
3
6
  export const FfmpegContextInit = () => {
4
7
  return {
5
8
  command: ffmpeg(),
@@ -41,3 +41,4 @@ export declare const readTemplatePrompt: (templateName: string) => string;
41
41
  export declare const getAvailableTemplates: () => MulmoScriptTemplateFile[];
42
42
  export declare const writingMessage: (filePath: string) => void;
43
43
  export declare const readAndParseJson: <S extends ZodSchema<any>>(filePath: string, schema: S) => ReturnType<S["parse"]>;
44
+ export declare const generateTimestampedFileName: (prefix: string) => string;
package/lib/utils/file.js CHANGED
@@ -182,3 +182,8 @@ export const readAndParseJson = (filePath, schema) => {
182
182
  const json = JSON.parse(fileContent);
183
183
  return schema.parse(json);
184
184
  };
185
+ export const generateTimestampedFileName = (prefix) => {
186
+ const now = new Date();
187
+ const pad = (n) => n.toString().padStart(2, "0");
188
+ return `${prefix}_${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
189
+ };
@@ -1,5 +1,5 @@
1
1
  import { MulmoStudio, MulmoScript } from "../types/index.js";
2
- export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, currentStudio: MulmoStudio | undefined, fileName: string) => {
2
+ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, currentStudio: MulmoStudio | undefined, fileName: string, videoCaption?: string) => {
3
3
  beats: {
4
4
  duration?: number | undefined;
5
5
  hash?: string | undefined;
@@ -16,6 +16,7 @@ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, curre
16
16
  outroPadding: number;
17
17
  bgmVolume: number;
18
18
  audioVolume: number;
19
+ suppressSpeech: boolean;
19
20
  bgm?: {
20
21
  url: string;
21
22
  kind: "url";
@@ -1,5 +1,5 @@
1
1
  import { GraphAILogger } from "graphai";
2
- import { mulmoScriptSchema, mulmoStudioSchema } from "../types/index.js";
2
+ import { mulmoScriptSchema, mulmoStudioSchema, mulmoCaptionParamsSchema } from "../types/index.js";
3
3
  const rebuildStudio = (currentStudio, mulmoScript, fileName) => {
4
4
  const isTest = process.env.NODE_ENV === "test";
5
5
  const parsed = isTest && currentStudio ? { data: mulmoStudioSchema.parse(currentStudio), success: true, error: null } : mulmoStudioSchema.safeParse(currentStudio);
@@ -36,7 +36,7 @@ const mulmoCredit = (speaker) => {
36
36
  },
37
37
  };
38
38
  };
39
- export const createOrUpdateStudioData = (_mulmoScript, currentStudio, fileName) => {
39
+ export const createOrUpdateStudioData = (_mulmoScript, currentStudio, fileName, videoCaption) => {
40
40
  const mulmoScript = _mulmoScript.__test_invalid__ ? _mulmoScript : mulmoScriptSchema.parse(_mulmoScript); // validate and insert default value
41
41
  const studio = rebuildStudio(currentStudio, mulmoScript, fileName);
42
42
  // TODO: Move this code out of this function later
@@ -46,5 +46,11 @@ export const createOrUpdateStudioData = (_mulmoScript, currentStudio, fileName)
46
46
  }
47
47
  studio.script = mulmoScriptSchema.parse(mulmoScript); // update the script
48
48
  studio.beats = studio.script.beats.map((_, index) => studio.beats[index] ?? {});
49
+ if (videoCaption) {
50
+ studio.script.captionParams = mulmoCaptionParamsSchema.parse({
51
+ ...(studio.script.captionParams ?? {}),
52
+ lang: videoCaption,
53
+ });
54
+ }
49
55
  return studio;
50
56
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mulmocast",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -48,7 +48,8 @@
48
48
  "latest": "yarn upgrade-interactive --latest",
49
49
  "format": "prettier --write '{src,scripts,assets/templates,assets/styles,draft,ideason,scripts_mag2,proto,test,graphai,output,docs/scripts}/**/*.{ts,json,yaml}'",
50
50
  "deep_research": "npx tsx ./src/tools/deep_research.ts",
51
- "fake_data": "npx tsx test/fake/sample.ts"
51
+ "fake_data": "npx tsx test/fake/sample.ts",
52
+ "mcp_server": "npx tsx ./src/mcp/server.ts"
52
53
  },
53
54
  "repository": "git+ssh://git@github.com/receptron/mulmocast-cli.git",
54
55
  "author": "snakajima",
@@ -68,6 +69,7 @@
68
69
  "@graphai/stream_agent_filter": "^2.0.2",
69
70
  "@graphai/vanilla": "^2.0.4",
70
71
  "@graphai/vanilla_node_agents": "^2.0.1",
72
+ "@modelcontextprotocol/sdk": "^1.13.1",
71
73
  "@tavily/core": "^0.5.8",
72
74
  "canvas": "^3.1.2",
73
75
  "clipboardy": "^4.0.0",
@@ -76,12 +78,12 @@
76
78
  "google-auth-library": "^9.15.1",
77
79
  "graphai": "^2.0.9",
78
80
  "inquirer": "^12.6.3",
79
- "marked": "^15.0.12",
81
+ "marked": "^16.0.0",
80
82
  "ora": "^8.2.0",
81
- "puppeteer": "^24.11.0",
83
+ "puppeteer": "^24.11.1",
82
84
  "replicate": "^1.0.1",
83
85
  "yaml": "^2.8.0",
84
- "yargs": "^17.7.2",
86
+ "yargs": "^18.0.0",
85
87
  "zod": "^3.25.67",
86
88
  "zod-to-json-schema": "^3.24.6"
87
89
  },