BilibiliLinksInfos

替换bilibili页面的视频链接为视频名,专栏链接为专栏文章名

As of 2020-03-27. See the latest version.

// ==UserScript==
// @name         BilibiliLinksInfos
// @namespace    https://www.ckylin.site/
// @version      1.0
// @description  替换bilibili页面的视频链接为视频名,专栏链接为专栏文章名
// @author       CKylinMC
// @include      *t.bilibili.com/*
// @include      *www.bilibili.com/video*
// @include      *www.bilibili.com/read*
// @include      *www.bilibili.com/bangumi/play*
// @include      *space.bilibili.com/*
// @grant        none
// @license      GPLv3 License
// ==/UserScript==

(function() {
function CKBilibiliLinksUtil(dom) {
    const that = this;
    this.dom = dom;
    this.desIndex = 0;
    this.des = [];
    this.link = "";
    this.ensureHTTPSAddr = function(url){
        if(url.indexOf("//:")==0) return url.replace("//:","https://");
        return url.replace("http://","https://");
    };
    /*
     * bv2av scripts copied from:
     * https: //mrhso.github.io/IshisashiWebsite/BVwhodoneit/
     * by: mrhso
     * Thx a lot!
     */
    // Converter start
    const table = [...'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'];
    const s = [11, 10, 3, 8, 4, 6];
    const xor = 177451812;
    const add = 8728348608;
    this.av2bv = function(av) {
        let num = NaN;
        if (Object.prototype.toString.call(av) === '[object Number]') {
            num = av;
        } else if (Object.prototype.toString.call(av) === '[object String]') {
            num = parseInt(av.replace(/[^0-9]/gu, ''));
        };
        if (isNaN(num) || num <= 0) {
            // 网页版直接输出这个结果了
            return '无法获得bv号';
        };

        num = (num ^ xor) + add;
        let result = [...'BV1  4 1 7  '];
        let i = 0;
        while (i < 6) {
            // 这里改写差点犯了运算符优先级的坑
            // 果然 Python 也不是特别熟练
            // 说起来 ** 按照传统语法应该写成 Math.pow(),但是我个人更喜欢 ** 一些
            result[s[i]] = table[Math.floor(num / 58 ** i) % 58];
            i += 1;
        };
        return result.join('');
    };
    this.bv2av = function (bv) {
        let str = '';
        if (bv.length === 12) {
            str = bv;
        } else if (bv.length === 10) {
            str = `BV${bv}`;
            // 根据官方 API,BV 号开头的 BV1 其实可以省略
            // 不过单独省略个 B 又不行(
        } else if (bv.length === 9) {
            str = `BV1${bv}`;
        } else {
            return '无法获得AV号';
        };
        if (!str.match(/[Bb][Vv][fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF]{10}/gu)) {
            return '无法获得AV号';
        };

        let result = 0;
        let i = 0;
        while (i < 6) {
            result += table.indexOf(str[s[i]]) * 58 ** i;
            i += 1;
        };
        return `av${result - add ^ xor}`;
    };
    // Converter end
    this.regMenuEvent = function () {
        that.dom.oncontextmenu = function (e) {
            that.desIndex++;
            if (that.desIndex >= that.des.length) that.desIndex = 0;
            that.dom.innerHTML = that.des[that.desIndex];
            return false;
        }
    }
    this.generateDesFromUrl = function(){
        var tmpindex;
        url = that.link.toLowerCase();
        if ((tmpindex = url.indexOf("/av")) != -1) {
            console.log(tmpindex);
            var id = that.link.substr(tmpindex + 1);
            if (id.indexOf("/")!=-1) {
                id = id.split("/")[0];
            }
            if (id.indexOf("?") != -1) {
                id = id.split("?")[0];
            }
            if (id.indexOf("#") != -1) {
                id = id.split("#")[0];
            }
            that.des.push(id);
            that.des.push(that.av2bv(id));
            that.des.push(that.link);
        } else if ((tmpindex = url.indexOf("/bv1")) != -1) {
            var id = that.link.substr(tmpindex + 1);
            if (id.indexOf("/") != -1) {
                id = id.split("/")[0];
            }
            if (id.indexOf("?") != -1) {
                id = id.split("?")[0];
            }
            if (id.indexOf("#") != -1) {
                id = id.split("#")[0];
            }
            that.des.push();
            that.des.push(that.bv2av(id));
            that.des.push(that.link);
        } else if ((tmpindex = url.indexOf("/cv")) != -1) {
            var id = that.link.substr(tmpindex + 1);
            if (id.indexOf("/")) {
                id = id.split("/")[0];
            }
            if (id.indexOf("?")) {
                id = id.split("?")[0];
            }
            if (id.indexOf("#")) {
                id = id.split("#")[0];
            }
            that.des.push();
            that.des.push(that.link);
        }
    };
    this.doGetInfos = function () {
        if (this.dom instanceof HTMLElement) {
            if(!'href' in this.dom) return;
            if(this.dom.className!="") return;
            if ((this.dom.href.indexOf("https://www.bilibili.com/video/") == 0 || this.dom.href.indexOf("http://www.bilibili.com/video/") == 0 || this.dom.href.indexOf("//www.bilibili.com/video/") == 0) && !this.dom.hasAttribute("data-blblinfo")) {
                this.dom.setAttribute("data-blblinfo", true);
                this.dom.innerHTML += " (正在获取信息...)";
                setTimeout(() => {
                    this.link = this.ensureHTTPSAddr(this.dom.href);
                    fetch(this.link).then(res => res.text(), failReason => {
                        this.dom.innerHTML.replace(" (正在获取信息...)", "(信息获取失败)");
                        that.des = [that.dom.innerHTML];
                        that.generateDesFromUrl();
                    })
                        .then(res => {
                            if (res.indexOf("视频去哪了呢?_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili") >= 0) {
                                that.dom.innerHTML = that.dom.innerHTML.replace(" (正在获取信息...)", "(视频不存在或已删除)");
                                that.des = [that.dom.innerHTML];
                                that.dom.style.color = "rgb(86, 86, 86)";
                                that.generateDesFromUrl();
                                return;
                            }
                            var parta = res.split("\"title\":\"");
                            if (parta.length >= 2) {
                                var partb = parta[1].split("\",\"pubdate");
                                that.dom.innerHTML = "[视频] " + unescape(partb[0].replace(/\\/g,"%"));
                            }
                            that.des = [that.dom.innerHTML];
                            that.generateDesFromUrl();
                            that.regMenuEvent();
                        });
                }, 100);
            }
            else if ((this.dom.href.indexOf("https://www.bilibili.com/read/cv") == 0 || this.dom.href.indexOf("http://www.bilibili.com/read/cv") == 0 || this.dom.href.indexOf("//www.bilibili.com/read/cv") == 0) && !this.dom.hasAttribute("data-blblinfo")) {
                this.dom.setAttribute("data-blblinfo", true);
                this.dom.innerHTML += " (正在获取信息...)";
                setTimeout(() => {
                            this.link = this.ensureHTTPSAddr(this.dom.href);
                            fetch(this.link).then(res => res.text(), failReason => {
                        this.dom.innerHTML.replace(" (正在获取信息...)", "(信息获取失败)");
                        that.des = [that.dom.innerHTML];
                        that.generateDesFromUrl();
                    })
                        .then(res => {
                            if (res.indexOf("<div class=\"error-txt\">页面不存在或已被删除</div>") >= 0) {
                                that.dom.innerHTML = that.dom.innerHTML.replace(" (正在获取信息...)", "(专栏不存在或已删除)");
                                that.des = [that.dom.innerHTML];
                                that.dom.style.color = "rgb(86, 86, 86)";
                                that.generateDesFromUrl();
                                return;
                            }
                            var parta = res.split("<meta name=\"name\" content=\"");
                            if (parta.length >= 2) {
                                var partb = parta[1].split("\">");
                                that.dom.innerHTML = "[专栏] " + unescape(partb[0].replace(/\\/g,"%"));
                            }
                            that.des = [that.dom.innerHTML];
                            that.generateDesFromUrl();
                            that.regMenuEvent();
                        });
                }, 100);
            }
            /*else if ((this.dom.href.indexOf("https://live.bilibili.com/") == 0 || this.dom.href.indexOf("//live.bilibili.com/") == 0) && !this.dom.hasAttribute("data-blblinfo")) {
                console.log(this.dom);
                this.dom.setAttribute("data-blblinfo", true);
                this.dom.innerHTML += " (正在获取信息...)";
                setTimeout(() => {
                    fetch(this.dom.href).then(res => res.text())
                        .then(res => {
                            var parta = res.split("title id=\"link-app-title\">");
                            if (parta.length >= 2) {
                                var partb = parta[1].split(" - 哔哩哔哩直播,二次元弹幕直播平台");
                                that.dom.innerHTML = "[直播间] " + partb[0];
                            }
                        });
                }, 100);
            }*/
        }
    }
}

function doUpdateAllLinksHook() {
    document.querySelectorAll("a").forEach((e, i) => {
        e.onmouseover = e => {
            if(!e.target.hasAttribute("title")){
                (new CKBilibiliLinksUtil(e.target)).doGetInfos();
            }
        }
    });
}
document.addEventListener("DOMSubtreeModified", function (e) {
    doUpdateAllLinksHook();
});
doUpdateAllLinksHook();
})();