diff --git a/src/i18n/lang/en-US/player.ts b/src/i18n/lang/en-US/player.ts index 1144eda..e2d93d5 100644 --- a/src/i18n/lang/en-US/player.ts +++ b/src/i18n/lang/en-US/player.ts @@ -33,6 +33,7 @@ export default { collapse: 'Collapse Lyrics', like: 'Like', lyric: 'Lyric', + noSongPlaying: 'No song playing', eq: 'Equalizer', playList: 'Play List', playMode: { diff --git a/src/i18n/lang/zh-CN/player.ts b/src/i18n/lang/zh-CN/player.ts index 3e34636..428c214 100644 --- a/src/i18n/lang/zh-CN/player.ts +++ b/src/i18n/lang/zh-CN/player.ts @@ -33,6 +33,7 @@ export default { collapse: '收起歌词', like: '喜欢', lyric: '歌词', + noSongPlaying: '没有正在播放的歌曲', eq: '均衡器', playList: '播放列表', playMode: { diff --git a/src/main/lyric.ts b/src/main/lyric.ts index d684d23..1faf498 100644 --- a/src/main/lyric.ts +++ b/src/main/lyric.ts @@ -25,9 +25,15 @@ const createWin = () => { const validPosition = x !== undefined && y !== undefined && x >= 0 && y >= 0 && x < screenWidth && y < screenHeight; + // 确保宽高合理 + const defaultWidth = 800; + const defaultHeight = 200; + const validWidth = width && width > 0 ? width : defaultWidth; + const validHeight = height && height > 0 ? height : defaultHeight; + lyricWindow = new BrowserWindow({ - width: width || 800, - height: height || 200, + width: validWidth, + height: validHeight, x: validPosition ? x : undefined, y: validPosition ? y : undefined, frame: false, @@ -50,6 +56,17 @@ const createWin = () => { } }); + // 监听窗口大小变化事件,保存新的尺寸 + lyricWindow.on('resize', () => { + if (lyricWindow && !lyricWindow.isDestroyed()) { + const [width, height] = lyricWindow.getSize(); + const [x, y] = lyricWindow.getPosition(); + + // 保存窗口位置和大小 + store.set('lyricWindowBounds', { x, y, width, height }); + } + }); + return lyricWindow; }; @@ -118,6 +135,7 @@ export const loadLyricWindow = (ipcMain: IpcMain, mainWin: BrowserWindow): void if (lyricWindow && !lyricWindow.isDestroyed()) { lyricWindow.webContents.send('lyric-window-close'); mainWin.webContents.send('lyric-control-back', 'close'); + mainWin.webContents.send('lyric-window-closed'); lyricWindow.destroy(); lyricWindow = null; } @@ -150,12 +168,14 @@ export const loadLyricWindow = (ipcMain: IpcMain, mainWin: BrowserWindow): void lyricWindow.setPosition(newX, newY); - // 保存新位置 - store.set('lyricWindowBounds', { - ...lyricWindow.getBounds(), + // 保存新位置,但只保存位置信息,不使用getBounds()避免在Windows下引起尺寸变化 + const bounds = { x: newX, - y: newY - }); + y: newY, + width: windowWidth, // 使用当前保存的宽度 + height: windowHeight // 使用当前保存的高度 + }; + store.set('lyricWindowBounds', bounds); }); // 添加鼠标穿透事件处理 diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index fdebf6e..91f1772 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -13,6 +13,7 @@ declare global { miniTray: () => void; restart: () => void; unblockMusic: (id: number, data: any) => Promise; + onLyricWindowClosed: (callback: () => void) => void; startDownload: (url: string) => void; onDownloadProgress: (callback: (progress: number, status: string) => void) => void; onDownloadComplete: (callback: (success: boolean, filePath: string) => void) => void; diff --git a/src/preload/index.ts b/src/preload/index.ts index c5dc994..dea198f 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -12,6 +12,10 @@ const api = { openLyric: () => ipcRenderer.send('open-lyric'), sendLyric: (data) => ipcRenderer.send('send-lyric', data), unblockMusic: (id) => ipcRenderer.invoke('unblock-music', id), + // 歌词窗口关闭事件 + onLyricWindowClosed: (callback: () => void) => { + ipcRenderer.on('lyric-window-closed', () => callback()); + }, // 更新相关 startDownload: (url: string) => ipcRenderer.send('start-download', url), onDownloadProgress: (callback: (progress: number, status: string) => void) => { diff --git a/src/renderer/hooks/MusicHook.ts b/src/renderer/hooks/MusicHook.ts index d0ab5d3..1bf98ec 100644 --- a/src/renderer/hooks/MusicHook.ts +++ b/src/renderer/hooks/MusicHook.ts @@ -1,5 +1,5 @@ import { createDiscreteApi } from 'naive-ui'; -import { computed, nextTick, onMounted, ref, watch } from 'vue'; +import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; import useIndexedDB from '@/hooks/IndexDBHook'; import { audioService } from '@/services/audioService'; @@ -263,11 +263,21 @@ watch( () => store.state.playMusic, () => { nextTick(async () => { + console.log('歌曲切换,更新歌词数据'); + // 更新歌词数据 lrcArray.value = playMusic.value.lyric?.lrcArray || []; lrcTimeArray.value = playMusic.value.lyric?.lrcTimeArray || []; + // 当歌词数据更新时,如果歌词窗口打开,则发送数据 - if (isElectron && isLyricWindowOpen.value && lrcArray.value.length > 0) { + if (isElectron && isLyricWindowOpen.value) { + console.log('歌词窗口已打开,同步最新歌词数据'); + // 不管歌词数组是否为空,都发送最新数据 sendLyricToWin(); + + // 再次延迟发送,确保歌词窗口已完全加载 + setTimeout(() => { + sendLyricToWin(); + }, 500); } }); }, @@ -451,8 +461,6 @@ export const pause = () => { } }; -const isPlaying = computed(() => store.state.play as boolean); - // 增加矫正时间 export const addCorrectionTime = (time: number) => (correctionTime.value += time); @@ -545,54 +553,183 @@ watch( // 发送歌词更新数据 export const sendLyricToWin = () => { if (!isElectron || !isLyricWindowOpen.value) { - console.log('Cannot send lyric: electron or lyric window not available'); + return; + } + + // 检查是否有播放的歌曲 + if (!playMusic.value || !playMusic.value.id) { return; } try { - if (lrcArray.value.length > 0) { + // 记录歌词发送状态 + if (lrcArray.value && lrcArray.value.length > 0) { const nowIndex = getLrcIndex(nowTime.value); + // 构建完整的歌词更新数据 const updateData = { type: 'full', nowIndex, nowTime: nowTime.value, - startCurrentTime: lrcTimeArray.value[nowIndex], - nextTime: lrcTimeArray.value[nowIndex + 1], - isPlay: isPlaying.value, + startCurrentTime: lrcTimeArray.value[nowIndex] || 0, + nextTime: lrcTimeArray.value[nowIndex + 1] || 0, + isPlay: store.state.play, lrcArray: lrcArray.value, lrcTimeArray: lrcTimeArray.value, allTime: allTime.value, playMusic: playMusic.value }; + + // 发送数据到歌词窗口 window.api.sendLyric(JSON.stringify(updateData)); + } 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: store.state.play, + 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); } }; +// 歌词同步定时器 +let lyricSyncInterval: any = null; + +// 开始歌词同步 +const startLyricSync = () => { + // 清除已有的定时器 + if (lyricSyncInterval) { + clearInterval(lyricSyncInterval); + } + + // 每秒同步一次歌词数据 + lyricSyncInterval = setInterval(() => { + if (isElectron && isLyricWindowOpen.value && store.state.play && playMusic.value?.id) { + // 发送当前播放进度的更新 + try { + const updateData = { + type: 'update', + nowIndex: getLrcIndex(nowTime.value), + nowTime: nowTime.value, + isPlay: store.state.play + }; + 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; + + // 检查是否有播放中的歌曲 + 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) { + // 立即打开窗口 + 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: store.state.play, + lrcArray: [{ text: '加载歌词中...', trText: '' }], + lrcTimeArray: [0], + allTime: allTime.value, + playMusic: playMusic.value + }; + window.api.sendLyric(JSON.stringify(emptyLyricData)); + } else { + // 发送完整歌词数据 + sendLyricToWin(); + } + + // 设置定时器,确保500ms后再次发送数据,以防窗口加载延迟 setTimeout(() => { - window.api.openLyric(); sendLyricToWin(); }, 500); - sendLyricToWin(); + + // 启动歌词同步 + startLyricSync(); } else { closeLyric(); + // 停止歌词同步 + stopLyricSync(); } }; -// 添加关闭歌词窗口的方法 +// 修改closeLyric函数,确保停止定时同步 export const closeLyric = () => { if (!isElectron) return; + isLyricWindowOpen.value = false; // 确保状态更新 windowData.electron.ipcRenderer.send('close-lyric'); + + // 停止歌词同步 + stopLyricSync(); }; +// 在组件挂载时设置对播放状态的监听 +watch( + () => store.state.play, + (isPlaying) => { + // 如果歌词窗口打开,根据播放状态控制同步 + if (isElectron && isLyricWindowOpen.value) { + if (isPlaying) { + startLyricSync(); + } else { + // 如果暂停播放,发送一次暂停状态的更新 + const pauseData = { + type: 'update', + isPlay: false + }; + window.api.sendLyric(JSON.stringify(pauseData)); + } + } + } +); + +// 在组件卸载时清理资源 +onUnmounted(() => { + stopLyricSync(); +}); + // 添加播放控制命令监听 if (isElectron) { windowData.electron.ipcRenderer.on('lyric-control-back', (_, command: string) => { @@ -613,7 +750,7 @@ if (isElectron) { store.commit('nextPlay'); break; case 'close': - closeLyric(); + isLyricWindowOpen.value = false; // 确保状态更新 break; default: console.log('Unknown command:', command); @@ -627,6 +764,13 @@ onMounted(() => { // 初始化音频监听器 setupAudioListeners(); + // 监听歌词窗口关闭事件 + if (isElectron) { + window.api.onLyricWindowClosed(() => { + isLyricWindowOpen.value = false; + }); + } + // 检查是否需要初始化 sound 对象 if (!sound.value && audioService.getCurrentSound()) { sound.value = audioService.getCurrentSound(); diff --git a/src/renderer/layout/components/PlayBar.vue b/src/renderer/layout/components/PlayBar.vue index e5ceeac..e3d34f8 100644 --- a/src/renderer/layout/components/PlayBar.vue +++ b/src/renderer/layout/components/PlayBar.vue @@ -113,11 +113,11 @@ - {{ t('player.playBar.lyric') }} + {{ playMusic.id ? t('player.playBar.lyric') : t('player.playBar.noSongPlaying') }} ; - lrcTimeArray: number[]; - allTime: number; - playMusic: SongResult; + lrcArray?: Array<{ text: string; trText: string }>; + lrcTimeArray?: number[]; + allTime?: number; + playMusic?: SongResult; }) => { // 确保数据存在且格式正确 if (!parsedData) { console.error('Invalid update data received:', parsedData); return; } + + // 根据数据类型处理 + if (parsedData.type === 'update') { + // 增量更新,只更新动态数据 + dynamicData.value = { + ...dynamicData.value, + nowTime: parsedData.nowTime || dynamicData.value.nowTime, + isPlay: typeof parsedData.isPlay === 'boolean' ? parsedData.isPlay : dynamicData.value.isPlay + }; + + // 更新索引(如果提供) + if (typeof parsedData.nowIndex === 'number') { + currentIndex.value = parsedData.nowIndex; + } + return; + } + + // 完整更新或空歌词提示 // 更新静态数据 staticData.value = { lrcArray: parsedData.lrcArray || [], lrcTimeArray: parsedData.lrcTimeArray || [], allTime: parsedData.allTime || 0, - playMusic: parsedData.playMusic || {} + playMusic: parsedData.playMusic || ({} as SongResult) }; // 更新动态数据 @@ -472,7 +491,7 @@ watch( { deep: true } ); -// 添��拖动相关变量 +// 添加拖动相关变量 const isDragging = ref(false); const startPosition = ref({ x: 0, y: 0 });