From 3b6eea609fd8bf90a9f81f10b5ea50ef2ad352f7 Mon Sep 17 00:00:00 2001 From: festoney8 Date: Thu, 18 Jul 2024 22:47:29 +0800 Subject: [PATCH 1/6] feat: hide video page player all popup items --- CHANGELOG.md | 5 ++++ src/rules/video.ts | 58 +++++++++++++++++++++++++++++++++++++--------- vite.config.ts | 2 +- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f6dbf56..3bbdf5fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## 3.9.2 + +- 新增:播放页 强制隐藏播放器内所有弹窗 +- 优化:播放页 弹窗净化 + ## 3.9.1 - 新增:空间页 动态列表净化 diff --git a/src/rules/video.ts b/src/rules/video.ts index 3905812b..f9772325 100644 --- a/src/rules/video.ts +++ b/src/rules/video.ts @@ -626,52 +626,88 @@ if (isPageVideo() || isPagePlaylist() || isPageFestival()) { new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-guide-all', description: '隐藏 一键三连', - itemCSS: `.bpx-player-video-area :is(.bili-guide-all, .bili-guide, .bili-follow-to-electric, .bili-guide-followed) { - display: none !important; - }`, + itemCSS: ` + .bili-follow-to-electric, + .bili-guide, + .bili-guide-all, + .bili-guide-animate, + .bili-guide-cyc, + .bili-guide-electric, + .bili-guide-follow, + .bili-guide-init-three, + .bili-guide-three, + .bili-guide-followed { + display: none !important; + }`, }), // 隐藏 投票 new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-vote', description: '隐藏 投票', - itemCSS: `.bpx-player-video-area :is(.bili-vote, .bili-cmd-shrink) {display: none !important;}`, + itemCSS: ` + .bili-vote, + .bili-vote-lottie, + .bili-vote-question, + .bili-vote-an, + .bili-cmd-shrink { + display: none !important; + }`, }), // 隐藏 播放效果调查, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-qoe-feedback', description: '隐藏 播放效果调查', defaultStatus: true, - itemCSS: `.bpx-player-video-area .bili-qoeFeedback {display: none !important;}`, + itemCSS: ` + .bili-qoeFeedback, + .bili-qoeFeedback-score, + .bili-qoeFeedback-vote { + display: none !important; + }`, }), // 隐藏 评分 new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-score', description: '隐藏 评分', - itemCSS: `.bpx-player-video-area .bili-score {display: none !important;}`, + itemCSS: ` + .bili-score, + .bili-score-area, + .bili-score-area-item, + .bili-score-count, + .bili-score-result, + .bili-score-title { + display: none !important; + }`, }), // 隐藏 评分总结 new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-score-sum', description: '隐藏 评分总结', - itemCSS: `.bpx-player-video-area .bili-scoreSum {display: none !important;}`, + itemCSS: `.bili-scoreSum {display: none !important;}`, }), // 隐藏 打卡 new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-clock', description: '隐藏 打卡', - itemCSS: `.bpx-player-video-area .bili-clock {display: none !important;}`, + itemCSS: `.bili-clock {display: none !important;}`, }), // 隐藏 视频预告 new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-reserve', description: '隐藏 视频预告', - itemCSS: `.bpx-player-video-area .bili-reserve {display: none !important;}`, + itemCSS: `.bili-reserve {display: none !important;}`, }), // 隐藏 视频链接 new CheckboxItem({ itemID: 'video-page-hide-bpx-player-bili-link', - description: '隐藏 视频链接(稍后再看)', - itemCSS: `.bpx-player-video-area .bili-link {display: none !important;}`, + description: '隐藏 视频链接 (稍后再看)', + itemCSS: `.bili-link {display: none !important;}`, + }), + // 隐藏 播放器内所有弹窗 (强制) + new CheckboxItem({ + itemID: 'video-page-hide-bpx-player-cmd-dm-inside', + description: '隐藏 播放器内所有弹窗 (强制)', + itemCSS: `.bpx-player-cmd-dm-inside {display: none !important;}`, }), // 隐藏 播放器内标题 new CheckboxItem({ diff --git a/vite.config.ts b/vite.config.ts index 9e6f552d..8f39c18c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ userscript: { name: 'bilibili 页面净化大师', namespace: 'http://tampermonkey.net/', - version: '3.9.1', + version: '3.9.2', description: '净化 B站/哔哩哔哩 页面,支持「精简功能、播放器净化、过滤视频、过滤评论、全站黑白名单」,提供 300+ 功能,定制自己的 B 站', author: 'festoney8', From 477fa4014b953d7fad016faa58df85eb6568853d Mon Sep 17 00:00:00 2001 From: festoney8 Date: Sat, 20 Jul 2024 00:31:22 +0800 Subject: [PATCH 2/6] feat: video page comment filter bot and callUser --- CHANGELOG.md | 3 +- src/filters/commentFilter/agency/agency.ts | 33 ++ src/filters/commentFilter/filters/core.ts | 33 +- .../commentFilter/filters/subfilters/bot.ts | 72 +++++ .../filters/subfilters/callBot.ts | 72 +++++ .../filters/subfilters/callUser.ts | 30 ++ .../commentFilter/pages/actions/action.ts | 94 +++++- src/filters/commentFilter/pages/video.ts | 281 ++++++++++-------- src/rules/video.ts | 68 ++--- 9 files changed, 522 insertions(+), 164 deletions(-) create mode 100644 src/filters/commentFilter/filters/subfilters/bot.ts create mode 100644 src/filters/commentFilter/filters/subfilters/callBot.ts create mode 100644 src/filters/commentFilter/filters/subfilters/callUser.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bbdf5fd..1ccec1d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ## 3.9.2 -- 新增:播放页 强制隐藏播放器内所有弹窗 - 优化:播放页 弹窗净化 +- 新增:播放页 强制隐藏播放器内所有弹窗 +- 新增:播放页 评论过滤 支持过滤新版评论区AI相关、@相关评论 ## 3.9.1 diff --git a/src/filters/commentFilter/agency/agency.ts b/src/filters/commentFilter/agency/agency.ts index 1cb3b9d6..33a54e4f 100644 --- a/src/filters/commentFilter/agency/agency.ts +++ b/src/filters/commentFilter/agency/agency.ts @@ -1,3 +1,6 @@ +import botFilterInstance from '../filters/subfilters/bot' +import callBotFilterInstance from '../filters/subfilters/callBot' +import callUserFilterInstance from '../filters/subfilters/callUser' import contentFilterInstance from '../filters/subfilters/content' import usernameFilterInstance from '../filters/subfilters/username' @@ -45,6 +48,36 @@ class CommentFilterAgency { break } } + notifyBot(event: string) { + switch (event) { + case 'disable': + botFilterInstance.setStatus(false) + break + case 'enable': + botFilterInstance.setStatus(true) + break + } + } + notifyCallBot(event: string) { + switch (event) { + case 'disable': + callBotFilterInstance.setStatus(false) + break + case 'enable': + callBotFilterInstance.setStatus(true) + break + } + } + notifyCallUser(event: string) { + switch (event) { + case 'disable': + callUserFilterInstance.setStatus(false) + break + case 'enable': + callUserFilterInstance.setStatus(true) + break + } + } } // 单例 diff --git a/src/filters/commentFilter/filters/core.ts b/src/filters/commentFilter/filters/core.ts index 8a89e5ff..cefa20a7 100644 --- a/src/filters/commentFilter/filters/core.ts +++ b/src/filters/commentFilter/filters/core.ts @@ -1,13 +1,16 @@ import settings from '../../../settings' import { error, log } from '../../../utils/logger' import { hideEle, isEleHide, showEle } from '../../../utils/tool' +import botFilterInstance from './subfilters/bot' +import callBotFilterInstance from './subfilters/callBot' +import callUserFilterInstance from './subfilters/callUser' import contentFilterInstance from './subfilters/content' import usernameFilterInstance from './subfilters/username' export interface ICommentSubFilter { isEnable: boolean setStatus(status: boolean): void - setParams(value: string[]): void + setParams?(value: string[]): void addParam?(value: string): void check(value: string): Promise } @@ -15,11 +18,13 @@ export interface ICommentSubFilter { export type CommentSelectorFunc = { username?: (comment: HTMLElement) => string | null content?: (comment: HTMLElement) => string | null + callUser?: (comment: HTMLElement) => string | null } interface CommentInfo { username?: string | undefined content?: string | undefined + callUser?: string | undefined } class CoreCommentFilter { @@ -34,8 +39,11 @@ class CoreCommentFilter { try { const checkContent = contentFilterInstance.isEnable && selectorFunc.content !== undefined const checkUsername = usernameFilterInstance.isEnable && selectorFunc.username !== undefined + const checkBot = botFilterInstance.isEnable && selectorFunc.username !== undefined + const checkCallBot = callBotFilterInstance.isEnable && selectorFunc.callUser !== undefined + const checkCallUser = callUserFilterInstance.isEnable && selectorFunc.callUser !== undefined - if (!checkContent && !checkUsername) { + if (!checkContent && !checkUsername && !checkBot && !checkCallBot && !checkCallUser) { // 黑名单全部关闭时 恢复全部评论 comments.forEach((comment) => showEle(comment)) return @@ -61,6 +69,27 @@ class CoreCommentFilter { info.username = username } } + if (checkBot) { + const username = selectorFunc.username!(comment) + if (username) { + blackTasks.push(botFilterInstance.check(username)) + info.username = username + } + } + if (checkCallBot) { + const callUser = selectorFunc.callUser!(comment) + if (callUser) { + blackTasks.push(callBotFilterInstance.check(callUser)) + info.callUser = callUser + } + } + if (checkCallUser) { + const callUser = selectorFunc.callUser!(comment) + if (callUser) { + blackTasks.push(callUserFilterInstance.check(callUser)) + info.callUser = callUser + } + } // 执行检测 Promise.all(blackTasks) diff --git a/src/filters/commentFilter/filters/subfilters/bot.ts b/src/filters/commentFilter/filters/subfilters/bot.ts new file mode 100644 index 00000000..cee67581 --- /dev/null +++ b/src/filters/commentFilter/filters/subfilters/bot.ts @@ -0,0 +1,72 @@ +import { error } from '../../../../utils/logger' +import { ICommentSubFilter } from '../core' + +class BotFilter implements ICommentSubFilter { + isEnable = false + // 8455326 @机器工具人 + // 234978716 @有趣的程序员 + // 1141159409 @AI视频小助理 + // 437175450 @AI视频小助理总结一下 (误伤) + // 1692825065 @AI笔记侠 + // 690155730 @AI视频助手 + // 689670224 @哔哩哔理点赞姬 + // 3494380876859618 @课代表猫 + // 1168527940 @AI课代表呀 + // 439438614 @木几萌Moe + // 1358327273 @星崽丨StarZai + // 3546376048741135 @AI沈阳美食家 + // 1835753760 @AI识片酱 + // 9868463 @AI头脑风暴 + // 358243654 @GPT_5 + // 393788832 @Juice_AI + // 91394217 @AI全文总结 + // 473018527 @AI视频总结 + // 3546639035795567 @AI总结视频 + private botSet = new Set([ + '机器工具人', + '有趣的程序员', + 'AI视频小助理', + 'AI视频小助理总结一下 (误伤)', + 'AI笔记侠', + 'AI视频助手', + '哔哩哔理点赞姬', + '课代表猫', + 'AI课代表呀', + '木几萌Moe', + '星崽丨StarZai', + 'AI沈阳美食家', + 'AI识片酱', + 'AI头脑风暴', + 'GPT_5', + 'Juice_AI', + 'AI全文总结', + 'AI视频总结', + 'AI总结视频', + ]) + + setStatus(status: boolean) { + this.isEnable = status + } + + check(username: string): Promise { + username = username.trim() + return new Promise((resolve, reject) => { + try { + if (!this.isEnable || username.length === 0 || this.botSet.size === 0) { + resolve('bot resolve, disable or empty') + } else if (this.botSet.has(username)) { + reject(`bot reject, ${username} in blacklist`) + } else { + resolve('bot resolve') + } + } catch (err) { + error(err) + resolve(`bot resolve, error`) + } + }) + } +} + +// 单例 +const botFilterInstance = new BotFilter() +export default botFilterInstance diff --git a/src/filters/commentFilter/filters/subfilters/callBot.ts b/src/filters/commentFilter/filters/subfilters/callBot.ts new file mode 100644 index 00000000..ec7f1bcb --- /dev/null +++ b/src/filters/commentFilter/filters/subfilters/callBot.ts @@ -0,0 +1,72 @@ +import { error } from '../../../../utils/logger' +import { ICommentSubFilter } from '../core' + +class CallBotFilter implements ICommentSubFilter { + isEnable = false + // 8455326 @机器工具人 + // 234978716 @有趣的程序员 + // 1141159409 @AI视频小助理 + // 437175450 @AI视频小助理总结一下 (误伤) + // 1692825065 @AI笔记侠 + // 690155730 @AI视频助手 + // 689670224 @哔哩哔理点赞姬 + // 3494380876859618 @课代表猫 + // 1168527940 @AI课代表呀 + // 439438614 @木几萌Moe + // 1358327273 @星崽丨StarZai + // 3546376048741135 @AI沈阳美食家 + // 1835753760 @AI识片酱 + // 9868463 @AI头脑风暴 + // 358243654 @GPT_5 + // 393788832 @Juice_AI + // 91394217 @AI全文总结 + // 473018527 @AI视频总结 + // 3546639035795567 @AI总结视频 + private botSet = new Set([ + '机器工具人', + '有趣的程序员', + 'AI视频小助理', + 'AI视频小助理总结一下 (误伤)', + 'AI笔记侠', + 'AI视频助手', + '哔哩哔理点赞姬', + '课代表猫', + 'AI课代表呀', + '木几萌Moe', + '星崽丨StarZai', + 'AI沈阳美食家', + 'AI识片酱', + 'AI头脑风暴', + 'GPT_5', + 'Juice_AI', + 'AI全文总结', + 'AI视频总结', + 'AI总结视频', + ]) + + setStatus(status: boolean) { + this.isEnable = status + } + + check(callUser: string): Promise { + callUser = callUser.trim() + return new Promise((resolve, reject) => { + try { + if (!this.isEnable || callUser.length === 0 || this.botSet.size === 0) { + resolve('callBot resolve, disable or empty') + } else if (this.botSet.has(callUser)) { + reject(`callBot reject, ${callUser} in blacklist`) + } else { + resolve('callBot resolve') + } + } catch (err) { + error(err) + resolve(`callBot resolve, error`) + } + }) + } +} + +// 单例 +const callBotFilterInstance = new CallBotFilter() +export default callBotFilterInstance diff --git a/src/filters/commentFilter/filters/subfilters/callUser.ts b/src/filters/commentFilter/filters/subfilters/callUser.ts new file mode 100644 index 00000000..e76f19fb --- /dev/null +++ b/src/filters/commentFilter/filters/subfilters/callUser.ts @@ -0,0 +1,30 @@ +import { error } from '../../../../utils/logger' +import { ICommentSubFilter } from '../core' + +class CallUserFilter implements ICommentSubFilter { + isEnable = false + + setStatus(status: boolean) { + this.isEnable = status + } + + check(callUser: string): Promise { + callUser = callUser.trim() + return new Promise((resolve, reject) => { + try { + if (this.isEnable && callUser.length) { + reject(`is callUser`) + } else { + resolve(`not callUser`) + } + } catch (err) { + error(err) + resolve(`callUser resolve, error`) + } + }) + } +} + +// 单例 +const callUserFilterInstance = new CallUserFilter() +export default callUserFilterInstance diff --git a/src/filters/commentFilter/pages/actions/action.ts b/src/filters/commentFilter/pages/actions/action.ts index 3a023e80..ef196aaf 100644 --- a/src/filters/commentFilter/pages/actions/action.ts +++ b/src/filters/commentFilter/pages/actions/action.ts @@ -7,9 +7,9 @@ import usernameFilterInstance from '../../filters/subfilters/username' // 定义各种黑名单功能、白名单功能的属性和行为 interface CommentFilterAction { statusKey: string - valueKey: string status: boolean - value: number | string | string[] + valueKey?: string + value?: number | string | string[] // 检测评论列表的函数 checkCommentList(fullSite: boolean): void blacklist?: WordList @@ -139,3 +139,93 @@ export class ContentAction implements CommentFilterAction { this.checkCommentList(true) } } + +export class BotAction implements CommentFilterAction { + statusKey: string + status: boolean + checkCommentList: (fullSite: boolean) => void + + /** + * 屏蔽AI发布的评论 + * @param statusKey 是否启用的GM key + * @param checkCommentList 检测评论列表函数 + */ + constructor(statusKey: string, checkCommentList: (fullSite: boolean) => void) { + this.statusKey = statusKey + this.status = GM_getValue(`BILICLEANER_${this.statusKey}`, false) + this.checkCommentList = checkCommentList + + contentFilterInstance.setStatus(this.status) + } + + enable() { + commentFilterAgencyInstance.notifyBot('enable') + this.checkCommentList(true) + this.status = true + } + disable() { + commentFilterAgencyInstance.notifyBot('disable') + this.checkCommentList(true) + this.status = false + } +} + +export class CallBotAction implements CommentFilterAction { + statusKey: string + status: boolean + checkCommentList: (fullSite: boolean) => void + + /** + * 过滤召唤AI的评论 + * @param statusKey 是否启用的GM key + * @param checkCommentList 检测评论列表函数 + */ + constructor(statusKey: string, checkCommentList: (fullSite: boolean) => void) { + this.statusKey = statusKey + this.status = GM_getValue(`BILICLEANER_${this.statusKey}`, false) + this.checkCommentList = checkCommentList + + contentFilterInstance.setStatus(this.status) + } + + enable() { + commentFilterAgencyInstance.notifyCallBot('enable') + this.checkCommentList(true) + this.status = true + } + disable() { + commentFilterAgencyInstance.notifyCallBot('disable') + this.checkCommentList(true) + this.status = false + } +} + +export class CallUserAction implements CommentFilterAction { + statusKey: string + status: boolean + checkCommentList: (fullSite: boolean) => void + + /** + * 过滤AT其他用户的评论 + * @param statusKey 是否启用的GM key + * @param checkCommentList 检测评论列表函数 + */ + constructor(statusKey: string, checkCommentList: (fullSite: boolean) => void) { + this.statusKey = statusKey + this.status = GM_getValue(`BILICLEANER_${this.statusKey}`, false) + this.checkCommentList = checkCommentList + + contentFilterInstance.setStatus(this.status) + } + + enable() { + commentFilterAgencyInstance.notifyCallUser('enable') + this.checkCommentList(true) + this.status = true + } + disable() { + commentFilterAgencyInstance.notifyCallUser('disable') + this.checkCommentList(true) + this.status = false + } +} diff --git a/src/filters/commentFilter/pages/video.ts b/src/filters/commentFilter/pages/video.ts index f5606126..0723baf8 100644 --- a/src/filters/commentFilter/pages/video.ts +++ b/src/filters/commentFilter/pages/video.ts @@ -2,8 +2,8 @@ import { Group } from '../../../components/group' import { CheckboxItem, ButtonItem } from '../../../components/item' import { debugCommentFilter as debug, error } from '../../../utils/logger' import { isPageBangumi, isPagePlaylist, isPageVideo } from '../../../utils/pageType' -import { showEle, waitForEle } from '../../../utils/tool' -import { ContentAction, UsernameAction } from './actions/action' +import { showEle } from '../../../utils/tool' +import { BotAction, CallBotAction, CallUserAction, ContentAction, UsernameAction } from './actions/action' import coreCommentFilterInstance, { CommentSelectorFunc } from '../filters/core' import settings from '../../../settings' import { ContextMenu } from '../../../components/contextmenu' @@ -33,14 +33,33 @@ let isNoteCommentWhitelistEnable = false let isLinkCommentWhitelistEnable = false if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { - let commentListContainer: HTMLElement // 一级评论 const rootCommentSelectorFunc: CommentSelectorFunc = { username: (comment: HTMLElement): string | null => { - const username = comment.querySelector('.root-reply-container .user-name')?.textContent?.trim() - return username ? username : null + if (isCommentV2()) { + const uname = + (comment as any).__data?.member?.uname || + comment.shadowRoot + ?.querySelector('bili-comment-renderer') + ?.shadowRoot?.querySelector('bili-comment-user-info') + ?.shadowRoot?.querySelector('#user-name') + ?.textContent?.trim() + return uname ? uname : null + } + + const uname = comment.querySelector('.root-reply-container .user-name')?.textContent?.trim() + return uname ? uname : null }, content: (comment: HTMLElement): string | null => { + if (isCommentV2()) { + const content = + (comment as any).__data?.content?.message?.trim() || + comment.shadowRoot + ?.querySelector('bili-comment-renderer') + ?.shadowRoot?.querySelector('bili-rich-text') + ?.shadowRoot?.querySelector('#contents')?.textContent + return content ? content : null + } let content = comment.querySelector('.root-reply-container .reply-content')?.textContent?.trim() const atUsers = comment.querySelectorAll('.root-reply-container .jump-link.user') if (atUsers.length) { @@ -53,14 +72,43 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { } return content ? content : null }, + callUser: (comment: HTMLElement): string | null => { + if (isCommentV2()) { + const contentNode = comment.shadowRoot + ?.querySelector('bili-comment-renderer') + ?.shadowRoot?.querySelector('bili-rich-text')?.shadowRoot + const uname = contentNode?.querySelector('a[data-user-profile-id]')?.textContent?.replace('@', '') + return uname ? uname : null + } + return null + }, } // 二级评论 const subCommentSelectorFunc: CommentSelectorFunc = { username: (comment: HTMLElement): string | null => { + if (isCommentV2()) { + const username = + (comment as any).__data?.member?.uname || + comment.shadowRoot + ?.querySelector('bili-comment-user-info') + ?.shadowRoot?.querySelector('#user-name a') + ?.textContent?.trim() + return username ? username : null + } const username = comment.querySelector('.sub-user-name')?.textContent?.trim() return username ? username : null }, content: (comment: HTMLElement): string | null => { + if (isCommentV2()) { + const contentNode = comment.shadowRoot?.querySelector('bili-rich-text')?.shadowRoot + let content = + (comment as any).__data?.content?.message?.trim() || + contentNode?.querySelector('#contents')?.textContent?.trim() + // 忽略二级回复中@多用户情况 + content = content.replace(/^回复 @[^@ ]+? :/, '').trim() + return content ? content : null + } + let content = comment.querySelector('.reply-content')?.textContent?.trim() const atUsers = comment.querySelectorAll('.reply-content .jump-link.user') if (atUsers.length && content) { @@ -74,65 +122,38 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { } return content ? content : null }, - } + callUser: (comment: HTMLElement): string | null => { + if (isCommentV2()) { + const contentNode = comment.shadowRoot?.querySelector('bili-rich-text')?.shadowRoot + const content = + (comment as any).__data?.content?.message?.trim() || + contentNode?.querySelector('#contents')?.textContent?.trim() + let uname + if (!content) { + uname = null + } else { + if ((content as string).startsWith('回复 @')) { + uname = contentNode + ?.querySelector('a[data-user-profile-id]:nth-of-type(2)') + ?.textContent?.replace('@', '') + } else { + uname = contentNode?.querySelector('a[data-user-profile-id]')?.textContent?.replace('@', '') + } + } + return uname ? uname : null + } - // 新版评论区 一级评论 - const rootCommentSelectorFuncV2: CommentSelectorFunc = { - username: (comment: HTMLElement): string | null => { - const username = - (comment as any).__data?.member?.uname || - comment.shadowRoot - ?.querySelector('bili-comment-renderer') - ?.shadowRoot?.querySelector('bili-comment-user-info') - ?.shadowRoot?.querySelector('#user-name') - ?.textContent?.trim() - return username ? username : null - }, - content: (comment: HTMLElement): string | null => { - const content = - (comment as any).__data?.content?.message?.trim() || - comment.shadowRoot - ?.querySelector('bili-comment-renderer') - ?.shadowRoot?.querySelector('bili-rich-text') - ?.shadowRoot?.querySelector('#contents')?.textContent - return content ? content : null - }, - } - // 新版评论区 二级评论 - const subCommentSelectorFuncV2: CommentSelectorFunc = { - username: (comment: HTMLElement): string | null => { - const username = - (comment as any).__data?.member?.uname || - comment.shadowRoot - ?.querySelector('bili-comment-user-info') - ?.shadowRoot?.querySelector('#user-name a') - ?.textContent?.trim() - return username ? username : null - }, - content: (comment: HTMLElement): string | null => { - const contentNode = comment.shadowRoot?.querySelector('bili-rich-text')?.shadowRoot - let content = - (comment as any).__data?.content?.message?.trim() || - contentNode?.querySelector('#contents')?.textContent?.trim() - // 忽略二级回复中@多用户情况 - content = content.replace(/^回复 @[^@ ]+? :/, '').trim() - return content ? content : null + return null }, } // 检测评论列表 const checkCommentList = (fullSite: boolean) => { - if (!commentListContainer) { - debug(`checkCommentList commentListContainer not exist`) - return - } try { + let rootComments: HTMLElement[] = [] + let subComments: HTMLElement[] = [] if (isCommentV2()) { - // shadow DOM 版评论区 - let rootComments: HTMLElement[] = [] - let subComments: HTMLElement[] = [] - - const shadowRoot = commentListContainer.querySelector('bili-comments')?.shadowRoot + const shadowRoot = document.querySelector('bili-comments')?.shadowRoot if (!shadowRoot) { return } @@ -177,7 +198,9 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { (isLinkCommentWhitelistEnable && root ?.querySelector('bili-rich-text') - ?.shadowRoot?.querySelector(`a:not([href*="space.bilibili.com"])`)) + ?.shadowRoot?.querySelector( + `a:not([href*="space.bilibili.com"]):not([data-video-time])`, + )) if (isWhite) { showEle(e) } @@ -199,24 +222,21 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { return !isWhite }) - rootComments.length && coreCommentFilterInstance.checkAll(rootComments, true, rootCommentSelectorFuncV2) - subComments.length && coreCommentFilterInstance.checkAll(subComments, true, subCommentSelectorFuncV2) + rootComments.length && coreCommentFilterInstance.checkAll(rootComments, true, rootCommentSelectorFunc) + subComments.length && coreCommentFilterInstance.checkAll(subComments, true, subCommentSelectorFunc) debug(`check ${rootComments.length} V2 root, ${subComments.length} V2 sub comments`) } else { - let rootComments: HTMLElement[] = [] - let subComments: HTMLElement[] = [] - if (fullSite) { - rootComments = Array.from(commentListContainer.querySelectorAll(`.reply-item`)) + rootComments = Array.from(document.querySelectorAll(`.reply-item`)) subComments = Array.from( - commentListContainer.querySelectorAll(`.sub-reply-item:not(.jump-link.user)`), + document.querySelectorAll(`.sub-reply-item:not(.jump-link.user)`), ) } else { rootComments = Array.from( - commentListContainer.querySelectorAll(`.reply-item:not([${settings.filterSign}])`), + document.querySelectorAll(`.reply-item:not([${settings.filterSign}])`), ) subComments = Array.from( - commentListContainer.querySelectorAll( + document.querySelectorAll( `.sub-reply-item:not(.jump-link.user):not([${settings.filterSign}])`, ), ) @@ -266,41 +286,41 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { } } - const checkV2 = (fullSite: boolean) => { - if (usernameAction.status || contentAction.status) { - checkCommentList(fullSite) - } - } /** - * 新版评论区过滤 + * 评论区过滤,新旧通用 * 在获取评论相关API时触发检测 * 多层 Shadow DOM 套娃对MutationObserver不友好 * 切换视频会导致observe对象被替换 * 使用监听一级二级评论载入方法触发评论区检测 */ + const startCheck = (fullSite: boolean) => { + ;(usernameAction.status || contentAction.status) && checkCommentList(fullSite) + } fetchHook.addPostFn((input: RequestInfo | URL, init: RequestInit | undefined, _resp?: Response) => { - if (isCommentV2()) { - if ( - typeof input === 'string' && - init?.method?.toUpperCase() === 'GET' && - input.includes('api.bilibili.com') - ) { - // 主评论载入 - if (input.includes('/v2/reply/wbi/main')) { - setTimeout(() => checkV2(false), 100) - setTimeout(() => checkV2(false), 200) - setTimeout(() => checkV2(false), 500) - setTimeout(() => checkV2(false), 1000) - setTimeout(() => checkV2(false), 2000) - setTimeout(() => checkV2(false), 3000) - } - // 二级评论翻页 - if (input.includes('/v2/reply/reply')) { - setTimeout(() => checkV2(true), 100) - setTimeout(() => checkV2(true), 200) - setTimeout(() => checkV2(true), 500) - setTimeout(() => checkV2(true), 1000) - setTimeout(() => checkV2(true), 2000) + if (typeof input === 'string' && init?.method?.toUpperCase() === 'GET' && input.includes('api.bilibili.com')) { + // 主评论载入 + if (input.includes('/v2/reply/wbi/main')) { + setTimeout(() => startCheck(false), 100) + setTimeout(() => startCheck(false), 200) + setTimeout(() => startCheck(false), 500) + setTimeout(() => startCheck(false), 1000) + setTimeout(() => startCheck(false), 2000) + setTimeout(() => startCheck(false), 3000) + } + // 二级评论翻页 + if (input.includes('/v2/reply/reply')) { + if (isCommentV2()) { + setTimeout(() => startCheck(true), 100) + setTimeout(() => startCheck(true), 200) + setTimeout(() => startCheck(true), 500) + setTimeout(() => startCheck(true), 1000) + setTimeout(() => startCheck(true), 2000) + } else { + setTimeout(() => startCheck(false), 100) + setTimeout(() => startCheck(false), 200) + setTimeout(() => startCheck(false), 500) + setTimeout(() => startCheck(false), 1000) + setTimeout(() => startCheck(false), 2000) } } } @@ -317,36 +337,9 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { 'global-comment-content-filter-value', checkCommentList, ) - - // 监听评论列表内部变化, 有变化时检测评论列表 - const watchCommentListContainer = () => { - if (commentListContainer && !isCommentV2()) { - if (usernameAction.status || contentAction.status) { - checkCommentList(true) - } - const commentObserver = new MutationObserver(() => { - if (usernameAction.status || contentAction.status) { - checkCommentList(false) - } - }) - commentObserver.observe(commentListContainer, { childList: true, subtree: true }) - } - } - - try { - // 旧版评论区过滤 - waitForEle(document, '#comment, #comment-body, .playlist-comment', (node: HTMLElement): boolean => { - return ['comment', 'comment-body'].includes(node.id) || node.className === 'playlist-comment' - }).then((ele) => { - if (ele) { - commentListContainer = ele - watchCommentListContainer() - } - }) - } catch (err) { - error(err) - error(`watch comment list ERROR`) - } + const botAction = new BotAction('video-comment-bot-filter-status', checkCommentList) + const callBotAction = new CallBotAction('video-comment-call-bot-filter-status', checkCommentList) + const callUserAction = new CallUserAction('video-comment-call-user-filter-status', checkCommentList) //======================================================================================= @@ -423,6 +416,44 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { new Group('comment-username-filter-group', '播放页 评论区 用户过滤', usernameItems), ) + // UI组件, 按类型过滤part + const typeItems = [ + // 过滤 召唤AI的评论 + new CheckboxItem({ + itemID: callBotAction.statusKey, + description: '过滤 召唤AI的评论', + enableFunc: async () => { + callBotAction.enable() + }, + disableFunc: async () => { + callBotAction.disable() + }, + }), + // 过滤 AI发布的评论 + new CheckboxItem({ + itemID: botAction.statusKey, + description: '过滤 AI发布的评论', + enableFunc: async () => { + botAction.enable() + }, + disableFunc: async () => { + botAction.disable() + }, + }), + // 过滤 @其他用户的评论 + new CheckboxItem({ + itemID: callUserAction.statusKey, + description: '过滤 @其他用户的评论', + enableFunc: async () => { + callUserAction.enable() + }, + disableFunc: async () => { + callUserAction.disable() + }, + }), + ] + videoPageCommentFilterGroupList.push(new Group('comment-type-filter-group', '播放页 评论区 按类型过滤', typeItems)) + // UI组件, 评论内容过滤part const contentItems = [ // 启用 播放页关键词过滤 @@ -521,7 +552,7 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { // 含超链接的评论 免过滤, 默认开启 new CheckboxItem({ itemID: 'video-comment-link-whitelist-status', - description: '含超链接的评论 免过滤\n(站内视频/URL/播放时间跳转)', + description: '含超链接的评论 免过滤\n(站内视频/URL)', defaultStatus: true, enableFunc: async () => { isLinkCommentWhitelistEnable = true diff --git a/src/rules/video.ts b/src/rules/video.ts index f9772325..1522abf4 100644 --- a/src/rules/video.ts +++ b/src/rules/video.ts @@ -1662,7 +1662,7 @@ if (isPageVideo() || isPagePlaylist()) { // 测试视频:https://www.bilibili.com/video/av1805762267 new CheckboxItem({ itemID: 'video-page-hide-top-vote-card', - description: '隐藏 投票栏 (红方/蓝方) ★', + description: '隐藏 投票栏 (红方/蓝方)', itemCSS: `.top-vote-card {display: none !important;}`, enableFunc: () => { shadow.register( @@ -1678,7 +1678,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 活动/notice, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-reply-notice', - description: '隐藏 活动/notice ★', + description: '隐藏 活动/notice', defaultStatus: true, itemCSS: `.reply-header .reply-notice {display: none !important;}`, enableFunc: () => { @@ -1695,7 +1695,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 评论编辑器 new CheckboxItem({ itemID: 'video-page-hide-main-reply-box', - description: '隐藏 评论编辑器 ★', + description: '隐藏 评论编辑器', // 不可使用display: none, 会使底部吸附评论框宽度变化 itemCSS: `.main-reply-box {height: 0 !important; visibility: hidden !important;} .comment-container .reply-list {margin-top: -20px !important;}`, @@ -1714,7 +1714,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 评论编辑器内占位文字, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-reply-box-textarea-placeholder', - description: '隐藏 评论编辑器内占位文字 ★', + description: '隐藏 评论编辑器内占位文字', defaultStatus: true, itemCSS: `.main-reply-box .reply-box-textarea::placeholder {color: transparent !important; user-select: none;} .fixed-reply-box .reply-box-textarea::placeholder {color: transparent !important; user-select: none;}`, @@ -1732,7 +1732,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 页面底部 吸附评论框, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-fixed-reply-box', - description: '隐藏 页面底部 吸附评论框 ★', + description: '隐藏 页面底部 吸附评论框', defaultStatus: true, itemCSS: `.fixed-reply-box {display: none !important;}`, enableFunc: () => { @@ -1749,7 +1749,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 用户卡片 new CheckboxItem({ itemID: 'video-page-hide-comment-user-card', - description: '隐藏 用户卡片\n鼠标放在用户名上时不显示卡片 ★', + description: '隐藏 用户卡片\n鼠标放在用户名上时不显示卡片', itemCSS: `.user-card {display: none!important;}`, enableFunc: () => { shadow.register( @@ -1765,7 +1765,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 评论右侧装饰 new CheckboxItem({ itemID: 'video-page-hide-reply-decorate', - description: '隐藏 评论右侧装饰 ★', + description: '隐藏 评论右侧装饰', itemCSS: `.reply-decorate {display: none !important;}`, enableFunc: () => { shadow.register( @@ -1781,7 +1781,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 粉丝牌 new CheckboxItem({ itemID: 'video-page-hide-fan-badge', - description: '隐藏 粉丝牌 ★', + description: '隐藏 粉丝牌', itemCSS: `.fan-badge {display: none !important;}`, enableFunc: () => { shadow.register( @@ -1798,7 +1798,7 @@ if (isPageVideo() || isPagePlaylist()) { // 测试视频:https://www.bilibili.com/video/av479061422 new CheckboxItem({ itemID: 'video-page-hide-contractor-box', - description: '隐藏 老粉、原始粉丝Tag ★', + description: '隐藏 老粉、原始粉丝Tag', itemCSS: `.contractor-box {display: none !important;}`, enableFunc: () => { shadow.register( @@ -1814,7 +1814,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 一级评论用户等级 new CheckboxItem({ itemID: 'video-page-hide-user-level', - description: '隐藏 一级评论用户等级 ★', + description: '隐藏 一级评论用户等级', itemCSS: `.user-level {display: none !important;}`, enableFunc: () => { shadow.register( @@ -1837,7 +1837,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 用户头像饰品 new CheckboxItem({ itemID: 'video-page-hide-bili-avatar-pendent-dom', - description: '隐藏 用户头像饰品 ★', + description: '隐藏 用户头像饰品', itemCSS: `.root-reply-avatar .bili-avatar-pendent-dom {display: none !important;} .comment-container .root-reply-avatar .bili-avatar {width: 48px !important; height:48px !important;}`, enableFunc: () => { @@ -1854,7 +1854,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 用户头像徽章 new CheckboxItem({ itemID: 'video-page-hide-bili-avatar-nft-icon', - description: '隐藏 用户头像徽章 ★', + description: '隐藏 用户头像徽章', itemCSS: `.bili-avatar-nft-icon {display: none !important;} .comment-container .bili-avatar-icon {display: none !important;}`, enableFunc: () => { @@ -1872,7 +1872,7 @@ if (isPageVideo() || isPagePlaylist()) { // 测试视频:https://www.bilibili.com/video/av1805762267 new CheckboxItem({ itemID: 'video-page-hide-vote-info', - description: '隐藏 用户投票 (红方/蓝方) ★', + description: '隐藏 用户投票 (红方/蓝方)', itemCSS: `.vote-info {display: none !important;}`, enableFunc: () => { shadow.register( @@ -1888,7 +1888,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 评论内容下Tag(UP觉得很赞) new CheckboxItem({ itemID: 'video-page-hide-reply-tag-list', - description: '隐藏 评论内容下Tag(UP觉得很赞) ★', + description: '隐藏 评论内容下Tag(UP觉得很赞)', itemCSS: `.reply-tag-list {display: none !important;}`, enableFunc: () => { shadow.register( @@ -1904,7 +1904,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 笔记评论前的小Logo, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-note-prefix', - description: '隐藏 笔记评论前的小Logo ★', + description: '隐藏 笔记评论前的小Logo', defaultStatus: true, itemCSS: `.note-prefix {display: none !important;}`, enableFunc: () => { @@ -1922,7 +1922,7 @@ if (isPageVideo() || isPagePlaylist()) { // 测试视频:https://www.bilibili.com/video/av1406084552 new CheckboxItem({ itemID: 'video-page-hide-jump-link-search-word', - description: '禁用 评论内容搜索关键词高亮 ★', + description: '禁用 评论内容搜索关键词高亮', defaultStatus: true, itemCSS: `.reply-content .jump-link.search-word {color: inherit !important;} .comment-container .reply-content .jump-link.search-word:hover {color: #008AC5 !important;} @@ -1941,10 +1941,10 @@ if (isPageVideo() || isPagePlaylist()) { shadow.unregister('bili-rich-text', 'video-page-hide-jump-link-search-word') }, }), - // 禁用 二级评论中的@高亮 + // 禁用 评论中的@高亮 new CheckboxItem({ itemID: 'video-page-hide-reply-content-user-highlight', - description: '禁用 二级评论中的@高亮 ★', + description: '禁用 评论中的@高亮', itemCSS: `.sub-reply-container .reply-content .jump-link.user {color: inherit !important;} .comment-container .sub-reply-container .reply-content .jump-link.user:hover {color: #40C5F1 !important;}`, enableFunc: () => { @@ -1963,7 +1963,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 召唤AI机器人的评论, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-at-reply-at-bots', - description: '隐藏 召唤AI机器人的评论', + description: '隐藏 召唤AI机器人的评论 (旧版)', defaultStatus: true, itemCSS: // 8455326 @机器工具人 @@ -2012,7 +2012,7 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 AI机器人发布的评论 new CheckboxItem({ itemID: 'video-page-hide-bots-reply', - description: '隐藏 AI机器人发布的评论', + description: '隐藏 AI机器人发布的评论 (旧版)', defaultStatus: false, itemCSS: // 8455326 @机器工具人 @@ -2060,37 +2060,37 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 包含@的 无人点赞评论 new CheckboxItem({ itemID: 'video-page-hide-zero-like-at-reply', - description: '隐藏 包含@的 无人点赞评论', + description: '隐藏 包含@的 无人点赞评论 (旧版)', itemCSS: `.reply-item:has(.root-reply .jump-link.user):not(:has(.delete-reply, .top-icon, .sub-up-icon, .reply-info .reply-like span)) {display: none !important;}`, }), // 隐藏 包含@的 全部评论 new CheckboxItem({ itemID: 'video-page-hide-at-reply-all', - description: '隐藏 包含@的 全部评论', + description: '隐藏 包含@的 全部评论 (旧版)', itemCSS: `.reply-item:has(.root-reply .jump-link.user):not(:has(.delete-reply, .top-icon, .sub-up-icon)) {display: none !important;}`, }), // 隐藏 LV1 无人点赞评论 new CheckboxItem({ itemID: 'video-page-hide-zero-like-lv1-reply', - description: '隐藏 LV1 无人点赞评论', + description: '隐藏 LV1 无人点赞评论 (旧版)', itemCSS: `.reply-item:has(.user-level.level-1):not(:has(.delete-reply, .top-icon, .sub-up-icon, .reply-info .reply-like span)) {display: none !important;}`, }), // 隐藏 LV2 无人点赞评论 new CheckboxItem({ itemID: 'video-page-hide-zero-like-lv2-reply', - description: '隐藏 LV2 无人点赞评论', + description: '隐藏 LV2 无人点赞评论 (旧版)', itemCSS: `.reply-item:has(.user-level.level-2):not(:has(.delete-reply, .top-icon, .sub-up-icon, .reply-info .reply-like span)) {display: none !important;}`, }), // 隐藏 LV3 无人点赞评论 new CheckboxItem({ itemID: 'video-page-hide-zero-like-lv3-reply', - description: '隐藏 LV3 无人点赞评论', + description: '隐藏 LV3 无人点赞评论 (旧版)', itemCSS: `.reply-item:has(.user-level.level-3):not(:has(.delete-reply, .top-icon, .sub-up-icon, .reply-info .reply-like span)) {display: none !important;}`, }), // 一级评论 踩/回复 只在hover时显示, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-root-reply-dislike-reply-btn', - description: '一级评论 踩/回复 只在hover时显示 ★', + description: '一级评论 踩/回复 只在hover时显示', defaultStatus: true, itemCSS: ` .reply-item:not(:has(i.disliked)) :is(.reply-btn, .reply-dislike) { @@ -2139,7 +2139,7 @@ if (isPageVideo() || isPagePlaylist()) { // 二级评论 踩/回复 只在hover时显示, 默认开启 new CheckboxItem({ itemID: 'video-page-hide-sub-reply-dislike-reply-btn', - description: '二级评论 踩/回复 只在hover时显示 ★', + description: '二级评论 踩/回复 只在hover时显示', defaultStatus: true, itemCSS: ` .sub-reply-item:not(:has(i.disliked)) :is(.sub-reply-btn, .sub-reply-dislike) { @@ -2187,7 +2187,7 @@ if (isPageVideo() || isPagePlaylist()) { // 测试视频:https://www.bilibili.com/video/av1406211916 new CheckboxItem({ itemID: 'video-page-hide-emoji-large', - description: '隐藏 大表情 ★', + description: '隐藏 大表情', itemCSS: `.emoji-large {display: none !important;}`, enableFunc: () => { shadow.register( @@ -2204,7 +2204,7 @@ if (isPageVideo() || isPagePlaylist()) { // 测试视频:https://www.bilibili.com/video/av1406211916 new CheckboxItem({ itemID: 'video-page-hide-emoji-large-zoom', - description: '大表情变成小表情 ★', + description: '大表情变成小表情', itemCSS: `.emoji-large {zoom: 0.5;}`, enableFunc: () => { shadow.register( @@ -2220,7 +2220,7 @@ if (isPageVideo() || isPagePlaylist()) { // 用户名 全部大会员色 new CheckboxItem({ itemID: 'video-page-reply-user-name-color-pink', - description: '用户名 全部大会员色 ★', + description: '用户名 全部大会员色', itemCSS: `.reply-item .user-name, .comment-container .reply-item .sub-user-name {color: #FB7299 !important;}`, enableFunc: () => { shadow.register( @@ -2237,7 +2237,7 @@ if (isPageVideo() || isPagePlaylist()) { // 用户名 全部恢复默认色 new CheckboxItem({ itemID: 'video-page-reply-user-name-color-default', - description: '用户名 全部恢复默认色 ★', + description: '用户名 全部恢复默认色', itemCSS: `.reply-item .user-name, .comment-container .reply-item .sub-user-name {color: #61666d !important;}`, enableFunc: () => { shadow.register( @@ -2255,7 +2255,7 @@ if (isPageVideo() || isPagePlaylist()) { // 测试视频:https://www.bilibili.com/video/av34205337 new CheckboxItem({ itemID: 'video-page-reply-view-image-optimize', - description: '笔记图片 查看大图优化 ★', + description: '笔记图片 查看大图优化', defaultStatus: true, // 单图模式隐藏底部图片列表, 多图模式淡化列表, hover复原, 左右按钮增大 itemCSS: `.reply-view-image .last-image, .reply-view-image .next-image {zoom: 1.4;} @@ -2278,11 +2278,11 @@ if (isPageVideo() || isPagePlaylist()) { // 隐藏 整个评论区 #42 new CheckboxItem({ itemID: 'video-page-hide-comment', - description: '隐藏 整个评论区 ★', + description: '隐藏 整个评论区', itemCSS: `#comment, #comment-module {display: none;}`, }), ] - videoGroupList.push(new Group('video-comment', '评论区 (标★已适配新版评论区)', commentItems)) + videoGroupList.push(new Group('video-comment', '评论区', commentItems)) // 右下角 const sidebarItems = [ From 085f826fd30e7d19a0e08a07eb1cbd997020c86c Mon Sep 17 00:00:00 2001 From: festoney8 Date: Sat, 20 Jul 2024 22:00:23 +0800 Subject: [PATCH 3/6] feat: live page auto best quality #108 --- src/components/item.ts | 8 ++++++-- src/global.d.ts | 22 ++++++++++++++++++++++ src/rules/live.ts | 41 ++++++++++++++++++++++++++++++++++++++--- src/utils/pageType.ts | 2 +- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/components/item.ts b/src/components/item.ts index 927e5ca9..c4b30014 100644 --- a/src/components/item.ts +++ b/src/components/item.ts @@ -156,14 +156,18 @@ export class CheckboxItem implements IItem { if (['complete', 'interactive'].includes(document.readyState)) { this.option.enableFunc()?.then().catch() } else { - document.addEventListener('DOMContentLoaded', this.option.enableFunc) + document.addEventListener('DOMContentLoaded', () => { + this.option.enableFunc && this.option.enableFunc()?.then().catch() + }) } break case 'document-idle': if (document.readyState === 'complete') { this.option.enableFunc()?.then().catch() } else { - document.addEventListener('load', this.option.enableFunc) + window.addEventListener('load', () => { + this.option.enableFunc && this.option.enableFunc()?.then().catch() + }) } break default: diff --git a/src/global.d.ts b/src/global.d.ts index 13b1dbbe..9ef1a51c 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -22,5 +22,27 @@ export declare global { comment_next_version?: 'ELEMENTS' | 'DEFAULT' } } + EmbedPlayer?: { + instance?: { + getPlayerInfo: () => { + quality?: string + qualityCandidates?: { + qn?: string + desc?: string + }[] + } + switchQuality?: function + } + } + livePlayer?: { + getPlayerInfo: () => { + quality?: string + qualityCandidates?: { + qn?: string + desc?: string + }[] + } + switchQuality?: function + } } } diff --git a/src/rules/live.ts b/src/rules/live.ts index 35e1475b..7acf84fa 100644 --- a/src/rules/live.ts +++ b/src/rules/live.ts @@ -1,6 +1,7 @@ +import { unsafeWindow } from '$' import { Group } from '../components/group' import { CheckboxItem } from '../components/item' -import { debugRules as debug } from '../utils/logger' +import { debugRules as debug, error } from '../utils/logger' import { isPageLiveHome, isPageLiveRoom } from '../utils/pageType' import fontFaceRegular from './styles/fontFaceRegular.scss?inline' @@ -159,7 +160,7 @@ if (isPageLiveRoom()) { enableFunc: async () => { let cnt = 0 const id = setInterval(() => { - if (document.querySelector('.rendererRoot, #internationalHeader')) { + if (document.querySelector('.rendererRoot, #main.live-activity-full-main, #internationalHeader')) { if (!location.href.includes('/blanc/')) { window.location.href = location.href.replace( 'live.bilibili.com/', @@ -174,6 +175,40 @@ if (isPageLiveRoom()) { }, enableFuncRunAt: 'document-end', }), + // 自动切换最高画质 + new CheckboxItem({ + itemID: 'auto-best-quality', + description: '自动切换最高画质 (实验功能)', + enableFunc: async () => { + const qualityFn = () => { + const player = unsafeWindow.EmbedPlayer?.instance || unsafeWindow.livePlayer + if (player) { + try { + const info = player?.getPlayerInfo() + const arr = player?.getPlayerInfo().qualityCandidates + if (info && arr && arr.length) { + let maxQn = 0 + arr.forEach((v) => { + if (v.qn && parseInt(v.qn) > maxQn) { + maxQn = parseInt(v.qn) + } + }) + if (maxQn && info.quality && maxQn > parseInt(info.quality)) { + player.switchQuality(`${maxQn}`) + } + } + } catch (err) { + error('auto-best-quality error', err) + } + } + } + setTimeout(qualityFn, 2000) + setTimeout(qualityFn, 4000) + setTimeout(qualityFn, 6000) + setTimeout(qualityFn, 8000) + }, + enableFuncRunAt: 'document-idle', + }), ] liveGroupList.push(new Group('live-basic', '直播页 基本功能', basicItems)) @@ -283,7 +318,7 @@ if (isPageLiveRoom()) { new CheckboxItem({ itemID: 'live-page-head-web-player-awesome-pk-vm', description: '隐藏 直播PK特效', - itemCSS: `#pk-vm, #awesome-pk-vm {display: none !important;}`, + itemCSS: `#pk-vm, #awesome-pk-vm, #universal-pk-vm {display: none !important;}`, }), // 隐藏 滚动礼物通告 new CheckboxItem({ diff --git a/src/utils/pageType.ts b/src/utils/pageType.ts index 5ad657eb..69472419 100644 --- a/src/utils/pageType.ts +++ b/src/utils/pageType.ts @@ -32,7 +32,7 @@ const currPage = (): string => { } if (host === 'live.bilibili.com') { // 匹配blanc页(赛事直播or活动直播用),用于对iframe内直播生效 - if (pathname.match(/^\/(?:blanc\/)?\d+/)) { + if (pathname.match(/^\/(?:blanc\/)?\d+(#\/)?/)) { return 'liveRoom' } // 匹配各种直播页iframe、直播活动, 不做处理 From 486171b1b30b3a294ff47707bb6aa9b61f4222ad Mon Sep 17 00:00:00 2001 From: festoney8 Date: Sun, 21 Jul 2024 01:02:00 +0800 Subject: [PATCH 4/6] feat: comment filter support user level --- CHANGELOG.md | 1 + src/filters/commentFilter/agency/agency.ts | 16 +++ src/filters/commentFilter/filters/core.ts | 19 ++- .../commentFilter/filters/subfilters/level.ts | 32 +++++ .../commentFilter/pages/actions/action.ts | 40 +++++++ .../commentFilter/pages/dynamicAndSpace.ts | 67 ++++++++++- src/filters/commentFilter/pages/space.ts | 0 src/filters/commentFilter/pages/video.ts | 111 +++++++++++++++++- 8 files changed, 278 insertions(+), 8 deletions(-) create mode 100644 src/filters/commentFilter/filters/subfilters/level.ts delete mode 100644 src/filters/commentFilter/pages/space.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ccec1d8..86753a9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - 优化:播放页 弹窗净化 - 新增:播放页 强制隐藏播放器内所有弹窗 - 新增:播放页 评论过滤 支持过滤新版评论区AI相关、@相关评论 +- 新增:评论过滤器 按用户等级过滤 ## 3.9.1 diff --git a/src/filters/commentFilter/agency/agency.ts b/src/filters/commentFilter/agency/agency.ts index 33a54e4f..04a55be5 100644 --- a/src/filters/commentFilter/agency/agency.ts +++ b/src/filters/commentFilter/agency/agency.ts @@ -2,6 +2,7 @@ import botFilterInstance from '../filters/subfilters/bot' import callBotFilterInstance from '../filters/subfilters/callBot' import callUserFilterInstance from '../filters/subfilters/callUser' import contentFilterInstance from '../filters/subfilters/content' +import levelFilterInstance from '../filters/subfilters/level' import usernameFilterInstance from '../filters/subfilters/username' // 代理, 接收页面操作通知, 更新子过滤器的参数 @@ -78,6 +79,21 @@ class CommentFilterAgency { break } } + notifyLevel(event: string, value?: number) { + switch (event) { + case 'disable': + levelFilterInstance.setStatus(false) + break + case 'enable': + levelFilterInstance.setStatus(true) + break + case 'change': + if (typeof value === 'number') { + levelFilterInstance.setParams(value) + } + break + } + } } // 单例 diff --git a/src/filters/commentFilter/filters/core.ts b/src/filters/commentFilter/filters/core.ts index cefa20a7..bb0c95d6 100644 --- a/src/filters/commentFilter/filters/core.ts +++ b/src/filters/commentFilter/filters/core.ts @@ -5,26 +5,29 @@ import botFilterInstance from './subfilters/bot' import callBotFilterInstance from './subfilters/callBot' import callUserFilterInstance from './subfilters/callUser' import contentFilterInstance from './subfilters/content' +import levelFilterInstance from './subfilters/level' import usernameFilterInstance from './subfilters/username' export interface ICommentSubFilter { isEnable: boolean setStatus(status: boolean): void - setParams?(value: string[]): void + setParams?(value: string[] | number): void addParam?(value: string): void - check(value: string): Promise + check(value: string | number): Promise } export type CommentSelectorFunc = { username?: (comment: HTMLElement) => string | null content?: (comment: HTMLElement) => string | null callUser?: (comment: HTMLElement) => string | null + level?: (comment: HTMLElement) => number | null } interface CommentInfo { username?: string | undefined content?: string | undefined callUser?: string | undefined + level?: number | undefined } class CoreCommentFilter { @@ -42,8 +45,9 @@ class CoreCommentFilter { const checkBot = botFilterInstance.isEnable && selectorFunc.username !== undefined const checkCallBot = callBotFilterInstance.isEnable && selectorFunc.callUser !== undefined const checkCallUser = callUserFilterInstance.isEnable && selectorFunc.callUser !== undefined + const checkLevel = levelFilterInstance.isEnable && selectorFunc.level !== undefined - if (!checkContent && !checkUsername && !checkBot && !checkCallBot && !checkCallUser) { + if (!checkContent && !checkUsername && !checkBot && !checkCallBot && !checkCallUser && !checkLevel) { // 黑名单全部关闭时 恢复全部评论 comments.forEach((comment) => showEle(comment)) return @@ -90,6 +94,15 @@ class CoreCommentFilter { info.callUser = callUser } } + if (checkLevel) { + const level = selectorFunc.level!(comment) + if (level) { + blackTasks.push(levelFilterInstance.check(level)) + info.level = level + } + } + + // debug(info) // 执行检测 Promise.all(blackTasks) diff --git a/src/filters/commentFilter/filters/subfilters/level.ts b/src/filters/commentFilter/filters/subfilters/level.ts new file mode 100644 index 00000000..2d7a85e6 --- /dev/null +++ b/src/filters/commentFilter/filters/subfilters/level.ts @@ -0,0 +1,32 @@ +import { ICommentSubFilter } from '../core' + +class LevelFilter implements ICommentSubFilter { + private threshold = 0 + isEnable = false + + setStatus(status: boolean) { + this.isEnable = status + } + + setParams(threshold: number) { + this.threshold = threshold + } + + check(level: number): Promise { + return new Promise((resolve, reject) => { + if (!this.isEnable || this.threshold === 0) { + resolve(`Level resolve, disable or 0`) + } else { + if (level >= this.threshold) { + resolve(`Level OK`) + } else { + reject(`Level reject`) + } + } + }) + } +} + +// 单例 +const levelFilterInstance = new LevelFilter() +export default levelFilterInstance diff --git a/src/filters/commentFilter/pages/actions/action.ts b/src/filters/commentFilter/pages/actions/action.ts index ef196aaf..1a5b5782 100644 --- a/src/filters/commentFilter/pages/actions/action.ts +++ b/src/filters/commentFilter/pages/actions/action.ts @@ -2,6 +2,7 @@ import { GM_getValue } from '$' import { WordList } from '../../../../components/wordlist' import commentFilterAgencyInstance from '../../agency/agency' import contentFilterInstance from '../../filters/subfilters/content' +import levelFilterInstance from '../../filters/subfilters/level' import usernameFilterInstance from '../../filters/subfilters/username' // 定义各种黑名单功能、白名单功能的属性和行为 @@ -229,3 +230,42 @@ export class CallUserAction implements CommentFilterAction { this.status = false } } + +export class LevelAction implements CommentFilterAction { + statusKey: string + valueKey: string + checkCommentList: (fullSite: boolean) => void + status: boolean + value: number + + /** + * 等级过滤操作 + * @param statusKey 是否启用的GM key + * @param valueKey 存储数据的GM key + * @param checkCommentList 检测评论列表函数 + */ + constructor(statusKey: string, valueKey: string, checkCommentList: (fullSite: boolean) => void) { + this.statusKey = statusKey + this.valueKey = valueKey + this.checkCommentList = checkCommentList + this.status = GM_getValue(`BILICLEANER_${this.statusKey}`, false) + this.value = GM_getValue(`BILICLEANER_${this.valueKey}`, 60) + // 配置子过滤器 + levelFilterInstance.setStatus(this.status) + levelFilterInstance.setParams(this.value) + } + enable() { + commentFilterAgencyInstance.notifyLevel('enable') + this.checkCommentList(true) + this.status = true + } + disable() { + commentFilterAgencyInstance.notifyLevel('disable') + this.checkCommentList(true) + this.status = false + } + change(value: number) { + commentFilterAgencyInstance.notifyLevel('change', value) + this.checkCommentList(true) + } +} diff --git a/src/filters/commentFilter/pages/dynamicAndSpace.ts b/src/filters/commentFilter/pages/dynamicAndSpace.ts index 76d553a3..64be5004 100644 --- a/src/filters/commentFilter/pages/dynamicAndSpace.ts +++ b/src/filters/commentFilter/pages/dynamicAndSpace.ts @@ -1,9 +1,9 @@ import { Group } from '../../../components/group' -import { CheckboxItem, ButtonItem } from '../../../components/item' +import { CheckboxItem, ButtonItem, NumberItem } from '../../../components/item' import { debugCommentFilter as debug, error } from '../../../utils/logger' import { isPageDynamic, isPageSpace } from '../../../utils/pageType' import { showEle, waitForEle } from '../../../utils/tool' -import { ContentAction, UsernameAction } from './actions/action' +import { ContentAction, LevelAction, UsernameAction } from './actions/action' import coreCommentFilterInstance, { CommentSelectorFunc } from '../filters/core' import settings from '../../../settings' import { ContextMenu } from '../../../components/contextmenu' @@ -25,6 +25,15 @@ let isLinkCommentWhitelistEnable = false if (isPageDynamic() || isPageSpace()) { let commentListContainer: HTMLElement // 一级评论 + const lvMap = new Map([ + ['level-h', 6.5], + ['level-6', 6], + ['level-5', 5], + ['level-4', 4], + ['level-3', 3], + ['level-2', 2], + ['level-1', 1], + ]) const rootCommentSelectorFunc: CommentSelectorFunc = { username: (comment: Element): string | null => { const username = comment.querySelector('.root-reply-container .user-name')?.textContent?.trim() @@ -43,6 +52,15 @@ if (isPageDynamic() || isPageSpace()) { } return content ? content : null }, + level: (comment: HTMLElement): number | null => { + const c = comment.querySelector('.root-reply-container .user-level')?.className + const matchLv = c && c.match(/level-([1-6]|h)/) + if (matchLv && matchLv.length) { + const lv = lvMap.get(matchLv[0]) + return lv ? lv : null + } + return null + }, } // 二级评论 const subCommentSelectorFunc: CommentSelectorFunc = { @@ -64,6 +82,15 @@ if (isPageDynamic() || isPageSpace()) { } return content ? content : null }, + level: (comment: HTMLElement): number | null => { + const c = comment.querySelector('.sub-user-info .sub-user-level')?.className + const matchLv = c && c.match(/level-([1-6]|h)/) + if (matchLv && matchLv.length) { + const lv = lvMap.get(matchLv[0]) + return lv ? lv : null + } + return null + }, } // 检测评论列表 const checkCommentList = (fullSite: boolean) => { @@ -140,6 +167,11 @@ if (isPageDynamic() || isPageSpace()) { 'global-comment-content-filter-value', checkCommentList, ) + const levelAction = new LevelAction( + 'dynamic-comment-level-filter-status', + 'global-comment-level-filter-value', + checkCommentList, + ) // 监听评论列表内部变化, 有变化时检测评论列表 const watchCommentListContainer = () => { @@ -276,6 +308,37 @@ if (isPageDynamic() || isPageSpace()) { ] dynamicPageCommentFilterGroupList.push(new Group('comment-content-filter-group', '评论区 关键词过滤', contentItems)) + // UI组件, 等级过滤part + const levelItems = [ + // 启用 播放页等级过滤 + new CheckboxItem({ + itemID: levelAction.statusKey, + description: '启用 用户等级过滤', + enableFunc: async () => { + levelAction.enable() + }, + disableFunc: async () => { + levelAction.disable() + }, + }), + // 设定最低等级 + new NumberItem({ + itemID: levelAction.valueKey, + description: '设定最低等级 (0~6)', + defaultValue: 3, + minValue: 0, + maxValue: 6, + disableValue: 0, + unit: '', + callback: async (value: number) => { + levelAction.change(value) + }, + }), + ] + dynamicPageCommentFilterGroupList.push( + new Group('comment-level-filter-whitelist-group', '评论区 等级过滤', levelItems), + ) + // UI组件, 白名单part const whitelistItems = [ // 一级评论 免过滤 diff --git a/src/filters/commentFilter/pages/space.ts b/src/filters/commentFilter/pages/space.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/filters/commentFilter/pages/video.ts b/src/filters/commentFilter/pages/video.ts index 0723baf8..d066d160 100644 --- a/src/filters/commentFilter/pages/video.ts +++ b/src/filters/commentFilter/pages/video.ts @@ -1,9 +1,9 @@ import { Group } from '../../../components/group' -import { CheckboxItem, ButtonItem } from '../../../components/item' +import { CheckboxItem, ButtonItem, NumberItem } from '../../../components/item' import { debugCommentFilter as debug, error } from '../../../utils/logger' import { isPageBangumi, isPagePlaylist, isPageVideo } from '../../../utils/pageType' import { showEle } from '../../../utils/tool' -import { BotAction, CallBotAction, CallUserAction, ContentAction, UsernameAction } from './actions/action' +import { BotAction, CallBotAction, CallUserAction, ContentAction, LevelAction, UsernameAction } from './actions/action' import coreCommentFilterInstance, { CommentSelectorFunc } from '../filters/core' import settings from '../../../settings' import { ContextMenu } from '../../../components/contextmenu' @@ -33,6 +33,23 @@ let isNoteCommentWhitelistEnable = false let isLinkCommentWhitelistEnable = false if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { + const lvMap = new Map([ + ['level_h.svg', 6.5], + ['level_6.svg', 6], + ['level_5.svg', 5], + ['level_4.svg', 4], + ['level_3.svg', 3], + ['level_2.svg', 2], + ['level_1.svg', 1], + + ['level-h', 6.5], + ['level-6', 6], + ['level-5', 5], + ['level-4', 4], + ['level-3', 3], + ['level-2', 2], + ['level-1', 1], + ]) // 一级评论 const rootCommentSelectorFunc: CommentSelectorFunc = { username: (comment: HTMLElement): string | null => { @@ -82,6 +99,37 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { } return null }, + level: (comment: HTMLElement): number | null => { + if (isCommentV2()) { + const url = comment.shadowRoot + ?.querySelector('bili-comment-renderer') + ?.shadowRoot?.querySelector('bili-comment-user-info') + ?.shadowRoot?.querySelector('#user-level img') + ?.getAttribute('src') + if (url) { + const lvMap = new Map([ + ['level_h.svg', 6.5], + ['level_6.svg', 6], + ['level_5.svg', 5], + ['level_4.svg', 4], + ['level_3.svg', 3], + ['level_2.svg', 2], + ['level_1.svg', 1], + ]) + const arr = url.split('/') + const lv = lvMap.get(arr[arr.length - 1]) + return lv ? lv : null + } + return null + } + const c = comment.querySelector('.root-reply-container .user-level')?.className + const matchLv = c && c.match(/level-([1-6]|h)/) + if (matchLv && matchLv.length) { + const lv = lvMap.get(matchLv[0]) + return lv ? lv : null + } + return null + }, } // 二级评论 const subCommentSelectorFunc: CommentSelectorFunc = { @@ -145,6 +193,27 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { return null }, + level: (comment: HTMLElement): number | null => { + if (isCommentV2()) { + const url = comment.shadowRoot + ?.querySelector('bili-comment-user-info') + ?.shadowRoot?.querySelector('#user-level img') + ?.getAttribute('src') + if (url) { + const arr = url.split('/') + const lv = lvMap.get(arr[arr.length - 1]) + return lv ? lv : null + } + return null + } + const c = comment.querySelector('.sub-user-info .sub-user-level')?.className + const matchLv = c && c.match(/level-([1-6]|h)/) + if (matchLv && matchLv.length) { + const lv = lvMap.get(matchLv[0]) + return lv ? lv : null + } + return null + }, } // 检测评论列表 @@ -199,7 +268,7 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { root ?.querySelector('bili-rich-text') ?.shadowRoot?.querySelector( - `a:not([href*="space.bilibili.com"]):not([data-video-time])`, + `a:not([href*="space.bilibili.com"], [href*="search.bilibili.com"], [data-video-time])`, )) if (isWhite) { showEle(e) @@ -340,6 +409,11 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { const botAction = new BotAction('video-comment-bot-filter-status', checkCommentList) const callBotAction = new CallBotAction('video-comment-call-bot-filter-status', checkCommentList) const callUserAction = new CallUserAction('video-comment-call-user-filter-status', checkCommentList) + const levelAction = new LevelAction( + 'video-comment-level-filter-status', + 'global-comment-level-filter-value', + checkCommentList, + ) //======================================================================================= @@ -479,6 +553,37 @@ if (isPageVideo() || isPageBangumi() || isPagePlaylist()) { ] videoPageCommentFilterGroupList.push(new Group('comment-content-filter-group', '评论区 关键词过滤', contentItems)) + // UI组件, 等级过滤part + const levelItems = [ + // 启用 播放页等级过滤 + new CheckboxItem({ + itemID: levelAction.statusKey, + description: '启用 用户等级过滤', + enableFunc: async () => { + levelAction.enable() + }, + disableFunc: async () => { + levelAction.disable() + }, + }), + // 设定最低等级 + new NumberItem({ + itemID: levelAction.valueKey, + description: '设定最低等级 (0~6)', + defaultValue: 3, + minValue: 0, + maxValue: 6, + disableValue: 0, + unit: '', + callback: async (value: number) => { + levelAction.change(value) + }, + }), + ] + videoPageCommentFilterGroupList.push( + new Group('comment-level-filter-whitelist-group', '评论区 等级过滤', levelItems), + ) + // UI组件, 白名单part const whitelistItems = [ // 一级评论 免过滤 From 3cb0bcfc384083402d0b320baa4ee505ee07a52b Mon Sep 17 00:00:00 2001 From: festoney8 Date: Sun, 21 Jul 2024 01:03:17 +0800 Subject: [PATCH 5/6] feat: comment filter support user level #108 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86f2cd34..25329a53 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ - **视频过滤**:根据视频时长、UP 主黑白名单、标题关键词黑白名单、BV 号筛选视频推荐 -- **评论过滤**:根据用户名、评论内容关键词筛选评论 +- **评论过滤**:根据用户名、关键词、评论类型、用户等级筛选评论 ![](images/preview.jpg) From d4a08461675fb198206674520e82d53cb61dc7ea Mon Sep 17 00:00:00 2001 From: festoney8 Date: Sun, 21 Jul 2024 15:47:09 +0800 Subject: [PATCH 6/6] fix: level filter default value bug --- src/filters/commentFilter/pages/actions/action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filters/commentFilter/pages/actions/action.ts b/src/filters/commentFilter/pages/actions/action.ts index 1a5b5782..c7ff95df 100644 --- a/src/filters/commentFilter/pages/actions/action.ts +++ b/src/filters/commentFilter/pages/actions/action.ts @@ -249,7 +249,7 @@ export class LevelAction implements CommentFilterAction { this.valueKey = valueKey this.checkCommentList = checkCommentList this.status = GM_getValue(`BILICLEANER_${this.statusKey}`, false) - this.value = GM_getValue(`BILICLEANER_${this.valueKey}`, 60) + this.value = GM_getValue(`BILICLEANER_${this.valueKey}`, 3) // 配置子过滤器 levelFilterInstance.setStatus(this.status) levelFilterInstance.setParams(this.value)