YouTube Direct Downloader (Cobalt)

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

Pada tanggal 12 April 2025. Lihat %(latest_version_link).

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         YouTube Direct Downloader (Cobalt)
// @description  Add a custom 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.4
// @author       afkarxyz
// @namespace    https://github.com/afkarxyz/userscripts/
// @supportURL   https://github.com/afkarxyz/userscripts/issues
// @license      MIT
// @match        https://www.youtube.com/*
// @match        https://youtube.com/*
// @grant        GM.xmlHttpRequest
// @connect      c.blahaj.ca
// @connect      dwnld.nichind.dev
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    const PRIMARY_API_URL = 'https://c.blahaj.ca/';
    const FALLBACK_API_URL = 'https://dwnld.nichind.dev/';
    let currentApiUrl = PRIMARY_API_URL;

    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"
    };

    const style = document.createElement('style');
    style.textContent = `
        .cobalt-download-btn {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            margin-left: 8px;
            transition: background-color 0.2s;
        }
        html[dark] .cobalt-download-btn {
            background-color: #ffffff1a;
        }
        html:not([dark]) .cobalt-download-btn {
            background-color: #0000000d;
        }
        html[dark] .cobalt-download-btn:hover {
            background-color: #ffffff33;
        }
        html:not([dark]) .cobalt-download-btn:hover {
            background-color: #00000014;
        }
        .cobalt-download-btn svg {
            width: 18px;
            height: 18px;
        }
        html[dark] .cobalt-download-btn svg {
            fill: var(--yt-spec-text-primary, #fff);
        }
        html:not([dark]) .cobalt-download-btn svg {
            fill: var(--yt-spec-text-primary, #030303);
        }
    `;
    document.head.appendChild(style);

    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://greatest.deepsurf.us/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, apiType) {
            const statusSpan = document.getElementById('api-status');
            if (statusSpan) {
                statusSpan.textContent = isOnline ? apiType : '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) {
        checkSingleApiStatus(PRIMARY_API_URL, function(isOnline) {
            if (isOnline) {
                currentApiUrl = PRIMARY_API_URL;
                callback(true, 'Primary');
            } else {
                checkSingleApiStatus(FALLBACK_API_URL, function(fallbackIsOnline) {
                    if (fallbackIsOnline) {
                        currentApiUrl = FALLBACK_API_URL;
                        callback(true, 'Fallback');
                    } else {
                        currentApiUrl = PRIMARY_API_URL;
                        callback(false, 'Offline');
                    }
                });
            }
        });
    }

    function checkSingleApiStatus(apiUrl, callback) {
        GM.xmlHttpRequest({
            method: 'GET',
            url: apiUrl,
            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'
        };
    
        function attemptDownload(apiUrl, isRetry = false) {
            statusElement.textContent = isRetry ? 'Trying fallback API...' : 'Preparing download...';
            
            GM.xmlHttpRequest({
                method: 'POST',
                url: apiUrl,
                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 {
                            if (!isRetry && apiUrl === PRIMARY_API_URL) {
                                attemptDownload(FALLBACK_API_URL, true);
                            } else {
                                statusElement.textContent = 'Error: No download URL found';
                                console.error('No URL in response:', data);
                            }
                        }
                    } catch (error) {
                        if (!isRetry && apiUrl === PRIMARY_API_URL) {
                            attemptDownload(FALLBACK_API_URL, true);
                        } else {
                            statusElement.textContent = 'Error: API service might be temporarily unavailable';
                            console.error('Error processing response:', error);
                        }
                    }
                },
                onerror: function(error) {
                    if (!isRetry && apiUrl === PRIMARY_API_URL) {
                        attemptDownload(FALLBACK_API_URL, true);
                    } else {
                        statusElement.textContent = 'Network error. Please check your connection.';
                        console.error('Network error:', error);
                    }
                }
            });
        }
    
        attemptDownload(currentApiUrl);
    }

    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
            };
        }
    
        function attemptDownload(apiUrl, isRetry = false) {
            statusElement.textContent = isRetry ? 'Trying fallback API...' : 'Preparing audio download...';
            
            GM.xmlHttpRequest({
                method: 'POST',
                url: apiUrl,
                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 {
                            if (!isRetry && apiUrl === PRIMARY_API_URL) {
                                attemptDownload(FALLBACK_API_URL, true);
                            } else {
                                statusElement.textContent = 'Error: No download URL found';
                                console.error('No URL in response:', data);
                            }
                        }
                    } catch (error) {
                        if (!isRetry && apiUrl === PRIMARY_API_URL) {
                            attemptDownload(FALLBACK_API_URL, true);
                        } else {
                            statusElement.textContent = 'Error: API service might be temporarily unavailable';
                            console.error('Error processing response:', error);
                        }
                    }
                },
                onerror: function(error) {
                    if (!isRetry && apiUrl === PRIMARY_API_URL) {
                        attemptDownload(FALLBACK_API_URL, true);
                    } else {
                        statusElement.textContent = 'Network error. Please check your connection.';
                        console.error('Network error:', error);
                    }
                }
            });
        }
    
        attemptDownload(currentApiUrl);
    }

    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 createDownloadButton() {
        const downloadButton = document.createElement('div');
        downloadButton.className = 'cobalt-download-btn';
        
        const svgNS = "http://www.w3.org/2000/svg";
        const svg = document.createElementNS(svgNS, "svg");
        svg.setAttribute("viewBox", "0 0 512 512");
        
        const path = document.createElementNS(svgNS, "path");
        path.setAttribute("d", "M256 464c114.9 0 208-93.1 208-208c0-13.3 10.7-24 24-24s24 10.7 24 24c0 141.4-114.6 256-256 256S0 397.4 0 256c0-13.3 10.7-24 24-24s24 10.7 24 24c0 114.9 93.1 208 208 208zM377.6 232.3l-104 112c-4.5 4.9-10.9 7.7-17.6 7.7s-13-2.8-17.6-7.7l-104-112c-9-9.7-8.5-24.9 1.3-33.9s24.9-8.5 33.9 1.3L232 266.9 232 24c0-13.3 10.7-24 24-24s24 10.7 24 24l0 242.9 62.4-67.2c9-9.7 24.2-10.3 33.9-1.3s10.3 24.2 1.3 33.9z");
        
        svg.appendChild(path);
        downloadButton.appendChild(svg);
        
        downloadButton.addEventListener('click', function() {
            const customDialog = modifyQualityOptionsAndRemoveElements();
            document.body.appendChild(customDialog);
        });
        
        return downloadButton;
    }
    
    function insertDownloadButton() {
        const targetSelector = '#owner';
        const target = document.querySelector(targetSelector);
        
        if (target && !document.querySelector('.cobalt-download-btn')) {
            const downloadButton = createDownloadButton();
            target.appendChild(downloadButton);
        }
    }
    
    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;
    }
    
    const observer = new MutationObserver(() => {
        if (window.location.pathname.includes('/watch')) {
            insertDownloadButton();
        }
    });
    
    observer.observe(document.body, { childList: true, subtree: true });
    
    insertDownloadButton();
    
    window.addEventListener('yt-navigate-finish', insertDownloadButton);
})();