fix: preserve source aspect for alibaba auto size
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-19 01:08:31 +08:00
parent d6a06e0cd0
commit 5be4d0e9df

View File

@ -120,6 +120,79 @@ async function inferAspectRatioFromImageUrl(imageUrl: string): Promise<number |
}
}
async function inferImageDimensionsFromImageUrl(
imageUrl: string,
): Promise<{ width: number; height: number; aspectRatio: number } | null> {
if (!/^https?:\/\//i.test(imageUrl)) return null;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ALIBABA_IMAGE_FETCH_TIMEOUT_MS);
try {
const response = await fetch(imageUrl, { signal: controller.signal });
if (!response.ok) {
console.warn("[Alibaba Image] Failed to fetch reference image dimensions:", response.status);
return null;
}
const buffer = Buffer.from(await response.arrayBuffer());
if (!buffer.length) return null;
const metadata = await sharp(buffer).metadata();
if (!metadata.width || !metadata.height || metadata.height <= 0) return null;
return {
width: metadata.width,
height: metadata.height,
aspectRatio: metadata.width / metadata.height,
};
} catch (error) {
console.warn("[Alibaba Image] Failed to infer image dimensions from reference image:", error);
return null;
} finally {
clearTimeout(timeoutId);
}
}
function calculateAlibabaSizeFromDimensions(
width: number,
height: number,
baseSize: number,
): string {
const safeWidth = Math.max(1, Math.round(width));
const safeHeight = Math.max(1, Math.round(height));
const longestSide = Math.max(safeWidth, safeHeight);
const scale = baseSize / longestSide;
let scaledWidth = Math.round(safeWidth * scale);
let scaledHeight = Math.round(safeHeight * scale);
if (scaledWidth > ALIBABA_MAX_SIZE || scaledHeight > ALIBABA_MAX_SIZE) {
const clampScale = Math.min(
ALIBABA_MAX_SIZE / scaledWidth,
ALIBABA_MAX_SIZE / scaledHeight,
);
scaledWidth = Math.round(scaledWidth * clampScale);
scaledHeight = Math.round(scaledHeight * clampScale);
}
if (scaledWidth < ALIBABA_MIN_SIZE || scaledHeight < ALIBABA_MIN_SIZE) {
const boostScale = Math.max(
ALIBABA_MIN_SIZE / scaledWidth,
ALIBABA_MIN_SIZE / scaledHeight,
);
scaledWidth = Math.round(scaledWidth * boostScale);
scaledHeight = Math.round(scaledHeight * boostScale);
}
scaledWidth = Math.max(ALIBABA_MIN_SIZE, Math.min(ALIBABA_MAX_SIZE, scaledWidth));
scaledHeight = Math.max(ALIBABA_MIN_SIZE, Math.min(ALIBABA_MAX_SIZE, scaledHeight));
scaledWidth = Math.round(scaledWidth / 2) * 2;
scaledHeight = Math.round(scaledHeight / 2) * 2;
return `${scaledWidth}*${scaledHeight}`;
}
/**
* 创建缩略图
*/
@ -501,17 +574,29 @@ export function createAlibabaImage({ apiKey, baseURL }: AlibabaImageConfig) {
if (aspectRatio === "auto") {
// 阿里文档在 auto 下默认接近 1024*1024,这会让 4K 选择失效。
// 这里改为根据参考图比例计算 size,确保 1K/2K/4K 档位真正生效。
const referenceImageUrl = imageUrls[imageUrls.length - 1];
const inferredAspectRatio = await inferAspectRatioFromImageUrl(referenceImageUrl);
if (inferredAspectRatio) {
params.size = calculateAlibabaSizeByAspect(inferredAspectRatio, baseSize);
const referenceImageUrl = imageUrls[0];
const inferredDimensions = await inferImageDimensionsFromImageUrl(referenceImageUrl);
if (inferredDimensions) {
params.size = calculateAlibabaSizeFromDimensions(
inferredDimensions.width,
inferredDimensions.height,
baseSize,
);
console.log(
`[Alibaba Image] Auto mode resolved size: imageSize=${imageSize}, inferredAspect=${inferredAspectRatio} => size=${params.size}`
`[Alibaba Image] Auto mode resolved size: imageSize=${imageSize}, reference=${inferredDimensions.width}x${inferredDimensions.height}, inferredAspect=${inferredDimensions.aspectRatio} => size=${params.size}`
);
} else {
console.log(
`[Alibaba Image] Auto mode keeps provider default size: imageSize=${imageSize}, inferredAspect=unavailable`
);
const inferredAspectRatio = await inferAspectRatioFromImageUrl(referenceImageUrl);
if (inferredAspectRatio) {
params.size = calculateAlibabaSizeByAspect(inferredAspectRatio, baseSize);
console.log(
`[Alibaba Image] Auto mode resolved size via aspect fallback: imageSize=${imageSize}, inferredAspect=${inferredAspectRatio} => size=${params.size}`
);
} else {
console.log(
`[Alibaba Image] Auto mode keeps provider default size: imageSize=${imageSize}, inferredAspect=unavailable`
);
}
}
} else {
const calculatedSize = calculateSize(aspectRatio, baseSize);