Bilibili 轴Man小助手

将评论区的轴转换至Bilibili的笔记,实现手机可点的特性

目前为 2022-02-11 提交的版本。查看 最新版本

// ==UserScript==
// @name         Bilibili 轴Man小助手
// @namespace    http://tampermonkey.net/
// @version      0.9.3
// @description  将评论区的轴转换至Bilibili的笔记,实现手机可点的特性
// @author       as042971
// @include      *://www.bilibili.com/video/av*
// @include      *://www.bilibili.com/video/BV*
// @license MIT
// @grant        none
// @esversion    8
// ==/UserScript==

(function() {
    'use strict';
    // 设置 useIndent = true 会在文本前增加缩进和引导线
    const useIndent = true;
    // 设置 useNewLine = true 会在文本后增加空行
    const useNewLine = false;
    const insertNewLine = function (quill) {
        let currentPosition = quill.getSelection(true);
        quill.insertText(currentPosition,'\n','silent');
    };
    const markTime = function (quill, index, seconds, cidCount, title) {
        let currentPosition = quill.getSelection(true);
        quill.insertEmbed(currentPosition, 'tag', {
            'cid': window.cid,
            'oid_type': 1,
            'status': 0,
            'index': index,
            'seconds': seconds,
            'cidCount': cidCount,
            'key': (new Date).getTime(),
            'title': title,
            'epid': 0
        }, 'silent');
        currentPosition.index += 2;
        quill.setSelection(currentPosition);
    };
    const insertText = function (quill, text, guide) {
        let currentPosition = quill.getSelection(true);
        if (useIndent) {
            // 插入引导线
            let guideStr = (guide)? "  └─ " : '\n     ';
            quill.insertText(currentPosition, guideStr,{'color': '#cccccc'}, 'silent');
        }

        let mark = false;
        if (text.charAt(text.length-1) == '*') {
            text = text.substr(0, text.length - 1);
            mark = true;
        }

        // 使用正则表达式分割链接
        // 增加前后缀以避免BV在头尾出现
        let exText = '*' + text + '*';
        let textParts = exText.split(/BV[A-Za-z0-9]{10}/);
        let bvParts = exText.match(/BV[A-Za-z0-9]{10}/);
        for (let i = 0; i < textParts.length; i++) {
            // 增加文本部分
            let textPart = textParts[i];
            if (i == 0) {
                // 删去先导符号
                textPart = textPart.substr(1, textPart.length - 1);
            }
            if (i == textParts.length - 1) {
                // 删去结尾符号
                textPart = textPart.substr(0, textPart.length - 1);
            }
            if (textPart) {
                currentPosition = quill.getSelection(true);
                if (mark) {
                    quill.insertText(currentPosition, textPart,{'color': '#ee230d', 'bold': true, 'link': null}, 'silent');
                } else {
                    quill.insertText(currentPosition, textPart,{'color': null, 'link': null, 'bold': false}, 'silent');
                }
            }

            // 增加链接部分
            if (i != textParts.length - 1) {
                currentPosition = quill.getSelection(true);
                let bvPart = bvParts[i];
                quill.insertText(currentPosition, '打开', {'color': '#0b84ed', 'link':'https://www.bilibili.com/video/'+ bvPart ,'bold': false }, 'silent');
            }
        }
    };
    const parseTime = function (timeStr) {
        const timePart = timeStr.split(":");
        if (timePart.length == 3) {
            return parseInt(timePart[0]) * 3600 + parseInt(timePart[1]) * 60 + parseInt(timePart[2]);
        } else {
            return parseInt(timePart[0]) * 60 + parseInt(timePart[1]);
        }
    };
    const handleTimeline = function (inputStr) {
        // 获取分p信息
        let pages = window.__INITIAL_STATE__.videoData.pages;
        let cid = window.cid;
        let cidCount = pages.length;
        let index = 0;
        let title = '';
        for (index=0; index < pages.length; index++) {
            if (pages[index].cid == cid) {
                title = pages[index].part;
                break;
            }
        }
        index += 1;
        title = 'P' + index;

        let quill = document.querySelector('.ql-container').__quill;
        // h:mm:ss 型时间
        const timeRegex = /^(\d{1})\:([0-5]{1}\d{1})\:([0-5]{1}\d{1})$/;
        // mm:ss 型时间
        const timeRegex2 = /^([0-5]{1}\d{1})\:([0-5]{1}\d{1})$/;
        // 通过换行分隔
        const inputStrList = inputStr.split(/[\r\n]+/);
        for (let i = 0; i < inputStrList.length; i++) {
            let inputStrItem = inputStrList[i];
            let nonTimeStr = '';
            let time = 0;
            // 通过空格分隔
            const inputPart = inputStrItem.split(' ');
            for (let j = 0; j < inputPart.length; j++) {
                let part = inputPart[j];
                if (part) {
                    if (timeRegex.test(part) || timeRegex2.test(part)) {
                        // 这是一个时间戳
                        // 结束上一次的非时间戳内容
                        if (nonTimeStr != '') {
                            if (time != 0) {
                                quill.setSelection(0, 5);
                                markTime(quill, index, time, cidCount, title);
                                insertText(quill, nonTimeStr, true);
                                time = 0;
                            } else {
                                insertText(quill, nonTimeStr, false);
                            }
                            if (useNewLine) {
                                insertNewLine(quill);
                            }
                            nonTimeStr = '';
                        }
                        // 标记这个时间戳
                        time = parseTime(part);
                    } else {
                        if (nonTimeStr != '') {
                            nonTimeStr += ' ';
                        }
                        nonTimeStr += part;
                    }
                }
            }
            if (nonTimeStr != '') {
                if (time != 0) {
                    markTime(quill, index, time, cidCount, title);
                    insertText(quill, nonTimeStr, true);
                    time = 0;
                } else {
                    insertText(quill, nonTimeStr, false);
                }
                if (useNewLine) {
                    insertNewLine(quill);
                }
            }
        }
        // 必须进行一次user插入,否则无法正常保存
        let currentPosition = quill.getSelection(true);
        quill.insertText(currentPosition, '', 'user');
    };
    const inject = function(node) {
        let rawTimeline = document.createElement('textarea');
        rawTimeline.setAttribute('id', 'inject-timeline');
        rawTimeline.setAttribute('class', 'ipt-txt');
        rawTimeline.setAttribute('rows', '1');
        rawTimeline.setAttribute('placeholder', '将轴粘贴至这里...');
        rawTimeline.oninput = async function () {
            let data = rawTimeline.value;
            rawTimeline.value = "";
            rawTimeline.setAttribute('disabled', 'disabled');
            rawTimeline.setAttribute('placeholder', '处理中,请稍后...');
            handleTimeline(data);
            rawTimeline.removeAttribute('disabled');
            rawTimeline.setAttribute('placeholder', '将轴粘贴至这里...');
        };
        node.insertBefore(rawTimeline, node.childNodes[3]);
    };
    let app = document.getElementById('app');
    let observerOptions = {
      childList: true,
      attributes: false,
      subtree: false
    };
    let observer = new MutationObserver((mutation_records) => {
        let note = document.querySelector('.active-note');
        if (note) {
            inject(note);
            observer.disconnect();
        }
    });
    observer.observe(app, observerOptions);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址