您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
吧啦吧啦
当前为
// ==UserScript== // @name 自用bilibili脚本 // @namespace mimiko/bilibili // @version 0.0.22 // @description 吧啦吧啦 // @author Mimiko // @license MIT // @match *://*.bilibili.com/* // @grant GM.addStyle // grant GM.registerMenuCommand // grant GM.xmlHttpRequest // @run-at document-end // ==/UserScript== // http://greasyfork.icu/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; } .container:first-child { display: none; } .feed-card { display: block !important; margin-top: 0 !important; } .feed-card.is-hidden { position: relative; } .feed-card.is-hidden .bili-video-card { transition: opacity 0.3s; opacity: 0.1; } .feed-card.is-hidden .bili-video-card:hover { opacity: 1; } .feed-card.is-hidden .reason { 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; z-index: 1; } .feed-card.is-hidden:hover .reason { opacity: 0; } `); // container const [container] = await Z.getElements(".container"); const ctn = document.createElement("div"); container.classList.forEach((it) => ctn.classList.add(it)); container.parentNode?.append(ctn); // add cache clear button const groupBtn = (await Z.getElements(".feed-roll-btn"))[0]; const btnClear = document.createElement("button"); btnClear.classList.add("primary-btn", "roll-btn"); btnClear.innerHTML = "<span>✖</span>"; btnClear.setAttribute("title", "清空缓存"); btnClear.addEventListener("click", () => { cache.clear(); alert("缓存已清空"); }); btnClear.style.marginTop = "10px"; groupBtn.append(btnClear); // check items const hide = () => { observer.disconnect(); const listItem = [...container.children].filter((it) => it.classList.contains("feed-card"), ); 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) { ctn.prepend(it); return; } if (reason === "播放量不存在") return; it.classList.add("is-hidden"); const tip = document.createElement("div"); tip.classList.add("reason"); tip.textContent = reason; it.append(tip); ctn.append(it); }); cache.save(); observer.observe(container, { childList: true, }); }; const observer = new MutationObserver(hide); 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(); })();