yt-dlp Video Command

Add yt-dlp copy command option to download videos

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         yt-dlp Video Command
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Add yt-dlp copy command option to download videos
// @author       tris
// @match        *://*/*
// @license      MIT
// @grant        GM_registerMenuCommand
// @grant        GM_setClipboard
// ==/UserScript==

(function() {
    'use strict';

    GM_registerMenuCommand("🔥 Copy yt-dlp Command", showDownloadPopup);

    function showDownloadPopup() {
        const existingPopup = document.getElementById('ytdlp-popup');
        if (existingPopup) {
            existingPopup.remove();
        }

        const popup = document.createElement('div');
        popup.id = 'ytdlp-popup';
        popup.innerHTML = `
            <div class="ytdlp-overlay">
                <div class="ytdlp-modal">
                    <div class="ytdlp-header">
                        <div class="ytdlp-header-content">
                            <h3>Copy command</h3>
                            <p>Generate yt-dlp download commands</p>
                        </div>
                        <button class="ytdlp-close">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <line x1="18" y1="6" x2="6" y2="18"></line>
                                <line x1="6" y1="6" x2="18" y2="18"></line>
                            </svg>
                        </button>
                    </div>

                    <div class="ytdlp-content">
                        <div class="ytdlp-field">
                            <label for="video-url">Video URL</label>
                            <input type="text" id="video-url" placeholder="Paste your video URL here..." value="">
                            <div class="ytdlp-hint">Supports YouTube, Twitch, TikTok, and 1000+ sites</div>
                        </div>

                        <div class="ytdlp-field-row">
                            <div class="ytdlp-field">
                                <label for="quality-select">Quality</label>
                                <select id="quality-select">
                                    <option value="best">Best Quality</option>
                                    <option value="worst">Worst Quality</option>
                                    <option value="best[height<=720]">720p or lower</option>
                                    <option value="best[height<=480]">480p or lower</option>
                                    <option value="best[height<=360]">360p or lower</option>
                                    <option value="bestvideo+bestaudio">Best Video + Best Audio</option>
                                    <option value="bestaudio">Audio Only</option>
                                </select>
                            </div>

                            <div class="ytdlp-field">
                                <label for="output-format">Format</label>
                                <select id="output-format">
                                    <option value="mp4">MP4</option>
                                    <option value="mkv">MKV</option>
                                    <option value="webm">WebM</option>
                                    <option value="avi">AVI</option>
                                    <option value="mp3">MP3 (Audio)</option>
                                    <option value="m4a">M4A (Audio)</option>
                                </select>
                            </div>
                        </div>

                        <div class="ytdlp-field">
                            <label for="output-path">Output Directory <span class="ytdlp-optional">(optional)</span></label>
                            <input type="text" id="output-path" placeholder="e.g., ~/Downloads/Videos">
                        </div>

                        <button class="ytdlp-generate-btn" id="copy-command">
                            Generate Command
                        </button>

                        <div class="ytdlp-output" id="command-output" style="display: none;">
                            <div class="ytdlp-output-header">
                                <span>Generated Command</span>
                                <button class="ytdlp-copy-btn" id="copy-again-btn">Copy</button>
                            </div>
                            <div class="ytdlp-code-block">
                                <code id="command-text"></code>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        `;

        const style = document.createElement('style');
        style.textContent = `
            .ytdlp-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.6);
                display: flex;
                justify-content: center;
                align-items: center;
                z-index: 10001;
                animation: ytdlp-fadeIn 0.15s ease-out;
            }

            @keyframes ytdlp-fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }

            @keyframes ytdlp-slideIn {
                from {
                    opacity: 0;
                    transform: scale(0.96) translateY(8px);
                }
                to {
                    opacity: 1;
                    transform: scale(1) translateY(0);
                }
            }

            .ytdlp-modal {
                background: #1a1a1a;
                border: 1px solid #333;
                border-radius: 12px;
                width: 90%;
                max-width: 480px;
                max-height: 90vh;
                overflow: hidden;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                animation: ytdlp-slideIn 0.2s ease-out;
                color: #e5e5e5;
            }

            .ytdlp-header {
                padding: 20px 20px 16px 20px;
                border-bottom: 1px solid #333;
                display: flex;
                justify-content: space-between;
                align-items: flex-start;
            }

            .ytdlp-header-content h3 {
                margin: 0 0 4px 0;
                font-size: 18px;
                font-weight: 600;
                color: #f5f5f5;
                line-height: 1.2;
            }

            .ytdlp-header-content p {
                margin: 0;
                font-size: 14px;
                color: #a3a3a3;
                font-weight: 400;
            }

            .ytdlp-close {
                background: none;
                border: none;
                color: #a3a3a3;
                cursor: pointer;
                padding: 4px;
                border-radius: 6px;
                transition: all 0.15s ease;
                display: flex;
                align-items: center;
                justify-content: center;
            }

            .ytdlp-close:hover {
                background: #2a2a2a;
                color: #e5e5e5;
            }

            .ytdlp-content {
                padding: 20px;
            }

            .ytdlp-field {
                margin-bottom: 20px;
            }

            .ytdlp-field-row {
                display: flex;
                gap: 16px;
                margin-bottom: 20px;
            }

            .ytdlp-field-row .ytdlp-field {
                flex: 1;
                margin-bottom: 0;
            }

            .ytdlp-field label {
                display: block;
                margin-bottom: 6px;
                font-size: 14px;
                font-weight: 500;
                color: #e5e5e5;
            }

            .ytdlp-optional {
                color: #737373;
                font-weight: 400;
                font-size: 14px;
            }

            .ytdlp-field input,
            .ytdlp-field select {
                width: 100%;
                padding: 10px 12px;
                border: 1px solid #404040;
                border-radius: 8px;
                background: #262626;
                color: #e5e5e5;
                font-size: 14px;
                font-family: inherit;
                box-sizing: border-box;
                transition: border-color 0.15s ease;
            }

            .ytdlp-field input:focus,
            .ytdlp-field select:focus {
                outline: none;
                border-color: #d97706;
            }

            .ytdlp-field input::placeholder {
                color: #737373;
            }

            .ytdlp-hint {
                font-size: 13px;
                color: #737373;
                margin-top: 6px;
            }

            .ytdlp-generate-btn {
                width: 100%;
                padding: 12px;
                background: #d97706;
                color: white;
                border: none;
                border-radius: 8px;
                font-size: 14px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.15s ease;
                font-family: inherit;
                margin-bottom: 20px;
            }

            .ytdlp-generate-btn:hover {
                background: #b45309;
            }

            .ytdlp-generate-btn:active {
                transform: translateY(1px);
            }

            .ytdlp-output {
                animation: ytdlp-slideDown 0.2s ease-out;
            }

            @keyframes ytdlp-slideDown {
                from {
                    opacity: 0;
                    transform: translateY(-8px);
                }
                to {
                    opacity: 1;
                    transform: translateY(0);
                }
            }

            .ytdlp-output-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 8px;
            }

            .ytdlp-output-header span {
                font-size: 14px;
                font-weight: 500;
                color: #e5e5e5;
            }

            .ytdlp-copy-btn {
                background: #404040;
                color: #e5e5e5;
                border: none;
                padding: 6px 12px;
                border-radius: 6px;
                font-size: 13px;
                cursor: pointer;
                transition: all 0.15s ease;
                font-family: inherit;
            }

            .ytdlp-copy-btn:hover {
                background: #525252;
            }

            .ytdlp-code-block {
                background: #0f0f0f;
                border: 1px solid #333;
                border-radius: 8px;
                padding: 12px;
            }

            .ytdlp-code-block code {
                display: block;
                color: #22c55e;
                font-family: 'Courier New', monospace;
                font-size: 13px;
                line-height: 1.4;
                word-break: break-all;
                white-space: pre-wrap;
                cursor: pointer;
            }

            .ytdlp-code-block:hover {
                background: #171717;
            }

            .ytdlp-notification {
                position: fixed;
                top: 20px;
                left: 50%;
                transform: translateX(-50%);
                background: #0f0f0f;
                border: 1px solid #333;
                color: #e5e5e5;
                padding: 10px 16px;
                border-radius: 8px;
                font-family: inherit;
                font-size: 14px;
                z-index: 10002;
                animation: ytdlp-notificationSlide 0.2s ease-out;
            }

            .ytdlp-notification.error {
                background: #450a0a;
                border-color: #dc2626;
                color: #fecaca;
            }

            @keyframes ytdlp-notificationSlide {
                from {
                    opacity: 0;
                    transform: translateX(-50%) translateY(-10px);
                }
                to {
                    opacity: 1;
                    transform: translateX(-50%) translateY(0);
                }
            }

            @media (max-width: 640px) {
                .ytdlp-modal {
                    width: 95%;
                    margin: 16px;
                }

                .ytdlp-field-row {
                    flex-direction: column;
                    gap: 20px;
                }

                .ytdlp-field-row .ytdlp-field {
                    margin-bottom: 0;
                }
            }
        `;

        popup.appendChild(style);
        document.body.appendChild(popup);

        const closeBtn = popup.querySelector('.ytdlp-close');
        const overlay = popup.querySelector('.ytdlp-overlay');
        const copyBtn = popup.querySelector('#copy-command');
        const copyAgainBtn = popup.querySelector('#copy-again-btn');

        closeBtn.addEventListener('click', () => popup.remove());
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) popup.remove();
        });

        copyBtn.addEventListener('click', generateAndCopyCommand);
        copyAgainBtn.addEventListener('click', () => {
            const commandText = document.getElementById('command-text').textContent;
            if (commandText) {
                copyToClipboard(commandText);
                showNotification('Command copied to clipboard!');
            }
        });

        const commandCode = popup.querySelector('#command-text');
        commandCode.addEventListener('click', () => {
            if (commandCode.textContent) {
                copyToClipboard(commandCode.textContent);
                showNotification('Command copied to clipboard!');
            }
        });

        const urlInput = popup.querySelector('#video-url');
        urlInput.focus();
    }

    function generateAndCopyCommand() {
        const url = document.getElementById('video-url').value.trim();
        const quality = document.getElementById('quality-select').value;
        const format = document.getElementById('output-format').value;
        const outputPath = document.getElementById('output-path').value.trim();

        if (!url) {
            showNotification('Please enter a video URL', 'error');
            return;
        }

        let command = 'yt-dlp';

        if (quality !== 'best') {
            command += ` -f "${quality}"`;
        }

        if (['mp3', 'm4a'].includes(format)) {
            command += ` --extract-audio --audio-format ${format}`;
        } else if (format !== 'mp4') {
            command += ` --merge-output-format ${format}`;
        }

        if (outputPath) {
            command += ` -o "${outputPath}/%(title)s.%(ext)s"`;
        }

        command += ` "${url}"`;

        const commandOutput = document.getElementById('command-output');
        const commandText = document.getElementById('command-text');
        commandText.textContent = command;
        commandOutput.style.display = 'block';

        copyToClipboard(command);
        showNotification('yt-dlp command copied to clipboard!');
    }

    function copyToClipboard(text) {
        if (typeof GM_setClipboard !== 'undefined') {
            GM_setClipboard(text);
            return;
        }

        if (navigator.clipboard) {
            navigator.clipboard.writeText(text).catch(() => {
                fallbackCopy(text);
            });
        } else {
            fallbackCopy(text);
        }
    }

    function fallbackCopy(text) {
        const textArea = document.createElement('textarea');
        textArea.value = text;
        textArea.style.position = 'fixed';
        textArea.style.opacity = '0';
        document.body.appendChild(textArea);
        textArea.select();
        document.execCommand('copy');
        document.body.removeChild(textArea);
    }

    function showNotification(message, type = 'success') {
        const existingNotifications = document.querySelectorAll('.ytdlp-notification');
        existingNotifications.forEach(notification => notification.remove());

        const notification = document.createElement('div');
        notification.textContent = message;
        notification.className = `ytdlp-notification ${type === 'error' ? 'error' : ''}`;

        document.body.appendChild(notification);

        setTimeout(() => {
            notification.style.opacity = '0';
            notification.style.transform = 'translateX(-50%) translateY(-10px)';
            notification.style.transition = 'all 0.2s ease-out';
            setTimeout(() => notification.remove(), 200);
        }, 3000);
    }
})();