Files
Toolbox/dev_test_scripts/debug/debug_tts_inner.js
T
yangxiangyuan d4bf3627fd feat: 新增运动节拍训练工具套件与子工具页面
新增运动节拍RepClock全套小程序页面、后端API与数据库服务,新增subhub子工具汇总页面,优化全局主页样式与配置,为web静态资源添加版本缓存参数,补充配套调试脚本
2026-09-09 14:43:06 +08:00

116 lines
5.2 KiB
JavaScript

const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const envPath = path.join(process.env.USERPROFILE || 'C:\\Users\\Administrator', 'Toolbox_local_creds.env.local')
if (fs.existsSync(envPath)) {
const text = fs.readFileSync(envPath, 'utf8')
for (const line of text.split(/\r?\n/)) {
if (!line || /^\s*#/.test(line)) continue
const i = line.indexOf('=')
if (i < 0) continue
const k = line.slice(0, i).trim(), v = line.slice(i + 1).trim().replace(/^["']|["']$/g, '')
if (k && !(k in process.env)) process.env[k] = v
}
}
process.chdir('d:/Trae_Files/TRAE-Toolbox')
global.__builtin_fetch = globalThis.fetch
const TTS_ROOT = path.join(process.cwd(), 'uploads', 'mp_rep_clock', 'tts_cache')
if (!fs.existsSync(TTS_ROOT)) fs.mkdirSync(TTS_ROOT, { recursive: true })
const VOICE_ID_DEFAULT = 'female_01'
const callCosyVoice = async (text, voice = VOICE_ID_DEFAULT) => {
const apiKey = process.env.DASHSCOPE_API_KEY || ''
console.log('[callCosyVoice] apiKey.len=', apiKey.length, 'text=', JSON.stringify(text), 'voice=', voice)
if (!apiKey) return { ok: false, error: 'DASHSCOPE_API_KEY 未配置' }
const t = String(text || '').trim()
if (!t) return { ok: false, error: 'empty text' }
const isMale = voice.indexOf('male') >= 0
const headers = {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
}
const fallbacks = []
fallbacks.push({
name: 'Fallback-A:Qwen3-TTS',
url: 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation',
body: {
model: 'qwen3-tts-flash',
input: {
text: t,
voice: isMale ? 'Cherry' : 'Cherry',
language_type: 'Chinese',
},
},
})
fallbacks.push({
name: 'Fallback-B:CosyVoice-v3',
url: 'https://dashscope.aliyuncs.com/api/v1/services/audio/tts/SpeechSynthesizer',
body: {
model: 'cosyvoice-v3-flash',
input: {
text: t,
voice: isMale ? 'longxiaocheng' : 'longanyang',
format: 'mp3',
sample_rate: 24000,
},
},
})
for (let idx = 0; idx < fallbacks.length; idx++) {
const fb = fallbacks[idx]
console.log(' [fb ' + idx + '] try ' + fb.name + ' url=' + fb.url)
console.log(' [fb ' + idx + '] body=', JSON.stringify(fb.body))
try {
const resp = await global.__builtin_fetch(fb.url, {
method: 'POST',
headers,
body: JSON.stringify(fb.body),
})
console.log(' [fb ' + idx + '] resp.status=', resp ? resp.status : 'null', 'resp.ok=', resp ? resp.ok : 'null')
if (!resp || !resp.ok) {
const txt = resp ? await resp.text().catch(() => '') : ''
console.log(' [fb ' + idx + '] !ok resp.text head=', (txt || '').slice(0, 500))
continue
}
const j = await resp.json()
console.log(' [fb ' + idx + '] json keys=', Object.keys(j || {}).join(','))
let audioUrl = ''
try {
const output = (j && j.output) || null
console.log(' [fb ' + idx + '] output keys=', output ? Object.keys(output).join(',') : 'null')
if (output) {
if (output.audio && typeof output.audio.url === 'string') { audioUrl = output.audio.url; console.log(' [fb ' + idx + '] audioUrl via output.audio.url') }
if (!audioUrl && typeof output.audio_url === 'string') { audioUrl = output.audio_url; console.log(' [fb ' + idx + '] audioUrl via output.audio_url') }
if (!audioUrl && typeof output.audioUrl === 'string') { audioUrl = output.audioUrl; console.log(' [fb ' + idx + '] audioUrl via output.audioUrl') }
if (!audioUrl && Array.isArray(output.results) && output.results[0]) {
const first = output.results[0] || {}
if (first.audio && typeof first.audio.url === 'string') { audioUrl = first.audio.url; console.log(' [fb ' + idx + '] audioUrl via output.results[0].audio.url') }
}
}
} catch (e) { console.log(' [fb ' + idx + '] output ex=', e.message) }
console.log(' [fb ' + idx + '] audioUrl=', audioUrl)
if (!audioUrl) continue
const download = await global.__builtin_fetch(audioUrl)
console.log(' [fb ' + idx + '] download status=', download ? download.status : 'null')
if (!download || !download.ok) continue
const buf = Buffer.from(await download.arrayBuffer())
const storedName = `rc_${Date.now()}_${crypto.randomBytes(4).toString('hex')}.mp3`
const absPath = path.join(TTS_ROOT, storedName)
fs.writeFileSync(absPath, buf)
const relPath = `uploads/mp_rep_clock/tts_cache/${storedName}`
const stat = fs.statSync(absPath)
const durationMs = Math.max(1000, Math.round(stat.size / (12 * 1024 / 8)))
console.log(' [fb ' + idx + '] OK storedName=' + storedName + ' size=' + stat.size + ' durationMs=' + durationMs)
return { ok: true, url: `/${relPath}`, durationMs }
} catch (e) {
console.log(' [fb ' + idx + '] EXCEPTION=', e && e.stack ? e.stack : String(e))
}
}
return { ok: false, error: 'All TTS fallbacks failed' }
}
;(async () => {
console.log('=== callCosyVoice(热身开合跳, female_01) ===')
const r = await callCosyVoice('热身开合跳', 'female_01')
console.log('\nFINAL RESULT=', JSON.stringify(r, null, 2))
})()