Greasy Fork

Greasy Fork is available in English.

bilibili网页视频简介中的链接可以点击

增加bilibili网页视频简介文字的交互,部分网页链接、社交平台跳转、邮箱跳转、视频时间跳转、微信号和群号可扫二维码录入手机、百度盘4位密码点击复制

当前为 2020-10-23 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name                bilibili网页视频简介中的链接可以点击
// @namespace           http://tampermonkey.net/
// @version             2.2
// @description         增加bilibili网页视频简介文字的交互,部分网页链接、社交平台跳转、邮箱跳转、视频时间跳转、微信号和群号可扫二维码录入手机、百度盘4位密码点击复制
// @description:en      black to blue!
// @description:zh-CN   增加bilibili网页视频简介文字的交互,部分网页链接、社交平台跳转、邮箱跳转、视频时间跳转、微信号和群号可扫二维码录入手机、百度盘4位密码点击复制
// @author              beibeibeibei
// @match               *.bilibili.com/video/*
// @grant               GM_setClipboard
// @require             https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js
// @require             https://cdn.jsdelivr.net/npm/kjua/dist/kjua.min.js
// ==/UserScript==

(function () {
    'use strict';
    let $ = jQuery.noConflict(); // line09   https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js
    let QR = kjua; //////////////// line10   https://cdn.jsdelivr.net/npm/kjua/dist/kjua.min.js


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////22到190行都是识别规则////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //可以点击
    /*  0A*/ let URLreg = /([hH][tT]{2}[pP]([sS]?):\/\/|[wW]{3}.|[wW][aA][pP].|[fF][tT][pP].|[fF][iI][lL][eE].|[bB]{2}[sS].|[nN][eE][wW][sS].|[bB][lL][oO][gG].)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/;
    function url2URL(url) {
        return '<a target="_blank" href="' + url + '">' + url + '</a>';
    }
    /*  0B*/ let baidupanlink = /pan.baidu.com\/s\/[\-0-9a-zA-Z_]{8,27}/;
    function url2URL2(url) {
        return '<a target="_blank" href="https://' + url + '">' + url + '</a>';
    }
    /*  1A*/ let BVlink = /(?<![hH][tT]{2}[pP][sS]?:\/\/www.bilibili.com\/video\/)(bv|BV)[A-Za-z0-9]{10}/;
    function BVnum2BVlink(BVnum) {
        return '<a target="_blank" href="//www.bilibili.com/video/' + BVnum + '">' + BVnum + '</a>';
    }
    /*  1B*/ let avlink = /(?<![hH][tT]{2}[pP][sS]?:\/\/www.bilibili.com\/video\/)av[0-9]+/;
    function avnum2avlink(AVnum) {
        return '<a target="_blank" href="//www.bilibili.com/video/' + AVnum + '">' + AVnum + '</a>';
    }
    /*  1C*/ let cvlink = /(?<![hH][tT]{2}[pP][sS]?:\/\/www.bilibili.com\/read\/)cv[1-9][0-9]*/;
    function cvnum2cvlink(cvnum) {
        return '<a target="_blank" href="//www.bilibili.com/read/' + cvnum + '/">' + cvnum + '</a>';
    }
    /*  1D*/ let smlink = /(?<![hH][tT]{2}[pP][sS]?:\/\/www.nicovideo.jp\/watch\/)sm[0-9]+/;
    function smnum2smlink(smnum) {
        return '<a target="_blank" href="http://acg.tv/' + smnum + '/">' + smnum + '</a>';
    }
    /*  2A*/ let weibo1 = /((新浪)?(微|官)博(已更名为|名)?|围脖)(@|@|@ | @|:|:@|: @)[\-0-9A-Z_a-z\u4e00-\u9fa5]{2,30}/;
    function wbstr2wblink1(wbstr) {
        let wb = wbstr.match(/(?<=((新浪)?(微|官)博(已更名为|名)?|围脖)(@|@|@ | @|:|:@|: @))[\-0-9A-Z_a-z\u4e00-\u9fa5]{2,30}/)[0];
        return '<a target="_blank" href="https://s.weibo.com/user?q=' + wb + '&amp;Refer=weibo_user">' + wbstr + '</a>';
    }
    /*  2B*/ let weibo2 = /(微博【)[\-0-9A-Z_a-z\u4e00-\u9fa5]{2,30}(】)/;
    function wbstr2wblink2(wbstr) {
        let wb = wbstr.match(/(?<=微博【)[\-0-9A-Z_a-z\u4e00-\u9fa5]{2,30}(?=】)/)[0];
        return '<a target="_blank" href="https://s.weibo.com/user?q=' + wb + '&amp;Refer=weibo_user">' + wbstr + '</a>';
    }
    /*  2C*/ let weibo3 = /(@|@|:|:@)[\-0-9A-Z_a-z\u4e00-\u9fa5]{3,30}( 的微博)/;
    function wbstr2wblink3(wbstr) {
        let wb = wbstr.match(/(?<=(@|@|:|:@))[\-0-9A-Z_a-z\u4e00-\u9fa5]{3,30}(?= 的微博)/)[0];
        return '<a target="_blank" href="https://s.weibo.com/user?q=' + wb + '&amp;Refer=weibo_user">' + wbstr + '</a>';
    }
    /*  3 */ let youtube = /(youtube|YouTube|Youtube)(频道| ID)?[:|:| ] ?(?!快手)[\-0-9A-Z_a-z\u4e00-\u9fa5【】']+/;
    function ytb2ytblink(ytbstr) {
        let ytb = ytbstr.match(/(?<=(youtube|YouTube|Youtube)(频道| ID)?[:|:| ] ?)[\u4e00-\u9fa5【】']+/)[0];
        return '<a target="_blank" href="https://www.youtube.com/results?search_query=' + ytb + '">' + ytbstr + '</a>';
    }
    //显示二维码,将字符串扫入手机
    /*  4A*/ let WeChat1 = /联系微信[0-9A-Za-z\u4e00-\u9fa5]+|微信搜一搜:[0-9A-Za-z\u4e00-\u9fa5]+/;
    function wechat2qr1(wechatstr) {
        let wechat = wechatstr.match(/(?<=微信)[0-9A-Za-z\u4e00-\u9fa5]+|(?<=微信搜一搜:)[0-9A-Za-z\u4e00-\u9fa5]+/)[0];
        return '<a class="qrtampermonkey" value="' + wechat + '">' + wechatstr + '</a>';
    }
    /*  4B*/ let WeChat2 = /【联系微信:[0-9A-Za-z\u4e00-\u9fa5]+,非诚勿扰】/; let WeChat2QR = /(?<=【联系微信:)[0-9A-Za-z\u4e00-\u9fa5]+(?=,非诚勿扰】)/;
    function wechat2qr2(wechatstr) {
        let wechat = wechatstr.match(/(?<=【联系微信:)[0-9A-Za-z\u4e00-\u9fa5]+(?=,非诚勿扰】)/)[0];
        return '<a class="qrtampermonkey" value="' + wechat + '">' + wechatstr + '</a>';
    }
    /*  4C*/ let WeChatOfficial1 = /(微信)?公众号(:|:|:  )[0-9A-Za-z\u4e00-\u9fa5]{4,30}/;
    function WeChatOfficial2qr1(WeChatOfficialstr) {
        let WeChatOfficial = WeChatOfficialstr.match(/(?<=(微信)?公众号(:|:|:  ))[0-9A-Za-z\u4e00-\u9fa5]{4,30}/)[0];
        return '<a class="qrtampermonkey" value="' + WeChatOfficial + '">' + WeChatOfficialstr + '</a>';
    }
    /*  4D*/ let WeChatOfficial2 = /(微信)?公众号【[0-9A-Za-z\u4e00-\u9fa5]{4,30}】/;
    function WeChatOfficial2qr2(WeChatOfficialstr) {
        let WeChatOfficial = WeChatOfficialstr.match(/(?<=(微信)?公众号【)[0-9A-Za-z\u4e00-\u9fa5]{4,30}(?=】)/)[0];
        return '<a class="qrtampermonkey" value="' + WeChatOfficial + '">' + WeChatOfficialstr + '</a>';
    }
    /*  5 */ let qqgroup = /[qQ][qQ]?(交流)?群(号)?(:|~)?[0-9]+|搜索QQ群:[\u4e00-\u9fa5]+|(?<![0-9qQ])群(号|号码)?(:|: |:| )?[0-9]+(?!:)|群([0-9]+|⑧|⑨|⑩):[0-9]+|[0-9]+群:[0-9]+/;
    function qqgroupstr2qr(qqgroupstr) {
        let qqgroupnum = qqgroupstr.match(/(?<=([qQ][qQ]?(交流)?群(号)?(:|~)?))[0-9]+|(?<=搜索QQ群:)[\u4e00-\u9fa5]+|(?<=((?<![0-9qQ])群(号|号码)?(:|: |:| )?))[0-9]+(?!:)|(?<=(群([0-9]+|⑧|⑨|⑩):))[0-9]+|(?<=([0-9]+群:))[0-9]+/)[0];
        return '<a class="qrtampermonkey" value="' + qqgroupnum + '">' + qqgroupstr + '</a>';
    }
    //其他
    //邮箱链接
    let mailaddress = /[0-9A-Za-z_\-]+@(qq|163|126|gmail|outlook|foxmail|aliyun|rd.netease|global-link-m|bigdongdong).com/;
    /*  6 */ let mail = RegExp(/(商务)?(合作)?(联系)?邮箱(:|:|::| |:\n)?/.source + mailaddress.source + /|/.source + /商务/.source + mailaddress.source);
    function mailstr2mail(mailaddressstr) {
        let mail = mailaddressstr.match(RegExp(/(?<=(商务)?(合作)?(联系)?邮箱(:|:|::| )?)/.source + mailaddress.source + /|/.source + /(?<=商务)/.source + mailaddress.source))[0];
        return '<a href="mailto:' + mail + '">' + mailaddressstr + '</a>';
    }
    //时间跳转
    /*  7 */ let videotime1 = /[0-9]+:[0-9]{2}(?![0-9])/; //时间数字串,修改视频进度条 //暂时还不支持3个冒号的???
    function changevideotime1(time){
        let text = time.split(":");
        let value = text[0] * 60 + text[1] * 1;
        return '<a class="timetampermonkey" value="' + value + '">' + time + '</a>';
    }
    /*  8 */ let videotime2 = /[0-9]{1,2}分[0-9]{1,2}秒|[0-9]{1,2}分|[0-9]{1,2}秒/; //时间数字文字组合串,修改视频进度条
    function changevideotime2(time) {
        let timetest_m_s = time.split(/[分秒]/);
        let timetest_m = time.split(/[分]/);
        let timetest_s = time.split(/[秒]/);
        let value = 0;
        if (timetest_m_s.toString() == timetest_m.toString()) { // 例子:2分
            value = timetest_m[0] * 60;
        } else if (timetest_m_s.toString() == timetest_s.toString()) { // 例子:2秒
            value = timetest_s[0] * 1;
        } else { // 剩下的就是分秒都有 例子:1分01秒
            value = timetest_m_s[0] * 60 + timetest_m_s[1] * 1;
        }

        return '<a class="timetampermonkey" value="' + value + '">' + time + '</a>';
    }
    //密码点击复制
    /*  9 */ let four_digit_password = /密码(:|: )[0-9a-z]{4}/;
    function copy_password(text) {
        let password = text.match(/(?<=密码(:|: ))[0-9a-z]{4}/)[0];
        return '<a class="copytampermonkey" title="点击复制" value="' + password + '">' + text + '</a>';
    }
    //删除重复文字 //修复up主口吃的问题
    /* 10A*/ let repeatWORD1 = /(三连|三连啊|求三连|三连我!|关注|求关注|关注我!|评论我!|不要白嫖!|不要白嫖!!!!|一件三连叭~~~)\1+/;
    function Norepeat1(repeatstr) {
        repeatstr.match(new RegExp(repeatWORD1));
        return RegExp.$1;
    }
    /* 10B*/ let repeatWORD2 = /(三)\1+(连)/;
    function Norepeat2(repeatstr) {
        repeatstr.match(new RegExp(repeatWORD2));
        return RegExp.$1 + RegExp.$2;
    }
    /* 10C*/ let repeatWORD3 = /(关)\1+(注)(?!注)/;
    function Norepeat3(repeatstr) {
        repeatstr.match(new RegExp(repeatWORD3));
        return RegExp.$1 + RegExp.$2;
    }
    /* 10D*/ let repeatWORD4 = /(关)\1+(注)\2+/;
    function Norepeat4(repeatstr) {
        repeatstr.match(new RegExp(repeatWORD3));
        return RegExp.$1 + RegExp.$2;
    }
    /* 10E*/ let repeatWORD5 = /(三连一定|一键三连哦|关注)(!)\2+/;
    function Norepeat5(repeatstr) {
        repeatstr.match(new RegExp(repeatWORD3));
        return RegExp.$1 + RegExp.$2;
    }

    let R = [
        /*  0A*/[URLreg, url2URL],
        /*  0B*/[baidupanlink, url2URL2],
        /*  1A*/[BVlink, BVnum2BVlink],
        /*  1B*/[avlink, avnum2avlink],
        /*  1C*/[cvlink, cvnum2cvlink],
        /*  1D*/[smlink, smnum2smlink],
        /*  2A*/[weibo1, wbstr2wblink1],
        /*  2B*/[weibo2, wbstr2wblink2],
        /*  2C*/[weibo3, wbstr2wblink3],
        /*  3 */[youtube, ytb2ytblink],
        /*  4A*/[WeChat1, wechat2qr1],
        /*  4B*/[WeChat2, wechat2qr2],
        /*  4C*/[WeChatOfficial1, WeChatOfficial2qr1],
        /*  4D*/[WeChatOfficial2, WeChatOfficial2qr2],
        /*  5 */[qqgroup, qqgroupstr2qr],
        /*  6 */[mail, mailstr2mail],
        /*  7 */[videotime1, changevideotime1],
        /*  8 */[videotime2, changevideotime2],
        /*  9 */[four_digit_password, copy_password],
        /* 10A*/[repeatWORD1, Norepeat1],
        /* 10B*/[repeatWORD2, Norepeat2],
        /* 10C*/[repeatWORD3, Norepeat3],
        /* 10D*/[repeatWORD4, Norepeat4],
        /* 10E*/[repeatWORD5, Norepeat5],
    ];


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////190行////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    /**
    * @description 在字符串中搜索符合正则的所有位置
    * @param text {string} 字符串
    * @param reg {RegExp} 正则表达式
    * @param shift {number} 开头或结尾的数字可能需要偏移
    * @example split_text("example",/e/)
    * @return {Array} [['命中的字符串', '正则', '命中的字符串的开头', '命中的字符串的位置结尾']]
    */
    function split_text(text, reg, shift = 0) {
        let R = new RegExp(reg, 'g');
        let A = [];
        let result;

        while ((result = R.exec(text))) {
            //console.log("匹配文本 = " + result[0] + "    index = " + result.index + "    lastIndex = " + R.lastIndex);
            if (A.length != 0) {
                let i = A[A.length - 1][3]; // 最后一个结果组中的结尾数字
                if (i != result.index) {
                    A.push([text.substring(i, result.index), -1, i, result.index]);
                }
            }
            A.push([result[0], reg, result.index, R.lastIndex]);
        }
        if (A.length != 0) {
            let i1 = A[0][2]; // 第一个结果组中的开头数字
            if (i1 != 0) {
                A.unshift([text.substring(0, i1), -1, 0, i1]);
            }

            let i2 = A[A.length - 1][3]; // 最后一个结果组中的结尾数字
            if (i2 != text.length) {
                A.push([text.substring(i2, text.length), -1, i2, text.length]);
            }
        }

        if (shift != 0) {
            for (let i = 0, l = A.length; i < l; i++) {
                A[i] = [A[i][0], A[i][1], A[i][2] + shift, A[i][3] + shift];
            }
        }
        if (A.length == 0) {
            A = [[text, -1, 0 + shift, text.length + shift]];
        }
        return A;
    }

    /**
    * @description 分割字符串
    * @param text {string} 字符串
    * @param REG_array {array} 正则表达式数组
    * @example get_split_array("example",[/e/,/x/])
    * @return {Array} [['命中的字符串', '正则', '命中的字符串的开头', '命中的字符串的位置结尾']]
    */
    function get_split_array(text, REG_array) {
        let A = [[text, -1, 0, text.length]];
        for (let I = 0, L = REG_array.length; I < L; I++) {
            for (let i = A.length - 1, l = -1; i > l; i--) {
                if (A[i][1] == -1) {
                    let B = split_text(A[i][0], REG_array[I][0], A[i][2]);
                    for (let j = B.length - 1, le = -1; j > le; j--) {
                        A.splice(i + 1, 0, B[j]);
                        if (j == 0) {
                            A.splice(i, 1);
                        }
                    }
                }
            }
        }
        return A;
    }

    /**
    * @description 字符串数组添加元素内容
    * @param arr {array} 字符串数组
    * @param REG_array {array} 正则表达式数组
    */
    function array_add_element(arr, REG_array) {
        for (let i = 0, l = arr.length; i < l; i++) {
            if (arr[i][1] == -1) {
                arr[i][4] = arr[i][0];
            }
            else {
                for (let j = 0, lR = REG_array.length; j < lR; j++) {
                    if (arr[i][1].source == (REG_array[j][0]).source) {
                        arr[i][4] = REG_array[j][1](arr[i][0]);
                        break;
                    }
                }
            }
        }
    }

    /**
    * @description 从字符串数组中获取元素内容
    * @param arr {array} 字符串数组
    */
    function array_get_element(arr) {
        let infotampermonkey = "";
        for (let i = 0, l = arr.length; i < l; i++) {
            infotampermonkey = infotampermonkey + arr[i][4];
        }
        return infotampermonkey;
    }

    function main() {
        //获取原简介内容
        let text = $("#v_desc > div.info").text();
        let text_array = get_split_array(text, R);
        array_add_element(text_array, R); // console.table(text_array);
        let infotampermonkey = array_get_element(text_array);

        //添加a标签的颜色样式和鼠标悬浮颜色样式
        let infotampermonkeystyle =
            "<style>" +
            ".video-desc .infotampermonkey a {color: #00a1d6}" +
            ".video-desc .infotampermonkey a:hover {color: #f25d8e}" +
            "</style>";

        //准备一个div用于存放二维码图片
        let qrcode_div = '<div id="qrcodetampermonkey" style="z-index: 1001; position: absolute; top: 0px; left: 0px;"></div>';

        //在旧简介div后添加一个新的简介div
        if ($("#v_desc > div.infotampermonkey").length == 0) {
            $("#v_desc > div.info").after('<div class="infotampermonkey" style="white-space: pre-line; overflow: hidden;">' + infotampermonkeystyle + qrcode_div + infotampermonkey + '</div>');
        }

        //隐藏旧简介div
        $("#v_desc > div.info").hide();
        //新简介使用和旧简介一样的元素高度
        $("#v_desc > div.infotampermonkey").css("height", $("#v_desc > div.info").css("height"));
        $("#v_desc > div.infotampermonkey").css("width", $("#v_desc > div.info").css("width"));

        //给需要显示二维码的元素添加鼠标悬浮显示二维码的效果
        $(".qrtampermonkey").hover(function () {
            let qrtext = $(this).attr("value");
            $("#qrcodetampermonkey").append(QR({ text: qrtext, fill: '#00a1d6', size: 100, ecLevel: 'L', rounded: 40 }));
            $("#qrcodetampermonkey").css({ "top": $(this).position().top + $(this).height(), "left": $(this).position().left + $(this).width() });
            $("#qrcodetampermonkey").show();
        }, function () {
            $("#qrcodetampermonkey").hide();
            $("#qrcodetampermonkey").empty();
        });

        //给需要修改视频时间的元素添加点击事件
        $(document).on("click", ".timetampermonkey", function () {
            let time = $(this).attr("value");
            $("video")[0].currentTime = time;
        });

        //给需要触发复制的元素添加点击事件
        $(document).on("click", ".copytampermonkey", function () {
            let copy_text = $(this).attr("value");
            GM_setClipboard(copy_text);

            $(this).after('<a id="alert_after_copy" style="color:black;">(已复制)</a>');
            setTimeout(function () {
                $("#alert_after_copy").remove();
            }, 2500)
        });
    }

    function debounce(fn, delay) {
        delay = delay || 10;
        let handle;
        return function (e) {
            clearTimeout(handle);
            handle = setTimeout(() => {
                fn(e);
            }, delay);
        }
    }



    $(document).on("DOMNodeInserted", "#v_desc > div.info", debounce(main));
    //给"展开更多"、"收起"按钮添加点击事件,让按钮可以像控制旧简介div高度一样控制新简介div高度
    let flag = true;//显示"展开更多"就是true,默认就是true
    $(document).on("click", "#v_desc > div.btn", function () {
        if (flag) {
            $("#v_desc > div.infotampermonkey").css("height", "auto");
            flag = false;
        } else {
            $("#v_desc > div.infotampermonkey").css("height", "63px");
            flag = true;
        }
    });



})();

//搜索框占行
//搜索...:                                                                       □ .* □ Aa   查找下一个   查找上一个   关闭