F95Zone.to | Link Grabber

This script helps you get download links on F95Zone faster and easier. It adds a small icon next to links to send them to JDownloader 2 or copy them instantly.

Yüklemeden önce, Sleazy Fork, bu betiğin anti-özellikler içerdiğini bilmenizi ister; bunlar sizin değil, betik yazarının yararına olan şeylerdir.

Bu betik ziyaret ettiğiniz sitelere reklam kodu yükler. Script sahibi şöyle açıklıyor: Redirects links through external site to bypass Gofile & Pixeldrain download quotas.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name          F95Zone.to | Link Grabber
// @namespace     https://greasyfork.org/fr/users/1468290-payamarre
// @version       2.1
// @description   This script helps you get download links on F95Zone faster and easier. It adds a small icon next to links to send them to JDownloader 2 or copy them instantly.
// @author        NoOne
// @match         https://f95zone.to/*
// @grant         GM_xmlhttpRequest
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_registerMenuCommand
// @grant         GM_setClipboard
// @grant         GM_notification
// @connect       f95zone.to
// @connect       127.0.0.1
// @connect       localhost
// @license       MIT
// @icon          https://www.google.com/s2/favicons?domain=f95zone.to
// @antifeature  ads  Redirects links through external site to bypass Gofile & Pixeldrain download quotas.
// ==/UserScript==

(function() {
    'use strict';

    const TM_ORIGIN = `moz-extension://${GM_getValue("script_id") || (() => { let id = crypto.randomUUID(); GM_setValue("script_id", id); return id; })()}`;
    const BYPASS = { GOFILE: "https://gf.1drv.eu.org/", PIXEL: "https://cdn.pixeldrain.eu.cc/" };

    const DEFAULT_HOSTS = [
        { id: "bowfile", name: "Bowfile.com", pattern: "bowfile.com" },
        { id: "bunkr", name: "Bunkrrr.org", pattern: "bunkrrr.org" },
        { id: "buzzheavier", name: "Buzzheavier.com", pattern: "buzzheavier.com" },
        { id: "datanodes", name: "Datanodes.to", pattern: "datanodes.to" },
        { id: "google", name: "Drive.google.com", pattern: "drive.google.com" },
        { id: "filesfm", name: "Files.fm", pattern: "files.fm" },
        { id: "gofile", name: "Gofile.io", pattern: "gofile.io" },
        { id: "kraken", name: "Krakenfiles.com", pattern: "krakenfiles.com" },
        { id: "mediafire", name: "Mediafire.com", pattern: "mediafire.com" },
        { id: "mega", name: "Mega.nz", pattern: "mega.nz" },
        { id: "mixdrop", name: "Mixdrop.ag", pattern: "mixdrop.ag" },
        { id: "pixeldrain", name: "Pixeldrain.com", pattern: "pixeldrain.com" },
        { id: "uploadhaven", name: "Uploadhaven.com", pattern: "uploadhaven.com" },
        { id: "uploadnow", name: "Uploadnow.io", pattern: "uploadnow.io" },
        { id: "viking", name: "Vikingfile.com", pattern: "vikingfile.com" },
        { id: "workupload", name: "Workupload.com", pattern: "workupload.com" }
    ].sort((a, b) => a.name.localeCompare(b.name));

    const globalStyle = document.createElement("style");
    globalStyle.textContent = `
        #ug-modal-overlay { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; background: rgba(0, 0, 0, 0.8) !important; display: flex !important; align-items: center !important; justify-content: center !important; z-index: 2147483647 !important; font-family: 'Inter', system-ui, sans-serif !important; backdrop-filter: blur(4px); }
        .ug-modal { border-radius: 16px !important; width: 520px !important; display: flex !important; flex-direction: column !important; max-height: 85vh !important; box-shadow: 0 20px 50px rgba(0,0,0,0.6) !important; overflow: hidden !important; border: 1px solid rgba(128,128,128,0.2) !important; }
        .ug-dark { background: #1c1e26 !important; color: #d1d5db !important; }
        .ug-dark .ug-header { background: #232630 !important; border-bottom: 1px solid #2f3341 !important; }
        .ug-dark .ug-card { background: #232630 !important; border: 1px solid #2f3341 !important; }
        .ug-dark .ug-kbd { background: #1c1e26 !important; border: 1px solid #3f445b !important; color: #58a6ff !important; }
        .ug-dark .ug-input { background: #1c1e26 !important; border: 1px solid #3f445b !important; color: #fff !important; }
        .ug-light { background: #f0f2f5 !important; color: #1f2937 !important; }
        .ug-light .ug-header { background: #ffffff !important; border-bottom: 1px solid #e5e7eb !important; }
        .ug-light .ug-card { background: #ffffff !important; border: 1px solid #e5e7eb !important; }
        .ug-light .ug-kbd { background: #f9fafb !important; border: 1px solid #d1d5db !important; color: #2563eb !important; }
        .ug-light .ug-input { background: #ffffff !important; border: 1px solid #d1d5db !important; color: #111 !important; }
        .ug-header { padding: 18px 24px !important; display: flex !important; justify-content: space-between !important; align-items: center !important; font-weight: 600 !important; }
        .ug-theme-toggle { cursor: pointer !important; opacity: 0.7 !important; transition: 0.2s !important; }
        .ug-theme-toggle:hover { opacity: 1 !important; color: #3b82f6 !important; }
        .ug-content { padding: 24px !important; overflow-y: auto !important; flex: 1 !important; }
        .ug-section { margin-bottom: 28px !important; }
        .ug-label { font-size: 11px !important; color: #6b7280 !important; text-transform: uppercase !important; margin-bottom: 12px !important; display: block; letter-spacing: 1.2px !important; font-weight: 700 !important; }
        .ug-card { border-radius: 12px !important; padding: 16px !important; margin-bottom: 12px !important; }
        .ug-row { display: flex !important; justify-content: space-between !important; align-items: center !important; margin-bottom: 14px !important; gap: 15px !important; }
        .ug-info b { font-size: 14px !important; display: block !important; }
        .ug-info span { font-size: 12px !important; opacity: 0.6 !important; line-height: 1.4 !important; }
        .ug-kbd { padding: 6px 12px !important; border-radius: 8px !important; font-family: ui-monospace, monospace !important; font-weight: 600 !important; font-size: 12px !important; cursor: pointer !important; min-width: 90px !important; text-align: center !important; transition: 0.2s !important; }
        .ug-toggle-label { position: relative !important; display: inline-block !important; width: 40px !important; height: 22px !important; cursor: pointer !important; }
        .ug-toggle-label input { opacity: 0 !important; width: 0 !important; height: 0 !important; }
        .ug-toggle-slider { position: absolute !important; cursor: pointer !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; background-color: #4b5563 !important; border-radius: 20px !important; transition: .3s !important; }
        input:checked + .ug-toggle-slider { background-color: #3b82f6 !important; }
        .ug-toggle-slider:before { position: absolute !important; content: "" !important; height: 16px !important; width: 16px !important; left: 3px !important; bottom: 3px !important; background-color: white !important; border-radius: 50% !important; transition: .3s !important; }
        input:checked + .ug-toggle-slider:before { transform: translateX(18px) !important; }
        .ug-input-bar { display: flex !important; align-items: center !important; position: relative !important; width: 100% !important; margin-bottom: 12px !important; }
        .ug-input { width: 100% !important; border-radius: 24px !important; padding: 12px 50px 12px 20px !important; font-size: 14px !important; outline: none !important; border: 1px solid transparent !important; }
        .ug-add-btn { position: absolute !important; right: 6px !important; width: 34px !important; height: 34px !important; border-radius: 50% !important; background: #3b82f6 !important; color: white !important; border: none !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; }
        .ug-list-item { display: flex !important; align-items: center !important; padding: 12px 8px !important; border-bottom: 1px solid rgba(128,128,128,0.1) !important; gap: 15px !important; }
        .ug-del-btn { color: #ef4444 !important; cursor: pointer !important; border: none !important; background: none !important; font-size: 16px !important; opacity: 0.6 !important; }
        .ug-domain-text { flex: 1 !important; font-size: 14px !important; }
        .ug-disabled { opacity: 0.25 !important; text-decoration: line-through !important; }

        .dlx { cursor:pointer; background:none; border:none; font-size:16px; margin-left:8px; color:#ffffff !important; display:inline-block; transition:color 0.2s; padding:0; vertical-align:middle; text-decoration:none !important; }
        .dlx:hover { color:#ff7300 !important; }
        .dlx-loading { animation: jd-spin 2s infinite linear; }
        .action-success { color:#2ecc71 !important; }
        @keyframes jd-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
    `;
    document.head.appendChild(globalStyle);

    if (!document.getElementById('ug-fa-css')) {
        const fa = document.createElement("link");
        fa.id = 'ug-fa-css'; fa.rel = "stylesheet";
        fa.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css";
        document.head.appendChild(fa);
    }

    function getCleanDomain(url) {
        try { if (url.includes('/masked/')) { const parts = url.split('/masked/'); if (parts[1]) return parts[1].split('/')[0].toLowerCase(); }
        return new URL(url).hostname.replace('www.', '').toLowerCase(); } catch(e) { return null; }
    }

    function refreshAllButtons() {
        document.querySelectorAll('.dlx').forEach(b => b.remove());
        document.querySelectorAll('.dlx-p').forEach(a => a.classList.remove('dlx-p'));
        findLinks();
    }

    function toggleHost(domain) {
        if (!domain) return;
        const defaultHost = DEFAULT_HOSTS.find(h => domain.includes(h.pattern));
        if (defaultHost) {
            const current = GM_getValue("svc_" + defaultHost.id, true);
            GM_setValue("svc_" + defaultHost.id, !current);
            GM_notification({ text: `${defaultHost.name}: ${!current ? 'ENABLED' : 'DISABLED'}`, title: "F95 Grabber", timeout: 2000 });
        } else {
            let list = GM_getValue("custom_list_v2", []);
            const idx = list.findIndex(i => i.domain === domain);
            if (idx !== -1) { list.splice(idx, 1); } else { list.push({ domain: domain, enabled: true }); }
            GM_setValue("custom_list_v2", list);
        }
        refreshAllButtons();
    }

    function createModal() {
        if (document.getElementById('ug-modal-overlay')) return;

        const overlay = document.createElement('div');
        overlay.id = 'ug-modal-overlay';

        const theme = GM_getValue("ug_theme", "dark");
        let shortcut = GM_getValue("hotkey_v3", { alt: true, code: "KeyQ", display: "ALT + Q" });

        const renderHosts = () => DEFAULT_HOSTS.map(h => `
            <div class="ug-list-item">
                <span class="ug-domain-text">${h.name}</span>
                <label class="ug-toggle-label">
                    <input type="checkbox" class="cfg-host-live" data-id="${h.id}" ${GM_getValue("svc_"+h.id, true) ? 'checked' : ''}>
                    <span class="ug-toggle-slider"></span>
                </label>
            </div>`).join('');

        const renderCustom = () => GM_getValue("custom_list_v2", []).map((item, index) => `
            <div class="ug-list-item">
                <button class="ug-del-btn" data-index="${index}"><i class="fa-solid fa-trash-can"></i></button>
                <span class="ug-domain-text ${item.enabled ? '' : 'ug-disabled'}">${item.domain}</span>
                <label class="ug-toggle-label">
                    <input type="checkbox" class="cfg-custom-live" data-index="${index}" ${item.enabled ? 'checked' : ''}>
                    <span class="ug-toggle-slider"></span>
                </label>
            </div>`).join('');

        overlay.innerHTML = `
            <div class="ug-modal ug-${theme}">
                <div class="ug-header">
                    <div><i class="fa-solid fa-gear"></i> F95 Grabber Settings</div>
                    <i id="ug-theme-icon" class="fa-solid ${theme === 'dark' ? 'fa-moon' : 'fa-sun'} ug-theme-toggle"></i>
                </div>
                <div class="ug-content">
                    <div class="ug-section">
                        <span class="ug-label">Automation</span>
                        <div class="ug-card">
                            <div class="ug-row">
                                <div class="ug-info">
                                    <b>Send to JDownloader</b>
                                    <span>Direct transfer to port 9666. If OFF, copies to clipboard.</span>
                                </div>
                                <label class="ug-toggle-label">
                                    <input type="checkbox" id="cfg-jd-live" ${GM_getValue("jdEnabled", false) ? 'checked' : ''}>
                                    <span class="ug-toggle-slider"></span>
                                </label>
                            </div>
                        </div>
                    </div>

                    <div class="ug-section">
                        <span class="ug-label">Interaction</span>
                        <div class="ug-card">
                            <div class="ug-row">
                                <div class="ug-info">
                                    <b>Shortcut</b>
                                    <span>Hover a link and press to toggle custom domain.</span>
                                </div>
                                <div id="jd-shortcut-text" class="ug-kbd">${shortcut.display}</div>
                            </div>
                        </div>
                    </div>

                    <div class="ug-section">
                        <span class="ug-label">Custom Domains</span>
                        <div class="ug-input-bar">
                            <input type="text" id="new-domain" class="ug-input" placeholder="Add domain (e.g. mega.nz)...">
                            <button id="add-domain" class="ug-add-btn"><i class="fa-solid fa-plus"></i></button>
                        </div>
                        <div id="custom-list-container">${renderCustom()}</div>
                    </div>

                    <div class="ug-section">
                        <span class="ug-label">Default File Hosts</span>
                        <div id="default-hosts-container">${renderHosts()}</div>
                    </div>
                </div>
            </div>`;

        document.body.appendChild(overlay);

        overlay.onclick = (e) => { if (e.target === overlay) overlay.remove(); };

        document.getElementById('ug-theme-icon').onclick = function() {
            const newT = GM_getValue("ug_theme", "dark") === 'dark' ? 'light' : 'dark';
            GM_setValue('ug_theme', newT);
            overlay.querySelector('.ug-modal').className = `ug-modal ug-${newT}`;
            this.className = `fa-solid ${newT === 'dark' ? 'fa-moon' : 'fa-sun'} ug-theme-toggle`;
        };

        const shortcutBtn = document.getElementById('jd-shortcut-text');
        shortcutBtn.onclick = () => {
            shortcutBtn.textContent = "...";
            shortcutBtn.style.color = "#fbbf24";
            const recordHandler = (e) => {
                if (["Control", "Alt", "Shift", "Meta"].includes(e.key)) return;
                e.preventDefault();
                const combo = { ctrl: e.ctrlKey, alt: e.altKey, shift: e.shiftKey, code: e.code, display: `${e.ctrlKey ? 'CTRL + ' : ''}${e.altKey ? 'ALT + ' : ''}${e.shiftKey ? 'SHIFT + ' : ''}${e.key.toUpperCase()}` };
                GM_setValue("hotkey_v3", combo);
                shortcutBtn.textContent = combo.display;
                shortcutBtn.style.color = "";
                window.removeEventListener('keydown', recordHandler, true);
            };
            window.addEventListener('keydown', recordHandler, true);
        };

        document.getElementById('cfg-jd-live').onchange = (e) => GM_setValue("jdEnabled", e.target.checked);

        overlay.addEventListener('change', (e) => {
            if (e.target.classList.contains('cfg-host-live')) {
                GM_setValue("svc_" + e.target.dataset.id, e.target.checked);
                refreshAllButtons();
            }
            if (e.target.classList.contains('cfg-custom-live')) {
                let list = GM_getValue("custom_list_v2", []);
                list[e.target.dataset.index].enabled = e.target.checked;
                GM_setValue("custom_list_v2", list);
                e.target.closest('.ug-list-item').querySelector('.ug-domain-text').classList.toggle('ug-disabled', !e.target.checked);
                refreshAllButtons();
            }
        });

        document.getElementById('add-domain').onclick = () => {
            const input = document.getElementById('new-domain');
            const d = getCleanDomain(input.value.trim().includes('://') ? input.value.trim() : 'http://' + input.value.trim());
            if (d) {
                let list = GM_getValue("custom_list_v2", []);
                if (!list.find(i => i.domain === d)) {
                    list.push({ domain: d, enabled: true });
                    GM_setValue("custom_list_v2", list);
                    document.getElementById('custom-list-container').innerHTML = renderCustom();
                    input.value = "";
                    refreshAllButtons();
                }
            }
        };

        overlay.addEventListener('click', (e) => {
            if (e.target.closest('.ug-del-btn')) {
                const idx = e.target.closest('.ug-del-btn').dataset.index;
                let list = GM_getValue("custom_list_v2", []);
                list.splice(idx, 1);
                GM_setValue("custom_list_v2", list);
                document.getElementById('custom-list-container').innerHTML = renderCustom();
                refreshAllButtons();
            }
        });
    }

    GM_registerMenuCommand("F95 Grabber Settings", createModal);

    function handleFinalLink(link, btn) {
        if (GM_getValue("jdEnabled", false)) {
            GM_xmlhttpRequest({
                method: "POST", url: `http://127.0.0.1:9666/flashgot`, data: "urls=" + encodeURIComponent(link),
                headers: { "Content-Type": "application/x-www-form-urlencoded", "Origin": TM_ORIGIN, "Referer": TM_ORIGIN + "/" },
                onload: () => flashSuccess(btn)
            });
        } else { GM_setClipboard(link); flashSuccess(btn); }
    }

    function flashSuccess(btn) {
        const originalContent = btn.innerHTML;
        btn.innerHTML = '<i class="fa-solid fa-check action-success"></i>';
        setTimeout(() => { if(btn) btn.innerHTML = btn.dataset.originalIcon || originalContent; }, 1500);
    }

    function triggerCaptcha(anchor, btn, cb) {
        btn.innerHTML = '<i class="fa-solid fa-circle-exclamation" style="color:#f1c40f;"></i>';
        const w = window.open(anchor.href, "captcha", "width=600,height=800");
        const t = setInterval(() => { if(w && w.closed){ clearInterval(t); btn.innerHTML = '<i class="fa-solid fa-spinner dlx-loading"></i>'; setTimeout(cb, 500); } }, 800);
    }

    function processMasked(anchor, btn, id) {
        btn.innerHTML = '<i class="fa-solid fa-spinner dlx-loading"></i>';
        const makeReq = () => {
            GM_xmlhttpRequest({
                method: "POST", url: anchor.href, headers: {"Content-Type": "application/x-www-form-urlencoded"}, data: "xhr=1&download=1",
                onload: r => {
                    let data; try { data = JSON.parse(r.responseText); } catch(e) { triggerCaptcha(anchor, btn, makeReq); return; }
                    if(!data || !data.msg || /captcha|verify/i.test(data.msg)) { triggerCaptcha(anchor, btn, makeReq); return; }
                    let url = "";
                    if(id === "gofile") { let m = data.msg.match(/gofile\.io\/d\/([\w\d]+)/); if(m) url = BYPASS.GOFILE + m[1]; }
                    else if(id === "pixeldrain") { let m = data.msg.match(/pixeldrain\.com\/u\/([\w\d]+)/); if(m) url = BYPASS.PIXEL + m[1]; }
                    else { let m = data.msg.match(/href="([^"]+)"/i) || data.msg.match(/(https?:\/\/[^\s<>"]+)/i); if(m) url = m[1]; }
                    if(url) {
                        btn.href = url;
                        btn.dataset.originalIcon = '<i class="fa-solid fa-copy"></i>';
                        btn.dataset.bypassed = "true";
                        handleFinalLink(url, btn);
                    } else btn.innerHTML = '<i class="fa-solid fa-skull" style="color:#888;"></i>';
                }
            });
        };
        makeReq();
    }

    function findLinks() {
        const active = [...DEFAULT_HOSTS.filter(h => GM_getValue("svc_"+h.id, true)), ...GM_getValue("custom_list_v2", []).filter(i => i.enabled).map(i => ({ id: "custom", pattern: i.domain }))];
        active.forEach(host => {
            const links = document.querySelectorAll(`a[href*="${host.pattern}"]:not(.dlx):not(.dlx-p)`);
            links.forEach(a => {
                a.classList.add('dlx-p');
                const btn = document.createElement("a");
                btn.className = "dlx"; btn.href = a.href;
                const isMasked = a.href.includes('/masked/');
                const iconHTML = isMasked ? '<i class="fa-solid fa-arrows-rotate"></i>' : '<i class="fa-solid fa-copy"></i>';
                btn.innerHTML = iconHTML; btn.dataset.originalIcon = iconHTML;
                btn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); if(isMasked && !btn.dataset.bypassed) processMasked(a, btn, host.id); else handleFinalLink(btn.href, btn); };
                a.insertAdjacentElement("afterend", btn);
            });
        });
    }

    let hovered = null;
    document.addEventListener('mouseover', e => hovered = e.target.closest('a'));
    window.addEventListener('keydown', e => {
        const hk = GM_getValue("hotkey_v3", { alt: true, code: "KeyQ" });
        if (e.code === hk.code && e.ctrlKey === !!hk.ctrl && e.altKey === !!hk.alt && e.shiftKey === !!hk.shift && hovered) {
            e.preventDefault();
            const d = getCleanDomain(hovered.href);
            if(d) toggleHost(d);
        }
    });

    findLinks();
    const observer = new MutationObserver(() => findLinks());
    observer.observe(document.body, {childList: true, subtree: true});
})();