Cam ARNA

Multi-archive search tool with modern dashboard design + Import/Export + Caching + History

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Cam ARNA
// @namespace    http://tampermonkey.net/
// @version      2.4.1
// @description  Multi-archive search tool with modern dashboard design + Import/Export + Caching + History
// @author       user006-ui
// @license      MIT
// @match        https://*.stripchat.com/*
// @match        https://*.chaturbate.com/*
// @match        https://chaturbate.com/*
// @match        https://*.bongacams.com/*
// @match        https://bongacams.com/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_openInTab
// @connect      archivebate.com
// @connect      showcamrips.com
// @connect      camshowrecordings.com
// @connect      camwh.com
// @connect      topcamvideos.com
// @connect      lovecamporn.com
// @connect      camwhores.tv
// @connect      bestcam.tv
// @connect      xhomealone.com
// @connect      stream-leak.com
// @connect      mfcamhub.com
// @connect      camshowrecord.net
// @connect      camwhoresbay.com
// @connect      camsave1.com
// @connect      onscreens.me
// @connect      livecamrips.to
// @connect      cumcams.cc
// @connect      allmy.cam
// @connect      livecamsrip.com
// @connect      stripchat.com
// @connect      chaturbate.com
// @connect      bongacams.com
// @connect      camgirlfinder.net
// @connect      nrtool.to
// @connect      camsrip.com
// @connect      camcaps.tv
// @connect      curbate.tv
// @connect      rec-tube.com
// @connect      camshaip.com
// @connect      camsclips.net
// @connect      motherless.com
// @connect      webpussi.com
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration & Utils ---
    const Config = {
        version: '2.4.1',
        cacheTTL: 30 * 60 * 1000, // 30 minutes
        maxHistory: 10,
        colors: {
            bg: '#0f172a',
            surface: '#1e293b',
            border: '#334155',
            primary: '#6366f1',
            accent: '#818cf8',
            text: '#f8fafc',
            textMuted: '#94a3b8',
            success: '#10b981',
            error: '#ef4444'
        }
    };

    const Utils = {
        escapeRegex: (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
        isValidUsername: (str) => /^[a-zA-Z0-9_\-]{3,50}$/.test(str),
        openSafe: (url) => {
            if (typeof GM_openInTab === 'function') {
                GM_openInTab(url, { active: true, insert: true, setParent: true });
            } else {
                window.open(url, '_blank', 'noopener,noreferrer');
            }
        },
        getCurrentPlatform: () => {
            const h = window.location.host;
            if (h.includes('chaturbate')) return 'CB';
            if (h.includes('stripchat')) return 'SC';
            if (h.includes('bongacams')) return 'BC';
            return 'Other';
        },
        crypto: {
            secret: 'CAM_ARNA_SALT_v3_MODERN',
            encrypt: (text) => {
                if (!text) return '';
                try { return CryptoJS.AES.encrypt(text, Utils.crypto.secret).toString(); } catch (e) { return ''; }
            },
            decrypt: (ciphertext) => {
                if (!ciphertext) return '';
                try {
                    const bytes = CryptoJS.AES.decrypt(ciphertext, Utils.crypto.secret);
                    return bytes.toString(CryptoJS.enc.Utf8);
                } catch (e) { return ''; }
            }
        }
    };

    const Storage = {
        get: (key, defaultValue) => GM_getValue(key, defaultValue),
        set: (key, value) => GM_setValue(key, value)
    };

    // --- Sites Configuration ---
    const archiveSites = [
        { name: 'Archivebate', url: 'https://archivebate.com/profile/{username}', domain: 'archivebate.com' },
        { name: 'Showcamrips', url: 'https://showcamrips.com/model/en/{username}', domain: 'showcamrips.com' },
        { name: 'CamRecordings', url: 'https://www.camshowrecordings.com/model/{username}', domain: 'camshowrecordings.com' },
        { name: 'CamWH', url: 'https://camwh.com/tags/{username}/', domain: 'camwh.com' },
        { name: 'TopCam', url: 'https://www.topcamvideos.com/showall/?search={username}', domain: 'topcamvideos.com' },
        { name: 'LoveCam', url: 'https://lovecamporn.com/showall/?search={username}', domain: 'lovecamporn.com' },
        { name: 'Camwhores.tv', url: 'https://www.camwhores.tv/search/{username}/', domain: 'camwhores.tv' },
        { name: 'Bestcam', url: 'https://bestcam.tv/model/{username}', domain: 'bestcam.tv' },
        { name: 'XHome', url: 'https://xhomealone.com/tags/{username}/', domain: 'xhomealone.com' },
        { name: 'StreamLeak', url: 'https://stream-leak.com/models/{username}/', domain: 'stream-leak.com' },
        { name: 'MFCamHub', url: 'https://mfcamhub.com/models/{username}/', domain: 'mfcamhub.com' },
        { name: 'CamRecord', url: 'https://camshowrecord.net/video/list?page=1&model={username}', domain: 'camshowrecord.net' },
        { name: 'CW Bay', url: 'https://www.camwhoresbay.com/search/{username}/', domain: 'camwhoresbay.com' },
        { name: 'CamSave', url: 'https://www.camsave1.com/?search={username}&women=true', domain: 'camsave1.com' },
        { name: 'OnScreens', url: 'https://www.onscreens.me/m/{username}', domain: 'onscreens.me' },
        { name: 'LiveCamRips', url: 'https://livecamrips.to/search/{username}/1', domain: 'livecamrips.to' },
        { name: 'CumCams', url: 'https://cumcams.cc/performer/{username}', domain: 'cumcams.cc' },
        { name: 'AllMyCam', url: 'https://allmy.cam/search/{username}/', domain: 'allmy.cam' },
        { name: 'LCRip', url: 'https://www.livecamsrip.com/{username}/profile', domain: 'livecamsrip.com' },
        { name: 'CamsRip', url: 'https://camsrip.com/{username}/profile', domain: 'camsrip.com' },
        { name: 'CamCaps', url: 'https://camcaps.tv/search/videos/{username}', domain: 'camcaps.tv' },
        { name: 'Curbate', url: 'https://curbate.tv/search?q={username}', domain: 'curbate.tv' },
        { name: 'RecTube', url: 'https://rec-tube.com/search/{username}/', domain: 'rec-tube.com' },
        { name: 'CamShaip', url: 'https://camshaip.com/search/{username}/', domain: 'camshaip.com' },
        { name: 'CamsClips', url: 'https://www.camsclips.net/search/{username}/', domain: 'camsclips.net' },
        { name: 'Motherless', url: 'https://motherless.com/term/{username}', domain: 'motherless.com' },
        { name: 'WebPussi', url: 'https://www.webpussi.com/search/{username}/', domain: 'webpussi.com' }
    ];

    function getFaviconUrl(domain) {
        return `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;
    }

    // --- Page Checker Logic ---
    const PageChecker = {
        checkPage: function(url) {
            return new Promise((resolve) => {
                try {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: url,
                        timeout: 10000,
                        onload: (res) => {
                            try { resolve(this.analyze(res, url)); }
                            catch(e) { resolve(false); }
                        },
                        onerror: () => resolve(false),
                        ontimeout: () => resolve(false)
                    });
                } catch(e) {
                    resolve(false);
                }
            });
        },
        analyze: function(response, url) {
            try {
                if (!response || response.status === 404 || response.status >= 500) return false;
                const text = response.responseText;
                if (!text || typeof text !== 'string') return false;

                const lowerText = text.toLowerCase();
                const titleMatch = lowerText.match(/<title[^>]*>(.*?)<\/title>/i);
                const title = titleMatch ? titleMatch[1] : '';

                if (url.includes('livecamrips.to')) {
                    const noResultPatterns = ['no records found', 'no models found', 'no results', '0 models found'];
                    if (noResultPatterns.some(p => lowerText.includes(p))) return false;
                    if (!text.includes('class="video"') && !text.includes('model-card')) return false;
                }
                if (url.includes('cumcams.cc')) {
                    const notFoundPatterns = [ /<h1[^>]*>404<\/h1>/i, /performer\s*not\s*found/i ];
                    if (notFoundPatterns.some(p => p.test(text))) return false;
                    if (!text.includes('profile-info') && !text.includes('class="performer"')) return false;
                }
                if (url.includes('allmy.cam')) {
                    if (!text.includes('class="video-card"')) return false;
                }
                if (url.includes('showcamrips')) {
                    if (text.includes('data:image/png;base64')) return false;
                }
                if (url.includes('camshowrecordings.com')) {
                    if (!text.includes('class="h1modelpage"')) return false;
                }
                if (url.includes('camwhores.tv')) {
                    if (/no\s*videos?\s*found|0\s*videos/i.test(lowerText)) return false;
                }

                if (['not found', '404', 'error'].some(term => title.includes(term))) return false;
                const genericNotFound = [/no\s*videos?\s*found/i, /no\s*results?\s*found/i, /does\s*not\s*exist/i, /0\s*results?/i];
                if (genericNotFound.some(p => p.test(lowerText))) return false;

                return true;
            } catch(e) {
                return false; // Error parsing DOM or text = not found
            }
        }
    };

    // --- Styling ---
    function injectStyles() {
        GM_addStyle(`
            :root {
                --ca-bg: ${Config.colors.bg};
                --ca-surf: ${Config.colors.surface};
                --ca-border: ${Config.colors.border};
                --ca-prim: ${Config.colors.primary};
                --ca-acc: ${Config.colors.accent};
                --ca-text: ${Config.colors.text};
                --ca-muted: ${Config.colors.textMuted};
                --ca-success: ${Config.colors.success};
                --ca-error: ${Config.colors.error};
            }
            .ca-fab {
                position: fixed; bottom: 20px; right: 20px;
                width: 50px; height: 50px; border-radius: 50%;
                background: var(--ca-prim); color: white; border: none;
                cursor: pointer; z-index: 9999; font-size: 20px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                display: flex; align-items: center; justify-content: center;
            }
            .ca-badge {
                position: absolute; top: -5px; right: -5px;
                background: var(--ca-error); color: white;
                font-size: 11px; font-weight: bold;
                padding: 2px 6px; border-radius: 10px;
                display: none; box-shadow: 0 2px 4px rgba(0,0,0,0.2);
            }
            .ca-overlay {
                position: fixed; inset: 0; background: rgba(0,0,0,0.7);
                display: flex; align-items: center; justify-content: center;
                z-index: 10000; font-family: sans-serif;
            }
            .ca-panel {
                background: var(--ca-bg); width: 450px; max-height: 80vh;
                border-radius: 12px; border: 1px solid var(--ca-border);
                display: flex; flex-direction: column; overflow: hidden; color: var(--ca-text);
            }
            .ca-head { padding: 16px; border-bottom: 1px solid var(--ca-border); display: flex; justify-content: space-between; align-items: center; }
            .ca-brand { font-weight: bold; font-size: 18px; display: flex; align-items: center; gap: 8px; }
            .ca-tabs { display: flex; background: var(--ca-surf); padding: 4px; gap: 4px; flex-wrap: wrap; }
            .ca-tab { flex: 1; padding: 8px; border: none; background: none; color: var(--ca-muted); cursor: pointer; border-radius: 6px; font-size: 13px; }
            .ca-tab.active { background: var(--ca-prim); color: white; }
            .ca-body { padding: 16px; overflow-y: auto; display: flex; flex-direction: column; flex: 1; }
            .ca-search-box { position: relative; margin-bottom: 16px; }
            .ca-input { width: 100%; padding: 10px 10px 10px 35px; background: var(--ca-surf); border: 1px solid var(--ca-border); border-radius: 8px; color: white; box-sizing: border-box; }
            .ca-icon { position: absolute; left: 10px; top: 10px; width: 18px; color: var(--ca-muted); }
            .ca-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
            .ca-item { background: var(--ca-surf); padding: 10px; border-radius: 8px; border: 1px solid var(--ca-border); display: flex; align-items: center; gap: 10px; cursor: pointer; transition: 0.2s; position: relative; }
            .ca-item:hover { border-color: var(--ca-prim); }
            .ca-item img { width: 16px; height: 16px; }
            .ca-item-name { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
            .ca-status { width: 8px; height: 8px; border-radius: 50%; margin-left: auto; }
            .checking .ca-status { background: orange; animation: pulse 1s infinite; }
            .found { border-color: var(--ca-success) !important; }
            .found .ca-status { background: var(--ca-success); }
            .not-found { opacity: 0.5; filter: grayscale(1); }
            .not-found .ca-status { background: var(--ca-error); }
            .ca-toast { position: fixed; bottom: 80px; right: 20px; background: var(--ca-prim); padding: 8px 16px; border-radius: 4px; color: white; z-index: 10001; }
            .ca-close { background: none; border: none; color: var(--ca-muted); cursor: pointer; font-size: 18px; }
            .ca-btn-small { padding: 4px 8px; background: var(--ca-surf); border: 1px solid var(--ca-border); border-radius: 4px; color: var(--ca-text); cursor: pointer; font-size: 12px; }
            .ca-btn-small:hover { background: var(--ca-prim); border-color: var(--ca-prim); }
            .ca-tag { font-size: 10px; background: var(--ca-border); padding: 2px 6px; border-radius: 10px; color: var(--ca-text); }
            .ca-history-item { padding: 8px; border-bottom: 1px solid var(--ca-border); display: flex; justify-content: space-between; align-items: center; cursor: pointer; }
            .ca-history-item:hover { background: var(--ca-surf); }
            .ca-site-toggle { display: flex; justify-content: space-between; align-items: center; padding: 8px; border-bottom: 1px solid var(--ca-border); }
            @keyframes pulse { 0% { opacity: 0.5; } 50% { opacity: 1; } 100% { opacity: 0.5; } }
        `);
    }

    // --- Main UI Logic ---
    const UI = {
        isOpen: false,
        cacheManager: {
            get: (username) => {
                try {
                    let cache = Storage.get('ca_cache', {});
                    let entry = cache[username];
                    if (entry && Date.now() < entry.expires) return entry.results;
                } catch(e) {}
                return null;
            },
            set: (username, resultsMap) => {
                try {
                    let cache = Storage.get('ca_cache', {});
                    const now = Date.now();
                    for (let k in cache) { if (now > cache[k].expires) delete cache[k]; }
                    cache[username] = { expires: now + Config.cacheTTL, results: resultsMap };
                    Storage.set('ca_cache', cache);
                } catch(e) {}
            }
        },

        toggle: function() {
            if (this.isOpen) {
                document.querySelector('.ca-overlay')?.remove();
                this.isOpen = false;
            } else {
                this.render();
                this.isOpen = true;
                const activeUser = document.getElementById('ca-user').value.trim();
                if (activeUser) document.getElementById('ca-user').select();
            }
        },

        render: function() {
            const overlay = document.createElement('div');
            overlay.className = 'ca-overlay';
            overlay.onclick = (e) => { if(e.target === overlay) UI.toggle(); };

            const panel = document.createElement('div');
            panel.className = 'ca-panel';
            panel.innerHTML = `
                <div class="ca-head">
                    <div class="ca-brand">
                        <span>ARNA</span>
                        <span style="font-size:10px; color:var(--ca-muted); border:1px solid var(--ca-border); padding:1px 4px; border-radius:4px;">${Config.version}</span>
                    </div>
                    <button class="ca-close">✕</button>
                </div>
                <div class="ca-tabs">
                    <button class="ca-tab active" data-view="search">Search</button>
                    <button class="ca-tab" data-view="history">History</button>
                    <button class="ca-tab" data-view="saved">Saved</button>
                    <button class="ca-tab" data-view="tools">Tools</button>
                </div>
                <div class="ca-body">
                    <div class="ca-search-box">
                        <svg class="ca-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21l-4.35-4.35m1.35-5.65a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
                        <input type="text" class="ca-input" id="ca-user" placeholder="Search username..." autocomplete="off">
                    </div>

                    <!-- Search View -->
                    <div id="view-search">
                        <div style="font-size: 12px; color: var(--ca-muted); margin-bottom: 8px; display: flex; justify-content: space-between;">
                            <span>Archives / Recorders</span>
                            <span id="ca-counter">0 / 0 found</span>
                        </div>
                        <div class="ca-grid" id="ca-archives"></div>
                        <div class="ca-item" id="ca-btn-save" style="justify-content:center; border-style:dashed; opacity:0.8; margin-top: 12px;">
                            <span class="ca-item-name">Save to Favorites</span>
                        </div>
                    </div>

                    <!-- History View -->
                    <div id="view-history" style="display:none;">
                        <div style="font-size: 12px; color: var(--ca-muted); margin-bottom: 8px;">Recent Searches</div>
                        <div id="ca-history-list"></div>
                    </div>

                    <!-- Saved View -->
                    <div id="view-saved" style="display:none;">
                        <div style="display:flex; gap:8px; margin-bottom:12px;">
                            <button id="ca-btn-import" class="ca-btn-small" style="flex:1;">Import JSON</button>
                            <button id="ca-btn-export" class="ca-btn-small" style="flex:1;">Export JSON</button>
                            <input type="file" id="ca-file-input" style="display:none;" accept=".json">
                        </div>
                        <div id="ca-saved-list"></div>
                    </div>

                    <!-- Tools View -->
                    <div id="view-tools" style="display:none;">
                        <div style="font-size: 12px; color: var(--ca-muted); margin-bottom: 8px;">Quick Links</div>
                        <div class="ca-grid">
                            <div class="ca-item tool-btn" data-url="https://www.cbhours.com/user/{u}.html"><span class="ca-item-name">Schedule</span></div>
                            <div class="ca-item tool-btn" data-url="https://statbate.com/search/1/{u}"><span class="ca-item-name">Statistics</span></div>
                            <div class="ca-item tool-btn" data-url="https://camgirlfinder.net/models/sc/{u}"><span class="ca-item-name">Finder</span></div>
                            <div class="ca-item tool-btn" data-url="https://nrtool.to/nrtool/search?sites={u}"><span class="ca-item-name">Images</span></div>
                        </div>
                        <div style="font-size: 12px; color: var(--ca-muted); margin: 16px 0 8px 0;">Manage Sites</div>
                        <div id="ca-site-toggles" style="max-height: 150px; overflow-y: auto; background: var(--ca-surf); border-radius: 8px; border: 1px solid var(--ca-border);"></div>
                    </div>
                </div>
            `;
            overlay.appendChild(panel);
            document.body.appendChild(overlay);

            this.initArchivesGrid(panel);
            this.initSiteToggles(panel);
            this.setupEventListeners(panel, overlay);

            // Auto detect
            const input = panel.querySelector('#ca-user');
            this.detectUser(input);
        },

        initArchivesGrid: function(panel) {
            const grid = panel.querySelector('#ca-archives');
            grid.innerHTML = '';
            const disabledSites = Storage.get('ca_disabled_sites', []);

            archiveSites.forEach(site => {
                if (disabledSites.includes(site.name)) return;

                const el = document.createElement('div');
                el.className = 'ca-item archive-item';
                el.dataset.url = site.url;
                el.dataset.name = site.name;
                el.innerHTML = `
                    <img src="${getFaviconUrl(site.domain)}" loading="lazy">
                    <span class="ca-item-name">${site.name}</span>
                    <div class="ca-status"></div>
                `;
                el.onclick = () => {
                    const u = document.getElementById('ca-user').value.trim();
                    if (u) Utils.openSafe(site.url.replace('{username}', u));
                };
                grid.appendChild(el);
            });
        },

        initSiteToggles: function(panel) {
            const container = panel.querySelector('#ca-site-toggles');
            let disabledSites = Storage.get('ca_disabled_sites', []);

            archiveSites.forEach(site => {
                const row = document.createElement('div');
                row.className = 'ca-site-toggle';
                const isChecked = !disabledSites.includes(site.name);
                row.innerHTML = `
                    <span style="font-size: 13px;">${site.name}</span>
                    <input type="checkbox" ${isChecked ? 'checked' : ''}>
                `;
                row.querySelector('input').onchange = (e) => {
                    if (e.target.checked) {
                        disabledSites = disabledSites.filter(n => n !== site.name);
                    } else {
                        if (!disabledSites.includes(site.name)) disabledSites.push(site.name);
                    }
                    Storage.set('ca_disabled_sites', disabledSites);
                    this.initArchivesGrid(document.querySelector('.ca-panel')); // Refresh grid
                };
                container.appendChild(row);
            });
        },

        setupEventListeners: function(panel, overlay) {
            panel.querySelector('.ca-close').onclick = () => UI.toggle();

            const tabs = panel.querySelectorAll('.ca-tab');
            tabs.forEach(t => {
                t.onclick = () => {
                    tabs.forEach(x => x.classList.remove('active'));
                    t.classList.add('active');
                    panel.querySelectorAll('div[id^="view-"]').forEach(v => v.style.display = 'none');
                    panel.querySelector(`#view-${t.dataset.view}`).style.display = 'block';

                    if(t.dataset.view === 'saved') UI.loadSaved();
                    if(t.dataset.view === 'history') UI.loadHistory();
                };
            });

            const input = panel.querySelector('#ca-user');
            let debounce;
            input.oninput = () => {
                clearTimeout(debounce);
                debounce = setTimeout(() => UI.checkAll(input.value.trim()), 600);
            };

            // Save Favorites
            panel.querySelector('#ca-btn-save').onclick = () => {
                const u = input.value.trim();
                if (u && Utils.isValidUsername(u)) {
                    let current = Storage.get('ca_saved', []);
                    // Normalize old arrays to objects
                    current = current.map(item => typeof item === 'string' ? { user: item, platform: 'Unknown' } : item);

                    if (!current.find(i => i.user === u)) {
                        current.push({ user: u, platform: Utils.getCurrentPlatform() });
                        Storage.set('ca_saved', current);
                        UI.showToast(`Saved ${u}`);
                    } else {
                        UI.showToast(`${u} already saved`);
                    }
                }
            };

            // Import/Export
            const fileInput = panel.querySelector('#ca-file-input');
            panel.querySelector('#ca-btn-export').onclick = () => {
                const saved = Storage.get('ca_saved', []);
                if(saved.length === 0) return UI.showToast('Nothing to export');
                const blob = new Blob([JSON.stringify(saved, null, 2)], { type: 'application/json' });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = `arna_backup_${new Date().toISOString().slice(0,10)}.json`;
                a.click();
                URL.revokeObjectURL(url);
                UI.showToast('Export successful');
            };

            panel.querySelector('#ca-btn-import').onclick = () => fileInput.click();
            fileInput.onchange = (e) => {
                const file = e.target.files[0];
                if(!file) return;
                const reader = new FileReader();
                reader.onload = (ev) => {
                    try {
                        const list = JSON.parse(ev.target.result);
                        if(Array.isArray(list)) {
                            let current = Storage.get('ca_saved', []);
                            current = current.map(item => typeof item === 'string' ? { user: item, platform: 'Unknown' } : item);

                            const newItems = list.map(x => typeof x === 'string' ? { user: x, platform: 'Imported' } : x)
                                                 .filter(x => Utils.isValidUsername(x.user) && !current.find(c => c.user === x.user));

                            Storage.set('ca_saved', [...current, ...newItems]);
                            UI.showToast(`Imported ${newItems.length} profiles`);
                            UI.loadSaved();
                        } else {
                            UI.showToast('Invalid JSON format');
                        }
                    } catch(err) {
                        UI.showToast('Error reading file');
                    }
                };
                reader.readAsText(file);
                fileInput.value = '';
            };

            // Tools Buttons
            panel.querySelectorAll('.tool-btn').forEach(btn => {
                btn.onclick = () => {
                    const u = input.value.trim();
                    if(u) Utils.openSafe(btn.dataset.url.replace('{u}', u));
                };
            });
        },

        checkAll: async function(username) {
            if (!username || !Utils.isValidUsername(username)) return;

            // Save to history carefully
            try {
                let history = Storage.get('ca_history', []);
                history = history.filter(u => u !== username);
                history.unshift(username);
                if (history.length > Config.maxHistory) history = history.slice(0, Config.maxHistory);
                Storage.set('ca_history', history);
                const historyView = document.getElementById('view-history');
                if (historyView && historyView.style.display === 'block') this.loadHistory();
            } catch(e) {}

            const items = Array.from(document.querySelectorAll('.archive-item'));
            if (items.length === 0) return;

            items.forEach(el => {
                el.classList.remove('found', 'not-found');
                el.classList.add('checking');
            });

            const counterEl = document.getElementById('ca-counter');
            const totalItems = items.length;
            let checkedCount = 0;
            let foundCount = 0;
            if (counterEl) counterEl.innerText = `0 / ${totalItems} found`;

            // Check Cache first
            const cachedResults = this.cacheManager.get(username);
            const newResultsMap = cachedResults || {};

            const handleCompletion = () => {
                checkedCount++;
                if (checkedCount === totalItems) {
                    this.cacheManager.set(username, newResultsMap);
                }
            };

            const updateUI = (el, isFound) => {
                try {
                    el.classList.remove('checking');
                    if (isFound) {
                        el.classList.add('found');
                        foundCount++;
                    } else {
                        el.classList.add('not-found');
                    }
                    if (counterEl) counterEl.innerText = `${foundCount} / ${totalItems} found`;

                    // Update Badge
                    const badge = document.getElementById('ca-fab-badge');
                    if (badge) {
                        badge.innerText = foundCount;
                        badge.style.display = foundCount > 0 ? 'flex' : 'none';
                    }
                } catch(e) {}
            };

            // Process all items simultaneously
            items.forEach(async (el) => {
                const siteName = el.dataset.name;

                if (cachedResults && cachedResults[siteName] !== undefined) {
                    updateUI(el, cachedResults[siteName]);
                    handleCompletion();
                } else {
                    let exists = false;
                    try {
                        const url = el.dataset.url.replace('{username}', username);
                        exists = await PageChecker.checkPage(url);
                    } catch(e) {}

                    newResultsMap[siteName] = exists;
                    updateUI(el, exists);
                    handleCompletion();
                }
            });
        },

        loadSaved: function() {
            let list = Storage.get('ca_saved', []);
            list = list.map(item => typeof item === 'string' ? { user: item, platform: '?' } : item);

            const container = document.getElementById('ca-saved-list');
            container.innerHTML = '';

            if (list.length === 0) {
                container.innerHTML = '<div style="text-align:center; padding:20px; color:var(--ca-muted)">No saved profiles</div>';
                return;
            }

            list.forEach(item => {
                const div = document.createElement('div');
                div.className = 'ca-item';
                div.style.justifyContent = 'space-between';
                div.innerHTML = `
                    <div style="display:flex; align-items:center; gap:8px;">
                        <span style="font-weight:600">${item.user}</span>
                        <span class="ca-tag">${item.platform || '?'}</span>
                    </div>
                    <button class="ca-delete" style="border:none;background:none;cursor:pointer;color:var(--ca-muted);">✕</button>
                `;
                div.onclick = (e) => {
                    if (!e.target.classList.contains('ca-delete')) {
                        document.getElementById('ca-user').value = item.user;
                        document.querySelector('[data-view="search"]').click();
                        UI.checkAll(item.user);
                    }
                };
                div.querySelector('.ca-delete').onclick = (e) => {
                    e.stopPropagation();
                    const newList = list.filter(x => x.user !== item.user);
                    Storage.set('ca_saved', newList);
                    UI.loadSaved();
                };
                container.appendChild(div);
            });
        },

        loadHistory: function() {
            const history = Storage.get('ca_history', []);
            const container = document.getElementById('ca-history-list');
            container.innerHTML = '';

            if (history.length === 0) {
                container.innerHTML = '<div style="text-align:center; padding:20px; color:var(--ca-muted)">No history yet</div>';
                return;
            }

            history.forEach(u => {
                const div = document.createElement('div');
                div.className = 'ca-history-item';
                div.innerHTML = `<span style="font-weight:500">${u}</span> <span style="font-size:12px;color:var(--ca-muted)">↺</span>`;
                div.onclick = () => {
                    document.getElementById('ca-user').value = u;
                    document.querySelector('[data-view="search"]').click();
                    UI.checkAll(u);
                };
                container.appendChild(div);
            });
        },

        detectUser: function(input) {
            try {
                const path = window.location.pathname.split('/').filter(Boolean);
                let user = '';
                const host = window.location.host;

                if (host.includes('chaturbate') || host.includes('bongacams')) {
                    user = path[0];
                } else {
                    user = path[path.length - 1];
                }

                const ignoredPaths = ['auth', 'tags', 'couples', 'female', 'myfriends', 'male', 'trans', 'new-models', 'spy-mode'];

                if (user && Utils.isValidUsername(user) && !ignoredPaths.includes(user.toLowerCase())) {
                    input.value = user;
                    UI.checkAll(user);
                }
            } catch(e) {}
        },

        showToast: function(msg) {
            const t = document.createElement('div');
            t.className = 'ca-toast';
            t.innerText = msg;
            document.body.appendChild(t);
            setTimeout(() => t.remove(), 3000);
        }
    };

    function init() {
        injectStyles();

        const fab = document.createElement('div');
        fab.className = 'ca-fab';
        fab.innerHTML = `
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
            <div id="ca-fab-badge" class="ca-badge">0</div>
        `;
        fab.onclick = () => UI.toggle();
        document.body.appendChild(fab);

        document.addEventListener('keydown', (e) => {
            const target = e.target.tagName.toLowerCase();
            if (target === 'input' || target === 'textarea') return;

            if (e.shiftKey && e.key.toLowerCase() === 'a') {
                e.preventDefault();
                UI.toggle();
            }
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();