bilibili 移动端

b 站移动端网页推荐视频直接看

当前为 2023-06-13 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name               Bilibili Mobile
// @name:zh-CN         bilibili 移动端
// @namespace          https://github.com/jk278/bilibili-mobile
// @version            2.2.0
// @description        view bilibili mobile page recommended video directly 
// @description:zh-CN  b 站移动端网页推荐视频直接看
// @author             jk278
// @match              *://m.bilibili.com
// @match              *://m.bilibili.com/video/*
// @grant              none
// @run-at             document-start
// @icon               https://www.bilibili.com/favicon.ico
// ==/UserScript==

(function () {
  'use strict';

  function getIdByTitle(keyword, onResult) {
    // 禁止使用 const encodedKeyword = encodeURIComponent(keyword); 

    // 生成一个唯一的回调函数名称
    const callbackName = `jsonp_callback_${Date.now()}_${Math.floor(Math.random() * 100000)}`;

    // 将回调函数挂载到 window 对象上
    window[callbackName] = function (responseData) {
      console.log('test data: ', responseData.data.result[11].data[0]); // 这里会打印 JSON 数据
      if (responseData.data.result[11].data[0]) {
        const bvId = responseData.data.result[11].data[0].bvid;
        onResult(bvId);
      } else {
        onResult(null, "Error occurred while getting bvId.");
      }
    };

    const script = document.createElement('script');
    script.src = `https://api.bilibili.com/x/web-interface/search/all/v2?page=1&keyword=${keyword}&jsonp=jsonp&callback=${callbackName}`;
    document.body.appendChild(script);
  }

  function addTargetElementListener(targetElement) {
    if (targetElement) {
      const anchor = targetElement.firstChild;
      const h2Element = anchor.lastChild;

      const keyword = encodeURIComponent(h2Element.innerHTML);

      let videoUrl = null; // 初始化 videoUrl 为 null

      const callback = (bvId, error) => {
        if (bvId) {
          console.log("test bvId: ", bvId);
          videoUrl = `https://m.bilibili.com/video/${bvId}`; // 在回调内更新 videoUrl 的值
        } else {
          console.error("test bvId wrong: ", error);
        }
      };

      try {
        getIdByTitle(keyword, callback);
      } catch (error) {
        console.log('Test getIdByTitle Error!', error);
      }

      console.log('test anchor: ', anchor);
      anchor.addEventListener('click', async (event) => { // 这个时灵时不灵
        event.preventDefault();
        event.stopImmediatePropagation();
        console.log('Execute test!');

        // 如果 videoUrl 为空(尚未设置),不进行导航
        if (!videoUrl) {
          console.log('Test null! Skip navigation as videoUrl is not set yet.');
          return;
        } else {
          console.log('test videoUrl: ', videoUrl);
        }

        // 使用setTimeout将当前操作放在事件队列的末尾以确保正确执行
        await new Promise((resolve) => setTimeout(resolve, 0));
        window.location.href = videoUrl;
      }, true);
    }
    console.log('Execute Video! 添加监听器');
  }

  function observeCardBox() {
    const cardBox = document.querySelector('.card-box');
    const targetElements = cardBox.children;

    // 为初始子元素添加监听器
    Array.from(targetElements).forEach(addTargetElementListener);

    // 创建 MutationObserver 以监听子元素的变化
    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          mutation.addedNodes.forEach((addedNode) => {
            addTargetElementListener(addedNode);
          });
        }
      });
    });

    // 配置观察选项
    const observerConfig = {
      childList: true,
    };

    // 开始观察
    observer.observe(cardBox, observerConfig);
  }

  function changeTargetElementWithCSS(css) {
    const style = document.createElement('style');
    style.textContent = css;

    // 如果 document.documentElement 可用,将样式添加到文档
    if (document.documentElement) {
      document.documentElement.appendChild(style);
    } else {
      // 如果 document.documentElement 不可用,监听 readyStateChange 事件
      document.addEventListener('readystatechange', () => {
        if (!style.isConnected && document.readyState !== 'loading') {
          document.documentElement.appendChild(style);
        }
      });
    }
  }
  function customElementStyle() {
    // transparentElement // 调试时要注释掉
    const css1 = `.v-dialog, .v-dialog * { opacity: 0 !important; }`;

    // bigImgElement
    const css2 = `.main-cover { width: 100% !important; border-radius: 0 !important; }
    .natural-main-video { padding: 0 !important; }`;

    const css = `${css1}\n${css2}`;
    changeTargetElementWithCSS(css);
  }

  function runVideo() {

    const player = document.querySelector('.player-icon');
    console.log('test player: ', player);
    const play = document.querySelector('.play-icon');
    console.log('test player: ', play);
    if (play) {
      play.click();
    } else if (player) {
      player.click();
    }

    const timer = setInterval(function () {
      const dialog = document.querySelector('.dialog');
      if (dialog) {
        clearInterval(timer);
        dialog.lastChild.click();
      }
    }, 50);

    observeCardBox();

    console.log('Execute Video!');
  }

  function addHomeTargetElementListener(tag) {
    tag.addEventListener('click', async (event) => { // 异步,然后放到 js 末尾执行
      event.preventDefault(); // 阻止默认行为
      // event.stopPropagation(); // 阻止事件冒泡
      event.stopImmediatePropagation(); // 阻止其他事件监听器的执行
      console.log("test href: ", tag.getAttribute("href"));

      // 等待下一个事件循环迭代
      await new Promise((resolve) => setTimeout(resolve, 0)); // THIS!
      window.location.href = tag.getAttribute("href");

      // 在此处执行您所需的操作,例如更新页面状态、导航等。
      history.pushState(null, '', href); // 使用 pushState 更新浏览器的 URL
    }, true);
  }

  function runHome() {
    // observeHomeCardBox
    const cardBox = document.querySelector('.card-box');
    const aTags = cardBox.children;

    Array.from(aTags).forEach(addHomeTargetElementListener);

    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          mutation.addedNodes.forEach((addedNode) => {
            addHomeTargetElementListener(addedNode);
          });
        }
      });
    });

    const observerConfig = {
      childList: true,
    };

    observer.observe(cardBox, observerConfig);

    console.log('Execute Home!');
  }

  // 针对 VIA 浏览器优化,判断 DOM 状态
  function executeAfterDOMContentLoaded(callback) {
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', () => callback());
    } else {
      callback();
    }
  }

  customElementStyle();
  executeAfterDOMContentLoaded(() => {
    let pathname = window.location.pathname;
    if (pathname.startsWith('/video')) {
      runVideo();
    } else if (pathname === '/' || pathname === '') {
      runHome();
    }
  });

})();