The Best YouTube Downloader! Download Video (Full HD/4K/8K) & Audio (MP3) via Local Server. No limits, no waiting, max speed. Features: Type icons (Video/Audio), auto-clean, fixed UI.
目前為
// ==UserScript==
// @name YouTube Downloader - Local Server Interface - PRO
// @name:pt-BR YouTube Downloader - Local Server Interface - PRO
// @name:es YouTube Downloader - Local Server Interface - PRO
// @name:fr YouTube Downloader - Local Server Interface - PRO
// @name:de YouTube Downloader - Local Server Interface - PRO
// @name:it YouTube Downloader - Local Server Interface - PRO
// @name:ru YouTube Downloader - Local Server Interface - PRO
// @name:zh-CN YouTube Downloader - Local Server Interface - PRO
// @name:ja YouTube Downloader - Local Server Interface - PRO
// @name:ko YouTube Downloader - Local Server Interface - PRO
// @name:hi YouTube Downloader - Local Server Interface - PRO
// @name:id YouTube Downloader - Local Server Interface - PRO
// @namespace http://tampermonkey.net/
// @version 2.3.0
// @description The Best YouTube Downloader! Download Video (Full HD/4K/8K) & Audio (MP3) via Local Server. No limits, no waiting, max speed. Features: Type icons (Video/Audio), auto-clean, fixed UI.
// @description:pt-BR A melhor ferramenta para baixar YouTube! Baixe Vídeos (Full HD/4K/8K) e Áudio (MP3) via Servidor Local. Sem limites, sem espera, velocidade máxima. Recursos: Ícones de tipo, limpeza automática, UI fixa.
// @description:es ¡El mejor descargador de YouTube! Descarga video (Full HD/4K/8K) y audio (MP3) a través del servidor local. Sin límites, sin esperas, máxima velocidad. Características: Iconos de tipo, limpieza automática.
// @description:zh-CN 最好的YouTube下载器!通过本地服务器下载视频(全高清/4K/8K)和音频(MP3)。无限制,无需等待,最高速度。功能:类型图标,自动清理。
// @description:ru Лучший загрузчик YouTube! Скачивайте видео (Full HD/4K/8K) и аудио (MP3) через локальный сервер. Без ограничений, без ожиданий, максимальная скорость.
// @description:fr Le meilleur téléchargeur YouTube ! Téléchargez Vidéo (Full HD/4K/8K) et Audio (MP3) via serveur local. Sans limites, sans attente, vitesse maximale.
// @description:de Der beste YouTube-Downloader! Video (Full HD/4K/8K) & Audio (MP3) über lokalen Server herunterladen. Keine Limits, keine Wartezeit, maximale Geschwindigkeit.
// @description:ja 最高のYouTubeダウンローダー!ローカルサーバー経由でビデオ(フルHD / 4K / 8K)とオーディオ(MP3)をダウンロードします。制限なし、待機なし、最高速度。
// @description:it Il miglior downloader di YouTube! Scarica video (Full HD/4K/8K) e audio (MP3) tramite server locale. Nessun limite, nessuna attesa, massima velocità.
// @description:hi सर्वश्रेष्ठ यूट्यूब डाउनलोडर! स्थानीय सर्वर के माध्यम से वीडियो (पूर्ण एचडी/4K/8K) और ऑडियो (MP3) डाउनलोड करें। कोई सीमा नहीं, कोई प्रतीक्षा नहीं, अधिकतम गति।
// @description:id Pengunduh YouTube Terbaik! Unduh Video (Full HD/4K/8K) & Audio (MP3) melalui Server Lokal. Tanpa batas, tanpa menunggu, kecepatan maksimal.
// @description:ko 최고의 YouTube 다운로더! 로컬 서버를 통해 비디오(Full HD/4K/8K) 및 오디오(MP3)를 다운로드하십시오. 제한 없음, 대기 없음, 최대 속도.
// @description:ar أفضل تنزيل يوتيوب! قم بتنزيل الفيديو (Full HD/4K/8K) والصوت (MP3) عبر الخادم المحلي. لا حدود ، لا انتظار ، أقصى سرعة.
// @copyright 2025, Tauã B. Kloch Leite - All Rights Reserved.
// @author Tauã B. Kloch Leite
// @icon https://img.icons8.com/?size=100&id=9F8aDI7mYs6V&format=png&color=000000
// @match https://www.youtube.com/*
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @grant GM_setClipboard
// ==/UserScript==
(function () {
'use strict';
let policy = null;
if (window.trustedTypes && window.trustedTypes.createPolicy) {
try { policy = window.trustedTypes.createPolicy('yt-dl-policy', { createHTML: (s) => s }); } catch (e) {}
}
const safeHTML = (html) => policy ? policy.createHTML(html) : html;
const SERVER_URL = "http://127.0.0.1:5000";
const POLLING_INTERVAL = 3000;
const ICONS = {
pix: "https://upload.wikimedia.org/wikipedia/commons/a/a2/Logo%E2%80%94pix_powered_by_Banco_Central_%28Brazil%2C_2020%29.svg",
paypal: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
btc: "https://cryptologos.cc/logos/bitcoin-btc-logo.svg?v=025",
eth: "https://cryptologos.cc/logos/ethereum-eth-logo.svg?v=025",
sol: "https://cryptologos.cc/logos/solana-sol-logo.svg?v=025",
bnb: "https://cryptologos.cc/logos/bnb-bnb-logo.svg?v=025",
matic: "https://cryptologos.cc/logos/polygon-matic-logo.svg?v=025",
usdt: "https://cryptologos.cc/logos/tether-usdt-logo.svg?v=025",
ada: "https://cryptologos.cc/logos/cardano-ada-logo.svg?v=025",
doge: "https://cryptologos.cc/logos/dogecoin-doge-logo.svg?v=025"
};
const T = {
title: "Downloader Local v2.2", tab_dl: "Downloads", tab_sup: "Doação", vid: "🎬 Vídeo", aud: "🎵 Áudio", queue: "Fila", done: "Prontos", err: "Erros", refresh: "🔄 Atualizar", clear: "🗑️ Limpar Lista", conn_err: "Servidor Offline?", open: "Abrir", folder: "Pasta", sup_title: "APOIE O PROJETO", sup_desc: "Mantenha as atualizações vivas!", lbl_pix: "CHAVE PIX", btn_copy: "COPIAR", auto_dl: "⬇️ Salvo: ", open_panel: "🚀 Abrir Painel Server", toggle: "👁️ Mostrar/Ocultar"
};
const state = { enabledUI: GM_getValue("yt_dl_enabledUI", true), stats: {}, items: [], activeTab: 'dl' };
const setEnabledUI = (v) => { state.enabledUI = v; GM_setValue("yt_dl_enabledUI", v); renderPanel(); };
const getHistory = () => GM_getValue('yt_dl_history_local', []);
const addToHistory = (f) => { let h=getHistory(); if(!h.includes(f)){ h.push(f); if(h.length>50)h.shift(); GM_setValue('yt_dl_history_local', h); }};
const clearList = async () => {
try { await fetch(`${SERVER_URL}/clear`, { method: 'POST' }); } catch(e){}
GM_setValue('yt_dl_history_local', []);
state.items = [];
state.stats = { total:0, in_progress:0, finished:0, errors:0 };
renderPanel();
};
const css = `
.yt-dl-panel { position: fixed; bottom: 30px; left: 30px; width: 340px; background: #0f0f0f; color: #fff; border-radius: 12px; z-index: 2147483647; font-family: 'Roboto', sans-serif; border: 1px solid #333; font-size: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.9); display: flex; flex-direction: column; }
.yt-dl-head { background: #1a1a1a; display: flex; flex-direction: column; border-bottom: 1px solid #333; flex-shrink: 0; }
.yt-dl-head-top { padding: 12px 16px; display: flex; justify-content: space-between; align-items: center; }
.yt-dl-tabs { display: flex; width: 100%; background: #111; }
.yt-dl-tab { flex: 1; text-align: center; padding: 10px 0; cursor: pointer; color: #666; border-bottom: 2px solid transparent; font-weight: 700; text-transform: uppercase; font-size: 11px; transition: 0.2s; }
.yt-dl-tab.active { color: #fff; border-bottom: 2px solid #d63384; background: #222; }
.yt-dl-body { padding: 0; flex: 1; overflow-y: auto; max-height: 50vh; }
.yt-dl-content { padding: 15px; }
.yt-dl-btn-group { display: flex; gap: 8px; margin-bottom: 15px; }
.yt-dl-btn { flex: 1; border: none; padding: 10px; border-radius: 6px; cursor: pointer; color: #fff; font-weight: 700; font-size: 13px; display: flex; align-items: center; justify-content: center; gap: 5px; transition: 0.2s; box-shadow: 0 2px 5px rgba(0,0,0,0.2); }
.yt-dl-btn:hover { transform: translateY(-1px); filter: brightness(1.1); }
.btn-blue { background: #3ea6ff; color: #000; }
.btn-purple { background: #d63384; color: #fff; }
.btn-gray { background: #333; border: 1px solid #444; }
.btn-red { background: #d32f2f; }
.yt-dl-item { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid #222; }
.yt-dl-thumb { width: 44px; height: 44px; background: #000; border-radius: 6px; object-fit: cover; }
.yt-dl-info { flex: 1; overflow: hidden; display: flex; flex-direction: column; justify-content: center; }
.yt-dl-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; font-size: 12px; margin-bottom: 2px; }
.yt-dl-meta { font-size: 10px; display: flex; align-items: center; gap: 6px; }
.tag-type { padding: 2px 6px; border-radius: 4px; font-weight: bold; font-size: 9px; text-transform: uppercase; }
.tag-vid { background: #0f3d5c; color: #3ea6ff; border: 1px solid #1e5985; }
.tag-aud { background: #3c1f30; color: #ff66b2; border: 1px solid #7d2a58; }
.action-group { display: flex; gap: 5px; }
.btn-open { background: none; border: 1px solid #4caf50; color: #4caf50; cursor: pointer; font-size: 10px; border-radius: 4px; padding: 4px 8px; font-weight: bold; }
.btn-open:hover { background: #4caf50; color: #000; }
.btn-folder { background: none; border: 1px solid #aaa; color: #aaa; cursor: pointer; font-size: 10px; border-radius: 4px; padding: 4px 8px; }
.btn-folder:hover { background: #eee; color: #000; }
.sup-box { padding: 20px; text-align: center; }
.sup-row { display: flex; align-items: center; gap: 8px; background: #1a1a1a; padding: 8px; border-radius: 6px; border: 1px solid #333; margin-bottom: 8px; }
.sup-icon { width: 20px; height: 20px; object-fit: contain; }
.sup-val { flex: 1; background: none; border: none; color: #eee; font-size: 11px; font-family: monospace; outline: none; }
.sup-copy { background: #d63384; border: none; color: #fff; border-radius: 4px; cursor: pointer; font-size: 10px; padding: 4px 8px; }
/* CORREÇÃO PAYPAL ICONE */
.paypal-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; background: #003087; color: white; padding: 8px 20px; border-radius: 20px; text-decoration: none; font-weight: bold; margin: 20px auto 0 auto; width: fit-content; font-size: 12px; }
.paypal-btn img { height: 20px !important; margin: 0; }
`;
const injectCSS = () => { if(!document.getElementById("yt-dl-style")) { const s=document.createElement("style"); s.id="yt-dl-style"; s.textContent=css; document.head.appendChild(s); }};
const toast = (msg, success=true) => {
const el=document.createElement("div");
el.textContent=msg;
el.style.cssText=`position:fixed;top:20px;right:20px;background:${success?'#28a745':'#f44336'};color:#fff;padding:10px 20px;border-radius:4px;z-index:999999;font-weight:bold;box-shadow:0 5px 15px rgba(0,0,0,0.5)`;
document.body.appendChild(el);
setTimeout(()=>el.remove(), 3000);
};
const openLocalFile = async (filename) => {
try { await fetch(`${SERVER_URL}/open_file`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({filename: filename}) }); } catch(e) { toast(T.conn_err, false); }
};
// AGORA ENVIA O TIPO (VIDEO/AUDIO) PARA ABRIR A PASTA CERTA
const openFolder = async (type) => {
try { await fetch(`${SERVER_URL}/open_folder`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({type: type}) }); } catch(e) { toast(T.conn_err, false); }
};
const copyToClipboard = (text) => { GM_setClipboard(text); toast("Copiado!"); };
const refreshData = async () => {
try {
const [sRes, fRes] = await Promise.all([ fetch(`${SERVER_URL}/stats`), fetch(`${SERVER_URL}/files`) ]);
state.stats = await sRes.json();
const files = await fRes.json();
state.items = files.items || [];
state.items.forEach(i => {
if(i.status === 'finished' && i.filename && !getHistory().includes(i.filename)) {
addToHistory(i.filename);
toast(T.auto_dl + i.title.substring(0,20)+"...");
}
});
renderPanel();
} catch (e) {}
};
const send = async (type) => {
try {
await fetch(`${SERVER_URL}/${type === 'video' ? 'download' : 'download_audio'}`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({videoUrl: location.href}) });
refreshData(); toast("OK ✅");
} catch(e) { toast(T.conn_err, false); }
};
let panel;
const renderPanel = () => {
injectCSS();
if(!state.enabledUI) { if(panel) panel.style.display='none'; return; }
if(!panel) { panel=document.createElement('div'); panel.className='yt-dl-panel'; document.body.appendChild(panel); }
panel.style.display='flex';
const bodyDiv = panel.querySelector('.yt-dl-body');
const scrollTop = bodyDiv ? bodyDiv.scrollTop : 0;
const dlContent = `
<div class="yt-dl-content">
<div class="yt-dl-btn-group">
<button class="yt-dl-btn btn-blue" id="btn-vid">${T.vid}</button>
<button class="yt-dl-btn btn-purple" id="btn-aud">${T.aud}</button>
</div>
<div style="font-size:10px; color:#aaa; display:flex; justify-content:space-between; margin-bottom:10px; background:#1a1a1a; padding:8px; border-radius:6px;">
<span>${T.queue}: <b style="color:#ffeb3b">${state.stats.in_progress||0}</b></span>
<span>${T.done}: <b style="color:#4caf50">${state.stats.finished||0}</b></span>
<span>${T.err}: <b style="color:#f44336">${state.stats.errors||0}</b></span>
</div>
<div id="yt-dl-list">${state.items.slice().reverse().slice(0,5).map(i => {
const isAud = i.type === 'audio';
const tagClass = isAud ? 'tag-aud' : 'tag-vid';
const tagText = isAud ? 'MP3' : 'MP4';
const icon = isAud ? '🎵' : '🎬';
return `
<div class="yt-dl-item">
<img class="yt-dl-thumb" src="${i.thumb||''}">
<div class="yt-dl-info">
<div class="yt-dl-name" title="${i.title}">${icon} ${i.title||'...'}</div>
<div class="yt-dl-meta">
<span class="tag-type ${tagClass}">${tagText}</span>
<span style="color:${i.status==='finished'?'#4caf50':'#aaa'}">${i.status}</span>
</div>
</div>
${i.status==='finished' ? `
<div class="action-group">
<button class="btn-open" data-file="${encodeURIComponent(i.filename)}">▶️</button>
<button class="btn-folder" data-type="${i.type}" title="${T.folder}">📂</button>
</div>` : ''}
</div>`;
}).join('')}</div>
<div style="margin-top:15px; display:flex; gap:10px;">
<button class="yt-dl-btn btn-gray" id="btn-refresh" style="font-size:11px; padding:6px; flex:2;">${T.refresh}</button>
<button class="yt-dl-btn btn-red" id="btn-clear" style="font-size:11px; padding:6px; flex:1;">${T.clear}</button>
</div>
</div>`;
// LISTA CRIPTO
const cryptoList = [
{img: ICONS.btc, name: "BTC", val: "bc1q6gz3dtj9qvlxyyh3grz35x8xc7hkuj07knlemn"},
{img: ICONS.eth, name: "ETH", val: "0xd8724d0b19d355e9817d2a468f49e8ce067e70a6"},
{img: ICONS.sol, name: "SOL", val: "7ztAogE7SsyBw7mwVHhUr5ZcjUXQr99JoJ6oAgP99aCn"},
{img: ICONS.usdt, name: "USDT (BEP20)", val: "0xd8724d0b19d355e9817d2a468f49e8ce067e70a6"},
{img: ICONS.bnb, name: "BNB", val: "0xd8724d0b19d355e9817d2a468f49e8ce067e70a6"},
{img: ICONS.matic, name: "MATIC", val: "0xd8724d0b19d355e9817d2a468f49e8ce067e70a6"},
{img: ICONS.ada, name: "ADA", val: "addr1q8..."} // Adicione sua carteira se quiser
].map(c => `<div class="sup-row"><img src="${c.img}" class="sup-icon"><span style="font-size:9px;color:#888;width:30px">${c.name}</span><input type="text" class="sup-val" readonly value="${c.val}"><button class="sup-copy" data-val="${c.val}">${T.btn_copy}</button></div>`).join('');
const supContent = `<div class="sup-box"><div style="color:#d63384;font-weight:bold;margin-bottom:5px">${T.sup_title}</div><div style="color:#aaa;font-size:11px;margin-bottom:15px">${T.sup_desc}</div><div style="text-align:left;color:#d63384;font-weight:bold;font-size:10px;margin-bottom:5px">${T.lbl_pix}</div><div class="sup-row"><img src="${ICONS.pix}" class="sup-icon"><input type="text" class="sup-val" readonly value="69993230419"><button class="sup-copy" data-val="69993230419">${T.btn_copy}</button></div><div style="text-align:left;color:#d63384;font-weight:bold;font-size:10px;margin:15px 0 5px">CRYPTO WALLETS</div>${cryptoList}<a href="https://www.paypal.com/donate/?business=4J4UK7ACU3DS6" target="_blank" class="paypal-btn"><img src="${ICONS.paypal}"> PayPal</a></div>`;
const html = `
<div class="yt-dl-head">
<div class="yt-dl-head-top"><span style="font-weight:700;color:#fff;font-size:13px;">${T.title}</span><span id="yt-dl-close" style="cursor:pointer;color:#aaa;font-size:16px;">×</span></div>
<div class="yt-dl-tabs"><div class="yt-dl-tab ${state.activeTab==='dl'?'active':''}" id="tab-btn-dl">${T.tab_dl}</div><div class="yt-dl-tab ${state.activeTab==='sup'?'active':''}" id="tab-btn-sup">${T.tab_sup}</div></div>
</div>
<div class="yt-dl-body">${state.activeTab === 'dl' ? dlContent : supContent}</div>
<div style="background:#000;padding:8px;text-align:center;font-size:9px;color:#444;border-top:1px solid #222">Developed for <b>Tauã B. Kloch Leite</b> © 2025</div>
`;
panel.innerHTML = safeHTML(html);
if(panel.querySelector('.yt-dl-body')) panel.querySelector('.yt-dl-body').scrollTop = scrollTop;
document.getElementById('yt-dl-close').onclick = () => setEnabledUI(false);
document.getElementById('tab-btn-dl').onclick = () => { state.activeTab='dl'; renderPanel(); };
document.getElementById('tab-btn-sup').onclick = () => { state.activeTab='sup'; renderPanel(); };
if(state.activeTab === 'dl') {
document.getElementById('btn-vid').onclick = () => send('video');
document.getElementById('btn-aud').onclick = () => send('audio');
document.getElementById('btn-refresh').onclick = refreshData;
document.getElementById('btn-clear').onclick = clearList;
panel.querySelectorAll('.btn-open').forEach(b => b.onclick = (e) => openLocalFile(decodeURIComponent(e.target.dataset.file)));
// AQUI: Passa o dataset.type para a função abrir a pasta certa
panel.querySelectorAll('.btn-folder').forEach(b => b.onclick = (e) => openFolder(e.target.dataset.type));
} else {
panel.querySelectorAll('.sup-copy').forEach(btn => { btn.onclick = (e) => copyToClipboard(e.target.dataset.val); });
}
};
const addInlineButtons = () => {
const container = document.querySelector('[id^="top-level-buttons"]');
if (!container || container.querySelector("#yt-dl-inline-vid")) return;
const btnV = document.createElement("button"); btnV.id = "yt-dl-inline-vid"; btnV.textContent = T.vid; btnV.style.cssText = "background:#3ea6ff;color:#000;border:none;padding:6px 12px;border-radius:18px;margin-left:8px;cursor:pointer;font-weight:bold;"; btnV.onclick = (e) => { e.preventDefault(); send('video'); }; container.appendChild(btnV);
};
const observer = new MutationObserver(addInlineButtons);
observer.observe(document.body, { childList: true, subtree: true });
setInterval(refreshData, POLLING_INTERVAL);
window.addEventListener("keydown", (e) => { if (e.altKey && e.shiftKey && (e.key === "Y" || e.key === "y")) { setEnabledUI(!state.enabledUI); e.preventDefault(); } });
// ITENS DO MENU
GM_registerMenuCommand(`${T.toggle} (Alt+Shift+Y)`, () => setEnabledUI(!state.enabledUI));
GM_registerMenuCommand(`${T.open_panel}`, () => GM_openInTab(SERVER_URL + '/panel', {active:true}));
renderPanel(); refreshData();
})();