diff --git a/DEV.md b/DEV.md index 836903f..e05c466 100644 --- a/DEV.md +++ b/DEV.md @@ -15,7 +15,7 @@ - **国际化**:vue-i18n - **HTTP 客户端**:axios - **本地存储**:electron-store localstorage -- **网易云音乐 API**:netease-cloud-music-api +- **音乐 API**:netease-cloud-music-api - **音乐解锁**:@unblockneteasemusic/server ### 项目结构 @@ -93,7 +93,7 @@ AlgerMusicPlayer/ - **index.ts**: 应用主入口,负责创建窗口和应用生命周期管理 - **lyric.ts**: 歌词解析和处理 -- **unblockMusic.ts**: 网易云音乐解锁功能 +- **unblockMusic.ts**: 音乐解锁功能 - **server.ts**: 本地服务器 #### 预加载脚本 (src/preload) diff --git a/README.md b/README.md index 274392f..9c134c8 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ 主要功能如下 - 🎵 音乐推荐 -- 🔐 网易云账号登录与同步 +- 🔐 账号登录与同步 - 📝 功能 - 播放历史记录 - 歌曲收藏管理 diff --git a/src/i18n/lang/en-US/settings.ts b/src/i18n/lang/en-US/settings.ts index 3110941..5037890 100644 --- a/src/i18n/lang/en-US/settings.ts +++ b/src/i18n/lang/en-US/settings.ts @@ -105,9 +105,13 @@ export default { sourceLabels: { migu: 'Migu', kugou: 'Kugou', + kuwo: 'Kuwo', pyncmd: 'NetEase (Built-in)', + qq: 'QQ Music', + joox: 'JOOX', bilibili: 'Bilibili', gdmusic: 'GD Music', + lxMusic: 'LX Music', custom: 'Custom API' }, diff --git a/src/i18n/lang/ja-JP/settings.ts b/src/i18n/lang/ja-JP/settings.ts index 55e0b20..ed8f58f 100644 --- a/src/i18n/lang/ja-JP/settings.ts +++ b/src/i18n/lang/ja-JP/settings.ts @@ -102,9 +102,13 @@ export default { sourceLabels: { migu: 'Migu', kugou: 'Kugou', + kuwo: 'Kuwo', pyncmd: 'NetEase (内蔵)', + qq: 'QQ Music', + joox: 'JOOX', bilibili: 'Bilibili', gdmusic: 'GD 音楽台', + lxMusic: 'LX Music', custom: 'カスタム API' }, customApi: { diff --git a/src/i18n/lang/ko-KR/settings.ts b/src/i18n/lang/ko-KR/settings.ts index e2861d4..d142d59 100644 --- a/src/i18n/lang/ko-KR/settings.ts +++ b/src/i18n/lang/ko-KR/settings.ts @@ -103,9 +103,13 @@ export default { sourceLabels: { migu: 'Migu', kugou: 'Kugou', + kuwo: 'Kuwo', pyncmd: 'NetEase (내장)', + qq: 'QQ Music', + joox: 'JOOX', bilibili: 'Bilibili', gdmusic: 'GD Music', + lxMusic: 'LX Music', custom: '사용자 지정 API' }, diff --git a/src/i18n/lang/zh-CN/login.ts b/src/i18n/lang/zh-CN/login.ts index 69f5c1d..a88ee2a 100644 --- a/src/i18n/lang/zh-CN/login.ts +++ b/src/i18n/lang/zh-CN/login.ts @@ -5,14 +5,14 @@ export default { cookie: 'Cookie登录', uid: 'UID登录' }, - qrTip: '使用网易云APP扫码登录', - phoneTip: '使用网易云账号登录', - tokenTip: '输入有效的网易云音乐Cookie即可登录', + qrTip: '使用APP扫码登录', + phoneTip: '使用账号登录', + tokenTip: '输入有效的音乐Cookie即可登录', uidTip: '输入用户ID快速登录', placeholder: { phone: '手机号', password: '密码', - cookie: '请输入网易云音乐Cookie(token)', + cookie: '请输入音乐Cookie(token)', uid: '请输入用户ID(UID)' }, button: { @@ -45,7 +45,7 @@ export default { phoneLoginFailed: '手机号登录失败,请检查手机号和密码是否正确', autoGetCookieSuccess: '自动获取Cookie成功', autoGetCookieFailed: '自动获取Cookie失败', - autoGetCookieTip: '将打开网易云音乐登录页面,请完成登录后关闭窗口', + autoGetCookieTip: '将打开音乐登录页面,请完成登录后关闭窗口', qrCheckFailed: '检查二维码状态失败,请刷新重试', qrLoading: '正在加载二维码...', qrExpired: '二维码已过期,请点击刷新', @@ -57,6 +57,6 @@ export default { qrConfirmed: '登录成功,正在跳转...', qrGenerating: '正在生成二维码...' }, - qrTitle: '扫码登录网易云音乐', + qrTitle: '扫码登录', uidWarning: '注意:UID登录仅用于查看用户公开信息,无法访问需要登录权限的功能' }; diff --git a/src/i18n/lang/zh-CN/settings.ts b/src/i18n/lang/zh-CN/settings.ts index ee01312..8f57cc2 100644 --- a/src/i18n/lang/zh-CN/settings.ts +++ b/src/i18n/lang/zh-CN/settings.ts @@ -20,7 +20,7 @@ export default { language: '语言设置', languageDesc: '切换显示语言', tokenManagement: 'Cookie管理', - tokenManagementDesc: '管理网易云音乐登录Cookie', + tokenManagementDesc: '管理音乐登录Cookie', tokenStatus: '当前Cookie状态', tokenSet: '已设置', tokenNotSet: '未设置', @@ -61,7 +61,7 @@ export default { }, playback: { quality: '音质设置', - qualityDesc: '选择音乐播放音质(网易云VIP)', + qualityDesc: '选择音乐播放音质(不确保有效)', qualityOptions: { standard: '标准', higher: '较高', @@ -99,11 +99,15 @@ export default { // 音源标签 sourceLabels: { - migu: '咪咕音乐', - kugou: '酷狗音乐', - pyncmd: '网易云(内置)', + migu: 'migu', + kugou: 'kugou', + kuwo: 'kuwo', + pyncmd: 'pyncmd', + qq: 'qq', + joox: 'JOOX', bilibili: 'Bilibili', - gdmusic: 'GD音乐台', + gdmusic: 'gdmusic', + lxMusic: 'lxMusic', custom: '自定义 API' }, @@ -414,7 +418,7 @@ export default { }, cookie: { title: 'Cookie设置', - description: '请输入网易云音乐的Cookie:', + description: '请输入音乐的Cookie:', placeholder: '请粘贴完整的Cookie...', help: { format: 'Cookie通常以 "MUSIC_U=" 开头', diff --git a/src/i18n/lang/zh-Hant/settings.ts b/src/i18n/lang/zh-Hant/settings.ts index 5f5df3a..3cd9e18 100644 --- a/src/i18n/lang/zh-Hant/settings.ts +++ b/src/i18n/lang/zh-Hant/settings.ts @@ -98,14 +98,17 @@ export default { // 音源標籤 sourceLabels: { - migu: '咪咕音樂', - kugou: '酷狗音樂', - pyncmd: '網易雲(內建)', + migu: 'migu', + kugou: 'kugou', + kuwo: 'kuwo', + pyncmd: 'pyncmd', + qq: 'qq', + joox: 'JOOX', bilibili: 'Bilibili', - gdmusic: 'GD音樂台', + gdmusic: 'gdmusic', + lxMusic: 'lxMusic', custom: '自訂 API' }, - customApi: { sectionTitle: '自訂 API 設定', importConfig: '匯入 JSON 設定', diff --git a/src/main/modules/loginWindow.ts b/src/main/modules/loginWindow.ts index f6c6eaa..58b6afd 100644 --- a/src/main/modules/loginWindow.ts +++ b/src/main/modules/loginWindow.ts @@ -42,7 +42,7 @@ const openLoginWindow = async (mainWin: BrowserWindow) => { } }); - // 打开网易云登录页面 + // 打开登录页面 loginWindow.loadURL(loginUrl); // 阻止新窗口创建 diff --git a/src/main/modules/otherApi.ts b/src/main/modules/otherApi.ts index efdf8ee..d118ad3 100644 --- a/src/main/modules/otherApi.ts +++ b/src/main/modules/otherApi.ts @@ -5,7 +5,7 @@ import { ipcMain } from 'electron'; * 初始化其他杂项 API(如搜索建议等) */ export function initializeOtherApi() { - // 搜索建议(从酷狗获取) + // 搜索建议 ipcMain.handle('get-search-suggestions', async (_, keyword: string) => { if (!keyword || !keyword.trim()) { return []; diff --git a/src/renderer/api/lxMusicStrategy.ts b/src/renderer/api/lxMusicStrategy.ts index afed2e3..b821ebb 100644 --- a/src/renderer/api/lxMusicStrategy.ts +++ b/src/renderer/api/lxMusicStrategy.ts @@ -108,7 +108,7 @@ const convertToLxMusicInfo = (songResult: SongResult): LxMusicInfo => { singer: artistName, album: albumName, albumId, - source: 'wy', // 默认使用网易云作为源,因为我们的数据来自网易云 + source: 'wy', interval, img: songResult.picUrl || songResult.al?.picUrl || '' }; @@ -116,13 +116,11 @@ const convertToLxMusicInfo = (songResult: SongResult): LxMusicInfo => { /** * 获取最佳匹配的落雪音源 - * 因为我们的数据来自网易云,优先尝试 wy 音源 */ const getBestMatchingSource = ( availableSources: LxSourceKey[], _songSource?: string ): LxSourceKey | null => { - // 优先级顺序:网易云 > 酷我 > 咪咕 > 酷狗 > QQ音乐 const priority: LxSourceKey[] = ['wy', 'kw', 'mg', 'kg', 'tx']; for (const source of priority) { @@ -196,7 +194,9 @@ export class LxMusicStrategy { return null; } - console.log(`[LxMusicStrategy] 使用激活的音源: ${activeScript.name} (ID: ${activeScript.id})`); + console.log( + `[LxMusicStrategy] 使用激活的音源: ${activeScript.name} (ID: ${activeScript.id})` + ); // 获取或初始化执行器 let runner = getLxMusicRunner(); diff --git a/src/renderer/api/music.ts b/src/renderer/api/music.ts index 5fc2e9d..8dac4c7 100644 --- a/src/renderer/api/music.ts +++ b/src/renderer/api/music.ts @@ -26,7 +26,6 @@ export const getMusicUrl = async (id: number, isDownloaded: boolean = false) => id, level: settingStore.setData.musicQuality || 'higher', encodeType: settingStore.setData.musicQuality == 'lossless' ? 'aac' : 'flac', - // level为lossless时,encodeType=flac时网易云会返回hires音质,encodeType=aac时网易云会返回lossless音质 cookie: `${localStorage.getItem('token')} os=pc;` } }); diff --git a/src/renderer/api/search.ts b/src/renderer/api/search.ts index 4368bcf..eaf85ce 100644 --- a/src/renderer/api/search.ts +++ b/src/renderer/api/search.ts @@ -25,7 +25,7 @@ interface KugouSuggestionResponse { data: Suggestion[]; } -// 网易云搜索建议返回的数据结构(部分字段) +// 搜索建议返回的数据结构(部分字段) interface NeteaseSuggestResult { result?: { songs?: Array<{ name: string }>; @@ -36,7 +36,7 @@ interface NeteaseSuggestResult { } /** - * 从酷狗获取搜索建议 + * 获取搜索建议 * @param keyword 搜索关键词 */ export const getSearchSuggestions = async (keyword: string) => { @@ -54,7 +54,7 @@ export const getSearchSuggestions = async (keyword: string) => { console.log('[API] Running in Electron, using IPC proxy.'); responseData = await window.api.getSearchSuggestions(keyword); } else { - // 非 Electron 环境下,使用网易云接口 + // 非 Electron 环境下,使用接口 const res = await request.get('/search/suggest', { params: { keywords: keyword } }); @@ -67,7 +67,7 @@ export const getSearchSuggestions = async (keyword: string) => { // 去重并截取前10个 const unique = Array.from(new Set(names)).slice(0, 10); - console.log('[API] getSearchSuggestions: 网易云建议解析成功:', unique); + console.log('[API] getSearchSuggestions: 解析成功:', unique); return unique; } diff --git a/src/renderer/components/player/ReparsePopover.vue b/src/renderer/components/player/ReparsePopover.vue index a781723..aee5748 100644 --- a/src/renderer/components/player/ReparsePopover.vue +++ b/src/renderer/components/player/ReparsePopover.vue @@ -22,37 +22,58 @@
{{ t('player.reparse.title') }}
{{ t('player.reparse.desc') }}
-
+
-
-
- -
-
- {{ source.label }} -
+
@@ -78,50 +99,101 @@ import { useI18n } from 'vue-i18n'; import { CacheManager } from '@/api/musicParser'; import { playMusic } from '@/hooks/MusicHook'; +import { initLxMusicRunner, setLxMusicRunner } from '@/services/LxMusicSourceRunner'; import { SongSourceConfigManager } from '@/services/SongSourceConfigManager'; +import { useSettingsStore } from '@/store'; import { usePlayerStore } from '@/store/modules/player'; +import type { LxMusicScriptConfig } from '@/types/lxMusic'; import type { Platform } from '@/types/music'; +import { type MusicSourceGroup, useMusicSources } from '@/utils/musicSourceConfig'; + +type ReparseSourceItem = { + id: string; + platform: Platform; + label: string; + icon: string; + color: string; + group: MusicSourceGroup; + available: boolean; + configHint?: string; + lxScriptId?: string; +}; const playerStore = usePlayerStore(); +const settingsStore = useSettingsStore(); const { t } = useI18n(); const message = useMessage(); +const { allSources } = useMusicSources(); // 音源重新解析状态 const isReparsing = ref(false); -const currentReparsingSource = ref(null); +const currentReparsingId = ref(null); -// 实际存储选中音源的值 -const selectedSourcesValue = ref([]); +// 当前选中的音源条目 id(唯一标识,区分不同 lxMusic 脚本) +const selectedSourceId = ref(null); -const isReparse = computed(() => selectedSourcesValue.value.length > 0); +const isReparse = computed(() => selectedSourceId.value !== null); -// 可选音源列表 -const musicSourceOptions = ref([ - { label: 'MiGu', value: 'migu' as Platform }, - { label: 'KuGou', value: 'kugou' as Platform }, - { label: 'pyncmd', value: 'pyncmd' as Platform }, - { label: 'GdMuisc', value: 'gdmusic' as Platform } -]); +// 构建重解析音源列表:将 lxMusic 展开为每个导入的脚本 +const reparseSourceList = computed(() => { + const result: ReparseSourceItem[] = []; + for (const source of allSources.value) { + if (source.key === 'lxMusic') { + const scripts: LxMusicScriptConfig[] = settingsStore.setData.lxMusicScripts || []; + for (const script of scripts) { + result.push({ + id: `lxMusic:${script.id}`, + platform: 'lxMusic', + label: script.name, + icon: source.icon, + color: source.color, + group: source.group, + available: true, + lxScriptId: script.id + }); + } + // 没有导入任何脚本时显示占位 + if (scripts.length === 0) { + result.push({ + id: 'lxMusic', + platform: 'lxMusic', + label: 'lxMusic', + icon: source.icon, + color: source.color, + group: source.group, + available: false, + configHint: 'settings.playback.lxMusic.scripts.notConfigured' + }); + } + } else { + result.push({ + id: source.key, + platform: source.key, + label: source.key, + icon: source.icon, + color: source.color, + group: source.group, + available: source.available, + configHint: source.configHint + }); + } + } + return result; +}); -// 检查音源是否被选中 -const isCurrentSource = (source: Platform) => { - return selectedSourcesValue.value.includes(source); -}; +// 按分组排列音源 +const GROUP_ORDER: MusicSourceGroup[] = ['unblock', 'extended', 'plugin']; -// 获取音源图标 -const getSourceIcon = (source: Platform) => { - const iconMap: Record = { - migu: 'ri-music-2-fill', - kugou: 'ri-music-fill', - qq: 'ri-qq-fill', - joox: 'ri-disc-fill', - pyncmd: 'ri-netease-cloud-music-fill', - gdmusic: 'ri-google-fill', - kuwo: 'ri-music-fill', - lxMusic: 'ri-leaf-fill' - }; +const groupedSources = computed(() => { + return GROUP_ORDER.map((groupKey) => ({ + key: groupKey, + sources: reparseSourceList.value.filter((s) => s.group === groupKey) + })).filter((g) => g.sources.length > 0); +}); - return iconMap[source] || 'ri-music-2-fill'; +// 检查音源条目是否被选中 +const isCurrentSource = (sourceId: string) => { + return selectedSourceId.value === sourceId; }; // 初始化选中的音源 @@ -129,40 +201,59 @@ const initSelectedSources = () => { const songId = playMusic.value.id; const config = SongSourceConfigManager.getConfig(songId); - if (config) { - selectedSourcesValue.value = config.sources; + if (config && config.sources.length > 0) { + const platform = config.sources[0]; + if (platform === 'lxMusic') { + // lxMusic 需要结合当前激活的脚本 id 来定位 + const activeId = settingsStore.setData.activeLxMusicApiId; + selectedSourceId.value = activeId ? `lxMusic:${activeId}` : null; + } else { + selectedSourceId.value = platform; + } } else { - selectedSourcesValue.value = []; + selectedSourceId.value = null; } }; // 清除自定义音源 const clearCustomSource = () => { SongSourceConfigManager.clearConfig(playMusic.value.id); - selectedSourcesValue.value = []; + selectedSourceId.value = null; }; -// 直接重新解析当前歌曲 -const directReparseMusic = async (source: Platform) => { - if (isReparsing.value) { - return; +// 点击音源条目 +const handleSourceClick = async (source: ReparseSourceItem) => { + if (source.lxScriptId) { + await reparseWithLxScript(source); + } else { + await directReparseMusic(source); } +}; + +// 使用指定 lxMusic 脚本重新解析 +const reparseWithLxScript = async (source: ReparseSourceItem) => { + if (isReparsing.value || !source.lxScriptId) return; + + const scripts: LxMusicScriptConfig[] = settingsStore.setData.lxMusicScripts || []; + const script = scripts.find((s) => s.id === source.lxScriptId); + if (!script) return; try { isReparsing.value = true; - currentReparsingSource.value = source; + currentReparsingId.value = source.id; + + // 激活该脚本的 runner + setLxMusicRunner(null); + await initLxMusicRunner(script.script); + settingsStore.setSetData({ activeLxMusicApiId: script.id }); const songId = Number(playMusic.value.id); - await CacheManager.clearMusicCache(songId); - // 更新选中的音源值为当前点击的音源 - selectedSourcesValue.value = [source]; + selectedSourceId.value = source.id; + SongSourceConfigManager.setConfig(songId, ['lxMusic'], 'manual'); - // 使用 SongSourceConfigManager 保存配置(手动选择) - SongSourceConfigManager.setConfig(songId, [source], 'manual'); - - const success = await playerStore.reparseCurrentSong(source, false); + const success = await playerStore.reparseCurrentSong('lxMusic', false); if (success) { message.success(t('player.reparse.success')); @@ -174,7 +265,37 @@ const directReparseMusic = async (source: Platform) => { message.error(t('player.reparse.failed')); } finally { isReparsing.value = false; - currentReparsingSource.value = null; + currentReparsingId.value = null; + } +}; + +// 直接重新解析当前歌曲(非 lxMusic) +const directReparseMusic = async (source: ReparseSourceItem) => { + if (isReparsing.value) return; + + try { + isReparsing.value = true; + currentReparsingId.value = source.id; + + const songId = Number(playMusic.value.id); + await CacheManager.clearMusicCache(songId); + + selectedSourceId.value = source.id; + SongSourceConfigManager.setConfig(songId, [source.platform], 'manual'); + + const success = await playerStore.reparseCurrentSong(source.platform, false); + + if (success) { + message.success(t('player.reparse.success')); + } else { + message.error(t('player.reparse.failed')); + } + } catch (error) { + console.error('解析失败:', error); + message.error(t('player.reparse.failed')); + } finally { + isReparsing.value = false; + currentReparsingId.value = null; } }; @@ -209,7 +330,7 @@ watch( } .source-button { - &:hover:not(.opacity-50) { + &:hover:not(.opacity-50):not(.opacity-40) { @apply transform -translate-y-0.5 shadow-sm; } } diff --git a/src/renderer/components/settings/MusicSourceSettings.vue b/src/renderer/components/settings/MusicSourceSettings.vue index 905b57d..dcc50fb 100644 --- a/src/renderer/components/settings/MusicSourceSettings.vue +++ b/src/renderer/components/settings/MusicSourceSettings.vue @@ -33,15 +33,15 @@

-
@@ -53,7 +53,7 @@ }" :class="{ 'bg-gray-100 dark:bg-white/10': !isSourceSelected(source.key) }" > - +
@@ -75,102 +75,22 @@ >
-
-
- - -
-
- -
- -
-
- 落雪音源 -
- -
-
-

+ +

{{ activeLxApiId && lxMusicScriptInfo ? lxMusicScriptInfo.name : t('settings.playback.lxMusic.scripts.notConfigured') }}

-
-
- - -
-
- -
- -
-
- {{ - t('settings.playback.sourceLabels.custom') - }} -
- -
-
-

+ +

{{ settingsStore.setData.customApiPlugin ? t('settings.playback.customApi.status.imported') @@ -377,24 +297,7 @@ import { import { useSettingsStore } from '@/store'; import type { LxMusicScriptConfig, LxScriptInfo, LxSourceKey } from '@/types/lxMusic'; import { type Platform } from '@/types/music'; - -// ==================== 类型定义 ==================== -type ExtendedPlatform = Platform | 'custom' | 'lxMusic'; - -interface MusicSourceConfig { - key: string; - description?: string; - color: string; - disabled?: boolean; -} - -// ==================== 音源配置 ==================== -const MUSIC_SOURCES: MusicSourceConfig[] = [ - { key: 'migu', color: '#ff6600' }, - { key: 'kugou', color: '#2979ff' }, - { key: 'kuwo', color: '#ff8c00' }, - { key: 'pyncmd', color: '#ec4141' } -]; +import { useMusicSources } from '@/utils/musicSourceConfig'; // ==================== Props & Emits ==================== const props = defineProps({ @@ -403,8 +306,8 @@ const props = defineProps({ default: false }, sources: { - type: Array as () => ExtendedPlatform[], - default: () => ['migu', 'kugou', 'kuwo', 'pyncmd'] as ExtendedPlatform[] + type: Array as () => Platform[], + default: () => ['migu', 'kugou', 'kuwo', 'pyncmd'] as Platform[] } }); @@ -415,8 +318,9 @@ const { t } = useI18n(); const settingsStore = useSettingsStore(); const message = useMessage(); const visible = ref(props.show); -const selectedSources = ref([...props.sources]); +const selectedSources = ref([...props.sources]); const activeTab = ref('sources'); +const { allSources } = useMusicSources(); const tabs = computed(() => [ { key: 'sources', label: t('settings.playback.lxMusic.tabs.sources') }, @@ -459,7 +363,7 @@ const renameInputRef = ref(null); // ==================== 计算属性 ==================== const isSourceSelected = (sourceKey: string): boolean => { - return selectedSources.value.includes(sourceKey as ExtendedPlatform); + return selectedSources.value.includes(sourceKey as Platform); }; // ==================== 方法 ==================== @@ -488,7 +392,7 @@ const toggleSource = (sourceKey: string) => { } } - const index = selectedSources.value.indexOf(sourceKey as ExtendedPlatform); + const index = selectedSources.value.indexOf(sourceKey as Platform); if (index > -1) { // 至少保留一个音源 if (selectedSources.value.length <= 1) { @@ -497,7 +401,7 @@ const toggleSource = (sourceKey: string) => { } selectedSources.value.splice(index, 1); } else { - selectedSources.value.push(sourceKey as ExtendedPlatform); + selectedSources.value.push(sourceKey as Platform); } }; @@ -749,7 +653,7 @@ const saveScriptName = (apiId: string) => { * 确认选择 */ const handleConfirm = () => { - const defaultPlatforms: ExtendedPlatform[] = ['migu', 'kugou', 'kuwo', 'pyncmd']; + const defaultPlatforms: Platform[] = ['migu', 'kugou', 'kuwo', 'pyncmd']; const valuesToEmit = selectedSources.value.length > 0 ? [...new Set(selectedSources.value)] : defaultPlatforms; emit('update:sources', valuesToEmit); @@ -812,7 +716,7 @@ watch( // 同步外部sources属性变化 watch( () => props.sources, - (newVal: ExtendedPlatform[]) => { + (newVal: Platform[]) => { selectedSources.value = [...newVal]; }, { deep: true } diff --git a/src/renderer/types/lxMusic.ts b/src/renderer/types/lxMusic.ts index ee4005f..8b3b1f3 100644 --- a/src/renderer/types/lxMusic.ts +++ b/src/renderer/types/lxMusic.ts @@ -23,12 +23,6 @@ export type LxQuality = '128k' | '320k' | 'flac' | 'flac24bit'; /** * 支持的音源 key - * - kw: 酷我 - * - kg: 酷狗 - * - tx: QQ音乐 - * - wy: 网易云 - * - mg: 咪咕 - * - local: 本地音乐 */ export type LxSourceKey = 'kw' | 'kg' | 'tx' | 'wy' | 'mg' | 'local'; @@ -122,12 +116,12 @@ export const LX_EVENT_NAMES = { * 落雪音源 key 到平台名称的映射 */ export const LX_SOURCE_NAMES: Record = { - kw: '酷我', - kg: '酷狗', - tx: 'QQ音乐', - wy: '网易云', - mg: '咪咕', - local: '本地' + kw: 'kw', + kg: 'kg', + tx: 'tx', + wy: 'wy', + mg: 'mg', + local: 'local' }; /** diff --git a/src/renderer/types/music.ts b/src/renderer/types/music.ts index ed83561..c4e6a46 100644 --- a/src/renderer/types/music.ts +++ b/src/renderer/types/music.ts @@ -1,5 +1,14 @@ // 音乐平台类型 -export type Platform = 'qq' | 'migu' | 'kugou' | 'kuwo' | 'pyncmd' | 'joox' | 'gdmusic' | 'lxMusic'; +export type Platform = + | 'qq' + | 'migu' + | 'kugou' + | 'kuwo' + | 'pyncmd' + | 'joox' + | 'gdmusic' + | 'lxMusic' + | 'custom'; // 默认平台列表 export const DEFAULT_PLATFORMS: Platform[] = ['lxMusic', 'migu', 'kugou', 'kuwo', 'pyncmd']; diff --git a/src/renderer/utils/musicSourceConfig.ts b/src/renderer/utils/musicSourceConfig.ts new file mode 100644 index 0000000..a5e3bb1 --- /dev/null +++ b/src/renderer/utils/musicSourceConfig.ts @@ -0,0 +1,62 @@ +import { computed } from 'vue'; + +import { useSettingsStore } from '@/store'; +import type { Platform } from '@/types/music'; + +// ==================== 类型定义 ==================== + +export type MusicSourceGroup = 'unblock' | 'extended' | 'plugin'; + +export type MusicSourceMeta = { + key: Platform; + icon: string; + color: string; + group: MusicSourceGroup; +}; + +export type MusicSourceInfo = MusicSourceMeta & { + available: boolean; + configHint?: string; +}; + +// ==================== 静态注册表 ==================== + +export const MUSIC_SOURCE_REGISTRY: MusicSourceMeta[] = [ + // 内置解锁音源 (UnblockMusicStrategy) + { key: 'migu', icon: 'ri-music-2-fill', color: '#ff6600', group: 'unblock' }, + { key: 'kugou', icon: 'ri-music-fill', color: '#2979ff', group: 'unblock' }, + { key: 'kuwo', icon: 'ri-music-fill', color: '#ff8c00', group: 'unblock' }, + { key: 'pyncmd', icon: 'ri-netease-cloud-music-fill', color: '#ec4141', group: 'unblock' }, + // 扩展音源 (GDMusicStrategy) + { key: 'gdmusic', icon: 'ri-google-fill', color: '#4285f4', group: 'extended' }, + // 插件音源 (需要用户配置) + { key: 'lxMusic', icon: 'ri-leaf-fill', color: '#22c55e', group: 'plugin' }, + { key: 'custom', icon: 'ri-plug-fill', color: '#8b5cf6', group: 'plugin' } +]; + +// ==================== Composable ==================== + +export const useMusicSources = () => { + const settingsStore = useSettingsStore(); + + const allSources = computed(() => { + return MUSIC_SOURCE_REGISTRY.map((source) => { + let available = true; + let configHint: string | undefined; + + if (source.key === 'lxMusic') { + available = + (settingsStore.setData.lxMusicScripts?.length ?? 0) > 0 && + Boolean(settingsStore.setData.activeLxMusicApiId); + if (!available) configHint = 'settings.playback.lxMusic.scripts.notConfigured'; + } else if (source.key === 'custom') { + available = Boolean(settingsStore.setData.customApiPlugin); + if (!available) configHint = 'settings.playback.customApi.notImported'; + } + + return { ...source, available, configHint }; + }); + }); + + return { allSources }; +}; diff --git a/src/renderer/views/favorite/index.vue b/src/renderer/views/favorite/index.vue index 519ac16..16a5328 100644 --- a/src/renderer/views/favorite/index.vue +++ b/src/renderer/views/favorite/index.vue @@ -257,10 +257,9 @@ const getFavoriteSongs = async () => { try { const currentIds = getCurrentPageIds(); - // 分离网易云音乐ID和B站视频ID const musicIds = currentIds.filter((id) => typeof id === 'number') as number[]; - // 处理网易云音乐数据 + // 处理音乐数据 let neteaseSongs: SongResult[] = []; if (musicIds.length > 0) { const res = await getMusicDetail(musicIds); @@ -282,7 +281,7 @@ const getFavoriteSongs = async () => { .map((id) => { const strId = String(id); - // 查找网易云音乐 + // 查找音乐 const found = neteaseSongs.find((song) => String(song.id) === strId); return found; }) diff --git a/src/renderer/views/history/index.vue b/src/renderer/views/history/index.vue index 98dacdb..4dc7405 100644 --- a/src/renderer/views/history/index.vue +++ b/src/renderer/views/history/index.vue @@ -555,7 +555,7 @@ const loadHistoryData = async () => { // 根据分类处理不同的数据 if (currentCategory.value === 'songs') { - // 区分本地歌曲和网易云歌曲 + // 区分本地歌曲和云歌曲 const localItems: any[] = []; const neteaseItems: any[] = []; @@ -567,7 +567,7 @@ const loadHistoryData = async () => { } }); - // 获取网易云歌曲详情 + // 获取歌曲详情 let neteaseSongs: SongResult[] = []; if (neteaseItems.length > 0) { try { @@ -585,7 +585,7 @@ const loadHistoryData = async () => { }); } } catch (error) { - console.error('获取网易云歌曲详情失败:', error); + console.error('获取歌曲详情失败:', error); } } diff --git a/src/renderer/views/toplist/index.vue b/src/renderer/views/toplist/index.vue index 0bdd0d2..2090613 100644 --- a/src/renderer/views/toplist/index.vue +++ b/src/renderer/views/toplist/index.vue @@ -83,7 +83,7 @@ {{ item.name }}

- {{ item.updateFrequency || '网易云音乐榜单' }} + {{ item.updateFrequency }}