fix: background 完整重写,添加下载日志
This commit is contained in:
+22
-113
@@ -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 执行下载)
|
// 下载 PPTX(base64 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 默认行为)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user