fix: background 完整重写,添加下载日志

This commit is contained in:
李进
2026-07-27 14:47:53 +08:00
parent aeb75c746c
commit 224983c790
+22 -113
View File
@@ -1,6 +1,6 @@
const CAPTURE_FILE = "capture.js"; const CAPTURE_FILE = "capture.js";
const RUNNER_FILE = "runner.js"; const RUNNER_FILE = "runner.js";
const SETTINGS_KEY = "webToPPTSettings"; const SETTINGS_KEY = "webToPixsoSettings";
const DEFAULT_SETTINGS = { const DEFAULT_SETTINGS = {
useProxy: false, useProxy: false,
concurrency: "8", concurrency: "8",
@@ -10,7 +10,6 @@ const DEFAULT_SETTINGS = {
const MIN_CAPTURE_WIDTH = 320; const MIN_CAPTURE_WIDTH = 320;
const MAX_CAPTURE_WIDTH = 3840; const MAX_CAPTURE_WIDTH = 3840;
// 缓存最近一次采集数据,供 popup 获取
let lastCaptureData = null; let lastCaptureData = null;
const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
@@ -56,22 +55,14 @@ async function getActiveTab() {
} }
async function runCapture(tabId, options) { async function runCapture(tabId, options) {
await chrome.scripting.executeScript({ await chrome.scripting.executeScript({ target: { tabId }, files: [CAPTURE_FILE] });
target: { tabId }, await chrome.scripting.executeScript({ target: { tabId }, files: [RUNNER_FILE] });
files: [CAPTURE_FILE]
});
await chrome.scripting.executeScript({
target: { tabId },
files: [RUNNER_FILE]
});
const [{ result }] = await chrome.scripting.executeScript({ const [{ result }] = await chrome.scripting.executeScript({
target: { tabId }, target: { tabId },
func: captureOptions => window.__webToPPTRunCapture(captureOptions), func: captureOptions => window.__webToPPTRunCapture(captureOptions),
args: [options] args: [options]
}); });
if (!result) { if (!result) throw new Error("页面没有返回采集结果");
throw new Error("页面没有返回采集结果");
}
return result; return result;
} }
@@ -82,9 +73,7 @@ async function getTabViewportWidth(tabId) {
func: () => Math.round(window.innerWidth || document.documentElement.clientWidth || 0) func: () => Math.round(window.innerWidth || document.documentElement.clientWidth || 0)
}); });
return normalizeCaptureWidth(result, null); return normalizeCaptureWidth(result, null);
} catch { } catch { return null; }
return null;
}
} }
async function prepareCaptureViewport(tab, requestedWidth) { async function prepareCaptureViewport(tab, requestedWidth) {
@@ -92,52 +81,25 @@ async function prepareCaptureViewport(tab, requestedWidth) {
const beforeViewportWidth = await getTabViewportWidth(tab.id); const beforeViewportWidth = await getTabViewportWidth(tab.id);
const noop = async () => {}; const noop = async () => {};
if (!targetWidth || !beforeViewportWidth || Math.abs(beforeViewportWidth - targetWidth) <= 2) { if (!targetWidth || !beforeViewportWidth || Math.abs(beforeViewportWidth - targetWidth) <= 2) {
return { return { restore: noop, requestedWidth: targetWidth || beforeViewportWidth, beforeViewportWidth, actualViewportWidth: beforeViewportWidth, resizedWindow: false };
restore: noop,
requestedWidth: targetWidth || beforeViewportWidth,
beforeViewportWidth,
actualViewportWidth: beforeViewportWidth,
resizedWindow: false
};
} }
if (!tab.windowId || !chrome.windows?.get || !chrome.windows?.update) { if (!tab.windowId || !chrome.windows?.get || !chrome.windows?.update) {
throw new Error("当前浏览器不支持临时调整采集视口宽度"); throw new Error("当前浏览器不支持临时调整采集视口宽度");
} }
const originalWindow = await chrome.windows.get(tab.windowId); const originalWindow = await chrome.windows.get(tab.windowId);
const originalState = originalWindow.state || "normal"; const originalState = originalWindow.state || "normal";
const originalBounds = { const originalBounds = { left: originalWindow.left, top: originalWindow.top, width: originalWindow.width, height: originalWindow.height };
left: originalWindow.left,
top: originalWindow.top,
width: originalWindow.width,
height: originalWindow.height
};
const restore = async () => { const restore = async () => {
try { try {
if (originalState !== "normal") { if (originalState !== "normal") { await chrome.windows.update(tab.windowId, { state: "normal" }); await delay(120); }
await chrome.windows.update(tab.windowId, { state: "normal" });
await delay(120);
}
const restoreBounds = definedWindowBounds(originalBounds); const restoreBounds = definedWindowBounds(originalBounds);
if (Object.keys(restoreBounds).length) { if (Object.keys(restoreBounds).length) await chrome.windows.update(tab.windowId, restoreBounds);
await chrome.windows.update(tab.windowId, restoreBounds); if (originalState !== "normal") { await delay(120); await chrome.windows.update(tab.windowId, { state: originalState }); }
}
if (originalState !== "normal") {
await delay(120);
await chrome.windows.update(tab.windowId, { state: originalState });
}
await delay(250); await delay(250);
} catch {} } catch {}
}; };
try { try {
if (originalState !== "normal") { if (originalState !== "normal") { await chrome.windows.update(tab.windowId, { state: "normal" }); await delay(250); }
await chrome.windows.update(tab.windowId, { state: "normal" });
await delay(250);
}
let currentViewportWidth = beforeViewportWidth; let currentViewportWidth = beforeViewportWidth;
let currentWindow = await chrome.windows.get(tab.windowId); let currentWindow = await chrome.windows.get(tab.windowId);
for (let attempt = 0; attempt < 2; attempt += 1) { for (let attempt = 0; attempt < 2; attempt += 1) {
@@ -149,63 +111,18 @@ async function prepareCaptureViewport(tab, requestedWidth) {
if (Math.abs(currentViewportWidth - targetWidth) <= 2) break; if (Math.abs(currentViewportWidth - targetWidth) <= 2) break;
currentWindow = await chrome.windows.get(tab.windowId); currentWindow = await chrome.windows.get(tab.windowId);
} }
if (Math.abs(currentViewportWidth - targetWidth) > 2) throw new Error(`采集视口未生效:目标 ${targetWidth}px,实际 ${currentViewportWidth}px`);
if (Math.abs(currentViewportWidth - targetWidth) > 2) { return { restore, requestedWidth: targetWidth, beforeViewportWidth, actualViewportWidth: currentViewportWidth, resizedWindow: true };
throw new Error(`采集视口未生效:目标 ${targetWidth}px,实际 ${currentViewportWidth}px`); } catch (error) { await restore(); throw error; }
}
return {
restore,
requestedWidth: targetWidth,
beforeViewportWidth,
actualViewportWidth: currentViewportWidth,
resizedWindow: true
};
} catch (error) {
await restore();
throw error;
}
} }
async function captureCurrentTab(tab, settings) { async function captureCurrentTab(tab, settings) {
const viewport = await prepareCaptureViewport(tab, settings.captureWidth); const viewport = await prepareCaptureViewport(tab, settings.captureWidth);
try { try {
const data = await runCapture(tab.id, { const data = await runCapture(tab.id, { ...settings, captureWidth: viewport.requestedWidth });
...settings, data.capture = { ...(data.capture || {}), resizedWindow: viewport.resizedWindow, usedTemporaryWindow: viewport.resizedWindow, requestedWidth: viewport.requestedWidth || data.source?.actualViewportWidth || data.canvas?.width, beforeViewportWidth: viewport.beforeViewportWidth, actualViewportWidth: data.source?.actualViewportWidth || viewport.actualViewportWidth };
captureWidth: viewport.requestedWidth
});
data.capture = {
...(data.capture || {}),
resizedWindow: viewport.resizedWindow,
usedTemporaryWindow: viewport.resizedWindow,
requestedWidth: viewport.requestedWidth || data.source?.actualViewportWidth || data.canvas?.width,
beforeViewportWidth: viewport.beforeViewportWidth,
actualViewportWidth: data.source?.actualViewportWidth || viewport.actualViewportWidth
};
return data; return data;
} finally { } finally { await viewport.restore(); }
await viewport.restore();
}
}
async function downloadCapture(data) {
const json = JSON.stringify(data, null, 2);
const encodedJson = arrayBufferToBase64(new TextEncoder().encode(json));
const url = `data:application/json;charset=utf-8;base64,${encodedJson}`;
const title = data.source?.title || "webpage";
const safeTitle = title
.replace(/[\\/:*?"<>|]+/g, "-")
.replace(/\s+/g, "-")
.slice(0, 64) || "webpage";
const filename = `web-to-ppt/${safeTitle}-${Date.now()}.json`;
await chrome.downloads.download({
url,
filename,
saveAs: true
});
return filename;
} }
function arrayBufferToBase64(buffer) { function arrayBufferToBase64(buffer) {
@@ -220,7 +137,7 @@ function arrayBufferToBase64(buffer) {
// 消息处理 // 消息处理
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// 采集并缓存数据(不下载 JSON,由 popup 直接转化下载 PPTX) // 采集并缓存数据
if (message?.type === "WEB_TO_PPT_CAPTURE_START") { if (message?.type === "WEB_TO_PPT_CAPTURE_START") {
(async () => { (async () => {
const settings = normalizeSettings(message.options); const settings = normalizeSettings(message.options);
@@ -228,24 +145,19 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
const tab = sender?.tab || await getActiveTab(); const tab = sender?.tab || await getActiveTab();
assertCaptureableTab(tab); assertCaptureableTab(tab);
const data = await captureCurrentTab(tab, settings); const data = await captureCurrentTab(tab, settings);
lastCaptureData = data; // 缓存数据 lastCaptureData = data;
return { return { ok: true, actualViewportWidth: data.source?.actualViewportWidth };
ok: true,
actualViewportWidth: data.source?.actualViewportWidth,
requestedViewportWidth: data.source?.requestedViewportWidth
};
})() })()
.then(sendResponse) .then(sendResponse)
.catch(error => sendResponse({ ok: false, error: error.message || String(error) })); .catch(error => sendResponse({ ok: false, error: error.message || String(error) }));
return true; return true;
} }
// 获取最近一次采集数据(供 popup 转化用) // 获取缓存数据
if (message?.type === "WEB_TO_PPT_GET_DATA") { if (message?.type === "WEB_TO_PPT_GET_DATA") {
if (lastCaptureData) { if (lastCaptureData) {
sendResponse({ ok: true, data: lastCaptureData }); sendResponse({ ok: true, data: lastCaptureData });
} else { } else {
// 如果没有缓存,重新采集
(async () => { (async () => {
const settings = normalizeSettings({}); const settings = normalizeSettings({});
const tab = await getActiveTab(); const tab = await getActiveTab();
@@ -259,7 +171,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
return true; return true;
} }
// 下载 PPTX由 popup 调用,background 执行下载 // 下载 PPTXbase64 data URL
if (message?.type === "WEB_TO_PPT_DOWNLOAD") { if (message?.type === "WEB_TO_PPT_DOWNLOAD") {
(async () => { (async () => {
const dataUrl = 'data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,' + message.base64; const dataUrl = 'data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,' + message.base64;
@@ -274,11 +186,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
return false; return false;
}); });
// 安装时初始化设置
chrome.runtime.onInstalled.addListener(() => { chrome.runtime.onInstalled.addListener(() => {
chrome.storage.local.get({ [SETTINGS_KEY]: DEFAULT_SETTINGS }).then(result => { chrome.storage.local.get({ [SETTINGS_KEY]: DEFAULT_SETTINGS }).then(result => {
chrome.storage.local.set({ [SETTINGS_KEY]: normalizeSettings(result[SETTINGS_KEY]) }); chrome.storage.local.set({ [SETTINGS_KEY]: normalizeSettings(result[SETTINGS_KEY]) });
}); });
}); });
// 点击图标打开 popup(MV3 默认行为)