From e6dcb97b76a436d8878ee0d106067009c998da1f Mon Sep 17 00:00:00 2001 From: FoundTheWOUT Date: Thu, 25 Nov 2021 14:57:15 +0800 Subject: [PATCH 1/3] PlayerAgent support LightOff action. --- .../video/player/auto-light/index.ts | 32 +++++---- .../video/player/default-mode/index.ts | 6 +- .../player/intersection-actions/index.ts | 18 +++-- src/components/video/player-agent.ts | 51 ++++++++++++-- src/components/video/player-light.ts | 66 +++++++------------ 5 files changed, 103 insertions(+), 70 deletions(-) diff --git a/registry/lib/components/video/player/auto-light/index.ts b/registry/lib/components/video/player/auto-light/index.ts index 1b112c5ac..bd3b3a155 100644 --- a/registry/lib/components/video/player/auto-light/index.ts +++ b/registry/lib/components/video/player/auto-light/index.ts @@ -1,7 +1,10 @@ import { ComponentMetadata } from '@/components/types' +import { playerAgent } from '@/components/video/player-agent' import { videoChange } from '@/core/observer' import { allVideoUrls } from '@/core/utils/urls' +let playerAgentInstance + export const component: ComponentMetadata = { name: 'playerAutoLight', displayName: '播放时自动关灯', @@ -17,19 +20,26 @@ export const component: ComponentMetadata = { if (isEmbeddedPlayer()) { return } - const autoPlay = lodash.get( - JSON.parse(localStorage.getItem('bilibili_player_settings')), - 'video_status.autoplay', - false, - ) - videoChange(() => { - const video = dq('video') as HTMLVideoElement - if (autoPlay) { + + videoChange(async () => { + if (playerAgentInstance != null) { + const oldVideo = await playerAgentInstance.query.video.element() as HTMLVideoElement + oldVideo.removeEventListener('ended', lightOn) + oldVideo.removeEventListener('pause', lightOn) + oldVideo.removeEventListener('play', lightOff) + } + + playerAgentInstance = playerAgent + const { query, getPlayerConfig } = playerAgentInstance + const video = await query.video.element() as HTMLVideoElement + + if (getPlayerConfig('video_status.autoplay')) { lightOff() } - video.addEventListener('ended', () => lightOn()) - video.addEventListener('pause', () => lightOn()) - video.addEventListener('play', () => lightOff()) + + video.addEventListener('ended', lightOn) + video.addEventListener('pause', lightOn) + video.addEventListener('play', lightOff) }) }, } diff --git a/registry/lib/components/video/player/default-mode/index.ts b/registry/lib/components/video/player/default-mode/index.ts index e510bf2d4..abcd1fc0e 100644 --- a/registry/lib/components/video/player/default-mode/index.ts +++ b/registry/lib/components/video/player/default-mode/index.ts @@ -40,11 +40,7 @@ const entry: ComponentEntry = async ({ settings: { options } }) => { if (!video) { return } - const autoPlay = lodash.get( - JSON.parse(localStorage.getItem('bilibili_player_settings')), - 'video_status.autoplay', - false, - ) + const autoPlay = playerAgent.getPlayerConfig('video_status.autoplay') const action = actions.get(options.mode) const onplay = () => { const isNormalMode = !dq('body[class*=player-mode-]') diff --git a/registry/lib/components/video/player/intersection-actions/index.ts b/registry/lib/components/video/player/intersection-actions/index.ts index c7199bd69..0f06ef65d 100644 --- a/registry/lib/components/video/player/intersection-actions/index.ts +++ b/registry/lib/components/video/player/intersection-actions/index.ts @@ -23,11 +23,19 @@ export const component: ComponentMetadata = { pause: boolean light: boolean } - const { query: { video } } = playerAgent + const playerAgentInstance = playerAgent + const { query: { video } } = playerAgentInstance const videoEl = await video.element() as HTMLVideoElement // const playerWrap = await video.wrap() - const playerWrap = (dq('.player-wrap') || dq('.player-module')) as HTMLElement + // 如果有 video-player 优先的使用该盒子 + // 因为在稍后再看页面(medialist)视频也有 player-wrap + // 选择 player-wrap 会导致闪烁。 + const playerWrap = ( + document.getElementById('video-player') + ?? (dq('.player-wrap') || dq('.player-module')) + ) as HTMLElement + let observer: IntersectionObserver let intersectionLock = true // Lock intersection action @@ -101,11 +109,7 @@ export const component: ComponentMetadata = { ) function mountPlayListener() { - const autoPlay = lodash.get( - JSON.parse(localStorage.getItem('bilibili_player_settings')), - 'video_status.autoplay', - false, - ) + const autoPlay = playerAgentInstance.getPlayerConfig('video_status.autoplay') videoChange(async () => { if (autoPlay) { addPlayerOutEvent() diff --git a/src/components/video/player-agent.ts b/src/components/video/player-agent.ts index df3dec644..4722f2d05 100644 --- a/src/components/video/player-agent.ts +++ b/src/components/video/player-agent.ts @@ -61,6 +61,10 @@ interface PlayerQuery extends CustomNestedQuery { webFullscreen: QueryResult fullscreen: QueryResult } + settings: { + wrap: QueryResult, + lightOff: QueryResult + } } toastWrap: QueryResult danmakuTipLayer: QueryResult @@ -89,6 +93,9 @@ const click = (target: ElementQuery) => { export abstract class PlayerAgent { abstract type: AgentType abstract query: PlayerQuery + + abstract rawQueryObject: PlayerQuery + provideCustomQuery>( config: CustomQueryProvider, ) { @@ -125,6 +132,17 @@ export abstract class PlayerAgent { raiseEvent(checkbox, 'change') return checkbox.checked } + + abstract toggleLight(on: boolean): void + + // eslint-disable-next-line class-methods-use-this + getPlayerConfig(target:string) { + return lodash.get( + JSON.parse(localStorage.getItem('bilibili_player_settings')), + target, + false, + ) + } abstract isMute(): boolean /** 更改音量 (%) */ abstract changeVolume(change: number): number @@ -139,7 +157,7 @@ export class VideoPlayerAgent extends PlayerAgent { return unsafeWindow.player } type: AgentType = 'video' - query = selectorWrap({ + rawQueryObject = { playerWrap: '.player-wrap', bilibiliPlayer: '.bilibili-player', playerArea: '.bilibili-player-area', @@ -181,11 +199,16 @@ export class VideoPlayerAgent extends PlayerAgent { webFullscreen: '.bilibili-player-video-web-fullscreen', fullscreen: '.bilibili-player-video-btn-fullscreen', }, + settings: { + wrap: '.bilibili-player-video-btn-setting-wrap', + lightOff: '.bilibili-player-video-btn-setting-right-others-content-lightoff .bui-checkbox-input', + }, }, toastWrap: '.bilibili-player-video-toast-wrp', danmakuTipLayer: '.bilibili-player-dm-tip-wrap', danmakuSwitch: '.bilibili-player-video-danmaku-switch input', - }) as PlayerQuery + } + query = selectorWrap(this.rawQueryObject) as PlayerQuery isMute() { if (!this.nativeApi) { return null @@ -225,6 +248,12 @@ export class VideoPlayerAgent extends PlayerAgent { this.nativeApi.seek(video.currentTime + change, video.paused) return this.nativeApi.getCurrentTime() } + async toggleLight(on:boolean) { + // const checkbox = this.query.control.settings.lightOff.sync() as HTMLInputElement + const checkbox = await select(this.rawQueryObject.control.settings.lightOff) as HTMLInputElement + checkbox.checked = !on + raiseEvent(checkbox, 'change') + } } export class BwpPlayerAgent extends VideoPlayerAgent { type: AgentType = 'bwp' @@ -235,7 +264,7 @@ export class BwpPlayerAgent extends VideoPlayerAgent { } export class BangumiPlayerAgent extends PlayerAgent { type: AgentType = 'bangumi' - query = selectorWrap({ + rawQueryObject = { playerWrap: '.player-module', bilibiliPlayer: '.bpx-player-container', playerArea: '.bpx-player-primary-area', @@ -277,11 +306,16 @@ export class BangumiPlayerAgent extends PlayerAgent { webFullscreen: '.squirtle-video-pagefullscreen', fullscreen: '.squirtle-video-fullscreen', }, + settings: { + wrap: '.squirtle-setting-wrap', + lightOff: '.squirtle-lightoff', + }, }, toastWrap: '.bpx-player-tooltip-area', danmakuTipLayer: '.bpx-player-dialog-wrap', danmakuSwitch: '.bpx-player-dm-switch input', - }) as PlayerQuery + } + query = selectorWrap(this.rawQueryObject) as PlayerQuery constructor() { super() bpxPlayerPolyfill() @@ -321,6 +355,15 @@ export class BangumiPlayerAgent extends PlayerAgent { video.currentTime = lodash.clamp(video.currentTime + change, 0, video.duration) return video.currentTime } + toggleLight(on: boolean) { + const checkbox = this.query.control.settings.lightOff.sync() + const canLightOff = !checkbox.classList.contains('active') && !on + const canLightOn = checkbox.classList.contains('active') && on + + if (canLightOff || canLightOn) { + checkbox.dispatchEvent(new MouseEvent('click')) + } + } } export const playerAgent = (() => { diff --git a/src/components/video/player-light.ts b/src/components/video/player-light.ts index 289dfa109..98380a813 100644 --- a/src/components/video/player-light.ts +++ b/src/components/video/player-light.ts @@ -1,52 +1,32 @@ -import { select } from '@/core/spin-query' -import { matchUrlPattern, raiseEvent, none } from '@/core/utils' +import { matchUrlPattern, none } from '@/core/utils' import { loadLazyPlayerSettingsPanel } from '@/core/utils/lazy-panel' import { playerUrls } from '@/core/utils/urls' +import { playerAgent } from './player-agent' -let initialized = false +// let initialized = false -const setLightAdaptor = { - bangumi: (doLightOn: boolean) => async () => { - if (!initialized) { - loadLazyPlayerSettingsPanel('.squirtle-setting-wrap', '.squirtle-video-setting') - initialized = true - } - const checkbox = await select('.squirtle-lightoff') as HTMLElement - const event = new MouseEvent('click') - // 处于关灯状态,要开灯 -> 开灯 - checkbox.classList.contains('active') && doLightOn ? checkbox.dispatchEvent(event) : '' - // 处于开灯状态,要关灯 -> 关灯 - !checkbox.classList.contains('active') && !doLightOn ? checkbox.dispatchEvent(event) : '' - }, - fallback: (on: boolean) => { - if (!playerUrls.some(url => matchUrlPattern(url))) { - return none - } - return async () => { - if (!initialized) { - loadLazyPlayerSettingsPanel( - '.bilibili-player-video-btn-setting', - '.bilibili-player-video-btn-setting-wrap', - { - style: '.bilibili-player-video-btn-setting-wrap { display: none !important }', - }, - ) - initialized = true - } - const checkbox = await select('.bilibili-player-video-btn-setting-right-others-content-lightoff .bui-checkbox-input') as HTMLInputElement - checkbox.checked = !on - raiseEvent(checkbox, 'change') - } - }, -} +const setLight = (on: boolean) => { + if (!playerUrls.some(url => matchUrlPattern(url))) { + return none + } + return async () => { + const playerAgentInstance = playerAgent + const { + rawQueryObject: { + control: { settings, buttons }, + }, + } = playerAgentInstance -let setLight = setLightAdaptor.fallback -for (const key in setLightAdaptor) { - if (Object.prototype.hasOwnProperty.call(setLightAdaptor, key)) { - if (matchUrlPattern(key)) { - setLight = setLightAdaptor[key] - } + // if (!initialized) { + loadLazyPlayerSettingsPanel( + buttons.settings, + settings.wrap, + ) + // initialized = true + // } + playerAgentInstance.toggleLight(on) } } + export const lightOn = setLight(true) export const lightOff = setLight(false) From daad58fd60e5ceee2c368a2bd9e458f53be9eb74 Mon Sep 17 00:00:00 2001 From: FoundTheWOUT Date: Thu, 25 Nov 2021 15:25:22 +0800 Subject: [PATCH 2/3] Revert player-agent --- src/components/video/player-agent.ts | 15 +++++---------- src/components/video/player-light.ts | 6 +++--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/video/player-agent.ts b/src/components/video/player-agent.ts index 4722f2d05..144b24700 100644 --- a/src/components/video/player-agent.ts +++ b/src/components/video/player-agent.ts @@ -93,9 +93,6 @@ const click = (target: ElementQuery) => { export abstract class PlayerAgent { abstract type: AgentType abstract query: PlayerQuery - - abstract rawQueryObject: PlayerQuery - provideCustomQuery>( config: CustomQueryProvider, ) { @@ -157,7 +154,7 @@ export class VideoPlayerAgent extends PlayerAgent { return unsafeWindow.player } type: AgentType = 'video' - rawQueryObject = { + query = selectorWrap({ playerWrap: '.player-wrap', bilibiliPlayer: '.bilibili-player', playerArea: '.bilibili-player-area', @@ -207,8 +204,7 @@ export class VideoPlayerAgent extends PlayerAgent { toastWrap: '.bilibili-player-video-toast-wrp', danmakuTipLayer: '.bilibili-player-dm-tip-wrap', danmakuSwitch: '.bilibili-player-video-danmaku-switch input', - } - query = selectorWrap(this.rawQueryObject) as PlayerQuery + }) as PlayerQuery isMute() { if (!this.nativeApi) { return null @@ -250,7 +246,7 @@ export class VideoPlayerAgent extends PlayerAgent { } async toggleLight(on:boolean) { // const checkbox = this.query.control.settings.lightOff.sync() as HTMLInputElement - const checkbox = await select(this.rawQueryObject.control.settings.lightOff) as HTMLInputElement + const checkbox = await select(this.query.control.settings.lightOff.selector) as HTMLInputElement checkbox.checked = !on raiseEvent(checkbox, 'change') } @@ -264,7 +260,7 @@ export class BwpPlayerAgent extends VideoPlayerAgent { } export class BangumiPlayerAgent extends PlayerAgent { type: AgentType = 'bangumi' - rawQueryObject = { + query = selectorWrap({ playerWrap: '.player-module', bilibiliPlayer: '.bpx-player-container', playerArea: '.bpx-player-primary-area', @@ -314,8 +310,7 @@ export class BangumiPlayerAgent extends PlayerAgent { toastWrap: '.bpx-player-tooltip-area', danmakuTipLayer: '.bpx-player-dialog-wrap', danmakuSwitch: '.bpx-player-dm-switch input', - } - query = selectorWrap(this.rawQueryObject) as PlayerQuery + }) as PlayerQuery constructor() { super() bpxPlayerPolyfill() diff --git a/src/components/video/player-light.ts b/src/components/video/player-light.ts index 98380a813..f876ba519 100644 --- a/src/components/video/player-light.ts +++ b/src/components/video/player-light.ts @@ -12,15 +12,15 @@ const setLight = (on: boolean) => { return async () => { const playerAgentInstance = playerAgent const { - rawQueryObject: { + query: { control: { settings, buttons }, }, } = playerAgentInstance // if (!initialized) { loadLazyPlayerSettingsPanel( - buttons.settings, - settings.wrap, + buttons.settings.selector, + settings.wrap.selector, ) // initialized = true // } From c32ada8b69964a45570fdccf618597c979b49729 Mon Sep 17 00:00:00 2001 From: FoundTheWOUT Date: Fri, 26 Nov 2021 11:30:49 +0800 Subject: [PATCH 3/3] Feat player-agent: isAutoPlay --- registry/lib/components/video/player/auto-light/index.ts | 8 ++++---- .../lib/components/video/player/default-mode/index.ts | 3 +-- .../components/video/player/intersection-actions/index.ts | 6 ++---- src/components/video/player-agent.ts | 8 ++++++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/registry/lib/components/video/player/auto-light/index.ts b/registry/lib/components/video/player/auto-light/index.ts index bd3b3a155..cc95cc0e6 100644 --- a/registry/lib/components/video/player/auto-light/index.ts +++ b/registry/lib/components/video/player/auto-light/index.ts @@ -23,17 +23,17 @@ export const component: ComponentMetadata = { videoChange(async () => { if (playerAgentInstance != null) { - const oldVideo = await playerAgentInstance.query.video.element() as HTMLVideoElement + const oldVideo = await playerAgentInstance.query.video.element() oldVideo.removeEventListener('ended', lightOn) oldVideo.removeEventListener('pause', lightOn) oldVideo.removeEventListener('play', lightOff) } playerAgentInstance = playerAgent - const { query, getPlayerConfig } = playerAgentInstance - const video = await query.video.element() as HTMLVideoElement + const { query, isAutoPlay } = playerAgentInstance + const video = await query.video.element() - if (getPlayerConfig('video_status.autoplay')) { + if (isAutoPlay()) { lightOff() } diff --git a/registry/lib/components/video/player/default-mode/index.ts b/registry/lib/components/video/player/default-mode/index.ts index abcd1fc0e..462e54165 100644 --- a/registry/lib/components/video/player/default-mode/index.ts +++ b/registry/lib/components/video/player/default-mode/index.ts @@ -40,7 +40,6 @@ const entry: ComponentEntry = async ({ settings: { options } }) => { if (!video) { return } - const autoPlay = playerAgent.getPlayerConfig('video_status.autoplay') const action = actions.get(options.mode) const onplay = () => { const isNormalMode = !dq('body[class*=player-mode-]') @@ -48,7 +47,7 @@ const entry: ComponentEntry = async ({ settings: { options } }) => { action() } } - if (options.applyOnPlay && !autoPlay) { + if (options.applyOnPlay && !playerAgent.isAutoPlay()) { video.addEventListener('play', onplay, { once: true }) } else { onplay() diff --git a/registry/lib/components/video/player/intersection-actions/index.ts b/registry/lib/components/video/player/intersection-actions/index.ts index 0f06ef65d..99cfcd709 100644 --- a/registry/lib/components/video/player/intersection-actions/index.ts +++ b/registry/lib/components/video/player/intersection-actions/index.ts @@ -23,8 +23,7 @@ export const component: ComponentMetadata = { pause: boolean light: boolean } - const playerAgentInstance = playerAgent - const { query: { video } } = playerAgentInstance + const { query: { video } } = playerAgent const videoEl = await video.element() as HTMLVideoElement // const playerWrap = await video.wrap() @@ -109,9 +108,8 @@ export const component: ComponentMetadata = { ) function mountPlayListener() { - const autoPlay = playerAgentInstance.getPlayerConfig('video_status.autoplay') videoChange(async () => { - if (autoPlay) { + if (playerAgent.isAutoPlay()) { addPlayerOutEvent() } videoEl.addEventListener('play', addPlayerOutEvent) diff --git a/src/components/video/player-agent.ts b/src/components/video/player-agent.ts index 144b24700..9715bbb78 100644 --- a/src/components/video/player-agent.ts +++ b/src/components/video/player-agent.ts @@ -140,6 +140,11 @@ export abstract class PlayerAgent { false, ) } + + isAutoPlay() { + return this.getPlayerConfig('video_status.autoplay') + } + abstract isMute(): boolean /** 更改音量 (%) */ abstract changeVolume(change: number): number @@ -245,8 +250,7 @@ export class VideoPlayerAgent extends PlayerAgent { return this.nativeApi.getCurrentTime() } async toggleLight(on:boolean) { - // const checkbox = this.query.control.settings.lightOff.sync() as HTMLInputElement - const checkbox = await select(this.query.control.settings.lightOff.selector) as HTMLInputElement + const checkbox = await this.query.control.settings.lightOff() as HTMLInputElement checkbox.checked = !on raiseEvent(checkbox, 'change') }