YT Downloader

Youtube MP3 and MP4 downloader

// ==UserScript==
// @name         YT Downloader
// @namespace    http://tampermonkey.net/
// @version      2024-6-16.3
// @description  Youtube MP3 and MP4 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>
    /* For Chrome or Safari */
    progress::-webkit-progress-bar {
      background-color: #ff0000;
      ;
    }

    .content_collapsible_colors {
      display: none;
      overflow: hidden;
      background-color: #3f3f3f;
    }

    progress::-webkit-progress-value {
      background-color: #06d406;
    }

    .clasic-mode-short svg{
      background-color: #272727;
      padding: 13px;
      border-radius: 50%;
      margin-bottom: 10px;
      color: #fff;
      cursor: pointer;
    }
    .clasic-mode-short svg:hover{
      background-color: #252525
    }


    /* For Firefox */
    progress {
      background-color: #ff0000;
      border: none;
    }

    progress::-moz-progress-bar {
      background-color: #06d406;
    }

    .barralikes {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    </style>
    <meta http-equiv="Expires" content="0">
    <meta http-equiv="Last-Modified" content="0">
    <meta http-equiv="Cache-Control" content="no-cache, mustrevalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <style>
    .mdcm:hover {
      cursor: pointer;
      background-color: #6b6b6b;

    }

    .hidden {
      display: none;
    }

    .mdcm {
      font-size: 10px;
      background-color: #595959;
      border: none;
      color: #fbf4f4 !important;
      padding: 3px 0;
      margin-left: 10px;
      width: 70px;
      border-radius: 10px;
    }

    .btn_download {
      font-size: 1.2rem;
      padding: 3px;
      color: #fff;
      border-radius: 5px;
      background-color: #ec3203;
    }

    #subscribe-button>ytd-subscribe-button-renderer>yt-button-shape>button:hover {
      zoom: 0.9;
    }

    .containerButtons {
      position: relative;
      display: flex;
      justify-content: center;
      flex-wrap: wrap;
      align-items: center;
      user-select: none;

      border-radius: 20px;
      margin: 0 auto;
      padding: 4px;
    }

    .containerButtons button,
    input {
      display: flex;
      flex-direction: column-reverse;
      margin: 0 5px;
      align-items: center;
      cursor: pointer;
      border-radius: 10px;
    }

    .containerButtons h1,
    h2 {
      user-select: none;
    }

    #MDCM {
      animation: mdcm 10s infinite alternate;
      position: relative;
      transition: 4s;
    }

    .containerButtons button .containerButtons button svg {
      width: 50px;
      height: 40px;
    }
    .content_collapsible_colors {
      position: relative;
      width: 400px;
      margin: auto;
      padding: 10px;
      border-radius: 20px;
    }
    .content_collapsible_colors h2 {
      color: #fff !important;
    }
    .content_collapsible_colors div{
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .colors_buttons_collpases {
      opacity: 0;
      position: absolute;
      height: 24px;
      bottom: 0;
      top: 0;
      left: 10%;
      margin-left: 2px;
      width: 40px;
    }
    #eyes {
      opacity: 0;
      position: absolute;
      height: 24px;
      bottom: 0;
      top: 0;
      width: 24px;
    }

    /* width */
    ::-webkit-scrollbar {
      width: 4px;
      height: 10px;
    }

    /* Track */
    ::-webkit-scrollbar-track {
      background: ##d5d5d5;

    }

    /* Handle */
    ::-webkit-scrollbar-thumb {
      background: #000;

    }

    .containerall {
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .container .botoncalidades {
      margin: 3px 2px;
      width: 24.6%;
    }

    .botoncalidades:first-child {
      background-color: #0af;
    }

    .botoncalidades:last-child {
      background-color: red;
      width: 100px;
    }

    .selectcalidades,
    .botoncalidades,
    .selectcalidadesaudio {
      width: 50%;
      height: 27.8px;
      background-color: #fff;
      color: #000;
      font-size: 25px;
      text-align: center;
      border: none;
      font-size: 20px;
      margin: 2px 2px;
    }

    .botoncalidades {
      width: 70px;
      height: 30px;
      background-color: rgb(4, 156, 22);
      border: 0px solid #000;
      color: #fff;
      font-size: 20px;
      border-radius: 10px;
      margin: 2px 2px;
    }

    .botoncalidades:hover,
    .bntcontainer:hover {
      cursor: pointer;
    }

    .ocultarframe,
    .ocultarframeaudio {
      display: none;
    }

    .progress-button:hover {
      background-color: #000;
    }

    .progress-button {
      display: inline-block;
      font-size: 1em;
      color: #fff;
      text-decoration: none;
      line-height: 1;
      overflow: hidden;
      position: relative;
      text-align: center;
      width: 100%;
      height: 100%;
      box-shadow: 0 1px 1px #ccc;
      border-radius: 2px;
      cursor: pointer;
      background-color: #000;
    }

    #downloadButton:hover .progress-button {
      filter: brightness(95%);
    }

    .progress-button.in-progress,
    .progress-button.finished {
      color: red;
    }

    .progress-button.in-progress:after,
    .progress-button.finished:after {
      position: absolute;
      z-index: 2;
      width: 100%;
      height: 100%;
      text-align: center;
      top: 0;
      padding-top: inherit;
      color: #fff;
      left: 0;
    }

    .progress-button.in-progress:after {
      content: attr(data-loading);
    }

    .progress-button.finished:after {
      content: attr(data-finished);
    }

    .progress-button .tz-bar {
      background-color: #f00;
      height: 3px;
      bottom: 0;
      left: 0;
      width: 0;
      position: absolute;
      z-index: 1;
      border-radius: 0 0 2px 2px;
      -webkit-transition: width 0.5s, height 0.5s;
      -moz-transition: width 0.5s, height 0.5s;
      transition: width 0.5s, height 0.5s;
    }

    .progress-button .tz-bar.background-horizontal {
      height: 100%;
      border-radius: 2px;
    }

    .progress-button .tz-bar.background-vertical {
      height: 0;
      top: 0;
      width: 100%;
      border-radius: 2px;
    }

    .buttonTitle {
      font-size: 0.5em;
      margin-top: 4px;
    }

    #containerbutton {
      height: 100px;
      text-align: center;
    }

    #containerbutton:before {
      content: '';
      display: inline-block;
      vertical-align: middle;
      height: 100%;

    }


    #percentageText {
      width: 95%;
      display: inline-block;
      position: relative;
      vertical-align: middle;
      z-index: 3;
    }

    .headerbutton {
      position: fixed;
      top: 0;
      z-index: 1;
      width: 100%;
      background-color: #f1f1f1;
    }

    .progress-containerbutton {
      width: 99%;
      height: 20px;
      background: #3e3e3c;
    }

    .progress-bar {
      height: 20px;
      background: #f00;
      width: 0%;
    }

    .grecaptcha-badge {
      visibility: hidden;
    }

    button {
      margin: 0;
      padding: 0;
    }

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

    button:hover {
      color: #ec3203;
    }

    .newText{
    font-size: 118%;
    font-weight: 500;
    display: flex;
    }
      .newText span{
    margin-right: 40px;
    }
    .botones_div {
      background-color: transparent;
      border: none;
      color: #FFFFFF;
    }

    #downloadButton:hover{
    background-color: #32644b
    }

    </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: 177px;">
        <!-- Panel for Audio Quality -->
        <div class="ytp-panel" style="width: 397px; height: 177px;">

         <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="8k" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">

                    <span>FLAC UHQ </span>
                      <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="8k" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">

                    <span>WAV UHQ </span>
                      <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="4k" 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="1080" 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="720" 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="480" 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="360" 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: 177px;">
        <!-- Panel for Video Quality -->
        <div class="ytp-panel" style="width: 397px; height: 177px;">

         <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="8k" role="menuitem" tabindex="0" style="padding-left:20px">
                    <div class="newText">
                    <span>4320p 8K WEBM </span>

                      <button id="downloadButton8k" 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(  '8k','downloadButton8k')">
                      Click to Download
                      </button>
                    </div>

                </div>

                 <div class="newVideoTest" aria-haspopup="true"value="4k" role="menuitem" tabindex="1" style="padding-left:20px">
                    <div class="newText">   <span> 2160p 4K WEBM </span>
                    <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="1080" role="menuitem" tabindex="2" style="padding-left:20px">
                    <div class="newText">    <span>1080p </span>
                     <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 HD Mp4 </span>
                    <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 Mp4 </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 Mp4 </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 MP4" 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 MP3" 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 cargarScript(){
        currentVideoUrl = window.location.href;
        currentFunction = "";
        const rightControlsDiv = document.querySelector('.ytp-right-controls');

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

        // Inserta los botones dentro del div
        if (rightControlsDiv) {
            rightControlsDiv.insertAdjacentHTML('beforebegin', menuBotones);
            rightControlsDiv.insertAdjacentHTML('beforebegin', downloadMp4Mp3);
        }

        // Inserta 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 = () => {
                leovideotest.style.display = '';
                leoaudiotest.style.display = 'none';
            };
        }

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

    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=" + encodeURIComponent(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(cargarScript, 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(cargarScript, 2000);
        firstBoot = false;
    }
})();