Better Rule34

A script to improve the use of rule34, now with API key support!

< Valutazione su Better Rule34

Recensione: OK - lo script funziona, ma ha degli errori.

§
Pubblicato: 25/08/2025

Also, pressing "Random" also redirects to the post page with tag "all", no matter which version of the site I use. Also, no matter what version of the site I use, pressing the "Download All" button below the search bar also does nothing, also no matter which version of the site I use (as in, it doesn't work neither in desktop version, nor in mobile version.)

§
Pubblicato: 26/08/2025
Modificato: 26/08/2025

Hi. I asked ChatGPT that question, and with 2 hours of spare time, I came up with this patched version that includes only these two buttons. I tested this script and it worked for me (using brave browser with tampermonkey). edit: The download all button not working(((

// ==UserScript==
// @name         Better Rule34 fixed
// @namespace    http://tampermonkey.net/
// @version      0.90
// @description  barebone with only random and download all fixes
// @author       user342247947999
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM.xmlHttpRequest
// @require      https://unpkg.com/[email protected]
// ==/UserScript==
;(function(){
  "use strict";

  // ——— Simple GM+xmlHttpRequest to get text or blob ———
  function gmFetchText(url){
    return new Promise((res, rej) => {
      GM.xmlHttpRequest({
        method: "GET",
        url,
        responseType: "text",
        anonymous: false,
        headers: {
          Referer: "https://rule34.xxx/",
          Origin:  "https://rule34.xxx"
        },
        onload: r => (r.status>=200&&r.status<300) ? res(r.responseText) : rej(r.status),
        onerror: e => rej(e)
      });
    });
  }

  // ——— Read your current tags field (“foo bar” → “foo+bar”) ———
  function getTags(){
    const form = document.querySelector('form[action*="s=list"]')
               || document.querySelector(".tag-search")
               || document;
    const inp = form.querySelector("input[name=tags]");
    return inp ? inp.value.trim().replace(/\s+/g,"+") : "";
  }

  // ——— Random logic by scraping pages and thumbnails ———
  async function doRandom(){
    try {
      const tags = getTags();
      const base =
        "https://rule34.xxx/index.php?page=post&s=list"
        + (tags? `&tags=${tags}` : "");
      // 1) load first page HTML
      const html1 = await gmFetchText(base);
      const doc1 = new DOMParser().parseFromString(html1, "text/html");
      // 2) find all pagination links (<a href="…pid=xxxx">) and collect pids
      const pidSet = new Set();
      doc1.querySelectorAll('a[href*="pid="]').forEach(a=>{
        try {
          const u = new URL(a.href, base);
          pidSet.add(u.searchParams.get("pid")||"0");
        }catch(e){}
      });
      // always include 0
      pidSet.add("0");
      const pids = Array.from(pidSet);
      if (!pids.length) throw "no pages found";
      // 3) pick a random pid
      const pickPid = pids[Math.floor(Math.random()*pids.length)];
      // 4) fetch that page
      const pageUrl = pickPid==="0"? base : base + "&pid=" + pickPid;
      const html2 = await gmFetchText(pageUrl);
      const doc2 = new DOMParser().parseFromString(html2, "text/html");
      // 5) collect all thumbnail <a> under .image-list
      const thumbs = Array.from(doc2.querySelectorAll(".image-list a"));
      if (!thumbs.length) throw "no thumbnails";
      // 6) pick one at random and navigate
      const pick = thumbs[Math.floor(Math.random()*thumbs.length)];
      location.href = pick.href;
    }
    catch(err){
      alert("Random failed: " + err);
    }
  }

  // ——— DAPI JSON fetch (returns parsed array) ———
  function apiJSON(url){
    return new Promise((res, rej) => {
      GM.xmlHttpRequest({
        method: "GET",
        url: url + "&json=1",
        responseType: "json",
        anonymous: false,
        headers: {
          Referer: "https://rule34.xxx/",
          Origin:  "https://rule34.xxx"
        },
        onload(r){
          if(r.status>=200&&r.status<300){
            // some GM versions put parsed JSON in .response, others raw in .responseText
            const data = r.response || JSON.parse(r.responseText||"[]");
            return res(data);
          }
          rej("HTTP " + r.status);
        },
        onerror:e=>rej(e)
      });
    });
  }

  // ——— Download All logic ———
  async function doDownloadAll(){
    try {
      const tags = getTags();
      const idxUrl =
        "https://rule34.xxx/index.php?page=dapi&s=post&q=index"
        + "&limit=1000&pid=0"
        + (tags? `&tags=${tags}` : "");
      const posts = await apiJSON(idxUrl);
      if (!Array.isArray(posts) || !posts.length) throw "no posts";
      const zipFiles = {};
      for (let p of posts){
        // use sample for video to reduce size
        const ext = p.file_url.split(".").pop().toLowerCase();
        const isVid = ["mp4","webm","avi","mov"].includes(ext);
        const dl = isVid? p.sample_url : p.file_url;
        const blob = await fetch(dl).then(r=>r.blob());
        const name = dl.split("/").pop();
        zipFiles[name] = new Uint8Array(await blob.arrayBuffer());
      }
      const zipped = fflate.zipSync(zipFiles, { level: 0 });
      const blob = new Blob([zipped], { type:"application/zip" });
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = (tags||"all") + ".zip";
      document.body.appendChild(a);
      a.click();
      a.remove();
    }
    catch(err){
      alert("Download All failed: " + err);
    }
  }

  // ——— Inject our two buttons into the search form ———
  function inject(){
    const form = document.querySelector('form[action*="s=list"]')
               || document.querySelector(".tag-search");
    if (!form) return;
    const btnR = document.createElement("button");
    btnR.textContent = "Random";
    btnR.style.marginLeft = "8px";
    btnR.onclick = e => { e.preventDefault(); doRandom(); };
    const btnD = document.createElement("button");
    btnD.textContent = "Download All";
    btnD.style.marginLeft = "4px";
    btnD.onclick = e => { e.preventDefault(); doDownloadAll(); };
    form.appendChild(btnR);
    form.appendChild(btnD);
  }

  window.addEventListener("load", ()=> setTimeout(inject, 300));
})();

§
Pubblicato: 26/08/2025

and tested with Thorium x Violentmonkey

Pubblica risposta

Accedi per pubblicare una risposta.