哔哩哔哩新版首页排版调整和去广告(bilibili)

对新版B站首页的每行显示的视频数量进行调整, 同时删除所有广告, 并可设置屏蔽内容 (大尺寸屏幕每行将显示更多的视频)

Mint 2023.10.30.. Lásd a legutóbbi verzió

// ==UserScript==
// @name          哔哩哔哩新版首页排版调整和去广告(bilibili)
// @namespace     http://tampermonkey.net/
// @version       1.3.8
// @author        Ling2Ling4
// @description   对新版B站首页的每行显示的视频数量进行调整, 同时删除所有广告, 并可设置屏蔽内容 (大尺寸屏幕每行将显示更多的视频)
// @license MIT
// @icon data:image/png;base64,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
// @match         *://www.bilibili.com/*
// @exclude       *://www.bilibili.com/all*
// @exclude       *://www.bilibili.com/video*
// @exclude       *://www.bilibili.com/anime*
// @exclude       *://www.bilibili.com/pgc*
// @exclude       *://www.bilibili.com/live*
// @exclude       *://www.bilibili.com/article*
// @exclude       *://www.bilibili.com/upuser*
// @exclude       *://www.bilibili.com/match*
// @exclude       *://www.bilibili.com/platform*
// @exclude       *://www.bilibili.com/bangumi*
// @exclude       *://www.bilibili.com/cheese*
// @grant         GM_setValue
// @grant         GM_getValue
// @grant         GM_registerMenuCommand
// @compatible    chrome
// @compatible    edge
// @compatible    firefox
// ==/UserScript==

(function () {
  ("use strict");

  // >>>>> 请在浏览器右上角的油猴插件的设置面板中设置该插件的部分功能 <<<<<

  // 类名表 (或选择器)
  const classMap = {
    标题: "h3 a",
    作者: "bili-video-card__info--owner", // 含日期. 仅作者: bili-video-card__info--author
    分类: "floor-title",
    vDom: ["container", "no-banner-container", "is-version8"], // 视频区域 的容器元素
    video: "feed-card", // 换一换的视频 (一般是前十个)
    nav: "bili-header__bar", // 导航栏的元素
    banner: "bili-header__banner", // 横幅背景的元素
    btn: "roll-btn", // 右侧换一换按钮
    btn2: "flexible-roll-btn", // 新版右下角换一换按钮
  };
  let vDom, nav, banner;
  getDoms();
  if (!vDom) {
    return;
  }

  // 默认值
  const base_isClearAd = true; // 是否删除'广告'(屏蔽视频). 默认 true
  const base_isTrueEnd = false; // 是否将广告移至预加载视频的后面. 默认 false
  const base_isAutoLayout = true; // 是否根据缩放自动布局 (是否根据像素宽度布局). 默认 true
  const base_isLoadOne = false; // 是否视频全加载. 默认 false
  const base_videoNumRule =
    "0,1300,2; 1300,1800,3; 1800,3000,4; 3000,3700,5; 3700,6300,6"; // 视频排列规则, 其他尺寸按照初始方式排列
  const base_delClassArr = "广告, 推广"; // 屏蔽的类名列表, 子元素包含某类名也可屏蔽

  // 获取存储的数据
  const isClearAd = getValue("setting_isClearAd", base_isClearAd);
  const isTrueEnd = getValue("setting_isTrueEnd", base_isTrueEnd);
  const isAutoLayout = getValue("setting_isAutoLayout", base_isAutoLayout);
  const isLoadOne = getValue("setting_isLoadOne", base_isLoadOne);
  let videoNumRule = getValue("setting_videoNumRule", base_videoNumRule);
  let delClassArr = getValue("setting_delClassArr", base_delClassArr);

  // console.log(isClearAd, isTrueEnd, isLoadOne, videoNumRule, delClassArr);

  // 屏蔽的类名表
  const delClassMap = {
    广告: "bili-video-card__info--ad",
    推广: "bili-video-card__info--creative-ad",
    特殊: "floor-single-card",
    直播: "living", // 分类=直播
    番剧: "分类=番剧",
    综艺: "分类=综艺",
    课堂: "分类=课堂",
    漫画: "分类=漫画",
    国创: "分类=国创",
    电影: "分类=电影",
    纪录片: "分类=纪录片",
    电视剧: "分类=电视剧",
  };
  // 设置的文本
  const settingText = {
    isClearAd: `是否删除广告, 若不删除则会将所有广告移至视频列表的最后
默认: ${base_isClearAd ? "是 (确定)" : "否 (取消)"}
当前: `,
    isTrueEnd: `是否将广告移至预加载视频的后面, 关闭后广告将放置在预加载视频的前面 一般视频的后面. 开启的效率更高
默认: ${base_isTrueEnd ? "是 (确定)" : "否 (取消)"}
当前: `,
    isAutoLayout: `是否根据缩放自动调整视频布局
默认: ${base_isAutoLayout ? "是 (确定)" : "否 (取消)"}
当前: `,
    isLoadOne: `是否在进入网站时加载视口区域的全部视频, 开启时视频将会全部加载, 但会闪一下
默认: ${base_isLoadOne ? "是 (确定)" : "否 (取消)"}
当前: `,
    delClassArr: `屏蔽设置, 可根据需要自行修改, 可自定义, 每项用 ; 分隔
----默认:
${base_delClassArr}
----可选: ('特殊'包含了直播 番剧 课堂...)
广告、推广、特殊、直播、番剧、综艺、课堂、漫画、国创、电影、纪录片、电视剧
----自定义:
1. 标题=xxx, 可屏蔽标题含xxx的视频, xxx部分支持&&运算符, 如: 标题=A&&B, 表示屏蔽标题同时含有A B内容的视频
2. 作者=xxx, 可屏蔽作者名和发布日期中含xxx的视频`,
    videoNumRule: `视频排列规则, 每条规则用 ; 分隔. 其他尺寸按照初始方式排列
示例: 1450,2400,4 表示浏览器宽度在1450~2400像素时每行显示4个视频(前两行)
默认:
${base_videoNumRule}`,
  };
  const errKeyArr = ["", "_2"];
  const errKeyInfo = {
    disNum: "setting_err_disNum",
    errNum: "setting_err_num",
    errTime: "setting_err_time",
    isTip: "setting_err_isTip",
  };
  const disErrTipNum = 3; // 每小段报错弹窗提醒次数 (短时间内的提醒次数)
  const errTipNum = 5 * disErrTipNum; // 报错弹窗的总提醒次数
  const errTipInterval = 2; // 每段报错弹窗提醒时间间隔(小时)
  const errNumReset = 5; // 报错次数重置的天数
  const queryNum = 0; // 处理的视频数量, 对前 queryNum 个视频中的广告进行处理(删除或置后), 0表示对全部视频进行处理. 默认 0
  const marginTop1 = 40; // 第三行视频的上边距
  const marginTop2 = 24; // 第四行及以上视频的上边距
  const zoom = window.devicePixelRatio; // 获取浏览器缩放 (包括显示器缩放的影响)

  let cssDom;
  let cssText;
  let oldCssText;
  let isChange = false; // 每行视频数是否需要变化
  let showVideoNum = 3; // 当前每行显示的视频数 (以第一行为准), 网站默认值为3
  let videoNum = 0; // 视频总数
  let newVideoNum = 0; // 新获取的视频总数
  let firstAdIndex = 0; // 第一个广告的索引
  let pageZoom = 1; // 页面缩放

  let w = getW(); // 浏览器视口宽度
  videoNum = getVideoNum(vDom); // 计算当前视频总数
  let adArr = getAd(queryNum, delClassArr, newVideoNum, 1);
  delAd(adArr, vDom); // 将所有广告放置在最后 或 删除
  setTimeout(() => {
    delAdFn();
    loadTopVideo();
  }, 1000);
  zoomPage(); // 缩放页面
  setStyle(); // 调整视频排列
  resetErrInfo(); // 重置err相关的数据

  let rollBtn;
  let btnSvg;
  let rollBtn2;
  // 刷新视频
  window.addEventListener("click", () => {
    if (!rollBtn) {
      adArr = getAd(showVideoNum * 3 + 2, delClassArr, newVideoNum, 1);
      delAd(adArr, vDom);
      rollBtn = document.querySelector("button." + classMap.btn); // 换一换按钮
      btnSvg = rollBtn && rollBtn.querySelector("svg"); // 换一换按钮的旋转图标
      // 点击按钮后对新视频中的广告进行处理
      if (btnSvg) {
        btnSvg.addEventListener("transitionend", () => {
          // console.log("视频刷新成功");
          adArr = getAd(showVideoNum * 3 + 2 + 3, delClassArr, newVideoNum, 1);
          !isTrueEnd &&
            adArr.forEach((item) => {
              item.forEach((adItem) => {
                adItem.style.display = "block";
              });
            });
          delAd(adArr, vDom);
        });
      } else {
        rollBtn &&
          rollBtn.addEventListener("click", () => {
            setTimeout(() => {
              adArr = getAd(
                showVideoNum * 3 + 2 + 3,
                delClassArr,
                newVideoNum,
                1
              );
              delAd(adArr, vDom);
            }, 500);
          });
      }
    }
    if (!rollBtn2) {
      adArr = getAd(queryNum, delClassArr, newVideoNum, 1);
      delAd(adArr, vDom);
      rollBtn2 = document.querySelector("." + classMap.btn2); // 新版右下角的换一换按钮
      // 点击按钮后对新视频中的广告进行处理
      rollBtn2 &&
        rollBtn2.addEventListener("click", () => {
          setTimeout(() => {
            videoNum = getVideoNum(vDom); // 计算当前视频总数
            firstAdIndex = 0;
            adArr = getAd(
              queryNum,
              delClassArr,
              0,
              1,
              isTrueEnd ? true : false
            );
            delAd(adArr, vDom, true); // 强制删除广告
            loadTopVideo();
          }, 800);
        });
    }
  });

  // 窗口调整后重新计算视频的行数量
  let timer;
  window.addEventListener("resize", () => {
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      // console.log("窗口改变");
      const newW = getW();
      if (newW > w) {
        delAdFn(); // 若新增广告则删除
      }
      w = newW;
      zoomPage();
      setStyle();
    }, 400);
  });

  // 加载的新视频去除广告
  let timer2, timer3;
  window.addEventListener("wheel", () => {
    timer2 && clearTimeout(timer2);
    timer3 && clearTimeout(timer3);
    timer2 = setTimeout(() => {
      delAdFn(timer3);
    }, 600);
    timer3 = setTimeout(() => {
      delAdFn();
    }, 1500);
  });

  GM_registerMenuCommand("基础设置", () => {
    basicSetting(settingText);
  });
  GM_registerMenuCommand("屏蔽设置", () => {
    delSetting(settingText);
  });
  GM_registerMenuCommand("排列规则", () => {
    layoutSetting(settingText);
  });
  GM_registerMenuCommand("重置设置", () => {
    resetSettings();
  });

  // 获取视口宽度
  function getW() {
    let width =
      window.innerWidth ||
      document.documentElement.clientWidth ||
      document.body.clientWidth;
    // console.log("显示器缩放:", zoom);
    console.log("浏览器实际宽度:", width * window.devicePixelRatio);
    console.log("浏览器像素宽度:", width * zoom);
    !isAutoLayout && (width *= window.devicePixelRatio);
    return width;
  }

  function getDoms() {
    if (typeof classMap.vDom === "string") {
      vDom = document.querySelector("." + classMap.vDom);
    } else {
      classMap.vDom.forEach((item) => {
        vDom = document.querySelector("." + item);
      });
    }
    if (!vDom) {
      vDom = document.querySelector("." + classMap.video).parentElement;
    }
    nav = document.querySelector("." + classMap.nav);
    banner = document.querySelector("." + classMap.banner);
  }

  // 缩放页面 至消除横向滚动条
  function zoomPage() {
    if (document.getBoxObjectFor) {
      return; // 火狐不支持zoom, 而用scale会有bug
    }
    const rootDom = document.documentElement;
    let rate = rootDom.scrollWidth / getMainW();
    !document.body.style.overflow &&
      (document.body.style.overflow = "hidden auto");
    if (rate > 1) {
      // 存在横向滚动条
      pageZoom *= 1 / rate;
      rootDom.style.zoom = pageZoom;
    } else {
      pageZoom = 1;
      rootDom.style.zoom = 1;
      rate = rootDom.scrollWidth / getMainW();
      if (rate > 1) {
        pageZoom *= 1 / rate;
        rootDom.style.zoom = pageZoom;
      }
    }
    // 主区域的宽度, 部分时候总宽(导航栏)会大于主区域的宽度
    function getMainW() {
      let navW = nav ? nav.scrollWidth : 0;
      let bannerW = banner ? banner.scrollWidth : 0;
      return navW > bannerW ? navW : rootDom.clientWidth;
    }
  }

  /**
   * 获取所有的 推广 和 广告 的元素的列表
   * @param {*} queryNum 需要检索的视频数量
   * @param {Array} delClassArr 需要删除的类名列表
   * @param {*} vNum 视频总数
   * @param {*} startIndex 检索的视频的起始索引位
   * @param {Boolean} isAll 是否检索预加载视频以及后面的视频
   * @returns {Array} 含各类广告列表的列表 [[...],[...],...]
   */
  function getAd(queryNum, delClassArr, vNum, startIndex = 1, isAll = false) {
    const arr = [];
    delClassArr.forEach(() => {
      arr.push([]);
    });
    const vList = [].slice.call(vDom.children);
    let len = vNum || vList.length;
    len = len > vList.length ? vList.length : len;
    queryNum = queryNum || len; // 0则全检索
    queryNum += startIndex;
    if (queryNum > len) {
      queryNum = len;
    }
    // console.log("queryNum, vNum, startIndex, len\n",queryNum,vNum,startIndex,len);
    for (let i = startIndex; i < queryNum; i++) {
      const vItem = vList[i];
      // console.log(i, item);
      if (!isAll && !vItem.querySelector("a")) {
        break; // 如果是预加载的视频
      }
      for (let j = 0; j < delClassArr.length; j++) {
        if (isChecked(vItem, delClassArr[j])) {
          arr[j].push(vItem);
          break;
        }
      }
    }
    // console.log("广告列表:", arr);
    return arr;
  }

  // 删除广告 或 放置在最后, 返回减少的数量
  function delAd(adArr, dom = vDom, isDel = false) {
    for (let i = adArr.length - 1; i >= 0; i--) {
      delInArr(adArr[i]);
    }
    function delInArr(arr) {
      arr.forEach((item) => {
        if (isClearAd || isDel) {
          item.remove();
          newVideoNum--;
          videoNum--;
        } else {
          if (isTrueEnd) {
            dom.appendChild(item); // 放在最后 (预加载视频后)
          } else {
            dom.insertBefore(item, dom.children[newVideoNum]); // 放在预加载视频前
          }
        }
      });
    }
  }

  // 设置浏览器宽度在某个范围时[左闭右开], 每行显示的视频数
  function setVideoNum(vRule) {
    const min = +vRule[0] / (isAutoLayout ? zoom : 1);
    const max = +vRule[1] / (isAutoLayout ? zoom : 1);
    const num = +vRule[2];
    // console.log(min, max, num, ">", w);
    if ((min !== 0 && !min) || !max || !num) {
      errHandle({
        errTxt: `插件设置的视频排列规则设置中 '${vRule.join("")}' 格式书写错误`,
        key: errKeyArr[1], // 2
      });
      return;
    }
    if (w >= min && w < max) {
      cssText = `
      .container {grid-template-columns: repeat(${num + 2},1fr) !important}
      .container>div:nth-child(n){margin-top:${marginTop2}px !important}
      .container>div:nth-child(-n+${
        num * 3 + 2 + 1
      }){margin-top:${marginTop1}px !important;display:block !important}
      .container>div:nth-child(-n+${
        (num + 1) * 2 - 1
      }){margin-top:0px !important}`;
      isChange = true;
      showVideoNum = num;
      // console.log("每行 " + num + " 个视频");
    }
    if (!isChange) {
      cssText = ""; // 默认排列方式
      showVideoNum = 3;
    }
  }

  // 调整每行显示个数
  function setStyle() {
    isChange = false; // 每行视频数是否需要变化
    videoNumRule.forEach((item) => {
      setVideoNum(item);
    });
    if (isChange) {
      let isCssDom = !!cssDom; // 是否已添加style
      if (!isCssDom) {
        cssDom = document.createElement("style");
        cssDom.setAttribute("type", "text/css");
      }
      oldCssText !== cssText && (cssDom.innerHTML = cssText);
      oldCssText = cssText;
      !isCssDom && vDom.parentElement.insertBefore(cssDom, vDom);
    } else {
      // 尺寸缩小时触发
      if (!isChange && cssDom) {
        oldCssText = "";
        cssDom.innerHTML = "";
      }
    }
  }

  // 获取视频总数
  function getVideoNum(dom) {
    const arr = [].slice.call(dom.children);
    const len = arr.length;
    let i;
    let isGetAdIndex = false;
    for (i = 1; i < len; i++) {
      const item = arr[i];
      // 获取第一个广告的索引
      if (!isTrueEnd && !isGetAdIndex) {
        const vItem = dom.children[i];
        for (let j = 0; j < delClassArr.length; j++) {
          if (isChecked(vItem, delClassArr[j])) {
            isGetAdIndex = true;
            firstAdIndex = i;
            break;
          }
        }
      }
      // 如果是预加载视频
      if (!item.querySelector("a")) {
        newVideoNum = i;
        return i;
      }
    }
    newVideoNum = i;
    return i;
  }

  // 判断是否是查找的目标
  function isChecked(vEle, delStr) {
    let flag = false;
    const map = classMap;
    delStr = delClassMap[delStr] || delStr;
    // 自定义的屏蔽内容
    function custom(txt, type, selector) {
      const dom = vEle.querySelector(selector);
      if (!dom) {
        return;
      }
      const domTxt = dom.innerText;
      const txtArr = txt.replace(type, "").split("&&");
      if (!txtArr[0]) {
        return;
      }
      let f = false;
      txtArr.forEach((item) => {
        f = f || domTxt.includes(item, "");
      });
      flag = flag || f;
    }
    if (delStr.includes("标题=")) {
      custom(delStr, "标题=", map.标题);
    } else if (delStr.includes("作者=")) {
      custom(delStr, "作者=", "." + map.作者);
    } else if (delStr.includes("分类=")) {
      custom(delStr, "分类=", "." + map.分类);
    } else {
      flag = flag || vEle.classList.contains(delStr);
      try {
        flag = flag || vEle.querySelector("." + delStr);
      } catch (e) {
        errHandle({
          errTxt: `插件设置的屏蔽设置中 '${delStr}' 格式书写错误应以 '标题=' 或 '作者=' 开头`,
          e,
        });
      }
    }
    return flag;
  }

  // 根据视频总数是否变化删除广告
  function delAdFn(timer = null) {
    getVideoNum(vDom);
    if (newVideoNum > videoNum) {
      console.log("加载新视频");
      adArr = getAd(
        queryNum,
        delClassArr,
        newVideoNum,
        isTrueEnd ? videoNum : firstAdIndex
      );
      delAd(adArr, vDom);
      videoNum = newVideoNum;
      timer && clearTimeout(timer);
    }
  }

  // 加载顶部位置的接下来的一组视频
  function loadTopVideo() {
    isLoadOne && document.documentElement.scrollTo(0, 400);
    isLoadOne &&
      setTimeout(() => {
        document.documentElement.scrollTo(0, 0);
        setTimeout(() => {
          delAdFn();
        }, 800);
      }, 20);
  }

  // 获取存储的值, 并解析成对应数据类型
  function getValue(key, defa = "") {
    let value = GM_getValue(key);
    if (key === "setting_videoNumRule") {
      if (value !== undefined && value !== null) {
        value = getVideoNumRule(value);
      } else {
        defa = getVideoNumRule(defa);
      }
    } else if (key === "setting_delClassArr") {
      if (value !== undefined && value !== null) {
        value = getDelClassArr(value);
      } else {
        defa = getDelClassArr(defa);
      }
    }
    return value === undefined || value === null ? defa : value;
  }

  // 解析数据字符串为对应数据类型
  function getDelClassArr(value) {
    value = value.replaceAll("\n", "").replaceAll(" ", "");
    return value.split(/;|;|,|,/);
  }
  function getVideoNumRule(value) {
    value = value.split(/;|;/);
    return value.map((item) => item.split(/,|,/));
  }

  // 基础设置
  function basicSetting(txt) {
    GM_setValue(
      "setting_isAutoLayout",
      confirm(
        txt.isAutoLayout +
          (GM_getValue("setting_isAutoLayout") ? "是 (确定)" : "否 (取消)")
      )
    );
    GM_setValue(
      "setting_isLoadOne",
      confirm(
        txt.isLoadOne +
          (GM_getValue("setting_isLoadOne") ? "是 (确定)" : "否 (取消)")
      )
    );
    const isClearAd = confirm(
      txt.isClearAd +
        (GM_getValue("setting_isClearAd") ? "是 (确定)" : "否 (取消)")
    );
    GM_setValue("setting_isClearAd", isClearAd);
    if (!isClearAd) {
      const value = GM_getValue("setting_isTrueEnd");
      GM_setValue(
        "setting_isTrueEnd",
        confirm(
          txt.isTrueEnd +
            ((value === undefined ? base_isTrueEnd : value)
              ? "是 (确定)"
              : "否 (取消)")
        )
      );
    } else {
      GM_setValue("setting_isTrueEnd", base_isTrueEnd);
    }
    history.go(0); // 刷新页面
  }

  // 屏蔽设置
  function delSetting(txt) {
    const value = GM_getValue("setting_delClassArr");
    const newValue = prompt(
      txt.delClassArr,
      value === undefined || value === null ? base_delClassArr : value
    );
    GM_setValue("setting_delClassArr", newValue || value);
    delClassArr = getDelClassArr(newValue || value);
    let adArr = getAd(queryNum, delClassArr, newVideoNum, 1);
    delAd(adArr, vDom);
  }

  // 视频排列规则的设置
  function layoutSetting(txt) {
    const value = GM_getValue("setting_videoNumRule");
    const newValue = prompt(
      txt.videoNumRule,
      value === undefined || value === null ? base_videoNumRule : value
    );
    GM_setValue("setting_videoNumRule", newValue || value);
    videoNumRule = getVideoNumRule(newValue || value);
    zoomPage(); // 缩放页面
    setStyle(); // 调整视频排列
  }

  // 重置设置
  function resetSettings() {
    GM_setValue("setting_isClearAd", base_isClearAd);
    GM_setValue("setting_isTrueEnd", base_isTrueEnd);
    GM_setValue("setting_isAutoLayout", base_isAutoLayout);
    GM_setValue("setting_isLoadOne", base_isLoadOne);
    GM_setValue("setting_videoNumRule", base_videoNumRule);
    GM_setValue("setting_delClassArr", base_delClassArr);
    GM_setValue(errKeyInfo.errNum, 0);
    errKeyArr.forEach((key) => {
      GM_setValue(errKeyInfo.disNum + key, 0); // 重置
    });
    history.go(0); // 刷新页面
  }

  // 错误处理
  function errHandle({ e = null, errTxt = "", logTxt = "", key = "" } = {}) {
    let errNum = GM_getValue(errKeyInfo.errNum) || 0;
    if (errNum >= errTipNum) {
      return;
    }
    let disErrNum = GM_getValue(errKeyInfo.disNum + key) || 0;
    const curTime = Date.now();
    const errTime = GM_getValue(errKeyInfo.errTime + key) || curTime;
    let disS = (curTime - errTime) / 1000;
    disS = disS === 0 ? 5 : disS;
    if (disS < 5) {
      return;
    }
    let flag = GM_getValue(errKeyInfo.isTip + key); // 是否能弹窗提示
    flag = flag === undefined ? true : flag;
    e && console.log(e);
    console.log(logTxt || errTxt);
    if (disS >= errTipInterval * 60 * 60) {
      // 每errTipInterval小时允许提醒disErrTipNum次
      flag = true;
      GM_setValue(errKeyInfo.isTip + key, true);
      GM_setValue(errKeyInfo.disNum + key, 0);
    }
    if (
      flag &&
      disErrNum <= disErrTipNum &&
      disS < (errTipInterval / 10) * 60 * 60
    ) {
      // 在errTipInterval/10小时内允许disErrTipNum次提示
      errNum++;
      disErrNum++;
      GM_setValue(errKeyInfo.errNum, errNum);
      GM_setValue(errKeyInfo.disNum + key, disErrNum);
      GM_setValue(errKeyInfo.errTime + key, curTime);
      alert(errTxt);
      disErrNum === disErrTipNum && GM_setValue(errKeyInfo.isTip + key, false);
    }
  }

  // 重置err相关的数据
  function resetErrInfo() {
    const curTime = Date.now();
    const errTime = errKeyArr.reduce((a, b) => {
      const t = +GM_getValue(errKeyInfo.errTime + b);
      return t < a ? t : a;
    }, curTime);
    if ((curTime - errTime) / 1000 >= errNumReset * 24 * 60 * 60) {
      GM_setValue(errKeyInfo.errNum, 0); // 重置
      errKeyArr.forEach((key) => {
        GM_setValue(errKeyInfo.disNum + key, 0); // 重置
      });
      console.log("重置err相关的数据");
    }
  }
})();