Skip to content

Commit

Permalink
Merge pull request #68 from festoney8/dev
Browse files Browse the repository at this point in the history
merge dev to main, v3.5.0
  • Loading branch information
festoney8 authored Apr 30, 2024
2 parents f7813ab + bccb866 commit 7886cdb
Show file tree
Hide file tree
Showing 16 changed files with 510 additions and 85 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELOG

## 3.5.0

- 新增:热门/每周必看/排行榜页 时长过滤
- 新增:热门/每周必看/排行榜页 竖屏视频过滤
- 新增:热门/每周必看/排行榜页 视频质量过滤(实验功能)
- 新增:右键菜单中复制链接功能

## 3.4.7

- 优化:播放页视频信息置底
Expand Down
4 changes: 2 additions & 2 deletions src/components/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,11 @@ export class NumberItem implements IItem {

constructor(private option: INumberItemOption) {}

/** 获取数值, 初次安装使用禁用值 */
/** 获取数值, 初次安装使用默认值 */
getValue() {
this.itemValue = GM_getValue(`BILICLEANER_${this.option.itemID}`)
if (this.itemValue === undefined) {
this.itemValue = this.option.disableValue
this.itemValue = this.option.defaultValue
this.setValue(this.itemValue)
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/filters/videoFilter/agency/agency.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import bvidFilterInstance from '../filters/subfilters/bvid'
import dimensionFilterInstance from '../filters/subfilters/dimension'
import durationFilterInstance from '../filters/subfilters/duration'
import qualityFilterInstance from '../filters/subfilters/quality'
import titleKeywordFilterInstance from '../filters/subfilters/titleKeyword'
import titleKeywordWhitelistFilterInstance from '../filters/subfilters/titleKeywordWhitelist'
import uploaderFilterInstance from '../filters/subfilters/uploader'
Expand Down Expand Up @@ -45,6 +47,31 @@ class VideoFilterAgency {
break
}
}
notifyQuality(event: string, value?: number) {
switch (event) {
case 'disable':
qualityFilterInstance.setStatus(false)
break
case 'enable':
qualityFilterInstance.setStatus(true)
break
case 'change':
if (typeof value === 'number') {
qualityFilterInstance.setParams(value)
}
break
}
}
notifyDimension(event: string) {
switch (event) {
case 'disable':
dimensionFilterInstance.setStatus(false)
break
case 'enable':
dimensionFilterInstance.setStatus(true)
break
}
}
notifyBvid(event: string, value?: string | string[]) {
switch (event) {
case 'disable':
Expand Down
40 changes: 35 additions & 5 deletions src/filters/videoFilter/filters/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import settings from '../../../settings'
import { debugVideoFilter as debug, error, log } from '../../../utils/logger'
import { hideEle, isEleHide, showEle } from '../../../utils/tool'
import bvidFilterInstance from './subfilters/bvid'
import dimensionFilterInstance from './subfilters/dimension'
import durationFilterInstance from './subfilters/duration'
import qualityFilterInstance from './subfilters/quality'
import titleKeywordFilterInstance from './subfilters/titleKeyword'
import titleKeywordWhitelistFilterInstance from './subfilters/titleKeywordWhitelist'
import uploaderFilterInstance from './subfilters/uploader'
Expand All @@ -13,23 +15,27 @@ import uploaderWhitelistFilterInstance from './subfilters/uploaderWhitelist'
export interface IVideoSubFilter {
isEnable: boolean
setStatus(status: boolean): void
setParams(value: string[] | number): void
setParams?(value: string[] | number): void
addParam?(value: string): void
check(value: string): Promise<string>
check(value: string | boolean | number): Promise<string>
}

export type VideoSelectorFunc = {
duration?: (video: HTMLElement) => string | null
titleKeyword?: (video: HTMLElement) => string | null
bvid?: (video: HTMLElement) => string | null
uploader?: (video: HTMLElement) => string | null
coinLikeRatio?: (video: HTMLElement) => number | null
dimension?: (video: HTMLElement) => boolean | null
}

interface VideoInfo {
duration?: string | undefined
title?: string | undefined
uploader?: string | undefined
bvid?: string | undefined
coinLikeRatio?: number | undefined
dimension?: boolean | undefined // true横屏 false竖屏
}

class CoreVideoFilter {
Expand All @@ -44,6 +50,8 @@ class CoreVideoFilter {
debug(`checkAll start`)
try {
const checkDuration = durationFilterInstance.isEnable && selectorFunc.duration !== undefined
const checkQuality = qualityFilterInstance.isEnable && selectorFunc.coinLikeRatio !== undefined
const checkDimension = dimensionFilterInstance.isEnable && selectorFunc.dimension !== undefined
const checkTitleKeyword = titleKeywordFilterInstance.isEnable && selectorFunc.titleKeyword !== undefined
const checkUploader = uploaderFilterInstance.isEnable && selectorFunc.uploader !== undefined
const checkUploaderKeyword = uploaderKeywordFilterInstance.isEnable && selectorFunc.uploader !== undefined
Expand All @@ -53,7 +61,15 @@ class CoreVideoFilter {
const checkTitleKeywordWhitelist =
titleKeywordWhitelistFilterInstance.isEnable && selectorFunc.titleKeyword !== undefined

if (!checkDuration && !checkTitleKeyword && !checkUploader && !checkBvid) {
if (
!checkDuration &&
!checkQuality &&
!checkDimension &&
!checkTitleKeyword &&
!checkUploader &&
!checkUploaderKeyword &&
!checkBvid
) {
// 黑名单全部关闭时 恢复全部视频
videos.forEach((video) => showEle(video))
return
Expand All @@ -72,6 +88,20 @@ class CoreVideoFilter {
info.duration = duration
}
}
if (checkQuality) {
const ratio = selectorFunc.coinLikeRatio!(video)
if (ratio) {
blackTasks.push(qualityFilterInstance.check(ratio))
info.coinLikeRatio = ratio
}
}
if (checkDimension) {
const dimension = selectorFunc.dimension!(video)
if (dimension !== null) {
blackTasks.push(dimensionFilterInstance.check(dimension))
info.dimension = dimension
}
}
if (checkBvid) {
const bvid = selectorFunc.bvid!(video)
if (bvid) {
Expand Down Expand Up @@ -135,7 +165,7 @@ class CoreVideoFilter {
// debug(_result)
if (!isEleHide(video)) {
log(
`hide video\nbvid: ${info.bvid}\ntime: ${info.duration}\nup: ${info.uploader}\ntitle: ${info.title}`,
`hide video\nbvid: ${info.bvid}\ntime: ${info.duration}\nup: ${info.uploader}\nratio: ${info.coinLikeRatio}\ntitle: ${info.title}`,
)
}
hideEle(video)
Expand All @@ -148,7 +178,7 @@ class CoreVideoFilter {
} else {
if (!isEleHide(video)) {
log(
`hide video\nbvid: ${info.bvid}\ntime: ${info.duration}\nup: ${info.uploader}\ntitle: ${info.title}`,
`hide video\nbvid: ${info.bvid}\ntime: ${info.duration}\nup: ${info.uploader}\nratio: ${info.coinLikeRatio}\ntitle: ${info.title}`,
)
}
hideEle(video)
Expand Down
27 changes: 27 additions & 0 deletions src/filters/videoFilter/filters/subfilters/dimension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IVideoSubFilter } from '../core'

class DimensionFilter implements IVideoSubFilter {
isEnable = false

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

check(dimension: boolean): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (!this.isEnable) {
resolve(`Dimension filter disable`)
} else {
if (dimension) {
resolve(`Dimension is horizontal`)
} else {
reject(`Dimension is vertical`)
}
}
})
}
}

// 单例
const dimensionFilterInstance = new DimensionFilter()
export default dimensionFilterInstance
46 changes: 21 additions & 25 deletions src/filters/videoFilter/filters/subfilters/duration.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { error } from '../../../../utils/logger'
import { IVideoSubFilter } from '../core'

class DurationFilter implements IVideoSubFilter {
// 匹配时长的正则
private readonly pattern = /^(\d+:)?\d\d:\d\d$/g
// 时长阈值, 单位秒
private threshold = 0
isEnable = false
Expand All @@ -16,35 +13,34 @@ class DurationFilter implements IVideoSubFilter {
this.threshold = threshold
}

private isLegal(duration: string): boolean {
const hhmmss = duration.split(':')
if (hhmmss.length == 2) {
return parseInt(hhmmss[0]) * 60 + parseInt(hhmmss[1]) >= this.threshold
} else if (hhmmss.length > 2) {
return true
// duration转换为秒数, 支持 HH:MM:SS, MM:SS, 纯数字
durationToSec = (duration: string): number => {
duration = duration.trim()
if (duration.match(/^(?:\d+:)?\d+:\d+$/)) {
const parts = duration.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 (duration.match(/^\d+$/)) {
return parseInt(duration)
}
return true
return -1
}

check(duration: string): Promise<string> {
duration = duration.trim()
return new Promise<string>((resolve, reject) => {
try {
if (!this.isEnable || this.threshold === 0) {
resolve(`Duration resolve, disable or 0`)
return
} else if (duration && duration.match(this.pattern)) {
if (this.isLegal(duration)) {
resolve(`Duration resolve, duration OK`)
} else {
reject(`Duration reject, ${duration} < ${this.threshold}s`)
}
if (!this.isEnable || this.threshold === 0) {
resolve(`Duration resolve, disable or 0`)
} else {
const seconds = this.durationToSec(duration)
if (seconds > 0 && seconds > this.threshold) {
resolve(`Duration OK`)
} else {
resolve(`Duration resolve`)
reject(`Duration too short`)
}
} catch (err) {
error(err)
resolve(`Duration resolve, error`)
}
})
}
Expand Down
44 changes: 44 additions & 0 deletions src/filters/videoFilter/filters/subfilters/quality.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { IVideoSubFilter } from '../core'

class QualityFilter implements IVideoSubFilter {
// 质量过滤阈值
private threshold = 0
isEnable = false

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

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

// 根据coinLikeRatio计算视频质量, 参数源于爬虫数据拟合
calcQuality = (ratio: number): number => {
const A = -1.201e1
const B = 6.861e-1
const C = 7.369e-2
const D = 1.192e2
const ans = (A - D) / (1 + Math.pow(ratio / C, B)) + D
return ans > 0 ? ans : 0
}

check(ratio: number): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (!this.isEnable || this.threshold === 0) {
resolve(`Quality resolve, disable or 0`)
} else {
const score = this.calcQuality(ratio)
if (score > 0 && score > this.threshold) {
resolve(`Quality OK`)
} else {
reject(`Quality too bad`)
}
}
})
}
}

// 单例
const qualityFilterInstance = new QualityFilter()
export default qualityFilterInstance
Loading

0 comments on commit 7886cdb

Please sign in to comment.