Guest User

Universal Batoto Image Fix v2 (Mirror)

a guest
Dec 11th, 2025
9,006
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.48 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Universal Batoto Image Fix (Hybrid)
  3. // @namespace Umbrella_Corporation
  4. // @version 2.6
  5. // @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.
  6. // @run-at document-start
  7. // @grant GM_xmlhttpRequest
  8. // @grant GM_notification
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_registerMenuCommand
  12. // @connect pastebin.com
  13. // @icon https://www.google.com/s2/favicons?sz=64&domain=bato.to
  14. // @match *://bato.to/*
  15. // @match *://bato.si/*
  16. // @match *://dto.to/*
  17. // @match *://fto.to/*
  18. // @match *://hto.to/*
  19. // @match *://jto.to/*
  20. // @match *://lto.to/*
  21. // @match *://mto.to/*
  22. // @match *://nto.to/*
  23. // @match *://vto.to/*
  24. // @match *://wto.to/*
  25. // @match *://xto.to/*
  26. // @match *://yto.to/*
  27. // @match *://vba.to/*
  28. // @match *://wba.to/*
  29. // @match *://xba.to/*
  30. // @match *://yba.to/*
  31. // @match *://zba.to/*
  32. // @match *://bato.ac/*
  33. // @match *://bato.bz/*
  34. // @match *://bato.cc/*
  35. // @match *://bato.cx/*
  36. // @match *://bato.id/*
  37. // @match *://bato.pw/*
  38. // @match *://bato.sh/*
  39. // @match *://bato.vc/*
  40. // @match *://bato.day/*
  41. // @match *://bato.red/*
  42. // @match *://bato.run/*
  43. // @match *://batoto.in/*
  44. // @match *://batoto.tv/*
  45. // @match *://batotoo.com/*
  46. // @match *://batotwo.com/*
  47. // @match *://batpub.com/*
  48. // @match *://batread.com/*
  49. // @match *://battwo.com/*
  50. // @match *://xbato.com/*
  51. // @match *://xbato.net/*
  52. // @match *://xbato.org/*
  53. // @match *://zbato.com/*
  54. // @match *://zbato.net/*
  55. // @match *://zbato.org/*
  56. // @match *://comiko.net/*
  57. // @match *://comiko.org/*
  58. // @match *://mangatoto.com/*
  59. // @match *://mangatoto.net/*
  60. // @match *://mangatoto.org/*
  61. // @match *://batocomic.com/*
  62. // @match *://batocomic.net/*
  63. // @match *://batocomic.org/*
  64. // @match *://readtoto.com/*
  65. // @match *://readtoto.net/*
  66. // @match *://readtoto.org/*
  67. // @match *://kuku.to/*
  68. // @match *://okok.to/*
  69. // @match *://ruru.to/*
  70. // @match *://xdxd.to/*
  71. // ==/UserScript==
  72.  
  73. /* ============================================================
  74. ====================== FULL CHANGELOG =======================
  75. =============== (Reversed: v2.6 → v1.0) ====================
  76.  
  77. v2.6 — FINAL STABLE RELEASE:
  78. • Restores original v1.9.1 hybrid engine (unmodified)
  79. • Full modern updater + GM compatibility included
  80. • Reliable banner + chapter fixing
  81. • Removes unstable experiments from later 2.x builds
  82.  
  83. v2.5 — Pastebin Updater & GM Compatibility:
  84. • Universal support (Tampermonkey, Violentmonkey, Greasemonkey)
  85. • Auto-update every 3 days
  86. • Manual “Check for Updates” (menu command)
  87. • Notification system & fallback storage/networking
  88.  
  89. v2.4 — Optimization Pass:
  90. • Removed unnecessary components
  91. • Improved rotation stability
  92. • Faster execution, simplified structure
  93. • Fixed URL parsing corner cases
  94.  
  95. v2.3.1 — Stability & Timeout Overhaul:
  96. • Improved timeout scaling for large pages
  97. • Canvas detection timing fixed
  98. • Retry backoff became network-aware
  99. • Base timeout increased for reliability
  100.  
  101. v2.3 — Metadata + updater hardening for TM
  102.  
  103. v2.2 — Logging Hardening:
  104. • Safer protocol-relative & relative URL logging
  105. • Cleaner rotation logging
  106.  
  107. v2.1 — Chromium Timing Fix:
  108. • Ensures script runs early enough for image rewrite
  109.  
  110. v2.0 — Unified Engine Release:
  111. • Combined classic + hybrid engines
  112. • Added first full updater
  113. • Rotation stability improvements
  114.  
  115. v1.9.1 — Enhanced Hybrid:
  116. • Host-rotation (credits to redditor mindlesstourist3)
  117. • Dynamic retry/backoff
  118. • Canvas-based broken detection
  119. • Improved timeout scaling
  120. • Safer attribute rewriting
  121. • Rotation loop protection
  122.  
  123. v1.9 — Hybrid improvements:
  124. • Better rotation
  125. • Lazy-loader support sharpened
  126. • Wider mirror support
  127.  
  128. v1.8 — Added bato.si + mirror expansions
  129.  
  130. v1.7 — Improved MutationObserver for infinite scroll
  131.  
  132. v1.6 — Reduced CPU load via attribute-filtered observation
  133.  
  134. v1.5 — Major mirror expansion (.ac, .bz, .cc, .sh, .vc, .day…)
  135.  
  136. v1.4 — Banner fix + early updater concept
  137.  
  138. v1.3 — SPA & lazy-loader support via MutationObserver
  139.  
  140. v1.2 — Added data-original + improved srcset parsing
  141.  
  142. v1.1 — Added referrerpolicy rewrite + srcset improvements
  143.  
  144. v1.0 — First release: //k → //n image rewrite
  145. ===============================================================
  146. */
  147.  
  148. /* ============================================================
  149. ==================== HYBRID ENGINE v1.9.1 ==================
  150. ============================================================ */
  151.  
  152. (function () {
  153. 'use strict';
  154.  
  155. const log = window.__BTFX_LOG || (() => {});
  156. const DEBUG = window.__BTFX_DEBUG || false;
  157.  
  158. /* ------------------ Quickfix Utilities ------------------ */
  159.  
  160. const looksLikeBroken = s =>
  161. typeof s === 'string' && s.includes('//k') && s.includes('.mb');
  162.  
  163. const fixK = s =>
  164. typeof s === 'string' ? s.replace(/\/\/k/g, '//n') : s;
  165.  
  166. const safeSet = (el, attr, val) => {
  167. try { el.setAttribute(attr, val); } catch (e) {}
  168. };
  169.  
  170. const safeSrc = (img, val) => {
  171. try { img.src = val; }
  172. catch (_) {
  173. try { img.setAttribute('src', val); } catch (_) {}
  174. }
  175. };
  176.  
  177. /* ------------------ Rotation Utilities ------------------ */
  178.  
  179. const imgHostRegex = /(https:\/\/[a-z]+)([0-9]+)(\.[^\/]*)(\/.*)/i;
  180.  
  181. const computeNum = numberStr => {
  182. let n = parseInt(numberStr, 10);
  183. const max = Math.max(n, 12);
  184. n = (n + 1) % max;
  185. return String(n).padStart(numberStr.length, '0');
  186. };
  187.  
  188. const computeNewHostSrc = (getHosts, failed, oldSrc) => {
  189. const m = oldSrc.match(imgHostRegex);
  190. if (!m) return null;
  191.  
  192. const path = m[4];
  193. const hosts = getHosts();
  194. let newHost = hosts.find(h => !failed.some(f => f.startsWith(h)));
  195.  
  196. if (!newHost)
  197. newHost = m[1] + computeNum(m[2]) + m[3];
  198.  
  199. return newHost + path;
  200. };
  201.  
  202. /* ------------------ Load Timeout Calculation ------------------ */
  203.  
  204. const calcTimeout = img => {
  205. const base = 3500 + (0.028 * ((img.width || 0) * (img.height || 0)));
  206. let t = isNaN(base) ? 5000 : Math.max(5000, base);
  207.  
  208. const src = (img.src || '').toLowerCase();
  209. if (src.endsWith('.png')) t *= 3;
  210. else if (src.endsWith('.jpg') || src.endsWith('.jpeg')) t *= 2;
  211.  
  212. return Math.min(t, 100000);
  213. };
  214.  
  215. /* ------------------ Broken-image Detection ------------------ */
  216.  
  217. const isBroken = img => {
  218. const canvas = document.createElement('canvas');
  219. const ctx = canvas.getContext('2d');
  220. if (!ctx) return undefined;
  221.  
  222. try {
  223. canvas.width = canvas.height = 1;
  224. ctx.drawImage(img, 0, 0);
  225. ctx.getImageData(0, 0, 1, 1);
  226.  
  227. return new Error("Assuming broken (no security error)");
  228. } catch (e) {
  229. const s = String(e);
  230.  
  231. if (s.includes("broken")) return e;
  232. if (s.includes("insecure")) return undefined;
  233.  
  234. return undefined;
  235. }
  236. };
  237.  
  238. /* ------------------ Host Candidate Generator ------------------ */
  239.  
  240. const getHostsFactory = getter => () => {
  241. const imgs = getter();
  242. const set = new Map();
  243.  
  244. imgs.forEach(img => {
  245. if (!img.complete || img.erroredSrcs) return;
  246.  
  247. const m = (img.src || '').match(imgHostRegex);
  248. if (!m) return;
  249.  
  250. set.set(m[1] + m[2] + m[3], Math.random());
  251. });
  252.  
  253. return [...set.entries()]
  254. .sort((a, b) => b[1] - a[1])
  255. .map(([k]) => k);
  256. };
  257.  
  258. /* ------------------ Main Per-Image Logic ------------------ */
  259.  
  260. function attachHybrid(img) {
  261. if (!img || img._hybridHooked) return;
  262. img._hybridHooked = true;
  263.  
  264. /* === QUICKFIX PHASE: k→n, referrerpolicy, data-* attrs === */
  265.  
  266. try {
  267. const propSrc = img.src || '';
  268.  
  269. if (looksLikeBroken(propSrc))
  270. safeSrc(img, fixK(propSrc));
  271.  
  272. ['src', 'data-src', 'data-original', 'srcset', 'data-srcset']
  273. .forEach(attr => {
  274. const v = img.getAttribute(attr);
  275. if (v && v.includes('//k'))
  276. safeSet(img, attr, fixK(v));
  277. });
  278.  
  279. try { img.referrerPolicy = 'no-referrer'; } catch (_) {}
  280. safeSet(img, 'referrerpolicy', 'no-referrer');
  281. } catch (e) {}
  282.  
  283. /* === HEAVY HYBRID MODE ONLY IF HOST IS ROTATABLE === */
  284.  
  285. if (!imgHostRegex.test(img.src || ''))
  286. return;
  287.  
  288. img.erroredSrcs = [];
  289. const getHosts = getHostsFactory(() => document.querySelectorAll('img'));
  290.  
  291. let tries = 0;
  292. const maxTries = 12;
  293. let timer = null;
  294.  
  295. const stop = () => {
  296. try { img.removeEventListener('error', onError); } catch (_) {}
  297. try { img.removeEventListener('load', onLoad); } catch (_) {}
  298. if (timer) clearTimeout(timer);
  299. };
  300.  
  301. const retry = (instant) => {
  302. tries++;
  303. if (tries >= maxTries) {
  304. stop();
  305. return;
  306. }
  307.  
  308. const oldSrc = img.src;
  309. img.erroredSrcs.push(oldSrc);
  310.  
  311. const newSrc = computeNewHostSrc(getHosts, img.erroredSrcs, oldSrc);
  312. if (!newSrc) {
  313. stop();
  314. return;
  315. }
  316.  
  317. try { img.src = newSrc; }
  318. catch (_) { safeSrc(img, newSrc); }
  319.  
  320. scheduleTimeout();
  321. };
  322.  
  323. const onError = () => {
  324. clearTimeout(timer);
  325. retry(false);
  326. };
  327.  
  328. const onLoad = () => {
  329. clearTimeout(timer);
  330. if (isBroken(img)) retry(false);
  331. else stop();
  332. };
  333.  
  334. const scheduleTimeout = () => {
  335. if (timer) clearTimeout(timer);
  336. timer = setTimeout(() => retry(true), calcTimeout(img));
  337. };
  338.  
  339. img.addEventListener('error', onError);
  340. img.addEventListener('load', onLoad);
  341. scheduleTimeout();
  342. }
  343.  
  344. /* ------------------ DOM Scanner ------------------ */
  345.  
  346. function processNode(node) {
  347. if (!node) return;
  348.  
  349. if (node.tagName === 'IMG') {
  350. attachHybrid(node);
  351. return;
  352. }
  353.  
  354. const imgs = node.querySelectorAll?.('img');
  355. if (imgs) imgs.forEach(attachHybrid);
  356. }
  357.  
  358. /* ------------------ MutationObserver ------------------ */
  359.  
  360. const observer = new MutationObserver(muts => {
  361. muts.forEach(m => {
  362. if (m.type === 'childList') {
  363. m.addedNodes.forEach(n => n.nodeType === 1 && processNode(n));
  364. } else if (m.type === 'attributes' && m.target.tagName === 'IMG') {
  365. attachHybrid(m.target);
  366. }
  367. });
  368. });
  369.  
  370. /* ------------------ Initialization ------------------ */
  371.  
  372. function init() {
  373. processNode(document);
  374.  
  375. observer.observe(document, {
  376. childList: true,
  377. subtree: true,
  378. attributes: true,
  379. attributeFilter: [
  380. 'src',
  381. 'data-src',
  382. 'data-original',
  383. 'srcset',
  384. 'data-srcset'
  385. ]
  386. });
  387.  
  388. setTimeout(() => processNode(document), 900);
  389.  
  390. // Auto-check updates
  391. setTimeout(() => {
  392. try { window.__BTFX_CHECK_UPDATES(false); } catch (_) {}
  393. }, 15000);
  394. }
  395.  
  396. if (document.readyState === 'loading')
  397. document.addEventListener('DOMContentLoaded', init, { once: true });
  398. else
  399. init();
  400.  
  401. })();
Advertisement
Add Comment
Please, Sign In to add comment