Files
AlgerMusicPlayer/src/renderer/store/modules/playerCore.ts
T
alger 0cfec3dd82 refactor(player): 重构播放控制系统,移除 Howler.js 改用原生 HTMLAudioElement
- 新建 playbackController.ts,使用 generation-based 取消替代 playbackRequestManager 状态机
- audioService 重写:单一持久 HTMLAudioElement + Web Audio API,createMediaElementSource 只调一次
- playerCore 瘦身为纯状态管理,移除 handlePlayMusic/playAudio/checkPlaybackState
- playlist next/prev 简化,区分用户手动切歌和歌曲自然播完
- MusicHook 适配 HTMLAudioElement API(.currentTime/.duration/.paused)
- preloadService 从 Howl 实例缓存改为 URL 可用性验证
- 所有 view/component 调用者迁移到 playbackController.playTrack()

修复:快速切歌竞态、seek 到未缓冲位置失败、重启后自动播放循环提示、EQ 重建崩溃
2026-03-29 13:18:05 +08:00

204 lines
5.2 KiB
TypeScript

import { defineStore } from 'pinia';
import { computed, ref } from 'vue';
import { audioService } from '@/services/audioService';
import type { AudioOutputDevice } from '@/types/audio';
import type { SongResult } from '@/types/music';
/**
* 核心播放控制 Store
* 负责:播放/暂停、当前歌曲、音频URL、音量、播放速度、全屏状态
*/
export const usePlayerCoreStore = defineStore(
'playerCore',
() => {
// ==================== 状态 ====================
const play = ref(false);
const isPlay = ref(false);
const playMusic = ref<SongResult>({} as SongResult);
const playMusicUrl = ref('');
const musicFull = ref(false);
const playbackRate = ref(1.0);
const volume = ref(1);
const userPlayIntent = ref(false); // 用户是否想要播放
const isFmPlaying = ref(false); // 是否正在播放私人FM
// 音频输出设备
const audioOutputDeviceId = ref<string>(
localStorage.getItem('audioOutputDeviceId') || 'default'
);
const availableAudioDevices = ref<AudioOutputDevice[]>([]);
// ==================== Computed ====================
const currentSong = computed(() => playMusic.value);
const isPlaying = computed(() => isPlay.value);
// ==================== Actions ====================
/**
* 设置播放状态
*/
const setIsPlay = (value: boolean) => {
isPlay.value = value;
play.value = value;
window.electron?.ipcRenderer.send('update-play-state', value);
};
/**
* 设置全屏状态
*/
const setMusicFull = (value: boolean) => {
musicFull.value = value;
};
/**
* 设置播放速度
*/
const setPlaybackRate = (rate: number) => {
playbackRate.value = rate;
audioService.setPlaybackRate(rate);
};
/**
* 设置音量
*/
const setVolume = (newVolume: number) => {
const normalizedVolume = Math.max(0, Math.min(1, newVolume));
volume.value = normalizedVolume;
audioService.setVolume(normalizedVolume);
};
/**
* 获取音量
*/
const getVolume = () => volume.value;
/**
* 增加音量
*/
const increaseVolume = (step: number = 0.1) => {
const newVolume = Math.min(1, volume.value + step);
setVolume(newVolume);
return newVolume;
};
/**
* 减少音量
*/
const decreaseVolume = (step: number = 0.1) => {
const newVolume = Math.max(0, volume.value - step);
setVolume(newVolume);
return newVolume;
};
/**
* 暂停播放
*/
const handlePause = async () => {
try {
const currentSound = audioService.getCurrentSound();
if (currentSound) {
currentSound.pause();
}
setPlayMusic(false);
userPlayIntent.value = false;
} catch (error) {
console.error('暂停播放失败:', error);
}
};
/**
* 设置播放/暂停
*/
const setPlayMusic = async (value: boolean | SongResult) => {
if (typeof value === 'boolean') {
setIsPlay(value);
userPlayIntent.value = value;
} else {
const { playTrack } = await import('@/services/playbackController');
await playTrack(value);
play.value = true;
isPlay.value = true;
userPlayIntent.value = true;
}
};
// ==================== 音频输出设备管理 ====================
/**
* 刷新可用音频输出设备列表
*/
const refreshAudioDevices = async () => {
availableAudioDevices.value = await audioService.getAudioOutputDevices();
};
/**
* 切换音频输出设备
*/
const setAudioOutputDevice = async (deviceId: string): Promise<boolean> => {
const success = await audioService.setAudioOutputDevice(deviceId);
if (success) {
audioOutputDeviceId.value = deviceId;
}
return success;
};
/**
* 初始化设备变化监听
*/
const initAudioDeviceListener = () => {
if (navigator.mediaDevices) {
navigator.mediaDevices.addEventListener('devicechange', async () => {
await refreshAudioDevices();
const exists = availableAudioDevices.value.some(
(d) => d.deviceId === audioOutputDeviceId.value
);
if (!exists && audioOutputDeviceId.value !== 'default') {
await setAudioOutputDevice('default');
}
});
}
};
return {
// 状态
play,
isPlay,
playMusic,
playMusicUrl,
musicFull,
playbackRate,
volume,
userPlayIntent,
isFmPlaying,
audioOutputDeviceId,
availableAudioDevices,
// Computed
currentSong,
isPlaying,
// Actions
setIsPlay,
setMusicFull,
setPlayMusic,
setPlaybackRate,
setVolume,
getVolume,
increaseVolume,
decreaseVolume,
handlePause,
refreshAudioDevices,
setAudioOutputDevice,
initAudioDeviceListener
};
},
{
persist: {
key: 'player-core-store',
storage: localStorage,
pick: ['playMusic', 'playMusicUrl', 'playbackRate', 'volume', 'isPlay', 'audioOutputDeviceId']
}
}
);