Enhanced YouTube Download Button

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

目前為 2025-11-30 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

作者
Mike Stirling
評價
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;

})();