F95Zone

直接获取被masked的真实链接,无缝翻页

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         F95Zone
// @namespace    -
// @version      1.0.0
// @license      GPL-3.0
// @description  直接获取被masked的真实链接,无缝翻页
// @author       stars-location
// @match        https://f95zone.to/*
// @icon         https://f95zone.to/assets/favicon-32x32.png
// @grant        none
// @run-at       document-end
// ==/UserScript==



(function () {
    'use strict';

    document.addEventListener('click', function (e) {
        const link = e.target.closest('a');
        if (!link || !link.href) return;
       
        try {
            const url = new URL(link.href, window.location.origin);

            if (url.hostname !== 'f95zone.to' || !url.pathname.startsWith('/masked/')) {
                return;
            }
        } catch (err) {
            
            return;
        }

        e.preventDefault();
        e.stopImmediatePropagation();

        const maskedUrl = new URL(link.href, window.location.origin).href;

        fetch(maskedUrl, {
            method: 'POST',
            credentials: 'include',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: 'xhr=1&download=1'
        })
        .then(r => r.json())
        .then(data => {
             if (data.status === 'ok') {
                 window.open(data.msg, '_blank');
             } else {
                 window.open(maskedUrl, '_blank');
             }
        })
    }, true); 
})();



(function () {
    'use strict';

    if (!document.querySelector('.structItem--thread')) return;

    let isLoading = false;
    let hasMore = !!document.querySelector('a.pageNav-jump--next');
    let preloadedDoc = null;

    function preloadNextPage(){
        if (isLoading || !hasMore || preloadedDoc) return;
        isLoading = true;
        const nextLink = document.querySelector('a.pageNav-jump--next');
        if (!nextLink) {
            hasMore = false;
            return;
        }
        const nextPageUrl = nextLink.href;
        fetch(nextPageUrl, { credentials: 'include' })
            .then(res => {
                if (!res.ok) throw new Error('HTTP error'); 
                return res.text();
            })
            .then(html => {
                const doc = new DOMParser().parseFromString(html, 'text/html');
                const messages = doc.querySelector('.block-body')?.querySelectorAll('.message');
                if (messages && messages.length > 0) {
                    preloadedDoc = doc;
                } else {
                    throw 0;
                }
            })
            .catch(() => {})
            .finally(() => {
                isLoading = false;
            });
    }

    function loadNextPage() {
        if (!preloadedDoc) return;
        const newThreads = preloadedDoc.querySelector('.block-body').querySelectorAll('.message');
        const container = document.querySelector('.block-body');
        const nextPageUrl = document.querySelector('a.pageNav-jump--next').href;
        if (container && newThreads.length > 0) {
                newThreads.forEach(thread => container.appendChild(thread));
            }
            
        const newNext = preloadedDoc.querySelector('a.pageNav-jump--next');
        const currentNext = document.querySelector('a.pageNav-jump--next');
        if (newNext && currentNext) currentNext.href = newNext.href; 
        else {
            currentNext.remove(); 
            hasMore = false;
        }
        
        history.replaceState(null, '', nextPageUrl);


        updatePageNum();

        preloadedDoc = null;
    }

    function updatePageNum(){
        const oldPageNavAll = document.querySelectorAll('.pageNav-main');
        const oldPageNav = oldPageNavAll[0];
        const newPageNav = preloadedDoc.querySelector('.pageNav-main');
        const oldPageNum = oldPageNav.querySelector('.pageNav-page--current a').textContent.split('–')[0]; 
        const newPageNum = newPageNav.querySelector('.pageNav-page--current a').textContent;
        const pagesAfterCurrent1 = [];
        const pagesAfterCurrent2 = [];
        let foundCurrent = false;
        for (const li of oldPageNav.querySelectorAll('.pageNav-page')) {
            if (li.classList.contains('pageNav-page--current')) foundCurrent = true;
            if (foundCurrent) pagesAfterCurrent1.push(li);
        }
        pagesAfterCurrent1.forEach(el => el.remove());
        foundCurrent = false;
        for (const li of newPageNav.querySelectorAll('.pageNav-page')) {
            if (li.classList.contains('pageNav-page--current')) {
                foundCurrent = true;
                li.querySelector('a').textContent = oldPageNum + "–" + newPageNum;
            }
            if (foundCurrent) pagesAfterCurrent2.push(li);
        }
        if (pagesAfterCurrent2.length > 0) {
            pagesAfterCurrent2.forEach(nav => oldPageNav.appendChild(nav));
        }
        oldPageNavAll[1].innerHTML = oldPageNavAll[0].innerHTML;
    }


    window.addEventListener('scroll', () => {
        preloadNextPage();
    });
    
    const observer = new IntersectionObserver((entries) => {
        for (const entry of entries) {
            if (entry.isIntersecting) {
                if (preloadedDoc) loadNextPage();
                else preloadNextPage();
            }
        }
    }, { threshold: 0.1 });

    observer.observe(document.querySelectorAll('.pageNav-main')[1]);

})();