Repository navigation
Expand file tree
/
Copy pathbackground.js
More file actions
111 lines (101 loc) · 3.74 KB
/
Copy pathbackground.js
File metadata and controls
111 lines (101 loc) · 3.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Background service worker (MV3)
// - Handles long-running tasks: summarization, translation
// - Stores/retrieves options from chrome.storage
const DEFAULT_SETTINGS = {
provider: "deepseek",
apiBaseUrl: "https://api.deepseek.com/",
apiKey: "sk-a6e9d09e1e6f402b8e37391c68d07114",
model: "deepseek-chat",
targetLang: "zh-CN"
};
async function getSettings() {
const data = await chrome.storage.sync.get({ settings: DEFAULT_SETTINGS });
return { ...DEFAULT_SETTINGS, ...(data.settings || {}) };
}
async function setSettings(settings) {
await chrome.storage.sync.set({ settings });
}
chrome.runtime.onInstalled.addListener(() => {
// Initialize defaults on install if empty
chrome.storage.sync.get(["settings"], ({ settings }) => {
if (!settings) {
chrome.storage.sync.set({ settings: DEFAULT_SETTINGS });
}
});
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
(async () => {
try {
if (message?.type === "VCP_GET_SETTINGS") {
const settings = await getSettings();
sendResponse({ ok: true, settings });
return;
}
if (message?.type === "VCP_SET_SETTINGS") {
await setSettings(message.settings);
sendResponse({ ok: true });
return;
}
if (message?.type === "VCP_SUMMARIZE") {
const { transcript, title, url } = message;
const settings = await getSettings();
const summary = await summarizeTranscript({ transcript, title, url }, settings);
sendResponse({ ok: true, summary });
return;
}
if (message?.type === "VCP_TRANSLATE") {
const { lines, targetLang } = message;
const settings = await getSettings();
const translated = await translateLines(lines, targetLang || settings.targetLang, settings);
sendResponse({ ok: true, translated });
return;
}
} catch (error) {
sendResponse({ ok: false, error: error?.message || String(error) });
}
})();
return true; // keep port open for async
});
async function summarizeTranscript(payload, settings) {
const system = "你是一个资深视频内容助理,基于字幕生成中文内容概要。输出要点列表与简短摘要。";
const user = [
`标题: ${payload.title || "(未知)"}`,
`URL: ${payload.url || ""}`,
"字幕: \n" + payload.transcript.slice(0, 15000)
].join("\n\n");
const response = await callChatCompletions({ system, user, model: settings.model }, settings);
return response;
}
async function translateLines(lines, targetLang, settings) {
const system = `你是专业翻译,请将输入逐行翻译成${targetLang},保留时间戳。`;
const user = lines
.slice(0, 500) // bound cost: translate first 500 lines per request
.map(l => `${l.start} --> ${l.end}\n${l.text}`)
.join("\n\n");
const response = await callChatCompletions({ system, user, model: settings.model }, settings);
return response;
}
async function callChatCompletions({ system, user, model }, settings) {
const url = `${settings.apiBaseUrl}/chat/completions`;
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: settings.apiKey ? `Bearer ${settings.apiKey}` : ""
},
body: JSON.stringify({
model: model || settings.model,
messages: [
{ role: "system", content: system },
{ role: "user", content: user }
],
temperature: 0.3
})
});
if (!res.ok) {
const text = await res.text();
throw new Error(`LLM API error: ${res.status} ${text}`);
}
const data = await res.json();
return data?.choices?.[0]?.message?.content || "";
}