Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Link Copier Helper (JD package + JD detect + fallback)
- // @namespace https://discuss.eroscripts
- // @version 2.0
- // @description Send matching links to JDownloader as a per-page package (no URL rewriting). Fallback to clipboard if JD isn't running.
- // @match *://discuss.eroscripts.com/*
- // @grant GM_setClipboard
- // @grant GM_registerMenuCommand
- // @grant GM_xmlhttpRequest
- // @connect 127.0.0.1
- // @run-at document-idle
- // ==/UserScript==
- (function () {
- "use strict";
- // ---------------- CONFIGURATION ----------------
- const EXTENSIONS = [".mp4", ".webm", ".mov", ".srt", ".ass", ".mkv", ".funscript"];
- 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"];
- // JDownloader ExternInterface endpoints (localhost webserver) :contentReference[oaicite:2]{index=2}
- 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}
- const JD_FLASHGOT_URL = "http://127.0.0.1:9666/flashgot"; // accepts urls/package/referer :contentReference[oaicite:4]{index=4}
- // IMPORTANT: keep referer stable so you can "always allow" once.
- // If you send a different full URL each time, JD may keep asking.
- const JD_STABLE_REFERER = "https://discuss.eroscripts.com/";
- // Optional: bypass LinkGrabber and send straight to Downloads (0/1). :contentReference[oaicite:5]{index=5}
- const JD_AUTOSTART = 0;
- // ------------------------------------------------
- function isExtensionMatch(url) {
- try {
- const pathname = new URL(url, location.href).pathname.toLowerCase();
- return EXTENSIONS.some((ext) => pathname.endsWith(ext));
- } catch {
- return false;
- }
- }
- function isExternal(url) {
- try {
- const u = new URL(url, location.href);
- return (
- u.hostname !== location.hostname &&
- !IGNORE_EXTERNAL.includes(u.hostname.toLowerCase())
- );
- } catch {
- return false;
- }
- }
- function gatherLinksRaw() {
- const links = Array.from(document.querySelectorAll("a[href]"));
- const matches = links
- .map((a) => a.href)
- .filter((href) => isExtensionMatch(href) || isExternal(href));
- return [...new Set(matches)];
- }
- function getPackageName() {
- const t =
- document.querySelector("h1 .fancy-title")?.textContent?.trim() ||
- document.querySelector(".fancy-title")?.textContent?.trim() ||
- document.title.trim();
- const m = location.pathname.match(/\/t\/[^/]+\/(\d+)/);
- const withId = m ? `${t} [${m[1]}]` : t;
- return withId
- .replace(/[<>:"/\\|?*\u0000-\u001F]/g, "_")
- .replace(/\s+/g, " ")
- .trim()
- .slice(0, 150);
- }
- function gmXhr(opts) {
- // Tampermonkey provides GM_xmlhttpRequest
- return new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- ...opts,
- onload: (r) => resolve(r),
- onerror: (e) => reject(e),
- ontimeout: () => reject(new Error("timeout")),
- });
- });
- }
- async function isJdRunning() {
- // The official pattern is to load jdcheck.js and test the "jdownloader" variable. :contentReference[oaicite:6]{index=6}
- // In a userscript, we just fetch it and check for "jdownloader=true".
- try {
- const r = await gmXhr({
- method: "GET",
- url: JD_CHECK_URL,
- timeout: 800,
- });
- if (r.status !== 200) return false;
- return /jdownloader\s*=\s*true/i.test(r.responseText || "");
- } catch {
- return false;
- }
- }
- function formEncode(obj) {
- return Object.entries(obj)
- .filter(([, v]) => v !== undefined && v !== null)
- .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
- .join("&");
- }
- async function sendToJdFlashgot(urls, pkg) {
- // FlashGot Interface parameters: urls (required), description (required), referer, package, autostart... :contentReference[oaicite:7]{index=7}
- const body = formEncode({
- urls: urls.join("\n"),
- description: urls.map(() => pkg).join("\n"),
- referer: JD_STABLE_REFERER,
- package: pkg,
- autostart: JD_AUTOSTART ? 1 : 0,
- });
- return gmXhr({
- method: "POST",
- url: JD_FLASHGOT_URL,
- data: body,
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- timeout: 15000,
- });
- }
- async function sendLinks() {
- const list = gatherLinksRaw();
- if (!list.length) {
- alert("No matching links found on this page.");
- return;
- }
- const pkg = getPackageName();
- if (await isJdRunning()) {
- try {
- await sendToJdFlashgot(list, pkg);
- alert(`Sent ${list.length} link(s) to JDownloader as package:\n${pkg}`);
- return;
- } catch (e) {
- // fall through to clipboard
- console.warn("JD send failed, falling back to clipboard:", e);
- }
- }
- GM_setClipboard(list.join("\n"));
- alert(
- `JDownloader not reachable (or send failed).\nCopied ${list.length} link(s) to clipboard instead.`
- );
- }
- function addFloatingButton() {
- const btn = document.createElement("button");
- btn.textContent = "Send to JD";
- btn.style.cssText = `
- position: fixed;
- bottom: 5rem;
- right: 1rem;
- padding: .6rem 1rem;
- font: 14px/1 sans-serif;
- background: #0b6283;
- color: #fff;
- border: none;
- border-radius: 6px;
- box-shadow: 0 2px 5px rgba(0,0,0,.2);
- cursor: pointer;
- z-index: 9999;
- opacity: 0.8;
- transition: opacity .2s ease;
- `;
- btn.addEventListener("mouseenter", () => (btn.style.opacity = "1"));
- btn.addEventListener("mouseleave", () => (btn.style.opacity = "0.8"));
- btn.addEventListener("click", sendLinks);
- document.body.appendChild(btn);
- }
- if (typeof GM_registerMenuCommand !== "undefined") {
- GM_registerMenuCommand("Send matched links to JD (or clipboard fallback)", sendLinks);
- }
- addFloatingButton();
- })();
Advertisement
Add Comment
Please, Sign In to add comment