Skip to content

Commit

Permalink
refactor: url cleaner, bv2av
Browse files Browse the repository at this point in the history
  • Loading branch information
festoney8 committed Jun 14, 2024
1 parent ffdc29e commit 04384db
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 107 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## 3.7.1

- 重构:URL净化、BV号转AV号
- 优化:去除播放页网址跳变问题
- 优化:减少URL变化对浏览器历史记录的影响

## 3.7.0

- 新增:播放页 播放设定板块
Expand Down
10 changes: 0 additions & 10 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,6 @@ const main = async () => {
const COMMENT_FILTER_GROUPS = [...videoPageCommentFilterGroupList, ...dynamicPageCommentFilterGroupList]
COMMENT_FILTER_GROUPS.forEach((e) => e.enableGroup())

// 监听各种形式的URL变化 (普通监听无法检测到切换视频)
let lastURL = location.href
setInterval(() => {
const currURL = location.href
if (currURL !== lastURL) {
RULE_GROUPS.forEach((e) => e.reloadGroup())
lastURL = currURL
}
}, 500)

// 全局启动/关闭快捷键 chrome: Alt+B,firefox: Ctrl+Alt+B
let isGroupEnable = true
document.addEventListener('keydown', (event) => {
Expand Down
128 changes: 66 additions & 62 deletions src/rules/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
isPageSearch,
isPageVideo,
} from '../utils/page-type'
import URLCleanerInstance from '../utils/url-cleaner'

// Grouplist
const commonGroupList: Group[] = []
Expand Down Expand Up @@ -337,76 +338,79 @@ const basicItems = [
itemID: 'url-cleaner',
description: 'URL参数净化 (充电时需关闭)',
defaultStatus: true,
isItemFuncReload: true,
/**
* URL净化,移除query string中的跟踪参数/无用参数
* 净化掉vd_source参数会导致充电窗口载入失败
*/
itemFunc: () => {
// 直播域名各种iframe页面(天选、抽奖)和活动页特殊处理
if (location.href.match(/live\.bilibili\.com\/(p\/html|activity|blackboard)/)) {
return
}
const keysToRemove = new Set([
'from_source',
'spm_id_from',
'search_source',
'vd_source',
'unique_k',
'is_story_h5',
'from_spmid',
'share_plat',
'share_medium',
'share_from',
'share_source',
'share_tag',
'up_id',
'timestamp',
'mid',
'live_from',
'launch_id',
'session_id',
'share_session_id',
'broadcast_type',
'is_room_feed',
'spmid',
'plat_id',
'goto',
'report_flow_data',
'trackid',
'live_form',
'track_id',
'from',
'visit_id',
'extra_jump_from',
])
if (isPageSearch()) {
keysToRemove.add('vt')
}
if (isPageLiveRoom()) {
keysToRemove.add('bbid')
keysToRemove.add('ts')
}
const url = location.href
const urlObj = new URL(url)
const params = new URLSearchParams(urlObj.search)
const cleanParams = (url: string): string => {
try {
// 直播域名各种iframe页面(天选、抽奖)和活动页特殊处理
if (url.match(/live\.bilibili\.com\/(p\/html|activity|blackboard)/)) {
return url
}
const keysToRemove = new Set([
'from_source',
'spm_id_from',
'search_source',
'vd_source',
'unique_k',
'is_story_h5',
'from_spmid',
'share_plat',
'share_medium',
'share_from',
'share_source',
'share_tag',
'up_id',
'timestamp',
'mid',
'live_from',
'launch_id',
'session_id',
'share_session_id',
'broadcast_type',
'is_room_feed',
'spmid',
'plat_id',
'goto',
'report_flow_data',
'trackid',
'live_form',
'track_id',
'from',
'visit_id',
'extra_jump_from',
])
if (isPageSearch()) {
keysToRemove.add('vt')
}
if (isPageLiveRoom()) {
keysToRemove.add('bbid')
keysToRemove.add('ts')
}
const urlObj = new URL(url)
const params = new URLSearchParams(urlObj.search)

const temp = []
for (const k of params.keys()) {
keysToRemove.has(k) && temp.push(k)
}
for (const k of temp) {
params.delete(k)
}
if (params.get('p') === '1') {
params.delete('p')
}
const temp = []
for (const k of params.keys()) {
keysToRemove.has(k) && temp.push(k)
}
for (const k of temp) {
params.delete(k)
}
if (params.get('p') === '1') {
params.delete('p')
}

urlObj.search = params.toString()
const newURL = urlObj.toString().replace(/\/$/, '')
if (newURL !== url) {
history.replaceState(null, '', newURL)
urlObj.search = params.toString()
return urlObj.toString()
} catch (err) {
return url
}
}
URLCleanerInstance.cleanFnArr.push(cleanParams)
URLCleanerInstance.clean()
},
}),
// 隐藏页底 footer
Expand Down
75 changes: 41 additions & 34 deletions src/rules/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { debugRules as debug, error } from '../utils/logger'
import { matchAvidBvid, matchBvid, waitForEle } from '../utils/tool'
import { isPageFestival, isPagePlaylist, isPageVideo } from '../utils/page-type'
import { GM_getValue, GM_setValue, unsafeWindow } from '$'
import URLCleanerInstance from '../utils/url-cleaner'

/** 宽屏模式监听 */
let _isWide = unsafeWindow.isWide
Expand Down Expand Up @@ -90,45 +91,51 @@ if (isPageVideo() || isPagePlaylist()) {
itemID: 'video-page-bv2av',
description: 'BV号转AV号',
itemFunc: () => {
/**
* algo by bilibili-API-collect
* @see https://www.zhihu.com/question/381784377/answer/1099438784
* @see https://github.com/SocialSisterYi/bilibili-API-collect/issues/740
* @see https://socialsisteryi.github.io/bilibili-API-collect/docs/misc/bvid_desc.html
* @param bvid 输入BV号
* @returns 输出纯数字av号
*/
const XOR_CODE = 23442827791579n
const MASK_CODE = 2251799813685247n
const BASE = 58n
const data = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'
const dec = (bvid: string): number => {
const bvidArr = Array.from<string>(bvid)
;[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]]
;[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]]
bvidArr.splice(0, 3)
const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n)
return Number((tmp & MASK_CODE) ^ XOR_CODE)
}
const bv2av = (url: string): string => {
/**
* algo by bilibili-API-collect
* @see https://www.zhihu.com/question/381784377/answer/1099438784
* @see https://github.com/SocialSisterYi/bilibili-API-collect/issues/740
* @see https://socialsisteryi.github.io/bilibili-API-collect/docs/misc/bvid_desc.html
* @param bvid 输入BV号
* @returns 输出纯数字av号
*/
const XOR_CODE = 23442827791579n
const MASK_CODE = 2251799813685247n
const BASE = 58n
const data = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'
const dec = (bvid: string): number => {
const bvidArr = Array.from<string>(bvid)
;[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]]
;[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]]
bvidArr.splice(0, 3)
const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n)
return Number((tmp & MASK_CODE) ^ XOR_CODE)
}

try {
if (location.href.includes('bilibili.com/video/BV')) {
const bvid = matchBvid(location.href)
if (bvid) {
// 保留query string中分P参数, anchor中reply定位
let partNum = ''
const params = new URLSearchParams(location.search)
if (params.has('p')) {
partNum += `?p=${params.get('p')}`
try {
if (url.includes('bilibili.com/video/BV')) {
const bvid = matchBvid(url)
if (bvid) {
// 保留query string中分P参数, anchor中reply定位
const urlObj = new URL(url)
const params = new URLSearchParams(urlObj.search)
let partNum = ''
if (params.has('p')) {
partNum += `?p=${params.get('p')}`
}
const aid = dec(bvid)
return `https://www.bilibili.com/video/av${aid}/${partNum}${urlObj.hash}`
}
const aid = dec(bvid)
const newURL = `https://www.bilibili.com/video/av${aid}${partNum}${location.hash}`
history.replaceState(null, '', newURL)
}
return url
} catch (err) {
return url
}
} catch (err) {}
}
URLCleanerInstance.cleanFnArr.push(bv2av)
URLCleanerInstance.clean()
},
isItemFuncReload: true,
}),
// 净化分享, 默认开启, 关闭功能需刷新
new CheckboxItem({
Expand Down
73 changes: 73 additions & 0 deletions src/utils/url-cleaner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { unsafeWindow } from '$'
import { error } from './logger'

class URLCleaner {
origReplaceState = unsafeWindow.history.replaceState
origPushState = unsafeWindow.history.pushState

// URL清理函数
cleanFnArr: ((url: string) => string)[] = []

constructor() {
try {
this.hijack()
} catch (err) {
error('init URLCleaner error', err)
}
}

hijack() {
unsafeWindow.history.replaceState = (data: any, unused: string, url?: string | URL | null): void => {
try {
if (typeof url === 'string') {
// 修补url
if (!url.startsWith(location.origin) && !url.startsWith(location.hostname)) {
url = `${location.origin}${url.startsWith('/') ? '' : '/'}${url}`
}
const cleanURL = this.cleanFnArr.reduce((curr, fn) => fn(curr), url)
if (location.href.endsWith(cleanURL)) {
return
}
return this.origReplaceState.apply(unsafeWindow.history, [data, unused, cleanURL])
}
return this.origReplaceState.apply(unsafeWindow.history, [data, unused, url])
} catch (err) {
error('URLCleaner replaceState error', err)
return this.origReplaceState.apply(unsafeWindow.history, [data, unused, url])
}
}
unsafeWindow.history.pushState = (data: any, unused: string, url?: string | URL | null): void => {
try {
if (typeof url === 'string') {
if (!url.startsWith(location.origin) && !url.startsWith(location.hostname)) {
url = `${location.origin}${url.startsWith('/') ? '' : '/'}${url}`
}
const cleanURL = this.cleanFnArr.reduce((curr, fn) => fn(curr), url)
if (location.href.endsWith(cleanURL)) {
return
}
return this.origPushState.apply(unsafeWindow.history, [data, unused, cleanURL])
}
return this.origPushState.apply(unsafeWindow.history, [data, unused, url])
} catch (err) {
error('URLCleaner pushState error', err)
return this.origReplaceState.apply(unsafeWindow.history, [data, unused, url])
}
}
}

clean() {
try {
const cleanURL = this.cleanFnArr.reduce((curr, fn) => fn(curr), location.href)
if (location.href !== cleanURL) {
this.origReplaceState.apply(unsafeWindow.history, [null, '', cleanURL])
}
} catch (err) {
error('init URLCleaner error', err)
}
}
}

// 单例
const URLCleanerInstance = new URLCleaner()
export default URLCleanerInstance
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.7.0',
version: '3.7.1',
description:
'净化 B站/哔哩哔哩 页面,支持「精简功能、播放器净化、过滤视频、过滤评论、全站黑白名单」,提供 300+ 功能,定制自己的 B 站',
author: 'festoney8',
Expand Down

0 comments on commit 04384db

Please sign in to comment.