node-red-contrib-tts-ultimate 1.0.44 → 1.0.45
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 +4 -0
- package/package.json +1 -1
- package/ttsultimate/ttsultimate.js +74 -47
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.paypal.me/techtoday)
|
|
4
4
|
|
|
5
|
+
<p>
|
|
6
|
+
<b>Version 1.0.45</b> April 2022<br/>
|
|
7
|
+
- NEW: Additional players now resumes the queue as well (previously, only the main player was doing so).<br/>
|
|
8
|
+
</p>
|
|
5
9
|
<p>
|
|
6
10
|
<b>Version 1.0.44</b> April 2022<br/>
|
|
7
11
|
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-tts-ultimate",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.45",
|
|
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": {
|
|
@@ -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
|
-
|
|
101
|
+
_oPlayer.currentTrack().then(track => {
|
|
102
102
|
oRet = track;// .queuePosition || 1; // Get the current track in the queue.
|
|
103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
170
|
+
function SELECTTRACKSync(_queuePosition, _oPlayer = node.SonosClient) {
|
|
170
171
|
return new Promise((resolve, reject) => {
|
|
171
|
-
|
|
172
|
-
if (iWaitAfterSync > 2000) console.log("SELECTTRACKSync",
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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;
|