mulmocast 1.1.8 → 1.1.10

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.
@@ -2,6 +2,7 @@ import { mulmoCaptionParamsSchema } from "../types/index.js";
2
2
  import { GraphAI, GraphAILogger } from "graphai";
3
3
  import * as agents from "@graphai/vanilla";
4
4
  import { getHTMLFile, getCaptionImagePath, getOutputStudioFilePath } from "../utils/file.js";
5
+ import { localizedText } from "../utils/utils.js";
5
6
  import { renderHTMLToImage, interpolate } from "../utils/markdown.js";
6
7
  import { MulmoStudioContextMethods, MulmoPresentationStyleMethods } from "../methods/index.js";
7
8
  import { fileWriteAgent } from "@graphai/vanilla_node_agents";
@@ -30,14 +31,10 @@ const graph_data = {
30
31
  const canvasSize = MulmoPresentationStyleMethods.getCanvasSize(context.presentationStyle);
31
32
  const imagePath = getCaptionImagePath(context, index);
32
33
  const template = getHTMLFile("caption");
33
- const text = (() => {
34
- const multiLingual = context.multiLingual;
35
- if (captionParams.lang && multiLingual) {
36
- return multiLingual[index].multiLingualTexts[captionParams.lang].text;
37
- }
34
+ if (captionParams.lang && !context.multiLingual?.[index]?.multiLingualTexts?.[captionParams.lang]) {
38
35
  GraphAILogger.warn(`No multiLingual caption found for beat ${index}, lang: ${captionParams.lang}`);
39
- return beat.text;
40
- })();
36
+ }
37
+ const text = localizedText(beat, context.multiLingual?.[index], captionParams.lang, context.studio.script.lang);
41
38
  const htmlData = interpolate(template, {
42
39
  caption: text,
43
40
  width: `${canvasSize.width}`,
@@ -1,6 +1,6 @@
1
1
  import "dotenv/config";
2
2
  import type { CallbackFunction } from "graphai";
3
- import { LANG, LocalizedText, MulmoStudioContext } from "../types/index.js";
3
+ import type { LANG, MulmoStudioContext } from "../types/index.js";
4
4
  export declare const translateTextGraph: {
5
5
  version: number;
6
6
  nodes: {
@@ -25,7 +25,7 @@ export declare const translateTextGraph: {
25
25
  };
26
26
  splitText: {
27
27
  agent: (namedInputs: {
28
- localizedText: LocalizedText;
28
+ localizedText: import("../types/type.js").LocalizedText;
29
29
  targetLang: LANG;
30
30
  }) => string[];
31
31
  inputs: {
@@ -46,6 +46,10 @@ export declare const translateTextGraph: {
46
46
  };
47
47
  };
48
48
  };
49
+ export declare const getOutputMultilingualFilePathAndMkdir: (context: MulmoStudioContext) => {
50
+ outputMultilingualFilePath: string;
51
+ outDirPath: string;
52
+ };
49
53
  export declare const translateBeat: (index: number, context: MulmoStudioContext, targetLangs: string[], args?: {
50
54
  settings?: Record<string, string>;
51
55
  callbacks?: CallbackFunction[];
@@ -1,21 +1,17 @@
1
1
  import "dotenv/config";
2
- import { createHash } from "crypto";
3
2
  import fs from "fs";
4
3
  import { GraphAI, assert, isNull, GraphAILogger } from "graphai";
5
4
  import * as agents from "@graphai/vanilla";
6
5
  import { openAIAgent } from "@graphai/openai_agent";
7
6
  import { fileWriteAgent } from "@graphai/vanilla_node_agents";
8
- import { recursiveSplitJa } from "../utils/string.js";
7
+ import { splitText } from "../utils/string.js";
9
8
  import { settings2GraphAIConfig } from "../utils/utils.js";
10
9
  import { getMultiLingual } from "../utils/context.js";
11
10
  import { currentMulmoScriptVersion } from "../utils/const.js";
12
- import { getOutputMultilingualFilePath, mkdir, writingMessage } from "../utils/file.js";
11
+ import { getOutputMultilingualFilePath, mkdir, writingMessage, hashSHA256 } from "../utils/file.js";
13
12
  import { translateSystemPrompt, translatePrompts } from "../utils/prompt.js";
14
13
  import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
15
14
  const vanillaAgents = agents.default ?? agents;
16
- const hashSHA256 = (text) => {
17
- return createHash("sha256").update(text, "utf8").digest("hex");
18
- };
19
15
  // 1. translateGraph / map each beats.
20
16
  // 2. beatGraph / map each target lang.
21
17
  // 3. translateTextGraph / translate text.
@@ -43,18 +39,7 @@ export const translateTextGraph = {
43
39
  agent: "openAIAgent",
44
40
  },
45
41
  splitText: {
46
- agent: (namedInputs) => {
47
- const { localizedText, targetLang } = namedInputs;
48
- // Cache
49
- if (localizedText.texts) {
50
- return localizedText.texts;
51
- }
52
- if (targetLang === "ja") {
53
- return recursiveSplitJa(localizedText.text);
54
- }
55
- // not split
56
- return [localizedText.text];
57
- },
42
+ agent: splitText,
58
43
  inputs: {
59
44
  targetLang: ":targetLang",
60
45
  localizedText: ":localizedText",
@@ -213,6 +198,13 @@ const agentFilters = [
213
198
  nodeIds: ["localizedText"],
214
199
  },
215
200
  ];
201
+ export const getOutputMultilingualFilePathAndMkdir = (context) => {
202
+ const fileName = MulmoStudioContextMethods.getFileName(context);
203
+ const outDirPath = MulmoStudioContextMethods.getOutDirPath(context);
204
+ const outputMultilingualFilePath = getOutputMultilingualFilePath(outDirPath, fileName);
205
+ mkdir(outDirPath);
206
+ return { outputMultilingualFilePath, outDirPath };
207
+ };
216
208
  export const translateBeat = async (index, context, targetLangs, args) => {
217
209
  const { settings, callbacks } = args ?? {};
218
210
  // Validate inputs
@@ -223,10 +215,7 @@ export const translateBeat = async (index, context, targetLangs, args) => {
223
215
  throw new Error("targetLangs must be a non-empty array");
224
216
  }
225
217
  try {
226
- const fileName = MulmoStudioContextMethods.getFileName(context);
227
- const outDirPath = MulmoStudioContextMethods.getOutDirPath(context);
228
- const outputMultilingualFilePath = getOutputMultilingualFilePath(outDirPath, fileName);
229
- mkdir(outDirPath);
218
+ const { outputMultilingualFilePath } = getOutputMultilingualFilePathAndMkdir(context);
230
219
  const config = settings2GraphAIConfig(settings, process.env);
231
220
  assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty");
232
221
  const graph = new GraphAI(beatGraph, { ...vanillaAgents, fileWriteAgent, openAIAgent }, { agentFilters, config });
@@ -257,10 +246,7 @@ export const translate = async (context, args) => {
257
246
  const { settings, callbacks } = args ?? {};
258
247
  try {
259
248
  MulmoStudioContextMethods.setSessionState(context, "multiLingual", true);
260
- const fileName = MulmoStudioContextMethods.getFileName(context);
261
- const outDirPath = MulmoStudioContextMethods.getOutDirPath(context);
262
- const outputMultilingualFilePath = getOutputMultilingualFilePath(outDirPath, fileName);
263
- mkdir(outDirPath);
249
+ const { outputMultilingualFilePath, outDirPath } = getOutputMultilingualFilePathAndMkdir(context);
264
250
  const targetLangs = [...new Set([context.lang, context.studio.script.captionParams?.lang].filter((x) => !isNull(x)))];
265
251
  const config = settings2GraphAIConfig(settings, process.env);
266
252
  assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty");
@@ -1,3 +1,4 @@
1
1
  export * from "./types/index.js";
2
2
  export * from "./utils/provider2agent.js";
3
3
  export * from "./utils/const.js";
4
+ export * from "./utils/string.js";
@@ -2,3 +2,4 @@
2
2
  export * from "./types/index.js";
3
3
  export * from "./utils/provider2agent.js";
4
4
  export * from "./utils/const.js";
5
+ export * from "./utils/string.js";
@@ -1,3 +1,4 @@
1
+ import { GraphAILogger } from "graphai";
1
2
  import { mulmoScriptSchema, mulmoStudioMultiLingualFileSchema } from "../types/index.js";
2
3
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
4
  const validate_1_0 = (script) => {
@@ -37,6 +38,9 @@ export const MulmoStudioMultiLingualMethod = {
37
38
  validate(jsonData, studioBeatsLength) {
38
39
  // TODO version check
39
40
  const result = mulmoStudioMultiLingualFileSchema.safeParse(jsonData);
41
+ if (!result.success) {
42
+ GraphAILogger.warn("multiLingual file validation failed.");
43
+ }
40
44
  const dataSet = result.success ? result.data.multiLingual : [];
41
45
  while (dataSet.length < studioBeatsLength) {
42
46
  dataSet.push({ multiLingualTexts: {} });
@@ -5,7 +5,7 @@ export const imageDirName = "images";
5
5
  export const cacheDirName = "cache";
6
6
  export const pdf_modes = ["slide", "talk", "handout"];
7
7
  export const pdf_sizes = ["letter", "a4"];
8
- export const languages = ["en", "ja", "fr", "es"];
8
+ export const languages = ["en", "ja", "fr", "es", "de", "zh-CN", "zh-TW", "ko", "it", "pt", "ar", "hi"];
9
9
  export const storyToScriptGenerateMode = {
10
10
  stepWise: "step_wise",
11
11
  oneStep: "one_step",
@@ -48,3 +48,4 @@ export declare const getAvailableScriptTemplates: () => MulmoScript[];
48
48
  export declare const writingMessage: (filePath: string) => void;
49
49
  export declare const readAndParseJson: <S extends ZodSchema<any>>(filePath: string, schema: S) => ReturnType<S["parse"]>;
50
50
  export declare const generateTimestampedFileName: (prefix: string) => string;
51
+ export declare const hashSHA256: (text: string) => string;
package/lib/utils/file.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import { createHash } from "crypto";
3
4
  import { parse as yamlParse } from "yaml";
4
5
  import { fileURLToPath } from "url";
5
6
  import { GraphAILogger } from "graphai";
@@ -226,3 +227,6 @@ export const generateTimestampedFileName = (prefix) => {
226
227
  const pad = (n) => n.toString().padStart(2, "0");
227
228
  return `${prefix}_${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
228
229
  };
230
+ export const hashSHA256 = (text) => {
231
+ return createHash("sha256").update(text, "utf8").digest("hex");
232
+ };
@@ -1,3 +1,4 @@
1
+ // node & browser
1
2
  export const provider2TTSAgent = {
2
3
  nijivoice: {
3
4
  agentName: "ttsNijivoiceAgent",
@@ -1,3 +1,4 @@
1
+ import type { LANG, LocalizedText } from "../types/index.js";
1
2
  export declare function splitIntoSentencesJa(paragraph: string, divider: string, minimum: number): string[];
2
3
  export declare const recursiveSplitJa: (text: string) => string[];
3
4
  interface Replacement {
@@ -6,4 +7,8 @@ interface Replacement {
6
7
  }
7
8
  export declare function replacePairsJa(replacements: Replacement[]): (str: string) => string;
8
9
  export declare const replacementsJa: Replacement[];
10
+ export declare const splitText: (namedInputs: {
11
+ localizedText: LocalizedText;
12
+ targetLang: LANG;
13
+ }) => string[];
9
14
  export {};
@@ -56,3 +56,15 @@ export const replacementsJa = [
56
56
  { from: "%", to: "パーセント" },
57
57
  { from: "IPO", to: "アイピーオー" },
58
58
  ];
59
+ export const splitText = (namedInputs) => {
60
+ const { localizedText, targetLang } = namedInputs;
61
+ // Cache
62
+ if (localizedText.texts) {
63
+ return localizedText.texts;
64
+ }
65
+ if (targetLang === "ja") {
66
+ return recursiveSplitJa(localizedText.text);
67
+ }
68
+ // not split
69
+ return [localizedText.text];
70
+ };
@@ -9,7 +9,7 @@ export declare const llmPair: (_llm?: LLM, _model?: string) => {
9
9
  export declare const chunkArray: <T>(array: T[], size?: number) => T[][];
10
10
  export declare const isHttp: (fileOrUrl: string) => boolean;
11
11
  export declare const text2hash: (input: string) => string;
12
- export declare const localizedText: (beat: MulmoBeat, multiLingualData?: MulmoStudioMultiLingualData, lang?: string) => string;
12
+ export declare const localizedText: (beat: MulmoBeat, multiLingualData?: MulmoStudioMultiLingualData, targetLang?: string, defaultLang?: string) => string;
13
13
  export declare const sleep: (milliseconds: number) => Promise<unknown>;
14
14
  export declare function userAssert(condition: boolean, message: string): asserts condition;
15
15
  export declare const settings2GraphAIConfig: (settings?: Record<string, string>, env?: Record<string, string | undefined>) => ConfigDataDictionary<DefaultConfigData>;
@@ -20,9 +20,12 @@ export const isHttp = (fileOrUrl) => {
20
20
  export const text2hash = (input) => {
21
21
  return crypto.createHash("sha256").update(input).digest("hex");
22
22
  };
23
- export const localizedText = (beat, multiLingualData, lang) => {
24
- if (lang && multiLingualData?.multiLingualTexts?.[lang]?.text) {
25
- return multiLingualData.multiLingualTexts[lang].text;
23
+ export const localizedText = (beat, multiLingualData, targetLang, defaultLang) => {
24
+ if (targetLang === defaultLang) {
25
+ return beat.text;
26
+ }
27
+ if (targetLang && multiLingualData?.multiLingualTexts?.[targetLang]?.text) {
28
+ return multiLingualData.multiLingualTexts[targetLang].text;
26
29
  }
27
30
  return beat.text;
28
31
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mulmocast",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.node.js",
@@ -26,6 +26,7 @@
26
26
  "./scripts/test",
27
27
  "./assets/audio/silent60sec.mp3",
28
28
  "./assets/html/",
29
+ "./assets/images/",
29
30
  "./assets/templates/"
30
31
  ],
31
32
  "directories": {
@@ -82,8 +83,8 @@
82
83
  "fluent-ffmpeg": "^2.1.3",
83
84
  "google-auth-library": "^10.1.0",
84
85
  "graphai": "^2.0.13",
85
- "inquirer": "^12.7.0",
86
- "marked": "^16.1.1",
86
+ "inquirer": "^12.9.0",
87
+ "marked": "^16.1.2",
87
88
  "ora": "^8.2.0",
88
89
  "puppeteer": "^24.15.0",
89
90
  "replicate": "^1.0.1",
@@ -106,7 +107,7 @@
106
107
  "ts-node": "^10.9.2",
107
108
  "tsx": "^4.20.3",
108
109
  "typescript": "^5.9.2",
109
- "typescript-eslint": "^8.37.0"
110
+ "typescript-eslint": "^8.39.0"
110
111
  },
111
112
  "engines": {
112
113
  "node": ">=18.0.0"
@@ -2,6 +2,9 @@
2
2
  "$mulmocast": {
3
3
  "version": "1.1"
4
4
  },
5
+ "audioParams": {
6
+ "bgmVolume": 0
7
+ },
5
8
  "lang": "en",
6
9
  "beats": [
7
10
  {