mulmocast 0.0.1 → 0.0.2
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 +108 -12
- package/assets/html/chart.html +47 -0
- package/assets/html/mermaid.html +63 -0
- package/assets/templates/business.json +60 -6
- package/assets/templates/children_book.json +1 -3
- package/assets/templates/coding.json +103 -0
- package/lib/actions/audio.d.ts +1 -1
- package/lib/actions/audio.js +52 -81
- package/lib/actions/images.d.ts +1 -1
- package/lib/actions/images.js +48 -80
- package/lib/actions/movie.d.ts +1 -1
- package/lib/actions/movie.js +76 -76
- package/lib/actions/translate.d.ts +1 -1
- package/lib/actions/translate.js +16 -52
- package/lib/agents/add_bgm_agent.d.ts +1 -1
- package/lib/agents/add_bgm_agent.js +10 -14
- package/lib/agents/combine_audio_files_agent.d.ts +1 -1
- package/lib/agents/combine_audio_files_agent.js +40 -30
- package/lib/agents/image_google_agent.d.ts +1 -1
- package/lib/agents/image_google_agent.js +8 -11
- package/lib/agents/image_openai_agent.js +7 -14
- package/lib/agents/index.d.ts +8 -8
- package/lib/agents/index.js +13 -30
- package/lib/agents/mulmo_prompts_agent.d.ts +1 -1
- package/lib/agents/mulmo_prompts_agent.js +7 -11
- package/lib/agents/prompts_data.js +1 -4
- package/lib/agents/tts_nijivoice_agent.d.ts +1 -1
- package/lib/agents/tts_nijivoice_agent.js +8 -12
- package/lib/agents/tts_openai_agent.js +9 -16
- package/lib/agents/validate_mulmo_script_agent.d.ts +1 -1
- package/lib/agents/validate_mulmo_script_agent.js +6 -10
- package/lib/cli/args.d.ts +2 -1
- package/lib/cli/args.js +16 -14
- package/lib/cli/cli.js +64 -49
- package/lib/cli/common.js +1 -5
- package/lib/cli/tool-args.d.ts +2 -1
- package/lib/cli/tool-args.js +19 -18
- package/lib/cli/tool-cli.js +32 -51
- package/lib/methods/index.d.ts +3 -3
- package/lib/methods/index.js +3 -19
- package/lib/methods/mulmo_script.d.ts +10 -5
- package/lib/methods/mulmo_script.js +17 -11
- package/lib/methods/mulmo_script_template.d.ts +1 -1
- package/lib/methods/mulmo_script_template.js +4 -10
- package/lib/methods/mulmo_studio_context.d.ts +1 -1
- package/lib/methods/mulmo_studio_context.js +3 -9
- package/lib/tools/create_mulmo_script_from_url.d.ts +3 -0
- package/lib/tools/create_mulmo_script_from_url.js +152 -0
- package/lib/tools/create_mulmo_script_interactively.d.ts +3 -0
- package/lib/tools/create_mulmo_script_interactively.js +217 -0
- package/lib/tools/dump_prompt.js +5 -8
- package/lib/tools/prompt.js +9 -11
- package/lib/tools/seed_from_url2.d.ts +3 -0
- package/lib/tools/seed_from_url2.js +154 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.js +1 -17
- package/lib/types/schema.d.ts +433 -71
- package/lib/types/schema.js +126 -111
- package/lib/types/type.d.ts +7 -3
- package/lib/types/type.js +1 -2
- package/lib/utils/const.d.ts +2 -1
- package/lib/utils/const.js +4 -6
- package/lib/utils/file.d.ts +19 -4
- package/lib/utils/file.js +78 -71
- package/lib/utils/filters.d.ts +1 -0
- package/lib/utils/filters.js +47 -26
- package/lib/utils/image_preprocess.d.ts +14 -0
- package/lib/utils/image_preprocess.js +52 -0
- package/lib/utils/inquirer.d.ts +2 -0
- package/lib/utils/inquirer.js +33 -0
- package/lib/utils/markdown.d.ts +3 -1
- package/lib/utils/markdown.js +17 -19
- package/lib/utils/plugins.d.ts +5 -0
- package/lib/utils/plugins.js +11 -0
- package/lib/utils/preprocess.d.ts +24 -7
- package/lib/utils/preprocess.js +8 -14
- package/lib/utils/string.js +4 -10
- package/lib/utils/text_hash.js +2 -39
- package/package.json +12 -6
package/lib/actions/audio.js
CHANGED
|
@@ -1,53 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.audio = void 0;
|
|
40
|
-
require("dotenv/config");
|
|
41
|
-
const graphai_1 = require("graphai");
|
|
42
|
-
const agents = __importStar(require("@graphai/vanilla"));
|
|
43
|
-
const tts_nijivoice_agent_1 = __importDefault(require("../agents/tts_nijivoice_agent"));
|
|
44
|
-
const add_bgm_agent_1 = __importDefault(require("../agents/add_bgm_agent"));
|
|
45
|
-
const combine_audio_files_agent_1 = __importDefault(require("../agents/combine_audio_files_agent"));
|
|
46
|
-
const tts_openai_agent_1 = __importDefault(require("../agents/tts_openai_agent"));
|
|
47
|
-
const vanilla_node_agents_1 = require("@graphai/vanilla_node_agents");
|
|
48
|
-
const methods_1 = require("../methods");
|
|
49
|
-
const filters_1 = require("../utils/filters");
|
|
50
|
-
const file_1 = require("../utils/file");
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { GraphAI } from "graphai";
|
|
3
|
+
import * as agents from "@graphai/vanilla";
|
|
4
|
+
import ttsNijivoiceAgent from "../agents/tts_nijivoice_agent.js";
|
|
5
|
+
import addBGMAgent from "../agents/add_bgm_agent.js";
|
|
6
|
+
import combineAudioFilesAgent from "../agents/combine_audio_files_agent.js";
|
|
7
|
+
import ttsOpenaiAgent from "../agents/tts_openai_agent.js";
|
|
8
|
+
import { fileWriteAgent } from "@graphai/vanilla_node_agents";
|
|
9
|
+
import { MulmoScriptMethods } from "../methods/index.js";
|
|
10
|
+
import { fileCacheAgentFilter } from "../utils/filters.js";
|
|
11
|
+
import { getAudioArtifactFilePath, getAudioSegmentDirPath, getAudioCombinedFilePath, getOutputStudioFilePath, defaultBGMPath, mkdir, writingMessage, } from "../utils/file.js";
|
|
12
|
+
const { default: __, ...vanillaAgents } = agents;
|
|
51
13
|
// const rion_takanashi_voice = "b9277ce3-ba1c-4f6f-9a65-c05ca102ded0"; // たかなし りおん
|
|
52
14
|
// const ben_carter_voice = "bc06c63f-fef6-43b6-92f7-67f919bd5dae"; // ベン・カーター
|
|
53
15
|
const graph_tts = {
|
|
@@ -57,7 +19,7 @@ const graph_tts = {
|
|
|
57
19
|
const { beat, script, speakers } = namedInputs;
|
|
58
20
|
return {
|
|
59
21
|
voiceId: speakers[beat.speaker].voiceId,
|
|
60
|
-
speechOptions:
|
|
22
|
+
speechOptions: MulmoScriptMethods.getSpeechOptions(script, beat),
|
|
61
23
|
};
|
|
62
24
|
},
|
|
63
25
|
inputs: {
|
|
@@ -82,7 +44,8 @@ const graph_tts = {
|
|
|
82
44
|
agent: ":ttsAgent",
|
|
83
45
|
inputs: {
|
|
84
46
|
text: ":beat.text",
|
|
85
|
-
file: "${:
|
|
47
|
+
file: "${:audioSegmentDirPath}/${:beat.audioFile}.mp3", // TODO
|
|
48
|
+
force: ":context.force",
|
|
86
49
|
},
|
|
87
50
|
params: {
|
|
88
51
|
voice: ":preprocessor.voiceId",
|
|
@@ -97,13 +60,20 @@ const graph_data = {
|
|
|
97
60
|
concurrency: 8,
|
|
98
61
|
nodes: {
|
|
99
62
|
context: {},
|
|
100
|
-
|
|
101
|
-
|
|
63
|
+
audioArtifactFilePath: {},
|
|
64
|
+
audioCombinedFilePath: {},
|
|
102
65
|
outputStudioFilePath: {},
|
|
103
|
-
|
|
66
|
+
audioDirPath: {},
|
|
67
|
+
audioSegmentDirPath: {},
|
|
104
68
|
map: {
|
|
105
69
|
agent: "mapAgent",
|
|
106
|
-
inputs: {
|
|
70
|
+
inputs: {
|
|
71
|
+
rows: ":context.studio.beats",
|
|
72
|
+
script: ":context.studio.script",
|
|
73
|
+
audioDirPath: ":audioDirPath",
|
|
74
|
+
audioSegmentDirPath: ":audioSegmentDirPath",
|
|
75
|
+
context: ":context",
|
|
76
|
+
},
|
|
107
77
|
params: {
|
|
108
78
|
rowKey: "beat",
|
|
109
79
|
},
|
|
@@ -114,8 +84,8 @@ const graph_data = {
|
|
|
114
84
|
inputs: {
|
|
115
85
|
map: ":map",
|
|
116
86
|
context: ":context",
|
|
117
|
-
combinedFileName: ":
|
|
118
|
-
|
|
87
|
+
combinedFileName: ":audioCombinedFilePath",
|
|
88
|
+
audioDirPath: ":audioDirPath",
|
|
119
89
|
},
|
|
120
90
|
isResult: true,
|
|
121
91
|
},
|
|
@@ -129,12 +99,12 @@ const graph_data = {
|
|
|
129
99
|
addBGM: {
|
|
130
100
|
agent: "addBGMAgent",
|
|
131
101
|
params: {
|
|
132
|
-
musicFile: process.env.PATH_BGM ??
|
|
102
|
+
musicFile: process.env.PATH_BGM ?? defaultBGMPath,
|
|
133
103
|
},
|
|
134
104
|
inputs: {
|
|
135
105
|
wait: ":combineFiles",
|
|
136
|
-
voiceFile: ":
|
|
137
|
-
outputFile: ":
|
|
106
|
+
voiceFile: ":audioCombinedFilePath",
|
|
107
|
+
outputFile: ":audioArtifactFilePath",
|
|
138
108
|
script: ":context.studio.script",
|
|
139
109
|
},
|
|
140
110
|
isResult: true,
|
|
@@ -154,33 +124,34 @@ const graph_data = {
|
|
|
154
124
|
const agentFilters = [
|
|
155
125
|
{
|
|
156
126
|
name: "fileCacheAgentFilter",
|
|
157
|
-
agent:
|
|
127
|
+
agent: fileCacheAgentFilter,
|
|
158
128
|
nodeIds: ["tts"],
|
|
159
129
|
},
|
|
160
130
|
];
|
|
161
|
-
const audio = async (context, concurrency) => {
|
|
131
|
+
export const audio = async (context, concurrency) => {
|
|
162
132
|
const { studio, fileDirs } = context;
|
|
163
|
-
const { outDirPath,
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
(
|
|
168
|
-
|
|
133
|
+
const { outDirPath, audioDirPath } = fileDirs;
|
|
134
|
+
const audioArtifactFilePath = getAudioArtifactFilePath(outDirPath, studio.filename);
|
|
135
|
+
const audioSegmentDirPath = getAudioSegmentDirPath(audioDirPath, studio.filename);
|
|
136
|
+
const audioCombinedFilePath = getAudioCombinedFilePath(audioDirPath, studio.filename);
|
|
137
|
+
const outputStudioFilePath = getOutputStudioFilePath(outDirPath, studio.filename);
|
|
138
|
+
mkdir(outDirPath);
|
|
139
|
+
mkdir(audioSegmentDirPath);
|
|
169
140
|
graph_data.concurrency = concurrency;
|
|
170
|
-
const graph = new
|
|
171
|
-
...
|
|
172
|
-
fileWriteAgent
|
|
173
|
-
ttsOpenaiAgent
|
|
174
|
-
ttsNijivoiceAgent
|
|
175
|
-
addBGMAgent
|
|
176
|
-
combineAudioFilesAgent
|
|
141
|
+
const graph = new GraphAI(graph_data, {
|
|
142
|
+
...vanillaAgents,
|
|
143
|
+
fileWriteAgent,
|
|
144
|
+
ttsOpenaiAgent,
|
|
145
|
+
ttsNijivoiceAgent,
|
|
146
|
+
addBGMAgent,
|
|
147
|
+
combineAudioFilesAgent,
|
|
177
148
|
}, { agentFilters });
|
|
178
149
|
graph.injectValue("context", context);
|
|
179
|
-
graph.injectValue("
|
|
180
|
-
graph.injectValue("
|
|
150
|
+
graph.injectValue("audioArtifactFilePath", audioArtifactFilePath);
|
|
151
|
+
graph.injectValue("audioCombinedFilePath", audioCombinedFilePath);
|
|
181
152
|
graph.injectValue("outputStudioFilePath", outputStudioFilePath);
|
|
182
|
-
graph.injectValue("
|
|
153
|
+
graph.injectValue("audioSegmentDirPath", audioSegmentDirPath);
|
|
154
|
+
graph.injectValue("audioDirPath", audioDirPath);
|
|
183
155
|
await graph.run();
|
|
184
|
-
|
|
156
|
+
writingMessage(audioCombinedFilePath);
|
|
185
157
|
};
|
|
186
|
-
exports.audio = audio;
|
package/lib/actions/images.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { MulmoStudioContext } from "../types";
|
|
1
|
+
import { MulmoStudioContext } from "../types/index.js";
|
|
2
2
|
export declare const images: (context: MulmoStudioContext) => Promise<void>;
|
package/lib/actions/images.js
CHANGED
|
@@ -1,81 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.images = void 0;
|
|
40
|
-
const dotenv_1 = __importDefault(require("dotenv"));
|
|
41
|
-
const graphai_1 = require("graphai");
|
|
42
|
-
const agents = __importStar(require("@graphai/vanilla"));
|
|
43
|
-
const vanilla_node_agents_1 = require("@graphai/vanilla_node_agents");
|
|
44
|
-
const file_1 = require("../utils/file");
|
|
45
|
-
const filters_1 = require("../utils/filters");
|
|
46
|
-
const markdown_1 = require("../utils/markdown");
|
|
47
|
-
const image_google_agent_1 = __importDefault(require("../agents/image_google_agent"));
|
|
48
|
-
const image_openai_agent_1 = __importDefault(require("../agents/image_openai_agent"));
|
|
49
|
-
const methods_1 = require("../methods");
|
|
50
|
-
dotenv_1.default.config();
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import { GraphAI, GraphAILogger } from "graphai";
|
|
3
|
+
import * as agents from "@graphai/vanilla";
|
|
4
|
+
import { fileWriteAgent } from "@graphai/vanilla_node_agents";
|
|
5
|
+
import { getOutputStudioFilePath, mkdir } from "../utils/file.js";
|
|
6
|
+
import { fileCacheAgentFilter } from "../utils/filters.js";
|
|
7
|
+
import imageGoogleAgent from "../agents/image_google_agent.js";
|
|
8
|
+
import imageOpenaiAgent from "../agents/image_openai_agent.js";
|
|
9
|
+
import { MulmoScriptMethods } from "../methods/index.js";
|
|
10
|
+
import { processChart, processMarkdown, processTextSlide, processImage, processMermaid } from "../utils/image_preprocess.js";
|
|
11
|
+
const { default: __, ...vanillaAgents } = agents;
|
|
12
|
+
dotenv.config();
|
|
51
13
|
// const openai = new OpenAI();
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const { context, beat, index, suffix, imageDirPath } = namedInputs;
|
|
55
|
-
const imageParams = { ...
|
|
14
|
+
import { GoogleAuth } from "google-auth-library";
|
|
15
|
+
const imagePreprocessAgent = async (namedInputs) => {
|
|
16
|
+
const { context, beat, index, suffix, imageDirPath, imageAgentInfo } = namedInputs;
|
|
17
|
+
const imageParams = { ...imageAgentInfo.imageParams, ...beat.imageParams };
|
|
56
18
|
const prompt = (beat.imagePrompt || beat.text) + "\n" + (imageParams.style || "");
|
|
57
19
|
const imagePath = `${imageDirPath}/${context.studio.filename}/${index}${suffix}.png`;
|
|
58
|
-
const aspectRatio =
|
|
20
|
+
const aspectRatio = MulmoScriptMethods.getAspectRatio(context.studio.script);
|
|
21
|
+
const textSlideStyle = MulmoScriptMethods.getTextSlideStyle(context.studio.script, beat);
|
|
59
22
|
if (beat.image) {
|
|
23
|
+
const canvasSize = MulmoScriptMethods.getCanvasSize(context.studio.script);
|
|
24
|
+
const processorParams = { beat, context, imagePath, textSlideStyle, canvasSize };
|
|
60
25
|
if (beat.image.type === "textSlide") {
|
|
61
|
-
|
|
62
|
-
const markdown = `# ${slide.title}` + slide.bullets.map((text) => `- ${text}`).join("\n");
|
|
63
|
-
await (0, markdown_1.convertMarkdownToImage)(markdown, methods_1.MulmoScriptMethods.getTextSlideStyle(context.studio.script, beat), imagePath);
|
|
26
|
+
await processTextSlide(processorParams);
|
|
64
27
|
}
|
|
65
28
|
else if (beat.image.type === "markdown") {
|
|
66
|
-
|
|
67
|
-
await (0, markdown_1.convertMarkdownToImage)(markdown, methods_1.MulmoScriptMethods.getTextSlideStyle(context.studio.script, beat), imagePath);
|
|
29
|
+
await processMarkdown(processorParams);
|
|
68
30
|
}
|
|
69
31
|
else if (beat.image.type === "image") {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
else if (beat.image.source.kind === "path") {
|
|
75
|
-
const path = methods_1.MulmoStudioContextMethods.resolveAssetPath(context, beat.image.source.path);
|
|
32
|
+
const path = processImage(processorParams);
|
|
33
|
+
if (path) {
|
|
34
|
+
// undefined prompt indicates that image generation is not needed
|
|
76
35
|
return { path, prompt: undefined, imageParams, aspectRatio };
|
|
77
36
|
}
|
|
78
37
|
}
|
|
38
|
+
else if (beat.image.type === "chart") {
|
|
39
|
+
await processChart(processorParams);
|
|
40
|
+
}
|
|
41
|
+
else if (beat.image.type === "mermaid") {
|
|
42
|
+
await processMermaid(processorParams);
|
|
43
|
+
}
|
|
79
44
|
}
|
|
80
45
|
return { path: imagePath, prompt, imageParams, aspectRatio };
|
|
81
46
|
};
|
|
@@ -85,11 +50,11 @@ const graph_data = {
|
|
|
85
50
|
nodes: {
|
|
86
51
|
context: {},
|
|
87
52
|
imageDirPath: {},
|
|
88
|
-
|
|
53
|
+
imageAgentInfo: {},
|
|
89
54
|
outputStudioFilePath: {},
|
|
90
55
|
map: {
|
|
91
56
|
agent: "mapAgent",
|
|
92
|
-
inputs: { rows: ":context.studio.beats", context: ":context",
|
|
57
|
+
inputs: { rows: ":context.studio.beats", context: ":context", imageAgentInfo: ":imageAgentInfo", imageDirPath: ":imageDirPath" },
|
|
93
58
|
isResult: true,
|
|
94
59
|
params: {
|
|
95
60
|
rowKey: "beat",
|
|
@@ -98,18 +63,19 @@ const graph_data = {
|
|
|
98
63
|
graph: {
|
|
99
64
|
nodes: {
|
|
100
65
|
preprocessor: {
|
|
101
|
-
agent:
|
|
66
|
+
agent: imagePreprocessAgent,
|
|
102
67
|
inputs: {
|
|
103
68
|
context: ":context",
|
|
104
69
|
beat: ":beat",
|
|
105
70
|
index: ":__mapIndex",
|
|
106
71
|
suffix: "p",
|
|
107
72
|
imageDirPath: ":imageDirPath",
|
|
73
|
+
imageAgentInfo: ":imageAgentInfo",
|
|
108
74
|
},
|
|
109
75
|
},
|
|
110
76
|
imageGenerator: {
|
|
111
77
|
if: ":preprocessor.prompt",
|
|
112
|
-
agent: ":
|
|
78
|
+
agent: ":imageAgentInfo.agent",
|
|
113
79
|
params: {
|
|
114
80
|
model: ":preprocessor.imageParams.model",
|
|
115
81
|
size: ":preprocessor.imageParams.size",
|
|
@@ -120,6 +86,7 @@ const graph_data = {
|
|
|
120
86
|
prompt: ":preprocessor.prompt",
|
|
121
87
|
file: ":preprocessor.path", // only for fileCacheAgentFilter
|
|
122
88
|
text: ":preprocessor.prompt", // only for fileCacheAgentFilter
|
|
89
|
+
force: ":context.force",
|
|
123
90
|
},
|
|
124
91
|
defaultValue: {},
|
|
125
92
|
},
|
|
@@ -164,30 +131,31 @@ const graph_data = {
|
|
|
164
131
|
},
|
|
165
132
|
};
|
|
166
133
|
const googleAuth = async () => {
|
|
167
|
-
const auth = new
|
|
134
|
+
const auth = new GoogleAuth({
|
|
168
135
|
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
|
|
169
136
|
});
|
|
170
137
|
const client = await auth.getClient();
|
|
171
138
|
const accessToken = await client.getAccessToken();
|
|
172
139
|
return accessToken.token;
|
|
173
140
|
};
|
|
174
|
-
const images = async (context) => {
|
|
141
|
+
export const images = async (context) => {
|
|
175
142
|
const { studio, fileDirs } = context;
|
|
176
143
|
const { outDirPath, imageDirPath } = fileDirs;
|
|
177
|
-
|
|
144
|
+
mkdir(`${imageDirPath}/${studio.filename}`);
|
|
178
145
|
const agentFilters = [
|
|
179
146
|
{
|
|
180
147
|
name: "fileCacheAgentFilter",
|
|
181
|
-
agent:
|
|
148
|
+
agent: fileCacheAgentFilter,
|
|
182
149
|
nodeIds: ["imageGenerator"],
|
|
183
150
|
},
|
|
184
151
|
];
|
|
185
152
|
const options = {
|
|
186
153
|
agentFilters,
|
|
187
154
|
};
|
|
155
|
+
const imageAgentInfo = MulmoScriptMethods.getImageAgentInfo(studio.script);
|
|
188
156
|
// We need to get google's auth token only if the google is the text2image provider.
|
|
189
|
-
if (
|
|
190
|
-
|
|
157
|
+
if (imageAgentInfo.provider === "google") {
|
|
158
|
+
GraphAILogger.log("google was specified as text2image engine");
|
|
191
159
|
const token = await googleAuth();
|
|
192
160
|
options.config = {
|
|
193
161
|
imageGoogleAgent: {
|
|
@@ -196,16 +164,16 @@ const images = async (context) => {
|
|
|
196
164
|
},
|
|
197
165
|
};
|
|
198
166
|
}
|
|
167
|
+
GraphAILogger.info(`text2image: provider=${imageAgentInfo.provider} model=${imageAgentInfo.imageParams.model}`);
|
|
199
168
|
const injections = {
|
|
200
169
|
context,
|
|
201
|
-
|
|
202
|
-
outputStudioFilePath:
|
|
170
|
+
imageAgentInfo,
|
|
171
|
+
outputStudioFilePath: getOutputStudioFilePath(outDirPath, studio.filename),
|
|
203
172
|
imageDirPath,
|
|
204
173
|
};
|
|
205
|
-
const graph = new
|
|
174
|
+
const graph = new GraphAI(graph_data, { ...vanillaAgents, imageGoogleAgent, imageOpenaiAgent, fileWriteAgent }, options);
|
|
206
175
|
Object.keys(injections).forEach((key) => {
|
|
207
176
|
graph.injectValue(key, injections[key]);
|
|
208
177
|
});
|
|
209
178
|
await graph.run();
|
|
210
179
|
};
|
|
211
|
-
exports.images = images;
|
package/lib/actions/movie.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { MulmoStudioContext } from "../types";
|
|
1
|
+
import { MulmoStudioContext } from "../types/index.js";
|
|
2
2
|
export declare const movie: (context: MulmoStudioContext) => Promise<void>;
|
package/lib/actions/movie.js
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
filterComplexParts.push(
|
|
1
|
+
import ffmpeg from "fluent-ffmpeg";
|
|
2
|
+
import { GraphAILogger } from "graphai";
|
|
3
|
+
import { MulmoScriptMethods } from "../methods/index.js";
|
|
4
|
+
import { getAudioArtifactFilePath, getOutputVideoFilePath, writingMessage } from "../utils/file.js";
|
|
5
|
+
const createVideo = (audioArtifactFilePath, outputVideoPath, studio) => {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const start = performance.now();
|
|
8
|
+
let command = ffmpeg();
|
|
9
|
+
if (studio.beats.some((beat) => !beat.imageFile)) {
|
|
10
|
+
GraphAILogger.info("beat.imageFile is not set. Please run `yarn run images ${file}` ");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
// Add each image input
|
|
14
|
+
studio.beats.forEach((beat) => {
|
|
15
|
+
command = command.input(beat.imageFile); // HACK
|
|
16
|
+
});
|
|
17
|
+
const imageCount = studio.beats.length;
|
|
18
|
+
const canvasInfo = MulmoScriptMethods.getCanvasSize(studio.script);
|
|
19
|
+
const filterComplexParts = [];
|
|
20
|
+
studio.beats.forEach((beat, index) => {
|
|
21
|
+
// Resize background image to match canvas dimensions
|
|
22
|
+
const duration = beat.duration + (index === 0 ? MulmoScriptMethods.getPadding(studio.script) / 1000 : 0);
|
|
23
|
+
const parts = `[${index}:v]loop=loop=-1:size=1:start=0,` +
|
|
24
|
+
`trim=duration=${duration},` +
|
|
25
|
+
`fps=30,` +
|
|
26
|
+
`setpts=PTS-STARTPTS,` +
|
|
27
|
+
`scale=${canvasInfo.width}:${canvasInfo.height},` +
|
|
28
|
+
`setsar=1,format=yuv420p` +
|
|
29
|
+
`[v${index}]`;
|
|
30
|
+
// console.log(parts);
|
|
31
|
+
filterComplexParts.push(parts);
|
|
32
|
+
});
|
|
33
|
+
// Concatenate the trimmed images
|
|
34
|
+
const concatInput = studio.beats.map((_, index) => `[v${index}]`).join("");
|
|
35
|
+
filterComplexParts.push(`${concatInput}concat=n=${imageCount}:v=1:a=0[v]`);
|
|
36
|
+
// Apply the filter complex for concatenation and map audio input
|
|
37
|
+
command
|
|
38
|
+
.complexFilter(filterComplexParts)
|
|
39
|
+
.input(audioArtifactFilePath) // Add audio input
|
|
40
|
+
.outputOptions([
|
|
41
|
+
"-preset veryfast", // Faster encoding
|
|
42
|
+
"-map [v]", // Map the video stream
|
|
43
|
+
`-map ${imageCount /* + captionCount*/}:a`, // Map the audio stream (audio is the next input after all images)
|
|
44
|
+
"-c:v h264_videotoolbox", // Set video codec
|
|
45
|
+
"-threads 8",
|
|
46
|
+
"-filter_threads 8",
|
|
47
|
+
"-b:v 5M", // bitrate (only for videotoolbox)
|
|
48
|
+
"-bufsize",
|
|
49
|
+
"10M", // Add buffer size for better quality
|
|
50
|
+
"-maxrate",
|
|
51
|
+
"7M", // Maximum bitrate
|
|
52
|
+
"-r 30", // Set frame rate
|
|
53
|
+
"-pix_fmt yuv420p", // Set pixel format for better compatibility
|
|
54
|
+
])
|
|
55
|
+
.on("start", (__cmdLine) => {
|
|
56
|
+
GraphAILogger.log("Started FFmpeg ..."); // with command:', cmdLine);
|
|
57
|
+
})
|
|
58
|
+
.on("error", (err, stdout, stderr) => {
|
|
59
|
+
GraphAILogger.error("Error occurred:", err);
|
|
60
|
+
GraphAILogger.error("FFmpeg stdout:", stdout);
|
|
61
|
+
GraphAILogger.error("FFmpeg stderr:", stderr);
|
|
62
|
+
GraphAILogger.info("Video creation failed. An unexpected error occurred.");
|
|
63
|
+
reject();
|
|
64
|
+
})
|
|
65
|
+
.on("end", () => {
|
|
66
|
+
const end = performance.now();
|
|
67
|
+
GraphAILogger.info(`Video created successfully! ${Math.round(end - start) / 1000} sec`);
|
|
68
|
+
resolve(0);
|
|
69
|
+
})
|
|
70
|
+
.output(outputVideoPath)
|
|
71
|
+
.run();
|
|
36
72
|
});
|
|
37
|
-
// Concatenate the trimmed images
|
|
38
|
-
const concatInput = studio.beats.map((_, index) => `[v${index}]`).join("");
|
|
39
|
-
filterComplexParts.push(`${concatInput}concat=n=${imageCount}:v=1:a=0[v]`);
|
|
40
|
-
// Apply the filter complex for concatenation and map audio input
|
|
41
|
-
command
|
|
42
|
-
.complexFilter(filterComplexParts)
|
|
43
|
-
.input(audioPath) // Add audio input
|
|
44
|
-
.outputOptions([
|
|
45
|
-
"-preset veryfast", // Faster encoding
|
|
46
|
-
"-map [v]", // Map the video stream
|
|
47
|
-
`-map ${imageCount /* + captionCount*/}:a`, // Map the audio stream (audio is the next input after all images)
|
|
48
|
-
"-c:v h264_videotoolbox", // Set video codec
|
|
49
|
-
"-threads 8",
|
|
50
|
-
"-filter_threads 8",
|
|
51
|
-
"-b:v 5M", // bitrate (only for videotoolbox)
|
|
52
|
-
"-bufsize",
|
|
53
|
-
"10M", // Add buffer size for better quality
|
|
54
|
-
"-maxrate",
|
|
55
|
-
"7M", // Maximum bitrate
|
|
56
|
-
"-r 30", // Set frame rate
|
|
57
|
-
"-pix_fmt yuv420p", // Set pixel format for better compatibility
|
|
58
|
-
])
|
|
59
|
-
.on("start", (__cmdLine) => {
|
|
60
|
-
console.log("Started FFmpeg ..."); // with command:', cmdLine);
|
|
61
|
-
})
|
|
62
|
-
.on("error", (err, stdout, stderr) => {
|
|
63
|
-
console.error("Error occurred:", err);
|
|
64
|
-
console.error("FFmpeg stdout:", stdout);
|
|
65
|
-
console.error("FFmpeg stderr:", stderr);
|
|
66
|
-
})
|
|
67
|
-
.on("end", () => {
|
|
68
|
-
const end = performance.now();
|
|
69
|
-
console.log(`Video created successfully! ${Math.round(end - start) / 1000} sec`);
|
|
70
|
-
})
|
|
71
|
-
.output(outputVideoPath)
|
|
72
|
-
.run();
|
|
73
73
|
};
|
|
74
|
-
const movie = async (context) => {
|
|
74
|
+
export const movie = async (context) => {
|
|
75
75
|
const { studio, fileDirs } = context;
|
|
76
76
|
const { outDirPath } = fileDirs;
|
|
77
|
-
const
|
|
78
|
-
const outputVideoPath =
|
|
79
|
-
createVideo(
|
|
77
|
+
const audioArtifactFilePath = getAudioArtifactFilePath(outDirPath, studio.filename);
|
|
78
|
+
const outputVideoPath = getOutputVideoFilePath(outDirPath, studio.filename);
|
|
79
|
+
await createVideo(audioArtifactFilePath, outputVideoPath, studio);
|
|
80
|
+
writingMessage(outputVideoPath);
|
|
80
81
|
};
|
|
81
|
-
exports.movie = movie;
|