Youtube Audio Mode

Listen to only the audio on YouTube without loading the video.

Fra 21.07.2022. Se den seneste versjonen.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        Youtube Audio Mode
// @description Listen to only the audio on YouTube without loading the video.
// @version     0.2.4
// @author      Burn
// @namespace   https://openuserjs.org/users/burn
// @copyright   2022, burn (https://openuserjs.org/users/burn)
// @include     https://www.youtube.com/*
// @match       https://www.youtube.com/*
// @license     MIT
// @run-at      document-end
// @grant       GM.setValue
// @grant       GM.getValue
// @noframes
// ==/UserScript==

/*

PLEASE NOTE

I've found the original userscript on Github: https://github.com/teaqu/youtube-audio-mode
but it's not working anymore so I've opened an issue. After a month without receiving a reply
I've decided to update the script myself.

*/

(async function(open, originalFetch) {
    const DBG = false;
    let myLog = (msg) => {
        DBG && console.log(GM_info.script.name + " | " + msg);
    };

    window.addEventListener("yt-navigate-finish", audioMode);
    window.onYouTubeIframeAPIReady = await audioMode();

    async function audioMode() {
        if (location.pathname == "/watch") {
            let video = document.getElementsByTagName("video")[0];
            let audioMode = await GM.getValue("ytAudioMode");
            await addToMenu(audioMode);
            if (audioMode) {
                setPoster(video, ["maxres", "hq", "sd"]);
                watchFetchStream(video);
            } else {myLog("audiomode disabled");}
        }
    }

    function watchFetchStream(video) {
        const constantMock = originalFetch;
        unsafeWindow.fetch = function() {
            return new Promise((resolve, reject) => {
                constantMock.apply(this, arguments)
                .then((response) => {
                    if(response.url.indexOf("mime=audio") > -1) { // && response.type != "cors"){
                        video.pause();
                        video.src = response.url.split("&range")[0];
                        video.play();
                    }
                    resolve(response);
                })
                .catch((error) => {
                    reject(response);
                })
            });
        }
    }

    // Add audio mode to the settings menu
    async function addToMenu(audioMode) {
        let panel = document.getElementsByClassName("ytp-panel-menu")[0];
        if (!panel.innerHTML.includes("Audio Mode")) {
            panel.innerHTML += `
            <div class="ytp-menuitem"
                aria-checked="${audioMode}"
                id="audio-mode">
                <div class="ytp-menuitem-icon"></div>
                <div class="ytp-menuitem-label">Audio Mode</div>
                <div class="ytp-menuitem-content">
                    <div class="ytp-menuitem-toggle-checkbox">
                </div>
            </div>`;

            // Toggle audio mode on or off
            let audioToggle = document.getElementById("audio-mode");
            audioToggle.onclick = async function() {
                let audioMode = ! await GM.getValue("ytAudioMode");
                this.setAttribute("aria-checked", audioMode);
                GM.setValue("ytAudioMode", audioMode);
                location.reload();
            }
        }
    }

    // Set the video poster from thumbnails with the best avaliable format
    // https://developers.google.com/youtube/v3/docs/thumbnails
    async function setPoster(video, fmts) {
        let img = new Image();
        let videoId = location.search.match(/v=(.+?)(&|$)/)[1];
        img.src = `//i.ytimg.com/vi/${videoId}/${fmts.shift()}default.jpg`
        img.onload = function() {
            // A height 90 is YouTube"s not found image.
            if (img.height <= 90) {
                setPoster(video, fmts);
            } else {
                video.style.background = `url(${img.src}) no-repeat center`;
                video.style.backgroundSize = "contain";
            }
        };
    }
})(XMLHttpRequest.prototype.open, window.fetch || unsafeWindow.fetch);