cunhua

解决村花论坛收藏与搜索没有图片预览的问题

// ==UserScript==
// @name         cunhua
// @namespace    cunhua
// @version      2.1
// @description  解决村花论坛收藏与搜索没有图片预览的问题
// @author       You
// @require      https://cdn.bootcss.com/jquery/1.8.3/jquery.min.js
// @connect      *
// @include      /^https:\/\/(www\.|)cunhua/
// @match        https://cunhua.*/*
// @match        https://www.cunhua.*/*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function () {
    'use strict';

    function GMFetch(url, options = {}) {
        return new Promise((resolve, reject) => {
            const { method = 'GET', headers = {}, data = null } = options;
            const requestOptions = {
                method: method.toUpperCase(),
                headers: headers,
                url: url,
                onload: (response) => {
                    if (response.status >= 200 && response.status < 400) {
                        resolve(response.responseText);
                    } else {
                        reject(response.statusText);
                    }
                },
                onerror: (error) => {
                    reject(error);
                },
            };
            if (method.toUpperCase() === 'POST') {
                requestOptions.data = data;
            }
            GM_xmlhttpRequest(requestOptions);
        });
    }

    function xmlfetch(url, options = {}) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();

            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        resolve(xhr.responseText);
                    } else {
                        reject(new Error(xhr.statusText));
                    }
                }
            };

            xhr.open(options.method || 'GET', url);

            for (const header in options.headers || {}) {
                xhr.setRequestHeader(header, options.headers[header]);
            }

            xhr.send(options.body);
        });
    }


    async function fetch_real_url(url) {
        const resp = await fetch(url);
        const html = await resp.text();
        if(/<head>/.test(html)) return url;
        let [, jsStr] = /<script.*?>([\s\S]*?)<\/script>/gm.exec(html);
        const temp = `
        MuURL='';
        MuObj={
            href:'',replace:function(abc){MuURL=abc},
            assign:function(abc){MuURL=abc},
        };` ;
        jsStr = temp + jsStr.replaceAll('location', 'MuObj');
        //console.log(jsStr);
        let func = new Function(jsStr);
        func();
        MuURL = MuURL ? MuURL : (MuObj.href || MuObj);
        let [, _dsign] = /_dsign=(.*)/gm.exec(MuURL);
        const sign = url.includes('?') ? '&' : '?';
        const _url = `${url}${sign}_dsign=${_dsign}`;
        return _url;
    }

    async function get_images(url) {
        //console.log(url);
        const real_url = await fetch_real_url(url);
        const mobileOpt = { headers: { "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0" } };
        const pcOpt = {};
        const opt = url.includes('mobile=') ? mobileOpt : pcOpt;
        const resp = await fetch(real_url, opt);
        const html = await resp.text();
        let regex = /<img[^>]+id="aimg_\d+"[^>]*>/g;
        let matches = html.match(regex);
        //console.log(html);
        if (matches) {
            return matches.map((htmlString) => {
                const regex = /<img.*?src=["'](.*?)["']/;
                const [, src] = regex.exec(htmlString) ?? [];
                const regex2 = /<img.*?zoomfile=["'](.*?)["']/;
                const [, zoomfile] = regex2.exec(htmlString)?? [];
                return zoomfile ? zoomfile : src;
            });
        } else {
            return [];
        }
        // const urls = $(html).find('.message a>img')
        //     .map(function () {
        //         return $(this).attr('src');
        //     }).get();
        // return urls;
    }

    function preLoadImg(id, url) {
        console.log("preLoadImg", id, url);
        var img = new Image();
        img.src = url;
        img.onload = function () {
            document.getElementById(id).src = url;
        };
        img.onerror = function () {
            document.getElementById(id).src = "https://jsonp.gitee.io/video/img/404.png";
        }
    }

    $('.threadlist li>a:first-child,#favorite_ul li>a[target="_blank"],#threadlist li.pbw h3>a,table[summary="主题付费"] tr td>a[target="_blank"]').each(async function (index, vo) {
        let container = $(this).parent().append(`<div class="img-list" style="width: 100%; height:200px;padding-top:10px;overflow-x: auto; overflow-y: hidden; white-space: nowrap;"></div>`);
        const defaultImg = "https://jsonp.gitee.io/video/img/load.gif";
        let i = 0;
        while (i++ < 2) {
            container.find(".img-list").append(`<img src="${defaultImg}" id="id_${index}_img_${i - 1}" style="height: 200px; display: inline-block;padding:0 5px"/>`);
        }
        let url = $(this).attr('href');
        url = 'https://' + location.host + '/' + url;
        container.find(".img-list").attr("data-href", url).attr('data-index', index);
        lazyLoad();
    });

    function debounce(fn, delay) {
        let timer;
        return function () {
            const context = this;
            const args = arguments;
            clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(context, args);
            }, delay);
        };
    }

    function lazyLoad() {
        const lazyDivs = $(".img-list[data-href]");
        lazyDivs.each(async (i, div) => {
            const divTop = div.getBoundingClientRect().top;
            const divBottom = div.getBoundingClientRect().bottom;
            const winTop = window.innerHeight;
            if (divTop < winTop && divBottom > 0) {
                const href = $(div).data('href');
                const index = $(div).data('index');
                $(div).removeAttr("data-href");
                const links = await get_images(href);
                let count = links.length;
                $(div).parent().find('p').append(`<b>[${count}P]</b>`);
                while (count++ < 2) {
                    links.push("https://jsonp.gitee.io/video/img/404.png");
                }
                links.slice(0, 2).forEach((link, _index) => {
                    const dom_id = `id_${index}_img_${_index}`;
                    preLoadImg(dom_id, link);
                });
            }
        });
    }
    const lazyLoadDebounced = debounce(lazyLoad, 60);
    window.addEventListener('scroll', lazyLoadDebounced);
})();