Greasy Fork is available in English.

Bilibili - 优化未登录情况下的移动网页端

优化未登录情况下的移动网页端的使用体验 | V0.2 修复分P无法点击的问题

// ==UserScript==
// @name         Bilibili - 优化未登录情况下的移动网页端
// @namespace    https://bilibili.com/
// @version      0.2
// @description  优化未登录情况下的移动网页端的使用体验 | V0.2 修复分P无法点击的问题
// @license      GPL-3.0
// @author       DD1969
// @match        https://m.bilibili.com/*
// @icon         https://www.bilibili.com/favicon.ico
// @grant        none
// @run-at       document-end
// ==/UserScript==

(async function() {
  'use strict';

  // prevent downloading apk or opening external app
  Object.defineProperty(document, 'hidden', { get: () => true });
  const emptyFunction = () => {};
  setInterval(() => {
    if (window.PlayerAgent && window.PlayerAgent.openApp !== emptyFunction) {
      window.PlayerAgent.openApp = emptyFunction;
    }
  }, 500);

  // add custom style
  const styleElement = document.createElement('style');
  styleElement.textContent = `
    #app .m-navbar .right .face,
    #app .m-navbar .right .m-nav-openapp,
    .home-float-openapp,
    .caution-dialog,
    .launch-app-btn.m-video-main-launchapp,
    [class*=float-openapp],
    #relateRecomMore,
    .card > .open-app,
    #app .mplayer-widescreen-callapp,
    #app .mplayer-fullscreen-call-app,
    .visible-open-app-btn,
    .share-video-info .title-wrapper .title a.label,
    .share-video-info .title-wrapper .icon-spread,
    .up .interact-wrapper,
    .bottom-tabs,
    .list-view__state,
    .mplayer-control-btn-speed .mplayer-control-dot,
    .mplayer-control-btn-quality .mplayer-control-dot,
    #bilibiliPlayer .mplayer-comment-text,
    .v-card-toapp .card .label,
    .play-page-gotop,
    .natural-main-video .main-cover .play-icon,
    .tab-item.more {
      display: none !important;
    }

    .m-navbar .right .search {
      width: auto !important;
      height: 42px !important;
      margin-right: 0 !important;
      padding: 12px 0 8px 128px;
    }

    .m-search-together .list {
      overflow-x: hidden;
    }

    .fixed-module {
      position: relative !important;
    }

    .natural-main-video {
      padding-bottom: 24px;
      flex-direction: column;
    }

    .natural-main-video .main-cover {
      margin-bottom: 20px;
      width: 100% !important;
    }

    .natural-main-video .main-info {
      margin: 0;
      width: 100%;
    }

    .natural-main-video .main-info .btn.light {
      margin-top: 20px;
      width: 100%;
      height: 48px;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 16px;
    }

    .m-video-part-new .list {
      width: 100% !important;
      height: 160px;
      display: grid;
      grid-template-columns: 48% 48%;
      grid-row-gap: 8px;
      justify-content: space-around;
      overflow-y: auto !important;
    }

    .m-video-part-new .list .part-item {
      margin-right: 0 !important;
    }

    .m-video-part-new .list .part-item span {
      width: 100% !important;
      display: flex;
      align-items: center;
      white-space: nowrap !important;
    }

    .m-video-part-new .spread {
      display: none !important;
    }

    .natural-module > .m-video-related {
      margin-top: 0 !important;
      padding-top: 16px;
      border-top: 1px dashed #AAAAAA;
    }

    .bottom-tab > .bottom-tab-header {
      padding: 8px 0;
    }

    .v-dialog.natural-dialog:has(.to-see) {
      opacity: 0 !important;
    }

    .m-video-player {
      position: relative !important;
      top: initial !important;
      z-index: 999 !important;
      margin-top: 11.73333vmin;
    }

    .share-video-info {
      margin-top: 16px !important;
    }

    .share-video-info .title-name {
      margin-left: 0 !important;
    }

    .share-video-info h1.title-text {
      font-weight: bold !important;
      font-size: 16px !important;
    }

    .m-footer {
      margin-top: 40px;
      padding-top: 90px;
      border-top: 1px dashed #AAAAAA;
    }

    .playback-rate-option-container {
      width: 240px;
      padding: 8px;
      display: flex;
      flex-direction: column;
      align-items: center;
      background-color: #FFFFFF;
      border-radius: 4px;
      user-select: none;
    }

    .playback-rate-option {
      width: 100%;
      margin-top: 2px;
      padding: 8px 0;
      color: #FFFFFF;
      background-color: #00AEEC;
      border-top: 1px solid #EEEEEE;
      border-radius: 4px;
      text-align: center;
    }
  `;
  document.head.appendChild(styleElement);

  // open home video in new tab
  setInterval(() => {
    const videoCards = Array.from(document.querySelectorAll('.m-home .card-box a.v-card:not(.modified)'));
    for (const card of videoCards) {
      card.classList.add('modified');
      card.setAttribute('target', '_blank');
    }
  }, 1000);

  // open searched video in new tab
  setInterval(() => {
    const videoCards = Array.from(document.querySelectorAll('.video-list .card-box .v-card-single:not(.modified)'));
    for (const card of videoCards) {
      const maskElement = document.createElement('div');
      maskElement.style = `
        position: absolute;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100%;
        background-color: #000000;
        opacity: 0;
      `;
      maskElement.addEventListener('click', (e) => {
        e.stopPropagation();
        if (parseInt(card.dataset.aid) === 0) return;
        window.open(`https://m.bilibili.com/video/${av2bv(card.dataset.aid)}`, '_blank');
      });

      card.style.position = 'relative';
      card.classList.add('modified');
      card.appendChild(maskElement);
    }
  }, 1000);

  // click cancel btn which appeared after clicking video card in search page
  setInterval(() => {
    const openAppDialogCancelBtn = document.querySelector('.v-dialog .open-app-dialog .open-app-dialog-btn.cancel');
    if (openAppDialogCancelBtn) openAppDialogCancelBtn.click();
  }, 100);

  // click the "X" mark in the bottom dialog in video page
  const timer4CloseBtn = setInterval(() => {
    const openAppDialogCloseBtn = document.querySelector('.openapp-dialog .dialog-close');
    if (openAppDialogCloseBtn) {
      openAppDialogCloseBtn.click();
      clearInterval(timer4CloseBtn);
    }
  }, 100);

  // add publish date
  setInterval(() => {
    const publishDate = document.querySelector('meta[itemprop=datePublished]');
    const authorElement = document.querySelector('.natural-module .main-info .author');
    if (publishDate && authorElement && !authorElement.textContent.includes(' @ ')) {
      authorElement.textContent += ` @ ${publishDate.getAttribute('content')}`;
    }
  }, 500);

  // click the "play now" button on the top
  const timer4PlayBtn = setInterval(() => {
    const playNowBtn = document.querySelector('.natural-main-video .main-info .btn.light');
    if (playNowBtn) {
      playNowBtn.onclick = () => {
        const timer4ToSeeBtn = setInterval(() => {
          const toSeeBtn = document.querySelector('.to-see');
          if (toSeeBtn) {
            toSeeBtn.click();
            clearInterval(timer4ToSeeBtn);
          }
        }, 100);
      }
      clearInterval(timer4PlayBtn);
    }
  }, 100);

  // open space by clicking author avatar or name
  setInterval(() => {
    const upElement = document.querySelector('.share-video-info .up .face');
    const upID = window?.__INITIAL_STATE__?.video?.upInfo?.card?.mid;
    if (upElement && upID) {
      if (upElement.classList.contains('modified')) return;
      upElement.classList.add('modified');
      upElement.onclick = () => window.open(`https://m.bilibili.com/space/${upID}`, '_blank');
    }
  }, 1000);

  // open recommand video
  setInterval(() => {
    const videoCards = Array.from(document.querySelectorAll('.m-video-related .card-box .v-card-toapp:not(.modified)'));
    for (const card of videoCards) {
      card.classList.add('modified');
      card.querySelector('a').onclick = () => window.open(`https://m.bilibili.com/video/${av2bv(card.dataset.aid)}`, '_blank');
    }
  }, 1000);

  // open video in space
  setInterval(() => {
    const cards = Array.from(document.querySelectorAll('.dynamic-list .list .cover .card-content .main > [regstring][type="8"]:not(.modified)'));
    for (const card of cards) {
      card.classList.add('modified');
      card.onclick = () => window.open(`https://m.bilibili.com/video/${av2bv(Number(card.id))}`, '_blank');
    }
  }, 1000);

  // enable playback rate button
  const timer4PlaybackRateBtn = setInterval(() => {
    const playbackRateBtn = document.querySelector('.mplayer-control-bar .mplayer-control-btn-speed');
    if (playbackRateBtn) {
      playbackRateBtn.onclick = () => {
        const maskElement = document.createElement('div');
        maskElement.style = `
          position: fixed;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          align-items: center;
          background-color: rgba(0, 0, 0, 0.5);
          z-index: 999999;
        `;

        maskElement.innerHTML = `
          <div class="playback-rate-option-container">
            <span style="margin-bottom: 6px; padding: 6px 0;">播放倍速</span>
            <span class="playback-rate-option" data-rate="5">5.0x</span>
            <span class="playback-rate-option" data-rate="3">3.0x</span>
            <span class="playback-rate-option" data-rate="2">2.0x</span>
            <span class="playback-rate-option" data-rate="1.5">1.5x</span>
            <span class="playback-rate-option" data-rate="1">1.0x</span>
            <span class="playback-rate-option" data-rate="0.5">0.5x</span>
          </div>
        `;

        maskElement
          .querySelectorAll('.playback-rate-option')
          .forEach(optionElement => optionElement.addEventListener('click', function() {
            const videoElement = document.querySelector('#bilibiliPlayer video');
            if (videoElement) videoElement.playbackRate = parseFloat(this.dataset.rate);
          }));
        
        maskElement.onclick = () => maskElement.remove();

        (document.querySelector('#bilibiliPlayer > .mplayer.mplayer-wide') || document.body).appendChild(maskElement);
      }
      clearInterval(timer4PlaybackRateBtn);
    }
  }, 100);

  // av ID to bv ID
  function av2bv(avid) {
    const BYTES = ["B", "V", 1, "", "", "", "", "", "", "", "", ""];
    const BV_LEN = BYTES.length;
    const MAX_AID = 1n << 51n;
    const XOR_CODE = 23442827791579n;
    const BASE = 58n;
    const DIGIT_MAP = [0, 1, 2, 9, 7, 5, 6, 4, 8, 3, 10, 11];
    const ALPHABET = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'.split('');

    if(typeof avid !== "bigint") avid = BigInt(avid);
    const bytes = Array.from(BYTES);
    let bv_idx = BV_LEN - 1;
    let tmp = (MAX_AID | avid) ^ XOR_CODE;
    while (tmp !== 0n) {
      let table_idx = tmp % BASE;
      bytes[DIGIT_MAP[Number(bv_idx)]] = ALPHABET[Number(table_idx)];
      tmp /= BASE;
      bv_idx -= 1;
    }
    return bytes.join("");
  }

})();