verify-voice-in-training-package.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. #!/usr/bin/env node
  2. /**
  3. * 验证培训包中的语音克隆能力
  4. * 检查项:
  5. * 1. 语音服务模块是否可加载
  6. * 2. ffmpeg/wx-voice 依赖是否可用
  7. * 3. Fmode 后端连通性
  8. */
  9. const path = require('path');
  10. const fs = require('fs');
  11. const TRAINING_EXE = path.join(__dirname, '../dist/qiwei-training/qiwei-workbench.exe');
  12. const VOICE_CHECK_MARKERS = [
  13. 'VoiceCloneService',
  14. 'indextts2', // Fmode 后端接口路径
  15. 'ffmpeg',
  16. 'wx-voice',
  17. 'SILK',
  18. ];
  19. async function checkExeExists() {
  20. if (!fs.existsSync(TRAINING_EXE)) {
  21. console.error(`❌ 培训包 exe 不存在: ${TRAINING_EXE}`);
  22. console.error(' 请先运行: node scripts/build-training-package.mjs');
  23. process.exit(1);
  24. }
  25. const sizeMB = (fs.statSync(TRAINING_EXE).size / (1024 * 1024)).toFixed(1);
  26. console.log(`✅ 培训包 exe 已存在: ${sizeMB} MB`);
  27. }
  28. async function checkVoiceModuleInSource() {
  29. const voiceServicePath = path.join(__dirname, '../mcp/src/core/voice-clone-service.js');
  30. if (!fs.existsSync(voiceServicePath)) {
  31. console.error('❌ 源码中缺少 voice-clone-service.js');
  32. process.exit(1);
  33. }
  34. const content = fs.readFileSync(voiceServicePath, 'utf8');
  35. const missing = VOICE_CHECK_MARKERS.filter(marker => !content.includes(marker));
  36. if (missing.length > 0) {
  37. console.error(`❌ 语音服务模块缺少关键组件: ${missing.join(', ')}`);
  38. process.exit(1);
  39. }
  40. console.log('✅ 源码语音服务模块完整');
  41. }
  42. async function checkVoiceDependencies() {
  43. const packageJson = require('../package.json');
  44. const requiredDeps = [
  45. '@ffmpeg-installer/ffmpeg',
  46. '@ffprobe-installer/ffprobe',
  47. '@binsee/wx-voice',
  48. ];
  49. const missing = requiredDeps.filter(dep =>
  50. !packageJson.dependencies[dep] && !packageJson.optionalDependencies[dep]
  51. );
  52. if (missing.length > 0) {
  53. console.error(`❌ package.json 缺少语音依赖: ${missing.join(', ')}`);
  54. process.exit(1);
  55. }
  56. console.log('✅ package.json 语音依赖已声明');
  57. }
  58. async function checkFmodeBackendEndpoint() {
  59. const voiceServicePath = path.join(__dirname, '../mcp/src/core/voice-clone-service.js');
  60. const content = fs.readFileSync(voiceServicePath, 'utf8');
  61. const endpoints = [
  62. 'https://server.fmode.cn/api/voice/indextts2',
  63. 'http://127.0.0.1:7338/api/voice/indextts2',
  64. ];
  65. const hasEndpoint = endpoints.some(ep => content.includes(ep));
  66. if (!hasEndpoint) {
  67. console.error('❌ 语音服务未配置 Fmode 后端地址');
  68. process.exit(1);
  69. }
  70. console.log('✅ Fmode 语音后端地址已配置');
  71. }
  72. async function checkREADMEVoiceSection() {
  73. const readme = path.join(__dirname, '../dist/qiwei-training/README.md');
  74. if (!fs.existsSync(readme)) {
  75. console.error('❌ 培训包 README.md 不存在');
  76. process.exit(1);
  77. }
  78. const content = fs.readFileSync(readme, 'utf8');
  79. if (!content.includes('## 语音克隆功能')) {
  80. console.error('❌ README.md 缺少语音克隆说明');
  81. process.exit(1);
  82. }
  83. console.log('✅ README.md 包含语音克隆文档');
  84. }
  85. async function main() {
  86. console.log('开始验证培训包语音能力...\n');
  87. await checkExeExists();
  88. await checkVoiceModuleInSource();
  89. await checkVoiceDependencies();
  90. await checkFmodeBackendEndpoint();
  91. await checkREADMEVoiceSection();
  92. console.log('\n✅ 所有检查通过!培训包已包含完整语音克隆能力。');
  93. console.log('\n下一步测试:');
  94. console.log(' 1. 拷贝 dist/qiwei-training/ 到测试机');
  95. console.log(' 2. 配置 .env.local 中的 QIWEI_AUTH_TOKEN');
  96. console.log(' 3. 运行 qiwei-workbench.exe');
  97. console.log(' 4. 在工作台中录制参考音频并测试合成');
  98. }
  99. main().catch(err => {
  100. console.error('验证失败:', err.message);
  101. process.exit(1);
  102. });