mulmocast 0.0.5 → 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.
- package/README.md +257 -39
- package/assets/audio/silent60sec.mp3 +0 -0
- package/assets/html/caption.html +45 -0
- package/assets/html/chart.html +1 -1
- package/assets/html/mermaid.html +6 -2
- package/assets/html/tailwind.html +13 -0
- package/assets/templates/business.json +57 -4
- package/assets/templates/comic_strips.json +35 -0
- package/assets/templates/ghibli_strips.json +35 -0
- package/lib/actions/audio.js +24 -11
- package/lib/actions/captions.d.ts +2 -0
- package/lib/actions/captions.js +62 -0
- package/lib/actions/images.js +3 -2
- package/lib/actions/index.d.ts +1 -0
- package/lib/actions/index.js +1 -0
- package/lib/actions/movie.js +78 -86
- package/lib/actions/pdf.js +15 -5
- package/lib/actions/translate.js +32 -26
- package/lib/agents/add_bgm_agent.js +15 -39
- package/lib/agents/combine_audio_files_agent.js +43 -36
- package/lib/agents/index.d.ts +2 -3
- package/lib/agents/index.js +2 -3
- package/lib/agents/tts_google_agent.d.ts +4 -0
- package/lib/agents/tts_google_agent.js +51 -0
- package/lib/agents/validate_schema_agent.d.ts +19 -0
- package/lib/agents/validate_schema_agent.js +36 -0
- package/lib/cli/args.d.ts +2 -0
- package/lib/cli/args.js +9 -2
- package/lib/cli/bin.d.ts +3 -0
- package/lib/cli/bin.js +38 -0
- package/lib/cli/cli.js +34 -7
- package/lib/cli/commands/audio/builder.d.ts +14 -0
- package/lib/cli/commands/audio/builder.js +6 -0
- package/lib/cli/commands/audio/handler.d.ts +4 -0
- package/lib/cli/commands/audio/handler.js +7 -0
- package/lib/cli/commands/audio/index.d.ts +4 -0
- package/lib/cli/commands/audio/index.js +4 -0
- package/lib/cli/commands/image/builder.d.ts +14 -0
- package/lib/cli/commands/image/builder.js +6 -0
- package/lib/cli/commands/image/handler.d.ts +4 -0
- package/lib/cli/commands/image/handler.js +7 -0
- package/lib/cli/commands/image/index.d.ts +4 -0
- package/lib/cli/commands/image/index.js +4 -0
- package/lib/cli/commands/movie/builder.d.ts +18 -0
- package/lib/cli/commands/movie/builder.js +19 -0
- package/lib/cli/commands/movie/handler.d.ts +6 -0
- package/lib/cli/commands/movie/handler.js +12 -0
- package/lib/cli/commands/movie/index.d.ts +4 -0
- package/lib/cli/commands/movie/index.js +4 -0
- package/lib/cli/commands/pdf/builder.d.ts +18 -0
- package/lib/cli/commands/pdf/builder.js +19 -0
- package/lib/cli/commands/pdf/handler.d.ts +6 -0
- package/lib/cli/commands/pdf/handler.js +8 -0
- package/lib/cli/commands/pdf/index.d.ts +4 -0
- package/lib/cli/commands/pdf/index.js +4 -0
- package/lib/cli/commands/tool/index.d.ts +6 -0
- package/lib/cli/commands/tool/index.js +8 -0
- package/lib/cli/commands/tool/prompt/builder.d.ts +4 -0
- package/lib/cli/commands/tool/prompt/builder.js +11 -0
- package/lib/cli/commands/tool/prompt/handler.d.ts +4 -0
- package/lib/cli/commands/tool/prompt/handler.js +14 -0
- package/lib/cli/commands/tool/prompt/index.d.ts +4 -0
- package/lib/cli/commands/tool/prompt/index.js +4 -0
- package/lib/cli/commands/tool/schema/builder.d.ts +2 -0
- package/lib/cli/commands/tool/schema/builder.js +3 -0
- package/lib/cli/commands/tool/schema/handler.d.ts +2 -0
- package/lib/cli/commands/tool/schema/handler.js +12 -0
- package/lib/cli/commands/tool/schema/index.d.ts +4 -0
- package/lib/cli/commands/tool/schema/index.js +4 -0
- package/lib/cli/commands/tool/scripting/builder.d.ts +20 -0
- package/lib/cli/commands/tool/scripting/builder.js +63 -0
- package/lib/cli/commands/tool/scripting/handler.d.ts +12 -0
- package/lib/cli/commands/tool/scripting/handler.js +36 -0
- package/lib/cli/commands/tool/scripting/index.d.ts +4 -0
- package/lib/cli/commands/tool/scripting/index.js +4 -0
- package/lib/cli/commands/tool/story_to_script/builder.d.ts +18 -0
- package/lib/cli/commands/tool/story_to_script/builder.js +53 -0
- package/lib/cli/commands/tool/story_to_script/handler.d.ts +11 -0
- package/lib/cli/commands/tool/story_to_script/handler.js +35 -0
- package/lib/cli/commands/tool/story_to_script/index.d.ts +4 -0
- package/lib/cli/commands/tool/story_to_script/index.js +4 -0
- package/lib/cli/commands/translate/builder.d.ts +14 -0
- package/lib/cli/commands/translate/builder.js +5 -0
- package/lib/cli/commands/translate/handler.d.ts +4 -0
- package/lib/cli/commands/translate/handler.js +6 -0
- package/lib/cli/commands/translate/index.d.ts +4 -0
- package/lib/cli/commands/translate/index.js +4 -0
- package/lib/cli/common.d.ts +6 -2
- package/lib/cli/common.js +18 -7
- package/lib/cli/helpers.d.ts +38 -0
- package/lib/cli/helpers.js +115 -0
- package/lib/cli/tool-args.d.ts +1 -0
- package/lib/cli/tool-args.js +1 -1
- package/lib/cli/tool-cli.js +8 -0
- package/lib/methods/mulmo_script.d.ts +0 -1
- package/lib/methods/mulmo_script.js +4 -7
- package/lib/methods/mulmo_script_template.js +2 -12
- package/lib/tools/create_mulmo_script_from_url.d.ts +1 -1
- package/lib/tools/create_mulmo_script_from_url.js +43 -14
- package/lib/tools/create_mulmo_script_interactively.js +14 -13
- package/lib/tools/dump_prompt.js +2 -0
- package/lib/tools/story_to_script.d.ts +10 -0
- package/lib/tools/story_to_script.js +201 -0
- package/lib/types/cli_types.d.ts +14 -0
- package/lib/types/cli_types.js +1 -0
- package/lib/types/schema.d.ts +493 -176
- package/lib/types/schema.js +37 -7
- package/lib/types/type.d.ts +6 -1
- package/lib/utils/const.d.ts +1 -0
- package/lib/utils/const.js +1 -0
- package/lib/utils/ffmpeg_utils.d.ts +12 -0
- package/lib/utils/ffmpeg_utils.js +63 -0
- package/lib/utils/file.d.ts +7 -3
- package/lib/utils/file.js +24 -5
- package/lib/utils/image_plugins/chart.js +6 -1
- package/lib/utils/image_plugins/html_tailwind.d.ts +3 -0
- package/lib/utils/image_plugins/html_tailwind.js +18 -0
- package/lib/utils/image_plugins/index.d.ts +2 -1
- package/lib/utils/image_plugins/index.js +2 -1
- package/lib/utils/image_plugins/mermaid.js +1 -1
- package/lib/utils/image_plugins/tailwind.d.ts +3 -0
- package/lib/utils/image_plugins/tailwind.js +18 -0
- package/lib/utils/image_plugins/text_slide.js +9 -2
- package/lib/utils/markdown.d.ts +1 -1
- package/lib/utils/markdown.js +8 -2
- package/lib/utils/preprocess.d.ts +23 -12
- package/lib/utils/preprocess.js +4 -0
- package/lib/utils/prompt.d.ts +15 -0
- package/lib/utils/prompt.js +57 -0
- package/lib/utils/utils.d.ts +2 -0
- package/lib/utils/utils.js +10 -0
- package/package.json +27 -23
package/lib/types/schema.js
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/lib/types/type.d.ts
CHANGED
|
@@ -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[];
|
package/lib/utils/const.d.ts
CHANGED
package/lib/utils/const.js
CHANGED
|
@@ -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
|
+
};
|
package/lib/utils/file.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
-
|
|
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
|
-
|
|
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,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
|
-
|
|
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;
|
package/lib/utils/markdown.d.ts
CHANGED
|
@@ -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;
|
package/lib/utils/markdown.js
CHANGED
|
@@ -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
|
-
|
|
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
|
};
|
package/lib/utils/preprocess.js
CHANGED
|
@@ -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;
|