YouTube Direct Downloader (Cobalt)

Bypass the download button and provide options to download the video, video dubs, or audio directly from the YouTube page.

Verzia zo dňa 24.02.2025. Pozri najnovšiu verziu.

// ==UserScript==
// @name         YouTube Direct Downloader (Cobalt)
// @description  Bypass the download button and provide options to download the video, video dubs, or audio directly from the YouTube page.
// @icon         https://www.google.com/s2/favicons?sz=64&domain=cobalt.tools
// @version      1.0
// @author       afkarxyz
// @namespace    https://github.com/afkarxyz/misc-scripts/
// @supportURL   https://github.com/afkarxyz/misc-scripts/issues
// @license      MIT
// @match        https://www.youtube.com/*
// @match        https://youtube.com/*
// @grant        GM.xmlHttpRequest
// @connect      cobalt-api.ayo.tf
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    const LANGUAGE_MAP = {
        "af": "Afrikaans",
        "am": "አማርኛ",
        "ar": "العربية",
        "as": "Assamese",
        "az": "azərbaycan",
        "be": "Belarusian",
        "bg": "български",
        "bn": "বাংলা",
        "bs": "bosanski",
        "ca": "català",
        "cs": "čeština",
        "da": "dansk",
        "de": "Deutsch",
        "el": "Ελληνικά",
        "en": "English",
        "es": "español",
        "et": "eesti",
        "eu": "Basque",
        "fa": "فارسی",
        "fi": "suomi",
        "fil": "Filipino",
        "fr": "français",
        "gl": "Galician",
        "gu": "ગુજરાતી",
        "hi": "हिन्दी",
        "hr": "hrvatski",
        "hu": "magyar",
        "hy": "Armenian",
        "id": "Indonesia",
        "is": "Icelandic",
        "it": "italiano",
        "iw": "עברית",
        "ja": "日本語",
        "ka": "Georgian",
        "kk": "Kazakh",
        "km": "Khmer",
        "kn": "ಕನ್ನಡ",
        "ko": "한국어",
        "ky": "Kyrgyz",
        "lo": "Lao",
        "lt": "lietuvių",
        "lv": "latviešu",
        "mk": "Macedonian",
        "ml": "മലയാളം",
        "mn": "Mongolian",
        "mr": "मराठी",
        "ms": "Melayu",
        "my": "Burmese",
        "ne": "Nepali",
        "nl": "Nederlands",
        "no": "norsk",
        "or": "Odia",
        "pa": "ਪੰਜਾਬੀ",
        "pl": "polski",
        "pt": "português",
        "ro": "română",
        "ru": "русский",
        "si": "Sinhala",
        "sk": "slovenčina",
        "sl": "slovenščina",
        "sq": "Albanian",
        "sr": "српски",
        "sv": "svenska",
        "sw": "Kiswahili",
        "ta": "தமிழ்",
        "te": "తెలుగు",
        "th": "ไทย",
        "tr": "Türkçe",
        "uk": "українська",
        "ur": "اردو",
        "uz": "o'zbek",
        "vi": "Tiếng Việt",
        "zh-CN": "中文(中国)",
        "zh-HK": "中文(香港)",
        "zh-TW": "中文(台灣)",
        "zu": "Zulu"
    };

    function triggerDirectDownload(url) {
        const a = document.createElement('a');
        a.style.display = 'none';
        a.href = url;
        document.body.appendChild(a);
        a.click();
        setTimeout(() => {
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }, 100);
    }

    function createDownloadDialog() {
        const dialog = document.createElement('div');
        dialog.className = 'yt-download-dialog';
        dialog.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #000000;
            color: #e1e1e1;
            border-radius: 12px;
            box-shadow: 0 0 0 1px rgba(225,225,225,.1), 0 2px 4px 1px rgba(225,225,225,.18);
            font-family: 'IBM Plex Mono', 'Noto Sans Mono Variable', 'Noto Sans Mono', monospace;
            width: 400px;
            z-index: 9999;
        `;

        const dialogContent = document.createElement('div');
        dialogContent.style.padding = '16px';

        const styleElement = document.createElement('style');
        styleElement.textContent = `
            @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&display=swap');

            .quality-grid {
                display: grid;
                grid-template-columns: repeat(3, 1fr);
                gap: 8px;
                margin-bottom: 16px;
            }

            .quality-option {
                display: flex;
                align-items: center;
                padding: 8px;
                cursor: pointer;
            }

            .quality-option:hover {
                background: #191919;
                border-radius: 6px;
            }

            .logo-container {
                display: flex;
                align-items: center;
                gap: 8px;
                margin-bottom: 16px;
            }

            .subtitle {
                color: #e1e1e1;
                opacity: 0.7;
                font-size: 12px;
                margin-top: 4px;
            }

            .title {
                font-size: 18px;
                font-weight: 700;
            }

            .title-link {
                text-decoration: none;
                color: inherit;
                cursor: pointer;
                transition: opacity 0.2s ease;
            }

            .title-link:hover {
                opacity: 0.8;
            }

            .codec-selector {
                margin-bottom: 16px;
                display: flex;
                gap: 8px;
                justify-content: center;
            }

            .codec-button {
                background: transparent;
                border: 1px solid #e1e1e1;
                color: #e1e1e1;
                padding: 6px 12px;
                border-radius: 14px;
                cursor: pointer;
                font-family: inherit;
                font-size: 12px;
                transition: all 0.2s ease;
            }

            .codec-button:hover {
                background: #808080;
                color: #000000;
            }

            .codec-button.selected {
                background: #1ed760;
                border-color: #1ed760;
                color: #000000;
            }

            .download-status {
                text-align: center;
                margin: 16px 0;
                font-size: 12px;
                display: none;
            }

            .button-container {
                display: flex;
                justify-content: center;
                gap: 8px;
            }

            .switch-container {
                position: absolute;
                top: 16px;
                right: 16px;
                display: flex;
                align-items: center;
            }
            .switch-button {
                background: transparent;
                border: none;
                cursor: pointer;
                padding: 4px;
                transition: all 0.2s ease;
            }
            .switch-button svg {
                width: 20px;
                height: 20px;
                fill: #e1e1e1;
                transition: all 0.2s ease;
            }
            .switch-button:hover svg {
                fill: #1ed760;
            }
            .audio-options {
                display: none;
                margin-bottom: 16px;
            }
            .audio-options.active {
                display: block;
            }
            .dub-selector {
                margin-top: 16px;
                margin-bottom: 16px;
                display: none;
            }
            .dub-select {
                width: 80%;
                margin: 0 auto;
                display: block;
            }
            .dub-button {
                background: transparent;
                border: 1px solid #39a9db;
                color: #39a9db;
            }
            .dub-button:hover {
                background: #39a9db;
                color: #000000;
            }
            .dub-button.selected {
                background: #39a9db;
                border-color: #39a9db;
                color: #000000;
            }
        `;
        dialog.appendChild(styleElement);

        const logoContainer = document.createElement('div');
        logoContainer.className = 'logo-container';

        const logoSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        logoSvg.setAttribute('width', '24');
        logoSvg.setAttribute('height', '16');
        logoSvg.setAttribute('viewBox', '0 0 24 16');
        logoSvg.setAttribute('fill', 'none');

        const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path1.setAttribute('d', 'M0 15.6363L0 12.8594L9.47552 8.293L0 3.14038L0 0.363525L12.8575 7.4908V9.21862L0 15.6363Z');
        path1.setAttribute('fill', 'white');

        const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path2.setAttribute('d', 'M11.1425 15.6363V12.8594L20.6181 8.293L11.1425 3.14038V0.363525L24 7.4908V9.21862L11.1425 15.6363Z');
        path2.setAttribute('fill', 'white');

        logoSvg.appendChild(path1);
        logoSvg.appendChild(path2);

        const logoDiv = document.createElement('div');
        logoDiv.id = 'cobalt-logo';
        logoDiv.appendChild(logoSvg);

        logoContainer.appendChild(logoDiv);

        const titleContainer = document.createElement('div');
        const titleLink = document.createElement('a');
        titleLink.href = 'https://greasyfork.org/en/users/1376410';
        titleLink.target = '_blank';
        titleLink.rel = 'noopener noreferrer';
        titleLink.className = 'title-link';

        const title = document.createElement('div');
        title.className = 'title';
        title.textContent = 'cobalt';
        
        const statusSpan = document.createElement('span');
        statusSpan.id = 'api-status';
        statusSpan.style.marginLeft = '8px';
        statusSpan.style.fontSize = '14px';
        statusSpan.style.fontWeight = 'normal';
        statusSpan.textContent = 'checking...';
        title.appendChild(statusSpan);

        titleLink.appendChild(title);

        checkApiStatus(function(isOnline) {
            const statusSpan = document.getElementById('api-status');
            if (statusSpan) {
                statusSpan.textContent = isOnline ? 'Online' : 'Offline';
                statusSpan.style.color = isOnline ? '#1ed760' : '#f3727f';
            }
        });

        titleContainer.appendChild(titleLink);

        const subtitle = document.createElement('div');
        subtitle.className = 'subtitle';
        subtitle.textContent = 'YouTube Direct Downloader';

        titleContainer.appendChild(subtitle);
        logoContainer.appendChild(titleContainer);

        dialogContent.appendChild(logoContainer);

        const switchContainer = document.createElement('div');
        switchContainer.className = 'switch-container';

        const switchButton = document.createElement('button');
        switchButton.className = 'switch-button';
        switchButton.id = 'mode-switch';

        const switchSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        switchSvg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
        switchSvg.setAttribute('viewBox', '0 0 384 512');

        const switchPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        switchPath.setAttribute('d', 'M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM64 288c0-17.7 14.3-32 32-32l96 0c17.7 0 32 14.3 32 32l0 96c0 17.7-14.3 32-32 32l-96 0c-17.7 0-32-14.3-32-32l0-96zM300.9 397.9L256 368l0-64 44.9-29.9c2-1.3 4.4-2.1 6.8-2.1c6.8 0 12.3 5.5 12.3 12.3l0 103.4c0 6.8-5.5 12.3-12.3 12.3c-2.4 0-4.8-.7-6.8-2.1z');

        switchSvg.appendChild(switchPath);
        switchButton.appendChild(switchSvg);
        switchContainer.appendChild(switchButton);

        dialogContent.appendChild(switchContainer);

        const videoOptions = document.createElement('div');
        videoOptions.id = 'video-options';

        const videoCodecSelector = document.createElement('div');
        videoCodecSelector.className = 'codec-selector';

        ['h264', 'vp9', 'av1'].forEach(codec => {
            const button = document.createElement('button');
            button.className = 'codec-button';
            button.dataset.codec = codec;
            button.textContent = codec.toUpperCase();
            videoCodecSelector.appendChild(button);
        });

        const dubButton = document.createElement('button');
        dubButton.className = 'codec-button dub-button';
        dubButton.dataset.codec = 'dub';
        dubButton.textContent = 'DUB';
        videoCodecSelector.appendChild(dubButton);

        videoOptions.appendChild(videoCodecSelector);

        const qualityOptions = document.createElement('div');
        qualityOptions.id = 'quality-options';
        qualityOptions.className = 'quality-grid';
        videoOptions.appendChild(qualityOptions);

        const dubSelector = document.createElement('div');
        dubSelector.className = 'dub-selector';
        dubSelector.style.display = 'none';

        const dubSelect = document.createElement('select');
        dubSelect.className = 'dub-select';
        dubSelect.style.cssText = `
            padding: 8px;
            background: #191919;
            color: #e1e1e1;
            border: 1px solid #e1e1e1;
            border-radius: 6px;
            font-family: inherit;
            cursor: pointer;
        `;

        const defaultOption = document.createElement('option');
        defaultOption.value = '';
        defaultOption.textContent = 'Original Audio';
        dubSelect.appendChild(defaultOption);

        Object.entries(LANGUAGE_MAP).forEach(([code, name]) => {
            const option = document.createElement('option');
            option.value = code;
            option.textContent = `${name} (${code})`;
            dubSelect.appendChild(option);
        });

        dubSelector.appendChild(dubSelect);
        videoOptions.appendChild(dubSelector);

        dialogContent.appendChild(videoOptions);

        const audioOptions = document.createElement('div');
        audioOptions.id = 'audio-options';
        audioOptions.className = 'audio-options';

        const audioCodecSelector = document.createElement('div');
        audioCodecSelector.className = 'codec-selector';

        ['mp3', 'ogg', 'opus', 'wav'].forEach(codec => {
            const button = document.createElement('button');
            button.className = 'codec-button';
            button.dataset.codec = codec;
            button.textContent = codec.toUpperCase();
            audioCodecSelector.appendChild(button);
        });

        audioOptions.appendChild(audioCodecSelector);

        const bitrateOptions = document.createElement('div');
        bitrateOptions.id = 'bitrate-options';
        bitrateOptions.className = 'quality-grid';
        audioOptions.appendChild(bitrateOptions);

        dialogContent.appendChild(audioOptions);

        const downloadStatus = document.createElement('div');
        downloadStatus.className = 'download-status';
        downloadStatus.id = 'download-status';
        dialogContent.appendChild(downloadStatus);

        const buttonContainer = document.createElement('div');
        buttonContainer.className = 'button-container';

        const cancelButton = document.createElement('button');
        cancelButton.id = 'cancel-button';
        cancelButton.textContent = 'Cancel';
        cancelButton.style.cssText = `
            background: transparent;
            border: 1px solid #e1e1e1;
            color: #e1e1e1;
            font-size: 14px;
            font-weight: 500;
            padding: 8px 16px;
            cursor: pointer;
            font-family: inherit;
            border-radius: 18px;
        `;

        const downloadButton = document.createElement('button');
        downloadButton.id = 'download-button';
        downloadButton.textContent = 'Download';
        downloadButton.style.cssText = `
            background: transparent;
            border: 1px solid #e1e1e1;
            color: #e1e1e1;
            font-size: 14px;
            font-weight: 500;
            padding: 8px 16px;
            border-radius: 18px;
            cursor: pointer;
            font-family: inherit;
        `;

        buttonContainer.appendChild(cancelButton);
        buttonContainer.appendChild(downloadButton);

        dialogContent.appendChild(buttonContainer);

        dialog.appendChild(dialogContent);

        const backdrop = document.createElement('div');
        backdrop.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            z-index: 9998;
        `;
        document.body.appendChild(backdrop);

        backdrop.addEventListener('click', () => {
            closeDialog(dialog, backdrop);
        });

        const savedCodec = localStorage.getItem('cobaltToolsCodec') || 'h264';
        const savedQuality = localStorage.getItem('cobaltToolsQuality') || '1080p';
        const savedMode = localStorage.getItem('cobaltToolsMode') || 'video';
        const savedAudioCodec = localStorage.getItem('cobaltToolsAudioCodec') || 'mp3';
        const savedDub = localStorage.getItem('cobaltToolsDub') || '';

        return { dialog, backdrop, savedCodec, savedQuality, savedMode, savedAudioCodec, savedDub };
    }

    function closeDialog(dialog, backdrop) {
        dialog.remove();
        backdrop.remove();
    }

    function extractVideoId(url) {
        const urlObj = new URL(url);
        const searchParams = new URLSearchParams(urlObj.search);
        return searchParams.get('v');
    }

    function checkApiStatus(callback) {
        GM.xmlHttpRequest({
            method: 'GET',
            url: 'https://cobalt-api.ayo.tf/',
            timeout: 5000,
            onload: function(response) {
                callback(response.status >= 200 && response.status < 300);
            },
            onerror: function() {
                callback(false);
            },
            ontimeout: function() {
                callback(false);
            }
        });
    }

    function downloadVideo(quality, videoId, codec, dialog, backdrop) {
        const statusElement = dialog.querySelector('#download-status');
        statusElement.style.display = 'block';
        statusElement.textContent = 'Preparing download...';

        const dubSelect = dialog.querySelector('.dub-select');
        const selectedDub = dubSelect ? dubSelect.value : '';

        const payload = {
            url: `https://www.youtube.com/watch?v=${videoId}`,
            downloadMode: "auto",
            filenameStyle: "basic",
            videoQuality: quality.replace('p', ''),
            youtubeVideoCodec: codec,
            youtubeDubLang: selectedDub ? selectedDub : 'original'
        };

        GM.xmlHttpRequest({
            method: 'POST',
            url: 'https://cobalt-api.ayo.tf/',
            headers: {
                'accept': 'application/json',
                'content-type': 'application/json'
            },
            data: JSON.stringify(payload),
            responseType: 'json',
            onload: function(response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data.url) {
                        statusElement.textContent = 'Starting download...';
                        triggerDirectDownload(data.url);

                        setTimeout(() => {
                            closeDialog(dialog, backdrop);
                        }, 1000);
                    } else {
                        statusElement.textContent = 'Error: No download URL found';
                        console.error('No URL in response:', data);
                    }
                } catch (error) {
                    statusElement.textContent = 'Error: API service might be temporarily unavailable';
                    console.error('Error processing response:', error);
                }
            },
            onerror: function(error) {
                statusElement.textContent = 'Network error. Please check your connection.';
                console.error('Network error:', error);
            }
        });
    }

    function updateQualityOptions(dialog, codec, savedQuality) {
        const qualityOptions = dialog.querySelector('#quality-options');
        while (qualityOptions.firstChild) {
            qualityOptions.removeChild(qualityOptions.firstChild);
        }

        let qualities;
        if (codec === 'h264') {
            qualities = ['144p', '240p', '360p', '480p', '720p', '1080p'];
        } else if (codec === 'vp9') {
            qualities = ['144p', '240p', '360p', '480p', '720p', '1080p', '1440p', '4k'];
        } else {
            qualities = ['144p', '240p', '360p', '480p', '720p', '1080p', '1440p', '4k', '8k+'];
        }

        qualities.forEach((quality, index) => {
            const option = document.createElement('div');
            option.className = 'quality-option';

            const input = document.createElement('input');
            input.type = 'radio';
            input.id = `quality-${index}`;
            input.name = 'quality';
            input.value = quality;
            input.style.marginRight = '8px';

            const label = document.createElement('label');
            label.htmlFor = `quality-${index}`;
            label.style.fontSize = '14px';
            label.style.cursor = 'pointer';
            label.textContent = quality;

            option.appendChild(input);
            option.appendChild(label);
            qualityOptions.appendChild(option);

            option.addEventListener('click', function() {
                const radioButton = this.querySelector('input[type="radio"]');
                qualityOptions.querySelectorAll('input[type="radio"]').forEach(rb => {
                    rb.checked = false;
                });
                radioButton.checked = true;

                localStorage.setItem('cobaltToolsQuality', quality);
            });
        });

        const defaultQuality = qualities.includes(savedQuality) ? savedQuality : qualities[qualities.length - 1];
        const defaultRadio = dialog.querySelector(`input[name="quality"][value="${defaultQuality}"]`);
        if (defaultRadio) {
            defaultRadio.checked = true;
        }
    }

    function updateAudioOptions(dialog, codec, savedBitrate) {
        const bitrateOptions = dialog.querySelector('#bitrate-options');
        while (bitrateOptions.firstChild) {
            bitrateOptions.removeChild(bitrateOptions.firstChild);
        }

        if (codec === 'wav') {
            return;
        }

        const bitrates = ['8', '64', '96', '128', '256', '320'];

        bitrates.forEach((bitrate, index) => {
            const option = document.createElement('div');
            option.className = 'quality-option';

            const input = document.createElement('input');
            input.type = 'radio';
            input.id = `bitrate-${index}`;
            input.name = 'bitrate';
            input.value = bitrate;
            input.style.marginRight = '8px';

            const label = document.createElement('label');
            label.htmlFor = `bitrate-${index}`;
            label.style.fontSize = '14px';
            label.style.cursor = 'pointer';
            label.textContent = `${bitrate} kb/s`;

            option.appendChild(input);
            option.appendChild(label);
            bitrateOptions.appendChild(option);

            option.addEventListener('click', function() {
                const radioButton = this.querySelector('input[type="radio"]');
                bitrateOptions.querySelectorAll('input[type="radio"]').forEach(rb => {
                    rb.checked = false;
                });
                radioButton.checked = true;

                localStorage.setItem('cobaltToolsBitrate', bitrate);
            });
        });

        const defaultBitrate = bitrates.includes(savedBitrate) ? savedBitrate : bitrates[bitrates.length - 1];
        const defaultRadio = dialog.querySelector(`input[name="bitrate"][value="${defaultBitrate}"]`);
        if (defaultRadio) {
            defaultRadio.checked = true;
        }
    }

    function downloadAudio(format, bitrate, videoId, dialog, backdrop) {
        const statusElement = dialog.querySelector('#download-status');
        statusElement.style.display = 'block';
        statusElement.textContent = 'Preparing audio download...';

        let payload;
        if (format === 'wav') {
            payload = {
                url: `https://www.youtube.com/watch?v=${videoId}`,
                downloadMode: "audio",
                filenameStyle: "basic",
                audioFormat: "wav"
            };
        } else {
            payload = {
                url: `https://www.youtube.com/watch?v=${videoId}`,
                downloadMode: "audio",
                filenameStyle: "basic",
                audioFormat: format,
                audioBitrate: bitrate
            };
        }

        GM.xmlHttpRequest({
            method: 'POST',
            url: 'https://cobalt-api.ayo.tf/',
            headers: {
                'accept': 'application/json',
                'content-type': 'application/json'
            },
            data: JSON.stringify(payload),
            responseType: 'json',
            onload: function(response) {
                try {
                    const data = JSON.parse(response.responseText);
                    if (data.url) {
                        statusElement.textContent = 'Starting audio download...';
                        triggerDirectDownload(data.url);

                        setTimeout(() => {
                            closeDialog(dialog, backdrop);
                        }, 1000);
                    } else {
                        statusElement.textContent = 'Error: No download URL found';
                        console.error('No URL in response:', data);
                    }
                } catch (error) {
                    statusElement.textContent = 'Error: API service might be temporarily unavailable';
                    console.error('Error processing response:', error);
                }
            },
            onerror: function(error) {
                statusElement.textContent = 'Network error. Please check your connection.';
                console.error('Network error:', error);
            }
        });
    }

    function updateModeSwitch(modeSwitch, isAudioMode) {
        while (modeSwitch.firstChild) {
            modeSwitch.removeChild(modeSwitch.firstChild);
        }

        const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
        svg.setAttribute('viewBox', '0 0 384 512');

        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');

        if (isAudioMode) {
            path.setAttribute('d', 'M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zm2 226.3c37.1 22.4 62 63.1 62 109.7s-24.9 87.3-62 109.7c-7.6 4.6-17.4 2.1-22-5.4s-2.1-17.4 5.4-22C269.4 401.5 288 370.9 288 336s-18.6-65.5-46.5-82.3c-7.6-4.6-10-14.4-5.4-22s14.4-10 22-5.4zm-91.9 30.9c6 2.5 9.9 8.3 9.9 14.8l0 128c0 6.5-3.9 12.3-9.9 14.8s-12.9 1.1-17.4-3.5L113.4 376 80 376c-8.8 0-16-7.2-16-16l0-48c0-8.8 7.2-16 16-16l33.4 0 35.3-35.3c4.6-4.6 11.5-5.9 17.4-3.5zm51 34.9c6.6-5.9 16.7-5.3 22.6 1.3C249.8 304.6 256 319.6 256 336s-6.2 31.4-16.3 42.7c-5.9 6.6-16 7.1-22.6 1.3s-7.1-16-1.3-22.6c5.1-5.7 8.1-13.1 8.1-21.3s-3.1-15.7-8.1-21.3c-5.9-6.6-5.3-16.7 1.3-22.6z');
        } else {
            path.setAttribute('d', 'M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM64 288c0-17.7 14.3-32 32-32l96 0c17.7 0 32 14.3 32 32l0 96c0 17.7-14.3 32-32 32l-96 0c-17.7 0-32-14.3-32-32l0-96zM300.9 397.9L256 368l0-64 44.9-29.9c2-1.3 4.4-2.1 6.8-2.1c6.8 0 12.3 5.5 12.3 12.3l0 103.4c0 6.8-5.5 12.3-12.3 12.3c-2.4 0-4.8-.7-6.8-2.1z');
        }

        svg.appendChild(path);
        modeSwitch.appendChild(svg);
    }

    function modifyQualityOptionsAndRemoveElements() {
        const { dialog, backdrop, savedCodec, savedMode, savedAudioCodec, savedDub } = createDownloadDialog();
        let currentVideoId = null;
        let selectedVideoCodec = savedCodec;
        let selectedAudioCodec = savedAudioCodec;
        let isAudioMode = savedMode === 'audio';

        try {
            const url = window.location.href;
            currentVideoId = extractVideoId(url);
        } catch (error) {
            console.error('Error extracting video ID:', error);
            return;
        }

        const modeSwitch = dialog.querySelector('#mode-switch');
        const videoOptions = dialog.querySelector('#video-options');
        const audioOptions = dialog.querySelector('#audio-options');
        const dubSelector = dialog.querySelector('.dub-selector');

        function updateModeSwitchAndOptions() {
            updateModeSwitch(modeSwitch, isAudioMode);
            if (isAudioMode) {
                audioOptions.style.display = 'block';
                videoOptions.style.display = 'none';
            } else {
                videoOptions.style.display = 'block';
                audioOptions.style.display = 'none';
            }
        }

        updateModeSwitchAndOptions();

        modeSwitch.addEventListener('click', () => {
            isAudioMode = !isAudioMode;
            updateModeSwitchAndOptions();
            localStorage.setItem('cobaltToolsMode', isAudioMode ? 'audio' : 'video');
            updateCodecButtons();
        });

        function updateCodecButtons() {
            const videoCodecButtons = videoOptions.querySelectorAll('.codec-button');
            const audioCodecButtons = audioOptions.querySelectorAll('.codec-button');

            videoCodecButtons.forEach(button => {
                button.classList.remove('selected');
                if (button.dataset.codec === selectedVideoCodec) {
                    button.classList.add('selected');
                }
            });

            audioCodecButtons.forEach(button => {
                button.classList.remove('selected');
                if (button.dataset.codec === selectedAudioCodec) {
                    button.classList.add('selected');
                }
            });

            if (isAudioMode) {
                updateAudioOptions(dialog, selectedAudioCodec, localStorage.getItem('cobaltToolsBitrate') || '320');
            } else {
                updateQualityOptions(dialog, selectedVideoCodec, localStorage.getItem('cobaltToolsQuality') || '1080p');
            }

            if (selectedVideoCodec === 'dub') {
                dubSelector.style.display = 'block';
                dialog.querySelector('#quality-options').style.display = 'none';
            } else {
                dubSelector.style.display = 'none';
                dialog.querySelector('#quality-options').style.display = 'grid';
            }
        }

        const codecButtons = dialog.querySelectorAll('.codec-button');
        codecButtons.forEach(button => {
            button.addEventListener('click', () => {
                if (isAudioMode) {
                    selectedAudioCodec = button.dataset.codec;
                    localStorage.setItem('cobaltToolsAudioCodec', selectedAudioCodec);
                } else {
                    selectedVideoCodec = button.dataset.codec;
                    localStorage.setItem('cobaltToolsCodec', selectedVideoCodec);
                }
                updateCodecButtons();
            });
        });

        updateCodecButtons();

        const dubSelect = dialog.querySelector('.dub-select');
        if (dubSelect) {
            dubSelect.value = savedDub;
            dubSelect.addEventListener('change', () => {
                localStorage.setItem('cobaltToolsDub', dubSelect.value);
            });
        }

        const cancelButton = dialog.querySelector('#cancel-button');
        const downloadButton = dialog.querySelector('#download-button');

        if (cancelButton) {
            cancelButton.addEventListener('click', () => closeDialog(dialog, backdrop));
            cancelButton.addEventListener('mouseover', () => {
                cancelButton.style.background = '#f3727f';
                cancelButton.style.borderColor = '#f3727f';
                cancelButton.style.color = '#000000';
            });
            cancelButton.addEventListener('mouseout', () => {
                cancelButton.style.background = 'transparent';
                cancelButton.style.borderColor = '#e1e1e1';
                cancelButton.style.color = '#e1e1e1';
            });
        }

        if (downloadButton) {
            downloadButton.addEventListener('click', () => {
                if (isAudioMode) {
                    const selectedFormat = selectedAudioCodec;
                    const selectedBitrate = selectedFormat === 'wav' ? 'WAV' : dialog.querySelector('input[name="bitrate"]:checked')?.value || '320';
                    if (selectedFormat && currentVideoId) {
                        downloadAudio(selectedFormat, selectedBitrate, currentVideoId, dialog, backdrop);
                    }
                } else {
                    if (selectedVideoCodec === 'dub') {
                        downloadVideo('dub', currentVideoId, 'dub', dialog, backdrop);
                    } else {
                        const selectedQuality = dialog.querySelector('input[name="quality"]:checked');
                        if (selectedQuality && currentVideoId) {
                            downloadVideo(selectedQuality.value, currentVideoId, selectedVideoCodec, dialog, backdrop);
                        }
                    }
                }
            });
            downloadButton.addEventListener('mouseover', () => {
                downloadButton.style.background = '#1ed760';
                downloadButton.style.borderColor = '#1ed760';
                downloadButton.style.color = '#000000';
            });
            downloadButton.addEventListener('mouseout', () => {
                downloadButton.style.background = 'transparent';
                downloadButton.style.borderColor = '#e1e1e1';
                downloadButton.style.color = '#e1e1e1';
            });
        }

        return dialog;
    }

    function enableDownloadButton(button) {
        button.classList.remove('yt-spec-button-shape-next--disabled');
        button.classList.add('yt-spec-button-shape-next--mono');
        button.removeAttribute('disabled');
        button.setAttribute('aria-disabled', 'false');
    }

    function findAndEnableDownloadButtons() {
        const downloadButtons = document.querySelectorAll('ytd-download-button-renderer button');
        downloadButtons.forEach(button => {
            if (button.hasAttribute('disabled') || button.getAttribute('aria-disabled') === 'true') {
                enableDownloadButton(button);
            }
        });
    }
    
    function interceptDownloadButton() {
        const targetNode = document.body;
        const config = { childList: true, subtree: true };
    
        findAndEnableDownloadButtons();
    
        const callback = function(mutationsList, observer) {
            for(let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    const addedNodes = mutation.addedNodes;
                    for(let node of addedNodes) {
                        if(node.nodeType === Node.ELEMENT_NODE) {
                            const disabledButtons = node.querySelectorAll('ytd-download-button-renderer button[disabled], ytd-download-button-renderer button[aria-disabled="true"]');
                            disabledButtons.forEach(button => {
                                enableDownloadButton(button);
                            });
    
                            const downloadDialog = node.querySelector('ytd-download-quality-selector-renderer');
                            if(downloadDialog) {
                                node.remove();
                                const customDialog = modifyQualityOptionsAndRemoveElements();
                                document.body.appendChild(customDialog);
                                return;
                            }
                        }
                    }
                }
            }
        };
    
        const observer = new MutationObserver(callback);
        observer.observe(targetNode, config);
    
        setInterval(findAndEnableDownloadButtons, 2000);
    
        document.addEventListener('click', function(event) {
            if(event.target.closest('ytd-download-button-renderer button')) {
                event.stopPropagation();
                event.preventDefault();
                const customDialog = modifyQualityOptionsAndRemoveElements();
                document.body.appendChild(customDialog);
            }
        }, true);
    }

    interceptDownloadButton();
    console.log('YouTube Direct Downloader (Cobalt) is running');
})();