Greasy Fork 支持简体中文。

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.

< 腳本Restore animated thumbnail previews - youtube.com的回應

提問/評論

§
發表於:2024-12-06

As per Seekhare's post - was working fine for me until yesterday (Dec 5th) but no longer working as of today so I guess I'm one of the accounts they are trialling their changes on. Like others have stated, this is only for the homepage, still works on search results pages.

§
發表於:2025-02-06

Has suddenly stopped working for me again (yesterday, 5th Feb 2025). As before, only the homepage affected, still working on search results pages.

seekhare作者
§
發表於:2025-02-08

Curious you've had a reoccurance as so far I'd only been aware of it stopping once per account. In the past it's resolved to start working again after waiting 1 or 2 days. Has it started working again for you or has it remained?

§
發表於:2025-02-09

Curious you've had a reoccurance as so far I'd only been aware of it stopping once per account. In the past it's resolved to start working again after waiting 1 or 2 days. Has it started working again for you or has it remained?

Just checked it again, but unfortunately still not working (as before - works on the thumbnails on search results pages). When it first stopped working for me back in December, as per your previous message it suddenly started working again within a couple of days. So far this time it's been 4 days (at time of posting this) since it stopped. Can't get my head around why YouTube would disable this feature??

seekhare作者
§
發表於:2025-02-11

Previously I came up with this below code when I was looking for an alternative to fix the homepage animated thumbnails not working. I post it here as an option but I advise against using it if you care about your Youtube search history as this will pollute it massively. Basically it works by doing a youtube search for any video ID that you hover over on the homepage and since the animated thumbnails URL are still currently available on the search it can extract the thumb URL from that; which means it does a search request every time you hover on a tile on the homepage. For other pages it should use the old method which is still working on the non-homepage pages.

But I repeat, unless you are willing to disable your Youtube search history this is going to mess it up because of all the searches it needs to do to work so don't use it unless you are happy with that.

Because I use the search history, and I expect most people do, once I realised this limitation I decided to abandon this alternative idea and wasn't going to publish it and so the code was not polished but it "works" so to speak and since your homepage has not reverted since 4 days I'll leave it here (as-is without warranty or liability, maybe these searches will affect the algorithm suggestions or something who knows) for you to decide if you want to use it and give up your youtube search history.


// ==UserScript==
// @name Restore animated thumbnail previews - youtube.com - (Alternative via search)
// @namespace Violentmonkey Scripts seekhare
// @match http*://www.youtube.com/*
// @run-at document-start
// @grant GM_addStyle
// @version 2.0alpha
// @license MIT
// @author seekhare
// @description To restore animated thumbnail previews, requires inline previews to be disabled in your Youtube settings. Note: not Greasemonkey compatible. WARNING: Problem with searching by id is it adds an entry to your YT search history, for each tile you hover over. This your search history will become polluted. Basically this is only usable if you don't care about search history and disable your search history in your youtube account settings.
// ==/UserScript==

// WARNING: Problem with searching by id is it adds an entry to your YT search history, for each tile you hover over. This your search history will become polluted. Basically this is only usable if you don't care about search history and disable your search history in your youtube account settings.

Object.defineProperties(Object.prototype,{isPreviewDisabled:{get:function(){return false}, set:function(){}}}); // old way still valid for non-homepage pages

fadeInCSS = `img.animatedThumbTarget { animation: fadeIn 0.5s; }
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
`;
GM_addStyle(fadeInCSS);

const forceDisableNewSearchMethod = false; // force disable takes priority over force enable.
const forceEnableNewSearchMethod = true;
const logHeader = 'UserScript Restore YT Animated Thumbs:';
const searchUrl = 'https://www.youtube.com/results?search_query='; // use as in "https://www.youtube.com/results?search_query=IDabc123"
const homeUrl = 'https://www.youtube.com/';
const ytImageRootUrl = 'https://i.ytimg.com/';

function animatedThumbsEventEnter(event) {
//console.debug(logHeader, 'enter', event);
var target = event.target;
//console.debug(logHeader, 'target', target);
var atag = target.querySelector('a#thumbnail');
//console.debug(logHeader, 'atag', atag);
if (atag.hasAttribute('srcAnimated') === false) {
//do search url request to get animated thumb URL and store on img attribute "srcAnimated"
var videoId = atag.getAttribute('href').match(/watch\?v=([^&]*)/)[1]; //the href is like "/watch?v=IDabc123&t=123" so regex.
//console.debug(logHeader, 'videoId', videoId);
var thumbUrl = getAnimatedThumbURL(videoId);
if (thumbUrl != null) {
atag.setAttribute('srcAnimated', thumbUrl);
} else {
//if no animated thumb available just use static image.
var imgtag = target.querySelector('img.yt-core-image');
//console.debug(logHeader, 'imgtag', imgtag);
atag.setAttribute('srcAnimated', imgtag.getAttribute('src'));
}
}
var animatedImgNode = document.createElement("img");
animatedImgNode.setAttribute("src", atag.getAttribute('srcAnimated'));
animatedImgNode.setAttribute("id", "thumbnail");
animatedImgNode.setAttribute("class", "style-scope ytd-moving-thumbnail-renderer fade-in animatedThumbTarget"); //animatedThumbTarget is custom class, others are Youtube
var overlaytag = target.querySelector('div#mouseover-overlay');
overlaytag.appendChild(animatedImgNode);
return
}

function animatedThumbsEventLeave(event) {
//console.debug(logHeader, 'leave', event);
try {
var animatedNode = event.target.querySelector('img.animatedThumbTarget');
animatedNode.remove();
} catch {}
return
}

function getAnimatedThumbURL(videoId) {
const request = new XMLHttpRequest();
request.open("GET", searchUrl+videoId, false); // `false` makes the request synchronous
request.send(null);
if (request.status === 200) {
//console.debug('response', request.responseText);
// response is very large and desired data is near the end, need to see how else can improve regex speed...
//var trimmedResponseIndex = 500_000;
var trimmedResponseIndex = request.responseText.indexOf('an_webp/');
var trimmedResponse = request.responseText.substring(trimmedResponseIndex)
var regexSearch = new RegExp(`(an_webp\/${videoId}[^"]*)`); // /(an_webp\/IDabc123[^"]*)/
//console.debug(logHeader, 'regexSearch',regexSearch);
var regexResult = trimmedResponse.match(regexSearch);
//console.debug(logHeader, 'regexResult',regexResult);
try {
var url = ytImageRootUrl+regexResult[1].replaceAll('\\u0026', '&');
} catch {
var url = null
}
//console.debug(logHeader, 'url',url);
return url
} else {
console.error(logHeader, 'Could not GET "'+searchUrl+videoId+'". Response Status = '+request.status,request.statusText);
return null
}
}

function useNewSearchMethod() {
if (forceDisableNewSearchMethod) {
return false
} else if (forceEnableNewSearchMethod) {
return true
}
/*
if (window.location.pathname === '/') {
console.debug(logHeader, 'Location check method');
if (document.head.innerHTML.indexOf('an_webp') != -1 || document.body.innerHTML.indexOf('an_webp') != -1) {
return false
}
else {
return true
}
}*/
const request = new XMLHttpRequest();
request.open("GET", homeUrl, false); // `false` makes the request synchronous
request.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) {
runPageCheckForExistingElements();
return console.log(logHeader, "Using old method only, disabling new method.")
}
console.log(logHeader, "Enabling new method.")
// Select the node that will be observed for mutations
const targetNode = document;
//console.debug('targetNodeInit',targetNode);
// Options for the observer (which mutations to observe)
const config = {attributes: false, childList: true, subtree: true};
// Callback function to execute when mutations are observed
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);
}
}
}
}
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
runPageCheckForExistingElements();
}
setupMutationObserverSingle()

§
發表於:2025-02-20

Previously I came up with this below code when I was looking for an alternative to fix the homepage animated thumbnails not working. I post it here as an option but I advise against using it if you care about your Youtube search history as this will pollute it massively. Basically it works by doing a youtube search for any video ID that you hover over on the homepage and since the animated thumbnails URL are still currently available on the search it can extract the thumb URL from that; which means it does a search request every time you hover on a tile on the homepage. For other pages it should use the old method which is still working on the non-homepage pages.

But I repeat, unless you are willing to disable your Youtube search history this is going to mess it up because of all the searches it needs to do to work so don't use it unless you are happy with that.

Because I use the search history, and I expect most people do, once I realised this limitation I decided to abandon this alternative idea and wasn't going to publish it and so the code was not polished but it "works" so to speak and since your homepage has not reverted since 4 days I'll leave it here (as-is without warranty or liability, maybe these searches will affect the algorithm suggestions or something who knows) for you to decide if you want to use it and give up your youtube search history.

Thanks for the other solution. Hadn't got round to trying it when all of a sudden - today, 19/02, it started working again. Nothing had changed from my end so I've no idea why it seems to be stop/start for me. If it stops again I'll post another comment here to let you know.

發表回覆

登入以回復