Avjb VIP Unlock

Unlock all VIP videos, perfect mobile support, fixes lazy loaded thumbnails. Added double click 10s seek. No ads, no popups.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Advertisement:

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

Advertisement:

// ==UserScript==
// @name         Avjb VIP Unlock
// @namespace    avjb_vip_unlock
// @version      1.8.0
// @description  Unlock all VIP videos, perfect mobile support, fixes lazy loaded thumbnails. Added double click 10s seek. No ads, no popups.
// @author       codegeasse
// @match        https://avjb.com/*
// @match        https://avjb.cc/*
// @grant        GM_addStyle
// @license      MIT
// @require      https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.1.5/hls.min.js


// ==/UserScript==

(function() {
    'use strict';

    const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 768;

    // =============================================
    // SERVERS LIST — add/remove servers here
    // =============================================
    const SERVERS = [
        'https://7senver.jb-aiwei.cc',
        'https://99newline.jb-aiwei.cc',
        'https://88newline.jb-aiwei.cc',
        'https://newbox.jb-aiwei.cc',
        'https://r22.jb-aiwei.cc',
        'https://back.bbmmic2.com',
    ];

    // =============================================
    // Thumbnail fix logic
    // =============================================
    function fixThumbnails() {
        document.querySelectorAll('.thumb, .video-thumb, .videos-list a, .list-videos a, .card a').forEach(el => {
            if (el.dataset.thumbFixed) return;
            el.dataset.thumbFixed = 'true';
            el.dispatchEvent(new PointerEvent('pointerover', {bubbles: true, cancelable: true}));
            el.dispatchEvent(new TouchEvent('touchstart', {bubbles: true, cancelable: true}));
            el.dispatchEvent(new MouseEvent('mouseover', {bubbles: true, cancelable: true}));
            setTimeout(() => {
                el.dispatchEvent(new PointerEvent('pointermove', {bubbles: true}));
            }, 50);
        });

        document.querySelectorAll('a[href^="/video/"] img').forEach(img => {
            if (img.src && !img.src.includes('placeholder') && !img.src.includes('blank')) return;
            const link = img.closest('a[href^="/video/"]');
            if (!link) return;
            const videoId = link.href.split('/video/')[1].split('/')[0];
            if (!videoId) return;
            const folder = Math.floor(parseInt(videoId) / 1000) * 1000;
            img.src = `https://stat.avstatic.com/cdn1/contents/videos_screenshots/${folder}/${videoId}/385x233/1.jpg`;
        });

        document.querySelectorAll('img').forEach(img => {
            img.style.opacity = '1';
            img.style.visibility = 'visible';
            img.loading = 'eager';
            img.classList.remove('lazy', 'lazyload', 'b-lazy');
        });
    }

    function startThumbnailFixer() {
        fixThumbnails();
        setInterval(fixThumbnails, 700);
        let scrollTimeout;
        window.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(fixThumbnails, 150);
        }, { passive: true });
        const observer = new MutationObserver(() => {
            setTimeout(fixThumbnails, 100);
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    startThumbnailFixer();

    GM_addStyle(`
        img {
            opacity: 1 !important;
            visibility: visible !important;
        }

        .thumb, .video-thumb {
            background: none !important;
        }

        .seek-indicator {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            font-size: 42px;
            font-weight: bold;
            color: white;
            background: rgba(0,0,0,0.6);
            padding: 16px 20px;
            border-radius: 50%;
            z-index: 500;
            pointer-events: none;
            animation: seekFadeOut 0.7s ease-out forwards;
        }

        @keyframes seekFadeOut {
            0% { opacity: 1; transform: translateY(-50%) scale(0.7); }
            30% { opacity: 1; transform: translateY(-50%) scale(1.1); }
            100% { opacity: 0; transform: translateY(-50%) scale(1); }
        }

        #hlsPlayer.inline-mode {
            position: relative !important;
            width: 100% !important;
            max-width: 100vw !important;
            background: #000;
            border-radius: 0;
            box-shadow: none;
            border: none;
            padding: 0;
            margin: 0;
            z-index: 100;
        }

        #hlsPlayer.inline-mode .player-content {
            display: flex;
            flex-direction: column;
            width: 100%;
            position: relative;
        }

        #hlsPlayer.inline-mode #videoElement {
            width: 100%;
            max-height: 56.25vw;
            aspect-ratio: 16 / 9;
            background: #000;
            border-radius: 0;
            display: block;
        }

        #hlsPlayer.inline-mode .player-footer {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 12px;
            background: #1a1a1a;
            font-size: 13px;
            font-family: Arial, sans-serif;
        }

        #hlsPlayer.floating-mode {
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 480px;
            max-width: 40vw;
            background: #141414;
            border-radius: 12px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.6);
            z-index: 2147483647;
            overflow: hidden;
            border: 2px solid #ff4757;
            padding: 8px;
            display: none;
        }

        #hlsPlayer.floating-mode.open {
            display: block;
        }

        #hlsPlayer.floating-mode .player-content {
            display: flex;
            flex-direction: column;
            width: 100%;
            position: relative;
        }

        #hlsPlayer.floating-mode #videoElement {
            width: 100%;
            aspect-ratio: 16 / 9;
            background: #000;
            border-radius: 8px;
            display: block;
        }

        #hlsPlayer.floating-mode .player-footer {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 5px 4px;
            font-size: 13px;
            font-family: Arial, sans-serif;
        }

        #hlsToggleBtn {
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 50px;
            height: 50px;
            background: #1a1a1a;
            border: 2px solid #ff4757;
            border-radius: 50%;
            box-shadow: 0 6px 20px rgba(0,0,0,0.5);
            z-index: 2147483647;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            cursor: pointer;
            transition: transform 0.2s, box-shadow 0.2s;
            user-select: none;
        }

        #hlsToggleBtn:hover {
            transform: scale(1.1);
            box-shadow: 0 8px 25px rgba(255,71,87,0.4);
        }

        #hlsToggleBtn:active {
            transform: scale(0.95);
        }

        #showTips {
            color: #ccc;
        }

        #download {
            color: #ff4757;
            text-decoration: none;
            font-weight: bold;
            font-size: 13px;
            white-space: nowrap;
        }

        #download:hover {
            color: #ff6b81;
            text-decoration: underline;
        }

        .player-holder .no-access,
        .player-holder .premium-only,
        .player-holder .vip-lock {
            display: none !important;
        }
    `);

    // =============================================
    // VIDEO PLAYER
    // =============================================
    const playerDiv = document.createElement('div');
    playerDiv.id = 'hlsPlayer';
    playerDiv.innerHTML = `
        <div class="player-content">
            <video id="videoElement" controls preload="none" playsinline webkit-playsinline></video>
            <div class="player-footer">
                <span id="showTips">⌛️ Unlocking...</span>
                <a id="download" href="" target="_blank">⏬ 1-Click Download</a>
            </div>
        </div>
    `;

    const toggleBtn = document.createElement('div');
    toggleBtn.id = 'hlsToggleBtn';
    toggleBtn.innerText = '📺';

    const video = playerDiv.querySelector('#videoElement');
    const downloadEl = playerDiv.querySelector('#download');
    const showTipsEl = playerDiv.querySelector('#showTips');
    let hls = null;

    // =============================================
    // Double Click 10s Seek
    // =============================================
    const SEEK_SECONDS = 10;
    let lastClickTime = 0;

    video.addEventListener('click', handleDoubleClick);
    video.addEventListener('touchend', handleDoubleClick, { passive: false });

    function handleDoubleClick(e) {
        const now = Date.now();
        const timeSinceLastClick = now - lastClickTime;

        if (timeSinceLastClick < 300 && timeSinceLastClick > 30) {
            let clientX, clientY;
            if (e.type.startsWith('touch')) {
                clientX = e.changedTouches[0].clientX;
                clientY = e.changedTouches[0].clientY;
            } else {
                clientX = e.clientX;
                clientY = e.clientY;
            }

            const rect = video.getBoundingClientRect();
            const offsetX = clientX - rect.left;
            const offsetY = clientY - rect.top;
            const clickXpercent = offsetX / rect.width;
            const clickYpercent = offsetY / rect.height;

            if (clickYpercent > 0.9) {
                lastClickTime = 0;
                return;
            }

            e.preventDefault();
            e.stopPropagation();

            let indicator;
            if (clickXpercent < 0.5) {
                video.currentTime = Math.max(0, video.currentTime - SEEK_SECONDS);
                indicator = `⏪ -${SEEK_SECONDS}s`;
            } else {
                video.currentTime = Math.min(video.duration, video.currentTime + SEEK_SECONDS);
                indicator = `+${SEEK_SECONDS}s ⏩`;
            }

            const popup = document.createElement('div');
            popup.className = 'seek-indicator';
            popup.innerText = indicator;
            popup.style.left = clickXpercent < 0.5 ? '15%' : '65%';
            video.parentElement.appendChild(popup);
            setTimeout(() => popup.remove(), 700);

            lastClickTime = 0;
        } else {
            lastClickTime = now;
        }
    }

    // =============================================
    // HLS Stream Loader
    // =============================================
    function loadHlsStream(url) {
        downloadEl.href = `https://tools.thatwind.com/tool/m3u8downloader#m3u8=${url}&referer=${window.location.href}&filename=${encodeURIComponent(document.title)}`;

        if (Hls.isSupported()) {
            if (hls) hls.destroy();
            hls = new Hls();
            hls.loadSource(url);
            hls.attachMedia(video);
            hls.on(Hls.Events.MANIFEST_PARSED, () => {
                showTipsEl.innerText = '✅ Unlocked! Click to play';
                showTipsEl.style.color = '#2ed573';
            });
            hls.on(Hls.Events.ERROR, (event, data) => {
                if (data.fatal) {
                    showTipsEl.innerText = '❌ Load failed, please refresh and try again';
                    showTipsEl.style.color = '#ff4757';
                }
            });
        } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
            video.src = url;
            video.addEventListener('loadedmetadata', () => {
                showTipsEl.innerText = '✅ Unlocked! Click to play';
                showTipsEl.style.color = '#2ed573';
            });
        }
    }

    // =============================================
    // Try All Servers — use whichever responds first
    // =============================================
    function tryAllServers(folder, videoID) {
        showTipsEl.innerText = '⌛️ Finding best server...';
        let resolved = false;

        SERVERS.map(baseURL => {
            const url = `${baseURL}/videos/${folder}/${videoID}/index.m3u8`;
            return fetch(url, { method: 'HEAD', cache: 'no-store' })
                .then(res => {
                    if (res.ok && !resolved) {
                        resolved = true;
                        showTipsEl.innerText = `⌛️ Server found, loading...`;
                        loadHlsStream(url);
                    }
                })
                .catch(() => {});
        });

        // Fallback after 5s if no server responded
        setTimeout(() => {
            if (!resolved) {
                resolved = true;
                showTipsEl.innerText = '⚠️ Trying fallback server...';
                const url = `${SERVERS[0]}/videos/${folder}/${videoID}/index.m3u8`;
                loadHlsStream(url);
            }
        }, 5000);
    }

    // =============================================
    // Main Loop
    // =============================================
    let savedImgSrc = null;

    function earlyGrab() {
        if (!savedImgSrc) {
            const img = document.querySelector('.player-holder img[src*="videos_screenshots"]');
            const meta = document.querySelector('meta[property="og:image"]');
            if (img) savedImgSrc = img.src;
            else if (meta) savedImgSrc = meta.content;
        }
    }

    function check_circle_v2() {
        if (!document.body) return;

        const playerHolder = document.querySelector('.player-holder');
        if (!playerHolder) return;

        earlyGrab();

        if (isMobile && !document.getElementById('hlsPlayer')) {
            playerDiv.className = 'inline-mode';
            playerHolder.innerHTML = '';
            playerHolder.style.cssText = 'padding:0;margin:0;overflow:visible;';
            playerHolder.appendChild(playerDiv);
        } else if (!isMobile && !document.getElementById('hlsPlayer')) {
            playerDiv.className = 'floating-mode';
            document.body.appendChild(playerDiv);
            document.body.appendChild(toggleBtn);
            toggleBtn.addEventListener('click', () => {
                const isOpen = playerDiv.classList.toggle('open');
                toggleBtn.style.display = isOpen ? 'none' : 'flex';
            });
        }

        if (!savedImgSrc) {
            const meta = document.querySelector('meta[property="og:image"]');
            if (meta) savedImgSrc = meta.content;
        }

        if (savedImgSrc) {
            const tmp = savedImgSrc.split('/');
            let folder = null, videoID = null;

            for (let i = 0; i < tmp.length - 1; i++) {
                if (/^\d+000$/.test(tmp[i]) && /^\d+$/.test(tmp[i + 1])) {
                    folder = tmp[i];
                    videoID = parseInt(tmp[i + 1]);
                    break;
                }
            }

            if (folder && videoID) {
                tryAllServers(folder, videoID);
                clearInterval(my_timer);
            }
        }
    }

    let my_timer = setInterval(check_circle_v2, 1500);

})();