const fs = require('fs'); const path = require('path'); const ROOT_DIR = path.join(__dirname, '..'); const APP_CONFIG_FILE = path.join(ROOT_DIR, 'config.json'); const DEFAULT_APP_CONFIG = { api: { music: 'https://api.chksz.top/api/163_music', lyric: 'https://api.chksz.top/api/163_lyric', search: 'https://api.chksz.top/api/163_search', playlist: 'https://api.chksz.top/api/163_playlist', neteaseBaseUrl: 'https://music.163.com', }, paths: { downloadDir: 'downloads', historyFile: 'download_history.json', runtimeConfigFile: 'downloader_config.json', trashDir: '.trash', tempZipDir: '.tmp_zip', }, defaults: { quality: 'jymaster', retries: 3, historyLimit: 500, }, }; function loadAppConfig() { try { if (!fs.existsSync(APP_CONFIG_FILE)) return DEFAULT_APP_CONFIG; const config = JSON.parse(fs.readFileSync(APP_CONFIG_FILE, 'utf-8')); return { api: { ...DEFAULT_APP_CONFIG.api, ...(config.api || {}) }, paths: { ...DEFAULT_APP_CONFIG.paths, ...(config.paths || {}) }, defaults: { ...DEFAULT_APP_CONFIG.defaults, ...(config.defaults || {}) }, }; } catch (e) { console.warn(`⚠️ 配置文件读取失败,将使用默认配置: ${e.message}`); return DEFAULT_APP_CONFIG; } } function resolveConfigPath(configPath, fallback) { const value = typeof configPath === 'string' && configPath.trim() ? configPath.trim() : fallback; return path.isAbsolute(value) ? value : path.join(ROOT_DIR, value); } function getConfigString(value, fallback) { return typeof value === 'string' && value.trim() ? value.trim() : fallback; } const APP_CONFIG = loadAppConfig(); const API = { music: getConfigString(APP_CONFIG.api.music, DEFAULT_APP_CONFIG.api.music), lyric: getConfigString(APP_CONFIG.api.lyric, DEFAULT_APP_CONFIG.api.lyric), search: getConfigString(APP_CONFIG.api.search, DEFAULT_APP_CONFIG.api.search), playlist: getConfigString(APP_CONFIG.api.playlist, DEFAULT_APP_CONFIG.api.playlist), neteaseBaseUrl: getConfigString(APP_CONFIG.api.neteaseBaseUrl, DEFAULT_APP_CONFIG.api.neteaseBaseUrl), }; const NETEASE_BASE_URL = API.neteaseBaseUrl.replace(/\/$/, ''); const DOWNLOAD_DIR = resolveConfigPath(APP_CONFIG.paths.downloadDir, DEFAULT_APP_CONFIG.paths.downloadDir); const HISTORY_FILE = resolveConfigPath(APP_CONFIG.paths.historyFile, DEFAULT_APP_CONFIG.paths.historyFile); const CONFIG_FILE = resolveConfigPath(APP_CONFIG.paths.runtimeConfigFile, DEFAULT_APP_CONFIG.paths.runtimeConfigFile); const TRASH_DIR = resolveConfigPath(APP_CONFIG.paths.trashDir, DEFAULT_APP_CONFIG.paths.trashDir); const TEMP_ZIP_DIR = resolveConfigPath(APP_CONFIG.paths.tempZipDir, DEFAULT_APP_CONFIG.paths.tempZipDir); const QUALITY_LEVELS = ['standard', 'exhigh', 'lossless', 'hires', 'jymaster', 'sky', 'jyeffect']; const QUALITY_LABELS = { standard: '标准', exhigh: '极高', lossless: '无损', hires: 'Hi-Res', jymaster: '超清母带', sky: '空间音频', jyeffect: '高清臻品', }; const DEFAULT_LEVEL = QUALITY_LEVELS.includes(APP_CONFIG.defaults.quality) ? APP_CONFIG.defaults.quality : DEFAULT_APP_CONFIG.defaults.quality; const DEFAULT_RETRIES = Number.isInteger(APP_CONFIG.defaults.retries) && APP_CONFIG.defaults.retries > 0 ? APP_CONFIG.defaults.retries : DEFAULT_APP_CONFIG.defaults.retries; const HISTORY_LIMIT = Number.isInteger(APP_CONFIG.defaults.historyLimit) && APP_CONFIG.defaults.historyLimit > 0 ? APP_CONFIG.defaults.historyLimit : DEFAULT_APP_CONFIG.defaults.historyLimit; function loadRuntimeConfig() { try { if (fs.existsSync(CONFIG_FILE)) return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8')); } catch {} return {}; } function saveRuntimeConfig(config) { fs.mkdirSync(path.dirname(CONFIG_FILE), { recursive: true }); fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8'); } function getConfig(key, defaultVal) { const config = loadRuntimeConfig(); return config[key] !== undefined ? config[key] : defaultVal; } function setConfig(key, val) { const config = loadRuntimeConfig(); config[key] = val; saveRuntimeConfig(config); } if (!fs.existsSync(DOWNLOAD_DIR)) fs.mkdirSync(DOWNLOAD_DIR, { recursive: true }); module.exports = { API, NETEASE_BASE_URL, DOWNLOAD_DIR, HISTORY_FILE, CONFIG_FILE, TRASH_DIR, TEMP_ZIP_DIR, QUALITY_LEVELS, QUALITY_LABELS, DEFAULT_LEVEL, DEFAULT_RETRIES, HISTORY_LIMIT, getConfig, setConfig, };