您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Simple Youtube Video Downloader
当前为
// ==UserScript== // @name Youtube Video Downloader // @namespace http://tampermonkey.net/ // @author fb // @version 1.1 // @description Simple Youtube Video Downloader // @match https://www.youtube.com/watch* // @grant GM_xmlhttpRequest // @connect p.oceansaver.in // @license GPL-3.0-or-later // @run-at document-end // ==/UserScript== (function() { 'use strict'; let animInterval = null; let originalText = ''; const observer = new MutationObserver((mutations, obs) => { const actionsInner = document.querySelector('#end'); if (!actionsInner) return; obs.disconnect(); injectUI(actionsInner); }); observer.observe(document.body, { childList: true, subtree: true }); function injectUI(container) { if (document.getElementById('download-button')) return; const wrapper = document.createElement('div'); wrapper.style.display = 'flex'; wrapper.style.alignItems = 'center'; wrapper.style.marginRight = "10px"; const select = document.createElement('select'); select.id = 'format'; select.className = 'doc'; select.style.marginRight = '8px'; select.innerHTML = ` <optgroup label="Audio"> <option value="mp3">MP3</option> <option value="m4a">M4A</option> <option value="webm">WEBM</option> <option value="aac">AAC</option> <option value="flac">FLAC</option> <option value="opus">OPUS</option> <option value="ogg">OGG</option> <option value="wav">WAV</option> </optgroup> <optgroup label="Video"> <option value="360">MP4 (360p)</option> <option value="480">MP4 (480p)</option> <option value="720">MP4 (720p)</option> <option selected value="1080">MP4 (1080p)</option> <option value="1440">MP4 (1440p)</option> <option value="4k">WEBM (4K)</option> </optgroup> `; const style = document.createElement('style'); style.textContent = ` :root { --btn-bg: #272727; --btn-hover-bg: #3f3f3f; --btn-color: #fff; --btn-radius: 18px; --btn-padding: 0 20px; --btn-font: 500 14px/36px "Roboto","Arial",sans-serif; --btn-cursor: pointer; } #download-button, #format { display: inline-flex; align-items: center; color: var(--btn-color); background-color: var(--btn-bg); border: none; border-radius: var(--btn-radius); padding: var(--btn-padding); white-space: nowrap; text-transform: none; font: var(--btn-font); cursor: var(--btn-cursor); transition: background-color .2s ease; } #download-button:hover, #format:hover { background-color: var(--btn-hover-bg); } #download-button { padding-left: 40px; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-width='0.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4v12'/%3E%3Cpath d='M8 12l4 4 4-4'/%3E%3Cpath d='M4 18h16'/%3E%3C/g%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: 8px center; background-size: 28px; } #format { appearance: none; -webkit-appearance: none; -moz-appearance: none; padding-right: 40px; background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D'10'%20height%3D'7'%20xmlns%3D'http%3A//www.w3.org/2000/svg'%3E%3Cpath%20d%3D'M0%200l5%207%205-7z'%20fill%3D'%23fff'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; } #format::-ms-expand { display: none; } `; document.head.appendChild(style); const btn = document.createElement('button'); btn.id = 'download-button'; btn.textContent = 'Download'; btn.addEventListener('click', startDownload); wrapper.appendChild(select); wrapper.appendChild(btn); container.insertAdjacentElement('afterbegin', wrapper); } function startDownload() { const btn = document.getElementById('download-button'); if (!btn) return; const fmt = document.getElementById('format').value; const videoUrl = encodeURIComponent(window.location.href); const initUrl = `https://p.oceansaver.in/ajax/download.php?format=${fmt}&url=${videoUrl}`; startButtonAnimation(btn); GM_xmlhttpRequest({ method: 'GET', url: initUrl, responseType: 'json', onload(res) { const data = res.response; if (!data || !data.success) { stopButtonAnimation(); alert('❌ Failed to initialize download'); return; } pollProgress(data.progress_url); }, onerror() { stopButtonAnimation(); alert('❌ Network error while starting download'); } }); } function pollProgress(progressUrl) { const intervalId = setInterval(() => { GM_xmlhttpRequest({ method: 'GET', url: progressUrl, responseType: 'json', onload(res) { const p = res.response; if (!p) { clearInterval(intervalId); stopButtonAnimation(); return; } if (p.success) { clearInterval(intervalId); triggerFileDownload(p.download_url); stopButtonAnimation(); } else { console.log(`Download progress: ${p.progress || 'unknown'}`); } }, onerror() { console.error('Error polling download progress'); clearInterval(intervalId); stopButtonAnimation(); } }); }, 1500); } function triggerFileDownload(url) { const a = document.createElement('a'); a.href = url; a.download = ''; document.body.appendChild(a); a.click(); document.body.removeChild(a); } function startButtonAnimation(btn) { originalText = btn.textContent; let dots = 0; animInterval = setInterval(() => { dots = (dots + 1) % 4; btn.textContent = 'Downloading' + '.'.repeat(dots); }, 500); } function stopButtonAnimation() { const btn = document.getElementById('download-button'); if (animInterval) { clearInterval(animInterval); animInterval = null; } if (btn) { btn.disabled = false; btn.textContent = originalText || 'Download'; } } })();