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 { 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[] = []; // 正在运行的任务池 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 }; }