Youtube MP4 downloader (Web)

Download button

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 MP4 downloader (Web) 
// @namespace    http://tampermonkey.net/
// @version      82.4
// @description  Download button
// @author       Frank
// @match        https://www.youtube.com/watch*
// @match        https://m.youtube.com/watch*
// @match        https://youtube.com/watch*
// @icon         https://www.youtube.com/favicon.ico
// @grant        GM_openInTab
// @grant        GM.openInTab
// @run-at       document-idle
// @compatible   chrome
// @compatible   firefox
// @compatible   edge
// @compatible   opera
// @compatible   safari
// ==/UserScript==
// @license MIT

(function() {
    'use strict';

    // Wacht tot de pagina geladen is (cross-browser compatible)
    function waitForElement(selector, callback, maxAttempts = 50) {
        let attempts = 0;
        
        const checkElement = () => {
            const element = document.querySelector(selector);
            if (element) {
                callback(element);
                return true;
            }
            
            attempts++;
            if (attempts >= maxAttempts) {
                console.log('YouTube Download Button: Element niet gevonden na ' + maxAttempts + ' pogingen');
                return false;
            }
            
            return false;
        };
        
        // Probeer direct
        if (checkElement()) return;
        
        // Gebruik MutationObserver voor moderne browsers
        if (typeof MutationObserver !== 'undefined') {
            const observer = new MutationObserver((mutations, obs) => {
                if (checkElement()) {
                    obs.disconnect();
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
            
            // Timeout na 10 seconden
            setTimeout(() => observer.disconnect(), 10000);
        } else {
            // Fallback voor oudere browsers
            const interval = setInterval(() => {
                if (checkElement() || attempts >= maxAttempts) {
                    clearInterval(interval);
                }
            }, 200);
        }
    }

    // Functie om de video ID te krijgen
    function getVideoId() {
        const urlParams = new URLSearchParams(window.location.search);
        return urlParams.get('v');
    }

    // Functie om de download knop te maken
    function createDownloadButton() {
        const videoId = getVideoId();
        if (!videoId) return null;

        // Maak de knop container (zonder YouTube classes)
        const buttonContainer = document.createElement('div');
        buttonContainer.style.marginLeft = '8px';
        buttonContainer.style.display = 'inline-flex';

        // Maak de knop met custom styling
        const button = document.createElement('button');
        button.setAttribute('aria-label', 'Download MP4');
        button.style.display = 'flex';
        button.style.alignItems = 'center';
        button.style.gap = '8px';
        button.style.backgroundColor = '#1976d2';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '18px';
        button.style.padding = '10px 16px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '14px';
        button.style.fontWeight = '500';
        button.style.fontFamily = 'Roboto, Arial, sans-serif';
        button.style.transition = 'background-color 0.2s';
        
        // Hover effect
        button.addEventListener('mouseenter', () => {
            button.style.backgroundColor = '#1565c0';
        });
        button.addEventListener('mouseleave', () => {
            button.style.backgroundColor = '#1976d2';
        });

        // Voeg icoon toe (download icon)
        const icon = document.createElement('div');
        icon.style.display = 'flex';
        icon.style.alignItems = 'center';
        icon.innerHTML = `
            <svg height="20" viewBox="0 0 24 24" width="20" focusable="false" style="pointer-events: none; display: block;">
                <path d="M17 18v1H6v-1h11zm-.5-6.6-.7-.7-3.8 3.7V4h-1v10.4l-3.8-3.8-.7.7 5 5 5-4.9z" fill="white"></path>
            </svg>
        `;

        // Voeg tekst container toe
        const textContainer = document.createElement('div');
        textContainer.style.display = 'flex';
        textContainer.style.alignItems = 'center';
        textContainer.style.gap = '6px';

        // Voeg tekst toe
        const text = document.createElement('span');
        text.textContent = 'Download MP4';
        text.style.lineHeight = '1';

        // Voeg groen vinkje toe
        const checkmark = document.createElement('span');
        checkmark.innerHTML = `
            <svg height="18" width="18" viewBox="0 0 24 24" style="display: block;">
                <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" fill="#00d000"></path>
            </svg>
        `;

        textContainer.appendChild(text);
        textContainer.appendChild(checkmark);

        button.appendChild(icon);
        button.appendChild(textContainer);
        buttonContainer.appendChild(button);

        // Voeg click event toe
        button.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            
            // Haal de volledige YouTube URL op
            const currentUrl = window.location.href;
            
            // Maak de SaveFrom.net URL met de YouTube link in de zoekbalk
            const saveFromUrl = `https://en1.savefrom.net/#url=${encodeURIComponent(currentUrl)}`;
            
            // Open SaveFrom.net in een nieuw tabblad (cross-browser compatible)
            try {
                // Probeer eerst GM_openInTab (Tampermonkey/Greasemonkey)
                if (typeof GM_openInTab !== 'undefined') {
                    GM_openInTab(saveFromUrl, { active: true, insert: true });
                } else if (typeof GM !== 'undefined' && GM.openInTab) {
                    GM.openInTab(saveFromUrl, { active: true, insert: true });
                } else {
                    // Fallback naar window.open voor alle browsers
                    const newWindow = window.open(saveFromUrl, '_blank', 'noopener,noreferrer');
                    if (newWindow) {
                        newWindow.opener = null;
                    }
                }
            } catch (err) {
                // Als alles faalt, gebruik gewone window.open
                window.open(saveFromUrl, '_blank');
            }
        });

        return buttonContainer;
    }

    // Functie om de knop toe te voegen
    function addDownloadButton() {
        // Verwijder bestaande knop als die er al is
        const existingButton = document.getElementById('mp4-download-button');
        if (existingButton) {
            existingButton.remove();
        }

        // Zoek de container met de andere knoppen (like, dislike, share, etc.)
        waitForElement('#actions-inner', (actionsContainer) => {
            // Wacht even voor de knoppen container
            setTimeout(() => {
                const buttonGroup = actionsContainer.querySelector('#top-level-buttons-computed');
                if (buttonGroup) {
                    const downloadButton = createDownloadButton();
                    if (downloadButton) {
                        downloadButton.id = 'mp4-download-button';
                        buttonGroup.appendChild(downloadButton);
                    }
                }
            }, 500);
        });
    }

    // Voeg de knop toe bij het laden
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', addDownloadButton);
    } else {
        addDownloadButton();
    }

    // Voeg de knop opnieuw toe als de URL verandert (bij navigatie binnen YouTube)
    // Werkt in alle browsers
    let lastUrl = location.href;
    
    // MutationObserver voor moderne browsers
    if (typeof MutationObserver !== 'undefined') {
        new MutationObserver(() => {
            const url = location.href;
            if (url !== lastUrl) {
                lastUrl = url;
                setTimeout(addDownloadButton, 1000);
            }
        }).observe(document, { subtree: true, childList: true });
    }
    
    // Fallback: periodieke check voor oudere browsers
    setInterval(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            addDownloadButton();
        }
    }, 2000);

})();