From 4cc31e526f9a4b88d41c08c665de54367904852d Mon Sep 17 00:00:00 2001 From: festoney8 Date: Thu, 18 Jan 2024 11:35:25 +0800 Subject: [PATCH] feat: update folder name, filter support temporary selector --- src/{core => components}/group.ts | 0 src/{core => components}/item.ts | 0 src/{core => components}/panel.css | 0 src/{core => components}/panel.scss | 0 src/{core => components}/panel.ts | 0 src/filters/filters.ts | 121 +++++++++----- src/filters/homepage-filter.ts | 6 +- src/filters/popular-filter.ts | 244 ++++++++++++++++++++++++++++ src/filters/video-filter.ts | 4 +- src/main.ts | 4 +- src/rules/bangumi.ts | 4 +- src/rules/common.ts | 4 +- src/rules/dynamic.ts | 4 +- src/rules/homepage.ts | 4 +- src/rules/live.ts | 4 +- src/rules/popular.ts | 4 +- src/rules/search.ts | 4 +- src/rules/video.ts | 4 +- 18 files changed, 351 insertions(+), 60 deletions(-) rename src/{core => components}/group.ts (100%) rename src/{core => components}/item.ts (100%) rename src/{core => components}/panel.css (100%) rename src/{core => components}/panel.scss (100%) rename src/{core => components}/panel.ts (100%) create mode 100644 src/filters/popular-filter.ts diff --git a/src/core/group.ts b/src/components/group.ts similarity index 100% rename from src/core/group.ts rename to src/components/group.ts diff --git a/src/core/item.ts b/src/components/item.ts similarity index 100% rename from src/core/item.ts rename to src/components/item.ts diff --git a/src/core/panel.css b/src/components/panel.css similarity index 100% rename from src/core/panel.css rename to src/components/panel.css diff --git a/src/core/panel.scss b/src/components/panel.scss similarity index 100% rename from src/core/panel.scss rename to src/components/panel.scss diff --git a/src/core/panel.ts b/src/components/panel.ts similarity index 100% rename from src/core/panel.ts rename to src/components/panel.ts diff --git a/src/filters/filters.ts b/src/filters/filters.ts index 18a7c329..b5a06b1f 100644 --- a/src/filters/filters.ts +++ b/src/filters/filters.ts @@ -150,6 +150,7 @@ export class MainFilter { * @param selectors */ setupSelectors(selectors: Selectors) { + // 设定默认selector this.durationSelector = selectors.duration ? selectors.duration : undefined this.titleSelector = selectors.title ? selectors.title : undefined this.uploaderSelector = selectors.uploader ? selectors.uploader : undefined @@ -210,48 +211,94 @@ export class MainFilter { * 支持首页、播放页右栏、热门视频/每周必看/排行榜 * @param videos 由调用函数传入的视频列表,包含一组视频HTML节点,每个节点内应包含 标题、时长、UP主、视频链接等 * @param sign 是否标记已过滤项 + * @param selectors 可选, 用于覆盖默认selector, 用于例外情况 */ - checkAll(videos: HTMLElement[], sign = true) { - const checkDuration = this.durationEnable && this.durationFilterInstance && this.durationSelector - const checkTitle = this.titleEnable && this.titleFilterInstance && this.titleSelector - const checkUploader = this.uploaderEnable && this.uploaderFilterInstance && this.uploaderSelector - if (!checkDuration && !checkTitle && !checkUploader) { - return - } - videos.forEach((video) => { - // 构建任务,对标题、时长、UP主进行并行检查 - const tasks: Promise[] = [] - if (checkDuration) { - const duration = video.querySelector(this.durationSelector!)?.textContent?.trim() - if (duration) { - tasks.push(this.durationFilterInstance!.check(duration)) - } + checkAll(videos: HTMLElement[], sign = true, selectors?: Selectors) { + if (!selectors) { + // 使用默认selector + const checkDuration = this.durationEnable && this.durationFilterInstance && this.durationSelector + const checkTitle = this.titleEnable && this.titleFilterInstance && this.titleSelector + const checkUploader = this.uploaderEnable && this.uploaderFilterInstance && this.uploaderSelector + if (!checkDuration && !checkTitle && !checkUploader) { + return } - if (checkTitle) { - const title = video.querySelector(this.titleSelector!)?.textContent?.trim() - if (title) { - tasks.push(this.titleFilterInstance!.check(title)) + videos.forEach((video) => { + // 构建任务,对标题、时长、UP主进行并行检查 + const tasks: Promise[] = [] + if (checkDuration) { + const duration = video.querySelector(this.durationSelector!)?.textContent?.trim() + if (duration) { + tasks.push(this.durationFilterInstance!.check(duration)) + } } - } - if (checkUploader) { - const uploader = video.querySelector(this.uploaderSelector!)?.textContent?.trim() - if (uploader) { - tasks.push(this.uploaderFilterInstance!.check(uploader)) + if (checkTitle) { + const title = video.querySelector(this.titleSelector!)?.textContent?.trim() + if (title) { + tasks.push(this.titleFilterInstance!.check(title)) + } + } + if (checkUploader) { + const uploader = video.querySelector(this.uploaderSelector!)?.textContent?.trim() + if (uploader) { + tasks.push(this.uploaderFilterInstance!.check(uploader)) + } } + Promise.all(tasks) + .then(() => { + this.showVideo(video) + }) + .catch(() => { + this.hideVideo(video) + }) + .finally(() => { + // 标记已过滤的视频 + if (sign) { + video.setAttribute(settings.filterSign, '') + } + }) + }) + } else { + // 使用临时selector + const checkDuration = this.durationEnable && this.durationFilterInstance && selectors.duration + const checkTitle = this.titleEnable && this.titleFilterInstance && selectors.title + const checkUploader = this.uploaderEnable && this.uploaderFilterInstance && selectors.uploader + if (!checkDuration && !checkTitle && !checkUploader) { + return } - Promise.all(tasks) - .then(() => { - this.showVideo(video) - }) - .catch(() => { - this.hideVideo(video) - }) - .finally(() => { - // 标记已过滤的视频 - if (sign) { - video.setAttribute(settings.filterSign, '') + videos.forEach((video) => { + // 构建任务,对标题、时长、UP主进行并行检查 + const tasks: Promise[] = [] + if (checkDuration) { + const duration = video.querySelector(selectors.duration!)?.textContent?.trim() + if (duration) { + tasks.push(this.durationFilterInstance!.check(duration)) } - }) - }) + } + if (checkTitle) { + const title = video.querySelector(selectors.title!)?.textContent?.trim() + if (title) { + tasks.push(this.titleFilterInstance!.check(title)) + } + } + if (checkUploader) { + const uploader = video.querySelector(selectors.uploader!)?.textContent?.trim() + if (uploader) { + tasks.push(this.uploaderFilterInstance!.check(uploader)) + } + } + Promise.all(tasks) + .then(() => { + this.showVideo(video) + }) + .catch(() => { + this.hideVideo(video) + }) + .finally(() => { + if (sign) { + video.setAttribute(settings.filterSign, '') + } + }) + }) + } } } diff --git a/src/filters/homepage-filter.ts b/src/filters/homepage-filter.ts index 2f6b30e1..43b70b3a 100644 --- a/src/filters/homepage-filter.ts +++ b/src/filters/homepage-filter.ts @@ -1,6 +1,6 @@ import { GM_getValue } from '$' -import { Group } from '../core/group' -import { CheckboxItem, NumberItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem, NumberItem } from '../components/item' import { isPageHomepage } from '../utils/page-type' import { debug, error } from '../utils/logger' import { DurationFilterConfig, MainFilter } from './filters' @@ -16,7 +16,7 @@ const homepageDurationFilterValueID = 'homepage-duration-filter-threshold' // 主过滤器配置 const mainFilterInstanse = new MainFilter() -// 设定selector +// 设定默认selector mainFilterInstanse.setupSelectors({ duration: 'span.bili-video-card__stats__duration', title: 'h3.bili-video-card__info--tit', diff --git a/src/filters/popular-filter.ts b/src/filters/popular-filter.ts new file mode 100644 index 00000000..3c35dea0 --- /dev/null +++ b/src/filters/popular-filter.ts @@ -0,0 +1,244 @@ +// import { GM_getValue } from '$' +// import { Group } from '../core/group' +// import { CheckboxItem, NumberItem } from '../core/item' +// import { isPageHomepage } from '../utils/page-type' +// import { debug, error } from '../utils/logger' +// import { DurationFilterConfig, MainFilter } from './filters' +// import settings from '../settings' + +// // 开关itemID、数值itemID, 获取过滤器状态和参数 +// const popularDurationFilterItemID = 'popular-duration-filter' +// const popularDurationFilterValueID = 'popular-duration-filter-threshold' +// // const popularTitleFilterItemID = 'popular-title-filter' +// // const popularTitleFilterValueID = 'popular-title-filter-keywords' +// // const popularUploaderFilterItemID = 'popular-uploader-filter' +// // const popularUploaderFilterValueID = 'popular-uploader-filter-blacklist' + +// // 主过滤器配置 +// const mainFilterInstanse = new MainFilter() +// // 设定selector +// mainFilterInstanse.setupSelectors({ +// duration: 'span.bili-video-card__stats__duration', +// title: 'h3.bili-video-card__info--tit', +// uploader: 'span.bili-video-card__info--author', +// }) +// // 配置durationFilter +// let durationEnable: boolean +// let durationFilterConfig: DurationFilterConfig +// const setupDurationFilter = () => { +// durationEnable = GM_getValue(`BILICLEANER_${popularDurationFilterItemID}`, false) +// const durationThreshold: number = GM_getValue( +// `BILICLEANER_VALUE_${popularDurationFilterValueID}`, +// settings.durationThreshold, +// ) +// durationFilterConfig = { +// threshold: durationThreshold, +// } +// mainFilterInstanse.setupDurationFilter(durationEnable, durationFilterConfig) +// debug('setupDurationFilter') +// } +// // // 配置titleFilter +// // let titleEnable: boolean +// // let titleFilterConfig: TitleFilterConfig +// // const setupTitleFilter = () => { +// // debug('setupTitleFilter') +// // // const titleEnable: boolean = GM_getValue(`BILICLEANER_${popularTitleFilterItemID}`, false) +// // // const titleThreshold: number = GM_getValue( +// // // `BILICLEANER_VALUE_${popularTitleFilterValueID}`, +// // // settings.titleThreshold, +// // // ) +// // // const titleFilterConfig: TitleFilterConfig = {} +// // // mainFilterInstanse.setupTitleFilter(titleEnable, titleFilterConfig) +// // } +// // // 配置uploaderFilter +// // let uploaderEnable: boolean +// // let uploaderFilterConfig: UploaderFilterConfig +// // const setupUploaderFilter = () => { +// // debug('setupUploaderFilter') +// // // const uploaderEnable: boolean = GM_getValue(`BILICLEANER_${popularUploaderFilterItemID}`, false) +// // // const uploaderThreshold: number = GM_getValue( +// // // `BILICLEANER_VALUE_${popularUploaderFilterValueID}`, +// // // settings.uploaderThreshold, +// // // ) +// // // const uploaderFilterConfig: UploaderFilterConfig = { +// // // } +// // // mainFilterInstanse.setupDurationFilter(uploaderEnable, uploaderFilterConfig) +// // } + +// ////////////////////////////////////////////////////////////////////////////////// + +// // 功能组 +// const durationItems: (CheckboxItem | NumberItem)[] = [] +// // const titleItems: (CheckboxItem | NumberItem)[] = [] +// // const uploaderItems: (CheckboxItem | NumberItem)[] = [] +// const popularFilterGroupList: Group[] = [] +// if (isPageHomepage()) { +// // 视频列表 +// let videoListContainer: HTMLElement + +// // 1. 视频过滤主流程, 监听视频列表容器出现 +// let isMainFilterFuncRunning = false +// const mainFilterFunc = () => { +// if (isMainFilterFuncRunning) { +// return +// } +// isMainFilterFuncRunning = true +// // 检测/监听视频列表上层节点出现 +// videoListContainer = document.querySelector( +// '#app .popular-video-container, #app .rank-container', +// ) as HTMLFormElement +// if (videoListContainer) { +// debug('popular videoList exist') +// watchVideoListContainer() +// } else { +// debug('popular videoList obverser start') +// const videoListObverser = new MutationObserver((mutationList) => { +// mutationList.forEach((mutation) => { +// if (mutation.addedNodes) { +// mutation.addedNodes.forEach((node) => { +// if ( +// (node as Element).className === 'popular-video-container' || +// (node as Element).className === 'rank-container' +// ) { +// // 出现后观测视频列表变化 +// debug('popular videoList appear') +// videoListObverser.disconnect() +// videoListContainer = document.querySelector( +// '#app .popular-video-container, #app .rank-container', +// ) as HTMLFormElement +// watchVideoListContainer() +// } +// }) +// } +// }) +// }) +// videoListObverser.observe(document, { childList: true, subtree: true }) +// } +// } +// // 2. 监听视频数量变化 +// const watchVideoListContainer = () => { +// if (videoListContainer) { +// checkVideoList(true) // 初次全站过滤 +// const videoObverser = new MutationObserver(() => { +// checkVideoList() +// }) +// // 涉及切换tab操作, 监听subtree +// videoObverser.observe(videoListContainer, { childList: true, subtree: true }) +// debug('watchVideoListContainer OK') +// } +// } +// /** +// * 3. 检查视频列表 +// * 调用过滤器检测列表内的视频, 单次检测在10ms内 +// * @param includeVisited 是否包含已过滤的视频, 为true则重新进行全站过滤 +// */ +// const checkVideoList = (includeVisited = false) => { +// try { +// // video-card, 支持匹配 热门视频/每周必看/入站必刷 +// let cardVideos: NodeListOf +// // rank-item, 匹配 排行榜 +// let rankVideos: NodeListOf +// if (!includeVisited) { +// cardVideos = videoListContainer.querySelectorAll( +// `.card-list .video-card:not([${settings.filterSign}]), +// .video-list .video-card:not([${settings.filterSign}])`, +// ) +// rankVideos = videoListContainer.querySelectorAll( +// `.rank-list .rank-item:not([${settings.filterSign}])`, +// ) +// } else { +// cardVideos = videoListContainer.querySelectorAll( +// `.card-list .video-card, +// .video-list .video-card`, +// ) +// rankVideos = videoListContainer.querySelectorAll(`.rank-list .rank-item`) +// } +// cardVideos.length && mainFilterInstanse.checkAll([...cardVideos]) +// rankVideos.length && mainFilterInstanse.checkAll([...rankVideos]) +// debug(`checkVideoList check ${cardVideos.length} card videos, ${rankVideos.length} rank videos`) +// } catch (err) { +// error(err) +// error('checkVideoList error') +// } +// } +// //////////////////////////////////////////////////////////////////////////// +// // 功能函数, 开关开启时触发, 页面载入时触发, 全站过滤 +// const durationFilterFunc = () => { +// setupDurationFilter() +// if (isMainFilterFuncRunning) { +// checkVideoList(true) +// } else { +// mainFilterFunc() +// } +// } +// // const titleFilterFunc = () => { +// // setupTitleFilter() +// // mainFilterFunc() +// // } +// // const uploaderFilterFunc = () => { +// // setupUploaderFilter() +// // mainFilterFunc() +// // } + +// // 开关关闭回调, 修改阈值为0, 触发一次全站恢复 +// const durationDisableCallback = () => { +// debug('durationDisableCallback start') +// durationFilterConfig.threshold = 0 +// mainFilterInstanse.setupDurationFilter(true, durationFilterConfig) +// checkVideoList(true) +// mainFilterInstanse.setupDurationFilter(false, durationFilterConfig) +// debug('durationDisableCallback OK') +// } +// // 数值变化回调, 实时修改阈值, 触发一次全站过滤 (包含已过滤项) +// const durationChangeCallback = (currValue: number) => { +// debug('durationChangeCallback start') +// durationFilterConfig.threshold = currValue +// mainFilterInstanse.setupDurationFilter(durationEnable, durationFilterConfig) +// if (durationEnable) { +// checkVideoList(true) +// } +// debug('durationChangeCallback OK') +// } +// // // 回调, 添加title关键词触发 +// // const titleCallback = (currValue: number) => { +// // debug('titleCallback') +// // // mainFilterInstanse.setupDurationFilter(titleEnable, titleFilterConfig) +// // // checkVideoList(true) +// // } +// // // 回调, 新增屏蔽UP主触发 +// // const uploaderCallback = (currValue: number) => { +// // debug('uploaderCallback') +// // // mainFilterInstanse.setupDurationFilter(uploaderEnable, uploaderFilterConfig) +// // // checkVideoList(true) +// // } + +// // 首页 时长过滤part +// { +// // 启用 视频时长过滤 +// durationItems.push( +// new CheckboxItem( +// popularDurationFilterItemID, +// '启用 视频时长过滤', +// false, +// durationFilterFunc, +// false, +// null, +// durationDisableCallback, +// ), +// ) +// durationItems.push( +// new NumberItem( +// popularDurationFilterValueID, +// '设定最低时长 (0~300s)', +// 60, +// 0, +// 300, +// '秒', +// durationChangeCallback, +// ), +// ) +// } +// popularFilterGroupList.push(new Group('popular-duration-filter-group', '首页 视频时长过滤', durationItems)) +// } + +// export { popularFilterGroupList } diff --git a/src/filters/video-filter.ts b/src/filters/video-filter.ts index 08819f2f..fb0868f4 100644 --- a/src/filters/video-filter.ts +++ b/src/filters/video-filter.ts @@ -1,6 +1,6 @@ import { GM_getValue } from '$' -import { Group } from '../core/group' -import { CheckboxItem, NumberItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem, NumberItem } from '../components/item' import { isPageVideo } from '../utils/page-type' import { debug, error } from '../utils/logger' import { DurationFilterConfig, MainFilter } from './filters' diff --git a/src/main.ts b/src/main.ts index 28c77c61..66115d8d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ import { GM_registerMenuCommand } from '$' import { log, error, debug } from './utils/logger' import { init } from './init' -import { Group } from './core/group' +import { Group } from './components/group' import { homepageGroupList } from './rules/homepage' import { commonGroupList } from './rules/common' import { videoGroupList } from './rules/video' @@ -14,7 +14,7 @@ import { popularGroupList } from './rules/popular' import { homepageFilterGroupList } from './filters/homepage-filter' import { isPageHomepage, isPagePopular, isPageVideo } from './utils/page-type' import { videoFilterGroupList } from './filters/video-filter' -import panelInstance from './core/panel' +import panelInstance from './components/panel' log('script start') diff --git a/src/rules/bangumi.ts b/src/rules/bangumi.ts index cfe40c71..0c69a267 100644 --- a/src/rules/bangumi.ts +++ b/src/rules/bangumi.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem } from '../components/item' import { debug } from '../utils/logger' import { isPageBangumi } from '../utils/page-type' diff --git a/src/rules/common.ts b/src/rules/common.ts index 66889435..1df4463a 100644 --- a/src/rules/common.ts +++ b/src/rules/common.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem, RadioItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem, RadioItem } from '../components/item' import { debug } from '../utils/logger' import { isPageBangumi, diff --git a/src/rules/dynamic.ts b/src/rules/dynamic.ts index 912c1181..9085bca9 100644 --- a/src/rules/dynamic.ts +++ b/src/rules/dynamic.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem } from '../components/item' import { debug } from '../utils/logger' import { isPageDynamic } from '../utils/page-type' diff --git a/src/rules/homepage.ts b/src/rules/homepage.ts index 23a4d922..501b5b2f 100644 --- a/src/rules/homepage.ts +++ b/src/rules/homepage.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem, RadioItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem, RadioItem } from '../components/item' import { isPageHomepage } from '../utils/page-type' const basicItems: CheckboxItem[] = [] diff --git a/src/rules/live.ts b/src/rules/live.ts index 3fb1b4cc..017dac92 100644 --- a/src/rules/live.ts +++ b/src/rules/live.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem } from '../components/item' import { isPageLive } from '../utils/page-type' const basicItems: CheckboxItem[] = [] diff --git a/src/rules/popular.ts b/src/rules/popular.ts index 8e18dc42..5e6e6199 100644 --- a/src/rules/popular.ts +++ b/src/rules/popular.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem, RadioItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem, RadioItem } from '../components/item' import { isPagePopular } from '../utils/page-type' const basicItems: CheckboxItem[] = [] diff --git a/src/rules/search.ts b/src/rules/search.ts index d0e53f21..6f923937 100644 --- a/src/rules/search.ts +++ b/src/rules/search.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem } from '../components/item' import { isPageSearch } from '../utils/page-type' const basicItems: CheckboxItem[] = [] diff --git a/src/rules/video.ts b/src/rules/video.ts index 8062931b..4e8aedef 100644 --- a/src/rules/video.ts +++ b/src/rules/video.ts @@ -1,5 +1,5 @@ -import { Group } from '../core/group' -import { CheckboxItem } from '../core/item' +import { Group } from '../components/group' +import { CheckboxItem } from '../components/item' import { debug } from '../utils/logger' import { isPagePlaylist, isPageVideo } from '../utils/page-type'