Ethscriptions commited on
Commit
97f00e6
·
verified ·
1 Parent(s): 5b9a309

gemini-stars: 旁白文案、TTS 音频与时间轴(step 2)

Browse files
gemini-stars/README.md CHANGED
@@ -59,7 +59,7 @@ gemini-stars/
59
  ## 4. 步骤与进度看板
60
 
61
  - [x] Step 1:撰写本计划(本文件)并推送
62
- - [ ] Step 2:撰写旁白文案,生成 TTS 音频与 `timeline.js`,推送
63
  - [ ] Step 3:场景动画引擎与全部场景(横/竖屏自适应),推送
64
  - [ ] Step 4:配乐与音效合成、混音、播放器,推送
65
  - [ ] Step 5:MP4 本地导出(24/30/60fps,横/竖屏),推送
 
59
  ## 4. 步骤与进度看板
60
 
61
  - [x] Step 1:撰写本计划(本文件)并推送
62
+ - [x] Step 2:撰写旁白文案,生成 TTS 音频与 `timeline.js`,推送
63
  - [ ] Step 3:场景动画引擎与全部场景(横/竖屏自适应),推送
64
  - [ ] Step 4:配乐与音效合成、混音、播放器,推送
65
  - [ ] Step 5:MP4 本地导出(24/30/60fps,横/竖屏),推送
gemini-stars/audio/v01.mp3 ADDED
Binary file (42.2 kB). View file
 
gemini-stars/audio/v02.mp3 ADDED
Binary file (29.1 kB). View file
 
gemini-stars/audio/v03.mp3 ADDED
Binary file (33 kB). View file
 
gemini-stars/audio/v04.mp3 ADDED
Binary file (25.6 kB). View file
 
gemini-stars/audio/v05.mp3 ADDED
Binary file (35.1 kB). View file
 
gemini-stars/audio/v06.mp3 ADDED
Binary file (40.6 kB). View file
 
gemini-stars/audio/v07.mp3 ADDED
Binary file (56.7 kB). View file
 
gemini-stars/audio/v08.mp3 ADDED
Binary file (51.3 kB). View file
 
gemini-stars/audio/v09.mp3 ADDED
Binary file (22.6 kB). View file
 
gemini-stars/audio/v10.mp3 ADDED
Binary file (38.9 kB). View file
 
gemini-stars/audio/v11.mp3 ADDED
Binary file (62.8 kB). View file
 
gemini-stars/audio/v12.mp3 ADDED
Binary file (21.7 kB). View file
 
gemini-stars/audio/v13.mp3 ADDED
Binary file (42.9 kB). View file
 
gemini-stars/audio/v14.mp3 ADDED
Binary file (58.5 kB). View file
 
gemini-stars/audio/v15.mp3 ADDED
Binary file (36.3 kB). View file
 
gemini-stars/audio/v16.mp3 ADDED
Binary file (42.3 kB). View file
 
gemini-stars/audio/v17.mp3 ADDED
Binary file (36.1 kB). View file
 
gemini-stars/audio/v18.mp3 ADDED
Binary file (41.2 kB). View file
 
gemini-stars/audio/v19.mp3 ADDED
Binary file (33.6 kB). View file
 
gemini-stars/audio/v20.mp3 ADDED
Binary file (42.5 kB). View file
 
gemini-stars/audio/v21.mp3 ADDED
Binary file (24.5 kB). View file
 
gemini-stars/audio/v22.mp3 ADDED
Binary file (31.8 kB). View file
 
gemini-stars/audio/v23.mp3 ADDED
Binary file (36.3 kB). View file
 
gemini-stars/audio/v24.mp3 ADDED
Binary file (51.8 kB). View file
 
gemini-stars/audio/v25.mp3 ADDED
Binary file (43.9 kB). View file
 
gemini-stars/audio/v26.mp3 ADDED
Binary file (46.1 kB). View file
 
gemini-stars/audio/v27.mp3 ADDED
Binary file (40.3 kB). View file
 
gemini-stars/audio/v28.mp3 ADDED
Binary file (33 kB). View file
 
gemini-stars/audio/v29.mp3 ADDED
Binary file (21.3 kB). View file
 
gemini-stars/timeline.js ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // 由 tools/make_voice.py 自动生成,请勿手改
2
+ export const TIMELINE = {
3
+ "duration": 214.32,
4
+ "scenes": [
5
+ {
6
+ "id": "intro",
7
+ "start": 0,
8
+ "lines": [
9
+ {
10
+ "file": "audio/v01.mp3",
11
+ "text": "冬天的夜里,如果你抬头望向东方,会看到两颗明亮的星星,肩并着肩。",
12
+ "start": 2.6,
13
+ "end": 9.63
14
+ },
15
+ {
16
+ "file": "audio/v02.mp3",
17
+ "text": "它们就像一对形影不离的兄弟——这,就是双子座。",
18
+ "start": 10.01,
19
+ "end": 14.86
20
+ }
21
+ ],
22
+ "end": 15.64
23
+ },
24
+ {
25
+ "id": "find",
26
+ "start": 15.64,
27
+ "lines": [
28
+ {
29
+ "file": "audio/v03.mp3",
30
+ "text": "想找到它很简单。先找到猎户座腰带上的三颗星,",
31
+ "start": 16.24,
32
+ "end": 21.74
33
+ },
34
+ {
35
+ "file": "audio/v04.mp3",
36
+ "text": "再顺着猎户座的身体,向东北方向望过去。",
37
+ "start": 22.12,
38
+ "end": 26.39
39
+ },
40
+ {
41
+ "file": "audio/v05.mp3",
42
+ "text": "那两颗挨得很近的亮星,就是双子的头:北河二和北河三。",
43
+ "start": 26.77,
44
+ "end": 32.63
45
+ },
46
+ {
47
+ "file": "audio/v06.mp3",
48
+ "text": "从它们向下延伸出两串星,像两个手拉着手的小人,站在银河岸边。",
49
+ "start": 33.01,
50
+ "end": 39.78
51
+ }
52
+ ],
53
+ "end": 40.64
54
+ },
55
+ {
56
+ "id": "myth",
57
+ "start": 40.64,
58
+ "lines": [
59
+ {
60
+ "file": "audio/v07.mp3",
61
+ "text": "在古希腊神话里,他们是一对孪生兄弟:哥哥卡斯托尔擅长驯马,弟弟波吕克斯是拳击高手。",
62
+ "start": 41.36,
63
+ "end": 50.82
64
+ },
65
+ {
66
+ "file": "audio/v08.mp3",
67
+ "text": "可他们的命运并不相同:卡斯托尔是凡人,而波吕克斯是宙斯之子,拥有不朽的生命。",
68
+ "start": 51.2,
69
+ "end": 59.74
70
+ },
71
+ {
72
+ "file": "audio/v09.mp3",
73
+ "text": "一次争斗中,卡斯托尔中箭身亡。",
74
+ "start": 60.12,
75
+ "end": 63.89
76
+ },
77
+ {
78
+ "file": "audio/v10.mp3",
79
+ "text": "波吕克斯悲痛万分,他请求宙斯:把我的不朽,分一半给哥哥吧。",
80
+ "start": 64.27,
81
+ "end": 70.75
82
+ },
83
+ {
84
+ "file": "audio/v11.mp3",
85
+ "text": "宙斯被打动了。从此兄弟俩一天在天上,一天在冥界,永远不再分开,并肩化作了夜空中的双子座。",
86
+ "start": 71.13,
87
+ "end": 81.59
88
+ }
89
+ ],
90
+ "end": 82.45
91
+ },
92
+ {
93
+ "id": "castor",
94
+ "start": 82.45,
95
+ "lines": [
96
+ {
97
+ "file": "audio/v12.mp3",
98
+ "text": "神话很浪漫,而科学更惊奇。",
99
+ "start": 83.17,
100
+ "end": 86.79
101
+ },
102
+ {
103
+ "file": "audio/v13.mp3",
104
+ "text": "哥哥北河二,用肉眼看只是一颗星,可在望远镜里,它分成了好几颗。",
105
+ "start": 87.17,
106
+ "end": 94.32
107
+ },
108
+ {
109
+ "file": "audio/v14.mp3",
110
+ "text": "天文学家发现,它其实是六颗恒星组成的大家庭:三对双星彼此绕转,距离我们大约五十一光年。",
111
+ "start": 94.7,
112
+ "end": 104.44
113
+ }
114
+ ],
115
+ "end": 105.22
116
+ },
117
+ {
118
+ "id": "pollux",
119
+ "start": 105.22,
120
+ "lines": [
121
+ {
122
+ "file": "audio/v15.mp3",
123
+ "text": "弟弟北河三,是一颗橙色的巨星,距离我们大约三十四光年。",
124
+ "start": 105.82,
125
+ "end": 111.87
126
+ },
127
+ {
128
+ "file": "audio/v16.mp3",
129
+ "text": "有趣的是,它排名叫“三”,却比“二”号哥哥更亮,是整个双子座最亮的星。",
130
+ "start": 112.25,
131
+ "end": 119.31
132
+ },
133
+ {
134
+ "file": "audio/v17.mp3",
135
+ "text": "在它身旁,还有一颗质量至少是木星两倍的行星,默默绕转。",
136
+ "start": 119.69,
137
+ "end": 125.71
138
+ }
139
+ ],
140
+ "end": 126.57
141
+ },
142
+ {
143
+ "id": "meteor",
144
+ "start": 126.57,
145
+ "lines": [
146
+ {
147
+ "file": "audio/v18.mp3",
148
+ "text": "每年十二月中旬,双子座还会送来一份盛大的礼物——双子座流星雨。",
149
+ "start": 127.29,
150
+ "end": 134.15
151
+ },
152
+ {
153
+ "file": "audio/v19.mp3",
154
+ "text": "在理想的暗夜里,高峰时每小时能看到一百多颗流星。",
155
+ "start": 134.53,
156
+ "end": 140.12
157
+ },
158
+ {
159
+ "file": "audio/v20.mp3",
160
+ "text": "大多数流星雨来自彗星,而它的母体,却是一颗名叫法厄同的小行星。",
161
+ "start": 140.5,
162
+ "end": 147.58
163
+ }
164
+ ],
165
+ "end": 148.44
166
+ },
167
+ {
168
+ "id": "discover",
169
+ "start": 148.44,
170
+ "lines": [
171
+ {
172
+ "file": "audio/v21.mp3",
173
+ "text": "双子座还见证过天文史上的重要时刻。",
174
+ "start": 149.16,
175
+ "end": 153.24
176
+ },
177
+ {
178
+ "file": "audio/v22.mp3",
179
+ "text": "一七八一年,赫歇尔在双子座附近发现了天王星;",
180
+ "start": 153.62,
181
+ "end": 158.92
182
+ },
183
+ {
184
+ "file": "audio/v23.mp3",
185
+ "text": "一九三零年,汤博在双子座天区的照片里,找到了冥王星。",
186
+ "start": 159.3,
187
+ "end": 165.35
188
+ }
189
+ ],
190
+ "end": 166.21
191
+ },
192
+ {
193
+ "id": "zodiac",
194
+ "start": 166.21,
195
+ "lines": [
196
+ {
197
+ "file": "audio/v24.mp3",
198
+ "text": "双子座也是黄道星座之一。每年六月下旬到七月下旬,太阳会从它的天区穿过。",
199
+ "start": 166.93,
200
+ "end": 175.57
201
+ },
202
+ {
203
+ "file": "audio/v25.mp3",
204
+ "text": "而在占星文化里,五月二十一日到六月二十一日出生的人被称作双子座,",
205
+ "start": 175.95,
206
+ "end": 183.27
207
+ },
208
+ {
209
+ "file": "audio/v26.mp3",
210
+ "text": "人们常用好奇、健谈、灵动来形容他们——当然,这只是一种有趣的文化想象。",
211
+ "start": 183.65,
212
+ "end": 191.33
213
+ }
214
+ ],
215
+ "end": 192.19
216
+ },
217
+ {
218
+ "id": "outro",
219
+ "start": 192.19,
220
+ "lines": [
221
+ {
222
+ "file": "audio/v27.mp3",
223
+ "text": "两颗星,一段兄弟情,一场流星雨,和一个装满秘密的六星家族。",
224
+ "start": 192.91,
225
+ "end": 199.63
226
+ },
227
+ {
228
+ "file": "audio/v28.mp3",
229
+ "text": "下一个晴朗的冬夜,记得抬头,找一找那对并肩的星星。",
230
+ "start": 200.01,
231
+ "end": 205.51
232
+ },
233
+ {
234
+ "file": "audio/v29.mp3",
235
+ "text": "它们已经在那里,等了你很久很久。",
236
+ "start": 205.89,
237
+ "end": 209.44
238
+ }
239
+ ],
240
+ "end": 214.32
241
+ }
242
+ ]
243
+ };
gemini-stars/tools/make_voice.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """生成双子座旁白音频与时间轴 timeline.js
2
+ 依赖: pip install edge-tts imageio-ffmpeg
3
+ 用法: python tools/make_voice.py
4
+ """
5
+ import asyncio, json, os, re, subprocess
6
+ import edge_tts, imageio_ffmpeg
7
+
8
+ HERE = os.path.dirname(os.path.abspath(__file__))
9
+ ROOT = os.path.dirname(HERE)
10
+ VOICE = "zh-CN-XiaoxiaoNeural"
11
+ RATE = "-4%"
12
+
13
+ SCENES = [
14
+ ("intro", 2.6, [
15
+ "冬天的夜里,如果你抬头望向东方,会看到两颗明亮的星星,肩并着肩。",
16
+ "它们就像一对形影不离的兄弟——这,就是双子座。"]),
17
+ ("find", 1.0, [
18
+ "想找到它很简单。先找到猎户座腰带上的三颗星,",
19
+ "再顺着猎户座的身体,向东北方向望过去。",
20
+ "那两颗挨得很近的亮星,就是双子的头:北河二和北河三。",
21
+ "从它们向下延伸出两串星,像两个手拉着手的小人,站在银河岸边。"]),
22
+ ("myth", 1.2, [
23
+ "在古希腊神话里,他们是一对孪生兄弟:哥哥卡斯托尔擅长驯马,弟弟波吕克斯是拳击高手。",
24
+ "可他们的命运并不相同:卡斯托尔是凡人,而波吕克斯是宙斯之子,拥有不朽的生命。",
25
+ "一次争斗中,卡斯托尔中箭身亡。",
26
+ "波吕克斯悲痛万分,他请求宙斯:把我的不朽,分一半给哥哥吧。",
27
+ "宙斯被打动了。从此兄弟俩一天在天上,一天在冥界,永远不再分开,并肩化作了夜空中的双子座。"]),
28
+ ("castor", 1.2, [
29
+ "神话很浪漫,而科学更惊奇。",
30
+ "哥哥北河二,用肉眼看只是一颗星,可在望远镜里,它分成了好几颗。",
31
+ "天文学家发现,它其实是六颗恒星组成的大家庭:三对双星彼此绕转,距离我们大约五十一光年。"]),
32
+ ("pollux", 1.0, [
33
+ "弟弟北河三,是一颗橙色的巨星,距离我们大约三十四光年。",
34
+ "有趣的是,它排名叫“三”,却比“二”号哥哥更亮,是整个双子座最亮的星。",
35
+ "在它身旁,还有一颗质量至少是木星两倍的行星,默默绕转。"]),
36
+ ("meteor", 1.2, [
37
+ "每年十二月中旬,双子座还会送来一份盛大的礼物——双子座流星雨。",
38
+ "在理想的暗夜里,高峰时每小时能看到一百多颗流星。",
39
+ "大多数流星雨来自彗星,而它的母体,却是一颗名叫法厄同的小行星。"]),
40
+ ("discover", 1.2, [
41
+ "双子座还见证过天文史上的重要时刻。",
42
+ "一七八一年,赫歇尔在双子座附近发现了天王星;",
43
+ "一九三零年,汤博在双子座天区的照片里,找到了冥王星。"]),
44
+ ("zodiac", 1.2, [
45
+ "双子座也是黄道星座之一。每年六月下旬到七月下旬,太阳会从它的天区穿过。",
46
+ "而在占星文化里,五月二十一日到六月二十一日出生的人被称作双子座,",
47
+ "人们常用好奇、健谈、灵动来形容他们——当然,这只是一种有趣的文化想象。"]),
48
+ ("outro", 1.2, [
49
+ "两颗星,一段兄弟情,一场流星雨,和一个装满秘密的六星家族。",
50
+ "下一个晴朗的冬夜,记得抬头,找一找那对并肩的星星。",
51
+ "它们已经在那里,等了你很久很久。"]),
52
+ ]
53
+ GAP = 0.38
54
+ TAIL = 4.5
55
+ FF = imageio_ffmpeg.get_ffmpeg_exe()
56
+
57
+ def dur(path):
58
+ r = subprocess.run([FF, "-i", path], capture_output=True, text=True).stderr
59
+ h, m, s = re.search(r"Duration: (\d+):(\d+):([\d.]+)", r).groups()
60
+ return int(h) * 3600 + int(m) * 60 + float(s)
61
+
62
+ async def main():
63
+ os.makedirs(os.path.join(ROOT, "audio"), exist_ok=True)
64
+ t = 0.0; idx = 0; scenes = []
65
+ for sid, lead, lines in SCENES:
66
+ t += lead
67
+ sc = {"id": sid, "start": round(t - lead * 0.6 if scenes else 0, 3), "lines": []}
68
+ for text in lines:
69
+ idx += 1
70
+ fn = f"audio/v{idx:02d}.mp3"
71
+ out = os.path.join(ROOT, fn)
72
+ if not os.path.exists(out):
73
+ await edge_tts.Communicate(text, VOICE, rate=RATE).save(out)
74
+ d = dur(out)
75
+ sc["lines"].append({"file": fn, "text": text, "start": round(t, 3), "end": round(t + d, 3)})
76
+ t += d + GAP
77
+ scenes.append(sc)
78
+ for i, sc in enumerate(scenes):
79
+ sc["end"] = scenes[i + 1]["start"] if i + 1 < len(scenes) else round(t + TAIL, 3)
80
+ total = scenes[-1]["end"]
81
+ js = "// 由 tools/make_voice.py 自动生成,请勿手改\nexport const TIMELINE = " + json.dumps({"duration": total, "scenes": scenes}, ensure_ascii=False, indent=1) + ";\n"
82
+ open(os.path.join(ROOT, "timeline.js"), "w").write(js)
83
+ print("total", total)
84
+
85
+ asyncio.run(main())