增加小图
This commit is contained in:
parent
a9acf38d3b
commit
9b93cbb7b4
427
data/vendor/yunwu.ts
vendored
Normal file
427
data/vendor/yunwu.ts
vendored
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
// ==================== 类型定义 ====================
|
||||||
|
// 文本模型
|
||||||
|
interface TextModel {
|
||||||
|
name: string;
|
||||||
|
modelName: string;
|
||||||
|
type: "text";
|
||||||
|
think: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图像模型
|
||||||
|
interface ImageModel {
|
||||||
|
name: string;
|
||||||
|
modelName: string;
|
||||||
|
type: "image";
|
||||||
|
mode: ("text" | "singleImage" | "multiReference")[];
|
||||||
|
associationSkills?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频模型
|
||||||
|
interface VideoModel {
|
||||||
|
name: string;
|
||||||
|
modelName: string;
|
||||||
|
type: "video";
|
||||||
|
mode: (
|
||||||
|
| "singleImage"
|
||||||
|
| "startEndRequired"
|
||||||
|
| "endFrameOptional"
|
||||||
|
| "startFrameOptional"
|
||||||
|
| "text"
|
||||||
|
| ("videoReference" | "imageReference" | "audioReference" | "textReference")[]
|
||||||
|
)[];
|
||||||
|
associationSkills?: string;
|
||||||
|
audio: "optional" | false | true;
|
||||||
|
durationResolutionMap: { duration: number[]; resolution: string[] }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TTSModel {
|
||||||
|
name: string;
|
||||||
|
modelName: string;
|
||||||
|
type: "tts";
|
||||||
|
voices: { title: string; voice: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VendorConfig {
|
||||||
|
id: string;
|
||||||
|
author: string;
|
||||||
|
description?: string;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
inputs: { key: string; label: string; type: "text" | "password" | "url"; required: boolean; placeholder?: string }[];
|
||||||
|
inputValues: Record<string, string>;
|
||||||
|
models: (TextModel | ImageModel | VideoModel)[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 全局工具函数声明 ====================
|
||||||
|
declare const zipImage: (completeBase64: string, size: number) => Promise<string>;
|
||||||
|
declare const zipImageResolution: (completeBase64: string, width: number, height: number) => Promise<string>;
|
||||||
|
declare const mergeImages: (completeBase64: string[], maxSize?: string) => Promise<string>;
|
||||||
|
declare const urlToBase64: (url: string) => Promise<string>;
|
||||||
|
declare const pollTask: (
|
||||||
|
fn: () => Promise<{ completed: boolean; data?: string; error?: string }>,
|
||||||
|
interval?: number,
|
||||||
|
timeout?: number,
|
||||||
|
) => Promise<{ completed: boolean; data?: string; error?: string }>;
|
||||||
|
declare const axios: any;
|
||||||
|
declare const createOpenAI: any;
|
||||||
|
declare const createDeepSeek: any;
|
||||||
|
declare const createZhipu: any;
|
||||||
|
declare const createQwen: any;
|
||||||
|
declare const createAnthropic: any;
|
||||||
|
declare const createOpenAICompatible: any;
|
||||||
|
declare const createXai: any;
|
||||||
|
declare const createMinimax: any;
|
||||||
|
declare const createGoogleGenerativeAI: any;
|
||||||
|
declare const logger: (logstring: string) => void;
|
||||||
|
declare const jsonwebtoken: any;
|
||||||
|
|
||||||
|
// ==================== 供应商数据 ====================
|
||||||
|
const vendor: VendorConfig = {
|
||||||
|
id: "yunwu",
|
||||||
|
author: "Toonflow",
|
||||||
|
description: "OpenAI标准格式接口,您可以修改请求地址并手动添加缺失的模型。",
|
||||||
|
name: "云雾中转",
|
||||||
|
icon: "",
|
||||||
|
inputs: [
|
||||||
|
{ key: "apiKey", label: "API密钥", type: "password", required: true },
|
||||||
|
{ key: "baseUrl", label: "请求地址", type: "url", required: true, placeholder: "以v1结束,示例:https://yunwu.ai/v1" },
|
||||||
|
],
|
||||||
|
inputValues: {
|
||||||
|
apiKey: "",
|
||||||
|
baseUrl: "https://yunwu.ai/v1",
|
||||||
|
},
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
name: "Doubao-Seedream-5.0-lite",
|
||||||
|
type: "image",
|
||||||
|
modelName: "doubao-seedream-5-0-260128",
|
||||||
|
mode: ["text", "singleImage", "multiReference"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Gemini-3-Pro-Image-Preview",
|
||||||
|
type: "image",
|
||||||
|
modelName: "gemini-3.1-flash-image-preview",
|
||||||
|
mode: ["text", "singleImage", "multiReference"],
|
||||||
|
associationSkills: "高质量图像生成,支持文本生成图像、图像编辑",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Claude-sonnet-4.6",
|
||||||
|
type: "text",
|
||||||
|
modelName: "claude-sonnet-4-6",
|
||||||
|
think: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Claude-haiku-4.5-20251001",
|
||||||
|
type: "text",
|
||||||
|
modelName: "claude-haiku-4-5-20251001",
|
||||||
|
think: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Grok-Video-3",
|
||||||
|
type: "video",
|
||||||
|
modelName: "grok-video-3",
|
||||||
|
mode: ["text", "singleImage"],
|
||||||
|
audio: false,
|
||||||
|
durationResolutionMap: [
|
||||||
|
{ duration: [6, 10], resolution: ["720P", "1080P"] }
|
||||||
|
],
|
||||||
|
associationSkills: "文本生成视频,支持图片垫图"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
exports.vendor = vendor;
|
||||||
|
|
||||||
|
// ==================== 适配器函数 ====================
|
||||||
|
|
||||||
|
// 文本请求函数
|
||||||
|
const textRequest = (textModel: TextModel) => {
|
||||||
|
if (!vendor.inputValues.apiKey) throw new Error("缺少API Key");
|
||||||
|
if (!vendor.inputValues.baseUrl) throw new Error("缺少请求地址(baseUrl)");
|
||||||
|
|
||||||
|
const apiKey = vendor.inputValues.apiKey.replace(/^Bearer\s+/i, "");
|
||||||
|
const baseURL = vendor.inputValues.baseUrl;
|
||||||
|
|
||||||
|
return createOpenAI({
|
||||||
|
baseURL: baseURL,
|
||||||
|
apiKey: apiKey,
|
||||||
|
}).chat(textModel.modelName);
|
||||||
|
};
|
||||||
|
exports.textRequest = textRequest;
|
||||||
|
|
||||||
|
// 图片请求函数(修正版:使用 /v1/chat/completions 兼容接口)
|
||||||
|
interface ImageConfig {
|
||||||
|
prompt: string;
|
||||||
|
imageBase64: string[];
|
||||||
|
size: "1K" | "2K" | "4K";
|
||||||
|
aspectRatio: `${number}:${number}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageRequest = async (imageConfig: ImageConfig, imageModel: ImageModel) => {
|
||||||
|
const { apiKey, baseUrl } = vendor.inputValues;
|
||||||
|
if (!apiKey) throw new Error("缺少API Key");
|
||||||
|
if (!baseUrl) throw new Error("缺少请求地址(baseUrl)");
|
||||||
|
|
||||||
|
const cleanApiKey = apiKey.replace(/^Bearer\s+/i, "");
|
||||||
|
const baseURL = baseUrl.replace(/\/$/, "");
|
||||||
|
const endpoint = baseURL + "/chat/completions";
|
||||||
|
|
||||||
|
// 构建用户消息内容(支持多图垫图)
|
||||||
|
const content: any[] = [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: imageConfig.prompt,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 添加参考图片(垫图)
|
||||||
|
if (imageConfig.imageBase64 && imageConfig.imageBase64.length > 0) {
|
||||||
|
for (const imgBase64 of imageConfig.imageBase64) {
|
||||||
|
let dataUrl = imgBase64;
|
||||||
|
if (!imgBase64.startsWith("data:image")) {
|
||||||
|
dataUrl = `data:image/png;base64,${imgBase64}`;
|
||||||
|
}
|
||||||
|
content.push({
|
||||||
|
type: "image_url",
|
||||||
|
image_url: { url: dataUrl },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:云雾中转站可能支持通过额外参数传递图像尺寸/比例,
|
||||||
|
// 若不确定,可将 size 和 aspectRatio 拼接到 prompt 中(推荐)。
|
||||||
|
// 这里采用追加提示词的方式,确保模型理解期望的分辨率和比例。
|
||||||
|
let finalPrompt = imageConfig.prompt;
|
||||||
|
const sizeMap: Record<string, string> = { "1K": "1024x1024", "2K": "2048x2048", "4K": "4096x4096" };
|
||||||
|
const resolution = sizeMap[imageConfig.size] || "1024x1024";
|
||||||
|
finalPrompt += `\n请生成一张比例为 ${imageConfig.aspectRatio}、分辨率不低于 ${resolution} 的图片。`;
|
||||||
|
content[0].text = finalPrompt;
|
||||||
|
|
||||||
|
const requestBody = {
|
||||||
|
model: imageModel.modelName,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: content,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
max_tokens: 4096, // 防止输出截断
|
||||||
|
response_format: { type: "json_object" }, // 部分中转站需要 JSON 输出
|
||||||
|
};
|
||||||
|
|
||||||
|
logger(`[图像生成] 请求URL: ${endpoint}`);
|
||||||
|
logger(`[图像生成] 模型: ${imageModel.modelName}`);
|
||||||
|
logger(`[图像生成] 参考图片数量: ${imageConfig.imageBase64?.length || 0}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post(endpoint, requestBody, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${cleanApiKey}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
timeout: 120000,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${JSON.stringify(response.data)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const assistantMessage = response.data?.choices?.[0]?.message?.content;
|
||||||
|
if (!assistantMessage) {
|
||||||
|
throw new Error("响应中没有 assistant 消息内容");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取图像数据(支持直接返回 base64 data URL 或普通 URL)
|
||||||
|
let imageBase64: string | null = null;
|
||||||
|
// 情况1:消息内容本身就是 data:image 开头
|
||||||
|
if (assistantMessage.startsWith("data:image")) {
|
||||||
|
imageBase64 = assistantMessage;
|
||||||
|
}
|
||||||
|
// 情况2:包含 Markdown 图片链接 
|
||||||
|
else {
|
||||||
|
const markdownMatch = assistantMessage.match(/!\[.*?\]\((.*?)\)/);
|
||||||
|
if (markdownMatch && markdownMatch[1]) {
|
||||||
|
const url = markdownMatch[1];
|
||||||
|
if (url.startsWith("data:image")) {
|
||||||
|
imageBase64 = url;
|
||||||
|
} else {
|
||||||
|
imageBase64 = await urlToBase64(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 情况3:直接是纯文本 URL
|
||||||
|
else if (assistantMessage.match(/^https?:\/\/[^\s]+\.(png|jpg|jpeg|gif|webp)/i)) {
|
||||||
|
imageBase64 = await urlToBase64(assistantMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!imageBase64) {
|
||||||
|
// 最后尝试:也许整个 content 就是 base64 字符串(无前缀)
|
||||||
|
if (/^[A-Za-z0-9+/=]+$/.test(assistantMessage) && assistantMessage.length > 100) {
|
||||||
|
imageBase64 = `data:image/png;base64,${assistantMessage}`;
|
||||||
|
} else {
|
||||||
|
throw new Error(`无法从响应中提取图像数据: ${assistantMessage.substring(0, 200)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger(`[图像生成] 成功,图片大小: ${(imageBase64.length / 1024).toFixed(2)} KB`);
|
||||||
|
return imageBase64;
|
||||||
|
} catch (error: any) {
|
||||||
|
logger(`[图像生成] 失败: ${error.message}`);
|
||||||
|
if (error.response) {
|
||||||
|
logger(`[图像生成] API 错误详情: ${JSON.stringify(error.response.data)}`);
|
||||||
|
throw new Error(`图像生成失败: ${error.response.data?.error?.message || error.message}`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.imageRequest = imageRequest;
|
||||||
|
|
||||||
|
// 视频请求函数(保持原有实现,若云雾中转站有专用视频接口可按需修改)
|
||||||
|
interface VideoConfig {
|
||||||
|
duration: number;
|
||||||
|
resolution: string;
|
||||||
|
aspectRatio: "16:9" | "9:16";
|
||||||
|
prompt: string;
|
||||||
|
imageBase64?: string[];
|
||||||
|
audio?: boolean;
|
||||||
|
mode:
|
||||||
|
| "singleImage"
|
||||||
|
| "multiImage"
|
||||||
|
| "gridImage"
|
||||||
|
| "startEndRequired"
|
||||||
|
| "endFrameOptional"
|
||||||
|
| "startFrameOptional"
|
||||||
|
| "text"
|
||||||
|
| ("video" | "image" | "audio" | "text")[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoRequest = async (videoConfig: VideoConfig, videoModel: VideoModel) => {
|
||||||
|
const { apiKey, baseUrl: rawBaseUrl } = vendor.inputValues;
|
||||||
|
if (!apiKey) throw new Error("缺少API Key");
|
||||||
|
const baseUrl = rawBaseUrl?.trim();
|
||||||
|
if (!baseUrl) throw new Error("缺少请求地址(baseUrl)");
|
||||||
|
|
||||||
|
const createEndpoint = baseUrl.replace(/\/$/, "") + "/video/create";
|
||||||
|
const queryEndpoint = baseUrl.replace(/\/$/, "") + "/video/query";
|
||||||
|
|
||||||
|
let images: string[] | undefined;
|
||||||
|
if (videoConfig.imageBase64 && videoConfig.imageBase64.length > 0) {
|
||||||
|
logger(`[视频生成] 原始图片数组: ${JSON.stringify(videoConfig.imageBase64)}`);
|
||||||
|
images = videoConfig.imageBase64
|
||||||
|
.filter(img => img && typeof img === 'string' && img.length > 0)
|
||||||
|
.map(img => {
|
||||||
|
if (img.startsWith("data:image")) return img;
|
||||||
|
return `data:image/png;base64,${img}`;
|
||||||
|
});
|
||||||
|
if (images.length === 0) {
|
||||||
|
logger(`[视频生成] 警告: 所有图片都无效,将忽略图片参数`);
|
||||||
|
images = undefined;
|
||||||
|
} else {
|
||||||
|
logger(`[视频生成] 有效图片数量: ${images.length}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let aspectRatioParam: string;
|
||||||
|
switch (videoConfig.aspectRatio) {
|
||||||
|
case "16:9": aspectRatioParam = "3:2"; break;
|
||||||
|
case "9:16": aspectRatioParam = "2:3"; break;
|
||||||
|
default: aspectRatioParam = "1:1";
|
||||||
|
}
|
||||||
|
|
||||||
|
let sizeParam: string = "720P";
|
||||||
|
if (videoConfig.resolution && videoConfig.resolution.includes("1080")) sizeParam = "1080P";
|
||||||
|
|
||||||
|
const createBody: any = {
|
||||||
|
model: videoModel.modelName,
|
||||||
|
prompt: videoConfig.prompt,
|
||||||
|
aspect_ratio: aspectRatioParam,
|
||||||
|
size: sizeParam,
|
||||||
|
};
|
||||||
|
if (images && images.length > 0) createBody.images = images;
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger(`[视频生成] 创建请求体: ${JSON.stringify({ ...createBody, images: images ? `${images.length}张图片` : undefined })}`);
|
||||||
|
const createResp = await axios.post(createEndpoint, createBody, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${apiKey.replace(/^Bearer\s+/i, "")}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
timeout: 30000,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (createResp.status !== 200 || !createResp.data?.id) {
|
||||||
|
throw new Error(`创建任务失败: ${JSON.stringify(createResp.data)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskId = createResp.data.id;
|
||||||
|
logger(`[视频生成] 任务已创建,ID: ${taskId}`);
|
||||||
|
|
||||||
|
const pollResult = await pollTask(
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
const queryResp = await axios.get(queryEndpoint, {
|
||||||
|
params: { id: taskId },
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${apiKey.replace(/^Bearer\s+/i, "")}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
timeout: 15000,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (queryResp.status !== 200) {
|
||||||
|
return { completed: false, error: `查询失败: HTTP ${queryResp.status}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = queryResp.data;
|
||||||
|
const status = data.status;
|
||||||
|
logger(`[视频生成] 任务状态: ${status}`);
|
||||||
|
|
||||||
|
if (status === "succeeded" || status === "completed" || status === "success") {
|
||||||
|
if (data.video_url) {
|
||||||
|
return { completed: true, data: data.video_url };
|
||||||
|
} else {
|
||||||
|
return { completed: false, error: "任务成功但未返回视频URL" };
|
||||||
|
}
|
||||||
|
} else if (status === "failed" || status === "error") {
|
||||||
|
return { completed: false, error: `视频生成失败: ${data.error || "未知错误"}` };
|
||||||
|
} else {
|
||||||
|
return { completed: false };
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
logger(`[视频生成] 轮询出错: ${err.message}`);
|
||||||
|
return { completed: false, error: err.message };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
3000,
|
||||||
|
300000
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!pollResult.completed) {
|
||||||
|
throw new Error(pollResult.error || "视频生成超时或失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoUrl = pollResult.data;
|
||||||
|
logger(`[视频生成] 成功,视频URL: ${videoUrl}`);
|
||||||
|
return videoUrl;
|
||||||
|
} catch (error: any) {
|
||||||
|
logger(`[视频生成] 失败: ${error.message}`);
|
||||||
|
if (error.response) {
|
||||||
|
logger(`[视频生成] API 错误详情: ${JSON.stringify(error.response.data)}`);
|
||||||
|
throw new Error(`视频生成失败: ${error.response.data?.error?.message || error.message}`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.videoRequest = videoRequest;
|
||||||
|
|
||||||
|
// TTS 请求函数(占位)
|
||||||
|
interface TTSConfig {
|
||||||
|
text: string;
|
||||||
|
voice: string;
|
||||||
|
speechRate: number;
|
||||||
|
pitchRate: number;
|
||||||
|
volume: number;
|
||||||
|
}
|
||||||
|
const ttsRequest = async (ttsConfig: TTSConfig, ttsModel: TTSModel) => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
exports.ttsRequest = ttsRequest;
|
||||||
106
src/router.ts
106
src/router.ts
@ -1,4 +1,4 @@
|
|||||||
// @routes-hash 341ae5b09b9564f4ea847d0edead3c77
|
// @routes-hash 8fa7db9525fe2ff96ba91e9bd6c22b65
|
||||||
import { Express } from "express";
|
import { Express } from "express";
|
||||||
|
|
||||||
import route1 from "./routes/agents/clearMemory";
|
import route1 from "./routes/agents/clearMemory";
|
||||||
@ -27,32 +27,32 @@ import route23 from "./routes/assetsGenerate/batchPolishAssetsPrompt";
|
|||||||
import route24 from "./routes/assetsGenerate/cancelGenerate";
|
import route24 from "./routes/assetsGenerate/cancelGenerate";
|
||||||
import route25 from "./routes/assetsGenerate/generateAssets";
|
import route25 from "./routes/assetsGenerate/generateAssets";
|
||||||
import route26 from "./routes/assetsGenerate/polishAssetsPrompt";
|
import route26 from "./routes/assetsGenerate/polishAssetsPrompt";
|
||||||
import route27 from "./routes/cornerScape/batchBindAudio";
|
import route27 from "./routes/common/getBigImage";
|
||||||
import route28 from "./routes/cornerScape/getAllAssets";
|
import route28 from "./routes/cornerScape/batchBindAudio";
|
||||||
import route29 from "./routes/cornerScape/updateAssetsAudio";
|
import route29 from "./routes/cornerScape/getAllAssets";
|
||||||
import route30 from "./routes/general/generalStatistics";
|
import route30 from "./routes/cornerScape/updateAssetsAudio";
|
||||||
import route31 from "./routes/general/getSingleProject";
|
import route31 from "./routes/general/generalStatistics";
|
||||||
import route32 from "./routes/general/updateProject";
|
import route32 from "./routes/general/getSingleProject";
|
||||||
import route33 from "./routes/login/login";
|
import route33 from "./routes/general/updateProject";
|
||||||
import route34 from "./routes/modelSelect/getModelDetail";
|
import route34 from "./routes/login/login";
|
||||||
import route35 from "./routes/modelSelect/getModelList";
|
import route35 from "./routes/modelSelect/getModelDetail";
|
||||||
import route36 from "./routes/novel/addNovel";
|
import route36 from "./routes/modelSelect/getModelList";
|
||||||
import route37 from "./routes/novel/batchDeleteNovel";
|
import route37 from "./routes/novel/addNovel";
|
||||||
import route38 from "./routes/novel/delNovel";
|
import route38 from "./routes/novel/batchDeleteNovel";
|
||||||
import route39 from "./routes/novel/event/batchDeleteEvent";
|
import route39 from "./routes/novel/delNovel";
|
||||||
import route40 from "./routes/novel/event/deletEvent";
|
import route40 from "./routes/novel/event/batchDeleteEvent";
|
||||||
import route41 from "./routes/novel/event/generateEvents";
|
import route41 from "./routes/novel/event/deletEvent";
|
||||||
import route42 from "./routes/novel/event/getEvent";
|
import route42 from "./routes/novel/event/generateEvents";
|
||||||
import route43 from "./routes/novel/getNovel";
|
import route43 from "./routes/novel/event/getEvent";
|
||||||
import route44 from "./routes/novel/getNovelData";
|
import route44 from "./routes/novel/getNovel";
|
||||||
import route45 from "./routes/novel/getNovelEventState";
|
import route45 from "./routes/novel/getNovelData";
|
||||||
import route46 from "./routes/novel/getNovelIndex";
|
import route46 from "./routes/novel/getNovelEventState";
|
||||||
import route47 from "./routes/novel/updateNovel";
|
import route47 from "./routes/novel/getNovelIndex";
|
||||||
import route48 from "./routes/other/deleteAllData";
|
import route48 from "./routes/novel/updateNovel";
|
||||||
import route49 from "./routes/other/getVersion";
|
import route49 from "./routes/other/deleteAllData";
|
||||||
import route50 from "./routes/production/assets/batchGenerateAssetsImage";
|
import route50 from "./routes/other/getVersion";
|
||||||
import route51 from "./routes/production/assets/deleteAssetsDireve";
|
import route51 from "./routes/production/assets/batchGenerateAssetsImage";
|
||||||
import route52 from "./routes/production/assets/getAssetsData";
|
import route52 from "./routes/production/assets/deleteAssetsDireve";
|
||||||
import route53 from "./routes/production/assets/pollingImage";
|
import route53 from "./routes/production/assets/pollingImage";
|
||||||
import route54 from "./routes/production/assets/updateAssetsUrl";
|
import route54 from "./routes/production/assets/updateAssetsUrl";
|
||||||
import route55 from "./routes/production/editImage/generateFlowImage";
|
import route55 from "./routes/production/editImage/generateFlowImage";
|
||||||
@ -182,32 +182,32 @@ export default async (app: Express) => {
|
|||||||
app.use("/api/assetsGenerate/cancelGenerate", route24);
|
app.use("/api/assetsGenerate/cancelGenerate", route24);
|
||||||
app.use("/api/assetsGenerate/generateAssets", route25);
|
app.use("/api/assetsGenerate/generateAssets", route25);
|
||||||
app.use("/api/assetsGenerate/polishAssetsPrompt", route26);
|
app.use("/api/assetsGenerate/polishAssetsPrompt", route26);
|
||||||
app.use("/api/cornerScape/batchBindAudio", route27);
|
app.use("/api/common/getBigImage", route27);
|
||||||
app.use("/api/cornerScape/getAllAssets", route28);
|
app.use("/api/cornerScape/batchBindAudio", route28);
|
||||||
app.use("/api/cornerScape/updateAssetsAudio", route29);
|
app.use("/api/cornerScape/getAllAssets", route29);
|
||||||
app.use("/api/general/generalStatistics", route30);
|
app.use("/api/cornerScape/updateAssetsAudio", route30);
|
||||||
app.use("/api/general/getSingleProject", route31);
|
app.use("/api/general/generalStatistics", route31);
|
||||||
app.use("/api/general/updateProject", route32);
|
app.use("/api/general/getSingleProject", route32);
|
||||||
app.use("/api/login/login", route33);
|
app.use("/api/general/updateProject", route33);
|
||||||
app.use("/api/modelSelect/getModelDetail", route34);
|
app.use("/api/login/login", route34);
|
||||||
app.use("/api/modelSelect/getModelList", route35);
|
app.use("/api/modelSelect/getModelDetail", route35);
|
||||||
app.use("/api/novel/addNovel", route36);
|
app.use("/api/modelSelect/getModelList", route36);
|
||||||
app.use("/api/novel/batchDeleteNovel", route37);
|
app.use("/api/novel/addNovel", route37);
|
||||||
app.use("/api/novel/delNovel", route38);
|
app.use("/api/novel/batchDeleteNovel", route38);
|
||||||
app.use("/api/novel/event/batchDeleteEvent", route39);
|
app.use("/api/novel/delNovel", route39);
|
||||||
app.use("/api/novel/event/deletEvent", route40);
|
app.use("/api/novel/event/batchDeleteEvent", route40);
|
||||||
app.use("/api/novel/event/generateEvents", route41);
|
app.use("/api/novel/event/deletEvent", route41);
|
||||||
app.use("/api/novel/event/getEvent", route42);
|
app.use("/api/novel/event/generateEvents", route42);
|
||||||
app.use("/api/novel/getNovel", route43);
|
app.use("/api/novel/event/getEvent", route43);
|
||||||
app.use("/api/novel/getNovelData", route44);
|
app.use("/api/novel/getNovel", route44);
|
||||||
app.use("/api/novel/getNovelEventState", route45);
|
app.use("/api/novel/getNovelData", route45);
|
||||||
app.use("/api/novel/getNovelIndex", route46);
|
app.use("/api/novel/getNovelEventState", route46);
|
||||||
app.use("/api/novel/updateNovel", route47);
|
app.use("/api/novel/getNovelIndex", route47);
|
||||||
app.use("/api/other/deleteAllData", route48);
|
app.use("/api/novel/updateNovel", route48);
|
||||||
app.use("/api/other/getVersion", route49);
|
app.use("/api/other/deleteAllData", route49);
|
||||||
app.use("/api/production/assets/batchGenerateAssetsImage", route50);
|
app.use("/api/other/getVersion", route50);
|
||||||
app.use("/api/production/assets/deleteAssetsDireve", route51);
|
app.use("/api/production/assets/batchGenerateAssetsImage", route51);
|
||||||
app.use("/api/production/assets/getAssetsData", route52);
|
app.use("/api/production/assets/deleteAssetsDireve", route52);
|
||||||
app.use("/api/production/assets/pollingImage", route53);
|
app.use("/api/production/assets/pollingImage", route53);
|
||||||
app.use("/api/production/assets/updateAssetsUrl", route54);
|
app.use("/api/production/assets/updateAssetsUrl", route54);
|
||||||
app.use("/api/production/editImage/generateFlowImage", route55);
|
app.use("/api/production/editImage/generateFlowImage", route55);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export default router.post("/", async (req, res) => {
|
|||||||
const list = await u.db("o_artStyle").select("*");
|
const list = await u.db("o_artStyle").select("*");
|
||||||
const data = await Promise.all(
|
const data = await Promise.all(
|
||||||
list.map(async (item: any) => {
|
list.map(async (item: any) => {
|
||||||
const fileUrl = await u.oss.getFileUrl(item.fileUrl);
|
const fileUrl = await u.oss.getSmallImageUrl(item.fileUrl);
|
||||||
return { ...item, fileUrl };
|
return { ...item, fileUrl };
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export default router.post(
|
|||||||
const childAssetsWithSrc = await Promise.all(
|
const childAssetsWithSrc = await Promise.all(
|
||||||
childAssets.map(async (child) => ({
|
childAssets.map(async (child) => ({
|
||||||
...child,
|
...child,
|
||||||
src: child.filePath && (await u.oss.getFileUrl(child.filePath!)),
|
src: child.filePath && (await filterTypeGetFileUrl(child.filePath!, child.type)),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ export default router.post(
|
|||||||
parentAssets.map(async (parent) => ({
|
parentAssets.map(async (parent) => ({
|
||||||
...parent,
|
...parent,
|
||||||
sonAssets: childAssetsWithSrc.filter((child) => child.assetsId === parent.id),
|
sonAssets: childAssetsWithSrc.filter((child) => child.assetsId === parent.id),
|
||||||
src: parent.filePath && (await u.oss.getFileUrl(parent.filePath!)),
|
src: parent.filePath && (await filterTypeGetFileUrl(parent.filePath!, parent.type)),
|
||||||
...(parent.type == "audio" ? { sex: parent.describe?.split("|")[0], describe: parent.describe?.split("|")[1] } : {}),
|
...(parent.type == "audio" ? { sex: parent.describe?.split("|")[0], describe: parent.describe?.split("|")[1] } : {}),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
@ -77,3 +77,11 @@ export default router.post(
|
|||||||
res.status(200).send(success({ data: result, total: totalQuery?.total }));
|
res.status(200).send(success({ data: result, total: totalQuery?.total }));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
async function filterTypeGetFileUrl(url: string, type: string) {
|
||||||
|
if (type == 'role' || type == 'tool' || type == 'scene') {
|
||||||
|
return await u.oss.getSmallImageUrl(url)
|
||||||
|
} else {
|
||||||
|
return await u.oss.getFileUrl(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@ export default router.post(
|
|||||||
const tempAssets = await Promise.all(
|
const tempAssets = await Promise.all(
|
||||||
rawTempAssets.map(async (item) => ({
|
rawTempAssets.map(async (item) => ({
|
||||||
...item,
|
...item,
|
||||||
filePath: item.filePath ? await u.oss.getFileUrl(item.filePath) : "",
|
filePath: item.filePath ? await u.oss.getSmallImageUrl(item.filePath) : "",
|
||||||
selected: assets?.imageId != null && Number(item.id) === Number(assets.imageId),
|
selected: assets?.imageId != null && Number(item.id) === Number(assets.imageId),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export default router.post(
|
|||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
data.map(async (item: any) => ({
|
data.map(async (item: any) => ({
|
||||||
...item,
|
...item,
|
||||||
filePath: item.filePath ? await u.oss.getFileUrl(item.filePath) : null,
|
filePath: item.filePath ? await u.oss.getSmallImageUrl(item.filePath) : null,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
res.status(200).send(success(result));
|
res.status(200).send(success(result));
|
||||||
|
|||||||
@ -127,7 +127,7 @@ export default router.post("/", validateFields(requestSchema), async (req, res)
|
|||||||
resolution,
|
resolution,
|
||||||
});
|
});
|
||||||
|
|
||||||
const path = await u.oss.getFileUrl(imagePath);
|
const path = await u.oss.getSmallImageUrl(imagePath);
|
||||||
await u.db("o_assets").where("id", id).update({ imageId });
|
await u.db("o_assets").where("id", id).update({ imageId });
|
||||||
|
|
||||||
return res.status(200).send(success({ path, assetsId: id }));
|
return res.status(200).send(success({ path, assetsId: id }));
|
||||||
|
|||||||
19
src/routes/common/getBigImage.ts
Normal file
19
src/routes/common/getBigImage.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import express from "express";
|
||||||
|
import u from "@/utils";
|
||||||
|
import { success } from "@/lib/responseFormat";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { validateFields } from "@/middleware/middleware";
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// 获取生成图片
|
||||||
|
export default router.post(
|
||||||
|
"/",
|
||||||
|
validateFields({
|
||||||
|
url: z.string()
|
||||||
|
}),
|
||||||
|
async (req, res) => {
|
||||||
|
const { url } = req.body
|
||||||
|
const bigImageUrl = await u.oss.getFileUrl(u.replaceUrl(url))
|
||||||
|
res.status(200).send(success(bigImageUrl));
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -51,12 +51,12 @@ export default router.post(
|
|||||||
const historyImagesWithUrl = await Promise.all(
|
const historyImagesWithUrl = await Promise.all(
|
||||||
historyImages.map(async (img: any) => ({
|
historyImages.map(async (img: any) => ({
|
||||||
id: img.id,
|
id: img.id,
|
||||||
filePath: img.filePath && (await u.oss.getFileUrl(img.filePath)),
|
filePath: img.filePath && (await u.oss.getSmallImageUrl(img.filePath)),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
...parent,
|
...parent,
|
||||||
filePath: parent.filePath && (await u.oss.getFileUrl(parent.filePath!)),
|
filePath: parent.filePath && (await u.oss.getSmallImageUrl(parent.filePath!)),
|
||||||
historyImages: historyImagesWithUrl,
|
historyImages: historyImagesWithUrl,
|
||||||
relepedAudio:repleAssets[parent.id] ?? []
|
relepedAudio:repleAssets[parent.id] ?? []
|
||||||
};
|
};
|
||||||
|
|||||||
@ -109,7 +109,7 @@ export default router.post(
|
|||||||
return {
|
return {
|
||||||
id: item.id!,
|
id: item.id!,
|
||||||
state: "已完成",
|
state: "已完成",
|
||||||
src: await u.oss.getFileUrl(savePath),
|
src: await u.oss.getSmallImageUrl(savePath),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await u
|
await u
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
import express from "express";
|
|
||||||
import u from "@/utils";
|
|
||||||
import { z } from "zod";
|
|
||||||
import { success } from "@/lib/responseFormat";
|
|
||||||
import { validateFields } from "@/middleware/middleware";
|
|
||||||
import { o_assets } from "@/types/database";
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
export default router.post(
|
|
||||||
"/",
|
|
||||||
validateFields({
|
|
||||||
projectId: z.number(),
|
|
||||||
}),
|
|
||||||
async (req, res) => {
|
|
||||||
const { projectId } = req.body;
|
|
||||||
const parentAssetsData = await u.db("o_assets").where("projectId", projectId).whereNotNull("assetsId");
|
|
||||||
const parentIds = parentAssetsData.map((i) => i.id);
|
|
||||||
const sonAssetsData = await u.db("o_assets").whereIn("assetsId", parentIds);
|
|
||||||
const sonAssetsMap: Record<number, o_assets[]> = {};
|
|
||||||
|
|
||||||
const imageIds = [...parentAssetsData.map((i) => i.imageId).concat(sonAssetsData.map((i) => i.imageId))].filter(Boolean);
|
|
||||||
const imagePaths = await u
|
|
||||||
.db("o_image")
|
|
||||||
.whereIn("id", imageIds as unknown as string[])
|
|
||||||
.select("id", "filePath");
|
|
||||||
const imageSignUrls = await Promise.all(
|
|
||||||
imagePaths.map(async (i) => {
|
|
||||||
return { id: i.id, src: i.filePath ? await u.oss.getFileUrl(i.filePath) : null };
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const imageUrlMap: Record<number, string | null> = {};
|
|
||||||
imageSignUrls.forEach((i, index) => {
|
|
||||||
imageUrlMap[i.id!] = i.src;
|
|
||||||
});
|
|
||||||
sonAssetsData.forEach((i) => {
|
|
||||||
if (!sonAssetsMap[i.assetsId!]) {
|
|
||||||
sonAssetsMap[i.assetsId!] = [];
|
|
||||||
}
|
|
||||||
const obj = {
|
|
||||||
assetsId: i.id,
|
|
||||||
name: i.name,
|
|
||||||
desc: i.describe,
|
|
||||||
src: imageUrlMap[i.imageId!] ?? null,
|
|
||||||
derive: sonAssetsMap[i.id!] ?? [],
|
|
||||||
};
|
|
||||||
sonAssetsMap[i.assetsId!].push(obj);
|
|
||||||
});
|
|
||||||
const returnData = parentAssetsData.map((i) => {
|
|
||||||
return {
|
|
||||||
assetsId: i.id,
|
|
||||||
name: i.name,
|
|
||||||
desc: i.describe,
|
|
||||||
src: imageUrlMap[i.imageId!] ?? null,
|
|
||||||
derive: sonAssetsMap[i.id!] ?? [],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
res.status(200).send(success(returnData));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@ -21,7 +21,7 @@ export default router.post(
|
|||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
data.map(async (item: any) => ({
|
data.map(async (item: any) => ({
|
||||||
...item,
|
...item,
|
||||||
src: item.filePath ? await u.oss.getFileUrl(item.filePath) : null,
|
src: item.filePath ? await u.oss.getSmallImageUrl(item.filePath) : null,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
res.status(200).send(success(result));
|
res.status(200).send(success(result));
|
||||||
|
|||||||
@ -8,8 +8,9 @@ const router = express.Router();
|
|||||||
|
|
||||||
async function urlToBase64(imageUrl: string): Promise<string> {
|
async function urlToBase64(imageUrl: string): Promise<string> {
|
||||||
if (imageUrl.startsWith("/oss/")) {
|
if (imageUrl.startsWith("/oss/")) {
|
||||||
return await u.oss.getImageBase64(u.replaceUrl(imageUrl));
|
return await u.oss.getImageBase64(u.replaceUrl(imageUrl).replace("/smallImage", ""));
|
||||||
}
|
}
|
||||||
|
imageUrl = await u.oss.getFileUrl(u.replaceUrl(imageUrl))
|
||||||
const response = await axios.get(imageUrl, { responseType: "arraybuffer" });
|
const response = await axios.get(imageUrl, { responseType: "arraybuffer" });
|
||||||
const contentType = response.headers["content-type"] || "image/png";
|
const contentType = response.headers["content-type"] || "image/png";
|
||||||
const base64 = Buffer.from(response.data, "binary").toString("base64");
|
const base64 = Buffer.from(response.data, "binary").toString("base64");
|
||||||
@ -51,7 +52,7 @@ export default router.post(
|
|||||||
const savePath = `${projectId}/workFlow/${u.uuid()}.jpg`;
|
const savePath = `${projectId}/workFlow/${u.uuid()}.jpg`;
|
||||||
await imageClass.save(savePath);
|
await imageClass.save(savePath);
|
||||||
|
|
||||||
const url = await u.oss.getFileUrl(savePath);
|
const url = await u.oss.getSmallImageUrl(savePath);
|
||||||
return res.status(200).send(success({ url }));
|
return res.status(200).send(success({ url }));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(400).send(error(u.error(e).message))
|
res.status(400).send(error(u.error(e).message))
|
||||||
|
|||||||
@ -18,14 +18,13 @@ export default router.post(
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
parseFlow.nodes.map(async (node: any) => {
|
parseFlow.nodes.map(async (node: any) => {
|
||||||
if (node.type === "upload") {
|
if (node.type === "upload") {
|
||||||
node.data.image = node.data.image ? await u.oss.getFileUrl(node.data.image) : "";
|
node.data.image = node.data.image ? await u.oss.getSmallImageUrl(node.data.image) : "";
|
||||||
} else if (node.type === "generated") {
|
} else if (node.type === "generated") {
|
||||||
node.data.generatedImage = node.data.generatedImage ? await u.oss.getFileUrl(node.data.generatedImage) : "";
|
node.data.generatedImage = node.data.generatedImage ? await u.oss.getSmallImageUrl(node.data.generatedImage) : "";
|
||||||
console.log("%c Line:25 🍋 node.data.references", "background:#42b983", node.data.references);
|
|
||||||
|
|
||||||
node.data.references = await Promise.all(node.data.references.map(async (item: { image: string }) => {
|
node.data.references = await Promise.all(node.data.references.map(async (item: { image: string }) => {
|
||||||
return {
|
return {
|
||||||
image: await u.oss.getFileUrl(item.image)
|
image: await u.oss.getSmallImageUrl(item.image)
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ export default router.post(
|
|||||||
const savePath = `/${projectId}/imageFlow/${scriptId}/${uuid()}.${ext}`;
|
const savePath = `/${projectId}/imageFlow/${scriptId}/${uuid()}.${ext}`;
|
||||||
|
|
||||||
await u.oss.writeFile(savePath, Buffer.from(base64Data.match(/base64,([A-Za-z0-9+/=]+)/)[1] ?? "", "base64"));
|
await u.oss.writeFile(savePath, Buffer.from(base64Data.match(/base64,([A-Za-z0-9+/=]+)/)[1] ?? "", "base64"));
|
||||||
const url = await u.oss.getFileUrl(savePath);
|
const url = await u.oss.getSmallImageUrl(savePath);
|
||||||
res.status(200).send(success(url));
|
res.status(200).send(success(url));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export default router.post(
|
|||||||
type: item.type ?? "",
|
type: item.type ?? "",
|
||||||
prompt: item.prompt ?? "",
|
prompt: item.prompt ?? "",
|
||||||
desc: item.describe ?? "",
|
desc: item.describe ?? "",
|
||||||
src: item.filePath && (await u.oss.getFileUrl(item.filePath!)),
|
src: item.filePath && (await u.oss.getSmallImageUrl(item.filePath!)),
|
||||||
derive: await Promise.all(
|
derive: await Promise.all(
|
||||||
childAssetsData
|
childAssetsData
|
||||||
.filter((child) => child.assetsId === item.id)
|
.filter((child) => child.assetsId === item.id)
|
||||||
@ -64,7 +64,7 @@ export default router.post(
|
|||||||
type: child.type,
|
type: child.type,
|
||||||
prompt: child.prompt,
|
prompt: child.prompt,
|
||||||
desc: child.describe ?? "",
|
desc: child.describe ?? "",
|
||||||
src: child.filePath && (await u.oss.getFileUrl(child.filePath!)),
|
src: child.filePath && (await u.oss.getSmallImageUrl(child.filePath!)),
|
||||||
state: child.state ?? "未生成", //todo:矫正状态值
|
state: child.state ?? "未生成", //todo:矫正状态值
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
@ -91,7 +91,7 @@ export default router.post(
|
|||||||
storyboardData.map(async (i) => {
|
storyboardData.map(async (i) => {
|
||||||
if (i.filePath) {
|
if (i.filePath) {
|
||||||
try {
|
try {
|
||||||
i.filePath = await u.oss.getFileUrl(i.filePath);
|
i.filePath = await u.oss.getSmallImageUrl(i.filePath);
|
||||||
} catch {
|
} catch {
|
||||||
i.filePath = "";
|
i.filePath = "";
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ export default router.post(
|
|||||||
type: item.type ?? "",
|
type: item.type ?? "",
|
||||||
prompt: item.prompt ?? "",
|
prompt: item.prompt ?? "",
|
||||||
desc: item.describe ?? "",
|
desc: item.describe ?? "",
|
||||||
src: item.filePath && (await u.oss.getFileUrl(item.filePath!)),
|
src: item.filePath && (await u.oss.getSmallImageUrl(item.filePath!)),
|
||||||
flowId: item.flowId,
|
flowId: item.flowId,
|
||||||
derive: await Promise.all(
|
derive: await Promise.all(
|
||||||
childAssetsData
|
childAssetsData
|
||||||
@ -130,7 +130,7 @@ export default router.post(
|
|||||||
prompt: child.prompt,
|
prompt: child.prompt,
|
||||||
type: child.type,
|
type: child.type,
|
||||||
desc: child.describe ?? "",
|
desc: child.describe ?? "",
|
||||||
src: child.filePath && (await u.oss.getFileUrl(child.filePath!)),
|
src: child.filePath && (await u.oss.getSmallImageUrl(child.filePath!)),
|
||||||
state: child.state ?? "未生成",
|
state: child.state ?? "未生成",
|
||||||
errorReason: child?.errorReason ?? "",
|
errorReason: child?.errorReason ?? "",
|
||||||
flowId: child.flowId,
|
flowId: child.flowId,
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export default router.post(
|
|||||||
storyboardData.map(async (i) => {
|
storyboardData.map(async (i) => {
|
||||||
return {
|
return {
|
||||||
...i,
|
...i,
|
||||||
filePath: i.filePath ? await u.oss.getFileUrl(i.filePath!) : "",
|
filePath: i.filePath ? await u.oss.getSmallImageUrl(i.filePath!) : "",
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -58,7 +58,7 @@ export default router.post(
|
|||||||
const charactersWithUrl = await Promise.all(
|
const charactersWithUrl = await Promise.all(
|
||||||
characters.map(async (c) => {
|
characters.map(async (c) => {
|
||||||
if (c.avatar) {
|
if (c.avatar) {
|
||||||
return { ...c, avatar: await u.oss.getFileUrl(c.avatar) };
|
return { ...c, avatar: await u.oss.getSmallImageUrl(c.avatar) };
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -94,7 +94,7 @@ export default router.post(
|
|||||||
lastStoryboard.map(async (i) => {
|
lastStoryboard.map(async (i) => {
|
||||||
return {
|
return {
|
||||||
associateAssetsIds: await u.db("o_assets2Storyboard").where("storyboardId", i.id).orderBy("rowid").select("assetId").pluck("assetId"),
|
associateAssetsIds: await u.db("o_assets2Storyboard").where("storyboardId", i.id).orderBy("rowid").select("assetId").pluck("assetId"),
|
||||||
src: i.filePath ? await u.oss.getFileUrl(i.filePath) : "",
|
src: i.filePath ? await u.oss.getSmallImageUrl(i.filePath) : "",
|
||||||
id: i.id,
|
id: i.id,
|
||||||
trackId: i.trackId,
|
trackId: i.trackId,
|
||||||
prompt: i.prompt,
|
prompt: i.prompt,
|
||||||
|
|||||||
@ -33,7 +33,7 @@ export default router.post(
|
|||||||
id: i.id,
|
id: i.id,
|
||||||
prompt: i.prompt,
|
prompt: i.prompt,
|
||||||
state: i.state,
|
state: i.state,
|
||||||
src: i.filePath ? await u.oss.getFileUrl(i.filePath!) : "",
|
src: i.filePath ? await u.oss.getSmallImageUrl(i.filePath!) : "",
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export default router.post(
|
|||||||
const result = await Promise.all(
|
const result = await Promise.all(
|
||||||
data.map(async (item: any) => ({
|
data.map(async (item: any) => ({
|
||||||
...item,
|
...item,
|
||||||
src: item.filePath ? await u.oss.getFileUrl(item.filePath) : null,
|
src: item.filePath ? await u.oss.getSmallImageUrl(item.filePath) : null,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
res.status(200).send(success(result));
|
res.status(200).send(success(result));
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export default router.post(
|
|||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
totalFilePaths.map(async (item: { id: string, filePath: string, sources: string }) => {
|
totalFilePaths.map(async (item: { id: string, filePath: string, sources: string }) => {
|
||||||
result[`${item.id}:${item.sources}`] = item.filePath ? await u.oss.getFileUrl(item.filePath) : "";
|
result[`${item.id}:${item.sources}`] = item.filePath ? await u.oss.getSmallImageUrl(item.filePath) : "";
|
||||||
}))
|
}))
|
||||||
|
|
||||||
res.status(200).send(success({ data: result }));
|
res.status(200).send(success({ data: result }));
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export default router.post(
|
|||||||
const storyboardList = await u.db("o_storyboard").where({ scriptId, projectId }).orderBy("index", "asc");
|
const storyboardList = await u.db("o_storyboard").where({ scriptId, projectId }).orderBy("index", "asc");
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
storyboardList.map(async (i) => {
|
storyboardList.map(async (i) => {
|
||||||
i.filePath = i.filePath ? await u.oss.getFileUrl(i.filePath) : "";
|
i.filePath = i.filePath ? await u.oss.getSmallImageUrl(i.filePath) : "";
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const storyboardTrackRecord: Record<number, any[]> = {};
|
const storyboardTrackRecord: Record<number, any[]> = {};
|
||||||
@ -100,7 +100,7 @@ export default router.post(
|
|||||||
type: i.type,
|
type: i.type,
|
||||||
fileType: "image" as const,
|
fileType: "image" as const,
|
||||||
sources: "assets",
|
sources: "assets",
|
||||||
src: i.filePath ? await u.oss.getFileUrl(i.filePath) : "",
|
src: i.filePath ? await u.oss.getSmallImageUrl(i.filePath) : "",
|
||||||
};
|
};
|
||||||
const sid = i.storyboardId as number;
|
const sid = i.storyboardId as number;
|
||||||
if (!otherDataMap[sid]) otherDataMap[sid] = [];
|
if (!otherDataMap[sid]) otherDataMap[sid] = [];
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default router.post(
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
videoList.map(async (s) => ({
|
videoList.map(async (s) => ({
|
||||||
...s,
|
...s,
|
||||||
src: s.filePath ? await u.oss.getFileUrl(s.filePath) : "",
|
src: s.filePath ? await u.oss.getSmallImageUrl(s.filePath) : "",
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import isPathInside from "is-path-inside";
|
|||||||
import getPath, { isEletron } from "@/utils/getPath";
|
import getPath, { isEletron } from "@/utils/getPath";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import sharp from "sharp";
|
||||||
|
|
||||||
// 规范化路径:去除前导斜杠,并将路径分隔符统一转换为系统分隔符
|
// 规范化路径:去除前导斜杠,并将路径分隔符统一转换为系统分隔符
|
||||||
function normalizeUserPath(userPath: string): string {
|
function normalizeUserPath(userPath: string): string {
|
||||||
@ -169,6 +170,43 @@ class OSS {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图片的缩略图 URL(最长边不超过 512px,等比缩放)。
|
||||||
|
* 缩略图保存在原路径同目录下的 smallImage 子文件夹中。
|
||||||
|
* 若缩略图已存在则直接返回其 URL;若不存在则同步生成并保存后返回缩略图 URL,
|
||||||
|
* 生成失败时返回原图 URL。
|
||||||
|
* @param userRelPath 用户传入的相对文件路径(使用 / 作为分隔符)
|
||||||
|
* @returns 缩略图 URL(已存在或生成成功)或原图 URL(生成失败时)
|
||||||
|
*/
|
||||||
|
async getSmallImageUrl(userRelPath: string): Promise<string> {
|
||||||
|
// 构造缩略图相对路径:在原路径的目录层级前插入 smallImage 目录
|
||||||
|
// 例如:123/abc.jpg => smallImage/123/abc.jpg
|
||||||
|
const smallImageRelPath = `smallImage/${userRelPath.replace(/^[/\\]+/, "")}`;
|
||||||
|
|
||||||
|
if (await this.fileExists(smallImageRelPath)) {
|
||||||
|
return this.getFileUrl(smallImageRelPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缩略图不存在:同步生成,生成失败则返回原图 URL
|
||||||
|
const originalUrl = await this.getFileUrl(userRelPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.ensureInit();
|
||||||
|
const srcAbsPath = resolveSafeLocalPath(userRelPath, this.rootDir);
|
||||||
|
const dstAbsPath = resolveSafeLocalPath(smallImageRelPath, this.rootDir);
|
||||||
|
await fs.mkdir(path.dirname(dstAbsPath), { recursive: true });
|
||||||
|
await sharp(srcAbsPath)
|
||||||
|
.resize(512, 512, { fit: "inside", withoutEnlargement: true })
|
||||||
|
.toFile(dstAbsPath);
|
||||||
|
console.info(`[${dstAbsPath}]小图写入成功`);
|
||||||
|
return this.getFileUrl(smallImageRelPath);
|
||||||
|
} catch (e) {
|
||||||
|
// 生成失败返回原图
|
||||||
|
console.warn("[OSS] 生成缩略图失败:", e);
|
||||||
|
return originalUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new OSS();
|
export default new OSS();
|
||||||
|
|||||||
@ -3,7 +3,7 @@ export default function replaceUrl(url: string): string {
|
|||||||
let cleanedPath = '';
|
let cleanedPath = '';
|
||||||
try {
|
try {
|
||||||
const pathname = new URL(url).pathname;
|
const pathname = new URL(url).pathname;
|
||||||
cleanedPath = pathname.replace(/^\/oss/, '');
|
cleanedPath = pathname.replace(/^\/oss/, '').replace(/^\/smallImage/, '');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 如果不是有效的URL,则直接返回原字符串
|
// 如果不是有效的URL,则直接返回原字符串
|
||||||
cleanedPath = url;
|
cleanedPath = url;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user