/** * 批量生成拼音音频URL脚本 * 支持多种TTS服务:有道词典、百度语音、科大讯飞等 */ const fs = require('fs'); const path = require('path'); // 拼音列表 const initials = ['b','p','m','f','d','t','n','l','g','k','h','j','q','x','zh','ch','sh','r','z','c','s','y','w']; const finals = ['a','o','e','i','u','ü','ai','ei','ui','ao','ou','iu','ie','üe','er','an','en','in','un','ün','ang','eng','ing','ong']; const overalls = ['zhi','chi','shi','ri','zi','ci','si','yi','wu','yu','ye','yue','yuan','yin','yun','ying']; /** * 有道词典TTS服务 * 优点:免费、无需注册、支持中文 * 缺点:音质一般 */ function getYoudaoUrl(text) { // 从环境变量读取TTS基础URL const ttsBaseUrl = process.env.TTS_BASE_URL || 'https://dict.youdao.com/dictvoice'; return `${ttsBaseUrl}?audio=${encodeURIComponent(text)}&type=1`; } /** * 生成所有拼音的音频URL配置 */ function generateAudioConfig() { // 从环境变量读取配置 const ttsProvider = process.env.TTS_PROVIDER || 'youdao'; const ttsBaseUrl = process.env.TTS_BASE_URL || 'https://dict.youdao.com/dictvoice'; const config = { // TTS服务提供商 provider: ttsProvider, // 基础URL baseUrl: ttsBaseUrl, // 所有拼音的音频URL audios: {} }; // 声母 initials.forEach(symbol => { config.audios[symbol] = { url: getYoudaoUrl(symbol), name: getPinyinName(symbol), type: 'initial' }; }); // 韵母 finals.forEach(symbol => { config.audios[symbol] = { url: getYoudaoUrl(symbol), name: getPinyinName(symbol), type: 'final' }; }); // 整体认读 overalls.forEach(symbol => { config.audios[symbol] = { url: getYoudaoUrl(symbol), name: getPinyinName(symbol), type: 'overall' }; }); return config; } /** * 获取拼音名称 */ function getPinyinName(symbol) { const nameMap = { 'b': '玻', 'p': '坡', 'm': '摸', 'f': '佛', 'd': '得', 't': '特', 'n': '讷', 'l': '勒', 'g': '哥', 'k': '科', 'h': '喝', 'j': '基', 'q': '欺', 'x': '希', 'zh': '知', 'ch': '蚩', 'sh': '诗', 'r': '日', 'z': '资', 'c': '雌', 's': '思', 'y': '医', 'w': '巫', 'a': '啊', 'o': '喔', 'e': '鹅', 'i': '衣', 'u': '乌', 'ü': '迂', 'ai': '哀', 'ei': '诶', 'ui': '威', 'ao': '熬', 'ou': '欧', 'iu': '优', 'ie': '耶', 'üe': '约', 'er': '儿', 'an': '安', 'en': '恩', 'in': '因', 'un': '温', 'ün': '晕', 'ang': '昂', 'eng': '亨', 'ing': '英', 'ong': '雍', 'zhi': '织', 'chi': '吃', 'shi': '狮', 'ri': '日', 'zi': '资', 'ci': '疵', 'si': '丝', 'yi': '衣', 'wu': '乌', 'yu': '迂', 'ye': '耶', 'yue': '约', 'yuan': '冤', 'yin': '因', 'yun': '晕', 'ying': '英' }; return nameMap[symbol] || symbol; } /** * 生成SQL更新语句(用于直接更新数据库) */ function generateSqlUpdates() { const config = generateAudioConfig(); const sqls = []; Object.entries(config.audios).forEach(([symbol, data]) => { const sql = `UPDATE pinyin_contents SET audio_url = '${data.url}' WHERE symbol = '${symbol}';`; sqls.push(sql); }); return sqls.join('\n'); } /** * 生成MongoDB更新脚本 */ function generateMongoScript() { const config = generateAudioConfig(); const updates = []; Object.entries(config.audios).forEach(([symbol, data]) => { updates.push({ updateOne: { filter: { symbol: symbol }, update: { $set: { audioUrl: data.url } } } }); }); return `db.pinyincontents.bulkWrite(${JSON.stringify(updates, null, 2)});`; } /** * 保存配置文件 */ function saveConfig() { const config = generateAudioConfig(); const outputPath = path.join(__dirname, '../../config/pinyin-audio-config.json'); // 确保目录存在 const dir = path.dirname(outputPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(outputPath, JSON.stringify(config, null, 2)); console.log(`✅ 音频配置已保存到: ${outputPath}`); // 生成SQL文件 const sqlPath = path.join(__dirname, '../../config/update-audio-urls.sql'); fs.writeFileSync(sqlPath, generateSqlUpdates()); console.log(`✅ SQL更新脚本已保存到: ${sqlPath}`); // 生成MongoDB脚本 const mongoPath = path.join(__dirname, '../../config/update-audio-urls.js'); fs.writeFileSync(mongoPath, generateMongoScript()); console.log(`✅ MongoDB更新脚本已保存到: ${mongoPath}`); // 输出统计 console.log('\n📊 生成统计:'); console.log(` - 声母: ${initials.length} 个`); console.log(` - 韵母: ${finals.length} 个`); console.log(` - 整体认读: ${overalls.length} 个`); console.log(` - 总计: ${initials.length + finals.length + overalls.length} 个拼音`); } // 运行 if (require.main === module) { saveConfig(); } module.exports = { generateAudioConfig, generateSqlUpdates, generateMongoScript, getYoudaoUrl };