This commit is contained in:
@@ -15,6 +15,8 @@ export interface GlobalAudioSource {
|
||||
targetType?: AudioPlayTargetType
|
||||
targetId?: string
|
||||
lang?: AudioLanguage | string
|
||||
channelCode?: string
|
||||
voiceGender?: 'male' | 'female'
|
||||
detailRoute?: string
|
||||
title?: string
|
||||
}
|
||||
@@ -37,14 +39,12 @@ const error = ref('')
|
||||
const currentTime = ref(0)
|
||||
const duration = ref(0)
|
||||
const displayMode = ref<AudioDisplayMode>('mini')
|
||||
const playbackRate = ref(1)
|
||||
const muted = ref(false)
|
||||
const pendingLanguage = ref<AudioLanguage | ''>('')
|
||||
const activeHostId = ref('')
|
||||
const lastClosedSource = ref<GlobalAudioSource | null>(null)
|
||||
const closeVersion = ref(0)
|
||||
const playbackRate = ref(1)
|
||||
const muted = ref(false)
|
||||
|
||||
const PLAYBACK_RATES = [1, 1.25, 1.5, 2] as const
|
||||
|
||||
let audioElement: HTMLAudioElement | null = null
|
||||
let retryOnError: GlobalAudioRetryHandler | null = null
|
||||
@@ -78,17 +78,13 @@ const syncAudioDuration = () => {
|
||||
: currentAudio.value?.duration || 0
|
||||
}
|
||||
|
||||
const applyAudioPreferences = (audio: HTMLAudioElement) => {
|
||||
audio.playbackRate = playbackRate.value
|
||||
audio.muted = muted.value
|
||||
}
|
||||
|
||||
const ensureAudioElement = () => {
|
||||
if (audioElement || typeof window === 'undefined' || !window.Audio) return audioElement
|
||||
|
||||
const audio = new window.Audio()
|
||||
audio.preload = 'metadata'
|
||||
applyAudioPreferences(audio)
|
||||
audio.playbackRate = playbackRate.value
|
||||
audio.muted = muted.value
|
||||
audio.addEventListener('loadedmetadata', syncAudioDuration)
|
||||
audio.addEventListener('play', () => {
|
||||
playing.value = true
|
||||
@@ -123,6 +119,25 @@ const ensureAudioElement = () => {
|
||||
return audioElement
|
||||
}
|
||||
|
||||
const setPlaybackRate = (rate: number) => {
|
||||
const nextRate = Math.max(0.5, Math.min(2, Number.isFinite(rate) ? rate : 1))
|
||||
playbackRate.value = nextRate
|
||||
if (audioElement) {
|
||||
audioElement.playbackRate = nextRate
|
||||
}
|
||||
}
|
||||
|
||||
const setMuted = (nextMuted: boolean) => {
|
||||
muted.value = nextMuted
|
||||
if (audioElement) {
|
||||
audioElement.muted = nextMuted
|
||||
}
|
||||
}
|
||||
|
||||
const toggleMute = () => {
|
||||
setMuted(!muted.value)
|
||||
}
|
||||
|
||||
const resetState = () => {
|
||||
currentAudio.value = null
|
||||
currentSource.value = null
|
||||
@@ -134,11 +149,6 @@ const resetState = () => {
|
||||
duration.value = 0
|
||||
displayMode.value = 'mini'
|
||||
pendingLanguage.value = ''
|
||||
playbackRate.value = 1
|
||||
muted.value = false
|
||||
if (audioElement) {
|
||||
applyAudioPreferences(audioElement)
|
||||
}
|
||||
retryOnError = null
|
||||
retrying = false
|
||||
retryUsed = false
|
||||
@@ -242,7 +252,8 @@ const play = async (audio: AudioItem, options: GlobalAudioPlayOptions = {}) => {
|
||||
element.src = audio.audioUrl
|
||||
element.load()
|
||||
}
|
||||
applyAudioPreferences(element)
|
||||
element.playbackRate = playbackRate.value
|
||||
element.muted = muted.value
|
||||
|
||||
try {
|
||||
await element.play()
|
||||
@@ -275,7 +286,7 @@ const resume = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const switchLanguage = async (lang: AudioLanguage, localAudio?: AudioItem | null) => {
|
||||
const switchLanguage = async (lang: AudioLanguage) => {
|
||||
if (pendingLanguage.value) {
|
||||
return false
|
||||
}
|
||||
@@ -307,35 +318,6 @@ const switchLanguage = async (lang: AudioLanguage, localAudio?: AudioItem | null
|
||||
}
|
||||
|
||||
try {
|
||||
if (localAudio?.audioUrl) {
|
||||
currentSource.value = nextSource
|
||||
return await play(localAudio, {
|
||||
source: nextSource,
|
||||
retryOnError: retryOnError || undefined,
|
||||
displayMode: preservedMode
|
||||
})
|
||||
}
|
||||
|
||||
if (localAudio) {
|
||||
stopAudioElement()
|
||||
currentSource.value = nextSource
|
||||
currentAudio.value = {
|
||||
...localAudio,
|
||||
audioUrl: '',
|
||||
duration: 0,
|
||||
language: lang
|
||||
}
|
||||
visible.value = true
|
||||
displayMode.value = preservedMode
|
||||
playing.value = false
|
||||
loading.value = false
|
||||
currentTime.value = 0
|
||||
duration.value = 0
|
||||
error.value = '当前语言暂无语音讲解'
|
||||
showToast(error.value)
|
||||
return false
|
||||
}
|
||||
|
||||
const playInfo = await audioPlayInfoRepository.getPlayInfo({
|
||||
targetType: source.targetType,
|
||||
targetId: source.targetId,
|
||||
@@ -464,22 +446,6 @@ const seekToPercent = (percent: number) => {
|
||||
currentTime.value = targetTime
|
||||
}
|
||||
|
||||
const cyclePlaybackRate = () => {
|
||||
const currentIndex = PLAYBACK_RATES.indexOf(playbackRate.value as typeof PLAYBACK_RATES[number])
|
||||
const nextIndex = currentIndex >= 0 ? (currentIndex + 1) % PLAYBACK_RATES.length : 0
|
||||
playbackRate.value = PLAYBACK_RATES[nextIndex]
|
||||
if (audioElement) {
|
||||
audioElement.playbackRate = playbackRate.value
|
||||
}
|
||||
}
|
||||
|
||||
const toggleMuted = () => {
|
||||
muted.value = !muted.value
|
||||
if (audioElement) {
|
||||
audioElement.muted = muted.value
|
||||
}
|
||||
}
|
||||
|
||||
const registerHost = () => {
|
||||
hostSequence += 1
|
||||
const hostId = `global-audio-host-${hostSequence}`
|
||||
@@ -496,7 +462,7 @@ const unregisterHost = (hostId: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
const isCurrentSource = (source: Pick<GlobalAudioSource, 'targetType' | 'targetId' | 'lang'>) => {
|
||||
const isCurrentSource = (source: Pick<GlobalAudioSource, 'targetType' | 'targetId' | 'lang' | 'channelCode'>) => {
|
||||
if (!currentAudio.value || !currentSource.value) return false
|
||||
|
||||
return Boolean(
|
||||
@@ -505,6 +471,7 @@ const isCurrentSource = (source: Pick<GlobalAudioSource, 'targetType' | 'targetI
|
||||
&& currentSource.value.targetType === source.targetType
|
||||
&& currentSource.value.targetId === source.targetId
|
||||
&& (!source.lang || !currentSource.value.lang || currentSource.value.lang === source.lang)
|
||||
&& (!source.channelCode || currentSource.value.channelCode === source.channelCode)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -518,12 +485,12 @@ export const useGlobalAudioPlayer = () => ({
|
||||
currentTime,
|
||||
duration,
|
||||
displayMode,
|
||||
playbackRate,
|
||||
muted,
|
||||
pendingLanguage,
|
||||
activeHostId,
|
||||
lastClosedSource,
|
||||
closeVersion,
|
||||
playbackRate,
|
||||
muted,
|
||||
hasAudio: computed(() => Boolean(currentAudio.value)),
|
||||
play,
|
||||
pause,
|
||||
@@ -531,13 +498,14 @@ export const useGlobalAudioPlayer = () => ({
|
||||
stop,
|
||||
close,
|
||||
setDisplayMode,
|
||||
setPlaybackRate,
|
||||
setMuted,
|
||||
toggleMute,
|
||||
collapse,
|
||||
switchLanguage,
|
||||
handleEnded,
|
||||
handleError,
|
||||
seekToPercent,
|
||||
cyclePlaybackRate,
|
||||
toggleMuted,
|
||||
registerHost,
|
||||
activateHost,
|
||||
unregisterHost,
|
||||
|
||||
Reference in New Issue
Block a user