🏠 Home 

YouTube Cobalt Tools Download Button

Adds a download button to YouTube videos using Cobalt API for downloading videos or audio.

< 脚本YouTube Cobalt Tools Download Button的反馈

评价:好评 - 脚本运行良好

§
发表于:2024-07-02

Cobalt blocked api use now I have fixed it by going to the site itself

// ==UserScript==
// @name         YouTube Cobalt Tools Download Button
// @namespace   http://tampermonkey.net/
// @version     0.4
// @description Adds a download button to YouTube videos using Cobalt API for downloading videos or audio.
// @author       yodaluca23
// @license     GNU GPLv3
// @match       https://*.youtube.com/*
// @match       https://cobalt.tools/
// @grant       GM.xmlHttpRequest
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_openInTab
// ==/UserScript==
(function() {
'use strict';
let lastFetchedQualities = [];
let currentPageUrl = window.location.href;
let initialInjectDelay = 2000;
let navigationInjectDelay = 1000;
function isYouTubeWatchURL() {
return window.location.href.includes("youtube.com/watch?");
}
function fetchVideoQualities(callback) {
GM.xmlHttpRequest({
method: 'GET',
url: window.location.href,
headers: {
'User-Agent': navigator.userAgent,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
},
onload: function(response) {
if (response.status === 200) {
const videoQualities = extractQualities(response.responseText);
const strippedQualities = stripQualityLabels(videoQualities);
const filteredQualities = filterAndRemoveDuplicates(strippedQualities);
console.log('Video Qualities:', filteredQualities);
lastFetchedQualities = filteredQualities;
callback(filteredQualities);
} else {
console.error('Failed to fetch video qualities. Status:', response.status);
callback([]);
}
},
onerror: function(err) {
console.error('Error fetching YouTube video page:', err);
callback([]);
}
});
}
function extractQualities(html) {
const regex = /"(qualityLabel|width)":"([^"]+)"/g;
const qualities = [];
let match;
while ((match = regex.exec(html)) !== null) {
if (match[1] === 'qualityLabel') {
qualities.push(match[2]);
}
}
return qualities;
}
function stripQualityLabels(qualities) {
return qualities.map(quality => {
const index = quality.indexOf('p');
return index !== -1 ? quality.substring(0, index + 1) : quality;
});
}
function filterAndRemoveDuplicates(qualities) {
const filteredQualities = [];
const seenQualities = new Set();
for (let quality of qualities) {
if (!quality.includes('Premium') && !seenQualities.has(quality)) {
filteredQualities.push(quality);
seenQualities.add(quality);
}
}
filteredQualities.sort((a, b) => compareQuality(a, b));
return filteredQualities;
}
function compareQuality(a, b) {
const regex = /(\d+)p/;
const resA = parseInt(a.match(regex)[1]);
const resB = parseInt(b.match(regex)[1]);
return resB - resA;
}
function arraysEqual(arr1, arr2) {
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
return true;
}
function injectDownloadButton() {
setTimeout(() => {
const existingButton = document.getElementById('cobalt-download-btn');
if (existingButton) {
existingButton.remove();
}
const downloadButton = document.createElement('button');
downloadButton.id = 'cobalt-download-btn';
downloadButton.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m yt-spec-button-shape-next--icon-leading';
downloadButton.setAttribute('aria-label', 'Download');
downloadButton.setAttribute('title', 'Download');
downloadButton.innerHTML = `
<div class="yt-spec-button-shape-next__icon">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" focusable="false" style="pointer-events: none; display: inline-block; width: 24px; height: 24px; vertical-align: middle;">
<path fill="currentColor" 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>
</div>
<div class="yt-spec-button-shape-next__button-text-content">Download</div>
`;
downloadButton.style.backgroundColor = 'rgb(44, 44, 44)';
downloadButton.style.border = '0px solid rgb(204, 204, 204)';
downloadButton.style.borderRadius = '30px';
downloadButton.style.fontSize = '14px';
downloadButton.style.padding = '8px 16px';
downloadButton.style.cursor = 'pointer';
downloadButton.style.marginLeft = '8px';
downloadButton.style.marginRight = '0px';
downloadButton.onclick = () => showQualityPopup(currentPageUrl);
const actionMenu = document.querySelector('.top-level-buttons');
actionMenu.appendChild(downloadButton);
}, initialInjectDelay);
}
function removeNativeDownloadButton() {
setTimeout(() => {
const nativeDownloadButtonInOverflow = document.querySelector('ytd-menu-service-item-download-renderer');
if (nativeDownloadButtonInOverflow) {
nativeDownloadButtonInOverflow.remove();
}
const nativeDownloadButton = document.querySelector('ytd-download-button-renderer');
if (nativeDownloadButton) {
nativeDownloadButton.remove();
}
}, initialInjectDelay);
}
function showQualityPopup(videoUrl) {
fetchVideoQualities((qualities) => {
const formatOptions = ['mp4', 'webm', 'ogg', 'mp3', 'opus', 'wav'];
const qualityPrompt = `
<div id="cobalt-quality-picker" style="
background: black;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
max-width: 300px;
width: 100%;
max-height: 400px;
overflow-y: auto;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);">
<h2 style="text-align: center; margin-bottom: 20px; font-size: 1.5em; color: #fff;">Select Quality</h2>
<label for="cobalt-format" style="display: block; margin-bottom: 10px; font-weight: bold; color: #fff; ">Format:</label>
<select id="cobalt-format" style="margin-bottom: 10px; width: 100%; padding: 5px; border-radius: 5px; border: 1px solid #ccc; color: #fff; background: black;">
${formatOptions.map(format => `<option value="${format}">${format}</option>`).join('')}
</select>
<label id="quality-label" for="cobalt-quality" style="display: block; margin-bottom: 10px; font-weight: bold; color: #fff;">Quality:</label>
<select id="cobalt-quality" style="margin-bottom: 10px; width: 100%; padding: 5px; border-radius: 5px; border: 1px solid #ccc; color: #fff; background: black;">
${qualities.map(q => `<option value="${q}">${q}</option>`).join('')}
</select>
<div id="cobalt-loading" style="display: none; margin-bottom: 10px; text-align: center; color: #fff;">Loading...</div>
<button id="cobalt-start-download" style="display: block; width: 100%; padding: 10px; background-color: black; color: #fff; border: none; border-radius: 5px; cursor: pointer;">Download</button>
</div>`
const popupContainer = document.createElement('div');
popupContainer.innerHTML = qualityPrompt;
document.body.appendChild(popupContainer);
document.addEventListener('click', (event) => {
if (!popupContainer.contains(event.target)) {
document.body.removeChild(popupContainer);
}
}, { once: true });
const qualityDropdown = document.getElementById('cobalt-quality');
const loadingIndicator = document.getElementById('cobalt-loading');
const formatDropdown = document.getElementById('cobalt-format');
const startDownloadBtn = document.getElementById('cobalt-start-download');
formatDropdown.addEventListener('change', () => {
const isAudioFormat = formatDropdown.value === 'mp3' || formatDropdown.value === 'opus' || formatDropdown.value === 'wav';
const qualityLabel = document.getElementById('quality-label');
if (isAudioFormat) {
qualityLabel.style.display = 'none';
qualityDropdown.style.display = 'none';
} else {
qualityLabel.style.display = 'block';
qualityDropdown.style.display = 'block';
}
});
startDownloadBtn.addEventListener('click', async () => {
try {
loadingIndicator.style.display = 'block';
startDownloadBtn.disabled = true;
startDownloadBtn.style.cursor = 'not-allowed';
const format = formatDropdown.value;
const quality = qualityDropdown.value;
GM_setValue("quality", quality);
GM_setValue("format", format);
GM_setValue("url", window.location.href);
const url = 'https://cobalt.tools';
try {
GM_openInTab(url, true);
} catch (e) {
window.open(url, '_blank');
}
} catch (err) {
console.error('Error fetching download URL:', err);
GM_notification('Failed to fetch download link. Please try again.', 'Error');
} finally {
loadingIndicator.style.display = 'none';
startDownloadBtn.disabled = false;
startDownloadBtn.style.cursor = 'pointer';
}
document.body.removeChild(popupContainer);
});
});
}
function initializeDownloadButton() {
injectDownloadButton();
removeNativeDownloadButton();
}
if(window.location.href.startsWith('https://cobalt')) {
const format = GM_getValue("format");
document.querySelector("#settings-footer").click();
const settings = document.getElementsByClassName("switch");
let thing = '';
thing = (format == "webm" || format == "mp4") ? GM_getValue("quality") : format;
if(format == "webm" || format == "mp4") document.querySelector('#tab-button-settings-audio').click();
//console.log(quality);
for (let i = 0; i < settings.length; i++) {
// console.log(settings[i].innerText);
// This works but sometimes it just fail to download above 1080p
if (settings[i].textContent == thing) {
settings[i].click();
break;
}
}
if (format == "webm") document.querySelector("#vCodec-vp9").click();
document.querySelector(".tab-settings.switch.back-button").click();
if((format != "webm" && format != "mp4")) document.querySelector('#audioMode-true').click();
download(GM_getValue("url")); //This the method on cobalt website
}
else {
if (isYouTubeWatchURL()) {
setTimeout(() => {
initializeDownloadButton();
}, initialInjectDelay);
}
window.onpopstate = function(event) {
setTimeout(() => {
if (currentPageUrl !== window.location.href) {
currentPageUrl = window.location.href;
console.log('URL changed:', currentPageUrl);
if (isYouTubeWatchURL()) {
initializeDownloadButton();
}
const existingPopup = document.querySelector('#cobalt-quality-picker');
if (existingPopup) {
existingPopup.remove();
}
}
}, navigationInjectDelay);
};
const observer = new MutationObserver(mutations => {
for (let mutation of mutations) {
if (mutation.type === 'childList' && mutation.target.classList.contains('html5-video-player')) {
console.log('Video player changed');
setTimeout(() => {
currentPageUrl = window.location.href;
if (isYouTubeWatchURL()) {
initializeDownloadButton();
}
}, navigationInjectDelay);
const existingPopup = document.querySelector('#cobalt-quality-picker');
if (existingPopup) {
existingPopup.remove();
}
break;
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
})();
yodaluca23作者
§
发表于:2024-07-02

I talked to them about it in their Discord, it should be fixed now, it was mistakenly taken down with the goodtube take down. https://github.com/imputnet/cobalt/issues/600

§
发表于:2024-07-02
编辑于:2024-07-02

Bruh

Please still change // @match *://*.youtube.com/*
To
// @match https://www.youtube.com/*
Because Cromite recognizes scripts as invalid if there is a * in the domain

Also I styled the quality popup a bit take a look at it if you want

发表回复

登录以发表回复。