返回首頁 

Greasy Fork is available in English.

Restore animated thumbnail previews - youtube.com

To restore animated thumbnail previews, requires inline previews to be disabled in your YouTube settings. Note: not Greasemonkey compatible. v2.0 adds fallback option of a still image carousel for the homepage where YouTube has changed their data structure to no longer provide an_webp animated thumbnail urls, which is affecting some users; for such users non-homepage pages are unaffected and continue to use the an_webp animated thumbs.


安装此脚本?
// ==UserScript==// @name        Restore animated thumbnail previews - youtube.com// @namespace   Violentmonkey Scripts seekhare// @match       http*://www.youtube.com/*// @run-at      document-start// @grant       GM_addStyle// @version     2.0// @license     MIT// @author      seekhare// @description To restore animated thumbnail previews, requires inline previews to be disabled in your YouTube settings. Note: not Greasemonkey compatible. v2.0 adds fallback option of a still image carousel for the homepage where YouTube has changed their data structure to no longer provide an_webp animated thumbnail urls, which is affecting some users; for such users non-homepage pages are unaffected and continue to use the an_webp animated thumbs.// ==/UserScript==Object.defineProperties(Object.prototype,{isPreviewDisabled:{get:function(){return false}, set:function(){}}}); // old way preferred and none-the-less still valid for non-homepage pages for affected users.fadeInCSS = `img.animatedThumbTarget { animation: fadeIn 0.5s; object-fit: cover;}@keyframes fadeIn {0% { opacity: 0; }100% { opacity: 1; }}`;GM_addStyle(fadeInCSS);const forceDisableNewHomepageMethod = false; // force disable the new homepage method.const forceEnableNewHomepageMethod = false; // force disable takes priority over force enable.const logHeader = 'UserScript Restore YT Animated Thumbs:';const homeUrl = 'https://www.youtube.com/';const ytImageBaseUrl = 'https://i.ytimg.com/vi/';const ytImageNames = ['hq1.jpg', 'hq2.jpg', 'hq3.jpg']; // e.g. https://i.ytimg.com/vi/UujGYE5mOnI/0.jpgconst carouselDelay = 850; //milliseconds, how long to display each image.function animatedThumbsEventEnter(event) {//console.debug(logHeader, 'enter', event);var target = event.target;//console.debug(logHeader, 'target', target);if (target.querySelector('div[aria-label="LIVE"]') != null) { // don't apply to video tiles that are live, can't do in observer as child element not present then.target.removeEventListener('mouseenter', animatedThumbsEventEnter);target.removeEventListener('mouseleave', animatedThumbsEventLeave);return}var atag = target.querySelector('a#thumbnail');//console.debug(logHeader, 'atag', atag);if (atag.hasAttribute('videoId') === false) {//extract videoId from href and store on an attributevar videoId = atag.getAttribute('href').match(/watch\?v=([^&]*)/)[1]; //the href is like "/watch?v=IDabc123&t=123" so regex.//console.debug(logHeader, 'videoId', videoId);atag.setAttribute('videoId', videoId);}var animatedImgNode = document.createElement("img");animatedImgNode.setAttribute('videoId', atag.getAttribute('videoId'));animatedImgNode.setAttribute("carouselIndex", 0);animatedImgNode.setAttribute("id", "thumbnail");animatedImgNode.setAttribute("class", "style-scope ytd-moving-thumbnail-renderer fade-in animatedThumbTarget"); //animatedThumbTarget is custom class, others are YoutubeupdateCarousel(animatedImgNode);var overlaytag = target.querySelector('div#mouseover-overlay');overlaytag.appendChild(animatedImgNode);animatedImgNode.timer = setInterval(updateCarousel, carouselDelay, animatedImgNode);return}function animatedThumbsEventLeave(event) {//console.debug(logHeader, 'leave', event);try {var animatedImgNode = event.target.querySelector('img.animatedThumbTarget');clearTimeout(animatedImgNode.timer);animatedImgNode.remove();} catch {}return}function updateCarousel(animatedImgNode) {var index = parseInt(animatedImgNode.getAttribute("carouselIndex"));//console.debug(logHeader, 'index', index);var imgURL = ytImageBaseUrl + animatedImgNode.getAttribute('videoId') + '/' + ytImageNames[index];animatedImgNode.setAttribute("src", imgURL);var nextIndex = (index+1) % ytImageNames.length;animatedImgNode.setAttribute("carouselIndex", nextIndex);}function useNewSearchMethod() {if (forceDisableNewHomepageMethod) {return false} else if (forceEnableNewHomepageMethod) {return true}if (window.location.pathname  === '/') {console.debug(logHeader, 'Pathname check method');if (document.head.innerHTML.indexOf('an_webp') != -1 || document.body.innerHTML.indexOf('an_webp') != -1) {return false}else {return true}} else {// if not entered youtube via homepage then do a request here to determine if user's homepage is affected by YouTube's changes removing animated thumbs.console.debug(logHeader, 'XMLHttpRequest check method');const request = new XMLHttpRequest();request.open("GET", homeUrl, false); // `false` makes the request synchronousrequest.send(null);if (request.status === 200) {//console.debug('response', request.responseText);var trimmedResponseIndex = request.responseText.indexOf('an_webp/');if (trimmedResponseIndex != -1) {return false}else {return true}} else {console.error(logHeader, 'Could not GET "'+homeUrl+'". Response Status = '+request.status, request.statusText);return true}}}function runPageCheckForExistingElements() {//Can run this just incase some elements were already created before observer set up.var list = document.getElementsByTagName("ytd-rich-item-renderer");for (var element of list) {//console.debug(logHeader, element);element.addEventListener('mouseenter', animatedThumbsEventEnter);element.addEventListener('mouseleave', animatedThumbsEventLeave);}}function setupMutationObserverSingle() {if (useNewSearchMethod() === false) {return console.log(logHeader, "Using old method only (preferred), disabling new method.")}console.log(logHeader, "Enabling new image method for homepage.")const targetNode = document;//console.debug('targetNodeInit',targetNode);const config = {attributes: false, childList: true, subtree: true};const callback = (mutationList, observer) => {for (const mutation of mutationList) {//console.debug(logHeader, "Mutation", mutation);for (const element of mutation.addedNodes) {if (element.nodeName === 'YTD-RICH-ITEM-RENDERER') {//console.debug(logHeader, "Adding event listeners to element", element);element.addEventListener('mouseenter', animatedThumbsEventEnter);element.addEventListener('mouseleave', animatedThumbsEventLeave);}}}}const observer = new MutationObserver(callback);observer.observe(targetNode, config);runPageCheckForExistingElements();}document.addEventListener("DOMContentLoaded", function(){setupMutationObserverSingle()});