// ==UserScript== // @name Universal Batoto Image Fix (Hybrid) // @namespace Umbrella_Corporation // @version 2.6 // @description Stable v2.6 release: original v1.9.1 hybrid engine + full updater system. Fixes Batoto images by rewriting //k→//n and applying host rotation, retry/backoff, and broken-image detection. // @run-at document-start // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @connect pastebin.com // @icon https://www.google.com/s2/favicons?sz=64&domain=bato.to // Mirrors: // // @match *://bato.to/* // @match *://bato.si/* // @match *://dto.to/* // @match *://fto.to/* // @match *://hto.to/* // @match *://jto.to/* // @match *://lto.to/* // @match *://mto.to/* // @match *://nto.to/* // @match *://vto.to/* // @match *://wto.to/* // @match *://xto.to/* // @match *://yto.to/* // @match *://vba.to/* // @match *://wba.to/* // @match *://xba.to/* // @match *://yba.to/* // @match *://zba.to/* // @match *://bato.ac/* // @match *://bato.bz/* // @match *://bato.cc/* // @match *://bato.cx/* // @match *://bato.id/* // @match *://bato.pw/* // @match *://bato.sh/* // @match *://bato.vc/* // @match *://bato.day/* // @match *://bato.red/* // @match *://bato.run/* // @match *://batoto.in/* // @match *://batoto.tv/* // @match *://batotoo.com/* // @match *://batotwo.com/* // @match *://batpub.com/* // @match *://batread.com/* // @match *://battwo.com/* // @match *://xbato.com/* // @match *://xbato.net/* // @match *://xbato.org/* // @match *://zbato.com/* // @match *://zbato.net/* // @match *://zbato.org/* // @match *://comiko.net/* // @match *://comiko.org/* // @match *://mangatoto.com/* // @match *://mangatoto.net/* // @match *://mangatoto.org/* // @match *://batocomic.com/* // @match *://batocomic.net/* // @match *://batocomic.org/* // @match *://readtoto.com/* // @match *://readtoto.net/* // @match *://readtoto.org/* // @match *://kuku.to/* // @match *://okok.to/* // @match *://ruru.to/* // @match *://xdxd.to/* // ==/UserScript== /* ============================================================ ====================== FULL CHANGELOG ======================= =============== (Reversed: v2.6 → v1.0) ==================== v2.6 — FINAL STABLE RELEASE: • Restores original v1.9.1 hybrid engine (unmodified) • Full modern updater + GM compatibility included • Reliable banner + chapter fixing • Removes unstable experiments from later 2.x builds v2.5 — Pastebin Updater & GM Compatibility: • Universal support (Tampermonkey, Violentmonkey, Greasemonkey) • Auto-update every 3 days • Manual “Check for Updates” (menu command) • Notification system & fallback storage/networking v2.4 — Optimization Pass: • Removed unnecessary components • Improved rotation stability • Faster execution, simplified structure • Fixed URL parsing corner cases v2.3.1 — Stability & Timeout Overhaul: • Improved timeout scaling for large pages • Canvas detection timing fixed • Retry backoff became network-aware • Base timeout increased for reliability v2.3 — Metadata + updater hardening for TM v2.2 — Logging Hardening: • Safer protocol-relative & relative URL logging • Cleaner rotation logging v2.1 — Chromium Timing Fix: • Ensures script runs early enough for image rewrite v2.0 — Unified Engine Release: • Combined classic + hybrid engines • Added first full updater • Rotation stability improvements v1.9.1 — Enhanced Hybrid: • Host-rotation (credits to redditor mindlesstourist3) • Dynamic retry/backoff • Canvas-based broken detection • Improved timeout scaling • Safer attribute rewriting • Rotation loop protection v1.9 — Hybrid improvements: • Better rotation • Lazy-loader support sharpened • Wider mirror support v1.8 — Added bato.si + mirror expansions v1.7 — Improved MutationObserver for infinite scroll v1.6 — Reduced CPU load via attribute-filtered observation v1.5 — Major mirror expansion (.ac, .bz, .cc, .sh, .vc, .day…) v1.4 — Banner fix + early updater concept v1.3 — SPA & lazy-loader support via MutationObserver v1.2 — Added data-original + improved srcset parsing v1.1 — Added referrerpolicy rewrite + srcset improvements v1.0 — First release: //k → //n image rewrite =============================================================== */ // ==UserScript== // @name Universal Batoto Image Fix (Hybrid) // @namespace Umbrella_Corporation // @version 2.6 // @description Stable v2.6 release: original v1.9.1 hybrid engine + full updater system. Fixes Batoto images by rewriting //k→//n and applying host rotation, retry/backoff, and broken-image detection. // @run-at document-start // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @connect pastebin.com // @icon https://www.google.com/s2/favicons?sz=64&domain=bato.to // @updateURL https://pastebin.com/raw/c0mBHwtH // @downloadURL https://pastebin.com/raw/c0mBHwtH // Mirrors: // // @match *://bato.to/* // @match *://bato.si/* // @match *://dto.to/* // @match *://fto.to/* // @match *://hto.to/* // @match *://jto.to/* // @match *://lto.to/* // @match *://mto.to/* // @match *://nto.to/* // @match *://vto.to/* // @match *://wto.to/* // @match *://xto.to/* // @match *://yto.to/* // @match *://vba.to/* // @match *://wba.to/* // @match *://xba.to/* // @match *://yba.to/* // @match *://zba.to/* // @match *://bato.ac/* // @match *://bato.bz/* // @match *://bato.cc/* // @match *://bato.cx/* // @match *://bato.id/* // @match *://bato.pw/* // @match *://bato.sh/* // @match *://bato.vc/* // @match *://bato.day/* // @match *://bato.red/* // @match *://bato.run/* // @match *://batoto.in/* // @match *://batoto.tv/* // @match *://batotoo.com/* // @match *://batotwo.com/* // @match *://batpub.com/* // @match *://batread.com/* // @match *://battwo.com/* // @match *://xbato.com/* // @match *://xbato.net/* // @match *://xbato.org/* // @match *://zbato.com/* // @match *://zbato.net/* // @match *://zbato.org/* // @match *://comiko.net/* // @match *://comiko.org/* // @match *://mangatoto.com/* // @match *://mangatoto.net/* // @match *://mangatoto.org/* // @match *://batocomic.com/* // @match *://batocomic.net/* // @match *://batocomic.org/* // @match *://readtoto.com/* // @match *://readtoto.net/* // @match *://readtoto.org/* // @match *://kuku.to/* // @match *://okok.to/* // @match *://ruru.to/* // @match *://xdxd.to/* // ==/UserScript== /* ============================================================ ====================== FULL CHANGELOG ======================= =============== (Reversed: v2.6 → v1.0) ==================== v2.6 — FINAL STABLE RELEASE: • Restores original v1.9.1 hybrid engine (unmodified) • Full modern updater + GM compatibility included • Reliable banner + chapter fixing • Removes unstable experiments from later 2.x builds v2.5 — Pastebin Updater & GM Compatibility: • Universal support (Tampermonkey, Violentmonkey, Greasemonkey) • Auto-update every 3 days • Manual “Check for Updates” (menu command) • Notification system & fallback storage/networking v2.4 — Optimization Pass: • Removed unnecessary components • Improved rotation stability • Faster execution, simplified structure • Fixed URL parsing corner cases v2.3.1 — Stability & Timeout Overhaul: • Improved timeout scaling for large pages • Canvas detection timing fixed • Retry backoff became network-aware • Base timeout increased for reliability v2.3 — Metadata + updater hardening for TM v2.2 — Logging Hardening: • Safer protocol-relative & relative URL logging • Cleaner rotation logging v2.1 — Chromium Timing Fix: • Ensures script runs early enough for image rewrite v2.0 — Unified Engine Release: • Combined classic + hybrid engines • Added first full updater • Rotation stability improvements v1.9.1 — Enhanced Hybrid: • Host-rotation (credits to redditor mindlesstourist3) • Dynamic retry/backoff • Canvas-based broken detection • Improved timeout scaling • Safer attribute rewriting • Rotation loop protection v1.9 — Hybrid improvements: • Better rotation • Lazy-loader support sharpened • Wider mirror support v1.8 — Added bato.si + mirror expansions v1.7 — Improved MutationObserver for infinite scroll v1.6 — Reduced CPU load via attribute-filtered observation v1.5 — Major mirror expansion (.ac, .bz, .cc, .sh, .vc, .day…) v1.4 — Banner fix + early updater concept v1.3 — SPA & lazy-loader support via MutationObserver v1.2 — Added data-original + improved srcset parsing v1.1 — Added referrerpolicy rewrite + srcset improvements v1.0 — First release: //k → //n image rewrite =============================================================== */ /* ============================================================ ==================== HYBRID ENGINE v1.9.1 ================== ============================================================ */ (function () { 'use strict'; const log = window.__BTFX_LOG || (() => {}); const DEBUG = window.__BTFX_DEBUG || false; /* ------------------ Quickfix Utilities ------------------ */ const looksLikeBroken = s => typeof s === 'string' && s.includes('//k') && s.includes('.mb'); const fixK = s => typeof s === 'string' ? s.replace(/\/\/k/g, '//n') : s; const safeSet = (el, attr, val) => { try { el.setAttribute(attr, val); } catch (e) {} }; const safeSrc = (img, val) => { try { img.src = val; } catch (_) { try { img.setAttribute('src', val); } catch (_) {} } }; /* ------------------ Rotation Utilities ------------------ */ const imgHostRegex = /(https:\/\/[a-z]+)([0-9]+)(\.[^\/]*)(\/.*)/i; const computeNum = numberStr => { let n = parseInt(numberStr, 10); const max = Math.max(n, 12); n = (n + 1) % max; return String(n).padStart(numberStr.length, '0'); }; const computeNewHostSrc = (getHosts, failed, oldSrc) => { const m = oldSrc.match(imgHostRegex); if (!m) return null; const path = m[4]; const hosts = getHosts(); let newHost = hosts.find(h => !failed.some(f => f.startsWith(h))); if (!newHost) newHost = m[1] + computeNum(m[2]) + m[3]; return newHost + path; }; /* ------------------ Load Timeout Calculation ------------------ */ const calcTimeout = img => { const base = 3500 + (0.028 * ((img.width || 0) * (img.height || 0))); let t = isNaN(base) ? 5000 : Math.max(5000, base); const src = (img.src || '').toLowerCase(); if (src.endsWith('.png')) t *= 3; else if (src.endsWith('.jpg') || src.endsWith('.jpeg')) t *= 2; return Math.min(t, 100000); }; /* ------------------ Broken-image Detection ------------------ */ const isBroken = img => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (!ctx) return undefined; try { canvas.width = canvas.height = 1; ctx.drawImage(img, 0, 0); ctx.getImageData(0, 0, 1, 1); return new Error("Assuming broken (no security error)"); } catch (e) { const s = String(e); if (s.includes("broken")) return e; if (s.includes("insecure")) return undefined; return undefined; } }; /* ------------------ Host Candidate Generator ------------------ */ const getHostsFactory = getter => () => { const imgs = getter(); const set = new Map(); imgs.forEach(img => { if (!img.complete || img.erroredSrcs) return; const m = (img.src || '').match(imgHostRegex); if (!m) return; set.set(m[1] + m[2] + m[3], Math.random()); }); return [...set.entries()] .sort((a, b) => b[1] - a[1]) .map(([k]) => k); }; /* ------------------ Main Per-Image Logic ------------------ */ function attachHybrid(img) { if (!img || img._hybridHooked) return; img._hybridHooked = true; /* === QUICKFIX PHASE: k→n, referrerpolicy, data-* attrs === */ try { const propSrc = img.src || ''; if (looksLikeBroken(propSrc)) safeSrc(img, fixK(propSrc)); ['src', 'data-src', 'data-original', 'srcset', 'data-srcset'] .forEach(attr => { const v = img.getAttribute(attr); if (v && v.includes('//k')) safeSet(img, attr, fixK(v)); }); try { img.referrerPolicy = 'no-referrer'; } catch (_) {} safeSet(img, 'referrerpolicy', 'no-referrer'); } catch (e) {} /* === HEAVY HYBRID MODE ONLY IF HOST IS ROTATABLE === */ if (!imgHostRegex.test(img.src || '')) return; img.erroredSrcs = []; const getHosts = getHostsFactory(() => document.querySelectorAll('img')); let tries = 0; const maxTries = 12; let timer = null; const stop = () => { try { img.removeEventListener('error', onError); } catch (_) {} try { img.removeEventListener('load', onLoad); } catch (_) {} if (timer) clearTimeout(timer); }; const retry = (instant) => { tries++; if (tries >= maxTries) { stop(); return; } const oldSrc = img.src; img.erroredSrcs.push(oldSrc); const newSrc = computeNewHostSrc(getHosts, img.erroredSrcs, oldSrc); if (!newSrc) { stop(); return; } try { img.src = newSrc; } catch (_) { safeSrc(img, newSrc); } scheduleTimeout(); }; const onError = () => { clearTimeout(timer); retry(false); }; const onLoad = () => { clearTimeout(timer); if (isBroken(img)) retry(false); else stop(); }; const scheduleTimeout = () => { if (timer) clearTimeout(timer); timer = setTimeout(() => retry(true), calcTimeout(img)); }; img.addEventListener('error', onError); img.addEventListener('load', onLoad); scheduleTimeout(); } /* ------------------ DOM Scanner ------------------ */ function processNode(node) { if (!node) return; if (node.tagName === 'IMG') { attachHybrid(node); return; } const imgs = node.querySelectorAll?.('img'); if (imgs) imgs.forEach(attachHybrid); } /* ------------------ MutationObserver ------------------ */ const observer = new MutationObserver(muts => { muts.forEach(m => { if (m.type === 'childList') { m.addedNodes.forEach(n => n.nodeType === 1 && processNode(n)); } else if (m.type === 'attributes' && m.target.tagName === 'IMG') { attachHybrid(m.target); } }); }); /* ------------------ Initialization ------------------ */ function init() { processNode(document); observer.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: [ 'src', 'data-src', 'data-original', 'srcset', 'data-srcset' ] }); setTimeout(() => processNode(document), 900); // Auto-check updates setTimeout(() => { try { window.__BTFX_CHECK_UPDATES(false); } catch (_) {} }, 15000); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init, { once: true }); else init(); })();