// ==UserScript== // @name 自用bilibili脚本 // @namespace mimiko/bilibili // @version 0.0.20 // @description 吧啦吧啦 // @author Mimiko // @license MIT // @match *://*.bilibili.com/* // @grant GM.addStyle // grant GM.registerMenuCommand // grant GM.xmlHttpRequest // @downloadURL none // ==/UserScript== // https://greasyfork.org/zh-CN/scripts/436748-%E8%87%AA%E7%94%A8bilibili%E8%84%9A%E6%9C%AC "use strict"; (() => { if (window.top !== window.self) return; // variable /** 工具类 */ const Z = { /** `watchPath`方法的数据 */ dataWatch: { /** 回调函数列表 */ list: [], /** 路由 */ path: "", /** 定时器 */ timer: 0, }, /** 防抖函数 */ debounce: (fn, delay) => { let timer = 0; return (...args) => { if (timer) window.clearTimeout(timer); timer = window.setTimeout(() => fn(...args), delay); }; }, /** 获取元素;异步函数,会重复执行直至目标元素出现 */ getElements: ( selector, ) => new Promise((resolve) => { const fn = () => { if (document.hidden) return; const $el = document.querySelectorAll(selector); if (!$el.length) return; window.clearInterval(timer); resolve([...$el]); }; const timer = window.setInterval(fn, 50); fn(); }), /** 隐藏元素;通过在目标元素上写入内联样式实现 */ hideElements: ( ...listSelector ) => { GM.addStyle( listSelector .map((selector) => `${selector} { display: none !important; }`) .join("\n"), ); }, /** 从本地存储中读取数据 */ load: (name) => { try { const data = localStorage.getItem(`mimiko-gms/${name}`); if (!data) return null; return JSON.parse(data); } catch (e) { alert(`读取缓存失败:${e.message}`); return null; } }, /** 移除元素 */ removeElements: (selector) => document.querySelectorAll(selector).forEach(($it) => $it.remove()), /** 保存数据到本地存储 */ save: (name, data) => localStorage.setItem(`mimiko-gms/${name}`, JSON.stringify(data)), /** 等待一段时间 */ sleep: (ts) => new Promise((resolve) => setTimeout(resolve, ts)), /** 监听路由变化 */ watchPath: (callback) => { Z.dataWatch.list.push(callback); window.clearInterval(Z.dataWatch.timer); Z.dataWatch.timer = window.setInterval(() => { const { pathname } = window.location; if (pathname === Z.dataWatch.path) return; Z.dataWatch.path = pathname; Z.dataWatch.list.forEach((it) => it()); }, 200); }, }; /** 本地存储;目前用于记录哪些视频已经在首页出现过 */ class Cache { /** 记录视频上限 */ limit = 5e4; /** 视频列表 */ list; /** 构造函数;会从本地存储中读取记录 */ constructor() { this.list = Z.load("cache-recommend") ?? []; } /** 添加视频 */ add(id) { const it = this.get(id); const it2 = [id, it[1] + 1]; const list2 = this.list.filter((it) => it[0] !== id); list2.push(it2); this.list = list2.slice(-this.limit); } /** 清空视频 */ clear() { this.list = []; this.save(); } /** 获取视频 */ get(id) { return this.list.find((it) => it[0] === id) ?? [id, 0]; } /** 保存记录 */ save() { Z.save("cache-recommend", this.list); } } const cache = new Cache(); // function /** 首页 */ const asIndex = async () => { GM.addStyle(` body { min-width: auto; } .bili-footer { display: none; } @media (max-width: 1099.9px) { .recommend-container__2-line>*:nth-of-type(1n + 6) { display: block !important; } } .bili-grid:has(#推广) { display: none; } .bili-grid:has(#直播) { display: none; } .bili-grid:has(#漫画) { display: none; } .bili-grid:has(#课堂) { display: none; } .bili-grid:has(#专栏) { display: none; } .bili-video-card.is-rcmd.hidden .bili-video-card__wrap { opacity: 0.1; transition: opacity 0.3s; } .bili-video-card.is-rcmd.hidden:hover .bili-video-card__wrap { opacity: 1; } .bili-video-card.is-rcmd.hidden .reason-tip { position: absolute; width: 160px; height: 32px; left: 50%; top: 32%; text-align: center; line-height: 32px; background-color: rgba(0, 0, 0, 0.8); color: #fff; font-size: 12px; border-radius: 4px; pointer-events: none; transform: translate(-50%, -50%); transition: opacity 0.3s; } .bili-video-card.is-rcmd.hidden:hover .reason-tip { opacity: 0; } `); (await Z.getElements(".recommended-swipe"))[0].remove(); // add cache clear button const containerButton = (await Z.getElements(".roll-btn-wrap"))[0]; const btnClear = document.createElement("button"); btnClear.classList.add("primary-btn", "roll-btn"); btnClear.innerHTML = ""; btnClear.setAttribute("title", "清空缓存"); btnClear.addEventListener("click", () => { cache.clear(); alert("缓存已清空"); }); btnClear.style.marginTop = "10px"; containerButton.appendChild(btnClear); // check items const [container] = await Z.getElements(".recommend-container__2-line"); const hide = async () => { observer.disconnect(); const listItem = await Z.getElements(".bili-video-card.is-rcmd"); listItem.forEach((it) => { const check = () => { // play count too low const stats = it.querySelector(".bili-video-card__stats"); if (!stats) return "播放量不存在"; const text = stats.textContent; if (!text) return "播放量为空"; if (!text.includes("万")) return "播放量不足1万"; const amount = parseFloat(text.split("万")[0]); if (amount < 3) return "播放量不足3万"; // has been viewed const link = it.querySelector("a"); if (!link) return "链接不存在"; // info const info = it.querySelector(".bili-video-card__info--icon-text"); const id = link.href.replace(/.*\/BV/, "").replace(/\/\?.*/, ""); if (cache.get(id)[1] > (info?.textContent === "已关注" ? 2 : 0)) return `已出现${cache.get(id)[1]}次`; cache.add(id); return ""; }; const reason = check(); if (!reason) return; it.classList.add("hidden"); const reasonTip = document.createElement("div"); reasonTip.classList.add("reason-tip"); reasonTip.textContent = reason; it.appendChild(reasonTip); }); cache.save(); listItem.sort((a, b) => { const aHidden = a.classList.contains("hidden"); const bHidden = b.classList.contains("hidden"); if (aHidden && !bHidden) return 1; if (!aHidden && bHidden) return -1; return 0; }); listItem.forEach((it) => container.appendChild(it)); observer.observe(container, { childList: true, }); }; const observer = new MutationObserver(hide); await hide(); }; /** 视频页 */ const asVideo = () => { // hide share GM.addStyle(` .video-share-popover { display: none; } `); // fullscreen const fullscreen = async () => { const [btn] = await Z.getElements(".bpx-player-ctrl-web"); btn.click(); }; // auto fullscreen const autoFS = Z.debounce(async () => { if (window.innerWidth > 1080) return; const [player] = await Z.getElements("#bilibili-player"); if (player.classList.contains("mode-webscreen")) return; await fullscreen(); }, 1e3); Z.watchPath(autoFS); window.addEventListener("resize", autoFS); // reset hotkey document.addEventListener("keydown", (e) => { if ( e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement ) return; if (["q", "w", "e", "d", "f", "m"].includes(e.key)) { e.preventDefault(); e.stopPropagation(); } if (e.key === "f") fullscreen(); }); }; /** 主函数 */ const main = async () => { const { pathname } = window.location; if (["/", "/index.html"].includes(pathname)) await asIndex(); if (pathname.startsWith("/video/")) asVideo(); }; // execute main(); })();