Last active
January 6, 2025 09:09
-
-
Save thibaultmol/fdff6880f5a63fe2afc11ef2843561c7 to your computer and use it in GitHub Desktop.
Automatically plays animated GIFs in Kagi image search results
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 Kagi Image Search GIF Animator (Hover) | |
// @version 1 | |
// @description Plays animated GIFs in Kagi image search results on hover | |
// @author Thibaultmol | |
// @match https://kagi.com/images* | |
// @updateURL https://gist.github.com/thibaultmol/fdff6880f5a63fe2afc11ef2843561c7/raw/d814a567f1d5177a496ce837160ef519c79200a4/kagi-gif-autoplay.user.js | |
// @downloadURL https://gist.github.com/thibaultmol/fdff6880f5a63fe2afc11ef2843561c7/raw/d814a567f1d5177a496ce837160ef519c79200a4/kagi-gif-autoplay.user.js | |
// @grant GM_getValue | |
// @grant GM_setValue | |
// @grant GM_registerMenuCommand | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// Cache for preloaded images | |
const preloadCache = new Map(); | |
// Initialize settings | |
if (typeof GM_getValue('preloadEnabled') === 'undefined') { | |
GM_setValue('preloadEnabled', true); | |
} | |
// Add menu command to toggle preloading | |
GM_registerMenuCommand('Toggle GIF Preloading', () => { | |
const currentValue = GM_getValue('preloadEnabled'); | |
GM_setValue('preloadEnabled', !currentValue); | |
alert(`GIF Preloading is now ${!currentValue ? 'enabled' : 'disabled'}`); | |
}); | |
function isGifUrl(url) { | |
return url.toLowerCase().includes('.gif'); | |
} | |
function preloadImage(url) { | |
if (!GM_getValue('preloadEnabled')) return; | |
if (preloadCache.has(url)) { | |
return preloadCache.get(url); | |
} | |
const img = new Image(); | |
const promise = new Promise((resolve) => { | |
img.onload = () => resolve(url); | |
img.src = url; | |
}); | |
preloadCache.set(url, promise); | |
return promise; | |
} | |
function setupGifHoverBehavior(link) { | |
const img = link.querySelector('img._0_img_src'); | |
if (!img || !link.href || !isGifUrl(link.href)) return; | |
const staticUrl = img.src; | |
const animatedUrl = link.href; | |
const width = img.width; | |
const height = img.height; | |
// Store dimensions and URLs as data attributes | |
img.dataset.staticUrl = staticUrl; | |
img.dataset.animatedUrl = animatedUrl; | |
img.dataset.width = width; | |
img.dataset.height = height; | |
// Preload if enabled | |
if (GM_getValue('preloadEnabled')) { | |
preloadImage(animatedUrl); | |
} | |
// Add hover event listeners | |
link.addEventListener('mouseenter', () => { | |
img.src = img.dataset.animatedUrl; | |
img.width = img.dataset.width; | |
img.height = img.dataset.height; | |
}); | |
link.addEventListener('mouseleave', () => { | |
img.src = img.dataset.staticUrl; | |
img.width = img.dataset.width; | |
img.height = img.dataset.height; | |
}); | |
} | |
function initializeGifBehavior() { | |
const imgLinks = document.querySelectorAll('a._0_img_link_el'); | |
imgLinks.forEach(setupGifHoverBehavior); | |
} | |
// Initial setup | |
initializeGifBehavior(); | |
// Create observer for dynamically loaded content | |
const observer = new MutationObserver((mutations) => { | |
for (const mutation of mutations) { | |
if (mutation.addedNodes.length) { | |
initializeGifBehavior(); | |
} | |
} | |
}); | |
// Start observing | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
})(); |
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 Kagi Image Search GIF Animator | |
// @version 1 | |
// @description Automatically plays animated GIFs in Kagi image search results | |
// @author Thibaultmol | |
// @match https://kagi.com/images* | |
// @updateURL https://gist.github.com/thibaultmol/fdff6880f5a63fe2afc11ef2843561c7/raw/f1a6e0a12294b5b9198c4c6b24ed76e36fad934e/kagi-gif-autoplay.user.js | |
// @downloadURL https://gist.github.com/thibaultmol/fdff6880f5a63fe2afc11ef2843561c7/raw/f1a6e0a12294b5b9198c4c6b24ed76e36fad934e/kagi-gif-autoplay.user.js | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// Function to check if URL potentially points to a GIF | |
function isGifUrl(url) { | |
return url.toLowerCase().includes('.gif'); | |
} | |
// Function to replace static image with animated one | |
function replaceWithAnimatedGif() { | |
const imgLinks = document.querySelectorAll('a._0_img_link_el'); | |
imgLinks.forEach(link => { | |
const img = link.querySelector('img._0_img_src'); | |
if (img && link.href && isGifUrl(link.href)) { | |
// Get the animated GIF URL directly from the link's href | |
// The href already contains the correct proxy URL structure | |
const animatedUrl = link.href; | |
// Preserve original dimensions | |
const width = img.width; | |
const height = img.height; | |
// Update the image source while maintaining dimensions | |
img.src = animatedUrl; | |
img.width = width; | |
img.height = height; | |
} | |
}); | |
} | |
// Initial replacement | |
replaceWithAnimatedGif(); | |
// Create observer to handle dynamically loaded content | |
const observer = new MutationObserver((mutations) => { | |
for (const mutation of mutations) { | |
if (mutation.addedNodes.length) { | |
replaceWithAnimatedGif(); | |
} | |
} | |
}); | |
// Start observing the document for changes | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment