|
// ==UserScript== |
|
// @name YouTube audio compressor |
|
// @namespace https://eaguru.guru/ |
|
// @version 0.5.2 |
|
// @description Adds an audio compressor option to YouTube videos. Now with over-engineering! Based on code by Vivelin and Wareya. |
|
// @author notwa |
|
// @match https://*.youtube.com/* |
|
// @updateURL https://gist.github.com/notwa/9b8466b0c2ca48d756afcd02a5e43739/raw/YouTube%2520audio%2520compressor.user.js |
|
// @run-at document-idle |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
(() => { |
|
"use strict"; |
|
|
|
// Boolean: |
|
let boosterActive = false; |
|
let compressorActive = false; |
|
let fancyCompression = true; |
|
|
|
// AudioContext: |
|
let context = null; |
|
|
|
// AudioNode: |
|
let source = null; |
|
let gainIn = null; |
|
let filterIn = null; |
|
let compressor = null; |
|
let filterOut = null; |
|
let gainOut = null; |
|
let limiter = null; |
|
|
|
// DOM: |
|
let boosterMenuItem = null; |
|
let compressorMenuItem = null; |
|
|
|
// these filters very roughly approximate the inverse of ISO 226:2003 at 70 phons. |
|
// NOTE: these MUST have all zeros (and poles) within the unit circle, |
|
// else the output signal will grow indefinitely (i.e. blow up). |
|
// normally, only the poles of an IIR are subject to this constraint. |
|
// however, as a quick 'n' dirty method to reverse the effect |
|
// of the filters after compression, i am using their inverses, |
|
// wherein the numerators and denominators are swapped. |
|
// TODO: reduce ringing. |
|
// |
|
// zeroth-degree terms are fixed at 1.0. |
|
// 1.0 + num_1 * z^-1 + num_2 * z^-2 |
|
// H(z) = gain * ----------------------------------- |
|
// 1.0 + den_1 * z^-1 + den_2 * z^-2 |
|
// gain num_1 num_2 den_1 den_2 |
|
const iir44100 = [+0.4707103, -1.1384015, +0.1384129, -1.5989047, +0.6369472]; |
|
const iir48000 = [+0.4560796, -1.1713186, +0.1713303, -1.6291792, +0.6617519]; |
|
const iir88200 = [+0.3747475, -1.4029588, +0.4029728, -1.7895837, +0.8000784]; |
|
const iir96000 = [+0.3665513, -1.4342713, +0.4342856, -1.8057964, +0.8147329]; |
|
|
|
const iirs = { |
|
44100: iir44100, |
|
48000: iir48000, |
|
88200: iir88200, |
|
96000: iir96000 |
|
} |
|
|
|
function createFilter(coeffs, flip, moreGain = 1.0) { |
|
const gain = flip ? moreGain / coeffs[0] : moreGain * coeffs[0]; |
|
const b0 = gain; |
|
const b1 = gain * coeffs[flip ? 3 : 1]; |
|
const b2 = gain * coeffs[flip ? 4 : 2]; |
|
const a0 = 1.0; |
|
const a1 = coeffs[flip ? 1 : 3]; |
|
const a2 = coeffs[flip ? 2 : 4]; |
|
const num = [b0, b1, b2]; |
|
const den = [a0, a1, a2]; |
|
const filter = context.createIIRFilter(num, den); |
|
return filter; |
|
} |
|
|
|
function createMenuItem(title, defaultState) { |
|
const menuItem = document.createElement("div"); |
|
menuItem.className = "ytp-menuitem"; |
|
menuItem.setAttribute("role", "menuitemcheckbox"); |
|
menuItem.setAttribute("aria-checked", "false"); |
|
menuItem.tabIndex = 0; |
|
|
|
const icon = document.createElement("div"); |
|
icon.className = "ytp-menuitem-icon"; |
|
menuItem.appendChild(icon); |
|
|
|
const label = document.createElement("div"); |
|
label.className = "ytp-menuitem-label"; |
|
label.textContent = title; |
|
menuItem.appendChild(label); |
|
|
|
const toggleCheckbox = document.createElement("div"); |
|
toggleCheckbox.className = "ytp-menuitem-toggle-checkbox"; |
|
|
|
const content = document.createElement("div"); |
|
content.className = "ytp-menuitem-content"; |
|
content.appendChild(toggleCheckbox); |
|
menuItem.appendChild(content); |
|
|
|
if (defaultState && defaultState !== "false") { |
|
menuItem.setAttribute("aria-checked", "true"); |
|
} else { |
|
menuItem.setAttribute("aria-checked", "false"); |
|
} |
|
|
|
console.log(menuItem); |
|
return menuItem; |
|
} |
|
|
|
function ariaToggle(element) { |
|
const oldState = element.getAttribute("aria-checked"); |
|
const newState = oldState === "false" ? true : false; |
|
element.setAttribute("aria-checked", newState.toString()); |
|
return newState; |
|
} |
|
|
|
function reconnect() { |
|
if (source) source.disconnect(); |
|
if (gainIn) gainIn.disconnect(); |
|
if (filterIn) filterIn.disconnect(); |
|
if (compressor) compressor.disconnect(); |
|
if (filterOut) filterOut.disconnect(); |
|
if (gainOut) gainOut.disconnect(); |
|
if (limiter) limiter.disconnect(); |
|
|
|
if (!source) return false; |
|
|
|
let sequence = [source]; |
|
if (boosterActive || compressorActive) sequence.push(gainIn); |
|
if (compressorActive) { |
|
if (fancyCompression) sequence.push(filterIn); |
|
sequence.push(compressor); |
|
if (fancyCompression) sequence.push(filterOut); |
|
} |
|
if (boosterActive || compressorActive) sequence.push(gainOut); |
|
if (boosterActive) sequence.push(limiter); |
|
|
|
// hook it all up: |
|
sequence = sequence.filter((item) => item); // remove falsy (null) elements |
|
for (let i = 1; i < sequence.length; i++) { |
|
sequence[i - 1].connect(sequence[i]); |
|
} |
|
sequence.at(-1).connect(context.destination); |
|
|
|
let compDrive = 1.0; |
|
let limitDrive = 1.0; |
|
if (boosterActive && limiter) { |
|
compDrive *= 3.1622776601683795; // +10 dB |
|
if (compressorActive) { |
|
compDrive *= 0.7943282347242815; // -2 dB |
|
limitDrive *= 1.4125375446227544; // +3 dB |
|
} |
|
} else if (compressorActive) { |
|
compDrive *= 0.7079457843841379; // -3 dB |
|
limitDrive *= 0.7079457843841379; // -3 dB |
|
} |
|
gainIn.gain.setValueAtTime(compDrive, context.currentTime); |
|
gainOut.gain.setValueAtTime(limitDrive, context.currentTime); |
|
|
|
return true; |
|
} |
|
|
|
function setupCompressor(player, ytpPanelMenu) { |
|
if (!context) { |
|
context = new AudioContext(); |
|
|
|
// load and register the limiter code: |
|
const uri = "data:text/javascript;base64,Ly8gcG9ydCBvZiBodHRwczovL2dpdGh1Yi5jb20vd2FyZXlhL0xpbWl0ZXJUZXN0L2Jsb2IvbWFpbi9NYWluLmdkCgpjbGFzcyBMaW1pdGVyIGV4dGVuZHMgQXVkaW9Xb3JrbGV0UHJvY2Vzc29yIHsKICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7CiAgICBzdXBlcigpOwoKICAgIHRoaXMuc3JhdGUgPSBzYW1wbGVSYXRlOwoKICAgIHRoaXMucHJlX2dhaW4gPSAxLjA7CiAgICB0aGlzLnBvc3RfZ2FpbiA9IDEuMDsKICAgIHRoaXMubGltaXQgPSAwLjg5MTI1MDkzODEzMzc0NTY7IC8vIC0xIGRlY2liZWxzCgogICAgaWYgKGZhbHNlKSB7CiAgICAgIHRoaXMuYXR0YWNrICA9IDAuMDAxMDsKICAgICAgdGhpcy5zdXN0YWluID0gMC4wNDAwOwogICAgICB0aGlzLnJlbGVhc2UgPSAwLjA0MDA7CiAgICB9IGVsc2UgeyAvLyB1bHRyYWZhc3QKICAgICAgdGhpcy5hdHRhY2sgID0gMC4wMDA2OwogICAgICB0aGlzLnN1c3RhaW4gPSAwLjAwNjA7CiAgICAgIHRoaXMucmVsZWFzZSA9IDAuMDMwMDsKICAgIH0KCiAgICB0aGlzLnJlbGVhc2VfbWVtb3J5ID0gMC4wOwoKICAgIHRoaXMuYm94X2JsdXIgPSAwOwogICAgLy8gdGhpcy5hbXAgPSAxLjA7IC8vIG5vdCBzdGF0ZWZ1bAogICAgdGhpcy5yZWZfYW1wID0gMS4wOwoKICAgIHRoaXMubWVtb3J5X2N1cnNvciA9IDA7CiAgICB0aGlzLnN1c3RhaW5lZF9hbXAgPSBudWxsOwogICAgdGhpcy5zYW1wbGVfbWVtb3J5ID0gbnVsbDsKCiAgICB0aGlzLmFtcF9tZW1vcnlfY3Vyc29yID0gMDsKICAgIHRoaXMuYW1wX21lbW9yeSA9IG51bGw7CiAgICB0aGlzLmFtcF9taW5fYnVja2V0cyA9IG51bGw7CiAgICB0aGlzLmFtcF9idWNrZXRfc2l6ZSA9IDA7CgogICAgdGhpcy5wZWFrcyA9IG51bGw7CiAgICB0aGlzLmRlbGF5cyA9IFtdOwoKICAgIGNvbnN0IHRvU2FtcGxlcyA9ICh0KSA9PiBNYXRoLm1heChNYXRoLnJvdW5kKHRoaXMuc3JhdGUgKiB0KSwgMSk7CgogICAgbGV0IG1lbW9yeV9jb3VudCA9IHRvU2FtcGxlcyh0aGlzLmF0dGFjayk7CiAgICB0aGlzLnNhbXBsZV9tZW1vcnkgPSBuZXcgRmxvYXQzMkFycmF5KG1lbW9yeV9jb3VudCk7CiAgICB0aGlzLnN1c3RhaW5lZF9hbXAgPSBuZXcgRmxvYXQzMkFycmF5KG1lbW9yeV9jb3VudCk7CgogICAgbGV0IGFtcF9jb3VudCA9IHRvU2FtcGxlcyh0aGlzLmF0dGFjayArIHRoaXMuc3VzdGFpbik7CiAgICB0aGlzLmFtcF9idWNrZXRfc2l6ZSA9IE1hdGguc3FydChhbXBfY291bnQpIHwgMDsKICAgIHRoaXMuYW1wX21lbW9yeSA9IG5ldyBGbG9hdDMyQXJyYXkoYW1wX2NvdW50KTsKICAgIHRoaXMuYW1wX21lbW9yeS5maWxsKDEuMCk7CgogICAgbGV0IGFtcF9idWNrZXRfY291bnQgPSBhbXBfY291bnQgLyB0aGlzLmFtcF9idWNrZXRfc2l6ZSB8IDA7CiAgICB0aGlzLmFtcF9taW5fYnVja2V0cyA9IG5ldyBGbG9hdDMyQXJyYXkoYW1wX2J1Y2tldF9jb3VudCk7CiAgICB0aGlzLmFtcF9taW5fYnVja2V0cy5maWxsKDEuMCk7CgogICAgdGhpcy5wZWFrcyA9IG5ldyBGbG9hdDMyQXJyYXkoMTI4KTsgLy8gZ3Vlc3MgdGhlIGJ1ZmZlciBzaXplCiAgICB0aGlzLmRlbGF5cyA9IFt0aGlzLnNhbXBsZV9tZW1vcnldOwogIH0KCiAgcHJvY2VzcyhpbnB1dHMsIG91dHB1dHMsIHBhcmFtZXRlcnMpIHsKICAgIGNvbnN0IG1lbW9yeV9jb3VudCA9IHRoaXMuc2FtcGxlX21lbW9yeS5sZW5ndGg7IC8vIHRoaXMgc2hvd3MgdXAgYSBsb3QKCiAgICBjb25zdCBpbnB1dCA9IGlucHV0c1swXTsKICAgIGNvbnN0IG91dHB1dCA9IG91dHB1dHNbMF07CgogICAgaWYgKGlucHV0Lmxlbmd0aCA9PT0gMCkgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgd2hpbGUgKHRoaXMuZGVsYXlzLmxlbmd0aCA8IGlucHV0Lmxlbmd0aCkgewogICAgICB0aGlzLmRlbGF5cy5wdXNoKG5ldyBGbG9hdDMyQXJyYXkobWVtb3J5X2NvdW50KSk7CiAgICB9CgogICAgY29uc3QgYmxvY2tTaXplID0gaW5wdXRbMF0ubGVuZ3RoOwogICAgaWYgKGJsb2NrU2l6ZSA+IDAgJiYgdGhpcy5wZWFrcy5sZW5ndGggIT09IGJsb2NrU2l6ZSkgewogICAgICB0aGlzLnBlYWtzID0gbmV3IEZsb2F0MzJBcnJheShibG9ja1NpemUpOwogICAgfQogICAgdGhpcy5wZWFrcy5maWxsKDAuMCk7CgogICAgZm9yIChsZXQgYyA9IDA7IGMgPCBpbnB1dC5sZW5ndGg7IGMrKykgewogICAgICBjb25zdCBzb3VyY2UgPSBpbnB1dFtjXTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBibG9ja1NpemU7IGkrKykgewogICAgICAgIHRoaXMucGVha3NbaV0gPSBNYXRoLm1heCh0aGlzLnBlYWtzW2ldLCBNYXRoLmFicyhzb3VyY2VbaV0pKTsKICAgICAgfQogICAgfQoKICAgIGNvbnN0IGRlbGF5X2N1cnNvciA9IHRoaXMubWVtb3J5X2N1cnNvcjsKCiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJsb2NrU2l6ZTsgaSsrKSB7CiAgICAgIGNvbnN0IHNhbXBsZSA9IHRoaXMucGVha3NbaV0gKiB0aGlzLnByZV9nYWluOwoKICAgICAgLy8gZG8gdGhlIHJlbGVhc2UgY3VydmUKICAgICAgdGhpcy5yZWxlYXNlX21lbW9yeSA9IE1hdGgubWF4KDAuMCwgdGhpcy5yZWxlYXNlX21lbW9yeSAtIDEuMCAvIHRoaXMuc3JhdGUpOwogICAgICBsZXQgYW1wID0gMS4wOwogICAgICBpZiAodGhpcy5yZWxlYXNlID4gMC4wKSB7CiAgICAgICAgbGV0IHQgPSAxLjAgLSB0aGlzLnJlbGVhc2VfbWVtb3J5IC8gdGhpcy5yZWxlYXNlOwogICAgICAgIGFtcCA9ICgxLjAgLSB0KSAqIHRoaXMucmVmX2FtcCArIHQ7CiAgICAgIH0KCiAgICAgIC8vIHJlc2V0IHN1c3RhaW4gaWYgd2UgZXhjZWVkIHRoZSBsaW1pdAogICAgICBsZXQgcmVmX3ZhbCA9IE1hdGguYWJzKGFtcCAqIHNhbXBsZSk7CiAgICAgIGlmIChyZWZfdmFsID49IHRoaXMubGltaXQpIHsKICAgICAgICBsZXQgYW1vdW50ID0gdGhpcy5saW1pdCAvIHJlZl92YWw7CiAgICAgICAgYW1wICo9IGFtb3VudDsKICAgICAgICByZWZfdmFsICo9IGFtb3VudDsKICAgICAgICB0aGlzLnJlZl9hbXAgPSBhbXA7CiAgICAgICAgdGhpcy5yZWxlYXNlX21lbW9yeSA9IHRoaXMucmVsZWFzZTsKICAgICAgfQoKICAgICAgdGhpcy5hbXBfbWVtb3J5W3RoaXMuYW1wX21lbW9yeV9jdXJzb3JdID0gYW1wOwoKICAgICAgLy8gdXBkYXRlIGFmZmVjdGVkIGJ1Y2tldAogICAgICBsZXQgYnVja2V0ID0gdGhpcy5hbXBfbWVtb3J5X2N1cnNvciAvIHRoaXMuYW1wX2J1Y2tldF9zaXplIHwgMDsKICAgICAgdGhpcy5hbXBfbWluX2J1Y2tldHNbYnVja2V0XSA9IDEuMDsKICAgICAgbGV0IGVuZCA9IE1hdGgubWluKChidWNrZXQgKyAxKSAqIHRoaXMuYW1wX2J1Y2tldF9zaXplLCB0aGlzLmFtcF9tZW1vcnkubGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaiA9IGJ1Y2tldCAqIHRoaXMuYW1wX2J1Y2tldF9zaXplOyBqIDwgZW5kOyBqKyspIHsKICAgICAgICB0aGlzLmFtcF9taW5fYnVja2V0c1tidWNrZXRdID0gTWF0aC5taW4odGhpcy5hbXBfbWluX2J1Y2tldHNbYnVja2V0XSwgdGhpcy5hbXBfbWVtb3J5W2pdKTsKICAgICAgfQoKICAgICAgLy8gbm93IGdldCB0aGUgY3VycmVudCBzdXN0YWluIHZhbHVlCiAgICAgIGxldCBtaW5fYW1wID0gMS4wOwogICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuYW1wX21pbl9idWNrZXRzLmxlbmd0aDsgaisrKSB7CiAgICAgICAgbGV0IHBhc3RfYW1wID0gdGhpcy5hbXBfbWluX2J1Y2tldHNbal07CiAgICAgICAgbWluX2FtcCA9IE1hdGgubWluKG1pbl9hbXAsIHBhc3RfYW1wKTsKICAgICAgfQoKICAgICAgdGhpcy5hbXBfbWVtb3J5X2N1cnNvciArPSAxOwogICAgICB0aGlzLmFtcF9tZW1vcnlfY3Vyc29yICU9IHRoaXMuYW1wX21lbW9yeS5sZW5ndGg7CgogICAgICAvLyB1cGRhdGUgdGhlIHN1c3RhaW4gYnVmZmVyCiAgICAgIHRoaXMuYm94X2JsdXIgLT0gdGhpcy5zdXN0YWluZWRfYW1wW3RoaXMubWVtb3J5X2N1cnNvcl0gKiAzMjc2NyB8IDA7CiAgICAgIHRoaXMuc3VzdGFpbmVkX2FtcFt0aGlzLm1lbW9yeV9jdXJzb3JdID0gbWluX2FtcDsKICAgICAgdGhpcy5ib3hfYmx1ciArPSB0aGlzLnN1c3RhaW5lZF9hbXBbdGhpcy5tZW1vcnlfY3Vyc29yXSAqIDMyNzY3IHwgMDsKCiAgICAgIC8vIHVwZGF0ZSB0aGUgc2FtcGxlIG1lbW9yeSBidWZmZXIKICAgICAgLy8gTk9URShub3R3YSk6IHdlIGluc3RlYWQgZG8gdGhpcyBsYXRlciBmb3IgZWFjaCBjaGFubmVsLgogICAgICAvL2xldCByZXRfc2FtcGxlID0gdGhpcy5zdXN0YWluICsgdGhpcy5hdHRhY2sgPiAwLjAgPyB0aGlzLnNhbXBsZV9tZW1vcnlbdGhpcy5tZW1vcnlfY3Vyc29yXSA6IHNhbXBsZTsKICAgICAgLy90aGlzLnNhbXBsZV9tZW1vcnlbdGhpcy5tZW1vcnlfY3Vyc29yXSA9IHNhbXBsZTsKCiAgICAgIHRoaXMubWVtb3J5X2N1cnNvciArPSAxOwogICAgICB0aGlzLm1lbW9yeV9jdXJzb3IgJT0gbWVtb3J5X2NvdW50OwoKICAgICAgdGhpcy5wZWFrc1tpXSA9IHRoaXMuYm94X2JsdXIgLyAzMjc2NyAvIG1lbW9yeV9jb3VudDsKICAgIH0KCiAgICBjb25zdCBmaW5hbF9nYWluID0gdGhpcy5wcmVfZ2FpbiAqIHRoaXMucG9zdF9nYWluOwogICAgZm9yIChsZXQgYyA9IDA7IGMgPCBpbnB1dC5sZW5ndGg7IGMrKykgewogICAgICBjb25zdCBzb3VyY2UgPSBpbnB1dFtjXTsKICAgICAgY29uc3QgdGFyZ2V0ID0gb3V0cHV0W2NdOwogICAgICBpZiAodGhpcy5zdXN0YWluICsgdGhpcy5hdHRhY2sgPD0gMC4wKSB7CiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBibG9ja1NpemU7IGkrKykgewogICAgICAgICAgLy8gcmV0dXJuIHRoZSBsaW1pdGVkIHNhbXBsZQogICAgICAgICAgdGFyZ2V0W2ldID0gc291cmNlW2ldICogdGhpcy5wZWFrc1tpXSAqIGZpbmFsX2dhaW47CiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIGNvbnN0IGRlbGF5ID0gdGhpcy5kZWxheXNbY107CiAgICAgICAgbGV0IGxvY2FsX2N1cnNvciA9IGRlbGF5X2N1cnNvcjsKICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJsb2NrU2l6ZTsgaSsrKSB7CiAgICAgICAgICBjb25zdCByZXRfc2FtcGxlID0gZGVsYXlbbG9jYWxfY3Vyc29yXTsKICAgICAgICAgIGRlbGF5W2xvY2FsX2N1cnNvcl0gPSBzb3VyY2VbaV07CgogICAgICAgICAgbG9jYWxfY3Vyc29yICs9IDE7CiAgICAgICAgICBsb2NhbF9jdXJzb3IgJT0gbWVtb3J5X2NvdW50OwoKICAgICAgICAgIC8vIHJldHVybiB0aGUgbGltaXRlZCBzYW1wbGUKICAgICAgICAgIHRhcmdldFtpXSA9IHJldF9zYW1wbGUgKiB0aGlzLnBlYWtzW2ldICogZmluYWxfZ2FpbjsKICAgICAgICB9CiAgICAgIH0KICAgIH0KCiAgICByZXR1cm4gZmFsc2U7IC8vIGRvY3Mgc2F5IHRvIHJldHVybiBmYWxzZSBmb3IgcHJvY2Vzc29ycyB3aXRoIG5vIHJldmVyYiB0YWlscwogIH0KfQoKcmVnaXN0ZXJQcm9jZXNzb3IoImxpbWl0ZXIiLCBMaW1pdGVyKTsK"; |
|
context.audioWorklet.addModule(uri).then(() => { |
|
limiter = new AudioWorkletNode(context, "limiter"); |
|
console.info("Limiter successfully initialized."); |
|
reconnect(); |
|
}); |
|
} |
|
|
|
const iir = iirs[context.sampleRate]; |
|
if (typeof iir === "undefined") { |
|
fancyCompression = false; |
|
} |
|
|
|
const now = context.currentTime; |
|
if (!source) { |
|
source = context.createMediaElementSource(player); |
|
} |
|
if (!gainIn) { |
|
gainIn = context.createGain(); |
|
gainIn.gain.setValueAtTime(1.0, now); |
|
} |
|
if (!filterIn && fancyCompression) { |
|
filterIn = createFilter(iir, false, 2.0); |
|
} |
|
if (!compressor) { |
|
compressor = context.createDynamicsCompressor(); |
|
compressor.threshold.setValueAtTime(-50, now); |
|
compressor.knee.setValueAtTime(40, now); |
|
compressor.ratio.setValueAtTime(12, now); |
|
compressor.attack.setValueAtTime(0.01, now); |
|
compressor.release.setValueAtTime(0.33, now); |
|
} |
|
if (!filterOut && fancyCompression) { |
|
filterOut = createFilter(iir, true, 0.5); |
|
} |
|
if (!gainOut) { |
|
gainOut = context.createGain(); |
|
gainOut.gain.setValueAtTime(1.0, now); |
|
} |
|
|
|
if (!boosterMenuItem) { |
|
boosterMenuItem = createMenuItem("Level Boost", boosterActive); |
|
boosterMenuItem.onclick = () => { |
|
boosterActive = ariaToggle(boosterMenuItem); |
|
reconnect(); |
|
}; |
|
ytpPanelMenu.appendChild(boosterMenuItem); |
|
} |
|
|
|
if (!compressorMenuItem) { |
|
compressorMenuItem = createMenuItem("Compressor", compressorActive); |
|
compressorMenuItem.onclick = () => { |
|
compressorActive = ariaToggle(compressorMenuItem); |
|
reconnect(); |
|
}; |
|
ytpPanelMenu.appendChild(compressorMenuItem); |
|
} |
|
|
|
reconnect(); |
|
} |
|
|
|
function attemptSetup() { |
|
const player = document.querySelector("#ytd-player video"); |
|
const ytpPanelMenu = document.querySelector("#ytd-player .ytp-settings-menu .ytp-panel-menu"); |
|
if (player && ytpPanelMenu) { |
|
setupCompressor(player, ytpPanelMenu); |
|
console.info("Compressor options added."); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
function trySetupLater() { |
|
console.debug("Polling for player..."); |
|
|
|
const intervalId = window.setInterval(() => { |
|
if (attemptSetup()) { |
|
window.clearInterval(intervalId); |
|
} |
|
}, 100); |
|
|
|
const cancelId = window.setTimeout(() => { |
|
window.clearInterval(intervalId); |
|
}, 10000); |
|
} |
|
|
|
if (!attemptSetup()) { |
|
trySetupLater(); |
|
window.addEventListener("yt-navigate-finish", trySetupLater); |
|
} |
|
})(); |