1// ***************************************************************************************************** 2// ******* speak2web UNIVERSAL VOICE SEARCH *********** 3// ******* Get your subscription at *********** 4// ******* https://speak2web.com/plugin#plans *********** 5// ******* Need support? https://speak2web.com/support *********** 6// ******* Licensed GPLv2+ *********** 7//****************************************************************************************************** 8 9// Cross browser 'trim()' funtion support 10if (typeof String.prototype.trim !== 'function') { String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g, ''); } } 11 12var respTimeOut = false; 13var errcnt = 0; 14let uvsHostName = typeof (vs.uvsCurrentHostName) != 'undefined' ? vs.uvsCurrentHostName : null; 15 16/** 17 * An audio player handler Object 18 * 19 */ 20var uvsAudioPlayer = { 21 'htmlAudioElement': document.createElement('AUDIO'), 22 'lastFilePath': null, 23 'antiMuteButtonPlaybacks': [uvsSilenceSoundPath], 24 'isAntiMutePlayback': false, 25 'configure': function (filePath = null, playbackEndedCallback = null) { 26 try { 27 let pathOfFile = typeof filePath != 'undefined' && filePath ? filePath : null; 28 29 if (pathOfFile) { 30 let partialFilePath = !!uvsIsMuteSimon ? uvsSilenceSoundPath : pathOfFile; 31 this.htmlAudioElement.src = vs._uvsPath + partialFilePath; 32 this.htmlAudioElement.preload = 'auto'; 33 this.lastFilePath = pathOfFile; 34 35 if (this.antiMuteButtonPlaybacks.indexOf(pathOfFile) !== -1) { 36 this.isAntiMutePlayback = true; 37 } else { 38 this.isAntiMutePlayback = false; 39 } 40 } else { 41 this.htmlAudioElement.src = ''; 42 this.isAntiMutePlayback = false; 43 } 44 45 /** 46 * The play event occurs when the audio has been started or is no longer paused. 47 */ 48 this.htmlAudioElement.onplay = function () { 49 // Do nothing for now 50 }.bind(this); 51 52 /** 53 * The ended event occurs when the audio has reached the end. 54 */ 55 this.htmlAudioElement.onended = function () { 56 this.htmlAudioElement.src = '' 57 this.isAntiMutePlayback = false; 58 59 // Callback to be executed when video playback ends 60 if (pathOfFile && (typeof playbackEndedCallback === "function")) { 61 playbackEndedCallback(); 62 playbackEndedCallback = null; 63 } 64 }.bind(this); 65 66 /** 67 * The error event occurs when an error occurred during the loading of an audio 68 */ 69 this.htmlAudioElement.onerror = function () { 70 this.isAntiMutePlayback = false; 71 }.bind(this); 72 73 /** 74 * The playing event occurs when the audio is playing after having been paused or stopped for buffering. 75 */ 76 this.htmlAudioElement.onplaying = function () { 77 // Do nothing for now 78 }.bind(this); 79 } catch (err) { 80 this.clear(); 81 this.isAntiMutePlayback = false; 82 } 83 }, 84 'play': function () { 85 try { 86 if (this.htmlAudioElement && !!this.htmlAudioElement.src) { 87 this.htmlAudioElement.play().catch(function (error) { 88 console.log('VDN Exception: Failed to play audio.'); 89 }); 90 } 91 } catch (err) { 92 this.clear(); 93 } 94 }, 95 'stop': function () { 96 try { 97 this.clear(); 98 } catch (err) { 99 this.clear(); 100 } 101 }, 102 'clear': function () { 103 try { 104 if (this.htmlAudioElement) { 105 let duration = isNaN(this.htmlAudioElement.duration) ? 0 : this.htmlAudioElement.duration; 106 this.htmlAudioElement.currentTime = duration; 107 } 108 109 this.lastFilePath = null; 110 } catch (err) { 111 this.lastFilePath = null; 112 this.isAntiMutePlayback = false;
113 } 114 }, 115 'isPlaying': function () { 116 let currentTime = isNaN(this.htmlAudioElement.currentTime) ? 0 : this.htmlAudioElement.currentTime; 117 let duration = isNaN(this.htmlAudioElement.duration) ? 0 : this.htmlAudioElement.duration; 118 119 return currentTime < duration; 120 } 121}; 122 123function stt(blob, errorRecovery, cb) { 124 if (errorRecovery == false) { 125 let i = Math.floor(Math.random() * 10); 126 let resp = uvsAlternativeResponse['randomLib']; 127 128 if (respTimeOut == false) { 129 // Play 'random' playback 130 uvsAudioPlayer.configure(resp[i]); 131 uvsAudioPlayer.play(); 132 133 respTimeOut = true; 134 135 setTimeout(function () { 136 respTimeOut = false; 137 }, 6000); 138 } 139 } 140 141} 142 143/** 144 * Function to make asynch call to GCP server for STT 145 * 146 * @param String base64AudioStr Base64 String representation of Audio Blob 147 * @retur Promise 148 * 149 */ 150function uvsGcpStt(base64AudioStr) { 151 return new Promise(function (resolve, reject) { 152 if (!( 153 !!uvsSttLanguageContext['gcp']['endPoint'] && 154 !!uvsSttLanguageContext['gcp']['key'] && 155 !!uvsSttLanguageContext['gcp']['langCode'] && 156 typeof base64AudioStr != 'undefined' && 157 !!base64AudioStr 158 )) { 159 reject(null); 160 return; 161 } 162 163 let uvsXhr = new XMLHttpRequest(); 164 165 uvsXhr.onreadystatechange = function () { 166 if (this.readyState == 4) { 167 try { 168 let res = JSON.parse(this.response); 169 170 if (this.status === 200) { 171 errcnt = 0; 172 let results = typeof res != 'undefined' && res instanceof Object && 'results' in res ? res['results'] : []; 173 let efficientResult = !!results && results.length > 0 && results[0] instanceof Object ? results[0] : {}; 174 let alternatives = 'alternatives' in efficientResult && !!efficientResult['alternatives'] ? efficientResult['alternatives'] : []; 175 let alternativeObj = alternatives.length > 0 && alternatives[0] instanceof Object ? alternatives[0] : {}; 176 let transcript = 'transcript' in alternativeObj && !!alternativeObj['transcript'] ? alternativeObj['transcript'] : null; 177 178 if (typeof transcript != 'undefined' && !!transcript) { 179 resolve(transcript); 180 } else { 181 reject(null); 182 } 183 } else { 184 // Handle response errors 185 let error = 'error' in res ? res['error'] : {}; 186 let message = 'message' in error && !!error['message'] ? error['message'].toLowerCase() : ''; 187 188 if (errcnt < 1 && !!message && message.indexOf('api key') !== -1) { 189 errcnt++; 190 191 //$$$$$$$$$$$$$$$ FETCH NEW TOKEN MIGHT HAVE EXPIRED $$$$$$$$$$$$$$$$$$ 192 uvsRefreshVoiceServicesKeys().then(function (result) { 193 uvsSttLanguageContext['gcp']['key'] = result; 194 195 // Try to transcript again with updated key 196 uvsGcpStt().then(function (res) { 197 if (!!res) { 198 resolve(res); 199 } else { 200 errcnt = 0; 201 reject(null); 202 } 203 }).catch(function (err) { 204 errcnt = 0; 205 reject(null); 206 }) 207 }).catch(function (error) { 208 alert(error); 209 errcnt = 0; 210 reject(null); 211 }); 212 } else { 213 errcnt = 0; 214 215 let i = Math.floor(Math.random() * 10); 216 let resp = uvsAlternativeResponse['randomLib']; 217 218 // Play 'random' playback 219 uvsAudioPlayer.configure(resp[i]); 220 uvsAudioPlayer.play(); 221 222 reject(null); 223 } 224 } 225 } catch (err) { 226 reject(null); 227 } 228 } 229 } 230 231 // Handle parsing or transmission errors 232 uvsXhr.onerror = function (error) { reject(null); } 233 234 uvsXhr.open("POST", uvsSttLanguageContext['gcp']['endPoint'] + uvsSttLanguageContext['gcp']['qs']['key'] + uvsSttLanguageContext['gcp']['key'], true); 235 uvsXhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); 236 237 let recognitionConfig = { 238 'config': { 239 'encoding': 'ENCODING_UNSPECIFIED', 240 'languageCode': uvsSttLanguageContext['gcp']['langCode'], 241 'enableWordTimeOffsets': false, 242 }, 243 'audio': { 244 'content': base64AudioStr 245 }, 246 }; 247
248 uvsXhr.send(JSON.stringify(recognitionConfig, null, true)); 249 }) 250} 251 252/** 253 * Function to log STT service call 254 * 255 * @param {uvsUpdateLastValue - int/Number} : 0 to not to update last value or 1 to update last value 256 */ 257function uvsLogServiceCall(uvsUpdateLastValue = 0) { 258 try { 259 let uvsXhr = new XMLHttpRequest(); 260 261 uvsXhr.onreadystatechange = function () { 262 if (this.readyState == 4 && this.status == 200) { 263 let res = JSON.parse(this.responseText); 264 265 // Update localized variables of service log 266 uvsServiceLogs.updatedAt = res.updatedAt || uvsServiceLogs.updatedAt; 267 uvsServiceLogs.currentValue = res.currentValue || uvsServiceLogs.currentValue; 268 uvsServiceLogs.lastValue = res.lastValue || uvsServiceLogs.lastValue; 269 } 270 }; 271 272 uvsXhr.open("POST", uvsAjaxObj.ajax_url, true); 273 uvsXhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 274 uvsXhr.send("action=uvs_log_service_call&_ajax_nonce=" + uvsAjaxObj.nonce + "&updateLastValue=" + uvsUpdateLastValue); 275 } catch (err) { 276 // Do nothing for now 277 } 278} 279 280/** 281 * Function to get current host/domain full URL 282 * 283 */ 284function uvsGetCurrentHostURL() { 285 var currentHostUrl = null; 286 try { 287 if (!(typeof (window.location) != 'undefined' 288 && typeof (window.location.hostname) != 'undefined' 289 && typeof (window.location.protocol) != 'undefined')) { 290 return uvsGetHostName(); 291 } 292 293 var thisProtocol = window.location.protocol; 294 var thisHostname = window.location.hostname; 295 296 currentHostUrl = thisProtocol + '//' + thisHostname; 297 } catch (err) { 298 currentHostUrl = uvsGetHostName(); 299 console.log('Something went wrong while discovering current domain.'); 300 } 301 302 return currentHostUrl; 303} 304 305/** 306 * Function to get current host name from backend. 307 */ 308function uvsGetHostName() { 309 return uvsHostName; 310} 311
312/** 313 * Function to obtain voice services token and keys 314 * 315 */ 316function uvsRefreshVoiceServicesKeys() { 317 return new Promise(function (resolve, reject) { 318 let uvsXhr = new XMLHttpRequest(); 319 320 uvsXhr.onreadystatechange = function () { 321 if (this.readyState == 4) { 322 if (this.status === 200) { 323 let res = JSON.parse(this.response); 324 325 if (typeof res == 'undefined') reject(uvsErrorLibrary['outOfService']); 326 327 if (!!uvsSttLanguageContext['gcp']['stt'] && 'gStt' in res && !!res['gStt']) { 328 resolve(res['gStt']); 329 return; 330 } 331 332 reject(uvsErrorLibrary['outOfService']); 333 } else { 334 // Handle response errors 335 reject(uvsErrorLibrary['outOfService']); 336 } 337 } 338 }; 339 340 let queryString = "?action=uvs_refresh_access_keys&_ajax_nonce=" + uvsAjaxObj.keys_nonce; 341 uvsXhr.open("GET", uvsAjaxObj.ajax_url + queryString, true); 342 343 // Handle parsing or transmission errors 344 uvsXhr.onerror = function (error) { reject(uvsErrorLibrary['outOfService']); } 345 346 uvsXhr.send(null); 347 }); 348} 349
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.