mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge branch 'the1812:preview-features' into preview-features
This commit is contained in:
commit
c4c1c1c2ec
@ -45,6 +45,7 @@ $prefix: 'simplifyComments-switch';
|
||||
.reply-info,
|
||||
.sub-reply-info {
|
||||
display: inline-flex !important;
|
||||
flex-basis: 100% !important;
|
||||
}
|
||||
.reply-time,
|
||||
.sub-reply-time {
|
||||
|
||||
@ -13,7 +13,7 @@ export interface KeyBindingActionContext {
|
||||
}
|
||||
export interface KeyBindingAction {
|
||||
displayName: string
|
||||
run: (context: KeyBindingActionContext) => any
|
||||
run: (context: KeyBindingActionContext) => unknown
|
||||
prevent?: boolean
|
||||
ignoreTyping?: boolean
|
||||
}
|
||||
|
||||
@ -74,6 +74,13 @@ const entry: ComponentEntry = async ({ settings, metadata }) => {
|
||||
},
|
||||
true,
|
||||
)
|
||||
addComponentListener(
|
||||
`${metadata.name}.showPlaceholder`,
|
||||
(value: boolean) => {
|
||||
document.body.classList.toggle('promotion-show-placeholder', value)
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'removePromotions',
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
@import 'common';
|
||||
|
||||
#slide_ad,
|
||||
.v-wrap .vcd,
|
||||
.ad-report,
|
||||
@ -80,3 +82,25 @@ body.dark .blocked-ads.new {
|
||||
body:not(.preserve-feed-goods) .bili-dyn-content__orig__additional:has(.dyn-goods) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.bili-video-card__wrap {
|
||||
a[href*="cm.bilibili.com"] {
|
||||
> * {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
& ~ .bili-video-card__info {
|
||||
display: none !important;
|
||||
}
|
||||
body.promotion-show-placeholder &::before {
|
||||
content: "🚫已屏蔽广告";
|
||||
border-radius: 6px;
|
||||
@include h-center();
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #8882;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,480 +0,0 @@
|
||||
import { select } from '@/core/spin-query'
|
||||
import { isBwpVideo, raiseEvent } from '@/core/utils'
|
||||
import { bangumiUrls, matchCurrentPage } from '@/core/utils/urls'
|
||||
import { bpxPlayerPolyfill } from './player-adaptor/bpx'
|
||||
import { v3PlayerPolyfill } from './player-adaptor/v3'
|
||||
|
||||
/** 元素查询函数, 调用时执行 `SpinQuery.select` 查询, 可访问 `selector` 获取选择器 */
|
||||
type ElementQuery<Target = HTMLElement> = {
|
||||
(): Promise<Target | null>
|
||||
sync: () => Target | null
|
||||
selector: string
|
||||
}
|
||||
interface CustomNestedQuery<QueryResult> {
|
||||
[key: string]: QueryResult | CustomNestedQuery<QueryResult>
|
||||
}
|
||||
interface CustomQuery<QueryResult> extends CustomNestedQuery<QueryResult> {
|
||||
[key: string]: QueryResult
|
||||
}
|
||||
type AgentType = 'video' | 'bangumi'
|
||||
type CustomQueryProvider<TargetType> = {
|
||||
[key in AgentType]?: TargetType
|
||||
} & { video: TargetType }
|
||||
interface PlayerQuery<QueryResult> extends CustomNestedQuery<QueryResult> {
|
||||
playerWrap: QueryResult
|
||||
bilibiliPlayer: QueryResult
|
||||
playerArea: QueryResult
|
||||
video: {
|
||||
element: QueryResult
|
||||
wrap: QueryResult
|
||||
top: QueryResult
|
||||
state: QueryResult
|
||||
panel: QueryResult
|
||||
popup: QueryResult
|
||||
subtitle: QueryResult
|
||||
basDanmaku: QueryResult
|
||||
advDanmaku: QueryResult
|
||||
danmaku: QueryResult
|
||||
container: QueryResult
|
||||
}
|
||||
control: {
|
||||
element: QueryResult
|
||||
wrap: QueryResult
|
||||
mask: QueryResult
|
||||
top: QueryResult
|
||||
progress: QueryResult
|
||||
bottom: QueryResult
|
||||
bottomLeft: QueryResult
|
||||
bottomCenter: QueryResult
|
||||
bottomRight: QueryResult
|
||||
buttons: {
|
||||
start: QueryResult
|
||||
next: QueryResult
|
||||
time: QueryResult
|
||||
quality: QueryResult
|
||||
pageList: QueryResult
|
||||
speed: QueryResult
|
||||
subtitle: QueryResult
|
||||
volume: QueryResult
|
||||
settings: QueryResult
|
||||
pip: QueryResult
|
||||
widescreen: QueryResult
|
||||
webFullscreen: QueryResult
|
||||
fullscreen: QueryResult
|
||||
}
|
||||
settings: {
|
||||
wrap: QueryResult
|
||||
lightOff: QueryResult
|
||||
}
|
||||
}
|
||||
toastWrap: QueryResult
|
||||
danmakuTipLayer: QueryResult
|
||||
danmakuSwitch: QueryResult
|
||||
}
|
||||
const elementQuery = (selector: string): ElementQuery => {
|
||||
const func = () => select<HTMLElement>(selector)
|
||||
func.selector = selector
|
||||
func.sync = () => dq(selector) as HTMLElement
|
||||
return func
|
||||
}
|
||||
const selectorWrap = (query: CustomNestedQuery<string>): CustomNestedQuery<ElementQuery> => {
|
||||
const map = (value: string | Record<string, any>): ElementQuery | Record<string, any> => {
|
||||
if (typeof value !== 'string') {
|
||||
return lodash.mapValues(value, map)
|
||||
}
|
||||
return elementQuery(value)
|
||||
}
|
||||
return lodash.mapValues(query, map) as CustomNestedQuery<ElementQuery>
|
||||
}
|
||||
const click = (target: ElementQuery) => {
|
||||
const button = target.sync()
|
||||
button?.click()
|
||||
return button
|
||||
}
|
||||
export abstract class PlayerAgent {
|
||||
abstract type: AgentType
|
||||
abstract query: PlayerQuery<ElementQuery>
|
||||
provideCustomQuery<CustomQueryType extends CustomQuery<string>>(
|
||||
config: CustomQueryProvider<CustomQueryType>,
|
||||
) {
|
||||
const custom = selectorWrap(config[this.type] ?? config.video) as CustomQuery<ElementQuery>
|
||||
return {
|
||||
...this,
|
||||
custom,
|
||||
} as this & { custom: { [key in keyof CustomQueryType]: ElementQuery } }
|
||||
}
|
||||
widescreen() {
|
||||
return click(this.query.control.buttons.widescreen)
|
||||
}
|
||||
webFullscreen() {
|
||||
return click(this.query.control.buttons.webFullscreen)
|
||||
}
|
||||
fullscreen() {
|
||||
return click(this.query.control.buttons.fullscreen)
|
||||
}
|
||||
togglePlay() {
|
||||
return click(this.query.control.buttons.start)
|
||||
}
|
||||
togglePip() {
|
||||
return click(this.query.control.buttons.pip)
|
||||
}
|
||||
toggleMute() {
|
||||
return click(this.query.control.buttons.volume)
|
||||
}
|
||||
toggleDanmaku() {
|
||||
const checkbox = this.query.danmakuSwitch.sync() as HTMLInputElement
|
||||
if (!checkbox) {
|
||||
return null
|
||||
}
|
||||
checkbox.checked = !checkbox.checked
|
||||
raiseEvent(checkbox, 'change')
|
||||
return checkbox.checked
|
||||
}
|
||||
|
||||
/** true 开灯,false 关灯 */
|
||||
async toggleLight(on: boolean) {
|
||||
const checkbox = (await this.query.control.settings.lightOff()) as HTMLInputElement
|
||||
// 关灯状态 && 要开灯 -> 开灯
|
||||
checkbox.checked && on && checkbox.click()
|
||||
// 开灯状态 && 要关灯 -> 关灯
|
||||
!checkbox.checked && !on && checkbox.click()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
getPlayerConfig(target: string) {
|
||||
return lodash.get(JSON.parse(localStorage.getItem('bilibili_player_settings')), target, false)
|
||||
}
|
||||
|
||||
isAutoPlay() {
|
||||
return this.getPlayerConfig('video_status.autoplay')
|
||||
}
|
||||
|
||||
abstract isMute(): boolean
|
||||
/** 更改音量 (%) */
|
||||
abstract changeVolume(change: number): number
|
||||
/** 跳转到指定时间 */
|
||||
abstract seek(time: number): number
|
||||
/** 更改时间 */
|
||||
abstract changeTime(change: number): number
|
||||
}
|
||||
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',
|
||||
bilibiliPlayer: '.bilibili-player',
|
||||
playerArea: '.bilibili-player-area',
|
||||
video: {
|
||||
element: '.bilibili-player-video video',
|
||||
wrap: '.bilibili-player-video-wrap',
|
||||
top: '.bilibili-player-video-top',
|
||||
state: '.bilibili-player-video-state',
|
||||
panel: '.bilibili-player-video-panel',
|
||||
popup: '.bilibili-player-video-popup',
|
||||
subtitle: '.bilibili-player-video-subtitle',
|
||||
basDanmaku: '.bilibili-player-video-bas-danmaku',
|
||||
advDanmaku: '.bilibili-player-video-adv-danmaku',
|
||||
danmaku: '.bilibili-player-video-danmaku',
|
||||
container: '.bilibili-player-video',
|
||||
},
|
||||
control: {
|
||||
element: '.bilibili-player-control',
|
||||
wrap: '.bilibili-player-control-wrap',
|
||||
mask: '.bilibili-player-control-mask',
|
||||
top: '.bilibili-player-control-top',
|
||||
progress: '.bilibili-player-video-progress',
|
||||
bottom: '.bilibili-player-control-bottom',
|
||||
bottomLeft: '.bilibili-player-control-bottom-left',
|
||||
bottomCenter: '.bilibili-player-control-bottom-center',
|
||||
bottomRight: '.bilibili-player-control-bottom-right',
|
||||
buttons: {
|
||||
start: '.bilibili-player-video-btn-start',
|
||||
next: '.bilibili-player-video-btn-next',
|
||||
time: '.bilibili-player-video-time',
|
||||
quality: '.bilibili-player-btn-quality',
|
||||
pageList: '.bilibili-player-video-btn-pagelist',
|
||||
speed: '.bilibili-player-video-btn-speed',
|
||||
subtitle: '.bilibili-player-video-btn-subtitle',
|
||||
volume: '.bilibili-player-video-btn-volume .bilibili-player-iconfont-volume',
|
||||
settings: '.bilibili-player-video-btn-setting',
|
||||
pip: '.bilibili-player-video-btn-pip',
|
||||
widescreen: '.bilibili-player-video-btn-widescreen',
|
||||
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<ElementQuery>
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.checkBwpVideo()
|
||||
}
|
||||
checkBwpVideo() {
|
||||
const videoSelector = this.query.video.element.selector
|
||||
const bwpSelector = '.bilibili-player-video bwp-video,.bpx-player-video-area bwp-video'
|
||||
this.query.video.element = (() => {
|
||||
const func = async () => {
|
||||
if (await isBwpVideo()) {
|
||||
return select<HTMLElement>(bwpSelector)
|
||||
}
|
||||
return select<HTMLElement>(videoSelector)
|
||||
}
|
||||
func.selector = videoSelector
|
||||
func.sync = () => dq(videoSelector) as HTMLElement
|
||||
isBwpVideo().then(bwp => {
|
||||
if (!bwp) {
|
||||
return
|
||||
}
|
||||
func.selector = bwpSelector
|
||||
func.sync = () => dq(bwpSelector) as HTMLElement
|
||||
})
|
||||
return func
|
||||
})()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
export class BangumiPlayerAgent extends PlayerAgent {
|
||||
type: AgentType = 'bangumi'
|
||||
query = selectorWrap({
|
||||
playerWrap: '.player-module',
|
||||
bilibiliPlayer: '.bpx-player-container',
|
||||
playerArea: '.bpx-player-primary-area',
|
||||
video: {
|
||||
element: '.bpx-player-video-wrap video',
|
||||
wrap: '.bpx-player-video-area',
|
||||
top: '.bpx-player-top-wrap',
|
||||
state: '.bpx-player-state-wrap',
|
||||
panel: '.bpx-player-ending-panel',
|
||||
popup: '.bpx-player-dialog-wrap',
|
||||
subtitle: '.bpx-player-subtitle-wrap',
|
||||
basDanmaku: '.bpx-player-bas-dm-wrap',
|
||||
advDanmaku: '.bpx-player-adv-dm-wrap',
|
||||
danmaku: '.bpx-player-row-dm-wrap',
|
||||
container: '.bpx-player-video-wrap',
|
||||
},
|
||||
control: {
|
||||
element: '.squirtle-controller',
|
||||
wrap: '.bpx-player-control-wrap',
|
||||
mask: '.bpx-player-control-mask',
|
||||
top: '.bpx-player-control-top',
|
||||
progress: '.squirtle-progress-wrap',
|
||||
bottom: '.squirtle-controller-wrap',
|
||||
bottomLeft: '.squirtle-controller-wrap-left',
|
||||
bottomCenter: '.squirtle-controller-wrap-center',
|
||||
bottomRight: '.squirtle-controller-wrap-right',
|
||||
buttons: {
|
||||
start: '.squirtle-video-start',
|
||||
next: '.squirtle-video-next',
|
||||
time: '.squirtle-time-wrap',
|
||||
quality: '.squirtle-video-quality',
|
||||
pageList: '.squirtle-video-pagelist',
|
||||
speed: '.squirtle-video-speed',
|
||||
subtitle: '.squirtle-video-subtitle',
|
||||
volume: '.squirtle-video-volume .squirtle-volume-icon',
|
||||
settings: '.squirtle-video-setting',
|
||||
pip: '.squirtle-video-pip',
|
||||
widescreen: '.squirtle-video-widescreen',
|
||||
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<ElementQuery>
|
||||
constructor() {
|
||||
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
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
export class VideoPlayerMixedAgent extends VideoPlayerV2Agent {
|
||||
query = selectorWrap({
|
||||
playerWrap: '.player-wrap',
|
||||
bilibiliPlayer: '.bilibili-player, #bilibili-player',
|
||||
playerArea: '.bilibili-player-area, .bpx-player-primary-area',
|
||||
video: {
|
||||
element: '.bilibili-player-video video, .bpx-player-video-wrap video',
|
||||
wrap: '.bilibili-player-video-wrap, .bpx-player-video-area',
|
||||
top: '.bilibili-player-video-top, .bpx-player-top-wrap',
|
||||
state: '.bilibili-player-video-state, .bpx-player-state-wrap',
|
||||
panel: '.bilibili-player-video-panel, .bpx-player-ending-panel',
|
||||
popup: '.bilibili-player-video-popup, .bpx-player-dialog-wrap',
|
||||
subtitle: '.bilibili-player-video-subtitle, .bpx-player-subtitle-wrap',
|
||||
basDanmaku: '.bilibili-player-video-bas-danmaku, .bpx-player-bas-dm-wrap',
|
||||
advDanmaku: '.bilibili-player-video-adv-danmaku, .bpx-player-adv-dm-wrap',
|
||||
danmaku: '.bilibili-player-video-danmaku, .bpx-player-row-dm-wrap',
|
||||
container: '.bilibili-player-video, .bpx-player-video-perch',
|
||||
},
|
||||
control: {
|
||||
element: '.bilibili-player-control, .bpx-player-control-entity',
|
||||
wrap: '.bilibili-player-control-wrap, .bpx-player-control-wrap',
|
||||
mask: '.bilibili-player-control-mask, .bpx-player-control-mask',
|
||||
top: '.bilibili-player-control-top, .bpx-player-control-top',
|
||||
progress: '.bilibili-player-video-progress, .bpx-player-progress',
|
||||
bottom: '.bilibili-player-control-bottom, .bpx-player-control-bottom',
|
||||
bottomLeft: '.bilibili-player-control-bottom-left, ,.bpx-player-control-bottom-left',
|
||||
bottomCenter: '.bilibili-player-control-bottom-center, .bpx-player-control-bottom-center',
|
||||
bottomRight: '.bilibili-player-control-bottom-right, .bpx-player-control-bottom-right',
|
||||
buttons: {
|
||||
start: '.bilibili-player-video-btn-start, .bpx-player-ctrl-play',
|
||||
next: '.bilibili-player-video-btn-next, .bpx-player-ctrl-btn-next',
|
||||
time: '.bilibili-player-video-time, .bpx-player-ctrl-time',
|
||||
quality: '.bilibili-player-btn-quality, .bpx-player-ctrl-quality',
|
||||
pageList: '.bilibili-player-video-btn-pagelist, .bpx-player-ctrl-eplist',
|
||||
speed: '.bilibili-player-video-btn-speed, .bpx-player-ctrl-playbackrate',
|
||||
subtitle: '.bilibili-player-video-btn-subtitle, .bpx-player-ctrl-subtitle',
|
||||
volume:
|
||||
'.bilibili-player-video-btn-volume .bilibili-player-iconfont-volume, .bpx-player-ctrl-volume .bpx-player-ctrl-volume-icon',
|
||||
settings: '.bilibili-player-video-btn-setting, .bpx-player-ctrl-setting',
|
||||
pip: '.bilibili-player-video-btn-pip, .bpx-player-ctrl-pip',
|
||||
widescreen: '.bilibili-player-video-btn-widescreen, .bpx-player-ctrl-wide',
|
||||
webFullscreen: '.bilibili-player-video-web-fullscreen, .bpx-player-ctrl-web',
|
||||
fullscreen: '.bilibili-player-video-btn-fullscreen, .bpx-player-ctrl-full',
|
||||
},
|
||||
settings: {
|
||||
wrap: '.bilibili-player-video-btn-setting-wrap, .bpx-player-ctrl-setting-box',
|
||||
lightOff:
|
||||
'.bilibili-player-video-btn-setting-right-others-content-lightoff .bui-checkbox-input, .bpx-player-ctrl-setting-lightoff .bui-checkbox-input',
|
||||
},
|
||||
},
|
||||
toastWrap: '.bilibili-player-video-toast-wrp, .bpx-player-dialog-wrap',
|
||||
danmakuTipLayer: '.bilibili-player-dm-tip-wrap, .bpx-player-dm-tip',
|
||||
danmakuSwitch: '.bilibili-player-video-danmaku-switch input, .bpx-player-dm-switch input',
|
||||
}) as PlayerQuery<ElementQuery>
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
export const playerAgent = (() => {
|
||||
if (matchCurrentPage(bangumiUrls)) {
|
||||
return new BangumiPlayerAgent()
|
||||
}
|
||||
return new VideoPlayerMixedAgent()
|
||||
})()
|
||||
104
src/components/video/player-agent/bangumi.ts
Normal file
104
src/components/video/player-agent/bangumi.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import { bpxPlayerPolyfill } from '../player-adaptor/bpx'
|
||||
import { PlayerAgent, selectorWrap } from './base'
|
||||
import { AgentType, PlayerQuery, ElementQuery } from './types'
|
||||
|
||||
export class BangumiPlayerAgent extends PlayerAgent {
|
||||
type: AgentType = 'bangumi'
|
||||
query = selectorWrap({
|
||||
playerWrap: '.player-module',
|
||||
bilibiliPlayer: '.bpx-player-container',
|
||||
playerArea: '.bpx-player-primary-area',
|
||||
video: {
|
||||
element: '.bpx-player-video-wrap video',
|
||||
wrap: '.bpx-player-video-area',
|
||||
top: '.bpx-player-top-wrap',
|
||||
state: '.bpx-player-state-wrap',
|
||||
panel: '.bpx-player-ending-panel',
|
||||
popup: '.bpx-player-dialog-wrap',
|
||||
subtitle: '.bpx-player-subtitle-wrap',
|
||||
basDanmaku: '.bpx-player-bas-dm-wrap',
|
||||
advDanmaku: '.bpx-player-adv-dm-wrap',
|
||||
danmaku: '.bpx-player-row-dm-wrap',
|
||||
container: '.bpx-player-video-wrap',
|
||||
},
|
||||
control: {
|
||||
element: '.squirtle-controller',
|
||||
wrap: '.bpx-player-control-wrap',
|
||||
mask: '.bpx-player-control-mask',
|
||||
top: '.bpx-player-control-top',
|
||||
progress: '.squirtle-progress-wrap',
|
||||
bottom: '.squirtle-controller-wrap',
|
||||
bottomLeft: '.squirtle-controller-wrap-left',
|
||||
bottomCenter: '.squirtle-controller-wrap-center',
|
||||
bottomRight: '.squirtle-controller-wrap-right',
|
||||
buttons: {
|
||||
start: '.squirtle-video-start',
|
||||
next: '.squirtle-video-next',
|
||||
time: '.squirtle-time-wrap',
|
||||
quality: '.squirtle-video-quality',
|
||||
pageList: '.squirtle-video-pagelist',
|
||||
speed: '.squirtle-video-speed',
|
||||
subtitle: '.squirtle-video-subtitle',
|
||||
volume: '.squirtle-video-volume .squirtle-volume-icon',
|
||||
settings: '.squirtle-video-setting',
|
||||
pip: '.squirtle-video-pip',
|
||||
widescreen: '.squirtle-video-widescreen',
|
||||
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<ElementQuery>
|
||||
constructor() {
|
||||
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
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
97
src/components/video/player-agent/base.ts
Normal file
97
src/components/video/player-agent/base.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { select } from '@/core/spin-query'
|
||||
import { raiseEvent } from '@/core/utils'
|
||||
import {
|
||||
ElementQuery,
|
||||
CustomNestedQuery,
|
||||
AgentType,
|
||||
PlayerQuery,
|
||||
CustomQuery,
|
||||
CustomQueryProvider,
|
||||
} from './types'
|
||||
|
||||
export const elementQuery = (selector: string): ElementQuery => {
|
||||
const func = () => select<HTMLElement>(selector)
|
||||
func.selector = selector
|
||||
func.sync = () => dq(selector) as HTMLElement
|
||||
return func
|
||||
}
|
||||
export const selectorWrap = (query: CustomNestedQuery<string>): CustomNestedQuery<ElementQuery> => {
|
||||
const map = (value: string | Record<string, any>): ElementQuery | Record<string, any> => {
|
||||
if (typeof value !== 'string') {
|
||||
return lodash.mapValues(value, map)
|
||||
}
|
||||
return elementQuery(value)
|
||||
}
|
||||
return lodash.mapValues(query, map) as CustomNestedQuery<ElementQuery>
|
||||
}
|
||||
export const click = (target: ElementQuery) => {
|
||||
const button = target.sync()
|
||||
button?.click()
|
||||
return button
|
||||
}
|
||||
export abstract class PlayerAgent {
|
||||
abstract type: AgentType
|
||||
abstract query: PlayerQuery<ElementQuery>
|
||||
provideCustomQuery<CustomQueryType extends CustomQuery<string>>(
|
||||
config: CustomQueryProvider<CustomQueryType>,
|
||||
) {
|
||||
const custom = selectorWrap(config[this.type] ?? config.video) as CustomQuery<ElementQuery>
|
||||
return {
|
||||
...this,
|
||||
custom,
|
||||
} as this & { custom: { [key in keyof CustomQueryType]: ElementQuery } }
|
||||
}
|
||||
widescreen() {
|
||||
return click(this.query.control.buttons.widescreen)
|
||||
}
|
||||
webFullscreen() {
|
||||
return click(this.query.control.buttons.webFullscreen)
|
||||
}
|
||||
fullscreen() {
|
||||
return click(this.query.control.buttons.fullscreen)
|
||||
}
|
||||
togglePlay() {
|
||||
return click(this.query.control.buttons.start)
|
||||
}
|
||||
togglePip() {
|
||||
return click(this.query.control.buttons.pip)
|
||||
}
|
||||
toggleMute() {
|
||||
return click(this.query.control.buttons.volume)
|
||||
}
|
||||
toggleDanmaku() {
|
||||
const checkbox = this.query.danmakuSwitch.sync() as HTMLInputElement
|
||||
if (!checkbox) {
|
||||
return null
|
||||
}
|
||||
checkbox.checked = !checkbox.checked
|
||||
raiseEvent(checkbox, 'change')
|
||||
return checkbox.checked
|
||||
}
|
||||
|
||||
/** true 开灯,false 关灯 */
|
||||
async toggleLight(on: boolean) {
|
||||
const checkbox = (await this.query.control.settings.lightOff()) as HTMLInputElement
|
||||
// 关灯状态 && 要开灯 -> 开灯
|
||||
checkbox.checked && on && checkbox.click()
|
||||
// 开灯状态 && 要关灯 -> 关灯
|
||||
!checkbox.checked && !on && checkbox.click()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
getPlayerConfig(target: string) {
|
||||
return lodash.get(JSON.parse(localStorage.getItem('bilibili_player_settings')), target, false)
|
||||
}
|
||||
|
||||
isAutoPlay() {
|
||||
return this.getPlayerConfig('video_status.autoplay')
|
||||
}
|
||||
|
||||
abstract isMute(): boolean
|
||||
/** 更改音量 (%) */
|
||||
abstract changeVolume(change: number): number
|
||||
/** 跳转到指定时间 */
|
||||
abstract seek(time: number): number
|
||||
/** 更改时间 */
|
||||
abstract changeTime(change: number): number
|
||||
}
|
||||
10
src/components/video/player-agent/index.ts
Normal file
10
src/components/video/player-agent/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { bangumiUrls, matchCurrentPage } from '@/core/utils/urls'
|
||||
import { BangumiPlayerAgent } from './bangumi'
|
||||
import { VideoPlayerMixedAgent } from './video-player-mixed'
|
||||
|
||||
export const playerAgent = (() => {
|
||||
if (matchCurrentPage(bangumiUrls)) {
|
||||
return new BangumiPlayerAgent()
|
||||
}
|
||||
return new VideoPlayerMixedAgent()
|
||||
})()
|
||||
0
src/components/video/player-agent/live-player.ts
Normal file
0
src/components/video/player-agent/live-player.ts
Normal file
67
src/components/video/player-agent/types.ts
Normal file
67
src/components/video/player-agent/types.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/** 元素查询函数, 调用时执行 `SpinQuery.select` 查询, 可访问 `selector` 获取选择器 */
|
||||
export type ElementQuery<Target = HTMLElement> = {
|
||||
(): Promise<Target | null>
|
||||
sync: () => Target | null
|
||||
selector: string
|
||||
}
|
||||
export interface CustomNestedQuery<QueryResult> {
|
||||
[key: string]: QueryResult | CustomNestedQuery<QueryResult>
|
||||
}
|
||||
export interface CustomQuery<QueryResult> extends CustomNestedQuery<QueryResult> {
|
||||
[key: string]: QueryResult
|
||||
}
|
||||
export type AgentType = 'video' | 'bangumi' | 'live'
|
||||
export type CustomQueryProvider<TargetType> = {
|
||||
[key in AgentType]?: TargetType
|
||||
} & { video: TargetType }
|
||||
export interface PlayerQuery<QueryResult> extends CustomNestedQuery<QueryResult> {
|
||||
playerWrap: QueryResult
|
||||
bilibiliPlayer: QueryResult
|
||||
playerArea: QueryResult
|
||||
video: {
|
||||
element: QueryResult
|
||||
wrap: QueryResult
|
||||
top: QueryResult
|
||||
state: QueryResult
|
||||
panel: QueryResult
|
||||
popup: QueryResult
|
||||
subtitle: QueryResult
|
||||
basDanmaku: QueryResult
|
||||
advDanmaku: QueryResult
|
||||
danmaku: QueryResult
|
||||
container: QueryResult
|
||||
}
|
||||
control: {
|
||||
element: QueryResult
|
||||
wrap: QueryResult
|
||||
mask: QueryResult
|
||||
top: QueryResult
|
||||
progress: QueryResult
|
||||
bottom: QueryResult
|
||||
bottomLeft: QueryResult
|
||||
bottomCenter: QueryResult
|
||||
bottomRight: QueryResult
|
||||
buttons: {
|
||||
start: QueryResult
|
||||
next: QueryResult
|
||||
time: QueryResult
|
||||
quality: QueryResult
|
||||
pageList: QueryResult
|
||||
speed: QueryResult
|
||||
subtitle: QueryResult
|
||||
volume: QueryResult
|
||||
settings: QueryResult
|
||||
pip: QueryResult
|
||||
widescreen: QueryResult
|
||||
webFullscreen: QueryResult
|
||||
fullscreen: QueryResult
|
||||
}
|
||||
settings: {
|
||||
wrap: QueryResult
|
||||
lightOff: QueryResult
|
||||
}
|
||||
}
|
||||
toastWrap: QueryResult
|
||||
danmakuTipLayer: QueryResult
|
||||
danmakuSwitch: QueryResult
|
||||
}
|
||||
83
src/components/video/player-agent/video-player-mixed.ts
Normal file
83
src/components/video/player-agent/video-player-mixed.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { v3PlayerPolyfill } from '../player-adaptor/v3'
|
||||
import { selectorWrap } from './base'
|
||||
import { PlayerQuery, ElementQuery } from './types'
|
||||
import { VideoPlayerV2Agent } from './video-player-v2'
|
||||
|
||||
export class VideoPlayerMixedAgent extends VideoPlayerV2Agent {
|
||||
query = selectorWrap({
|
||||
playerWrap: '.player-wrap',
|
||||
bilibiliPlayer: '.bilibili-player, #bilibili-player',
|
||||
playerArea: '.bilibili-player-area, .bpx-player-primary-area',
|
||||
video: {
|
||||
element: '.bilibili-player-video video, .bpx-player-video-wrap video',
|
||||
wrap: '.bilibili-player-video-wrap, .bpx-player-video-area',
|
||||
top: '.bilibili-player-video-top, .bpx-player-top-wrap',
|
||||
state: '.bilibili-player-video-state, .bpx-player-state-wrap',
|
||||
panel: '.bilibili-player-video-panel, .bpx-player-ending-panel',
|
||||
popup: '.bilibili-player-video-popup, .bpx-player-dialog-wrap',
|
||||
subtitle: '.bilibili-player-video-subtitle, .bpx-player-subtitle-wrap',
|
||||
basDanmaku: '.bilibili-player-video-bas-danmaku, .bpx-player-bas-dm-wrap',
|
||||
advDanmaku: '.bilibili-player-video-adv-danmaku, .bpx-player-adv-dm-wrap',
|
||||
danmaku: '.bilibili-player-video-danmaku, .bpx-player-row-dm-wrap',
|
||||
container: '.bilibili-player-video, .bpx-player-video-perch',
|
||||
},
|
||||
control: {
|
||||
element: '.bilibili-player-control, .bpx-player-control-entity',
|
||||
wrap: '.bilibili-player-control-wrap, .bpx-player-control-wrap',
|
||||
mask: '.bilibili-player-control-mask, .bpx-player-control-mask',
|
||||
top: '.bilibili-player-control-top, .bpx-player-control-top',
|
||||
progress: '.bilibili-player-video-progress, .bpx-player-progress',
|
||||
bottom: '.bilibili-player-control-bottom, .bpx-player-control-bottom',
|
||||
bottomLeft: '.bilibili-player-control-bottom-left, ,.bpx-player-control-bottom-left',
|
||||
bottomCenter: '.bilibili-player-control-bottom-center, .bpx-player-control-bottom-center',
|
||||
bottomRight: '.bilibili-player-control-bottom-right, .bpx-player-control-bottom-right',
|
||||
buttons: {
|
||||
start: '.bilibili-player-video-btn-start, .bpx-player-ctrl-play',
|
||||
next: '.bilibili-player-video-btn-next, .bpx-player-ctrl-btn-next',
|
||||
time: '.bilibili-player-video-time, .bpx-player-ctrl-time',
|
||||
quality: '.bilibili-player-btn-quality, .bpx-player-ctrl-quality',
|
||||
pageList: '.bilibili-player-video-btn-pagelist, .bpx-player-ctrl-eplist',
|
||||
speed: '.bilibili-player-video-btn-speed, .bpx-player-ctrl-playbackrate',
|
||||
subtitle: '.bilibili-player-video-btn-subtitle, .bpx-player-ctrl-subtitle',
|
||||
volume:
|
||||
'.bilibili-player-video-btn-volume .bilibili-player-iconfont-volume, .bpx-player-ctrl-volume .bpx-player-ctrl-volume-icon',
|
||||
settings: '.bilibili-player-video-btn-setting, .bpx-player-ctrl-setting',
|
||||
pip: '.bilibili-player-video-btn-pip, .bpx-player-ctrl-pip',
|
||||
widescreen: '.bilibili-player-video-btn-widescreen, .bpx-player-ctrl-wide',
|
||||
webFullscreen: '.bilibili-player-video-web-fullscreen, .bpx-player-ctrl-web',
|
||||
fullscreen: '.bilibili-player-video-btn-fullscreen, .bpx-player-ctrl-full',
|
||||
},
|
||||
settings: {
|
||||
wrap: '.bilibili-player-video-btn-setting-wrap, .bpx-player-ctrl-setting-box',
|
||||
lightOff:
|
||||
'.bilibili-player-video-btn-setting-right-others-content-lightoff .bui-checkbox-input, .bpx-player-ctrl-setting-lightoff .bui-checkbox-input',
|
||||
},
|
||||
},
|
||||
toastWrap: '.bilibili-player-video-toast-wrp, .bpx-player-dialog-wrap',
|
||||
danmakuTipLayer: '.bilibili-player-dm-tip-wrap, .bpx-player-dm-tip',
|
||||
danmakuSwitch: '.bilibili-player-video-danmaku-switch input, .bpx-player-dm-switch input',
|
||||
}) as PlayerQuery<ElementQuery>
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
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()
|
||||
}
|
||||
}
|
||||
141
src/components/video/player-agent/video-player-v2.ts
Normal file
141
src/components/video/player-agent/video-player-v2.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import { select } from '@/core/spin-query'
|
||||
import { isBwpVideo } from '@/core/utils'
|
||||
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',
|
||||
bilibiliPlayer: '.bilibili-player',
|
||||
playerArea: '.bilibili-player-area',
|
||||
video: {
|
||||
element: '.bilibili-player-video video',
|
||||
wrap: '.bilibili-player-video-wrap',
|
||||
top: '.bilibili-player-video-top',
|
||||
state: '.bilibili-player-video-state',
|
||||
panel: '.bilibili-player-video-panel',
|
||||
popup: '.bilibili-player-video-popup',
|
||||
subtitle: '.bilibili-player-video-subtitle',
|
||||
basDanmaku: '.bilibili-player-video-bas-danmaku',
|
||||
advDanmaku: '.bilibili-player-video-adv-danmaku',
|
||||
danmaku: '.bilibili-player-video-danmaku',
|
||||
container: '.bilibili-player-video',
|
||||
},
|
||||
control: {
|
||||
element: '.bilibili-player-control',
|
||||
wrap: '.bilibili-player-control-wrap',
|
||||
mask: '.bilibili-player-control-mask',
|
||||
top: '.bilibili-player-control-top',
|
||||
progress: '.bilibili-player-video-progress',
|
||||
bottom: '.bilibili-player-control-bottom',
|
||||
bottomLeft: '.bilibili-player-control-bottom-left',
|
||||
bottomCenter: '.bilibili-player-control-bottom-center',
|
||||
bottomRight: '.bilibili-player-control-bottom-right',
|
||||
buttons: {
|
||||
start: '.bilibili-player-video-btn-start',
|
||||
next: '.bilibili-player-video-btn-next',
|
||||
time: '.bilibili-player-video-time',
|
||||
quality: '.bilibili-player-btn-quality',
|
||||
pageList: '.bilibili-player-video-btn-pagelist',
|
||||
speed: '.bilibili-player-video-btn-speed',
|
||||
subtitle: '.bilibili-player-video-btn-subtitle',
|
||||
volume: '.bilibili-player-video-btn-volume .bilibili-player-iconfont-volume',
|
||||
settings: '.bilibili-player-video-btn-setting',
|
||||
pip: '.bilibili-player-video-btn-pip',
|
||||
widescreen: '.bilibili-player-video-btn-widescreen',
|
||||
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<ElementQuery>
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.checkBwpVideo()
|
||||
}
|
||||
checkBwpVideo() {
|
||||
const videoSelector = this.query.video.element.selector
|
||||
const bwpSelector = '.bilibili-player-video bwp-video,.bpx-player-video-area bwp-video'
|
||||
this.query.video.element = (() => {
|
||||
const func = async () => {
|
||||
if (await isBwpVideo()) {
|
||||
return select<HTMLElement>(bwpSelector)
|
||||
}
|
||||
return select<HTMLElement>(videoSelector)
|
||||
}
|
||||
func.selector = videoSelector
|
||||
func.sync = () => dq(videoSelector) as HTMLElement
|
||||
isBwpVideo().then(bwp => {
|
||||
if (!bwp) {
|
||||
return
|
||||
}
|
||||
func.selector = bwpSelector
|
||||
func.sync = () => dq(bwpSelector) as HTMLElement
|
||||
})
|
||||
return func
|
||||
})()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user