您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
增強影片音量上限 , 最高增幅至10倍 , 未測試是否所有網域皆可使用 *://*/* , 目前只match特定網域
当前为
// ==UserScript== // @name Video Volume Booster // @version 0.0.24 // @author HentaiSaru // @license MIT // @icon https://cdn-icons-png.flaticon.com/512/8298/8298181.png // @description 增強影片音量上限 , 最高增幅至10倍 , 未測試是否所有網域皆可使用 *://*/* , 目前只match特定網域 // @run-at document-start // @match *://*.twitch.tv/* // @match *://*.youtube.com/* // @match *://*.bilibili.com/* // @exclude *://video.eyny.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @namespace https://greasyfork.org/users/989635 // ==/UserScript== var Booster, modal, enabledDomains = GM_getValue("啟用網域", []), domain = window.location.hostname, Increase = 1.0; (function() { FindVideo(); MenuHotkey(); MonitorAjax();// 暴力解法(多少影響效能 , 個人沒感覺) GM_registerMenuCommand("🔊 [開關] 自動增幅", function() {Useboost(enabledDomains, domain)}); GM_registerMenuCommand("🛠️ 設置增幅", function() {IncrementalSetting()}); GM_registerMenuCommand("📜 菜單熱鍵", function() { alert("可使用熱鍵方式呼叫設置菜單!!\n\n快捷組合 : (Alt + B)"); }); })(); /* 監聽 Ajex 變化(fetch的不支援) */ async function MonitorAjax() { let Video, VideoCache; const originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function () { this.addEventListener("readystatechange", function () { Video = document.querySelector("video"); if (this.readyState === 4 && Video) { if (!Video.hasAttribute("data-audio-context") && Video !== VideoCache) { FindVideo(); VideoCache = Video; } } }); return originalXHROpen.apply(this, arguments); } } /* 搜索 Video 元素 */ async function FindVideo() { let interval, timeout=0; interval = setInterval(function() { const videoElement = document.querySelector("video"); if (videoElement) { if (enabledDomains.includes(domain)) { // 沒開啟自動增幅的網頁也可以嘗試使用 let inc = GM_getValue(domain, []); if (inc.length !== 0) { Increase = inc; } } try { Booster = booster(videoElement, Increase); } catch (error) {console.log(error)} clearInterval(interval); } else { timeout++; if (timeout === 3) { clearInterval(interval); } } }, 300); } /* 註冊快捷鍵(開啟菜單) */ async function MenuHotkey() { document.addEventListener("keydown", function(event) { if (event.altKey && event.key === "b") { IncrementalSetting(); } }); } /* 音量增量 */ function booster(video, increase) { const audioContext = new (window.AudioContext || window.webkitAudioContext); // 音頻來源 const source = audioContext.createMediaElementSource(video); // 增益節點 const gainNode = audioContext.createGain(); // 動態壓縮節點 const compressorNode = audioContext.createDynamicsCompressor(); // 將預設音量調整至 100% (有可能被其他腳本調整) video.volume = 1; // 設置增量 gainNode.gain.value = increase * increase; // 設置動態壓縮器的參數(通用性測試!!) compressorNode.ratio.value = 9; // 壓縮率 compressorNode.knee.value = 6; // 壓縮過度反應時間(越小越快) compressorNode.threshold.value = -9; // 壓縮閾值 compressorNode.attack.value = 0.003; // 開始壓縮的速度 compressorNode.release.value = 0.6; // 釋放壓縮的速度 // 進行節點連結 source.connect(gainNode); gainNode.connect(compressorNode); compressorNode.connect(audioContext.destination); video.setAttribute("data-audio-context", true); return { // 設置音量 setVolume: function(increase) { gainNode.gain.value = increase * increase; Increase = increase; } } } /* 使用自動增幅 */ function Useboost(enabledDomains, domain) { if (enabledDomains.includes(domain)) { // 從已啟用列表中移除當前網域 enabledDomains = enabledDomains.filter(function(value) { return value !== domain; }); alert("❌ 禁用自動增幅"); } else { // 添加當前網域到已啟用列表 enabledDomains.push(domain); alert("✅ 啟用自動增幅"); } GM_setValue("啟用網域", enabledDomains); location.reload(); } GM_addStyle(` .YT-modal-background { top: 0; left: 0; width: 100%; height: 100%; z-index: 9999; display: flex; position: fixed; overflow: auto; justify-content: center; align-items: center; background-color: rgba(0, 0, 0, 0.1); } .YT-modal-button { top: 0; margin: 3% 2%; color: #d877ff; font-size: 16px; font-weight: bold; border-radius: 3px; background-color: #ffebfa; border: 1px solid rgb(124, 183, 252); } .YT-modal-button:hover, .YT-modal-button:focus { color: #fc0e85; cursor: pointer; text-decoration: none; } .YT-modal-content { width: 400px; padding: 5px; overflow: auto; background-color: #cff4ff; border-radius: 10px; text-align: center; border: 2px ridge #82c4e2; border-collapse: collapse; margin: 2% auto 8px auto; } .multiplier { font-size:25px; color:rgb(253, 1, 85); margin: 10px; font-weight:bold; } .slider { width: 350px; } .hidden { display: none; } `); /* 設定模態 */ function IncrementalSetting() { if (modal) { modal.remove(); modal = null; } modal = document.createElement('div'); modal.innerHTML = ` <div class="YT-modal-content"> <h2 style="color: #3754f8;">音量增量</h2> <div style="margin:1rem auto 1rem auto;"> <div class="multiplier"> <span><img src="https://cdn-icons-png.flaticon.com/512/8298/8298181.png" width="5%">增量倍數 </span><span id="currentValue">${Increase}</span><span> 倍</span> </div> <input type="range" class="slider" min="0" max="10.0" value="${Increase}" step="0.1"><br> </div> <div style="text-align: right;"> <button class="YT-modal-button" id="save">保存設置</button> <button class="YT-modal-button" id="close">退出選單</button> </div> </div> ` modal.classList.add('YT-modal-background'); document.body.appendChild(modal); modal.classList.remove('hidden'); // 監聽設定拉條 modal.addEventListener("input", function(event) { if (event.target.classList.contains("slider")) { let currentValueElement = document.getElementById("currentValue"); let currentValue = event.target.value; currentValueElement.textContent = currentValue; Booster.setVolume(currentValue); } }); // 監聽保存按鈕 let saveButton = modal.querySelector("#save"); saveButton.addEventListener("click", () => { if (enabledDomains.includes(domain)) { let rangeValue = parseFloat(modal.querySelector(".slider").value); GM_setValue(domain, rangeValue); modal.classList.add("hidden"); } else { alert("需啟用自動增幅才可保存"); } }); // 監聽關閉按鈕點擊 let CloseButton = modal.querySelector("#close"); CloseButton.addEventListener("click", () => { modal.classList.add("hidden"); }); }