Browse Source

chore(tts): 入仓本次研究用的辅助脚本

- parse-cosyvoice-dom.py: DOM 文本 → 阿里云 CosyVoice 音色 JSON
- trigger-tts-and-check.js: 触发 TTS 任务入队 + 查 status
- check-tts-result.js: 查 TtsTask 完成状态 + audioUrl
MyFramework User 1 month ago
parent
commit
5bc6743050

+ 17 - 0
deploy-package/scripts/check-tts-result.js

@@ -0,0 +1,17 @@
+const { PrismaClient } = require('@prisma/client');
+const p = new PrismaClient();
+(async () => {
+  for (const bookId of [77, 78]) {
+    const b = await p.book.findUnique({ where: { id: bookId }, select: { id: true, title: true, targetLanguage: true, genStage: true } });
+    console.log(`book ${b.id} (${b.targetLanguage}) genStage=${b.genStage} title=${b.title}`);
+    const chs = await p.bookChapter.findMany({ where: { bookId }, select: { id: true, title: true, audioUrl: true, audioDuration: true, content: true, genStage: true } });
+    for (const ch of chs) {
+      console.log(`  ch#${ch.id} genStage=${ch.genStage} dur=${ch.audioDuration}s url=${(ch.audioUrl||'').slice(-60)}`);
+      if (ch.content) {
+        const sample = ch.content.substring(0, 150).replace(/\n/g, ' / ');
+        console.log(`    content sample: ${sample}`);
+      }
+    }
+  }
+  await p.$disconnect();
+})();

+ 67 - 0
deploy-package/scripts/parse-cosyvoice-dom.py

@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""parse-cosyvoice-dom.py — 解析阿里云 CosyVoice 音色页面 DOM,输出 JSON + 摘要"""
+import re
+import json
+import sys
+from collections import defaultdict
+
+PATH = r"C:/Users/caoyg/AppData/Local/Temp/cosyvoice.html"
+text = open(PATH, encoding='utf-8').read()
+
+# 切分 4 个模型段
+SECTIONS = [
+    ('cosyvoice-v3-flash', r'cosyvoice-v3-flash音色列表(.*?)cosyvoice-v3-plus音色列表'),
+    ('cosyvoice-v3-plus',  r'cosyvoice-v3-plus音色列表(.*?)(?=下一篇|cosyvoice-v2音色列表)'),
+    ('cosyvoice-v2',       r'cosyvoice-v2音色列表(.*?)cosyvoice-v1音色列表'),
+    ('cosyvoice-v1',       r'cosyvoice-v1音色列表(.*?)(?=上一篇|下一篇|\Z)'),
+]
+
+all_models = {}
+
+for model, pat in SECTIONS:
+    m = re.search(pat, text, re.DOTALL)
+    if not m:
+        continue
+    section = m.group(1)
+    voices = []
+    positions = [mm.start() for mm in re.finditer(r'名称[::]\s*.+?\n', section)]
+    positions.append(len(section))
+    for i in range(len(positions) - 1):
+        chunk = section[positions[i]:positions[i+1]]
+        voice = {}
+        n = re.search(r'^名称[::]\s*(.+?)\s*$', chunk, re.MULTILINE)
+        if n: voice['name'] = n.group(1).strip()
+        v = re.search(r'voice参数[::]\s*(\w+)', chunk)
+        if v: voice['voice_id'] = v.group(1)
+        t = re.search(r'特质[::]\s*(.+?)\n', chunk)
+        if t: voice['desc'] = t.group(1).strip()[:80]
+        a = re.search(r'年龄[::]\s*(.+?)\n', chunk)
+        if a: voice['age'] = a.group(1).strip()
+        l = re.search(r'语言[::]\s*(.+?)\n', chunk)
+        if l: voice['lang'] = l.group(1).strip()[:60]
+        voice['instruct'] = '支持' if 'Instruct:支持' in chunk else '不支持' if 'Instruct:不支持' in chunk else '?'
+        # scene/地区
+        s = re.search(r'适用场景[::]\s*(.+?)\n', chunk)
+        if s: voice['scene'] = s.group(1).strip()[:40]
+        if voice.get('name'):
+            voices.append(voice)
+    all_models[model] = voices
+
+# 输出 JSON
+with open(r"C:/Users/caoyg/AppData/Local/Temp/cosyvoice-voices.json", "w", encoding="utf-8") as f:
+    json.dump(all_models, f, ensure_ascii=False, indent=2)
+
+# 输出摘要
+sys.stdout.reconfigure(encoding='utf-8')
+for model, voices in all_models.items():
+    print(f"\n=== {model}: {len(voices)} 个音色 ===")
+    lang_count = defaultdict(int)
+    for v in voices:
+        lang = v.get('lang', '?')
+        # 简化为第一个语种
+        first = lang.split('、')[0].split('(')[0]
+        lang_count[first] += 1
+    print(f"  语种分布:")
+    for lang, cnt in sorted(lang_count.items(), key=lambda x: -x[1]):
+        print(f"    {lang}: {cnt}")

+ 18 - 0
deploy-package/scripts/trigger-tts-and-check.js

@@ -0,0 +1,18 @@
+// 触发 book 77/78 音频 + 查 TtsTask
+const p = new (require('@prisma/client').PrismaClient)();
+(async () => {
+  const { bookStore } = require('./dist/modules/book-generator/book-generator.store.js');
+  for (const bookId of [77, 78]) {
+    const chs = await p.bookChapter.findMany({ where: { bookId } });
+    console.log('=== book', bookId, '-', chs.length, '章 ===');
+    for (const ch of chs) {
+      try {
+        await bookStore.generateChapterAudioById(ch.id, 1, undefined, {});
+        console.log('  queued ch#' + ch.id);
+      } catch (e) {
+        console.log('  err ch#' + ch.id + ': ' + e.message.slice(0, 200));
+      }
+    }
+  }
+  await p.$disconnect();
+})();