☰

WOW.XXX Wicked Tab

Multi-select videos and open them in a beautiful instant Fullscreen Grid Viewer!

スクリプトをインストールするには、Tampermonkey, Greasemonkey や Violentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、 Tampermonkey や Violentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、Tampermonkey や Violentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、Tampermonkey や Userscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら、通報はこちらへお寄せください
// ==UserScript==
// @name         WOW.XXX Wicked Tab
// @namespace    http://tampermonkey.net/
// @version      1.2.1
// @description  Multi-select videos and open them in a beautiful instant Fullscreen Grid Viewer!
// @author       6969RandomGuy6969
// @license MIT
// @match        https://www.wow.xxx/*
// @match        https://wow.xxx/*
// @icon         https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://wow.xxx/&size=256
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @connect      wow.xxx
// ==/UserScript==

(function () {
    'use strict';

    const WICKED_KEY        = 'wp_wicked_videos';
    const WICKED_SEL_KEY    = 'wp_wicked_selections';

    let autoPlayAll = GM_getValue('wickedAutoPlayAll', false);

    GM_registerMenuCommand(
        `${autoPlayAll ? '✅' : '⬜'} Wicked Tab: Auto-play all thumbnails`,
        () => {
            autoPlayAll = !autoPlayAll;
            GM_setValue('wickedAutoPlayAll', autoPlayAll);
            alert(`Wicked Tab thumbnail autoplay ${autoPlayAll ? 'ENABLED' : 'DISABLED'}.\\nReopen Wicked Tab to apply.`);
        }
    );

    function fixUrl(src, type) {
        if (!src) return '';
        let url = src;
        if (url.startsWith('//')) url = window.location.protocol + url;
        else if (url.startsWith('/')) url = window.location.origin + url;

        if (url.includes('www.wow.xxx')) {
            if (type === 'preview' || url.includes('/preview/')) {
                url = url.replace('www.wow.xxx', 'cast.wow.xxx');
            } else if (type === 'thumb' || url.match(/www\.wow\.xxx\/\d+\//)) {
                url = url.replace('www.wow.xxx', 'img.wow.xxx');
            }
        }
        return url;
    }

    function extractId(videoUrl) {
        if (!videoUrl) return null;
        // Match anything after /video/ until the next slash or end of string
        const m = videoUrl.match(/\/videos?\/([^\/]+)\/?/);
        return m ? m[1] : null;
    }

    function fetchPreviewUrl(videoUrl, callback) {
        GM_xmlhttpRequest({
            method: 'GET', url: videoUrl, timeout: 10000,
            onload: r => {
                try {
                    const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
                    const img = doc.querySelector('img[data-preview]');
                    callback(fixUrl(img?.getAttribute('data-preview') || '', 'preview'));
                } catch(e) { callback(''); }
            },
            onerror:   () => callback(''),
            ontimeout: () => callback('')
        });
    }

    function getMainImg(card) {
        // Skip the f3.svg loader image and get the actual thumbnail
        return card.querySelector('img.thumb-img') || card.querySelector('img[itemprop="thumbnail"]') || card.querySelector('img:not([src*="f3.svg"])') || card.querySelector('img');
    }

    function extractThumb(card) {
        const img = getMainImg(card);
        const src = img?.getAttribute('data-webp') || img?.getAttribute('data-src') || img?.getAttribute('data-original') || img?.src || '';
        return fixUrl(src, 'thumb');
    }

    function extractPreviewMp4(card) {
        const img = getMainImg(card);
        const previewEl = card.querySelector('[data-preview]');
        const p = previewEl ? previewEl.getAttribute('data-preview') : (img?.getAttribute('data-preview') || '');
        return fixUrl(p, 'preview');
    }

    function extractDuration(card) {
        return card.querySelector('.thumb__info-item, .duration')?.textContent?.trim() || '';
    }

    function extractRating(card) {
        const m = (card.querySelector('.thumb__info, .rating')?.textContent || '').match(/(\d+)%/);
        return m ? m[1] + '%' : '';
    }

    function extractViews(card) {
        for (const sp of [...(card.querySelectorAll('.thumb__info span, .views') || [])]) {
            const t = sp.textContent.trim();
            if (/^\d[\d.,]*[KkMm]?$/.test(t) && !t.includes(':') && !t.includes('%')) return t;
        }
        return '';
    }

    function getPath(urlStr) {
        if (!urlStr) return '';
        try { return new URL(urlStr, window.location.origin).pathname; } catch(e) { return urlStr; }
    }

    function generateShareLink() {
        if (!wickedState.sel.length) return alert('Select videos first!');
        const compactData = wickedState.sel.map(v => ({
            i: v.id || extractId(v.url),
            t: getPath(v.thumb),
            p: getPath(v.preview),
            n: v.title || 'Video'
        }));
        // Use proper UTF-8 Base64 encoding to prevent 3x size inflation
        const b64 = btoa(unescape(encodeURIComponent(JSON.stringify(compactData))));
        const link = `https://www.wow.xxx/#wicked_share=${b64}`;
        GM_setClipboard(link);
        alert('✅ Share Link Copied to Clipboard!\\n\\nAnyone with this script installed can paste this link in their browser to instantly open your exact Grid Viewer!');
    }

    function checkShareLink() {
        let shareData = '';
        if (window.location.hash.startsWith('#wicked_share=')) {
            shareData = window.location.hash.substring(14);
        } else {
            shareData = new URLSearchParams(window.location.search).get('wicked_share') || '';
        }

        if (shareData) {
            try {
                const raw = atob(shareData);
                const jsonStr = raw.startsWith('%') ? decodeURIComponent(raw) : decodeURIComponent(escape(raw));
                const arr = JSON.parse(jsonStr);
                if (Array.isArray(arr) && arr.length > 0) {
                    const importedSel = arr.map(v => ({
                        url: v.i ? `https://www.wow.xxx/videos/${v.i}/` : '',
                        id: v.i || '',
                        thumb: v.t ? fixUrl(v.t, 'thumb') : '',
                        preview: v.p ? fixUrl(v.p, 'preview') : '',
                        title: v.n || 'Shared Video',
                        duration: '', rating: '', views: ''
                    })).filter(v => v.url);

                    if (importedSel.length > 0) {
                        wickedState.sel = importedSel;
                        try { localStorage[WICKED_SEL_KEY] = JSON.stringify(wickedState.sel); } catch(e){}
                        window.history.replaceState(null, null, window.location.pathname);
                        setTimeout(() => {
                            updateWickedUI();
                            openFullscreenGrid();
                        }, 500);
                    }
                }
            } catch(e) {
                console.error("Invalid wicked_share link", e);
            }
        }
    }

    const wickedStore = {
        getSel: () => { try { return JSON.parse(localStorage[WICKED_SEL_KEY] || '[]') } catch { return [] } },
        setSel: d  => { try { localStorage[WICKED_SEL_KEY] = JSON.stringify(d); return 1 } catch { return 0 } },
        clrSel: () => delete localStorage[WICKED_SEL_KEY]
    };

    const wickedState = { sel: [], vis: false };

    // --- FULL PREMIUM CSS ---
    const css = document.createElement('style');
    css.textContent = `
        /* iOS-inspired Sleek Minimalist Theme */
        .wicked-select-overlay{position:absolute;top:8px;left:8px;width:24px;height:24px;background:rgba(10,132,255,.9);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:none;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:14px;color:#fff;opacity:0;transition:all .3s cubic-bezier(.16,1,.3,1);z-index:10;pointer-events:none;box-shadow:0 4px 12px rgba(10,132,255,.4);transform:scale(0.8)}
        body.wicked-active .item:hover .wicked-select-overlay{opacity:0.5;transform:scale(1)}
        body.wicked-active .item.wicked-selected .wicked-select-overlay{opacity:1;transform:scale(1)}
        .item.wicked-selected::before{content:"";position:absolute;inset:0;border:2px solid #0A84FF;border-radius:inherit;pointer-events:none;z-index:5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.2)}
        .item{position:relative}

        .wp-wicked-sort-btn{display:inline-flex;align-items:center;gap:6px;background:rgba(28,28,30,.75);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);color:#fff;border:1px solid rgba(255,255,255,.1);border-radius:20px;padding:6px 12px;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;font-size:13px;font-weight:500;transition:all .3s;outline:none;margin-left:12px;vertical-align:middle;box-shadow:0 4px 12px rgba(0,0,0,.15)}
        .wp-wicked-sort-btn:hover{background:rgba(44,44,46,.9)}
        body.wicked-active .wp-wicked-sort-btn[style*="fixed"]{opacity:0;pointer-events:none;transform:translateY(10px) scale(0.9)}

        .wicked-icon{font-weight:600;letter-spacing:-0.3px}
        .wicked-badge{background:#0A84FF;color:#fff;padding:2px 6px;border-radius:10px;font-size:11px;font-weight:700;min-width:16px;text-align:center;display:none;transition:transform .2s}
        .wicked-badge.visible{display:inline-block}

        .wicked-logo-badge{position:absolute;top:-4px;right:-8px;background:#0A84FF;color:#fff;padding:2px 6px;border-radius:10px;font-size:11px;font-weight:700;min-width:16px;text-align:center;box-shadow:0 2px 8px rgba(10,132,255,.4);cursor:pointer;z-index:1000;display:none}

        /* iOS Pill Control Panel */
        #wicked-control-panel{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(20px);background:rgba(28,28,30,.85);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);border:1px solid rgba(255,255,255,.1);border-radius:40px;padding:8px 12px;box-shadow:0 12px 40px rgba(0,0,0,.4);z-index:10000;display:none!important;flex-direction:row;align-items:center;gap:6px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;min-width:auto;white-space:nowrap}
        #wicked-control-panel.visible{display:flex!important;animation:slideUpPill .4s cubic-bezier(.16,1,.3,1) forwards}
        @keyframes slideUpPill{from{opacity:0;transform:translate(-50%,20px) scale(.95)}to{opacity:1;transform:translate(-50%,0) scale(1)}}

        .wicked-counter{font-size:14px;font-weight:500;color:#fff;display:flex;align-items:center;gap:6px;padding-left:4px}
        #wicked-count{background:#0A84FF;color:#fff;padding:2px 8px;border-radius:12px;font-size:13px;display:inline-flex;align-items:center;justify-content:center;font-weight:600;min-width:24px;line-height:1.2;box-shadow:0 2px 6px rgba(10,132,255,.3)}
        .wicked-counter .mob-hide{color:#EBEBF5}
        .wicked-divider{width:1px;height:20px;background:rgba(255,255,255,.15);margin:0 4px}

        .wicked-btn{background:transparent;color:#fff;border:none;padding:8px 12px;border-radius:20px;cursor:pointer;font-weight:500;font-size:14px;transition:all .2s ease;display:flex;align-items:center;gap:6px;font-family:inherit}
        .wicked-btn:hover{background:rgba(255,255,255,.1)}
        .wicked-btn.primary{color:#0A84FF;font-weight:600}
        .wicked-btn.primary:hover{background:rgba(10,132,255,.15)}
        .wicked-btn.danger{color:#FF453A}
        .wicked-btn.danger:hover{background:rgba(255,69,58,.15)}
        .wicked-btn svg{width:18px;height:18px;stroke-width:2.5px;display:block}

        /* Fullscreen Overlay Styles */
        #wicked-fullscreen-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#000;z-index:9999999;overflow-y:auto;display:none;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
        #wicked-fullscreen-overlay::-webkit-scrollbar{width:0px;display:none}

        #grid-header{background:rgba(28,28,30,.8);backdrop-filter:blur(30px);-webkit-backdrop-filter:blur(30px);border-bottom:1px solid rgba(255,255,255,.1);padding:12px 20px;display:flex;align-items:center;position:sticky;top:0;z-index:100}
        .grid-logo{font-size:18px;font-weight:700;letter-spacing:-0.5px;color:#fff}
        .grid-logo-text{color:#fff}
        .grid-version{font-size:11px;background:rgba(255,255,255,.1);color:rgba(255,255,255,.8);padding:2px 6px;border-radius:8px;margin-left:6px;vertical-align:middle;font-weight:500;display:inline-block}
        #grid-header h2{font-size:15px;font-weight:500;display:flex;align-items:center;gap:8px;margin:0 0 0 16px;color:#fff}
        .video-count{color:rgba(255,255,255,.5)}
        .grid-header-actions{margin-left:auto;display:flex;gap:8px}
        .grid-header-btn{background:rgba(255,255,255,.1);color:#fff;border:none;padding:6px 14px;border-radius:16px;cursor:pointer;font-size:14px;font-weight:500;transition:all .2s;font-family:inherit}
        .grid-header-btn:hover{background:rgba(255,255,255,.2)}
        .grid-header-btn.close{color:#0A84FF;font-weight:600;background:transparent}
        .grid-header-btn.close:hover{background:rgba(10,132,255,.15)}

        .autoplay-badge{display:inline-flex;align-items:center;gap:4px;background:rgba(48,209,88,.15);color:#30D158;font-size:12px;font-weight:500;padding:4px 10px;border-radius:12px;margin-left:12px}

        #grid-viewer-container{display:grid;grid-template-columns:1fr;gap:16px;padding:16px;max-width:1920px;margin:0 auto}

        .video-grid-item{background:transparent;border:none;border-radius:16px;overflow:visible;position:relative;animation:fadeInUp .4s cubic-bezier(.16,1,.3,1) backwards}
        @keyframes fadeInUp{from{opacity:0;transform:translateY(20px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}

        .vid-thumb-wrap{position:relative;width:100%;aspect-ratio:16/9;background:#1C1C1E;overflow:hidden;cursor:pointer;border-radius:16px;box-shadow:0 4px 20px rgba(0,0,0,.3);border:1px solid rgba(255,255,255,.05);transition:transform .3s cubic-bezier(.16,1,.3,1),box-shadow .3s}
        .vid-thumb{width:100%;height:100%;object-fit:cover;display:block;transition:transform .3s ease, opacity .3s;position:absolute;inset:0}

        .vid-thumb-wrap:active{transform:scale(0.97)}
        @media(hover:hover){
            .vid-thumb-wrap:hover{transform:scale(1.02);box-shadow:0 8px 30px rgba(0,0,0,.5)}
            .vid-thumb-wrap:hover .vid-thumb{transform:scale(1.03)}
            .vid-thumb-wrap:has(.vid-preview):hover .vid-thumb{opacity:0}
            .vid-thumb-wrap:has(.vid-preview):hover .vid-preview{opacity:1}
            .vid-thumb-wrap:hover .vid-action-btns{opacity:1}
            .vid-thumb-wrap:hover .vid-play-btn{opacity:1;transform:scale(1)}
            .vid-thumb-wrap:has(.vid-preview):hover .vid-play-btn{opacity:0}
        }

        .vid-thumb-wrap.autoplay-active:has(.vid-preview) .vid-thumb{opacity:0;transform:scale(1.03)}
        .vid-preview{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .4s;pointer-events:none}
        .vid-thumb-wrap.autoplay-active .vid-preview{opacity:1}

        .vid-play-btn{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;opacity:0;transition:all .3s cubic-bezier(.16,1,.3,1);pointer-events:none;z-index:2;transform:scale(0.9)}
        .vid-play-btn svg{width:48px;height:48px;filter:drop-shadow(0 4px 12px rgba(0,0,0,.4))}
        .vid-play-btn circle{fill:rgba(255,255,255,.25);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
        .vid-thumb-wrap.autoplay-active .vid-play-btn{opacity:0}

        .vid-duration{position:absolute;bottom:10px;right:10px;background:rgba(0,0,0,.6);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#fff;font-size:12px;font-weight:600;padding:4px 8px;border-radius:8px;pointer-events:none;z-index:3;letter-spacing:0.5px}

        .vid-rating-bar{position:absolute;bottom:0;left:0;right:0;height:4px;background:rgba(255,255,255,.1);z-index:3}
        .vid-rating-fill{height:100%;background:#30D158;border-radius:0 2px 2px 0}

        .vid-action-btns{position:absolute;top:10px;right:10px;display:flex;gap:8px;opacity:0;transition:opacity .2s;z-index:4}
        .vid-action-btn{width:32px;height:32px;border-radius:16px;padding:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.5);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#fff;border:none;cursor:pointer;font-size:16px;font-weight:500;transition:all .2s;box-shadow:0 2px 8px rgba(0,0,0,.2)}
        .vid-action-btn.remove:hover{background:#FF453A}
        .vid-action-btn.reload:hover{background:#0A84FF}

        .vid-player{width:100%;height:100%;object-fit:cover;background:#000;display:block;position:absolute;inset:0;z-index:5}
        .vid-player-loading{position:absolute;inset:0;z-index:6;background:rgba(0,0,0,.5);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:#fff;font-size:13px;font-weight:500}
        .vid-player-loading::before{content:"";width:24px;height:24px;border:3px solid rgba(255,255,255,.2);border-top-color:#fff;border-radius:50%;animation:spin 1s linear infinite}
        @keyframes spin{to{transform:rotate(360deg)}}

        .vid-info{padding:12px 4px}
        .vid-title{font-size:15px;font-weight:500;color:#fff;text-decoration:none;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;transition:color .2s;letter-spacing:-0.2px}
        .vid-title:hover{color:#0A84FF}
        .vid-meta{display:flex;align-items:center;gap:12px;margin-top:6px;font-size:13px;color:#8E8E93;flex-wrap:wrap;font-weight:400}
        .vid-meta-rating{color:#30D158;font-weight:500}

        .vid-error{position:absolute;inset:0;background:#1C1C1E;z-index:5;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#FF453A;gap:8px;font-size:13px;font-weight:500}
        .vid-error::before{content:"⚠";font-size:24px}

        .vid-thumb-wrap.fetching-preview .vid-thumb{animation:previewFetch 1.5s ease-in-out infinite}
        @keyframes previewFetch{0%,100%{opacity:1}50%{opacity:.6}}

        @media(min-width:600px){
            #wicked-control-panel{bottom:32px;padding:10px 20px;border-radius:40px;gap:8px}
            .wicked-btn{font-size:15px;padding:10px 16px}
            #grid-header{padding:16px 32px}
            .grid-header-actions{gap:12px}
            #grid-viewer-container{grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:24px;padding:24px 32px}
            .vid-action-btns { opacity: 0; }
        }
        @media(max-width:599px){
            .mob-hide { display: none !important; }
            #grid-header { padding: 10px 12px; }
            .grid-logo { font-size: 16px; }
            #grid-header h2 { margin: 0 0 0 8px; font-size: 14px; }
            .grid-header-actions { gap: 4px; }
            .grid-header-btn { padding: 6px 12px; font-size: 13px; }
            .autoplay-badge { padding: 4px 6px; margin-left: 8px; border-radius: 8px; }
            .vid-action-btns { opacity: 1; transform: scale(0.9); transform-origin: top right; }
            .vid-play-btn { opacity: 1; background: transparent; }
            .vid-thumb-wrap:has(.vid-preview) .vid-play-btn { opacity: 0; }
            #wicked-control-panel { bottom: 16px; padding: 6px 10px; border-radius: 30px; gap: 4px; width: auto; max-width: 95%; background: rgba(28,28,30,0.95); box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
            .wicked-counter { font-size: 13px; padding: 0 2px; }
            #wicked-count { font-size: 12px; padding: 2px 6px; min-width: 20px; }
            .wicked-btn { padding: 6px; width: 36px; height: 36px; border-radius: 18px; justify-content: center; }
            .wicked-btn svg { margin: 0; width: 18px; height: 18px; }
        }
        @media(min-width:1200px){
            #grid-header{padding:16px 48px}
            #grid-viewer-container{grid-template-columns:repeat(auto-fill,minmax(400px,1fr));gap:32px;padding:32px 48px}
        }
        @media(min-width:1800px){
            #grid-viewer-container{grid-template-columns:repeat(auto-fill,minmax(480px,1fr))}
        }
    `;
    document.head.appendChild(css);

    wickedState.sel = wickedStore.getSel();

    function createWickedButton() {
        if (document.getElementById('wicked-toggle-btn')) return;
        const sortEl = document.querySelector('.grid__headline .sort, .headline .sort, .headline-wrap');
        const btn = document.createElement('button');
        btn.id        = 'wicked-toggle-btn';
        btn.className = 'wp-wicked-sort-btn';
        btn.innerHTML = `<strong class="wicked-icon">Wicked</strong><span class="wicked-badge" id="wicked-badge">0</span>`;
        btn.onclick   = toggleWickedPanel;
        if (sortEl) {
            sortEl.after(btn);
        } else {
            btn.style.position = 'fixed';
            btn.style.bottom = '20px';
            btn.style.left = '20px';
            btn.style.zIndex = '9999';
            document.body.appendChild(btn);
        }
        updateWickedUI();
    }

    function createLogoBadge() {
        if (document.querySelector('.wicked-logo-badge')) return;
        const logoLink = document.querySelector('a.header__logo.js-logo');
        if (!logoLink) return;
        logoLink.style.position = 'relative';
        const badge = document.createElement('div');
        badge.className   = 'wicked-logo-badge';
        badge.textContent = wickedState.sel.length;
        badge.style.display = wickedState.sel.length > 0 ? 'block' : 'none';
        badge.onclick = e => { e.preventDefault(); e.stopPropagation(); toggleWickedPanel(); };
        logoLink.appendChild(badge);
    }

    function createWickedPanel() {
        if (document.getElementById('wicked-control-panel')) return;
        const p = document.createElement('div');
        p.id = 'wicked-control-panel';
        p.innerHTML = `
            <div class="wicked-counter"><span id="wicked-count">0</span> <span class="mob-hide">Selected</span></div>
            <div class="wicked-divider"></div>
            <button class="wicked-btn primary" id="open-wicked-btn" title="Open Grid Viewer">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>
                <span class="mob-hide">Grid</span>
            </button>
            <button class="wicked-btn primary" id="share-wicked-btn" title="Share Selection">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path><polyline points="16 6 12 2 8 6"></polyline><line x1="12" y1="2" x2="12" y2="15"></line></svg>
                <span class="mob-hide">Share</span>
            </button>
            <button class="wicked-btn danger" id="clear-wicked-btn" title="Clear Selection">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
                <span class="mob-hide">Clear</span>
            </button>
            <div class="wicked-divider"></div>
            <button class="wicked-btn" id="close-panel-btn" title="Close Panel" style="color:#8E8E93">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
            </button>`;
        document.body.appendChild(p);
        p.querySelector('#open-wicked-btn').onclick  = openFullscreenGrid;
        p.querySelector('#share-wicked-btn').onclick = generateShareLink;
        p.querySelector('#clear-wicked-btn').onclick = clearWickedSelection;
        p.querySelector('#close-panel-btn').onclick  = toggleWickedPanel;
        updateWickedUI();
    }

    function updateWickedUI() {
        const n = wickedState.sel.length;
        const badge1 = document.getElementById('wicked-badge');
        if (badge1) { badge1.textContent = n; badge1.classList.toggle('visible', n > 0); }

        const badge2 = document.querySelector('.wicked-logo-badge');
        if (badge2) { badge2.textContent = n; badge2.style.display = n > 0 ? 'block' : 'none'; }

        const c = document.getElementById('wicked-count');
        if (c) c.textContent = n;
    }

    function toggleWickedPanel() {
        const p = document.getElementById('wicked-control-panel');
        if (!p) return;
        wickedState.vis = !wickedState.vis;
        p.classList.toggle('visible', wickedState.vis);
        document.body.classList.toggle('wicked-active', wickedState.vis);
    }

    function clearWickedSelection() {
        wickedState.sel = []; wickedStore.clrSel(); updateWickedUI();
        document.querySelectorAll('.item.wicked-selected').forEach(el => el.classList.remove('wicked-selected'));
    }

    function processVideoThumbnails() {
        document.querySelectorAll('.item:not([data-wicked-processed])').forEach(card => {
            card.dataset.wickedProcessed = '1';
            const link = card.querySelector('a[href*="/videos/"]');
            if (!link) return;
            const url = link.href;

            const ov = document.createElement('div');
            ov.className = 'wicked-select-overlay';
            ov.innerHTML = '✓';
            const imgWrap = card.querySelector('.box-img') || card.querySelector('.thumb__img') || card.querySelector('.img') || card;
            imgWrap.appendChild(ov);

            if (wickedState.sel.some(v => v.url === url)) card.classList.add('wicked-selected');

            const toggle = (e) => {
                if (!wickedState.vis) return;
                if (!(e.ctrlKey || e.metaKey) && e.type !== 'contextmenu') return;
                e.preventDefault(); e.stopPropagation();

                const i = wickedState.sel.findIndex(v => v.url === url);
                if (i > -1) {
                    wickedState.sel.splice(i, 1);
                    card.classList.remove('wicked-selected');
                } else {
                    const title = link.getAttribute('title') || card.querySelector('.name, .title, strong.title, .thumb__title')?.textContent.trim() || 'Video';

                    const newObj = {
                        url, title,
                        thumb:    extractThumb(card),
                        preview:  extractPreviewMp4(card),
                        duration: extractDuration(card),
                        rating:   extractRating(card),
                        views:    extractViews(card),
                        id:       extractId(url)
                    };
                    wickedState.sel.push(newObj);
                    card.classList.add('wicked-selected');
                }

                wickedStore.setSel(wickedState.sel);
                updateWickedUI();
            };

            card.addEventListener('click', toggle, true);
            card.addEventListener('contextmenu', toggle, true);
        });
    }

    // --- FULLSCREEN GRID VIEWER ---
    function openFullscreenGrid() {
        if (!wickedState.sel.length) return alert('Select videos first (Right-Click or Ctrl+Click on thumbnails)!');

        let overlay = document.getElementById('wicked-fullscreen-overlay');
        if (!overlay) {
            overlay = document.createElement('div');
            overlay.id = 'wicked-fullscreen-overlay';
            document.body.appendChild(overlay);
        }

        document.body.style.overflow = 'hidden'; // Stop background scrolling
        overlay.style.display = 'block';
        overlay.innerHTML = `
            <div id="grid-header">
                <div class="grid-logo"><span class="grid-logo-text">Wicked</span><span class="grid-version mob-hide">v1.2</span></div>
                <h2><span class="video-count"><span id="grid-video-count-num">${wickedState.sel.length}</span> <span class="mob-hide" style="font-weight:normal">videos</span></span></h2>
                ${autoPlayAll ? `<span class="autoplay-badge" title="Auto-preview Enabled"><svg viewBox="0 0 24 24" fill="currentColor" width="10" height="10" style="margin-top:1px"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg><span class="mob-hide" style="margin-left:4px">Auto-preview</span></span>` : ''}
                <div class="grid-header-actions">
                    <button class="grid-header-btn" id="share-grid-btn">Share</button>
                    <button class="grid-header-btn close" id="close-grid-btn">Done</button>
                </div>
            </div>
            <div id="grid-viewer-container"></div>
        `;

        document.getElementById('close-grid-btn').onclick = () => {
            overlay.style.display = 'none';
            overlay.innerHTML = ''; // Kill players instantly
            document.body.style.overflow = ''; // Restore scroll position
        };
        document.getElementById('share-grid-btn').onclick = generateShareLink;

        const container = overlay.querySelector('#grid-viewer-container');

        const autoPlayObserver = autoPlayAll ? new IntersectionObserver(entries => {
            entries.forEach(entry => {
                const tw   = entry.target;
                const prev = tw.querySelector('.vid-preview');
                if (!prev) return;
                if (entry.isIntersecting) {
                    tw.classList.add('autoplay-active');
                    prev.play().catch(() => {});
                } else {
                    tw.classList.remove('autoplay-active');
                    prev.pause(); prev.currentTime = 0;
                }
            });
        }, { rootMargin: '200px', threshold: 0.1 }) : null;

        function backgroundFetchPreviews(vids, items) {
            const missing = vids.map((v, i) => ({ v, i })).filter(x => !x.v.preview);
            if (!missing.length) return;

            let delay = 0;
            missing.forEach(({ v, i }) => {
                setTimeout(() => {
                    const item      = items[i];
                    const thumbWrap = item?.querySelector('.vid-thumb-wrap');
                    if (thumbWrap) thumbWrap.classList.add('fetching-preview');

                    fetchPreviewUrl(v.url, previewUrl => {
                        if (thumbWrap) thumbWrap.classList.remove('fetching-preview');
                        if (!previewUrl) return;

                        previewUrl = fixUrl(previewUrl, 'preview');

                        const stored = wickedStore.getSel();
                        const idx    = stored.findIndex(s => s.url === v.url);
                        if (idx > -1) {
                            stored[idx].preview = previewUrl;
                            wickedStore.setSel(stored);
                        }

                        if (!thumbWrap || thumbWrap.querySelector('.vid-preview')) return;
                        const videoEl = document.createElement('video');
                        videoEl.className   = 'vid-preview';
                        videoEl.src         = previewUrl;
                        videoEl.muted       = true;
                        videoEl.loop        = true;
                        videoEl.playsInline = true;
                        videoEl.preload     = 'auto';
                        const thumbImg = thumbWrap.querySelector('.vid-thumb');
                        thumbImg ? thumbImg.after(videoEl) : thumbWrap.prepend(videoEl);

                        if (autoPlayAll && autoPlayObserver) {
                            autoPlayObserver.observe(thumbWrap);
                        } else {
                            thumbWrap.addEventListener('mouseenter', () => videoEl.play().catch(() => {}));
                            thumbWrap.addEventListener('mouseleave', () => { videoEl.pause(); videoEl.currentTime = 0; });
                        }
                    });
                }, delay);
                delay += 800;
            });
        }

        const itemEls = [];

        wickedState.sel.forEach((vid, idx) => {
            const item = document.createElement('div');
            item.className = 'video-grid-item';
            item.style.animationDelay = `${Math.min(idx * 0.04, 0.4)}s`;
            itemEls.push(item);

            const thumb     = vid.thumb || '';
            const preview   = vid.preview  || '';
            const dur       = vid.duration || '';
            const rating    = vid.rating   || '';
            const views     = vid.views    || '';
            const ratingNum = parseInt(rating) || 0;

            item.innerHTML = `
                <div class="vid-thumb-wrap">
                    <img class="vid-thumb" src="${thumb}" onerror="this.style.opacity='0.2'">
                    ${preview ? `<video class="vid-preview" src="${preview}" muted loop playsinline></video>` : ''}
                    <div class="vid-play-btn">
                        <svg viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
                            <circle cx="28" cy="28" r="28" fill="rgba(0,0,0,0.5)"/>
                            <polygon points="22,16 42,28 22,40" fill="white"/>
                        </svg>
                    </div>
                    ${dur ? `<span class="vid-duration">${dur}</span>` : ''}
                    ${ratingNum > 0 ? `<div class="vid-rating-bar"><div class="vid-rating-fill" style="width:${ratingNum}%"></div></div>` : ''}
                    <div class="vid-action-btns">
                        <button class="vid-action-btn reload" title="Reset">↻</button>
                        <button class="vid-action-btn remove" title="Remove">✕</button>
                    </div>
                </div>
                <div class="vid-info">
                    <a href="${vid.url}" target="_blank" class="vid-title">${vid.title}</a>
                    <div class="vid-meta">
                        ${dur    ? `<span>${dur}</span>`                           : ''}
                        ${views  ? `<span>${views}</span>`                         : ''}
                        ${rating ? `<span class="vid-meta-rating">★ ${rating}</span>` : ''}
                    </div>
                </div>
            `;
            container.appendChild(item);

            const thumbWrap = item.querySelector('.vid-thumb-wrap');
            const previewEl = item.querySelector('.vid-preview');

            if (previewEl) {
                if (autoPlayAll && autoPlayObserver) {
                    autoPlayObserver.observe(thumbWrap);
                } else {
                    thumbWrap.addEventListener('mouseenter', () => previewEl.play().catch(() => {}));
                    thumbWrap.addEventListener('mouseleave', () => { previewEl.pause(); previewEl.currentTime = 0; });
                }
            }

            thumbWrap.onclick = e => {
                if (e.target.classList.contains('vid-action-btn')) return;
                if (previewEl) { previewEl.pause(); previewEl.currentTime = 0; }
                loadRealPlayer(vid, item);
            };

            item.querySelector('.vid-action-btn.reload').onclick = e => {
                e.stopPropagation();
                const tw = item.querySelector('.vid-thumb-wrap');
                tw.querySelectorAll('.vid-player,.vid-error,.vid-player-loading,iframe').forEach(el => el.remove());
                tw.querySelectorAll('.vid-thumb,.vid-preview,.vid-play-btn,.vid-duration,.vid-rating-bar,.vid-action-btns').forEach(el => el.style.display = '');
                tw.style.cursor = 'pointer';
                tw.classList.remove('autoplay-active');
                const prev = tw.querySelector('.vid-preview');
                if (prev && autoPlayAll && autoPlayObserver) autoPlayObserver.observe(tw);
            };

            item.querySelector('.vid-action-btn.remove').onclick = e => {
                e.stopPropagation();
                if (confirm('Remove this video from selection?')) {
                    const currentIdx = wickedState.sel.findIndex(v => v.url === vid.url);
                    if (currentIdx > -1) wickedState.sel.splice(currentIdx, 1);

                    wickedStore.setSel(wickedState.sel);
                    updateWickedUI();

                    // remove from Grid UI instantly without reloading
                    item.remove();
                    const countNum = document.getElementById('grid-video-count-num');
                    if (countNum) countNum.textContent = wickedState.sel.length;

                    // remove visual selection on main page too
                    const matchingCard = document.querySelector(`.item a[href="${vid.url}"]`)?.closest('.item');
                    if (matchingCard) matchingCard.classList.remove('wicked-selected');
                }
            };
        });

        setTimeout(() => backgroundFetchPreviews(wickedState.sel, itemEls), 1500);
    }

    function loadRealPlayer(vid, item) {
        const thumbWrap = item.querySelector('.vid-thumb-wrap');
        if (thumbWrap.querySelector('.vid-player, iframe')) return;

        const loader = document.createElement('div');
        loader.className = 'vid-player-loading';
        loader.innerHTML = '<span>Loading Secure Player…</span>';
        thumbWrap.appendChild(loader);

        const iframe = document.createElement('iframe');
        iframe.src = vid.url; // Load the actual video page
        // Sandbox prevents frame-busters from redirecting our parent page!
        iframe.sandbox = "allow-scripts allow-same-origin allow-forms allow-popups";
        iframe.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;border:none;background:#000;z-index:5;opacity:0;transition:opacity 0.3s;';
        iframe.allowFullscreen = true;

        iframe.onload = () => {
            try {
                const doc = iframe.contentDocument || iframe.contentWindow.document;

                // 1. Hide ALL top-level elements first (Destroys related videos, headers, comments)
                Array.from(doc.body.children).forEach(c => c.style.setProperty('display', 'none', 'important'));

                // 2. Find the exact native player container using specific IDs
                const player = doc.getElementById('kt_player') || doc.querySelector('.video-holder') || doc.querySelector('.main-player') || doc.querySelector('.player-holder') || doc.querySelector('.video-wrap') || doc.querySelector('video')?.parentElement;

                if (player) {
                    // 3. Make the player absolutely massive and fixed on top
                    player.style.cssText = 'display:block!important;position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;max-width:100vw!important;max-height:100vh!important;z-index:2147483647!important;background:#000!important;margin:0!important;padding:0!important;visibility:visible!important;opacity:1!important;';

                    // 4. Walk back up the DOM tree and unhide ONLY the wrappers needed to reach the body
                    let p = player.parentElement;
                    while (p && p !== doc.body) {
                        p.style.cssText += ';display:block!important;position:static!important;transform:none!important;filter:none!important;opacity:1!important;visibility:visible!important;width:100%!important;height:100%!important;padding:0!important;margin:0!important;';
                        p = p.parentElement;
                    }

                    // 5. Try to auto-play the video
                    setTimeout(() => {
                        const v = player.querySelector('video');
                        if (v) v.play().catch(()=>{});
                    }, 500);
                }

                // 6. Extra CSS backup to prevent scrollbars
                const style = doc.createElement('style');
                style.textContent = 'html,body{overflow:hidden!important;background:#000!important;margin:0!important;padding:0!important}';
                doc.head.appendChild(style);

            } catch(e) {}

            loader.remove();
            iframe.style.opacity = '1';
        };

        // Fallback to remove loader if iframe is slow
        setTimeout(() => { if(loader.parentNode) loader.remove(); iframe.style.opacity = '1'; }, 5000);

        thumbWrap.querySelectorAll('.vid-thumb,.vid-preview,.vid-play-btn').forEach(el => el.style.display = 'none');
        thumbWrap.classList.remove('autoplay-active');
        thumbWrap.style.cursor = 'default';
        thumbWrap.appendChild(iframe);
    }

    document.addEventListener('keydown', e => {
        if (e.ctrlKey || e.metaKey) return;
        const tag = document.activeElement?.tagName;
        if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
        if (document.activeElement?.isContentEditable) return;
        if ((e.key === 'g' || e.key === 'G') && wickedState.sel.length) { e.preventDefault(); openFullscreenGrid(); }
        if (e.key === 'w' || e.key === 'W') { e.preventDefault(); toggleWickedPanel(); }
        if (e.key === 'Escape') {
            const overlay = document.getElementById('wicked-fullscreen-overlay');
            if (overlay && overlay.style.display === 'block') {
                document.getElementById('close-grid-btn')?.click();
            } else if (wickedState.vis) {
                toggleWickedPanel();
            }
        }
    });

    createLogoBadge();
    createWickedButton();
    createWickedPanel();
    processVideoThumbnails();
    checkShareLink();

    new MutationObserver(() => {
        processVideoThumbnails();
        if (!document.getElementById('wicked-toggle-btn')) createWickedButton();
        if (!document.querySelector('.wicked-logo-badge')) createLogoBadge();
    }).observe(document.body, { childList: true, subtree: true });

})();