按钮位置

支持的链接格式
pc分享链接

手机分享链接

手机长按复制bv号

插件代码,自取
// ==UserScript==
// @name 快速插入b站链接插件
// @namespace Violentmonkey Scripts
// @match https://www.deepflood.com/post-*
// @match https://www.deepflood.com/new-discussion
// @grant GM_xmlhttpRequest
// @version 1.0
// @author -
// @description 2025/10/15 23:27:27
// ==/UserScript==
(() => {
'use strict';
const SELECTORS = {
editor: '.CodeMirror',
toolbar: '.mde-toolbar',
imgBtn: '.toolbar-item.i-icon.i-icon-pic[title="图片"]',
container: '#bzhan-toolbar-container'
};
const DOM = {
editor: null,
getEditor: () => DOM.editor?.CodeMirror,
svgIcon: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 512 512">
<path fill="currentColor" d="M202.667 261.333v32a26.666 26.666 0 0 1-45.523 18.856a26.67 26.67 0 0 1-7.811-18.856v-32a26.667 26.667 0 0 1 53.334 0m266.666-58.666v170.666A74.667 74.667 0 0 1 394.667 448H117.333a74.67 74.67 0 0 1-74.666-74.667V202.667A74.67 74.67 0 0 1 117.333 128h32l-24.106-23.893A26.551 26.551 0 0 1 144 58.784a26.55 26.55 0 0 1 18.773 7.776L224.427 128h64l61.653-61.44a26.55 26.55 0 1 1 37.547 37.547L362.667 128h32a74.67 74.67 0 0 1 74.666 74.667m-53.333 0a21.335 21.335 0 0 0-21.333-21.334H117.333A21.333 21.333 0 0 0 96 202.667v170.666a21.335 21.335 0 0 0 21.333 21.334h277.334A21.333 21.333 0 0 0 416 373.333zm-80 32a26.666 26.666 0 0 0-26.667 26.666v32a26.666 26.666 0 0 0 45.523 18.856a26.67 26.67 0 0 0 7.811-18.856v-32A26.667 26.667 0 0 0 336 234.667" />
</svg>`
};
// ===== 工具函数 (Utility Functions) =====
// 封装通用的、无副作用的辅助函数。
const Utils = {
/**
* 异步等待指定选择器的元素出现在DOM中。
* 对于单页应用(SPA)中动态加载的元素特别有用。
* @param {string} selector - CSS选择器。
* @returns {Promise<Element>}
*/
waitForElement: selector => new Promise(res => {
const el = document.querySelector(selector);
if (el) return res(el);
new MutationObserver((_, o) => {
const found = document.querySelector(selector);
if (found) { o.disconnect(); res(found); }
}).observe(document.body, { childList: true, subtree: true });
}),
getLongLink: shortLink => new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: shortLink,
followRedirect: false,
onload: function(res) {
const longLink = res.finalUrl
if(longLink){
resolve(res.finalUrl)
} else {
reject(res)
}
},
onerror: function(err) {
reject(err)
}
});
}),
generateLink: bvId => `https://www.bilibili.com/video/${bvId}`,
parseLink: async text => {
const matchReg = {
shortLink: /https:\/\/b23\.tv\/\S+/,
bvId: /BV[a-zA-Z0-9]{10}/
}
const isContainShortLink = matchReg.shortLink.test(text)
const isContainLongLink= matchReg.bvId.test(text)
const getShortLink = t => t.match(matchReg.shortLink)?.[0]
const getBvId = t => t.match(matchReg.bvId)?.[0]
if (isContainShortLink){
const shortLink = getShortLink(text)
const longLink = await Utils.getLongLink(shortLink)
const bvId = getBvId(longLink)
return Utils.generateLink(bvId)
} else if(isContainLongLink) {
const bvId = getBvId(text)
return Utils.generateLink(bvId)
}
},
/**
* 将Markdown文本插入到CodeMirror编辑器中。
* @param {string} markdown - 要插入的Markdown文本。
*/
insertMarkdown: markdown => {
const cm = DOM.getEditor();
if (cm) {
const cursor = cm.getCursor();
cm.replaceRange(`\n${markdown}\n`, cursor);
}
},
/**
* 创建一个Promise来进行延迟。
* @param {number} ms - 延迟的毫秒数。
* @returns {Promise<void>}
*/
delay: ms => new Promise(r => setTimeout(r, ms))
};
const UI = {
/**
* 在NodeSeek编辑器的工具栏上创建并注入脚本的UI组件。
* @param {Element} toolbar - 编辑器工具栏元素。
*/
setupToolbar: async toolbar => {
if (!toolbar || toolbar.querySelector(SELECTORS.container)) return;
const container = document.createElement('div');
container.id = 'bzhan-toolbar-container';
container.className = 'bzhan-toolbar-container';
toolbar.appendChild(container);
const bBtn = document.createElement('button');
bBtn.id = 'bzhan-button';
bBtn.className = 'bzhan-button toolbar-item i-icon';
// 样式
bBtn.style.appearance = 'none';
bBtn.style.border = 'none';
bBtn.style.background = 'inherit';
bBtn.style.cursor = 'pointer';
bBtn.insertAdjacentHTML("afterbegin", DOM.svgIcon);
const imgBtn = toolbar.querySelector(SELECTORS.imgBtn);
if (imgBtn) {
toolbar.insertBefore(bBtn, imgBtn)
bBtn.addEventListener('click', async () => {
const bLink = prompt("请输入b站视频bv号,或者分享链接", "");
if (bLink != null && bLink != ""){
const inputText = await Utils.parseLink(bLink)
if(inputText){
Utils.insertMarkdown(``)
}else{
alert('很抱歉,未能成功生成链接,可能是遇到了错误')
}
}
});
}
}
};
// ===== 初始化 (Initialization) =====
// 脚本的入口点和主逻辑流程。
const init = async () => {
// 1. 异步等待编辑器加载完成
Utils.waitForElement(SELECTORS.editor).then(editor => {
DOM.editor = editor;
});
// 2. 异步等待工具栏加载,然后注入UI
Utils.waitForElement(SELECTORS.toolbar).then(UI.setupToolbar);
// 3. 使用MutationObserver监控DOM变化,以应对SPA页面切换导致的UI丢失问题
const observer = new MutationObserver(() => {
const toolbar = document.querySelector(SELECTORS.toolbar);
if (toolbar && !toolbar.querySelector(SELECTORS.container)) {
UI.setupToolbar(toolbar);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'style']
});
// 6. 补充监听特定点击事件,兼容某些tab切换场景
document.addEventListener('click', e => {
if (e.target.closest('.tab-option')) {
setTimeout(() => {
const toolbar = document.querySelector(SELECTORS.toolbar);
if (toolbar && !toolbar.querySelector(SELECTORS.container)) {
UI.setupToolbar(toolbar);
}
}, 100);
}
});
};
// 脚本从load事件后开始执行
window.addEventListener('load', init);
})();
牛的
牛逼