Skip to content

Commit

Permalink
feat: video filter support space page
Browse files Browse the repository at this point in the history
  • Loading branch information
festoney8 committed Apr 4, 2024
1 parent 4adc1c0 commit 9319e6f
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 6 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## 3.3.5

- 新增:视频过滤支持空间页(UP主投稿列表、视频合集列表)
- 修复:标题关键词白名单失效bug

## 3.3.4

- 新增:隐藏 评论区用户卡片
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@

> [!IMPORTANT]
>
> - **重要:下列页面均有独立菜单,不同页面菜单各不相同**,按下图打开菜单进行设置,实时生效
> - **页面净化:「首页、播放页、影视番剧播放页、直播间、搜索页、动态页、热门页、频道页」**
> - **视频过滤:「首页、播放页、搜索页、热门页、频道页」**
> - **视频过滤:「首页、播放页、搜索页、热门页、频道页、空间页**
> - **评论过滤:「播放页、影视番剧播放页」**
> - **每个页面都有独立菜单**,按下图打开菜单进行设置,实时生效
![](images/usage.png)

Expand Down
2 changes: 1 addition & 1 deletion src/filters/videoFilter/filters/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class CoreVideoFilter {
const checkUploaderWhitelist =
uploaderWhitelistFilterInstance.isEnable && selectorFunc.uploader !== undefined
const checkTitleKeywordWhitelist =
titleKeywordWhitelistFilterInstance.isEnable && selectorFunc.uploader !== undefined
titleKeywordWhitelistFilterInstance.isEnable && selectorFunc.titleKeyword !== undefined

if (!checkDuration && !checkTitleKeyword && !checkUploader && !checkBvid) {
// 黑名单全部关闭时 恢复全部视频
Expand Down
271 changes: 271 additions & 0 deletions src/filters/videoFilter/pages/space.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
import { debugVideoFilter as debug, error } from '../../../utils/logger'
import { ButtonItem, CheckboxItem, NumberItem } from '../../../components/item'
import { Group } from '../../../components/group'
import coreFilterInstance, { VideoSelectorFunc } from '../filters/core'
import { isPageSpace } from '../../../utils/page-type'
import { matchBvid, waitForEle } from '../../../utils/tool'
import { BvidAction, DurationAction, TitleKeywordAction, TitleKeywordWhitelistAction } from './actions/action'
import { ContextMenu } from '../../../components/contextmenu'

const spacePageVideoFilterGroupList: Group[] = []

if (isPageSpace()) {
let videoListContainer: HTMLElement
// 构建SelectorFunc
const submitSelectorFunc: VideoSelectorFunc = {
duration: (video: Element): string | null => {
const duration = video.querySelector('span.length')?.textContent
return duration ? duration : null
},
titleKeyword: (video: Element): string | null => {
const titleKeyword =
video.querySelector('a.title')?.textContent || video.querySelector('a.title')?.getAttribute('title')
return titleKeyword ? titleKeyword : null
},
bvid: (video: Element): string | null => {
const href = video.querySelector('a.title')?.getAttribute('href')
return href ? matchBvid(href) : null
},
}
const collectionSelectorFunc = submitSelectorFunc

// 检测视频列表
const checkVideoList = (_fullSite: boolean) => {
// debug('checkVideoList start')
if (!videoListContainer) {
// 在container未出现时, 各项屏蔽功能enable会调用checkVideoList, 需要判空
debug(`checkVideoList videoListContainer not exist`)
return
}
try {
// 投稿视频
if (location.pathname.match(/^\/\d+\/video$/)) {
const submitVideos = [
...videoListContainer.querySelectorAll<HTMLElement>(`#submit-video :is(.small-item,.list-item)`),
]
submitVideos.length && coreFilterInstance.checkAll(submitVideos, false, submitSelectorFunc)
debug(`checkVideoList check ${submitVideos.length} submit video`)
}
// 视频合集、视频系列
if (location.pathname.match(/^\/\d+\/channel\/(collectiondetail|seriesdetail)/)) {
const collectionVideos = [
...videoListContainer.querySelectorAll<HTMLElement>(
`:is(#page-collection-detail,#page-series-detail) li.small-item`,
),
]
collectionVideos.length && coreFilterInstance.checkAll(collectionVideos, false, collectionSelectorFunc)
debug(`checkVideoList check ${collectionVideos.length} collection video`)
}
} catch (err) {
error(err)
error('checkVideoList error')
}
}

// 初始化 行为实例
const spaceDurationAction = new DurationAction(
'space-duration-filter-status',
'global-duration-filter-value',
checkVideoList,
)
const spaceBvidAction = new BvidAction('space-bvid-filter-status', 'global-bvid-filter-value', checkVideoList)
const spaceTitleKeywordAction = new TitleKeywordAction(
'space-title-keyword-filter-status',
'global-title-keyword-filter-value',
checkVideoList,
)
const spaceTitleKeyworldWhitelistAction = new TitleKeywordWhitelistAction(
'space-title-keyword-whitelist-filter-status',
'global-title-keyword-whitelist-filter-value',
checkVideoList,
)

// 监听视频列表内部变化, 有变化时检测视频列表
const watchVideoListContainer = () => {
if (videoListContainer) {
debug('watchVideoListContainer start')
if (spaceDurationAction.status || spaceBvidAction.status || spaceTitleKeywordAction.status) {
checkVideoList(true)
}
const videoObverser = new MutationObserver(() => {
if (spaceDurationAction.status || spaceBvidAction.status || spaceTitleKeywordAction.status) {
// 全量检测
checkVideoList(true)
}
})
videoObverser.observe(videoListContainer, { childList: true, subtree: true })
debug('watchVideoListContainer OK')
}
}
try {
// 监听视频列表出现
waitForEle(document, '#app', (node: Node): boolean => {
return node instanceof HTMLElement && (node as HTMLElement).id === 'app'
}).then((ele) => {
if (ele) {
videoListContainer = ele
watchVideoListContainer()
}
})
} catch (err) {
error(err)
error(`watch video list ERROR`)
}

//=======================================================================================

// 右键监听函数, 空间页右键单击指定元素时修改右键菜单, 用于屏蔽视频BVID
let isContextMenuFuncRunning = false
let isContextMenuBvidEnable = false
const contextMenuFunc = () => {
if (isContextMenuFuncRunning) {
return
}
isContextMenuFuncRunning = true
const menu = new ContextMenu()
// 监听右键单击
document.addEventListener('contextmenu', (e) => {
menu.hide()
if (e.target instanceof HTMLElement) {
// debug(e.target.classList)
if (isContextMenuBvidEnable && e.target.classList.contains('title')) {
// 命中视频标题, 提取bvid
const href = e.target.getAttribute('href')
if (href) {
const bvid = matchBvid(href)
if (bvid) {
e.preventDefault()
menu.registerMenu(`屏蔽视频 ${bvid}`, () => {
spaceBvidAction.add(bvid)
})
menu.show(e.clientX, e.clientY)
}
}
} else {
menu.hide()
}
}
})
debug('contextMenuFunc listen contextmenu')
}

//=======================================================================================
// 构建UI菜单

// UI组件, 时长过滤
const durationItems = [
// 启用 空间页时长过滤
new CheckboxItem({
itemID: spaceDurationAction.statusKey,
description: '启用 时长过滤',
itemFunc: () => {
spaceDurationAction.enable()
},
callback: () => {
spaceDurationAction.disable()
},
}),
// 设定最低时长
new NumberItem({
itemID: spaceDurationAction.valueKey,
description: '设定最低时长 (0~300s)',
defaultValue: 60,
minValue: 0,
maxValue: 300,
disableValue: 0,
unit: '秒',
callback: (value: number) => {
spaceDurationAction.change(value)
},
}),
]
spacePageVideoFilterGroupList.push(new Group('space-duration-filter-group', '空间页 视频时长过滤', durationItems))

// UI组件, 标题关键词过滤
const titleKeywordItems = [
// 启用 空间页关键词过滤
new CheckboxItem({
itemID: spaceTitleKeywordAction.statusKey,
description: '启用 标题关键词过滤',
itemFunc: () => {
spaceTitleKeywordAction.enable()
},
callback: () => {
spaceTitleKeywordAction.disable()
},
}),
// 编辑 标题关键词黑名单
new ButtonItem({
itemID: 'space-title-keyword-edit-button',
description: '编辑 标题关键词黑名单(支持正则)',
name: '编辑',
itemFunc: () => {
spaceTitleKeywordAction.blacklist.show()
},
}),
]
spacePageVideoFilterGroupList.push(
new Group('space-title-keyword-filter-group', '空间页 标题关键词过滤', titleKeywordItems),
)

// UI组件, bvid过滤
const bvidItems = [
// 启用 空间页BV号过滤
new CheckboxItem({
itemID: spaceBvidAction.statusKey,
description: '启用 BV号过滤',
itemFunc: () => {
// 启用右键功能
isContextMenuBvidEnable = true
contextMenuFunc()
spaceBvidAction.enable()
},
callback: () => {
// 禁用右键功能
isContextMenuBvidEnable = false
spaceBvidAction.disable()
},
}),
// 编辑 BV号黑名单
new ButtonItem({
itemID: 'space-bvid-edit-button',
description: '编辑 BV号黑名单',
name: '编辑',
itemFunc: () => {
spaceBvidAction.blacklist.show()
},
}),
]
spacePageVideoFilterGroupList.push(
new Group('space-bvid-filter-group', '空间页 BV号过滤 (右键单击标题)', bvidItems),
)

// UI组件, 例外和白名单
const whitelistItems = [
// 启用 空间页标题关键词白名单
new CheckboxItem({
itemID: spaceTitleKeyworldWhitelistAction.statusKey,
description: '启用 标题关键词白名单',
itemFunc: () => {
spaceTitleKeyworldWhitelistAction.enable()
},
callback: () => {
spaceTitleKeyworldWhitelistAction.disable()
},
}),
// 编辑 标题关键词白名单
new ButtonItem({
itemID: 'space-title-keyword-whitelist-edit-button',
description: '编辑 标题关键词白名单(支持正则)',
name: '编辑',
itemFunc: () => {
spaceTitleKeyworldWhitelistAction.whitelist.show()
},
}),
]
spacePageVideoFilterGroupList.push(
new Group('space-whitelist-filter-group', '空间页 白名单设定 (免过滤)', whitelistItems),
)
}

export { spacePageVideoFilterGroupList }
23 changes: 21 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
isPagePlaylist,
isPagePopular,
isPageSearch,
isPageSpace,
isPageVideo,
} from './utils/page-type'
import { homepagePageVideoFilterGroupList } from './filters/videoFilter/pages/homepage'
Expand All @@ -29,6 +30,7 @@ import { channelGroupList } from './rules/channel'
import { channelPageVideoFilterGroupList } from './filters/videoFilter/pages/channel'
import panelInstance from './components/panel'
import { videoPageCommentFilterGroupList } from './filters/commentFilter/pages/video'
import { spacePageVideoFilterGroupList } from './filters/videoFilter/pages/space'

log('script start')

Expand Down Expand Up @@ -62,6 +64,7 @@ const main = async () => {
...popularPageVideoFilterGroupList,
...searchPageVideoFilterGroupList,
...channelPageVideoFilterGroupList,
...spacePageVideoFilterGroupList,
]
VIDEO_FILTER_GROUPS.forEach((e) => e.enableGroup())

Expand Down Expand Up @@ -134,7 +137,15 @@ const main = async () => {
createPanelWithMode('rule', RULE_GROUPS)
})
// 视频过滤设置
if (isPageHomepage() || isPageVideo() || isPagePopular() || isPageSearch() || isPageChannel() || isPagePlaylist()) {
if (
isPageHomepage() ||
isPageVideo() ||
isPagePopular() ||
isPageSearch() ||
isPageChannel() ||
isPagePlaylist() ||
isPageSpace()
) {
GM_registerMenuCommand('✅视频过滤设置', () => {
createPanelWithMode('videoFilter', VIDEO_FILTER_GROUPS)
})
Expand All @@ -146,7 +157,15 @@ const main = async () => {
})
}
// 视频过滤 快捷按钮
if (isPageHomepage() || isPageVideo() || isPagePopular() || isPageSearch() || isPageChannel() || isPagePlaylist()) {
if (
isPageHomepage() ||
isPageVideo() ||
isPagePopular() ||
isPageSearch() ||
isPageChannel() ||
isPagePlaylist() ||
isPageSpace()
) {
const videoFilterSideBtnID = 'video-filter-side-btn'
const sideBtn = new SideBtn(
videoFilterSideBtnID,
Expand Down
4 changes: 4 additions & 0 deletions src/utils/page-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const currPage = (): string => {
if (href.includes('bilibili.com/list/')) {
return 'playlist'
}
if (host === 'space.bilibili.com') {
return 'space'
}
// 频道子分类
if (!href.includes('bilibili.com/v/popular/') && href.includes('bilibili.com/v/')) {
return 'channel'
Expand All @@ -60,3 +63,4 @@ export const isPageBangumi = () => ans === 'bangumi'
export const isPagePlaylist = () => ans === 'playlist'
export const isPageBnj = () => ans === 'bnj'
export const isPageChannel = () => ans === 'channel'
export const isPageSpace = () => ans === 'space'
2 changes: 1 addition & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default defineConfig({
userscript: {
name: 'bilibili 页面净化大师',
namespace: 'http://tampermonkey.net/',
version: '3.3.4',
version: '3.3.5',
description:
'净化 B站/哔哩哔哩 网页元素,去广告,BV号转AV号,播放器净化,过滤视频,过滤评论,提供300+项功能,定制自己的B站页面',
author: 'festoney8',
Expand Down

0 comments on commit 9319e6f

Please sign in to comment.