Youtube Audio Mode

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

Versione datata 21/07/2022. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==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);