Skip to content

Commit 769648f

Browse files
committed
fix(processor): 修复 GIF 下载策略在开启格式转换时不生效的问题
1 parent 56efd8c commit 769648f

3 files changed

Lines changed: 92 additions & 11 deletions

File tree

scratch_test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { ImageItem, Settings } from "./src/types";
2+
3+
// 模拟逻辑测试函数 (简化版,不包含 DOM 操作)
4+
function simulateConvertLogic(blobType: string, img: ImageItem, settings: Settings) {
5+
const targetFormat = settings.downloadLogic?.targetFormat || "original";
6+
const gifStrategy = settings.gifStrategy || "keep";
7+
8+
const originalFormat = img.format.toLowerCase();
9+
const actualMimeType = blobType.toLowerCase();
10+
11+
const isGif = originalFormat === "gif" || actualMimeType === "image/gif";
12+
13+
let shouldConvert = false;
14+
let extension = "png";
15+
16+
// 模拟后缀逻辑
17+
const urlExt = img.url.split(".").pop()?.split(/[?#]/)[0]?.toLowerCase();
18+
if (originalFormat !== "unknown" && originalFormat.length <= 5) {
19+
extension = originalFormat;
20+
} else {
21+
extension = urlExt && urlExt.length <= 5 ? urlExt : "png";
22+
}
23+
if (extension === "jpeg") extension = "jpg";
24+
25+
console.log(`- 输入: URL=${img.url}, SnifferFormat=${img.format}, BlobType=${blobType}`);
26+
console.log(`- 识别结果: isGif=${isGif}, 策略=${gifStrategy}, 目标格式=${targetFormat}`);
27+
28+
if (isGif) {
29+
if (gifStrategy === "keep") {
30+
return { action: "KEEP", extension: "gif" };
31+
} else if (gifStrategy === "firstFrame") {
32+
shouldConvert = true;
33+
} else if (gifStrategy === "skip") {
34+
return { action: "SKIP" };
35+
}
36+
}
37+
38+
if (targetFormat !== "original") {
39+
if (targetFormat !== originalFormat || isGif) {
40+
shouldConvert = true;
41+
}
42+
}
43+
44+
return { action: shouldConvert ? "CONVERT" : "DOWNLOAD", extension };
45+
}
46+
47+
// 测试用例
48+
const settings: Settings = {
49+
gifStrategy: "keep",
50+
downloadLogic: { targetFormat: "webp", quality: 80 }
51+
} as any;
52+
53+
console.log("场景 1: GIF 保持原图 (即使开启了 WebP 转换)");
54+
console.log(simulateConvertLogic("image/gif", { url: "test.gif", format: "GIF" } as any, settings));
55+
56+
console.log("\n场景 2: 伪装 URL 的 GIF (Sniffer 识别错误)");
57+
console.log(simulateConvertLogic("image/gif", { url: "test.jpg", format: "JPG" } as any, settings));
58+
59+
console.log("\n场景 3: 跳过 GIF");
60+
console.log(simulateConvertLogic("image/gif", { url: "test.gif", format: "GIF" } as any, { ...settings, gifStrategy: "skip" } as any));
61+
62+
console.log("\n场景 4: GIF 提取首帧");
63+
console.log(simulateConvertLogic("image/gif", { url: "test.gif", format: "GIF" } as any, { ...settings, gifStrategy: "firstFrame" } as any));

src/core/processor.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export class ImageProcessor {
7979
blob = converted.blob;
8080
extension = converted.extension;
8181
} catch (convErr) {
82+
if (convErr instanceof Error && convErr.message === "SKIP_GIF") {
83+
console.log(`[Processor] Skipping GIF after fetch: ${img.url}`);
84+
continue;
85+
}
8286
console.warn(
8387
`[Processor] Format conversion failed, using original:`,
8488
convErr,
@@ -189,6 +193,10 @@ export class ImageProcessor {
189193
blob = converted.blob;
190194
extension = converted.extension;
191195
} catch (convErr) {
196+
if (convErr instanceof Error && convErr.message === "SKIP_GIF") {
197+
console.log(`[Processor] ZIP: Skipping GIF after fetch: ${img.url}`);
198+
continue;
199+
}
192200
console.warn(`[Processor] ZIP conversion failed:`, convErr);
193201
}
194202

src/core/utils/image-converter.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ export async function convertImage(
1313
const quality = (settings.downloadLogic?.quality || 85) / 100;
1414

1515
const originalFormat = img.format.toLowerCase();
16+
const actualMimeType = blob.type.toLowerCase();
1617

1718
// 1. Check if conversion is needed
18-
const isGif = originalFormat === "gif";
19+
// Use both metadata and actual blob type for robustness
20+
const isGif = originalFormat === "gif" || actualMimeType === "image/gif";
1921

2022
let shouldConvert = false;
2123

@@ -25,24 +27,32 @@ export async function convertImage(
2527
extension = originalFormat !== "unknown" ? originalFormat : "png";
2628
} else {
2729
const urlExt = img.url.split(".").pop()?.split(/[?#]/)[0]?.toLowerCase();
28-
extension =
29-
urlExt && urlExt.length <= 5
30-
? urlExt
31-
: originalFormat !== "unknown"
32-
? originalFormat
33-
: "png";
30+
// Prioritize original format if it's known and consistent with blob
31+
if (originalFormat !== "unknown" && originalFormat.length <= 5) {
32+
extension = originalFormat;
33+
} else {
34+
extension = urlExt && urlExt.length <= 5 ? urlExt : "png";
35+
}
3436
}
37+
38+
// Final extension cleanup
3539
if (extension === "jpeg") extension = "jpg";
3640

41+
// GIF Special Handling: This should take PRECEDENCE over targetFormat
3742
if (isGif) {
38-
if (gifStrategy === "firstFrame") {
43+
if (gifStrategy === "keep") {
44+
// Force .gif extension for kept GIFs
45+
return { blob, extension: "gif" };
46+
} else if (gifStrategy === "firstFrame") {
3947
shouldConvert = true;
40-
} else if (gifStrategy === "keep") {
41-
return { blob, extension };
48+
} else if (gifStrategy === "skip") {
49+
// If we somehow reached here with skip strategy (e.g. processor sniffer failure)
50+
// we throw to allow processor to catch and skip
51+
throw new Error("SKIP_GIF");
4252
}
43-
// "skip" is handled in processor
4453
}
4554

55+
// Global format conversion (only if not already handled by gifStrategy or if it's a non-GIF)
4656
if (targetFormat !== "original") {
4757
// If target format is different from original, or if it's a GIF being flattened
4858
if (targetFormat !== originalFormat || isGif) {

0 commit comments

Comments
 (0)