Add download button to YouTube videos that redirects to addyoutube.com
// ==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;
})();