90 lines
3.2 KiB
TypeScript
90 lines
3.2 KiB
TypeScript
import type { CanvasNodeData } from "@/types/canvas";
|
|
|
|
export const MEDIA_CONVERSION_SCHEMA_VERSION = 1 as const;
|
|
|
|
export const MEDIA_CONVERSION_NODE_TYPE = "media-conversion" as const;
|
|
|
|
export type MediaConversionOperation = "grayscale" | "edge-canny" | "lineart" | "depth" | "pose" | "cutout";
|
|
export type MediaConversionStatus = "idle" | "processing" | "completed" | "stale" | "skipped" | "unavailable" | "error";
|
|
export type MediaConversionOutputKind = "image" | "video";
|
|
|
|
export type MediaConversionNodeState = {
|
|
schemaVersion: typeof MEDIA_CONVERSION_SCHEMA_VERSION;
|
|
operation: MediaConversionOperation;
|
|
status: MediaConversionStatus;
|
|
sourceNodeId?: string;
|
|
sourceFingerprint?: string;
|
|
outputKind?: MediaConversionOutputKind;
|
|
resultStorageKey?: string;
|
|
resultWidth?: number;
|
|
resultHeight?: number;
|
|
detectedPeople?: number;
|
|
errorCode?: string;
|
|
errorMessage?: string;
|
|
startedAt?: string;
|
|
updatedAt?: string;
|
|
};
|
|
|
|
export const MEDIA_CONVERSION_OPERATION_LABELS: Record<MediaConversionOperation, string> = {
|
|
grayscale: "灰度图",
|
|
"edge-canny": "Canny 边缘",
|
|
lineart: "AI 线稿",
|
|
depth: "深度图",
|
|
pose: "姿态骨架",
|
|
cutout: "透明抠图",
|
|
};
|
|
|
|
export const MEDIA_CONVERSION_OPERATION_DESCRIPTIONS: Record<MediaConversionOperation, string> = {
|
|
grayscale: "普通图像算法,适合先检查明暗层次",
|
|
"edge-canny": "普通图像算法,提取清晰轮廓",
|
|
lineart: "本地 ControlNet Aux 线稿预处理,首次运行需要加载模型",
|
|
depth: "本地 Depth Anything V2 Small,首次运行需要加载模型",
|
|
pose: "本地 OpenPose 人体骨架预处理,未检测到人物时会跳过",
|
|
cutout: "需要安装并验证本地抠图模型",
|
|
};
|
|
|
|
export function createDefaultMediaConversionState(): MediaConversionNodeState {
|
|
return {
|
|
schemaVersion: MEDIA_CONVERSION_SCHEMA_VERSION,
|
|
operation: "edge-canny",
|
|
status: "idle",
|
|
};
|
|
}
|
|
|
|
export function mediaConversionSourceFingerprint(node: CanvasNodeData) {
|
|
const metadata = node.metadata;
|
|
const source = [
|
|
node.id,
|
|
metadata?.storageKey || "",
|
|
compactSource(metadata?.content || metadata?.previewContent || ""),
|
|
metadata?.mimeType || "",
|
|
metadata?.bytes || "",
|
|
metadata?.naturalWidth || "",
|
|
metadata?.naturalHeight || "",
|
|
metadata?.durationMs || "",
|
|
].join("|");
|
|
let hash = 2166136261;
|
|
for (let index = 0; index < source.length; index += 1) {
|
|
hash ^= source.charCodeAt(index);
|
|
hash = Math.imul(hash, 16777619);
|
|
}
|
|
return `v1-${(hash >>> 0).toString(16).padStart(8, "0")}`;
|
|
}
|
|
|
|
export function mediaConversionOperationLabel(operation: MediaConversionOperation) {
|
|
return MEDIA_CONVERSION_OPERATION_LABELS[operation];
|
|
}
|
|
|
|
export function mediaConversionOperationDescription(operation: MediaConversionOperation) {
|
|
return MEDIA_CONVERSION_OPERATION_DESCRIPTIONS[operation];
|
|
}
|
|
|
|
export function isLocalImageOperation(operation: MediaConversionOperation) {
|
|
return operation === "grayscale" || operation === "edge-canny";
|
|
}
|
|
|
|
function compactSource(value: string) {
|
|
if (value.length <= 2048) return value;
|
|
return `${value.slice(0, 1024)}|${value.slice(-1024)}|${value.length}`;
|
|
}
|