Avjb VIP Unlock

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

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

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

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

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

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

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

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

Advertisement:

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

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

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

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

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

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

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

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);

})();