mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge pull request #5407 from KDH-KDHKDH/feature/front-back-volume
Feature/front back volume 直播前后台音量自动调节
This commit is contained in:
commit
541fa51bf1
8
registry/lib/components/live/front-back-volume/index.md
Normal file
8
registry/lib/components/live/front-back-volume/index.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# 使用说明
|
||||||
|
|
||||||
|
<!-- (http://localhost:23333/registry/dist/components/live/front-back-volume.js) -->
|
||||||
|
|
||||||
|
1. 分别设定前台/后台音量
|
||||||
|
2. 当你切换到其他标签页或最小化窗口时,播放器会自动改用“后台音量”
|
||||||
|
3. 若未生效,请先点击一下直播页面再试。
|
||||||
|
4. 手动调音量不受影响
|
||||||
236
registry/lib/components/live/front-back-volume/index.ts
Normal file
236
registry/lib/components/live/front-back-volume/index.ts
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
import {
|
||||||
|
defineComponentMetadata,
|
||||||
|
defineOptionsMetadata,
|
||||||
|
OptionsOfMetadata,
|
||||||
|
} from '@/components/define'
|
||||||
|
import { liveUrls } from '@/core/utils/urls'
|
||||||
|
import { select } from '@/core/spin-query'
|
||||||
|
import { delay } from '@/core/utils'
|
||||||
|
import { addComponentListener, getComponentSettings } from '@/core/settings'
|
||||||
|
|
||||||
|
// 音量常数配置
|
||||||
|
const DEFAULT_FOREGROUND_VOLUME = 10
|
||||||
|
const DEFAULT_BACKGROUND_VOLUME = 3
|
||||||
|
|
||||||
|
const options = defineOptionsMetadata({
|
||||||
|
foregroundVolume: {
|
||||||
|
displayName: '前台音量',
|
||||||
|
defaultValue: DEFAULT_FOREGROUND_VOLUME,
|
||||||
|
validator: (value: number) => {
|
||||||
|
const num = Math.round(value)
|
||||||
|
return num >= 0 && num <= 100 ? num : DEFAULT_FOREGROUND_VOLUME
|
||||||
|
},
|
||||||
|
},
|
||||||
|
backgroundVolume: {
|
||||||
|
displayName: '后台音量',
|
||||||
|
defaultValue: DEFAULT_BACKGROUND_VOLUME,
|
||||||
|
validator: (value: number) => {
|
||||||
|
const num = Math.round(value)
|
||||||
|
return num >= 0 && num <= 100 ? num : DEFAULT_BACKGROUND_VOLUME
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enableLogging: {
|
||||||
|
displayName: '启用日志输出',
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Options = OptionsOfMetadata<typeof options>
|
||||||
|
|
||||||
|
let videoElement: HTMLVideoElement | null = null
|
||||||
|
let isInitialized = false
|
||||||
|
let currentForegroundVolume = DEFAULT_FOREGROUND_VOLUME / 100
|
||||||
|
let currentBackgroundVolume = DEFAULT_BACKGROUND_VOLUME / 100
|
||||||
|
|
||||||
|
// 事件处理函数
|
||||||
|
let handleLoadedMetadata: (() => void) | null = null
|
||||||
|
let handleVisibilityChange: (() => void) | null = null
|
||||||
|
let handleFocus: (() => void) | null = null
|
||||||
|
let handleBlur: (() => void) | null = null
|
||||||
|
|
||||||
|
// 获取当前直播间号
|
||||||
|
const getLiveRoomId = (): string => {
|
||||||
|
let matched = location.href.match(/live.bilibili.com\/(\d+)/)
|
||||||
|
if (matched) {
|
||||||
|
return matched[1]
|
||||||
|
}
|
||||||
|
matched = location.href.match(/live.bilibili.com\/blanc\/(\d+)/)
|
||||||
|
return matched ? matched[1] : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const findVideoElement = async (): Promise<HTMLVideoElement | null> => {
|
||||||
|
try {
|
||||||
|
const video = await select('video')
|
||||||
|
if (video && (video as HTMLVideoElement).readyState > 0) {
|
||||||
|
return video as HTMLVideoElement
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const adjustVolume = (enableLogging: boolean) => {
|
||||||
|
if (!videoElement) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isForeground = document.visibilityState === 'visible' && document.hasFocus()
|
||||||
|
const targetVolume = isForeground ? currentForegroundVolume : currentBackgroundVolume
|
||||||
|
const roomId = getLiveRoomId()
|
||||||
|
|
||||||
|
videoElement.volume = Math.max(0, Math.min(1, targetVolume))
|
||||||
|
|
||||||
|
if (enableLogging) {
|
||||||
|
console.log(
|
||||||
|
`[前后台音量调节] 房间号: ${roomId} - 音量已设置为: ${
|
||||||
|
isForeground ? '前台' : '后台'
|
||||||
|
} ${Math.round(targetVolume * 100)}%`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupVolumeControl = async (enableLogging: boolean): Promise<void> => {
|
||||||
|
try {
|
||||||
|
videoElement = await findVideoElement()
|
||||||
|
const roomId = getLiveRoomId()
|
||||||
|
|
||||||
|
if (!videoElement) {
|
||||||
|
if (enableLogging) {
|
||||||
|
console.log(`[前后台音量调节] 房间号: ${roomId} - 未找到视频元素,10秒后重试...`)
|
||||||
|
}
|
||||||
|
await delay(10000)
|
||||||
|
return setupVolumeControl(enableLogging)
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustVolume(enableLogging)
|
||||||
|
|
||||||
|
if (!isInitialized) {
|
||||||
|
handleLoadedMetadata = () => {
|
||||||
|
if (videoElement) {
|
||||||
|
adjustVolume(enableLogging)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleVisibilityChange = () => adjustVolume(enableLogging)
|
||||||
|
handleFocus = () => adjustVolume(enableLogging)
|
||||||
|
handleBlur = () => adjustVolume(enableLogging)
|
||||||
|
|
||||||
|
videoElement.addEventListener('loadedmetadata', handleLoadedMetadata)
|
||||||
|
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||||
|
window.addEventListener('focus', handleFocus)
|
||||||
|
window.addEventListener('blur', handleBlur)
|
||||||
|
|
||||||
|
isInitialized = true
|
||||||
|
|
||||||
|
if (enableLogging) {
|
||||||
|
console.log(`[前后台音量调节] 房间号: ${roomId} - B站直播音量自动调节器已激活`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
} catch (error) {
|
||||||
|
const roomId = getLiveRoomId()
|
||||||
|
if (enableLogging) {
|
||||||
|
console.error(`[前后台音量调节] 房间号: ${roomId} - 设置音量控制时出错:`, error)
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const component = defineComponentMetadata({
|
||||||
|
name: 'frontBackVolume',
|
||||||
|
displayName: '直播前后台音量自动调节',
|
||||||
|
description: '自动调节B站直播音量,前台和后台使用不同音量',
|
||||||
|
author: {
|
||||||
|
name: 'KDH-KDHKDH',
|
||||||
|
link: 'https://github.com/KDH-KDHKDH',
|
||||||
|
},
|
||||||
|
tags: [componentsTags.live],
|
||||||
|
urlInclude: liveUrls,
|
||||||
|
entry: async ({ settings, metadata }) => {
|
||||||
|
const { foregroundVolume, backgroundVolume, enableLogging } = settings.options
|
||||||
|
|
||||||
|
currentForegroundVolume = foregroundVolume / 100
|
||||||
|
currentBackgroundVolume = backgroundVolume / 100
|
||||||
|
|
||||||
|
addComponentListener(`${metadata.name}.foregroundVolume`, (newVolume: number) => {
|
||||||
|
currentForegroundVolume = newVolume / 100
|
||||||
|
adjustVolume(enableLogging)
|
||||||
|
})
|
||||||
|
|
||||||
|
addComponentListener(`${metadata.name}.backgroundVolume`, (newVolume: number) => {
|
||||||
|
currentBackgroundVolume = newVolume / 100
|
||||||
|
adjustVolume(enableLogging)
|
||||||
|
})
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (!videoElement || !document.contains(videoElement)) {
|
||||||
|
setupVolumeControl(enableLogging)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await delay(2000)
|
||||||
|
await setupVolumeControl(enableLogging)
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
reload: async () => {
|
||||||
|
const settings = getComponentSettings<Options>('frontBackVolume')
|
||||||
|
|
||||||
|
if (videoElement && handleLoadedMetadata) {
|
||||||
|
videoElement.removeEventListener('loadedmetadata', handleLoadedMetadata)
|
||||||
|
}
|
||||||
|
if (handleVisibilityChange) {
|
||||||
|
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||||
|
}
|
||||||
|
if (handleFocus) {
|
||||||
|
window.removeEventListener('focus', handleFocus)
|
||||||
|
}
|
||||||
|
if (handleBlur) {
|
||||||
|
window.removeEventListener('blur', handleBlur)
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitialized = false
|
||||||
|
videoElement = null
|
||||||
|
|
||||||
|
handleLoadedMetadata = null
|
||||||
|
handleVisibilityChange = null
|
||||||
|
handleFocus = null
|
||||||
|
handleBlur = null
|
||||||
|
|
||||||
|
currentForegroundVolume = settings.options.foregroundVolume / 100
|
||||||
|
currentBackgroundVolume = settings.options.backgroundVolume / 100
|
||||||
|
|
||||||
|
await setupVolumeControl(settings.options.enableLogging)
|
||||||
|
},
|
||||||
|
unload: () => {
|
||||||
|
if (videoElement && handleLoadedMetadata) {
|
||||||
|
videoElement.removeEventListener('loadedmetadata', handleLoadedMetadata)
|
||||||
|
}
|
||||||
|
if (handleVisibilityChange) {
|
||||||
|
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||||
|
}
|
||||||
|
if (handleFocus) {
|
||||||
|
window.removeEventListener('focus', handleFocus)
|
||||||
|
}
|
||||||
|
if (handleBlur) {
|
||||||
|
window.removeEventListener('blur', handleBlur)
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitialized = false
|
||||||
|
videoElement = null
|
||||||
|
|
||||||
|
handleLoadedMetadata = null
|
||||||
|
handleVisibilityChange = null
|
||||||
|
handleFocus = null
|
||||||
|
handleBlur = null
|
||||||
|
|
||||||
|
currentForegroundVolume = DEFAULT_FOREGROUND_VOLUME / 100
|
||||||
|
currentBackgroundVolume = DEFAULT_BACKGROUND_VOLUME / 100
|
||||||
|
},
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue
Block a user