Skip to content

Commit

Permalink
Add new player API and nano API (#4341)
Browse files Browse the repository at this point in the history
  • Loading branch information
the1812 committed Nov 19, 2023
1 parent 30666cb commit 8894659
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 106 deletions.
38 changes: 5 additions & 33 deletions src/components/video/player-agent/bangumi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,46 +59,18 @@ export class BangumiPlayerAgent extends PlayerAgent {
super()
bpxPlayerPolyfill()
}
isMute() {
const icon = this.query.control.buttons.volume.sync() as HTMLElement
return icon?.classList.contains('squirtle-volume-mute-state') ?? false
}
changeVolume(change: number) {
const video = this.query.video.element.sync() as HTMLVideoElement
if (!video) {
return null
}
video.volume = lodash.clamp(video.volume + change / 100, 0, 1)
return Math.round(video.volume * 100)
}

seek(time: number) {
const video = this.query.video.element.sync() as HTMLVideoElement
if (!video) {
return null
const seekResult = super.seek(time)
if (seekResult === null) {
return seekResult
}
video.play()
setTimeout(() => {
video.currentTime = lodash.clamp(time, 0, video.duration)
const toastText = dq('.bpx-player-toast-row .bpx-player-toast-item .bpx-player-toast-text')
if (toastText?.textContent?.startsWith('已为您定位至')) {
toastText.textContent = '已为您定位至00:00'
}
})
return video.currentTime
}
changeTime(change: number) {
const video = this.query.video.element.sync() as HTMLVideoElement
if (!video) {
return null
}
video.currentTime = lodash.clamp(video.currentTime + change, 0, video.duration)
return video.currentTime
}
async toggleLight(on: boolean) {
const checkbox = this.query.control.settings.lightOff.sync()
// 开灯状态 && 关灯 -> 关灯
!checkbox.classList.contains('active') && !on && checkbox.click()
// 关灯状态 && 开灯 -> 开灯
checkbox.classList.contains('active') && on && checkbox.click()
return seekResult
}
}
146 changes: 134 additions & 12 deletions src/components/video/player-agent/base.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable class-methods-use-this */
import { select } from '@/core/spin-query'
import { raiseEvent } from '@/core/utils'
import {
Expand Down Expand Up @@ -29,9 +30,21 @@ export const click = (target: ElementQuery) => {
button?.click()
return button
}
export abstract class PlayerAgent {

export enum PlayerAgentEventTypes {
Play = 'play',
}
export abstract class PlayerAgent
extends EventTarget
implements EnumEventTarget<`${PlayerAgentEventTypes}`>
{
abstract type: AgentType
abstract query: PlayerQuery<ElementQuery>

constructor() {
super()
}

provideCustomQuery<CustomQueryType extends CustomQuery<string>>(
config: CustomQueryProvider<CustomQueryType>,
) {
Expand Down Expand Up @@ -71,19 +84,27 @@ export abstract class PlayerAgent {

/** true 开灯,false 关灯 */
async toggleLight(on?: boolean) {
const checkbox = (await this.query.control.settings.lightOff()) as HTMLInputElement
if (!this.nativeApi) {
return null
}
const isCurrentLightOff = this.nativeApi.getLightOff()
// 无指定参数, 直接 toggle
if (on === undefined) {
checkbox.click()
return
this.nativeApi.setLightOff(!isCurrentLightOff)
return !isCurrentLightOff
}
// 关灯状态 && 要开灯 -> 开灯
checkbox.checked && on && checkbox.click()
// 开灯状态 && 要关灯 -> 关灯
!checkbox.checked && !on && checkbox.click()
if (on && isCurrentLightOff) {
this.nativeApi.setLightOff(true)
return true
}
if (!on && !isCurrentLightOff) {
this.nativeApi.setLightOff(false)
return false
}
return null
}

// eslint-disable-next-line class-methods-use-this
getPlayerConfig(target: string) {
return lodash.get(JSON.parse(localStorage.getItem('bilibili_player_settings')), target, false)
}
Expand All @@ -92,11 +113,112 @@ export abstract class PlayerAgent {
return this.getPlayerConfig('video_status.autoplay')
}

abstract isMute(): boolean
// https://github.com/the1812/Bilibili-Evolved/discussions/4341
get nativeApi() {
return unsafeWindow.player || window.playerRaw
}

get nanoApi() {
return unsafeWindow.nano
}

private eventHandlerMap = new Map<
EventListenerOrEventListenerObject,
{
handler: () => void
options: boolean | AddEventListenerOptions
}
>()

addEventListener(
type: `${PlayerAgentEventTypes}`,
callback: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void {
super.addEventListener(type, callback, options)
switch (type) {
default: {
break
}
case PlayerAgentEventTypes.Play: {
const handler = () => {
this.dispatchEvent(new Event(PlayerAgentEventTypes.Play))
}
if (typeof options === 'object' && options.once) {
this.nativeApi.once(this.nanoApi.EventType.Player_Initialized, handler)
} else {
this.nativeApi.on(this.nanoApi.EventType.Player_Initialized, handler)
}
this.eventHandlerMap.set(callback, {
handler,
options,
})
}
}
}

removeEventListener(
type: `${PlayerAgentEventTypes}`,
callback: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions,
): void {
super.removeEventListener(type, callback, options)
switch (type) {
default: {
break
}
case PlayerAgentEventTypes.Play: {
const handlerData = this.eventHandlerMap.get(callback)
if (!handlerData || lodash.isEqual(options, handlerData.options)) {
break
}
this.nativeApi.off(this.nanoApi.EventType.Player_Initialized, handlerData.handler)
}
}
}

/** 获取是否静音 */
isMute() {
if (!this.nativeApi) {
return null
}
if (this.nativeApi.isMuted) {
return this.nativeApi.isMuted()
}
return this.nativeApi.isMute()
}
/** 更改音量 (%) */
abstract changeVolume(change: number): number
changeVolume(change: number) {
if (!this.nativeApi) {
return null
}
if (this.nativeApi.getVolume) {
const current = this.nativeApi.getVolume()
this.nativeApi.setVolume(current + change / 100)
return Math.round(this.nativeApi.getVolume() * 100)
}
const current = this.nativeApi.volume()
this.nativeApi.volume(current + change / 100)
return Math.round(this.nativeApi.volume() * 100)
}
/** 跳转到指定时间 */
abstract seek(time: number): number
seek(time: number) {
if (!this.nativeApi) {
return null
}
this.nativeApi.seek(time)
return this.nativeApi.getCurrentTime() as number
}
/** 更改时间 */
abstract changeTime(change: number): number
changeTime(change: number) {
if (!this.nativeApi) {
return null
}
const video = this.query.video.element.sync() as HTMLVideoElement
if (!video) {
return null
}
this.nativeApi.seek(video.currentTime + change, video.paused)
return this.nativeApi.getCurrentTime()
}
}
18 changes: 1 addition & 17 deletions src/components/video/player-agent/video-player-mixed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { selectorWrap } from './base'
import { PlayerQuery, ElementQuery } from './types'
import { VideoPlayerV2Agent } from './video-player-v2'

/** 兼容旧版播放器和 BPX 播放器的 VideoPlayerAgent */
export class VideoPlayerMixedAgent extends VideoPlayerV2Agent {
query = selectorWrap({
playerWrap: '.player-wrap',
Expand Down Expand Up @@ -63,21 +64,4 @@ export class VideoPlayerMixedAgent extends VideoPlayerV2Agent {
this.checkBwpVideo()
v3PlayerPolyfill()
}

seek(time: number) {
if (!this.nativeApi) {
return null
}
this.nativeApi.play()
setTimeout(() => {
this.nativeApi.seek(time)
const toastText = dq(
'.bilibili-player-video-toast-bottom .bilibili-player-video-toast-item:first-child .bilibili-player-video-toast-item-text span:nth-child(2)',
)
if (toastText) {
toastText.textContent = ' 00:00'
}
})
return this.nativeApi.getCurrentTime()
}
}
46 changes: 4 additions & 42 deletions src/components/video/player-agent/video-player-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { PlayerAgent, selectorWrap } from './base'
import { AgentType, PlayerQuery, ElementQuery } from './types'

export class VideoPlayerV2Agent extends PlayerAgent {
// eslint-disable-next-line class-methods-use-this
get nativeApi() {
return unsafeWindow.player
}
type: AgentType = 'video'
query = selectorWrap({
playerWrap: '.player-wrap',
Expand Down Expand Up @@ -89,53 +85,19 @@ export class VideoPlayerV2Agent extends PlayerAgent {
})()
}

isMute() {
if (!this.nativeApi) {
return null
}
if (this.nativeApi.isMuted) {
return this.nativeApi.isMuted()
}
return this.nativeApi.isMute()
}
changeVolume(change: number) {
if (!this.nativeApi) {
return null
}
if (this.nativeApi.getVolume) {
const current = this.nativeApi.getVolume()
this.nativeApi.setVolume(current + change / 100)
return Math.round(this.nativeApi.getVolume() * 100)
}
const current = this.nativeApi.volume()
this.nativeApi.volume(current + change / 100)
return Math.round(this.nativeApi.volume() * 100)
}
seek(time: number) {
if (!this.nativeApi) {
return null
const seekResult = super.seek(time)
if (seekResult === null) {
return seekResult
}
this.nativeApi.play()
setTimeout(() => {
this.nativeApi.seek(time)
const toastText = dq(
'.bilibili-player-video-toast-bottom .bilibili-player-video-toast-item:first-child .bilibili-player-video-toast-item-text span:nth-child(2)',
)
if (toastText) {
toastText.textContent = ' 00:00'
}
})
return this.nativeApi.getCurrentTime()
}
changeTime(change: number) {
if (!this.nativeApi) {
return null
}
const video = this.query.video.element.sync() as HTMLVideoElement
if (!video) {
return null
}
this.nativeApi.seek(video.currentTime + change, video.paused)
return this.nativeApi.getCurrentTime()
return seekResult
}
}
3 changes: 1 addition & 2 deletions src/core/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,7 @@ export const isNotHtml = () => document.contentType !== 'text/html'
* @param eventName 事件名称
*/
export const raiseEvent = (element: HTMLElement, eventName: string) => {
const event = document.createEvent('HTMLEvents')
event.initEvent(eventName, true, true)
const event = new Event(eventName)
element.dispatchEvent(event)
}
/** 根据图片URL生成 `srcset`, 范围从 `@1x` 至 `@4x`, 每 `0.25x` 产生一个 `src`
Expand Down

0 comments on commit 8894659

Please sign in to comment.