// ==UserScript==
// @name Media Volume Booster
// @name:zh-TW 媒體音量增強器
// @name:zh-CN 媒体音量增强器
// @name:en Media Volume Booster
// @version 2025.08.08-Beta
// @author Canaan HS
// @description 調整媒體音量與濾波器,增強倍數最高 20 倍,設置可記住並自動應用。部分網站可能無效、無聲音或無法播放,可選擇禁用。
// @description:zh-TW 調整媒體音量與濾波器,增強倍數最高 20 倍,設置可記住並自動應用。部分網站可能無效、無聲音或無法播放,可選擇禁用。
// @description:zh-CN 调整媒体音量与滤波器,增强倍数最高 20 倍,设置可记住并自动应用。部分网站可能无效、无声音或无法播放,可选择禁用。
// @description:en Adjust media volume and filters with enhancement factor up to 20x. Settings are saved and auto-applied. May not work on some sites (causing no sound or playback issues). Can be disabled if needed.
// @noframes
// @match *://*/*
// @icon https://cdn-icons-png.flaticon.com/512/16108/16108408.png
// @license MPL-2.0
// @namespace https://greasyfork.org/users/989635
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_getResourceURL
// @grant GM_registerMenuCommand
// @grant GM_addValueChangeListener
// @resource Img https://cdn-icons-png.flaticon.com/512/11243/11243783.png
// @require https://update.greasyfork.org/scripts/487608/1637584/SyntaxLite_min.js
// @run-at document-body
// ==/UserScript==
(function () {
const Share = {
Parame: null,
SetControl: null
};
const Default = {
Gain: 1,
LowFilterGain: 1.2,
LowFilterFreq: 200,
MidFilterQ: 1,
MidFilterGain: 1.6,
MidFilterFreq: 2e3,
HighFilterGain: 1.8,
HighFilterFreq: 1e4,
CompressorRatio: 3,
CompressorKnee: 4,
CompressorThreshold: -8,
CompressorAttack: .03,
CompressorRelease: .2
};
function CreateMenu(Lib2, Share2, Img, Transl2) {
return async () => {
const shadowID = "Booster_Menu";
if (Lib2.$q(`#${shadowID}`)) return;
const shadow = Lib2.createElement(Lib2.body, "div", {
id: shadowID
});
const shadowRoot = shadow.attachShadow({
mode: "open"
});
const style = `
<style>
:host {
--primary-color: #3a7bfd;
--secondary-color: #00d4ff;
--text-color: #ffffff;
--slider-track: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
--background-dark: #1a1f2c;
--background-panel: #252b3a;
--highlight-color: #00e5ff;
--border-radius: 12px;
--hover-bg: rgba(0, 229, 255, 0.06);
--hover-border: rgba(0, 229, 255, 0.15);
}
${shadowID} {
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
z-index: 999999;
overflow: auto;
position: fixed;
align-items: center;
justify-content: center;
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
transition: opacity 0.4s ease;
background-color: rgba(0, 0, 0, 0.4);
}
${shadowID}.close {
animation: fadeOut 0.4s ease forwards;
}
.Booster-Modal-Content {
min-width: 420px;
max-width: 460px;
width: 100%;
padding: 20px;
padding-inline-end: 10px;
overflow-y: auto;
scrollbar-gutter: stable;
text-align: center;
border-radius: var(--border-radius);
background-color: var(--background-dark);
border: 1px solid rgba(78, 164, 255, 0.3);
box-shadow:
inset -6px 0 10px -8px rgba(0, 0, 0, 0.5),
0 10px 30px rgba(0, 0, 0, 0.5),
0 0 15px rgba(0, 212, 255, 0.2);
color: var(--text-color);
max-height: 85vh;
transition: all 0.5s ease;
}
.Booster-Modal-Content.close {
animation: shrinkFadeOut 0.8s ease forwards;
}
.Booster-Modal-Content::-webkit-scrollbar {
width: 8px;
}
.Booster-Modal-Content::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 8px;
}
.Booster-Modal-Content::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
}
.Booster-Title {
margin-top: 0;
color: var(--secondary-color);
font-size: 22px;
font-weight: 600;
letter-spacing: 0.5px;
margin-bottom: 20px;
text-shadow: 0 0 10px rgba(0, 212, 255, 0.4);
transform: translateY(-10px);
}
.Booster-Multiplier {
margin: 1.5rem 0;
font-size: 22px;
font-weight: 500;
}
.Booster-Multiplier img {
width: 24px;
margin-right: 8px;
vertical-align: middle;
}
.Booster-Multiplier span {
display: flex;
align-items: center;
justify-content: center;
}
#Booster-CurrentValue {
color: var(--highlight-color);
font-weight: 700;
margin: 0 5px;
font-size: 26px;
}
.Booster-Slider {
-webkit-appearance: none;
appearance: none;
width: 90%;
height: 6px;
cursor: pointer;
margin: 2rem 0 3.5rem 0;
background: var(--slider-track);
border-radius: 3px;
outline: none;
}
.Booster-Slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--secondary-color);
cursor: pointer;
box-shadow: 0 0 8px rgba(0, 212, 255, 0.6);
}
.Booster-Slider::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--secondary-color);
cursor: pointer;
border: none;
box-shadow: 0 0 8px rgba(0, 212, 255, 0.6);
}
.Booster-Slider::-moz-range-progress {
background: var(--slider-track);
border-radius: 3px;
height: 6px;
}
.Booster-Buttons {
display: flex;
justify-content: flex-end;
margin-top: 20px;
gap: 10px;
}
.Booster-Modal-Button {
color: var(--text-color);
cursor: pointer;
font-size: 15px;
font-weight: 500;
padding: 8px 16px;
border-radius: 6px;
background-color: rgba(58, 123, 253, 0.2);
border: 1px solid rgba(78, 164, 255, 0.3);
transition: all 0.2s ease;
outline: none;
}
.Booster-Modal-Button:hover {
background-color: rgba(58, 123, 253, 0.4);
box-shadow: 0 0 10px rgba(0, 212, 255, 0.4);
transform: translateY(-2px);
}
#Booster-Sound-Save {
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
border: none;
position: relative;
overflow: hidden;
}
#Booster-Sound-Save:hover {
box-shadow: 0 0 15px rgba(0, 212, 255, 0.6);
}
#Booster-Sound-Save:after {
content: "";
position: absolute;
top: -50%;
left: -60%;
width: 20%;
height: 200%;
transform: rotate(30deg);
background: rgba(255, 255, 255, 0.13);
background: linear-gradient(
to right,
rgba(255, 255, 255, 0.13) 0%,
rgba(255, 255, 255, 0.13) 77%,
rgba(255, 255, 255, 0.5) 92%,
rgba(255, 255, 255, 0.0) 100%
);
}
#Booster-Sound-Save:hover:after {
opacity: 1;
left: 130%;
transition: left 0.7s ease, opacity 0.5s ease;
}
.Booster-Accordion {
background-color: var(--background-panel);
color: var(--text-color);
cursor: pointer;
padding: 12px 15px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.3s;
border-radius: 8px;
margin-bottom: 8px;
font-weight: 500;
display: flex;
justify-content: space-between;
align-items: center;
transform: translateY(10px);
}
.Booster-Accordion:after {
content: '+';
color: var(--secondary-color);
font-weight: bold;
float: right;
margin-left: 5px;
}
.Booster-Accordion.active {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin-bottom: 0;
}
.Booster-Accordion.active:after {
content: "-";
}
.Booster-Panel {
max-height: 0;
overflow: hidden;
padding: 0 15px;
margin-top: 0;
margin-bottom: 8px;
transition: max-height 0.3s ease-out;
background-color: var(--background-panel);
border-radius: 0 0 8px 8px;
}
.Booster-Panel.active {
margin-bottom: 15px;
padding: 10px 15px 15px;
}
.Booster-Control-Group {
margin-bottom: 15px;
}
.Booster-Control-Label {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
}
.Booster-Mini-Slider {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 4px;
background: var(--slider-track);
border-radius: 2px;
outline: none;
}
.Booster-Mini-Slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--secondary-color);
cursor: pointer;
}
.Booster-Mini-Slider::-moz-range-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--secondary-color);
cursor: pointer;
border: none;
}
.Booster-Mini-Slider::-moz-range-progress {
background: var(--slider-track);
border-radius: 2px;
height: 4px;
}
.Booster-Label {
padding: 0.1rem 0.2rem;
font-size: larger;
font-weight: bolder;
cursor: pointer;
border-radius: 6px;
min-width: 50px;
text-align: center;
color: var(--highlight-color);
transition: background-color 0.2s;
}
.Booster-Label:hover {
background-color: var(--hover-bg);
border-color: var(--hover-border);
box-shadow: 0 0 0.5rem rgba(0, 229, 255, 0.12);
transform: translateY(-1px);
}
.Booster-Label-Input {
width: 60px;
padding: 2px 5px;
font-size: 18px;
font-weight: bolder;
color: var(--text-color);
background-color: var(--background-panel);
border: 1px solid var(--primary-color);
border-radius: 4px;
text-align: center;
outline: none;
}
@keyframes fadeOut {
from {opacity: 1;}
to {opacity: 0; pointer-events: none;}
}
@keyframes shrinkFadeOut {
from {transform: scale(1); opacity: 1;}
to {transform: scale(0.5); opacity: 0;}
}
</style>
`;
const generateOtherTemplate = (label, groups) => `
<button class="Booster-Accordion">${Transl2(label)}</button>
<div class="Booster-Panel">
${groups.map(group => `
<div class="Booster-Control-Group">
<div class="Booster-Control-Label">
<span>${Transl2(group.label)}</span>
<span id="${group.id}-Label" class="Booster-Label">${Share2.Parame[group.id]}</span>
</div>
<input type="range" id="${group.id}" class="Booster-Mini-Slider" min="${group.min}" max="${group.max}" value="${Share2.Parame[group.id]}" step="${group.step}">
</div>
`).join("")}
</div>
`;
shadowRoot.$iHtml(`
${style}
<${shadowID} id="Booster-Modal-Menu">
<div class="Booster-Modal-Content">
<h2 class="Booster-Title">${Transl2("音量增強器")}</h2>
<div class="Booster-Multiplier">
<span>
<img src="${Img}">${Transl2("增強倍數 ")}
<span id="Gain-Label" class="Booster-Label">${Share2.Parame.Gain}</span>${Transl2(" 倍")}
</span>
<input type="range" id="Gain" class="Booster-Slider" min="0" max="20.0" value="${Share2.Parame.Gain}" step="0.1">
</div>
${generateOtherTemplate("低頻設定", [{
label: "增益",
id: "LowFilterGain",
min: "-12",
max: "12",
step: "0.1"
}, {
label: "頻率",
id: "LowFilterFreq",
min: "20",
max: "1000",
step: "20"
}])}
${generateOtherTemplate("中頻設定", [{
label: "增益",
id: "MidFilterGain",
min: "-12",
max: "12",
step: "0.1"
}, {
label: "頻率",
id: "MidFilterFreq",
min: "200",
max: "8000",
step: "100"
}, {
label: "Q值",
id: "MidFilterQ",
min: "0.5",
max: "5",
step: "0.1"
}])}
${generateOtherTemplate("高頻設定", [{
label: "增益",
id: "HighFilterGain",
min: "-12",
max: "12",
step: "0.1"
}, {
label: "頻率",
id: "HighFilterFreq",
min: "2000",
max: "22000",
step: "500"
}])}
${generateOtherTemplate("動態壓縮", [{
label: "壓縮率",
id: "CompressorRatio",
min: "1",
max: "30",
step: "0.1"
}, {
label: "過渡反應",
id: "CompressorKnee",
min: "0",
max: "40",
step: "1"
}, {
label: "閾值",
id: "CompressorThreshold",
min: "-60",
max: "0",
step: "1"
}, {
label: "起音速度",
id: "CompressorAttack",
min: "0.001",
max: "0.5",
step: "0.001"
}, {
label: "釋放速度",
id: "CompressorRelease",
min: "0.01",
max: "2",
step: "0.01"
}])}
<div class="Booster-Buttons">
<button class="Booster-Modal-Button" id="Booster-Menu-Close">${Transl2("關閉")}</button>
<button class="Booster-Modal-Button" id="Booster-Sound-Save">${Transl2("保存")}</button>
</div>
</div>
</${shadowID}>
`);
const shadowGate = shadow.shadowRoot;
const modal = shadowGate.querySelector(shadowID);
const content = shadowGate.querySelector(".Booster-Modal-Content");
function deleteMenu() {
modal.classList.add("close");
content.classList.add("close");
setTimeout(() => {
shadow.remove();
}, 800);
}
const displayMap = {
...Object.fromEntries([...shadowGate.querySelectorAll(".Booster-Label")].map(el => [el.id, el]))
};
function updateControl(id, value) {
displayMap[`${id}-Label`].textContent = value;
shadowGate.querySelector(`#${id}`).value = value;
Share2.SetControl(id, value);
}
content.addEventListener("input", event => {
const target = event.target;
if (target.type !== "range") return;
const id = target.id;
const value = parseFloat(target.value);
updateControl(id, value);
});
content.addEventListener("click", event => {
const target = event.target;
if (!target.classList.contains("Booster-Label") || target.isEditing) return;
target.isEditing = true;
const originalValue = target.textContent.trim();
const controlId = target.id.replace("-Label", "");
const slider = shadowGate.querySelector(`#${controlId}`);
const input = Lib2.createElement("input", {
class: "Booster-Label-Input",
value: originalValue,
on: [{
type: "blur",
listener: () => {
let newValue = parseFloat(input.value);
const min = parseFloat(slider.min);
const max = parseFloat(slider.max);
if (isNaN(newValue)) {
newValue = parseFloat(originalValue);
} else if (newValue < min) {
newValue = min;
} else if (newValue > max) {
newValue = max;
}
target.isEditing = false;
updateControl(controlId, newValue);
target.textContent = newValue;
},
add: {
once: true
}
}, {
type: "keydown",
listener: e => {
if (e.key === "Enter") e.target.blur();
if (e.key === "Escape") {
e.target.value = originalValue;
e.target.blur();
}
}
}]
});
target.textContent = "";
target.appendChild(input);
input.focus();
});
modal.addEventListener("click", click => {
const target = click.target;
click.stopPropagation();
if (target.classList.contains("Booster-Accordion")) {
target.classList.toggle("active");
const panel = target.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
panel.classList.remove("active");
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
panel.classList.add("active");
}
} else if (target.id === "Booster-Sound-Save") {
Lib2.setV(Lib2.domain, Share2.Parame);
deleteMenu();
} else if (target.id === "Booster-Menu-Close" || target.id === "Booster-Modal-Menu") {
deleteMenu();
}
});
};
}
const Words = {
Traditional: {},
Simplified: {
"🛠️ 調整菜單": "🛠️ 调整菜单",
"✂️ 斷開增幅": "✂️ 断开增幅",
"🔗 恢復增幅": "🔗 恢复增幅",
"❌ 禁用網域": "❌ 禁用网域",
"✅ 啟用網域": "✅ 启用网域",
"增強錯誤": "增强错误",
"音量增強器": "音量增强器",
"增強倍數 ": "增强倍数 ",
" 倍": " 倍",
"增益": "增益",
"頻率": "频率",
"Q值": "Q值",
"低頻設定": "低频设置",
"中頻設定": "中频设置",
"高頻設定": "高频设置",
"動態壓縮": "动态压缩",
"壓縮率": "压缩率",
"過渡反應": "过渡反应",
"閾值": "阈值",
"起音速度": "起音速度",
"釋放速度": "释放速度",
"關閉": "关闭",
"保存": "保存",
"不支援的媒體跳過": "不支持的媒体跳过",
"不支援音頻增強節點": "不支持音频增强节点",
"添加增強節點成功": "添加增强节点成功",
"添加增強節點失敗": "添加增强节点失败",
"當前沒有被增幅的媒體": "当前没有被增幅的媒体",
"快捷組合 : (Alt + B)": "快捷组合 : (Alt + B)"
},
English: {
"🛠️ 調整菜單": "🛠️ Settings Menu",
"✂️ 斷開增幅": "✂️ Disconnect Amplification",
"🔗 恢復增幅": "🔗 Restore Amplification",
"❌ 禁用網域": "❌ Disable Domain",
"✅ 啟用網域": "✅ Enable Domain",
"增強錯誤": "Enhancement Error",
"音量增強器": "Volume Booster",
"增強倍數 ": "Enhancement Multiplier ",
" 倍": "x",
"增益": "Gain",
"頻率": "Frequency",
"Q值": "Q Factor",
"低頻設定": "Low Frequency Settings",
"中頻設定": "Mid Frequency Settings",
"高頻設定": "High Frequency Settings",
"動態壓縮": "Dynamic Compressor",
"壓縮率": "Compression Ratio",
"過渡反應": "Knee",
"閾值": "Threshold",
"起音速度": "Attack",
"釋放速度": "Release",
"關閉": "Close",
"保存": "Save",
"不支援的媒體跳過": "Unsupported Media Skipped",
"不支援音頻增強節點": "Audio Enhancement Node Not Supported",
"添加增強節點成功": "Enhancement Node Added Successfully",
"添加增強節點失敗": "Failed to Add Enhancement Node",
"當前沒有被增幅的媒體": "No media is currently being amplified",
"快捷組合 : (Alt + B)": "Shortcut: (Alt + B)"
}
};
const bannedDomains = (() => {
let banned = new Set(Lib.getV("Banned", []));
let excludeStatus = banned.has(Lib.$domain);
return {
isEnabled: callback => callback(!excludeStatus),
addBanned: async () => {
excludeStatus ? banned.delete(Lib.$domain) : banned.add(Lib.$domain);
Lib.setV("Banned", [...banned]);
location.reload();
}
};
})();
const {
Transl
} = (() => {
const matcher = Lib.translMatcher(Words);
return {
Transl: Str => matcher[Str] ?? Str
};
})();
(async () => {
let menu = null;
let updated = false;
let processing = false;
let initialized = false;
const enhancedNodes = [];
const processedElements = new Map();
let mediaAudioContent = null;
const audioContext = window.AudioContext || window.webkitAudioContext;
const updateParame = () => {
let Config = Lib.getV(Lib.$domain, {});
if (typeof Config === "number") {
Config = {
Gain: Config
};
}
Share.Parame = Object.assign(Default, Config);
};
function boosterCore(mediaObject) {
try {
if (!audioContext) throw new Error(Transl("不支援音頻增強節點"));
if (!mediaAudioContent) mediaAudioContent = new audioContext();
if (mediaAudioContent.state === "suspended") mediaAudioContent.resume();
const successNode = [];
for (const media of mediaObject) {
processedElements.set(media, true);
if (media.mediaKeys || media.encrypted || window.MediaSource && media.srcObject instanceof MediaSource) {
Lib.log(Transl("不支援的媒體跳過"), media, {
collapsed: false
});
continue;
}
try {
if (!media.crossOrigin) media.crossOrigin = "anonymous";
const SourceNode = mediaAudioContent.createMediaElementSource(media);
const GainNode = mediaAudioContent.createGain();
const LowFilterNode = mediaAudioContent.createBiquadFilter();
const MidFilterNode = mediaAudioContent.createBiquadFilter();
const HighFilterNode = mediaAudioContent.createBiquadFilter();
const CompressorNode = mediaAudioContent.createDynamicsCompressor();
GainNode.gain.value = Share.Parame.Gain;
LowFilterNode.type = "lowshelf";
LowFilterNode.gain.value = Share.Parame.LowFilterGain;
LowFilterNode.frequency.value = Share.Parame.LowFilterFreq;
MidFilterNode.type = "peaking";
MidFilterNode.Q.value = Share.Parame.MidFilterQ;
MidFilterNode.gain.value = Share.Parame.MidFilterGain;
MidFilterNode.frequency.value = Share.Parame.MidFilterFreq;
HighFilterNode.type = "highshelf";
HighFilterNode.gain.value = Share.Parame.HighFilterGain;
HighFilterNode.frequency.value = Share.Parame.HighFilterFreq;
CompressorNode.ratio.value = Share.Parame.CompressorRatio;
CompressorNode.knee.value = Share.Parame.CompressorKnee;
CompressorNode.threshold.value = Share.Parame.CompressorThreshold;
CompressorNode.attack.value = Share.Parame.CompressorAttack;
CompressorNode.release.value = Share.Parame.CompressorRelease;
SourceNode.connect(GainNode).connect(LowFilterNode).connect(MidFilterNode).connect(HighFilterNode).connect(CompressorNode).connect(mediaAudioContent.destination);
enhancedNodes.push({
Connected: true,
Destination: mediaAudioContent.destination,
SourceNode: SourceNode,
GainNode: GainNode,
LowFilterNode: LowFilterNode,
MidFilterNode: MidFilterNode,
HighFilterNode: HighFilterNode,
CompressorNode: CompressorNode,
Gain: GainNode.gain,
LowFilterGain: LowFilterNode.gain,
LowFilterFreq: LowFilterNode.frequency,
MidFilterQ: MidFilterNode.Q,
MidFilterGain: MidFilterNode.gain,
MidFilterFreq: MidFilterNode.frequency,
HighFilterGain: HighFilterNode.gain,
HighFilterFreq: HighFilterNode.frequency,
CompressorRatio: CompressorNode.ratio,
CompressorKnee: CompressorNode.knee,
CompressorThreshold: CompressorNode.threshold,
CompressorAttack: CompressorNode.attack,
CompressorRelease: CompressorNode.release
});
successNode.push(media);
} catch (e) {
Lib.log(Transl("添加增強節點失敗"), media, {
collapsed: false
});
}
}
if (successNode.length > 0) {
processing = false;
Lib.log(Transl("添加增強節點成功"), successNode, {
collapsed: false
});
if (!initialized) {
let regChange2 = function () {
Lib.regMenu({
[Transl(disconnected ? "🔗 恢復增幅" : "✂️ 斷開增幅")]: () => {
if (enhancedNodes.length === 0) {
alert(Transl("當前沒有被增幅的媒體"));
return;
}
enhancedNodes.forEach(items => {
const {
Connected,
SourceNode,
GainNode,
LowFilterNode,
MidFilterNode,
HighFilterNode,
CompressorNode,
Destination
} = items;
if (disconnected && !Connected) {
SourceNode.connect(GainNode).connect(LowFilterNode).connect(MidFilterNode).connect(HighFilterNode).connect(CompressorNode).connect(Destination);
items.Connected = true;
} else if (!disconnected && Connected) {
SourceNode.disconnect();
GainNode.disconnect();
LowFilterNode.disconnect();
MidFilterNode.disconnect();
HighFilterNode.disconnect();
CompressorNode.disconnect();
SourceNode.connect(Destination);
items.Connected = false;
}
});
disconnected = !disconnected;
regChange2();
},
[Transl("🛠️ 調整菜單")]: {
desc: Transl("快捷組合 : (Alt + B)"),
func: () => {
menu();
}
}
}, {
index: 2
});
};
var regChange = regChange2;
initialized = true;
let disconnected = false;
regChange2();
Lib.onEvent(document, "keydown", event => {
if (event.altKey && event.key.toUpperCase() == "B") menu();
}, {
passive: true,
capture: true,
mark: "Media-Booster-Hotkey"
});
Lib.storeListen([Lib.$domain], call => {
if (call.far && call.key === Lib.$domain) {
Object.entries(call.nv).forEach(([type, value]) => {
Share.SetControl(type, value);
});
}
});
}
}
} catch (error) {
Lib.log(Transl("增強錯誤"), error, {
type: "error",
collapsed: false
});
}
}
function trigger(media) {
try {
if (!updated) {
updated = true;
updateParame();
}
boosterCore(media);
} catch (error) {
Lib.log("Trigger Error : ", error, {
type: "error",
collapsed: false
});
}
}
bannedDomains.isEnabled(Status => {
const regMenu = async name => {
Lib.regMenu({
[name]: () => bannedDomains.addBanned()
});
};
if (Status) {
Share.SetControl = (type, value) => {
Share.Parame[type] = value;
enhancedNodes.forEach(items => {
items[type].value = value;
});
};
menu = CreateMenu(Lib, Share, GM_getResourceURL("Img"), Transl);
const findMedia = Lib.$debounce(func => {
const media = [];
const tree = document.createTreeWalker(Lib.body, NodeFilter.SHOW_ELEMENT, {
acceptNode: node => {
const tag = node.tagName;
if (tag === "VIDEO" || tag === "AUDIO") {
if (!processedElements.has(node)) return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_SKIP;
}
});
while (tree.nextNode()) {
media.push(tree.currentNode);
}
media.length > 0 && func(media);
}, 50);
Lib.$observer(Lib.body, () => {
if (processing) return;
findMedia(media => {
processing = true;
trigger(media);
});
}, {
mark: "Media-Booster",
attributes: false,
throttle: 200
}, () => {
regMenu(Transl("❌ 禁用網域"));
});
Lib.onUrlChange(() => {
processedElements.clear();
});
} else regMenu(Transl("✅ 啟用網域"));
});
})();
})();