fix: precompile PSD template render segments
This commit is contained in:
parent
720652b68a
commit
0a001fc5f4
@ -18,6 +18,7 @@ import { normalizeAgentPsdTemplateCategoryTags } from "@/lib/agents/psd-template
|
|||||||
import { resolveAgentPsdLocalCachePath } from "@/lib/agents/psd-local-cache";
|
import { resolveAgentPsdLocalCachePath } from "@/lib/agents/psd-local-cache";
|
||||||
import {
|
import {
|
||||||
preparePsdTemplateRenderAssets,
|
preparePsdTemplateRenderAssets,
|
||||||
|
uploadPreparedPsdTemplateRenderSegments,
|
||||||
} from "@/lib/agents/psd-template-parser";
|
} from "@/lib/agents/psd-template-parser";
|
||||||
import { readAgentPsdFonts } from "@/lib/agents/psd-font-settings";
|
import { readAgentPsdFonts } from "@/lib/agents/psd-font-settings";
|
||||||
import { repairAgentPsdTemplateAssetsIfNeeded } from "@/lib/agents/psd-template-repair";
|
import { repairAgentPsdTemplateAssetsIfNeeded } from "@/lib/agents/psd-template-repair";
|
||||||
@ -222,6 +223,11 @@ export async function PUT(
|
|||||||
preparedAssets.renderBaseBuffer.byteOffset + preparedAssets.renderBaseBuffer.byteLength
|
preparedAssets.renderBaseBuffer.byteOffset + preparedAssets.renderBaseBuffer.byteLength
|
||||||
) as ArrayBuffer
|
) as ArrayBuffer
|
||||||
);
|
);
|
||||||
|
const uploadedRenderSegments = await uploadPreparedPsdTemplateRenderSegments({
|
||||||
|
templateId: currentTemplate.id,
|
||||||
|
segments: preparedAssets.renderSegments,
|
||||||
|
uploadAsset,
|
||||||
|
});
|
||||||
|
|
||||||
const updatedTemplate = {
|
const updatedTemplate = {
|
||||||
...currentTemplate,
|
...currentTemplate,
|
||||||
@ -234,6 +240,7 @@ export async function PUT(
|
|||||||
status: nextStatus,
|
status: nextStatus,
|
||||||
renderBaseUrl: uploadedRenderBase.url,
|
renderBaseUrl: uploadedRenderBase.url,
|
||||||
renderBasePath: uploadedRenderBase.pathname,
|
renderBasePath: uploadedRenderBase.pathname,
|
||||||
|
renderSegments: uploadedRenderSegments,
|
||||||
layers: nextLayers,
|
layers: nextLayers,
|
||||||
layerCount: nextLayers.length,
|
layerCount: nextLayers.length,
|
||||||
assetVersion: AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
assetVersion: AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
||||||
|
|||||||
@ -10,7 +10,10 @@ import {
|
|||||||
type AgentPsdTemplateLayerSlot,
|
type AgentPsdTemplateLayerSlot,
|
||||||
} from "@/lib/agents/psd-template-config";
|
} from "@/lib/agents/psd-template-config";
|
||||||
import { resolveAgentPsdLocalCachePath } from "@/lib/agents/psd-local-cache";
|
import { resolveAgentPsdLocalCachePath } from "@/lib/agents/psd-local-cache";
|
||||||
import { preparePsdTemplateRenderAssets } from "@/lib/agents/psd-template-parser";
|
import {
|
||||||
|
preparePsdTemplateRenderAssets,
|
||||||
|
uploadPreparedPsdTemplateRenderSegments,
|
||||||
|
} from "@/lib/agents/psd-template-parser";
|
||||||
import { readAgentPsdTemplates } from "@/lib/agents/psd-template-settings";
|
import { readAgentPsdTemplates } from "@/lib/agents/psd-template-settings";
|
||||||
import { createStorageUploadSession } from "@/lib/storage/service";
|
import { createStorageUploadSession } from "@/lib/storage/service";
|
||||||
|
|
||||||
@ -181,7 +184,7 @@ async function backfillTemplateAssetsOnce(params: {
|
|||||||
...currentTemplate,
|
...currentTemplate,
|
||||||
parseStatus: "processing",
|
parseStatus: "processing",
|
||||||
parseProgress: Math.max(88, Number(currentTemplate.parseProgress || 0)),
|
parseProgress: Math.max(88, Number(currentTemplate.parseProgress || 0)),
|
||||||
parseStage: "正在后台生成模板底图",
|
parseStage: "正在后台生成模板底图和快速导出分层",
|
||||||
parseError: undefined,
|
parseError: undefined,
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
}),
|
}),
|
||||||
@ -191,6 +194,11 @@ async function backfillTemplateAssetsOnce(params: {
|
|||||||
`agent-psd-base-${template.id}-${Date.now()}.png`,
|
`agent-psd-base-${template.id}-${Date.now()}.png`,
|
||||||
toArrayBuffer(preparedAssets.renderBaseBuffer)
|
toArrayBuffer(preparedAssets.renderBaseBuffer)
|
||||||
);
|
);
|
||||||
|
const uploadedRenderSegments = await uploadPreparedPsdTemplateRenderSegments({
|
||||||
|
templateId: template.id,
|
||||||
|
segments: preparedAssets.renderSegments,
|
||||||
|
uploadAsset,
|
||||||
|
});
|
||||||
|
|
||||||
await updateTemplateById({
|
await updateTemplateById({
|
||||||
templateId: template.id,
|
templateId: template.id,
|
||||||
@ -205,6 +213,7 @@ async function backfillTemplateAssetsOnce(params: {
|
|||||||
...currentTemplate,
|
...currentTemplate,
|
||||||
renderBaseUrl: uploadedRenderBase.url,
|
renderBaseUrl: uploadedRenderBase.url,
|
||||||
renderBasePath: uploadedRenderBase.pathname,
|
renderBasePath: uploadedRenderBase.pathname,
|
||||||
|
renderSegments: uploadedRenderSegments,
|
||||||
layers: mergedLayers,
|
layers: mergedLayers,
|
||||||
layerCount: mergedLayers.length,
|
layerCount: mergedLayers.length,
|
||||||
assetVersion: AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
assetVersion: AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import type { AgentPsdTemplateCategoryTagConfig } from "@/lib/agents/psd-templat
|
|||||||
import { normalizeAgentPsdTemplateCategoryTags } from "@/lib/agents/psd-template-category-tag-config";
|
import { normalizeAgentPsdTemplateCategoryTags } from "@/lib/agents/psd-template-category-tag-config";
|
||||||
|
|
||||||
export const AGENT_PSD_TEMPLATES_KEY = "agent_psd_templates";
|
export const AGENT_PSD_TEMPLATES_KEY = "agent_psd_templates";
|
||||||
export const AGENT_PSD_TEMPLATE_ASSET_VERSION = 6;
|
export const AGENT_PSD_TEMPLATE_ASSET_VERSION = 7;
|
||||||
|
|
||||||
export type AgentPsdTemplateStatus = "active" | "disabled";
|
export type AgentPsdTemplateStatus = "active" | "disabled";
|
||||||
export type AgentPsdTemplateParseStatus =
|
export type AgentPsdTemplateParseStatus =
|
||||||
@ -83,6 +83,13 @@ export type AgentPsdTemplateLayerSlot =
|
|||||||
| AgentPsdTemplateImageLayerSlot
|
| AgentPsdTemplateImageLayerSlot
|
||||||
| AgentPsdTemplateTextLayerSlot;
|
| AgentPsdTemplateTextLayerSlot;
|
||||||
|
|
||||||
|
export interface AgentPsdTemplateRenderSegment {
|
||||||
|
id: string;
|
||||||
|
beforeLayerId?: string;
|
||||||
|
url: string;
|
||||||
|
path?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AgentPsdTemplate {
|
export interface AgentPsdTemplate {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -103,6 +110,7 @@ export interface AgentPsdTemplate {
|
|||||||
previewPath?: string;
|
previewPath?: string;
|
||||||
renderBaseUrl?: string;
|
renderBaseUrl?: string;
|
||||||
renderBasePath?: string;
|
renderBasePath?: string;
|
||||||
|
renderSegments?: AgentPsdTemplateRenderSegment[];
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
layerCount: number;
|
layerCount: number;
|
||||||
@ -443,6 +451,36 @@ export function parseAgentPsdTemplateLayers(value: unknown) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseAgentPsdTemplateRenderSegments(value: unknown) {
|
||||||
|
if (!Array.isArray(value)) return [] as AgentPsdTemplateRenderSegment[];
|
||||||
|
|
||||||
|
const segments: AgentPsdTemplateRenderSegment[] = [];
|
||||||
|
const seenIds = new Set<string>();
|
||||||
|
|
||||||
|
for (const entry of value) {
|
||||||
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = entry as Record<string, unknown>;
|
||||||
|
const id = normalizeText(record.id);
|
||||||
|
const url = normalizeUrl(record.url);
|
||||||
|
if (!id || !url || seenIds.has(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
seenIds.add(id);
|
||||||
|
segments.push({
|
||||||
|
id,
|
||||||
|
beforeLayerId: normalizeText(record.beforeLayerId) || undefined,
|
||||||
|
url,
|
||||||
|
path: normalizeText(record.path) || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
export function parseAgentPsdTemplates(value: unknown) {
|
export function parseAgentPsdTemplates(value: unknown) {
|
||||||
if (!Array.isArray(value)) return [] as AgentPsdTemplate[];
|
if (!Array.isArray(value)) return [] as AgentPsdTemplate[];
|
||||||
|
|
||||||
@ -512,6 +550,7 @@ export function parseAgentPsdTemplates(value: unknown) {
|
|||||||
renderBaseUrl:
|
renderBaseUrl:
|
||||||
normalizeOptionalUrl(record.renderBaseUrl) || normalizeOptionalUrl(record.previewUrl),
|
normalizeOptionalUrl(record.renderBaseUrl) || normalizeOptionalUrl(record.previewUrl),
|
||||||
renderBasePath: normalizeText(record.renderBasePath) || undefined,
|
renderBasePath: normalizeText(record.renderBasePath) || undefined,
|
||||||
|
renderSegments: parseAgentPsdTemplateRenderSegments(record.renderSegments),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
layerCount,
|
layerCount,
|
||||||
@ -547,6 +586,14 @@ export function hasAgentPsdTemplateStoredLayerSource(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasAgentPsdTemplateRenderSegments(
|
||||||
|
template: Pick<AgentPsdTemplate, "renderSegments">
|
||||||
|
) {
|
||||||
|
return Boolean(
|
||||||
|
template.renderSegments?.some((segment) => String(segment.url || "").trim())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function hasAgentPsdTemplateDedicatedRenderBase(
|
export function hasAgentPsdTemplateDedicatedRenderBase(
|
||||||
template: Pick<AgentPsdTemplate, "renderBaseUrl" | "renderBasePath" | "previewUrl">
|
template: Pick<AgentPsdTemplate, "renderBaseUrl" | "renderBasePath" | "previewUrl">
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
isAgentPsdTextLayerSlot,
|
isAgentPsdTextLayerSlot,
|
||||||
type AgentPsdTemplateImageContentBounds,
|
type AgentPsdTemplateImageContentBounds,
|
||||||
type AgentPsdTemplateLayerSlot,
|
type AgentPsdTemplateLayerSlot,
|
||||||
|
type AgentPsdTemplateRenderSegment,
|
||||||
} from "@/lib/agents/psd-template-config";
|
} from "@/lib/agents/psd-template-config";
|
||||||
|
|
||||||
const DEFAULT_PSD_TEMPLATE_ASSET_CONCURRENCY = 4;
|
const DEFAULT_PSD_TEMPLATE_ASSET_CONCURRENCY = 4;
|
||||||
@ -119,6 +120,12 @@ export interface PsdTemplateSourceLayerEntry {
|
|||||||
composite: () => Promise<Uint8ClampedArray>;
|
composite: () => Promise<Uint8ClampedArray>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PreparedPsdTemplateRenderSegment {
|
||||||
|
id: string;
|
||||||
|
beforeLayerId?: string;
|
||||||
|
buffer: Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveVisibleContentBounds(params: {
|
function resolveVisibleContentBounds(params: {
|
||||||
rgba: Uint8ClampedArray;
|
rgba: Uint8ClampedArray;
|
||||||
width: number;
|
width: number;
|
||||||
@ -666,6 +673,35 @@ function toArrayBuffer(buffer: Buffer) {
|
|||||||
) as ArrayBuffer;
|
) as ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function uploadPreparedPsdTemplateRenderSegments(params: {
|
||||||
|
templateId: string;
|
||||||
|
segments: PreparedPsdTemplateRenderSegment[];
|
||||||
|
uploadAsset: StorageUploadFunction;
|
||||||
|
}) {
|
||||||
|
const uploadedSegments = await mapWithConcurrency(
|
||||||
|
params.segments,
|
||||||
|
DEFAULT_PSD_TEMPLATE_ASSET_CONCURRENCY,
|
||||||
|
async (segment) => {
|
||||||
|
const uploaded = await params.uploadAsset(
|
||||||
|
`agent-psd-segment-${params.templateId}-${sanitizeFileSegment(
|
||||||
|
segment.id,
|
||||||
|
"segment"
|
||||||
|
)}-${Date.now()}.png`,
|
||||||
|
toArrayBuffer(segment.buffer)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: segment.id,
|
||||||
|
beforeLayerId: segment.beforeLayerId,
|
||||||
|
url: uploaded.url,
|
||||||
|
path: uploaded.pathname,
|
||||||
|
} satisfies AgentPsdTemplateRenderSegment;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return uploadedSegments;
|
||||||
|
}
|
||||||
|
|
||||||
async function mapWithConcurrency<T, R>(
|
async function mapWithConcurrency<T, R>(
|
||||||
items: T[],
|
items: T[],
|
||||||
concurrency: number,
|
concurrency: number,
|
||||||
@ -727,8 +763,13 @@ export async function preparePsdTemplateRenderAssets(params: {
|
|||||||
fileBuffer: params.fileBuffer,
|
fileBuffer: params.fileBuffer,
|
||||||
});
|
});
|
||||||
const psd = await parsePsdBuffer(psdBuffer);
|
const psd = await parsePsdBuffer(psdBuffer);
|
||||||
|
const canvasWidth = Math.max(1, Math.round(Number(psd.width || 0)));
|
||||||
|
const canvasHeight = Math.max(1, Math.round(Number(psd.height || 0)));
|
||||||
const layerEntries = collectPsdLayerEntries(Array.isArray(psd.children) ? psd.children : []);
|
const layerEntries = collectPsdLayerEntries(Array.isArray(psd.children) ? psd.children : []);
|
||||||
const nodeMap = new Map(layerEntries.map((entry) => [entry.layer.layerId, entry]));
|
const nodeMap = new Map(layerEntries.map((entry) => [entry.layer.layerId, entry]));
|
||||||
|
const layerSlotByPsdLayerId = new Map(
|
||||||
|
params.layers.map((layer) => [layer.layerId, layer])
|
||||||
|
);
|
||||||
const editableLayerIdSet = new Set(
|
const editableLayerIdSet = new Set(
|
||||||
params.layers
|
params.layers
|
||||||
.filter(isAgentPsdTemplateLayerUserEditable)
|
.filter(isAgentPsdTemplateLayerUserEditable)
|
||||||
@ -900,19 +941,28 @@ export async function preparePsdTemplateRenderAssets(params: {
|
|||||||
nextLayers[index] = layer;
|
nextLayers[index] = layer;
|
||||||
});
|
});
|
||||||
|
|
||||||
const baseEntries = [...layerEntries]
|
const drawableEntries = [...layerEntries]
|
||||||
.reverse()
|
.reverse()
|
||||||
.filter(
|
.filter((entry) => entry.node && typeof entry.node.composite === "function");
|
||||||
(entry) =>
|
|
||||||
!editableLayerIdSet.has(entry.layer.layerId) &&
|
|
||||||
entry.node &&
|
|
||||||
typeof entry.node.composite === "function"
|
|
||||||
);
|
|
||||||
|
|
||||||
const baseCompositeResults = await mapWithConcurrency(
|
const layerCompositeResults = await mapWithConcurrency(
|
||||||
baseEntries,
|
drawableEntries,
|
||||||
assetConcurrency,
|
assetConcurrency,
|
||||||
async (entry, index) => {
|
async (entry, index) => {
|
||||||
|
const layerSlot = layerSlotByPsdLayerId.get(entry.layer.layerId);
|
||||||
|
const isEditableBoundary = layerSlot
|
||||||
|
? isAgentPsdTemplateLayerUserEditable(layerSlot)
|
||||||
|
: editableLayerIdSet.has(entry.layer.layerId);
|
||||||
|
|
||||||
|
if (isEditableBoundary) {
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
input: null,
|
||||||
|
beforeLayerId: layerSlot?.id || "",
|
||||||
|
editableBoundary: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const rgba = await entry.node.composite!();
|
const rgba = await entry.node.composite!();
|
||||||
const overlayBuffer = await rgbaToPngBuffer(
|
const overlayBuffer = await rgbaToPngBuffer(
|
||||||
@ -926,8 +976,8 @@ export async function preparePsdTemplateRenderAssets(params: {
|
|||||||
top: entry.layer.y,
|
top: entry.layer.y,
|
||||||
width: entry.layer.width,
|
width: entry.layer.width,
|
||||||
height: entry.layer.height,
|
height: entry.layer.height,
|
||||||
canvasWidth: Math.max(1, Math.round(Number(psd.width || 0))),
|
canvasWidth,
|
||||||
canvasHeight: Math.max(1, Math.round(Number(psd.height || 0))),
|
canvasHeight,
|
||||||
});
|
});
|
||||||
if (!fittedOverlay) {
|
if (!fittedOverlay) {
|
||||||
return null;
|
return null;
|
||||||
@ -936,6 +986,8 @@ export async function preparePsdTemplateRenderAssets(params: {
|
|||||||
return {
|
return {
|
||||||
index,
|
index,
|
||||||
input: fittedOverlay,
|
input: fittedOverlay,
|
||||||
|
beforeLayerId: "",
|
||||||
|
editableBoundary: false,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("[PSD Template] Failed to compose base layer:", {
|
console.warn("[PSD Template] Failed to compose base layer:", {
|
||||||
@ -947,27 +999,77 @@ export async function preparePsdTemplateRenderAssets(params: {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseCompositeInputs = baseCompositeResults
|
const sortedLayerCompositeResults = layerCompositeResults
|
||||||
|
.filter(
|
||||||
|
(
|
||||||
|
result
|
||||||
|
): result is {
|
||||||
|
index: number;
|
||||||
|
input: { input: Buffer; left: number; top: number } | null;
|
||||||
|
beforeLayerId: string;
|
||||||
|
editableBoundary: boolean;
|
||||||
|
} => Boolean(result)
|
||||||
|
)
|
||||||
|
.sort((left, right) => left.index - right.index);
|
||||||
|
const baseCompositeInputs = sortedLayerCompositeResults
|
||||||
.filter(
|
.filter(
|
||||||
(
|
(
|
||||||
result
|
result
|
||||||
): result is {
|
): result is {
|
||||||
index: number;
|
index: number;
|
||||||
input: { input: Buffer; left: number; top: number };
|
input: { input: Buffer; left: number; top: number };
|
||||||
} => Boolean(result)
|
beforeLayerId: string;
|
||||||
|
editableBoundary: false;
|
||||||
|
} => Boolean(result.input) && !result.editableBoundary
|
||||||
)
|
)
|
||||||
.sort((left, right) => left.index - right.index)
|
|
||||||
.map((result) => result.input);
|
.map((result) => result.input);
|
||||||
|
|
||||||
const sharp = await getSharp();
|
const sharp = await getSharp();
|
||||||
const transparentBaseBuffer = await createTransparentCanvasBuffer(psd.width, psd.height);
|
const transparentBaseBuffer = await createTransparentCanvasBuffer(
|
||||||
|
canvasWidth,
|
||||||
|
canvasHeight
|
||||||
|
);
|
||||||
|
const composeSegmentBuffer = (
|
||||||
|
inputs: Array<{ input: Buffer; left: number; top: number }>
|
||||||
|
) => sharp(transparentBaseBuffer).composite(inputs).png().toBuffer();
|
||||||
const renderBaseBuffer = await sharp(transparentBaseBuffer)
|
const renderBaseBuffer = await sharp(transparentBaseBuffer)
|
||||||
.composite(baseCompositeInputs)
|
.composite(baseCompositeInputs)
|
||||||
.png()
|
.png()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
const renderSegments: PreparedPsdTemplateRenderSegment[] = [];
|
||||||
|
let pendingSegmentInputs: Array<{ input: Buffer; left: number; top: number }> = [];
|
||||||
|
|
||||||
|
for (const result of sortedLayerCompositeResults) {
|
||||||
|
if (result.editableBoundary) {
|
||||||
|
if (pendingSegmentInputs.length > 0) {
|
||||||
|
renderSegments.push({
|
||||||
|
id: `before-${sanitizeFileSegment(
|
||||||
|
result.beforeLayerId,
|
||||||
|
`layer-${result.index}`
|
||||||
|
)}`,
|
||||||
|
beforeLayerId: result.beforeLayerId,
|
||||||
|
buffer: await composeSegmentBuffer(pendingSegmentInputs),
|
||||||
|
});
|
||||||
|
pendingSegmentInputs = [];
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.input) {
|
||||||
|
pendingSegmentInputs.push(result.input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingSegmentInputs.length > 0) {
|
||||||
|
renderSegments.push({
|
||||||
|
id: "above-editable-layers",
|
||||||
|
buffer: await composeSegmentBuffer(pendingSegmentInputs),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
layers: nextLayers,
|
layers: nextLayers,
|
||||||
renderBaseBuffer,
|
renderBaseBuffer,
|
||||||
|
renderSegments,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,11 @@ import "server-only";
|
|||||||
|
|
||||||
import { uploadFile } from "@/lib/storage/service";
|
import { uploadFile } from "@/lib/storage/service";
|
||||||
import { fetchRemoteAssetBuffer } from "@/lib/agents/psd-remote-asset";
|
import { fetchRemoteAssetBuffer } from "@/lib/agents/psd-remote-asset";
|
||||||
|
import { loadPsdTemplateSource } from "@/lib/agents/psd-template-parser";
|
||||||
import {
|
import {
|
||||||
hasAgentPsdTemplateStoredLayerSource,
|
hasAgentPsdTemplateStoredLayerSource,
|
||||||
isAgentPsdImageLayerSlot,
|
isAgentPsdImageLayerSlot,
|
||||||
|
isAgentPsdLayerSuperLocked,
|
||||||
isAgentPsdTemplateLayerUserEditable,
|
isAgentPsdTemplateLayerUserEditable,
|
||||||
isAgentPsdTextLayerSlot,
|
isAgentPsdTextLayerSlot,
|
||||||
resolveAgentPsdTextLayerEffectiveAlign,
|
resolveAgentPsdTextLayerEffectiveAlign,
|
||||||
@ -181,8 +183,7 @@ function getDefaultSourceImageLayer(
|
|||||||
) {
|
) {
|
||||||
const editableImageLayers = layers.filter(
|
const editableImageLayers = layers.filter(
|
||||||
(layer): layer is AgentPsdTemplateImageLayerSlot =>
|
(layer): layer is AgentPsdTemplateImageLayerSlot =>
|
||||||
isAgentPsdTemplateLayerUserEditable(layer) &&
|
layer.isEditable && isAgentPsdImageLayerSlot(layer)
|
||||||
isAgentPsdImageLayerSlot(layer)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -462,18 +463,68 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
const hasDedicatedRenderBase =
|
const hasDedicatedRenderBase =
|
||||||
Boolean(renderBaseUrl) &&
|
Boolean(renderBaseUrl) &&
|
||||||
renderBaseUrl !== String(template.previewUrl || "").trim();
|
renderBaseUrl !== String(template.previewUrl || "").trim();
|
||||||
|
const templateLayerOrderIds = template.layers.map((layer) => layer.id);
|
||||||
|
const isUsingTemplateLayerOrder =
|
||||||
|
orderedTemplateLayers.length === templateLayerOrderIds.length &&
|
||||||
|
orderedTemplateLayers.every(
|
||||||
|
(layer, index) => layer.id === templateLayerOrderIds[index]
|
||||||
|
);
|
||||||
const defaultSourceImageLayerId =
|
const defaultSourceImageLayerId =
|
||||||
getDefaultSourceImageLayer(orderedTemplateLayers)?.id || "";
|
getDefaultSourceImageLayer(orderedTemplateLayers)?.id || "";
|
||||||
|
const hasImageAssignment = (layer: AgentPsdTemplate["layers"][number]) =>
|
||||||
|
isAgentPsdImageLayerSlot(layer) &&
|
||||||
|
Boolean(String(imageAssignments[layer.id] || "").trim());
|
||||||
|
const shouldRenderAsStaticLayer = (
|
||||||
|
layer: AgentPsdTemplate["layers"][number]
|
||||||
|
) => !isAgentPsdTemplateLayerUserEditable(layer) && !hasImageAssignment(layer);
|
||||||
const hasHiddenStaticLayer = orderedTemplateLayers.some(
|
const hasHiddenStaticLayer = orderedTemplateLayers.some(
|
||||||
(layer) =>
|
(layer) =>
|
||||||
!isAgentPsdTemplateLayerUserEditable(layer) &&
|
shouldRenderAsStaticLayer(layer) &&
|
||||||
hiddenLayerIdSet.has(layer.id)
|
hiddenLayerIdSet.has(layer.id)
|
||||||
);
|
);
|
||||||
|
const hasVisibleAssignedImageLayer = orderedTemplateLayers.some(
|
||||||
|
(layer) =>
|
||||||
|
!hiddenLayerIdSet.has(layer.id) && hasImageAssignment(layer)
|
||||||
|
);
|
||||||
|
const hasStaticLayerPositionOffset = orderedTemplateLayers.some((layer) => {
|
||||||
|
if (
|
||||||
|
isAgentPsdTemplateLayerUserEditable(layer) ||
|
||||||
|
isAgentPsdLayerSuperLocked(layer)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const offset = normalizeTextPositionOffset(layerPositionOffsets[layer.id]);
|
||||||
|
return Math.abs(offset.offsetX) > 0.01 || Math.abs(offset.offsetY) > 0.01;
|
||||||
|
});
|
||||||
|
const renderSegments = (template.renderSegments || []).filter((segment) =>
|
||||||
|
Boolean(String(segment.url || "").trim())
|
||||||
|
);
|
||||||
|
const shouldUseRenderSegments =
|
||||||
|
hasVisibleAssignedImageLayer &&
|
||||||
|
renderSegments.length > 0 &&
|
||||||
|
isUsingTemplateLayerOrder &&
|
||||||
|
!hasHiddenStaticLayer &&
|
||||||
|
!hasStaticLayerPositionOffset;
|
||||||
const shouldUseRenderBaseFallback =
|
const shouldUseRenderBaseFallback =
|
||||||
hasDedicatedRenderBase &&
|
hasDedicatedRenderBase &&
|
||||||
!hasHiddenStaticLayer;
|
!hasHiddenStaticLayer &&
|
||||||
|
!hasVisibleAssignedImageLayer;
|
||||||
|
const shouldHydrateLayerSourcesFromPsd =
|
||||||
|
hasVisibleAssignedImageLayer && !shouldUseRenderSegments;
|
||||||
|
const renderSegmentByBeforeLayerId = new Map(
|
||||||
|
renderSegments
|
||||||
|
.filter((segment) => String(segment.beforeLayerId || "").trim())
|
||||||
|
.map((segment) => [String(segment.beforeLayerId || "").trim(), segment])
|
||||||
|
);
|
||||||
|
const topRenderSegments = renderSegments.filter(
|
||||||
|
(segment) => !String(segment.beforeLayerId || "").trim()
|
||||||
|
);
|
||||||
|
|
||||||
function getLayerPositionOffset(layerId: string) {
|
function getLayerPositionOffset(layerId: string) {
|
||||||
|
const layer = layerMap.get(layerId);
|
||||||
|
if (layer && isAgentPsdLayerSuperLocked(layer)) {
|
||||||
|
return normalizeTextPositionOffset();
|
||||||
|
}
|
||||||
return normalizeTextPositionOffset(layerPositionOffsets[layerId]);
|
return normalizeTextPositionOffset(layerPositionOffsets[layerId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,17 +567,22 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
if (shouldUseRenderBaseFallback) {
|
if (shouldUseRenderBaseFallback) {
|
||||||
pushAsset(renderBaseUrl, "PSD 模板底图");
|
pushAsset(renderBaseUrl, "PSD 模板底图");
|
||||||
}
|
}
|
||||||
|
if (shouldUseRenderSegments) {
|
||||||
|
for (const segment of renderSegments) {
|
||||||
|
pushAsset(segment.url, "PSD 模板快速导出分层");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const layer of orderedTemplateLayers) {
|
for (const layer of orderedTemplateLayers) {
|
||||||
if (hiddenLayerIdSet.has(layer.id)) {
|
if (hiddenLayerIdSet.has(layer.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const storedLayerSourceUrl = getStoredLayerSourceUrl(layer);
|
const storedLayerSourceUrl = getStoredLayerSourceUrl(layer);
|
||||||
if (!isAgentPsdTemplateLayerUserEditable(layer)) {
|
if (shouldRenderAsStaticLayer(layer)) {
|
||||||
if (shouldUseRenderBaseFallback) {
|
if (shouldUseRenderBaseFallback || shouldUseRenderSegments) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pushAsset(
|
pushAsset(
|
||||||
storedLayerSourceUrl,
|
storedLayerSourceUrl,
|
||||||
"PSD 图层素材",
|
"PSD 图层素材",
|
||||||
@ -601,6 +657,111 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
|
|
||||||
const compositeInputs: Array<{ input: Buffer; left: number; top: number }> =
|
const compositeInputs: Array<{ input: Buffer; left: number; top: number }> =
|
||||||
[];
|
[];
|
||||||
|
let psdSourceLayerEntryMapPromise: Promise<
|
||||||
|
Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
layer: { width: number; height: number };
|
||||||
|
composite: () => Promise<Uint8ClampedArray>;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
> | null = null;
|
||||||
|
const psdSourceLayerBufferPromiseMap = new Map<string, Promise<Buffer | null>>();
|
||||||
|
|
||||||
|
async function getPsdSourceLayerEntryMap() {
|
||||||
|
if (!shouldHydrateLayerSourcesFromPsd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!psdSourceLayerEntryMapPromise) {
|
||||||
|
psdSourceLayerEntryMapPromise = loadPsdTemplateSource({
|
||||||
|
fileUrl: template.fileUrl,
|
||||||
|
filePath: template.filePath,
|
||||||
|
}).then((source) => {
|
||||||
|
const entryMap = new Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
layer: { width: number; height: number };
|
||||||
|
composite: () => Promise<Uint8ClampedArray>;
|
||||||
|
}
|
||||||
|
>();
|
||||||
|
for (const entry of source.layers) {
|
||||||
|
entryMap.set(entry.layer.layerId, {
|
||||||
|
layer: {
|
||||||
|
width: entry.layer.width,
|
||||||
|
height: entry.layer.height,
|
||||||
|
},
|
||||||
|
composite: entry.composite,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return entryMap;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return psdSourceLayerEntryMapPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readPsdSourceLayerBuffer(
|
||||||
|
layer: AgentPsdTemplate["layers"][number]
|
||||||
|
) {
|
||||||
|
if (!shouldHydrateLayerSourcesFromPsd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!psdSourceLayerBufferPromiseMap.has(layer.layerId)) {
|
||||||
|
psdSourceLayerBufferPromiseMap.set(
|
||||||
|
layer.layerId,
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const entryMap = await getPsdSourceLayerEntryMap();
|
||||||
|
const entry = entryMap?.get(layer.layerId);
|
||||||
|
if (!entry) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rgba = await entry.composite();
|
||||||
|
return sharp(Buffer.from(rgba), {
|
||||||
|
raw: {
|
||||||
|
width: Math.max(1, Math.round(entry.layer.width || layer.width)),
|
||||||
|
height: Math.max(1, Math.round(entry.layer.height || layer.height)),
|
||||||
|
channels: 4,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.png(PSD_RENDER_PNG_OPTIONS)
|
||||||
|
.toBuffer();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[PSD Template] failed to hydrate layer from PSD source:", {
|
||||||
|
templateId: template.id,
|
||||||
|
layerId: layer.layerId,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return psdSourceLayerBufferPromiseMap.get(layer.layerId)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readReusableLayerBuffer(
|
||||||
|
layer: AgentPsdTemplate["layers"][number]
|
||||||
|
) {
|
||||||
|
const sourceUrl = getStoredLayerSourceUrl(layer);
|
||||||
|
if (sourceUrl) {
|
||||||
|
try {
|
||||||
|
return await readCachedBuffer(sourceUrl, "PSD 图层素材");
|
||||||
|
} catch (error) {
|
||||||
|
const hydratedBuffer = await readPsdSourceLayerBuffer(layer);
|
||||||
|
if (hydratedBuffer) {
|
||||||
|
return hydratedBuffer;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return readPsdSourceLayerBuffer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
async function appendBufferOverlay(params: {
|
async function appendBufferOverlay(params: {
|
||||||
input: Buffer;
|
input: Buffer;
|
||||||
@ -631,10 +792,19 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
options: { optional?: boolean } = {}
|
options: { optional?: boolean } = {}
|
||||||
) {
|
) {
|
||||||
const sourceUrl = getStoredLayerSourceUrl(layer);
|
const sourceUrl = getStoredLayerSourceUrl(layer);
|
||||||
if (
|
if ((!hasAgentPsdTemplateStoredLayerSource(layer) && !sourceUrl) || !sourceUrl) {
|
||||||
(!hasAgentPsdTemplateStoredLayerSource(layer) && !sourceUrl) ||
|
const hydratedBuffer = await readPsdSourceLayerBuffer(layer);
|
||||||
!sourceUrl
|
if (hydratedBuffer) {
|
||||||
) {
|
const layerPositionOffset = getLayerPositionOffset(layer.id);
|
||||||
|
return appendBufferOverlay({
|
||||||
|
input: hydratedBuffer,
|
||||||
|
left: layer.x + layerPositionOffset.offsetX,
|
||||||
|
top: layer.y + layerPositionOffset.offsetY,
|
||||||
|
width: layer.width,
|
||||||
|
height: layer.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (options.optional) {
|
if (options.optional) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -645,7 +815,13 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
|
|
||||||
const layerPositionOffset = getLayerPositionOffset(layer.id);
|
const layerPositionOffset = getLayerPositionOffset(layer.id);
|
||||||
try {
|
try {
|
||||||
const overlayBuffer = await readCachedBuffer(sourceUrl, "PSD 图层素材");
|
const overlayBuffer = await readReusableLayerBuffer(layer);
|
||||||
|
if (!overlayBuffer) {
|
||||||
|
if (options.optional) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw new Error("Missing PSD layer asset");
|
||||||
|
}
|
||||||
const didAppend = await appendBufferOverlay({
|
const didAppend = await appendBufferOverlay({
|
||||||
input: overlayBuffer,
|
input: overlayBuffer,
|
||||||
left: layer.x + layerPositionOffset.offsetX,
|
left: layer.x + layerPositionOffset.offsetX,
|
||||||
@ -693,6 +869,42 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const appendedRenderSegmentIds = new Set<string>();
|
||||||
|
async function appendRenderSegmentOverlay(
|
||||||
|
segment: (typeof renderSegments)[number] | undefined
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!shouldUseRenderSegments ||
|
||||||
|
!segment ||
|
||||||
|
appendedRenderSegmentIds.has(segment.id)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendedRenderSegmentIds.add(segment.id);
|
||||||
|
const segmentBuffer = await readCachedBuffer(
|
||||||
|
segment.url,
|
||||||
|
"PSD 模板快速导出分层"
|
||||||
|
);
|
||||||
|
await appendBufferOverlay({
|
||||||
|
input: segmentBuffer,
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: canvasWidth,
|
||||||
|
height: canvasHeight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function appendRenderSegmentBeforeLayer(layerId: string) {
|
||||||
|
await appendRenderSegmentOverlay(renderSegmentByBeforeLayerId.get(layerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function appendTopRenderSegments() {
|
||||||
|
for (const segment of topRenderSegments) {
|
||||||
|
await appendRenderSegmentOverlay(segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
shouldUseRenderBaseFallback &&
|
shouldUseRenderBaseFallback &&
|
||||||
(!defaultSourceImageLayerId || hiddenLayerIdSet.has(defaultSourceImageLayerId))
|
(!defaultSourceImageLayerId || hiddenLayerIdSet.has(defaultSourceImageLayerId))
|
||||||
@ -701,12 +913,16 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const layer of [...orderedTemplateLayers].reverse()) {
|
for (const layer of [...orderedTemplateLayers].reverse()) {
|
||||||
|
if (shouldUseRenderSegments && isAgentPsdTemplateLayerUserEditable(layer)) {
|
||||||
|
await appendRenderSegmentBeforeLayer(layer.id);
|
||||||
|
}
|
||||||
|
|
||||||
if (hiddenLayerIdSet.has(layer.id)) {
|
if (hiddenLayerIdSet.has(layer.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAgentPsdTemplateLayerUserEditable(layer)) {
|
if (!isAgentPsdTemplateLayerUserEditable(layer)) {
|
||||||
if (shouldUseRenderBaseFallback) {
|
if (shouldUseRenderBaseFallback || shouldUseRenderSegments) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await appendStoredLayer(layer, { optional: shouldUseRenderBaseFallback });
|
await appendStoredLayer(layer, { optional: shouldUseRenderBaseFallback });
|
||||||
@ -1001,6 +1217,8 @@ export async function renderAgentPsdTemplate(params: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await appendTopRenderSegments();
|
||||||
|
|
||||||
const transparentCanvas = await sharp({
|
const transparentCanvas = await sharp({
|
||||||
create: {
|
create: {
|
||||||
width: canvasWidth,
|
width: canvasWidth,
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
import { readAgentPsdFonts } from "@/lib/agents/psd-font-settings";
|
import { readAgentPsdFonts } from "@/lib/agents/psd-font-settings";
|
||||||
import {
|
import {
|
||||||
AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
||||||
|
hasAgentPsdTemplateRenderSegments,
|
||||||
isAgentPsdImageLayerSlot,
|
isAgentPsdImageLayerSlot,
|
||||||
isAgentPsdLayerSuperLocked,
|
isAgentPsdLayerSuperLocked,
|
||||||
isAgentPsdTemplateLayerUserEditable,
|
isAgentPsdTemplateLayerUserEditable,
|
||||||
@ -21,6 +22,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
parsePsdTemplateFileFromSource,
|
parsePsdTemplateFileFromSource,
|
||||||
preparePsdTemplateRenderAssets,
|
preparePsdTemplateRenderAssets,
|
||||||
|
uploadPreparedPsdTemplateRenderSegments,
|
||||||
} from "@/lib/agents/psd-template-parser";
|
} from "@/lib/agents/psd-template-parser";
|
||||||
import { resolveAgentPsdLocalCachePath } from "@/lib/agents/psd-local-cache";
|
import { resolveAgentPsdLocalCachePath } from "@/lib/agents/psd-local-cache";
|
||||||
import { createStorageUploadSession } from "@/lib/storage/service";
|
import { createStorageUploadSession } from "@/lib/storage/service";
|
||||||
@ -66,6 +68,10 @@ function hasDedicatedRenderBase(template: AgentPsdTemplate) {
|
|||||||
return Boolean(renderBaseUrl) && renderBaseUrl !== previewUrl;
|
return Boolean(renderBaseUrl) && renderBaseUrl !== previewUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasStaticTemplateLayers(template: AgentPsdTemplate) {
|
||||||
|
return template.layers.some((layer) => !isAgentPsdTemplateLayerUserEditable(layer));
|
||||||
|
}
|
||||||
|
|
||||||
function repairEditableImageLayerSelection(template: AgentPsdTemplate) {
|
function repairEditableImageLayerSelection(template: AgentPsdTemplate) {
|
||||||
const imageLayers = template.layers.filter(isAgentPsdImageLayerSlot);
|
const imageLayers = template.layers.filter(isAgentPsdImageLayerSlot);
|
||||||
const unlockedImageLayers = imageLayers.filter(
|
const unlockedImageLayers = imageLayers.filter(
|
||||||
@ -255,6 +261,8 @@ export async function repairAgentPsdTemplateAssetsIfNeeded(
|
|||||||
Number(template.assetVersion || 0) !== AGENT_PSD_TEMPLATE_ASSET_VERSION;
|
Number(template.assetVersion || 0) !== AGENT_PSD_TEMPLATE_ASSET_VERSION;
|
||||||
const needsRepair = needsAssetVersionRefresh || hasMissingLayerAssets(template);
|
const needsRepair = needsAssetVersionRefresh || hasMissingLayerAssets(template);
|
||||||
const needsRenderBaseRepair = !hasDedicatedRenderBase(template);
|
const needsRenderBaseRepair = !hasDedicatedRenderBase(template);
|
||||||
|
const needsRenderSegmentRepair =
|
||||||
|
hasStaticTemplateLayers(template) && !hasAgentPsdTemplateRenderSegments(template);
|
||||||
const fonts = await readAgentPsdFonts().catch(() => []);
|
const fonts = await readAgentPsdFonts().catch(() => []);
|
||||||
const needsFontDetection = needsTextMetadataRepair(template);
|
const needsFontDetection = needsTextMetadataRepair(template);
|
||||||
|
|
||||||
@ -311,7 +319,12 @@ export async function repairAgentPsdTemplateAssetsIfNeeded(
|
|||||||
(layer, index) => JSON.stringify(layer) !== JSON.stringify(template.layers[index])
|
(layer, index) => JSON.stringify(layer) !== JSON.stringify(template.layers[index])
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!needsRepair && !hasFontRepair && !needsRenderBaseRepair) {
|
if (
|
||||||
|
!needsRepair &&
|
||||||
|
!hasFontRepair &&
|
||||||
|
!needsRenderBaseRepair &&
|
||||||
|
!needsRenderSegmentRepair
|
||||||
|
) {
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,11 +348,20 @@ export async function repairAgentPsdTemplateAssetsIfNeeded(
|
|||||||
toArrayBuffer(preparedAssets.renderBaseBuffer)
|
toArrayBuffer(preparedAssets.renderBaseBuffer)
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
const uploadedRenderSegments =
|
||||||
|
needsRenderSegmentRepair || needsAssetVersionRefresh
|
||||||
|
? await uploadPreparedPsdTemplateRenderSegments({
|
||||||
|
templateId: template.id,
|
||||||
|
segments: preparedAssets.renderSegments,
|
||||||
|
uploadAsset,
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...template,
|
...template,
|
||||||
renderBaseUrl: uploadedRenderBase?.url || template.renderBaseUrl,
|
renderBaseUrl: uploadedRenderBase?.url || template.renderBaseUrl,
|
||||||
renderBasePath: uploadedRenderBase?.pathname || template.renderBasePath,
|
renderBasePath: uploadedRenderBase?.pathname || template.renderBasePath,
|
||||||
|
renderSegments: uploadedRenderSegments || template.renderSegments,
|
||||||
layers: preparedAssets.layers,
|
layers: preparedAssets.layers,
|
||||||
assetVersion: AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
assetVersion: AGENT_PSD_TEMPLATE_ASSET_VERSION,
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export async function tryDeleteAgentPsdTemplateAssets(template: AgentPsdTemplate
|
|||||||
template.filePath,
|
template.filePath,
|
||||||
template.previewPath,
|
template.previewPath,
|
||||||
template.renderBasePath,
|
template.renderBasePath,
|
||||||
|
...(template.renderSegments || []).map((segment) => segment.path),
|
||||||
...template.layers.map((layer) => layer.previewPath),
|
...template.layers.map((layer) => layer.previewPath),
|
||||||
...template.layers
|
...template.layers
|
||||||
.filter(isAgentPsdImageLayerSlot)
|
.filter(isAgentPsdImageLayerSlot)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user