DeepFloodbeta

【油猴】快速插入b站视频链接插件

按钮位置

lUYtHaxr3DLMfDu9TOCzopnLgsaHbpf3.webp

支持的链接格式

pc分享链接
ApdaPvU1Mqgfv2ljWwJU77FdnV7IZS9k.webp

手机分享链接
Kj098jtEPXSBIiTADXsPM6ZKHyMm34wc.webp

手机长按复制bv号
FqB0qv8ua0ynucQq4tguwXjF5UKboJah.webp

插件代码,自取


// ==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(`![](${inputText})`)
            }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);
})();


特别感谢nodeimage油猴插件的作者shuai 在里面copy了一大堆代码