import { computed, readonly, ref, watch } from 'vue' import { useLocale } from '~/utils/locale' import { getLoginStatus } from '~/utils/neteaseApi' // 音质配置 export const QUALITY_OPTIONS = { netease: [ { value: 2, key: 'standard' }, { value: 4, key: 'neteaseHq' }, { value: 5, key: 'neteaseSq' }, { value: 6, key: 'neteaseHiRes' }, { value: 9, key: 'neteaseMaster' } ], tencent: [ { value: 4, key: 'standard' }, { value: 8, key: 'tencentHq' }, { value: 10, key: 'tencentSq' }, { value: 11, key: 'tencentHiRes' }, { value: 14, key: 'tencentMaster' } ], bilibili: [{ value: 1, key: 'bilibiliDefault' }], migu: [ { value: 1, key: 'standard' }, { value: 2, key: 'miguHq' }, { value: 3, key: 'miguSq' }, { value: 4, key: 'miguZq24' }, ] } // 默认音质设置 const DEFAULT_QUALITY = { netease: 4, // HQ极高 (320k) tencent: 8, // HQ高音质 bilibili: 1, migu: 1 // 标准音质(咪咕匿名仅提供 128k) } // 全局音质状态,确保所有组件共享同一个状态 let globalAudioQuality: any = null // 网易云登录状态,全局共享 const isNeteaseLoggedIn = ref(false) let isLoginStatusInitialized = false // 网易 VIP 标志(localStorage netease_vip):vipType 非 0 即 VIP,仅 VIP 登录态才优先官方播放源 const persistNeteaseVipFlag = (profile: any) => { if (typeof window === 'undefined') return try { if (profile && typeof profile.vipType === 'number') { localStorage.setItem('netease_vip', profile.vipType !== 0 ? '1' : '0') } else { localStorage.removeItem('netease_vip') } } catch (error) { console.error('[audioQuality] Failed to persist netease_vip:', error) } } const clearNeteaseVipFlag = () => { if (typeof window === 'undefined') return try { localStorage.removeItem('netease_vip') } catch { // localStorage 不可用时忽略 } } // 每会话一次的 VIP 状态静默刷新守卫 let isNeteaseVipRefreshing = false let isNeteaseVipRefreshed = false // 静默刷新 VIP 状态:有 cookie 时调 /login/status 校验登录态并同步 VIP 标志 const refreshNeteaseVipStatus = async () => { if (typeof window === 'undefined' || isNeteaseVipRefreshed || isNeteaseVipRefreshing) return let cookie: string | null = null try { cookie = localStorage.getItem('netease_cookie') } catch { // localStorage 不可用时跳过刷新 } if (!cookie) return isNeteaseVipRefreshing = true try { const res = await getLoginStatus(cookie) const dataObj = res.body?.data || res.body if (dataObj && dataObj.account) { isNeteaseLoggedIn.value = true persistNeteaseVipFlag(dataObj.profile) } else { // 登录失效:仅清 VIP 标志,cookie 与组件本地状态由组件级校验负责清理 clearNeteaseVipFlag() isNeteaseLoggedIn.value = false } } catch { // 网络异常保留本地状态 } finally { isNeteaseVipRefreshing = false isNeteaseVipRefreshed = true } } export function useAudioQuality() { const { ui } = useLocale() const locale = computed(() => ui.value?.audioQuality || {}) // 检查网易云登录状态 const checkNeteaseLoginStatus = () => { if (typeof window === 'undefined') return const cookie = localStorage.getItem('netease_cookie') isNeteaseLoggedIn.value = !!cookie } // 从localStorage读取音质设置 const getStoredQuality = () => { try { const stored = localStorage.getItem('audioQuality') return stored ? JSON.parse(stored) : DEFAULT_QUALITY } catch { return DEFAULT_QUALITY } } // 使用全局状态,确保所有组件共享 if (!globalAudioQuality) { globalAudioQuality = ref(getStoredQuality()) // 监听状态变化,自动保存到localStorage watch( globalAudioQuality, (newValue) => { try { localStorage.setItem('audioQuality', JSON.stringify(newValue)) } catch (error) { console.error('[audioQuality] Failed to save quality settings:', error) } }, { deep: true } ) } // 初始化登录状态监听 if (!isLoginStatusInitialized && typeof window !== 'undefined') { checkNeteaseLoginStatus() refreshNeteaseVipStatus() window.addEventListener('storage', (e) => { if (e.key === 'netease_cookie') { checkNeteaseLoginStatus() // 其他标签页登录态变化时重新同步 VIP 标志 isNeteaseVipRefreshed = false refreshNeteaseVipStatus() } }) isLoginStatusInitialized = true } const audioQuality = globalAudioQuality // 保存音质设置到localStorage const saveQuality = (platform: string, quality: number) => { audioQuality.value[platform] = quality // localStorage保存已经通过watch自动处理 } // 获取指定平台的音质设置 const getQuality = (platform: string) => { // 处理 netease-podcast 别名 if (platform === 'netease-podcast') { platform = 'netease' } const stored = audioQuality.value[platform] // 已保存的音质值不再存在于选项列表时(如咪咕音质收敛后残留旧值),回落默认值 const platformOptions = (QUALITY_OPTIONS as Record>)[platform] || [] const isValid = platformOptions.some((option) => option.value === stored) if (isValid) { return stored } return (DEFAULT_QUALITY as Record)[platform] } // 获取指定平台的音质选项 const getQualityOptions = (platform: string) => { // 处理 netease-podcast 别名 if (platform === 'netease-podcast') { platform = 'netease' } return (QUALITY_OPTIONS[platform] || []).map((option) => ({ ...option, label: locale.value?.options?.[option.key]?.label || option.key, description: locale.value?.options?.[option.key]?.description || '使用推荐音质设置' })) } // 获取音质标签 const getQualityLabel = (platform: string, quality: number) => { // 处理 netease-podcast 别名 if (platform === 'netease-podcast') { platform = 'netease' } const options = getQualityOptions(platform) const option = options.find((opt) => opt.value === quality) return option ? option.label : locale.value.unknown } // 获取音质描述 const getQualityDescription = (platform: string, quality: number) => { // 处理 netease-podcast 别名 if (platform === 'netease-podcast') { platform = 'netease' } const options = getQualityOptions(platform) const option = options.find((opt) => opt.value === quality) return option ? option.description : '' } // 计算属性:当前音质设置的可读文本 const currentQualityText = computed(() => { const netease = getQualityLabel('netease', getQuality('netease')) const tencent = getQualityLabel('tencent', getQuality('tencent')) return { netease, tencent } }) return { audioQuality: readonly(audioQuality), saveQuality, getQuality, getQualityOptions, getQualityLabel, getQualityDescription, currentQualityText, QUALITY_OPTIONS, DEFAULT_QUALITY, checkNeteaseLoginStatus, isNeteaseLoggedIn: readonly(isNeteaseLoggedIn), persistNeteaseVipFlag, clearNeteaseVipFlag } }