Youtube MP4 downloader (Web)

Download button

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo 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 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);

})();