Skip to content

Commit eed9ec9

Browse files
committed
added SEPIA STT worker module and client beta
1 parent 7db594e commit eed9ec9

File tree

2 files changed

+773
-0
lines changed

2 files changed

+773
-0
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
//SEPIA STT Server Client:
2+
3+
class SepiaSttSocketClient {
4+
5+
constructor(serverUrl, clientId, accessToken, engineOptions, serverOptions){
6+
this.serverUrl = (serverUrl || "http://localhost:20741").replace("\/$", "");
7+
this.socketHost = this.serverUrl.replace(/^(http)/, "ws");
8+
this.clientId = clientId || "any";
9+
this.accessToken = accessToken || "test1234";
10+
11+
if (!engineOptions) engineOptions = {};
12+
if (!serverOptions) serverOptions = {};
13+
14+
this.serverVersion = "";
15+
this.asrEngine = "";
16+
17+
this.availableLanguages = [];
18+
this.availableModels = [];
19+
this.availableFeatures = [];
20+
this.activeOptions = Object.assign({
21+
samplerate: 16000,
22+
continuous: false,
23+
language: "",
24+
model: ""
25+
}, engineOptions);
26+
27+
this.activeLanguageCode = this.activeOptions.language || "";
28+
this.activeAsrModel = this.activeOptions.model || "";
29+
this.phrases = this.activeOptions.phrases || [];
30+
31+
this._msgId = 0;
32+
33+
this.websocket = undefined;
34+
35+
this.autoCloseOnLastFinal = true; //applies to non-continuous setup only
36+
37+
this.connectionIsOpen = false;
38+
this.isReadyForStream = false;
39+
40+
this._onOpen = serverOptions.onOpen || function(){};
41+
this._onReady = serverOptions.onReady || function(activeOptions){};
42+
this._onClose = serverOptions.onClose || function(){};
43+
this._onResult = serverOptions.onResult || function(res){};
44+
this._onError = serverOptions.onError || function(err){
45+
console.error("SepiaSttSocketClient ERROR", err);
46+
};
47+
48+
this._skipAutoWelcome = serverOptions.skipAutoWelcome;
49+
this._doDebug = serverOptions.doDebug;
50+
}
51+
52+
log(msg){
53+
if (this._doDebug){
54+
console.log("SepiaSttSocketClient", msg);
55+
}
56+
}
57+
58+
//--- HTTP Interface ---
59+
60+
pingServer(successCallback, errorCallback){
61+
fetch(this.serverUrl + "/ping")
62+
.then(function(res){ return res.json(); })
63+
.then(function(json){
64+
if (successCallback) successCallback(json);
65+
})
66+
.catch(function(err){
67+
if (errorCallback) errorCallback(err);
68+
});
69+
}
70+
71+
loadServerInfo(successCallback, errorCallback){
72+
fetch(this.serverUrl + "/settings", {
73+
method: "GET"
74+
}).then(function(res){
75+
if (res.ok){
76+
return res.json();
77+
}else{
78+
if (errorCallback) errorCallback({
79+
"name": "FetchError", "message": res.statusText, "code": res.status
80+
});
81+
}
82+
}).then(function(json){
83+
if (json && json.settings){
84+
this._handleServerSettings(json.settings);
85+
if (successCallback) successCallback(json.settings);
86+
}else{
87+
if (successCallback) successCallback({});
88+
}
89+
}).catch(function(err){
90+
if (errorCallback) errorCallback(err);
91+
});
92+
}
93+
94+
_handleServerSettings(settings){
95+
this.serverVersion = settings.version;
96+
this.asrEngine = settings.engine || "";
97+
this.availableLanguages = settings.languages || [];
98+
this.availableModels = settings.models || [];
99+
this.availableFeatures = settings.features || [];
100+
}
101+
102+
//--- WebSocket interface ---
103+
104+
newMessageId(){
105+
var msgId = ++this._msgId;
106+
if (msgId > 999999){
107+
msgId = 1;
108+
}
109+
return msgId;
110+
}
111+
112+
openConnection(){
113+
if (this.websocket && this.websocket.readyState == this.websocket.OPEN){
114+
this._onError({name: "SocketConnectionError", message: "Connection was already OPEN"});
115+
return false;
116+
}
117+
var self = this;
118+
119+
//CREATE
120+
this.websocket = new WebSocket(this.socketHost);
121+
122+
//ONOPEN
123+
this.websocket.onopen = function(){
124+
self.log("Connection OPEN");
125+
self.connectionIsOpen = true;
126+
self._onOpen();
127+
//send welcome
128+
if (!self._skipAutoWelcome){
129+
self.sendWelcome();
130+
}
131+
}
132+
//ONCLOSE
133+
this.websocket.onclose = function(){
134+
self.log("Connection CLOSED");
135+
self.connectionIsOpen = false;
136+
self.isReadyForStream = false;
137+
self._onClose();
138+
}
139+
//ONMESSAGE
140+
this.websocket.onmessage = function(event){
141+
if (event && event.data && typeof event.data == "string"){
142+
self.log("Connection MESSAGE: " + event.data); //DEBUG
143+
try {
144+
self.handleSocketMessage(JSON.parse(event.data));
145+
}catch(err){
146+
console.error("SepiaSttSocketClient - MessageParserError", err);
147+
self.handleSocketError({name: "SocketMessageParserError", message: "Message handler saw invlaid JSON?!"});
148+
}
149+
}
150+
}
151+
//ONERROR
152+
this.websocket.onerror = function(error){
153+
self.handleSocketError(error);
154+
}
155+
156+
return true;
157+
}
158+
159+
closeConnection(){
160+
if (this.websocket && this.websocket.readyState == this.websocket.OPEN){
161+
this.websocket.close();
162+
return true;
163+
}else{
164+
//fail silently?
165+
return false;
166+
}
167+
}
168+
169+
handleSocketMessage(msgJson){
170+
if (msgJson.type == "error"){
171+
this.handleSocketError(msgJson);
172+
}else{
173+
//console.log("handleSocketMessage", JSON.stringify(msgJson)); //DEBUG
174+
175+
if (msgJson.type == "ping"){
176+
//TODO: send only after welcome
177+
this.sendJson({type: "pong", msg_id: msgJson.msg_id});
178+
179+
}else if (msgJson.type == "welcome"){
180+
this.log("Connection WELCOME");
181+
//read active session/engine options
182+
this.activeOptions = msgJson.info? msgJson.info.options : {};
183+
this.activeLanguageCode = this.activeOptions.language || "";
184+
this.activeAsrModel = this.activeOptions.model || "";
185+
this.isReadyForStream = true;
186+
this._onReady(this.activeOptions);
187+
188+
}else if (msgJson.type == "result"){
189+
this._onResult(msgJson);
190+
if (msgJson.isFinal && !this.activeAsrModel.continuous && this.autoCloseOnLastFinal){
191+
//after final result, close connection
192+
this.closeConnection();
193+
}
194+
}else if (msgJson.type == "response"){
195+
//anything?
196+
}
197+
}
198+
}
199+
200+
handleSocketError(err){
201+
var error = {};
202+
if (!err) error = {name: "SocketMessageError", message: "unknown"};
203+
else if (typeof err == "string") error = {name: "SocketMessageError", message: err};
204+
else error = {name: (err.name || "SocketMessageError"), message: (err.message || "unknown")};
205+
this._onError(error);
206+
//Errors are not acceptable :-p - close in any case
207+
this.closeConnection();
208+
}
209+
210+
sendJson(json){
211+
if (this.websocket && this.websocket.readyState == this.websocket.OPEN){
212+
this.websocket.send(JSON.stringify(json));
213+
return true;
214+
}else{
215+
this._onError({name: "SocketConnectionError", message: "Connection is closed. Cannot send message."});
216+
//throw error?
217+
return false;
218+
}
219+
}
220+
sendBytes(bufferChunk){
221+
if (!this.websocket || this.websocket.readyState != this.websocket.OPEN){
222+
this._onError({name: "SocketConnectionError", message: "Connection is closed. Cannot send message."});
223+
return false;
224+
}
225+
this.websocket.send(bufferChunk); //this can be a typed array (recommended), view or blob (I guess)
226+
return true;
227+
}
228+
sendMessage(text){
229+
return this.sendJson({
230+
"type": "chat",
231+
"data": {"text": text},
232+
"msg_id": this.newMessageId()
233+
});
234+
}
235+
sendWelcome(options){
236+
var optionsToSend = options || this.activeOptions;
237+
return this.sendJson({
238+
"type": "welcome",
239+
"data": optionsToSend,
240+
"client_id": this.clientId,
241+
"access_token": this.accessToken,
242+
"msg_id": this.newMessageId()
243+
});
244+
}
245+
sendAudioEnd(byteLength){
246+
return this.sendJson({
247+
"type": "audioend",
248+
"data": {"byteLength": byteLength},
249+
"msg_id": this.newMessageId()
250+
});
251+
}
252+
}
253+

0 commit comments

Comments
 (0)