feat: 优化音频监听器初始化和设置保存逻辑

- 在 App.vue 中引入 initAudioListeners 函数,确保在播放音乐时初始化音频监听器。
- 在 MusicHook.ts 中重构音频监听器的初始化逻辑,增加音频加载的超时处理。
- 在设置页面中实现防抖保存功能,避免频繁更新设置,提高性能和用户体验。

这些更改旨在提升音频播放的稳定性和设置管理的效率。
This commit is contained in:
alger
2025-03-21 00:19:15 +08:00
parent 650e4ff786
commit fa39d4ca55
5 changed files with 138 additions and 49 deletions

View File

@@ -12,7 +12,7 @@
<script setup lang="ts">
import { darkTheme, lightTheme } from 'naive-ui';
import { computed, onMounted, watch } from 'vue';
import { computed, nextTick, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import homeRouter from '@/router/home';
@@ -21,6 +21,7 @@ import { usePlayerStore } from '@/store/modules/player';
import { useSettingsStore } from '@/store/modules/settings';
import { isElectron } from '@/utils';
import { initAudioListeners } from './hooks/MusicHook';
import { isMobile } from './utils';
const { locale } = useI18n();
@@ -61,18 +62,27 @@ const handleSetLanguage = (_: any, value: string) => {
// settingsStore.setLanguage(value);
};
onMounted(() => {
settingsStore.initializeSettings();
handleSetLanguage(null, settingsStore.setData.language);
settingsStore.initializeTheme();
settingsStore.initializeSystemFonts();
playerStore.initializePlayState();
if (isMobile.value) {
menuStore.setMenus(homeRouter.filter((item) => item.meta.isMobile));
}
settingsStore.initializeSettings();
handleSetLanguage(null, settingsStore.setData.language);
settingsStore.initializeTheme();
settingsStore.initializeSystemFonts();
if (isMobile.value) {
menuStore.setMenus(homeRouter.filter((item) => item.meta.isMobile));
}
if (isElectron) {
window.electron.ipcRenderer.on('language-changed', handleSetLanguage);
if (isElectron) {
window.electron.ipcRenderer.on('language-changed', handleSetLanguage);
}
onMounted(async () => {
// 先初始化播放状态
await playerStore.initializePlayState();
// 如果有正在播放的音乐,则初始化音频监听器
if (playerStore.playMusic && playerStore.playMusic.id) {
// 使用 nextTick 确保 DOM 更新后再初始化
await nextTick();
initAudioListeners();
}
});
</script>

View File

@@ -1,5 +1,5 @@
import { createDiscreteApi } from 'naive-ui';
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { computed, nextTick, onUnmounted, ref, watch } from 'vue';
import i18n from '@/../i18n/renderer';
import useIndexedDB from '@/hooks/IndexDBHook';
@@ -763,37 +763,74 @@ if (isElectron) {
}
// 在组件挂载时设置监听器
onMounted(() => {
// 初始化音频监听器
setupAudioListeners();
// 监听歌词窗口关闭事件
if (isElectron) {
window.api.onLyricWindowClosed(() => {
isLyricWindowOpen.value = false;
});
}
// 检查是否需要初始化 sound 对象
if (!sound.value && audioService.getCurrentSound()) {
sound.value = audioService.getCurrentSound();
// 如果当前处于播放状态,启动进度更新
if (playerStore.play && sound.value) {
// 如果有保存的播放进度,应用它
if (playerStore.savedPlayProgress !== undefined && sound.value) {
try {
// 设置音频位置
sound.value.seek(playerStore.savedPlayProgress);
// 同时更新时间显示,这样进度条也会更新
nowTime.value = playerStore.savedPlayProgress;
console.log('恢复播放进度:', playerStore.savedPlayProgress);
} catch (e) {
console.error('恢复播放进度失败:', e);
}
}
startProgressAnimation();
export const initAudioListeners = async () => {
try {
// 确保有正在播放的音乐
if (!playerStore.playMusic || !playerStore.playMusic.id) {
console.log('没有正在播放的音乐,跳过音频监听器初始化');
return;
}
// 确保有音频实例
const initialSound = audioService.getCurrentSound();
if (!initialSound) {
console.log('没有音频实例,等待音频加载...');
// 等待音频加载完成
await new Promise<void>((resolve) => {
const checkInterval = setInterval(() => {
const sound = audioService.getCurrentSound();
if (sound) {
clearInterval(checkInterval);
resolve();
}
}, 100);
// 设置超时
setTimeout(() => {
clearInterval(checkInterval);
console.log('等待音频加载超时');
resolve();
}, 5000);
});
}
// 初始化音频监听器
setupAudioListeners();
// 监听歌词窗口关闭事件
if (isElectron) {
window.api.onLyricWindowClosed(() => {
isLyricWindowOpen.value = false;
});
}
// 获取最新的音频实例
const finalSound = audioService.getCurrentSound();
if (finalSound) {
// 更新全局 sound 引用
sound.value = finalSound;
// 如果当前处于播放状态,启动进度更新
if (playerStore.play) {
// 如果有保存的播放进度,应用它
if (playerStore.savedPlayProgress !== undefined) {
try {
// 设置音频位置
finalSound.seek(playerStore.savedPlayProgress);
// 同时更新时间显示
nowTime.value = playerStore.savedPlayProgress;
console.log('恢复播放进度:', playerStore.savedPlayProgress);
} catch (e) {
console.error('恢复播放进度失败:', e);
}
}
startProgressAnimation();
}
} else {
console.warn('无法获取音频实例,跳过进度更新初始化');
}
} catch (error) {
console.error('初始化音频监听器失败:', error);
}
});
};

View File

@@ -359,7 +359,7 @@ export const usePlayerStore = defineStore('player', () => {
try {
console.log('settingStore.setData', settingStore.setData);
const isPlaying = settingStore.setData.autoPlay;
await handlePlayMusic(savedPlayMusic, isPlaying);
await handlePlayMusic({ ...savedPlayMusic, playMusicUrl: undefined }, isPlaying);
if (savedProgress) {
try {

View File

@@ -37,6 +37,7 @@ export const useSettingsStore = defineStore('settings', () => {
if (isElectron) {
window.electron.ipcRenderer.send('set-store-value', 'set', cloneDeep(mergedData));
console.log('mergedData', mergedData);
setData.value = cloneDeep(mergedData);
} else {
localStorage.setItem('appSettings', JSON.stringify(cloneDeep(mergedData)));

View File

@@ -461,9 +461,11 @@
</template>
<script setup lang="ts">
import { useDebounceFn } from '@vueuse/core';
import { debounce } from 'lodash';
import type { FormRules } from 'naive-ui';
import { useMessage } from 'naive-ui';
import { computed, h, nextTick, onMounted, ref, watch } from 'vue';
import { computed, h, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import localData from '@/../main/set.json';
@@ -483,6 +485,15 @@ import config from '../../../../package.json';
const settingsStore = useSettingsStore();
const userStore = useUserStore();
// 创建一个本地缓存的setData避免频繁更新
const localSetData = ref({ ...settingsStore.setData });
// 在组件卸载时保存设置
onUnmounted(() => {
// 确保最终设置被保存
settingsStore.setSetData(localSetData.value);
});
const checking = ref(false);
const updateInfo = ref<UpdateResult>({
hasUpdate: false,
@@ -493,14 +504,44 @@ const updateInfo = ref<UpdateResult>({
const { t } = useI18n();
// 创建一个防抖的保存函数
// const debouncedSaveSettings = debounce((newData) => {
// settingsStore.setSetData(newData);
// }, 500);
const saveSettings = useDebounceFn((data) => {
settingsStore.setSetData(data);
}, 500);
// 使用计算属性来管理设置数据
const setData = computed({
get: () => settingsStore.setData,
get: () => localSetData.value,
set: (newData) => {
settingsStore.setSetData(newData);
localSetData.value = newData;
}
});
// 监听localSetData变化保存设置
watch(
() => localSetData.value,
(newValue) => {
saveSettings(newValue);
},
{ deep: true }
);
// 监听store中setData的变化同步到本地
watch(
() => settingsStore.setData,
(newValue) => {
// 只在初始加载时更新本地数据,避免循环更新
if (JSON.stringify(localSetData.value) !== JSON.stringify(newValue)) {
localSetData.value = { ...newValue };
}
},
{ deep: true, immediate: true }
);
const isDarkTheme = computed({
get: () => settingsStore.theme === 'dark',
set: () => settingsStore.toggleTheme()