B站动态自定义过滤(修正版)

B站动态自定义过滤,可过滤转发类型,过滤关注分组

  1. // ==UserScript==
  2. // @name B站动态自定义过滤(修正版)
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.4
  5. // @description B站动态自定义过滤,可过滤转发类型,过滤关注分组
  6. // @author Eric Lam、cwk44
  7. // @include /^https?:\/\/t\.bilibili\.com\/[^\/]*$/
  8. // @require https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @license GPL
  12. // ==/UserScript==
  13.  
  14. (async function() {
  15. 'use strict';
  16. const posts = {
  17. normal: [],
  18. videoRel: [],
  19. repost: [],
  20. videos: [],
  21. followings: {},
  22. followingsAll: []
  23. }
  24. const defaultEnabled = {
  25. normal: true,
  26. videoRel: true,
  27. repost: true,
  28. videos: true
  29. }
  30. const followings = {}
  31. function getSettings(){
  32. try {
  33. return { ...defaultEnabled, ...JSON.parse(window.localStorage['hide_feed_settings']) }
  34. }catch(err){
  35. console.debug(`cannot found old settings: [${err.message}], using default settings`)
  36. return defaultEnabled
  37. }
  38. }
  39. const enabled = getSettings()
  40. for (const key in enabled){
  41. if (!Object.keys(defaultEnabled).includes(key)) delete enabled[key]
  42. }
  43.  
  44. let allFeedsState = true
  45.  
  46. const feedCardCallback = (mu, o) => {
  47. for (const nodes of mu){
  48. if ($(nodes.target).hasClass('loading-content') && $(nodes.addedNodes[0]).hasClass('tc-slate')){
  49. console.debug('changed tab')
  50. posts.repost = []
  51. posts.normal = []
  52. posts.videos = []
  53. posts.videoRel = []
  54. return
  55. }
  56. for (const node of nodes.addedNodes){
  57. const tid = parseInt(getTagId())
  58. if ($(node).find('.bili-dyn-content__orig.reference').length > 0) {
  59. console.debug('found repost')
  60. const repostContent = $(node).find('.bili-dyn-content__orig.reference')
  61. const isVideo = repostContent.find('.bili-dyn-card-video').length > 0
  62. console.debug(`this repost is video: ${isVideo}`)
  63. const card = repostContent.parents('.bili-dyn-item')
  64. if (isVideo){
  65. posts.videos.push(card)
  66. if (!enabled.videos) $(card).hide()
  67. }else{
  68. posts.repost.push(card)
  69. if (!enabled.repost) $(card).hide()
  70. }
  71. } else if($(node).find('.bili-dyn-content__orig').length > 0){
  72. console.debug('found normal')
  73. const content = $(node).find('.bili-dyn-content__orig')
  74. const card = content.parents('.bili-dyn-item')
  75. const isVideo = content.find('.bili-dyn-card-video').length > 0
  76. console.debug(`this normal is video: ${isVideo}`)
  77. if (isVideo){
  78. posts.videoRel.push(card)
  79. if (!enabled.videoRel) $(card).hide()
  80. }else{
  81. posts.normal.push(card)
  82. if (!enabled.normal) $(card).hide()
  83. }
  84. } else {
  85. console.debug('found unknown post')
  86. }
  87. if (allFeedsState) handleGroupFilter(node).catch(console.error)
  88. }
  89. }
  90. }
  91.  
  92. let feedCard = $('.bili-dyn-list')
  93.  
  94. while(feedCard.length == 0){
  95. console.log('bili-dyn-list not found, wait 0.5 sec')
  96. await sleep(500)
  97. feedCard = $('.bili-dyn-list')
  98. }
  99.  
  100. try {
  101. new MutationObserver(feedCardCallback).observe(feedCard[0], { subtree: true, childList: true, attributes: false })
  102. }catch(err){
  103. alert(`自定义过滤载入失败,${err.message}, 请刷新`)
  104. return
  105. }
  106.  
  107. const hideAll = (arr) => arr.forEach(s => $(s).hide())
  108. const showAll = (arr) => arr.forEach(s => $(s).show())
  109.  
  110. function handle(key, target){
  111. const val = $(target).prop('checked')
  112. enabled[key] = val
  113. // 关注分组失效
  114. const tid = parseInt(getTagId())
  115. const cards = posts[key].filter(c => tid == 0 || jqInclude(posts.followings[tid],c))
  116. //const cards = posts[key]
  117. if (val){
  118. showAll(cards)
  119. }else{
  120. hideAll(cards)
  121. }
  122. }
  123.  
  124. function jqInclude(arr, c){
  125. return arr.includes(c) || arr.some(r => r[0] == c[0])
  126. }
  127.  
  128. feedCard.parent('section').before(`
  129. <div class="tab-bar filter-list filter-grid">
  130. <div>
  131. <input id="normal-checker" type="checkbox" checked>纯动态
  132. </div>
  133. <div>
  134. <input id="video-release-checker" type="checkbox" checked>投稿视频
  135. </div>
  136. <div>
  137. <input id="repost-checker" type="checkbox" checked>转发动态
  138. </div>
  139. <div>
  140. <input id="repost-video-checker" type="checkbox" checked>转发视频
  141. </div>
  142. </div>
  143. <div class="tab-bar filter-list" id="followings-group">
  144. 分组过滤:
  145. <select id="f-groups" class="filter-select" value="0">
  146. </select>
  147. </div>
  148. <style>
  149. .filter-list {
  150. background-color: white;
  151. min-height: 10px;
  152. margin-bottom: 10px;
  153. text-align: center;
  154. padding: 15px;
  155. }
  156. .filter-grid{
  157. display: grid;
  158. grid-template-columns: repeat(4, 3fr);
  159. }
  160. .filter-select {
  161. position: relative;
  162. padding: 5px;
  163. flex-direction: column;
  164. width: 50%;
  165. border-style: solid;
  166. border-radius: 3px;
  167. border-width: 1px;
  168. border-color: #c9c9c9;
  169. }
  170. </style>
  171. `)
  172.  
  173. const followGroupInfo = {}
  174.  
  175.  
  176. const groups = await getFollowingGroups()
  177. for (const group of groups){
  178. const key = group.tagid
  179. if (group.tagid === 0) group.name = '全部关注'
  180. $('#f-groups').append(`
  181. <option value="${group.tagid}">${group.name}</option>
  182. `)
  183. followGroupInfo[group.tagid] = group
  184. posts.followings[group.tagid] = []
  185. }
  186.  
  187. $('#f-groups').val(0)
  188.  
  189. $('input#normal-checker').prop('checked', enabled.normal)
  190. $('input#video-release-checker').prop('checked', enabled.videoRel)
  191. $('input#repost-checker').prop('checked', enabled.repost)
  192. $('input#repost-video-checker').prop('checked', enabled.videos)
  193.  
  194. $('input#normal-checker').on('change', e => handle('normal', e.target))
  195. $('input#video-release-checker').on('change', e => handle('videoRel', e.target))
  196. $('input#repost-checker').on('change', e => handle('repost', e.target))
  197. $('input#repost-video-checker').on('change', e => handle('videos', e.target))
  198.  
  199.  
  200. $('#f-groups').on('change', e => {
  201. const tagid = parseInt(e.target.value)
  202.  
  203. var uid = 319278
  204. getFollowings(tagid, uid).then(followList => {
  205. if (tagid === 0) {
  206. //showAll(Object.values(posts.followings).flatMap(n => n))
  207. showAll(posts.followingsAll)
  208. }else{
  209. let filter = (c) => true
  210.  
  211. //for(const tid in posts.followings){
  212. const cards = posts.followingsAll
  213.  
  214. for (var i in cards) {
  215. var name = $(cards[i]).find('.bili-dyn-title__text').text().trim();
  216.  
  217. var isMatch = false
  218. for (var j in followList) {
  219. if (name == followList[j].uname) {
  220. isMatch = true
  221. }
  222. }
  223.  
  224. if (isMatch) {
  225. $(cards[i]).show()
  226. } else {
  227. $(cards[i]).hide()
  228. }
  229. }
  230.  
  231. /*
  232. if (tid == tagid){
  233. showAll(cards)
  234. filter = (c) => !cards.includes(c)
  235. }else{
  236. hideAll(cards.filter(filter))
  237. }*/
  238. //}
  239. }
  240. for(const key in enabled){
  241. const val = enabled[key]
  242. const tid = parseInt(getTagId())
  243. const cards = posts[key].filter(c => tid == 0 || jqInclude(posts.followings[tid],c))
  244. if (!val){
  245. hideAll(cards)
  246. }
  247. }
  248. })
  249. })
  250.  
  251. window.onunload = function(){
  252. window.localStorage['hide_feed_settings'] = JSON.stringify(enabled)
  253. }
  254.  
  255.  
  256. while($('.bili-dyn-up-list__content').length == 0){
  257. console.log('bili-dyn-up-list__content not found, wait 0.5 sec')
  258. await sleep(500)
  259. }
  260.  
  261. try{
  262. new MutationObserver(([mu], o) => {
  263. console.log($(mu.target).find('bili-dyn-up-list__item.active > .bili-dyn-up-list__item__name')[0])
  264. const allTargets = $(mu.target).hasClass('.active') && $(mu.target).find('.bili-dyn-up-list__item__name')[0]?.innerText == '全部动态'
  265. allFeedsState = allTargets
  266. if (allTargets){
  267. console.debug('showing followings group')
  268. $('#followings-group').show()
  269. }else{
  270. console.debug('hiding followings group')
  271. $('#followings-group').hide()
  272. $('#f-groups').val(0)
  273. for(const tid in posts.followings){
  274. posts.followings[tid] = []
  275. }
  276. }
  277. }).observe($('.bili-dyn-up-list__content')[0], { childList: false, subtree: true, attributes: true})
  278. }catch(err){
  279. alert(`自定义过滤载入失败,${err.message}, 请刷新`)
  280. return
  281. }
  282.  
  283.  
  284. async function handleGroupFilter(node){
  285. if (!$(node).hasClass('bili-dyn-list__item')) {
  286. return
  287. }
  288.  
  289. try {
  290. const reg = /(\d+)\/dynamic$/
  291. const card = $(node);
  292. //.parents('.bili-dyn-item')
  293.  
  294. // const url = card.find('a.c-pointer.user-head').prop('href')
  295. //const regexResult = reg.exec(card.find('a.c-pointer.user-head').prop('href'))
  296. //if (!regexResult){
  297. // 无效uid,可能是番剧?
  298. // return
  299. //}
  300. //const mid = parseInt(regexResult.pop())
  301. var mid = 319278
  302. //查找某个关注的uid是哪个分组
  303. //if (followings[mid].length == 0){
  304. //if (typeof posts.followings[0] == "undefined") {
  305. // posts.followings[0] = []
  306. //}
  307. // posts.followings[0].push(card)
  308. posts.followingsAll.push(card)
  309.  
  310. $(node).find('.bili-dyn-time')[0].innerText += ' (默认分组)'
  311. //}else{
  312. //for (const tagid of followings[mid]){
  313. // posts.followings[tagid].push(card)
  314. // $(node).find('.bili-dyn-time')[0].innerText += ` (${followGroupInfo[tagid].name})`
  315. //}
  316. //}
  317. //const currentSelect = getTagId()
  318. //if (!followings[mid].includes(currentSelect) && currentSelect != 0) card.hide()
  319. }catch(err){
  320. console.error(err)
  321. }
  322. }
  323.  
  324. })().catch(console.error);
  325.  
  326. async function sleep(ms) {
  327. return new Promise((res,) => setTimeout(res, ms))
  328. }
  329.  
  330. function getTagId(){
  331. return $('#f-groups').val()
  332. }
  333.  
  334. async function getFollowingGroups(){
  335. try {
  336. const { data } = await webRequest('https://api.bilibili.com/x/relation/tags?jsonp=jsonp')
  337. return data
  338. }catch(err){
  339. console.error(err)
  340. return []
  341. }
  342. }
  343.  
  344. async function getUserGroups(uid){
  345. try {
  346. const { data } = await webRequest(`https://api.bilibili.com/x/relation/tag/user?fid=${uid}&jsonp=jsonp`)
  347. return Object.keys(data)
  348. }catch(err){
  349. console.error(err)
  350. return []
  351. }
  352. }
  353.  
  354. async function webRequest(url){
  355. const res = await fetch(url, { credentials: 'include' })
  356. if (!res.ok) {
  357. let backupResponse = undefined
  358. if (res.status == 412){
  359. console.warn(`412 request too fast`)
  360. res.statusText = '请求过快导致被B站服务器禁止'
  361. backupResponse = GM_getValue(`cache:${url}`, undefined)
  362. }
  363. if (backupResponse) {
  364. console.warn(`using backup http cache:`)
  365. console.log(backupResponse)
  366. return backupResponse
  367. }
  368. else throw { ...res, message: `${res.statusText}(${res.status})` }
  369. }
  370. const json = await res.json()
  371. if (json.code) throw json
  372. GM_setValue(`cache:${url}`, json)
  373. return json
  374. }
  375.  
  376. async function getFollowings(tagId, uid){
  377. try {
  378. const {data} = await webRequest(`https://api.bilibili.com/x/relation/tag?mid=${uid}&tagid=${tagId}&pn=1&ps=50&jsonp=jsonp`)
  379. // const data = await response.json();
  380. // console.log(data);
  381. return data
  382. }catch(err){
  383. console.error(err)
  384. return []
  385. }
  386. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址