您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
破除信息茧房,我看什么我决定!
// ==UserScript== // @name B站-破茧 // @namespace Violentmonkey Scripts // @match *://www.bilibili.com/video/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_addValueChangeListener // @grant GM_info // @grant GM_addElement // @grant GM_addStyle // @grant GM_openInTab // @version 0.4.7.8 // @inject-into page // @require https://greasyfork.org/scripts/476522-config-manager/code/Config_Manager.js?version=1285584 // @author axototl // @license MPL-2.0 // @description 破除信息茧房,我看什么我决定! // @icon https://s1.hdslb.com/bfs/static/laputa-home/client/assets/vip-login-banner.c0cbe3b2.png // ==/UserScript== 'use strict'; (() => { if (GM_info.scriptHandler === "Violentmonkey") return true; GM_addElement("p", { textContent: `脚本 ${GM_info.script.name} 大量使用了暴力猴独有特性, 建议使用Violentmonkey(暴力猴)获得更好体验.`, style: "position: fixed; bottom: 0; right: 0; color: #F92672; font-size: 16px; font-weight: bold;" }).onclick = () => GM_openInTab("https://violentmonkey.github.io/get-it/"); return false; })(); let kw; (() => { const upd = (x => kw = x?.split(' ').filter(el => !!el)); upd(GM_getValue("keywords")); GM_registerMenuCommand("设置自定义搜索关键词", () => { const p = prompt("请输入自定义搜索关键词,用 空格 隔开", kw.join(" ")); if (p == null) return; GM_setValue("keywords", p || null); }); GM_addValueChangeListener("keywords", (_1, _2, nv) => upd(nv)); })(); const translate = json => { const params = new URLSearchParams(); for (const it in json) params.append(it, json[it]); return params; }; let sender; const sendTrash = (uri, body) => fetch(uri, { referrer: "no-referrer", referrerPolicy: "no-referrer", method: "POST", credentials: "omit", priority: "low", mode: 'no-cors', body, }); let Random; // Random-js Lib let engine; const delay = n => new Promise(resolve => setTimeout(resolve, n)); // const cid_api = [ // "https://api.bilibili.com/x/web-interface/view/detail?aid=", // "https://api.bilibili.com/x/web-interface/view?aid=" // ]; const vid_arr = []; const get_rand = async () => { const MAX = config.maxaid; const _rnd_helper = Random.integer(2, MAX); const getf = () => _rnd_helper(engine); for (let aid; ;) { if (vid_arr.length <= 0) for (let _ = 1; _ <= 20; ++_) vid_arr.push(getf()); aid = vid_arr.pop(); let res = await (await fetch("https://api.bilibili.com/x/web-interface/view?aid=" + aid)).json(); if (res.code == -412) { throw Error("API被拦截"); } if (res.code != 0) { await delay(500); continue; } res = res.data; if (undefined != res.forward) { aid = res.forward; continue; } let subtype = null; let type = res.rights?.movie ? (subtype = 2, 4) : 3; res = res.pages; const ct = res[(Math.random() * res.length) | 0]; return {aid, type, subtype, ...ct}; } } const getmid = async () => { let js = await (await fetch("https://api.bilibili.com/x/web-interface/nav", {credentials: "include"})).json(); // console.debug(js); return js.data?.mid; }; let jscookie; // getmid(); let id0, id2; const main_light = () => { id0 = setInterval(async () => { if (!config.cookie) { config.light_main = false; return; } const csrf = jscookie.get("bili_jct"); if (!csrf) return; const {aid, cid, duration: last} = await get_rand(); const para = { aid, cid, progress: Math.trunc(last*Math.random()), csrf }; const params = translate(para); sender("https://api.bilibili.com/x/v2/history/report", params); }, config.delay); }; // 建议根据自己的喜好动态调整 const refer_uris = [ "https://www.bilibili.com/", "https://www.bing.com/", "https://www.zhihu.com/", "https://example.org/", '', ]; const main_classical = () => { const engine = Random.MersenneTwister19937.autoSeed(); const _die4 = Random.die(4); const die4 = () => _die4(engine); // let x = 0; // let reducing = false; id0 = setInterval(async () => { // if (x > 65536) reducing = true; // if (reducing) // if (--x > 0) return; // else reducing = false; // else ++x; // 暂时禁用冷却 // 冷却时间 65536 防止卡顿 const {aid, cid, duration: last, type, subtype: sub_type} = await get_rand(); // const time2 = (next()) % last; const time = Random.die(last)(engine); // console.log("time = ", time, "time2 = ", t2); const now = (Date.now() / 1000 | 0); const real = (1 + die4()) * time; const play_type = Random.pick(engine, [0, 2, 3]); const para = { start_ts: now - real, aid, cid, type, sub_type: sub_type ?? 0, dt: 2, play_type, realtime: real, played_time: time, real_played_time: time, refer_url: Random.pick(engine, refer_uris), quality: 0, video_duration: last, last_play_progress_time: time, max_play_progress_time: time, outer: 0, extra: '{"player_version":"4.5.7"}', }; const jct = config.cookie ? jscookie.get("bili_jct") : undefined; // console.debug("your CSRF token(bili_jct) =", jct); if (!!jct) para.csrf = jct; const params = translate(para); // console.debug("parsing -------->"); sender("https://api.bilibili.com/x/click-interface/web/heartbeat", params); }, config.delay); }; const unload = () => (clearInterval(id0)); const load_query = () => { if (kw?.length == 0) return; const _ref_pick = Random.picker(kw); const ref_pick = () =>_ref_pick(engine); const cfg = {credentials: "include", referrer: "search.bilibili.com"}; const MAX = 100; id2 = setInterval(async () => { let res = fetch("https://api.bilibili.com/x/web-interface/search/all/v2?keyword=" + ref_pick(), cfg); if (vid_arr.length > MAX || !config.main) return; res = await (await res).json(); if (res.code != 0) return config.query = false; res = res.data.result; while(res.length > 0) { const s = res.pop(); if (s.result_type === 'video') { res = s; break; } } if (typeof res !== 'object') return; res = res.data; Random.shuffle(engine, res); for (const x of res) vid_arr.push(x.aid); }, 15e3); }; const unload_query = () => clearInterval(id2); let load; const _need_cookie = (_1, val) => val ? config.cookie : true; const reload_cb = (_1, _2, nv) => { if (!config.main) return; unload(); load = (nv ? main_light : main_classical); load(); }; let __dislike_div; const cfgs = [ { name: "delay", type: "uint", desc: "设置轮询时间", tips: "设置轮询时间(请输入正数)", default: 60e3, }, { name: "maxaid", type: "uint", desc: "设置最大AV号", tips: "请输入轮询应当获取到的最大av号\n(注意只需要输入数字即可)", default: (1e9|0), }, { name: "use_beacon", type: "bool", default: false, desc: "[实验性] 使用SendBeacon API(缓解内存泄露)", callback: (_1, _2, nv) => (sender = (nv ? (uri, para) => navigator.sendBeacon(uri, para) : sendTrash)), init: true, autoClose: false, }, { name: "query", type: 'bool', desc: "轮询搜索API (定制推送内容)", callback: (_1, _2, nv) => ((nv ? load_query : unload_query)()), default: false, init: true, autoClose: false, }, { name: "cookie", type: "bool", desc: "携带Cookie(会污染历史记录)", default: true, judge: (_, i) => !i || !!cookie.get("bili_jct"), autoClose: false, }, { name: "light_main", type: "bool", desc: "[试验性] 使用轻量版主程序", default: false, callback: reload_cb, init: (_1, val) => load = (val ? main_light : main_classical), autoClose: false, judge: _need_cookie, }, { name: "main", type: 'bool', desc: "主程序 (清洗个人喜好)", callback: ((_1, _2, nv) => (nv ? load : unload)()), default: true, init: true, }, { name: "access_key", type: "other", desc: "设置access_token", tips: "请输入access_token", default: null, }, { name: "dislike", type: "bool", desc: "[实验性] 点踩 (需APP_token)", callback: (_1, _2, nv) => (nv ? (__dislike_div = dislike_f()) : __dislike_div?.remove()), judge: () => (alert("注意,只有在bilibili.com/video/av123456 情况下可用 BV号不可用"), true), init: (name, val) => (val ? (__dislike_div = dislike_f()) : 0), }, ]; let md5; const require = mod => import(`https://fastly.jsdelivr.net/npm/${mod}/+esm`); (async () => { Random = await require("random-js"); // engine = Random.MersenneTwister19937.autoSeed(); engine = Random.nativeMath; const {default: cookie} = await require("js-cookie"); jscookie = cookie; const {md5: hash} = await require("js-md5"); md5 = hash; // console.debug("Loading....."); cfgs.forEach(register); GM_registerMenuCommand("[实验性] 登录(获取Access Token)", getkey_ui); GM_addStyle(` #gm_deslike { /* 设置形状及阴影 */ color: white; width: 50px; height: 50px; border-radius: 50%; background-color: black; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); display: flex; align-items: center; justify-content: center; /* 设置按钮的位置 */ position: fixed; right: 20px; bottom: 20px; /* 设置过渡属性 */ transition: width 0.5s, height 0.5s; } #gm_deslike:hover { /* 鼠标悬停时 */ width: 60px; height: 60px; } `); })(); async function dislike_f() { const paras = { access_key: config.access_key, dislike: 0, }; // console.warn("11111111111111111111111111111"); let id = /av(\d+)/.exec(location.pathname); if (id) id = id[1]; else { id = /BV(\w+)/.exec(location.pathname); if (!id) return -2; id = (bv => { const pos = [11, 10, 3, 8, 4, 6]; const base = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'; const table = {}; for (let i = 0; i < base.length; i++) table[base[i]] = i; let r = 0; for (let i = 0; i < pos.length; i++) r += table[bv[pos[i]]] * 58 ** i; return (r - 8728348608) ^ 177451812; })(id[0]); } // console.warn("你好222222222222222"); paras.aid = id; const div = GM_addElement("div", {id: "gm_deslike"}); const p = GM_addElement(div, "p", {textContent: "不喜欢"}); const par = translate(paras); par.sort(); p.onmousedown = async ev => { div.remove(); if (ev.button == 2) return; let res = fetch("https://app.biliapi.net/x/v2/view/dislike", { referrer: "no-referrer", referrerPolicy: "no-referrer", method: "POST", credentials: "omit", priority: "low", headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: par, }); res = (await res); console.log(res); res = await res.json(); if (res.code === 0) return; alert("点踩失败\n 原因: " + res.message); }; return div; } function api_signer (salt, appkey, paras) { let par = { appkey, local_id: 0, ts: Math.trunc(Date.now()/1000), }; Object.assign(par, paras); par = translate(par); par.sort(); const sign = md5('' + par + salt); par.append("sign", sign); return { method: "POST", credentials: "omit", referrer: "no-referrer", referrerPolicy: "no-referrer", headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: par, }; } let _share_win; async function _alerter(uri) { const {default: QRCode} = await require("qrcode-svg"); let img = new QRCode({content: uri}).svg(); img = new Blob([img], {type: "image/svg+xml"}); img = URL.createObjectURL(img); // console.log("你好 ========================="); let xhtm = `<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN"> <head> <script> setTimeout(()=>{ alert("已超时!请重新获取二维码。"); window.close(); }, ${180e3 | 0}); </script> </head> <body> <p style="font-size: 30px;">请在180秒内扫扫描以下二维码登录 <br/><br/> 若过期请关闭此标签页并重新开始</p> <img src="${img}" /> </body> </html> `; xhtm = new Blob([xhtm], {type: "application/xhtml+xml"}); xhtm = URL.createObjectURL(xhtm); const revoker = () => [img, xhtm].forEach(URL.revokeObjectURL); if (GM_info.platform?.browserName === "firefox") { _share_win = window.open(xhtm, "_blank"); // Firefox disallow calling GM_openInTab with Data URL. alert("请注意窗口顶部,请允许打开弹出式窗口"); } else { _share_win = GM_openInTab(xhtm); } setTimeout(revoker, 180e3 | 0); } async function getkey_ui () { const sign = par => api_signer("59b43e04ad6965f34319062b478f83dd", "4409e2ce8ffd12b8", par); let res = fetch("https://passport.bilibili.com/x/passport-tv-login/qrcode/auth_code", sign()); res = await (await res).json(); console.log(" =====> parsing <======"); if (res.code != 0) return new Error(res.message); res = res.data; await _alerter(res.url); const auth_code = res.auth_code; const get_id_worker = setInterval(async () => { let res = fetch("https://passport.bilibili.com/x/passport-tv-login/qrcode/poll", sign({auth_code})); res = await (await res).json(); if (res.code == 86038 || res.code < 0) { clearInterval(get_id_worker); throw "Timeout!"; } else if (res.code != 0) return console.warn("登录未成功,原因:", res.message); res = res.data; config.access_key = res.access_token; clearInterval(get_id_worker); _share_win.close(); alert("登录成功! 已获取到Access Token!"); }, 5000); };