function getVideoId() { const urlParams = new URLSearchParams(window.location.search); let videoId = urlParams.get('v'); if (!videoId) { try { const playerApiElement = document.querySelector("ytd-player"); if (playerApiElement && playerApiElement.player_) { const player = playerApiElement.player_; if (player.videoData) { videoId = player.videoData.videoId || player.videoData.video_id; } } } catch (e) { console.warn("Could not get video ID from player API."); } } return videoId ? decodeURIComponent(videoId).trim() : null; }
function downloadThumbnail() { const videoId = getVideoId(); if (videoId) { const thumbnailUrl = `https://img.youtube.com/vi/${encodeURIComponent(videoId)}/maxresdefault.jpg`; window.open(thumbnailUrl, '_blank'); } else { alert('Could not get video ID.'); } }
function createThumbnailButton() { const likeDislikeContainer = document.querySelector('#top-level-buttons-computed') || document.querySelector('#top-level-buttons'); if (!likeDislikeContainer || document.getElementById('thumbnail-button')) return;
I modified it and here it is:
// ==UserScript==
// @name YouTube - View Thumbnail (Fixed)
// @namespace https://vinicius.is-a.dev/
// @version 2.7
// @author You
// @license MIT
// @description Adds a "Download Thumbnail" button next to Like/Dislike buttons, even inside playlists.
// @match *://www.youtube.com/watch*
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant GM_registerMenuCommand
// @downloadURL https://update.gf.qytechs.cn/scripts/537130/YouTube%20-%20Download%20Thumbnail.user.js
// @updateURL https://update.gf.qytechs.cn/scripts/537130/YouTube%20-%20Download%20Thumbnail.meta.js
// ==/UserScript==
(function () {
'use strict';
function getVideoId() {
const urlParams = new URLSearchParams(window.location.search);
let videoId = urlParams.get('v');
if (!videoId) {
try {
const playerApiElement = document.querySelector("ytd-player");
if (playerApiElement && playerApiElement.player_) {
const player = playerApiElement.player_;
if (player.videoData) {
videoId = player.videoData.videoId || player.videoData.video_id;
}
}
} catch (e) {
console.warn("Could not get video ID from player API.");
}
}
return videoId ? decodeURIComponent(videoId).trim() : null;
}
function downloadThumbnail() {
const videoId = getVideoId();
if (videoId) {
const thumbnailUrl = `https://img.youtube.com/vi/${encodeURIComponent(videoId)}/maxresdefault.jpg`;
window.open(thumbnailUrl, '_blank');
} else {
alert('Could not get video ID.');
}
}
function createThumbnailButton() {
const likeDislikeContainer = document.querySelector('#top-level-buttons-computed') ||
document.querySelector('#top-level-buttons');
if (!likeDislikeContainer || document.getElementById('thumbnail-button')) return;
const wrapper = document.createElement('div');
const buttonViewModel = document.createElement('div');
buttonViewModel.className = 'yt-spec-button-view-model style-scope ytd-menu-renderer';
buttonViewModel.id = 'thumbnail-button';
const button = document.createElement('button');
button.className =
'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m yt-spec-button-shape-next--enable-backdrop-filter-experiment';
button.setAttribute('aria-label', 'Thumbnail');
button.title = 'Thumbnail';
const textContent = document.createElement('div');
textContent.className = 'yt-spec-button-shape-next__button-text-content';
textContent.textContent = 'Thumbnail';
const touchFeedback = document.createElement('div');
touchFeedback.className = 'yt-spec-touch-feedback-shape yt-spec-touch-feedback-shape--touch-response';
const stroke = document.createElement('div');
stroke.className = 'yt-spec-touch-feedback-shape__stroke';
const fill = document.createElement('div');
fill.className = 'yt-spec-touch-feedback-shape__fill';
touchFeedback.appendChild(stroke);
touchFeedback.appendChild(fill);
button.appendChild(textContent);
button.appendChild(touchFeedback);
button.addEventListener('click', downloadThumbnail);
buttonViewModel.appendChild(button);
wrapper.appendChild(buttonViewModel);
wrapper.style.marginLeft = '8px';
likeDislikeContainer.appendChild(wrapper);
}
function waitForButtonsAndInject(retries = 20) {
const interval = setInterval(() => {
const likeDislikeContainer = document.querySelector('#top-level-buttons-computed') ||
document.querySelector('#top-level-buttons');
if (likeDislikeContainer && likeDislikeContainer.offsetHeight > 0) {
clearInterval(interval);
createThumbnailButton();
}
if (--retries < 0) {
clearInterval(interval);
}
}, 500);
}
function setup() {
waitForButtonsAndInject();
// Respond to YouTube internal navigation (playlist videos, SPA)
window.addEventListener('yt-navigate-finish', () => {
waitForButtonsAndInject();
});
// MutationObserver fallback
const observer = new MutationObserver(() => {
waitForButtonsAndInject();
});
observer.observe(document.body, { childList: true, subtree: true });
}
setup();
})();