1
1
// script.js
2
2
// 获取 WebSocket URL
3
- const ws = new WebSocket ( WEBSOCKET_URL ) ;
4
-
5
- ws . onopen = ( ) => {
6
- console . log ( 'WebSocket 连接已建立' ) ;
7
- // 在聊天窗口添加提示信息
8
- appendSystemMessage ( '提示:您可以长按麦克风按钮 & 长按 键盘 V 进行录音' ) ;
9
- } ;
10
-
11
- ws . onmessage = function ( event ) {
12
- if ( typeof event . data === 'string' ) {
13
- const response = JSON . parse ( event . data ) ;
14
- console . log ( '收到 WebSocket 响应:' , response ) ;
15
-
16
- if ( response . type ) {
17
- // 处理带有 type 字段的消息(语音识别等)
18
- switch ( response . type ) {
19
- case 'audio_stored' :
20
- appendAudioMessage ( '你' , response . audio_url ) ;
21
- break ;
22
-
23
- case 'recognition_complete' :
24
- appendMessage ( '你' , response . text ) ;
25
- break ;
26
-
27
- case 'recognition_error' :
28
- appendSystemMessage ( `识别错误: ${ response . error } ` ) ;
29
- break ;
30
-
31
- case 'tts_complete' :
32
- // 移除"正在生成语音..."的系统消息
33
- const systemMessages = document . querySelectorAll ( '.message.system' ) ;
34
- systemMessages . forEach ( msg => {
35
- if ( msg . textContent === '正在生成语音...' ) {
36
- msg . remove ( ) ;
3
+ let ws ;
4
+
5
+ // 创建新的 WebSocket 连接的函数
6
+ function createWebSocket ( ) {
7
+ ws = new WebSocket ( WEBSOCKET_URL ) ;
8
+
9
+ ws . onopen = ( ) => {
10
+ console . log ( 'WebSocket 连接已建立' ) ;
11
+ appendSystemMessage ( '提示:您可以长按麦克风按钮 & 长按 键盘 V 进行录音' ) ;
12
+ } ;
13
+
14
+ ws . onmessage = function ( event ) {
15
+ if ( typeof event . data === 'string' ) {
16
+ const response = JSON . parse ( event . data ) ;
17
+ console . log ( '收到 WebSocket 响应:' , response ) ;
18
+
19
+ if ( response . type ) {
20
+ // 处理带有 type 字段的消息(语音识别等)
21
+ switch ( response . type ) {
22
+ case 'audio_stored' :
23
+ appendAudioMessage ( '你' , response . audio_url ) ;
24
+ break ;
25
+
26
+ case 'recognition_complete' :
27
+ appendMessage ( '你' , response . text ) ;
28
+ if ( isOneShotMode ) {
29
+ appendSystemMessage ( '单次模式:识别完成,连接将关闭' ) ;
30
+ // 给用户一点时间看到结果
31
+ setTimeout ( ( ) => {
32
+ ws . close ( ) ;
33
+ } , 2000 ) ;
37
34
}
38
- } ) ;
39
-
40
- // 添加 AI 的文本和音频消息
41
- appendMessage ( 'AI' , response . text ) ;
42
- appendAudioMessage ( 'AI' , response . audio_url ) ;
43
- break ;
44
-
45
- default :
46
- console . log ( 'Unknown message type:' , response . type ) ;
35
+ break ;
36
+
37
+ case 'recognition_error' :
38
+ appendSystemMessage ( `识别错误: ${ response . error } ` ) ;
39
+ break ;
40
+
41
+ case 'tts_complete' :
42
+ // 移除"正在生成语音..."的系统消息
43
+ const systemMessages = document . querySelectorAll ( '.message.system' ) ;
44
+ systemMessages . forEach ( msg => {
45
+ if ( msg . textContent === '正在生成语音...' ) {
46
+ msg . remove ( ) ;
47
+ }
48
+ } ) ;
49
+
50
+ // 添加 AI 的文本和音频消息
51
+ appendMessage ( 'AI' , response . text ) ;
52
+ appendAudioMessage ( 'AI' , response . audio_url ) ;
53
+ if ( isOneShotMode ) {
54
+ appendSystemMessage ( '单次模式:语音合成完成,连接将关闭' ) ;
55
+ setTimeout ( ( ) => {
56
+ ws . close ( ) ;
57
+ } , 2000 ) ;
58
+ }
59
+ break ;
60
+
61
+ default :
62
+ console . log ( 'Unknown message type:' , response . type ) ;
63
+ }
47
64
}
48
65
}
49
- }
50
- } ;
51
-
52
- ws . onerror = ( error ) => {
53
- console . error ( 'WebSocket 错误:' , error ) ;
54
- } ;
55
-
56
- // 添加重连逻辑
57
- let reconnectAttempts = 0 ;
58
- const maxReconnectAttempts = 5 ;
59
-
60
- ws . onclose = ( event ) => {
61
- console . log ( 'WebSocket connection closed:' , event ) ;
66
+ } ;
67
+
68
+ ws . onerror = ( error ) => {
69
+ console . error ( 'WebSocket 错误:' , error ) ;
70
+ } ;
71
+
72
+ let reconnectAttempts = 0 ;
73
+ const maxReconnectAttempts = 5 ;
62
74
63
- if ( reconnectAttempts < maxReconnectAttempts ) {
64
- reconnectAttempts ++ ;
65
- const timeout = Math . min ( 1000 * Math . pow ( 2 , reconnectAttempts ) , 10000 ) ;
75
+ ws . onclose = ( event ) => {
76
+ console . log ( 'WebSocket connection closed:' , event ) ;
66
77
67
- appendSystemMessage ( `连接已断开,${ timeout / 1000 } 秒后尝试重新连接...` ) ;
78
+ if ( isOneShotMode ) {
79
+ appendSystemMessage ( '单次模式:连接已关闭' ) ;
80
+ return ;
81
+ }
68
82
69
- setTimeout ( ( ) => {
70
- ws = new WebSocket ( WEBSOCKET_URL ) ;
71
- // 重新绑定事件处理器
72
- } , timeout ) ;
73
- } else {
74
- appendSystemMessage ( '连接已断开,请刷新页面重试' ) ;
75
- }
76
- } ;
83
+ if ( reconnectAttempts < maxReconnectAttempts ) {
84
+ reconnectAttempts ++ ;
85
+ const timeout = Math . min ( 1000 * Math . pow ( 2 , reconnectAttempts ) , 10000 ) ;
86
+
87
+ appendSystemMessage ( `连接已断开,${ timeout / 1000 } 秒后尝试重新连接...` ) ;
88
+
89
+ setTimeout ( ( ) => {
90
+ ws = new WebSocket ( WEBSOCKET_URL ) ;
91
+ // 重新绑定事件处理器
92
+ } , timeout ) ;
93
+ } else {
94
+ appendSystemMessage ( '连接已断开,请刷新页面重试' ) ;
95
+ }
96
+ } ;
97
+ }
98
+
99
+ // 初始化第一个 WebSocket 连接
100
+ createWebSocket ( ) ;
77
101
78
102
const chatWindow = document . getElementById ( 'chat-window' ) ;
79
103
const textInput = document . getElementById ( 'text-input' ) ;
@@ -109,16 +133,47 @@ function generateSessionId() {
109
133
110
134
let currentSessionId = null ;
111
135
136
+ // 添加模式切换相关变量和函数
137
+ let isOneShotMode = false ;
138
+ const modeToggle = document . getElementById ( 'mode-toggle' ) ;
139
+ const modeLabel = document . getElementById ( 'mode-label' ) ;
140
+
141
+ modeToggle . addEventListener ( 'change' , function ( ) {
142
+ isOneShotMode = this . checked ;
143
+ modeLabel . textContent = isOneShotMode ? '单次模式' : '持续模式' ;
144
+ appendSystemMessage ( `已切换到${ modeLabel . textContent } ` ) ;
145
+ } ) ;
146
+
112
147
function startRecording ( ) {
113
148
if ( isRecording ) return ;
114
149
115
- // 生成新的会话 ID
150
+ // 在单次模式下,强制创建新的 WebSocket 连接
151
+ if ( isOneShotMode ) {
152
+ createWebSocket ( ) ;
153
+ ws . onopen = ( ) => {
154
+ startRecordingProcess ( ) ;
155
+ } ;
156
+ return ;
157
+ }
158
+
159
+ // 非单次模式下的原有逻辑
160
+ if ( ! ws || ws . readyState === WebSocket . CLOSED ) {
161
+ createWebSocket ( ) ;
162
+ ws . onopen = ( ) => {
163
+ startRecordingProcess ( ) ;
164
+ } ;
165
+ } else {
166
+ startRecordingProcess ( ) ;
167
+ }
168
+ }
169
+
170
+ function startRecordingProcess ( ) {
116
171
currentSessionId = generateSessionId ( ) ;
117
172
118
- // 发送开始信号
119
173
ws . send ( JSON . stringify ( {
120
174
type : "audio_start" ,
121
- session_id : currentSessionId
175
+ session_id : currentSessionId ,
176
+ one_shot : isOneShotMode
122
177
} ) ) ;
123
178
124
179
navigator . mediaDevices . getUserMedia ( { audio : true } )
@@ -134,7 +189,6 @@ function startRecording() {
134
189
135
190
mediaRecorder . ondataavailable = e => {
136
191
if ( e . data && e . data . size > 0 ) {
137
- // 直接发送二进制数据
138
192
ws . send ( e . data ) ;
139
193
}
140
194
} ;
@@ -150,9 +204,15 @@ function startRecording() {
150
204
// 发送结束信号
151
205
ws . send ( JSON . stringify ( {
152
206
type : "audio_end" ,
153
- session_id : currentSessionId
207
+ session_id : currentSessionId ,
208
+ one_shot : isOneShotMode
154
209
} ) ) ;
155
210
211
+ // 如果是单次模式,等待响应后关闭连接
212
+ if ( isOneShotMode ) {
213
+ appendSystemMessage ( '单次模式:等待响应中...' ) ;
214
+ }
215
+
156
216
currentSessionId = null ;
157
217
} ;
158
218
} )
@@ -231,7 +291,7 @@ function sendTextMessage(text) {
231
291
// 显示发送的消息
232
292
appendMessage ( '你' , text ) ;
233
293
234
- // �� 过 WebSocket 发送文字消息
294
+ // 过 WebSocket 发送文字消息
235
295
ws . send ( JSON . stringify ( {
236
296
text : text ,
237
297
require_tts : true
0 commit comments