Greasy Fork

Greasy Fork is available in English.

聚合网页(美女图聚合展示演化而来)by SeLang

目标是聚合网页,省去翻页烦恼。有需要聚合的网址请反馈。 QQ群号:455809302,点击链接加入群【油猴脚本私人定制】:https://jq.qq.com/?_wv=1027&k=45p9bea

当前为 2020-09-30 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         聚合网页(美女图聚合展示演化而来)by SeLang
// @namespace    http://cmsv1.findmd5.com/
// @version      0.05
// @description  目标是聚合网页,省去翻页烦恼。有需要聚合的网址请反馈。 QQ群号:455809302,点击链接加入群【油猴脚本私人定制】:https://jq.qq.com/?_wv=1027&k=45p9bea
// @author       selang
// @include      /https?\:\/\/*/
// @require      https://cdn.staticfile.org/jquery/1.12.4/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/ipfs/0.50.2/index.min.js
// @connect      *
// @grant        GM_download
// @grant        GM_openInTab
// @grant        GM_getTab
// @grant        GM_getTabs
// @grant        GM_saveTab
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        unsafeWindow
// ==/UserScript==

(async function () {
    if (window.top === window.self) {
        const node = await Ipfs.create();

        //日志
        function log() {
            if (true) {
                console.log.apply(this, arguments);
            }
        };

        function err() {
            if (true) {
                console.error.apply(this, arguments);
            }
        }

        function priorityLog() {
            console.log.apply(this, arguments);
        }

        const AsyncFunction = Object.getPrototypeOf(async function () {
        }).constructor;

        Array.prototype.distinct = function () {
            let arr = this;
            let result = [];
            let obj = {};

            for (let i of arr) {
                if (!obj[i]) {
                    result.push(i);
                    obj[i] = 1;
                }
            }
            return result;
        }

        const Alpha_Script = {
            sleep: function (time = 100) {
                return new Promise(resolve => {
                    setTimeout(function () {
                        resolve();
                    }, time);
                })
            },
            obtainHtmlAsync: function (options) {
                options = options || {};
                if (!options.url) {
                    throw new Error("参数不合法");
                }
                return new Promise(resolve => {
                    options.headers = options.headers || Alpha_Script.parseHeaders("Accept:image/webp,image/*,*/*;q=0.8\n" +
                        "Accept-Encoding:gzip, deflate, sdch\n" +
                        "Accept-Language:zh-CN,zh;q=0.8\n" +
                        "Referer:" + window.location.href + "\n" +
                        "User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
                    );
                    options.method = options.method || 'GET';
                    let responseType = options.responseType;
                    switch (responseType) {
                        case "blob":
                            options.onload = options.onload || function (response) {
                                if (response && response.status && response.status >= 200 && response.status < 300) {
                                    resolve(response);
                                }
                            }
                            break;
                        default:
                            options.onload = options.onload || function (response) {
                                if (response && response.status && response.status >= 200 && response.status < 300) {
                                    let html = response.responseText;
                                    resolve(html);
                                }
                            }
                    }

                    GM_xmlhttpRequest(options);
                });
            },
            asyncPool: function (poolLimit, array, iteratorFn) {
                let i = 0;
                const ret = [];
                const executing = [];
                const enqueue = function () {
                    //Boundary processing, array is an empty array
                    if (i === array.length) {
                        return Promise.resolve();
                    }
                    //Initialize a promise every enqueue
                    const item = array[i];
                    const p = Promise.resolve(i).then((i) => iteratorFn(item, i, array));
                    i++;
                    //Put into promises array
                    ret.push(p);
                    //After the promise is executed, remove it from the executing array
                    const e = p.then(() => executing.splice(executing.indexOf(e), 1));
                    //Insert the executing number to indicate the executing promise
                    executing.push(e);
                    //Using promise.rece, whenever the number of promises in the executing array is less than poollimit, the new promise is instantiated and executed
                    let r = Promise.resolve();
                    if (executing.length >= poolLimit) {
                        r = Promise.race(executing);
                    }
                    //Recursion until array is traversed
                    return r.then(() => enqueue());
                };
                return enqueue().then(() => Promise.allSettled(ret));
            },
            obtainHtml: function (options) {
                options = options || {};
                if (!options.url || !options.method) {
                    throw new Error("参数不合法");
                }
                GM_xmlhttpRequest(options);
            },
            parseHeaders: function (headStr) {
                var o = {};
                var myregexp = /^([^:]+):(.*)$/img;
                var match = /^([^:]+):(.*)$/img.exec(headStr);
                while (match != null) {
                    o[match[1].trim()] = match[2].trim();
                    match = myregexp.exec(headStr);
                }
                return o;
            },
            //获取参数
            getParam: function (dest, name) {
                var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
                var r = dest.match(reg);
                if (r != null) return decodeURI(r[2]);
                return null;
            },
            isArray: function (value) {
                return Object.prototype.toString.apply(value) === '[object Array]';
            }
        };

        (function () {
            'use strict';
            priorityLog('欢迎进群:455809302交流。一起玩。');
            priorityLog('一起玩不论是不是技术人员都欢迎。只要有创意也欢迎加入。点击链接加入群【油猴脚本私人级别定制】:https://jq.qq.com/?_wv=1027&k=460soLy。');
            priorityLog('未实现:');

            function validateUrl(url) {
                let validate = true;
                let lowerCaseUrl = url.toLowerCase();
                if (url.startsWith('#')) {
                    validate = false;
                } else if (lowerCaseUrl.startsWith('//')) {
                    url = `${window.location.protocol}${url}`;
                } else if (lowerCaseUrl.startsWith("/")) {
                    url = `${window.location.protocol}//${window.location.hostname}${url}`;
                } else if (lowerCaseUrl.startsWith('https://') || lowerCaseUrl.startsWith('http://')) {

                } else {
                    let prefixRegex = /(.*?\/)[^\/]*$/i;
                    let __matched = prefixRegex.exec(window.location.href);
                    if (__matched != null) {
                        url = `${__matched[1]}${url}`;
                    } else {
                        url = `${window.location.protocol}//${window.location.hostname}/${url}`;
                    }
                }

                return {validate, url};
            }

            async function parseNextPages(nextSelector = 'a:contains("下一页")') {
                let nextPages = [];
                let existNextPage = false;
                nextPages.push({
                    url: window.location.href,
                    html: $('html').prop("outerHTML")
                });
                let nextEs = $(nextSelector);
                log('解析下一页开始...');
                while (nextEs.length > 0) {
                    existNextPage = true;
                    let nextPageUrl = nextEs.attr('href');
                    let validateUrlResult = validateUrl(nextPageUrl);
                    if (!validateUrlResult.validate) {
                        break;
                    }
                    nextPageUrl = validateUrlResult.url;
                    log(nextPageUrl);
                    let html = await Alpha_Script.obtainHtmlAsync({url: nextPageUrl});
                    nextPages.push({
                        url: nextPageUrl,
                        html
                    });
                    let parseHTML = $.parseHTML(html);
                    nextEs = $(parseHTML).find(nextSelector);
                    // await Alpha_Script.sleep(1000);
                }
                log('解析下一页结束...');
                return {existNextPage, nextPages};
            }

            /**
             * 插件图片聚合例子
             * @param parseNextPages 解析下一页的公用方法
             * @param $  jquery
             * @param log 日志输出
             * @param Alpha_Script 公用对象
             * @returns {Promise<*[]>} 图片合集
             */
            async function example(parseNextPages, $, log, Alpha_Script) {
                //下一页css选择器
                let nextPageSelector = 'a:contains("下一页")';
                //要聚合的图片css选择器
                let imgSelector = 'img';
                let {existNextPage, nextPages} = await parseNextPages(nextPageSelector);
                if (existNextPage) {
                    nextPages.map(nextPage => {
                        let images = Array.from($($(nextPage.html)).find(imgSelector)).map(e => {
                            let src = e.src;
                            let dataOriginal = $(e).attr('data-original');
                            if (dataOriginal) {
                                src = dataOriginal;
                            }
                            return src;
                        });
                        nextPage.imgs = images;
                    });
                    let imgs = nextPages.flatMap(page => page.imgs);
                    return imgs;
                } else {
                    return [];
                }
            }

            /**
             * 校验cid是否符合rule
             * @param cid
             * @returns {Promise<{validate: boolean}|{date: *, parseRule: *, url: *, validate: boolean, desc: *}>}
             */
            async function parseRuleFromIPFS(cid) {
                const {date, desc, parseRule, url, pre, excludeWebsites} = (await node.dag.get(cid)).value;
                if (parseRule && url && desc && date) {
                    return {validate: true, date, desc, parseRule, url, pre, excludeWebsites};
                } else {
                    return {validate: false};
                }
            }

            /**
             * 获取cid下所有的规则
             * @param cid
             * @returns {Promise<[]>}
             */
            async function obtainRulesFromIPFS(cid) {
                let rules = [];
                let cids = [];
                while (true) {
                    try {
                        let rule = await parseRuleFromIPFS(cid);
                        if (rule.validate) {
                            rules.push(rule);
                            if (rule.pre) {
                                cid = rule.pre;
                                if (cids.includes(cid)) {
                                    err('闭环了');
                                    break;
                                } else {
                                    cids.push(cid);
                                }
                            } else {
                                break;
                            }
                        } else {
                            break;
                        }
                    } catch (e) {
                        err(e);
                        break;
                    }
                }
                return rules;
            }

            (async () => {
                // let cidStr;
                {
                    //这里是一个插件的例子
                    let cid = await node.dag.put(
                        {
                            url: '通用',
                            desc: '通用聚合',
                            parseRule: `return await (${example.toString()})(parseNextPages,$,log,Alpha_Script)`,
                            excludeWebsites: ['https://xxxxx需要排除的网站'],
                            date: '2020年9月30日'
                        }
                    );
                    cid = await node.dag.put(
                        {
                            url: '这里可以写书写规则的网址',
                            desc: '第二个通用聚合',
                            parseRule: `return await (${example.toString()})(parseNextPages,$,log,Alpha_Script)`,
                            excludeWebsites: ['https://xxxxx需要排除的网站'],
                            date: '2020年9月30日',
                            pre: cid.toString()
                        }
                    );
                    let cidStr = cid.toLocaleString();
                    // cidStr = cid.toLocaleString();
                    console.log('你的插件分享的地址为:', cidStr);
                }
                // 你自己写的或者他人分享的cidPath;
                let cidStr = "bafyreicfn763eq4qmjq4icgt6nwrcot7pu6otsiizowddeb3h7fassbo5y";
                let rules = await obtainRulesFromIPFS(cidStr);
                log('当前规则总数:', rules.length);
                for (let rule of rules) {
                    log('当前执行规则>> %s 编写规则参考地址:%s\r\n 规则内容:%s', rule.desc, rule.url, rule.parseRule);
                    let excludeWebsites = rule.excludeWebsites;
                    if (Alpha_Script.isArray(excludeWebsites)) {
                        let findAnyOnes = excludeWebsites.filter(excludeWebsite => window.location.href.startsWith(excludeWebsite));
                        if (findAnyOnes.length > 0) {
                            log('当前规则被排除');
                            continue;
                        }
                    }
                    let ruleFunc = rule.parseRule;
                    let imgs = await (new AsyncFunction('parseNextPages', '$', 'log', 'Alpha_Script', ruleFunc))(parseNextPages, $, log, Alpha_Script);
                    imgs = imgs.distinct();
                    if (imgs.length > 0) {
                        log('规则找到图片');
                        let containerHtml = imgs.map((e, i) => `<div id="c_${i}"></div>`).join("");
                        let inject = `<div>${containerHtml}</div><script type="application/javascript">${imageWidth.toString()}${loadHidden.toString()}</script>`;
                        $('script').remove();
                        $('body').html(inject);
                        await Alpha_Script.asyncPool(10, imgs, async function (src, i) {
                            let blob = await downloadImg2Blob(src);
                            let url = URL.createObjectURL(blob);
                            $(`#c_${i}`).append(`<img src="${url}" onload="loadHidden(this)"/>`);
                        });
                        log('规则执行完毕');
                        break;
                        // await Alpha_Script.sleep(5000);
                    }
                }

                log('执行完毕');
            })();

            async function downloadImg2Blob(imgSrc) {
                let response = await Alpha_Script.obtainHtmlAsync({
                    url: imgSrc,
                    method: 'GET',
                    headers: {
                        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
                        // "Accept-Encoding": "gzip, deflate, sdch",
                        // "Accept-Language": "zh-CN,zh;q=0.8",
                        "Referer": window.location.href,
                        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
                    },
                    responseType: 'blob'
                });
                let responseHeaders = Alpha_Script.parseHeaders(response.responseHeaders);
                let contentType = responseHeaders['Content-Type'];
                if (!contentType) {
                    contentType = "image/png";
                }
                let blob = new Blob([response.response], {type: contentType});
                return blob;
            }

            async function loadHidden(e) {
                let {height, width} = await imageWidth(e);
                if (!(height > 500 && width > 500)) {
                    e.style.display = 'none';
                }
            }

            function getImageWidth(url) {
                let img = new Image();
                img.src = url;
                return imageWidth(img);
            }

            function imageWidth(img) {
                return new Promise(resolve => {
                    // 如果图片被缓存,则直接返回缓存数据
                    if (img.complete) {
                        resolve(img);
                    } else {
                        // 完全加载完毕的事件
                        img.onload = function () {
                            resolve(img);
                        }
                    }

                });
            }

            GM_registerMenuCommand("规则列表", ruleListFunc, "R");

            function ruleListFunc() {
                log("我是规则列表");
            }
        })();
    }
})
();