|
24 | 24 | OPENAI_TTS_URL = "https://api.openai.com/v1/audio/speech" |
25 | 25 | OPENAI_KEY = os.environ.get("OPENAI_API_KEY", "") |
26 | 26 |
|
| 27 | +CATEGORIES = ["医疗健康", "教育", "文化艺术", "劳动者", "商务科技", "生活"] |
| 28 | + |
27 | 29 | PRESETS = [ |
28 | 30 | {"id": "lingnan_uncle", "name": "岭南大叔", "file": "lingnan_uncle.png", "voice": "onyx", "gender": "male", |
29 | | - "desc": "50 岁广东老街坊", "samples": ["今天带大家品品正宗广州早茶", "来来来,饮啖茶先"]}, |
| 31 | + "category": "生活", "desc": "50 岁广东老街坊", "samples": ["今天带大家品品正宗广州早茶", "来来来,饮啖茶先"]}, |
30 | 32 | {"id": "shenzhen_student", "name": "深圳中学生", "file": "shenzhen_student.png", "voice": "shimmer", "gender": "female", |
31 | | - "desc": "15 岁活泼女生", "samples": ["同学们好!今天聊聊科技创新", "暑假我参加了编程夏令营"]}, |
| 33 | + "category": "生活", "desc": "15 岁活泼女生", "samples": ["同学们好!今天聊聊科技创新", "暑假我参加了编程夏令营"]}, |
32 | 34 | {"id": "news_anchor", "name": "新闻主播", "file": "news_anchor.png", "voice": "nova", "gender": "female", |
33 | | - "desc": "30 岁专业女主播", "samples": ["观众朋友们好,欢迎收看今天的新闻", "接下来是一则重要报道"]}, |
| 35 | + "category": "商务科技", "desc": "30 岁专业女主播", "samples": ["观众朋友们好,欢迎收看今天的新闻", "接下来是一则重要报道"]}, |
34 | 36 | {"id": "tech_engineer", "name": "IT工程师", "file": "tech_engineer.png", "voice": "echo", "gender": "male", |
35 | | - "desc": "28 岁码农小王", "samples": ["今天来聊聊大模型的最新进展", "这个 bug 我找了三天终于搞定了"]}, |
| 37 | + "category": "商务科技", "desc": "28 岁码农小王", "samples": ["今天来聊聊大模型的最新进展", "这个 bug 我找了三天终于搞定了"]}, |
36 | 38 | {"id": "retired_professor", "name": "退休教授", "file": "retired_professor.png", "voice": "onyx", "gender": "male", |
37 | | - "desc": "65 岁历史学者", "samples": ["让我们回顾一下这段波澜壮阔的历史", "读书破万卷,下笔如有神"]}, |
| 39 | + "category": "教育", "desc": "65 岁历史学者", "samples": ["让我们回顾一下这段波澜壮阔的历史", "读书破万卷,下笔如有神"]}, |
38 | 40 | {"id": "college_girl", "name": "大学生小林", "file": "college_girl.png", "voice": "nova", "gender": "female", |
39 | | - "desc": "20 岁开朗学姐", "samples": ["学弟学妹们,选课有什么想问的吗?", "大学四年最重要的是找到自己的方向"]}, |
| 41 | + "category": "教育", "desc": "20 岁开朗学姐", "samples": ["学弟学妹们,选课有什么想问的吗?", "大学四年最重要的是找到自己的方向"]}, |
40 | 42 | {"id": "fitness_coach", "name": "健身教练", "file": "fitness_coach.png", "voice": "echo", "gender": "male", |
41 | | - "desc": "32 岁运动达人", "samples": ["来!今天做三组深蹲,跟我一起动", "健身不只是练肌肉,更是练意志力"]}, |
| 43 | + "category": "生活", "desc": "32 岁运动达人", "samples": ["来!今天做三组深蹲,跟我一起动", "健身不只是练肌肉,更是练意志力"]}, |
42 | 44 | {"id": "doctor_li", "name": "李医生", "file": "doctor_li.png", "voice": "nova", "gender": "female", |
43 | | - "desc": "38 岁温柔医师", "samples": ["别紧张,我来给你解释一下检查结果", "预防胜于治疗,记得定期体检"]}, |
| 45 | + "category": "医疗健康", "desc": "38 岁温柔医师", "samples": ["别紧张,我来给你解释一下检查结果", "预防胜于治疗,记得定期体检"]}, |
44 | 46 | {"id": "hip_hop_youth", "name": "说唱少年", "file": "hip_hop_youth.png", "voice": "echo", "gender": "male", |
45 | | - "desc": "22 岁地下说唱", "samples": ["这首歌献给所有追梦的年轻人", "我的音乐就是我的态度"]}, |
| 47 | + "category": "生活", "desc": "22 岁地下说唱", "samples": ["这首歌献给所有追梦的年轻人", "我的音乐就是我的态度"]}, |
46 | 48 | {"id": "business_woman", "name": "商务总监", "file": "business_woman.png", "voice": "nova", "gender": "female", |
47 | | - "desc": "42 岁职场精英", "samples": ["这个季度的业绩目标,我们一起看看", "谈判的关键是找到双赢的方案"]}, |
| 49 | + "category": "商务科技", "desc": "42 岁职场精英", "samples": ["这个季度的业绩目标,我们一起看看", "谈判的关键是找到双赢的方案"]}, |
48 | 50 | {"id": "grandma_chen", "name": "陈奶奶", "file": "grandma_chen.png", "voice": "shimmer", "gender": "female", |
49 | | - "desc": "75 岁慈祥老人", "samples": ["孩子们,奶奶给你们讲个故事", "做人啊,最重要的就是善良"]}, |
| 51 | + "category": "生活", "desc": "75 岁慈祥老人", "samples": ["孩子们,奶奶给你们讲个故事", "做人啊,最重要的就是善良"]}, |
50 | 52 | {"id": "child_host", "name": "小小主持人", "file": "child_host.png", "voice": "alloy", "gender": "male", |
51 | | - "desc": "10 岁阳光少年", "samples": ["大家好!我是今天的小主持人", "接下来请欣赏我的才艺表演"]}, |
| 53 | + "category": "教育", "desc": "10 岁阳光少年", "samples": ["大家好!我是今天的小主持人", "接下来请欣赏我的才艺表演"]}, |
52 | 54 | {"id": "yoga_teacher", "name": "瑜伽老师", "file": "yoga_teacher.png", "voice": "shimmer", "gender": "female", |
53 | | - "desc": "30 岁身心教练(半身)", "samples": ["深呼吸,感受当下的平静", "今天的主题是肩颈放松"]}, |
| 55 | + "category": "生活", "desc": "30 岁身心教练(半身)", "samples": ["深呼吸,感受当下的平静", "今天的主题是肩颈放松"]}, |
54 | 56 | {"id": "street_musician", "name": "街头歌手", "file": "street_musician.png", "voice": "echo", "gender": "male", |
55 | | - "desc": "25 岁独立音乐人(全身)", "samples": ["这首歌送给每一个在路上的人", "音乐不需要舞台,哪里都可以唱"]}, |
| 57 | + "category": "文化艺术", "desc": "25 岁独立音乐人(全身)", "samples": ["这首歌送给每一个在路上的人", "音乐不需要舞台,哪里都可以唱"]}, |
56 | 58 | {"id": "chef_wang", "name": "王大厨", "file": "chef_wang.png", "voice": "onyx", "gender": "male", |
57 | | - "desc": "45 岁川菜大师(半身)", "samples": ["今天教大家做一道正宗的麻婆豆腐", "火候是厨艺的灵魂"]}, |
| 59 | + "category": "生活", "desc": "45 岁川菜大师(半身)", "samples": ["今天教大家做一道正宗的麻婆豆腐", "火候是厨艺的灵魂"]}, |
58 | 60 | {"id": "nurse_zhang", "name": "张护士", "file": "nurse_zhang.png", "voice": "nova", "gender": "female", |
59 | | - "desc": "28 岁温暖守护者", "samples": ["别怕,打针一点都不疼", "记得按时吃药,早日康复"]}, |
| 61 | + "category": "医疗健康", "desc": "28 岁温暖守护者", "samples": ["别怕,打针一点都不疼", "记得按时吃药,早日康复"]}, |
60 | 62 | {"id": "kindergarten_teacher", "name": "幼儿园老师", "file": "kindergarten_teacher.png", "voice": "shimmer", "gender": "female", |
61 | | - "desc": "25 岁孩子王", "samples": ["小朋友们,今天我们来画画吧", "每个孩子都是独一无二的星星"]}, |
| 63 | + "category": "教育", "desc": "25 岁孩子王", "samples": ["小朋友们,今天我们来画画吧", "每个孩子都是独一无二的星星"]}, |
62 | 64 | {"id": "farmer_uncle", "name": "老农大伯", "file": "farmer_uncle.png", "voice": "onyx", "gender": "male", |
63 | | - "desc": "55 岁稻田守望者", "samples": ["今年的稻子长势不错", "种地这事,急不来,得看天"]}, |
| 65 | + "category": "劳动者", "desc": "55 岁稻田守望者", "samples": ["今年的稻子长势不错", "种地这事,急不来,得看天"]}, |
64 | 66 | {"id": "firefighter", "name": "消防战士", "file": "firefighter.png", "voice": "echo", "gender": "male", |
65 | | - "desc": "30 岁逆行英雄", "samples": ["我们的使命是保护每一个人的安全", "火灾预防比救火更重要"]}, |
| 67 | + "category": "劳动者", "desc": "30 岁逆行英雄", "samples": ["我们的使命是保护每一个人的安全", "火灾预防比救火更重要"]}, |
66 | 68 | {"id": "delivery_rider", "name": "外卖小哥", "file": "delivery_rider.png", "voice": "echo", "gender": "male", |
67 | | - "desc": "28 岁城市骑手(半身)", "samples": ["您的订单马上就到", "风里雨里,我们一直在路上"]}, |
| 69 | + "category": "劳动者", "desc": "28 岁城市骑手(半身)", "samples": ["您的订单马上就到", "风里雨里,我们一直在路上"]}, |
68 | 70 | {"id": "elderly_calligrapher", "name": "书法老人", "file": "elderly_calligrapher.png", "voice": "onyx", "gender": "male", |
69 | | - "desc": "75 岁翰墨传承人", "samples": ["写字如做人,一笔一划都要端正", "这幅字送给有缘人"]}, |
| 71 | + "category": "文化艺术", "desc": "75 岁翰墨传承人", "samples": ["写字如做人,一笔一划都要端正", "这幅字送给有缘人"]}, |
70 | 72 | {"id": "deaf_artist", "name": "无声画家", "file": "deaf_artist.png", "voice": "shimmer", "gender": "female", |
71 | | - "desc": "29 岁听障艺术家", "samples": ["我的画笔就是我的声音", "色彩能传达语言无法表达的情感"]}, |
| 73 | + "category": "文化艺术", "desc": "29 岁听障艺术家", "samples": ["我的画笔就是我的声音", "色彩能传达语言无法表达的情感"]}, |
72 | 74 | {"id": "taxi_driver", "name": "出租车师傅", "file": "taxi_driver.png", "voice": "onyx", "gender": "male", |
73 | | - "desc": "50 岁城市百科全书", "samples": ["我开了二十年出租,这城市的故事都装在车里", "上车吧,我带你抄近路"]}, |
| 75 | + "category": "劳动者", "desc": "50 岁城市百科全书", "samples": ["我开了二十年出租,这城市的故事都装在车里", "上车吧,我带你抄近路"]}, |
74 | 76 | {"id": "tea_master", "name": "茶艺师", "file": "tea_master.png", "voice": "nova", "gender": "female", |
75 | | - "desc": "42 岁传统文化守护者", "samples": ["品茶如品人生,需要慢慢体会", "这壶龙井,是今年明前的新茶"]}, |
| 77 | + "category": "文化艺术", "desc": "42 岁传统文化守护者", "samples": ["品茶如品人生,需要慢慢体会", "这壶龙井,是今年明前的新茶"]}, |
76 | 78 | {"id": "postal_worker", "name": "乡邮员", "file": "postal_worker.png", "voice": "echo", "gender": "male", |
77 | | - "desc": "45 岁大山信使", "samples": ["这条山路我走了二十年,每一家都认识", "再远的地方,信也能送到"]}, |
| 79 | + "category": "劳动者", "desc": "45 岁大山信使", "samples": ["这条山路我走了二十年,每一家都认识", "再远的地方,信也能送到"]}, |
78 | 80 | {"id": "welder_woman", "name": "女焊工", "file": "welder_woman.png", "voice": "nova", "gender": "female", |
79 | | - "desc": "35 岁钢铁玫瑰", "samples": ["焊缝要均匀,就像做人要踏实", "谁说女人不能做这行?"]}, |
| 81 | + "category": "劳动者", "desc": "35 岁钢铁玫瑰", "samples": ["焊缝要均匀,就像做人要踏实", "谁说女人不能做这行?"]}, |
80 | 82 | {"id": "blind_musician", "name": "盲人琴师", "file": "blind_musician.png", "voice": "onyx", "gender": "male", |
81 | | - "desc": "62 岁二胡演奏家", "samples": ["音乐是光,即使看不见也能感受到", "这首二泉映月,送给所有在黑暗中坚持的人"]}, |
| 83 | + "category": "文化艺术", "desc": "62 岁二胡演奏家", "samples": ["音乐是光,即使看不见也能感受到", "这首二泉映月,送给所有在黑暗中坚持的人"]}, |
82 | 84 | {"id": "migrant_mom", "name": "打工妈妈", "file": "migrant_mom.png", "voice": "nova", "gender": "female", |
83 | | - "desc": "35 岁远方的牵挂", "samples": ["孩子,妈妈在外面一切都好", "等攒够了钱,就回家陪你"]}, |
| 85 | + "category": "生活", "desc": "35 岁远方的牵挂", "samples": ["孩子,妈妈在外面一切都好", "等攒够了钱,就回家陪你"]}, |
84 | 86 | {"id": "game_developer", "name": "游戏开发者", "file": "game_developer.png", "voice": "echo", "gender": "male", |
85 | | - "desc": "26 岁独立开发者", "samples": ["这个游戏的灵感来自我的童年", "做游戏最重要的是好玩"]}, |
| 87 | + "category": "商务科技", "desc": "26 岁独立开发者", "samples": ["这个游戏的灵感来自我的童年", "做游戏最重要的是好玩"]}, |
86 | 88 | {"id": "librarian", "name": "图书管理员", "file": "librarian.png", "voice": "nova", "gender": "female", |
87 | | - "desc": "52 岁知识守门人", "samples": ["这本书我推荐了三十年", "图书馆是最公平的地方,人人都能来"]}, |
| 89 | + "category": "教育", "desc": "52 岁知识守门人", "samples": ["这本书我推荐了三十年", "图书馆是最公平的地方,人人都能来"]}, |
88 | 90 | {"id": "fisherman", "name": "老渔民", "file": "fisherman.png", "voice": "onyx", "gender": "male", |
89 | | - "desc": "55 岁海上老人(全身)", "samples": ["大海教会我的,比学校多得多", "今天浪不大,出海正好"]}, |
| 91 | + "category": "劳动者", "desc": "55 岁海上老人(全身)", "samples": ["大海教会我的,比学校多得多", "今天浪不大,出海正好"]}, |
90 | 92 | {"id": "ballet_dancer", "name": "芭蕾舞者", "file": "ballet_dancer.png", "voice": "shimmer", "gender": "female", |
91 | | - "desc": "23 岁翩翩起舞(半身)", "samples": ["每一次旋转都是和自己的对话", "台上一分钟,台下十年功"]}, |
| 93 | + "category": "文化艺术", "desc": "23 岁翩翩起舞(半身)", "samples": ["每一次旋转都是和自己的对话", "台上一分钟,台下十年功"]}, |
92 | 94 | {"id": "construction_worker", "name": "建筑工人", "file": "construction_worker.png", "voice": "echo", "gender": "male", |
93 | | - "desc": "35 岁城市建造者", "samples": ["这栋楼是我们一砖一瓦盖起来的", "虽然辛苦,但看到高楼拔地而起很自豪"]}, |
| 95 | + "category": "劳动者", "desc": "35 岁城市建造者", "samples": ["这栋楼是我们一砖一瓦盖起来的", "虽然辛苦,但看到高楼拔地而起很自豪"]}, |
94 | 96 | {"id": "flower_shop_owner", "name": "花店老板娘", "file": "flower_shop_owner.png", "voice": "nova", "gender": "female", |
95 | | - "desc": "40 岁用花传情(半身)", "samples": ["每束花都有它的语言", "今天这束向日葵,送给需要阳光的人"]}, |
| 97 | + "category": "生活", "desc": "40 岁用花传情(半身)", "samples": ["每束花都有它的语言", "今天这束向日葵,送给需要阳光的人"]}, |
96 | 98 | ] |
97 | 99 |
|
98 | 100 | VOICE_LABELS = { |
@@ -123,20 +125,25 @@ def _find_preset(avatar_id): |
123 | 125 | async def list_voices(): |
124 | 126 | return [{"id": k, "label": v} for k, v in VOICE_LABELS.items()] |
125 | 127 |
|
| 128 | +@app.get("/api/categories") |
| 129 | +async def list_categories(): |
| 130 | + return CATEGORIES |
| 131 | + |
126 | 132 | @app.get("/api/avatars") |
127 | 133 | async def list_avatars(): |
128 | 134 | result = [] |
129 | 135 | for p in PRESETS: |
130 | 136 | result.append({ |
131 | 137 | "id": p["id"], "name": p["name"], "preset": True, |
132 | 138 | "voice": p["voice"], "gender": p["gender"], |
| 139 | + "category": p.get("category", ""), |
133 | 140 | "desc": p.get("desc", ""), "samples": p.get("samples", []), |
134 | 141 | }) |
135 | 142 | for aid, info in _load_meta().items(): |
136 | 143 | result.append({ |
137 | 144 | "id": aid, "name": info["name"], "preset": False, |
138 | 145 | "voice": info.get("voice", "nova"), "gender": info.get("gender", "unknown"), |
139 | | - "desc": "", "samples": [], |
| 146 | + "category": "", "desc": "", "samples": [], |
140 | 147 | }) |
141 | 148 | return result |
142 | 149 |
|
|
0 commit comments