Greasy Fork

YouTube 视频下载助手|多种清晰度HD🔥|Video&Audio 📥

YouTube视频下载神器,支持1080P/2K高清视频下载,支持字幕下载,支持视频/音频分离下载,支持短视频下载

目前为 2024-12-22 提交的版本。查看 最新版本

// ==UserScript==
// @name         YouTube 视频下载助手|多种清晰度HD🔥|Video&Audio 📥
// @name:en      YouTube Video Downloader | HD Quality Options | Video&Audio 📥
// @name:ja      YouTube動画ダウンローダー|HD高画質|ビデオ&オーディオ 📥
// @name:es      Descargador de YouTube | Alta Calidad HD | Video y Audio 📥
// @name:pt      Baixador de YouTube | Qualidade HD | Vídeo e Áudio 📥
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  YouTube视频下载神器,支持1080P/2K高清视频下载,支持字幕下载,支持视频/音频分离下载,支持短视频下载
// @description:en  Download YouTube videos in HD(1080P/2K), subtitles support, video/audio separate download, shorts download, completely free & no ads
// @description:ja  YouTubeビデオをHD(1080P/2K)でダウンロード、字幕対応、ビデオ/オーディオ分離ダウンロード、ショート動画対応、完全無料&広告なし
// @description:es  Descarga videos de YouTube en HD(1080P/2K), soporte de subtítulos, descarga separada de video/audio, descarga de shorts, completamente gratis y sin anuncios
// @description:pt  Baixe vídeos do YouTube em HD(1080P/2K), suporte a legendas, download separado de vídeo/áudio, download de shorts, totalmente gratuito e sem anúncios
// @author       YouhouLab
// @license      MIT
// @match        https://www.youtube.com/watch*
// @match        https://www.youtube.com/shorts/*
// @match        https://m.youtube.com/watch*
// @match        https://m.youtube.com/shorts/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      www.addyoutube.com
// @run-at       document-end
// @homepage     https://github.com/youhou-project
// @supportURL   https://github.com/youhou-project/issues
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11
// @compatible   chrome
// @compatible   firefox
// @compatible   opera
// @compatible   edge
// @compatible   safari
// @keywords     youtube, download, video, audio, subtitle, shorts, hd, 1080p, 2k, free, no ads, addyoutube
// ==/UserScript==

(function() {
    'use strict';

    const i18n = {
        'zh': {
            downloadText: '免费下载',
            error: {
                addNormalButton: '添加普通下载按钮时出错:',
                addShortsButton: '添加Shorts下载按钮时出错:'
            }
        },
        'en': {
            downloadText: 'Free Download',
            error: {
                addNormalButton: 'Error adding normal download button:',
                addShortsButton: 'Error adding Shorts download button:'
            }
        },
        'ja': {
            downloadText: '無料ダウンロード',
            error: {
                addNormalButton: '通常ダウンロードボタンの追加エラー:',
                addShortsButton: 'Shortsダウンロードボタンの追加エラー:'
            }
        },
        'es': {
            downloadText: 'Descarga Gratis',
            error: {
                addNormalButton: 'Error al agregar botón de descarga normal:',
                addShortsButton: 'Error al agregar botón de descarga Shorts:'
            }
        },
        'pt': {
            downloadText: 'Download Grátis',
            error: {
                addNormalButton: 'Erro ao adicionar botão de download normal:',
                addShortsButton: 'Erro ao adicionar botão de download Shorts:'
            }
        }
    };

    function getCurrentLanguage() {
        const lang = navigator.language.split('-')[0];
        return i18n[lang] ? lang : 'en';
    }

    const currentLang = getCurrentLanguage();
    const texts = i18n[currentLang];

    function getVideoId() {
        const url = window.location.href;
        if (url.includes('/shorts/')) {
            return url.split('/shorts/')[1].split('?')[0];
        }
        return new URLSearchParams(window.location.search).get('v');
    }

    function addNormalDownloadButton() {
        try {
            if (document.getElementById('download-btn')) return;

            const buttonContainer = document.querySelector('#top-level-buttons-computed');
            if (!buttonContainer) {
                return;
            }

            const downloadBtn = document.createElement('button');
            downloadBtn.id = 'download-btn';
            downloadBtn.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m';
            downloadBtn.style.marginLeft = '8px';

            const btnContent = document.createElement('div');
            btnContent.className = 'cbox';
            btnContent.style.display = 'flex';
            btnContent.style.alignItems = 'center';
            
            const icon = document.createElement('span');
            icon.className = 'material-icons';
            icon.style.marginRight = '6px';
            
            const text = document.createElement('span');
            text.textContent = texts.downloadText;
            
            btnContent.appendChild(icon);
            btnContent.appendChild(text);
            downloadBtn.appendChild(btnContent);

            downloadBtn.addEventListener('click', function() {
                const currentUrl = window.location.href;
                const newUrl = currentUrl.replace('youtube.com', 'addyoutube.com');
                window.open(newUrl, '_blank');
            });

            buttonContainer.appendChild(downloadBtn);
        } catch (error) {
            console.error(texts.error.addNormalButton, error);
        }
    }

    function addShortsDownloadButton() {
        try {
            if (document.getElementById('shorts-download-btn')) return;

            const likeButton = document.querySelector('#like-button');
            if (!likeButton) {
                return;
            }

            const buttonContainer = likeButton.parentElement;
            if (!buttonContainer) {
                return;
            }

            const downloadBtn = document.createElement('button');
            downloadBtn.id = 'shorts-download-btn';
            downloadBtn.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m';
            downloadBtn.style.cssText = `
                height: 36px;
                padding: 0 16px;
                margin: 8px 0;
                background: transparent;
                border: none;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                color: #fff;
            `;

            const btnContent = document.createElement('div');
            btnContent.style.display = 'flex';
            btnContent.style.alignItems = 'center';
            btnContent.style.gap = '6px';
            
            const icon = document.createElement('span');
            icon.className = 'material-icons';
            icon.style.fontSize = '20px';
            icon.textContent = 'download';
            
            const text = document.createElement('span');
            text.textContent = texts.downloadText;
            text.style.fontSize = '14px';
            
            btnContent.appendChild(icon);
            btnContent.appendChild(text);
            downloadBtn.appendChild(btnContent);

            downloadBtn.addEventListener('click', function() {
                const currentUrl = window.location.href;
                const newUrl = currentUrl.replace('youtube.com', 'addyoutube.com');
                window.open(newUrl, '_blank');
            });

            buttonContainer.appendChild(downloadBtn);
        } catch (error) {
            console.error(texts.error.addShortsButton, error);
        }
    }

    function addDownloadButton() {
        try {
            const isShorts = window.location.pathname.includes('/shorts/');
            if (isShorts) {
                addShortsDownloadButton();
            } else {
                addNormalDownloadButton();
            }
        } catch (error) {
            console.error('Error adding download button:', error);
        }
    }

    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    const debouncedAddButton = debounce(addDownloadButton, 500);

    const observer = new MutationObserver((mutations) => {
        const shouldUpdate = mutations.some(mutation => {
            return mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0;
        });

        if (shouldUpdate) {
            debouncedAddButton();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', addDownloadButton);
    } else {
        addDownloadButton();
    }
})();