node-red-contrib-tts-ultimate 1.0.39 → 1.0.42
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/.vscode/launch.json +1 -1
- package/CHANGELOG.md +36 -18
- package/README.md +16 -9
- package/package.json +3 -3
- package/ttsultimate/ownfileultimate.html +1 -1
- package/ttsultimate/ttsultimate-config.html +4 -8
- package/ttsultimate/ttsultimate-config.js +53 -50
- package/ttsultimate/ttsultimate.html +2 -2
- package/ttsultimate/ttsultimate.js +81 -85
package/.vscode/launch.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -2,90 +2,108 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.paypal.me/techtoday)
|
|
4
4
|
|
|
5
|
+
<p>
|
|
6
|
+
<b>Version 1.0.42</b> March 2022<br/>
|
|
7
|
+
- FIX: fix purging option that wasn't working if you set to always purge the cached files at startup.<br/>
|
|
8
|
+
- FIX: invalid code in some sync functions.<br/>
|
|
9
|
+
</p>
|
|
10
|
+
<p>
|
|
11
|
+
<b>Version 1.0.41</b> March 2022<br/>
|
|
12
|
+
- NEW: for Polly TTS, you can choose between neural and standard engine.<br/>
|
|
13
|
+
</p>
|
|
14
|
+
<p>
|
|
15
|
+
<b>Version 1.0.40</b> January 2022<br/>
|
|
16
|
+
- NEW: you can now select your own folder to save the TTS cached files.<br/>
|
|
17
|
+
- NEW: getting rid of file lenght issue by hashing the TTS cached files requested from TTS engines. Now the file names will be MD5 HEX hashed.<br/>
|
|
18
|
+
- NEW: now the input messages are passed through to the output pin.<br/>
|
|
19
|
+
- CAUTION: due to the new file management, the node will need to download again the TTS files from your TTS engine. Keep it in mind, because you can be charged by Amazon, Google or Microsoft.<br/>
|
|
20
|
+
</p>
|
|
5
21
|
<p>
|
|
6
22
|
<b>Version 1.0.39</b> January 2022<br/>
|
|
7
23
|
- SSML: fixed an issue prevent using it.<br/>
|
|
8
24
|
- SSML: if SSML is enabled, the text auto split function is disabled, to avoid splitting SSML XML text.<br/>
|
|
9
25
|
- Microsoft Azure: update TTS engine to 1.19.0<br/>
|
|
10
26
|
- Google paid TTS: update TTS engine to 3.4.0<br/>
|
|
27
|
+
</p>
|
|
11
28
|
<p>
|
|
12
29
|
<b>Version 1.0.38</b> December 2021<br/>
|
|
13
30
|
- Removed some unwanted startup logs.<br/>
|
|
14
31
|
- Fixed ownfile sample code. Thanks to plats98.<br/>
|
|
15
|
-
|
|
32
|
+
</p>
|
|
16
33
|
<p>
|
|
17
34
|
<b>Version 1.0.36</b> November 2021<br/>
|
|
18
35
|
- NEW: Autosplit function: you can now set the maximum lenght of the text-parts, in case your spoken text is too long for the allowed TTS Engine limits.<br/>
|
|
19
|
-
|
|
36
|
+
</p>
|
|
20
37
|
<p>
|
|
21
38
|
<b>Version 1.0.35</b> October 2021<br/>
|
|
22
39
|
- NEW: You can force unmuting all players, then restore their previous state once finished playing.<br/>
|
|
23
|
-
|
|
40
|
+
</p>
|
|
24
41
|
<p>
|
|
25
42
|
<b>Version 1.0.34</b> October 2021<br/>
|
|
26
43
|
- FIX: fixed an issue in retrieving voices if you have more than one TTS engine enabled at the same time.<br/>
|
|
27
|
-
|
|
44
|
+
</p>
|
|
28
45
|
<p>
|
|
29
46
|
<b>Version 1.0.33</b> October 2021<br/>
|
|
30
47
|
- NEW VOICE ENGINE: Microsoft Azure TTS.<br/>
|
|
31
|
-
|
|
48
|
+
</p>
|
|
32
49
|
<p>
|
|
33
50
|
<b>Version 1.0.32</b> September 2021<br/>
|
|
34
51
|
- Fix few restore issues. Line-in restore fix and only when it was playing. Amazon Music and Spotify considered as stream instead of music queue.<br/>
|
|
35
|
-
|
|
52
|
+
</p>
|
|
36
53
|
<p>
|
|
37
54
|
<b>Version 1.0.31</b> September 2021<br/>
|
|
38
55
|
- NEW: you can now choose voice PITCH and RATE. Avaiable only with Google TTS engine with credentials.<br/>
|
|
39
|
-
|
|
56
|
+
</p>
|
|
40
57
|
<p>
|
|
41
58
|
<b>Version 1.0.29</b> September 2021<br/>
|
|
42
59
|
- NEW: you can now choose not to use Sonos as player. In this case, the node will output an array of mp3, ready to be played by third parties nodes.<br/>
|
|
43
|
-
|
|
60
|
+
</p>
|
|
44
61
|
<p>
|
|
45
62
|
<b>Version 1.0.28</b> September 2021<br/>
|
|
46
63
|
- Fixed queue resuming play even if was in stop (only occurs in some circumstances).<br/>
|
|
47
|
-
|
|
64
|
+
</p>
|
|
48
65
|
<p>
|
|
49
66
|
<b>Version 1.0.27</b> September 2021<br/>
|
|
50
67
|
- Hided some unwanted logs.<br/>
|
|
51
|
-
|
|
68
|
+
</p>
|
|
52
69
|
<p>
|
|
53
70
|
<b>Version 1.0.26</b> August 2021<br/>
|
|
54
71
|
- FIX: after playing tts, if you have no previous queue and you are on old Sonos V1, the last TTS played remains in the queue (it shouldn't).<br/>
|
|
55
|
-
|
|
72
|
+
</p>
|
|
56
73
|
<p>
|
|
57
74
|
<b>Version 1.0.25</b> August 2021<br/>
|
|
58
75
|
- Optimized setting volume speed.<br/>
|
|
59
|
-
|
|
76
|
+
</p>
|
|
60
77
|
<p>
|
|
61
78
|
<b>Version 1.0.24</b> August 2021<br/>
|
|
62
79
|
- Fixed a little issue with sonos beam, switching volumes with a 1-2 seconds delay.<br/>
|
|
63
|
-
|
|
80
|
+
</p>
|
|
64
81
|
<p>
|
|
65
82
|
<b>Version 1.0.23</b> August 2021<br/>
|
|
66
83
|
- Fixed a volume issue. The playing queue was jumping briefly at TTS volume before stopping. That was annoiyng.<br/>
|
|
67
84
|
- Fixed issues with some async function not really async, so there was glitches in volume settings, seeking and so on, specially with playlist and queues.<br/>
|
|
68
85
|
- There are known issues with resuming play of sonos streams, they work for a while, then stop.<br/>
|
|
69
|
-
|
|
86
|
+
</p>
|
|
70
87
|
<p>
|
|
71
88
|
<b>Version 1.0.22</b> Juli 2021<br/>
|
|
72
89
|
- The additional players don't obey to msg.volume input node message override (they instead get the volume set by the config window, that is OK, but they must also obey to the override msg). Fixed<br/>
|
|
73
|
-
|
|
90
|
+
</p>
|
|
74
91
|
<p>
|
|
75
92
|
<b>Version 1.0.21</b> Juli 2021<br/>
|
|
76
93
|
- The additional players in the group, now reverts to the previous volume after the speech.<br/>
|
|
77
|
-
|
|
94
|
+
</p>
|
|
78
95
|
<p>
|
|
79
96
|
<b>Version 1.0.20</b> May 2021<br/>
|
|
80
97
|
- Fixed an issue preventing TTS working on Windows machines. Thanks @McFozzy75<br/>
|
|
81
|
-
|
|
98
|
+
</p>
|
|
82
99
|
<p>
|
|
83
100
|
<b>Version 1.0.19</b> February 2021<br/>
|
|
84
101
|
- The previous limit of 200 chars (before the TTS text is automatically split) has been increased to 220.<br/>
|
|
85
|
-
|
|
102
|
+
</p>
|
|
86
103
|
<p>
|
|
87
104
|
<b>Version 1.0.18</b> January 2021<br/>
|
|
88
105
|
- Better handling of payloads long more than 200 chars.<br/>
|
|
106
|
+
</p>
|
|
89
107
|
<p>
|
|
90
108
|
<b>Version 1.0.16</b> January 2021<br/>
|
|
91
109
|
- Currently, the FREE GOOGLE TRANSLATE TTS engine has changed some voice codes. I've been fixed that. You need to do nothing.<br/>
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
## DESCRIPTION
|
|
25
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/>
|
|
26
|
-
Uses Amazon Polly, 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/>
|
|
26
|
+
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
27
|
The node can also create a ***TTS file (without the use of any Sonos device)***, to be read by third parties nodes.<br/>
|
|
28
28
|
This is a major ***upgrade from the previously popular node SonosPollyTTS*** (SonosPollyTTS is not developed anymore).<br/>
|
|
29
29
|
**Node v.12.0.0 or newer is needed**.
|
|
@@ -126,10 +126,11 @@ On each deploy or node-red restart, delete all tts files in the cache. This is u
|
|
|
126
126
|
Don't delete the files cached. Useful if you wish to keep the tts files, even in case of internet outages, node-red restart or reboots.
|
|
127
127
|
<br/>
|
|
128
128
|
|
|
129
|
-
**
|
|
129
|
+
**Cache root folder**
|
|
130
130
|
<br/>
|
|
131
|
-
|
|
132
|
-
This
|
|
131
|
+
Set your preferred output folder for the files downloaded by the TTS Engine.<br/>
|
|
132
|
+
This is useful if you wish to save the TTS cached files in a folder accessible, for example, by a third party web servers to serve an AirPlay2 speaker.<br/>
|
|
133
|
+
Leave this field blank for the default.<br/>
|
|
133
134
|
<br/>
|
|
134
135
|
<br/>
|
|
135
136
|
|
|
@@ -139,7 +140,10 @@ This field allow you to set the maximum lenght (**in chars**) of each splitted t
|
|
|
139
140
|
Select the TTS SERVICE ENGINE NODE, as stated above.
|
|
140
141
|
|
|
141
142
|
**Voice**<br/>
|
|
142
|
-
Select your preferred voice. If you use Amazon, Polly voices will be displayed. If you use Google, google voices will be displayed. Google service without authentication, has a limited set of voices.
|
|
143
|
+
Select your preferred voice. If you use Amazon, Polly voices will be displayed (standard and neural). If you use Google, google voices will be displayed. Google service without authentication, has a limited set of voices.
|
|
144
|
+
|
|
145
|
+
**Enable SSML**<br/>
|
|
146
|
+
Enable the SSML XML notation. Please be aware, not all the TTS engines supports that.
|
|
143
147
|
|
|
144
148
|
**Rate**<br/>
|
|
145
149
|
Only avaiable if you choose Google TTS Engine (with credentials). Specifies the speech speed (Between 0.25 and 4.0, default 1).
|
|
@@ -236,18 +240,21 @@ The node has two output pins. The first pin is to signal play status, the second
|
|
|
236
240
|
**OUTPUT PIN 1**<br/>
|
|
237
241
|
Payload is ***true*** when the node has finished playing, ***false*** if the node is playing<br/>
|
|
238
242
|
In case you selected ***No player, only output file name.*** in the **Player** property, you'll get a message with an additional property *filesArray*, containing an array of all mp3 files ready to be played with third party nodes.<br/>
|
|
243
|
+
The property ***passThroughMessage*** contains the input msg passed through the output.<br/>
|
|
244
|
+
|
|
239
245
|
```js
|
|
240
246
|
{
|
|
241
247
|
"payload":true,
|
|
248
|
+
"passThroughMessage" : {original message object}
|
|
242
249
|
"filesArray":[
|
|
243
250
|
{
|
|
244
|
-
"file":"/Users/supergiovane/.node-red/sonospollyttsstorage/hailingpermanentfiles/
|
|
251
|
+
"file":"/Users/supergiovane/.node-red/sonospollyttsstorage/hailingpermanentfiles/hail.mp3"
|
|
245
252
|
},
|
|
246
253
|
{
|
|
247
|
-
"file":"/Users/supergiovane/.node-red/sonospollyttsstorage/ttsfiles/
|
|
254
|
+
"file":"/Users/supergiovane/.node-red/sonospollyttsstorage/ttsfiles/345938475938457.mp3"
|
|
248
255
|
},
|
|
249
256
|
{
|
|
250
|
-
"file":"/Users/supergiovane/.node-red/sonospollyttsstorage/ttsfiles/
|
|
257
|
+
"file":"/Users/supergiovane/.node-red/sonospollyttsstorage/ttsfiles/3666HJGH565656.mp3"
|
|
251
258
|
}
|
|
252
259
|
],
|
|
253
260
|
"_msgid":"8b6b22a45dfd5236"
|
|
@@ -352,7 +359,7 @@ This node allow you to upload your custom message and play it via ttsultimate wi
|
|
|
352
359
|
**Name**<br/>
|
|
353
360
|
Node name
|
|
354
361
|
|
|
355
|
-
**File to be
|
|
362
|
+
**File to be played** <br/>
|
|
356
363
|
Select a file to be played. You can upload one or multiple files at the same time via the "upload" button.
|
|
357
364
|
|
|
358
365
|
**Priority**<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.42",
|
|
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": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"IOT",
|
|
23
23
|
"speech",
|
|
24
24
|
"ttsultimate",
|
|
25
|
-
"sonospollytts"
|
|
25
|
+
"sonospollytts",
|
|
26
|
+
"neural"
|
|
26
27
|
],
|
|
27
28
|
"node-red": {
|
|
28
29
|
"nodes": {
|
|
@@ -39,7 +40,6 @@
|
|
|
39
40
|
"homepage": "https://github.com/Supergiovane/node-red-contrib-tts-ultimate",
|
|
40
41
|
"dependencies": {
|
|
41
42
|
"aws-sdk": "2.816.0",
|
|
42
|
-
"crypto-js": "^3.1.9-1",
|
|
43
43
|
"fs": "0.0.1-security",
|
|
44
44
|
"sonos": "1.14.1",
|
|
45
45
|
"util": ">=0.10.1",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
83
|
// The only way is to wait some time, then refresh
|
|
84
|
-
setTimeout(function () {
|
|
84
|
+
let t = setTimeout(function () {
|
|
85
85
|
node.refreshListaFiles().then((success, error) => {
|
|
86
86
|
$("#ownFileUpload").val("");// Otherwise will not re-upload a file with the same name
|
|
87
87
|
$("#node-input-selectedFile").val("OwnFile_" + file.name);
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
purgediratrestart: { value: "leave", required: false },
|
|
20
20
|
ttsservice: { value: "googletranslate", required: false },
|
|
21
|
-
|
|
21
|
+
TTSRootFolderPath: { value: "", required: false }
|
|
22
22
|
|
|
23
23
|
},
|
|
24
24
|
credentials: {
|
|
@@ -33,10 +33,6 @@
|
|
|
33
33
|
oneditprepare: function () {
|
|
34
34
|
var node = this;
|
|
35
35
|
|
|
36
|
-
// 01/11/2021
|
|
37
|
-
//if (node.limitTTSFilenameLenght === undefined) {
|
|
38
|
-
// $("#node-config-input-limitTTSFilenameLenght").val(220);
|
|
39
|
-
//}
|
|
40
36
|
// 21/03/2020 Check if the node is the absolute first in the flow. In this case, it has no http server instatiaced
|
|
41
37
|
$.getJSON('ttsultimateGetEthAddress', (data) => {
|
|
42
38
|
$("#pleaseDeploy").hide();
|
|
@@ -215,10 +211,10 @@
|
|
|
215
211
|
</div>
|
|
216
212
|
|
|
217
213
|
<div class="form-row">
|
|
218
|
-
<label for="node-config-input-
|
|
219
|
-
<input type="text" id="node-config-input-
|
|
214
|
+
<label for="node-config-input-TTSRootFolderPath"><i class="fa fa-folder-o"></i> Cache root folder</label>
|
|
215
|
+
<input type="text" id="node-config-input-TTSRootFolderPath" placeholder="The folder must exist. Leave blank for default">
|
|
220
216
|
</div>
|
|
221
|
-
|
|
217
|
+
|
|
222
218
|
</div>
|
|
223
219
|
|
|
224
220
|
</script>
|
|
@@ -20,10 +20,9 @@ module.exports = function (RED) {
|
|
|
20
20
|
RED.nodes.createNode(this, config);
|
|
21
21
|
var node = this;
|
|
22
22
|
node.noderedipaddress = typeof config.noderedipaddress === "undefined" ? "" : config.noderedipaddress;
|
|
23
|
-
node.userDir = path.join(RED.settings.userDir, "sonospollyttsstorage"); // 09/03/2020 Storage of ttsultimate (otherwise, at each upgrade to a newer version, the node path is wiped out and recreated, loosing all custom files)
|
|
24
23
|
node.whoIsUsingTheServer = ""; // Client node.id using the server, because only a ttsultimate node can use the serve at once.
|
|
25
24
|
node.ttsservice = config.ttsservice || "googletranslate";
|
|
26
|
-
node.
|
|
25
|
+
node.TTSRootFolderPath = (config.TTSRootFolderPath === undefined || config.TTSRootFolderPath === "") ? path.join(RED.settings.userDir, "sonospollyttsstorage") : path.join(config.TTSRootFolderPath, "sonospollyttsstorage");
|
|
27
26
|
// node.polly = null;
|
|
28
27
|
// node.googleTTS = null;
|
|
29
28
|
// node.googleTranslateTTS = null;
|
|
@@ -44,38 +43,38 @@ module.exports = function (RED) {
|
|
|
44
43
|
return true;
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
|
-
if (!setupDirectory(node.
|
|
48
|
-
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up MAIN directory: ' + node.
|
|
46
|
+
if (!setupDirectory(node.TTSRootFolderPath)) {
|
|
47
|
+
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up MAIN directory: ' + node.TTSRootFolderPath);
|
|
49
48
|
}
|
|
50
|
-
if (!setupDirectory(path.join(node.
|
|
51
|
-
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up cache directory: ' + path.join(node.
|
|
49
|
+
if (!setupDirectory(path.join(node.TTSRootFolderPath, "ttsfiles"))) {
|
|
50
|
+
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up cache directory: ' + path.join(node.TTSRootFolderPath, "ttsfiles"));
|
|
52
51
|
} else {
|
|
53
|
-
RED.log.info('ttsultimate-config ' + node.id + ': TTS cache set to ' + path.join(node.
|
|
52
|
+
RED.log.info('ttsultimate-config ' + node.id + ': TTS cache set to ' + path.join(node.TTSRootFolderPath, "ttsfiles"));
|
|
54
53
|
}
|
|
55
|
-
if (!setupDirectory(path.join(node.
|
|
56
|
-
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set google creds directory: ' + path.join(node.
|
|
54
|
+
if (!setupDirectory(path.join(node.TTSRootFolderPath, "ttsultimategooglecredentials"))) {
|
|
55
|
+
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set google creds directory: ' + path.join(node.TTSRootFolderPath, "ttsultimategooglecredentials"));
|
|
57
56
|
} else {
|
|
58
|
-
RED.log.info('ttsultimate-config ' + node.id + ': google credentials path set to ' + path.join(node.
|
|
57
|
+
RED.log.info('ttsultimate-config ' + node.id + ': google credentials path set to ' + path.join(node.TTSRootFolderPath, "ttsultimategooglecredentials"));
|
|
59
58
|
}
|
|
60
|
-
if (!setupDirectory(path.join(node.
|
|
61
|
-
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up hailing directory: ' + path.join(node.
|
|
59
|
+
if (!setupDirectory(path.join(node.TTSRootFolderPath, "hailingpermanentfiles"))) {
|
|
60
|
+
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up hailing directory: ' + path.join(node.TTSRootFolderPath, "hailingpermanentfiles"));
|
|
62
61
|
} else {
|
|
63
|
-
RED.log.info('ttsultimate-config ' + node.id + ': hailing path set to ' + path.join(node.
|
|
62
|
+
RED.log.info('ttsultimate-config ' + node.id + ': hailing path set to ' + path.join(node.TTSRootFolderPath, "hailingpermanentfiles"));
|
|
64
63
|
// 09/03/2020 Copy defaults to the userDir
|
|
65
64
|
fs.readdirSync(path.join(__dirname, "hailingpermanentfiles")).forEach(file => {
|
|
66
65
|
try {
|
|
67
|
-
fs.copyFileSync(path.join(__dirname, "hailingpermanentfiles", file), path.join(node.
|
|
66
|
+
fs.copyFileSync(path.join(__dirname, "hailingpermanentfiles", file), path.join(node.TTSRootFolderPath, "hailingpermanentfiles", file));
|
|
68
67
|
} catch (error) { }
|
|
69
68
|
});
|
|
70
69
|
}
|
|
71
|
-
if (!setupDirectory(path.join(node.
|
|
72
|
-
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up permanent files directory: ' + path.join(node.
|
|
70
|
+
if (!setupDirectory(path.join(node.TTSRootFolderPath, "ttspermanentfiles"))) {
|
|
71
|
+
RED.log.error('ttsultimate-config ' + node.id + ': Unable to set up permanent files directory: ' + path.join(node.TTSRootFolderPath, "ttspermanentfiles"));
|
|
73
72
|
} else {
|
|
74
|
-
RED.log.info('ttsultimate-config ' + node.id + ': permanent files path set to ' + path.join(node.
|
|
73
|
+
RED.log.info('ttsultimate-config ' + node.id + ': permanent files path set to ' + path.join(node.TTSRootFolderPath, "ttspermanentfiles"));
|
|
75
74
|
// 09/03/2020 // Copy the samples of permanent files into the userDir
|
|
76
75
|
fs.readdirSync(path.join(__dirname, "ttspermanentfiles")).forEach(file => {
|
|
77
76
|
try {
|
|
78
|
-
fs.copyFileSync(path.join(__dirname, "ttspermanentfiles", file), path.join(node.
|
|
77
|
+
fs.copyFileSync(path.join(__dirname, "ttspermanentfiles", file), path.join(node.TTSRootFolderPath, "ttspermanentfiles", file));
|
|
79
78
|
} catch (error) { }
|
|
80
79
|
});
|
|
81
80
|
}
|
|
@@ -105,8 +104,8 @@ module.exports = function (RED) {
|
|
|
105
104
|
if (node.ttsservice === "googletts") {
|
|
106
105
|
try {
|
|
107
106
|
// 23/12/2020 Set environment path of googleTTS
|
|
108
|
-
RED.log.info("ttsultimate-config " + node.id + ": Google credentials are stored in the file " + path.join(node.
|
|
109
|
-
process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(node.
|
|
107
|
+
RED.log.info("ttsultimate-config " + node.id + ": Google credentials are stored in the file " + path.join(node.TTSRootFolderPath, "ttsultimategooglecredentials", "googlecredentials.json"));
|
|
108
|
+
process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(node.TTSRootFolderPath, "ttsultimategooglecredentials", "googlecredentials.json");
|
|
110
109
|
try {
|
|
111
110
|
node.googleTTS = new GoogleTTS.TextToSpeechClient();
|
|
112
111
|
RED.log.info("ttsultimate-config " + node.id + ": Google TTS service enabled. ")
|
|
@@ -265,7 +264,7 @@ module.exports = function (RED) {
|
|
|
265
264
|
var jListOwnFiles = [];
|
|
266
265
|
var sName = "";
|
|
267
266
|
try {
|
|
268
|
-
fs.readdirSync(path.join(node.
|
|
267
|
+
fs.readdirSync(path.join(node.TTSRootFolderPath, "hailingpermanentfiles")).forEach(file => {
|
|
269
268
|
if (file.indexOf("Hailing_") > -1) {
|
|
270
269
|
sName = file.replace("Hailing_", "").replace(".mp3", "");
|
|
271
270
|
jListOwnFiles.push({ name: sName, filename: file });
|
|
@@ -280,7 +279,7 @@ module.exports = function (RED) {
|
|
|
280
279
|
RED.httpAdmin.get("/deleteHailingFile", RED.auth.needsPermission('TTSConfigNode.read'), function (req, res) {
|
|
281
280
|
// Delete the file
|
|
282
281
|
try {
|
|
283
|
-
var newPath = path.join(node.
|
|
282
|
+
var newPath = path.join(node.TTSRootFolderPath, "hailingpermanentfiles", req.query.FileName);
|
|
284
283
|
fs.unlinkSync(newPath)
|
|
285
284
|
} catch (error) { }
|
|
286
285
|
res.json({ status: 220 });
|
|
@@ -292,7 +291,7 @@ module.exports = function (RED) {
|
|
|
292
291
|
form.parse(req, function (err, fields, files) {
|
|
293
292
|
try {
|
|
294
293
|
if (files.customHailing.name.indexOf(".mp3") !== -1) {
|
|
295
|
-
var newPath = path.join(node.
|
|
294
|
+
var newPath = path.join(node.TTSRootFolderPath, "hailingpermanentfiles", "Hailing_" + files.customHailing.name);
|
|
296
295
|
// 30/12/2020 To avoid XDEV issue: oldpath and newpath are not on the same mounted filesystem. (Linux permits a filesystem to be mounted at multiple points,
|
|
297
296
|
// but rename() does not work across different mount points, even if the same filesystem is mounted on both.)
|
|
298
297
|
// Instead of renaming it, i must copy the file and then delete the old one.
|
|
@@ -325,7 +324,7 @@ module.exports = function (RED) {
|
|
|
325
324
|
if (err) { };
|
|
326
325
|
// Allow only json
|
|
327
326
|
if (files.googleCreds.name.indexOf(".json") !== -1) {
|
|
328
|
-
var newPath = path.join(node.
|
|
327
|
+
var newPath = path.join(node.TTSRootFolderPath, "ttsultimategooglecredentials", "googlecredentials.json");
|
|
329
328
|
// Set the environment variable
|
|
330
329
|
process.env.GOOGLE_APPLICATION_CREDENTIALS = newPath;
|
|
331
330
|
// 30/12/2020 To avoid XDEV issue: oldpath and newpath are not on the same mounted filesystem. (Linux permits a filesystem to be mounted at multiple points,
|
|
@@ -370,7 +369,13 @@ module.exports = function (RED) {
|
|
|
370
369
|
} else {
|
|
371
370
|
for (let index = 0; index < data.Voices.length; index++) {
|
|
372
371
|
const oVoice = data.Voices[index];
|
|
373
|
-
|
|
372
|
+
if (oVoice.hasOwnProperty("SupportedEngines")) {
|
|
373
|
+
oVoice.SupportedEngines.forEach(voicetype => {
|
|
374
|
+
jListVoices.push({ name: oVoice.LanguageName + " (" + oVoice.LanguageCode + ") " + oVoice.Name + " - " + oVoice.Gender + " - " + voicetype, id: oVoice.Id + "#engineType:" + voicetype })
|
|
375
|
+
});
|
|
376
|
+
} else {
|
|
377
|
+
jListVoices.push({ name: oVoice.LanguageName + " (" + oVoice.LanguageCode + ") " + oVoice.Name + " - " + oVoice.Gender, id: oVoice.Id })
|
|
378
|
+
}
|
|
374
379
|
}
|
|
375
380
|
res.json(jListVoices)
|
|
376
381
|
}
|
|
@@ -442,7 +447,7 @@ module.exports = function (RED) {
|
|
|
442
447
|
// Allow only mp3
|
|
443
448
|
try {
|
|
444
449
|
if (files.customTTS.name.indexOf(".mp3") !== -1) {
|
|
445
|
-
var newPath = path.join(node.
|
|
450
|
+
var newPath = path.join(node.TTSRootFolderPath, "ttspermanentfiles", "OwnFile_" + files.customTTS.name);
|
|
446
451
|
// 30/12/2020 To avoid XDEV issue: oldpath and newpath are not on the same mounted filesystem. (Linux permits a filesystem to be mounted at multiple points,
|
|
447
452
|
// but rename() does not work across different mount points, even if the same filesystem is mounted on both.)
|
|
448
453
|
// Instead of renaming it, i must copy the file and then delete the old one.
|
|
@@ -472,7 +477,7 @@ module.exports = function (RED) {
|
|
|
472
477
|
var jListOwnFiles = [];
|
|
473
478
|
var sName = "";
|
|
474
479
|
try {
|
|
475
|
-
fs.readdirSync(path.join(node.
|
|
480
|
+
fs.readdirSync(path.join(node.TTSRootFolderPath, "ttspermanentfiles")).forEach(file => {
|
|
476
481
|
if (file.indexOf("OwnFile_") > -1) {
|
|
477
482
|
sName = file.replace("OwnFile_", '').replace(".mp3", '');
|
|
478
483
|
jListOwnFiles.push({ name: sName, filename: file });
|
|
@@ -489,12 +494,12 @@ module.exports = function (RED) {
|
|
|
489
494
|
if (req.query.FileName == "DELETEallFiles") {
|
|
490
495
|
// Delete all OwnFiles_
|
|
491
496
|
try {
|
|
492
|
-
fs.readdir(path.join(node.
|
|
497
|
+
fs.readdir(path.join(node.TTSRootFolderPath, "ttspermanentfiles"), (err, files) => {
|
|
493
498
|
files.forEach(function (file) {
|
|
494
499
|
if (file.indexOf("OwnFile_") !== -1) {
|
|
495
|
-
RED.log.warn("ttsultimate-config " + node.id + ": Deleted file " + path.join(node.
|
|
500
|
+
RED.log.warn("ttsultimate-config " + node.id + ": Deleted file " + path.join(node.TTSRootFolderPath, "ttspermanentfiles", file));
|
|
496
501
|
try {
|
|
497
|
-
fs.unlinkSync(path.join(node.
|
|
502
|
+
fs.unlinkSync(path.join(node.TTSRootFolderPath, "ttspermanentfiles", file));
|
|
498
503
|
} catch (error) { }
|
|
499
504
|
}
|
|
500
505
|
});
|
|
@@ -504,7 +509,7 @@ module.exports = function (RED) {
|
|
|
504
509
|
} else {
|
|
505
510
|
// Delete only one file
|
|
506
511
|
try {
|
|
507
|
-
var newPath = path.join(node.
|
|
512
|
+
var newPath = path.join(node.TTSRootFolderPath, "ttspermanentfiles", req.query.FileName);
|
|
508
513
|
try {
|
|
509
514
|
fs.unlinkSync(newPath)
|
|
510
515
|
} catch (error) { }
|
|
@@ -533,22 +538,20 @@ module.exports = function (RED) {
|
|
|
533
538
|
if (node.purgediratrestart === "purge") {
|
|
534
539
|
// Delete all files, that are'nt OwnFiles_
|
|
535
540
|
try {
|
|
536
|
-
fs.readdirSync(path.join(node.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
541
|
+
let files = fs.readdirSync(path.join(node.TTSRootFolderPath, "ttsfiles"));
|
|
542
|
+
try {
|
|
543
|
+
if (files.length > 0) {
|
|
544
|
+
files.forEach(function (file) {
|
|
545
|
+
RED.log.info("ttsultimate-config " + node.id + ": Deleted TTS file " + path.join(node.TTSRootFolderPath, "ttsfiles", file));
|
|
546
|
+
try {
|
|
547
|
+
fs.unlinkSync(path.join(node.TTSRootFolderPath, "ttsfiles", file));
|
|
548
|
+
} catch (error) {
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
};
|
|
552
|
+
} catch (error) { }
|
|
548
553
|
|
|
549
|
-
}
|
|
550
554
|
|
|
551
|
-
});
|
|
552
555
|
} catch (error) { }
|
|
553
556
|
};
|
|
554
557
|
|
|
@@ -567,13 +570,13 @@ module.exports = function (RED) {
|
|
|
567
570
|
var query = url_parts.query;
|
|
568
571
|
|
|
569
572
|
res.setHeader('Content-Disposition', 'attachment; filename=tts.mp3')
|
|
570
|
-
if (fs.existsSync(query.f)) {
|
|
573
|
+
if (fs.existsSync(query.f.toString())) {
|
|
571
574
|
// 26/01/2021 security check
|
|
572
575
|
// File should be something like mydocs/.node-red/sonospollyttsstorage/ttsfiles/Hello_de-DE.mp3
|
|
573
|
-
if (path.extname(query.f) === ".mp3" && path.dirname(path.dirname(query.f)).endsWith("sonospollyttsstorage")) {
|
|
574
|
-
var readStream = fs.createReadStream(query.f);
|
|
576
|
+
if (path.extname(query.f.toString()) === ".mp3" && path.dirname(path.dirname(query.f.toString())).endsWith("sonospollyttsstorage")) {
|
|
577
|
+
var readStream = fs.createReadStream(query.f.toString());
|
|
575
578
|
readStream.on("error", function (error) {
|
|
576
|
-
RED.log.error("ttsultimate-config " + node.id + ": Playsonos error opening stream : " + query.f + ' : ' + error);
|
|
579
|
+
RED.log.error("ttsultimate-config " + node.id + ": Playsonos error opening stream : " + query.f.toString() + ' : ' + error);
|
|
577
580
|
res.end();
|
|
578
581
|
return;
|
|
579
582
|
});
|
|
@@ -632,7 +635,7 @@ module.exports = function (RED) {
|
|
|
632
635
|
} catch (error) {
|
|
633
636
|
|
|
634
637
|
}
|
|
635
|
-
setTimeout(function () {
|
|
638
|
+
let t = setTimeout(function () {
|
|
636
639
|
// Wait some time to allow time to do promises.
|
|
637
640
|
done();
|
|
638
641
|
}, 500);
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
};
|
|
217
217
|
|
|
218
218
|
|
|
219
|
-
// 26/10/2020 Retrieve all avaiables
|
|
219
|
+
// 26/10/2020 Retrieve all avaiables voices
|
|
220
220
|
// #####################################
|
|
221
221
|
function getVoices() {
|
|
222
222
|
$('#node-input-voice')
|
|
@@ -336,7 +336,7 @@
|
|
|
336
336
|
}
|
|
337
337
|
// Refresh the combo
|
|
338
338
|
// The only way is to wait some time, then refresh
|
|
339
|
-
setTimeout(function () {
|
|
339
|
+
let t = setTimeout(function () {
|
|
340
340
|
node.refreshHailingList().then((success, error) => {
|
|
341
341
|
$("#ownFileUpload").val("");// Otherwise will not re-upload a file with the same name
|
|
342
342
|
$("#node-input-sonoshailing").val("Hailing_" + file.name);
|
|
@@ -2,11 +2,10 @@ module.exports = function (RED) {
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var fs = require('fs');
|
|
5
|
-
var MD5 = require('crypto-js').MD5;
|
|
6
5
|
var util = require('util');
|
|
7
6
|
var path = require('path');
|
|
8
7
|
const sonos = require('sonos');
|
|
9
|
-
|
|
8
|
+
const crypto = require("crypto");
|
|
10
9
|
|
|
11
10
|
function slug(_text) {
|
|
12
11
|
var sRet = _text;
|
|
@@ -53,7 +52,7 @@ module.exports = function (RED) {
|
|
|
53
52
|
return;
|
|
54
53
|
}
|
|
55
54
|
node.ssml = config.ssml;
|
|
56
|
-
node.oTimerSonosConnectionCheck;
|
|
55
|
+
node.oTimerSonosConnectionCheck = null;
|
|
57
56
|
node.sSonosIPAddress = "";
|
|
58
57
|
node.sonosCoordinatorGroupName = "";
|
|
59
58
|
node.sonoshailing = "0"; // Hailing file
|
|
@@ -64,7 +63,7 @@ module.exports = function (RED) {
|
|
|
64
63
|
node.msg = {}; // 08/05/2019 Node message
|
|
65
64
|
node.msg.completed = true;
|
|
66
65
|
node.msg.connectionerror = true;
|
|
67
|
-
node.userDir = path.join(RED.settings.userDir, "sonospollyttsstorage")
|
|
66
|
+
node.userDir = node.server.TTSRootFolderPath === undefined ? path.join(RED.settings.userDir, "sonospollyttsstorage") : node.server.TTSRootFolderPath;
|
|
68
67
|
node.oAdditionalSonosPlayers = []; // 20/03/2020 Contains other players to be grouped
|
|
69
68
|
node.rules = config.rules || [{}];
|
|
70
69
|
node.sNoderedURL = "";
|
|
@@ -78,6 +77,8 @@ module.exports = function (RED) {
|
|
|
78
77
|
node.speakingrate = config.speakingrate === undefined ? "1" : config.speakingrate; // 21/09/2021 AudioConfig speakingrate
|
|
79
78
|
node.unmuteIfMuted = config.unmuteIfMuted === undefined ? false : config.unmuteIfMuted; // 21/10/2021 Unmute if previiously muted.
|
|
80
79
|
node.sonosCoordinatorIsPreviouslyMuted = false;
|
|
80
|
+
node.passThroughMessage = {};
|
|
81
|
+
node.bTimeOutPlay = false;
|
|
81
82
|
|
|
82
83
|
if (typeof node.server !== "undefined" && node.server !== null) {
|
|
83
84
|
node.sNoderedURL = node.server.sNoderedURL || "";
|
|
@@ -120,7 +121,7 @@ module.exports = function (RED) {
|
|
|
120
121
|
return new Promise((resolve, reject) => {
|
|
121
122
|
node.SonosClient.play(_toPlay).then(result => {
|
|
122
123
|
if (iWaitAfterSync > 2000) console.log("PLAYSYNC")
|
|
123
|
-
setTimeout(() => {
|
|
124
|
+
let t = setTimeout(() => {
|
|
124
125
|
resolve(true);
|
|
125
126
|
}, iWaitAfterSync);
|
|
126
127
|
}).catch(err => {
|
|
@@ -135,7 +136,7 @@ module.exports = function (RED) {
|
|
|
135
136
|
return new Promise((resolve, reject) => {
|
|
136
137
|
node.SonosClient.seek(_Position).then(result => {
|
|
137
138
|
if (iWaitAfterSync > 2000) console.log("SEEKSync", _Position)
|
|
138
|
-
setTimeout(() => {
|
|
139
|
+
let t = setTimeout(() => {
|
|
139
140
|
resolve(true);
|
|
140
141
|
}, iWaitAfterSync);
|
|
141
142
|
}).catch(err => {
|
|
@@ -154,7 +155,7 @@ module.exports = function (RED) {
|
|
|
154
155
|
STOPSync(); // The SetQueue automatically starts playing, so i need to stop it now!
|
|
155
156
|
} catch (error) {
|
|
156
157
|
}
|
|
157
|
-
setTimeout(() => {
|
|
158
|
+
let t = setTimeout(() => {
|
|
158
159
|
resolve(true);
|
|
159
160
|
}, iWaitAfterSync);
|
|
160
161
|
}).catch(err => {
|
|
@@ -169,7 +170,7 @@ module.exports = function (RED) {
|
|
|
169
170
|
return new Promise((resolve, reject) => {
|
|
170
171
|
node.SonosClient.selectTrack(_queuePositiom).then(result => {
|
|
171
172
|
if (iWaitAfterSync > 2000) console.log("SELECTTRACKSync", _queuePositiom)
|
|
172
|
-
setTimeout(() => {
|
|
173
|
+
let t = setTimeout(() => {
|
|
173
174
|
resolve(true);
|
|
174
175
|
}, iWaitAfterSync);
|
|
175
176
|
}).catch(err => {
|
|
@@ -184,7 +185,7 @@ module.exports = function (RED) {
|
|
|
184
185
|
return new Promise((resolve, reject) => {
|
|
185
186
|
node.SonosClient.stop().then(result => {
|
|
186
187
|
if (iWaitAfterSync > 2000) console.log("STOPSync")
|
|
187
|
-
setTimeout(() => {
|
|
188
|
+
let t = setTimeout(() => {
|
|
188
189
|
resolve(true);
|
|
189
190
|
}, iWaitAfterSync);
|
|
190
191
|
}).catch(err => {
|
|
@@ -509,7 +510,7 @@ module.exports = function (RED) {
|
|
|
509
510
|
}
|
|
510
511
|
}
|
|
511
512
|
}
|
|
512
|
-
setTimeout(() => { return true; }, 5000); // Wait some seconds
|
|
513
|
+
let t = setTimeout(() => { return true; }, 5000); // Wait some seconds
|
|
513
514
|
};
|
|
514
515
|
|
|
515
516
|
// Handle the queue
|
|
@@ -536,7 +537,7 @@ module.exports = function (RED) {
|
|
|
536
537
|
RED.log.error("ttsultimate: Error grouping speakers: " + error.message);
|
|
537
538
|
}
|
|
538
539
|
|
|
539
|
-
node.send([{ payload: node.msg.completed }, null]);
|
|
540
|
+
node.send([{ passThroughMessage: node.passThroughMessage, payload: node.msg.completed }, null]);
|
|
540
541
|
|
|
541
542
|
// 24/08/2021 If something was playing, stop the player https://github.com/Supergiovane/node-red-contrib-tts-ultimate/issues/32
|
|
542
543
|
try {
|
|
@@ -578,9 +579,16 @@ module.exports = function (RED) {
|
|
|
578
579
|
OutputFormat: "mp3",
|
|
579
580
|
SampleRate: '22050',
|
|
580
581
|
Text: msg,
|
|
581
|
-
TextType: node.ssml ? 'ssml' : 'text'
|
|
582
|
-
VoiceId: node.voiceId
|
|
582
|
+
TextType: node.ssml ? 'ssml' : 'text'
|
|
583
583
|
};
|
|
584
|
+
// 02/03/2022 check wether standard or neural engine is POLLY is selected
|
|
585
|
+
if (node.voiceId.includes("#engineType:")) {
|
|
586
|
+
params.VoiceId = node.voiceId.split("#engineType:")[0];
|
|
587
|
+
params.Engine = node.voiceId.split("#engineType:")[1];
|
|
588
|
+
} else {
|
|
589
|
+
params.VoiceId = node.voiceId;
|
|
590
|
+
}
|
|
591
|
+
|
|
584
592
|
data = await synthesizeSpeechPolly([node.server.polly, params]);
|
|
585
593
|
} else if (node.server.ttsservice === "googletts") {
|
|
586
594
|
node.setNodeStatus({ fill: 'green', shape: 'ring', text: 'Downloading from Google TTS...' });
|
|
@@ -612,13 +620,14 @@ module.exports = function (RED) {
|
|
|
612
620
|
data = await synthesizeSpeechMicrosoftAzureTTS(node.server.microsoftAzureTTS, params);
|
|
613
621
|
}
|
|
614
622
|
// Save the downloaded file into the cache
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}
|
|
623
|
+
try {
|
|
624
|
+
fs.writeFileSync(sFileToBePlayed, data);
|
|
625
|
+
} catch (error) {
|
|
626
|
+
RED.log.error("ttsultimate: node id: " + node.id + " Unable to save the file " + error.message);
|
|
627
|
+
node.setNodeStatus({ fill: "red", shape: "ring", text: "Unable to save the file " + sFileToBePlayed + " " + error.message });
|
|
628
|
+
throw (error);
|
|
629
|
+
}
|
|
630
|
+
|
|
622
631
|
} catch (error) {
|
|
623
632
|
RED.log.error("ttsultimate: node id: " + node.id + " Error Downloading TTS: " + error.message + ". THE TTS SERVICE MAY BE DOWN.");
|
|
624
633
|
node.setNodeStatus({ fill: 'red', shape: 'ring', text: 'Error Downloading TTS:' + error.message });
|
|
@@ -774,10 +783,10 @@ module.exports = function (RED) {
|
|
|
774
783
|
}
|
|
775
784
|
|
|
776
785
|
// Signal end playing
|
|
777
|
-
setTimeout(() => {
|
|
786
|
+
let t = setTimeout(() => {
|
|
778
787
|
node.msg.completed = true;
|
|
779
788
|
node.currentMSGbeingSpoken = {};
|
|
780
|
-
node.send([{ payload: node.msg.completed }, null]);
|
|
789
|
+
node.send([{ passThroughMessage: node.passThroughMessage, payload: node.msg.completed }, null]);
|
|
781
790
|
node.bBusyPlayingQueue = false
|
|
782
791
|
node.server.whoIsUsingTheServer = ""; // Signal to other ttsultimate node, that i'm not using the Sonos device anymore
|
|
783
792
|
}, 1000)
|
|
@@ -787,10 +796,10 @@ module.exports = function (RED) {
|
|
|
787
796
|
// Output the array of files
|
|
788
797
|
|
|
789
798
|
// Signal end playing
|
|
790
|
-
setTimeout(() => {
|
|
799
|
+
let t = setTimeout(() => {
|
|
791
800
|
node.msg.completed = true;
|
|
792
801
|
node.currentMSGbeingSpoken = {};
|
|
793
|
-
node.send([{ payload: node.msg.completed, filesArray: noPlayerFileArray }, null]);
|
|
802
|
+
node.send([{ passThroughMessage: node.passThroughMessage, payload: node.msg.completed, filesArray: noPlayerFileArray }, null]);
|
|
794
803
|
node.bBusyPlayingQueue = false
|
|
795
804
|
node.server.whoIsUsingTheServer = ""; // Signal to other ttsultimate node, that i'm not using the Sonos device anymore
|
|
796
805
|
}, 1000)
|
|
@@ -811,6 +820,9 @@ module.exports = function (RED) {
|
|
|
811
820
|
// return;
|
|
812
821
|
// }
|
|
813
822
|
|
|
823
|
+
// 05/01/2022 Set the passtrough message o come cazzo si scrive
|
|
824
|
+
node.passThroughMessage = RED.util.cloneMessage(msg);
|
|
825
|
+
|
|
814
826
|
// 09/01/2021 Set the main player and groups IP on request
|
|
815
827
|
// *********************************
|
|
816
828
|
if (msg.hasOwnProperty("setConfig")) {
|
|
@@ -941,45 +953,46 @@ module.exports = function (RED) {
|
|
|
941
953
|
}
|
|
942
954
|
// ########################
|
|
943
955
|
|
|
944
|
-
// 03/01/2022 if ssml is enabled, disable the auto split function
|
|
945
|
-
if (!node.ssml) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
} else {
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
}
|
|
956
|
+
// // 03/01/2022 if ssml is enabled, disable the auto split function
|
|
957
|
+
// if (!node.ssml) {
|
|
958
|
+
// // SSML disabled
|
|
959
|
+
// // 30/01/2021 split the text if it's too long, otherwies i'll have issues with filename too long.
|
|
960
|
+
// if (msg.payload.length >= node.server.limitTTSFilenameLenght) {
|
|
961
|
+
// let sTemp = "";
|
|
962
|
+
// let aSeps = [".", ",", ":", ";", "!", "?"];
|
|
963
|
+
// let sPayload = msg.payload.replace(/[\r\n]+/gm, "");
|
|
964
|
+
// for (let index = 0; index < sPayload.length; index++) {
|
|
965
|
+
// const element = sPayload.substr(index, 1);
|
|
966
|
+
// sTemp += element;
|
|
967
|
+
// if (aSeps.indexOf(element) > -1 && sTemp.length > 20) {
|
|
968
|
+
// const oMsg = RED.util.cloneMessage(msg);
|
|
969
|
+
// oMsg.payload = sTemp;
|
|
970
|
+
// node.tempMSGStorage.push(oMsg);
|
|
971
|
+
// sTemp = "";
|
|
972
|
+
// }
|
|
973
|
+
// if (sTemp.length > node.server.limitTTSFilenameLenght && element === " ") {
|
|
974
|
+
// // Split using space
|
|
975
|
+
// const oMsg = RED.util.cloneMessage(msg);
|
|
976
|
+
// oMsg.payload = sTemp;
|
|
977
|
+
// node.tempMSGStorage.push(oMsg);
|
|
978
|
+
// sTemp = "";
|
|
979
|
+
// }
|
|
980
|
+
// }
|
|
981
|
+
// // Remaining
|
|
982
|
+
// const oMsg = RED.util.cloneMessage(msg);
|
|
983
|
+
// oMsg.payload = sTemp;
|
|
984
|
+
// node.tempMSGStorage.push(oMsg);
|
|
985
|
+
|
|
986
|
+
// } else {
|
|
987
|
+
// node.tempMSGStorage.push(msg);
|
|
988
|
+
// }
|
|
989
|
+
// } else {
|
|
990
|
+
// // SSML enabled
|
|
991
|
+
// node.tempMSGStorage.push(msg);
|
|
992
|
+
// }
|
|
981
993
|
|
|
982
994
|
// Starts main queue watching
|
|
995
|
+
node.tempMSGStorage.push(msg);
|
|
983
996
|
node.waitForQueue();
|
|
984
997
|
|
|
985
998
|
});
|
|
@@ -1007,7 +1020,7 @@ module.exports = function (RED) {
|
|
|
1007
1020
|
clearTimeout(node.oTimerSonosConnectionCheck);
|
|
1008
1021
|
if (node.timerbTimeOutPlay !== null) clearTimeout(node.timerbTimeOutPlay);
|
|
1009
1022
|
node.msg.completed = true;
|
|
1010
|
-
node.send([{ payload: node.msg.completed }, null]);
|
|
1023
|
+
node.send([{ passThroughMessage: node.passThroughMessage, payload: node.msg.completed }, null]);
|
|
1011
1024
|
node.setNodeStatus({ fill: "green", shape: "ring", text: "Shutdown" });
|
|
1012
1025
|
node.flushQueue();
|
|
1013
1026
|
done();
|
|
@@ -1075,28 +1088,12 @@ module.exports = function (RED) {
|
|
|
1075
1088
|
});
|
|
1076
1089
|
};
|
|
1077
1090
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
// Filename format: "text_voice.mp3"
|
|
1086
|
-
var filename = util.format('%s_%s%s%s%s.%s', basename, _sVoice, _speakingpitch, _speakingrate, ssml_text, extension);
|
|
1087
|
-
|
|
1088
|
-
// If filename is too long, cut it and add hash
|
|
1089
|
-
if (filename.length > 250) {
|
|
1090
|
-
var hash = MD5(basename);
|
|
1091
|
-
|
|
1092
|
-
// Filename format: "text_hash_voice.mp3"
|
|
1093
|
-
var ending = util.format('_%s_%s%s%s%s.%s', hash, _sVoice, _speakingpitch, _speakingrate, ssml_text, extension);
|
|
1094
|
-
var beginning = basename.slice(0, 250 - ending.length);
|
|
1095
|
-
|
|
1096
|
-
filename = beginning + ending;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
return filename;
|
|
1091
|
+
// 04/01/2021 hashing filename to avoid issues with long filenames.
|
|
1092
|
+
function getFilename(_text, _sVoice, _isSSML, _extension, _speakingpitch, _speakingrate) {
|
|
1093
|
+
let sTextToBeHashed = _text.concat(_sVoice, _isSSML, _speakingpitch, _speakingrate);
|
|
1094
|
+
const hashSum = crypto.createHash('md5');
|
|
1095
|
+
hashSum.update(sTextToBeHashed);
|
|
1096
|
+
return hashSum.digest('hex') + "." + _extension;
|
|
1100
1097
|
}
|
|
1101
1098
|
|
|
1102
1099
|
function notifyError(msg, err) {
|
|
@@ -1109,7 +1106,6 @@ module.exports = function (RED) {
|
|
|
1109
1106
|
});
|
|
1110
1107
|
// Set error in message
|
|
1111
1108
|
msg.error = errorMessage;
|
|
1112
|
-
|
|
1113
1109
|
}
|
|
1114
1110
|
|
|
1115
1111
|
|