869 lines
32 KiB
JavaScript
869 lines
32 KiB
JavaScript
importScripts("/js/function.js", "/js/init.js");
|
||
|
||
// Service Worker 5分钟后会强制终止扩展
|
||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1271154
|
||
// https://stackoverflow.com/questions/66618136/persistent-service-worker-in-chrome-extension/70003493#70003493
|
||
chrome.webNavigation.onBeforeNavigate.addListener(function () { return; });
|
||
chrome.webNavigation.onHistoryStateUpdated.addListener(function () { return; });
|
||
chrome.runtime.onConnect.addListener(function (Port) {
|
||
if (chrome.runtime.lastError || Port.name !== "HeartBeat") return;
|
||
Port.postMessage("HeartBeat");
|
||
Port.onMessage.addListener(function (message, Port) { return; });
|
||
const interval = setInterval(function () {
|
||
clearInterval(interval);
|
||
Port.disconnect();
|
||
}, 250000);
|
||
Port.onDisconnect.addListener(function () {
|
||
interval && clearInterval(interval);
|
||
if (chrome.runtime.lastError) { return; }
|
||
});
|
||
});
|
||
|
||
/**
|
||
* 定时任务
|
||
* nowClear clear 清理冗余数据
|
||
* save 保存数据
|
||
*/
|
||
chrome.alarms.onAlarm.addListener(function (alarm) {
|
||
if (alarm.name === "nowClear" || alarm.name === "clear") {
|
||
clearRedundant();
|
||
return;
|
||
}
|
||
if (alarm.name === "save") {
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||
return;
|
||
}
|
||
});
|
||
|
||
// onBeforeRequest 浏览器发送请求之前使用正则匹配发送请求的URL
|
||
// chrome.webRequest.onBeforeRequest.addListener(
|
||
// function (data) {
|
||
// try { findMedia(data, true); } catch (e) { console.log(e); }
|
||
// }, { urls: ["<all_urls>"] }, ["requestBody"]
|
||
// );
|
||
// 保存requestHeaders
|
||
chrome.webRequest.onSendHeaders.addListener(
|
||
function (data) {
|
||
if (G && G.initSyncComplete && !G.enable) { return; }
|
||
if (data.requestHeaders) {
|
||
G.requestHeaders.set(data.requestId, data.requestHeaders);
|
||
data.allRequestHeaders = data.requestHeaders;
|
||
}
|
||
try { findMedia(data, true); } catch (e) { console.log(e); }
|
||
}, { urls: ["<all_urls>"] }, ['requestHeaders',
|
||
chrome.webRequest.OnBeforeSendHeadersOptions.EXTRA_HEADERS].filter(Boolean)
|
||
);
|
||
// onResponseStarted 浏览器接收到第一个字节触发,保证有更多信息判断资源类型
|
||
chrome.webRequest.onResponseStarted.addListener(
|
||
function (data) {
|
||
try {
|
||
data.allRequestHeaders = G.requestHeaders.get(data.requestId);
|
||
if (data.allRequestHeaders) {
|
||
G.requestHeaders.delete(data.requestId);
|
||
}
|
||
findMedia(data);
|
||
} catch (e) { console.log(e, data); }
|
||
}, { urls: ["<all_urls>"] }, ["responseHeaders"]
|
||
);
|
||
// 删除失败的requestHeadersData
|
||
chrome.webRequest.onErrorOccurred.addListener(
|
||
function (data) {
|
||
G.requestHeaders.delete(data.requestId);
|
||
G.blackList.delete(data.requestId);
|
||
}, { urls: ["<all_urls>"] }
|
||
);
|
||
|
||
function findMedia(data, isRegex = false, filter = false, timer = false) {
|
||
// Service Worker被强行杀死之后重新自我唤醒,等待全局变量初始化完成。
|
||
if (!G || !G.initSyncComplete || !G.initLocalComplete || G.tabId == undefined || cacheData.init) {
|
||
if (timer) { return; }
|
||
setTimeout(() => {
|
||
findMedia(data, isRegex, filter, true);
|
||
}, 500);
|
||
return;
|
||
}
|
||
// 检查 是否启用 是否在当前标签是否在屏蔽列表中
|
||
const blockUrlFlag = data.tabId && data.tabId > 0 && G.blockUrlSet.has(data.tabId);
|
||
if (!G.enable || (G.blockUrlWhite ? !blockUrlFlag : blockUrlFlag)) {
|
||
return;
|
||
}
|
||
|
||
data.getTime = Date.now();
|
||
|
||
if (!isRegex && G.blackList.has(data.requestId)) {
|
||
G.blackList.delete(data.requestId);
|
||
return;
|
||
}
|
||
// 屏蔽特殊页面发起的资源
|
||
if (data.initiator != "null" &&
|
||
data.initiator != undefined &&
|
||
isSpecialPage(data.initiator)) { return; }
|
||
if (G.isFirefox &&
|
||
data.originUrl &&
|
||
isSpecialPage(data.originUrl)) { return; }
|
||
// 屏蔽特殊页面的资源
|
||
if (isSpecialPage(data.url)) { return; }
|
||
const urlParsing = new URL(data.url);
|
||
let [name, ext] = fileNameParse(urlParsing.pathname);
|
||
|
||
//正则匹配
|
||
if (isRegex && !filter) {
|
||
for (let key in G.Regex) {
|
||
if (!G.Regex[key].state) { continue; }
|
||
G.Regex[key].regex.lastIndex = 0;
|
||
let result = G.Regex[key].regex.exec(data.url);
|
||
if (result == null) { continue; }
|
||
if (G.Regex[key].blackList) {
|
||
G.blackList.add(data.requestId);
|
||
return;
|
||
}
|
||
data.extraExt = G.Regex[key].ext ? G.Regex[key].ext : undefined;
|
||
if (result.length == 1) {
|
||
findMedia(data, true, true);
|
||
return;
|
||
}
|
||
result.shift();
|
||
result = result.map(str => decodeURIComponent(str));
|
||
if (!result[0].startsWith('https://') && !result[0].startsWith('http://')) {
|
||
result[0] = urlParsing.protocol + "//" + data.url;
|
||
}
|
||
data.url = result.join("");
|
||
findMedia(data, true, true);
|
||
return;
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 非正则匹配
|
||
if (!isRegex) {
|
||
// 获取头部信息
|
||
data.header = getResponseHeadersValue(data);
|
||
//检查后缀
|
||
if (!filter && ext != undefined) {
|
||
filter = CheckExtension(ext, data.header?.size);
|
||
if (filter == "break") { return; }
|
||
}
|
||
//检查类型
|
||
if (!filter && data.header?.type != undefined) {
|
||
filter = CheckType(data.header.type, data.header?.size);
|
||
if (filter == "break") { return; }
|
||
}
|
||
//查找附件
|
||
if (!filter && data.header?.attachment != undefined) {
|
||
const res = data.header.attachment.match(reFilename);
|
||
if (res && res[1]) {
|
||
[name, ext] = fileNameParse(decodeURIComponent(res[1]));
|
||
filter = CheckExtension(ext, 0);
|
||
if (filter == "break") { return; }
|
||
}
|
||
}
|
||
//放过类型为media的资源
|
||
if (data.type == "media") {
|
||
filter = true;
|
||
}
|
||
}
|
||
|
||
if (!filter) { return; }
|
||
|
||
// 谜之原因 获取得资源 tabId可能为 -1 firefox中则正常
|
||
// 检查是 -1 使用当前激活标签得tabID
|
||
data.tabId = data.tabId == -1 ? G.tabId : data.tabId;
|
||
|
||
cacheData[data.tabId] ??= [];
|
||
cacheData[G.tabId] ??= [];
|
||
|
||
// 缓存数据大于9999条 清空缓存 避免内存占用过多
|
||
if (cacheData[data.tabId].length > G.maxLength) {
|
||
cacheData[data.tabId] = [];
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||
return;
|
||
}
|
||
|
||
// 查重 避免CPU占用 大于500 强制关闭查重
|
||
// if (G.checkDuplicates && cacheData[data.tabId].length <= 500) {
|
||
// for (let item of cacheData[data.tabId]) {
|
||
// if (item.url.length == data.url.length &&
|
||
// item.cacheURL.pathname == urlParsing.pathname &&
|
||
// item.cacheURL.host == urlParsing.host &&
|
||
// item.cacheURL.search == urlParsing.search) { return; }
|
||
// }
|
||
// }
|
||
|
||
if (G.checkDuplicates && cacheData[data.tabId].length <= 500) {
|
||
const tabFingerprints = G.urlMap.get(data.tabId) || new Set();
|
||
if (tabFingerprints.has(data.url)) {
|
||
return; // 找到重复,直接返回
|
||
}
|
||
tabFingerprints.add(data.url);
|
||
G.urlMap.set(data.tabId, tabFingerprints);
|
||
if (tabFingerprints.size >= 500) {
|
||
tabFingerprints.clear();
|
||
}
|
||
}
|
||
|
||
chrome.tabs.get(data.tabId, async function (webInfo) {
|
||
if (chrome.runtime.lastError) { return; }
|
||
data.requestHeaders = getRequestHeaders(data);
|
||
// requestHeaders 中cookie 单独列出来
|
||
if (data.requestHeaders?.cookie) {
|
||
data.cookie = data.requestHeaders.cookie;
|
||
data.requestHeaders.cookie = undefined;
|
||
}
|
||
const info = {
|
||
name: name,
|
||
url: data.url,
|
||
size: data.header?.size,
|
||
ext: ext,
|
||
type: data.mime ?? data.header?.type,
|
||
tabId: data.tabId,
|
||
isRegex: isRegex,
|
||
requestId: data.requestId ?? Date.now().toString(),
|
||
initiator: data.initiator,
|
||
requestHeaders: data.requestHeaders,
|
||
cookie: data.cookie,
|
||
// cacheURL: { host: urlParsing.host, search: urlParsing.search, pathname: urlParsing.pathname },
|
||
getTime: data.getTime
|
||
};
|
||
// 不存在扩展使用类型
|
||
if (info.ext === undefined && info.type !== undefined) {
|
||
info.ext = info.type.split("/")[1];
|
||
}
|
||
// 正则匹配的备注扩展
|
||
if (data.extraExt) {
|
||
info.ext = data.extraExt;
|
||
}
|
||
// 不存在 initiator 和 referer 使用web url代替initiator
|
||
if (info.initiator == undefined || info.initiator == "null") {
|
||
info.initiator = info.requestHeaders?.referer ?? webInfo?.url;
|
||
}
|
||
// 装载页面信息
|
||
info.title = webInfo?.title ?? "NULL";
|
||
info.favIconUrl = webInfo?.favIconUrl;
|
||
info.webUrl = webInfo?.url;
|
||
// 屏蔽资源
|
||
if (!isRegex && G.blackList.has(data.requestId)) {
|
||
G.blackList.delete(data.requestId);
|
||
return;
|
||
}
|
||
// 发送到popup 并检查自动下载
|
||
chrome.runtime.sendMessage({ Message: "popupAddData", data: info }, function () {
|
||
if (G.featAutoDownTabId.size > 0 && G.featAutoDownTabId.has(info.tabId) && chrome.downloads?.State) {
|
||
try {
|
||
const downDir = info.title == "NULL" ? "CatCatch/" : stringModify(info.title) + "/";
|
||
let fileName = isEmpty(info.name) ? stringModify(info.title) + '.' + info.ext : decodeURIComponent(stringModify(info.name));
|
||
if (G.TitleName) {
|
||
fileName = filterFileName(templates(G.downFileName, info));
|
||
} else {
|
||
fileName = downDir + fileName;
|
||
}
|
||
chrome.downloads.download({
|
||
url: info.url,
|
||
filename: fileName
|
||
});
|
||
} catch (e) { return; }
|
||
}
|
||
if (chrome.runtime.lastError) { return; }
|
||
});
|
||
|
||
// 数据发送
|
||
if (G.send2local) {
|
||
try { send2local("catch", { ...info, requestHeaders: data.allRequestHeaders }, info.tabId); } catch (e) { console.log(e); }
|
||
}
|
||
|
||
// 储存数据
|
||
cacheData[info.tabId] ??= [];
|
||
cacheData[info.tabId].push(info);
|
||
|
||
// 当前标签媒体数量大于100 开启防抖 等待5秒储存 或 积累10个资源储存一次。
|
||
if (cacheData[info.tabId].length >= 100 && debounceCount <= 10) {
|
||
debounceCount++;
|
||
clearTimeout(debounce);
|
||
debounce = setTimeout(function () { save(info.tabId); }, 5000);
|
||
return;
|
||
}
|
||
// 时间间隔小于500毫秒 等待2秒储存
|
||
if (Date.now() - debounceTime <= 500) {
|
||
clearTimeout(debounce);
|
||
debounceTime = Date.now();
|
||
debounce = setTimeout(function () { save(info.tabId); }, 2000);
|
||
return;
|
||
}
|
||
save(info.tabId);
|
||
});
|
||
}
|
||
// cacheData数据 储存到 chrome.storage.local
|
||
function save(tabId) {
|
||
clearTimeout(debounce);
|
||
debounceTime = Date.now();
|
||
debounceCount = 0;
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData }, function () {
|
||
chrome.runtime.lastError && console.log(chrome.runtime.lastError);
|
||
});
|
||
cacheData[tabId] && SetIcon({ number: cacheData[tabId].length, tabId: tabId });
|
||
}
|
||
|
||
/**
|
||
* 监听 扩展 message 事件
|
||
*/
|
||
chrome.runtime.onMessage.addListener(function (Message, sender, sendResponse) {
|
||
if (chrome.runtime.lastError) { return; }
|
||
if (!G.initLocalComplete || !G.initSyncComplete) {
|
||
sendResponse("error");
|
||
return true;
|
||
}
|
||
// 以下检查是否有 tabId 不存在使用当前标签
|
||
Message.tabId = Message.tabId ?? G.tabId;
|
||
|
||
// 从缓存中保存数据到本地
|
||
if (Message.Message == "pushData") {
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// 获取所有数据
|
||
if (Message.Message == "getAllData") {
|
||
sendResponse(cacheData);
|
||
return true;
|
||
}
|
||
/**
|
||
* 设置扩展图标数字
|
||
* 提供 type 删除标签为 tabId 的数字
|
||
* 不提供type 删除所有标签的数字
|
||
*/
|
||
if (Message.Message == "ClearIcon") {
|
||
Message.type ? SetIcon({ tabId: Message.tabId }) : SetIcon();
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// 启用/禁用扩展
|
||
if (Message.Message == "enable") {
|
||
G.enable = !G.enable;
|
||
chrome.storage.sync.set({ enable: G.enable });
|
||
chrome.action.setIcon({ path: G.enable ? "/img/icon.png" : "/img/icon-disable.png" });
|
||
sendResponse(G.enable);
|
||
return true;
|
||
}
|
||
/**
|
||
* 提供requestId数组 获取指定的数据
|
||
*/
|
||
if (Message.Message == "getData" && Message.requestId) {
|
||
// 判断Message.requestId是否数组
|
||
if (!Array.isArray(Message.requestId)) {
|
||
Message.requestId = [Message.requestId];
|
||
}
|
||
const response = [];
|
||
if (Message.requestId.length) {
|
||
for (let item in cacheData) {
|
||
for (let data of cacheData[item]) {
|
||
if (Message.requestId.includes(data.requestId)) {
|
||
response.push(data);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
sendResponse(response.length ? response : "error");
|
||
return true;
|
||
}
|
||
/**
|
||
* 提供 tabId 获取该标签数据
|
||
*/
|
||
if (Message.Message == "getData") {
|
||
sendResponse(cacheData[Message.tabId]);
|
||
return true;
|
||
}
|
||
/**
|
||
* 获取各按钮状态
|
||
* 模拟手机 自动下载 启用 以及各种脚本状态
|
||
*/
|
||
if (Message.Message == "getButtonState") {
|
||
let state = {
|
||
MobileUserAgent: G.featMobileTabId.has(Message.tabId),
|
||
AutoDown: G.featAutoDownTabId.has(Message.tabId),
|
||
enable: G.enable,
|
||
}
|
||
G.scriptList.forEach(function (item, key) {
|
||
state[item.key] = item.tabId.has(Message.tabId);
|
||
});
|
||
sendResponse(state);
|
||
return true;
|
||
}
|
||
// 对tabId的标签 进行模拟手机操作
|
||
if (Message.Message == "mobileUserAgent") {
|
||
mobileUserAgent(Message.tabId, !G.featMobileTabId.has(Message.tabId));
|
||
chrome.tabs.reload(Message.tabId, { bypassCache: true });
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// 对tabId的标签 开启 关闭 自动下载
|
||
if (Message.Message == "autoDown") {
|
||
if (G.featAutoDownTabId.has(Message.tabId)) {
|
||
G.featAutoDownTabId.delete(Message.tabId);
|
||
} else {
|
||
G.featAutoDownTabId.add(Message.tabId);
|
||
}
|
||
(chrome.storage.session ?? chrome.storage.local).set({ featAutoDownTabId: Array.from(G.featAutoDownTabId) });
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// 对tabId的标签 脚本注入或删除
|
||
if (Message.Message == "script") {
|
||
if (!G.scriptList.has(Message.script)) {
|
||
sendResponse("error no exists");
|
||
return false;
|
||
}
|
||
const script = G.scriptList.get(Message.script);
|
||
const scriptTabid = script.tabId;
|
||
const refresh = Message.refresh ?? script.refresh;
|
||
if (scriptTabid.has(Message.tabId)) {
|
||
scriptTabid.delete(Message.tabId);
|
||
if (Message.script == "search.js") {
|
||
G.deepSearchTemporarilyClose = Message.tabId;
|
||
}
|
||
refresh && chrome.tabs.reload(Message.tabId, { bypassCache: true });
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
scriptTabid.add(Message.tabId);
|
||
if (refresh) {
|
||
chrome.tabs.reload(Message.tabId, { bypassCache: true });
|
||
} else {
|
||
const files = [`catch-script/${Message.script}`];
|
||
script.i18n && files.unshift("catch-script/i18n.js");
|
||
chrome.scripting.executeScript({
|
||
target: { tabId: Message.tabId, allFrames: script.allFrames },
|
||
files: files,
|
||
injectImmediately: true,
|
||
world: script.world
|
||
});
|
||
}
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// 脚本注入 脚本申请多语言文件
|
||
if (Message.Message == "scriptI18n") {
|
||
chrome.scripting.executeScript({
|
||
target: { tabId: Message.tabId, allFrames: true },
|
||
files: ["catch-script/i18n.js"],
|
||
injectImmediately: true,
|
||
world: "MAIN"
|
||
});
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// Heart Beat
|
||
if (Message.Message == "HeartBeat") {
|
||
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
|
||
if (tabs[0] && tabs[0].id) {
|
||
G.tabId = tabs[0].id;
|
||
}
|
||
});
|
||
sendResponse("HeartBeat OK");
|
||
return true;
|
||
}
|
||
// 清理数据
|
||
if (Message.Message == "clearData") {
|
||
// 当前标签
|
||
if (Message.type) {
|
||
delete cacheData[Message.tabId];
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||
clearRedundant();
|
||
sendResponse("OK");
|
||
return true;
|
||
}
|
||
// 其他标签
|
||
for (let item in cacheData) {
|
||
if (item == Message.tabId) { continue; }
|
||
delete cacheData[item];
|
||
}
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||
clearRedundant();
|
||
sendResponse("OK");
|
||
return true;
|
||
}
|
||
// 清理冗余数据
|
||
if (Message.Message == "clearRedundant") {
|
||
clearRedundant();
|
||
sendResponse("OK");
|
||
return true;
|
||
}
|
||
// 从 content-script 或 catch-script 传来的媒体url
|
||
if (Message.Message == "addMedia") {
|
||
chrome.tabs.query({}, function (tabs) {
|
||
for (let item of tabs) {
|
||
if (item.url == Message.href) {
|
||
findMedia({ url: Message.url, tabId: item.id, extraExt: Message.extraExt, mime: Message.mime, requestId: Message.requestId, requestHeaders: Message.requestHeaders }, true, true);
|
||
return true;
|
||
}
|
||
}
|
||
findMedia({ url: Message.url, tabId: -1, extraExt: Message.extraExt, mime: Message.mime, requestId: Message.requestId, initiator: Message.href, requestHeaders: Message.requestHeaders }, true, true);
|
||
});
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// ffmpeg网页通信
|
||
if (Message.Message == "catCatchFFmpeg") {
|
||
const data = { ...Message, Message: "ffmpeg", tabId: Message.tabId ?? sender.tab.id, version: G.ffmpegConfig.version };
|
||
chrome.tabs.query({ url: G.ffmpegConfig.url + "*" }, function (tabs) {
|
||
if (chrome.runtime.lastError || !tabs.length) {
|
||
chrome.tabs.create({ url: G.ffmpegConfig.url, active: Message.active ?? true }, function (tab) {
|
||
if (chrome.runtime.lastError) { return; }
|
||
G.ffmpegConfig.tab = tab.id;
|
||
G.ffmpegConfig.cacheData.push(data);
|
||
});
|
||
return true;
|
||
}
|
||
if (tabs[0].status == "complete") {
|
||
chrome.tabs.sendMessage(tabs[0].id, data);
|
||
} else {
|
||
G.ffmpegConfig.tab = tabs[0].id;
|
||
G.ffmpegConfig.cacheData.push(data);
|
||
}
|
||
});
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
// 发送数据到本地
|
||
if (Message.Message == "send2local" && G.send2local) {
|
||
try { send2local(Message.action, Message.data, Message.tabId); } catch (e) { console.log(e); }
|
||
sendResponse("ok");
|
||
return true;
|
||
}
|
||
});
|
||
|
||
// 选定标签 更新G.tabId
|
||
// chrome.tabs.onHighlighted.addListener(function (activeInfo) {
|
||
// if (activeInfo.windowId == -1 || !activeInfo.tabIds || !activeInfo.tabIds.length) { return; }
|
||
// G.tabId = activeInfo.tabIds[0];
|
||
// });
|
||
|
||
/**
|
||
* 监听 切换标签
|
||
* 更新全局变量 G.tabId 为当前标签
|
||
*/
|
||
chrome.tabs.onActivated.addListener(function (activeInfo) {
|
||
G.tabId = activeInfo.tabId;
|
||
if (cacheData[G.tabId] !== undefined) {
|
||
SetIcon({ number: cacheData[G.tabId].length, tabId: G.tabId });
|
||
return;
|
||
}
|
||
SetIcon({ tabId: G.tabId });
|
||
});
|
||
|
||
// 切换窗口,更新全局变量G.tabId
|
||
chrome.windows.onFocusChanged.addListener(function (activeInfo) {
|
||
if (activeInfo == -1) { return; }
|
||
chrome.tabs.query({ active: true, windowId: activeInfo }, function (tabs) {
|
||
if (tabs[0] && tabs[0].id) {
|
||
G.tabId = tabs[0].id;
|
||
} else {
|
||
G.tabId = -1;
|
||
}
|
||
});
|
||
}, { filters: ["normal"] });
|
||
|
||
/**
|
||
* 监听 标签页面更新
|
||
* 检查 清理数据
|
||
* 检查 是否在屏蔽列表中
|
||
*/
|
||
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
|
||
if (isSpecialPage(tab.url) || tabId <= 0 || !G.initSyncComplete) { return; }
|
||
if (changeInfo.status && changeInfo.status == "loading" && G.autoClearMode == 2) {
|
||
G.urlMap.delete(tabId);
|
||
chrome.alarms.get("save", function (alarm) {
|
||
if (!alarm) {
|
||
delete cacheData[tabId];
|
||
SetIcon({ tabId: tabId });
|
||
chrome.alarms.create("save", { when: Date.now() + 1000 });
|
||
}
|
||
});
|
||
}
|
||
// 检查当前标签是否在屏蔽列表中
|
||
if (changeInfo.url && tabId > 0 && G.blockUrl.length) {
|
||
G.blockUrlSet.delete(tabId);
|
||
if (isLockUrl(changeInfo.url)) {
|
||
G.blockUrlSet.add(tabId);
|
||
}
|
||
}
|
||
chrome.sidePanel.setOptions({
|
||
tabId,
|
||
path: "popup.html?tabId=" + tabId
|
||
});
|
||
});
|
||
|
||
/**
|
||
* 监听 frame 正在载入
|
||
* 检查 是否在屏蔽列表中 (frameId == 0 为主框架)
|
||
* 检查 自动清理 (frameId == 0 为主框架)
|
||
* 检查 注入脚本
|
||
*/
|
||
chrome.webNavigation.onCommitted.addListener(function (details) {
|
||
if (isSpecialPage(details.url) || details.tabId <= 0 || !G.initSyncComplete) { return; }
|
||
|
||
// 刷新页面 检查是否在屏蔽列表中
|
||
if (details.frameId == 0 && details.transitionType == "reload") {
|
||
G.blockUrlSet.delete(details.tabId);
|
||
if (isLockUrl(details.url)) {
|
||
G.blockUrlSet.add(details.tabId);
|
||
}
|
||
}
|
||
|
||
// 刷新清理角标数
|
||
if (details.frameId == 0 && (!['auto_subframe', 'manual_subframe', 'form_submit'].includes(details.transitionType)) && G.autoClearMode == 1) {
|
||
delete cacheData[details.tabId];
|
||
G.urlMap.delete(details.tabId);
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||
SetIcon({ tabId: details.tabId });
|
||
}
|
||
|
||
// chrome内核版本 102 以下不支持 chrome.scripting.executeScript API
|
||
if (G.version < 102) { return; }
|
||
|
||
if (G.deepSearch && G.deepSearchTemporarilyClose != details.tabId) {
|
||
G.scriptList.get("search.js").tabId.add(details.tabId);
|
||
G.deepSearchTemporarilyClose = null;
|
||
}
|
||
|
||
// catch-script 脚本
|
||
G.scriptList.forEach(function (item, script) {
|
||
if (!item.tabId.has(details.tabId) || !item.allFrames) { return true; }
|
||
|
||
const files = [`catch-script/${script}`];
|
||
item.i18n && files.unshift("catch-script/i18n.js");
|
||
chrome.scripting.executeScript({
|
||
target: { tabId: details.tabId, frameIds: [details.frameId] },
|
||
files: files,
|
||
injectImmediately: true,
|
||
world: item.world
|
||
});
|
||
});
|
||
|
||
// 模拟手机
|
||
if (G.initLocalComplete && G.featMobileTabId.size > 0 && G.featMobileTabId.has(details.tabId)) {
|
||
chrome.scripting.executeScript({
|
||
args: [G.MobileUserAgent.toString()],
|
||
target: { tabId: details.tabId, frameIds: [details.frameId] },
|
||
func: function () {
|
||
Object.defineProperty(navigator, 'userAgent', { value: arguments[0], writable: false });
|
||
},
|
||
injectImmediately: true,
|
||
world: "MAIN"
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 监听 标签关闭 清理数据
|
||
*/
|
||
chrome.tabs.onRemoved.addListener(function (tabId) {
|
||
// 清理缓存数据
|
||
chrome.alarms.get("nowClear", function (alarm) {
|
||
!alarm && chrome.alarms.create("nowClear", { when: Date.now() + 1000 });
|
||
});
|
||
if (G.initSyncComplete) {
|
||
G.blockUrlSet.has(tabId) && G.blockUrlSet.delete(tabId);
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 浏览器 扩展快捷键
|
||
*/
|
||
chrome.commands.onCommand.addListener(function (command) {
|
||
if (command == "auto_down") {
|
||
if (G.featAutoDownTabId.has(G.tabId)) {
|
||
G.featAutoDownTabId.delete(G.tabId);
|
||
} else {
|
||
G.featAutoDownTabId.add(G.tabId);
|
||
}
|
||
(chrome.storage.session ?? chrome.storage.local).set({ featAutoDownTabId: Array.from(G.featAutoDownTabId) });
|
||
} else if (command == "catch") {
|
||
const scriptTabid = G.scriptList.get("catch.js").tabId;
|
||
scriptTabid.has(G.tabId) ? scriptTabid.delete(G.tabId) : scriptTabid.add(G.tabId);
|
||
chrome.tabs.reload(G.tabId, { bypassCache: true });
|
||
} else if (command == "m3u8") {
|
||
chrome.tabs.create({ url: "m3u8.html" });
|
||
} else if (command == "clear") {
|
||
delete cacheData[G.tabId];
|
||
(chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||
clearRedundant();
|
||
SetIcon({ tabId: G.tabId });
|
||
} else if (command == "enable") {
|
||
G.enable = !G.enable;
|
||
chrome.storage.sync.set({ enable: G.enable });
|
||
chrome.action.setIcon({ path: G.enable ? "/img/icon.png" : "/img/icon-disable.png" });
|
||
} else if (command == "reboot") {
|
||
chrome.runtime.reload();
|
||
} else if (command == "deepSearch") {
|
||
const script = G.scriptList.get("search.js");
|
||
const scriptTabid = script.tabId;
|
||
if (scriptTabid.has(G.tabId)) {
|
||
scriptTabid.delete(G.tabId);
|
||
G.deepSearchTemporarilyClose = G.tabId;
|
||
chrome.tabs.reload(G.tabId, { bypassCache: true });
|
||
return;
|
||
}
|
||
scriptTabid.add(G.tabId);
|
||
chrome.tabs.reload(G.tabId, { bypassCache: true });
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 监听 页面完全加载完成 判断是否在线ffmpeg页面
|
||
* 如果是在线ffmpeg 则发送数据
|
||
*/
|
||
chrome.webNavigation.onCompleted.addListener(function (details) {
|
||
if (G.ffmpegConfig.tab && details.tabId == G.ffmpegConfig.tab) {
|
||
setTimeout(() => {
|
||
G.ffmpegConfig.cacheData.forEach(data => {
|
||
chrome.tabs.sendMessage(details.tabId, data);
|
||
});
|
||
G.ffmpegConfig.cacheData = [];
|
||
G.ffmpegConfig.tab = 0;
|
||
}, 500);
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 检查扩展名和大小
|
||
* @param {String} ext
|
||
* @param {Number} size
|
||
* @returns {Boolean|String}
|
||
*/
|
||
function CheckExtension(ext, size) {
|
||
const Ext = G.Ext.get(ext);
|
||
if (!Ext) { return false; }
|
||
if (!Ext.state) { return "break"; }
|
||
if (Ext.size != 0 && size != undefined && size <= Ext.size * 1024) { return "break"; }
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 检查类型和大小
|
||
* @param {String} dataType
|
||
* @param {Number} dataSize
|
||
* @returns {Boolean|String}
|
||
*/
|
||
function CheckType(dataType, dataSize) {
|
||
const typeInfo = G.Type.get(dataType.split("/")[0] + "/*") || G.Type.get(dataType);
|
||
if (!typeInfo) { return false; }
|
||
if (!typeInfo.state) { return "break"; }
|
||
if (typeInfo.size != 0 && dataSize != undefined && dataSize <= typeInfo.size * 1024) { return "break"; }
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 获取文件名及扩展名
|
||
* @param {String} pathname
|
||
* @returns {Array}
|
||
*/
|
||
function fileNameParse(pathname) {
|
||
let fileName = decodeURI(pathname.split("/").pop());
|
||
let ext = fileName.split(".");
|
||
ext = ext.length == 1 ? undefined : ext.pop().toLowerCase();
|
||
return [fileName, ext ? ext : undefined];
|
||
}
|
||
|
||
/**
|
||
* 获取响应头信息
|
||
* @param {Object} data
|
||
* @returns {Object}
|
||
*/
|
||
function getResponseHeadersValue(data) {
|
||
const header = {};
|
||
if (data.responseHeaders == undefined || data.responseHeaders.length == 0) { return header; }
|
||
for (let item of data.responseHeaders) {
|
||
item.name = item.name.toLowerCase();
|
||
if (item.name == "content-length") {
|
||
header.size ??= parseInt(item.value);
|
||
} else if (item.name == "content-type") {
|
||
header.type = item.value.split(";")[0].toLowerCase();
|
||
} else if (item.name == "content-disposition") {
|
||
header.attachment = item.value;
|
||
} else if (item.name == "content-range") {
|
||
let size = item.value.split('/')[1];
|
||
if (size !== '*') {
|
||
header.size = parseInt(size);
|
||
}
|
||
}
|
||
}
|
||
return header;
|
||
}
|
||
|
||
/**
|
||
* 获取请求头
|
||
* @param {Object} data
|
||
* @returns {Object|Boolean}
|
||
*/
|
||
function getRequestHeaders(data) {
|
||
if (data.allRequestHeaders == undefined || data.allRequestHeaders.length == 0) { return false; }
|
||
const header = {};
|
||
for (let item of data.allRequestHeaders) {
|
||
item.name = item.name.toLowerCase();
|
||
if (item.name == "referer") {
|
||
header.referer = item.value;
|
||
} else if (item.name == "origin") {
|
||
header.origin = item.value;
|
||
} else if (item.name == "cookie") {
|
||
header.cookie = item.value;
|
||
} else if (item.name == "authorization") {
|
||
header.authorization = item.value;
|
||
}
|
||
}
|
||
if (Object.keys(header).length) {
|
||
return header;
|
||
}
|
||
return false;
|
||
}
|
||
//设置扩展图标
|
||
function SetIcon(obj) {
|
||
if (obj?.number == 0 || obj?.number == undefined) {
|
||
chrome.action.setBadgeText({ text: "", tabId: obj?.tabId ?? G.tabId }, function () { if (chrome.runtime.lastError) { return; } });
|
||
// chrome.action.setTitle({ title: "还没闻到味儿~", tabId: obj.tabId }, function () { if (chrome.runtime.lastError) { return; } });
|
||
} else if (G.badgeNumber) {
|
||
obj.number = obj.number > 999 ? "999+" : obj.number.toString();
|
||
chrome.action.setBadgeText({ text: obj.number, tabId: obj.tabId }, function () { if (chrome.runtime.lastError) { return; } });
|
||
// chrome.action.setTitle({ title: "抓到 " + obj.number + " 条鱼", tabId: obj.tabId }, function () { if (chrome.runtime.lastError) { return; } });
|
||
}
|
||
}
|
||
|
||
// 模拟手机端
|
||
function mobileUserAgent(tabId, change = false) {
|
||
if (change) {
|
||
G.featMobileTabId.add(tabId);
|
||
(chrome.storage.session ?? chrome.storage.local).set({ featMobileTabId: Array.from(G.featMobileTabId) });
|
||
chrome.declarativeNetRequest.updateSessionRules({
|
||
removeRuleIds: [tabId],
|
||
addRules: [{
|
||
"id": tabId,
|
||
"action": {
|
||
"type": "modifyHeaders",
|
||
"requestHeaders": [{
|
||
"header": "User-Agent",
|
||
"operation": "set",
|
||
"value": G.MobileUserAgent
|
||
}]
|
||
},
|
||
"condition": {
|
||
"tabIds": [tabId],
|
||
"resourceTypes": Object.values(chrome.declarativeNetRequest.ResourceType)
|
||
}
|
||
}]
|
||
});
|
||
return true;
|
||
}
|
||
G.featMobileTabId.delete(tabId) && (chrome.storage.session ?? chrome.storage.local).set({ featMobileTabId: Array.from(G.featMobileTabId) });
|
||
chrome.declarativeNetRequest.updateSessionRules({
|
||
removeRuleIds: [tabId]
|
||
});
|
||
}
|
||
|
||
// 判断特殊页面
|
||
function isSpecialPage(url) {
|
||
if (!url || url == "null") { return true; }
|
||
return !(url.startsWith("http://") || url.startsWith("https://") || url.startsWith("blob:"));
|
||
}
|
||
|
||
// 测试
|
||
// chrome.storage.local.get(function (data) { console.log(data.MediaData) });
|
||
// chrome.declarativeNetRequest.getSessionRules(function (rules) { console.log(rules); });
|
||
// chrome.tabs.query({}, function (tabs) { for (let item of tabs) { console.log(item.id); } });
|