init_system_voices.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. """
  2. 系统音色初始化脚本
  3. 初始化系统预置音色数据到数据库。
  4. 注意:此脚本用于初始化数据库,系统运行时所有音色数据均从数据库获取,不包含硬编码。
  5. 数据来源说明:
  6. - 当前版本:基于阿里云百炼平台CosyVoice模型的官方音色文档手动整理
  7. - 未来版本:如果DashScope SDK提供获取系统音色的API,可改为从SDK同步
  8. 需求: 4.1
  9. """
  10. import sys
  11. from pathlib import Path
  12. # 添加项目根目录到路径
  13. sys.path.insert(0, str(Path(__file__).parent.parent))
  14. from app.database import SessionLocal
  15. from app.models.audio import SystemVoice
  16. # 系统预置音色数据
  17. # 注意:此数据仅用于初始化数据库,系统运行时所有音色均从数据库获取
  18. # 基于阿里云百炼平台CosyVoice模型的官方音色文档整理
  19. # 如果DashScope SDK未来提供获取系统音色的API,可改为从SDK动态获取
  20. SYSTEM_VOICES = [
  21. # ==================== 社交陪伴类 ====================
  22. {
  23. "voice_id": "longanyang",
  24. "name": "龙安洋",
  25. "trait": "阳光大男孩",
  26. "age": "20~30岁",
  27. "category": "社交陪伴",
  28. "languages": ["中文(普通话)", "英文"],
  29. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  30. "ssml_support": True,
  31. "instruct_support": True,
  32. "timestamp_support": True
  33. },
  34. {
  35. "voice_id": "longanhuan",
  36. "name": "龙安欢",
  37. "trait": "欢脱元气女",
  38. "age": "20~30岁",
  39. "category": "社交陪伴",
  40. "languages": ["中文(普通话)", "英文"],
  41. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  42. "ssml_support": True,
  43. "instruct_support": True,
  44. "timestamp_support": True
  45. },
  46. {
  47. "voice_id": "longanjing",
  48. "name": "龙安静",
  49. "trait": "温柔知性女",
  50. "age": "20~30岁",
  51. "category": "社交陪伴",
  52. "languages": ["中文(普通话)", "英文"],
  53. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  54. "ssml_support": True,
  55. "instruct_support": True,
  56. "timestamp_support": True
  57. },
  58. {
  59. "voice_id": "longanshu",
  60. "name": "龙安舒",
  61. "trait": "成熟稳重男",
  62. "age": "30~40岁",
  63. "category": "社交陪伴",
  64. "languages": ["中文(普通话)", "英文"],
  65. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  66. "ssml_support": True,
  67. "instruct_support": True,
  68. "timestamp_support": True
  69. },
  70. # ==================== 客服类 ====================
  71. {
  72. "voice_id": "longxiaoxia",
  73. "name": "龙小夏",
  74. "trait": "甜美客服女声",
  75. "age": "20~30岁",
  76. "category": "客服",
  77. "languages": ["中文(普通话)"],
  78. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  79. "ssml_support": True,
  80. "instruct_support": True,
  81. "timestamp_support": True
  82. },
  83. {
  84. "voice_id": "longlaotie",
  85. "name": "龙老铁",
  86. "trait": "亲切客服男声",
  87. "age": "30~40岁",
  88. "category": "客服",
  89. "languages": ["中文(普通话)"],
  90. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  91. "ssml_support": True,
  92. "instruct_support": True,
  93. "timestamp_support": True
  94. },
  95. # ==================== 新闻播报类 ====================
  96. {
  97. "voice_id": "longshuo",
  98. "name": "龙硕",
  99. "trait": "专业新闻男声",
  100. "age": "30~40岁",
  101. "category": "新闻播报",
  102. "languages": ["中文(普通话)"],
  103. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  104. "ssml_support": True,
  105. "instruct_support": True,
  106. "timestamp_support": True
  107. },
  108. {
  109. "voice_id": "longwan",
  110. "name": "龙婉",
  111. "trait": "专业新闻女声",
  112. "age": "30~40岁",
  113. "category": "新闻播报",
  114. "languages": ["中文(普通话)"],
  115. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  116. "ssml_support": True,
  117. "instruct_support": True,
  118. "timestamp_support": True
  119. },
  120. # ==================== 有声书类 ====================
  121. {
  122. "voice_id": "longyue",
  123. "name": "龙悦",
  124. "trait": "温暖讲述女声",
  125. "age": "30~40岁",
  126. "category": "有声书",
  127. "languages": ["中文(普通话)"],
  128. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  129. "ssml_support": True,
  130. "instruct_support": True,
  131. "timestamp_support": True
  132. },
  133. {
  134. "voice_id": "longcheng",
  135. "name": "龙城",
  136. "trait": "磁性讲述男声",
  137. "age": "30~40岁",
  138. "category": "有声书",
  139. "languages": ["中文(普通话)"],
  140. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  141. "ssml_support": True,
  142. "instruct_support": True,
  143. "timestamp_support": True
  144. },
  145. {
  146. "voice_id": "longshu",
  147. "name": "龙书",
  148. "trait": "儿童故事女声",
  149. "age": "20~30岁",
  150. "category": "有声书",
  151. "languages": ["中文(普通话)"],
  152. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  153. "ssml_support": True,
  154. "instruct_support": True,
  155. "timestamp_support": True
  156. },
  157. # ==================== 短视频配音类 ====================
  158. {
  159. "voice_id": "longxiaochun",
  160. "name": "龙小淳",
  161. "trait": "活泼解说女声",
  162. "age": "20~30岁",
  163. "category": "短视频配音",
  164. "languages": ["中文(普通话)"],
  165. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  166. "ssml_support": True,
  167. "instruct_support": True,
  168. "timestamp_support": True
  169. },
  170. {
  171. "voice_id": "longxiaobai",
  172. "name": "龙小白",
  173. "trait": "幽默解说男声",
  174. "age": "20~30岁",
  175. "category": "短视频配音",
  176. "languages": ["中文(普通话)"],
  177. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  178. "ssml_support": True,
  179. "instruct_support": True,
  180. "timestamp_support": True
  181. },
  182. # ==================== 多语言类 ====================
  183. {
  184. "voice_id": "longjielidou",
  185. "name": "龙杰力豆",
  186. "trait": "英语男声",
  187. "age": "30~40岁",
  188. "category": "多语言",
  189. "languages": ["英文", "中文(普通话)"],
  190. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  191. "ssml_support": True,
  192. "instruct_support": True,
  193. "timestamp_support": True
  194. },
  195. {
  196. "voice_id": "longmeimei",
  197. "name": "龙美美",
  198. "trait": "英语女声",
  199. "age": "20~30岁",
  200. "category": "多语言",
  201. "languages": ["英文", "中文(普通话)"],
  202. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  203. "ssml_support": True,
  204. "instruct_support": True,
  205. "timestamp_support": True
  206. },
  207. {
  208. "voice_id": "longyuuko",
  209. "name": "龙优子",
  210. "trait": "日语女声",
  211. "age": "20~30岁",
  212. "category": "多语言",
  213. "languages": ["日文", "中文(普通话)"],
  214. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  215. "ssml_support": True,
  216. "instruct_support": True,
  217. "timestamp_support": True
  218. },
  219. {
  220. "voice_id": "longtaro",
  221. "name": "龙太郎",
  222. "trait": "日语男声",
  223. "age": "30~40岁",
  224. "category": "多语言",
  225. "languages": ["日文", "中文(普通话)"],
  226. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  227. "ssml_support": True,
  228. "instruct_support": True,
  229. "timestamp_support": True
  230. },
  231. {
  232. "voice_id": "longmina",
  233. "name": "龙美娜",
  234. "trait": "韩语女声",
  235. "age": "20~30岁",
  236. "category": "多语言",
  237. "languages": ["韩文", "中文(普通话)"],
  238. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  239. "ssml_support": True,
  240. "instruct_support": True,
  241. "timestamp_support": True
  242. },
  243. # ==================== 方言类 ====================
  244. {
  245. "voice_id": "longdongbei",
  246. "name": "龙东北",
  247. "trait": "东北话男声",
  248. "age": "30~40岁",
  249. "category": "方言",
  250. "languages": ["中文(东北话)"],
  251. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  252. "ssml_support": True,
  253. "instruct_support": True,
  254. "timestamp_support": True
  255. },
  256. {
  257. "voice_id": "longsichuan",
  258. "name": "龙四川",
  259. "trait": "四川话男声",
  260. "age": "30~40岁",
  261. "category": "方言",
  262. "languages": ["中文(四川话)"],
  263. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  264. "ssml_support": True,
  265. "instruct_support": True,
  266. "timestamp_support": True
  267. },
  268. {
  269. "voice_id": "longminnan",
  270. "name": "龙闽南",
  271. "trait": "闽南语女声",
  272. "age": "30~40岁",
  273. "category": "方言",
  274. "languages": ["中文(闽南语)"],
  275. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  276. "ssml_support": True,
  277. "instruct_support": True,
  278. "timestamp_support": True
  279. },
  280. {
  281. "voice_id": "longyueyu",
  282. "name": "龙粤语",
  283. "trait": "粤语男声",
  284. "age": "30~40岁",
  285. "category": "方言",
  286. "languages": ["中文(粤语)"],
  287. "models": ["cosyvoice-v3-flash", "cosyvoice-v3-plus"],
  288. "ssml_support": True,
  289. "instruct_support": True,
  290. "timestamp_support": True
  291. },
  292. # ==================== CosyVoice-v2 专用音色 ====================
  293. {
  294. "voice_id": "longxiaochun_v2",
  295. "name": "龙小淳V2",
  296. "trait": "活泼解说女声",
  297. "age": "20~30岁",
  298. "category": "通用",
  299. "languages": ["中文(普通话)", "英文"],
  300. "models": ["cosyvoice-v2"],
  301. "ssml_support": True,
  302. "instruct_support": False,
  303. "timestamp_support": True
  304. },
  305. {
  306. "voice_id": "longxiaoxia_v2",
  307. "name": "龙小夏V2",
  308. "trait": "甜美客服女声",
  309. "age": "20~30岁",
  310. "category": "通用",
  311. "languages": ["中文(普通话)", "英文"],
  312. "models": ["cosyvoice-v2"],
  313. "ssml_support": True,
  314. "instruct_support": False,
  315. "timestamp_support": True
  316. },
  317. {
  318. "voice_id": "longlaotie_v2",
  319. "name": "龙老铁V2",
  320. "trait": "亲切客服男声",
  321. "age": "30~40岁",
  322. "category": "通用",
  323. "languages": ["中文(普通话)", "英文"],
  324. "models": ["cosyvoice-v2"],
  325. "ssml_support": True,
  326. "instruct_support": False,
  327. "timestamp_support": True
  328. },
  329. {
  330. "voice_id": "longshuo_v2",
  331. "name": "龙硕V2",
  332. "trait": "专业新闻男声",
  333. "age": "30~40岁",
  334. "category": "通用",
  335. "languages": ["中文(普通话)", "英文"],
  336. "models": ["cosyvoice-v2"],
  337. "ssml_support": True,
  338. "instruct_support": False,
  339. "timestamp_support": True
  340. },
  341. {
  342. "voice_id": "longwan_v2",
  343. "name": "龙婉V2",
  344. "trait": "专业新闻女声",
  345. "age": "30~40岁",
  346. "category": "通用",
  347. "languages": ["中文(普通话)", "英文"],
  348. "models": ["cosyvoice-v2"],
  349. "ssml_support": True,
  350. "instruct_support": False,
  351. "timestamp_support": True
  352. },
  353. ]
  354. def init_system_voices():
  355. """初始化系统音色数据"""
  356. db = SessionLocal()
  357. inserted = 0
  358. updated = 0
  359. try:
  360. for voice_data in SYSTEM_VOICES:
  361. # 检查是否已存在
  362. existing = db.query(SystemVoice).filter(
  363. SystemVoice.voice_id == voice_data["voice_id"]
  364. ).first()
  365. if existing:
  366. # 更新现有记录
  367. existing.name = voice_data["name"]
  368. existing.trait = voice_data["trait"]
  369. existing.age = voice_data["age"]
  370. existing.category = voice_data["category"]
  371. existing.languages = voice_data["languages"]
  372. existing.models = voice_data["models"]
  373. existing.ssml_support = voice_data["ssml_support"]
  374. existing.instruct_support = voice_data["instruct_support"]
  375. existing.timestamp_support = voice_data.get("timestamp_support", False)
  376. existing.is_active = True
  377. updated += 1
  378. else:
  379. # 创建新记录
  380. voice = SystemVoice(
  381. voice_id=voice_data["voice_id"],
  382. name=voice_data["name"],
  383. trait=voice_data["trait"],
  384. age=voice_data["age"],
  385. category=voice_data["category"],
  386. languages=voice_data["languages"],
  387. models=voice_data["models"],
  388. ssml_support=voice_data["ssml_support"],
  389. instruct_support=voice_data["instruct_support"],
  390. timestamp_support=voice_data.get("timestamp_support", False),
  391. is_active=True
  392. )
  393. db.add(voice)
  394. inserted += 1
  395. db.commit()
  396. print(f"系统音色初始化完成: 新增 {inserted} 条, 更新 {updated} 条, 共 {inserted + updated} 条")
  397. except Exception as e:
  398. db.rollback()
  399. print(f"系统音色初始化失败: {e}")
  400. raise
  401. finally:
  402. db.close()
  403. def list_system_voices():
  404. """列出所有系统音色"""
  405. db = SessionLocal()
  406. try:
  407. voices = db.query(SystemVoice).filter(SystemVoice.is_active == True).all()
  408. print(f"\n当前系统音色列表 (共 {len(voices)} 个):")
  409. print("-" * 80)
  410. # 按分类分组显示
  411. categories = {}
  412. for voice in voices:
  413. cat = voice.category or "未分类"
  414. if cat not in categories:
  415. categories[cat] = []
  416. categories[cat].append(voice)
  417. for category, voice_list in sorted(categories.items()):
  418. print(f"\n【{category}】")
  419. for voice in voice_list:
  420. models_str = ", ".join(voice.models) if voice.models else "无"
  421. print(f" - {voice.voice_id}: {voice.name} ({voice.trait}) - 模型: {models_str}")
  422. print("-" * 80)
  423. finally:
  424. db.close()
  425. def clear_system_voices():
  426. """清空所有系统音色(谨慎使用)"""
  427. db = SessionLocal()
  428. try:
  429. count = db.query(SystemVoice).delete()
  430. db.commit()
  431. print(f"已清空 {count} 条系统音色记录")
  432. except Exception as e:
  433. db.rollback()
  434. print(f"清空失败: {e}")
  435. raise
  436. finally:
  437. db.close()
  438. if __name__ == "__main__":
  439. import argparse
  440. parser = argparse.ArgumentParser(description="系统音色管理脚本")
  441. parser.add_argument(
  442. "action",
  443. choices=["init", "list", "clear"],
  444. nargs="?",
  445. default="init",
  446. help="操作类型: init(初始化), list(列表), clear(清空)"
  447. )
  448. args = parser.parse_args()
  449. if args.action == "init":
  450. init_system_voices()
  451. elif args.action == "list":
  452. list_system_voices()
  453. elif args.action == "clear":
  454. confirm = input("确定要清空所有系统音色吗?(yes/no): ")
  455. if confirm.lower() == "yes":
  456. clear_system_voices()
  457. else:
  458. print("操作已取消")