1
This commit is contained in:
@@ -2,19 +2,24 @@ import { getPlatformById } from '@/config/platforms';
|
||||
import type { CrawlProgressStep, CrawlTaskState } from '@/types';
|
||||
import type { BackgroundCommand, BackgroundResponse, CrawlStateResponse } from './types';
|
||||
|
||||
/** chrome.storage.local 中保存当前爬取任务状态的键名。 */
|
||||
const CRAWL_TASK_STORAGE_KEY = 'crawlTaskState';
|
||||
|
||||
/** 扩展安装完成时的初始化入口,当前仅保留日志方便调试生命周期。 */
|
||||
export async function handleInstalled(): Promise<void> {
|
||||
console.log('[background] installed');
|
||||
}
|
||||
|
||||
/** 浏览器启动并加载扩展时的初始化入口,当前仅保留日志方便调试生命周期。 */
|
||||
export async function handleStartup(): Promise<void> {
|
||||
console.log('[background] startup');
|
||||
}
|
||||
|
||||
/** 监听窗口关闭事件;如果关闭的是爬取窗口,就把当前任务标记为取消。 */
|
||||
export async function handleWindowRemoved(windowId: number): Promise<void> {
|
||||
console.log('[background] window removed', windowId);
|
||||
|
||||
/** 当前保存的爬取任务状态。 */
|
||||
const state = await getCrawlTaskState();
|
||||
|
||||
if (state?.windowId === windowId && state.status === 'running') {
|
||||
@@ -28,6 +33,7 @@ export async function handleWindowRemoved(windowId: number): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/** 根据 popup/content 发来的 action 分发到对应的后台处理函数。 */
|
||||
export async function handleBackgroundCommand(
|
||||
message: BackgroundCommand,
|
||||
): Promise<BackgroundResponse | CrawlStateResponse> {
|
||||
@@ -43,14 +49,18 @@ export async function handleBackgroundCommand(
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建新的爬取任务,打开目标平台窗口,并把初始时间轴状态写入 storage。 */
|
||||
async function startCrawl(platformId: string): Promise<CrawlStateResponse> {
|
||||
/** 根据平台 ID 找到对应的平台爬取配置。 */
|
||||
const platform = getPlatformById(platformId);
|
||||
|
||||
if (!platform) {
|
||||
return { ok: false, error: '平台配置不存在' };
|
||||
}
|
||||
|
||||
/** 当前任务的开始时间戳,用于计算正计时。 */
|
||||
const startedAt = Date.now();
|
||||
/** 窗口创建前的初始任务状态,先写入 storage 让所有页面能立即感知爬取开始。 */
|
||||
const nextState: CrawlTaskState = {
|
||||
id: `${platform.id}-${startedAt}`,
|
||||
platformId: platform.id,
|
||||
@@ -68,11 +78,14 @@ async function startCrawl(platformId: string): Promise<CrawlStateResponse> {
|
||||
await setCrawlTaskState(nextState);
|
||||
|
||||
try {
|
||||
/** background 创建出来的目标平台窗口信息。 */
|
||||
const windowInfo = await createCrawlWindow(platform.baseUrl);
|
||||
/** 补充 windowId 后的任务状态,后续可用于取消或监听窗口关闭。 */
|
||||
const stateWithWindow = { ...nextState, windowId: windowInfo.id };
|
||||
await setCrawlTaskState(stateWithWindow);
|
||||
return { ok: true, data: stateWithWindow };
|
||||
} catch (error: unknown) {
|
||||
/** 窗口创建失败时写入的失败状态,供 popup/content 显示错误进度。 */
|
||||
const failedState: CrawlTaskState = {
|
||||
...nextState,
|
||||
status: 'failed',
|
||||
@@ -85,13 +98,16 @@ async function startCrawl(platformId: string): Promise<CrawlStateResponse> {
|
||||
}
|
||||
}
|
||||
|
||||
/** 取消当前爬取任务,并尝试关闭正在爬取的平台窗口。 */
|
||||
async function cancelCrawl(): Promise<CrawlStateResponse> {
|
||||
/** 当前保存的爬取任务状态。 */
|
||||
const state = await getCrawlTaskState();
|
||||
|
||||
if (!state) {
|
||||
return { ok: true, data: null };
|
||||
}
|
||||
|
||||
/** 用户取消后的任务状态,当前执行步骤会显示为失败并附带取消原因。 */
|
||||
const canceledState: CrawlTaskState = {
|
||||
...state,
|
||||
status: 'canceled',
|
||||
@@ -109,16 +125,21 @@ async function cancelCrawl(): Promise<CrawlStateResponse> {
|
||||
return { ok: true, data: canceledState };
|
||||
}
|
||||
|
||||
/** 从 chrome.storage.local 读取当前爬取任务状态。 */
|
||||
async function getCrawlTaskState(): Promise<CrawlTaskState | null> {
|
||||
/** chrome.storage.local 返回的原始键值对象。 */
|
||||
const result = await chrome.storage.local.get(CRAWL_TASK_STORAGE_KEY);
|
||||
/** 取出的任务状态候选值,需要经过结构校验后才能使用。 */
|
||||
const state = result[CRAWL_TASK_STORAGE_KEY];
|
||||
return isCrawlTaskState(state) ? state : null;
|
||||
}
|
||||
|
||||
/** 将最新爬取任务状态写入 chrome.storage.local,供 popup 和 content script 同步读取。 */
|
||||
async function setCrawlTaskState(state: CrawlTaskState): Promise<void> {
|
||||
await chrome.storage.local.set({ [CRAWL_TASK_STORAGE_KEY]: state });
|
||||
}
|
||||
|
||||
/** 打开一个普通浏览器窗口承载目标平台页面。 */
|
||||
function createCrawlWindow(url: string): Promise<chrome.windows.Window> {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.windows.create(
|
||||
@@ -130,6 +151,7 @@ function createCrawlWindow(url: string): Promise<chrome.windows.Window> {
|
||||
height: 900,
|
||||
},
|
||||
(windowInfo) => {
|
||||
/** Chrome 扩展 API 回调中的运行时错误。 */
|
||||
const runtimeError = chrome.runtime.lastError;
|
||||
|
||||
if (runtimeError) {
|
||||
@@ -148,6 +170,7 @@ function createCrawlWindow(url: string): Promise<chrome.windows.Window> {
|
||||
});
|
||||
}
|
||||
|
||||
/** 根据窗口 ID 关闭爬取窗口;关闭失败时不阻塞取消状态写入。 */
|
||||
function removeWindow(windowId: number): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.windows.remove(windowId, () => {
|
||||
@@ -156,6 +179,7 @@ function removeWindow(windowId: number): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
/** 粗略判断 storage 中读取到的值是否像一个爬取任务状态对象。 */
|
||||
function isCrawlTaskState(value: unknown): value is CrawlTaskState {
|
||||
return typeof value === 'object' && value !== null && 'id' in value && 'steps' in value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user