// ==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.1
// @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.y2mate.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, y2mate
// ==/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 videoId = getVideoId();
if (videoId) {
window.open(`https://www.y2mate.com/youtube/${videoId}`, '_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 videoId = getVideoId();
if (videoId) {
window.open(`https://www.y2mate.com/youtube/${videoId}`, '_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();
}
})();