Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==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
- // @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();
- })();
Advertisement
Add Comment
Please, Sign In to add comment