Youtube Downloader

Youtube Audio and Video downloader

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Youtube Downloader
// @namespace    http://tampermonkey.net/
// @version      2029.1
// @description  Youtube Audio and Video downloader
// @author       LeonelM
// @match        https://www.youtube.com/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const stylesCss = `
    <style>
    button {
      margin: 0;
      padding: 0;
    }

    .newVideoTest:hover{
   background-color: rgba(80,80,80,0.8)
    }

    button:hover {
      color: #ec3203;
    }
    .btn1{
    margin-right: -15px;
    }

    .newText{
    font-size: 118%;
    font-weight: 500;
    display: flex;
    }

    .newText span{
       margin-right: 4px;
       margin-top: 5px;
       color: #e3e3e3;
    }
    .newVideoTest:hover .newText span{
       margin-right: 4px;
       margin-top: 5px;
           font-weight: bold;
           color: white;
    }

    .botones_div {
      background-color: transparent;
      border: none;
      color: #FFFFFF;
    }
    .ytp-swatch-color-white{
        color: var(--yt-spec-static-overlay-text-secondary);
        font-size: 13px;
        font-weight: bolder;
    }
    .newVideoTest:hover .newText .ytp-swatch-color-white{
        color: rgb(255 38 0);
        font-size: 13px;
    }

    </style>
    `;

    const menuBotones = `
    <style>
        ${stylesCss}
    </style>

       <div class="ytp-popup ytp-settings-menu leotest leoaudiotest" data-layer="6" id="ytp-id-18" style="width: 397px; height: 277px;">
        <!-- Panel for Audio Quality -->
        <div class="ytp-panel" style="width: 397px; height: 277px;">

         <span style="padding-left:20px;font-size: large;font-weight: 900;align-content: center;und;und;flex-wrap: wrap;display: flex;reverse;justify-content: space-between;flex-direction: row;align-items: center;">Download Audio

          <button title="Close" type="button" class="btn3 botones_div" style="
              left: auto;
             padding-left: 20px;
             position: relative;
              ">
          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-x" width="30" height="48" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
            <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
            <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
            <path d="M10 10l4 4m0 -4l-4 4"></path>
          </svg>
           </button>

         </span>

          <div class="ytp-panel-menu" role="menu" style="height: 177px;">

          <div class="newVideoTest" aria-haspopup="true" value="flac" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">

                    <span>FLAC </span><sup class="ytp-swatch-color-white">UHQ</sup>
                      <button id="downloadButtonflac" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  'flac','downloadButtonflac')">
                      Click to Download
                      </button>

                    </div>

                </div>

               <div class="newVideoTest" aria-haspopup="true" value="wav" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">

                    <span>WAV</span><sup class="ytp-swatch-color-white">UHQ</sup>
                      <button id="downloadButtonwav" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload( 'wav','downloadButtonwav')">
                      Click to Download
                      </button>

                    </div>

                </div>

                 <div class="newVideoTest" aria-haspopup="true"value="mp3" role="menuitem" tabindex="1" style="padding-left:20px">
                    <div class="newText">   <span>MP3</span>
                    <button id="downloadButtonmp3" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload( 'mp3','downloadButtonmp3')">
                      Click to Download
                      </button>

                    </div>
                </div>

                <div class="newVideoTest" aria-haspopup="true" value="m4a" role="menuitem" tabindex="2" style="padding-left:20px">
                    <div class="newText">    <span>M4A </span>
                     <button id="downloadButtonm4a" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload( 'm4a','downloadButtonm4a')">
                      Click to Download
                      </button>

                    </div>
                </div>

                <div class="newVideoTest" aria-haspopup="true" value="aac" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">    <span>AAC </span>
                    <button id="downloadButtonaac" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  'aac','downloadButtonaac')">
                      Click to Download
                      </button>

                    </div>
                </div>

                 <div class="newVideoTest" aria-haspopup="true" value="opus" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">    <span>OPUS </span>
                    <button id="downloadButtonopus" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload( 'opus','downloadButtonopus')">
                      Click to Download
                      </button>

                    </div>
                </div>

                <div class="newVideoTest" aria-haspopup="true" value="ogg" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">   <span> OGG </span>
                    <button id="downloadButtonogg" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  'ogg','downloadButtonogg')">
                      Click to Download
                      </button>

                    </div>
                </div>

            </div>

        </div>

    </div>

   <div class="ytp-popup ytp-settings-menu leotest leovideotest" data-layer="6" id="ytp-id-18" style="width: 397px; height: 277px;">
        <!-- Panel for Video Quality -->
        <div class="ytp-panel" style="width: 397px; height: 277px;">

         <span style="padding-left:20px;font-size: large;font-weight: 900;align-content: center;und;und;flex-wrap: wrap;display: flex;reverse;justify-content: space-between;flex-direction: row;align-items: center;">Download Video

          <button title="Close" type="button" class="btn3 botones_div" style="
             left: auto;
             padding-left: 20px;
             position: relative;
         ">
          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-x" width="30" height="48" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
            <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
            <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"></path>
            <path d="M10 10l4 4m0 -4l-4 4"></path>
          </svg>
           </button>

         </span>

          <div class="ytp-panel-menu" role="menu" style="height: 177px;">

               <div class="newVideoTest" aria-haspopup="true" value="4k" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">
                    <span>2160p</span><sup class="ytp-swatch-color-white">4K</sup>

                      <button id="downloadButton4k" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  '4k','downloadButton4k')">
                      Click to Download
                      </button>
                    </div>

                </div>

                 <div class="newVideoTest" aria-haspopup="true"value="1440" role="menuitem" tabindex="1" style="padding-left:20px">
                    <div class="newText">   <span>1440p</span><sup class="ytp-swatch-color-white">2K</sup>
                    <button id="downloadButton2k" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload( '1440','downloadButton2k')">
                      Click to Download
                      </button>

                    </div>
                </div>

                <div class="newVideoTest" aria-haspopup="true" value="1080" role="menuitem" tabindex="2" style="padding-left:20px">
                    <div class="newText">    <span>1080p</span><sup class="ytp-swatch-color-white">Full HD</sup>
                     <button id="downloadButton1080" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  '1080','downloadButton1080')">
                      Click to Download
                      </button>

                    </div>
                </div>

                <div class="newVideoTest" aria-haspopup="true" value="720" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">    <span>720p</span><sup class="ytp-swatch-color-white">HD</sup>
                    <button id="downloadButton720" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  '720','downloadButton720')">
                      Click to Download
                      </button>

                    </div>
                </div>

                 <div class="newVideoTest" aria-haspopup="true" value="480" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">    <span>480p</span>
                    <button id="downloadButton480" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  '480','downloadButton480')">
                      Click to Download
                      </button>

                    </div>
                </div>

                <div class="newVideoTest" aria-haspopup="true" value="360" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">   <span> 360p</span>
                    <button id="downloadButton360" style="place-content:center;margin-left: auto;height:100%;top: 8px;width:45%;padding: 9px;background-color:#2986cc;color: white;border: none;display: flex;border-radius: 5px;position: relative;" onclick="initiateDownload(  '360','downloadButton360')">
                      Click to Download
                      </button>

                    </div>
                </div>

            </div>

        </div>

    </div>

    </div>
    `;

    // HTML de los botones
    const downloadMp4Mp3 = `
           <button title="Download Video" type="button" class="btn1 botones_div">
          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-download"
             width="30"  height="48" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
            stroke-linecap="round" stroke-linejoin="round">
           <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
           <path d="M14 3v4a1 1 0 0 0 1 1h4"></path>
           <path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"></path>
           <path d="M12 17v-6"></path>
           <path d="M9.5 14.5l2.5 2.5l2.5 -2.5"></path>
          </svg>
        </button>
        <button title="Download Audio" type="button" class="btn2 botones_div">
          <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-music" width="30"
            height="48" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
            stroke-linecap="round" stroke-linejoin="round">
            <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
            <path d="M14 3v4a1 1 0 0 0 1 1h4"></path>
            <path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"></path>
            <path d="M11 16m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path>
            <path d="M12 16l0 -5l2 1"></path>
          </svg>
        </button>
      `;

    let currentVideoUrl = window.location.href;
    let currentFunction = "";

    let firstBoot = true;

    function removeButtons(){
        let existingMenuBotones = document.querySelector('.leoaudiotest');
        let existingMenuBotones2 = document.querySelector('.leovideotest');
        let existingMenuBotones3 = document.querySelector('.btn1');
        let existingMenuBotones4 = document.querySelector('.btn2');


        if (existingMenuBotones) {
            existingMenuBotones.remove();
        }
        if (existingMenuBotones2) {
            existingMenuBotones2.remove();
        }
        if (existingMenuBotones3) {
            existingMenuBotones3.remove();
        }
        if (existingMenuBotones4) {
            existingMenuBotones4.remove();
        }
    }

    function loadScriptFixed() {
        // Definir una política de Trusted Types si aún no existe
        if (!window.trustedTypes.defaultPolicy) {
            window.trustedTypes.createPolicy('default', {
                createHTML: (input) => input // Aquí puedes agregar sanitización adicional si es necesario
            });
        }
        currentVideoUrl = window.location.href;
        currentFunction = "";

        // Remover botones existentes si ya están presentes
        removeButtons();

        const rightControlsDiv = document.querySelector('.ytp-right-controls');

        // Crear contenido seguro usando TrustedHTML
        const safeMenuBotones = window.trustedTypes.defaultPolicy.createHTML(menuBotones);
        const safeDownloadMp4Mp3 = window.trustedTypes.defaultPolicy.createHTML(downloadMp4Mp3);

        // Insertar contenido "seguro" en el DOM usando insertAdjacentHTML
        rightControlsDiv.insertAdjacentHTML('beforebegin', safeMenuBotones);
        rightControlsDiv.insertAdjacentHTML('beforebegin', safeDownloadMp4Mp3);

        // Insertar el CSS en el head del documento
        const existingStyleSheet = document.querySelector('#custom-style-sheet');
        if (existingStyleSheet) {
            existingStyleSheet.remove();
        }

        const styleSheet = document.createElement("style");
        styleSheet.type = "text/css";
        styleSheet.id = 'custom-style-sheet';
        styleSheet.innerText = stylesCss;
        document.head.appendChild(styleSheet);

        const leoaudiotest = document.querySelector('.leoaudiotest');
        const leovideotest = document.querySelector('.leovideotest');

        const btn1mp4 = document.querySelector('.btn1');
        const btn2mp3 = document.querySelector('.btn2');
        const btn3cancel = document.querySelectorAll('.btn3');

        leovideotest.style.display = 'none';
        leoaudiotest.style.display = 'none';

        for (let i = 0; i < btn3cancel.length; i++) {
            if (btn3cancel[i]) {
                btn3cancel[i].onclick = () => {
                    leovideotest.style.display = 'none';
                    leoaudiotest.style.display = 'none';
                };
            }
        }

        if (btn1mp4) {
            btn1mp4.onclick = () => {
                if (leovideotest.style.display != ''){
                    leovideotest.style.display = '';
                } else{
                    leovideotest.style.display = 'none';
                }
                leoaudiotest.style.display = 'none';

            };
        }

        if (btn2mp3) {
            btn2mp3.onclick = () => {
                 if (leoaudiotest.style.display != ''){
                    leoaudiotest.style.display = '';
                } else{
                    leoaudiotest.style.display = 'none';
                }
                leovideotest.style.display = 'none';
            };
        }
    }

    window.initiateDownload = function(format, buttonID) {
        console.log("id:" + buttonID);
        const button = document.getElementById(buttonID);
        const url = window.location.href;
        currentFunction = format;

        // Check if the video URL has changed
        if (url !== currentVideoUrl) {
            resetButton(buttonID, currentFunction);
            currentVideoUrl = url;
        }

        button.innerHTML = 'Preparing...';
        button.style.backgroundColor = '#53c9ff';

        fetch("https://loader.to/ajax/download.php?url=" + currentVideoUrl + "&format=" + format + "&start=1&end=1")
            .then(response => {
            if (!response.ok) {
                return response.text().then(text => { throw new Error('Network response was not ok: ' + text); });
            }
            return response.json();
        })
            .then(data => {
            if (data && data.success) {
                window.checkProgress(data.id, data.download_url, buttonID);
            } else {
                alert('Failed to initiate download.');
                button.innerHTML = 'Failed';
                button.style.backgroundColor = '#BF4B4B';
            }
        })
            .catch(error => {
            alert('An error occurred while trying to download the video: ' + error.message);
            button.innerHTML = 'Error';
            button.style.backgroundColor = '#BF4B4B';
            console.error('Error:', error);
        });
    }

    window.checkProgress = function(id, downloadUrl, buttonID) {
        const button = document.getElementById(buttonID);

        fetch("https://loader.to/ajax/progress.php?id=" + id)
            .then(response => {
            if (!response.ok) {
                return response.text().then(text => { throw new Error('Network response was not ok: ' + text); });
            }
            return response.json();
        })
            .then(data => {
            if (data && data.download_url) {
                button.innerHTML = 'Download Now!';
                button.style.backgroundColor = '#64c896';
                button.onclick = function() {
                    window.open(data.download_url, '_blank');
                    resetButton(buttonID, currentFunction); // Reiniciar el botón después de la descarga
                };
            } else {
                setTimeout(function() {
                    window.checkProgress(id, downloadUrl, buttonID);
                }, 750);
            }
        })
            .catch(error => {
            alert('An error occurred while checking download progress: ' + error.message);
            console.error('Error:', error);
        });
    };

    function resetButton(buttonID, prevFormat) {
        const button = document.getElementById(buttonID);
        button.innerHTML = 'Download again';
        button.style.backgroundColor = '#64c896'; // Restablecer color original
    }

    // Function to handle URL changes
    function handleUrlChange() {
        if (window.location.href !== currentVideoUrl) {
            currentVideoUrl = window.location.href;
            console.log("url changed");
            removeButtons();
            setTimeout(loadScriptFixed, 2000);
        }
    }

    // Observe changes in the video URL
    const observer = new MutationObserver(handleUrlChange);
    observer.observe(document.body, { childList: true, subtree: true });

    window.addEventListener('popstate', handleUrlChange);

    if (firstBoot){
        setTimeout(loadScriptFixed, 6000);
        firstBoot = false;
    }
})();