-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathindex.html
927 lines (825 loc) · 30.3 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="在线免费的语音合成、文字转语音、文字合成配音服务,基于edge-tts语音合成服务提供,无需登录无需注册,在线免费合成语音"/>
<meta name="keyword" content="语音合成,文字合成,文字配音,合成语音,edge-tts,pyvideotrans"/>
<title>语音合成-文字转语音-srt字幕转语音-tts.pyvideotrans.com</title>
<link href="/output.css" rel="stylesheet">
<script src="/vue.js"></script>
<style>
#layui-nav {
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 99;
overflow: auto;
white-space: nowrap;
background: #eee;
}
.layui-nav .layui-nav-item {
position: relative;
display: inline-block;
margin-top: 0;
list-style: none;
vertical-align: middle;
line-height: 60px;
}
.layui-nav .layui-nav-item a {
color: #333;
}
.layui-nav .layui-nav-item a {
display: block;
padding: 0 20px;
color: #333;
transition: all .3s;
-webkit-transition: all .3s;
}
.layui-nav .layui-nav-item a:hover, .layui-nav .layui-this a {
color: #009688;
text-decoration: none;
}
#app{margin-top:50px}
</style>
</head>
<body class="bg-gray-100 font-sans">
<ul id="layui-nav" class="layui-nav">
<li class="layui-nav-item"><a href="https://pyvideotrans.com">pyVideoTrans</a></li>
<li class="layui-nav-item"><a href="https://tool.pyvideotrans.com" >视频工具箱</a></li>
<li class="layui-nav-item"><a href="https://stt.pyvideotrans.com">语音识别</a></li>
<li class="layui-nav-item "><a href="https://srt.pyvideotrans.com" >字幕翻译</a></li>
<li class="layui-nav-item layui-this"><a href="https://tts.pyvideotrans.com">语音合成</a></li>
</ul>
<div class="container mx-auto p-4 md:p-8" id="app">
<h1 class="text-3xl font-bold mb-2 text-center text-gray-800">文字合成语音/SRT字幕转语音</h1>
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
<div class="mb-4">
<label for="fileInput" class="block text-gray-700 font-medium mb-2">选择想合成语音的文件 (srt/txt):</label>
<input type="file" id="fileInput" @change="handleFileSelect" accept=".srt,.txt" class="w-full border border-gray-300 px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="mb-4">
<label for="textInput" class="block font-medium mb-2 ">输入文本<span class="ms-2 text-xs text-gray-700">行尾加<code>[50]</code>代表添加50毫秒静音,<code>[500]</code>代表加500毫秒静音,中括号内数字代表静音毫秒数</span></label>
<textarea placeholder="选择txt文本或srt字幕上传,或直接在此输入要配音的文字" id="textInput" v-model="text" rows="5" class="w-full border border-gray-300 px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"></textarea>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-white rounded-lg shadow-md p-6">
<label for="language" class="block text-gray-700 font-medium mb-2">文本语言:</label>
<select id="language" v-model="selectedLanguage" class="w-full border border-gray-300 px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option v-for="(roles, language) in language_role" :value="language" :key="language">{{ names[language] }}</option>
</select>
</div>
<div class="bg-white rounded-lg shadow-md p-6" v-if="selectedLanguage">
<div class="flex">
<label for="voice" class="block text-gray-700 font-medium mb-2">配音角色</label>
(<span id="shiting" class="cursor-pointer" @click="shiting">试听</span>)
</div>
<select id="voice" v-model="selectedVoice" class="w-full border border-gray-300 px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option v-for="voice in voices" :value="voice" :key="voice">{{ voice.split('-').slice(-1)[0] }}</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-2 mt-4">
<div class="bg-white rounded-lg shadow-md p-6">
<label for="speed" class="block text-gray-700 font-medium mb-2">语速 (0.1 -> 5.0):</label>
<div class="flex items-center">
<input type="range" id="speed" v-model.number="speed" min="0.1" max="5.0" step="0.1" class="w-full mr-2">
<span class="text-gray-600">{{ speed }}</span>
</div>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<label for="pitch" class="block text-gray-700 font-medium mb-2">音调 (-100 -> +100):</label>
<div class="flex items-center">
<input type="range" id="pitch" v-model.number="pitch" min="-100" max="100" class="w-full mr-2">
<span class="text-gray-600">{{ pitch }}</span>
</div>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<label for="volume" class="block text-gray-700 font-medium mb-2">音量 (0.1 -> 5):</label>
<div class="flex items-center">
<input type="range" id="volume" v-model.volume="volume" min="0.1" step="0.1" max="5" class="w-full mr-2">
<span class="text-gray-600">{{ volume }}</span>
</div>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<label for="style" class="block text-gray-700 font-medium mb-2">语气(部分不起作用):</label>
<select id="style" v-model="selectedStyle" class="w-full border border-gray-300 px-3 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option v-for="st in styles" :value="st" :key="st">{{ st }}</option>
</select>
</div>
</div>
<div id="audioContainer" class="my-3 text-center bg-white flex justify-center py-3 "></div>
<div class="mt-8 text-center">
<button @click="generateSpeech" :disabled="isGenerating" class="bg-red-500 hover:bg-red-700 text-white font-bold py-3 px-6 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 disabled:opacity-50 disabled:cursor-not-allowed">
{{ isGenerating ? '语音合成中...' : '执行合成语音' }}
</button>
</div>
<table class="min-w-full divide-y divide-gray-200 my-4">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">语气风格</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">风格说明</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>friendly</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达一种愉快、怡人且温暖的语气。 听起来很真诚且满怀关切。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>advertisement_upbeat</code></td>
<td class="px-6 py-4 text-sm text-gray-500">用兴奋和精力充沛的语气推广产品或服务。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>affectionate</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以较高的音调和音量表达温暖而亲切的语气。 说话者处于吸引听众注意力的状态。 说话者的个性往往是讨喜的。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>angry</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达生气和厌恶的语气。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>assistant</code></td>
<td class="px-6 py-4 text-sm text-gray-500">数字助理用的是热情而轻松的语气。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>calm</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以沉着冷静的态度说话。 语气、音调和韵律与其他语音类型相比要统一得多。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>chat</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达轻松随意的语气。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>cheerful</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达积极愉快的语气。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>customerservice</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以友好热情的语气为客户提供支持。</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>depressed</code></td>
<td class="px-6 py-4 text-sm text-gray-500">调低音调和音量来表达忧郁、沮丧的语气。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>disgruntled</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达轻蔑和抱怨的语气。 这种情绪的语音表现出不悦和蔑视。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>documentary-narration</code></td>
<td class="px-6 py-4 text-sm text-gray-500">用一种轻松、感兴趣和信息丰富的风格讲述纪录片,适合纪录片、专家评论和类似内容。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>embarrassed</code></td>
<td class="px-6 py-4 text-sm text-gray-500">在说话者感到不舒适时表达不确定、犹豫的语气。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>empathetic</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达关心和理解。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>envious</code></td>
<td class="px-6 py-4 text-sm text-gray-500">当你渴望别人拥有的东西时,表达一种钦佩的语气。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>excited</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达乐观和充满希望的语气。 似乎发生了一些美好的事情,说话人对此满意。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>fearful</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以较高的音调、较高的音量和较快的语速来表达恐惧、紧张的语气。 说话人处于紧张和不安的状态。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>gentle</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以较低的音调和音量表达温和、礼貌和愉快的语气。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>hopeful</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达一种温暖且渴望的语气。 听起来像是会有好事发生在说话人身上。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>lyrical</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以优美又带感伤的方式表达情感。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>narration-professional</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以专业、客观的语气朗读内容。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>narration-relaxed</code></td>
<td class="px-6 py-4 text-sm text-gray-500">为内容阅读表达一种舒缓而悦耳的语气。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>newscast</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以正式专业的语气叙述新闻。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>newscast-casual</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以通用、随意的语气发布一般新闻。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>newscast-formal</code></td>
<td class="px-6 py-4 text-sm text-gray-500">以正式、自信和权威的语气发布新闻。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>poetry-reading</code></td>
<td class="px-6 py-4 text-sm text-gray-500">在读诗时表达出带情感和节奏的语气。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>sad</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达悲伤语气。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>serious</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达严肃和命令的语气。 说话者的声音通常比较僵硬,节奏也不那么轻松。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>shouting</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达一种听起来好像声音在远处或在另一个地方的语气,努力让别人听清楚。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>sports_commentary</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达一种既轻松又感兴趣的语气,用于播报体育赛事。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>sports_commentary_excited</code></td>
<td class="px-6 py-4 text-sm text-gray-500">用快速且充满活力的语气播报体育赛事精彩瞬间。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>whispering</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达一种柔和的语气,试图发出安静而柔和的声音。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>terrified</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达一种害怕的语气,语速快且声音颤抖。 听起来说话人处于不稳定的疯狂状态。
</td>
</tr>
<tr>
<td class="px-6 py-4 whitespace-nowrap"><code>unfriendly</code></td>
<td class="px-6 py-4 text-sm text-gray-500">表达一种冷淡无情的语气。</td>
</tr>
</tbody>
</table>
<footer class="bg-gray-100 mt-6 mb-2 py-4 mt-8">
<div class="container mx-auto text-center text-gray-500 text-sm">
版权所有 © 2023 pyvideotrans.com
</div>
<div class="container mx-auto text-center text-gray-500 text-sm"><a href="https://pyvideotrans.com">完整版功能支持语音识别、语音合成、字幕翻译、视频翻译,免费下载 https://pyvideotrans.com</a></div>
</footer>
</div>
<script>
const app = Vue.createApp({
data() {
return {
text: '',
styles:[
'friendly',
"advertisement_upbeat",
"affectionate",
"angry",
"assistant",
"calm",
"chat",
"cheerful",
"customerservice",
"depressed",
"disgruntled",
"documentary-narration",
"embarrassed",
"empathetic",
"envious",
"excited",
"fearful",
"gentle",
"hopeful",
"lyrical",
"narration-professional",
"narration-relaxed",
"newscast",
"newscast-casual",
"newscast-formal",
"poetry-reading",
"sad",
"serious",
"shouting",
"sports_commentary",
"sports_commentary_excited",
"whispering",
"terrified",
"unfriendly"
],
names:{
"vi":"越南语",
"uk":"乌克兰语",
"tr":"土耳其语",
"th":"泰国语",
"sv":"瑞典语",
"es":"西班牙语",
"ru":"俄罗斯语",
"pt":"葡萄牙语",
"pl":"波兰语",
"ms":"马来西亚语",
"ko":"韩语",
"kk":"哈萨克斯坦",
"ja":"日语",
"it":"意大利语",
"id":"印尼语",
"hu":"匈牙利语",
"hi":"印地语",
"he":"希伯来语",
"zh":"中文",
"de":"德语",
"fr":"法语",
"en":"英语",
"nl":"荷兰语",
"cs":"捷克语",
"bn":"孟加拉语",
"ar":"阿拉伯语",
},
welcome:{
"zh": "你好啊,我亲爱的朋友,希望你的每一天都是美好愉快的!",
"en": "Hello, my dear friend. I hope your every day is beautiful and enjoyable!",
"fr": "Bonjour mon cher ami. J'espère que votre quotidien est beau et agréable !",
"de": "Hallo mein lieber Freund. Ich hoffe, dass Ihr Tag schön und angenehm ist!",
"ja": "こんにちは私の親愛なる友人。 あなたの毎日が美しく楽しいものでありますように!",
"ko": "안녕, 내 사랑하는 친구. 당신의 매일이 아름답고 즐겁기를 바랍니다!",
"ru": "Привет, мой дорогой друг. Желаю, чтобы каждый твой день был прекрасен и приятен!",
"es": "Hola mi querido amigo. ¡Espero que cada día sea hermoso y agradable!",
"th": "สวัสดีเพื่อนรัก. ฉันหวังว่าทุกวันของคุณจะสวยงามและสนุกสนาน!",
"it": "Ciao caro amico mio. Spero che ogni tuo giorno sia bello e divertente!",
"pt": "Olá meu querido amigo. Espero que todos os seus dias sejam lindos e agradáveis!",
"vi": "Xin chào người bạn thân yêu của tôi. Tôi hy vọng mỗi ngày của bạn đều đẹp và thú vị!",
"ar": "مرحبا صديقي العزيز. أتمنى أن يكون كل يوم جميلاً وممتعًا!",
"tr": "Merhaba sevgili arkadaşım. Umarım her gününüz güzel ve keyifli geçer!",
"hi": "नमस्ते मेरे प्यारे दोस्त। मुझे आशा है कि आपका हर दिन सुंदर और आनंददायक हो!!",
"hu": "Helló kedves barátom. Remélem minden napod szép és kellemes!",
"uk": "Привіт, мій дорогий друже, сподіваюся, ти щодня прекрасна!",
"id": "Halo, temanku, semoga kamu cantik setiap hari!",
"ms": "Helo, sahabat saya, saya harap anda cantik setiap hari!",
"kk": "Сәлеметсіз бе, менің қымбатты досым, сендер күн сайын әдемісің деп үміттенемін!",
"cs": "Ahoj, můj drahý příteli, doufám, že jsi každý den krásná!",
"pl": "Witam, mój drogi przyjacielu, mam nadzieję, że jesteś piękna każdego dnia!",
"nl": "Hallo mijn lieve vriend, ik hoop dat elke dag goed en fijn voor je is!!",
"sv": "Hej min kära vän, jag hoppas att varje dag är en bra och trevlig dag för dig!",
},
language_role: { "zh": [
"zh-HK-HiuGaaiNeural",
"zh-HK-HiuMaanNeural",
"zh-HK-WanLungNeural",
"zh-CN-XiaoxiaoNeural",
"zh-CN-XiaoyiNeural",
"zh-CN-YunjianNeural",
"zh-CN-YunxiNeural",
"zh-CN-YunxiaNeural",
"zh-CN-YunyangNeural",
"zh-CN-liaoning-XiaobeiNeural",
"zh-TW-HsiaoChenNeural",
"zh-TW-YunJheNeural",
"zh-TW-HsiaoYuNeural",
"zh-CN-shaanxi-XiaoniNeural"
],
"en": [
"en-AU-NatashaNeural",
"en-AU-WilliamNeural",
"en-CA-ClaraNeural",
"en-CA-LiamNeural",
"en-HK-SamNeural",
"en-HK-YanNeural",
"en-IN-NeerjaExpressiveNeural",
"en-IN-NeerjaNeural",
"en-IN-PrabhatNeural",
"en-IE-ConnorNeural",
"en-IE-EmilyNeural",
"en-KE-AsiliaNeural",
"en-KE-ChilembaNeural",
"en-NZ-MitchellNeural",
"en-NZ-MollyNeural",
"en-NG-AbeoNeural",
"en-NG-EzinneNeural",
"en-PH-JamesNeural",
"en-PH-RosaNeural",
"en-SG-LunaNeural",
"en-SG-WayneNeural",
"en-ZA-LeahNeural",
"en-ZA-LukeNeural",
"en-TZ-ElimuNeural",
"en-TZ-ImaniNeural",
"en-GB-LibbyNeural",
"en-GB-MaisieNeural",
"en-GB-RyanNeural",
"en-GB-SoniaNeural",
"en-GB-ThomasNeural",
"en-US-AvaMultilingualNeural",
"en-US-AndrewMultilingualNeural",
"en-US-EmmaMultilingualNeural",
"en-US-BrianMultilingualNeural",
"en-US-AvaNeural",
"en-US-AndrewNeural",
"en-US-EmmaNeural",
"en-US-BrianNeural",
"en-US-AnaNeural",
"en-US-AriaNeural",
"en-US-ChristopherNeural",
"en-US-EricNeural",
"en-US-GuyNeural",
"en-US-JennyNeural",
"en-US-MichelleNeural",
"en-US-RogerNeural",
"en-US-SteffanNeural"
],
"ja": [
"ja-JP-KeitaNeural",
"ja-JP-NanamiNeural"
],
"ko": [
"ko-KR-HyunsuNeural",
"ko-KR-InJoonNeural",
"ko-KR-SunHiNeural"
],
"fr": [
"fr-BE-CharlineNeural",
"fr-BE-GerardNeural",
"fr-CA-ThierryNeural",
"fr-CA-AntoineNeural",
"fr-CA-JeanNeural",
"fr-CA-SylvieNeural",
"fr-FR-VivienneMultilingualNeural",
"fr-FR-RemyMultilingualNeural",
"fr-FR-DeniseNeural",
"fr-FR-EloiseNeural",
"fr-FR-HenriNeural",
"fr-CH-ArianeNeural",
"fr-CH-FabriceNeural"
],
"de": [
"de-AT-IngridNeural",
"de-AT-JonasNeural",
"de-DE-SeraphinaMultilingualNeural",
"de-DE-FlorianMultilingualNeural",
"de-DE-AmalaNeural",
"de-DE-ConradNeural",
"de-DE-KatjaNeural",
"de-DE-KillianNeural",
"de-CH-JanNeural",
"de-CH-LeniNeural"
],
"ru": [
"ru-RU-DmitryNeural",
"ru-RU-SvetlanaNeural"
],
"es": [
"es-AR-ElenaNeural",
"es-AR-TomasNeural",
"es-BO-MarceloNeural",
"es-BO-SofiaNeural",
"es-CL-CatalinaNeural",
"es-CL-LorenzoNeural",
"es-ES-XimenaNeural",
"es-CO-GonzaloNeural",
"es-CO-SalomeNeural",
"es-CR-JuanNeural",
"es-CR-MariaNeural",
"es-CU-BelkysNeural",
"es-CU-ManuelNeural",
"es-DO-EmilioNeural",
"es-DO-RamonaNeural",
"es-EC-AndreaNeural",
"es-EC-LuisNeural",
"es-SV-LorenaNeural",
"es-SV-RodrigoNeural",
"es-GQ-JavierNeural",
"es-GQ-TeresaNeural",
"es-GT-AndresNeural",
"es-GT-MartaNeural",
"es-HN-CarlosNeural",
"es-HN-KarlaNeural",
"es-MX-DaliaNeural",
"es-MX-JorgeNeural",
"es-NI-FedericoNeural",
"es-NI-YolandaNeural",
"es-PA-MargaritaNeural",
"es-PA-RobertoNeural",
"es-PY-MarioNeural",
"es-PY-TaniaNeural",
"es-PE-AlexNeural",
"es-PE-CamilaNeural",
"es-PR-KarinaNeural",
"es-PR-VictorNeural",
"es-ES-AlvaroNeural",
"es-ES-ElviraNeural",
"es-US-AlonsoNeural",
"es-US-PalomaNeural",
"es-UY-MateoNeural",
"es-UY-ValentinaNeural",
"es-VE-PaolaNeural",
"es-VE-SebastianNeural"
],
"vi": [
"vi-VN-HoaiMyNeural",
"vi-VN-NamMinhNeural"
],"th": [
"th-TH-NiwatNeural",
"th-TH-PremwadeeNeural"
],
"ar": [
"ar-DZ-AminaNeural",
"ar-DZ-IsmaelNeural",
"ar-BH-AliNeural",
"ar-BH-LailaNeural",
"ar-EG-SalmaNeural",
"ar-EG-ShakirNeural",
"ar-IQ-BasselNeural",
"ar-IQ-RanaNeural",
"ar-JO-SanaNeural",
"ar-JO-TaimNeural",
"ar-KW-FahedNeural",
"ar-KW-NouraNeural",
"ar-LB-LaylaNeural",
"ar-LB-RamiNeural",
"ar-LY-ImanNeural",
"ar-LY-OmarNeural",
"ar-MA-JamalNeural",
"ar-MA-MounaNeural",
"ar-OM-AbdullahNeural",
"ar-OM-AyshaNeural",
"ar-QA-AmalNeural",
"ar-QA-MoazNeural",
"ar-SA-HamedNeural",
"ar-SA-ZariyahNeural",
"ar-SY-AmanyNeural",
"ar-SY-LaithNeural",
"ar-TN-HediNeural",
"ar-TN-ReemNeural",
"ar-AE-FatimaNeural",
"ar-AE-HamdanNeural",
"ar-YE-MaryamNeural",
"ar-YE-SalehNeural"
],
"bn": [
"bn-BD-NabanitaNeural",
"bn-BD-PradeepNeural",
"bn-IN-BashkarNeural",
"bn-IN-TanishaaNeural"
],
"cs": [
"cs-CZ-AntoninNeural",
"cs-CZ-VlastaNeural"
],
"nl": [
"nl-BE-ArnaudNeural",
"nl-BE-DenaNeural",
"nl-NL-ColetteNeural",
"nl-NL-FennaNeural",
"nl-NL-MaartenNeural"
],
"he": [
"he-IL-AvriNeural",
"he-IL-HilaNeural"
],
"hi": [
"hi-IN-MadhurNeural",
"hi-IN-SwaraNeural"
],
"hu": [
"hu-HU-NoemiNeural",
"hu-HU-TamasNeural"
],
"id": [
"id-ID-ArdiNeural",
"id-ID-GadisNeural"
],
"it": [
"it-IT-GiuseppeNeural",
"it-IT-DiegoNeural",
"it-IT-ElsaNeural",
"it-IT-IsabellaNeural"
],
"kk": [
"kk-KZ-AigulNeural",
"kk-KZ-DauletNeural"
],
"ms": [
"ms-MY-OsmanNeural",
"ms-MY-YasminNeural"
],
"pl": [
"pl-PL-MarekNeural",
"pl-PL-ZofiaNeural"
],
"pt": [
"pt-BR-ThalitaNeural",
"pt-BR-AntonioNeural",
"pt-BR-FranciscaNeural",
"pt-PT-DuarteNeural",
"pt-PT-RaquelNeural"
],
"sv": [
"sv-SE-MattiasNeural",
"sv-SE-SofieNeural"
],
"tr": [
"tr-TR-AhmetNeural",
"tr-TR-EmelNeural"
],
"uk": [
"uk-UA-OstapNeural",
"uk-UA-PolinaNeural"
],
},
selectedLanguage: 'zh',
selectedVoice: '',
selectedStyle:'friendly',
speed: 1.0,
volume: 1.0,
pitch: 0,
isGenerating: false,
apiKey: 'adgas213423235saeg' // Replace with your actual API key
};
},
watch: {
selectedLanguage(newLanguage) {
// This will run whenever selectedLanguage changes
this.$nextTick(() => {
if (this.voices.length > 0) {
this.selectedVoice = this.voices[0];
} else {
// Handle the case where the new language has no voices
this.selectedVoice = null;
console.warn(`No voices available for language: ${newLanguage}`);
}
});
}
},
mounted() {
// Ensure language_role is loaded before setting default voice
this.$nextTick(() => {
this.selectedVoice = this.voices[0];
});
},
computed: {
voices() {
return this.language_role[this.selectedLanguage] || [];
}
},
methods: {
handleFileSelect(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
this.text = e.target.result;
};
reader.readAsText(file);
}
},
shiting(){
if(!this.selectedVoice || this.selectedVoice=='No'){
return alert('必须选择音色');
}
this.generateSpeech(this.welcome[this.selectedLanguage])
},
parseText() {
console.log('aaaaa')
let lines = this.text.trim().split('\n');
console.log(lines)
let filteredLines = [];
for (let i = 0; i < lines.length; i++) {
let line = lines[i].trim();
const isSequenceNumber = /^\s*?\d+\s*?$/;
const isTimecode = /^\d{1,2}:\d{1,2}:\d{1,2}(,\d{1,3})?\s*?\-\->\s*?\d{1,2}:\d{1,2}:\d{1,2}(,\d{1,3})\s*?$/;
if (isSequenceNumber.test(line)) {
// Check if the next line is a timecode
if (i + 1 < lines.length && isTimecode.test(lines[i + 1].trim())) {
continue; // Skip this line (sequence number) if the next line is a timecode
}
} else if (isTimecode.test(line)) {
continue; // Skip this line if it's a timecode
}
if (line !== '') { // Add the line only if it's not empty after trimming
filteredLines.push(line);
}
}
return filteredLines.join('\n');
},
async generateSpeech(listener_text) {
console.log(listener_text)
const workerUrl = 'https://ttsapi.pyvideotrans.com/v1/audio/speech';
const parsedText = typeof listener_text==='string'?listener_text:this.parseText();
if(!this.selectedVoice || this.selectedVoice=='No'){
return alert('必须选择音色');
}
if(!parsedText){
return alert('必须上传或输入要合成的文字');
}
console.log(parsedText)
const requestBody = {
"model": "tts-1",
"input": parsedText,
"voice": this.selectedVoice,
"response_format": "mp3",
"speed": this.speed,
"volume": this.volume,
"pitch": this.pitch,
"style":this.selectedStyle
};
console.log(requestBody);
//return
this.isGenerating = true;
try {
const response = await fetch(workerUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status} ${response.statusText}`);
}
const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
// Remove existing audio element and download button if any
const existingAudio = document.getElementById('audioElement');
if (existingAudio) {
existingAudio.remove();
}
const existingDownloadButton = document.getElementById('downloadButton');
if (existingDownloadButton) {
existingDownloadButton.remove();
}
const audioElement = document.createElement('audio');
audioElement.id = 'audioElement';
audioElement.src = audioUrl;
audioElement.controls = true;
audioElement.autoplay = true;
// Create download button
const downloadButton = document.createElement('button');
downloadButton.id = 'downloadButton';
downloadButton.textContent = '下载';
downloadButton.className = 'ml-2 font-bold py-2 px-4 rounded'; // Add some basic styling
// Add download functionality
downloadButton.addEventListener('click', () => {
const link = document.createElement('a');
link.href = audioUrl;
link.download = getmp3name(); // Set default filename
document.body.appendChild(link); // Required for Firefox
link.click();
document.body.removeChild(link); // Clean up
});
if(typeof listener_text !=='string'){
const audioContainer = document.getElementById('audioContainer');
audioContainer.appendChild(audioElement);
audioContainer.appendChild(downloadButton); // Append button to the same container
}
} catch (error) {
console.error('Error generating speech:', error);
alert("Error generating speech: " + error.message);
} finally {
this.isGenerating = false;
}
}
}
}).mount('#app');
function getmp3name() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day}-${hours}-${minutes}-${seconds}.mp3`;
}
</script>
<script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
<script>LA.init({id:"KO5lYldwVUTyC8eE",ck:"KO5lYldwVUTyC8eE"})</script>
</body>
</html>