Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge dev to main, v3.8.0 #100

Merged
merged 10 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# CHANGELOG

## 3.8.0

- 新增:动态过滤器
- 新增:动态过滤 用户名过滤
- 新增:动态过滤 标题过滤
- 新增:动态过滤 时长过滤
- 新增:直播页 折叠排行榜/大航海
- 新增:直播页 禁用壁纸
- 优化:评论区 踩/回复只在hover时显示,增加延迟减少跳变
- 优化:顶栏 隐藏活动,搜索框样式

## 3.7.4

- 修复:动态页 隐藏动态右侧饰品
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
> - **页面净化:「首页、播放页、影视番剧播放页、直播间、搜索页、动态页、热门页、频道页」**
> - **视频过滤:「首页、播放页、搜索页、热门页、频道页、空间页」**
> - **评论过滤:「播放页、影视番剧播放页、动态页」**
> - **动态过滤:「动态页」**

![](images/usage.png)

Expand Down
21 changes: 0 additions & 21 deletions src/components/wordlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ export class WordList {
GM_setValue(`BILICLEANER_${this.listID}`, this.wordArr)
}
private getValue() {
debug(`key`, `BILICLEANER_${this.listID}`)
this.wordArr = GM_getValue(`BILICLEANER_${this.listID}`, [])
debug(`list ${this.listID} getValue ${this.wordArr.length} lines`)
this.wordSet = new Set(this.wordArr)
}

Expand All @@ -60,25 +58,6 @@ export class WordList {
}
}

// /** 添加多个值到列表 */
// addValues(values: string[]) {
// try {
// this.getValue()
// values.forEach((value) => {
// value = value.trim()
// if (value && !this.wordSet.has(value)) {
// this.wordArr.push(value)
// this.wordSet.add(value)
// }
// })
// this.setValue()
// debug(`list ${this.listID} add ${values.length} lines, OK`)
// } catch (err) {
// error(err)
// error(`list ${this.listID} add ${values.length} lines, ERROR`)
// }
// }

/**
* 编辑整个列表
* @param values 编辑框内输入的列表
Expand Down
2 changes: 1 addition & 1 deletion src/filters/commentFilter/filters/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class CoreCommentFilter {
.catch((_result) => {
// 命中黑名单
// debug(_result)
if (whiteTasks) {
if (whiteTasks.length) {
Promise.all(whiteTasks)
.then((_result) => {
// 命中黑名单,未命中白名单
Expand Down
18 changes: 7 additions & 11 deletions src/filters/commentFilter/pages/dynamic.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { GM_getValue } from '$'
import { Group } from '../../../components/group'
import { CheckboxItem, ButtonItem } from '../../../components/item'
import { debugCommentFilter as debug, error } from '../../../utils/logger'
import { isPageDynamic } from '../../../utils/page-type'
import { isPageDynamic } from '../../../utils/pageType'
import { showEle, waitForEle } from '../../../utils/tool'
import { ContentAction, UsernameAction } from './actions/action'
import coreCommentFilterInstance, { CommentSelectorFunc } from '../filters/core'
Expand All @@ -16,15 +15,12 @@ let isContextMenuFuncRunning = false
let isContextMenuUsernameEnable = false

// 白名单功能开关
let isRootCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_dynamic-comment-root-whitelist-status', false)
let isSubCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_dynamic-comment-sub-whitelist-status', false)
let isUploaderCommentWhitelistEnable: boolean = GM_getValue(
'BILICLEANER_dynamic-comment-uploader-whitelist-status',
true,
)
let isPinnedCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_dynamic-comment-pinned-whitelist-status', true)
let isNoteCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_dynamic-comment-note-whitelist-status', true)
let isLinkCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_dynamic-comment-link-whitelist-status', true)
let isRootCommentWhitelistEnable = false
let isSubCommentWhitelistEnable = false
let isUploaderCommentWhitelistEnable = true
let isPinnedCommentWhitelistEnable = true
let isNoteCommentWhitelistEnable = true
let isLinkCommentWhitelistEnable = true

if (isPageDynamic()) {
let commentListContainer: HTMLElement
Expand Down
15 changes: 7 additions & 8 deletions src/filters/commentFilter/pages/video.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { GM_getValue } from '$'
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/page-type'
import { isPageBangumi, isPagePlaylist, isPageVideo } from '../../../utils/pageType'
import { showEle, waitForEle } from '../../../utils/tool'
import { ContentAction, UsernameAction } from './actions/action'
import coreCommentFilterInstance, { CommentSelectorFunc } from '../filters/core'
Expand All @@ -16,12 +15,12 @@ let isContextMenuFuncRunning = false
let isContextMenuUsernameEnable = false

// 白名单功能开关
let isRootCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_video-comment-root-whitelist-status', false)
let isSubCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_video-comment-sub-whitelist-status', false)
let isUploaderCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_video-comment-uploader-whitelist-status', true)
let isPinnedCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_video-comment-pinned-whitelist-status', true)
let isNoteCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_video-comment-note-whitelist-status', true)
let isLinkCommentWhitelistEnable: boolean = GM_getValue('BILICLEANER_video-comment-link-whitelist-status', true)
let isRootCommentWhitelistEnable = false
let isSubCommentWhitelistEnable = false
let isUploaderCommentWhitelistEnable = true
let isPinnedCommentWhitelistEnable = true
let isNoteCommentWhitelistEnable = true
let isLinkCommentWhitelistEnable = true

if (isPageVideo() || isPageBangumi() || isPagePlaylist()) {
let commentListContainer: HTMLElement
Expand Down
70 changes: 70 additions & 0 deletions src/filters/dynFilter/agency/agency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import dynDurationFilterInstance from '../filters/subfilters/dynDuration'
import dynTitleFilterInstance from '../filters/subfilters/dynTitle'
import dynUploaderFilterInstance from '../filters/subfilters/dynUploader'

// 代理, 接收页面操作通知, 更新子过滤器的参数
class DynUploaderFilterAgency {
notifyDynUploader(event: string, value?: string | string[]) {
switch (event) {
case 'disable':
dynUploaderFilterInstance.setStatus(false)
break
case 'enable':
dynUploaderFilterInstance.setStatus(true)
break
case 'add':
if (typeof value === 'string') {
if (value.trim()) {
dynUploaderFilterInstance.addParam(value.trim())
}
}
break
case 'edit':
if (Array.isArray(value)) {
dynUploaderFilterInstance.setParams(value.map((v) => v.trim()).filter((v) => v))
}
break
}
}
notifyDynTitle(event: string, value?: string | string[]) {
switch (event) {
case 'disable':
dynTitleFilterInstance.setStatus(false)
break
case 'enable':
dynTitleFilterInstance.setStatus(true)
break
case 'add':
if (typeof value === 'string') {
if (value.trim()) {
dynTitleFilterInstance.addParam(value.trim())
}
}
break
case 'edit':
if (Array.isArray(value)) {
dynTitleFilterInstance.setParams(value.map((v) => v.trim()).filter((v) => v))
}
break
}
}
notifyDynDuration(event: string, value?: number) {
switch (event) {
case 'disable':
dynDurationFilterInstance.setStatus(false)
break
case 'enable':
dynDurationFilterInstance.setStatus(true)
break
case 'change':
if (typeof value === 'number') {
dynDurationFilterInstance.setParams(value)
}
break
}
}
}

// 单例
const dynUploaderFilterAgencyInstance = new DynUploaderFilterAgency()
export default dynUploaderFilterAgencyInstance
129 changes: 129 additions & 0 deletions src/filters/dynFilter/filters/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import settings from '../../../settings'
import { debugDynFilter as debug, error, log } from '../../../utils/logger'
import { hideEle, isEleHide, showEle } from '../../../utils/tool'
import dynDurationFilterInstance from './subfilters/dynDuration'
import dynTitleFilterInstance from './subfilters/dynTitle'
import dynUploaderFilterInstance from './subfilters/dynUploader'

export interface IDynSubFilter {
isEnable: boolean
setStatus(status: boolean): void
setParams?(value: string[] | number): void
addParam?(value: string): void
check(value: string): Promise<string>
}

export type DynSelectorFunc = {
dynUploader?: (dyn: HTMLElement) => string | null
dynDuration?: (dyn: HTMLElement) => string | null
dynTitle?: (dyn: HTMLElement) => string | null
}

interface DynInfo {
dynUploader?: string | undefined
dynDuration?: string | undefined
dynTitle?: string | undefined
}

class CoreDynFilter {
/**
* 对动态内容进行并发检测
* @param dyns 动态列表
* @param sign 是否标记已过滤项
* @param selectorFunc 使用selector选取元素的函数
*/
checkAll(dyns: HTMLElement[], sign = true, selectorFunc: DynSelectorFunc) {
try {
const checkDynUploader = dynUploaderFilterInstance.isEnable && selectorFunc.dynUploader !== undefined
const checkDynDuration = dynDurationFilterInstance.isEnable && selectorFunc.dynDuration !== undefined
const checkDynTitle = dynTitleFilterInstance.isEnable && selectorFunc.dynTitle !== undefined

if (!checkDynUploader && !checkDynDuration && !checkDynTitle) {
// 黑名单全部关闭时 恢复全部动态
dyns.forEach((dyn) => showEle(dyn))
return
}

dyns.forEach((dyn) => {
const info: DynInfo = {}

// 构建黑白名单任务, 调用各个子过滤器的check()方法检测
const blackTasks: Promise<string>[] = []
const whiteTasks: Promise<string>[] = []

if (checkDynUploader) {
const dynUploader = selectorFunc.dynUploader!(dyn)
if (dynUploader) {
blackTasks.push(dynUploaderFilterInstance.check(dynUploader))
info.dynUploader = dynUploader
}
}
if (checkDynDuration) {
const dynDuration = selectorFunc.dynDuration!(dyn)
if (dynDuration) {
blackTasks.push(dynDurationFilterInstance.check(dynDuration))
info.dynDuration = dynDuration
}
}
if (checkDynTitle) {
const dynTitle = selectorFunc.dynTitle!(dyn)
if (dynTitle) {
blackTasks.push(dynTitleFilterInstance.check(dynTitle))
info.dynTitle = dynTitle
}
}

// 执行检测
Promise.all(blackTasks)
.then((_result) => {
// 未命中黑名单
// debug(_result)
showEle(dyn)
Promise.all(whiteTasks)
.then((_result) => {})
.catch((_result) => {})
})
.catch((_result) => {
// 命中黑名单
debug(_result)
if (whiteTasks.length) {
Promise.all(whiteTasks)
.then((_result) => {
// 命中黑名单,未命中白名单
// debug(_result)
if (!isEleHide(dyn)) {
log(`hide dyn
dynUploader: ${info.dynUploader}
dynDuration: ${info.dynDuration}
dynTitle: ${info.dynTitle}`)
}
hideEle(dyn)
})
.catch((_result) => {
// 命中白名单
// debug(_result)
showEle(dyn)
})
} else {
if (!isEleHide(dyn)) {
log(`hide dyn
dynUploader: ${info.dynUploader}
dynDuration: ${info.dynDuration}
dynTitle: ${info.dynTitle}`)
}
hideEle(dyn)
}
})

// 标记已过滤动态
sign && dyn.setAttribute(settings.filterSign, '')
})
} catch (err) {
error(err)
error('CoreDynFilter checkAll error')
}
}
}

const coreDynFilterInstance = new CoreDynFilter()
export default coreDynFilterInstance
51 changes: 51 additions & 0 deletions src/filters/dynFilter/filters/subfilters/dynDuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { IDynSubFilter } from '../core'

class DynDurationFilter implements IDynSubFilter {
// 时长阈值, 单位秒
private threshold = 0
isEnable = false

setStatus(status: boolean) {
this.isEnable = status
}

setParams(threshold: number) {
this.threshold = threshold
}

// dynDuration转换为秒数, 支持 HH:MM:SS, MM:SS, 纯数字
dynDurationToSec = (dynDuration: string): number => {
dynDuration = dynDuration.trim()
if (dynDuration.match(/^(?:\d+:)?\d+:\d+$/)) {
const parts = dynDuration.split(':').map((part) => parseInt(part))
if (parts.length === 3) {
return parts[0] * 3600 + parts[1] * 60 + parts[2]
}
if (parts.length === 2) {
return parts[0] * 60 + parts[1]
}
} else if (dynDuration.match(/^\d+$/)) {
return parseInt(dynDuration)
}
return -1
}

check(dynDuration: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (!this.isEnable || this.threshold === 0) {
resolve(`DynDuration resolve, disable or 0`)
} else {
const seconds = this.dynDurationToSec(dynDuration)
if (seconds > 0 && seconds > this.threshold) {
resolve(`DynDuration OK`)
} else {
reject(`DynDuration too short`)
}
}
})
}
}

// 单例
const dynDurationFilterInstance = new DynDurationFilter()
export default dynDurationFilterInstance
Loading