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

126 lines
5.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs')
const path = require('path')
const envPath = 'C:/Users/Administrator/Toolbox_local_creds.env.local'
if (fs.existsSync(envPath)) {
const txt = fs.readFileSync(envPath, 'utf-8')
txt.split(/\r?\n/).forEach(line => {
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/i)
if (!m) return
let v = m[2].trim()
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1)
if (!(m[1] in process.env)) process.env[m[1]] = v
})
}
process.chdir('d:/Trae_Files/TRAE-Toolbox')
global.__builtin_fetch = globalThis.fetch
const API_KEY = process.env.DASHSCOPE_API_KEY
const { getDb } = require(path.join(process.cwd(), 'src/server/mp_rep_clock/db'))
const tts = require(path.join(process.cwd(), 'src/server/mp_rep_clock/tts.js'))
console.log('=== 0. 自检 global/环境 ===')
console.log(' global.__builtin_fetch === globalThis.fetch:', global.__builtin_fetch === globalThis.fetch)
console.log(' typeof global.__builtin_fetch:', typeof global.__builtin_fetch)
console.log(' process.cwd():', process.cwd())
console.log(' TTS_ROOT:', tts.TTS_ROOT)
console.log(' TTS_ROOT exists:', fs.existsSync(tts.TTS_ROOT))
console.log(' apiKey.len:', API_KEY.length)
;(async () => {})()
async function main() {
console.log('\n=== 0.5 INLINE callCosyVoice 对比(与 debug_tts_inner.js 相同) ===')
const crypto = require('crypto')
const TTS_ROOT = tts.TTS_ROOT
const callCosyVoiceInline = async (text, voice) => {
const apiKey = process.env.DASHSCOPE_API_KEY || ''
const t = String(text || '').trim()
const isMale = voice.indexOf('male') >= 0
const headers = { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }
const fallbacks = [
{
name: 'Inline-A',
url: 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation',
body: { model: 'qwen3-tts-flash', input: { text: t, voice: 'Cherry', language_type: 'Chinese' } },
},
]
for (const fb of fallbacks) {
try {
const resp = await global.__builtin_fetch(fb.url, {
method: 'POST', headers, body: JSON.stringify(fb.body),
})
console.log(' inline fb', fb.name, 'status=', resp && resp.status)
if (!resp || !resp.ok) continue
const j = await resp.json()
const output = (j && j.output) || null
const audioUrl = output && output.audio && output.audio.url || ''
console.log(' inline fb', fb.name, 'audioUrl=', audioUrl.slice(0, 80))
if (!audioUrl) continue
const download = await global.__builtin_fetch(audioUrl)
if (!download || !download.ok) continue
const buf = Buffer.from(await download.arrayBuffer())
const storedName = `inline_${Date.now()}_${crypto.randomBytes(4).toString('hex')}.mp3`
const absPath = path.join(TTS_ROOT, storedName)
fs.writeFileSync(absPath, buf)
const durationMs = Math.max(1000, Math.round(buf.length / (12 * 1024 / 8)))
return { ok: true, url: `/uploads/mp_rep_clock/tts_cache/${storedName}`, durationMs }
} catch (e) { console.log(' inline ex', e.message) }
}
return { ok: false }
}
const inlineR = await callCosyVoiceInline('热身开合跳', 'female_01')
console.log(' inline result ok=', inlineR.ok, 'url=', inlineR.url)
console.log('\n=== 1. 检查 DASHSCOPE_API_KEY ===')
console.log(' len=', String(process.env.DASHSCOPE_API_KEY || '').length)
console.log()
console.log('=== 2. 先跑单条 synthesize 验证 Fallback ===')
const single = await tts.synthesize('开始训练,测试一下', 'female_01')
console.log(' single=', JSON.stringify(single, null, 2))
console.log()
console.log('=== 3. 全量 synthesizeBatch(title=测试一下) ===')
const card = {
title: '测试一下',
loopMode: 'sequential_once',
loopCount: 1,
announceLeadSeconds: '5,3',
steps: [
{ name: '热身开合跳', durationSec: 30, type: 'action' },
{ name: '平板支撑', durationSec: 60, type: 'action', restDurationSec: 15, voiceText: '' },
],
}
const batch = await tts.synthesizeBatch(card, 'female_01')
console.log(' batch total=', batch.total, 'success=', batch.success)
batch.results.forEach((r, i) => {
console.log(` [${i}] ok=${r.ok} cached=${r.cached} text=${JSON.stringify(r.text)} url=${r.url || ''} err=${r.error || ''}`)
})
console.log()
console.log('=== 4. TTS_ROOT uploads/mp_rep_clock/tts_cache files.Count ===')
const root = tts.TTS_ROOT
const files = fs.existsSync(root) ? fs.readdirSync(root).filter(f => f.endsWith('.mp3')) : []
console.log(' root=', root)
console.log(' files.Count=', files.length)
files.slice(-12).forEach(f => {
const stat = fs.statSync(path.join(root, f))
console.log(' -', f, stat.size, 'bytes')
})
console.log()
console.log('=== 5. rc_tts_cache 表行数(非 deleted)===')
try {
const row = getDb().prepare(`SELECT COUNT(*) as c FROM rc_tts_cache WHERE deleted = 0`).get()
console.log(' count=', row && row.c)
} catch (e) { console.log(' err=', e.message) }
console.log()
console.log('=== 6. 全部播报 lines 生成 ===')
const lines = tts.buildCardTTSBatch(card)
console.log(' lines=', lines.length)
lines.forEach((l, i) => console.log(` [${i}]`, l))
}
main().catch(e => console.error('FATAL', e))