mulmocast 0.0.4 → 0.0.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.
Files changed (134) hide show
  1. package/README.md +257 -39
  2. package/assets/audio/silent60sec.mp3 +0 -0
  3. package/assets/html/caption.html +45 -0
  4. package/assets/html/chart.html +1 -1
  5. package/assets/html/mermaid.html +6 -2
  6. package/assets/html/tailwind.html +13 -0
  7. package/assets/templates/business.json +57 -4
  8. package/assets/templates/comic_strips.json +35 -0
  9. package/assets/templates/ghibli_strips.json +35 -0
  10. package/lib/actions/audio.js +24 -11
  11. package/lib/actions/captions.d.ts +2 -0
  12. package/lib/actions/captions.js +62 -0
  13. package/lib/actions/images.js +3 -2
  14. package/lib/actions/index.d.ts +1 -0
  15. package/lib/actions/index.js +1 -0
  16. package/lib/actions/movie.js +78 -86
  17. package/lib/actions/pdf.js +15 -5
  18. package/lib/actions/translate.js +32 -26
  19. package/lib/agents/add_bgm_agent.js +15 -39
  20. package/lib/agents/combine_audio_files_agent.js +43 -36
  21. package/lib/agents/index.d.ts +2 -3
  22. package/lib/agents/index.js +2 -3
  23. package/lib/agents/tts_google_agent.d.ts +4 -0
  24. package/lib/agents/tts_google_agent.js +51 -0
  25. package/lib/agents/validate_schema_agent.d.ts +19 -0
  26. package/lib/agents/validate_schema_agent.js +36 -0
  27. package/lib/cli/args.d.ts +2 -0
  28. package/lib/cli/args.js +9 -2
  29. package/lib/cli/bin.d.ts +3 -0
  30. package/lib/cli/bin.js +38 -0
  31. package/lib/cli/cli.js +34 -7
  32. package/lib/cli/commands/audio/builder.d.ts +14 -0
  33. package/lib/cli/commands/audio/builder.js +6 -0
  34. package/lib/cli/commands/audio/handler.d.ts +4 -0
  35. package/lib/cli/commands/audio/handler.js +7 -0
  36. package/lib/cli/commands/audio/index.d.ts +4 -0
  37. package/lib/cli/commands/audio/index.js +4 -0
  38. package/lib/cli/commands/image/builder.d.ts +14 -0
  39. package/lib/cli/commands/image/builder.js +6 -0
  40. package/lib/cli/commands/image/handler.d.ts +4 -0
  41. package/lib/cli/commands/image/handler.js +7 -0
  42. package/lib/cli/commands/image/index.d.ts +4 -0
  43. package/lib/cli/commands/image/index.js +4 -0
  44. package/lib/cli/commands/movie/builder.d.ts +18 -0
  45. package/lib/cli/commands/movie/builder.js +19 -0
  46. package/lib/cli/commands/movie/handler.d.ts +6 -0
  47. package/lib/cli/commands/movie/handler.js +12 -0
  48. package/lib/cli/commands/movie/index.d.ts +4 -0
  49. package/lib/cli/commands/movie/index.js +4 -0
  50. package/lib/cli/commands/pdf/builder.d.ts +18 -0
  51. package/lib/cli/commands/pdf/builder.js +19 -0
  52. package/lib/cli/commands/pdf/handler.d.ts +6 -0
  53. package/lib/cli/commands/pdf/handler.js +8 -0
  54. package/lib/cli/commands/pdf/index.d.ts +4 -0
  55. package/lib/cli/commands/pdf/index.js +4 -0
  56. package/lib/cli/commands/tool/index.d.ts +6 -0
  57. package/lib/cli/commands/tool/index.js +8 -0
  58. package/lib/cli/commands/tool/prompt/builder.d.ts +4 -0
  59. package/lib/cli/commands/tool/prompt/builder.js +11 -0
  60. package/lib/cli/commands/tool/prompt/handler.d.ts +4 -0
  61. package/lib/cli/commands/tool/prompt/handler.js +14 -0
  62. package/lib/cli/commands/tool/prompt/index.d.ts +4 -0
  63. package/lib/cli/commands/tool/prompt/index.js +4 -0
  64. package/lib/cli/commands/tool/schema/builder.d.ts +2 -0
  65. package/lib/cli/commands/tool/schema/builder.js +3 -0
  66. package/lib/cli/commands/tool/schema/handler.d.ts +2 -0
  67. package/lib/cli/commands/tool/schema/handler.js +12 -0
  68. package/lib/cli/commands/tool/schema/index.d.ts +4 -0
  69. package/lib/cli/commands/tool/schema/index.js +4 -0
  70. package/lib/cli/commands/tool/scripting/builder.d.ts +20 -0
  71. package/lib/cli/commands/tool/scripting/builder.js +63 -0
  72. package/lib/cli/commands/tool/scripting/handler.d.ts +12 -0
  73. package/lib/cli/commands/tool/scripting/handler.js +36 -0
  74. package/lib/cli/commands/tool/scripting/index.d.ts +4 -0
  75. package/lib/cli/commands/tool/scripting/index.js +4 -0
  76. package/lib/cli/commands/tool/story_to_script/builder.d.ts +18 -0
  77. package/lib/cli/commands/tool/story_to_script/builder.js +53 -0
  78. package/lib/cli/commands/tool/story_to_script/handler.d.ts +11 -0
  79. package/lib/cli/commands/tool/story_to_script/handler.js +35 -0
  80. package/lib/cli/commands/tool/story_to_script/index.d.ts +4 -0
  81. package/lib/cli/commands/tool/story_to_script/index.js +4 -0
  82. package/lib/cli/commands/translate/builder.d.ts +14 -0
  83. package/lib/cli/commands/translate/builder.js +5 -0
  84. package/lib/cli/commands/translate/handler.d.ts +4 -0
  85. package/lib/cli/commands/translate/handler.js +6 -0
  86. package/lib/cli/commands/translate/index.d.ts +4 -0
  87. package/lib/cli/commands/translate/index.js +4 -0
  88. package/lib/cli/common.d.ts +6 -2
  89. package/lib/cli/common.js +18 -7
  90. package/lib/cli/helpers.d.ts +38 -0
  91. package/lib/cli/helpers.js +115 -0
  92. package/lib/cli/run.d.ts +1 -0
  93. package/lib/cli/run.js +1 -0
  94. package/lib/cli/tool-args.d.ts +1 -0
  95. package/lib/cli/tool-args.js +1 -1
  96. package/lib/cli/tool-cli.js +8 -0
  97. package/lib/methods/mulmo_script.d.ts +0 -1
  98. package/lib/methods/mulmo_script.js +4 -7
  99. package/lib/methods/mulmo_script_template.js +2 -12
  100. package/lib/tools/create_mulmo_script_from_url.d.ts +1 -1
  101. package/lib/tools/create_mulmo_script_from_url.js +43 -14
  102. package/lib/tools/create_mulmo_script_interactively.js +14 -13
  103. package/lib/tools/dump_prompt.js +2 -0
  104. package/lib/tools/story_to_script.d.ts +10 -0
  105. package/lib/tools/story_to_script.js +201 -0
  106. package/lib/types/cli_types.d.ts +14 -0
  107. package/lib/types/cli_types.js +1 -0
  108. package/lib/types/schema.d.ts +493 -176
  109. package/lib/types/schema.js +37 -7
  110. package/lib/types/type.d.ts +6 -1
  111. package/lib/utils/const.d.ts +1 -0
  112. package/lib/utils/const.js +1 -0
  113. package/lib/utils/ffmpeg_utils.d.ts +12 -0
  114. package/lib/utils/ffmpeg_utils.js +63 -0
  115. package/lib/utils/file.d.ts +7 -3
  116. package/lib/utils/file.js +24 -5
  117. package/lib/utils/image_plugins/chart.js +6 -1
  118. package/lib/utils/image_plugins/html_tailwind.d.ts +3 -0
  119. package/lib/utils/image_plugins/html_tailwind.js +18 -0
  120. package/lib/utils/image_plugins/index.d.ts +2 -1
  121. package/lib/utils/image_plugins/index.js +2 -1
  122. package/lib/utils/image_plugins/mermaid.js +1 -1
  123. package/lib/utils/image_plugins/tailwind.d.ts +3 -0
  124. package/lib/utils/image_plugins/tailwind.js +18 -0
  125. package/lib/utils/image_plugins/text_slide.js +9 -2
  126. package/lib/utils/markdown.d.ts +1 -1
  127. package/lib/utils/markdown.js +8 -2
  128. package/lib/utils/preprocess.d.ts +23 -12
  129. package/lib/utils/preprocess.js +4 -0
  130. package/lib/utils/prompt.d.ts +15 -0
  131. package/lib/utils/prompt.js +57 -0
  132. package/lib/utils/utils.d.ts +2 -0
  133. package/lib/utils/utils.js +10 -0
  134. package/package.json +27 -23
@@ -4,7 +4,7 @@ const URLStringSchema = z.string().url();
4
4
  export const localizedTextSchema = z
5
5
  .object({
6
6
  text: z.string(),
7
- lang: z.string(),
7
+ lang: langSchema,
8
8
  // caption: z.string(),
9
9
  texts: z.array(z.string()).optional(),
10
10
  ttsTexts: z.array(z.string()).optional(),
@@ -77,7 +77,8 @@ export const mulmoTextSlideMediaSchema = z
77
77
  type: z.literal("textSlide"),
78
78
  slide: z.object({
79
79
  title: z.string(),
80
- bullets: z.array(z.string()),
80
+ subtitle: z.string().optional(),
81
+ bullets: z.array(z.string()).optional(),
81
82
  }),
82
83
  })
83
84
  .strict();
@@ -96,6 +97,12 @@ export const mulmoMermaidMediaSchema = z
96
97
  appendix: z.array(z.string()).optional().describe("The appendix of the mermaid diagram; typically, style information."),
97
98
  })
98
99
  .strict();
100
+ export const mulmoHtmlTailwindMediaSchema = z
101
+ .object({
102
+ type: z.literal("html_tailwind"),
103
+ html: stringOrStringArray,
104
+ })
105
+ .strict();
99
106
  export const mulmoImageAssetSchema = z.union([
100
107
  mulmoMarkdownMediaSchema,
101
108
  mulmoWebMediaSchema,
@@ -106,6 +113,7 @@ export const mulmoImageAssetSchema = z.union([
106
113
  mulmoTextSlideMediaSchema,
107
114
  mulmoChartMediaSchema,
108
115
  mulmoMermaidMediaSchema,
116
+ mulmoHtmlTailwindMediaSchema,
109
117
  ]);
110
118
  const mulmoAudioMediaSchema = z
111
119
  .object({
@@ -133,17 +141,28 @@ export const textSlideParamsSchema = z
133
141
  cssStyles: stringOrStringArray,
134
142
  })
135
143
  .strict();
144
+ /* TODO: Add something later
136
145
  export const videoParamsSchema = z
137
- .object({
146
+ .object({
138
147
  padding: z.number().optional(), // msec
148
+ })
149
+ .strict();
150
+ */
151
+ export const audioParamsSchema = z
152
+ .object({
153
+ introPadding: z.number().describe("Padding at the beginning of the audio"), // seconds
154
+ padding: z.number().describe("Padding between beats"), // seconds
155
+ closingPadding: z.number().describe("Padding before the last beat"), // seconds
156
+ outroPadding: z.number().describe("Padding at the end of the audio"), // seconds
139
157
  })
140
158
  .strict();
141
159
  export const mulmoBeatSchema = z
142
160
  .object({
143
161
  speaker: speakerIdSchema.default("Presenter"),
144
- text: z.string(),
162
+ text: z.string().describe("Text to be spoken. If empty, the audio is not generated."),
145
163
  image: mulmoImageAssetSchema.optional(),
146
164
  audio: mulmoAudioAssetSchema.optional(),
165
+ duration: z.number().optional().describe("Duration of the beat. Used only when the text is empty"),
147
166
  imageParams: mulmoImageParamsSchema.optional(), // beat specific parameters
148
167
  speechOptions: speechOptionsSchema.optional(),
149
168
  textSlideParams: textSlideParamsSchema.optional(),
@@ -163,7 +182,7 @@ export const mulmoCastCreditSchema = z
163
182
  credit: z.literal("closing").optional(),
164
183
  })
165
184
  .strict();
166
- export const text2SpeechProviderSchema = z.union([z.literal("openai"), z.literal("nijivoice")]).default("openai");
185
+ export const text2SpeechProviderSchema = z.union([z.literal("openai"), z.literal("nijivoice"), z.literal("google")]).default("openai");
167
186
  export const mulmoSpeechParamsSchema = z
168
187
  .object({
169
188
  provider: text2SpeechProviderSchema, // has default value
@@ -191,7 +210,13 @@ export const mulmoPresentationStyleSchema = z.object({
191
210
  .optional(),
192
211
  // for textSlides
193
212
  textSlideParams: textSlideParamsSchema.optional(),
194
- videoParams: videoParamsSchema.optional(),
213
+ // videoParams: videoParamsSchema.optional(),
214
+ audioParams: audioParamsSchema.default({
215
+ introPadding: 1.0,
216
+ padding: 0.3,
217
+ closingPadding: 0.8,
218
+ outroPadding: 1.0,
219
+ }),
195
220
  // TODO: Switch to showCaptions later
196
221
  omitCaptions: z.boolean().optional(), // default is false
197
222
  });
@@ -216,18 +241,23 @@ export const mulmoScriptSchema = mulmoPresentationStyleSchema
216
241
  .strict();
217
242
  export const mulmoStudioBeatSchema = z
218
243
  .object({
219
- multiLingualTexts: multiLingualTextsSchema.optional(),
220
244
  hash: z.string().optional(),
221
245
  duration: z.number().optional(),
222
246
  audioFile: z.string().optional(),
223
247
  imageFile: z.string().optional(), // path to the image
248
+ captionFile: z.string().optional(), // path to the caption image
224
249
  })
225
250
  .strict();
251
+ export const mulmoStudioMultiLingualDataSchema = z.object({
252
+ multiLingualTexts: multiLingualTextsSchema,
253
+ });
254
+ export const mulmoStudioMultiLingualSchema = z.array(mulmoStudioMultiLingualDataSchema).min(1);
226
255
  export const mulmoStudioSchema = z
227
256
  .object({
228
257
  script: mulmoScriptSchema,
229
258
  filename: z.string(),
230
259
  beats: z.array(mulmoStudioBeatSchema).min(1),
260
+ multiLingual: mulmoStudioMultiLingualSchema,
231
261
  })
232
262
  .strict();
233
263
  export const mulmoScriptTemplateSchema = z
@@ -1,4 +1,4 @@
1
- import { langSchema, localizedTextSchema, mulmoBeatSchema, mulmoScriptSchema, mulmoStudioSchema, mulmoStudioBeatSchema, mulmoStoryboardSchema, mulmoStoryboardSceneSchema, speakerDictionarySchema, mulmoImageParamsSchema, mulmoSpeechParamsSchema, textSlideParamsSchema, speechOptionsSchema, mulmoCanvasDimensionSchema, mulmoScriptTemplateSchema, text2ImageProviderSchema, text2SpeechProviderSchema, mulmoPresentationStyleSchema, mulmoMermaidMediaSchema, mulmoTextSlideMediaSchema, mulmoMarkdownMediaSchema, mulmoImageMediaSchema, mulmoChartMediaSchema, mediaSourceSchema } from "./schema.js";
1
+ import { langSchema, localizedTextSchema, mulmoBeatSchema, mulmoScriptSchema, mulmoStudioSchema, mulmoStudioBeatSchema, mulmoStoryboardSchema, mulmoStoryboardSceneSchema, mulmoStudioMultiLingualSchema, mulmoStudioMultiLingualDataSchema, speakerDictionarySchema, mulmoImageParamsSchema, mulmoSpeechParamsSchema, textSlideParamsSchema, speechOptionsSchema, mulmoCanvasDimensionSchema, mulmoScriptTemplateSchema, text2ImageProviderSchema, text2SpeechProviderSchema, mulmoPresentationStyleSchema, multiLingualTextsSchema, mulmoMermaidMediaSchema, mulmoTextSlideMediaSchema, mulmoMarkdownMediaSchema, mulmoImageMediaSchema, mulmoChartMediaSchema, mediaSourceSchema } from "./schema.js";
2
2
  import { pdf_modes, pdf_sizes } from "../utils/const.js";
3
3
  import { z } from "zod";
4
4
  export type LANG = z.infer<typeof langSchema>;
@@ -20,6 +20,9 @@ export type MulmoStudioBeat = z.infer<typeof mulmoStudioBeatSchema>;
20
20
  export type MulmoMediaSource = z.infer<typeof mediaSourceSchema>;
21
21
  export type MulmoStudio = z.infer<typeof mulmoStudioSchema>;
22
22
  export type MulmoScriptTemplate = z.infer<typeof mulmoScriptTemplateSchema>;
23
+ export type MulmoStudioMultiLingual = z.infer<typeof mulmoStudioMultiLingualSchema>;
24
+ export type MulmoStudioMultiLingualData = z.infer<typeof mulmoStudioMultiLingualDataSchema>;
25
+ export type MultiLingualTexts = z.infer<typeof multiLingualTextsSchema>;
23
26
  export type MulmoTextSlideMedia = z.infer<typeof mulmoTextSlideMediaSchema>;
24
27
  export type MulmoMarkdownMedia = z.infer<typeof mulmoMarkdownMediaSchema>;
25
28
  export type MulmoImageMedia = z.infer<typeof mulmoImageMediaSchema>;
@@ -36,7 +39,9 @@ export type FileDirs = {
36
39
  export type MulmoStudioContext = {
37
40
  fileDirs: FileDirs;
38
41
  studio: MulmoStudio;
42
+ lang?: string;
39
43
  force: boolean;
44
+ caption?: string;
40
45
  };
41
46
  export type ScriptingParams = {
42
47
  urls: string[];
@@ -4,3 +4,4 @@ export declare const imageDirName = "images";
4
4
  export declare const cacheDirName = "cache";
5
5
  export declare const pdf_modes: string[];
6
6
  export declare const pdf_sizes: string[];
7
+ export declare const languages: string[];
@@ -4,3 +4,4 @@ export const imageDirName = "images";
4
4
  export const cacheDirName = "cache";
5
5
  export const pdf_modes = ["slide", "talk", "handout"];
6
6
  export const pdf_sizes = ["letter", "a4"];
7
+ export const languages = ["en", "ja"];
@@ -0,0 +1,12 @@
1
+ import ffmpeg from "fluent-ffmpeg";
2
+ export type FfmpegContext = {
3
+ command: ffmpeg.FfmpegCommand;
4
+ inputCount: number;
5
+ filterComplex: string[];
6
+ };
7
+ export declare const FfmpegContextInit: () => FfmpegContext;
8
+ export declare const FfmpegContextAddInput: (context: FfmpegContext, input: string) => number;
9
+ export declare const FfmpegContextPushFormattedAudio: (context: FfmpegContext, sourceId: string, outputId: string, duration?: number | undefined) => void;
10
+ export declare const FfmpegContextInputFormattedAudio: (context: FfmpegContext, input: string, duration?: number | undefined) => string;
11
+ export declare const FfmpegContextGenerateOutput: (context: FfmpegContext, output: string, options?: string[]) => Promise<number>;
12
+ export declare const ffmpegGetMediaDuration: (filePath: string) => Promise<number>;
@@ -0,0 +1,63 @@
1
+ import ffmpeg from "fluent-ffmpeg";
2
+ import { GraphAILogger } from "graphai";
3
+ export const FfmpegContextInit = () => {
4
+ return {
5
+ command: ffmpeg(),
6
+ inputCount: 0,
7
+ filterComplex: [],
8
+ };
9
+ };
10
+ export const FfmpegContextAddInput = (context, input) => {
11
+ context.command.input(input);
12
+ context.inputCount++;
13
+ return context.inputCount - 1; // returned the index of the input
14
+ };
15
+ export const FfmpegContextPushFormattedAudio = (context, sourceId, outputId, duration = undefined) => {
16
+ if (duration !== undefined) {
17
+ context.filterComplex.push(`${sourceId}atrim=duration=${duration},aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo${outputId}`);
18
+ }
19
+ else {
20
+ context.filterComplex.push(`${sourceId}aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo${outputId}`);
21
+ }
22
+ };
23
+ export const FfmpegContextInputFormattedAudio = (context, input, duration = undefined) => {
24
+ const index = FfmpegContextAddInput(context, input);
25
+ const audioId = `[a${index}]`;
26
+ FfmpegContextPushFormattedAudio(context, `[${index}:a]`, audioId, duration);
27
+ return audioId;
28
+ };
29
+ export const FfmpegContextGenerateOutput = (context, output, options = []) => {
30
+ return new Promise((resolve, reject) => {
31
+ context.command
32
+ .complexFilter(context.filterComplex)
33
+ .outputOptions(options)
34
+ .output(output)
35
+ .on("start", (__cmdLine) => {
36
+ GraphAILogger.log("Started FFmpeg ..."); // with command:', cmdLine);
37
+ })
38
+ .on("error", (err, stdout, stderr) => {
39
+ GraphAILogger.error("Error occurred:", err);
40
+ GraphAILogger.error("FFmpeg stdout:", stdout);
41
+ GraphAILogger.error("FFmpeg stderr:", stderr);
42
+ GraphAILogger.info("Video/Audio creation failed. An unexpected error occurred.");
43
+ reject();
44
+ })
45
+ .on("end", () => {
46
+ resolve(0);
47
+ })
48
+ .run();
49
+ });
50
+ };
51
+ export const ffmpegGetMediaDuration = (filePath) => {
52
+ return new Promise((resolve, reject) => {
53
+ ffmpeg.ffprobe(filePath, (err, metadata) => {
54
+ if (err) {
55
+ GraphAILogger.info("Error while getting metadata:", err);
56
+ reject(err);
57
+ }
58
+ else {
59
+ resolve(metadata.format.duration);
60
+ }
61
+ });
62
+ });
63
+ };
@@ -1,5 +1,6 @@
1
1
  import { MulmoScript, MulmoScriptTemplate, MulmoMediaSource, MulmoStudioContext } from "../types/index.js";
2
2
  import { PDFMode } from "../types/index.js";
3
+ import { ZodSchema } from "zod";
3
4
  export declare function readMulmoScriptFile<T = MulmoScript>(path: string, errorMessage: string): {
4
5
  mulmoData: T;
5
6
  mulmoDataPath: string;
@@ -24,16 +25,18 @@ export declare const fetchMulmoScriptFile: (url: string) => Promise<{
24
25
  script?: undefined;
25
26
  }>;
26
27
  export declare const getOutputStudioFilePath: (outDirPath: string, fileName: string) => string;
28
+ export declare const resolveDirPath: (dirPath: string, studioFileName: string) => string;
27
29
  export declare const getAudioSegmentDirPath: (audioDirPath: string, studioFileName: string) => string;
28
30
  export declare const getAudioSegmentFilePath: (audioDirPath: string, studioFileName: string, fileName: string) => string;
29
- export declare const getAudioCombinedFilePath: (audioDirPath: string, fileName: string) => string;
31
+ export declare const getAudioCombinedFilePath: (audioDirPath: string, fileName: string, lang?: string) => string;
30
32
  export declare const getAudioArtifactFilePath: (outDirPath: string, fileName: string) => string;
31
- export declare const getOutputVideoFilePath: (outDirPath: string, fileName: string) => string;
32
- export declare const getOutputPdfFilePath: (outDirPath: string, fileName: string, pdfMode: PDFMode) => string;
33
+ export declare const getOutputVideoFilePath: (outDirPath: string, fileName: string, lang?: string, caption?: string) => string;
34
+ export declare const getOutputPdfFilePath: (outDirPath: string, fileName: string, pdfMode: PDFMode, lang?: string) => string;
33
35
  export declare const getTemplateFilePath: (templateName: string) => string;
34
36
  export declare const mkdir: (dirPath: string) => void;
35
37
  export declare const silentPath: string;
36
38
  export declare const silentLastPath: string;
39
+ export declare const silent60secPath: string;
37
40
  export declare const defaultBGMPath: string;
38
41
  export declare const getHTMLFile: (filename: string) => string;
39
42
  export declare const getBaseDirPath: (basedir?: string) => string;
@@ -44,3 +47,4 @@ export declare const getAvailableTemplates: () => (MulmoScriptTemplate & {
44
47
  })[];
45
48
  export declare const writingMessage: (filePath: string) => void;
46
49
  export declare const resolveMediaSource: (source: MulmoMediaSource, context: MulmoStudioContext) => string | null;
50
+ export declare const readAndParseJson: <S extends ZodSchema<any>>(filePath: string, schema: S) => ReturnType<S["parse"]>;
package/lib/utils/file.js CHANGED
@@ -45,23 +45,35 @@ export const fetchMulmoScriptFile = async (url) => {
45
45
  export const getOutputStudioFilePath = (outDirPath, fileName) => {
46
46
  return path.resolve(outDirPath, fileName + "_studio.json");
47
47
  };
48
+ export const resolveDirPath = (dirPath, studioFileName) => {
49
+ return path.resolve(dirPath, studioFileName);
50
+ };
51
+ // TODO: probably better to just use resolveDirPath instead.
48
52
  export const getAudioSegmentDirPath = (audioDirPath, studioFileName) => {
49
53
  return path.resolve(audioDirPath, studioFileName);
50
54
  };
51
55
  export const getAudioSegmentFilePath = (audioDirPath, studioFileName, fileName) => {
52
56
  return path.resolve(getAudioSegmentDirPath(audioDirPath, studioFileName), fileName + ".mp3");
53
57
  };
54
- export const getAudioCombinedFilePath = (audioDirPath, fileName) => {
58
+ export const getAudioCombinedFilePath = (audioDirPath, fileName, lang) => {
59
+ if (lang) {
60
+ return path.resolve(audioDirPath, fileName, `${fileName}_${lang}.mp3`);
61
+ }
55
62
  return path.resolve(audioDirPath, fileName, fileName + ".mp3");
56
63
  };
57
64
  export const getAudioArtifactFilePath = (outDirPath, fileName) => {
58
65
  return path.resolve(outDirPath, fileName + ".mp3");
59
66
  };
60
- export const getOutputVideoFilePath = (outDirPath, fileName) => {
61
- return path.resolve(outDirPath, fileName + ".mp4");
67
+ export const getOutputVideoFilePath = (outDirPath, fileName, lang, caption) => {
68
+ const suffix = lang ? `_${lang}` : "";
69
+ const suffix2 = caption ? `__${caption}` : "";
70
+ return path.resolve(outDirPath, `${fileName}${suffix}${suffix2}.mp4`);
62
71
  };
63
- export const getOutputPdfFilePath = (outDirPath, fileName, pdfMode) => {
64
- return path.resolve(outDirPath, fileName + "_" + pdfMode + ".pdf");
72
+ export const getOutputPdfFilePath = (outDirPath, fileName, pdfMode, lang) => {
73
+ if (lang) {
74
+ return path.resolve(outDirPath, `${fileName}_${pdfMode}_${lang}.pdf`);
75
+ }
76
+ return path.resolve(outDirPath, `${fileName}_${pdfMode}.pdf`);
65
77
  };
66
78
  export const getTemplateFilePath = (templateName) => {
67
79
  return path.resolve(__dirname, "../../assets/templates/" + templateName + ".json");
@@ -74,6 +86,7 @@ export const mkdir = (dirPath) => {
74
86
  };
75
87
  export const silentPath = path.resolve(__dirname, "../../assets/audio/silent300.mp3");
76
88
  export const silentLastPath = path.resolve(__dirname, "../../assets/audio/silent800.mp3");
89
+ export const silent60secPath = path.resolve(__dirname, "../../assets/audio/silent60sec.mp3");
77
90
  export const defaultBGMPath = path.resolve(__dirname, "../../assets/music/StarsBeyondEx.mp3");
78
91
  export const getHTMLFile = (filename) => {
79
92
  const htmlPath = path.resolve(__dirname, `../../assets/html/${filename}.html`);
@@ -131,3 +144,9 @@ export const resolveMediaSource = (source, context) => {
131
144
  }
132
145
  return null;
133
146
  };
147
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
+ export const readAndParseJson = (filePath, schema) => {
149
+ const fileContent = fs.readFileSync(filePath, "utf-8");
150
+ const json = JSON.parse(fileContent);
151
+ return schema.parse(json);
152
+ };
@@ -5,11 +5,16 @@ const processChart = async (params) => {
5
5
  const { beat, imagePath, canvasSize, textSlideStyle } = params;
6
6
  if (!beat.image || beat.image.type !== imageType)
7
7
  return;
8
+ const isCircular = beat.image.chartData.type === "pie" ||
9
+ beat.image.chartData.type === "doughnut" ||
10
+ beat.image.chartData.type === "polarArea" ||
11
+ beat.image.chartData.type === "radar";
12
+ const chart_width = isCircular ? Math.min(canvasSize.width, canvasSize.height) * 0.75 : canvasSize.width * 0.75;
8
13
  const template = getHTMLFile("chart");
9
14
  const htmlData = interpolate(template, {
10
15
  title: beat.image.title,
11
16
  style: textSlideStyle,
12
- width: Math.round(canvasSize.width * 0.625).toString(),
17
+ chart_width: chart_width.toString(),
13
18
  chart_data: JSON.stringify(beat.image.chartData),
14
19
  });
15
20
  await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height);
@@ -0,0 +1,3 @@
1
+ import { ImageProcessorParams } from "../../types/index.js";
2
+ export declare const imageType = "html_tailwind";
3
+ export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
@@ -0,0 +1,18 @@
1
+ import { getHTMLFile } from "../file.js";
2
+ import { renderHTMLToImage, interpolate } from "../markdown.js";
3
+ export const imageType = "html_tailwind";
4
+ const processHtmlTailwind = async (params) => {
5
+ const { beat, imagePath, canvasSize } = params;
6
+ if (!beat.image || beat.image.type !== imageType)
7
+ return;
8
+ const html = Array.isArray(beat.image.html) ? beat.image.html.join("\n") : beat.image.html;
9
+ const template = getHTMLFile("tailwind");
10
+ const htmlData = interpolate(template, {
11
+ // style: textSlideStyle,
12
+ // width: Math.round(canvasSize.width * 0.625).toString(),
13
+ html_body: html,
14
+ });
15
+ await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height);
16
+ return imagePath;
17
+ };
18
+ export const process = processHtmlTailwind;
@@ -4,4 +4,5 @@ import * as pluginImage from "./image.js";
4
4
  import * as pluginChart from "./chart.js";
5
5
  import * as pluginMermaid from "./mermaid.js";
6
6
  import * as pluginMovie from "./movie.js";
7
- export declare const imagePlugins: (typeof pluginTextSlide | typeof pluginMarkdown | typeof pluginImage | typeof pluginChart | typeof pluginMermaid | typeof pluginMovie)[];
7
+ import * as pluginHtmlTailwind from "./html_tailwind.js";
8
+ export declare const imagePlugins: (typeof pluginTextSlide | typeof pluginMarkdown | typeof pluginImage | typeof pluginChart | typeof pluginMermaid | typeof pluginMovie | typeof pluginHtmlTailwind)[];
@@ -4,4 +4,5 @@ import * as pluginImage from "./image.js";
4
4
  import * as pluginChart from "./chart.js";
5
5
  import * as pluginMermaid from "./mermaid.js";
6
6
  import * as pluginMovie from "./movie.js";
7
- export const imagePlugins = [pluginTextSlide, pluginMarkdown, pluginImage, pluginChart, pluginMermaid, pluginMovie];
7
+ import * as pluginHtmlTailwind from "./html_tailwind.js";
8
+ export const imagePlugins = [pluginTextSlide, pluginMarkdown, pluginImage, pluginChart, pluginMermaid, pluginMovie, pluginHtmlTailwind];
@@ -14,7 +14,7 @@ const processMermaid = async (params) => {
14
14
  style: textSlideStyle,
15
15
  diagram_code: `${diagram_code}\n${beat.image.appendix?.join("\n") ?? ""}`,
16
16
  });
17
- await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height);
17
+ await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height, true);
18
18
  }
19
19
  return imagePath;
20
20
  };
@@ -0,0 +1,3 @@
1
+ import { ImageProcessorParams } from "../../types/index.js";
2
+ export declare const imageType = "tailwind";
3
+ export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
@@ -0,0 +1,18 @@
1
+ import { getHTMLFile } from "../file.js";
2
+ import { renderHTMLToImage, interpolate } from "../markdown.js";
3
+ export const imageType = "tailwind";
4
+ const processTailwind = async (params) => {
5
+ const { beat, imagePath, canvasSize } = params;
6
+ if (!beat.image || beat.image.type !== imageType)
7
+ return;
8
+ const html = Array.isArray(beat.image.html) ? beat.image.html.join("\n") : beat.image.html;
9
+ const template = getHTMLFile("tailwind");
10
+ const htmlData = interpolate(template, {
11
+ // style: textSlideStyle,
12
+ // width: Math.round(canvasSize.width * 0.625).toString(),
13
+ html_body: html,
14
+ });
15
+ await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height);
16
+ return imagePath;
17
+ };
18
+ export const process = processTailwind;
@@ -5,8 +5,15 @@ const processTextSlide = async (params) => {
5
5
  if (!beat.image || beat.image.type !== imageType)
6
6
  return;
7
7
  const slide = beat.image.slide;
8
- const markdown = `# ${slide.title}\n` + slide.bullets.map((text) => `- ${text}`).join("\n");
9
- await renderMarkdownToImage(markdown, textSlideStyle, imagePath, canvasSize.width, canvasSize.height);
8
+ const markdown = `# ${slide.title}\n` + (slide.subtitle ? `## ${slide.subtitle}\n` : "") + (slide.bullets ?? []).map((text) => `- ${text}`).join("\n");
9
+ const topMargin = (() => {
10
+ if (slide.bullets?.length && slide.bullets.length > 0) {
11
+ return "";
12
+ }
13
+ const marginTop = slide.subtitle ? canvasSize.height * 0.4 : canvasSize.height * 0.45;
14
+ return `body {margin-top: ${marginTop}px;}`;
15
+ })();
16
+ await renderMarkdownToImage(markdown, textSlideStyle + topMargin, imagePath, canvasSize.width, canvasSize.height);
10
17
  return imagePath;
11
18
  };
12
19
  export const process = processTextSlide;
@@ -1,3 +1,3 @@
1
- export declare const renderHTMLToImage: (html: string, outputPath: string, width: number, height: number) => Promise<void>;
1
+ export declare const renderHTMLToImage: (html: string, outputPath: string, width: number, height: number, isMermaid?: boolean, omitBackground?: boolean) => Promise<void>;
2
2
  export declare const renderMarkdownToImage: (markdown: string, style: string, outputPath: string, width: number, height: number) => Promise<void>;
3
3
  export declare const interpolate: (template: string, data: Record<string, string>) => string;
@@ -2,7 +2,7 @@ import { GraphAILogger } from "graphai";
2
2
  import { marked } from "marked";
3
3
  import puppeteer from "puppeteer";
4
4
  const isCI = process.env.CI === "true";
5
- export const renderHTMLToImage = async (html, outputPath, width, height) => {
5
+ export const renderHTMLToImage = async (html, outputPath, width, height, isMermaid = false, omitBackground = false) => {
6
6
  // Use Puppeteer to render HTML to an image
7
7
  const browser = await puppeteer.launch({
8
8
  args: isCI ? ["--no-sandbox"] : [],
@@ -12,8 +12,14 @@ export const renderHTMLToImage = async (html, outputPath, width, height) => {
12
12
  await page.setContent(html);
13
13
  // Adjust page settings if needed (like width, height, etc.)
14
14
  await page.setViewport({ width, height });
15
+ if (isMermaid) {
16
+ await page.waitForFunction(() => {
17
+ const el = document.querySelector(".mermaid");
18
+ return el && el.dataset.ready === "true";
19
+ }, { timeout: 20000 });
20
+ }
15
21
  // Step 3: Capture screenshot of the page (which contains the Markdown-rendered HTML)
16
- await page.screenshot({ path: outputPath });
22
+ await page.screenshot({ path: outputPath, omitBackground: omitBackground });
17
23
  await browser.close();
18
24
  GraphAILogger.info(`HTML image rendered to ${outputPath}`);
19
25
  };
@@ -2,16 +2,10 @@ import { MulmoStudio, MulmoScript } from "../types/index.js";
2
2
  export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, currentStudio: MulmoStudio | undefined, fileName: string) => {
3
3
  beats: {
4
4
  duration?: number | undefined;
5
- multiLingualTexts?: Record<string, {
6
- text: string;
7
- lang: string;
8
- texts?: string[] | undefined;
9
- ttsTexts?: string[] | undefined;
10
- duration?: number | undefined;
11
- }> | undefined;
12
5
  hash?: string | undefined;
13
6
  audioFile?: string | undefined;
14
7
  imageFile?: string | undefined;
8
+ captionFile?: string | undefined;
15
9
  }[];
16
10
  script: {
17
11
  $mulmocast: {
@@ -23,7 +17,7 @@ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, curre
23
17
  height: number;
24
18
  };
25
19
  speechParams: {
26
- provider: "openai" | "nijivoice";
20
+ provider: "openai" | "nijivoice" | "google";
27
21
  speakers: Record<string, {
28
22
  voiceId: string;
29
23
  displayName?: Record<string, string> | undefined;
@@ -33,9 +27,16 @@ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, curre
33
27
  } | undefined;
34
28
  }>;
35
29
  };
30
+ audioParams: {
31
+ introPadding: number;
32
+ padding: number;
33
+ closingPadding: number;
34
+ outroPadding: number;
35
+ };
36
36
  beats: {
37
37
  text: string;
38
38
  speaker: string;
39
+ duration?: number | undefined;
39
40
  speechOptions?: {
40
41
  speed?: number | undefined;
41
42
  instruction?: string | undefined;
@@ -110,7 +111,8 @@ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, curre
110
111
  type: "textSlide";
111
112
  slide: {
112
113
  title: string;
113
- bullets: string[];
114
+ subtitle?: string | undefined;
115
+ bullets?: string[] | undefined;
114
116
  };
115
117
  } | {
116
118
  type: "chart";
@@ -133,6 +135,9 @@ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, curre
133
135
  type: "mermaid";
134
136
  title: string;
135
137
  appendix?: string[] | undefined;
138
+ } | {
139
+ type: "html_tailwind";
140
+ html: string | string[];
136
141
  } | undefined;
137
142
  audio?: {
138
143
  type: "audio";
@@ -176,9 +181,6 @@ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, curre
176
181
  textSlideParams?: {
177
182
  cssStyles: string | string[];
178
183
  } | undefined;
179
- videoParams?: {
180
- padding?: number | undefined;
181
- } | undefined;
182
184
  omitCaptions?: boolean | undefined;
183
185
  description?: string | undefined;
184
186
  references?: {
@@ -191,4 +193,13 @@ export declare const createOrUpdateStudioData: (_mulmoScript: MulmoScript, curre
191
193
  __test_invalid__?: boolean | undefined;
192
194
  };
193
195
  filename: string;
196
+ multiLingual: {
197
+ multiLingualTexts: Record<string, {
198
+ text: string;
199
+ lang: string;
200
+ texts?: string[] | undefined;
201
+ ttsTexts?: string[] | undefined;
202
+ duration?: number | undefined;
203
+ }>;
204
+ }[];
194
205
  };
@@ -8,6 +8,7 @@ const rebuildStudio = (currentStudio, mulmoScript, fileName) => {
8
8
  script: mulmoScript,
9
9
  filename: fileName,
10
10
  beats: [...Array(mulmoScript.beats.length)].map(() => ({})),
11
+ multiLingual: [...Array(mulmoScript.beats.length)].map(() => ({ multiLingualTexts: {} })),
11
12
  };
12
13
  };
13
14
  const mulmoCredit = (speaker) => {
@@ -42,6 +43,9 @@ export const createOrUpdateStudioData = (_mulmoScript, currentStudio, fileName)
42
43
  mulmoScript.beats.forEach((beat, index) => {
43
44
  // Filling the default values
44
45
  studio.script.beats[index] = mulmoBeatSchema.parse(beat);
46
+ if (!studio.multiLingual[index]) {
47
+ studio.multiLingual[index] = { multiLingualTexts: {} };
48
+ }
45
49
  });
46
50
  return studio;
47
51
  };
@@ -0,0 +1,15 @@
1
+ import { MulmoBeat, MulmoScript, MulmoScriptTemplate, MulmoStoryboard } from "../types/index.js";
2
+ export declare const imagePrompt: (beat: MulmoBeat, style?: string) => string;
3
+ export declare const graphDataScriptFromUrlPrompt: (sourceTextInput: string) => string;
4
+ export declare const graphDataScriptGeneratePrompt: (scene: string) => string;
5
+ export declare const getMulmoScriptTemplateSystemPrompt: (template: MulmoScriptTemplate) => string;
6
+ export declare const interactiveClarificationPrompt = "If there are any unclear points, be sure to ask the user questions and clarify them before generating the script.";
7
+ export declare const prefixPrompt = "Here is the web content that can be used as reference material for the script:";
8
+ export declare const translateSystemPrompt = "Please translate the given text into the language specified in language (in locale format, like en, ja, fr, ch).";
9
+ export declare const translatePrompts: string[];
10
+ export declare const sceneToBeatsPrompt: ({ sampleBeats, beatsPerScene, allScenes, }: {
11
+ sampleBeats: MulmoScript["beats"];
12
+ beatsPerScene: number;
13
+ allScenes: string;
14
+ }) => string;
15
+ export declare const storyToScriptInfoPrompt: (scriptWithoutBeats: Omit<MulmoScript, "beats">, story: MulmoStoryboard) => string;