下载非内核
This commit is contained in:
@@ -2,17 +2,17 @@
|
||||
* Thing Extension - 后台 Service Worker
|
||||
*
|
||||
* 职责:
|
||||
* 1. 接管浏览器下载,转发到 aria2
|
||||
* 1. 接管浏览器下载,转发到 Thing 下载引擎
|
||||
* 2. 资源嗅探:识别视频/音频/图片/压缩包/安装包等,存储到内存供 popup 查看
|
||||
* 3. 右键菜单"使用 aria2 下载"
|
||||
* 3. 右键菜单"使用 Thing 下载"
|
||||
*
|
||||
* 注:Service Worker 是短生命周期的,配置需持久化到 chrome.storage
|
||||
*/
|
||||
|
||||
// ===== 默认配置 =====
|
||||
const DEFAULT_CONFIG = {
|
||||
rpcUrl: 'http://127.0.0.1:6800/jsonrpc',
|
||||
rpcSecret: '',
|
||||
serverUrl: 'http://127.0.0.1:16800',
|
||||
secret: '',
|
||||
interceptDownload: true,
|
||||
minSize: 0,
|
||||
excludeDomains: [],
|
||||
@@ -301,48 +301,61 @@ async function saveConfig(config) {
|
||||
await chrome.storage.local.set({ config })
|
||||
}
|
||||
|
||||
// ===== aria2 JSON-RPC 调用 =====
|
||||
async function aria2Call(method, params = []) {
|
||||
// ===== Thing 下载引擎 REST API 调用 =====
|
||||
async function apiRequest(path, options = {}) {
|
||||
const config = await getConfig()
|
||||
const rpcParams = []
|
||||
if (config.rpcSecret) {
|
||||
rpcParams.push(`token:${config.rpcSecret}`)
|
||||
const url = config.serverUrl.replace(/\/$/, '') + path
|
||||
const headers = { 'Content-Type': 'application/json' }
|
||||
if (config.secret) {
|
||||
headers['Authorization'] = `Bearer ${config.secret}`
|
||||
}
|
||||
rpcParams.push(...params)
|
||||
|
||||
const resp = await fetch(config.rpcUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
id: Date.now().toString(),
|
||||
method,
|
||||
params: rpcParams
|
||||
})
|
||||
const resp = await fetch(url, {
|
||||
...options,
|
||||
headers: { ...headers, ...options.headers }
|
||||
})
|
||||
if (!resp.ok) {
|
||||
throw new Error(`aria2 RPC HTTP ${resp.status}`)
|
||||
const text = await resp.text().catch(() => '')
|
||||
throw new Error(`API HTTP ${resp.status}: ${text || resp.statusText}`)
|
||||
}
|
||||
const data = await resp.json()
|
||||
if (data.error) {
|
||||
throw new Error(`aria2 RPC error: ${data.error.message} (${data.error.code})`)
|
||||
}
|
||||
return data.result
|
||||
return resp.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 URI 下载任务到 aria2
|
||||
* 测试连接(GET /health)
|
||||
*/
|
||||
async function aria2AddUri(url, filename, referer, cookies, headers) {
|
||||
const options = {}
|
||||
if (filename) options.out = filename
|
||||
if (referer) options.referer = referer
|
||||
const headerList = []
|
||||
if (cookies) headerList.push(`Cookie: ${cookies}`)
|
||||
if (headers && headers.length) headerList.push(...headers)
|
||||
if (headerList.length) options.header = headerList
|
||||
options['user-agent'] = navigator.userAgent
|
||||
return aria2Call('aria2.addUri', [[url], options])
|
||||
async function testConnection() {
|
||||
const config = await getConfig()
|
||||
const url = config.serverUrl.replace(/\/$/, '') + '/health'
|
||||
const resp = await fetch(url)
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下载任务(POST /api/downloads)
|
||||
*/
|
||||
async function addDownload(url, filename, referer, cookies, headers) {
|
||||
const body = { url }
|
||||
if (filename) body.filename = filename
|
||||
// 构造请求头
|
||||
const headerMap = {}
|
||||
headerMap['User-Agent'] = navigator.userAgent
|
||||
if (referer) headerMap['Referer'] = referer
|
||||
if (cookies) headerMap['Cookie'] = cookies
|
||||
if (headers && headers.length) {
|
||||
for (const h of headers) {
|
||||
const idx = h.indexOf(':')
|
||||
if (idx > 0) {
|
||||
headerMap[h.slice(0, idx).trim()] = h.slice(idx + 1).trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
body.headers = headerMap
|
||||
const result = await apiRequest('/api/downloads', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
return result.id
|
||||
}
|
||||
|
||||
// ===== 下载拦截 =====
|
||||
@@ -373,10 +386,10 @@ async function handleDownloadCreated(downloadItem) {
|
||||
const filename = downloadItem.filename || ''
|
||||
|
||||
try {
|
||||
const gid = await aria2AddUri(url, filename, downloadItem.referrer, '')
|
||||
await notify('已添加到 aria2', `${filename || url}\nGID: ${gid}`)
|
||||
const id = await addDownload(url, filename, downloadItem.referrer, '')
|
||||
await notify('已添加到 Thing', `${filename || url}`)
|
||||
} catch (e) {
|
||||
await notify('aria2 添加失败', `${filename || url}\n${e.message}`)
|
||||
await notify('Thing 添加失败', `${filename || url}\n${e.message}`)
|
||||
try { await chrome.downloads.download({ url }) } catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
@@ -399,7 +412,7 @@ async function notify(title, message) {
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
chrome.contextMenus.create({
|
||||
id: 'thing-download-link',
|
||||
title: '使用 aria2 下载此链接',
|
||||
title: '使用 Thing 下载此链接',
|
||||
contexts: ['link']
|
||||
})
|
||||
chrome.contextMenus.create({
|
||||
@@ -414,10 +427,10 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => {
|
||||
const url = info.linkUrl
|
||||
const filename = url.split('/').pop()?.split('?')[0] || ''
|
||||
try {
|
||||
const gid = await aria2AddUri(url, filename, info.pageUrl, '')
|
||||
await notify('已添加到 aria2', `${filename || url}\nGID: ${gid}`)
|
||||
await addDownload(url, filename, info.pageUrl, '')
|
||||
await notify('已添加到 Thing', `${filename || url}`)
|
||||
} catch (e) {
|
||||
await notify('aria2 添加失败', `${e.message}`)
|
||||
await notify('Thing 添加失败', `${e.message}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -603,8 +616,8 @@ chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
||||
return true
|
||||
}
|
||||
if (msg.type === 'testConnection') {
|
||||
aria2Call('aria2.getVersion', [])
|
||||
.then(res => sendResponse({ ok: true, version: res.version }))
|
||||
testConnection()
|
||||
.then(() => sendResponse({ ok: true }))
|
||||
.catch(e => sendResponse({ ok: false, error: e.message }))
|
||||
return true
|
||||
}
|
||||
@@ -629,8 +642,8 @@ chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
||||
}
|
||||
if (msg.type === 'downloadSniffed') {
|
||||
const { url, filename, referer } = msg
|
||||
aria2AddUri(url, filename, referer || '', '')
|
||||
.then(gid => sendResponse({ ok: true, gid }))
|
||||
addDownload(url, filename, referer || '', '')
|
||||
.then(id => sendResponse({ ok: true, id }))
|
||||
.catch(e => sendResponse({ ok: false, error: e.message }))
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user