analyze-model-lang-matrix.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """analyze-model-lang-matrix.py — 解析 sysvoices-dom,生成 model × lang × voice 矩阵"""
  4. import re, json, sys
  5. from collections import defaultdict
  6. PATH = r"C:/Users/caoyg/AppData/Local/Temp/sysvoices-dom.txt"
  7. text = open(PATH, encoding='utf-8').read()
  8. # 阿里云页面有 4 个章节(每个章节都重复了"本页导读"导航,所以取第一个匹配即可)
  9. SECTIONS = [
  10. ('cosyvoice-v3-flash', r'cosyvoice-v3-flash\s*音色列表'),
  11. ('cosyvoice-v3-plus', r'cosyvoice-v3-plus\s*音色列表'),
  12. ('cosyvoice-v2', r'cosyvoice-v2\s*音色列表'),
  13. ('cosyvoice-v1', r'cosyvoice-v1\s*音色列表'),
  14. ]
  15. # 取每个模型标题第一个出现的位置(跳过右侧栏的目录)
  16. positions = []
  17. for model, pat in SECTIONS:
  18. matches = list(re.finditer(pat, text))
  19. # 选最小位置的(主体内容不是侧栏目录)
  20. if matches:
  21. m = min(matches, key=lambda x: x.start())
  22. positions.append((m.start(), model))
  23. positions.sort()
  24. all_voices = defaultdict(list)
  25. all_voices_per_model = {}
  26. for i, (start, model) in enumerate(positions):
  27. end = positions[i + 1][0] if i + 1 < len(positions) else len(text)
  28. section = text[start:end]
  29. voices = []
  30. pos = [mm.start() for mm in re.finditer(r'名称[::]\s*.+?\n', section)]
  31. pos.append(len(section))
  32. for j in range(len(pos) - 1):
  33. chunk = section[pos[j]:pos[j + 1]]
  34. v = {}
  35. n = re.search(r'^名称[::]\s*(.+?)\s*$', chunk, re.MULTILINE)
  36. if n: v['name'] = n.group(1).strip()
  37. vp = re.search(r'voice参数[::]\s*(\w+)', chunk)
  38. if vp: v['voice_id'] = vp.group(1)
  39. t = re.search(r'特质[::]\s*(.+?)\n', chunk)
  40. if t: v['desc'] = t.group(1).strip()[:80]
  41. a = re.search(r'年龄[::]\s*(.+?)\n', chunk)
  42. if a: v['age'] = a.group(1).strip()
  43. l = re.search(r'语言[::]\s*(.+?)\n', chunk)
  44. if l: v['lang'] = l.group(1).strip()[:60]
  45. v['instruct'] = '支持' if 'Instruct:支持' in chunk else '不支持' if 'Instruct:不支持' in chunk else '?'
  46. if v.get('name'):
  47. voices.append(v)
  48. all_voices_per_model[model] = voices
  49. # 输出汇总
  50. sys.stdout.reconfigure(encoding='utf-8')
  51. total_voices = 0
  52. all_langs = set()
  53. for model, voices in all_voices_per_model.items():
  54. print(f'\n=== {model}: {len(voices)} 个音色 ===')
  55. lang_count = defaultdict(int)
  56. for v in voices:
  57. lang = v.get('lang', '?')
  58. for l in re.split('[、,]', lang):
  59. l = l.strip()
  60. if not l: continue
  61. all_langs.add(l)
  62. lang_count[l] += 1
  63. for l, c in sorted(lang_count.items(), key=lambda x: (-x[1], x[0])):
  64. print(f' {l}: {c}')
  65. total_voices += len(voices)
  66. print(f'\n========== 总览 ==========')
  67. print(f'总音色数: {total_voices}')
  68. print(f'涉及语种 ({len(all_langs)} 种):')
  69. for l in sorted(all_langs): print(f' - {l}')
  70. # 写 JSON 给前端用
  71. with open(r"C:/Users/caoyg/AppData/Local/Temp/alicloud-voices-matrix.json", 'w', encoding='utf-8') as f:
  72. json.dump(all_voices_per_model, f, ensure_ascii=False, indent=2)
  73. print(f'\nJSON: {len(all_voices_per_model)} 个模型 → C:/Users/caoyg/AppData/Local/Temp/alicloud-voices-matrix.json')