B站小窗口视频功能显示

增强B站功能,B站小窗口视频的操作栏显示

// ==UserScript==
// @name            B站小窗口视频功能显示
// @description     增强B站功能,B站小窗口视频的操作栏显示
// @version         0.0.5
// @author          Grant Howard, Coulomb-G
// @copyright       2024, Grant Howard
// @license         MIT
// @match           *://*.bilibili.com/video/*
// @match           *://*.bilibili.com/bangumi/play/*
// @exclude         *://api.bilibili.com/*
// @exclude         *://api.*.bilibili.com/*
// @exclude         *://*.bilibili.com/api/*
// @exclude         *://member.bilibili.com/studio/bs-editor/*
// @exclude         *://t.bilibili.com/h5/dynamic/specification
// @exclude         *://bbq.bilibili.com/*
// @exclude         *://message.bilibili.com/pages/nav/header_sync
// @exclude         *://s1.hdslb.com/bfs/seed/jinkela/short/cols/iframe.html
// @exclude         *://open-live.bilibili.com/*
// @run-at          document-start
// @grant           unsafeWindow
// @grant           GM_getValue
// @grant           GM_setValue
// @grant           GM_deleteValue
// @grant           GM_info
// @grant           GM_xmlhttpRequest
// @grant           GM_registerMenuCommand
// @grant           GM_unregisterMenuCommand
// @grant           GM_addStyle
// @connect         raw.githubusercontent.com
// @connect         github.com
// @connect         cdn.jsdelivr.net
// @connect         cn.bing.com
// @connect         www.bing.com
// @connect         translate.google.cn
// @connect         translate.google.com
// @connect         localhost
// @connect         *
// @icon            https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/images/logo-small.png
// @icon64          https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/images/logo.png
// @namespace https://greasyfork.org/users/734541
// ==/UserScript==

(() => {
  GM_addStyle(`
    #commenBoxBody{
      position: fixed;
      z-index: 100;
      bottom: 5px;
      left: 0;
      width:300px;
      height: 32px;
      background: transparent;
      padding: 0;
    }
    `);

  const console = (() => {
    const _console = window.console;
    return {
      log: (...args) => {
        _console.log(
          `%c ZAIZAI `,
          'padding: 2px 1px; border-radius: 3px; color: #fff; background: #42c02e; font-weight: bold;',
          ...args,
        );
      },
    };
  })();

  function waitTime(callback, options = { time: 500, isSetup: false }) {
    let timeout = null;
    return new Promise((resolve) => {
      if (options.isSetup) {
        let res = callback();
        if (res) resolve(res);
      }
      timeout = setInterval(() => {
        let res = callback();
        if (res) {
          clearInterval(timeout);
          resolve(res);
        }
      }, options.time);
    });
  }

  /**
   * 创建一个节流函数
   * @param {Function} func 需要节流的函数
   * @param {number} wait 函数执行的最小时间间隔
   * @param {boolean} [options={leading=true, trailing=true}] 配置对象
   * @returns {Function} 节流后的函数
   */
  function throttle(func, wait, options = {}) {
    let timeout = null;
    let context, args;
    let previousNow = 0;
    const { leading = true, trailing = true } = options;

    function wrapper() {
      // Save the context and arguments
      context = this;
      args = arguments;
      const previous = previousNow;
      const remaining = wait - (Date.now() - previous);
      const now = Date.now();

      if (remaining <= 0 || remaining > wait) {
        // 如果当前调用距离上次调用超过了wait时间,则立即执行
        clearTimeout(timeout);
        timeout = null;
        func.apply(context, args);
        previousNow = now;
      } else if (!timeout && trailing) {
        // 如果设置了trailing,则在wait时间结束时执行
        timeout = setTimeout(() => {
          func.apply(context, args);
          previousNow = now;
        }, remaining);
      }

      if (leading && !timeout) {
        // 如果设置了leading,则在第一次调用时立即执行
        func.apply(context, args);
        previousNow = now;
      }
    }

    return wrapper;
  }

  console.log('B站小窗口视频功能显示');

  const state = {
    is: false,
  };

  const onMouseover = async () => {
    if (state.is) {
      return;
    }
    state.is = true;
    const els = await waitTime(
      () => {
        const bpxplayercontrolmask = document.querySelector('.bpx-player-control-mask');

        const bpxplayercontrolentity = document.querySelector('.bpx-player-control-entity');
        const bpxplayercontrolbottom = document.querySelector('.bpx-player-control-bottom');
        if (bpxplayercontrolmask && bpxplayercontrolentity && bpxplayercontrolbottom) {
          return [bpxplayercontrolmask, bpxplayercontrolentity, bpxplayercontrolbottom];
        }
      },
      {
        time: 0,
      },
    );
    els[0].style.opacity = 1;
    els[0].style.display = 'block';
    els[1].style.opacity = 1;
    els[1].style.display = 'block';
    els[2].style.opacity = 1;
    els[2].style.display = 'flex';
    console.log('onMouseover 显示');
    console.log(els);
  };

  const onMouseout = async (e) => {
    if (!state.is && e.tae) {
      return;
    }
    state.is = false;
    const els = await waitTime(
      () => {
        const bpxplayercontrolmask = document.querySelector('.bpx-player-control-mask');

        const bpxplayercontrolentity = document.querySelector('.bpx-player-control-entity');
        const bpxplayercontrolbottom = document.querySelector('.bpx-player-control-bottom');
        if (bpxplayercontrolmask && bpxplayercontrolentity && bpxplayercontrolbottom) {
          return [bpxplayercontrolmask, bpxplayercontrolentity, bpxplayercontrolbottom];
        }
      },
      {
        time: 0,
      },
    );
    els[0].style.opacity = 0;
    els[0].style.display = 'none';
    els[1].style.opacity = 0;
    els[1].style.display = 'none';
    els[2].style.opacity = 0;
    els[2].style.display = 'none';
    console.log('onMouseout 隐藏');
    console.log(els);
  };

  const addCommentBoxButton = async () => {
    if (document.querySelector('#zaizai_commentBox')) {
      console.log('已经添加了评论框按钮');
      return;
    }
    const elBut = await waitTime(() => {
      let button = document.querySelector('.bpx-player-ctrl-btn.bpx-player-ctrl-playbackrate');
      let commentBox = document.querySelector('.bpx-player-video-inputbar');
      if (button && commentBox) {
        button = button.cloneNode(true);
        button.children[0].textContent = '框';
        button.id = 'zaizai_commentBox';

        commentBox = commentBox.cloneNode(true);

        commentBox.querySelector('.bpx-player-video-btn-dm').remove();
        commentBox.querySelector('.bpx-player-video-preview-emoji-wrap').remove();
        commentBox.querySelector('.bpx-player-dm-hint').remove();

        // 隐藏按钮
        const hiheEl = document.createElement('div');
        hiheEl.classList.add('bpx-player-video-btn-dm');
        hiheEl.style.color = '#000';
        hiheEl.style.lineHeight = '25px';
        hiheEl.textContent = '隐藏';
        commentBox.prepend(hiheEl);

        // 容器
        const commenBoxBody = document.createElement('div');
        commenBoxBody.id = 'commenBoxBody';
        commenBoxBody.classList.add('bpx-player-sending-bar');
        commenBoxBody.style.display = 'none';
        commenBoxBody.style.padding = '0';
        commenBoxBody.appendChild(commentBox);

        const sendBut = commentBox.querySelector('.bpx-player-dm-btn-send');
        const inputEl = commentBox.querySelector('.bpx-player-dm-input');

        return [button, commenBoxBody, hiheEl, sendBut, inputEl];
      }
    });
    elBut[0].addEventListener('click', () => {
      let display = elBut[1].style.display;
      elBut[1].removeAttribute('tsbrowser_force_hidden');
      if (display === 'none') {
        elBut[1].style.display = 'block';
        elBut[1].style.zIndex = 9999;
        elBut[1].style.left = (document.body.getBoundingClientRect().width - 300) / 2 + 'px';
      } else {
        elBut[1].style.display = 'none';
      }
    });
    elBut[2].addEventListener('click', () => {
      elBut[1].style.display = 'none';
    });
    elBut[3].addEventListener('click', () => {
      const value = elBut[4].value;
      const playerInput = document.querySelector('#bilibili-player .bpx-player-dm-input');
      playerInput.focus();
      document.execCommand('insertText', false, value);
      document.querySelector('#bilibili-player .bpx-player-dm-btn-send').click();
      elBut[4].value = '';
      elBut[3].classList.add('bui-disabled');
      elBut[3].children[0].textContent = '0';
      let timeout = null;
      let count = 1;
      timeout = setInterval(() => {
        count++;
        if (count >= 6) {
          elBut[3].classList.remove('bui-disabled');
          elBut[3].children[0].textContent = '发送';
          clearTimeout(timeout);
        } else {
          elBut[3].children[0].textContent = count;
        }
      }, 1000);
    });
    document.body.appendChild(elBut[1]);
    document.querySelector('.bpx-player-control-bottom-right').prepend(elBut[0]);
  };

  window.onload = () => {
    window.addEventListener('resize', async () => {
      const { innerWidth, innerHeight } = window;
      if (innerWidth <= 565 && innerHeight <= 320) {
        window.addEventListener('mouseover', onMouseover);
        window.addEventListener('mouseout', onMouseout);
        addCommentBoxButton();
      } else if (innerWidth > 565 && innerHeight > 320) {
        window.removeEventListener('mouseover', onMouseover);
        window.removeEventListener('mouseout', onMouseout);
        state.is = false;

        /**
         * 放大后清空 style 不然b站原本的css不会生效
         */
        const els = await waitTime(
          () => {
            const bpxplayercontrolmask = document.querySelector('.bpx-player-control-mask');

            const bpxplayercontrolentity = document.querySelector('.bpx-player-control-entity');
            const bpxplayercontrolbottom = document.querySelector('.bpx-player-control-bottom');
            if (bpxplayercontrolmask && bpxplayercontrolentity && bpxplayercontrolbottom) {
              return [bpxplayercontrolmask, bpxplayercontrolentity, bpxplayercontrolbottom];
            }
          },
          {
            time: 0,
          },
        );
        els.forEach((item) => {
          item.style.cssText = '';
        });
      }

      if (innerWidth <= 520) {
        document.querySelector('.bpx-player-ctrl-time').style.display = 'none';
        document.querySelector('.bpx-player-ctrl-quality').style.display = 'none';
      } else {
        document.querySelector('.bpx-player-ctrl-time').style.display = 'block';
        document.querySelector('.bpx-player-ctrl-quality').style.display = 'block';
      }
    });
  };
})();