Files
custom-plugin/entrypoints/background.ts
2026-05-05 02:47:36 +08:00

88 lines
2.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export default defineBackground(() => {
console.log('Hello background!', { id: browser.runtime.id });
// 监听下载请求
browser.runtime.onMessage.addListener(async (msg) => {
if (msg.type === 'DOWNLOAD_PINS') {
const pins: PinData[] = msg.pins;
const concurrency: number = msg.concurrency || 3;
const result = await startBatchDownload(pins, concurrency);
// 通知 popup 下载完成
browser.runtime.sendMessage({
type: 'DOWNLOAD_DONE',
success: result.success,
failed: result.failed,
total: result.total,
});
}
});
});
// 定义单个下载任务 (封装成 Promise)
async function downloadOne(pin: PinData): Promise<void> {
return new Promise(async (resolve) => {
// 处理文件名非法字符(清理文件名)
const safeName = (pin.title || 'image').replace(/[\n\r\t]/g, ' ').replace(/[<>:"/\\|?*]/g, '').replace(/\s+/g, ' ').trim().slice(0, 100);
const filename = `Pins/${safeName}.jpg`;
browser.downloads.download({
url: pin.imgSrc,
filename: filename,
headers: [{ name: 'Referer', value: 'https://huaban.com/' }],
conflictAction: 'uniquify'
}, (id) => {
if (!id) {
console.warn(`跳过: ${pin.title} (可能 URL 无效)`);
return resolve(); // 即使失败也要 resolve否则会阻塞队列
}
// 监听下载状态,只有当下载真正完成(或中断)时才释放资源
const listener = (delta: Browser.downloads.DownloadDelta) => {
if (delta.id !== id) return;
if (delta.state && delta.state.current === 'complete') {
browser.downloads.onChanged.removeListener(listener);
resolve();
}
// 处理中断或取消的情况,防止队列卡死
else if (delta.state && delta.state.current === 'interrupted') {
browser.downloads.onChanged.removeListener(listener);
console.warn(`下载中断: ${pin.title}`, JSON.stringify(delta));
resolve();
}
};
browser.downloads.onChanged.addListener(listener);
});
});
}
// 并发调度器
export async function startBatchDownload(pins: PinData[], limit: number = 3) {
let success = 0;
let failed = 0;
const executing: Promise<void>[] = []; // 正在运行的任务池
for (const pin of pins) {
// 创建任务
const task = downloadOne(pin);
// 加入任务池
const wrapper = task.finally(() => {
const idx = executing.indexOf(wrapper);
if (idx !== -1) executing.splice(idx, 1);
});
executing.push(wrapper);
// 关键逻辑:如果池子满了(达到 limit等待任意一个任务完成
if (executing.length >= limit) {
await Promise.race(executing);
}
}
// 等待剩余任务全部完成
await Promise.all(executing);
console.log("所有下载任务已处理完毕");
return { success, failed, total: pins.length };
}