volume set per channel

allows you to set different volumes for different twitch channels!

目前為 2025-07-29 提交的版本,檢視 最新版本

// ==UserScript==
// @name         volume set per channel
// @namespace    http://tampermonkey.net/
// @version      2025-07-29
// @description  allows you to set different volumes for different twitch channels!
// @author       trevrosa
// @run-at       document-body
// @match        https://www.twitch.tv/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=twitch.tv
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

function log(msg) {
    console.log(`volumeset: ${msg}`)
}

// https://stackoverflow.com/a/61511955
function waitForElem(selector) {
    return new Promise(resolve => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver(mutations => {
            if (document.querySelector(selector)) {
                observer.disconnect();
                resolve(document.querySelector(selector));
            }
        });

        // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
}

(function() {
    'use strict';

    let lastPage = window.location.pathname;

    // https://twitch.tv/xdd
    // ^ three `/`s, 4 split regions
    if (window.location.toString().split("/").length != 4) {
        return;
    }

    function setVolume(slider) {
        const volume = GM_getValue(window.location.pathname, null);
        if (!volume) {
            return;
        }

        // change the slider's value to what we want
        slider.value = volume;

        // invoke the react event handler to then change the volume (https://stackoverflow.com/a/77083516)
        const reactHandlerKey = Object.keys(slider).find(key => key.startsWith('__reactProps$')); // changed to reactProps
        const changeEvent = new Event('change', { bubbles: true });
        Object.defineProperty(changeEvent, 'currentTarget', {writable: false, value: slider}); // https://stackoverflow.com/a/49122553
        slider[reactHandlerKey].onChange(changeEvent);

        log(`set the volume to ${volume} (${window.location.pathname})`)
    }

    log("waiting for volume slider")
    waitForElem("input[type='range']").then((slider) => {
        slider.onchange = (e) => {
            const volume = parseFloat(e.target.value);
            GM_setValue(window.location.pathname, volume)
            log(`${window.location.pathname} saved to ${volume} volume`);
        }
        log("set volume slider listener");

        setVolume(slider);
    })

    setInterval(() => {
        const newPage = window.location.pathname;
        if (newPage == lastPage) {
            return;
        } else {
            lastPage = newPage;
            setVolume(document.querySelector("input[type='range']"));
        }
    }, 500);
})();