// ==UserScript== // @name Bilibili 轴Man小助手 // @namespace http://tampermonkey.net/ // @version 0.9.2 // @description 将评论区的轴转换至Bilibili的笔记,实现手机可点的特性 // @author as042971 // @include *://www.bilibili.com/video/av* // @include *://www.bilibili.com/video/BV* // @license MIT // @grant none // @esversion 8 // @downloadURL none // ==/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; 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); })();