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
|
@@ -1,46 +1,22 @@
|
|
|
1
1
|
import { GraphAILogger } from "graphai";
|
|
2
|
-
import
|
|
3
|
-
import { MulmoScriptMethods } from "../methods/index.js";
|
|
2
|
+
import { FfmpegContextAddInput, FfmpegContextInit, FfmpegContextGenerateOutput, ffmpegGetMediaDuration } from "../utils/ffmpeg_utils.js";
|
|
4
3
|
const addBGMAgent = async ({ namedInputs, params, }) => {
|
|
5
4
|
const { voiceFile, outputFile, script } = namedInputs;
|
|
6
5
|
const { musicFile } = params;
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.complexFilter([
|
|
22
|
-
// Add a 2-second delay to the speech
|
|
23
|
-
`[1:a]adelay=${padding}|${padding}, volume=4[a1]`, // 4000ms delay for both left and right channels
|
|
24
|
-
// Set the background music volume to 0.2
|
|
25
|
-
`[0:a]volume=0.2[a0]`,
|
|
26
|
-
// Mix the delayed speech and the background music
|
|
27
|
-
`[a0][a1]amix=inputs=2:duration=longest:dropout_transition=3[amixed]`,
|
|
28
|
-
// Trim the output to the length of speech + 8 seconds
|
|
29
|
-
`[amixed]atrim=start=0:end=${totalDuration}[trimmed]`,
|
|
30
|
-
// Add fade out effect for the last 4 seconds
|
|
31
|
-
`[trimmed]afade=t=out:st=${totalDuration - padding / 1000}:d=${padding}`,
|
|
32
|
-
])
|
|
33
|
-
.on("error", (err) => {
|
|
34
|
-
GraphAILogger.info("Error: " + err.message);
|
|
35
|
-
reject(err);
|
|
36
|
-
})
|
|
37
|
-
.on("end", () => {
|
|
38
|
-
resolve(0);
|
|
39
|
-
})
|
|
40
|
-
.save(outputFile);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
await promise;
|
|
6
|
+
const speechDuration = await ffmpegGetMediaDuration(voiceFile);
|
|
7
|
+
const introPadding = script.audioParams.introPadding;
|
|
8
|
+
const outroPadding = script.audioParams.outroPadding;
|
|
9
|
+
const totalDuration = speechDuration + introPadding + outroPadding;
|
|
10
|
+
GraphAILogger.log("totalDucation:", speechDuration, totalDuration);
|
|
11
|
+
const ffmpegContext = FfmpegContextInit();
|
|
12
|
+
const musicInputIndex = FfmpegContextAddInput(ffmpegContext, musicFile);
|
|
13
|
+
const voiceInputIndex = FfmpegContextAddInput(ffmpegContext, voiceFile);
|
|
14
|
+
ffmpegContext.filterComplex.push(`[${musicInputIndex}:a]aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo, volume=0.2[music]`);
|
|
15
|
+
ffmpegContext.filterComplex.push(`[${voiceInputIndex}:a]aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo, volume=2, adelay=${introPadding * 1000}|${introPadding * 1000}[voice]`);
|
|
16
|
+
ffmpegContext.filterComplex.push(`[music][voice]amix=inputs=2:duration=longest[mixed]`);
|
|
17
|
+
ffmpegContext.filterComplex.push(`[mixed]atrim=start=0:end=${totalDuration}[trimmed]`);
|
|
18
|
+
ffmpegContext.filterComplex.push(`[trimmed]afade=t=out:st=${totalDuration - outroPadding}:d=${outroPadding}[faded]`);
|
|
19
|
+
await FfmpegContextGenerateOutput(ffmpegContext, outputFile, ["-map", "[faded]"]);
|
|
44
20
|
return outputFile;
|
|
45
21
|
};
|
|
46
22
|
const addBGMAgentInfo = {
|
|
@@ -1,45 +1,52 @@
|
|
|
1
1
|
import { GraphAILogger } from "graphai";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
const combineAudioFilesAgent = async ({ namedInputs }) => {
|
|
5
|
-
const { context, combinedFileName
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
// TODO: Remove hard-coded 0.8 and 0.3
|
|
16
|
-
resolve(metadata.format.duration + (isLastGap ? 0.8 : 0.3));
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
await Promise.all(context.studio.beats.map(async (studioBeat, index) => {
|
|
22
|
-
const isLastGap = index === context.studio.beats.length - 2;
|
|
2
|
+
import { silent60secPath } from "../utils/file.js";
|
|
3
|
+
import { FfmpegContextInit, FfmpegContextGenerateOutput, FfmpegContextInputFormattedAudio, ffmpegGetMediaDuration } from "../utils/ffmpeg_utils.js";
|
|
4
|
+
const combineAudioFilesAgent = async ({ namedInputs, }) => {
|
|
5
|
+
const { context, combinedFileName } = namedInputs;
|
|
6
|
+
const ffmpegContext = FfmpegContextInit();
|
|
7
|
+
const longSilentId = FfmpegContextInputFormattedAudio(ffmpegContext, silent60secPath);
|
|
8
|
+
// We cannot reuse longSilentId. We need to explicitly split it for each beat.
|
|
9
|
+
const silentIds = context.studio.beats.map((_, index) => `[ls_${index}]`);
|
|
10
|
+
ffmpegContext.filterComplex.push(`${longSilentId}asplit=${silentIds.length}${silentIds.join("")}`);
|
|
11
|
+
const inputIds = (await Promise.all(context.studio.beats.map(async (studioBeat, index) => {
|
|
12
|
+
const isClosingGap = index === context.studio.beats.length - 2;
|
|
23
13
|
if (studioBeat.audioFile) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
const audioId = FfmpegContextInputFormattedAudio(ffmpegContext, studioBeat.audioFile);
|
|
15
|
+
const padding = (() => {
|
|
16
|
+
if (index === context.studio.beats.length - 1) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
return isClosingGap ? context.studio.script.audioParams.closingPadding : context.studio.script.audioParams.padding;
|
|
20
|
+
})();
|
|
21
|
+
studioBeat.duration = (await ffmpegGetMediaDuration(studioBeat.audioFile)) + padding;
|
|
22
|
+
if (padding > 0) {
|
|
23
|
+
const silentId = silentIds.pop();
|
|
24
|
+
ffmpegContext.filterComplex.push(`${silentId}atrim=start=0:end=${padding}[padding_${index}]`);
|
|
25
|
+
return [audioId, `[padding_${index}]`];
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
return [audioId];
|
|
29
|
+
}
|
|
27
30
|
}
|
|
28
31
|
else {
|
|
29
|
-
|
|
32
|
+
// NOTE: We come here when the text is empty and no audio property is specified.
|
|
33
|
+
studioBeat.duration = context.studio.script.beats[index].duration ?? 1.0;
|
|
34
|
+
const silentId = silentIds.pop();
|
|
35
|
+
ffmpegContext.filterComplex.push(`${silentId}atrim=start=0:end=${studioBeat.duration}[silent_${index}]`);
|
|
36
|
+
return [`[silent_${index}]`];
|
|
30
37
|
}
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
})
|
|
41
|
-
.mergeToFile(combinedFileName, audioDirPath);
|
|
38
|
+
}))).flat();
|
|
39
|
+
// HACK: Because the last beat may not use an silent audio, we need to consume it to make ffmpeg happy.
|
|
40
|
+
if (silentIds.length > 1) {
|
|
41
|
+
throw new Error("UNEXPECTED: silentIds.length > 1");
|
|
42
|
+
}
|
|
43
|
+
silentIds.forEach((silentId) => {
|
|
44
|
+
GraphAILogger.log(`Using extra silentId: ${silentId}`);
|
|
45
|
+
ffmpegContext.filterComplex.push(`${silentId}atrim=start=0:end=${0.01}[silent_extra]`);
|
|
46
|
+
inputIds.push("[silent_extra]");
|
|
42
47
|
});
|
|
48
|
+
ffmpegContext.filterComplex.push(`${inputIds.join("")}concat=n=${inputIds.length}:v=0:a=1[aout]`);
|
|
49
|
+
await FfmpegContextGenerateOutput(ffmpegContext, combinedFileName, ["-map", "[aout]"]);
|
|
43
50
|
return {
|
|
44
51
|
studio: context.studio,
|
|
45
52
|
};
|
package/lib/agents/index.d.ts
CHANGED
|
@@ -2,12 +2,11 @@ import addBGMAgent from "./add_bgm_agent.js";
|
|
|
2
2
|
import combineAudioFilesAgent from "./combine_audio_files_agent.js";
|
|
3
3
|
import imageGoogleAgent from "./image_google_agent.js";
|
|
4
4
|
import imageOpenaiAgent from "./image_openai_agent.js";
|
|
5
|
-
import mulmoPromptsAgent from "./mulmo_prompts_agent.js";
|
|
6
5
|
import ttsNijivoiceAgent from "./tts_nijivoice_agent.js";
|
|
7
6
|
import ttsOpenaiAgent from "./tts_openai_agent.js";
|
|
8
|
-
import
|
|
7
|
+
import validateSchemaAgent from "./validate_schema_agent.js";
|
|
9
8
|
import { browserlessAgent } from "@graphai/browserless_agent";
|
|
10
9
|
import { textInputAgent } from "@graphai/input_agents";
|
|
11
10
|
import { openAIAgent } from "@graphai/openai_agent";
|
|
12
11
|
import { fileWriteAgent } from "@graphai/vanilla_node_agents";
|
|
13
|
-
export { openAIAgent, fileWriteAgent, browserlessAgent, textInputAgent, addBGMAgent, combineAudioFilesAgent, imageGoogleAgent, imageOpenaiAgent,
|
|
12
|
+
export { openAIAgent, fileWriteAgent, browserlessAgent, textInputAgent, addBGMAgent, combineAudioFilesAgent, imageGoogleAgent, imageOpenaiAgent, ttsNijivoiceAgent, ttsOpenaiAgent, validateSchemaAgent, };
|
package/lib/agents/index.js
CHANGED
|
@@ -2,13 +2,12 @@ import addBGMAgent from "./add_bgm_agent.js";
|
|
|
2
2
|
import combineAudioFilesAgent from "./combine_audio_files_agent.js";
|
|
3
3
|
import imageGoogleAgent from "./image_google_agent.js";
|
|
4
4
|
import imageOpenaiAgent from "./image_openai_agent.js";
|
|
5
|
-
import mulmoPromptsAgent from "./mulmo_prompts_agent.js";
|
|
6
5
|
import ttsNijivoiceAgent from "./tts_nijivoice_agent.js";
|
|
7
6
|
import ttsOpenaiAgent from "./tts_openai_agent.js";
|
|
8
|
-
import
|
|
7
|
+
import validateSchemaAgent from "./validate_schema_agent.js";
|
|
9
8
|
import { browserlessAgent } from "@graphai/browserless_agent";
|
|
10
9
|
import { textInputAgent } from "@graphai/input_agents";
|
|
11
10
|
import { openAIAgent } from "@graphai/openai_agent";
|
|
12
11
|
// import * as vanilla from "@graphai/vanilla";
|
|
13
12
|
import { fileWriteAgent } from "@graphai/vanilla_node_agents";
|
|
14
|
-
export { openAIAgent, fileWriteAgent, browserlessAgent, textInputAgent, addBGMAgent, combineAudioFilesAgent, imageGoogleAgent, imageOpenaiAgent,
|
|
13
|
+
export { openAIAgent, fileWriteAgent, browserlessAgent, textInputAgent, addBGMAgent, combineAudioFilesAgent, imageGoogleAgent, imageOpenaiAgent, ttsNijivoiceAgent, ttsOpenaiAgent, validateSchemaAgent, };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { GraphAILogger } from "graphai";
|
|
2
|
+
import * as textToSpeech from "@google-cloud/text-to-speech";
|
|
3
|
+
const client = new textToSpeech.TextToSpeechClient();
|
|
4
|
+
export const ttsGoogleAgent = async ({ namedInputs, params }) => {
|
|
5
|
+
const { text } = namedInputs;
|
|
6
|
+
const { voice, suppressError, speed } = params;
|
|
7
|
+
// Construct the voice request
|
|
8
|
+
const voiceParams = {
|
|
9
|
+
languageCode: "en-US", // TODO: Make this configurable
|
|
10
|
+
ssmlGender: "FEMALE", // TODO: Make this configurable
|
|
11
|
+
};
|
|
12
|
+
if (voice) {
|
|
13
|
+
voiceParams.name = voice;
|
|
14
|
+
}
|
|
15
|
+
// Construct the request
|
|
16
|
+
const request = {
|
|
17
|
+
input: { text: text },
|
|
18
|
+
voice: voiceParams,
|
|
19
|
+
audioConfig: {
|
|
20
|
+
audioEncoding: "MP3",
|
|
21
|
+
speakingRate: speed || 1.0,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
// Call the Text-to-Speech API
|
|
26
|
+
const [response] = await client.synthesizeSpeech(request);
|
|
27
|
+
return { buffer: response.audioContent };
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
if (suppressError) {
|
|
31
|
+
return {
|
|
32
|
+
error: e,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
GraphAILogger.info(e);
|
|
36
|
+
throw new Error("TTS Google Error");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const ttsGoogleAgentInfo = {
|
|
40
|
+
name: "ttsGoogleAgent",
|
|
41
|
+
agent: ttsGoogleAgent,
|
|
42
|
+
mock: ttsGoogleAgent,
|
|
43
|
+
samples: [],
|
|
44
|
+
description: "Google TTS agent",
|
|
45
|
+
category: ["tts"],
|
|
46
|
+
author: "Receptron Team",
|
|
47
|
+
repository: "https://github.com/receptron/graphai-agents/tree/main/tts/tts-openai-agent",
|
|
48
|
+
license: "MIT",
|
|
49
|
+
environmentVariables: ["OPENAI_API_KEY"],
|
|
50
|
+
};
|
|
51
|
+
export default ttsGoogleAgentInfo;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AgentFunction, AgentFunctionInfo, DefaultConfigData } from "graphai";
|
|
2
|
+
import { MulmoScript } from "../types/index.js";
|
|
3
|
+
import { ZodSchema } from "zod";
|
|
4
|
+
interface ValidateMulmoScriptInputs {
|
|
5
|
+
text: string;
|
|
6
|
+
schema: ZodSchema;
|
|
7
|
+
}
|
|
8
|
+
interface ValidateMulmoScriptResponse {
|
|
9
|
+
isValid: boolean;
|
|
10
|
+
data?: MulmoScript;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Zod schema validation agent
|
|
15
|
+
* Validates if a JSON string conforms to the Zod schema
|
|
16
|
+
*/
|
|
17
|
+
export declare const validateSchemaAgent: AgentFunction<object, ValidateMulmoScriptResponse, ValidateMulmoScriptInputs, DefaultConfigData>;
|
|
18
|
+
declare const validateMulmoScriptAgentInfo: AgentFunctionInfo;
|
|
19
|
+
export default validateMulmoScriptAgentInfo;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema validation agent
|
|
4
|
+
* Validates if a JSON string conforms to the Zod schema
|
|
5
|
+
*/
|
|
6
|
+
export const validateSchemaAgent = async ({ namedInputs, }) => {
|
|
7
|
+
const { text, schema } = namedInputs;
|
|
8
|
+
try {
|
|
9
|
+
assert(schema, "schema is required");
|
|
10
|
+
assert(text, "text is required");
|
|
11
|
+
const jsonData = JSON.parse(text);
|
|
12
|
+
const parsed = schema.parse(jsonData);
|
|
13
|
+
return {
|
|
14
|
+
isValid: true,
|
|
15
|
+
data: parsed,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return {
|
|
20
|
+
isValid: false,
|
|
21
|
+
error: error instanceof Error ? error.message : String(error),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const validateMulmoScriptAgentInfo = {
|
|
26
|
+
name: "validateSchemaAgent",
|
|
27
|
+
agent: validateSchemaAgent,
|
|
28
|
+
mock: validateSchemaAgent,
|
|
29
|
+
samples: [],
|
|
30
|
+
description: "Validates if a JSON string conforms to the Zod schema",
|
|
31
|
+
category: ["validation"],
|
|
32
|
+
author: "Receptron Team",
|
|
33
|
+
repository: "https://github.com/receptron/mulmocast-cli/tree/main/src/agents/validate_schema_agent.ts",
|
|
34
|
+
license: "MIT",
|
|
35
|
+
};
|
|
36
|
+
export default validateMulmoScriptAgentInfo;
|
package/lib/cli/args.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ export declare const getArgs: () => {
|
|
|
3
3
|
v: boolean;
|
|
4
4
|
o: string | undefined;
|
|
5
5
|
b: string | undefined;
|
|
6
|
+
l: string | undefined;
|
|
6
7
|
a: string | undefined;
|
|
7
8
|
i: string | undefined;
|
|
8
9
|
f: boolean;
|
|
10
|
+
c: string | undefined;
|
|
9
11
|
pdf_mode: string;
|
|
10
12
|
pdf_size: string;
|
|
11
13
|
_: (string | number)[];
|
package/lib/cli/args.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import yargs from "yargs";
|
|
2
2
|
import { hideBin } from "yargs/helpers";
|
|
3
3
|
import { commonOptions } from "./common.js";
|
|
4
|
-
import { pdf_modes, pdf_sizes } from "../utils/const.js";
|
|
4
|
+
import { pdf_modes, pdf_sizes, languages } from "../utils/const.js";
|
|
5
5
|
export const getArgs = () => {
|
|
6
6
|
return commonOptions(yargs(hideBin(process.argv)))
|
|
7
7
|
.scriptName("mulmo")
|
|
@@ -23,6 +23,13 @@ export const getArgs = () => {
|
|
|
23
23
|
demandOption: false,
|
|
24
24
|
default: false,
|
|
25
25
|
type: "boolean",
|
|
26
|
+
})
|
|
27
|
+
.option("c", {
|
|
28
|
+
alias: "caption",
|
|
29
|
+
description: "Video captions",
|
|
30
|
+
choices: languages,
|
|
31
|
+
demandOption: false,
|
|
32
|
+
type: "string",
|
|
26
33
|
})
|
|
27
34
|
.option("pdf_mode", {
|
|
28
35
|
description: "pdf mode",
|
|
@@ -40,7 +47,7 @@ export const getArgs = () => {
|
|
|
40
47
|
return yargs
|
|
41
48
|
.positional("action", {
|
|
42
49
|
describe: "action to perform",
|
|
43
|
-
choices: ["translate", "audio", "images", "movie", "pdf"
|
|
50
|
+
choices: ["translate", "audio", "images", "movie", "pdf"],
|
|
44
51
|
type: "string",
|
|
45
52
|
})
|
|
46
53
|
.positional("file", {
|
package/lib/cli/bin.d.ts
ADDED
package/lib/cli/bin.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import yargs from "yargs/yargs";
|
|
4
|
+
import { hideBin } from "yargs/helpers";
|
|
5
|
+
import * as translateCmd from "./commands/translate/index.js";
|
|
6
|
+
import * as audioCmd from "./commands/audio/index.js";
|
|
7
|
+
import * as imagesCmd from "./commands/image/index.js";
|
|
8
|
+
import * as movieCmd from "./commands/movie/index.js";
|
|
9
|
+
import * as pdfCmd from "./commands/pdf/index.js";
|
|
10
|
+
import * as toolCmd from "./commands/tool/index.js";
|
|
11
|
+
import { GraphAILogger } from "graphai";
|
|
12
|
+
export const main = async () => {
|
|
13
|
+
const cli = yargs(hideBin(process.argv))
|
|
14
|
+
.scriptName("mulmo")
|
|
15
|
+
.usage("$0 <command> [options]")
|
|
16
|
+
.option("v", {
|
|
17
|
+
alias: "verbose",
|
|
18
|
+
describe: "verbose log",
|
|
19
|
+
demandOption: true,
|
|
20
|
+
default: false,
|
|
21
|
+
type: "boolean",
|
|
22
|
+
})
|
|
23
|
+
.command(translateCmd)
|
|
24
|
+
.command(audioCmd)
|
|
25
|
+
.command(imagesCmd)
|
|
26
|
+
.command(movieCmd)
|
|
27
|
+
.command(pdfCmd)
|
|
28
|
+
.command(toolCmd)
|
|
29
|
+
.demandCommand()
|
|
30
|
+
.strict()
|
|
31
|
+
.help()
|
|
32
|
+
.alias("help", "h");
|
|
33
|
+
await cli.parseAsync();
|
|
34
|
+
};
|
|
35
|
+
main().catch((error) => {
|
|
36
|
+
GraphAILogger.info("An unexpected error occurred:", error);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
package/lib/cli/cli.js
CHANGED
|
@@ -6,18 +6,32 @@ import { GraphAILogger } from "graphai";
|
|
|
6
6
|
import { getArgs } from "./args.js";
|
|
7
7
|
import { createOrUpdateStudioData } from "../utils/preprocess.js";
|
|
8
8
|
import { outDirName, imageDirName, audioDirName } from "../utils/const.js";
|
|
9
|
-
import { translate, audio, images, movie, pdf } from "../actions/index.js";
|
|
10
|
-
import { getBaseDirPath, getFullPath, readMulmoScriptFile, fetchMulmoScriptFile, getOutputStudioFilePath } from "../utils/file.js";
|
|
9
|
+
import { translate, audio, images, movie, pdf, captions } from "../actions/index.js";
|
|
10
|
+
import { getBaseDirPath, getFullPath, readMulmoScriptFile, fetchMulmoScriptFile, getOutputStudioFilePath, resolveDirPath } from "../utils/file.js";
|
|
11
11
|
import { isHttp } from "../utils/utils.js";
|
|
12
|
+
import clipboardy from "clipboardy";
|
|
12
13
|
export const getFileObject = (_args) => {
|
|
13
14
|
const { basedir, file, outdir, imagedir, audiodir } = _args;
|
|
14
15
|
const baseDirPath = getBaseDirPath(basedir);
|
|
15
|
-
const
|
|
16
|
-
const fileName =
|
|
16
|
+
const outDirPath = getFullPath(baseDirPath, outdir ?? outDirName);
|
|
17
|
+
const { fileOrUrl, fileName } = (() => {
|
|
18
|
+
if (file === "__clipboard") {
|
|
19
|
+
// We generate a new unique script file from clipboard text in the output directory
|
|
20
|
+
const now = new Date();
|
|
21
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
22
|
+
const fileName = `script_${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
23
|
+
const clipboardText = clipboardy.readSync();
|
|
24
|
+
const fileOrUrl = resolveDirPath(outDirPath, `${fileName}.json`);
|
|
25
|
+
fs.writeFileSync(fileOrUrl, clipboardText, "utf8");
|
|
26
|
+
return { fileOrUrl, fileName };
|
|
27
|
+
}
|
|
28
|
+
const fileOrUrl = file ?? "";
|
|
29
|
+
const fileName = path.parse(fileOrUrl).name;
|
|
30
|
+
return { fileOrUrl, fileName };
|
|
31
|
+
})();
|
|
17
32
|
const isHttpPath = isHttp(fileOrUrl);
|
|
18
33
|
const mulmoFilePath = isHttpPath ? "" : getFullPath(baseDirPath, fileOrUrl);
|
|
19
34
|
const mulmoFileDirPath = path.dirname(isHttpPath ? baseDirPath : mulmoFilePath);
|
|
20
|
-
const outDirPath = getFullPath(baseDirPath, outdir ?? outDirName);
|
|
21
35
|
const imageDirPath = getFullPath(outDirPath, imagedir ?? imageDirName);
|
|
22
36
|
const audioDirPath = getFullPath(outDirPath, audiodir ?? audioDirName);
|
|
23
37
|
const outputStudioFilePath = getOutputStudioFilePath(outDirPath, fileName);
|
|
@@ -50,7 +64,7 @@ export const main = async () => {
|
|
|
50
64
|
GraphAILogger.setLevelEnabled("log", false);
|
|
51
65
|
GraphAILogger.setLevelEnabled("warn", false);
|
|
52
66
|
}
|
|
53
|
-
const { action, force, pdf_mode, pdf_size } = args;
|
|
67
|
+
const { action, f: force, pdf_mode, pdf_size, l: lang, c: caption } = args;
|
|
54
68
|
const mulmoScript = await fetchScript(isHttpPath, mulmoFilePath, fileOrUrl);
|
|
55
69
|
// Create or update MulmoStudio file with MulmoScript
|
|
56
70
|
const currentStudio = readMulmoScriptFile(outputStudioFilePath);
|
|
@@ -68,8 +82,11 @@ export const main = async () => {
|
|
|
68
82
|
studio,
|
|
69
83
|
fileDirs: files,
|
|
70
84
|
force: Boolean(force),
|
|
85
|
+
lang: lang,
|
|
86
|
+
caption: caption,
|
|
71
87
|
};
|
|
72
|
-
if (action === "translate") {
|
|
88
|
+
if (action === "translate" || lang || caption) {
|
|
89
|
+
GraphAILogger.log("run translate");
|
|
73
90
|
await translate(context);
|
|
74
91
|
}
|
|
75
92
|
if (action === "audio") {
|
|
@@ -79,6 +96,9 @@ export const main = async () => {
|
|
|
79
96
|
await images(context);
|
|
80
97
|
}
|
|
81
98
|
if (action === "movie") {
|
|
99
|
+
if (caption) {
|
|
100
|
+
await captions(context);
|
|
101
|
+
}
|
|
82
102
|
await audio(context);
|
|
83
103
|
await images(context);
|
|
84
104
|
await movie(context);
|
|
@@ -87,4 +107,11 @@ export const main = async () => {
|
|
|
87
107
|
await images(context);
|
|
88
108
|
await pdf(context, pdf_mode, pdf_size);
|
|
89
109
|
}
|
|
110
|
+
if (context.studio.script.title) {
|
|
111
|
+
GraphAILogger.info(context.studio.script.title);
|
|
112
|
+
}
|
|
113
|
+
if (context.studio.script.references) {
|
|
114
|
+
const textOutput = context.studio.script.references.map((reference) => `${reference.title}\n${reference.url}`).join("\n");
|
|
115
|
+
GraphAILogger.info(textOutput);
|
|
116
|
+
}
|
|
90
117
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Argv } from "yargs";
|
|
2
|
+
export declare const builder: (yargs: Argv) => Argv<{
|
|
3
|
+
o: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
b: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
l: string | undefined;
|
|
8
|
+
} & {
|
|
9
|
+
f: boolean;
|
|
10
|
+
} & {
|
|
11
|
+
file: string | undefined;
|
|
12
|
+
} & {
|
|
13
|
+
a: string | undefined;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { audio } from "../../../actions/index.js";
|
|
2
|
+
import { initializeContext, runTranslateIfNeeded } from "../../../cli/helpers.js";
|
|
3
|
+
export const handler = async (argv) => {
|
|
4
|
+
const context = await initializeContext(argv);
|
|
5
|
+
await runTranslateIfNeeded(context, argv);
|
|
6
|
+
await audio(context);
|
|
7
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Argv } from "yargs";
|
|
2
|
+
export declare const builder: (yargs: Argv) => Argv<{
|
|
3
|
+
o: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
b: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
l: string | undefined;
|
|
8
|
+
} & {
|
|
9
|
+
f: boolean;
|
|
10
|
+
} & {
|
|
11
|
+
file: string | undefined;
|
|
12
|
+
} & {
|
|
13
|
+
i: string | undefined;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { images } from "../../../actions/index.js";
|
|
2
|
+
import { initializeContext, runTranslateIfNeeded } from "../../../cli/helpers.js";
|
|
3
|
+
export const handler = async (argv) => {
|
|
4
|
+
const context = await initializeContext(argv);
|
|
5
|
+
await runTranslateIfNeeded(context, argv);
|
|
6
|
+
await images(context);
|
|
7
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Argv } from "yargs";
|
|
2
|
+
export declare const builder: (yargs: Argv) => Argv<{
|
|
3
|
+
o: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
b: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
l: string | undefined;
|
|
8
|
+
} & {
|
|
9
|
+
f: boolean;
|
|
10
|
+
} & {
|
|
11
|
+
file: string | undefined;
|
|
12
|
+
} & {
|
|
13
|
+
a: string | undefined;
|
|
14
|
+
} & {
|
|
15
|
+
i: string | undefined;
|
|
16
|
+
} & {
|
|
17
|
+
c: string | undefined;
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { commonOptions } from "../../common.js";
|
|
2
|
+
import { languages } from "../../../utils/const.js";
|
|
3
|
+
export const builder = (yargs) => commonOptions(yargs)
|
|
4
|
+
.option("a", {
|
|
5
|
+
alias: "audiodir",
|
|
6
|
+
describe: "Audio output directory",
|
|
7
|
+
type: "string",
|
|
8
|
+
})
|
|
9
|
+
.option("i", {
|
|
10
|
+
alias: "imagedir",
|
|
11
|
+
describe: "Image output directory",
|
|
12
|
+
type: "string",
|
|
13
|
+
})
|
|
14
|
+
.option("c", {
|
|
15
|
+
alias: "caption",
|
|
16
|
+
describe: "Video captions",
|
|
17
|
+
choices: languages,
|
|
18
|
+
type: "string",
|
|
19
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { audio, images, movie, captions } from "../../../actions/index.js";
|
|
2
|
+
import { initializeContext, runTranslateIfNeeded } from "../../../cli/helpers.js";
|
|
3
|
+
export const handler = async (argv) => {
|
|
4
|
+
const context = await initializeContext(argv);
|
|
5
|
+
await runTranslateIfNeeded(context, argv);
|
|
6
|
+
await audio(context);
|
|
7
|
+
await images(context);
|
|
8
|
+
if (context.caption) {
|
|
9
|
+
await captions(context);
|
|
10
|
+
}
|
|
11
|
+
await movie(context);
|
|
12
|
+
};
|