Merge pull request #173 from algerkong/fix/overlapping-playback

🐞 fix: 修复音乐播放重复声音的问题,添加锁机制,添加防抖机制,优化音频服务和快捷键处理逻辑
This commit is contained in:
Alger
2025-04-29 23:33:44 +08:00
committed by GitHub
2 changed files with 122 additions and 52 deletions
+46 -1
View File
@@ -41,6 +41,9 @@ class AudioService {
private seekDebounceTimer: NodeJS.Timeout | null = null; private seekDebounceTimer: NodeJS.Timeout | null = null;
// 添加操作锁防止并发操作
private operationLock = false;
constructor() { constructor() {
if ('mediaSession' in navigator) { if ('mediaSession' in navigator) {
this.initMediaSession(); this.initMediaSession();
@@ -358,6 +361,14 @@ class AudioService {
// 播放控制相关 // 播放控制相关
play(url?: string, track?: SongResult, isPlay: boolean = true): Promise<Howl> { play(url?: string, track?: SongResult, isPlay: boolean = true): Promise<Howl> {
// 如果操作锁已激活,说明有操作正在进行中,直接返回
if (this.operationLock) {
console.log('audioService: 操作锁激活,忽略当前播放请求');
return Promise.reject(new Error('操作锁激活,请等待当前操作完成'));
}
this.operationLock = true;
// 如果没有提供新的 URL 和 track,且当前有音频实例,则继续播放 // 如果没有提供新的 URL 和 track,且当前有音频实例,则继续播放
if (this.currentSound && !url && !track) { if (this.currentSound && !url && !track) {
// 如果有进行中的seek操作,等待其完成 // 如果有进行中的seek操作,等待其完成
@@ -366,15 +377,17 @@ class AudioService {
this.seekLock = false; this.seekLock = false;
} }
this.currentSound.play(); this.currentSound.play();
this.operationLock = false;
return Promise.resolve(this.currentSound); return Promise.resolve(this.currentSound);
} }
// 如果没有提供必要的参数,返回错误 // 如果没有提供必要的参数,返回错误
if (!url || !track) { if (!url || !track) {
this.operationLock = false;
return Promise.reject(new Error('Missing required parameters: url and track')); return Promise.reject(new Error('Missing required parameters: url and track'));
} }
return new Promise((resolve, reject) => { return new Promise<Howl>((resolve, reject) => {
let retryCount = 0; let retryCount = 0;
const maxRetries = 1; const maxRetries = 1;
@@ -507,11 +520,15 @@ class AudioService {
} }
} catch (error) { } catch (error) {
console.error('Error creating audio instance:', error); console.error('Error creating audio instance:', error);
this.operationLock = false;
reject(error); reject(error);
} }
}; };
tryPlay(); tryPlay();
}).finally(() => {
// 无论成功或失败都解除操作锁
this.operationLock = false;
}); });
} }
@@ -524,6 +541,13 @@ class AudioService {
} }
stop() { stop() {
if (this.operationLock) {
console.log('audioService: 操作锁激活,忽略当前停止请求');
return;
}
this.operationLock = true;
if (this.currentSound) { if (this.currentSound) {
try { try {
// 确保任何进行中的seek操作被取消 // 确保任何进行中的seek操作被取消
@@ -538,11 +562,14 @@ class AudioService {
} }
this.currentSound = null; this.currentSound = null;
} }
this.currentTrack = null; this.currentTrack = null;
if ('mediaSession' in navigator) { if ('mediaSession' in navigator) {
navigator.mediaSession.playbackState = 'none'; navigator.mediaSession.playbackState = 'none';
} }
this.disposeEQ(); this.disposeEQ();
this.operationLock = false;
} }
setVolume(volume: number) { setVolume(volume: number) {
@@ -553,6 +580,13 @@ class AudioService {
} }
seek(time: number) { seek(time: number) {
if (this.operationLock) {
console.log('audioService: 操作锁激活,忽略当前seek请求');
return;
}
this.operationLock = true;
if (this.currentSound) { if (this.currentSound) {
try { try {
// 直接执行seek操作,避免任何过滤或判断 // 直接执行seek操作,避免任何过滤或判断
@@ -564,9 +598,18 @@ class AudioService {
console.error('Seek操作失败:', error); console.error('Seek操作失败:', error);
} }
} }
this.operationLock = false;
} }
pause() { pause() {
if (this.operationLock) {
console.log('audioService: 操作锁激活,忽略当前暂停请求');
return;
}
this.operationLock = true;
if (this.currentSound) { if (this.currentSound) {
try { try {
// 如果有进行中的seek操作,等待其完成 // 如果有进行中的seek操作,等待其完成
@@ -579,6 +622,8 @@ class AudioService {
console.error('Error pausing audio:', error); console.error('Error pausing audio:', error);
} }
} }
this.operationLock = false;
} }
clearAllListeners() { clearAllListeners() {
+76 -51
View File
@@ -7,6 +7,10 @@ import { usePlayerStore, useSettingsStore } from '@/store';
import { isElectron } from '.'; import { isElectron } from '.';
import { showShortcutToast } from './shortcutToast'; import { showShortcutToast } from './shortcutToast';
// 添加一个简单的防抖机制
let actionTimeout: NodeJS.Timeout | null = null;
const ACTION_DELAY = 300; // 毫秒
interface ShortcutConfig { interface ShortcutConfig {
key: string; key: string;
enabled: boolean; enabled: boolean;
@@ -27,6 +31,17 @@ let appShortcuts: ShortcutsConfig = {};
* @param action 快捷键动作 * @param action 快捷键动作
*/ */
export async function handleShortcutAction(action: string) { export async function handleShortcutAction(action: string) {
// 如果存在未完成的动作,则忽略当前请求
if (actionTimeout) {
console.log('忽略快速连续的动作请求:', action);
return;
}
// 设置防抖锁
actionTimeout = setTimeout(() => {
actionTimeout = null;
}, ACTION_DELAY);
const playerStore = usePlayerStore(); const playerStore = usePlayerStore();
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
@@ -38,61 +53,71 @@ export async function handleShortcutAction(action: string) {
showShortcutToast(message, iconName); showShortcutToast(message, iconName);
}; };
switch (action) { try {
case 'togglePlay': switch (action) {
if (playerStore.play) { case 'togglePlay':
await audioService.pause(); if (playerStore.play) {
showToast(t('player.playBar.pause'), 'ri-pause-circle-line'); await audioService.pause();
} else { showToast(t('player.playBar.pause'), 'ri-pause-circle-line');
await audioService.play(); } else {
showToast(t('player.playBar.play'), 'ri-play-circle-line'); await audioService.play();
} showToast(t('player.playBar.play'), 'ri-play-circle-line');
break; }
case 'prevPlay': break;
playerStore.prevPlay(); case 'prevPlay':
showToast(t('player.playBar.prev'), 'ri-skip-back-line'); await playerStore.prevPlay();
break; showToast(t('player.playBar.prev'), 'ri-skip-back-line');
case 'nextPlay': break;
playerStore.nextPlay(); case 'nextPlay':
showToast(t('player.playBar.next'), 'ri-skip-forward-line'); await playerStore.nextPlay();
break; showToast(t('player.playBar.next'), 'ri-skip-forward-line');
case 'volumeUp': break;
if (currentSound && currentSound?.volume() < 1) { case 'volumeUp':
currentSound?.volume((currentSound?.volume() || 0) + 0.1); if (currentSound && currentSound?.volume() < 1) {
currentSound?.volume((currentSound?.volume() || 0) + 0.1);
showToast(
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`,
'ri-volume-up-line'
);
}
break;
case 'volumeDown':
if (currentSound && currentSound?.volume() > 0) {
currentSound?.volume((currentSound?.volume() || 0) - 0.1);
showToast(
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`,
'ri-volume-down-line'
);
}
break;
case 'toggleFavorite': {
const isFavorite = playerStore.favoriteList.includes(Number(playerStore.playMusic.id));
const numericId = Number(playerStore.playMusic.id);
if (isFavorite) {
playerStore.removeFromFavorite(numericId);
} else {
playerStore.addToFavorite(numericId);
}
showToast( showToast(
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`, isFavorite
'ri-volume-up-line' ? t('player.playBar.favorite', { name: playerStore.playMusic.name })
: t('player.playBar.unFavorite', { name: playerStore.playMusic.name }),
isFavorite ? 'ri-heart-fill' : 'ri-heart-line'
); );
break;
} }
break; default:
case 'volumeDown': console.log('未知的快捷键动作:', action);
if (currentSound && currentSound?.volume() > 0) { break;
currentSound?.volume((currentSound?.volume() || 0) - 0.1); }
showToast( } catch (error) {
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`, console.error(`执行快捷键动作 ${action} 时出错:`, error);
'ri-volume-down-line' } finally {
); // 确保在出错时也能清除超时
} if (actionTimeout) {
break; clearTimeout(actionTimeout);
case 'toggleFavorite': { actionTimeout = null;
const isFavorite = playerStore.favoriteList.includes(Number(playerStore.playMusic.id));
const numericId = Number(playerStore.playMusic.id);
if (isFavorite) {
playerStore.removeFromFavorite(numericId);
} else {
playerStore.addToFavorite(numericId);
}
showToast(
isFavorite
? t('player.playBar.favorite', { name: playerStore.playMusic.name })
: t('player.playBar.unFavorite', { name: playerStore.playMusic.name }),
isFavorite ? 'ri-heart-fill' : 'ri-heart-line'
);
break;
} }
default:
console.log('未知的快捷键动作:', action);
break;
} }
} }