您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
B站动态自定义过滤,可过滤转发类型,过滤关注分组
// ==UserScript== // @name B站动态自定义过滤(修正版) // @namespace http://tampermonkey.net/ // @version 0.1.4 // @description B站动态自定义过滤,可过滤转发类型,过滤关注分组 // @author Eric Lam、cwk44 // @include /^https?:\/\/t\.bilibili\.com\/[^\/]*$/ // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js // @grant GM_setValue // @grant GM_getValue // @license GPL // ==/UserScript== (async function() { 'use strict'; const posts = { normal: [], videoRel: [], repost: [], videos: [], followings: {}, followingsAll: [] } const defaultEnabled = { normal: true, videoRel: true, repost: true, videos: true } const followings = {} function getSettings(){ try { return { ...defaultEnabled, ...JSON.parse(window.localStorage['hide_feed_settings']) } }catch(err){ console.debug(`cannot found old settings: [${err.message}], using default settings`) return defaultEnabled } } const enabled = getSettings() for (const key in enabled){ if (!Object.keys(defaultEnabled).includes(key)) delete enabled[key] } let allFeedsState = true const feedCardCallback = (mu, o) => { for (const nodes of mu){ if ($(nodes.target).hasClass('loading-content') && $(nodes.addedNodes[0]).hasClass('tc-slate')){ console.debug('changed tab') posts.repost = [] posts.normal = [] posts.videos = [] posts.videoRel = [] return } for (const node of nodes.addedNodes){ const tid = parseInt(getTagId()) if ($(node).find('.bili-dyn-content__orig.reference').length > 0) { console.debug('found repost') const repostContent = $(node).find('.bili-dyn-content__orig.reference') const isVideo = repostContent.find('.bili-dyn-card-video').length > 0 console.debug(`this repost is video: ${isVideo}`) const card = repostContent.parents('.bili-dyn-item') if (isVideo){ posts.videos.push(card) if (!enabled.videos) $(card).hide() }else{ posts.repost.push(card) if (!enabled.repost) $(card).hide() } } else if($(node).find('.bili-dyn-content__orig').length > 0){ console.debug('found normal') const content = $(node).find('.bili-dyn-content__orig') const card = content.parents('.bili-dyn-item') const isVideo = content.find('.bili-dyn-card-video').length > 0 console.debug(`this normal is video: ${isVideo}`) if (isVideo){ posts.videoRel.push(card) if (!enabled.videoRel) $(card).hide() }else{ posts.normal.push(card) if (!enabled.normal) $(card).hide() } } else { console.debug('found unknown post') } if (allFeedsState) handleGroupFilter(node).catch(console.error) } } } let feedCard = $('.bili-dyn-list') while(feedCard.length == 0){ console.log('bili-dyn-list not found, wait 0.5 sec') await sleep(500) feedCard = $('.bili-dyn-list') } try { new MutationObserver(feedCardCallback).observe(feedCard[0], { subtree: true, childList: true, attributes: false }) }catch(err){ alert(`自定义过滤载入失败,${err.message}, 请刷新`) return } const hideAll = (arr) => arr.forEach(s => $(s).hide()) const showAll = (arr) => arr.forEach(s => $(s).show()) function handle(key, target){ const val = $(target).prop('checked') enabled[key] = val // 关注分组失效 const tid = parseInt(getTagId()) const cards = posts[key].filter(c => tid == 0 || jqInclude(posts.followings[tid],c)) //const cards = posts[key] if (val){ showAll(cards) }else{ hideAll(cards) } } function jqInclude(arr, c){ return arr.includes(c) || arr.some(r => r[0] == c[0]) } feedCard.parent('section').before(` <div class="tab-bar filter-list filter-grid"> <div> <input id="normal-checker" type="checkbox" checked>纯动态 </div> <div> <input id="video-release-checker" type="checkbox" checked>投稿视频 </div> <div> <input id="repost-checker" type="checkbox" checked>转发动态 </div> <div> <input id="repost-video-checker" type="checkbox" checked>转发视频 </div> </div> <div class="tab-bar filter-list" id="followings-group"> 分组过滤: <select id="f-groups" class="filter-select" value="0"> </select> </div> <style> .filter-list { background-color: white; min-height: 10px; margin-bottom: 10px; text-align: center; padding: 15px; } .filter-grid{ display: grid; grid-template-columns: repeat(4, 3fr); } .filter-select { position: relative; padding: 5px; flex-direction: column; width: 50%; border-style: solid; border-radius: 3px; border-width: 1px; border-color: #c9c9c9; } </style> `) const followGroupInfo = {} const groups = await getFollowingGroups() for (const group of groups){ const key = group.tagid if (group.tagid === 0) group.name = '全部关注' $('#f-groups').append(` <option value="${group.tagid}">${group.name}</option> `) followGroupInfo[group.tagid] = group posts.followings[group.tagid] = [] } $('#f-groups').val(0) $('input#normal-checker').prop('checked', enabled.normal) $('input#video-release-checker').prop('checked', enabled.videoRel) $('input#repost-checker').prop('checked', enabled.repost) $('input#repost-video-checker').prop('checked', enabled.videos) $('input#normal-checker').on('change', e => handle('normal', e.target)) $('input#video-release-checker').on('change', e => handle('videoRel', e.target)) $('input#repost-checker').on('change', e => handle('repost', e.target)) $('input#repost-video-checker').on('change', e => handle('videos', e.target)) $('#f-groups').on('change', e => { const tagid = parseInt(e.target.value) var uid = 319278 getFollowings(tagid, uid).then(followList => { if (tagid === 0) { //showAll(Object.values(posts.followings).flatMap(n => n)) showAll(posts.followingsAll) }else{ let filter = (c) => true //for(const tid in posts.followings){ const cards = posts.followingsAll for (var i in cards) { var name = $(cards[i]).find('.bili-dyn-title__text').text().trim(); var isMatch = false for (var j in followList) { if (name == followList[j].uname) { isMatch = true } } if (isMatch) { $(cards[i]).show() } else { $(cards[i]).hide() } } /* if (tid == tagid){ showAll(cards) filter = (c) => !cards.includes(c) }else{ hideAll(cards.filter(filter)) }*/ //} } for(const key in enabled){ const val = enabled[key] const tid = parseInt(getTagId()) const cards = posts[key].filter(c => tid == 0 || jqInclude(posts.followings[tid],c)) if (!val){ hideAll(cards) } } }) }) window.onunload = function(){ window.localStorage['hide_feed_settings'] = JSON.stringify(enabled) } while($('.bili-dyn-up-list__content').length == 0){ console.log('bili-dyn-up-list__content not found, wait 0.5 sec') await sleep(500) } try{ new MutationObserver(([mu], o) => { console.log($(mu.target).find('bili-dyn-up-list__item.active > .bili-dyn-up-list__item__name')[0]) const allTargets = $(mu.target).hasClass('.active') && $(mu.target).find('.bili-dyn-up-list__item__name')[0]?.innerText == '全部动态' allFeedsState = allTargets if (allTargets){ console.debug('showing followings group') $('#followings-group').show() }else{ console.debug('hiding followings group') $('#followings-group').hide() $('#f-groups').val(0) for(const tid in posts.followings){ posts.followings[tid] = [] } } }).observe($('.bili-dyn-up-list__content')[0], { childList: false, subtree: true, attributes: true}) }catch(err){ alert(`自定义过滤载入失败,${err.message}, 请刷新`) return } async function handleGroupFilter(node){ if (!$(node).hasClass('bili-dyn-list__item')) { return } try { const reg = /(\d+)\/dynamic$/ const card = $(node); //.parents('.bili-dyn-item') // const url = card.find('a.c-pointer.user-head').prop('href') //const regexResult = reg.exec(card.find('a.c-pointer.user-head').prop('href')) //if (!regexResult){ // 无效uid,可能是番剧? // return //} //const mid = parseInt(regexResult.pop()) var mid = 319278 //查找某个关注的uid是哪个分组 //if (followings[mid].length == 0){ //if (typeof posts.followings[0] == "undefined") { // posts.followings[0] = [] //} // posts.followings[0].push(card) posts.followingsAll.push(card) $(node).find('.bili-dyn-time')[0].innerText += ' (默认分组)' //}else{ //for (const tagid of followings[mid]){ // posts.followings[tagid].push(card) // $(node).find('.bili-dyn-time')[0].innerText += ` (${followGroupInfo[tagid].name})` //} //} //const currentSelect = getTagId() //if (!followings[mid].includes(currentSelect) && currentSelect != 0) card.hide() }catch(err){ console.error(err) } } })().catch(console.error); async function sleep(ms) { return new Promise((res,) => setTimeout(res, ms)) } function getTagId(){ return $('#f-groups').val() } async function getFollowingGroups(){ try { const { data } = await webRequest('https://api.bilibili.com/x/relation/tags?jsonp=jsonp') return data }catch(err){ console.error(err) return [] } } async function getUserGroups(uid){ try { const { data } = await webRequest(`https://api.bilibili.com/x/relation/tag/user?fid=${uid}&jsonp=jsonp`) return Object.keys(data) }catch(err){ console.error(err) return [] } } async function webRequest(url){ const res = await fetch(url, { credentials: 'include' }) if (!res.ok) { let backupResponse = undefined if (res.status == 412){ console.warn(`412 request too fast`) res.statusText = '请求过快导致被B站服务器禁止' backupResponse = GM_getValue(`cache:${url}`, undefined) } if (backupResponse) { console.warn(`using backup http cache:`) console.log(backupResponse) return backupResponse } else throw { ...res, message: `${res.statusText}(${res.status})` } } const json = await res.json() if (json.code) throw json GM_setValue(`cache:${url}`, json) return json } async function getFollowings(tagId, uid){ try { const {data} = await webRequest(`https://api.bilibili.com/x/relation/tag?mid=${uid}&tagid=${tagId}&pn=1&ps=50&jsonp=jsonp`) // const data = await response.json(); // console.log(data); return data }catch(err){ console.error(err) return [] } }