Created
November 1, 2025 06:21
-
-
Save Far-Se/c2d8e825e20ded62daa38f43150dacf2 to your computer and use it in GitHub Desktop.
Youtube yt-dlp command generator.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name yt-dlp Command Generator | |
| // @namespace http://tampermonkey.net/ | |
| // @version 1.1 | |
| // @description Generate yt-dlp commands with quality selection and metadata options | |
| // @match https://www.youtube.com/* | |
| // @grant GM_registerMenuCommand | |
| // @grant GM_setClipboard | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| // Fix for TrustedHTML error | |
| if (window.trustedTypes && window.trustedTypes.createPolicy) { | |
| window.trustedTypes.createPolicy('default', { | |
| createHTML: (string) => string | |
| }); | |
| } | |
| // Register menu command | |
| GM_registerMenuCommand('Download', showDownloadPopup,"d"); | |
| function parseMusicTitle(title) { | |
| // Remove common video type suffixes | |
| const suffixes = [ | |
| /\s*\(Official\s+Video\)/gi, | |
| /\s*\(Official\s+Music\s+Video\)/gi, | |
| /\s*\(Official\s+Audio\)/gi, | |
| /\s*\[Official\s+Video\]/gi, | |
| /\s*\[Official\s+Audio\]/gi, | |
| /\s*\(Lyric\s+Video\)/gi, | |
| /\s*\[Lyric\s+Video\]/gi, | |
| /\s*\(Audio\)/gi, | |
| /\s*\[Audio\]/gi, | |
| /\s*\(Visualizer\)/gi, | |
| /\s*\[Visualizer\]/gi, | |
| /\s*\(Music\s+Video\)/gi, | |
| /\s*\[Music\s+Video\]/gi, | |
| /\s*\(Official\)/gi, | |
| /\s*\[Official\]/gi | |
| ]; | |
| let cleanTitle = title; | |
| suffixes.forEach(suffix => { | |
| cleanTitle = cleanTitle.replace(suffix, ''); | |
| }); | |
| // Check for delimiters: —, -, // | |
| const delimiters = ['—', ' - ', ' // ', '//']; | |
| for (let delimiter of delimiters) { | |
| if (cleanTitle.includes(delimiter)) { | |
| const parts = cleanTitle.split(delimiter); | |
| if (parts.length >= 2) { | |
| // Take the second part (song name) and trim it | |
| let songName = parts[1].trim(); | |
| // Remove any remaining suffixes from the song name | |
| suffixes.forEach(suffix => { | |
| songName = songName.replace(suffix, ''); | |
| }); | |
| return songName.trim(); | |
| } | |
| } | |
| } | |
| // If no delimiter found, return cleaned title | |
| return cleanTitle.trim(); | |
| } | |
| function showDownloadPopup() { | |
| if(!window.location.pathname.includes('watch'))return; | |
| // Get video data from YouTube's player response | |
| const videoData = window.ytInitialPlayerResponse?.videoDetails; | |
| if (!videoData) { | |
| alert('Could not retrieve video information.\n Refresh the page first!'); | |
| return; | |
| } | |
| const videoId = videoData.videoId; | |
| const title = videoData.title; | |
| const author = videoData.author; | |
| const duration = formatDuration(videoData.lengthSeconds); | |
| const thumbnail = `https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`; | |
| // Parse music title to extract song name | |
| const songTitle = parseMusicTitle(title); | |
| // Create popup overlay | |
| const overlay = document.createElement('div'); | |
| overlay.id = 'ytdlp-popup-overlay'; | |
| overlay.innerHTML = ` | |
| <div id="ytdlp-popup"> | |
| <div id="ytdlp-header"> | |
| <h2>Download Video</h2> | |
| <button id="ytdlp-close">×</button> | |
| </div> | |
| <div id="ytdlp-video-info"> | |
| <img src="${thumbnail}" alt="Thumbnail" id="ytdlp-thumbnail"> | |
| <div id="ytdlp-details"> | |
| <div id="ytdlp-title">${escapeHtml(title)}</div> | |
| <div id="ytdlp-author">${escapeHtml(author)}</div> | |
| <div id="ytdlp-duration">Duration: ${duration}</div> | |
| </div> | |
| </div> | |
| <div id="ytdlp-metadata-section"> | |
| <h3>Metadata Options (for MP3/MP4)</h3> | |
| <div class="ytdlp-metadata-inputs"> | |
| <div class="ytdlp-input-group"> | |
| <label>Artist:</label> | |
| <input type="text" id="ytdlp-artist" placeholder="Artist name" value="${escapeHtml(author)}"> | |
| </div> | |
| <div class="ytdlp-input-group"> | |
| <label>Title:</label> | |
| <input type="text" id="ytdlp-song-title" placeholder="Song title" value="${escapeHtml(songTitle)}"> | |
| </div> | |
| <div class="ytdlp-checkbox-group"> | |
| <input type="checkbox" id="ytdlp-embed-thumbnail" checked> | |
| <label for="ytdlp-embed-thumbnail">Embed Thumbnail</label> | |
| </div> | |
| <div class="ytdlp-checkbox-group"> | |
| <input type="checkbox" id="ytdlp-embed-metadata" checked> | |
| <label for="ytdlp-embed-metadata">Embed Metadata</label> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="ytdlp-tables-container"> | |
| <div id="ytdlp-quality-section"> | |
| <h3>Video Quality Options</h3> | |
| <table id="ytdlp-quality-table"> | |
| <thead> | |
| <tr> | |
| <th>Quality</th> | |
| <th>Format</th> | |
| <th>Action</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| ${generateQualityRows(videoId)} | |
| </tbody> | |
| </table> | |
| </div> | |
| <div id="ytdlp-audio-section"> | |
| <h3>Audio Only Options</h3> | |
| <table id="ytdlp-audio-table"> | |
| <thead> | |
| <tr> | |
| <th>Format</th> | |
| <th>Quality</th> | |
| <th>Action</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| ${generateAudioRows(videoId)} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div id="ytdlp-metadata-section"> | |
| <h3>Copy yt-dlp command then open a command prompt and paste it</h3> | |
| </div> | |
| </div> | |
| `; | |
| document.body.appendChild(overlay); | |
| addStyles(); | |
| attachEventListeners(overlay, videoId); | |
| } | |
| function generateQualityRows(videoId) { | |
| const qualities = [ | |
| { label: 'Best Available', format: 'bestvideo+bestaudio/best', desc: 'MP4' }, | |
| { label: '2160p (4K)', format: 'bestvideo[height<=2160]+bestaudio/best[height<=2160]', desc: 'MP4' }, | |
| { label: '1440p (2K)', format: 'bestvideo[height<=1440]+bestaudio/best[height<=1440]', desc: 'MP4' }, | |
| { label: '1080p (FHD)', format: 'bestvideo[height<=1080]+bestaudio/best[height<=1080]', desc: 'MP4' }, | |
| { label: '720p (HD)', format: 'bestvideo[height<=720]+bestaudio/best[height<=720]', desc: 'MP4' }, | |
| { label: '480p', format: 'bestvideo[height<=480]+bestaudio/best[height<=480]', desc: 'MP4' }, | |
| { label: '360p', format: 'bestvideo[height<=360]+bestaudio/best[height<=360]', desc: 'MP4' }, | |
| { label: 'Video Only (Best)', format: 'bestvideo', desc: 'No Audio' } | |
| ]; | |
| return qualities.map(q => ` | |
| <tr> | |
| <td>${q.label}</td> | |
| <td>${q.desc}</td> | |
| <td> | |
| <button class="ytdlp-copy-btn" data-format="${q.format}" data-type="video"> | |
| Copy | |
| </button> | |
| </td> | |
| </tr> | |
| `).join(''); | |
| } | |
| function generateAudioRows(videoId) { | |
| const audioFormats = [ | |
| { label: 'M4A (Best Quality)', format: 'bestaudio', ext: 'm4a', quality: '0' }, | |
| { label: 'OPUS (Best Quality)', format: 'bestaudio[ext=opus]', ext: 'opus', quality: '0' }, | |
| { label: 'Audio Only (Best)', format: 'bestaudio', ext: 'best', quality: '0' }, | |
| { label: 'MP3 (320kbps)', format: 'bestaudio', ext: 'mp3', quality: '320K' }, | |
| { label: 'MP3 (256kbps)', format: 'bestaudio', ext: 'mp3', quality: '256K' }, | |
| { label: 'MP3 (192kbps)', format: 'bestaudio', ext: 'mp3', quality: '192K' }, | |
| ]; | |
| return audioFormats.map(a => ` | |
| <tr> | |
| <td>${a.label}</td> | |
| <td>${a.ext.toUpperCase()}</td> | |
| <td> | |
| <button class="ytdlp-copy-btn" data-format="${a.format}" data-type="audio" data-ext="${a.ext}" data-quality="${a.quality}"> | |
| Copy | |
| </button> | |
| </td> | |
| </tr> | |
| `).join(''); | |
| } | |
| function attachEventListeners(overlay, videoId) { | |
| // Close button | |
| overlay.querySelector('#ytdlp-close').addEventListener('click', () => { | |
| overlay.remove(); | |
| }); | |
| // Click outside to close | |
| overlay.addEventListener('click', (e) => { | |
| if (e.target.id === 'ytdlp-popup-overlay') { | |
| overlay.remove(); | |
| } | |
| }); | |
| // Copy buttons | |
| overlay.querySelectorAll('.ytdlp-copy-btn').forEach(btn => { | |
| btn.addEventListener('click', function() { | |
| const format = this.dataset.format; | |
| const type = this.dataset.type; | |
| const videoUrl = window.location.href; | |
| let command = 'yt-dlp '; | |
| // Add metadata options | |
| const embedThumbnail = document.getElementById('ytdlp-embed-thumbnail').checked; | |
| const embedMetadata = document.getElementById('ytdlp-embed-metadata').checked; | |
| const artist = document.getElementById('ytdlp-artist').value; | |
| const songTitle = document.getElementById('ytdlp-song-title').value; | |
| if (type === 'audio') { | |
| const ext = this.dataset.ext; | |
| const quality = this.dataset.quality; | |
| command += '-f ' + format + ' '; | |
| if (ext !== 'best') { | |
| command += '-x --audio-format ' + ext + ' '; | |
| if (quality !== '0') { | |
| command += '--audio-quality ' + quality + ' '; | |
| } | |
| } | |
| if (embedThumbnail) { | |
| command += '--embed-thumbnail '; | |
| } | |
| if (embedMetadata) { | |
| command += '--embed-metadata '; | |
| if (artist) { | |
| command += `--parse-metadata "title:%(artist)s" --parse-metadata "${artist}:%(artist)s" `; | |
| } | |
| if (songTitle) { | |
| command += `--parse-metadata "${songTitle}:%(title)s" `; | |
| } | |
| } | |
| command += '--add-metadata '; | |
| } else { | |
| command += '-f "' + format + '" '; | |
| if (embedMetadata && format.includes('bestvideo+bestaudio')) { | |
| command += '--embed-metadata '; | |
| if (artist) { | |
| command += `--parse-metadata "${artist}:%(artist)s" `; | |
| } | |
| if (songTitle) { | |
| command += `--parse-metadata "${songTitle}:%(title)s" `; | |
| } | |
| } | |
| command += '--merge-output-format mp4 '; | |
| } | |
| command += '"' + videoUrl + '" -o "%(title)s.%(ext)s"'; | |
| // Copy to clipboard | |
| GM_setClipboard(command); | |
| // Show feedback | |
| const originalText = this.textContent; | |
| this.textContent = 'Copied!'; | |
| this.style.backgroundColor = '#065fd4'; | |
| setTimeout(() => { | |
| this.textContent = originalText; | |
| this.style.backgroundColor = ''; | |
| }, 2000); | |
| }); | |
| }); | |
| } | |
| function formatDuration(seconds) { | |
| const hours = Math.floor(seconds / 3600); | |
| const minutes = Math.floor((seconds % 3600) / 60); | |
| const secs = seconds % 60; | |
| if (hours > 0) { | |
| return `${hours}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; | |
| } | |
| return `${minutes}:${String(secs).padStart(2, '0')}`; | |
| } | |
| function escapeHtml(text) { | |
| const div = document.createElement('div'); | |
| div.textContent = text; | |
| return div.innerHTML; | |
| } | |
| function addStyles() { | |
| if (document.getElementById('ytdlp-styles')) return; | |
| const style = document.createElement('style'); | |
| style.id = 'ytdlp-styles'; | |
| style.textContent = ` | |
| #ytdlp-popup-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.8); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 10000; | |
| font-family: "YouTube Sans", "Roboto", Arial, sans-serif; | |
| } | |
| #ytdlp-popup { | |
| background: #212121; | |
| border-radius: 12px; | |
| width: 90%; | |
| max-width: 1200px; | |
| max-height: 90vh; | |
| overflow-y: auto; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); | |
| color: #fff; | |
| } | |
| #ytdlp-popup::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| #ytdlp-popup::-webkit-scrollbar-track { | |
| background: #181818; | |
| } | |
| #ytdlp-popup::-webkit-scrollbar-thumb { | |
| background: #404040; | |
| border-radius: 4px; | |
| } | |
| #ytdlp-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 16px 24px; | |
| border-bottom: 1px solid #303030; | |
| } | |
| #ytdlp-header h2 { | |
| margin: 0; | |
| font-size: 20px; | |
| font-weight: 500; | |
| } | |
| #ytdlp-close { | |
| background: none; | |
| border: none; | |
| color: #fff; | |
| font-size: 32px; | |
| cursor: pointer; | |
| padding: 0; | |
| width: 40px; | |
| height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| border-radius: 50%; | |
| transition: background 0.2s; | |
| } | |
| #ytdlp-close:hover { | |
| background: #3f3f3f; | |
| } | |
| #ytdlp-video-info { | |
| display: flex; | |
| gap: 16px; | |
| padding: 24px; | |
| background: #181818; | |
| border-bottom: 1px solid #303030; | |
| } | |
| #ytdlp-thumbnail { | |
| width: 240px; | |
| height: 135px; | |
| object-fit: cover; | |
| border-radius: 8px; | |
| flex-shrink: 0; | |
| } | |
| #ytdlp-details { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| #ytdlp-title { | |
| font-size: 16px; | |
| font-weight: 500; | |
| line-height: 1.4; | |
| } | |
| #ytdlp-author { | |
| color: #aaa; | |
| font-size: 14px; | |
| } | |
| #ytdlp-duration { | |
| color: #aaa; | |
| font-size: 14px; | |
| } | |
| #ytdlp-metadata-section { | |
| padding: 24px; | |
| border-bottom: 1px solid #303030; | |
| } | |
| #ytdlp-metadata-section h3 { | |
| margin: 0 0 16px 0; | |
| font-size: 16px; | |
| font-weight: 500; | |
| } | |
| .ytdlp-metadata-inputs { | |
| display: grid; | |
| gap: 16px; | |
| } | |
| .ytdlp-input-group { | |
| display: grid; | |
| gap: 8px; | |
| } | |
| .ytdlp-input-group label { | |
| font-size: 14px; | |
| color: #aaa; | |
| } | |
| .ytdlp-input-group input[type="text"] { | |
| background: #181818; | |
| border: 1px solid #303030; | |
| color: #fff; | |
| padding: 10px 12px; | |
| border-radius: 4px; | |
| font-size: 14px; | |
| font-family: inherit; | |
| } | |
| .ytdlp-input-group input[type="text"]:focus { | |
| outline: none; | |
| border-color: #3ea6ff; | |
| } | |
| .ytdlp-checkbox-group { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .ytdlp-checkbox-group input[type="checkbox"] { | |
| width: 18px; | |
| height: 18px; | |
| cursor: pointer; | |
| } | |
| .ytdlp-checkbox-group label { | |
| font-size: 14px; | |
| cursor: pointer; | |
| } | |
| #ytdlp-tables-container { | |
| display: flex; | |
| gap: 24px; | |
| padding: 24px; | |
| flex-wrap: wrap; | |
| } | |
| #ytdlp-quality-section, | |
| #ytdlp-audio-section { | |
| flex: 1; | |
| min-width: 400px; | |
| } | |
| #ytdlp-quality-section h3, | |
| #ytdlp-audio-section h3 { | |
| margin: 0 0 16px 0; | |
| font-size: 16px; | |
| font-weight: 500; | |
| } | |
| #ytdlp-quality-table, | |
| #ytdlp-audio-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| } | |
| #ytdlp-quality-table th, | |
| #ytdlp-audio-table th { | |
| text-align: left; | |
| padding: 12px; | |
| background: #181818; | |
| font-weight: 500; | |
| font-size: 14px; | |
| border-bottom: 1px solid #303030; | |
| } | |
| #ytdlp-quality-table td, | |
| #ytdlp-audio-table td { | |
| padding: 12px; | |
| border-bottom: 1px solid #303030; | |
| font-size: 14px; | |
| } | |
| #ytdlp-quality-table tr:hover, | |
| #ytdlp-audio-table tr:hover { | |
| background: #282828; | |
| } | |
| .ytdlp-copy-btn { | |
| background: #3f3f3f; | |
| color: #fff; | |
| border: none; | |
| padding: 8px 16px; | |
| border-radius: 18px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: 500; | |
| transition: background 0.2s; | |
| white-space: nowrap; | |
| } | |
| .ytdlp-copy-btn:hover { | |
| background: #4f4f4f; | |
| } | |
| .ytdlp-copy-btn:active { | |
| background: #065fd4; | |
| } | |
| @media (max-width: 1024px) { | |
| #ytdlp-tables-container { | |
| flex-direction: column; | |
| } | |
| #ytdlp-quality-section, | |
| #ytdlp-audio-section { | |
| min-width: 100%; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| #ytdlp-video-info { | |
| flex-direction: column; | |
| } | |
| #ytdlp-thumbnail { | |
| width: 100%; | |
| height: auto; | |
| } | |
| #ytdlp-popup { | |
| width: 95%; | |
| } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment