gongxiangp2/lib/white-background-preview.ts
2026-07-31 10:40:00 +08:00

102 lines
3.0 KiB
TypeScript

import { existsSync } from 'fs'
import path from 'path'
import sharp from 'sharp'
import {
buildPublicDisplayUrl,
buildWhiteBackgroundPublicDisplayPathFromAssetPath,
} from '@/lib/public-display'
import { getWhiteBackgroundPreviewRelativePathFromAssetPath } from '@/lib/standard-model-white-background'
import { withImageProcessingSlot } from '@/lib/image-processing-limiter'
import { configureSharpRuntime } from '@/lib/sharp-runtime'
import { STANDARD_MODEL_WHITE_BG_UPLOAD_LIMITS } from '@/lib/standard-model-white-background-upload-guard'
configureSharpRuntime()
export const WHITE_BACKGROUND_PREVIEW_MAX_DIMENSION = 1500
const WHITE_BACKGROUND_ORIGINAL_WEBP_OPTIONS = {
quality: 92,
alphaQuality: 100,
}
const WHITE_BACKGROUND_PREVIEW_WEBP_OPTIONS = {
quality: 82,
alphaQuality: 100,
}
function isHttpUrl(url?: string | null): boolean {
return /^https?:\/\//i.test(String(url || '').trim())
}
function normalizeRootRelativePath(fileUrl: string): string {
const normalized = fileUrl.trim().replace(/\\/g, '/').replace(/^\.?\//, '')
return normalized ? `/${normalized}` : ''
}
function resolveAbsoluteLocalPath(fileUrl: string): string {
const normalized = fileUrl.trim().replace(/\\/g, '/')
if (path.isAbsolute(normalized)) {
return normalized
}
return path.join(process.cwd(), normalized.replace(/^\/+/, ''))
}
export async function buildWhiteBackgroundWebpVariants(buffer: Buffer) {
return withImageProcessingSlot(async () => {
const sharpOptions = {
limitInputPixels: STANDARD_MODEL_WHITE_BG_UPLOAD_LIMITS.maxPixelsPerImage,
sequentialRead: true,
failOn: 'error' as const,
}
const original = await sharp(buffer, sharpOptions)
.rotate()
.webp(WHITE_BACKGROUND_ORIGINAL_WEBP_OPTIONS)
.toBuffer()
const preview = await sharp(buffer, sharpOptions)
.rotate()
.resize({
width: WHITE_BACKGROUND_PREVIEW_MAX_DIMENSION,
height: WHITE_BACKGROUND_PREVIEW_MAX_DIMENSION,
fit: 'inside',
withoutEnlargement: true,
})
.webp(WHITE_BACKGROUND_PREVIEW_WEBP_OPTIONS)
.toBuffer()
return {
original,
preview,
}
})
}
export function resolveWhiteBackgroundPreviewFileUrl(fileUrl?: string | null): string | null {
const normalizedFileUrl = String(fileUrl || '').trim()
if (!normalizedFileUrl) {
return null
}
if (isHttpUrl(normalizedFileUrl)) {
return normalizedFileUrl
}
const previewRelativePath = getWhiteBackgroundPreviewRelativePathFromAssetPath(normalizedFileUrl)
if (previewRelativePath) {
const previewAbsolutePath = resolveAbsoluteLocalPath(previewRelativePath)
if (existsSync(previewAbsolutePath)) {
return normalizeRootRelativePath(previewRelativePath)
}
}
const publicDisplayPath = buildWhiteBackgroundPublicDisplayPathFromAssetPath(normalizedFileUrl)
const publicDisplayUrl = publicDisplayPath ? buildPublicDisplayUrl(publicDisplayPath) : null
if (publicDisplayUrl) {
return publicDisplayUrl
}
return normalizeRootRelativePath(normalizedFileUrl)
}