Files
AlgerMusicPlayer/src/renderer/hooks/MusicHook.ts
T

1006 lines
30 KiB
TypeScript
Raw Normal View History

import { cloneDeep } from 'lodash';
2025-02-19 01:01:43 +08:00
import { createDiscreteApi } from 'naive-ui';
2025-08-07 22:57:17 +08:00
import { computed, type ComputedRef, nextTick, onUnmounted, ref, watch } from 'vue';
import useIndexedDB from '@/hooks/IndexDBHook';
import { audioService } from '@/services/audioService';
import type { usePlayerStore } from '@/store';
2025-08-07 22:57:17 +08:00
import type { Artist, ILyricText, SongResult } from '@/types/music';
import { isElectron } from '@/utils';
2025-01-10 22:49:55 +08:00
import { getTextColors } from '@/utils/linearColor';
2025-10-11 20:23:54 +08:00
import { parseLyrics } from '@/utils/yrcParser';
2025-02-19 01:01:43 +08:00
2024-09-12 16:44:42 +08:00
const windowData = window as any;
// 全局 playerStore 引用,通过 initMusicHook 函数注入
let playerStore: ReturnType<typeof usePlayerStore> | null = null;
// 初始化函数,接受 store 实例
export const initMusicHook = (store: ReturnType<typeof usePlayerStore>) => {
playerStore = store;
// 创建 computed 属性
playMusic = computed(() => getPlayerStore().playMusic as SongResult);
artistList = computed(
() => (getPlayerStore().playMusic.ar || getPlayerStore().playMusic?.song?.artists) as Artist[]
);
// 在 store 注入后初始化需要 store 的功能
setupKeyboardListeners();
setupMusicWatchers();
setupCorrectionTimeWatcher();
setupPlayStateWatcher();
};
// 获取 playerStore 的辅助函数
const getPlayerStore = () => {
if (!playerStore) {
throw new Error('MusicHook not initialized. Call initMusicHook first.');
}
return playerStore;
};
export const lrcArray = ref<ILyricText[]>([]); // 歌词数组
export const lrcTimeArray = ref<number[]>([]); // 歌词时间数组
export const nowTime = ref(0); // 当前播放时间
export const allTime = ref(0); // 总播放时间
export const nowIndex = ref(0); // 当前播放歌词
export const currentLrcProgress = ref(0); // 来存储当前歌词的进度
export const sound = ref<Howl | null>(audioService.getCurrentSound());
export const isLyricWindowOpen = ref(false); // 新增状态
2025-01-10 22:49:55 +08:00
export const textColors = ref<any>(getTextColors());
// 这些 computed 属性需要在初始化后创建
export let playMusic: ComputedRef<SongResult>;
export let artistList: ComputedRef<Artist[]>;
2025-09-14 01:03:29 +08:00
export const musicDB = await useIndexedDB(
'musicDB',
[
{ name: 'music', keyPath: 'id' },
{ name: 'music_lyric', keyPath: 'id' },
{ name: 'api_cache', keyPath: 'id' },
{ name: 'music_url_cache', keyPath: 'id' },
{ name: 'music_failed_cache', keyPath: 'id' }
],
2025-10-11 20:23:54 +08:00
3
2025-09-14 01:03:29 +08:00
);
// 键盘事件处理器,在初始化后设置
const setupKeyboardListeners = () => {
document.onkeyup = (e) => {
// 检查事件目标是否是输入框元素
const target = e.target as HTMLElement;
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
return;
}
const store = getPlayerStore();
switch (e.code) {
case 'Space':
2026-03-04 19:53:50 +08:00
if (store.playMusic?.id) {
void store.setPlay({ ...store.playMusic });
}
break;
default:
}
};
};
2025-01-17 22:35:42 +08:00
const { message } = createDiscreteApi(['message']);
2026-03-04 19:53:50 +08:00
let audioListenersInitialized = false;
2025-10-11 20:23:54 +08:00
/**
* 解析歌词字符串并转换为ILyricText格式
* @param lyricsStr 歌词字符串
* @returns 解析后的歌词数据
*/
const parseLyricsString = async (
lyricsStr: string
): Promise<{ lrcArray: ILyricText[]; lrcTimeArray: number[]; hasWordByWord: boolean }> => {
if (!lyricsStr || typeof lyricsStr !== 'string') {
return { lrcArray: [], lrcTimeArray: [], hasWordByWord: false };
}
try {
const parseResult = parseLyrics(lyricsStr);
console.log('parseResult', parseResult);
2025-10-11 20:23:54 +08:00
if (!parseResult.success) {
console.error('歌词解析失败:', parseResult.error.message);
return { lrcArray: [], lrcTimeArray: [], hasWordByWord: false };
}
const { lyrics } = parseResult.data;
const lrcArray: ILyricText[] = [];
const lrcTimeArray: number[] = [];
let hasWordByWord = false;
for (const line of lyrics) {
// 检查是否有逐字歌词
const hasWords = line.words && line.words.length > 0;
if (hasWords) {
hasWordByWord = true;
}
lrcArray.push({
text: line.fullText,
trText: '', // 翻译文本稍后处理
words: hasWords
? line.words.map((word) => ({
...word
2025-10-11 20:23:54 +08:00
}))
: undefined,
hasWordByWord: hasWords,
startTime: line.startTime,
duration: line.duration
});
lrcTimeArray.push(line.startTime);
}
return { lrcArray, lrcTimeArray, hasWordByWord };
} catch (error) {
console.error('解析歌词时发生错误:', error);
return { lrcArray: [], lrcTimeArray: [], hasWordByWord: false };
}
};
// 设置音乐相关的监听器
const setupMusicWatchers = () => {
const store = getPlayerStore();
2025-03-08 19:00:50 +08:00
// 监听 playerStore.playMusic 的变化以更新歌词数据
watch(
2025-10-11 20:23:54 +08:00
() => store.playMusic.id,
async (newId, oldId) => {
// 如果没有歌曲ID,清空歌词
if (!newId) {
lrcArray.value = [];
lrcTimeArray.value = [];
nowIndex.value = 0;
return;
}
// 避免相同ID的重复执行(但允许初始化时执行)
if (newId === oldId && lrcArray.value.length > 0) return;
// 歌曲切换时重置歌词索引
if (newId !== oldId) {
nowIndex.value = 0;
}
await nextTick(async () => {
console.log('歌曲切换,更新歌词数据');
2025-10-11 20:23:54 +08:00
// 检查是否有原始歌词字符串需要解析
const lyricData = playMusic.value.lyric;
if (lyricData && typeof lyricData === 'string') {
// 如果歌词是字符串格式,使用新的解析器
const {
lrcArray: parsedLrcArray,
lrcTimeArray: parsedTimeArray,
hasWordByWord
} = await parseLyricsString(lyricData);
lrcArray.value = parsedLrcArray;
lrcTimeArray.value = parsedTimeArray;
// 更新歌曲的歌词数据结构
if (playMusic.value.lyric && typeof playMusic.value.lyric === 'object') {
playMusic.value.lyric.hasWordByWord = hasWordByWord;
}
} else {
// 使用现有的歌词数据结构
const rawLrc = lyricData?.lrcArray || [];
lrcTimeArray.value = lyricData?.lrcTimeArray || [];
try {
const { translateLyrics } = await import('@/services/lyricTranslation');
lrcArray.value = await translateLyrics(rawLrc as any);
} catch (e) {
console.error('翻译歌词失败,使用原始歌词:', e);
lrcArray.value = rawLrc as any;
}
}
// 当歌词数据更新时,如果歌词窗口打开,则发送数据
if (isElectron && isLyricWindowOpen.value) {
console.log('歌词窗口已打开,同步最新歌词数据');
// 不管歌词数组是否为空,都发送最新数据
2025-03-08 19:00:50 +08:00
sendLyricToWin();
// 再次延迟发送,确保歌词窗口已完全加载
setTimeout(() => {
sendLyricToWin();
}, 500);
}
});
},
2025-10-11 20:23:54 +08:00
{ immediate: true }
);
};
const setupAudioListeners = () => {
2026-03-04 19:53:50 +08:00
// 监听器只注册一次,避免重复绑定和误清理全局恢复监听器
if (audioListenersInitialized) {
return () => {};
}
audioListenersInitialized = true;
let interval: number | null = null;
2026-02-06 20:34:07 +08:00
// 播放状态恢复定时器:当 interval 因异常被清除时,自动恢复
let recoveryTimer: number | null = null;
let lyricThrottleCounter = 0;
let lastSavedProgress = 0;
const clearInterval = () => {
if (interval) {
window.clearInterval(interval);
interval = null;
}
};
2026-02-06 20:34:07 +08:00
const stopRecovery = () => {
if (recoveryTimer) {
window.clearInterval(recoveryTimer);
recoveryTimer = null;
}
};
/**
* 启动进度更新 interval
* 从 audioService 实时获取 sound 引用,避免闭包中 sound.value 过期
*/
const startProgressInterval = () => {
clearInterval();
interval = window.setInterval(() => {
try {
// 每次从 audioService 获取最新的 sound 引用,而不是依赖闭包中的 sound.value
const currentSound = audioService.getCurrentSound();
if (!currentSound) {
// sound 暂时为空(可能在切歌/重建中),不清除 interval,等待恢复
return;
}
if (typeof currentSound.seek !== 'function') {
// seek 方法不可用,跳过本次更新,不清除 interval
return;
}
const currentTime = currentSound.seek() as number;
if (typeof currentTime !== 'number' || Number.isNaN(currentTime)) {
// 无效时间,跳过本次更新
return;
}
// 同步 sound.value 引用(确保外部也能拿到最新的)
if (sound.value !== currentSound) {
sound.value = currentSound;
}
nowTime.value = currentTime;
allTime.value = currentSound.duration() as number;
// === 歌词索引更新 ===
2026-02-06 20:34:07 +08:00
const newIndex = getLrcIndex(nowTime.value);
if (newIndex !== nowIndex.value) {
nowIndex.value = newIndex;
currentLrcProgress.value = 0; // 换行时重置进度
2026-02-06 20:34:07 +08:00
if (isElectron && isLyricWindowOpen.value) {
sendLyricToWin();
}
}
// === 逐字歌词行内进度 ===
const { start, end } = currentLrcTiming.value;
if (typeof start === 'number' && typeof end === 'number' && start !== end) {
const elapsed = currentTime - start;
const duration = end - start;
const progress = (elapsed / duration) * 100;
currentLrcProgress.value = Math.min(Math.max(progress, 0), 100);
}
// === 节流发送轻量歌词进度更新(每 ~200ms / 约每 4 个 tick===
lyricThrottleCounter++;
if (isElectron && isLyricWindowOpen.value && lyricThrottleCounter % 4 === 0) {
try {
window.api.sendLyric(
JSON.stringify({
type: 'update',
nowIndex: nowIndex.value,
nowTime: nowTime.value,
isPlay: getPlayerStore().play
})
);
} catch {
// 忽略发送失败
}
2026-02-06 20:34:07 +08:00
}
// === localStorage 进度保存(每 ~2 秒)===
if (
Math.floor(currentTime) % 2 === 0 &&
Math.floor(currentTime) !== Math.floor(lastSavedProgress)
) {
lastSavedProgress = currentTime;
if (getPlayerStore().playMusic?.id) {
localStorage.setItem(
'playProgress',
JSON.stringify({
songId: getPlayerStore().playMusic.id,
progress: currentTime
})
);
}
}
2026-02-06 20:34:07 +08:00
} catch (error) {
console.error('进度更新 interval 出错:', error);
// 出错时不清除 interval,让下一次 tick 继续尝试
}
}, 50);
};
/**
* 启动播放状态恢复监控
* 每 500ms 检查一次:如果 store 认为在播放但 interval 已丢失,则恢复
*/
const startRecoveryMonitor = () => {
stopRecovery();
recoveryTimer = window.setInterval(() => {
try {
const store = getPlayerStore();
if (store.play && !interval) {
const currentSound = audioService.getCurrentSound();
if (currentSound && currentSound.playing()) {
console.warn('[MusicHook] 检测到播放中但 interval 丢失,自动恢复');
startProgressInterval();
}
}
} catch {
// 静默忽略
}
}, 500);
};
// 启动恢复监控
startRecoveryMonitor();
// 监听seek开始事件,立即更新UI
audioService.on('seek_start', (time) => {
// 直接更新显示位置,不检查拖动状态
nowTime.value = time;
});
// 监听seek完成事件
audioService.on('seek', () => {
try {
2026-02-06 20:34:07 +08:00
const currentSound = audioService.getCurrentSound();
if (currentSound) {
// 立即更新显示时间,不进行任何检查
const currentTime = currentSound.seek() as number;
if (typeof currentTime === 'number' && !Number.isNaN(currentTime)) {
nowTime.value = currentTime;
// 检查是否需要更新歌词
const newIndex = getLrcIndex(nowTime.value);
if (newIndex !== nowIndex.value) {
nowIndex.value = newIndex;
if (isElectron && isLyricWindowOpen.value) {
sendLyricToWin();
}
}
}
}
} catch (error) {
console.error('处理seek事件出错:', error);
}
});
// 立即更新一次时间和进度(解决初始化时进度条不显示问题)
const updateCurrentTimeAndDuration = () => {
const currentSound = audioService.getCurrentSound();
if (currentSound) {
try {
// 更新当前时间和总时长
const currentTime = currentSound.seek() as number;
if (typeof currentTime === 'number' && !Number.isNaN(currentTime)) {
nowTime.value = currentTime;
allTime.value = currentSound.duration() as number;
}
} catch (error) {
console.error('初始化时间和进度失败:', error);
}
}
};
// 立即执行一次更新
updateCurrentTimeAndDuration();
// 监听播放
audioService.on('play', () => {
getPlayerStore().setPlayMusic(true);
if (isElectron) {
window.api.sendSong(cloneDeep(getPlayerStore().playMusic));
}
2026-02-06 20:34:07 +08:00
// 启动进度更新
startProgressInterval();
});
// 监听暂停
audioService.on('pause', () => {
console.log('音频暂停事件触发');
getPlayerStore().setPlayMusic(false);
clearInterval();
if (isElectron && isLyricWindowOpen.value) {
sendLyricToWin();
}
});
2026-02-06 20:34:07 +08:00
const replayMusic = async (retryCount: number = 0) => {
const MAX_REPLAY_RETRIES = 3;
2025-01-17 22:34:49 +08:00
try {
// 如果当前有音频实例,先停止并销毁
2026-02-06 20:34:07 +08:00
const currentSound = audioService.getCurrentSound();
if (currentSound) {
currentSound.stop();
currentSound.unload();
2025-01-17 22:34:49 +08:00
}
2026-02-06 20:34:07 +08:00
sound.value = null;
2025-01-17 22:34:49 +08:00
// 重新播放当前歌曲
if (getPlayerStore().playMusicUrl && playMusic.value) {
const newSound = await audioService.play(getPlayerStore().playMusicUrl, playMusic.value);
2025-01-17 22:34:49 +08:00
sound.value = newSound as Howl;
setupAudioListeners();
} else {
2026-02-06 20:34:07 +08:00
console.error('单曲循环:无可用 URL 或歌曲数据');
getPlayerStore().nextPlay();
2025-01-17 22:34:49 +08:00
}
} catch (error) {
2026-02-06 20:34:07 +08:00
console.error('单曲循环重播失败:', error);
if (retryCount < MAX_REPLAY_RETRIES) {
console.log(`单曲循环重试 ${retryCount + 1}/${MAX_REPLAY_RETRIES}`);
setTimeout(() => replayMusic(retryCount + 1), 1000 * (retryCount + 1));
} else {
console.error('单曲循环重试次数用尽,切换下一首');
getPlayerStore().nextPlay();
}
2025-01-17 22:34:49 +08:00
}
};
// 监听结束
audioService.on('end', () => {
console.log('音频播放结束事件触发');
clearInterval();
if (getPlayerStore().playMode === 1) {
// 单曲循环模式
2026-02-06 20:34:07 +08:00
replayMusic();
} else {
// 顺序播放、列表循环、随机播放模式都使用统一的nextPlay方法
getPlayerStore().nextPlay();
}
});
audioService.on('previoustrack', () => {
getPlayerStore().prevPlay();
});
audioService.on('nexttrack', () => {
getPlayerStore().nextPlay();
});
2026-02-06 20:34:07 +08:00
return () => {
clearInterval();
stopRecovery();
};
};
export const play = () => {
2025-12-20 14:16:32 +08:00
const currentSound = audioService.getCurrentSound();
if (currentSound) {
currentSound.play();
// 在播放时也进行状态检测,防止URL已过期导致无声
getPlayerStore().checkPlaybackState(getPlayerStore().playMusic);
}
};
export const pause = () => {
const currentSound = audioService.getCurrentSound();
if (currentSound) {
try {
// 保存当前播放进度
const currentTime = currentSound.seek() as number;
if (getPlayerStore().playMusic && getPlayerStore().playMusic.id) {
localStorage.setItem(
'playProgress',
JSON.stringify({
songId: getPlayerStore().playMusic.id,
progress: currentTime
})
);
}
audioService.pause();
} catch (error) {
console.error('暂停播放出错:', error);
}
}
};
// 歌词矫正时间映射(每首歌独立)
const CORRECTION_KEY = 'lyric-correction-map';
const correctionTimeMap = ref<Record<string, number>>({});
// 初始化 correctionTimeMap
const loadCorrectionMap = () => {
try {
const raw = localStorage.getItem(CORRECTION_KEY);
correctionTimeMap.value = raw ? JSON.parse(raw) : {};
} catch {
correctionTimeMap.value = {};
}
};
const saveCorrectionMap = () => {
localStorage.setItem(CORRECTION_KEY, JSON.stringify(correctionTimeMap.value));
};
loadCorrectionMap();
// 歌词矫正时间,当前歌曲
export const correctionTime = ref(0);
// 设置歌词矫正时间的监听器
const setupCorrectionTimeWatcher = () => {
// 切歌时自动读取矫正时间
watch(
() => playMusic.value?.id,
(id) => {
if (!id) return;
correctionTime.value = correctionTimeMap.value[id] ?? 0;
},
{ immediate: true }
);
};
/**
* 调整歌词矫正时间(每首歌独立)
* @param delta 增加/减少的秒数(正为加,负为减)
*/
export const adjustCorrectionTime = (delta: number) => {
const id = playMusic.value?.id;
if (!id) return;
const newVal = Math.max(-10, Math.min(10, (correctionTime.value ?? 0) + delta));
correctionTime.value = newVal;
correctionTimeMap.value[id] = newVal;
saveCorrectionMap();
};
// 获取当前播放歌词
export const isCurrentLrc = (index: number, time: number): boolean => {
const currentTime = lrcTimeArray.value[index];
2025-10-11 20:23:54 +08:00
// 如果是最后一句歌词,只需要判断时间是否大于等于当前句的开始时间
if (index === lrcTimeArray.value.length - 1) {
const correctedTime = time + correctionTime.value;
return correctedTime >= currentTime;
}
// 非最后一句歌词,需要判断时间在当前句和下一句之间
const nextTime = lrcTimeArray.value[index + 1];
const correctedTime = time + correctionTime.value;
2025-10-11 20:23:54 +08:00
return correctedTime >= currentTime && correctedTime < nextTime;
};
// 获取当前播放歌词INDEX
export const getLrcIndex = (time: number): number => {
const correctedTime = time + correctionTime.value;
2025-10-11 20:23:54 +08:00
// 如果歌词数组为空,返回当前索引
if (lrcTimeArray.value.length === 0) {
return nowIndex.value;
}
// 处理最后一句歌词的情况
const lastIndex = lrcTimeArray.value.length - 1;
if (correctedTime >= lrcTimeArray.value[lastIndex]) {
nowIndex.value = lastIndex;
return lastIndex;
}
// 查找当前时间对应的歌词索引
for (let i = 0; i < lrcTimeArray.value.length - 1; i++) {
const currentTime = lrcTimeArray.value[i];
const nextTime = lrcTimeArray.value[i + 1];
if (correctedTime >= currentTime && correctedTime < nextTime) {
nowIndex.value = i;
return i;
}
}
2025-10-11 20:23:54 +08:00
return nowIndex.value;
};
2024-12-14 13:15:59 +08:00
// 获取当前播放歌词进度
const currentLrcTiming = computed(() => {
const start = lrcTimeArray.value[nowIndex.value] || 0;
const end = lrcTimeArray.value[nowIndex.value + 1] || start + 1;
return { start, end };
});
// 获取歌词样式
export const getLrcStyle = (index: number) => {
const currentTime = nowTime.value + correctionTime.value;
const start = lrcTimeArray.value[index];
const end = lrcTimeArray.value[index + 1] ?? start + 1;
if (currentTime >= start && currentTime < end) {
// 当前句,显示进度
const progress = ((currentTime - start) / (end - start)) * 100;
return {
backgroundImage: `linear-gradient(to right, #ffffff ${progress}%, #ffffff8a ${progress}%)`,
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
color: 'transparent',
transition: 'background-image 0.1s linear'
};
}
// 其它句
return {};
};
// 播放进度
export const useLyricProgress = () => {
// 如果已经在全局更新进度,立即返回
return {
getLrcStyle
};
};
// 设置当前播放时间
export const setAudioTime = (index: number) => {
const currentSound = sound.value;
if (!currentSound) return;
currentSound.seek(lrcTimeArray.value[index]);
currentSound.play();
};
// 获取当前播放的歌词
export const getCurrentLrc = () => {
const index = getLrcIndex(nowTime.value);
return {
currentLrc: lrcArray.value[index],
nextLrc: lrcArray.value[index + 1]
};
};
// 获取一句歌词播放时间几秒到几秒
export const getLrcTimeRange = (index: number) => ({
currentTime: lrcTimeArray.value[index],
nextTime: lrcTimeArray.value[index + 1]
});
// 监听歌词数组变化,当切换歌曲时重新初始化歌词窗口
watch(
() => lrcArray.value,
(newLrcArray) => {
if (newLrcArray.length > 0 && isElectron && isLyricWindowOpen.value) {
sendLyricToWin();
}
}
);
// 发送歌词更新数据
export const sendLyricToWin = () => {
if (!isElectron || !isLyricWindowOpen.value) {
2025-03-08 19:00:50 +08:00
return;
}
// 检查是否有播放的歌曲
if (!playMusic.value || !playMusic.value.id) {
return;
}
try {
2025-03-08 19:00:50 +08:00
// 记录歌词发送状态
if (lrcArray.value && lrcArray.value.length > 0) {
const nowIndex = getLrcIndex(nowTime.value);
2025-03-08 19:00:50 +08:00
// 构建完整的歌词更新数据
const updateData = {
type: 'full',
nowIndex,
nowTime: nowTime.value,
2025-03-08 19:00:50 +08:00
startCurrentTime: lrcTimeArray.value[nowIndex] || 0,
nextTime: lrcTimeArray.value[nowIndex + 1] || 0,
isPlay: getPlayerStore().play,
lrcArray: lrcArray.value,
lrcTimeArray: lrcTimeArray.value,
allTime: allTime.value,
playMusic: playMusic.value
};
2025-03-08 19:00:50 +08:00
// 发送数据到歌词窗口
window.api.sendLyric(JSON.stringify(updateData));
2025-03-08 19:00:50 +08:00
} else {
console.log('No lyric data available, sending empty lyric message');
// 发送没有歌词的提示
const emptyLyricData = {
type: 'empty',
nowIndex: 0,
nowTime: nowTime.value,
startCurrentTime: 0,
nextTime: 0,
isPlay: getPlayerStore().play,
2025-03-08 19:00:50 +08:00
lrcArray: [{ text: '当前歌曲暂无歌词', trText: '' }],
lrcTimeArray: [0],
allTime: allTime.value,
playMusic: playMusic.value
};
window.api.sendLyric(JSON.stringify(emptyLyricData));
}
} catch (error) {
console.error('Error sending lyric update:', error);
}
};
2025-03-08 19:00:50 +08:00
// 歌词同步定时器
let lyricSyncInterval: any = null;
// 开始歌词同步
const startLyricSync = () => {
// 清除已有的定时器
if (lyricSyncInterval) {
clearInterval(lyricSyncInterval);
}
// 每秒同步一次歌词数据
lyricSyncInterval = setInterval(() => {
if (isElectron && isLyricWindowOpen.value && getPlayerStore().play && playMusic.value?.id) {
2025-03-08 19:00:50 +08:00
// 发送当前播放进度的更新
try {
const updateData = {
type: 'update',
nowIndex: getLrcIndex(nowTime.value),
nowTime: nowTime.value,
isPlay: getPlayerStore().play
2025-03-08 19:00:50 +08:00
};
window.api.sendLyric(JSON.stringify(updateData));
} catch (error) {
console.error('发送歌词进度更新失败:', error);
}
}
}, 1000);
};
// 停止歌词同步
const stopLyricSync = () => {
if (lyricSyncInterval) {
clearInterval(lyricSyncInterval);
lyricSyncInterval = null;
}
};
// 修改openLyric函数,添加定时同步
export const openLyric = () => {
if (!isElectron) return;
2025-03-08 19:00:50 +08:00
// 检查是否有播放中的歌曲
if (!playMusic.value || !playMusic.value.id) {
console.log('没有正在播放的歌曲,无法打开歌词窗口');
return;
}
console.log('Opening lyric window with current song:', playMusic.value?.name);
isLyricWindowOpen.value = !isLyricWindowOpen.value;
if (isLyricWindowOpen.value) {
2025-03-08 19:00:50 +08:00
// 立即打开窗口
window.api.openLyric();
// 确保有歌词数据,如果没有,则使用默认的"无歌词"提示
if (!lrcArray.value || lrcArray.value.length === 0) {
// 如果当前播放的歌曲有ID但没有歌词,则尝试加载歌词
console.log('尝试加载歌词数据...');
// 发送默认的"无歌词"数据
const emptyLyricData = {
type: 'empty',
nowIndex: 0,
nowTime: nowTime.value,
startCurrentTime: 0,
nextTime: 0,
isPlay: getPlayerStore().play,
2025-03-08 19:00:50 +08:00
lrcArray: [{ text: '加载歌词中...', trText: '' }],
lrcTimeArray: [0],
allTime: allTime.value,
playMusic: playMusic.value
};
window.api.sendLyric(JSON.stringify(emptyLyricData));
} else {
// 发送完整歌词数据
sendLyricToWin();
}
// 设置定时器,确保500ms后再次发送数据,以防窗口加载延迟
setTimeout(() => {
sendLyricToWin();
}, 500);
2025-03-08 19:00:50 +08:00
// 启动歌词同步
startLyricSync();
} else {
closeLyric();
2025-03-08 19:00:50 +08:00
// 停止歌词同步
stopLyricSync();
}
};
2025-03-08 19:00:50 +08:00
// 修改closeLyric函数,确保停止定时同步
export const closeLyric = () => {
if (!isElectron) return;
2025-03-08 19:00:50 +08:00
isLyricWindowOpen.value = false; // 确保状态更新
windowData.electron.ipcRenderer.send('close-lyric');
2025-03-08 19:00:50 +08:00
// 停止歌词同步
stopLyricSync();
};
// 设置播放状态监听器
const setupPlayStateWatcher = () => {
// 在组件挂载时设置对播放状态的监听
watch(
() => getPlayerStore().play,
(isPlaying) => {
// 如果歌词窗口打开,根据播放状态控制同步
if (isElectron && isLyricWindowOpen.value) {
if (isPlaying) {
startLyricSync();
} else {
// 如果暂停播放,发送一次暂停状态的更新
const pauseData = {
type: 'update',
isPlay: false
};
window.api.sendLyric(JSON.stringify(pauseData));
}
2025-03-08 19:00:50 +08:00
}
}
);
};
2025-03-08 19:00:50 +08:00
// 在组件卸载时清理资源
onUnmounted(() => {
stopLyricSync();
});
2025-10-11 20:23:54 +08:00
// 导出歌词解析函数供外部使用
export { parseLyricsString };
// 添加播放控制命令监听
if (isElectron) {
windowData.electron.ipcRenderer.on('lyric-control-back', (_, command: string) => {
switch (command) {
case 'playpause':
2026-03-04 19:53:50 +08:00
if (getPlayerStore().playMusic?.id) {
void getPlayerStore().setPlay({ ...getPlayerStore().playMusic });
}
break;
case 'prev':
getPlayerStore().prevPlay();
break;
case 'next':
getPlayerStore().nextPlay();
break;
case 'close':
2025-03-08 19:00:50 +08:00
isLyricWindowOpen.value = false; // 确保状态更新
break;
default:
console.log('Unknown command:', command);
break;
}
});
}
// 在组件挂载时设置监听器
export const initAudioListeners = async () => {
try {
// 确保有正在播放的音乐
if (!getPlayerStore().playMusic || !getPlayerStore().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();
2025-03-08 19:00:50 +08:00
// 监听歌词窗口关闭事件
if (isElectron) {
window.api.onLyricWindowClosed(() => {
isLyricWindowOpen.value = false;
});
}
// 获取最新的音频实例
const finalSound = audioService.getCurrentSound();
if (finalSound) {
// 更新全局 sound 引用
sound.value = finalSound;
} else {
console.warn('无法获取音频实例,跳过进度更新初始化');
}
} catch (error) {
console.error('初始化音频监听器失败:', error);
}
};
// 监听URL过期事件,自动重新获取URL并恢复播放
audioService.on('url_expired', async (expiredTrack) => {
if (!expiredTrack) return;
console.log('检测到URL过期事件,准备重新获取URL', expiredTrack.name);
try {
2025-12-20 14:16:32 +08:00
// 使用 handlePlayMusic 重新播放,它会自动处理 URL 获取和状态跟踪
// 我们将 isFirstPlay 设为 true 以强制获取新 URL
const trackToPlay = {
...expiredTrack,
isFirstPlay: true,
playMusicUrl: undefined
};
2025-12-20 14:16:32 +08:00
await getPlayerStore().handlePlayMusic(trackToPlay, getPlayerStore().play);
2025-12-20 14:16:32 +08:00
message.success('已自动恢复播放');
} catch (error) {
console.error('处理URL过期事件失败:', error);
message.error('恢复播放失败,请手动点击播放');
}
});
// 添加音频就绪事件监听器
window.addEventListener('audio-ready', ((event: CustomEvent) => {
try {
const { sound: newSound } = event.detail;
if (newSound) {
// 更新本地 sound 引用
sound.value = newSound as Howl;
// 设置音频监听器
setupAudioListeners();
// 获取当前播放位置并更新显示
const currentPosition = newSound.seek() as number;
if (typeof currentPosition === 'number' && !Number.isNaN(currentPosition)) {
nowTime.value = currentPosition;
}
console.log('音频就绪,已设置监听器并更新进度显示');
}
} catch (error) {
console.error('处理音频就绪事件出错:', error);
}
}) as EventListener);