您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
吧啦吧啦
// ==UserScript== // @name 自用bilibili脚本 // @namespace mimiko/bilibili // @version 0.0.27 // @description 吧啦吧啦 // @author Mimiko // @license MIT // @match *://*.bilibili.com/* // @grant GM.addStyle // @grant GM.xmlHttpRequest // grant GM.registerMenuCommand // @run-at document-end // ==/UserScript== // https://gf.qytechs.cn/zh-CN/scripts/436748-%E8%87%AA%E7%94%A8bilibili%E8%84%9A%E6%9C%AC // interface "use strict"; (() => { if (window.top !== window.self) return; // variables /** 工具类 */ const Z = { /** 添加样式 */ addStyle: (listContent, ...listItem) => { const content = listContent .map((it, i) => `${it}${listItem[i] ?? ""}`) .join(""); GM.addStyle(content); }, /** 防抖函数 */ debounce: (fn, delay) => { let timer = 0; return (...args) => { if (timer) window.clearTimeout(timer); timer = window.setTimeout(() => fn(...args), delay); }; }, /** 输出日志 */ echo: (...args) => console.log(...args), /** 发起请求 */ get: (url, data = {}) => new Promise((resolve) => { GM.xmlHttpRequest({ method: "GET", url: `${url}?${new URLSearchParams( Object.entries(data).map((group) => [group[0], String(group[1])]), ).toString()}`, onload: (res) => resolve(JSON.parse(res.responseText).data), onerror: () => resolve(undefined), }); }), /** 获取元素;异步函数,会重复执行直至目标元素出现 */ 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 ) => { Z.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()), /** 运行函数 */ run: (fn) => fn(), /** 保存数据到本地存储 */ save: (name, data) => localStorage.setItem(`mimiko-gms/${name}`, JSON.stringify(data)), /** 等待一段时间 */ sleep: (ts) => new Promise((resolve) => setTimeout(resolve, ts)), }; /** 黑名单 */ const Blacklist = { list: [], /** 清空黑名单 */ clear: () => { if (!User.isLogin) return; Z.save("list-blacklist", []); Z.save("time-blacklist", 0); }, /** 判断是否在黑名单中 */ has: (name) => Blacklist.list.includes(name), /** 加载黑名单 */ load: async () => { if (!User.isLogin) return; const time = Z.load("time-blacklist"); if (!time || Date.now() - time > 864e5) { await Blacklist.update(); await Blacklist.load(); return; } Blacklist.list = Z.load("list-blacklist") ?? []; }, /** 保存黑名单 */ save: () => { if (!User.isLogin) return; Z.save("list-blacklist", Blacklist.list); Z.save("time-blacklist", Date.now()); }, /** 加载黑名单 */ update: async () => { const ps = 50; const fetcher = async (pn = 0) => { if (!User.isLogin) return []; const result = await Z.get( "https://api.bilibili.com/x/relation/blacks", { pn, ps, }, ); if (!result) return []; const list = result.list.map((it) => it.uname); if (list.length >= ps) return [...list, ...(await fetcher(pn + 1))]; return list; }; Blacklist.list = await fetcher(); Blacklist.save(); }, }; /** 本地存储;目前用于记录哪些视频已经在首页出现过 */ const Cache = { /** 记录视频上限 */ limit: 5e4, /** 视频列表 */ list: Z.load("cache-recommend") ?? [], /** 添加视频 */ add: (id) => { const it = Cache.get(id); const it2 = [id, it[1] + 1]; const list2 = Cache.list.filter((it) => it[0] !== id); list2.push(it2); Cache.list = list2.slice(-Cache.limit); }, /** 清空视频 */ clear: () => { Cache.list = []; Cache.save(); }, /** 获取视频 */ get: (id) => { return Cache.list.find((it) => it[0] === id) ?? [id, 0]; }, /** 保存记录 */ save: () => { Z.save("cache-recommend", Cache.list); }, }; /** 路由 */ const Router = { /** 计数 */ count: { index: 0, unknown: 0, video: 0, }, /** 路由表 */ map: { index: [], unknown: [], video: [], }, /** 路由 */ path: "", /** 初始化 */ init: () => { const fn = () => { const { pathname } = window.location; if (pathname === Router.path) return; Router.path = pathname; const name = Z.run(() => { if (Router.is("index")) return "index"; if (Router.is("video")) return "video"; return "unknown"; }); if (name === "unknown") return; Router.map[name].forEach(async (it) => { if (Router.count[name] === 0) it[1] = await it[0](); if (typeof it[2] === "function") it[2](); if (typeof it[1] === "function") it[2] = await it[1](); Router.count[name]++; }); }; window.setInterval(fn, 200); return Router; }, /** 判断路由 */ is: (name) => { const { pathname } = window.location; if (name === "index") return ["/", "/index.html"].includes(pathname); if (name === "video") return pathname.startsWith("/video/"); return true; }, /** 监听路由 */ watch: (name, fn) => { Router.map[name].push([fn, undefined, undefined]); return Router; }, }; /** 用户 */ const User = { isLogin: false, /** 更新用户状态 */ update: async () => { const result = await Z.get( "https://api.bilibili.com/x/web-interface/nav", ); if (!result) return; User.isLogin = result.isLogin; }, }; // functions /** 首页 */ const asIndex = async () => { Z.addStyle` body { min-width: auto; } .container:first-child { display: none; } .feed-roll-btn button:first-of-type { height: ${Z.run(() => { const picture = document.querySelector(".feed-card picture"); if (!picture) return 135; const { height } = picture.getBoundingClientRect(); return height; })}px !important; } .flexible-roll-btn { display: none !important; } .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"); const btnClear = document.createElement("button"); btnClear.classList.add("primary-btn", "roll-btn"); btnClear.innerHTML = "<span>✖</span>"; btnClear.setAttribute("title", "清空缓存"); btnClear.addEventListener("click", () => { Blacklist.clear(); Cache.clear(); alert("缓存已清空"); }); btnClear.style.marginTop = "20px"; groupBtn.append(btnClear); return async () => { await User.update(); await Blacklist.load(); const hide = () => { observer.disconnect(); const listItem = [...container.children].filter((it) => it.classList.contains("feed-card"), ); listItem.forEach((it) => { const check = () => { const author = it.querySelector(".bili-video-card__info--author")?.textContent ?? ""; if (!author) return "UP主不存在"; if (Blacklist.has(author)) return "UP主在黑名单中"; // play count too low const text = it.querySelector(".bili-video-card__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(); return () => observer.disconnect(); }; }; /** 视频页 */ const asVideo = () => { Z.addStyle` .video-share-popover { display: none; } `; // functions const execFS = async () => { const [btn] = await Z.getElements(".bpx-player-ctrl-web"); btn.click(); }; const execWS = async () => { const [btn] = await Z.getElements(".bpx-player-ctrl-wide"); btn.click(); }; // 自动全屏 const autoFS = Z.debounce(async () => { const isNarrow = window.innerWidth <= 1080; if (!isNarrow) return; const [player] = await Z.getElements("#bilibili-player"); if (player.classList.contains("mode-webscreen")) return; await execFS(); }, 1e3); // 注册(不可用)热键 const bindKey = (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") { execFS(); return; } if (e.key === "w") execWS(); }; return () => { autoFS(); window.addEventListener("resize", autoFS); document.addEventListener("keydown", bindKey); return () => { window.removeEventListener("resize", autoFS); document.removeEventListener("keydown", bindKey); }; }; }; // 执行 Z.run(() => { if (window.location.hostname !== "www.bilibili.com") return; Router.watch("index", asIndex).watch("video", asVideo).init(); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址