Guest User

eroscript jdown userscript

a guest
Mar 24th, 2026
50
0
132 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Link Copier Helper (JD package + JD detect + fallback)
  3. // @namespace    https://discuss.eroscripts
  4. // @version      2.0
  5. // @description  Send matching links to JDownloader as a per-page package (no URL rewriting). Fallback to clipboard if JD isn't running.
  6. // @match        *://discuss.eroscripts.com/*
  7. // @grant        GM_setClipboard
  8. // @grant        GM_registerMenuCommand
  9. // @grant        GM_xmlhttpRequest
  10. // @connect      127.0.0.1
  11. // @run-at       document-idle
  12. // ==/UserScript==
  13.  
  14. (function () {
  15.   "use strict";
  16.  
  17.   // ---------------- CONFIGURATION ----------------
  18.   const EXTENSIONS = [".mp4", ".webm", ".mov", ".srt", ".ass", ".mkv", ".funscript"];
  19.  
  20.   const IGNORE_EXTERNAL = ["discourse.org", "twitter.com", "x.com", "patreon", "povr.com", "wankzvr.com", "feelxvideos.com", "funosr.com", "blob:https://discuss", "funscript.org", "osrmarket.io", "yourhobbiescustomized.com"];
  21.  
  22.   // JDownloader ExternInterface endpoints (localhost webserver) :contentReference[oaicite:2]{index=2}
  23.   const JD_CHECK_URL = "http://127.0.0.1:9666/jdcheck.js"; // sets global jdownloader=true in the doc example :contentReference[oaicite:3]{index=3}
  24.   const JD_FLASHGOT_URL = "http://127.0.0.1:9666/flashgot"; // accepts urls/package/referer :contentReference[oaicite:4]{index=4}
  25.  
  26.   // IMPORTANT: keep referer stable so you can "always allow" once.
  27.   // If you send a different full URL each time, JD may keep asking.
  28.   const JD_STABLE_REFERER = "https://discuss.eroscripts.com/";
  29.  
  30.   // Optional: bypass LinkGrabber and send straight to Downloads (0/1). :contentReference[oaicite:5]{index=5}
  31.   const JD_AUTOSTART = 0;
  32.   // ------------------------------------------------
  33.  
  34.   function isExtensionMatch(url) {
  35.     try {
  36.       const pathname = new URL(url, location.href).pathname.toLowerCase();
  37.       return EXTENSIONS.some((ext) => pathname.endsWith(ext));
  38.     } catch {
  39.       return false;
  40.     }
  41.   }
  42.  
  43.   function isExternal(url) {
  44.     try {
  45.       const u = new URL(url, location.href);
  46.       return (
  47.         u.hostname !== location.hostname &&
  48.         !IGNORE_EXTERNAL.includes(u.hostname.toLowerCase())
  49.       );
  50.     } catch {
  51.       return false;
  52.     }
  53.   }
  54.  
  55.   function gatherLinksRaw() {
  56.     const links = Array.from(document.querySelectorAll("a[href]"));
  57.     const matches = links
  58.       .map((a) => a.href)
  59.       .filter((href) => isExtensionMatch(href) || isExternal(href));
  60.  
  61.     return [...new Set(matches)];
  62.   }
  63.  
  64.   function getPackageName() {
  65.     const t =
  66.       document.querySelector("h1 .fancy-title")?.textContent?.trim() ||
  67.       document.querySelector(".fancy-title")?.textContent?.trim() ||
  68.       document.title.trim();
  69.  
  70.     const m = location.pathname.match(/\/t\/[^/]+\/(\d+)/);
  71.     const withId = m ? `${t} [${m[1]}]` : t;
  72.  
  73.     return withId
  74.       .replace(/[<>:"/\\|?*\u0000-\u001F]/g, "_")
  75.      .replace(/\s+/g, " ")
  76.      .trim()
  77.      .slice(0, 150);
  78.  }
  79.  
  80.  function gmXhr(opts) {
  81.    // Tampermonkey provides GM_xmlhttpRequest
  82.    return new Promise((resolve, reject) => {
  83.      GM_xmlhttpRequest({
  84.        ...opts,
  85.        onload: (r) => resolve(r),
  86.        onerror: (e) => reject(e),
  87.        ontimeout: () => reject(new Error("timeout")),
  88.      });
  89.    });
  90.  }
  91.  
  92.  async function isJdRunning() {
  93.    // The official pattern is to load jdcheck.js and test the "jdownloader" variable. :contentReference[oaicite:6]{index=6}
  94.    // In a userscript, we just fetch it and check for "jdownloader=true".
  95.    try {
  96.      const r = await gmXhr({
  97.        method: "GET",
  98.        url: JD_CHECK_URL,
  99.        timeout: 800,
  100.      });
  101.      if (r.status !== 200) return false;
  102.      return /jdownloader\s*=\s*true/i.test(r.responseText || "");
  103.    } catch {
  104.      return false;
  105.    }
  106.  }
  107.  
  108.  function formEncode(obj) {
  109.    return Object.entries(obj)
  110.      .filter(([, v]) => v !== undefined && v !== null)
  111.      .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
  112.      .join("&");
  113.  }
  114.  
  115.  async function sendToJdFlashgot(urls, pkg) {
  116.    // FlashGot Interface parameters: urls (required), description (required), referer, package, autostart... :contentReference[oaicite:7]{index=7}
  117.    const body = formEncode({
  118.      urls: urls.join("\n"),
  119.      description: urls.map(() => pkg).join("\n"),
  120.      referer: JD_STABLE_REFERER,
  121.      package: pkg,
  122.      autostart: JD_AUTOSTART ? 1 : 0,
  123.    });
  124.  
  125.    return gmXhr({
  126.      method: "POST",
  127.      url: JD_FLASHGOT_URL,
  128.      data: body,
  129.      headers: { "Content-Type": "application/x-www-form-urlencoded" },
  130.      timeout: 15000,
  131.    });
  132.  }
  133.  
  134.  async function sendLinks() {
  135.    const list = gatherLinksRaw();
  136.    if (!list.length) {
  137.      alert("No matching links found on this page.");
  138.      return;
  139.    }
  140.  
  141.    const pkg = getPackageName();
  142.  
  143.    if (await isJdRunning()) {
  144.      try {
  145.        await sendToJdFlashgot(list, pkg);
  146.        alert(`Sent ${list.length} link(s) to JDownloader as package:\n${pkg}`);
  147.        return;
  148.      } catch (e) {
  149.        // fall through to clipboard
  150.        console.warn("JD send failed, falling back to clipboard:", e);
  151.      }
  152.    }
  153.  
  154.    GM_setClipboard(list.join("\n"));
  155.    alert(
  156.      `JDownloader not reachable (or send failed).\nCopied ${list.length} link(s) to clipboard instead.`
  157.    );
  158.  }
  159.  
  160.  function addFloatingButton() {
  161.    const btn = document.createElement("button");
  162.    btn.textContent = "Send to JD";
  163.    btn.style.cssText = `
  164.      position: fixed;
  165.      bottom: 5rem;
  166.      right: 1rem;
  167.      padding: .6rem 1rem;
  168.      font: 14px/1 sans-serif;
  169.      background: #0b6283;
  170.      color: #fff;
  171.      border: none;
  172.      border-radius: 6px;
  173.      box-shadow: 0 2px 5px rgba(0,0,0,.2);
  174.      cursor: pointer;
  175.      z-index: 9999;
  176.      opacity: 0.8;
  177.      transition: opacity .2s ease;
  178.    `;
  179.    btn.addEventListener("mouseenter", () => (btn.style.opacity = "1"));
  180.    btn.addEventListener("mouseleave", () => (btn.style.opacity = "0.8"));
  181.    btn.addEventListener("click", sendLinks);
  182.    document.body.appendChild(btn);
  183.  }
  184.  
  185.  if (typeof GM_registerMenuCommand !== "undefined") {
  186.    GM_registerMenuCommand("Send matched links to JD (or clipboard fallback)", sendLinks);
  187.  }
  188.  
  189.  addFloatingButton();
  190. })();
  191.  
Advertisement
Add Comment
Please, Sign In to add comment