您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A simple userscript to download YouTube videos in MAX QUALITY
当前为
// ==UserScript== // @name YouTube video downloader // @icon https://raw.githubusercontent.com/madkarmaa/youtube-downloader/main/images/icon.png // @namespace aGkgdGhlcmUgOik= // @source https://github.com/madkarmaa/youtube-downloader // @supportURL https://github.com/madkarmaa/youtube-downloader // @version 1.3.0 // @description A simple userscript to download YouTube videos in MAX QUALITY // @author mk_ // @match *://*.youtube.com/* // @connect co.wuk.sh // @connect raw.githubusercontent.com // @grant GM_addStyle // @grant GM.xmlHttpRequest // @grant GM.xmlhttpRequest // @run-at document-end // ==/UserScript== (async () => { 'use strict'; const randomNumber = Math.floor(Math.random() * Date.now()); function Cobalt(videoUrl, audioOnly = false) { // Use Promise because GM.xmlHttpRequest is async and behaves differently with different userscript managers return new Promise((resolve, reject) => { // https://github.com/wukko/cobalt/blob/current/docs/api.md GM.xmlHttpRequest({ method: 'POST', url: 'https://co.wuk.sh/api/json', headers: { 'Cache-Control': 'no-cache', Accept: 'application/json', 'Content-Type': 'application/json', }, data: JSON.stringify({ url: encodeURI(videoUrl), // video url vQuality: 'max', // always max quality filenamePattern: 'basic', // file name = video title isAudioOnly: audioOnly, disableMetadata: true, // privacy }), onload: (response) => { const data = JSON.parse(response.responseText); if (data?.url) resolve(data.url); else reject(data); }, onerror: (err) => reject(err), }); }); } // https://stackoverflow.com/a/61511955 function waitForElement(selector) { return new Promise((resolve) => { if (document.querySelector(selector)) return resolve(document.querySelector(selector)); const observer = new MutationObserver(() => { if (document.querySelector(selector)) { observer.disconnect(); resolve(document.querySelector(selector)); } }); observer.observe(document.body, { childList: true, subtree: true }); }); } function notify(title, message) { const notificationContainer = document.createElement('div'); notificationContainer.id = `yt-downloader-notification-${randomNumber}`; const titleElement = document.createElement('h3'); titleElement.textContent = title; const messageElement = document.createElement('span'); messageElement.textContent = message; const closeButton = document.createElement('button'); closeButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="1.5rem" viewBox="0 0 384 512"><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg>'; closeButton.addEventListener('click', () => { notificationContainer.remove(); }); notificationContainer.append(titleElement, messageElement, closeButton); document.body.appendChild(notificationContainer); } // true if youtube, false if youtube music const YOUTUBE_SERVICE = window.location.hostname.split('.')[0] !== 'music'; // wait for the button to copy to appear before continuing const buttonToCopy = await waitForElement( YOUTUBE_SERVICE ? 'div#player div.ytp-chrome-controls div.ytp-right-controls button[aria-label="Settings"]' : '[slot="player-bar"] div.middle-controls div.middle-controls-buttons #like-button-renderer #button-shape-dislike button[aria-label="Dislike"]' ); const downloadButton = document.createElement('button'); const buttonId = `yt-downloader-btn-${randomNumber}`; downloadButton.id = buttonId; downloadButton.title = 'Click to download as video\nRight click to download as audio'; downloadButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" height="24" viewBox="0 0 24 24" width="24" focusable="false" style="pointer-events: none; display: block; width: 100%; height: 100%;"><path d="M17 18v1H6v-1h11zm-.5-6.6-.7-.7-3.8 3.7V4h-1v10.4l-3.8-3.8-.7.7 5 5 5-4.9z"></path></svg>'; downloadButton.classList = buttonToCopy.classList; if (YOUTUBE_SERVICE) downloadButton.classList.add('ytp-hd-quality-badge'); downloadButton.classList.add(YOUTUBE_SERVICE ? 'YT' : 'YTM'); // normal click => download video downloadButton.addEventListener('click', async () => { if (!window.location.pathname.slice(1)) return notify('Hey!', 'The video/song player is not open, I cannot see the link to download!'); // do nothing if video is not focused try { window.open(await Cobalt(window.location.href), '_blank'); } catch (err) { notify('An error occurred!', JSON.stringify(err)); } }); // right click => download audio downloadButton.addEventListener('contextmenu', async (e) => { if (!window.location.pathname.slice(1)) return notify('Hey!', 'The video/song player is not open, I cannot see the link to download!'); // do nothing if video is not focused e.preventDefault(); try { window.open(await Cobalt(window.location.href, true), '_blank'); } catch (err) { notify('An error occurred!', JSON.stringify(err)); } return false; }); GM_addStyle(` #${buttonId}.YT > svg { margin-top: 3px; margin-bottom: -3px; } #${buttonId}:hover > svg { fill: #f00; } #yt-downloader-notification-${randomNumber} { background-color: #282828; color: #fff; border: 2px solid #fff; border-radius: 8px; position: fixed; top: 0; right: 0; margin-top: 10px; margin-right: 10px; padding: 15px; z-index: 999; } #yt-downloader-notification-${randomNumber} > h3 { color: #f00; font-size: 2.5rem; } #yt-downloader-notification-${randomNumber} > span { font-style: italic; font-size: 1.5rem; } #yt-downloader-notification-${randomNumber} > button { position: absolute; top: 0; right: 0; background: none; border: none; outline: none; width: fit-content; height: fit-content; margin: 5px; padding: 0; } #yt-downloader-notification-${randomNumber} > button > svg { fill: #fff; } `); const buttonsRow = await waitForElement( YOUTUBE_SERVICE ? 'div#player div.ytp-chrome-controls div.ytp-right-controls' : '[slot="player-bar"] div.middle-controls div.middle-controls-buttons' ); if (!buttonsRow.contains(downloadButton)) buttonsRow.insertBefore(downloadButton, buttonsRow.firstChild); })();