Enhanced YouTube Download Button

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

作者
Mike Stirling
日安装量
6
总安装量
6
评分
0 0 0
版本
2.1
创建于
2025-11-30
更新于
2025-11-30
大小
8.6 KB
许可证
MIT
适用于

// ==UserScript==
// @name Enhanced YouTube Download Button
// @namespace https://github.com/KXYZNEW
// @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/KXYZNEW
// @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;

})();