Enhanced YouTube Download Button

Add download button to YouTube videos that redirects to addyoutube.com

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Enhanced YouTube Download Button
// @namespace    https://github.com/MXKXYZ
// @version      2.1
// @description  Add download button to YouTube videos that redirects to addyoutube.com
// @author       KXYZNEW ([email protected])
// @contact      [email protected]
// @homepage     https://github.com/MXKXYZ
// @supportURL   https://github.com/MXKXYZ
// @match        https://www.youtube.com/*
// @match        https://youtube.com/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    // Script information
    const SCRIPT_INFO = {
        name: "Enhanced YouTube Download Button",
        version: "2.1",
        author: "KXYZNEW",
        email: "[email protected]",
        github: "MXKXYZ",
        created: "2024",
        description: "Adds download button to YouTube videos"
    };
    
    console.log(`[${SCRIPT_INFO.name} v${SCRIPT_INFO.version}] Initializing...`);
    console.log(`[${SCRIPT_INFO.name}] Author: ${SCRIPT_INFO.author} (${SCRIPT_INFO.email})`);
    
    const BASE_URL = "https://addyoutube.com";
    const BUTTON_ID = "enhancedDwnldBtn";
    
    // Multiple possible targets for better reliability
    const TARGET_SELECTORS = [
        "#owner",
        "#upload-info", 
        "#channel-name",
        ".ytd-video-owner-renderer",
        "#meta-contents",
        "#above-the-fold"
    ];

    const buttonStyle = `
        #${BUTTON_ID} {
            background-color: #28a745;
            color: #FFFFFF;
            border: 1px solid #3F3F3F;
            border-color: rgba(255,255,255,0.2);
            margin-left: 8px;
            padding: 0 16px;
            border-radius: 18px;
            font-size: 14px;
            font-family: Roboto, Noto, sans-serif;
            font-weight: 500;
            text-decoration: none;
            display: inline-flex;
            align-items: center;
            height: 36px;
            line-height: normal;
            cursor: pointer;
            transition: all 0.2s ease;
        }
        #${BUTTON_ID}:hover {
            background-color: #218838;
            transform: translateY(-1px);
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        #${BUTTON_ID}.loading {
            background-color: #6c757d;
            cursor: not-allowed;
        }
        #${BUTTON_ID}.error {
            background-color: #dc3545;
        }
        
        /* KXYZ Branding - Subtle watermark */
        .kxyz-watermark::after {
            content: "KXYZ";
            position: fixed;
            bottom: 10px;
            right: 10px;
            font-size: 10px;
            color: rgba(255,255,255,0.1);
            font-family: monospace;
            z-index: 9999;
            pointer-events: none;
        }
    `;

    // Add styles and branding
    const style = document.createElement('style');
    style.textContent = buttonStyle;
    document.head.appendChild(style);
    
    // Add subtle watermark
    document.body.classList.add('kxyz-watermark');

    function transformUrl(originalUrl) {
        return originalUrl.replace("youtube.com", "addyoutube.com");
    }

    function findBestTarget() {
        for (const selector of TARGET_SELECTORS) {
            const element = document.querySelector(selector);
            if (element && element.isConnected) {
                console.log(`[${SCRIPT_INFO.name}] Found target: ${selector}`);
                return element;
            }
        }
        console.warn(`[${SCRIPT_INFO.name}] No suitable target found`);
        return null;
    }

    function waitForElement() {
        return new Promise((resolve) => {
            // First check immediately
            const immediateElement = findBestTarget();
            if (immediateElement) {
                return resolve(immediateElement);
            }

            // Then wait for mutations
            const observer = new MutationObserver((mutations) => {
                const element = findBestTarget();
                if (element) {
                    resolve(element);
                    observer.disconnect();
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['class', 'id']
            });

            // Timeout after 10 seconds
            setTimeout(() => {
                observer.disconnect();
                resolve(findBestTarget());
            }, 10000);
        });
    }

    function createButton() {
        const downloadButton = document.createElement('a');
        downloadButton.href = transformUrl(window.location.href);
        downloadButton.target = '_blank';
        downloadButton.id = BUTTON_ID;
        downloadButton.innerText = 'Download';
        downloadButton.title = `Download this video via addyoutube.com | ${SCRIPT_INFO.name} v${SCRIPT_INFO.version} by ${SCRIPT_INFO.author}`;
        
        // Add KXYZ data attribute for identification
        downloadButton.setAttribute('data-kxyz-script', SCRIPT_INFO.name);
        downloadButton.setAttribute('data-author', SCRIPT_INFO.author);
        
        // Add loading state management
        downloadButton.addEventListener('click', function(e) {
            if (this.classList.contains('loading')) {
                e.preventDefault();
                return;
            }
            
            this.classList.add('loading');
            this.innerText = 'Redirecting...';
            
            // Log usage
            console.log(`[${SCRIPT_INFO.name}] Download initiated for: ${window.location.href}`);
            
            // Reset after 3 seconds if still on page
            setTimeout(() => {
                this.classList.remove('loading');
                this.innerText = 'Download';
            }, 3000);
        });
        
        return downloadButton;
    }

    function addButton() {
        // Remove existing button first
        const existingBtn = document.getElementById(BUTTON_ID);
        if (existingBtn) {
            existingBtn.remove();
        }

        waitForElement().then((btnContainer) => {
            if (!btnContainer) {
                console.warn(`[${SCRIPT_INFO.name}] Cannot find container for button`);
                return;
            }

            const downloadButton = createButton();
            
            // Try to insert after subscribe button if it exists
            const subscribeBtn = btnContainer.querySelector('#subscribe-button');
            if (subscribeBtn && subscribeBtn.parentNode) {
                subscribeBtn.parentNode.insertBefore(downloadButton, subscribeBtn.nextSibling);
            } else {
                btnContainer.appendChild(downloadButton);
            }
            
            console.log(`[${SCRIPT_INFO.name}] Button added successfully`);
            console.log(`[${SCRIPT_INFO.name}] Contact: ${SCRIPT_INFO.email} | GitHub: ${SCRIPT_INFO.github}`);
        }).catch(error => {
            console.error(`[${SCRIPT_INFO.name}] Error adding button: ${error}`);
        });
    }

    function updateButton() {
        const btn = document.getElementById(BUTTON_ID);
        if (btn) {
            btn.href = transformUrl(window.location.href);
            btn.classList.remove('loading', 'error');
            btn.innerText = 'Download';
        }
    }

    let currentUrl = window.location.href;

    function checkAndAddButton() {
        if (window.location.pathname === '/watch') {
            // Only re-add if URL changed or button doesn't exist
            if (window.location.href !== currentUrl || !document.getElementById(BUTTON_ID)) {
                addButton();
                currentUrl = window.location.href;
            }
        } else {
            // Remove button if not on watch page
            const btn = document.getElementById(BUTTON_ID);
            if (btn) {
                btn.remove();
            }
        }
    }

    // Enhanced navigation detection
    window.addEventListener("yt-navigate-finish", checkAndAddButton);
    
    // Also check on URL changes (for SPA navigation)
    let lastUrl = location.href;
    new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            setTimeout(checkAndAddButton, 500);
        }
    }).observe(document, { subtree: true, childList: true });

    // Initial check
    setTimeout(checkAndAddButton, 1000);
    
    // Re-check every 5 seconds as backup
    setInterval(checkAndAddButton, 5000);
    
    // Export script info to global scope for debugging
    window.KXYZ_YT_DOWNLOAD_SCRIPT = SCRIPT_INFO;

})();