Youtube Downloader

Youtube Audio and Video downloader

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

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