fix: 解决 Safari 浏览器音频播放问题
- 创建浏览器检测工具,专门检测 Safari 和音频可视化支持 - 在 Safari 浏览器上禁用 AudioContext 连接,避免播放问题 - 保持其他浏览器的音频可视化功能正常工作 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5be5b4812f
commit
8ee2b928f9
|
@ -7,7 +7,7 @@ import { usePlayQueueStore } from '../stores/usePlayQueueStore'
|
||||||
|
|
||||||
import LoadingIndicator from '../assets/icons/loadingindicator.vue'
|
import LoadingIndicator from '../assets/icons/loadingindicator.vue'
|
||||||
import PlayIcon from '../assets/icons/play.vue'
|
import PlayIcon from '../assets/icons/play.vue'
|
||||||
import { audioVisualizer, checkAndRefreshSongResource } from '../utils'
|
import { audioVisualizer, checkAndRefreshSongResource, supportsWebAudioVisualization } from '../utils'
|
||||||
|
|
||||||
const playQueueStore = usePlayQueueStore()
|
const playQueueStore = usePlayQueueStore()
|
||||||
const favourites = useFavourites()
|
const favourites = useFavourites()
|
||||||
|
@ -263,21 +263,40 @@ function updateCurrentTime() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[Player] 初始化 audioVisualizer')
|
// 检查浏览器是否支持音频可视化
|
||||||
const { barHeights, connectAudio, isAnalyzing, error } = audioVisualizer({
|
const isAudioVisualizationSupported = supportsWebAudioVisualization()
|
||||||
sensitivity: 1.5,
|
console.log('[Player] 音频可视化支持状态:', isAudioVisualizationSupported)
|
||||||
barCount: 6,
|
|
||||||
maxDecibels: -10,
|
|
||||||
bassBoost: 0.8,
|
|
||||||
midBoost: 1.2,
|
|
||||||
trebleBoost: 1.4,
|
|
||||||
threshold: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('[Player] audioVisualizer 返回值:', {
|
// 只在支持的浏览器上初始化音频可视化
|
||||||
barHeights: barHeights.value,
|
let barHeights = ref<number[]>([0, 0, 0, 0, 0, 0])
|
||||||
isAnalyzing: isAnalyzing.value,
|
let connectAudio = (_audio: HTMLAudioElement) => {}
|
||||||
})
|
let isAnalyzing = ref(false)
|
||||||
|
let error = ref<string | null>(null)
|
||||||
|
|
||||||
|
if (isAudioVisualizationSupported) {
|
||||||
|
console.log('[Player] 初始化 audioVisualizer')
|
||||||
|
const visualizer = audioVisualizer({
|
||||||
|
sensitivity: 1.5,
|
||||||
|
barCount: 6,
|
||||||
|
maxDecibels: -10,
|
||||||
|
bassBoost: 0.8,
|
||||||
|
midBoost: 1.2,
|
||||||
|
trebleBoost: 1.4,
|
||||||
|
threshold: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
barHeights = visualizer.barHeights
|
||||||
|
connectAudio = visualizer.connectAudio
|
||||||
|
isAnalyzing = visualizer.isAnalyzing
|
||||||
|
error = visualizer.error
|
||||||
|
|
||||||
|
console.log('[Player] audioVisualizer 返回值:', {
|
||||||
|
barHeights: barHeights.value,
|
||||||
|
isAnalyzing: isAnalyzing.value,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log('[Player] 音频可视化被禁用(Safari 或不支持的浏览器)')
|
||||||
|
}
|
||||||
|
|
||||||
// 监听播放列表变化
|
// 监听播放列表变化
|
||||||
watch(
|
watch(
|
||||||
|
@ -293,13 +312,17 @@ watch(
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
if (player.value) {
|
if (player.value) {
|
||||||
console.log('[Player] 连接音频元素到可视化器')
|
if (isAudioVisualizationSupported) {
|
||||||
console.log('[Player] 音频元素状态:', {
|
console.log('[Player] 连接音频元素到可视化器')
|
||||||
src: player.value.src?.substring(0, 50) + '...',
|
console.log('[Player] 音频元素状态:', {
|
||||||
readyState: player.value.readyState,
|
src: player.value.src?.substring(0, 50) + '...',
|
||||||
paused: player.value.paused,
|
readyState: player.value.readyState,
|
||||||
})
|
paused: player.value.paused,
|
||||||
connectAudio(player.value)
|
})
|
||||||
|
connectAudio(player.value)
|
||||||
|
} else {
|
||||||
|
console.log('[Player] 跳过音频可视化连接(不支持的浏览器)')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('[Player] ❌ 音频元素不存在')
|
console.log('[Player] ❌ 音频元素不存在')
|
||||||
}
|
}
|
||||||
|
@ -322,7 +345,7 @@ watch(
|
||||||
watch(
|
watch(
|
||||||
() => player.value,
|
() => player.value,
|
||||||
(audioElement) => {
|
(audioElement) => {
|
||||||
if (audioElement && playQueueStore.list.length > 0) {
|
if (audioElement && playQueueStore.list.length > 0 && isAudioVisualizationSupported) {
|
||||||
connectAudio(audioElement)
|
connectAudio(audioElement)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
98
src/utils/browserDetection.ts
Normal file
98
src/utils/browserDetection.ts
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* 浏览器检测工具
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否为 Safari 浏览器
|
||||||
|
* @returns {boolean} 如果是 Safari 返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
export function isSafari(): boolean {
|
||||||
|
const ua = navigator.userAgent.toLowerCase()
|
||||||
|
|
||||||
|
// 检测 Safari 浏览器(包括 iOS 和 macOS)
|
||||||
|
// Safari 的 User Agent 包含 'safari' 但不包含 'chrome' 或 'chromium'
|
||||||
|
const isSafariBrowser = ua.includes('safari') &&
|
||||||
|
!ua.includes('chrome') &&
|
||||||
|
!ua.includes('chromium') &&
|
||||||
|
!ua.includes('android')
|
||||||
|
|
||||||
|
// 额外检查:使用 Safari 特有的 API
|
||||||
|
const isSafariByFeature = 'safari' in window ||
|
||||||
|
/^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||||
|
|
||||||
|
return isSafariBrowser || isSafariByFeature
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否为移动版 Safari
|
||||||
|
* @returns {boolean} 如果是移动版 Safari 返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
export function isMobileSafari(): boolean {
|
||||||
|
return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否支持 Web Audio API 的完整功能
|
||||||
|
* @returns {boolean} 如果支持返回 true,否则返回 false
|
||||||
|
*/
|
||||||
|
export function supportsWebAudioVisualization(): boolean {
|
||||||
|
// Safari 在某些情况下对 AudioContext 的支持有限制
|
||||||
|
// 特别是在处理跨域音频资源时
|
||||||
|
if (isSafari()) {
|
||||||
|
console.log('[BrowserDetection] Safari detected, audio visualization disabled')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查基本的 Web Audio API 支持
|
||||||
|
const hasAudioContext = 'AudioContext' in window || 'webkitAudioContext' in window
|
||||||
|
const hasAnalyserNode = hasAudioContext && (
|
||||||
|
'AnalyserNode' in window ||
|
||||||
|
(window.AudioContext && 'createAnalyser' in AudioContext.prototype)
|
||||||
|
)
|
||||||
|
|
||||||
|
return hasAudioContext && hasAnalyserNode
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取浏览器信息
|
||||||
|
* @returns {object} 包含浏览器类型和版本信息的对象
|
||||||
|
*/
|
||||||
|
export function getBrowserInfo() {
|
||||||
|
const ua = navigator.userAgent
|
||||||
|
let browserName = 'Unknown'
|
||||||
|
let browserVersion = 'Unknown'
|
||||||
|
|
||||||
|
if (isSafari()) {
|
||||||
|
browserName = 'Safari'
|
||||||
|
const versionMatch = ua.match(/Version\/(\d+\.\d+)/)
|
||||||
|
if (versionMatch) {
|
||||||
|
browserVersion = versionMatch[1]
|
||||||
|
}
|
||||||
|
} else if (ua.includes('Chrome')) {
|
||||||
|
browserName = 'Chrome'
|
||||||
|
const versionMatch = ua.match(/Chrome\/(\d+\.\d+)/)
|
||||||
|
if (versionMatch) {
|
||||||
|
browserVersion = versionMatch[1]
|
||||||
|
}
|
||||||
|
} else if (ua.includes('Firefox')) {
|
||||||
|
browserName = 'Firefox'
|
||||||
|
const versionMatch = ua.match(/Firefox\/(\d+\.\d+)/)
|
||||||
|
if (versionMatch) {
|
||||||
|
browserVersion = versionMatch[1]
|
||||||
|
}
|
||||||
|
} else if (ua.includes('Edge')) {
|
||||||
|
browserName = 'Edge'
|
||||||
|
const versionMatch = ua.match(/Edge\/(\d+\.\d+)/)
|
||||||
|
if (versionMatch) {
|
||||||
|
browserVersion = versionMatch[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: browserName,
|
||||||
|
version: browserVersion,
|
||||||
|
isSafari: isSafari(),
|
||||||
|
isMobileSafari: isMobileSafari(),
|
||||||
|
supportsAudioVisualization: supportsWebAudioVisualization()
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,5 +2,16 @@ import artistsOrganize from "./artistsOrganize"
|
||||||
import { audioVisualizer } from "./audioVisualizer"
|
import { audioVisualizer } from "./audioVisualizer"
|
||||||
import cicdInfo from "./cicdInfo"
|
import cicdInfo from "./cicdInfo"
|
||||||
import { checkAndRefreshSongResource, checkAndRefreshMultipleSongs } from "./songResourceChecker"
|
import { checkAndRefreshSongResource, checkAndRefreshMultipleSongs } from "./songResourceChecker"
|
||||||
|
import { isSafari, isMobileSafari, supportsWebAudioVisualization, getBrowserInfo } from "./browserDetection"
|
||||||
|
|
||||||
export { artistsOrganize, audioVisualizer, cicdInfo, checkAndRefreshSongResource, checkAndRefreshMultipleSongs }
|
export {
|
||||||
|
artistsOrganize,
|
||||||
|
audioVisualizer,
|
||||||
|
cicdInfo,
|
||||||
|
checkAndRefreshSongResource,
|
||||||
|
checkAndRefreshMultipleSongs,
|
||||||
|
isSafari,
|
||||||
|
isMobileSafari,
|
||||||
|
supportsWebAudioVisualization,
|
||||||
|
getBrowserInfo
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user