Skip to content

Commit

Permalink
Merge pull request #459 from andywang425:dev
Browse files Browse the repository at this point in the history
7.1.1
close #457
  • Loading branch information
andywang425 authored Sep 5, 2023
2 parents 6f17f10 + 689d5a1 commit dd953b2
Show file tree
Hide file tree
Showing 41 changed files with 1,788 additions and 706 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# 更新日志

## [7.1.1] - 2023-9-5

## 新增

- 屏蔽挂机检测模块
- 更多模块运行时机
- 支持指定模块运行的frame,以及是否在默认模块运行完后运行

## 调整

- 部分模块微调:运行时机,运行frame,是否在默认模块运行完后运行
- 对体验优化板块中的所有模块进行了优化
- 由于ajax-hook存在一个对脚本影响较大的bug,暂时从cdn改为本地修改后的

## 修复

- 继续尝试修复部分情况下脚本被注入得太早导致完全失效的Bug

## [7.1.0] - 2023-8-30

## 新增
Expand Down
13 changes: 3 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,10 @@ npm run preview

## 已知问题

如果要调试或开发涉及到拦截网络请求(使用ajax-hook或fetch-hook)的模块,需检查一下脚本有没有被及时注入到页面上。因为如果注入得太晚会错过一些网络请求。
如果要开发或调试运行时机很早的模块,建议先编译脚本(npm run build)然后运行编译后的脚本。在 dev 状态下调试这类功能可能会遇到以下问题:

你可以在控制台里找到一条输出脚本被注入时的页面加载状态的日志:`document.readyState <状态>`
如果状态是`loading`那就没问题,但假如状态是`interactive`甚至`complete`,那就说明脚本被注入得太晚了。

造成这种现象的原因在开发状态下(npm run dev),首先要通过发送网络请求从vite获取到BLTH所需的各个文件,然后才能把BLTH注入到页面上。
在这个过程中B站的js可能先一步被获取到并执行,导致注入BLTH的时机被延后。

建议把浏览器开发者工具网络选项卡(Network)中的停用缓存(Disable cache)勾上,这样可以延后B站的js被获取到的时间。勾选之后大部分情况下脚本都能被及时注入。

当然先编译脚本然后运行编译后的脚本也是可以的,但这样就比较麻烦。
- 脚本会被注入得太晚,无法很好地测试
- 脚本会被注入得太早导致`document.head``null`而报错。

## commit 规范

Expand Down
963 changes: 632 additions & 331 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "bilibili-live-tasks-helper",
"private": true,
"version": "7.1.0",
"version": "7.1.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down Expand Up @@ -38,7 +38,7 @@
"terser": "^5.18.2",
"typescript": "^5.0.4",
"vite": "^4.3.5",
"vite-plugin-monkey": "^3.2.0",
"vite-plugin-monkey": "^3.5.0",
"vue-tsc": "^1.6.4"
}
}
9 changes: 4 additions & 5 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { useUIStore } from './stores/useUIStore'
import PanelHeader from './components/PanelHeader.vue'
import PanelAside from './components/PanelAside.vue'
import PanelMain from './components/PanelMain.vue'
import { dce, dq, pollingQuery } from './library/dom'
import { dce, dq, pollingQuery, isSelfTopFrame, topFrameDocuemntElement } from './library/dom'
import hotkeys from 'hotkeys-js'
import _ from 'lodash'
import { isSelfTopFrame, topFrameDocuemnt } from './library/dom'
import Logger from './library/logger'
const uiStore = useUIStore()
Expand Down Expand Up @@ -56,13 +55,13 @@ if (livePlayer) {
button.innerText = uiStore.isShowPanelButtonText
playerHeaderLeft.append(button)
if (!isSelfTopFrame()) {
// 在特殊直播间,脚本所在的 TargetFrame 只占屏幕中间一块地方
// 在特殊直播间,脚本所在的目标 frame 只占屏幕中间一块地方
// 如果焦点不在里面,快捷键会失效
// 所以这里额外把 hotkeys 注入到顶层 frame 确保快捷键总是可用
hotkeys(
'alt+b',
{
element: topFrameDocuemnt() as any
element: topFrameDocuemntElement()
},
throttleButtoOnClick
)
Expand All @@ -72,7 +71,7 @@ if (livePlayer) {
.catch(() => logger.error("Can't find playerHeaderLeft in time"))
// 监听页面缩放,调整控制面板大小
// 因为这个操作频率不高就不节流或防抖了
window.onresize = setPanelSize
window.addEventListener('resize', () => setPanelSize())
// 监听 html 根节点和 body 节点
// 主要是为了适配滚动条的显示/隐藏和实验室中的功能
const observer = new MutationObserver(() => setPanelSize())
Expand Down
6 changes: 6 additions & 0 deletions src/components/EnhanceExperience.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ const qualityDescList = ['原画', '蓝光PRO', '蓝光', '超清PRO', '超清',
<Info id="EnhanceExperience.noReport" />
</el-space>
</el-row>
<el-row>
<el-space wrap>
<el-switch v-model="config.sleepDetection.enabled" active-text="屏蔽挂机检测" />
<Info id="EnhanceExperience.sleepDetection" />
</el-space>
</el-row>
<el-divider />
</div>
</template>
4 changes: 2 additions & 2 deletions src/components/LiveTasks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ const handleEditList = () => {
}
})
// 利用 emitter 通知 BiliInfo 模块去获取数据
moduleStore.emitter.emit('BiliInfo', {
target: 'getFansMetals'
moduleStore.emitter.emit('Default_FansMedals', {
module: 'LiveTasks'
})
} else {
initSelection(medalInfoTableData.value)
Expand Down
127 changes: 127 additions & 0 deletions src/library/ajax-hook/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
interface XMLHttpRequestProxy extends XMLHttpRequest {
responseText: string
readyState: number
response: any
responseURL: string
responseXML: Document | null
status: number
statusText: string
xhr: OriginXMLHttpRequest
}

interface OriginXMLHttpRequest extends XMLHttpRequest {
getProxy(): XMLHttpRequestProxy
}

interface AttrGetterAndSetter<T = any> {
getter?: (value: T, xhr: OriginXMLHttpRequest) => T
setter?: (value: T, xhr: OriginXMLHttpRequest) => T
}

interface XhrRequestConfig {
method: string
url: string
headers: any
body: any
async: boolean
user: string
password: string
withCredentials: boolean
xhr: OriginXMLHttpRequest
}

interface XhrResponse {
config: XhrRequestConfig
headers: any
response: any
status: number
statusText?: string
}

type XhrErrorType = 'error' | 'timeout' | 'abort'

interface XhrError {
config: XhrRequestConfig
type: XhrErrorType
}

interface Hooks {
onreadystatechange?:
| ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any)
| null
onabort?:
| ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any)
| null
onerror?:
| ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any)
| null
onload?: ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any) | null
onloadend?:
| ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any)
| null
onloadstart?:
| ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any)
| null
onprogress?:
| ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any)
| null
ontimeout?:
| ((this: XMLHttpRequestProxy, xhr: OriginXMLHttpRequest, ev: ProgressEvent) => any)
| null
abort?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
getAllResponseHeaders?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
getResponseHeader?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
open?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
overrideMimeType?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
send?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
setRequestHeader?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
addEventListener?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any
removeEventListener?: (args: Array<any>, xhr: OriginXMLHttpRequest) => any

response?: AttrGetterAndSetter
responseText?: AttrGetterAndSetter<string>
readyState?: AttrGetterAndSetter<number>
responseType?: AttrGetterAndSetter<XMLHttpRequestResponseType>
responseURL?: AttrGetterAndSetter<string>
responseXML?: AttrGetterAndSetter<Document | null>
status?: AttrGetterAndSetter<number>
statusText?: AttrGetterAndSetter<string>
timeout?: AttrGetterAndSetter<number>
upload?: AttrGetterAndSetter<XMLHttpRequestUpload>
withCredentials?: AttrGetterAndSetter<boolean>
}

interface XhrHandler {
resolve(response: XhrResponse): void

reject(err: XhrError): void
}

interface XhrRequestHandler extends XhrHandler {
next(config: XhrRequestConfig): void
}

interface XhrResponseHandler extends XhrHandler {
next(response: XhrResponse): void
}

interface XhrErrorHandler extends XhrHandler {
next(error: XhrError): void
}

interface Proxy {
onRequest?: (config: XhrRequestConfig, handler: XhrRequestHandler) => void
onResponse?: (response: XhrResponse, handler: XhrResponseHandler) => void
onError?: (err: XhrError, handler: XhrErrorHandler) => void
}

export function proxy(
proxy: Proxy,
win?: Window
): { originXhr: XMLHttpRequest; unProxy: () => void }

export function unProxy(win?: Window)

export function hook(hooks: Hooks, win?: Window): { originXhr: XMLHttpRequest; unHook: () => void }

export function unHook(win?: Window)
10 changes: 10 additions & 0 deletions src/library/ajax-hook/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* author: wendux
* email: 824783146@qq.com
* source code: https://github.com/wendux/Ajax-hook
**/
export { hook } from './src/xhr-hook'
export { proxy } from './src/xhr-proxy'

// 因为原版的ajax-hook存在一个bug,https://github.com/wendux/ajax-hook/pull/122
// 所以暂时先使用 https://github.com/cyfung1031 修改后的版本
131 changes: 131 additions & 0 deletions src/library/ajax-hook/src/xhr-hook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* author: wendux
* email: 824783146@qq.com
* source code: https://github.com/wendux/Ajax-hook
*/

export var events = ['load', 'loadend', 'timeout', 'error', 'readystatechange', 'abort']

var OriginXhr = '__origin_xhr'

export function configEvent(event, xhrProxy) {
var e = {}
for (var attr in event) e[attr] = event[attr]
// xhrProxy instead
e.target = e.currentTarget = xhrProxy
return e
}

export function hook(proxy, win) {
win = win || window
var originXhr = win.XMLHttpRequest

var HookXMLHttpRequest = function () {
// We shouldn't hookAjax XMLHttpRequest.prototype because we can't
// guarantee that all attributes are on the prototype。
// Instead, hooking XMLHttpRequest instance can avoid this problem.

var xhr = new originXhr()

// Generate all callbacks(eg. onload) are enumerable (not undefined).
for (var i = 0; i < events.length; ++i) {
var key = 'on' + events[i]
if (xhr[key] === undefined) xhr[key] = null
}

for (var attr in xhr) {
var type = ''
try {
type = typeof xhr[attr] // May cause exception on some browser
} catch (e) {}
if (type === 'function') {
// hookAjax methods of xhr, such as `open`、`send` ...
this[attr] = hookFunction(attr)
} else if (attr !== OriginXhr) {
Object.defineProperty(this, attr, {
get: getterFactory(attr),
set: setterFactory(attr),
enumerable: true
})
}
}
var that = this
xhr.getProxy = function () {
return that
}
this[OriginXhr] = xhr
}

HookXMLHttpRequest.prototype = originXhr.prototype
HookXMLHttpRequest.prototype.constructor = HookXMLHttpRequest

win.XMLHttpRequest = HookXMLHttpRequest

Object.assign(win.XMLHttpRequest, {
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4
})

// Generate getter for attributes of xhr
function getterFactory(attr) {
return function () {
var v = this.hasOwnProperty(attr + '_') ? this[attr + '_'] : this[OriginXhr][attr]
var attrGetterHook = (proxy[attr] || {})['getter']
return (attrGetterHook && attrGetterHook(v, this)) || v
}
}

// Generate setter for attributes of xhr; by this we have an opportunity
// to hookAjax event callbacks (eg: `onload`) of xhr;
function setterFactory(attr) {
return function (v) {
var xhr = this[OriginXhr]
var that = this
var hook = proxy[attr]
// hookAjax event callbacks such as `onload`、`onreadystatechange`...
if (attr.substring(0, 2) === 'on') {
that[attr + '_'] = v
xhr[attr] = function (e) {
e = configEvent(e, that)
var ret = proxy[attr] && proxy[attr].call(that, xhr, e)
ret || v.call(that, e)
}
} else {
//If the attribute isn't writable, generate proxy attribute
var attrSetterHook = (hook || {})['setter']
v = (attrSetterHook && attrSetterHook(v, that)) || v
this[attr + '_'] = v
try {
// Not all attributes of xhr are writable(setter may undefined).
xhr[attr] = v
} catch (e) {}
}
}
}

// Hook methods of xhr.
function hookFunction(fun) {
return function () {
var args = [].slice.call(arguments)
if (proxy[fun]) {
var ret = proxy[fun].call(this, args, this[OriginXhr])
// If the proxy return value exists, return it directly,
// otherwise call the function of xhr.
if (ret) return ret
}
return this[OriginXhr][fun].apply(this[OriginXhr], args)
}
}

function unHook() {
win.XMLHttpRequest = originXhr
HookXMLHttpRequest.prototype.constructor = originXhr
originXhr = undefined
}

// Return the real XMLHttpRequest and unHook func
return { originXhr, unHook }
}
Loading

0 comments on commit dd953b2

Please sign in to comment.