在B站(Bilibili)网页版自动显示歌词

自动歌词显示

// ==UserScript==
// @name 在B站(Bilibili)网页版自动显示歌词
// @version 1.0.0
// @description 自动歌词显示
// @author Tian
// @namespace BilibiliMusicLRC
// @license MIT
// @match https://www.bilibili.com/video/*
// @require https://static.hdslb.com/js/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @connect ark.cn-beijing.volces.com
// ==/UserScript==

(function () {
  'use strict';

  // 存储密钥(建议通过脚本设置页面输入,不要直接硬编码)
  // 首次使用请在控制台执行:
  // GM_setValue('tencentSecretId', '你的SecretId')
  // GM_setValue('tencentSecretKey', '你的SecretKey')
  const SECRET_ID = 'AKIDToxtgHj03va2HNHzUmtnUxFMG6jAPakl';
  const SECRET_KEY = 'tYzlMlz0sfOzOgu0ufDXdVCgz62sUt54';
  const TOKEN = "";

  // API配置
  const host = "ark.cn-beijing.volces.com/api/v3/chat/completions";

  // 常量配置
  let music_Name = [];
  let music_Msg = [];
  let music_Chapter = [];
  let api_lock = false; // 防止重复请求锁

  /**
   * 初始化
   */
  function init() {
    // 也可以添加一个按钮,点击时调用API
    const apiButton = document.createElement('button');
    apiButton.id = 'call-nlp-api-button';
    apiButton.textContent = '重新查找歌词!';
    apiButton.style.position = 'fixed';
    apiButton.style.top = '80px';
    apiButton.style.left = '10px';
    apiButton.style.padding = '10px 15px';
    apiButton.style.zIndex = '9998';
    apiButton.style.cursor = 'pointer';
    apiButton.addEventListener('click', callNlpApi);
    document.body.appendChild(apiButton);
    // 结果显示区域
    const resultBox = document.createElement('div');
    resultBox.className = 'music-lyric-result-box';
    resultBox.style.position = 'fixed';
    resultBox.style.top = '170px';
    resultBox.style.left = '10px';
    resultBox.style.padding = '10px 15px';
    resultBox.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    resultBox.style.color = '#fff';
    resultBox.style.zIndex = '9999';
    resultBox.style.maxWidth = '300px';
    resultBox.style.maxHeight = '400px';
    resultBox.style.overflowY = 'auto';
    resultBox.style.borderRadius = '8px';
    resultBox.style.fontSize = '14px';
    resultBox.style.lineHeight = '1.5';
    resultBox.innerHTML = `
    <strong>识别到的歌词/作品:</strong>
    <br>
    <div class="box_item"></div>
    `;
    document.body.appendChild(resultBox);
    const resultInput = document.createElement('input');
    resultInput.type = 'text';
    resultInput.placeholder = '输入歌名获取歌词';
    resultInput.disabled = false;
    resultInput.readOnly = false;
    resultInput.autocomplete = 'off'; // 避免自动填充干扰
    // 添加input样式
    resultInput.className = 'music-lyric-input';
    resultInput.style.position = 'fixed';
    resultInput.style.top = '130px';
    resultInput.style.left = '10px';
    resultInput.style.width = '150px';
    resultInput.style.padding = '5px';
    resultInput.style.borderRadius = '5px';
    resultInput.style.border = '1px solid #ddd'; // 原代码border: none可能导致视觉上不可见
    resultInput.style.boxSizing = 'border-box';
    resultInput.style.fontSize = '14px';
    resultInput.style.outline = 'none'; // 可选:去除聚焦边框
    resultInput.style.zIndex = '9999'; // 确保在最上层,避免被遮挡
    resultInput.addEventListener('keydown', (e) => {
      e.stopPropagation(); // 阻止事件冒泡到页面
    });
    resultInput.addEventListener('input', (e) => {
      e.stopPropagation();
    });
    resultInput.addEventListener('blur', (e) => {
      getMusic(e.target.value);
      e.stopPropagation();
    });
    document.body.appendChild(resultInput);
  }

  /**
   * 发起API请求
   */
  async function callNlpApi() {
    if (api_lock) {
      return; // 如果锁定,则不执行
    }
    api_lock = true; // 上锁
    resetConstants();
    const box_item = document.querySelector('.box_item');
    if (box_item) {
      box_item.innerHTML = '识别中...';
    }
    try {
      const sampleText = $('.video-title').text();
      music_Chapter = [];
      let tempChapter = document.querySelectorAll('.bpx-player-ctrl-viewpoint-menu-item-content');
      for (let i = 0; i < tempChapter.length; i++) {
        music_Chapter.push(tempChapter[i].textContent);
      }
      console.log(music_Chapter);
      const payload = JSON.stringify({
        "model": "doubao-1-5-thinking-pro-250415",
        "messages": [
          { "role": "system", "content": "你是人工智能助手." },
          { "role": "user", "content": `请从以下文本中识别出歌词和作品名称,文本内容为:${sampleText + music_Chapter.join(',')}。只需要返回歌词和作品名称即可,用英文逗号分割,不需要其他多余的描述。如果没有识别出歌词或作品,请返回“ ”。` }
        ]
      });
      // 使用GM_xmlhttpRequest发送请求(避免跨域问题)
      GM_xmlhttpRequest({
        method: 'POST',
        url: `https://${host}`,
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer 628cbe0e-210d-46e0-bd89-cee9ff9957e0`
        },
        data: payload,
        onload: function (response) {
          console.log("API响应:", JSON.parse(response.responseText));
          // 可以在这里处理返回结果,例如显示在页面上
          box_item.innerHTML = '';
          api_lock = false; // 解锁
          showResult(JSON.parse(response.responseText).choices[0].message.content);
        },
        onerror: function (error) {
          api_lock = false; // 解锁
          box_item.innerHTML = '请求出错,请稍后再试.';
        }
      });

    } catch (error) {
      console.error("调用API时出错:", error);
    }
  }

  /**
   * 获取歌曲
   */
  async function getMusic(musicName) {
    if (api_lock) {
      return; // 如果锁定,则不执行
    }
    api_lock = true; // 上锁
    if (document.querySelector('.music-lyric-result-lrc')) {
      document.querySelector('.music-lyric-result-lrc').remove();
    }
    try {
      // 使用GM_xmlhttpRequest发送请求(避免跨域问题)
      GM_xmlhttpRequest({
        url: 'https://api.52vmy.cn/api/music/lrc?msg=' + encodeURIComponent(musicName) + '&n=1',
        method: 'GET',
        headers: {
          'Content-Type': 'application/json'
        },
        onload: function (response) {
          // 处理数据 正则删除[00:00.35]
          const cleanedLyric = response.responseText.replace(/\[.*?\]/g, '');
          music_Msg = cleanedLyric
            .split(/[\r\n]+/)  // 按任意换行符拆分(兼容所有系统)
            .filter(line => line.trim() !== '');  // 过滤空行/纯空白行
          const resultMusicLrc = document.createElement('div');
          resultMusicLrc.className = 'music-lyric-result-lrc';
          resultMusicLrc.style.position = 'fixed';
          resultMusicLrc.style.top = '80px';
          resultMusicLrc.style.right = '10px';
          resultMusicLrc.style.padding = '10px 15px';
          resultMusicLrc.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
          resultMusicLrc.style.color = '#fff';
          resultMusicLrc.style.zIndex = '9999';
          resultMusicLrc.style.maxWidth = '200px';
          resultMusicLrc.style.maxHeight = '300px';
          resultMusicLrc.style.overflowY = 'auto';
          resultMusicLrc.style.borderRadius = '8px';
          resultMusicLrc.style.fontSize = '14px';
          resultMusicLrc.style.lineHeight = '1.5';
          // 隐藏滑动条
          resultMusicLrc.style.scrollbarWidth = 'none'; // Firefox
          resultMusicLrc.style.msOverflowStyle = 'none'; // IE 10+
          // 按住拖动
          resultMusicLrc.style.cursor = 'move';
          resultMusicLrc.onmousedown = function (event) {
            const shiftX = event.clientX - resultMusicLrc.getBoundingClientRect().left
            const shiftY = event.clientY - resultMusicLrc.getBoundingClientRect().top
            function moveAt(pageX, pageY) {
              resultMusicLrc.style.left = pageX - shiftX + 'px'
              resultMusicLrc.style.top = pageY - shiftY + 'px'
            }
            function onMouseMove(event) {
              moveAt(event.pageX, event.pageY)
            }
            document.addEventListener('mousemove', onMouseMove)
            resultMusicLrc.onmouseup = function () {
              document.removeEventListener('mousemove', onMouseMove)
              resultMusicLrc.onmouseup = null
            }
            resultMusicLrc.ondragstart = function () {
              return false
            }

          }
          document.body.appendChild(resultMusicLrc);
          for (let i = 0; i < music_Msg.length; i++) {
            const lineDiv = document.createElement('div');
            lineDiv.textContent = music_Msg[i];
            lineDiv.style.padding = '5px';
            lineDiv.style.fontSize = '16px';
            resultMusicLrc.appendChild(lineDiv);
          }
          api_lock = false; // 解锁
        },
        onerror: function (error) {
          console.error("API请求错误:", error);
          api_lock = false; // 解锁
        }
      });

    } catch (error) {
      console.error("调用API时出错:", error);
      api_lock = false; // 解锁
    }
  }

  /**
   * 在页面上显示结果
   * @param {object} result API返回的结果
   */
  function showResult(result) {
    const resultBoxItem = document.querySelector('.box_item');
    // 先处理数据 按照,分割
    music_Name = result.split(',');
    // 去重
    music_Name = Array.from(new Set(music_Name));
    for (let i = 0; i < music_Name.length; i++) {
      const resultNameItem = document.createElement('div');
      resultNameItem.style.display = 'inline-block';
      resultNameItem.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
      resultNameItem.style.padding = '5px 10px';
      resultNameItem.style.borderRadius = '5px';
      resultNameItem.style.margin = '2px 5px 2px 0';
      resultNameItem.style.userSelect = 'none';
      resultNameItem.style.cursor = 'pointer';
      resultNameItem.innerHTML = music_Name[i];
      resultNameItem.addEventListener('click', function () {
        getMusic(music_Name[i]);
      });
      resultBoxItem.appendChild(resultNameItem);
    }
    if (music_Name.length === 0) {
      resultBoxItem.innerHTML = '未识别到歌词或作品';
    }
  }

  /**
   * 重置常量
   */
  function resetConstants() {
    music_Name = [];
    const resultBoxItem = document.querySelector('.box_item');
    if (resultBoxItem) {
      resultBoxItem.innerHTML = '';
    }
  }

  // 页面加载完成后执行
  $(document).ready(function () {
    init();
  });

})();