Multi-select videos and open them in a beautiful instant Fullscreen Grid Viewer!
// ==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 });
})();