diff --git a/package.json b/package.json index 3cdaeb7..dc4e7f1 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@unblockneteasemusic/server": "^0.27.8-patch.1", "electron-store": "^8.1.0", "electron-updater": "^6.1.7", + "font-list": "^1.5.1", "netease-cloud-music-api-alger": "^4.25.0" }, "devDependencies": { @@ -45,6 +46,7 @@ "@vue/runtime-core": "^3.5.0", "@vueuse/core": "^11.0.3", "@vueuse/electron": "^11.0.3", + "animate.css": "^4.1.1", "autoprefixer": "^10.4.20", "axios": "^1.7.7", "cross-env": "^7.0.3", @@ -78,8 +80,7 @@ "vue": "^3.4.30", "vue-router": "^4.4.3", "vue-tsc": "^2.0.22", - "vuex": "^4.1.0", - "animate.css": "^4.1.1" + "vuex": "^4.1.0" }, "build": { "appId": "com.alger.music", diff --git a/src/main/index.ts b/src/main/index.ts index ca5b686..9922d5d 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -5,6 +5,7 @@ import { join } from 'path'; import { loadLyricWindow } from './lyric'; import { initializeConfig } from './modules/config'; import { initializeFileManager } from './modules/fileManager'; +import { initializeFonts } from './modules/fonts'; import { initializeShortcuts, registerShortcuts } from './modules/shortcuts'; import { initializeTray } from './modules/tray'; import { createMainWindow, initializeWindowManager } from './modules/window'; @@ -30,6 +31,8 @@ function initialize() { initializeFileManager(); // 初始化窗口管理 initializeWindowManager(); + // 初始化字体管理 + initializeFonts(); // 创建主窗口 mainWindow = createMainWindow(icon); diff --git a/src/main/modules/config.ts b/src/main/modules/config.ts index a323687..9a6bee2 100644 --- a/src/main/modules/config.ts +++ b/src/main/modules/config.ts @@ -12,6 +12,17 @@ interface StoreType { author: string; authorUrl: string; musicApiPort: number; + closeAction: 'ask' | 'minimize' | 'close'; + musicQuality: string; + fontFamily: string; + proxyConfig: { + enable: boolean; + protocol: string; + host: string; + port: number; + }; + enableRealIP: boolean; + realIP: string; }; shortcuts: typeof defaultShortcuts; } diff --git a/src/main/modules/fonts.ts b/src/main/modules/fonts.ts new file mode 100644 index 0000000..f1c4930 --- /dev/null +++ b/src/main/modules/fonts.ts @@ -0,0 +1,42 @@ +import { ipcMain } from 'electron'; +import { getFonts } from 'font-list'; + +/** + * 清理字体名称 + * @param fontName 原始字体名称 + * @returns 清理后的字体名称 + */ +function cleanFontName(fontName: string): string { + return fontName + .trim() + .replace(/^["']|["']$/g, '') // 移除首尾的引号 + .replace(/\s+/g, ' '); // 将多个空格替换为单个空格 +} + +/** + * 获取系统字体列表 + */ +async function getSystemFonts(): Promise { + try { + // 使用 font-list 获取系统字体 + const fonts = await getFonts(); + // 清理字体名称并去重 + const cleanedFonts = [...new Set(fonts.map(cleanFontName))]; + // 添加系统默认字体并排序 + return ['system-ui', ...cleanedFonts].sort(); + } catch (error) { + console.error('获取系统字体失败:', error); + // 如果获取失败,至少返回系统默认字体 + return ['system-ui']; + } +} + +/** + * 初始化字体管理模块 + */ +export function initializeFonts() { + // 添加获取系统字体的 IPC 处理 + ipcMain.handle('get-system-fonts', async () => { + return await getSystemFonts(); + }); +} diff --git a/src/main/modules/update.ts b/src/main/modules/update.ts new file mode 100644 index 0000000..7dfdefe --- /dev/null +++ b/src/main/modules/update.ts @@ -0,0 +1,90 @@ +import axios from 'axios'; +import { exec } from 'child_process'; +import { app, BrowserWindow, ipcMain } from 'electron'; +import * as fs from 'fs'; +import * as path from 'path'; + +export function setupUpdateHandlers(_mainWindow: BrowserWindow) { + ipcMain.on('start-download', async (event, url: string) => { + try { + const response = await axios({ + url, + method: 'GET', + responseType: 'stream', + onDownloadProgress: (progressEvent: { loaded: number; total?: number }) => { + if (!progressEvent.total) return; + const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100); + const downloaded = (progressEvent.loaded / 1024 / 1024).toFixed(2); + const total = (progressEvent.total / 1024 / 1024).toFixed(2); + event.sender.send('download-progress', percent, `已下载 ${downloaded}MB / ${total}MB`); + } + }); + + const fileName = url.split('/').pop() || 'update.exe'; + const downloadPath = path.join(app.getPath('downloads'), fileName); + + // 创建写入流 + const writer = fs.createWriteStream(downloadPath); + + // 将响应流写入文件 + response.data.pipe(writer); + + // 处理写入完成 + writer.on('finish', () => { + event.sender.send('download-complete', true, downloadPath); + }); + + // 处理写入错误 + writer.on('error', (error) => { + console.error('Write file error:', error); + event.sender.send('download-complete', false, ''); + }); + } catch (error) { + console.error('Download failed:', error); + event.sender.send('download-complete', false, ''); + } + }); + + ipcMain.on('install-update', (_event, filePath: string) => { + if (!fs.existsSync(filePath)) { + console.error('Installation file not found:', filePath); + return; + } + + const { platform } = process; + + // 关闭当前应用 + app.quit(); + + // 根据不同平台执行安装 + if (platform === 'win32') { + exec(`"${filePath}"`, (error) => { + if (error) { + console.error('Error starting installer:', error); + } + }); + } else if (platform === 'darwin') { + // 挂载 DMG 文件 + exec(`open "${filePath}"`, (error) => { + if (error) { + console.error('Error opening DMG:', error); + } + }); + } else if (platform === 'linux') { + const ext = path.extname(filePath); + if (ext === '.AppImage') { + exec(`chmod +x "${filePath}" && "${filePath}"`, (error) => { + if (error) { + console.error('Error running AppImage:', error); + } + }); + } else if (ext === '.deb') { + exec(`pkexec dpkg -i "${filePath}"`, (error) => { + if (error) { + console.error('Error installing deb package:', error); + } + }); + } + } + }); +} diff --git a/src/main/set.json b/src/main/set.json index bcc706c..01421db 100644 --- a/src/main/set.json +++ b/src/main/set.json @@ -14,5 +14,7 @@ "authorUrl": "https://github.com/algerkong", "musicApiPort": 30488, "closeAction": "ask", - "musicQuality": "higher" + "musicQuality": "higher", + "fontFamily": "system-ui", + "fontScope": "global" } diff --git a/src/preload/index.ts b/src/preload/index.ts index 20809d2..14c1e0b 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -14,11 +14,18 @@ const api = { unblockMusic: (id) => ipcRenderer.invoke('unblock-music', id), // 歌词缓存相关 invoke: (channel: string, ...args: any[]) => { - const validChannels = ['get-cached-lyric', 'cache-lyric', 'clear-lyric-cache']; + const validChannels = [ + 'get-lyrics', + 'clear-lyrics-cache', + 'get-system-fonts', + 'get-cached-lyric', + 'cache-lyric', + 'clear-lyric-cache' + ]; if (validChannels.includes(channel)) { return ipcRenderer.invoke(channel, ...args); } - return Promise.reject(new Error(`Invalid channel: ${channel}`)); + return Promise.reject(new Error(`未授权的 IPC 通道: ${channel}`)); } }; diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 9da78ec..ff2c21f 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -12,7 +12,7 @@ - diff --git a/src/renderer/store/index.ts b/src/renderer/store/index.ts index a850321..dab1056 100644 --- a/src/renderer/store/index.ts +++ b/src/renderer/store/index.ts @@ -37,6 +37,7 @@ export interface State { showUpdateModal: boolean; showArtistDrawer: boolean; currentArtistId: number | null; + systemFonts: { label: string; value: string }[]; } const state: State = { @@ -59,7 +60,8 @@ const state: State = { musicFull: false, showUpdateModal: false, showArtistDrawer: false, - currentArtistId: null + currentArtistId: null, + systemFonts: [{ label: '系统默认', value: 'system-ui' }] }; const { handlePlayMusic, nextPlay, prevPlay } = useMusicListHook(); @@ -160,6 +162,15 @@ const mutations = { }, setCurrentArtistId(state, id: number) { state.currentArtistId = id; + }, + setSystemFonts(state, fonts: string[]) { + state.systemFonts = [ + { label: '系统默认', value: 'system-ui' }, + ...fonts.map((font) => ({ + label: font, + value: font + })) + ]; } }; @@ -167,7 +178,10 @@ const actions = { initializeSettings({ commit }: { commit: any }) { if (isElectron) { const setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set'); - commit('setSetData', setData || defaultSettings); + commit('setSetData', { + ...defaultSettings, + ...setData + }); } else { const savedSettings = localStorage.getItem('appSettings'); if (savedSettings) { @@ -213,6 +227,17 @@ const actions = { }, showArtist({ commit }, id: number) { commit('setCurrentArtistId', id); + }, + async initializeSystemFonts({ commit, state }) { + // 如果已经有字体列表(不只是默认字体),则不重复获取 + if (state.systemFonts.length > 1) return; + + try { + const fonts = await window.api.invoke('get-system-fonts'); + commit('setSystemFonts', fonts); + } catch (error) { + console.error('获取系统字体失败:', error); + } } }; diff --git a/src/renderer/views/set/index.vue b/src/renderer/views/set/index.vue index 74d983e..0478b62 100644 --- a/src/renderer/views/set/index.vue +++ b/src/renderer/views/set/index.vue @@ -31,6 +31,51 @@ +
+
+
字体设置
+
选择字体,优先使用排在前面的字体
+
+
+ + 全局 + 仅歌词 + + + +
+
+ +
+
字体预览
+
+
+
中文
+
静夜思 床前明月光 疑是地上霜
+
+
+
English
+
The quick brown fox jumps over the lazy dog
+
+
+
日本語
+
あいうえお かきくけこ さしすせそ
+
+
+
한국어
+
가나다라마 바사아자차 카타파하
+
+
+
+
动画速度
@@ -366,7 +411,7 @@