mulmocast 1.2.42 → 1.2.44
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/lib/actions/audio.d.ts +1 -0
- package/lib/actions/audio.js +16 -11
- package/lib/actions/image_references.js +3 -22
- package/lib/actions/images.js +3 -7
- package/lib/actions/movie.js +3 -2
- package/lib/actions/translate.js +2 -2
- package/lib/agents/add_bgm_agent.js +3 -3
- package/lib/agents/combine_audio_files_agent.js +5 -1
- package/lib/methods/mulmo_beat.js +1 -1
- package/lib/methods/mulmo_media_source.d.ts +6 -2
- package/lib/methods/mulmo_media_source.js +81 -6
- package/lib/types/schema.d.ts +29 -823
- package/lib/types/schema.js +6 -1
- package/lib/types/type.d.ts +3 -1
- package/lib/utils/context.d.ts +0 -48
- package/lib/utils/context.js +4 -5
- package/lib/utils/error_cause.d.ts +69 -0
- package/lib/utils/error_cause.js +97 -0
- package/lib/utils/ffmpeg_utils.js +2 -1
- package/lib/utils/file.js +1 -1
- package/lib/utils/filters.js +2 -2
- package/lib/utils/image_plugins/source.d.ts +2 -4
- package/lib/utils/image_plugins/source.js +5 -34
- package/lib/utils/utils.d.ts +0 -1
- package/lib/utils/utils.js +0 -14
- package/package.json +9 -9
package/lib/actions/audio.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GraphData } from "graphai";
|
|
2
2
|
import { MulmoStudioContext, MulmoBeat, PublicAPIArgs } from "../types/index.js";
|
|
3
3
|
export declare const getBeatAudioPath: (text: string, context: MulmoStudioContext, beat: MulmoBeat, lang?: string) => string | undefined;
|
|
4
|
+
export declare const getBeatAudioPathOrUrl: (text: string, context: MulmoStudioContext, beat: MulmoBeat, lang?: string) => string | undefined;
|
|
4
5
|
export declare const listLocalizedAudioPaths: (context: MulmoStudioContext) => (string | undefined)[];
|
|
5
6
|
export declare const audio_graph_data: GraphData;
|
|
6
7
|
export declare const generateBeatAudio: (index: number, context: MulmoStudioContext, args?: PublicAPIArgs & {
|
package/lib/actions/audio.js
CHANGED
|
@@ -9,38 +9,43 @@ import { getAudioArtifactFilePath, getAudioFilePath, getOutputStudioFilePath, re
|
|
|
9
9
|
import { localizedText, settings2GraphAIConfig } from "../utils/utils.js";
|
|
10
10
|
import { text2hash } from "../utils/utils_node.js";
|
|
11
11
|
import { provider2TTSAgent } from "../utils/provider2agent.js";
|
|
12
|
+
import { invalidAudioSourceError } from "../utils/error_cause.js";
|
|
12
13
|
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
13
14
|
import { MulmoMediaSourceMethods } from "../methods/mulmo_media_source.js";
|
|
14
15
|
dotenv.config({ quiet: true });
|
|
15
16
|
const vanillaAgents = agents.default ?? agents;
|
|
16
|
-
const
|
|
17
|
+
const getAudioPathOrUrl = (context, beat, maybeAudioFile) => {
|
|
17
18
|
if (beat.audio?.type === "audio") {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
return
|
|
19
|
+
const pathOrUrl = MulmoMediaSourceMethods.resolve(beat.audio.source, context);
|
|
20
|
+
if (pathOrUrl) {
|
|
21
|
+
return pathOrUrl;
|
|
21
22
|
}
|
|
22
|
-
throw new Error("Invalid audio source");
|
|
23
|
+
throw new Error("Invalid audio source", { cause: invalidAudioSourceError(context.studio.script.beats.indexOf(beat)) });
|
|
23
24
|
}
|
|
24
25
|
if (beat.text === undefined || beat.text === "" || context.studio.script.audioParams.suppressSpeech) {
|
|
25
26
|
return undefined; // It indicates that the audio is not needed.
|
|
26
27
|
}
|
|
27
|
-
return
|
|
28
|
+
return maybeAudioFile;
|
|
28
29
|
};
|
|
30
|
+
// for back forward compatible
|
|
29
31
|
export const getBeatAudioPath = (text, context, beat, lang) => {
|
|
32
|
+
return getBeatAudioPathOrUrl(text, context, beat, lang);
|
|
33
|
+
};
|
|
34
|
+
export const getBeatAudioPathOrUrl = (text, context, beat, lang) => {
|
|
30
35
|
const audioDirPath = MulmoStudioContextMethods.getAudioDirPath(context);
|
|
31
36
|
const { voiceId, provider, speechOptions, model } = MulmoStudioContextMethods.getAudioParam(context, beat, lang);
|
|
32
37
|
const hash_string = [text, voiceId, speechOptions?.instruction ?? "", speechOptions?.speed ?? 1.0, provider, model ?? ""].join(":");
|
|
33
|
-
GraphAILogger.log(`
|
|
38
|
+
GraphAILogger.log(`getBeatAudioPathOrUrl [${hash_string}]`);
|
|
34
39
|
const audioFileName = `${context.studio.filename}_${text2hash(hash_string)}`;
|
|
35
|
-
const
|
|
36
|
-
return
|
|
40
|
+
const maybeAudioFile = getAudioFilePath(audioDirPath, context.studio.filename, audioFileName, lang);
|
|
41
|
+
return getAudioPathOrUrl(context, beat, maybeAudioFile);
|
|
37
42
|
};
|
|
38
43
|
export const listLocalizedAudioPaths = (context) => {
|
|
39
44
|
const lang = context.lang ?? context.studio.script.lang;
|
|
40
45
|
return context.studio.script.beats.map((beat, index) => {
|
|
41
46
|
const multiLingual = context.multiLingual[index];
|
|
42
47
|
const text = localizedText(beat, multiLingual, lang);
|
|
43
|
-
return
|
|
48
|
+
return getBeatAudioPathOrUrl(text, context, beat, lang);
|
|
44
49
|
});
|
|
45
50
|
};
|
|
46
51
|
const preprocessorAgent = (namedInputs) => {
|
|
@@ -48,7 +53,7 @@ const preprocessorAgent = (namedInputs) => {
|
|
|
48
53
|
// const { lang } = context;
|
|
49
54
|
const text = localizedText(beat, multiLingual, lang);
|
|
50
55
|
const { voiceId, provider, speechOptions, model } = MulmoStudioContextMethods.getAudioParam(context, beat, lang);
|
|
51
|
-
const audioPath =
|
|
56
|
+
const audioPath = getBeatAudioPathOrUrl(text, context, beat, lang);
|
|
52
57
|
studioBeat.audioFile = audioPath; // TODO: Passing by reference is difficult to maintain, so pass it using graphai inputs
|
|
53
58
|
const needsTTS = !beat.audio && audioPath !== undefined;
|
|
54
59
|
return {
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
1
|
import { GraphAI, GraphAILogger } from "graphai";
|
|
3
|
-
import { getReferenceImagePath
|
|
4
|
-
import { getExtention } from "../utils/utils.js";
|
|
2
|
+
import { getReferenceImagePath } from "../utils/file.js";
|
|
5
3
|
import { graphOption } from "./images.js";
|
|
6
|
-
import { MulmoPresentationStyleMethods } from "../methods/index.js";
|
|
4
|
+
import { MulmoPresentationStyleMethods, MulmoMediaSourceMethods } from "../methods/index.js";
|
|
7
5
|
import { imageOpenaiAgent, mediaMockAgent, imageGenAIAgent, imageReplicateAgent } from "../agents/index.js";
|
|
8
6
|
// public api
|
|
9
7
|
// Application may call this function directly to generate reference image.
|
|
@@ -43,18 +41,6 @@ export const generateReferenceImage = async (inputs) => {
|
|
|
43
41
|
await graph.run();
|
|
44
42
|
return imagePath;
|
|
45
43
|
};
|
|
46
|
-
const downLoadImage = async (context, key, url) => {
|
|
47
|
-
const response = await fetch(url);
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
throw new Error(`Failed to download image: ${url}`);
|
|
50
|
-
}
|
|
51
|
-
const buffer = Buffer.from(await response.arrayBuffer());
|
|
52
|
-
// Detect file extension from Content-Type header or URL
|
|
53
|
-
const extension = getExtention(response.headers.get("content-type"), url);
|
|
54
|
-
const imagePath = getReferenceImagePath(context, key, extension);
|
|
55
|
-
await fs.promises.writeFile(imagePath, buffer);
|
|
56
|
-
return imagePath;
|
|
57
|
-
};
|
|
58
44
|
export const getImageRefs = async (context) => {
|
|
59
45
|
const images = context.presentationStyle.imageParams?.images;
|
|
60
46
|
if (!images) {
|
|
@@ -69,12 +55,7 @@ export const getImageRefs = async (context) => {
|
|
|
69
55
|
imageRefs[key] = await generateReferenceImage({ context, key, index, image, force: false });
|
|
70
56
|
}
|
|
71
57
|
else if (image.type === "image") {
|
|
72
|
-
|
|
73
|
-
imageRefs[key] = resolveAssetPath(context, image.source.path);
|
|
74
|
-
}
|
|
75
|
-
else if (image.source.kind === "url") {
|
|
76
|
-
imageRefs[key] = await downLoadImage(context, key, image.source.url);
|
|
77
|
-
}
|
|
58
|
+
imageRefs[key] = await MulmoMediaSourceMethods.imageReference(image.source, context, key);
|
|
78
59
|
}
|
|
79
60
|
}));
|
|
80
61
|
return imageRefs;
|
package/lib/actions/images.js
CHANGED
|
@@ -10,6 +10,7 @@ import { MulmoPresentationStyleMethods, MulmoStudioContextMethods } from "../met
|
|
|
10
10
|
import { getOutputStudioFilePath, mkdir } from "../utils/file.js";
|
|
11
11
|
import { fileCacheAgentFilter } from "../utils/filters.js";
|
|
12
12
|
import { settings2GraphAIConfig } from "../utils/utils.js";
|
|
13
|
+
import { audioCheckerError } from "../utils/error_cause.js";
|
|
13
14
|
import { extractImageFromMovie, ffmpegGetMediaDuration, trimMusic } from "../utils/ffmpeg_utils.js";
|
|
14
15
|
import { getImageRefs } from "./image_references.js";
|
|
15
16
|
import { imagePreprocessAgent, imagePluginAgent, htmlImageGeneratorAgent } from "./image_agents.js";
|
|
@@ -201,13 +202,8 @@ export const beat_graph_data = {
|
|
|
201
202
|
}
|
|
202
203
|
catch (error) {
|
|
203
204
|
GraphAILogger.error(error);
|
|
204
|
-
throw new Error(
|
|
205
|
-
cause:
|
|
206
|
-
type: "FileNotExist",
|
|
207
|
-
action: "images",
|
|
208
|
-
agentName: "audioChecker",
|
|
209
|
-
beat_index: namedInputs.index,
|
|
210
|
-
},
|
|
205
|
+
throw new Error(`audioChecker: ffmpegGetMediaDuration error: index=${namedInputs.index} file=${sourceFile}`, {
|
|
206
|
+
cause: audioCheckerError(namedInputs.index, sourceFile),
|
|
211
207
|
});
|
|
212
208
|
}
|
|
213
209
|
},
|
package/lib/actions/movie.js
CHANGED
|
@@ -2,6 +2,7 @@ import { GraphAILogger, assert } from "graphai";
|
|
|
2
2
|
import { mulmoTransitionSchema, mulmoFillOptionSchema } from "../types/index.js";
|
|
3
3
|
import { MulmoPresentationStyleMethods } from "../methods/index.js";
|
|
4
4
|
import { getAudioArtifactFilePath, getOutputVideoFilePath, writingMessage, isFile } from "../utils/file.js";
|
|
5
|
+
import { createVideoFileError, createVideoSourceError } from "../utils/error_cause.js";
|
|
5
6
|
import { FfmpegContextAddInput, FfmpegContextInit, FfmpegContextPushFormattedAudio, FfmpegContextGenerateOutput, } from "../utils/ffmpeg_utils.js";
|
|
6
7
|
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
7
8
|
// const isMac = process.platform === "darwin";
|
|
@@ -163,8 +164,8 @@ const createVideo = async (audioArtifactFilePath, outputVideoPath, context) => {
|
|
|
163
164
|
return timestamp; // Skip voice-over beats.
|
|
164
165
|
}
|
|
165
166
|
const sourceFile = studioBeat.lipSyncFile ?? studioBeat.soundEffectFile ?? studioBeat.movieFile ?? studioBeat.htmlImageFile ?? studioBeat.imageFile;
|
|
166
|
-
assert(!!sourceFile, `studioBeat.imageFile or studioBeat.movieFile is not set: index=${index}
|
|
167
|
-
assert(isFile(sourceFile), `studioBeat.imageFile or studioBeat.movieFile is not exist or not file: index=${index}
|
|
167
|
+
assert(!!sourceFile, `studioBeat.imageFile or studioBeat.movieFile is not set: index=${index}`, false, createVideoSourceError(index));
|
|
168
|
+
assert(isFile(sourceFile), `studioBeat.imageFile or studioBeat.movieFile is not exist or not file: index=${index} file=${sourceFile}`, false, createVideoFileError(index, sourceFile));
|
|
168
169
|
assert(!!studioBeat.duration, `studioBeat.duration is not set: index=${index}`);
|
|
169
170
|
const extraPadding = (() => {
|
|
170
171
|
// We need to consider only intro and outro padding because the other paddings were already added to the beat.duration
|
package/lib/actions/translate.js
CHANGED
|
@@ -241,7 +241,7 @@ export const translateBeat = async (index, context, targetLangs, args) => {
|
|
|
241
241
|
try {
|
|
242
242
|
const { outputMultilingualFilePath } = getOutputMultilingualFilePathAndMkdir(context);
|
|
243
243
|
const config = settings2GraphAIConfig(settings, process.env);
|
|
244
|
-
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty");
|
|
244
|
+
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty"); // TODO: cause
|
|
245
245
|
const graph = new GraphAI(beatGraph, { ...vanillaAgents, fileWriteAgent, openAIAgent }, { agentFilters, config });
|
|
246
246
|
graph.injectValue("context", context);
|
|
247
247
|
graph.injectValue("targetLangs", targetLangs);
|
|
@@ -276,7 +276,7 @@ export const translate = async (context, args) => {
|
|
|
276
276
|
? args?.targetLangs
|
|
277
277
|
: [...new Set([context.lang, context.studio.script.captionParams?.lang].filter((x) => !isNull(x)))];
|
|
278
278
|
const config = settings2GraphAIConfig(settings, process.env);
|
|
279
|
-
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty");
|
|
279
|
+
assert(!!config?.openAIAgent?.apiKey, "The OPENAI_API_KEY environment variable is missing or empty"); // TODO: cause
|
|
280
280
|
const graph = new GraphAI(translate_graph_data, { ...vanillaAgents, fileWriteAgent, openAIAgent }, { agentFilters, config });
|
|
281
281
|
graph.injectValue("context", context);
|
|
282
282
|
graph.injectValue("targetLangs", targetLangs);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
1
|
import { GraphAILogger } from "graphai";
|
|
3
2
|
import { FfmpegContextAddInput, FfmpegContextInit, FfmpegContextGenerateOutput, ffmpegGetMediaDuration } from "../utils/ffmpeg_utils.js";
|
|
4
3
|
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
4
|
+
import { isFile } from "../utils/file.js";
|
|
5
5
|
const addBGMAgent = async ({ namedInputs, params, }) => {
|
|
6
6
|
const { voiceFile, outputFile, context } = namedInputs;
|
|
7
7
|
const { musicFile } = params;
|
|
8
|
-
if (!
|
|
8
|
+
if (!isFile(voiceFile)) {
|
|
9
9
|
throw new Error(`AddBGMAgent voiceFile not exist: ${voiceFile}`);
|
|
10
10
|
}
|
|
11
|
-
if (!musicFile.match(/^http/) && !
|
|
11
|
+
if (!musicFile.match(/^http/) && !isFile(musicFile)) {
|
|
12
12
|
throw new Error(`AddBGMAgent musicFile not exist: ${musicFile}`);
|
|
13
13
|
}
|
|
14
14
|
const { duration: speechDuration } = await ffmpegGetMediaDuration(voiceFile);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { assert, GraphAILogger } from "graphai";
|
|
2
|
-
import { silent60secPath } from "../utils/file.js";
|
|
2
|
+
import { silent60secPath, isFile } from "../utils/file.js";
|
|
3
3
|
import { FfmpegContextInit, FfmpegContextGenerateOutput, FfmpegContextInputFormattedAudio, ffmpegGetMediaDuration, } from "../utils/ffmpeg_utils.js";
|
|
4
4
|
import { MulmoMediaSourceMethods } from "../methods/mulmo_media_source.js";
|
|
5
5
|
import { userAssert } from "../utils/utils.js";
|
|
6
|
+
import { getAudioInputIdsError } from "../utils/error_cause.js";
|
|
6
7
|
const getMovieDuration = async (context, beat) => {
|
|
7
8
|
if (beat.image?.type === "movie") {
|
|
8
9
|
const pathOrUrl = MulmoMediaSourceMethods.resolve(beat.image.source, context);
|
|
@@ -71,6 +72,9 @@ const getInputIds = (context, mediaDurations, ffmpegContext, silentIds) => {
|
|
|
71
72
|
const { silenceDuration } = mediaDurations[index];
|
|
72
73
|
const paddingId = `[padding_${index}]`;
|
|
73
74
|
if (studioBeat.audioFile) {
|
|
75
|
+
if (!/^https?:\/\//.test(studioBeat.audioFile)) {
|
|
76
|
+
assert(isFile(studioBeat.audioFile), `studioBeat.audioFile is not exist or not file: index=${index} file=${studioBeat.audioFile}`, false, getAudioInputIdsError(index, studioBeat.audioFile));
|
|
77
|
+
}
|
|
74
78
|
const audioId = FfmpegContextInputFormattedAudio(ffmpegContext, studioBeat.audioFile);
|
|
75
79
|
inputIds.push(audioId);
|
|
76
80
|
}
|
|
@@ -9,7 +9,7 @@ export const MulmoBeatMethods = {
|
|
|
9
9
|
getPlugin(beat) {
|
|
10
10
|
const plugin = findImagePlugin(beat?.image?.type);
|
|
11
11
|
if (!plugin) {
|
|
12
|
-
throw new Error(`invalid beat image type: ${beat.image}`);
|
|
12
|
+
throw new Error(`invalid beat image type: ${beat.image}`); // TODO: cause
|
|
13
13
|
}
|
|
14
14
|
return plugin;
|
|
15
15
|
},
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { MulmoMediaSource, MulmoStudioContext } from "../types/index.js";
|
|
1
|
+
import type { MulmoMediaSource, MulmoMediaMermaidSource, MulmoStudioContext, ImageType } from "../types/index.js";
|
|
2
|
+
export declare const getExtention: (contentType: string | null, url: string) => string;
|
|
2
3
|
export declare const MulmoMediaSourceMethods: {
|
|
3
|
-
getText(mediaSource:
|
|
4
|
+
getText(mediaSource: MulmoMediaMermaidSource, context: MulmoStudioContext): Promise<string | null>;
|
|
4
5
|
resolve(mediaSource: MulmoMediaSource | undefined, context: MulmoStudioContext): string | null;
|
|
6
|
+
imageReference(mediaSource: MulmoMediaSource, context: MulmoStudioContext, key: string): Promise<string>;
|
|
7
|
+
imagePluginSource(mediaSource: MulmoMediaSource, context: MulmoStudioContext, expectImagePath: string, imageType: ImageType): Promise<string>;
|
|
8
|
+
imagePluginSourcePath(mediaSource: MulmoMediaSource, context: MulmoStudioContext, expectImagePath: string, imageType: ImageType): string | undefined;
|
|
5
9
|
};
|
|
@@ -1,16 +1,52 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
-
import {
|
|
2
|
+
import { GraphAILogger, assert } from "graphai";
|
|
3
|
+
import { getFullPath, getReferenceImagePath, resolveAssetPath } from "../utils/file.js";
|
|
4
|
+
import { downLoadReferenceImageError, getTextError, imageReferenceUnknownMediaError, downloadImagePluginError, imagePluginUnknownMediaError, } from "../utils/error_cause.js";
|
|
5
|
+
// for image reference
|
|
6
|
+
export const getExtention = (contentType, url) => {
|
|
7
|
+
if (contentType?.includes("jpeg") || contentType?.includes("jpg")) {
|
|
8
|
+
return "jpg";
|
|
9
|
+
}
|
|
10
|
+
else if (contentType?.includes("png")) {
|
|
11
|
+
return "png";
|
|
12
|
+
}
|
|
13
|
+
// Fall back to URL extension
|
|
14
|
+
const urlExtension = url.split(".").pop()?.toLowerCase();
|
|
15
|
+
if (urlExtension && ["jpg", "jpeg", "png"].includes(urlExtension)) {
|
|
16
|
+
return urlExtension === "jpeg" ? "jpg" : urlExtension;
|
|
17
|
+
}
|
|
18
|
+
return "png"; // default
|
|
19
|
+
};
|
|
20
|
+
const downLoadReferenceImage = async (context, key, url) => {
|
|
21
|
+
const response = await fetch(url);
|
|
22
|
+
assert(response.ok, `Failed to download reference image: ${url}`, false, downLoadReferenceImageError(key, url));
|
|
23
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
24
|
+
// Detect file extension from Content-Type header or URL
|
|
25
|
+
const extension = getExtention(response.headers.get("content-type"), url);
|
|
26
|
+
const imagePath = getReferenceImagePath(context, key, extension);
|
|
27
|
+
await fs.promises.writeFile(imagePath, buffer);
|
|
28
|
+
return imagePath;
|
|
29
|
+
};
|
|
30
|
+
// for image
|
|
31
|
+
function pluginSourceFixExtention(path, imageType) {
|
|
32
|
+
if (imageType === "movie") {
|
|
33
|
+
if (!path.endsWith(".png")) {
|
|
34
|
+
GraphAILogger.warn(`Expected .png extension for movie type, got: ${path}`);
|
|
35
|
+
}
|
|
36
|
+
return path.replace(/\.png$/, ".mov");
|
|
37
|
+
}
|
|
38
|
+
return path;
|
|
39
|
+
}
|
|
40
|
+
// end of util
|
|
3
41
|
export const MulmoMediaSourceMethods = {
|
|
4
42
|
async getText(mediaSource, context) {
|
|
5
43
|
if (mediaSource.kind === "text") {
|
|
6
44
|
return mediaSource.text;
|
|
7
45
|
}
|
|
8
46
|
if (mediaSource.kind === "url") {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
return await res.text();
|
|
47
|
+
const response = await fetch(mediaSource.url);
|
|
48
|
+
assert(response.ok, `Failed to download mermaid code text: ${mediaSource.url}`, false, getTextError(mediaSource.url)); // TODO: index
|
|
49
|
+
return await response.text();
|
|
14
50
|
}
|
|
15
51
|
if (mediaSource.kind === "path") {
|
|
16
52
|
const path = getFullPath(context.fileDirs.mulmoFileDirPath, mediaSource.path);
|
|
@@ -29,4 +65,43 @@ export const MulmoMediaSourceMethods = {
|
|
|
29
65
|
}
|
|
30
66
|
return null;
|
|
31
67
|
},
|
|
68
|
+
// if url then download image and save it to file. both case return local image path. For image reference
|
|
69
|
+
async imageReference(mediaSource, context, key) {
|
|
70
|
+
if (mediaSource.kind === "path") {
|
|
71
|
+
return resolveAssetPath(context, mediaSource.path);
|
|
72
|
+
}
|
|
73
|
+
else if (mediaSource.kind === "url") {
|
|
74
|
+
return await downLoadReferenceImage(context, key, mediaSource.url);
|
|
75
|
+
}
|
|
76
|
+
// TODO base64
|
|
77
|
+
throw new Error(`imageReference media unknown error`, { cause: imageReferenceUnknownMediaError(key) });
|
|
78
|
+
},
|
|
79
|
+
async imagePluginSource(mediaSource, context, expectImagePath, imageType) {
|
|
80
|
+
if (mediaSource.kind === "url") {
|
|
81
|
+
const response = await fetch(mediaSource.url);
|
|
82
|
+
assert(response.ok, `Failed to download image plugin: ${imageType} ${mediaSource.url}`, false, downloadImagePluginError(mediaSource.url, imageType)); // TODO: key, id, index
|
|
83
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
84
|
+
// Detect file extension from Content-Type header or URL
|
|
85
|
+
const imagePath = pluginSourceFixExtention(expectImagePath, imageType);
|
|
86
|
+
await fs.promises.writeFile(imagePath, buffer);
|
|
87
|
+
return imagePath;
|
|
88
|
+
}
|
|
89
|
+
const path = MulmoMediaSourceMethods.resolve(mediaSource, context);
|
|
90
|
+
if (path) {
|
|
91
|
+
return path;
|
|
92
|
+
}
|
|
93
|
+
// base64??
|
|
94
|
+
GraphAILogger.error(`Image Plugin unknown ${imageType} source type:`, mediaSource);
|
|
95
|
+
throw new Error(`ERROR: unknown ${imageType} source type`, { cause: imagePluginUnknownMediaError(imageType) }); // TODO index
|
|
96
|
+
},
|
|
97
|
+
imagePluginSourcePath(mediaSource, context, expectImagePath, imageType) {
|
|
98
|
+
if (mediaSource?.kind === "url") {
|
|
99
|
+
return pluginSourceFixExtention(expectImagePath, imageType);
|
|
100
|
+
}
|
|
101
|
+
const path = MulmoMediaSourceMethods.resolve(mediaSource, context);
|
|
102
|
+
if (path) {
|
|
103
|
+
return path;
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
},
|
|
32
107
|
};
|