Skip to content

Commit 159dd03

Browse files
committed
🐞 fix: 修复音乐播放重复声音的问题,添加锁机制,添加防抖机制,优化音频服务和快捷键处理逻辑
1 parent 167c8ad commit 159dd03

File tree

2 files changed

+122
-52
lines changed

2 files changed

+122
-52
lines changed

src/renderer/services/audioService.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class AudioService {
4141

4242
private seekDebounceTimer: NodeJS.Timeout | null = null;
4343

44+
// 添加操作锁防止并发操作
45+
private operationLock = false;
46+
4447
constructor() {
4548
if ('mediaSession' in navigator) {
4649
this.initMediaSession();
@@ -358,6 +361,14 @@ class AudioService {
358361

359362
// 播放控制相关
360363
play(url?: string, track?: SongResult, isPlay: boolean = true): Promise<Howl> {
364+
// 如果操作锁已激活,说明有操作正在进行中,直接返回
365+
if (this.operationLock) {
366+
console.log('audioService: 操作锁激活,忽略当前播放请求');
367+
return Promise.reject(new Error('操作锁激活,请等待当前操作完成'));
368+
}
369+
370+
this.operationLock = true;
371+
361372
// 如果没有提供新的 URL 和 track,且当前有音频实例,则继续播放
362373
if (this.currentSound && !url && !track) {
363374
// 如果有进行中的seek操作,等待其完成
@@ -366,15 +377,17 @@ class AudioService {
366377
this.seekLock = false;
367378
}
368379
this.currentSound.play();
380+
this.operationLock = false;
369381
return Promise.resolve(this.currentSound);
370382
}
371383

372384
// 如果没有提供必要的参数,返回错误
373385
if (!url || !track) {
386+
this.operationLock = false;
374387
return Promise.reject(new Error('Missing required parameters: url and track'));
375388
}
376389

377-
return new Promise((resolve, reject) => {
390+
return new Promise<Howl>((resolve, reject) => {
378391
let retryCount = 0;
379392
const maxRetries = 1;
380393

@@ -507,11 +520,15 @@ class AudioService {
507520
}
508521
} catch (error) {
509522
console.error('Error creating audio instance:', error);
523+
this.operationLock = false;
510524
reject(error);
511525
}
512526
};
513527

514528
tryPlay();
529+
}).finally(() => {
530+
// 无论成功或失败都解除操作锁
531+
this.operationLock = false;
515532
});
516533
}
517534

@@ -524,6 +541,13 @@ class AudioService {
524541
}
525542

526543
stop() {
544+
if (this.operationLock) {
545+
console.log('audioService: 操作锁激活,忽略当前停止请求');
546+
return;
547+
}
548+
549+
this.operationLock = true;
550+
527551
if (this.currentSound) {
528552
try {
529553
// 确保任何进行中的seek操作被取消
@@ -538,11 +562,14 @@ class AudioService {
538562
}
539563
this.currentSound = null;
540564
}
565+
541566
this.currentTrack = null;
542567
if ('mediaSession' in navigator) {
543568
navigator.mediaSession.playbackState = 'none';
544569
}
545570
this.disposeEQ();
571+
572+
this.operationLock = false;
546573
}
547574

548575
setVolume(volume: number) {
@@ -553,6 +580,13 @@ class AudioService {
553580
}
554581

555582
seek(time: number) {
583+
if (this.operationLock) {
584+
console.log('audioService: 操作锁激活,忽略当前seek请求');
585+
return;
586+
}
587+
588+
this.operationLock = true;
589+
556590
if (this.currentSound) {
557591
try {
558592
// 直接执行seek操作,避免任何过滤或判断
@@ -564,9 +598,18 @@ class AudioService {
564598
console.error('Seek操作失败:', error);
565599
}
566600
}
601+
602+
this.operationLock = false;
567603
}
568604

569605
pause() {
606+
if (this.operationLock) {
607+
console.log('audioService: 操作锁激活,忽略当前暂停请求');
608+
return;
609+
}
610+
611+
this.operationLock = true;
612+
570613
if (this.currentSound) {
571614
try {
572615
// 如果有进行中的seek操作,等待其完成
@@ -579,6 +622,8 @@ class AudioService {
579622
console.error('Error pausing audio:', error);
580623
}
581624
}
625+
626+
this.operationLock = false;
582627
}
583628

584629
clearAllListeners() {

src/renderer/utils/appShortcuts.ts

Lines changed: 76 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { usePlayerStore, useSettingsStore } from '@/store';
77
import { isElectron } from '.';
88
import { showShortcutToast } from './shortcutToast';
99

10+
// 添加一个简单的防抖机制
11+
let actionTimeout: NodeJS.Timeout | null = null;
12+
const ACTION_DELAY = 300; // 毫秒
13+
1014
interface ShortcutConfig {
1115
key: string;
1216
enabled: boolean;
@@ -27,6 +31,17 @@ let appShortcuts: ShortcutsConfig = {};
2731
* @param action 快捷键动作
2832
*/
2933
export async function handleShortcutAction(action: string) {
34+
// 如果存在未完成的动作,则忽略当前请求
35+
if (actionTimeout) {
36+
console.log('忽略快速连续的动作请求:', action);
37+
return;
38+
}
39+
40+
// 设置防抖锁
41+
actionTimeout = setTimeout(() => {
42+
actionTimeout = null;
43+
}, ACTION_DELAY);
44+
3045
const playerStore = usePlayerStore();
3146
const settingsStore = useSettingsStore();
3247

@@ -38,61 +53,71 @@ export async function handleShortcutAction(action: string) {
3853
showShortcutToast(message, iconName);
3954
};
4055

41-
switch (action) {
42-
case 'togglePlay':
43-
if (playerStore.play) {
44-
await audioService.pause();
45-
showToast(t('player.playBar.pause'), 'ri-pause-circle-line');
46-
} else {
47-
await audioService.play();
48-
showToast(t('player.playBar.play'), 'ri-play-circle-line');
49-
}
50-
break;
51-
case 'prevPlay':
52-
playerStore.prevPlay();
53-
showToast(t('player.playBar.prev'), 'ri-skip-back-line');
54-
break;
55-
case 'nextPlay':
56-
playerStore.nextPlay();
57-
showToast(t('player.playBar.next'), 'ri-skip-forward-line');
58-
break;
59-
case 'volumeUp':
60-
if (currentSound && currentSound?.volume() < 1) {
61-
currentSound?.volume((currentSound?.volume() || 0) + 0.1);
56+
try {
57+
switch (action) {
58+
case 'togglePlay':
59+
if (playerStore.play) {
60+
await audioService.pause();
61+
showToast(t('player.playBar.pause'), 'ri-pause-circle-line');
62+
} else {
63+
await audioService.play();
64+
showToast(t('player.playBar.play'), 'ri-play-circle-line');
65+
}
66+
break;
67+
case 'prevPlay':
68+
await playerStore.prevPlay();
69+
showToast(t('player.playBar.prev'), 'ri-skip-back-line');
70+
break;
71+
case 'nextPlay':
72+
await playerStore.nextPlay();
73+
showToast(t('player.playBar.next'), 'ri-skip-forward-line');
74+
break;
75+
case 'volumeUp':
76+
if (currentSound && currentSound?.volume() < 1) {
77+
currentSound?.volume((currentSound?.volume() || 0) + 0.1);
78+
showToast(
79+
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`,
80+
'ri-volume-up-line'
81+
);
82+
}
83+
break;
84+
case 'volumeDown':
85+
if (currentSound && currentSound?.volume() > 0) {
86+
currentSound?.volume((currentSound?.volume() || 0) - 0.1);
87+
showToast(
88+
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`,
89+
'ri-volume-down-line'
90+
);
91+
}
92+
break;
93+
case 'toggleFavorite': {
94+
const isFavorite = playerStore.favoriteList.includes(Number(playerStore.playMusic.id));
95+
const numericId = Number(playerStore.playMusic.id);
96+
if (isFavorite) {
97+
playerStore.removeFromFavorite(numericId);
98+
} else {
99+
playerStore.addToFavorite(numericId);
100+
}
62101
showToast(
63-
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`,
64-
'ri-volume-up-line'
102+
isFavorite
103+
? t('player.playBar.favorite', { name: playerStore.playMusic.name })
104+
: t('player.playBar.unFavorite', { name: playerStore.playMusic.name }),
105+
isFavorite ? 'ri-heart-fill' : 'ri-heart-line'
65106
);
107+
break;
66108
}
67-
break;
68-
case 'volumeDown':
69-
if (currentSound && currentSound?.volume() > 0) {
70-
currentSound?.volume((currentSound?.volume() || 0) - 0.1);
71-
showToast(
72-
`${t('player.playBar.volume')}${Math.round((currentSound?.volume() || 0) * 100)}%`,
73-
'ri-volume-down-line'
74-
);
75-
}
76-
break;
77-
case 'toggleFavorite': {
78-
const isFavorite = playerStore.favoriteList.includes(Number(playerStore.playMusic.id));
79-
const numericId = Number(playerStore.playMusic.id);
80-
if (isFavorite) {
81-
playerStore.removeFromFavorite(numericId);
82-
} else {
83-
playerStore.addToFavorite(numericId);
84-
}
85-
showToast(
86-
isFavorite
87-
? t('player.playBar.favorite', { name: playerStore.playMusic.name })
88-
: t('player.playBar.unFavorite', { name: playerStore.playMusic.name }),
89-
isFavorite ? 'ri-heart-fill' : 'ri-heart-line'
90-
);
91-
break;
109+
default:
110+
console.log('未知的快捷键动作:', action);
111+
break;
112+
}
113+
} catch (error) {
114+
console.error(`执行快捷键动作 ${action} 时出错:`, error);
115+
} finally {
116+
// 确保在出错时也能清除超时
117+
if (actionTimeout) {
118+
clearTimeout(actionTimeout);
119+
actionTimeout = null;
92120
}
93-
default:
94-
console.log('未知的快捷键动作:', action);
95-
break;
96121
}
97122
}
98123

0 commit comments

Comments
 (0)