From 1aa38db4e66ed1ea47417ba5e6254c4b3652c8a3 Mon Sep 17 00:00:00 2001 From: adroitwhiz Date: Thu, 14 Nov 2019 08:10:26 -0500 Subject: [PATCH 1/2] replace nets with Fetch API --- package.json | 1 - src/extensions/scratch3_text2speech/index.js | 50 ++++++++++---------- src/extensions/scratch3_translate/index.js | 25 ++++------ src/util/fetch-with-timeout.js | 28 +++++++++++ webpack.config.js | 1 - 5 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 src/util/fetch-with-timeout.js diff --git a/package.json b/package.json index b6df89595..338ff7dc8 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "immutable": "3.8.1", "jszip": "^3.1.5", "minilog": "3.1.0", - "nets": "3.2.0", "scratch-parser": "5.0.0", "scratch-sb1-converter": "0.2.7", "scratch-translate-extension-languages": "0.0.20191118205314", diff --git a/src/extensions/scratch3_text2speech/index.js b/src/extensions/scratch3_text2speech/index.js index 4b26b6b02..f8cc408b4 100644 --- a/src/extensions/scratch3_text2speech/index.js +++ b/src/extensions/scratch3_text2speech/index.js @@ -1,5 +1,4 @@ const formatMessage = require('format-message'); -const nets = require('nets'); const languageNames = require('scratch-translate-extension-languages'); const ArgumentType = require('../../extension-support/argument-type'); @@ -8,6 +7,7 @@ const Cast = require('../../util/cast'); const MathUtil = require('../../util/math-util'); const Clone = require('../../util/clone'); const log = require('../../util/log'); +const fetchWithTimeout = require('../../util/fetch-with-timeout'); /** * Icon svg to be displayed in the blocks category menu, encoded as a data URI. @@ -722,46 +722,46 @@ class Scratch3Text2SpeechBlocks { path += `&text=${encodeURIComponent(words.substring(0, 128))}`; // Perform HTTP request to get audio file - return new Promise(resolve => { - nets({ - url: path, - timeout: SERVER_TIMEOUT - }, (err, res, body) => { - if (err) { - log.warn(err); - return resolve(); - } - - if (res.statusCode !== 200) { - log.warn(res.statusCode); - return resolve(); + return fetchWithTimeout(path, {}, SERVER_TIMEOUT) + .then(res => { + if (res.status !== 200) { + throw new Error(`HTTP ${res.status} error reaching translation service`); } + return res.arrayBuffer(); + }) + .then(buffer => { // Play the sound const sound = { data: { - buffer: body.buffer + buffer: buffer } }; - this.runtime.audioEngine.decodeSoundPlayer(sound).then(soundPlayer => { - this._soundPlayers.set(soundPlayer.id, soundPlayer); + return this.runtime.audioEngine.decodeSoundPlayer(sound); + }) + .then(soundPlayer => { + this._soundPlayers.set(soundPlayer.id, soundPlayer); - soundPlayer.setPlaybackRate(playbackRate); + soundPlayer.setPlaybackRate(playbackRate); - // Increase the volume - const engine = this.runtime.audioEngine; - const chain = engine.createEffectChain(); - chain.set('volume', SPEECH_VOLUME); - soundPlayer.connect(chain); + // Increase the volume + const engine = this.runtime.audioEngine; + const chain = engine.createEffectChain(); + chain.set('volume', SPEECH_VOLUME); + soundPlayer.connect(chain); - soundPlayer.play(); + soundPlayer.play(); + return new Promise(resolve => { soundPlayer.on('stop', () => { this._soundPlayers.delete(soundPlayer.id); resolve(); }); }); + }) + .catch(err => { + log.warn(err); + return; }); - }); } } module.exports = Scratch3Text2SpeechBlocks; diff --git a/src/extensions/scratch3_translate/index.js b/src/extensions/scratch3_translate/index.js index 3c1d4cd19..693a87f66 100644 --- a/src/extensions/scratch3_translate/index.js +++ b/src/extensions/scratch3_translate/index.js @@ -2,7 +2,7 @@ const ArgumentType = require('../../extension-support/argument-type'); const BlockType = require('../../extension-support/block-type'); const Cast = require('../../util/cast'); const log = require('../../util/log'); -const nets = require('nets'); +const fetchWithTimeout = require('../../util/fetch-with-timeout'); const languageNames = require('scratch-translate-extension-languages'); const formatMessage = require('format-message'); @@ -261,28 +261,21 @@ class Scratch3TranslateBlocks { urlBase += encodeURIComponent(args.WORDS); const tempThis = this; - const translatePromise = new Promise(resolve => { - nets({ - url: urlBase, - timeout: serverTimeoutMs - }, (err, res, body) => { - if (err) { - log.warn(`error fetching translate result! ${res}`); - resolve(''); - return ''; - } - const translated = JSON.parse(body).result; + const translatePromise = fetchWithTimeout(urlBase, {}, serverTimeoutMs) + .then(response => response.text()) + .then(responseText => { + const translated = JSON.parse(responseText).result; tempThis._translateResult = translated; // Cache what we just translated so we don't keep making the // same call over and over. tempThis._lastTextTranslated = args.WORDS; tempThis._lastLangTranslated = args.LANGUAGE; - resolve(translated); return translated; + }) + .catch(err => { + log.warn(`error fetching translate result! ${err}`); + return ''; }); - - }); - translatePromise.then(translatedText => translatedText); return translatePromise; } } diff --git a/src/util/fetch-with-timeout.js b/src/util/fetch-with-timeout.js new file mode 100644 index 000000000..983c888b3 --- /dev/null +++ b/src/util/fetch-with-timeout.js @@ -0,0 +1,28 @@ +/** + * Fetch a remote resource like `fetch` does, but with a time limit. + * @param {Request|string} resource Remote resource to fetch. + * @param {?object} init An options object containing any custom settings that you want to apply to the request. + * @param {number} timeout The amount of time before the request is canceled, in milliseconds + * @returns {Promise} The response from the server. + */ +const fetchWithTimeout = (resource, init, timeout) => { + let timeoutID = null; + // Not supported in Safari <11 + const controller = window.AbortController ? new window.AbortController() : null; + const signal = controller ? controller.signal : null; + // The fetch call races a timer. + return Promise.race([ + fetch(resource, Object.assign({signal}, init)).then(response => { + clearTimeout(timeoutID); + return response; + }), + new Promise((resolve, reject) => { + timeoutID = setTimeout(() => { + if (controller) controller.abort(); + reject(new Error(`Fetch timed out after ${timeout} ms`)); + }, timeout); + }) + ]); +}; + +module.exports = fetchWithTimeout; diff --git a/webpack.config.js b/webpack.config.js index 9b38815bd..6a8a3b32b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -77,7 +77,6 @@ module.exports = [ 'immutable': true, 'jszip': true, 'minilog': true, - 'nets': true, 'scratch-parser': true, 'socket.io-client': true, 'text-encoding': true From 1035e9da28eb975af605b7518f77a0a4305950dd Mon Sep 17 00:00:00 2001 From: adroitwhiz Date: Thu, 4 Mar 2021 00:29:50 -0500 Subject: [PATCH 2/2] Clean up TTS request code --- src/extensions/scratch3_text2speech/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/extensions/scratch3_text2speech/index.js b/src/extensions/scratch3_text2speech/index.js index f8cc408b4..f45c7010b 100644 --- a/src/extensions/scratch3_text2speech/index.js +++ b/src/extensions/scratch3_text2speech/index.js @@ -734,7 +734,7 @@ class Scratch3Text2SpeechBlocks { // Play the sound const sound = { data: { - buffer: buffer + buffer } }; return this.runtime.audioEngine.decodeSoundPlayer(sound); @@ -760,7 +760,6 @@ class Scratch3Text2SpeechBlocks { }) .catch(err => { log.warn(err); - return; }); } }