// ==UserScript==
// @name Universal Image Host Interceptor
// @namespace https://greasyfork.org/users/108513
// @version 1.2.0
// @description Clean and fast image viewing for Fastpic, Imgbox, ImgDrive, Pixhost, ImageBam, and many more. Features: Stealth Mode (no ads/flash), Auto-Redirect (gateway bypass), and Anti-Loop.
// @description:fr Visionneuse d'image rapide et propre pour Fastpic, Imgbox, ImgDrive, Pixhost, ImageBam et bien d'autres. Fonctionnalités : Mode Furtif (pas de pubs/flash), Redirection Auto (contournement des pages d'attente) et Anti-Boucle.
// @author seb-du17
// @match *://imgbox.com/*
// @match *://*.imgbox.com/*
// @match *://fastpic.org/view/*
// @match *://imgxxt.in/*
// @match *://imgdrive.net/*
// @match *://imagebam.com/view/*
// @match *://www.imagebam.com/image/*
// @match *://turboimagehost.com/*
// @match *://imagetwist.com/*
// @match *://vipr.im/*
// @match *://pixhost.to/*
// @match *://*.pixhost.to/*
// @match *://pimpandhost.com/*
// @match *://*.imagevenue.com/*
// @match *://imx.to/*
// @match *://*.imx.to/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=fastpic.org
// @grant none
// @run-at document-start
// @compatible firefox
// @compatible chrome
// @license MIT
// @downloadURL https://update.greasyfork.icu/scripts/559025/Universal%20Image%20Host%20Interceptor.user.js
// @updateURL https://update.greasyfork.icu/scripts/559025/Universal%20Image%20Host%20Interceptor.meta.js
// ==/UserScript==
/* jshint esversion:11 */
(function() {
'use strict';
// --- CONFIG ---
const CONFIG = {
SILENT_CONSOLE: true,
DEBUG: false,
// Watchers
USE_MUTATION_OBSERVER: true,
OBSERVE_ATTRIBUTES: true, // utile sur certains hosts qui changent src/content sans ajouter de nodes
OBSERVER_MAX_RUNTIME_MS: 12000,
// Throttle extract() calls (évite de re-parser 200 fois si Amazon-like pages)
CHECK_MIN_INTERVAL_MS: 80, // 1 check max toutes les ~80ms
// Fallback timer (toujours utile car certains DOM changent peu mais images/attrs oui)
POLL_INTERVAL_MS: 250,
POLL_MAX_ATTEMPTS: 40
};
// --- UTILS ---
const noop = () => {};
if (CONFIG.SILENT_CONSOLE) {
window.console.log = noop;
window.console.warn = noop;
}
const log = CONFIG.DEBUG ? console.log.bind(console) : noop;
const warn = CONFIG.DEBUG ? console.warn.bind(console) : noop;
const HOST = window.location.hostname;
const HREF = window.location.href;
// --- WATCHERS CONTROL ---
let pollerId = null;
let observer = null;
let observerTimeoutId = null;
const stopPoller = () => {
if (pollerId) {
clearInterval(pollerId);
pollerId = null;
}
};
const stopObserver = () => {
if (observer) {
try { observer.disconnect(); } catch (e) {}
observer = null;
}
if (observerTimeoutId) {
clearTimeout(observerTimeoutId);
observerTimeoutId = null;
}
};
const stopAllWatchers = () => {
stopPoller();
stopObserver();
};
// --- CORE ENGINE ---
const render = (url) => {
if (!document.documentElement) {
requestAnimationFrame(() => render(url));
return;
}
const stealthIds = ['fp-stealth', 'imx-stealth', 'drive-stealth', 'box-stealth', 'bam-stealth'];
stealthIds.forEach(id => {
const el = document.getElementById(id);
if (el) { el.remove(); }
});
document.documentElement.style.visibility = 'visible';
document.documentElement.style.background = 'none';
if (document.head) {
while (document.head.firstChild) { document.head.removeChild(document.head.firstChild); }
}
if (document.body) {
while (document.body.firstChild) { document.body.removeChild(document.body.firstChild); }
}
requestAnimationFrame(() => {
if (!document.head) { document.documentElement.appendChild(document.createElement('head')); }
if (!document.body) { document.documentElement.appendChild(document.createElement('body')); }
document.head.innerHTML = `
Image View
`;
document.body.innerHTML = `
`;
// “anti-injection” minimaliste (comme ton script)
new MutationObserver((mutations) => {
mutations.forEach((m) => {
m.addedNodes.forEach((n) => {
if (n && (n.tagName === 'SCRIPT' || n.tagName === 'IFRAME')) { n.remove(); }
});
});
}).observe(document.documentElement, { childList: true, subtree: true });
});
};
const cleanAndShow = (src, refererHost) => {
try { window.stop(); } catch (e) {}
src = src.replace(/\\\//g, '/');
if (src.startsWith('//')) {
src = window.location.protocol + src;
}
if (refererHost === 'fastpic') {
render(src);
return;
}
if (refererHost === 'pixhost') {
if (src.includes('/thumbs/')) {
const newSrc = src.replace('//t', '//img').replace('/thumbs/', '/images/');
const imgCheck = new Image();
imgCheck.onload = () => { render(newSrc); };
imgCheck.onerror = () => { render(src); };
imgCheck.src = newSrc;
return;
}
} else {
src = src.replace(/(\.|_)(md|th|tn|thumbnail|preview)(\.|_)/i, '$1$3');
}
render(src);
};
const finalize = (src, refererHost) => {
stopAllWatchers();
if (src) {
cleanAndShow(src, refererHost);
}
};
// --- MODULES ---
const Modules = {
fastpic: {
check: () => HOST.includes('fastpic'),
init: () => {
if (document.documentElement) {
const s = document.createElement('style');
s.id = 'fp-stealth';
s.textContent = 'html, body { visibility: hidden !important; background: #0e0e0e !important; }';
document.documentElement.appendChild(s);
}
},
extract: () => {
const imgs = document.getElementsByTagName('img');
for (let i = 0; i < imgs.length; i++) {
if (imgs[i].src && imgs[i].src.includes('/big/')) {
return imgs[i].src;
}
}
const imgOld = document.querySelector('#image');
if (imgOld && imgOld.src) { return imgOld.src; }
return null;
},
onFail: () => {
const s = document.getElementById('fp-stealth');
if (s) { s.remove(); }
if (document.documentElement) { document.documentElement.style.visibility = 'visible'; }
}
},
pixhost: {
check: () => HOST.includes('pixhost'),
extract: () => {
const scripts = document.getElementsByTagName('script');
for (let i = 0; i < scripts.length; i++) {
const html = scripts[i].innerHTML;
if (html.includes('pswp_items')) {
const urls = html.match(/https?:\\?\/\\?\/[^"']+\.(jpg|jpeg|png|webp)/gi);
if (urls && urls.length > 0) {
const best = urls.find(u => !u.includes('/thumbs/') && !u.includes('/show/')) || urls[0];
if (!best.includes('/show/')) { return best; }
}
}
}
const img = document.querySelector('#image, #show_image, img.image-center');
if (img && img.src && !img.src.includes('/show/')) { return img.src; }
return null;
}
},
imx: {
check: () => HOST.includes('imx.to'),
init: () => {
if (document.documentElement) {
const s = document.createElement('style');
s.id = 'imx-stealth';
s.textContent = 'html, body { visibility: hidden !important; background: #0e0e0e !important; }';
document.documentElement.appendChild(s);
}
},
extract: () => {
const img = document.querySelector('#iimg') || document.querySelector('img.centred');
if (img) { return img.src; }
const urlParts = HREF.split('/');
const id = urlParts[urlParts.length - 1];
if (id.length > 3) {
const candidate = document.querySelector(`img[src*="${id}"]`);
if (candidate && !candidate.src.includes('logo')) { return candidate.src; }
}
const continueBtn = document.querySelector('input[name="imgContinue"]');
if (continueBtn) {
const key = 'imx_click_' + HREF;
if (!sessionStorage.getItem(key)) {
sessionStorage.setItem(key, '1');
setTimeout(() => { continueBtn.click(); }, 500);
}
return null;
}
return null;
},
onFail: () => {
const s = document.getElementById('imx-stealth');
if (s) { s.remove(); }
if (document.documentElement) { document.documentElement.style.visibility = 'visible'; }
}
},
imagevenue: {
check: () => HOST.includes('imagevenue.com'),
extract: () => {
const continueLink = document.querySelector('a[title="Continue to ImageVenue"]');
if (continueLink && continueLink.href) {
if (continueLink.href !== HREF) { window.location.href = continueLink.href; }
return null;
}
const img = document.querySelector('img.card-img-top') || document.querySelector('#main-image');
if (img) { return img.src; }
const imgs = document.querySelectorAll('img[src*=".jpg"], img[src*=".jpeg"], img[src*=".png"]');
for (let i = 0; i < imgs.length; i++) {
if (imgs[i].naturalWidth > 300 || (imgs[i].style.width && parseInt(imgs[i].style.width) > 300)) {
return imgs[i].src;
}
}
return null;
}
},
imagetwist: {
check: () => HOST.includes('imagetwist') || HOST.includes('vipr.im'),
extract: () => {
let img = document.querySelector('img.pic');
if (img && img.src) { return img.src; }
const urlParts = HREF.split('/');
const filename = urlParts[urlParts.length - 1];
if (filename.length > 5) {
const selector = `img[src*="${filename}"]`;
const candidate = document.querySelector(selector);
if (candidate && candidate.src !== HREF) { return candidate.src; }
}
return null;
}
},
imgxxt: {
check: () => HOST.includes('imgxxt'),
extract: () => {
const link = document.querySelector('link[rel="image_src"]');
if (link) { return link.href; }
const meta = document.querySelector('meta[property="og:image"]');
if (meta) { return meta.content; }
const v = document.querySelector('.image-viewer-container img');
if (v) { return v.src; }
return null;
}
},
imgdrive: {
check: () => HOST.includes('imgdrive'),
init: () => {
if (document.documentElement) {
const s = document.createElement('style');
s.id = 'drive-stealth';
s.textContent = 'html, body { visibility: hidden !important; background: #0e0e0e !important; }';
document.documentElement.appendChild(s);
}
},
extract: () => {
const og = document.querySelector('meta[property="og:image"]');
if (og && og.content) {
const thumb = og.content;
if (thumb.includes('/small/')) {
const hd = thumb.replace('/small/', '/big/');
const imgTest = new Image();
imgTest.onload = () => { finalize(hd, null); };
imgTest.src = hd;
}
}
const continueLink = document.querySelector('a[onclick*="closeOverlay"]');
if (continueLink) {
if (typeof unsafeWindow !== 'undefined' && unsafeWindow.closeOverlay) {
unsafeWindow.closeOverlay();
} else {
continueLink.click();
}
return null;
}
const img = document.querySelector('img.centred_resized, img.main-image, img.pic');
if (img) { return img.src; }
const imgId = document.querySelector('#myImage, #main_image');
if (imgId) { return imgId.src; }
return null;
},
onFail: () => {
const s = document.getElementById('drive-stealth');
if (s) { s.remove(); }
if (document.documentElement) { document.documentElement.style.visibility = 'visible'; }
}
},
imgbox: {
check: () => HOST.includes('imgbox'),
init: () => {
if (document.documentElement) {
const s = document.createElement('style');
s.id = 'box-stealth';
s.textContent = 'html, body { visibility: hidden !important; background: #0e0e0e !important; }';
document.documentElement.appendChild(s);
}
},
extract: () => {
if (HREF.match(/\.(jpg|jpeg|png|gif)$/i)) { return HREF; }
const img = document.querySelector('#img');
if (img) { return img.src; }
return null;
},
onFail: () => {
const s = document.getElementById('box-stealth');
if (s) { s.remove(); }
if (document.documentElement) { document.documentElement.style.visibility = 'visible'; }
}
},
imagebam: {
check: () => HOST.includes('imagebam'),
init: () => {
if (document.documentElement) {
const s = document.createElement('style');
s.id = 'bam-stealth';
s.textContent = 'html, body { visibility: hidden !important; background: #0e0e0e !important; }';
document.documentElement.appendChild(s);
}
document.cookie = "nsfw_inter=1; path=/";
},
extract: () => {
const img = document.querySelector('img.main-image');
if (img) { return img.src; }
const continueLink = document.querySelector('a[data-shown="inter"]');
if (continueLink) { continueLink.click(); return null; }
const allLinks = document.getElementsByTagName('a');
for (let i = 0; i < allLinks.length; i++) {
if (allLinks[i].textContent.includes('Continue to your image')) {
allLinks[i].click();
return null;
}
}
const imgs = document.querySelectorAll('img[src*=".jpg"], img[src*=".jpeg"], img[src*=".png"]');
for (let i = 0; i < imgs.length; i++) {
if (imgs[i].naturalWidth > 300 || (imgs[i].style.width && parseInt(imgs[i].style.width) > 300)) {
return imgs[i].src;
}
}
return null;
},
onFail: () => {
const s = document.getElementById('bam-stealth');
if (s) { s.remove(); }
if (document.documentElement) { document.documentElement.style.visibility = 'visible'; }
}
},
generic: {
check: () => true,
extract: () => {
if (HOST.includes('pimpandhost')) { return document.querySelector('.main-image-wrapper')?.dataset.src; }
return null;
}
}
};
// --- PICK MODULE ---
let activeModule = Modules.generic;
if (Modules.fastpic.check()) { activeModule = Modules.fastpic; }
else if (Modules.imgdrive.check()) { activeModule = Modules.imgdrive; }
else if (Modules.imgbox.check()) { activeModule = Modules.imgbox; }
else if (Modules.imagebam.check()) { activeModule = Modules.imagebam; }
else if (Modules.pixhost.check()) { activeModule = Modules.pixhost; }
else if (Modules.imagetwist.check()) { activeModule = Modules.imagetwist; }
else if (Modules.imgxxt.check()) { activeModule = Modules.imgxxt; }
else if (Modules.imagevenue.check()) { activeModule = Modules.imagevenue; }
else if (Modules.imx.check()) { activeModule = Modules.imx; }
if (activeModule.init) { activeModule.init(); }
const getRefererHost = () => (
activeModule === Modules.fastpic ? 'fastpic' :
(activeModule === Modules.pixhost ? 'pixhost' : null)
);
// --- Extraction orchestration (MO + fallback poll) ---
let lastCheckTs = 0;
let checkScheduled = false;
const tryExtract = () => {
const now = Date.now();
if (now - lastCheckTs < CONFIG.CHECK_MIN_INTERVAL_MS) {
return;
}
lastCheckTs = now;
let src = null;
try {
src = activeModule.extract();
} catch (e) {
warn('[UHI] extract error', e);
src = null;
}
if (src) {
finalize(src, getRefererHost());
}
};
const scheduleCheck = () => {
if (checkScheduled) { return; }
checkScheduled = true;
requestAnimationFrame(() => {
checkScheduled = false;
tryExtract();
});
};
const startObserver = () => {
if (!CONFIG.USE_MUTATION_OBSERVER) { return; }
const start = () => {
if (!document.documentElement) {
requestAnimationFrame(start);
return;
}
observer = new MutationObserver(() => {
scheduleCheck();
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
attributes: !!CONFIG.OBSERVE_ATTRIBUTES,
attributeFilter: CONFIG.OBSERVE_ATTRIBUTES ? ['src', 'href', 'content', 'class', 'style'] : undefined
});
observerTimeoutId = setTimeout(() => {
stopObserver();
}, CONFIG.OBSERVER_MAX_RUNTIME_MS);
};
start();
};
const startPollerFallback = () => {
let attempts = 0;
pollerId = setInterval(() => {
attempts++;
tryExtract();
if (attempts > CONFIG.POLL_MAX_ATTEMPTS) {
stopPoller();
if (activeModule.onFail) { activeModule.onFail(); }
}
}, CONFIG.POLL_INTERVAL_MS);
};
// 1) Try immediately
tryExtract();
// 2) Start watchers if not already finalized
startObserver();
startPollerFallback();
})();