Stash Check

Combines Kemono Browser and Leak Finder Overlay with verified archive checkers and query engines from the FMHY NSFW Leaks list.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

Advertisement:

// ==UserScript==
// @name         Stash Check
// @namespace    stash-check
// @version      1.0
// @description  Combines Kemono Browser and Leak Finder Overlay with verified archive checkers and query engines from the FMHY NSFW Leaks list.
// @author       brainbaker
// @license      MIT
// @match        *://*.patreon.com/*
// @match        *://*.fanbox.cc/*
// @match        *://*.pixiv.net/*
// @match        *://*.discord.com/*
// @match        *://*.fantia.jp/*
// @match        *://*.boosty.to/*
// @match        *://*.dlsite.com/*
// @match        *://*.gumroad.com/*
// @match        *://*.subscribestar.com/*
// @match        *://*.subscribestar.adult/*
// @match        *://*.onlyfans.com/*
// @match        *://*.fansly.com/*
// @match        *://*.candfans.com/*
// @match        *://*.fantrie.com/*
// @connect      kemono.cr
// @connect      coomer.st
// @connect      pawchive.st
// @connect      pawchive.pw
// @connect      fansly.com
// @connect      fapello.com
// @connect      leaknudes.com
// @connect      hotleak.vip
// @connect      nudostar.tv
// @connect      faponic.com
// @grant        GM.xmlHttpRequest
// @grant        GM_xmlhttpRequest
// @grant        GM.openInTab
// @grant        GM_openInTab
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-idle
// ==/UserScript==

(function () {
    'use strict';

    // --- Polyfills & Helpers ---
    const gmXHR = (typeof GM !== 'undefined' && GM.xmlHttpRequest) ? GM.xmlHttpRequest : (typeof GM_xmlhttpRequest !== 'undefined' ? GM_xmlhttpRequest : null);
    const gmOpenTab = (typeof GM !== 'undefined' && GM.openInTab) ? GM.openInTab : (typeof GM_openInTab !== 'undefined' ? GM_openInTab : window.open);

    if (!gmXHR) {
        console.error('[Stash Check] GM.xmlHttpRequest is required for live status verification.');
    }

    const MAX_RETRIES = 2;
    const REQUEST_TIMEOUT = 10000;
    let currentCheckSessionId = 0;

    // SFW Favoring platforms
    const SFW_PLATFORMS = ['patreon', 'discord', 'fanbox', 'pixiv', 'fantia', 'boosty', 'dlsite', 'gumroad', 'subscribestar'];

    // --- Utility Functions ---
    function select(query, attribute = null) {
        const el = document.querySelector(query);
        return attribute ? el?.getAttribute(attribute) : el?.innerHTML;
    }

    function extract(string, prefix, suffix) {
        if (string == null) return null;
        let begIndex = string.indexOf(prefix);
        if (begIndex === -1) return null;
        else begIndex += prefix.length;
        let endIndex = string.indexOf(suffix, begIndex);
        if (endIndex === -1) endIndex = undefined;
        return string.slice(begIndex, endIndex);
    }

    function extractNextUrlPath(prefix, suffix = "/") {
        return extract(window.location.pathname, (prefix === "/") ? "/" : `/${prefix}/`, suffix);
    }

    function concatOrFalsy(...args) {
        for (let arg of args) {
            if (!arg) return arg;
        }
        return args.join("");
    }

    function compileURL({ domains, service, userID = null, postID = null } = {}) {
        let obj = {};
        for (let domain of domains) {
            let redirectURL = `https://${domain}/${service}`;
            if (userID) {
                redirectURL += `/user/${userID}`;
                if (postID) {
                    redirectURL += `/post/${postID}`;
                }
            } else continue;
            obj[domain.split(".").at(-2).toUpperCase()] = redirectURL;
        }
        return obj;
    }

    // --- Platform Extraction Methods ---
    function extractURLFromPatreon() {
        return compileURL({
            domains: ["kemono.cr", "pawchive.st"],
            service: "patreon",
            userID: extract(select("#__NEXT_DATA__"), '"creator":{"data":{"id":"', '"') ??
                extract(select("body"), '\\"https://www.patreon.com/api/user/', "\\"),
            postID: extractNextUrlPath("posts")?.split("-")?.at(-1)
        });
    }

    function extractURLFromFanbox() {
        return compileURL({
            domains: ["kemono.cr", "pawchive.st"],
            service: "fanbox",
            userID: extract(select('meta[property="og:image"]', "content"), "/creator/", "/") ??
                extract(select(".styled__StyledUserIcon-sc-1upaq18-10[style]", "style"), "/user/", "/") ??
                extract(select('a[href^="https://www.pixiv.net/users/"]', "href"), "/users/", "/"),
            postID: extractNextUrlPath("posts")
        });
    }

    function extractURLFromPixiv() {
        return compileURL({
            domains: ["kemono.cr", "pawchive.st"],
            service: "fanbox",
            userID: extractNextUrlPath("users") ??
                select("button[data-gtm-user-id]", "data-gtm-user-id") ??
                select("a.user-details-icon[href]", "href")?.split("/").at(-1)
        });
    }

    function extractURLFromDiscord() {
        const pathname = window.location.pathname.split("/");
        const serverID = /\d/.test(pathname[2]) ? `${pathname[2]}/${pathname?.[3] ?? ""}` : null;
        return serverID ? {
            "KEMONO": `https://kemono.cr/discord/server/${serverID}`
        } : {};
    }

    function extractURLFromFantia() {
        return compileURL({
            domains: ["kemono.cr"],
            service: "fantia",
            userID: extractNextUrlPath("fanclubs") ?? extract(select(".fanclub-header > a[href]", "href"), "/fanclubs/", "/"),
            postID: extractNextUrlPath("posts") ?? extractNextUrlPath("products")
        });
    }

    function extractURLFromBoosty() {
        return compileURL({
            domains: ["kemono.cr"],
            service: "boosty",
            userID: extractNextUrlPath("/"),
            postID: extractNextUrlPath("posts")
        });
    }

    function extractURLFromDlsite() {
        return compileURL({
            domains: ["kemono.cr"],
            service: "dlsite",
            userID: extractNextUrlPath("maker_id", ".html") ?? extract(select(".maker_name[itemprop=brand] > a", "href"), "/maker_id/", "."),
            postID: concatOrFalsy("RE", extractNextUrlPath("product_id")?.replace(/\D/g, ""))
        });
    }

    function extractURLFromGumroad() {
        const json = select("div#app[data-page]", "data-page");
        return compileURL({
            domains: ["kemono.cr"],
            service: "gumroad",
            userID: extract(json, '"external_id":"', '"') ?? extract(json, '"seller":{"id":"', '"'),
            postID: select('meta[property="product:retailer_item_id"]', "content")
        });
    }

    function extractURLFromSubscribeStar() {
        return compileURL({
            domains: ["kemono.cr"],
            service: "subscribestar",
            userID: select('img[data-type="avatar"][alt]', "alt")?.toLowerCase(),
            postID: extractNextUrlPath("posts")
        });
    }

    function extractURLFromOnlyFans() {
        return compileURL({
            domains: ["coomer.st"],
            service: "onlyfans",
            userID: select("#content .g-avatar[href]", "href")?.split("/")[1],
            postID: select("div.b-post:not(.is-not-post-page)", "id")?.replace(/\D/g, "")
        });
    }

    async function extractURLFromFansly() {
        let userID = null;
        const userName = select("div.feed-item-name a.username-wrapper", "href")?.split("/").at(-1) ??
            select("meta[property='og:title']", "content")?.slice(10) ??
            window.location.pathname.match(/^\/(?:profile\/)?([^/]+)/)?.[1];

        if (userName && !['explore', 'home', 'notifications', 'messages', 'settings'].includes(userName)) {
            try {
                const res = await new Promise((resolve) => {
                    makeRequest({ method: "GET", url: `https://apiv3.fansly.com/api/v1/account?usernames=${userName}`, onload: resolve, onerror: () => resolve(null) });
                });
                if (res && res.status === 200) {
                    userID = JSON.parse(res.responseText)?.response?.[0]?.id ?? null;
                }
            } catch (e) {}
        }
        return compileURL({
            domains: ["coomer.st"],
            service: "fansly",
            userID: userID ?? userName,
            postID: extractNextUrlPath("post")
        });
    }

    function extractURLFromCandFans() {
        return compileURL({
            domains: ["coomer.st"],
            service: "candfans",
            userID: extract(select("meta[property='og:image']", "content"), "/user/", "/") ?? window.location.pathname.split("/")[1],
            postID: extractNextUrlPath("show")
        });
    }

    function extractURLFromFantrie() {
        const ignored = ['my', 'home', 'settings', 'notifications', 'lists', 'chats', 'bookmarks', 'explore'];
        const parts = window.location.pathname.split('/');
        const username = (parts.length > 1 && parts[1] && !ignored.includes(parts[1])) ? parts[1] : null;
        return compileURL({
            domains: ["coomer.st"],
            service: "onlyfans",
            userID: username
        });
    }

    // --- Clean Display Name Extractor ---
    function getCleanDisplayName(service, handle) {
        let name = handle;
        if (service === 'patreon') {
            const h1 = document.querySelector('h1')?.textContent?.trim();
            if (h1 && h1.length < 40) return h1;
            const ogTitle = select('meta[property="og:title"]', 'content');
            if (ogTitle) {
                let clean = ogTitle.replace(/^Get more from\s+/i, '').replace(/\s+on Patreon$/i, '').replace(/\s*\|\s*Patreon$/i, '').trim();
                if (clean && clean.length < 35) return clean;
            }
            const parts = window.location.pathname.split('/').filter(p => p && !['posts', 'about', 'membership', 'collection', 'community', 'home', 'user'].includes(p.toLowerCase()));
            if (parts.length > 0 && !/\d{6,}/.test(parts[0])) return parts[0];
        } else if (service === 'gumroad') {
            const sub = window.location.hostname.split('.')[0];
            if (sub && !['www', 'app', 'gumroad', 'discover'].includes(sub.toLowerCase())) {
                return sub;
            }
            const ogTitle = select('meta[property="og:title"]', 'content');
            if (ogTitle) {
                let clean = ogTitle.replace(/^(Subscribe to|Buy from|Support|Follow)\s+/i, '').split('|')[0].trim();
                if (clean && clean.length < 30) return clean;
            }
        } else if (service === 'fanbox') {
            const h1 = document.querySelector('h1')?.textContent?.trim();
            if (h1 && h1.length < 35) return h1;
        }
        return name || service;
    }

    // --- Inject Styles ---
    function injectStyles() {
        if (document.getElementById('klf-ultimate-styles')) return;
        const style = document.createElement('style');
        style.id = 'klf-ultimate-styles';
        style.textContent = `
            #klf-overlay {
                position: fixed;
                bottom: 20px;
                right: 20px;
                width: 290px;
                background-color: #161618;
                border: 1px solid #38383e;
                border-radius: 10px;
                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.75);
                color: #f0f0f0;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                font-size: 13px;
                z-index: 9999999;
                overflow: hidden;
                transition: width 0.2s ease, opacity 0.2s ease;
                user-select: none;
            }
            #klf-overlay-header {
                padding: 10px 12px;
                background: linear-gradient(135deg, #28282e, #1f1f24);
                border-bottom: 1px solid #38383e;
                cursor: grab;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }
            #klf-overlay-header:active {
                cursor: grabbing;
            }
            #klf-overlay-header .klf-title {
                margin: 0;
                font-size: 13px;
                font-weight: 700;
                color: #fff;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                display: flex;
                align-items: center;
                gap: 6px;
            }
            #klf-overlay-header .klf-controls {
                display: flex;
                align-items: center;
                gap: 8px;
            }
            .klf-nsfw-toggle {
                display: flex;
                align-items: center;
                gap: 4px;
                font-size: 10px;
                font-weight: 700;
                color: #ffaa00;
                cursor: pointer;
                background: rgba(255, 170, 0, 0.15);
                padding: 2px 6px;
                border-radius: 4px;
                border: 1px solid rgba(255, 170, 0, 0.35);
                text-transform: uppercase;
                letter-spacing: 0.3px;
            }
            .klf-nsfw-toggle input {
                cursor: pointer;
                margin: 0;
            }
            #klf-overlay-header .klf-btn-icon {
                background: rgba(255, 255, 255, 0.1);
                border: none;
                border-radius: 4px;
                color: #ddd;
                width: 22px;
                height: 22px;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                font-size: 11px;
                transition: background 0.2s, color 0.2s;
            }
            #klf-overlay-header .klf-btn-icon:hover {
                background: #ff4757;
                color: #fff;
            }
            #klf-overlay-body {
                max-height: 420px;
                overflow-y: auto;
                transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            }
            #klf-overlay.collapsed #klf-overlay-body {
                max-height: 0 !important;
                overflow: hidden;
            }
            #klf-overlay-body::-webkit-scrollbar {
                width: 5px;
            }
            #klf-overlay-body::-webkit-scrollbar-track {
                background: #1a1a1c;
            }
            #klf-overlay-body::-webkit-scrollbar-thumb {
                background: #444;
                border-radius: 3px;
            }
            .klf-section {
                border-bottom: 1px solid #28282e;
            }
            .klf-section:last-child {
                border-bottom: none;
            }
            .klf-section-title {
                padding: 6px 12px;
                background-color: #1e1e22;
                font-size: 11px;
                font-weight: 600;
                text-transform: uppercase;
                letter-spacing: 0.5px;
                color: #999;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }
            .klf-list {
                list-style: none;
                padding: 4px 8px;
                margin: 0;
            }
            .klf-list li {
                margin: 2px 0;
            }
            .klf-list li.klf-hidden-sfw {
                display: none !important;
            }
            .klf-list a {
                text-decoration: none;
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 6px 8px;
                border-radius: 6px;
                color: #ccc;
                transition: background 0.15s, color 0.15s, transform 0.1s;
            }
            .klf-list a:hover {
                background-color: #2a2a30;
                color: #fff;
                transform: translateX(2px);
            }
            .klf-site-name {
                display: flex;
                align-items: center;
                gap: 6px;
                font-weight: 500;
            }
            .klf-note {
                font-size: 10px;
                color: #888;
            }
            .klf-badge {
                padding: 2px 6px;
                border-radius: 4px;
                font-size: 10px;
                font-weight: 700;
                text-transform: uppercase;
                letter-spacing: 0.3px;
            }
            .klf-badge.found {
                background-color: rgba(46, 213, 115, 0.2);
                color: #2ed573;
                border: 1px solid rgba(46, 213, 115, 0.4);
            }
            .klf-badge.incomplete {
                background-color: rgba(255, 171, 0, 0.2);
                color: #ffab00;
                border: 1px solid rgba(255, 171, 0, 0.4);
            }
            .klf-badge.not-found, .klf-badge.missing {
                background-color: rgba(255, 71, 87, 0.15);
                color: #ff4757;
                border: 1px solid rgba(255, 71, 87, 0.3);
            }
            .klf-badge.pending {
                background-color: rgba(164, 176, 190, 0.15);
                color: #a4b0be;
                border: 1px solid rgba(164, 176, 190, 0.3);
            }
            .klf-badge.search {
                background-color: rgba(30, 144, 255, 0.15);
                color: #1e90ff;
                border: 1px solid rgba(30, 144, 255, 0.3);
            }
            .klf-toolbar {
                padding: 8px 10px;
                background-color: #1c1c20;
                border-top: 1px solid #28282e;
                display: flex;
                gap: 6px;
            }
            .klf-btn {
                flex: 1;
                background: #2f3542;
                color: #fff;
                border: 1px solid #444;
                border-radius: 5px;
                padding: 6px 4px;
                font-size: 11px;
                font-weight: 600;
                cursor: pointer;
                text-align: center;
                transition: background 0.2s, border-color 0.2s;
            }
            .klf-btn:hover {
                background: #3742fa;
                border-color: #5352ed;
            }
        `;
        document.head.appendChild(style);
    }

    // --- Overlay UI Creation & Dragging ---
    function createOrGetOverlay(displayName, service) {
        injectStyles();
        let overlay = document.getElementById('klf-overlay');
        const isSfwPlatform = SFW_PLATFORMS.includes((service || '').toLowerCase());

        // On SFW platforms, always default NSFW option to unchecked/disabled on page load
        let showNsfw = isSfwPlatform ? false : true;

        if (!overlay) {
            overlay = document.createElement('div');
            overlay.id = 'klf-overlay';
            overlay.className = 'collapsed';
            overlay.innerHTML = `
                <div id="klf-overlay-header">
                    <h3 class="klf-title"><span id="klf-service-icon">⚡</span> <span id="klf-username-display">${displayName}</span></h3>
                    <div class="klf-controls">
                        <button class="klf-btn-icon" id="klf-toggle-collapse" title="Toggle Expand">◀</button>
                    </div>
                </div>
                <div id="klf-overlay-body">
                    <div class="klf-section" id="klf-sec-live">
                        <div class="klf-section-title">
                            <span>Live Leak Checks</span>
                            <label class="klf-nsfw-toggle" id="klf-nsfw-holder" style="display: none;" title="Toggle adult/NSFW leak sources">
                                <input type="checkbox" id="klf-nsfw-checkbox" ${showNsfw ? 'checked' : ''}>
                                <span>NSFW</span>
                            </label>
                        </div>
                        <ul class="klf-list" id="klf-list-live"></ul>
                    </div>
                    <div class="klf-section" id="klf-sec-search">
                        <div class="klf-section-title">
                            <span>Other Sites & Forums</span>
                        </div>
                        <ul class="klf-list" id="klf-list-search"></ul>
                    </div>
                </div>
                <div class="klf-toolbar">
                    <button class="klf-btn" id="klf-btn-open-found" title="Open all verified/found links in new tabs">Open All Found</button>
                    <button class="klf-btn" id="klf-btn-search-all" title="Open all search query links in new tabs">Search All Sites</button>
                </div>
            `;
            document.body.appendChild(overlay);

            const header = overlay.querySelector('#klf-overlay-header');
            const toggleBtn = overlay.querySelector('#klf-toggle-collapse');
            const nsfwCheckbox = overlay.querySelector('#klf-nsfw-checkbox');

            nsfwCheckbox.addEventListener('change', (e) => {
                applySfwFilter(service);
            });

            header.addEventListener('click', (e) => {
                if (e.target.closest('button') || e.target.closest('label') || e.target.closest('input')) return;
                if (header._didMove) return;
                overlay.classList.toggle('collapsed');
                toggleBtn.textContent = overlay.classList.contains('collapsed') ? '◀' : '▼';
            });

            toggleBtn.addEventListener('click', () => {
                overlay.classList.toggle('collapsed');
                toggleBtn.textContent = overlay.classList.contains('collapsed') ? '◀' : '▼';
            });

            overlay.querySelector('#klf-btn-open-found').addEventListener('click', () => {
                const foundLinks = overlay.querySelectorAll('#klf-list-live li:not(.klf-hidden-sfw) a[data-status="found"], #klf-list-live li:not(.klf-hidden-sfw) a[data-status="incomplete"]');
                if (foundLinks.length === 0) {
                    alert('No verified found leaks yet!');
                    return;
                }
                foundLinks.forEach((a) => {
                    if (a.href) gmOpenTab(a.href, true);
                });
            });

            overlay.querySelector('#klf-btn-search-all').addEventListener('click', () => {
                const searchLinks = overlay.querySelectorAll('#klf-list-search li:not(.klf-hidden-sfw) a');
                if (searchLinks.length === 0) {
                    alert('No search links visible!');
                    return;
                }
                searchLinks.forEach((a) => {
                    if (a.href) gmOpenTab(a.href, true);
                });
            });

            makeDraggable(overlay, header);
        } else {
            document.getElementById('klf-username-display').textContent = displayName;
            document.getElementById('klf-list-live').innerHTML = '';
            document.getElementById('klf-list-search').innerHTML = '';
            const nsfwCheckbox = document.getElementById('klf-nsfw-checkbox');
            if (nsfwCheckbox) nsfwCheckbox.checked = showNsfw;
        }

        const nsfwHolder = document.getElementById('klf-nsfw-holder');
        if (nsfwHolder) {
            nsfwHolder.style.display = isSfwPlatform ? 'flex' : 'none';
        }

        overlay.style.display = 'block';
        return overlay;
    }

    function makeDraggable(overlay, header) {
        let isDragging = false;
        let startX, startY, initialRight, initialBottom;

        header.addEventListener('mousedown', (e) => {
            if (e.target.closest('button') || e.target.closest('label') || e.target.closest('input')) return;
            isDragging = true;
            header._didMove = false;
            startX = e.clientX;
            startY = e.clientY;
            const rect = overlay.getBoundingClientRect();
            initialRight = window.innerWidth - rect.right;
            initialBottom = window.innerHeight - rect.bottom;
            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
                header._didMove = true;
            }
            let newRight = initialRight - dx;
            let newBottom = initialBottom - dy;

            newRight = Math.max(10, Math.min(window.innerWidth - overlay.offsetWidth - 10, newRight));
            newBottom = Math.max(10, Math.min(window.innerHeight - overlay.offsetHeight - 10, newBottom));

            overlay.style.right = `${newRight}px`;
            overlay.style.bottom = `${newBottom}px`;
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
        });
    }

    function applySfwFilter(service) {
        const isSfwPlatform = SFW_PLATFORMS.includes((service || '').toLowerCase());
        const checkbox = document.getElementById('klf-nsfw-checkbox');
        const showNsfw = checkbox ? checkbox.checked : !isSfwPlatform;

        const allListItems = document.querySelectorAll('#klf-list-live li, #klf-list-search li');
        allListItems.forEach(li => {
            const a = li.querySelector('a');
            const siteKey = a ? (a.dataset.sitekey || '') : '';
            if (isSfwPlatform && !showNsfw) {
                if (siteKey.startsWith('kemono') || siteKey.startsWith('pawchive')) {
                    li.classList.remove('klf-hidden-sfw');
                } else {
                    li.classList.add('klf-hidden-sfw');
                }
            } else {
                li.classList.remove('klf-hidden-sfw');
            }
        });

        const searchSec = document.getElementById('klf-sec-search');
        if (searchSec) {
            const visibleSearch = searchSec.querySelectorAll('li:not(.klf-hidden-sfw)').length;
            searchSec.style.display = visibleSearch > 0 ? 'block' : 'none';
        }

        const searchAllBtn = document.getElementById('klf-btn-search-all');
        if (searchAllBtn) {
            searchAllBtn.style.display = (isSfwPlatform && !showNsfw) ? 'none' : 'block';
        }
    }

    function addLinkItem(sectionId, siteName, url, note = '', initialStatus = 'search', badgeText = null) {
        const list = document.getElementById(`klf-list-${sectionId}`);
        if (!list) return null;

        const li = document.createElement('li');
        const a = document.createElement('a');
        const siteKey = siteName.toLowerCase().replace(/[^a-z0-9]/g, '') + (note ? '_' + note.toLowerCase().replace(/[^a-z0-9]/g, '') : '');

        a.href = url;
        a.target = '_blank';
        a.rel = 'noopener noreferrer';
        a.dataset.sitekey = siteKey;
        a.dataset.status = initialStatus;

        const displayNote = note ? ` <span class="klf-note">(${note})</span>` : '';
        const badge = badgeText || initialStatus.toUpperCase();

        a.innerHTML = `
            <span class="klf-site-name">${siteName}${displayNote}</span>
            <span class="klf-badge ${initialStatus}">${badge}</span>
        `;

        a.addEventListener('click', (e) => {
            if (e.ctrlKey) {
                e.preventDefault();
                gmOpenTab(url, true);
            }
        });

        li.appendChild(a);
        list.appendChild(li);
        return { a, li };
    }

    function updateLinkStatus(siteName, note, status, customBadgeText = null) {
        const siteKey = siteName.toLowerCase().replace(/[^a-z0-9]/g, '') + (note ? '_' + note.toLowerCase().replace(/[^a-z0-9]/g, '') : '');
        const a = document.querySelector(`a[data-sitekey="${siteKey}"]`);
        if (a) {
            a.dataset.status = status;
            const badge = a.querySelector('.klf-badge');
            if (badge) {
                badge.className = `klf-badge ${status}`;
                badge.textContent = customBadgeText || status.toUpperCase();
            }
        }
    }

    function makeRequest(details, retryCount = 0, sessionId = currentCheckSessionId) {
        if (!gmXHR) return;
        gmXHR({
            method: details.method || 'GET',
            url: details.url,
            headers: details.headers || { 'Accept': 'text/css' },
            timeout: REQUEST_TIMEOUT,
            onload(res) {
                if (sessionId !== currentCheckSessionId) return;
                if (details.onload) details.onload(res);
            },
            onerror(err) {
                if (sessionId !== currentCheckSessionId) return;
                if (retryCount < MAX_RETRIES) {
                    setTimeout(() => makeRequest(details, retryCount + 1, sessionId), 1000);
                } else if (details.onerror) {
                    details.onerror(err);
                }
            },
            ontimeout() {
                if (sessionId !== currentCheckSessionId) return;
                if (retryCount < MAX_RETRIES) {
                    setTimeout(() => makeRequest(details, retryCount + 1, sessionId), 1000);
                } else if (details.onerror) {
                    details.onerror({ status: 408 });
                }
            }
        });
    }

    function verifyArchiveEndpoint(siteName, url, note, sessionId) {
        try {
            const parsedUrl = new URL(url);
            let checkUrl = null;
            let isPost = parsedUrl.pathname.includes('/post/');

            if (parsedUrl.pathname.startsWith('/discord/server/')) {
                const serverId = parsedUrl.pathname.split('/')[3];
                checkUrl = `https://${parsedUrl.hostname}/api/v1/discord/server/${serverId}`;
            } else {
                checkUrl = `https://${parsedUrl.hostname}/api/v1${parsedUrl.pathname}${isPost ? '' : '/profile'}`;
            }

            makeRequest({
                method: 'GET',
                url: checkUrl,
                headers: { 'Accept': 'text/css' },
                onload(res) {
                    if (sessionId !== currentCheckSessionId) return;
                    if (parsedUrl.pathname.startsWith('/discord/server/')) {
                        if (res.status === 200) {
                            updateLinkStatus(siteName, note, 'found', 'FOUND');
                        } else {
                            updateLinkStatus(siteName, note, 'missing', 'MISSING');
                        }
                    } else {
                        if (res.status === 200 || res.status === 202) {
                            updateLinkStatus(siteName, note, isPost ? 'incomplete' : 'found', isPost ? 'POST FOUND' : 'FOUND');
                        } else if (res.status === 404) {
                            updateLinkStatus(siteName, note, 'missing', 'NOT FOUND');
                        } else {
                            updateLinkStatus(siteName, note, 'missing', `ERR ${res.status}`);
                        }
                    }
                },
                onerror() { updateLinkStatus(siteName, note, 'missing', 'OFFLINE'); }
            }, 0, sessionId);
        } catch (e) {
            updateLinkStatus(siteName, note, 'missing', 'ERROR');
        }
    }

    // --- Live Leak Checks: kemono, coomer, hotleak, nudostar, fapello, leaknudes, faponic ---
    function runLiveLeakChecks(handle, service, archives, sessionId) {
        // 1. Kemono (STRICTLY only for Kemono platforms: patreon, discord, fantia, boosty, gumroad, subscribestar, fanbox, pixiv, dlsite)
        if (archives["KEMONO"]) {
            addLinkItem('live', 'Kemono', archives["KEMONO"], '', 'pending', 'CHECKING');
            verifyArchiveEndpoint('Kemono', archives["KEMONO"], '', sessionId);
        }
        if (archives["PAWCHIVE"]) {
            addLinkItem('live', 'Pawchive', archives["PAWCHIVE"], '', 'pending', 'CHECKING');
            makeRequest({
                method: 'GET',
                url: archives["PAWCHIVE"],
                headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' },
                onload(res) {
                    const finalUrl = res.finalUrl || '';
                    const isFound = res.status === 200 && !finalUrl.includes('/artists');
                    updateLinkStatus('Pawchive', '', isFound ? 'found' : 'missing', isFound ? 'FOUND' : 'MISSING');
                }
            }, 0, sessionId);
        }

        // 2. Coomer (STRICTLY only for Coomer platforms: onlyfans, fansly, candfans, fantrie)
        if (archives["COOMER"]) {
            addLinkItem('live', 'Coomer', archives["COOMER"], '', 'pending', 'CHECKING');
            verifyArchiveEndpoint('Coomer', archives["COOMER"], '', sessionId);
        }

        // 3. Hotleak
        const hotleakUrl = `https://hotleak.vip/${handle}`;
        addLinkItem('live', 'Hotleak', hotleakUrl, '', 'pending', 'CHECKING');
        makeRequest({
            method: 'HEAD',
            url: hotleakUrl,
            onload(res) {
                updateLinkStatus('Hotleak', '', res.status === 200 ? 'found' : 'not-found', res.status === 200 ? 'FOUND' : 'NOT FOUND');
            },
            onerror() { updateLinkStatus('Hotleak', '', 'not-found', 'OFFLINE'); }
        }, 0, sessionId);

        // 4. Nudostar
        const nudostarUrl = `https://nudostar.tv/models/${handle}/`;
        addLinkItem('live', 'Nudostar', nudostarUrl, '', 'pending', 'CHECKING');
        makeRequest({
            method: 'HEAD',
            url: nudostarUrl,
            onload(res) {
                updateLinkStatus('Nudostar', '', res.status === 200 ? 'found' : 'not-found', res.status === 200 ? 'FOUND' : 'NOT FOUND');
            },
            onerror() { updateLinkStatus('Nudostar', '', 'not-found', 'OFFLINE'); }
        }, 0, sessionId);

        // 5. Fapello
        const fapelloUrl = `https://fapello.com/${handle}/`;
        addLinkItem('live', 'Fapello', fapelloUrl, '', 'pending', 'CHECKING');
        makeRequest({
            method: 'GET',
            url: fapelloUrl,
            onload(res) {
                const isFound = res.status === 200 && (res.finalUrl || '').includes(handle);
                updateLinkStatus('Fapello', '', isFound ? 'found' : 'not-found', isFound ? 'FOUND' : 'NOT FOUND');
            },
            onerror() { updateLinkStatus('Fapello', '', 'not-found', 'OFFLINE'); }
        }, 0, sessionId);

        if (handle.includes('_')) {
            const altUser = handle.replace(/_/g, '-');
            const fapelloAltUrl = `https://fapello.com/${altUser}/`;
            addLinkItem('live', 'Fapello', fapelloAltUrl, 'Alt Handle', 'pending', 'CHECKING');
            makeRequest({
                method: 'GET',
                url: fapelloAltUrl,
                onload(res) {
                    const isFound = res.status === 200 && (res.finalUrl || '').includes(altUser);
                    updateLinkStatus('Fapello', 'Alt Handle', isFound ? 'found' : 'not-found', isFound ? 'FOUND' : 'NOT FOUND');
                }
            }, 0, sessionId);
        }

        // 6. LeakNudes
        const leakNudesUrl = `https://leaknudes.com/model/${handle}`;
        addLinkItem('live', 'LeakNudes', leakNudesUrl, '', 'pending', 'CHECKING');
        makeRequest({
            method: 'GET',
            url: leakNudesUrl,
            onload(res) {
                const isFound = res.status === 200 && res.finalUrl === leakNudesUrl;
                updateLinkStatus('LeakNudes', '', isFound ? 'found' : 'not-found', isFound ? 'FOUND' : 'NOT FOUND');
            },
            onerror() { updateLinkStatus('LeakNudes', '', 'not-found', 'OFFLINE'); }
        }, 0, sessionId);

        // 7. Faponic
        const faponicUrl = `https://faponic.com/${handle}/`;
        addLinkItem('live', 'Faponic', faponicUrl, '', 'pending', 'CHECKING');
        makeRequest({
            method: 'GET',
            url: faponicUrl,
            onload(res) {
                const isFound = res.status === 200 && !(res.responseText || '').includes('<title>404 | Faponic</title>');
                updateLinkStatus('Faponic', '', isFound ? 'found' : 'not-found', isFound ? 'FOUND' : 'NOT FOUND');
            },
            onerror() { updateLinkStatus('Faponic', '', 'not-found', 'OFFLINE'); }
        }, 0, sessionId);
    }

    // --- Populate Other Sites & Forums ---
    function populateOtherSitesAndForums(handle) {
        const u = encodeURIComponent(handle);
        
        const searchSites = [
            { name: 'Bunkr Albums', url: `https://balbums.st/?search=${u}`, note: 'Album Query' },
            { name: 'TopFapGirls', url: `https://www.topfapgirls1.com/search/?q=${u}`, note: 'Search' },
            { name: 'EroThots', url: `https://erothots.co/search/?q=${u}`, note: 'Media Query' },
            { name: 'OSosedki', url: `https://ososedki.com/search?q=${u}`, note: 'Archive Query' },
            { name: 'RedditPlug', url: `https://redditplug.com/?s=${u}`, note: 'Mega/Bypass' },
            { name: 'TurboVid Library', url: `https://turbovid.cr/library?q=${u}`, note: 'Video Query' },
            { name: 'xxxTube', url: `https://x-x-x.tube/search/${u}/`, note: 'Tube Query' },
            { name: 'ThotHub', url: `https://thothub.to/search/${u}/`, note: 'KVS Query' },
            { name: 'NobodyHome', url: `https://nobodyhome.tv/index.php?search=${u}`, note: 'Protected Query' },
            { name: 'OnlyFans421', url: `https://duckduckgo.com/?q=site%3Arentry.org+${u}`, note: 'Rentry Bypass' },
            
            // Forums requiring signup placed at the lowest positions
            { name: 'SimpCity Forum', url: `https://simpcity.cr/search/search?keywords=${u}`, note: 'Requires Signup' },
            { name: 'ViperGirls Forum', url: `https://vipergirls.to/search.php?do=process&query=${u}`, note: 'Requires Signup' }
        ];

        searchSites.forEach(site => {
            addLinkItem('search', site.name, site.url, site.note, 'search', 'SEARCH');
        });
    }

    // --- Detect Creator Data & Archives ---
    async function getCreatorData() {
        const host = window.location.hostname;
        let archives = {};
        let handle = null;
        let service = null;

        if (host.includes('onlyfans.com') || host.includes('fantrie.com')) {
            service = host.includes('onlyfans') ? 'onlyfans' : 'fantrie';
            archives = extractURLFromOnlyFans();
            if (service === 'fantrie') {
                archives = extractURLFromFantrie();
            }
            const ignored = ['my', 'home', 'settings', 'notifications', 'lists', 'chats', 'bookmarks', 'explore', 'action'];
            const parts = window.location.pathname.split('/');
            handle = (parts.length > 1 && parts[1] && !ignored.includes(parts[1])) ? parts[1] : null;
            if (!handle && archives["COOMER"]) handle = archives["COOMER"].split('/').at(-1);
        } else if (host.includes('fansly.com')) {
            service = 'fansly';
            archives = await extractURLFromFansly();
            const ignored = ['explore', 'home', 'notifications', 'messages', 'settings'];
            const matches = window.location.pathname.match(/^\/(?:profile\/)?([^/]+)/);
            handle = (matches && matches[1] && !ignored.includes(matches[1])) ? matches[1] : null;
            if (!handle && archives["COOMER"]) handle = archives["COOMER"].split('/').at(-1);
        } else if (host.includes('patreon.com')) {
            service = 'patreon';
            archives = extractURLFromPatreon();
            handle = archives["KEMONO"]?.split('/').at(-1) || extractNextUrlPath("user") || window.location.pathname.split('/').at(-1);
        } else if (host.includes('fanbox.cc')) {
            service = 'fanbox';
            archives = extractURLFromFanbox();
            handle = host.split('.')[0] !== 'www' ? host.split('.')[0] : extractNextUrlPath('@');
        } else if (host.includes('pixiv.net')) {
            service = 'pixiv';
            archives = extractURLFromPixiv();
            handle = extractNextUrlPath("users") || archives["KEMONO"]?.split('/').at(-1);
        } else if (host.includes('discord.com')) {
            service = 'discord';
            archives = extractURLFromDiscord();
            handle = archives["KEMONO"]?.split('/').at(-1);
        } else if (host.includes('fantia.jp')) {
            service = 'fantia';
            archives = extractURLFromFantia();
            handle = extractNextUrlPath("fanclubs");
        } else if (host.includes('boosty.to')) {
            service = 'boosty';
            archives = extractURLFromBoosty();
            handle = extractNextUrlPath("/");
        } else if (host.includes('dlsite.com')) {
            service = 'dlsite';
            archives = extractURLFromDlsite();
            handle = extractNextUrlPath("maker_id", ".html");
        } else if (host.includes('gumroad.com')) {
            service = 'gumroad';
            archives = extractURLFromGumroad();
            handle = select('meta[property="og:title"]', "content") || "Gumroad Creator";
        } else if (host.includes('subscribestar.com') || host.includes('subscribestar.adult')) {
            service = 'subscribestar';
            archives = extractURLFromSubscribeStar();
            handle = select('img[data-type="avatar"][alt]', "alt")?.toLowerCase() || window.location.pathname.split('/')[1];
        } else if (host.includes('candfans.com')) {
            service = 'candfans';
            archives = extractURLFromCandFans();
            handle = window.location.pathname.split('/')[1];
        }

        if (!handle || handle === 'www') return null;
        const displayName = getCleanDisplayName(service, handle);
        return { handle, displayName, service, archives };
    }

    // --- Main Loop ---
    let lastCheckedHandle = null;
    let lastUrl = location.href;

    async function mainLoop() {
        const data = await getCreatorData();
        const overlay = document.getElementById('klf-overlay');

        if (data && data.handle && data.handle !== lastCheckedHandle) {
            lastCheckedHandle = data.handle;
            currentCheckSessionId++;
            const sessionId = currentCheckSessionId;

            createOrGetOverlay(data.displayName, data.service);

            // 1. Run Live Leak Checks
            runLiveLeakChecks(data.handle, data.service, data.archives, sessionId);

            // 2. Populate Other Sites & Forums
            populateOtherSitesAndForums(data.handle);

            // 3. Apply SFW / NSFW filter
            applySfwFilter(data.service);

        } else if (data && data.handle && overlay && overlay.style.display === 'none') {
            overlay.style.display = 'block';
        } else if (!data && overlay) {
            overlay.style.display = 'none';
            lastCheckedHandle = null;
        }
    }

    setTimeout(mainLoop, 1000);

    new MutationObserver(() => {
        if (location.href !== lastUrl) {
            lastUrl = location.href;
            setTimeout(mainLoop, 800);
        }
    }).observe(document.body, { subtree: true, childList: true });

})();