node-red-contrib-tts-ultimate 1.0.44 → 1.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  [![Donate via PayPal](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square)](https://www.paypal.me/techtoday)
4
4
 
5
+
6
+ <p>
7
+ <b>Version 1.0.48</b> Mai 2022<br/>
8
+ - Try to fixe a clunky issue with microsoft azure package, on nodejs versions that are not supported by Microsoft.<br/>
9
+ </p>
10
+ <p>
11
+ <b>Version 1.0.47</b> Mai 2022<br/>
12
+ - Fixed other compatibility issue with some Node version.<br/>
13
+ </p>
14
+ <p>
15
+ <b>Version 1.0.46</b> Mai 2022<br/>
16
+ - Fixed a compatibility issue with Node 18, where a breaking change has been introduced.<br/>
17
+ </p>
18
+ <p>
19
+ <b>Version 1.0.45</b> April 2022<br/>
20
+ - NEW: Additional players now resumes the queue as well (previously, only the main player was doing so).<br/>
21
+ </p>
5
22
  <p>
6
23
  <b>Version 1.0.44</b> April 2022<br/>
7
24
  - NEW: you can now adjust the additional player's volume, adapting it to the main sonos player volume. This is useful in case you've some recessed speakers, "speaking" too low or some too near speakers, "speaking" too high. You can adapt the volume in the config window or dinamically via msg input.<br/>
package/README.md CHANGED
@@ -22,7 +22,8 @@
22
22
 
23
23
 
24
24
  ## DESCRIPTION
25
- This node transforms a text into a speech audio. You can generate an audio file, or hear the voice through Sonos, bluetooth speakers, web pages, etc.<br/>
25
+ This node transforms a text into a speech audio that you can hear natively via <b>SONOS</b> speakers.<br/>
26
+ You can also generate an audio file for bluetooth speakers, web pages, etc.<br/>
26
27
  Uses Amazon Polly (standard and neural engines), Google TTS voices (even without credentials nor registration) and Microsoft TTS Azure voices, and you can use it with **your own audio file** as well and it can be used **totally offline** even without the use of TTS, without internet connection.<br/>
27
28
  The node can also create a ***TTS file (without the use of any Sonos device)***, to be read by third parties nodes.<br/>
28
29
  This is a major ***upgrade from the previously popular node SonosPollyTTS*** (SonosPollyTTS is not developed anymore).<br/>
@@ -34,6 +35,7 @@ This is a major ***upgrade from the previously popular node SonosPollyTTS*** (So
34
35
  * See <a href="https://github.com/Supergiovane/node-red-contrib-tts-ultimate/blob/master/CHANGELOG.md">here the changelog</a>
35
36
 
36
37
  ## FEATURES
38
+ * **Native Sonos support**: hear the TTS audio directly via Sonos. You can also group speakers, set an hailing sound, choose the volume of each speaker etc.
37
39
  * **Output audio file**: the node can just create the TTS file to be used by other nodes. In this case, you doesn't need to use Sonos as player.
38
40
  * **Amazon Voices, Gooogle Translate Voices, Google TTS Voices and Microsoft TTS Azure voices** are all supported, with all avaiables languages and genders.
39
41
  * **Automatic grouping** is supported. You can group all players you want to play your announcements.
@@ -215,9 +217,16 @@ return msg;
215
217
  ```
216
218
 
217
219
  ```js
218
- // Play custom hailing and custom mp3 taken from anywhere
219
- msg.sonoshailing = "IntruderAlert";
220
- msg.payload = "http://192.125.22.44/intruderalarm.mp3";
220
+ // Play smoke detection
221
+ msg.sonoshailing = "SmokeAlert";
222
+ msg.payload = "Warning, smoke detected. Fire extinguishers are in the kitchen, hall and garage.";
223
+ return msg;
224
+ ```
225
+
226
+ ```js
227
+ // Play an mp3
228
+ msg.sonoshailing = "MeteoJingle";
229
+ msg.payload = "http://192.125.22.44/meteotoday.mp3";
221
230
  return msg;
222
231
  ```
223
232
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-tts-ultimate",
3
- "version": "1.0.44",
3
+ "version": "1.0.48",
4
4
  "description": "Transforms the text in speech and hear it using Sonos player or generate an audio file to be used with third parties nodes. Works with voices from Amazon, Google (without credentials as well), Microsoft TTS Azure, or your own voice. You can also only create a TTS file to be read by third party nodes. Update of the popular SonosPollyTTS node.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -48,7 +48,7 @@
48
48
  "path": ">=0.12.7",
49
49
  "@google-cloud/text-to-speech": "3.4.0",
50
50
  "google-translate-tts": ">=0.2.1",
51
- "microsoft-cognitiveservices-speech-sdk": ">=1.19.0"
51
+ "microsoft-cognitiveservices-speech-sdk": "1.19.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "eslint": ">=4.18.2",
@@ -229,7 +229,7 @@ module.exports = function (RED) {
229
229
  } else {
230
230
  var sAddresses = "";
231
231
  oiFaces[ifname].forEach(function (iface) {
232
- if (iface.internal == false && iface.family === "IPv4") sAddresses = iface.address;
232
+ if (iface.internal == false && iface.family !== undefined && iface.family.toString().includes("4")) sAddresses = iface.address;
233
233
  });
234
234
  if (sAddresses !== "") jListInterfaces.push({ name: ifname, address: sAddresses });
235
235
  }
@@ -95,12 +95,12 @@ module.exports = function (RED) {
95
95
 
96
96
  //#region ASYNC DECLARATIONS
97
97
  // 30/12/2020 we are at the end of this crazy 2020
98
- function getMusicQueue() {
98
+ function getMusicQueue(_oPlayer = node.SonosClient) {
99
99
  return new Promise(function (resolve, reject) {
100
100
  var oRet = null;
101
- node.SonosClient.currentTrack().then(track => {
101
+ _oPlayer.currentTrack().then(track => {
102
102
  oRet = track;// .queuePosition || 1; // Get the current track in the queue.
103
- node.SonosClient.getCurrentState().then(state => {
103
+ _oPlayer.getCurrentState().then(state => {
104
104
  // A music queue is playing and no TTS is speaking?
105
105
  oRet.state = state;
106
106
  resolve(oRet);
@@ -115,11 +115,12 @@ module.exports = function (RED) {
115
115
  });
116
116
  };
117
117
 
118
+
118
119
  let iWaitAfterSync = 500;
119
120
  // 24/08/2021 Sync wrapper
120
- function PLAYSync(_toPlay) {
121
+ function PLAYSync(_toPlay, _oPlayer = node.SonosClient) {
121
122
  return new Promise((resolve, reject) => {
122
- node.SonosClient.play(_toPlay).then(result => {
123
+ _oPlayer.play(_toPlay).then(result => {
123
124
  if (iWaitAfterSync > 2000) console.log("PLAYSYNC")
124
125
  let t = setTimeout(() => {
125
126
  resolve(true);
@@ -132,9 +133,9 @@ module.exports = function (RED) {
132
133
  }
133
134
 
134
135
  // 24/08/2021 Sync wrapper
135
- function SEEKSync(_Position) {
136
+ function SEEKSync(_Position, _oPlayer = node.SonosClient) {
136
137
  return new Promise((resolve, reject) => {
137
- node.SonosClient.seek(_Position).then(result => {
138
+ _oPlayer.seek(_Position).then(result => {
138
139
  if (iWaitAfterSync > 2000) console.log("SEEKSync", _Position)
139
140
  let t = setTimeout(() => {
140
141
  resolve(true);
@@ -147,9 +148,9 @@ module.exports = function (RED) {
147
148
  }
148
149
 
149
150
  // 24/08/2021 Sync wrapper
150
- function SELECTQUEUESync() {
151
+ function SELECTQUEUESync(_oPlayer = node.SonosClient) {
151
152
  return new Promise((resolve, reject) => {
152
- node.SonosClient.selectQueue().then(result => {
153
+ _oPlayer.selectQueue().then(result => {
153
154
  if (iWaitAfterSync > 2000) console.log("SELECTQUEUESync")
154
155
  try {
155
156
  STOPSync(); // The SetQueue automatically starts playing, so i need to stop it now!
@@ -166,10 +167,10 @@ module.exports = function (RED) {
166
167
  }
167
168
 
168
169
  // 24/08/2021 Sync wrapper
169
- function SELECTTRACKSync(_queuePositiom) {
170
+ function SELECTTRACKSync(_queuePosition, _oPlayer = node.SonosClient) {
170
171
  return new Promise((resolve, reject) => {
171
- node.SonosClient.selectTrack(_queuePositiom).then(result => {
172
- if (iWaitAfterSync > 2000) console.log("SELECTTRACKSync", _queuePositiom)
172
+ _oPlayer.selectTrack(_queuePosition).then(result => {
173
+ if (iWaitAfterSync > 2000) console.log("SELECTTRACKSync", _queuePosition)
173
174
  let t = setTimeout(() => {
174
175
  resolve(true);
175
176
  }, iWaitAfterSync);
@@ -181,9 +182,9 @@ module.exports = function (RED) {
181
182
  }
182
183
 
183
184
  // 24/08/2021 Sync wrapper
184
- function STOPSync() {
185
+ function STOPSync(_oPlayer = node.SonosClient) {
185
186
  return new Promise((resolve, reject) => {
186
- node.SonosClient.stop().then(result => {
187
+ _oPlayer.stop().then(result => {
187
188
  if (iWaitAfterSync > 2000) console.log("STOPSync")
188
189
  let t = setTimeout(() => {
189
190
  resolve(true);
@@ -196,9 +197,9 @@ module.exports = function (RED) {
196
197
  }
197
198
 
198
199
  // 24/08/2021 Sync wrapper
199
- function GETVOLUMESync() {
200
+ function GETVOLUMESync(_oPlayer = node.SonosClient) {
200
201
  return new Promise((resolve, reject) => {
201
- node.SonosClient.getVolume().then(volume => {
202
+ _oPlayer.getVolume().then(volume => {
202
203
  if (iWaitAfterSync > 2000) console.log("GETVOLUMESync", volume)
203
204
  resolve(volume);
204
205
  }).catch(err => {
@@ -209,9 +210,9 @@ module.exports = function (RED) {
209
210
  }
210
211
 
211
212
  // 24/08/2021 Sync wrapper
212
- function SETVOLUMESync(_volume) {
213
+ function SETVOLUMESync(_volume, _oPlayer = node.SonosClient) {
213
214
  return new Promise((resolve, reject) => {
214
- node.SonosClient.setVolume(_volume).then(result => {
215
+ _oPlayer.setVolume(_volume).then(result => {
215
216
  if (iWaitAfterSync > 2000) console.log("SETVOLUMESync", _volume)
216
217
  resolve(true);
217
218
  }).catch(err => {
@@ -222,9 +223,9 @@ module.exports = function (RED) {
222
223
  }
223
224
 
224
225
  // 24/08/2021 Sync wrapper
225
- function setAVTransportURISync(_Uri) {
226
+ function setAVTransportURISync(_Uri, _oPlayer = node.SonosClient) {
226
227
  return new Promise((resolve, reject) => {
227
- node.SonosClient.setAVTransportURI(_Uri).then(volume => {
228
+ _oPlayer.setAVTransportURI(_Uri).then(volume => {
228
229
  if (iWaitAfterSync > 2000) console.log("setAVTransportURISync", _Uri)
229
230
  resolve(true);
230
231
  }).catch(err => {
@@ -235,9 +236,9 @@ module.exports = function (RED) {
235
236
  }
236
237
 
237
238
  // 24/08/2021 Sync wrapper
238
- function getCurrentStateSync() {
239
+ function getCurrentStateSync(_oPlayer = node.SonosClient) {
239
240
  return new Promise((resolve, reject) => {
240
- node.SonosClient.getCurrentState().then(state => {
241
+ _oPlayer.getCurrentState().then(state => {
241
242
  resolve(state);
242
243
  }).catch(err => {
243
244
  RED.log.error("ttsultimate: Error getCurrentStateSync: " + err.message);
@@ -247,9 +248,9 @@ module.exports = function (RED) {
247
248
  }
248
249
 
249
250
  // 21/10/2021 Sync wrapper
250
- function GETMutedSync() {
251
+ function GETMutedSync(_oPlayer = node.SonosClient) {
251
252
  return new Promise((resolve, reject) => {
252
- node.SonosClient.getMuted().then(state => {
253
+ _oPlayer.getMuted().then(state => {
253
254
  resolve(state);
254
255
  }).catch(err => {
255
256
  RED.log.error("ttsultimate: Error GETMutedSync: " + err.message);
@@ -259,9 +260,9 @@ module.exports = function (RED) {
259
260
  }
260
261
 
261
262
  // 21/10/2021 Sync wrapper
262
- function SETMutedSync(_muted) {
263
+ function SETMutedSync(_muted, _oPlayer = node.SonosClient) {
263
264
  return new Promise((resolve, reject) => {
264
- node.SonosClient.setMuted(_muted).then(state => {
265
+ _oPlayer.setMuted(_muted).then(state => {
265
266
  resolve(state);
266
267
  }).catch(err => {
267
268
  RED.log.error("ttsultimate: Error SETMutedSync: " + err.message);
@@ -283,15 +284,12 @@ module.exports = function (RED) {
283
284
  }
284
285
  // 30/03/2020 in the middle of coronavirus emergency. Group Speakers
285
286
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
286
- const element = node.oAdditionalSonosPlayers[index].oPlayer;
287
- try {
288
- await element.joinGroup(node.sonosCoordinatorGroupName);
289
- } catch (error) {
290
- RED.log.warn("ttsultimate: Error joining device " + error.message);
291
- }
292
- // 02/07/2021 Get the player's volume set by app, to be set again in ungroupspealers
287
+ let element = node.oAdditionalSonosPlayers[index].oPlayer;
288
+
289
+ // 02/07/2021 Get the additional's player's volume set by app and the current track, to be set again in ungroupspealers
293
290
  try {
294
291
  element.additionalPlayerPreviousVolumeSetByApp = await element.getVolume();
292
+ element.additionalPlayerCurrentTrack = await getMusicQueue(element);
295
293
  } catch (error) {
296
294
  RED.log.warn("ttsultimate: Error setting volume of joined device " + error.message);
297
295
  }
@@ -301,6 +299,12 @@ module.exports = function (RED) {
301
299
  } catch (error) {
302
300
  RED.log.warn("ttsultimate: Error getMuted of joined device " + error.message);
303
301
  }
302
+
303
+ try {
304
+ await element.joinGroup(node.sonosCoordinatorGroupName);
305
+ } catch (error) {
306
+ RED.log.warn("ttsultimate: Error joining device " + error.message);
307
+ }
304
308
  };
305
309
  }
306
310
 
@@ -322,7 +326,7 @@ module.exports = function (RED) {
322
326
  }
323
327
 
324
328
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
325
- const element = node.oAdditionalSonosPlayers[index].oPlayer;
329
+ let element = node.oAdditionalSonosPlayers[index].oPlayer;
326
330
  try {
327
331
  await element.leaveGroup();
328
332
  } catch (error) {
@@ -401,7 +405,7 @@ module.exports = function (RED) {
401
405
  });
402
406
  // Fill the node.oAdditionalSonosPlayers with all sonos object in the rules
403
407
  for (let index = 0; index < node.rules.length; index++) {
404
- const element = node.rules[index]; // Rule row is {host:"192.168.1.12,hostVolumeAdjust:0}
408
+ let element = node.rules[index]; // Rule row is {host:"192.168.1.12,hostVolumeAdjust:0}
405
409
  // 12/04/2022 Create an object containing the addidtional player and the adapted volume
406
410
  node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: Number(element.hostVolumeAdjust) });
407
411
  RED.log.info("ttsultimate: FOUND ADDITIONAL PLAYER " + element.host + " Adjusted volume: " + element.hostVolumeAdjust);
@@ -433,7 +437,7 @@ module.exports = function (RED) {
433
437
 
434
438
 
435
439
  // 30/12/2020 Supergiovane resume queue for radio, queue music, TV in , line in etc.
436
- async function resumeMusicQueue(_oTrack) {
440
+ async function resumeMusicQueue(_oTrack, _oPlayer = node.SonosClient) {
437
441
 
438
442
  if (_oTrack !== null) {
439
443
  // Do some checks on the track.
@@ -458,12 +462,13 @@ module.exports = function (RED) {
458
462
  if (_oTrack.state === "playing") {
459
463
  // 03/09/2021 Play if it was playing
460
464
  try {
461
- await PLAYSync(_oTrack.uri);
465
+ await PLAYSync(_oTrack.uri, _oPlayer);
462
466
  } catch (error) {
463
467
  return error;
464
468
  }
465
469
  try {
466
- await SEEKSync(_oTrack.position);
470
+ await delay(1000);
471
+ await SEEKSync(_oTrack.position, _oPlayer);
467
472
  } catch (error) {
468
473
  // Don't care
469
474
  }
@@ -471,31 +476,33 @@ module.exports = function (RED) {
471
476
  } else {
472
477
  if (_oTrack.trackType === "musicqueue") { // This indicates that is an audio file or stream station
473
478
  try {
474
- await SELECTQUEUESync();
479
+ await SELECTQUEUESync(_oPlayer);
475
480
  } catch (error) {
476
481
  return error;
477
482
  }
478
483
  try {
479
- await SELECTTRACKSync(_oTrack.queuePosition);
484
+ await delay(1000);
485
+ await SELECTTRACKSync(_oTrack.queuePosition, _oPlayer);
480
486
  } catch (error) {
481
487
  return error;
482
488
  }
483
489
  try {
484
- await SEEKSync(_oTrack.position);
490
+ await delay(1000);
491
+ await SEEKSync(_oTrack.position, _oPlayer);
485
492
  } catch (error) {
486
493
  // Don't care
487
494
  }
488
495
  if (_oTrack.state === "playing") {
489
496
  // 24/08/2021 Play if it was playing
490
497
  try {
491
- await PLAYSync();
498
+ await PLAYSync(_oPlayer);
492
499
  } catch (error) {
493
500
  return error;
494
501
  }
495
502
  } else {
496
503
  /// 03/09/2021
497
504
  try {
498
- await STOPSync();
505
+ await STOPSync(_oPlayer);
499
506
  } catch (error) {
500
507
  return error;
501
508
  }
@@ -504,7 +511,7 @@ module.exports = function (RED) {
504
511
  // Line in, TV in, etc...
505
512
  if (_oTrack.state === "playing") {
506
513
  try {
507
- await setAVTransportURISync(_oTrack.uri);
514
+ await setAVTransportURISync(_oTrack.uri, _oPlayer);
508
515
  } catch (error) {
509
516
  return error;
510
517
  }
@@ -514,6 +521,7 @@ module.exports = function (RED) {
514
521
  let t = setTimeout(() => { return true; }, 5000); // Wait some seconds
515
522
  };
516
523
 
524
+
517
525
  // Handle the queue
518
526
  async function HandleQueue() {
519
527
  node.bBusyPlayingQueue = true;
@@ -524,6 +532,7 @@ module.exports = function (RED) {
524
532
  var oCurTrack = null;
525
533
  try {
526
534
  oCurTrack = await getMusicQueue();
535
+ // 19/04/2022 The current track of additional players is read in the groupSpeakerySync function
527
536
  } catch (error) {
528
537
  oCurTrack = null;
529
538
  }
@@ -531,7 +540,7 @@ module.exports = function (RED) {
531
540
  // 05/12/2020 Set "completed" to false and send it
532
541
  node.msg.completed = false;
533
542
  try {
534
- await groupSpeakersSync(); // 20/03/2020 Group Speakers toghether
543
+ await groupSpeakersSync(); // 20/03/2020 Group Speakers toghether and reads each current track
535
544
  } catch (error) {
536
545
  // Don't care.
537
546
  node.setNodeStatus({ fill: "red", shape: "ring", text: "Error grouping speakers: " + error.message });
@@ -668,7 +677,7 @@ module.exports = function (RED) {
668
677
  if (node.oAdditionalSonosPlayers.length > 0) {
669
678
  // 05/07/2021 set the volume of additional coordinators
670
679
  for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
671
- const element = node.oAdditionalSonosPlayers[index].oPlayer;
680
+ let element = node.oAdditionalSonosPlayers[index].oPlayer;
672
681
  //node.oAdditionalSonosPlayers.push({ oPlayer: new sonos.Sonos(element.host), hostVolumeAdjust: element.hostVolumeAdjust });
673
682
  try {
674
683
  // 12/04/20222 Set the adjusted volume, based on the main player volume + the adjusted volume in %
@@ -766,7 +775,7 @@ module.exports = function (RED) {
766
775
 
767
776
  // Ungroup speaker
768
777
  try {
769
- await ungroupSpeakersSync();
778
+ await ungroupSpeakersSync(); // Ungroup speakers
770
779
  } catch (error) {
771
780
  // Don't care.
772
781
  node.setNodeStatus({ fill: "red", shape: "ring", text: "Error ungrouping speakers: " + error.message });
@@ -788,6 +797,24 @@ module.exports = function (RED) {
788
797
  node.setNodeStatus({ fill: 'red', shape: 'ring', text: "Error resuming queue: " + error.message });
789
798
  }
790
799
 
800
+
801
+ // 19/04/2022 Resume music queue of additional players
802
+ for (let index = 0; index < node.oAdditionalSonosPlayers.length; index++) {
803
+ let addPlayer = node.oAdditionalSonosPlayers[index].oPlayer;
804
+ let trackAddPlayer = addPlayer.additionalPlayerCurrentTrack;
805
+ if (trackAddPlayer !== null) {
806
+ try {
807
+ await resumeMusicQueue(trackAddPlayer, addPlayer);
808
+ node.setNodeStatus({ fill: 'green', shape: 'ring', text: "Done resuming queue additional player " + addPlayer.host || "" });
809
+ } catch (error) {
810
+ // Dont care
811
+ RED.log.warn("ttsultimate: Error resuming music queue of additional player " + error.message + " " + addPlayer.host || "");
812
+ }
813
+ } else {
814
+ node.setNodeStatus({ fill: 'green', shape: 'ring', text: "No queue to resume for " + addPlayer.host || "" });
815
+ }
816
+ }
817
+
791
818
  // Signal end playing
792
819
  let t = setTimeout(() => {
793
820
  node.msg.completed = true;