Auto-play video thumbnails

Auto-play video thumbnails with playback speed control and pause functionality on adult sites

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Auto-play video thumbnails
// @namespace    https://greasyfork.org/users/1168969
// @version      2.0.3.1
// @description  Auto-play video thumbnails with playback speed control and pause functionality on adult sites
// @author       6969RandomGuy6969
// @match        https://www.sxyprn.com/*
// @match        https://sxyprn.com/*
// @match        https://watchporn.to/*
// @match        https://yesporn.vip/*
// @match        https://www.theyarehuge.com/*
// @match        https://www.eporner.com/*
// @match        https://www.shyfap.net/*
// @match        https://www.wow.xxx/*
// @match        https://pornone.com/*
// @match        https://www.tnaflix.com/*
// @match        https://www.pornhits.com/*
// @match        https://hqporner.com/*
// @match        https://www.hqporner.com/*
// @match        https://m.hqporner.com/*
// @match        https://pornmz.com/*
// @match        https://youperv.com/*
// @match        https://*.youperv.com/*
// @match        https://superporn.com/*
// @match        https://www.superporn.com/*
// @grant        GM_registerMenuCommand
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @connect      *
// @icon         https://cdn-icons-png.flaticon.com/512/3998/3998861.png
// ==/UserScript==

(function () {
  'use strict';

  const SETTINGS = {
    playbackSpeed: GM_getValue('playbackSpeed', 1),
    isPaused: GM_getValue('isPaused', false),
    panelX: GM_getValue('panelX', null),
    panelY: GM_getValue('panelY', null)
  };

  const videoRegistry = new Set();
  let configPanel = null;

  const visibleVideos = new Set();
  const globalPlayObserver = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      const video = entry.target;
      if (entry.isIntersecting) {
        visibleVideos.add(video);
        if (!SETTINGS.isPaused && typeof video.play === 'function') video.play().catch(() => {});
      } else {
        visibleVideos.delete(video);
        if (typeof video.pause === 'function') video.pause();
      }
    });
  }, { rootMargin: '300px', threshold: 0.01 });

  // Block site's JS hover previews on handled cards to prevent disruption
  ['mouseenter', 'mouseleave', 'mouseover', 'mouseout', 'mousemove'].forEach(evt => {
    document.addEventListener(evt, e => {
      if (e.target instanceof Element && e.target.closest('.apt-handled')) {
        e.stopPropagation();
      }
    }, true);
  });

  function updateAllVideos() {
    videoRegistry.forEach(video => {
      if (video && video.parentNode) {
        if (typeof video.playbackRate !== 'undefined') {
          video.playbackRate = SETTINGS.playbackSpeed;
        }
        if (SETTINGS.isPaused) {
          if (typeof video.pause === 'function') video.pause();
        } else {
          if (visibleVideos.has(video)) {
            if (typeof video.play === 'function') video.play().catch(() => {});
          }
        }
      }
    });
  }

  function setPlaybackSpeed(speed) {
    speed = Math.max(0.25, Math.min(2, speed));
    SETTINGS.playbackSpeed = speed;
    GM_setValue('playbackSpeed', speed);
    updateAllVideos();
    updatePanelDisplay();
  }

  function togglePause() {
    SETTINGS.isPaused = !SETTINGS.isPaused;
    GM_setValue('isPaused', SETTINGS.isPaused);
    updateAllVideos();
    updatePanelDisplay();
  }

  function updatePanelDisplay() {
    if (!configPanel) return;
    const speedDisplay = configPanel.querySelector('.speed-display');
    const pauseBtn = configPanel.querySelector('.pause-btn');
    if (speedDisplay) speedDisplay.textContent = `${SETTINGS.playbackSpeed.toFixed(2)}x`;
    if (pauseBtn) pauseBtn.textContent = SETTINGS.isPaused ? '▶️ Play' : '⏸️ Pause';
  }

  function createConfigPanel() {
    if (configPanel) {
      configPanel.style.display = 'block';
      return;
    }

    const panel = document.createElement('div');
    panel.id = 'video-config-panel';
    panel.innerHTML = `
      <div class="panel-header">
        <span class="panel-title">Thumbnail Control</span>
        <button class="close-btn">×</button>
      </div>
      <div class="panel-body">
        <div class="speed-control">
          <button class="speed-btn minus-btn">−</button>
          <span class="speed-display">${SETTINGS.playbackSpeed.toFixed(2)}x</span>
          <button class="speed-btn plus-btn">+</button>
        </div>
        <button class="pause-btn">${SETTINGS.isPaused ? '▶️ Play' : '⏸️ Pause'}</button>
      </div>
    `;

    const style = document.createElement('style');
    style.textContent = `
      #video-config-panel {
        position: fixed;
        z-index: 999999;
        background: rgba(20, 20, 20, 0.95);
        backdrop-filter: blur(10px);
        -webkit-backdrop-filter: blur(10px);
        border: 1px solid rgba(255, 255, 255, 0.1);
        border-radius: 12px;
        padding: 0;
        font-family: Arial, sans-serif;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
        user-select: none;
        min-width: 160px;
        max-width: 200px;
      }
      #video-config-panel .panel-header {
        background: rgba(40, 40, 40, 0.8);
        padding: 8px 10px;
        border-bottom: 1px solid rgba(255, 255, 255, 0.1);
        display: flex;
        justify-content: space-between;
        align-items: center;
        cursor: move;
        border-radius: 11px 11px 0 0;
      }
      #video-config-panel .panel-title {
        color: #fff;
        font-size: 12px;
        font-weight: bold;
        text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
        white-space: nowrap;
      }
      #video-config-panel .close-btn {
        background: rgba(255, 255, 255, 0.1);
        border: none;
        color: #fff;
        font-size: 20px;
        font-weight: bold;
        cursor: pointer;
        padding: 0;
        width: 22px;
        height: 22px;
        line-height: 20px;
        border-radius: 4px;
        transition: all 0.2s;
        flex-shrink: 0;
      }
      #video-config-panel .close-btn:hover {
        background: rgba(255, 68, 68, 0.8);
        box-shadow: 0 0 10px rgba(255, 68, 68, 0.5);
      }
      #video-config-panel .panel-body {
        padding: 12px;
        background: rgba(30, 30, 30, 0.5);
        border-radius: 0 0 11px 11px;
      }
      #video-config-panel .speed-control {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        margin-bottom: 10px;
      }
      #video-config-panel .speed-btn {
        background: rgba(255, 255, 255, 0.15);
        backdrop-filter: blur(5px);
        -webkit-backdrop-filter: blur(5px);
        border: 1px solid rgba(255, 255, 255, 0.2);
        color: #fff;
        font-size: 18px;
        font-weight: bold;
        width: 32px;
        height: 32px;
        border-radius: 6px;
        cursor: pointer;
        transition: all 0.2s;
        padding: 0;
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
        display: flex;
        align-items: center;
        justify-content: center;
      }
      #video-config-panel .speed-btn:hover {
        background: rgba(255, 255, 255, 0.2);
        border-color: rgba(255, 255, 255, 0.3);
        box-shadow: 0 0 10px rgba(255, 255, 255, 0.2);
      }
      #video-config-panel .speed-btn:active { transform: scale(0.95); }
      #video-config-panel .speed-display {
        color: #fff;
        font-size: 14px;
        font-weight: bold;
        min-width: 50px;
        text-align: center;
        text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
      }
      #video-config-panel .pause-btn {
        width: 100%;
        background: rgba(255, 255, 255, 0.15);
        backdrop-filter: blur(5px);
        -webkit-backdrop-filter: blur(5px);
        border: 1px solid rgba(255, 255, 255, 0.2);
        color: #fff;
        padding: 8px;
        font-size: 13px;
        font-weight: 600;
        border-radius: 6px;
        cursor: pointer;
        transition: all 0.2s;
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
      }
      #video-config-panel .pause-btn:hover {
        background: rgba(255, 255, 255, 0.2);
        border-color: rgba(255, 255, 255, 0.3);
        box-shadow: 0 0 10px rgba(255, 255, 255, 0.2);
      }
      #video-config-panel .pause-btn:active { transform: scale(0.98); }
      @media (max-width: 600px) {
        #video-config-panel { min-width: 140px; max-width: 160px; }
        #video-config-panel .panel-header { padding: 6px 8px; }
        #video-config-panel .panel-title { font-size: 11px; }
        #video-config-panel .close-btn { width: 20px; height: 20px; font-size: 18px; }
        #video-config-panel .panel-body { padding: 8px; }
        #video-config-panel .speed-btn { width: 28px; height: 28px; font-size: 16px; }
        #video-config-panel .speed-display { font-size: 12px; min-width: 40px; }
        #video-config-panel .pause-btn { padding: 6px; font-size: 12px; }
      }
      @media (max-width: 400px) {
        #video-config-panel { min-width: 120px; max-width: 140px; }
        #video-config-panel .speed-control { gap: 4px; }
        #video-config-panel .speed-btn { width: 24px; height: 24px; font-size: 14px; }
        #video-config-panel .speed-display { font-size: 11px; min-width: 35px; }
      }
    `;
    document.head.appendChild(style);

    document.body.appendChild(panel);
    configPanel = panel;

    const panelWidth = 200;
    const panelHeight = 120;
    const margin = 10;

    if (SETTINGS.panelX !== null && SETTINGS.panelY !== null) {
      const maxX = window.innerWidth - panelWidth - margin;
      const maxY = window.innerHeight - panelHeight - margin;
      panel.style.left = Math.max(margin, Math.min(SETTINGS.panelX, maxX)) + 'px';
      panel.style.top  = Math.max(margin, Math.min(SETTINGS.panelY, maxY)) + 'px';
    } else {
      panel.style.right  = Math.min(20, window.innerWidth  - panelWidth  - margin) + 'px';
      panel.style.bottom = Math.min(20, window.innerHeight - panelHeight - margin) + 'px';
    }

    panel.querySelector('.close-btn').addEventListener('click', () => { panel.style.display = 'none'; });
    panel.querySelector('.minus-btn').addEventListener('click', () => { setPlaybackSpeed(SETTINGS.playbackSpeed - 0.25); });
    panel.querySelector('.plus-btn').addEventListener('click',  () => { setPlaybackSpeed(SETTINGS.playbackSpeed + 0.25); });
    panel.querySelector('.pause-btn').addEventListener('click', togglePause);

    // Draggable
    let isDragging = false, currentX, currentY, initialX, initialY;
    const header = panel.querySelector('.panel-header');

    header.addEventListener('mousedown', e => {
      if (e.target.classList.contains('close-btn')) return;
      isDragging = true;
      initialX = e.clientX - (SETTINGS.panelX || panel.offsetLeft);
      initialY = e.clientY - (SETTINGS.panelY || panel.offsetTop);
    });

    document.addEventListener('mousemove', e => {
      if (!isDragging) return;
      e.preventDefault();
      const rect = panel.getBoundingClientRect();
      const maxX = window.innerWidth  - rect.width  - margin;
      const maxY = window.innerHeight - rect.height - margin;
      currentX = Math.max(margin, Math.min(e.clientX - initialX, maxX));
      currentY = Math.max(margin, Math.min(e.clientY - initialY, maxY));
      panel.style.left   = currentX + 'px';
      panel.style.top    = currentY + 'px';
      panel.style.right  = 'auto';
      panel.style.bottom = 'auto';
    });

    document.addEventListener('mouseup', () => {
      if (isDragging) {
        SETTINGS.panelX = currentX || panel.offsetLeft;
        SETTINGS.panelY = currentY || panel.offsetTop;
        GM_setValue('panelX', SETTINGS.panelX);
        GM_setValue('panelY', SETTINGS.panelY);
      }
      isDragging = false;
    });

    window.addEventListener('resize', () => {
      if (!configPanel || configPanel.style.display === 'none') return;
      const rect = configPanel.getBoundingClientRect();
      let newX = rect.left, newY = rect.top, needs = false;
      if (rect.right  > window.innerWidth  - margin) { newX = window.innerWidth  - rect.width  - margin; needs = true; }
      if (rect.left   < margin)                       { newX = margin;                                     needs = true; }
      if (rect.bottom > window.innerHeight - margin)  { newY = window.innerHeight - rect.height - margin; needs = true; }
      if (rect.top    < margin)                       { newY = margin;                                     needs = true; }
      if (needs) {
        configPanel.style.left = newX + 'px'; configPanel.style.top = newY + 'px';
        configPanel.style.right = 'auto';     configPanel.style.bottom = 'auto';
        SETTINGS.panelX = newX; SETTINGS.panelY = newY;
        GM_setValue('panelX', newX); GM_setValue('panelY', newY);
      }
    });
  }

  // ==================== GM MENU ====================
  GM_registerMenuCommand('⚙️ Open Config UI', createConfigPanel);

  // ── Supported Sites ──
  GM_registerMenuCommand('🌐 Sxyprn', () => GM_openInTab('https://sxyprn.com', { active: true }));
  GM_registerMenuCommand('🌐 WatchPorn', () => GM_openInTab('https://watchporn.to', { active: true }));
  GM_registerMenuCommand('🌐 YesPorn', () => GM_openInTab('https://yesporn.vip', { active: true }));
  GM_registerMenuCommand('🌐 TheyAreHuge', () => GM_openInTab('https://www.theyarehuge.com', { active: true }));
  GM_registerMenuCommand('🌐 Eporner', () => GM_openInTab('https://www.eporner.com', { active: true }));
  GM_registerMenuCommand('🌐 ShyFap', () => GM_openInTab('https://www.shyfap.net', { active: true }));
  GM_registerMenuCommand('🌐 Wow.xxx', () => GM_openInTab('https://www.wow.xxx', { active: true }));
  GM_registerMenuCommand('🌐 Pornone', () => GM_openInTab('https://pornone.com', { active: true }));
  GM_registerMenuCommand('🌐 Tnaflix', () => GM_openInTab('https://www.tnaflix.com', { active: true }));
  GM_registerMenuCommand('🌐 PornHits', () => GM_openInTab('https://www.pornhits.com', { active: true }));
  GM_registerMenuCommand('🌐 HQPorner', () => GM_openInTab('https://hqporner.com', { active: true }));
  GM_registerMenuCommand('🌐 PornMZ', () => GM_openInTab('https://pornmz.com', { active: true }));
  GM_registerMenuCommand('🌐 YouPerv', () => GM_openInTab('https://youperv.com', { active: true }));
  GM_registerMenuCommand('🌐 SuperPorn', () => GM_openInTab('https://www.superporn.com', { active: true }));

  // ── Script Links ──
  GM_registerMenuCommand('🔞 More Scripts (Sleazyfork)', () => {
    GM_openInTab('https://sleazyfork.org/en/users/1168969-6969randomguy6969', { active: true });
  });
  GM_registerMenuCommand('📜 More Scripts (Greasyfork)', () => {
    GM_openInTab('https://greasyfork.org/en/users/1168969-6969randomguy6969', { active: true });
  });

  // ==================== CORE FUNCTIONALITY ====================
  const hostname = window.location.hostname;

  // ---- FIX 1: Don't hide the image until the video is actually ready to play ----
  // ---- FIX 2: Keep the image visible as fallback if video fails ----
  function insertPreviewVideo(image, videoUrl) {
    if (!videoUrl) return;
    if (image.dataset.previewAttached) return;
    image.dataset.previewAttached = '1';

    const card = image.closest('a, article, .thumb, .card, .post, .mb, .media-card_preview') || image.parentNode;
    if (card) card.classList.add('apt-handled');

    const video = document.createElement('video');
    video.src = videoUrl;
    video.muted = true;
    video.loop = true;
    video.playsInline = true;
    video.preload = 'auto';
    video.playbackRate = SETTINGS.playbackSpeed;
    video.style.cssText = 'width:100%;height:100%;object-fit:cover;position:absolute;top:0;left:0;z-index:10;pointer-events:none;';

    video.setAttribute('importance', 'high');
    video.setAttribute('fetchpriority', 'high');

    videoRegistry.add(video);

    const parent = image.parentNode;
    parent.style.position = 'relative';
    parent.appendChild(video);

    const showVideo = () => {
      video.style.opacity = '1';
      image.style.opacity = '0';
      if (!SETTINGS.isPaused && visibleVideos.has(video)) video.play().catch(() => {});
    };

    // Try canplay first
    video.addEventListener('canplay', showVideo, { once: true });

    // Fallback: if canplay doesn't fire in 800ms, show anyway
    // (site JS may suppress the event by manipulating the element)
    const fallbackTimer = setTimeout(() => {
      video.removeEventListener('canplay', showVideo);
      showVideo();
    }, 800);

    video.addEventListener('canplay', () => clearTimeout(fallbackTimer), { once: true });

    video.load();
    globalPlayObserver.observe(video);

    video.onerror = () => {
      clearTimeout(fallbackTimer);
      globalPlayObserver.unobserve(video);
      visibleVideos.delete(video);
      videoRegistry.delete(video);
      video.remove();
      image.style.opacity = '1';
    };
  }

  // IntersectionObserver wrapper
  const activeObservers = new Set();

  function observeElements(selector, getUrl) {
    // Don't set up duplicate observers for the same selector
    if (activeObservers.has(selector)) return;
    activeObservers.add(selector);
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const el = entry.target;
          const url = getUrl(el);
          if (url) insertPreviewVideo(el, url);
          observer.unobserve(el);
        }
      });
    }, { rootMargin: '500px', threshold: 0.01 });

    const observedElements = new WeakSet();

    function watch() {
      document.querySelectorAll(selector).forEach(el => {
        if (!observedElements.has(el)) {
          observer.observe(el);
          observedElements.add(el);
        }
      });
    }

    watch();

    let mutationTimeout;
    new MutationObserver(() => {
      clearTimeout(mutationTimeout);
      mutationTimeout = setTimeout(watch, 100);
    }).observe(document.body, { childList: true, subtree: true });

    setTimeout(watch, 500);
    setTimeout(watch, 1500);
  }

  // ==================== SITE HANDLERS ====================
  const siteHandlers = {

    "eporner.com": function () {
      function getPreviewUrl(id) {
        const s = id.toString();
        if (s.length < 5) return null;
        return `https://static-eu-cdn.eporner.com/thumbs/static4/${s[0]}/${s.slice(0,2)}/${s.slice(0,3)}/${s}/${s}-preview.webm`;
      }

      function initThumb(thumb) {
        if (thumb.dataset.epInit) return;
        thumb.dataset.epInit = '1';
        thumb.classList.add('apt-handled');

        const url = getPreviewUrl(thumb.dataset.id);
        if (!url) return;

        let vid = thumb.querySelector('video');
        if (!vid) {
          vid = document.createElement('video');
          vid.muted = true;
          vid.loop = true;
          vid.playsInline = true;
          vid.preload = 'auto';
          vid.style.cssText = 'position:absolute !important;inset:0 !important;width:100% !important;height:100% !important;object-fit:cover !important;z-index:10 !important;background:#000;opacity:1 !important;pointer-events:none;';
          const cont = thumb.querySelector('.mbimg, .mbcontent') || thumb;
          cont.style.position = 'relative';
          cont.style.overflow = 'hidden';
          cont.appendChild(vid);
        }

        if (vid.src !== url) {
          vid.removeAttribute('src');
          vid.src = url;
          vid.load();
        }

        // Register for global pause/speed control
        videoRegistry.add(vid);
        vid.playbackRate = SETTINGS.playbackSpeed;
        globalPlayObserver.observe(vid);
      }

      // Hide static img/badges so video shows through
      const style = document.createElement('style');
      style.textContent = `.mb img,.mvhdico,[style*="ajax_loader"]{opacity:0 !important;pointer-events:none !important}.mb .mbimg,.mb .mbcontent{background:#000 !important}`;
      document.head.appendChild(style);

      // Process existing cards
      document.querySelectorAll('div.mb[data-id]').forEach(initThumb);

      // Watch for new cards (infinite scroll / AJAX)
      new MutationObserver(() => {
        document.querySelectorAll('div.mb[data-id]:not([data-ep-init])').forEach(initThumb);
      }).observe(document.body, { childList: true, subtree: true });

      // Safety interval for any missed cards
      setInterval(() => {
        document.querySelectorAll('div.mb[data-id]').forEach(initThumb);
      }, 4000);
    },

    "sxyprn.com": function () {
      function watchSxy() {
        document.querySelectorAll('.mini_post_vid_thumb').forEach(img => {
          if (img.dataset.sxInit) return;
          img.dataset.sxInit = '1';

          let handled = false;
          const video = img.nextElementSibling;

          if (video && video.tagName === 'VIDEO') {
            // Strip inline JS that restricts playback (e.g., hvponplay)
            video.removeAttribute('onplay');
            video.removeAttribute('onpause');
            video.preload = 'auto';

            // Force it to be visible without overriding native layout dimensions
            video.style.display = 'block';
            video.style.opacity = '1';
            video.style.pointerEvents = 'none'; // Ensure clicks hit the wrapper
            img.style.opacity = '0';

            const src = video.getAttribute('src') || video.getAttribute('data-src') || video.dataset.src;
            if (src && !video.getAttribute('src')) {
              video.setAttribute('src', src);
            }

            if (src || video.currentSrc || video.querySelector('source')) {
              handled = true;
              if (typeof video.playbackRate !== 'undefined') {
                video.playbackRate = SETTINGS.playbackSpeed;
              }
              videoRegistry.add(video);
              globalPlayObserver.observe(video);
            }
          } else {
            // Fallback for elements without native video tag
            const fallbackSrc = img.getAttribute('data-video') || img.getAttribute('data-preview');
            if (fallbackSrc) {
              handled = true;
              insertPreviewVideo(img, fallbackSrc);
            }
          }

          if (handled) {
            const card = img.closest('a, .post_el') || img.parentNode;
            if (card) card.classList.add('apt-handled');
          }
        });
      }
      watchSxy();
      new MutationObserver(watchSxy).observe(document.body, { childList: true, subtree: true });
      setInterval(watchSxy, 5000);
    },

    "watchporn.to": () =>
      observeElements('img[data-preview]', el => el.getAttribute('data-preview')),

    "yesporn.vip": () =>
      observeElements('img[data-preview]', el => el.getAttribute('data-preview')),

    "theyarehuge.com": function () {
      observeElements('img[data-preview]', el => el.getAttribute('data-preview'));

      const style = document.createElement('style');
      style.textContent = `
        body, html { background-color:#000 !important; color:#d1d1d1 !important; }
        * { background-color:transparent !important; border-color:#444 !important; color:inherit !important; }
        a, p, h1, h2, h3, h4, h5, h6, span, div { color:#d1d1d1 !important; }
        img, video { filter:brightness(0.95) contrast(1.1); }
        .header, .footer, .sidebar, .navbar, .top-menu, .main-header { background-color:#000 !important; }
      `;
      document.head.appendChild(style);

      const logo = document.querySelector('img[src*="tah-logo-m.png"]');
      if (logo) logo.style.filter = 'invert(1) hue-rotate(-180deg) brightness(1) saturate(10)';
    },

    "shyfap.net": function () {
      function watchShyfap() {
        document.querySelectorAll('.media-card_preview').forEach(card => {
          if (card.dataset.sfInit) return;
          card.dataset.sfInit = '1';

          const videoUrl = card.getAttribute('data-preview');
          const video = card.querySelector('video');
          const img   = card.querySelector('img');

          let handled = false;
          if (video) {
            handled = true;
            video.muted = true;
            video.loop = true;
            video.playsInline = true;
            video.preload = 'auto';
            video.playbackRate = SETTINGS.playbackSpeed;
            video.style.width = '100%';
            video.style.height = '100%';
            video.style.objectFit = 'cover';
            video.style.pointerEvents = 'none';
            videoRegistry.add(video);
            if (img) img.style.display = 'none';
            globalPlayObserver.observe(video);
          } else if (img && videoUrl) {
            handled = true;
            insertPreviewVideo(img, videoUrl);
          }

          if (handled) card.classList.add('apt-handled');
        });
      }
      watchShyfap();
      new MutationObserver(watchShyfap).observe(document.body, { childList: true, subtree: true });
      setInterval(watchShyfap, 5000);
    },

    "wow.xxx": function () {
      observeElements('.thumb__img[data-preview] img', img => {
        const container = img.closest('.thumb__img');
        return container ? container.getAttribute('data-preview') : null;
      });
    },

    "pornone.com": function () {
      // pornone.com has NO mp4 previews — it cycles JPG frames from data-thumbs.
      // Frame URL: {data-path}d{thumbNum}.jpg
      // The site's own showImage() cycles every 800ms; we replicate it autonomously,
      // respecting isPaused and mapping playbackSpeed -> interval delay.
      // Speed 1x=800ms, 2x=400ms, 0.5x=1600ms, etc.

      const pnCyclers = new Map(); // img el -> { timer }

      function getIntervalMs() {
        return Math.round(800 / Math.max(0.25, SETTINGS.playbackSpeed));
      }

      function startCycler(img) {
        if (img.dataset.pnInit) return;
        img.dataset.pnInit = '1';

        const path = img.getAttribute('data-path');
        const thumbs = JSON.parse(img.getAttribute('data-thumbs') || '[]');
        if (!path || thumbs.length < 2) return;

        const card = img.closest('a, .thumb, .item') || img.parentNode;
        if (card) card.classList.add('apt-handled');

        // Preload first few frames
        thumbs.slice(0, 5).forEach(n => { (new Image()).src = path + 'd' + n + '.jpg'; });

        let idx = 1;
        img.dataset.pnIdx = '1';

        const state = { timer: null };
        pnCyclers.set(img, state);

        function tick() {
          if (!SETTINGS.isPaused) {
            img.src = path + 'd' + thumbs[idx] + '.jpg';
            // Preload next
            const next = thumbs[(idx + 1) % thumbs.length];
            (new Image()).src = path + 'd' + next + '.jpg';
            idx = (idx + 1) % thumbs.length;
            img.dataset.pnIdx = String(idx);
          }
          state.timer = setTimeout(tick, getIntervalMs());
        }

        if (!SETTINGS.isPaused) {
          state.timer = setTimeout(tick, getIntervalMs());
        } else {
          // Still schedule, but tick() will skip frame when paused
          state.timer = setTimeout(tick, getIntervalMs());
        }
      }

      // IntersectionObserver: only cycle visible thumbs
      const pnObserver = new IntersectionObserver(entries => {
        entries.forEach(entry => {
          const img = entry.target;
          const state = pnCyclers.get(img);
          if (entry.isIntersecting) {
            startCycler(img);
            // If previously stopped (scrolled away), restart
            if (state && !state.timer) {
              const path = img.getAttribute('data-path');
              const thumbs = JSON.parse(img.getAttribute('data-thumbs') || '[]');
              let idx = parseInt(img.dataset.pnIdx || '1', 10);
              function tick() {
                if (!SETTINGS.isPaused) {
                  img.src = path + 'd' + thumbs[idx] + '.jpg';
                  const next = thumbs[(idx + 1) % thumbs.length];
                  (new Image()).src = path + 'd' + next + '.jpg';
                  idx = (idx + 1) % thumbs.length;
                  img.dataset.pnIdx = String(idx);
                }
                state.timer = setTimeout(tick, getIntervalMs());
              }
              state.timer = setTimeout(tick, getIntervalMs());
            }
          } else {
            // Pause cycling off-screen to save resources
            if (state && state.timer) {
              clearTimeout(state.timer);
              state.timer = null;
            }
          }
        });
      }, { rootMargin: '300px', threshold: 0.01 });

      function watchThumbs() {
        document.querySelectorAll('img.thumbimg[data-thumbs][data-path]').forEach(img => {
          if (!img.dataset.pnObserved) {
            img.dataset.pnObserved = '1';
            pnObserver.observe(img);
          }
        });
      }

      watchThumbs();
      new MutationObserver(watchThumbs).observe(document.body, { childList: true, subtree: true });
      setTimeout(watchThumbs, 500);
      setTimeout(watchThumbs, 2000);
      setInterval(watchThumbs, 5000);
    },

    "tnaflix.com": function () {
      // tnaflix.com provides a direct `data-trailer` mp4 URL on the <a> tag.
      // Selector: a.thumb-chrome[data-trailer]  →  img inside it is the thumbnail.
      // Just grab data-trailer and hand it to insertPreviewVideo — cleanest possible.

      function initCard(anchor) {
        if (anchor.dataset.tnInit) return;
        anchor.dataset.tnInit = '1';

        const url = anchor.getAttribute('data-trailer');
        const img = anchor.querySelector('img');
        if (!url || !img) return;

        anchor.classList.add('apt-handled');
        insertPreviewVideo(img, url);
      }

      // IntersectionObserver — only load trailers near the viewport
      const tnObserver = new IntersectionObserver(entries => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            initCard(entry.target);
            tnObserver.unobserve(entry.target);
          }
        });
      }, { rootMargin: '400px', threshold: 0.01 });

      function watchCards() {
        document.querySelectorAll('a.thumb-chrome[data-trailer]').forEach(anchor => {
          if (!anchor.dataset.tnObserved) {
            anchor.dataset.tnObserved = '1';
            tnObserver.observe(anchor);
          }
        });
      }

      watchCards();
      new MutationObserver(watchCards).observe(document.body, { childList: true, subtree: true });
      setTimeout(watchCards, 500);
      setTimeout(watchCards, 2000);
      setInterval(watchCards, 5000);
    },

    "pornhits.com": function () {
      // pornhits.com: img[data-preview] carries the preview URL directly.
      // URL scheme: //pv2.pornhits.com/v2/preview/ID.mp4  (protocol-relative)
      // Just prefix https: and feed to insertPreviewVideo — done.

      observeElements('img[data-preview]', img => {
        const raw = img.getAttribute('data-preview');
        if (!raw) return null;
        return raw.startsWith('//') ? 'https:' + raw : raw;
      });
    },

    "hqporner.com": function () {
      // hqporner.com has NO mp4 previews — it cycles 10 JPG frames per card.
      // Each img[id^="slide"] or img[id^="cover_"] has src like: //cdn.../imgs/AA/BB/HASH_main.jpg
      // Frame URLs: //cdn.../imgs/AA/BB/HASH_1.jpg ... HASH_10.jpg  (10 frames)
      // Site cycles at 600ms; we replicate autonomously respecting isPaused/speed.
      // Speed 1x=600ms, 2x=300ms, 0.5x=1200ms etc.
      // The "play images" button just enables slideShowPlayer on click — we bypass
      // it entirely and start cycling immediately on IntersectionObserver trigger.

      const hqCyclers = new Map(); // img el -> { timer }

      function getIntervalMs() {
        return Math.round(600 / Math.max(0.25, SETTINGS.playbackSpeed));
      }

      function getFrameUrls(img) {
        // src: //fastporndelivery.hqporner.com/imgs/AA/BB/HASH_main.jpg
        // OR after "play images" click: HASH_10.jpg
        const src = img.getAttribute('src') || '';
        const base = src.replace(/https?:/, '').replace(/_(?:main|\d+)\.jpg$/, '');
        if (!base || !base.includes('/imgs/')) return null;
        const frames = [];
        for (let i = 1; i <= 10; i++) frames.push('https:' + base + '_' + i + '.jpg');
        return frames;
      }

      function startCycler(img) {
        if (img.dataset.hqInit) return;
        img.dataset.hqInit = '1';

        const frames = getFrameUrls(img);
        if (!frames) return;

        const card = img.closest('a, .video-box') || img.parentNode;
        if (card) card.classList.add('apt-handled');

        // Preload first 3 frames eagerly
        frames.slice(0, 3).forEach(u => { (new Image()).src = u; });

        let idx = 0;
        const state = { timer: null };
        hqCyclers.set(img, state);

        function tick() {
          if (!SETTINGS.isPaused) {
            img.setAttribute('src', frames[idx]);
            idx = (idx + 1) % frames.length;
            // Preload next
            (new Image()).src = frames[idx];
          }
          state.timer = setTimeout(tick, getIntervalMs());
        }
        state.timer = setTimeout(tick, getIntervalMs());
      }

      function stopCycler(img) {
        const state = hqCyclers.get(img);
        if (state && state.timer) {
          clearTimeout(state.timer);
          state.timer = null;
        }
      }

      function resumeCycler(img) {
        const state = hqCyclers.get(img);
        if (!state || state.timer) return;
        const frames = getFrameUrls(img);
        if (!frames) return;
        let idx = 0;
        function tick() {
          if (!SETTINGS.isPaused) {
            img.setAttribute('src', frames[idx]);
            idx = (idx + 1) % frames.length;
            (new Image()).src = frames[idx];
          }
          state.timer = setTimeout(tick, getIntervalMs());
        }
        state.timer = setTimeout(tick, getIntervalMs());
      }

      const hqObserver = new IntersectionObserver(entries => {
        entries.forEach(entry => {
          const img = entry.target;
          if (entry.isIntersecting) {
            startCycler(img);
            resumeCycler(img);
          } else {
            stopCycler(img);
          }
        });
      }, { rootMargin: '300px', threshold: 0.01 });

      function watchThumbs() {
        document.querySelectorAll('img[id^="slide"], img[id^="cover_"]').forEach(img => {
          if (!img.dataset.hqObserved) {
            img.dataset.hqObserved = '1';
            hqObserver.observe(img);
          }
        });
      }

      watchThumbs();
      new MutationObserver(watchThumbs).observe(document.body, { childList: true, subtree: true });
      setTimeout(watchThumbs, 500);
      setTimeout(watchThumbs, 2000);
      setInterval(watchThumbs, 5000);
    },

    "pornmz.com": function () {
      // pornmz.com: data-trailer on <article> carries a direct mp4 URL.
      // The thumbnail img.video-main-thumb sits inside .post-thumbnail-container
      // inside .post-thumbnail inside the <a> inside the <article>.
      // Strategy: find article[data-trailer], grab img.video-main-thumb inside it,
      // feed data-trailer to insertPreviewVideo — identical pattern to tnaflix.

      function initCard(article) {
        if (article.dataset.pmzInit) return;
        article.dataset.pmzInit = '1';

        const url = article.getAttribute('data-trailer');
        const img = article.querySelector('img.video-main-thumb');
        if (!url || !img) return;

        article.classList.add('apt-handled');
        insertPreviewVideo(img, url);
      }

      const pmzObserver = new IntersectionObserver(entries => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            initCard(entry.target);
            pmzObserver.unobserve(entry.target);
          }
        });
      }, { rootMargin: '400px', threshold: 0.01 });

      function watchCards() {
        document.querySelectorAll('article[data-trailer]').forEach(article => {
          if (!article.dataset.pmzObserved) {
            article.dataset.pmzObserved = '1';
            pmzObserver.observe(article);
          }
        });
      }

      watchCards();
      new MutationObserver(watchCards).observe(document.body, { childList: true, subtree: true });
      setTimeout(watchCards, 500);
      setTimeout(watchCards, 2000);
      setInterval(watchCards, 5000);
    },

    "youperv.com": function () {
      // 1. Inject Theme (as requested by user)
      let isDarkMode = GM_getValue('ypDarkMode', true);
      GM_registerMenuCommand('🌓 YouPerv Toggle Dark/Light Theme', () => {
          isDarkMode = !isDarkMode;
          GM_setValue('ypDarkMode', isDarkMode);
          location.reload();
      });

      const style = document.createElement('style');
      style.id = 'youperv-modern-theme';
      if (isDarkMode) {
          style.textContent = `
*{transition:background-color .2s ease,color .2s ease,border-color .2s ease!important}
body,.js{background:linear-gradient(135deg,#0a0a0a 0%,#1a1a1a 100%)!important;color:#b0b0b0!important;-webkit-font-smoothing:antialiased!important;-moz-osx-font-smoothing:grayscale!important}
.header img[src*="logo.png"],.header img[alt="logo"]{filter:drop-shadow(0 0 8px rgba(255,107,107,.6))drop-shadow(0 0 20px rgba(255,107,107,.3))!important}
.header,.nav,.footer,.bottom-nav{background:rgba(26,26,26,.95)!important;backdrop-filter:blur(10px)!important;-webkit-backdrop-filter:blur(10px)!important;border-color:rgba(255,107,107,.1)!important}
.header,.nav{box-shadow:0 4px 16px rgba(0,0,0,.3)!important}
.header a,.nav-in a{color:#b0b0b0!important;font-weight:500!important}
.header a:hover,.nav-in a:hover,.footer a:hover,a:hover{color:#ff8787!important}
.nav-in a{position:relative!important;overflow:hidden!important}
.nav-in a::before{content:''!important;position:absolute!important;bottom:0!important;left:0!important;width:0!important;height:2px!important;background:linear-gradient(90deg,#ff6b6b,#ff8787)!important;transition:width .3s ease!important}
.nav-in a:hover{background:rgba(255,135,135,.08)!important}
.nav-in a:hover::before{width:100%!important}
.items-title{background:linear-gradient(135deg,#fff 0%,#ff8787 100%)!important;-webkit-background-clip:text!important;-webkit-text-fill-color:transparent!important;background-clip:text!important;font-weight:600!important;letter-spacing:-.5px!important}
form[name="news_set_sort"] ul li,.catmen,.clouds_xsmall,.clouds_small,.clouds_medium,.clouds_large,.clouds_xlarge,.pagi-nav a,.pagi-nav span{transform:translateY(0)!important;transition:all .25s cubic-bezier(.4,0,.2,1)!important}
form[name="news_set_sort"] ul li,.items-sort li[class]{background:rgba(36,36,36,.8)!important;border-radius:20px!important;border:1px solid rgba(255,107,107,.1)!important;overflow:hidden!important;font-family:GothamProRegular,Tahoma,Geneva,sans-serif!important;font-weight:500!important;padding:8px 16px!important}
form[name="news_set_sort"] ul li a{color:#b0b0b0!important;font-weight:500!important;letter-spacing:.3px!important;transition:color .25s ease!important;font-family:inherit!important;display:block!important}
form[name="news_set_sort"] ul li:hover{background:rgba(42,42,42,.9)!important;border-color:rgba(255,135,135,.3)!important;transform:translateY(-2px)!important;box-shadow:0 4px 12px rgba(255,107,107,.15)!important}
form[name="news_set_sort"] ul li:hover a{color:#ff8787!important}
form[name="news_set_sort"] ul li.asc{background:linear-gradient(135deg,rgba(255,107,107,.2) 0%,rgba(255,135,135,.1) 100%)!important;border-color:rgba(255,107,107,.4)!important}
form[name="news_set_sort"] ul li.asc a{color:#ff8787!important}
.item{background:linear-gradient(135deg,rgba(26,26,26,.95) 0%,rgba(20,20,20,.95) 100%)!important;border:1px solid rgba(255,107,107,.08)!important;border-radius:12px!important;overflow:hidden!important;position:relative!important;backdrop-filter:blur(10px)!important;-webkit-backdrop-filter:blur(10px)!important;transform:translateY(0)!important;transition:all .3s cubic-bezier(.4,0,.2,1)!important}
.item::before{content:''!important;position:absolute!important;top:0!important;left:0!important;right:0!important;height:2px!important;background:linear-gradient(90deg,transparent,#ff6b6b,transparent)!important;opacity:0!important;transition:opacity .3s ease!important}
.item:hover{border-color:rgba(255,107,107,.3)!important;transform:translateY(-4px)!important;box-shadow:0 12px 32px rgba(0,0,0,.5),0 0 0 1px rgba(255,107,107,.1)!important}
.item:hover::before{opacity:1!important}
.item-img{background:#0a0a0a!important;position:relative!important;overflow:hidden!important}
.item-img::after{content:''!important;position:absolute!important;inset:0!important;background:linear-gradient(180deg,transparent 0%,rgba(0,0,0,.4) 100%)!important;opacity:0!important;transition:opacity .3s ease!important;pointer-events:none!important;z-index:5!important}
.item:hover .item-img::after{opacity:1!important}
.item-img img{transform:scale(1)!important;transition:transform .5s cubic-bezier(.34,1.56,.64,1),opacity .3s ease!important}
.item:hover .item-img img{transform:scale(1.08)!important}
.item-title{color:#e0e0e0!important;font-weight:500!important}
.item-title a{color:#e0e0e0!important;transition:color .3s ease!important}
.item-title a:hover{color:#ff8787!important}
.item-title h2{color:#e0e0e0!important;font-weight:500!important}
.item-title h2 i{color:#888!important}
.item-meta{background:linear-gradient(135deg,rgba(255,107,107,.15),rgba(255,135,135,.1))!important;color:#ff8787!important;border-radius:6px!important;font-weight:500!important;backdrop-filter:blur(5px)!important;-webkit-backdrop-filter:blur(5px)!important}
.meta-views{color:#888!important}
.tim{background:rgba(36,36,36,.6)!important;color:#888!important;border-radius:6px!important;backdrop-filter:blur(5px)!important;-webkit-backdrop-filter:blur(5px)!important}
.catmen{background:rgba(42,42,42,.8)!important;color:#b0b0b0!important;border-radius:8px!important;border:1px solid rgba(255,107,107,.1)!important;font-weight:500!important;backdrop-filter:blur(5px)!important;-webkit-backdrop-filter:blur(5px)!important}
.catmen:hover{background:rgba(58,58,58,.9)!important;color:#e0e0e0!important;border-color:rgba(255,135,135,.3)!important;transform:translateY(-2px)!important;box-shadow:0 4px 8px rgba(0,0,0,.3)!important}
.catmen2{background:linear-gradient(135deg,#ff6b6b 0%,#ff5252 100%)!important;color:#fff!important;border-radius:8px!important;font-weight:600!important;box-shadow:0 2px 8px rgba(255,107,107,.3)!important;transform:translateY(0)!important;transition:all .25s cubic-bezier(.4,0,.2,1)!important}
.catmen2:hover{background:linear-gradient(135deg,#ff5252 0%,#ff3838 100%)!important;transform:translateY(-2px)!important;box-shadow:0 4px 12px rgba(255,107,107,.5)!important}
.clouds_xsmall,.clouds_small,.clouds_medium,.clouds_large,.clouds_xlarge{background:rgba(36,36,36,.6)!important;border-radius:16px!important;border:1px solid rgba(255,107,107,.08)!important;opacity:1!important;backdrop-filter:blur(5px)!important;-webkit-backdrop-filter:blur(5px)!important}
.clouds_xsmall a,.clouds_small a,.clouds_medium a,.clouds_large a,.clouds_xlarge a{color:#b0b0b0!important;transition:color .25s ease!important}
.clouds_xsmall:hover,.clouds_small:hover,.clouds_medium:hover,.clouds_large:hover,.clouds_xlarge:hover{background:rgba(42,42,42,.8)!important;border-color:rgba(255,135,135,.2)!important;transform:translateY(-2px)!important}
.clouds_xsmall:hover a,.clouds_small:hover a,.clouds_medium:hover a,.clouds_large:hover a,.clouds_xlarge:hover a{color:#ff8787!important}
.pagi-nav{background:rgba(26,26,26,.8)!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:12px!important;backdrop-filter:blur(10px)!important;-webkit-backdrop-filter:blur(10px)!important}
.pagi-nav a,.pagi-nav span{color:#b0b0b0!important;background:rgba(36,36,36,.6)!important;border-radius:8px!important;font-weight:500!important}
.pagi-nav a:hover{background:rgba(42,42,42,.8)!important;color:#ff8787!important;transform:translateY(-2px)!important;box-shadow:0 4px 8px rgba(255,107,107,.2)!important}
.pagi-nav span{background:linear-gradient(135deg,rgba(255,107,107,.2),rgba(255,135,135,.1))!important;color:#ff8787!important}
.footer a,.footer{color:#888!important}
.search-box input[type="text"],input[type="text"],input[type="password"],input[type="email"],textarea,select{background:rgba(36,36,36,.6)!important;color:#e0e0e0!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:8px!important;backdrop-filter:blur(5px)!important;-webkit-backdrop-filter:blur(5px)!important;transition:all .25s ease!important}
.search-box input[type="text"]{border-radius:12px!important}
.search-box input[type="text"]:focus,input:focus,textarea:focus,select:focus{border-color:rgba(255,107,107,.5)!important;background:rgba(42,42,42,.8)!important;box-shadow:0 0 0 3px rgba(255,107,107,.1)!important;outline:none!important}
.search-box input[type="text"]:focus{box-shadow:0 0 0 3px rgba(255,107,107,.1),0 4px 12px rgba(255,107,107,.2)!important}
.search-box button,.search-box input[type="submit"],button,input[type="submit"],input[type="button"]{background:linear-gradient(135deg,#ff6b6b 0%,#ff5252 100%)!important;color:#fff!important;border:none!important;border-radius:8px!important;font-weight:600!important;box-shadow:0 4px 12px rgba(255,107,107,.3)!important;transform:translateY(0)!important;transition:all .25s cubic-bezier(.4,0,.2,1)!important}
.search-box button,.search-box input[type="submit"]{border-radius:12px!important}
.search-box button:hover,.search-box input[type="submit"]:hover,button:hover,input[type="submit"]:hover,input[type="button"]:hover{background:linear-gradient(135deg,#ff5252 0%,#ff3838 100%)!important;transform:translateY(-2px)!important;box-shadow:0 6px 16px rgba(255,107,107,.5)!important}
.side-panel{background:rgba(26,26,26,.8)!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:12px!important;backdrop-filter:blur(10px)!important;-webkit-backdrop-filter:blur(10px)!important}
h1,h2,h3,h4,h5,h6{color:#e0e0e0!important;font-weight:600!important;letter-spacing:-.3px!important}
p{color:#b0b0b0!important;line-height:1.6!important}
a{color:#b0b0b0!important}
.close-overlay{background:rgba(0,0,0,.98)!important;backdrop-filter:blur(20px)!important;-webkit-backdrop-filter:blur(20px)!important}
::-webkit-scrollbar{width:10px!important}
::-webkit-scrollbar-track{background:rgba(26,26,26,.5)!important}
::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgba(255,107,107,.5),rgba(255,135,135,.3))!important;border-radius:10px!important;border:2px solid transparent!important}
::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,rgba(255,107,107,.8),rgba(255,135,135,.6))!important}
::selection{background:rgba(255,107,107,.3)!important;color:#fff!important}
table{background:rgba(26,26,26,.8)!important;border-color:rgba(255,107,107,.1)!important;border-radius:8px!important;overflow:hidden!important}
th{background:rgba(36,36,36,.8)!important;color:#e0e0e0!important;font-weight:600!important}
td{color:#b0b0b0!important;border-color:rgba(255,107,107,.05)!important}
tr:hover td{background:rgba(36,36,36,.5)!important}
.ad-wrapper{opacity:.5!important;filter:grayscale(.3)!important}
.sect-desc{color:#888!important}
.fluid_controls_container,.fluid_controls_container *{color:#e0e0e0!important}
.fluid_controls_left,.fluid_controls_right{background:rgba(0,0,0,.6)!important}
.fluid_button{background:rgba(255,107,107,.15)!important;border-radius:6px!important;transition:all .2s ease!important}
.fluid_button:hover{background:rgba(255,107,107,.3)!important;transform:scale(1.05)!important}
.fluid_controls_progress_container{background:rgba(255,255,255,.1)!important;border-radius:4px!important}
.fluid_controls_currentpos{background:linear-gradient(90deg,#ff6b6b,#ff8787)!important;color:#fff!important;border-radius:4px!important;font-weight:600!important;text-shadow:0 1px 2px rgba(0,0,0,.8)!important}
.fluid_control_volume_currentpos{background:linear-gradient(90deg,#ff6b6b,#ff8787)!important;border-radius:4px!important}
.fluid_control_duration,.fluid_fluid_control_duration{color:#e0e0e0!important;font-weight:500!important;text-shadow:0 1px 2px rgba(0,0,0,.8)!important}
.fluid_control_volume_container{background:rgba(255,255,255,.1)!important;border-radius:4px!important}
.xd{background:rgba(26,26,26,.8)!important;color:#e0e0e0!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:8px!important;padding:8px!important}
.fm-fav{color:#ff8787!important}
.rating{color:#ff8787!important}
#story{background:rgba(36,36,36,.8)!important;color:#e0e0e0!important;border:1px solid rgba(255,107,107,.2)!important;border-radius:8px!important}
#story:focus{border-color:rgba(255,107,107,.5)!important;box-shadow:0 0 0 3px rgba(255,107,107,.1)!important}
.full-tags a,.full-tags{background:rgba(42,42,42,.8)!important;color:#b0b0b0!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:8px!important;padding:6px 12px!important;display:inline-block!important;margin:3px!important;font-weight:500!important;transition:all .25s ease!important}
.full-tags a:hover{background:rgba(58,58,58,.9)!important;color:#ff8787!important;border-color:rgba(255,135,135,.3)!important;transform:translateY(-2px)!important;box-shadow:0 4px 8px rgba(255,107,107,.2)!important}
.a2a_kit{background:transparent!important}
.a2a_kit a{background:rgba(42,42,42,.8)!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:8px!important;padding:6px 12px!important;transition:all .25s ease!important}
.a2a_kit a:hover{background:rgba(58,58,58,.9)!important;border-color:rgba(255,135,135,.3)!important;transform:translateY(-2px)!important}
.a2a_svg{border-radius:6px!important;transition:transform .2s ease!important}
.a2a_kit a:hover .a2a_svg{transform:scale(1.1)!important}
.full-tags{color:#e0e0e0!important;background:transparent!important;border:none!important;padding:0!important;margin-bottom:10px!important}
.fmeta{background:rgba(26,26,26,.8)!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:12px!important;padding:12px!important;margin:10px 0!important}
.fm-item{background:rgba(36,36,36,.6)!important;color:#b0b0b0!important;border-radius:8px!important;padding:8px 12px!important;margin:4px!important}
.fm-item i{color:#ff8787!important}
.f-desc,.full-text{color:#b0b0b0!important;background:rgba(26,26,26,.8)!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:12px!important;padding:15px!important;margin:10px 0!important}
.f-desc p,.full-text p{color:#b0b0b0!important;line-height:1.6!important}
.fcols{background:transparent!important}
.fleft,.fright{background:transparent!important}
.video-box,.fplayer{background:#0a0a0a!important;border:1px solid rgba(255,107,107,.1)!important;border-radius:12px!important;overflow:hidden!important;margin:10px 0!important}
.item-title p[style*="color"]{color:#888!important}
.item-title p span{color:#b0b0b0!important}
.item-title p a{color:#b0b0b0!important;text-decoration:none!important}
.item-title p a:hover{color:#ff8787!important}
          `;
      } else {
          style.textContent = `
*{transition:background-color .2s ease,color .2s ease,border-color .2s ease!important}
body,.js{background:linear-gradient(135deg,#f8f9fa 0%,#fff 100%)!important;color:#2c3e50!important}
.header,.nav,.footer{background:rgba(255,255,255,.95)!important;backdrop-filter:blur(10px)!important;border-color:rgba(231,76,60,.1)!important}
.header,.nav{box-shadow:0 2px 12px rgba(0,0,0,.08)!important}
.nav-in a:hover{color:#e74c3c!important;background:rgba(231,76,60,.08)!important}
.item{background:rgba(255,255,255,.98)!important;border:1px solid rgba(231,76,60,.08)!important;box-shadow:0 2px 8px rgba(0,0,0,.06)!important;border-radius:12px!important;transform:translateY(0)!important;transition:all .3s cubic-bezier(.4,0,.2,1)!important}
.item:hover{box-shadow:0 12px 32px rgba(0,0,0,.15)!important;border-color:rgba(231,76,60,.3)!important;transform:translateY(-4px)!important}
button,input[type="submit"],input[type="button"]{background:linear-gradient(135deg,#e74c3c 0%,#c0392b 100%)!important;box-shadow:0 4px 12px rgba(231,76,60,.3)!important;border-radius:8px!important;transform:translateY(0)!important;transition:all .25s cubic-bezier(.4,0,.2,1)!important}
button:hover,input[type="submit"]:hover,input[type="button"]:hover{background:linear-gradient(135deg,#c0392b 0%,#a93226 100%)!important;transform:translateY(-2px)!important;box-shadow:0 6px 16px rgba(231,76,60,.5)!important}
.fluid_controls_container,.fluid_controls_container *{color:#2c3e50!important}
.fluid_button{background:rgba(231,76,60,.15)!important}
.fluid_button:hover{background:rgba(231,76,60,.3)!important}
          `;
      }
      document.head.appendChild(style);

      // 2. Video Preview (Integrated with the pack's insertPreviewVideo)
      const videoCache = new Map();
      const preloadQueue = [];
      const MAX_CONCURRENT_PRELOADS = 2;
      let activePreloads = 0;

      function extractVideoUrl(pageUrl) {
          return new Promise((resolve, reject) => {
              if (videoCache.has(pageUrl)) {
                  resolve(videoCache.get(pageUrl));
                  return;
              }
              GM_xmlhttpRequest({
                  method: 'GET',
                  url: pageUrl,
                  onload: function(response) {
                      const parser = new DOMParser();
                      const doc = parser.parseFromString(response.responseText, 'text/html');
                      const videoSource = doc.querySelector('video source');
                      if (videoSource && videoSource.src) {
                          videoCache.set(pageUrl, videoSource.src);
                          resolve(videoSource.src);
                      } else {
                          reject('No video found');
                      }
                  },
                  onerror: reject
              });
          });
      }

      function processQueue() {
          if (activePreloads >= MAX_CONCURRENT_PRELOADS || preloadQueue.length === 0) return;
          const { pageUrl, img } = preloadQueue.shift();
          activePreloads++;

          extractVideoUrl(pageUrl)
              .then(videoUrl => {
                  if (videoUrl) {
                      // Apply pack's native behavior
                      insertPreviewVideo(img, videoUrl);
                  }
              })
              .catch(() => {})
              .finally(() => {
                  activePreloads--;
                  setTimeout(processQueue, 50);
              });
      }

      function initItem(item) {
          if (item.dataset.ypInit) return;
          item.dataset.ypInit = '1';

          const imgContainer = item.querySelector('.item-img');
          const link = item.querySelector('a[href*=".html"]');
          const img = imgContainer ? imgContainer.querySelector('img') : null;

          if (!img || !link) return;

          preloadQueue.push({ pageUrl: link.href, img });
          processQueue();
      }

      const ypFetchObserver = new IntersectionObserver(entries => {
          entries.forEach(entry => {
              if (entry.isIntersecting) {
                  initItem(entry.target);
                  ypFetchObserver.unobserve(entry.target);
              }
          });
      }, { rootMargin: '400px', threshold: 0.01 });

      function watchItems() {
          document.querySelectorAll('.item').forEach(item => {
              if (!item.dataset.ypObserved) {
                  item.dataset.ypObserved = '1';
                  ypFetchObserver.observe(item);
              }
          });
      }

      watchItems();
      new MutationObserver(watchItems).observe(document.body, { childList: true, subtree: true });
      setTimeout(watchItems, 500);
      setInterval(watchItems, 5000);
    },

    "superporn.com": function () {
      const videoCache = new Map();
      const preloadQueue = [];
      const MAX_CONCURRENT_PRELOADS = 2;
      let activePreloads = 0;

      function extractVideoUrl(pageUrl) {
          return new Promise((resolve, reject) => {
              if (videoCache.has(pageUrl)) {
                  resolve(videoCache.get(pageUrl));
                  return;
              }
              GM_xmlhttpRequest({
                  method: 'GET',
                  url: pageUrl,
                  onload: function(response) {
                      const parser = new DOMParser();
                      const doc = parser.parseFromString(response.responseText, 'text/html');
                      const videoSource = doc.querySelector('video source');
                      if (videoSource && videoSource.src) {
                          videoCache.set(pageUrl, videoSource.src);
                          resolve(videoSource.src);
                      } else {
                          reject('No video found');
                      }
                  },
                  onerror: reject
              });
          });
      }

      function processQueue() {
          if (activePreloads >= MAX_CONCURRENT_PRELOADS || preloadQueue.length === 0) return;
          const { pageUrl, img } = preloadQueue.shift();
          activePreloads++;

          extractVideoUrl(pageUrl)
              .then(videoUrl => {
                  if (videoUrl) {
                      insertPreviewVideo(img, videoUrl);
                  }
              })
              .catch(() => {})
              .finally(() => {
                  activePreloads--;
                  setTimeout(processQueue, 50);
              });
      }

      function initItem(item) {
          if (item.dataset.spInit) return;
          item.dataset.spInit = '1';

          const link = item.querySelector('a.thumb-duracion');
          const img = item.querySelector('img');

          if (!img || !link) return;

          preloadQueue.push({ pageUrl: link.href, img });
          processQueue();
      }

      const spFetchObserver = new IntersectionObserver(entries => {
          entries.forEach(entry => {
              if (entry.isIntersecting) {
                  initItem(entry.target);
                  spFetchObserver.unobserve(entry.target);
              }
          });
      }, { rootMargin: '400px', threshold: 0.01 });

      function watchItems() {
          document.querySelectorAll('.thumb-video').forEach(item => {
              if (!item.dataset.spObserved) {
                  item.dataset.spObserved = '1';
                  spFetchObserver.observe(item);
              }
          });
      }

      watchItems();
      new MutationObserver(watchItems).observe(document.body, { childList: true, subtree: true });
      setTimeout(watchItems, 500);
      setInterval(watchItems, 5000);
    }
  };

  // Dispatcher — run immediately AND on DOMContentLoaded as fallback
  function dispatch() {
    for (const domain in siteHandlers) {
      if (hostname.includes(domain)) {
        siteHandlers[domain]();
        break;
      }
    }
  }

  // Run now (for scripts injected after DOM is ready)
  dispatch();

  // Also run on DOMContentLoaded in case we injected before DOM was ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', dispatch);
  }

})();