node-red-contrib-tts-ultimate 3.1.1 → 3.2.1
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 +17 -0
- package/README.md +152 -68
- package/img/audio-file.svg +8 -0
- package/img/logo-v2.svg +39 -0
- package/package.json +7 -7
- package/scripts/discover-dlna.js +44 -0
- package/scripts/verify-googletranslate-split.js +1 -1
- package/ttsultimate/lib/dlna-discovery.js +98 -0
- package/ttsultimate/lib/dlna-player.js +163 -0
- package/ttsultimate/lib/googlecast-discovery.js +69 -0
- package/ttsultimate/lib/googletranslate.js +140 -0
- package/ttsultimate/ttsultimate-config copy.js +1 -1
- package/ttsultimate/ttsultimate-config.html +3 -3
- package/ttsultimate/ttsultimate-config.js +98 -28
- package/ttsultimate/ttsultimate.html +139 -19
- package/ttsultimate/ttsultimate.js +153 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.paypal.me/techtoday)
|
|
4
4
|
|
|
5
|
+
<b>Version 3.2.0</b> June 2026<br/>
|
|
6
|
+
|
|
7
|
+
- NEW: **Google Cast** player (Chromecast / Google Nest). Devices are discovered via mDNS (press "Discover" in the node), with multi-room playback on the main device + additional players.<br/>
|
|
8
|
+
- NEW: **DLNA / UPnP renderer** player (smart TVs, AV receivers, etc.). Devices are discovered via SSDP. A native UPnP client locates the AVTransport/RenderingControl services anywhere in the device tree, so it also works with renderers that nest a MediaRenderer sub-device (e.g. Sonos).<br/>
|
|
9
|
+
- NEW: the "Additional players" list now works for Google Cast and DLNA too (not only Sonos), to play the announcement on several devices at once.<br/>
|
|
10
|
+
- FIX: the built-in HTTP file server now serves the TTS audio with proper streaming headers (Content-Type, Content-Length, byte-range support and DLNA headers), so strict DLNA renderers (some TVs) start playing correctly.<br/>
|
|
11
|
+
- CHORE: removed the "upnp-mediarenderer-client" dependency (replaced by a native UPnP client); added "castv2-client" and "multicast-dns".<br/>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p>
|
|
15
|
+
<b>Version 3.1.2</b> June 2026<br/>
|
|
16
|
+
|
|
17
|
+
- CHORE: removed the external "google-translate-tts" dependency, replaced by a native built-in implementation (same voices and behaviour).<br/>
|
|
18
|
+
- CHORE: removed the redundant "path" dependency (Node.js built-in module is used instead).<br/>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p>
|
|
5
22
|
<b>Version 3.1.1</b> June 2026<br/>
|
|
6
23
|
|
|
7
24
|
- NEW: voice option fields (ElevenLabs Stability/Similarity/Style/Speed and Google Rate/Pitch) are now sliders showing the current value live.<br/>
|
package/README.md
CHANGED
|
@@ -1,16 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="img/logo-v2.svg" alt="TTS Ultimate" width="700">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://npmjs.org/package/node-red-contrib-tts-ultimate"><img src="https://img.shields.io/npm/v/node-red-contrib-tts-ultimate.svg" alt="NPM version"></a>
|
|
7
|
+
<a href="https://npmjs.org/package/node-red-contrib-tts-ultimate"><img src="https://img.shields.io/npm/dm/node-red-contrib-tts-ultimate.svg" alt="NPM downloads per month"></a>
|
|
8
|
+
<a href="https://npmjs.org/package/node-red-contrib-tts-ultimate"><img src="https://img.shields.io/npm/dt/node-red-contrib-tts-ultimate.svg" alt="NPM downloads total"></a>
|
|
9
|
+
<a href="https://github.com/Supergiovane/node-red-contrib-tts-ultimate/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License"></a>
|
|
10
|
+
<a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="JavaScript Standard Style"></a>
|
|
11
|
+
<a href="https://www.paypal.me/techtoday"><img src="https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square" alt="Donate via PayPal"></a>
|
|
12
|
+
<a href="https://www.facebook.com/supergiovaneDev"><img src="https://img.shields.io/badge/Visit%20me-Facebook-blue" alt="Visit me on Facebook"></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<strong>Text-to-speech for Node-RED with Sonos, Google Cast, DLNA/UPnP and file-only output.</strong>
|
|
17
|
+
</p>
|
|
2
18
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="#description">Description</a> ·
|
|
21
|
+
<a href="#supported-tts-engines">TTS engines</a> ·
|
|
22
|
+
<a href="#supported-players">Players</a> ·
|
|
23
|
+
<a href="#features">Features</a> ·
|
|
24
|
+
<a href="#tts-service-node">Configuration</a> ·
|
|
25
|
+
<a href="#ownfile-node-configuration">OwnFile</a>
|
|
26
|
+
</p>
|
|
10
27
|
|
|
11
|
-
<
|
|
28
|
+
<p align="center">
|
|
29
|
+
<img src="https://raw.githubusercontent.com/Supergiovane/node-red-contrib-tts-ultimate/master/img/main.png" width="82%" alt="TTS Ultimate Node-RED flow">
|
|
30
|
+
</p>
|
|
12
31
|
|
|
13
|
-
<details><summary>
|
|
32
|
+
<details><summary><strong>VIEW SAMPLE FLOW CODE</strong></summary>
|
|
14
33
|
|
|
15
34
|
> Adjust the nodes according to your setup
|
|
16
35
|
|
|
@@ -161,30 +180,95 @@
|
|
|
161
180
|
|
|
162
181
|
## DESCRIPTION
|
|
163
182
|
|
|
164
|
-
|
|
165
|
-
You can also generate an audio file for bluetooth speakers, web pages, etc.<br/>
|
|
166
|
-
You can also 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/>
|
|
167
|
-
The node can also create a **_TTS file (without the use of any Sonos device)_**, to be read by third parties nodes.<br/>
|
|
168
|
-
This is a major **_upgrade from the previously popular node SonosPollyTTS_** (SonosPollyTTS is not developed anymore).<br/>
|
|
183
|
+
TTS Ultimate transforms text into speech audio from Node-RED. It can play announcements directly on **Sonos** speakers, **Google Cast** devices (Chromecast / Google Nest) and generic **DLNA/UPnP** renderers such as smart TVs and AV receivers.
|
|
169
184
|
|
|
170
|
-
|
|
185
|
+
It can also work without any player: generate an audio file, cache it, and pass it to other nodes, web pages, Bluetooth workflows or third-party services. With the OwnFile node, you can upload and play your own audio messages and keep selected flows working completely offline.
|
|
171
186
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
</a>
|
|
176
|
-
<a href="https://cloud.google.com/text-to-speech" title="Google Cloud Text-to-Speech">
|
|
177
|
-
<img src="https://cdn.simpleicons.org/googlecloud/4285F4" height="126" alt="Google Cloud logo"/>
|
|
178
|
-
</a>
|
|
179
|
-
<a href="https://elevenlabs.io" title="ElevenLabs">
|
|
180
|
-
<img src="https://cdn.simpleicons.org/elevenlabs/000000" height="126" alt="ElevenLabs logo"/>
|
|
181
|
-
</a>
|
|
182
|
-
<a href="https://voice.ai/docs/api-reference/text-to-speech/generate-speech" title="Voice.ai">
|
|
183
|
-
<img src="https://voice.ai/favicon.ico" height="126" alt="Voice.ai logo"/>
|
|
184
|
-
</a>
|
|
185
|
-
</p>
|
|
187
|
+
This is a major upgrade from the previously popular **SonosPollyTTS** node, which is no longer developed.
|
|
188
|
+
|
|
189
|
+
## SUPPORTED TTS ENGINES
|
|
186
190
|
|
|
187
|
-
|
|
191
|
+
<table>
|
|
192
|
+
<tr>
|
|
193
|
+
<td align="center" width="25%">
|
|
194
|
+
<a href="https://www.npmjs.com/package/google-translate-tts" title="Google Translate TTS (free)">
|
|
195
|
+
<img src="https://cdn.simpleicons.org/googletranslate/4285F4" height="86" alt="Google Translate logo"/>
|
|
196
|
+
</a>
|
|
197
|
+
<br/>
|
|
198
|
+
<strong>Google free TTS</strong>
|
|
199
|
+
<br/>
|
|
200
|
+
<sub>No credentials required</sub>
|
|
201
|
+
</td>
|
|
202
|
+
<td align="center" width="25%">
|
|
203
|
+
<a href="https://cloud.google.com/text-to-speech" title="Google Cloud Text-to-Speech">
|
|
204
|
+
<img src="https://cdn.simpleicons.org/googlecloud/4285F4" height="86" alt="Google Cloud logo"/>
|
|
205
|
+
</a>
|
|
206
|
+
<br/>
|
|
207
|
+
<strong>Google Cloud TTS</strong>
|
|
208
|
+
<br/>
|
|
209
|
+
<sub>Advanced voices and tuning</sub>
|
|
210
|
+
</td>
|
|
211
|
+
<td align="center" width="25%">
|
|
212
|
+
<a href="https://elevenlabs.io" title="ElevenLabs">
|
|
213
|
+
<img src="https://cdn.simpleicons.org/elevenlabs/000000" height="86" alt="ElevenLabs logo"/>
|
|
214
|
+
</a>
|
|
215
|
+
<br/>
|
|
216
|
+
<strong>ElevenLabs</strong>
|
|
217
|
+
<br/>
|
|
218
|
+
<sub>V1 and V2 multilingual voices</sub>
|
|
219
|
+
</td>
|
|
220
|
+
<td align="center" width="25%">
|
|
221
|
+
<a href="https://voice.ai/docs/api-reference/text-to-speech/generate-speech" title="Voice.ai">
|
|
222
|
+
<img src="https://voice.ai/favicon.ico" height="86" alt="Voice.ai logo"/>
|
|
223
|
+
</a>
|
|
224
|
+
<br/>
|
|
225
|
+
<strong>Voice.ai</strong>
|
|
226
|
+
<br/>
|
|
227
|
+
<sub>API key based voices</sub>
|
|
228
|
+
</td>
|
|
229
|
+
</tr>
|
|
230
|
+
</table>
|
|
231
|
+
|
|
232
|
+
## SUPPORTED PLAYERS
|
|
233
|
+
|
|
234
|
+
<table>
|
|
235
|
+
<tr>
|
|
236
|
+
<td align="center" width="25%">
|
|
237
|
+
<a href="https://www.sonos.com" title="Sonos speakers">
|
|
238
|
+
<img src="https://cdn.simpleicons.org/sonos/000000" height="86" alt="Sonos logo"/>
|
|
239
|
+
</a>
|
|
240
|
+
<br/>
|
|
241
|
+
<strong>Sonos</strong>
|
|
242
|
+
<br/>
|
|
243
|
+
<sub>Native speakers and groups</sub>
|
|
244
|
+
</td>
|
|
245
|
+
<td align="center" width="25%">
|
|
246
|
+
<a href="https://developers.google.com/cast" title="Google Cast devices">
|
|
247
|
+
<img src="https://cdn.simpleicons.org/googlecast/4285F4" height="86" alt="Google Cast logo"/>
|
|
248
|
+
</a>
|
|
249
|
+
<br/>
|
|
250
|
+
<strong>Google Cast</strong>
|
|
251
|
+
<br/>
|
|
252
|
+
<sub>Chromecast and Google Nest</sub>
|
|
253
|
+
</td>
|
|
254
|
+
<td align="center" width="25%">
|
|
255
|
+
<a href="https://www.dlna.org" title="DLNA and UPnP renderers">
|
|
256
|
+
<img src="https://cdn.simpleicons.org/dlna/0066CC" height="86" alt="DLNA logo"/>
|
|
257
|
+
</a>
|
|
258
|
+
<br/>
|
|
259
|
+
<strong>DLNA / UPnP</strong>
|
|
260
|
+
<br/>
|
|
261
|
+
<sub>Smart TVs, AV receivers and renderers</sub>
|
|
262
|
+
</td>
|
|
263
|
+
<td align="center" width="25%">
|
|
264
|
+
<img src="img/audio-file.svg" height="86" alt="Audio file icon"/>
|
|
265
|
+
<br/>
|
|
266
|
+
<strong>No player</strong>
|
|
267
|
+
<br/>
|
|
268
|
+
<sub>Create an audio file only</sub>
|
|
269
|
+
</td>
|
|
270
|
+
</tr>
|
|
271
|
+
</table>
|
|
188
272
|
|
|
189
273
|
## CHANGELOG
|
|
190
274
|
|
|
@@ -192,22 +276,22 @@ This is a major **_upgrade from the previously popular node SonosPollyTTS_** (So
|
|
|
192
276
|
|
|
193
277
|
## FEATURES
|
|
194
278
|
|
|
195
|
-
- **Native Sonos support**:
|
|
196
|
-
- **
|
|
197
|
-
- **
|
|
198
|
-
- **
|
|
199
|
-
- **
|
|
200
|
-
- **Automatic
|
|
201
|
-
- **
|
|
202
|
-
- **
|
|
203
|
-
- **
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
279
|
+
- **Native Sonos support**: play TTS audio directly via Sonos, group speakers, set a hailing sound and choose the volume for each speaker.
|
|
280
|
+
- **Google Cast support**: play announcements on Chromecast / Google Nest devices, including multi-room playback.
|
|
281
|
+
- **DLNA / UPnP renderer support**: play TTS on smart TVs, AV receivers and other UPnP renderers. Renderers with nested MediaRenderer devices are supported too.
|
|
282
|
+
- **File-only output**: create the TTS file without using any player, then pass it to other nodes or services.
|
|
283
|
+
- **Multiple TTS engines**: Google free TTS, Google Cloud TTS, ElevenLabs and Voice.ai voices are supported.
|
|
284
|
+
- **Automatic grouping / multi-room**: add additional players and play one announcement on several devices at once.
|
|
285
|
+
- **Automatic discovery**: find Sonos and DLNA/UPnP renderers via SSDP, and Google Cast devices via mDNS.
|
|
286
|
+
- **Music resume**: resume the previous queue after an announcement where supported. Some radio-station queues may not resume reliably because of Sonos API limitations.
|
|
287
|
+
- **TTS caching**: generated audio is downloaded once and then served from cache. The cache survives reboots and updates.
|
|
288
|
+
- **Offline-ready OwnFile playback**: upload custom audio messages and use them without an internet connection.
|
|
289
|
+
|
|
290
|
+
## BREAKING CHANGE
|
|
291
|
+
|
|
292
|
+
> **Version 3.0.0 - April 2025**
|
|
293
|
+
>
|
|
294
|
+
> Amazon Polly and Microsoft Azure TTS have been removed because the old APIs required a larger update. Contributions are welcome via fork and PR. If you still need those TTS engines, stay on or revert to `2.0.10`.
|
|
211
295
|
|
|
212
296
|
---
|
|
213
297
|
|
|
@@ -215,26 +299,26 @@ This is a major **_upgrade from the previously popular node SonosPollyTTS_** (So
|
|
|
215
299
|
|
|
216
300
|
> **_NOTE IF YOU CANNOT UPLOAD YOUR OWN FILES_**
|
|
217
301
|
>
|
|
218
|
-
> If you're running
|
|
219
|
-
> You may
|
|
302
|
+
> If you're running Node-RED as a plugin for Home Assistant, RedMatic, etc...<br/>
|
|
303
|
+
> You may not be able to upload your own files. Please check that the user running Node-RED has permission **to write to the filesystem**.<br/>
|
|
220
304
|
|
|
221
305
|
<br/><br/>
|
|
222
306
|
|
|
223
|
-
|
|
307
|
+
## TTS Service Node
|
|
224
308
|
|
|
225
309
|
Here you can set all parameters you need. All nodes will refer to this config node, so you need to set it only once.<br/>
|
|
226
310
|
IF YOU RUN NODE-RED BEHIND DOCKER OR SOMETHING ELSE, BE AWARE: <br/>
|
|
227
|
-
|
|
311
|
+
PORTS USED BY THE NODE ARE 1980 (DEFAULT, HTTP FILE SERVER) AND 1400 (FOR SONOS DISCOVERY). <br/>
|
|
228
312
|
PLEASE ALLOW MDNS AND UDP AS WELL
|
|
229
313
|
|
|
230
314
|
**TTS Service**<br/>
|
|
231
|
-
You can choose between Voice.ai,
|
|
315
|
+
You can choose between Voice.ai, ElevenLabs.io, Google (without credentials), and Google TTS (requires credentials and registration with Google).<br/>
|
|
232
316
|
For Google TTS Engine, you can choose pitch and speed rate of the voice.
|
|
233
317
|
<br/>
|
|
234
318
|
<br/>
|
|
235
319
|
|
|
236
320
|
- **TTS Service using Google (without credentials)**<br/>
|
|
237
|
-
This is the simplest way. Just select the voice and you're done. You don't need any credential and you don't even need to be registered to any
|
|
321
|
+
This is the simplest way. Just select the voice and you're done. You don't need any credential and you don't even need to be registered to any Google service. The voice list is more limited than other services, but it works without hassles.
|
|
238
322
|
Note: long texts are automatically split into 200-character chunks (Google Translate TTS limit) and merged into a single audio output.
|
|
239
323
|
Manual verify: `npm run verify:googletranslate-split -- --voice it-IT --text "..." --out ./out.mp3`
|
|
240
324
|
|
|
@@ -251,21 +335,21 @@ For Google TTS Engine, you can choose pitch and speed rate of the voice.
|
|
|
251
335
|
<br/>
|
|
252
336
|
|
|
253
337
|
- **TTS Service using ElevenLabs**<br/>
|
|
254
|
-
Please use the V2 engine, as the V1 is deprecated and will
|
|
255
|
-
You have two
|
|
256
|
-
After registration at
|
|
338
|
+
Please use the V2 engine, as the V1 is deprecated and will no longer be supported. The V2 has multilingual voices and is more powerful.<br/>
|
|
339
|
+
You have two choices: register to ElevenLabs, or do not register. If you don't register to ElevenLabs.io, you will either have access to a limited amount of voices, or no access at all.<br/>
|
|
340
|
+
After registration at ElevenLabs.io, you can add any language to your personal list. The personal list will then be shown in the node voice list.<br/>
|
|
257
341
|
<br/>
|
|
258
342
|
|
|
259
343
|
- **TTS Service using Voice.ai**<br/>
|
|
260
|
-
Add your Voice.ai API key in the config node, deploy and restart Node-RED. The node will load your available voices and show them in the Voice dropdown
|
|
344
|
+
Add your Voice.ai API key in the config node, deploy and restart Node-RED. The node will load your available voices and show them in the Voice dropdown.<br/>
|
|
261
345
|
Note: SSML is not supported by this engine.
|
|
262
346
|
<br/>
|
|
263
347
|
|
|
264
|
-
**Node-
|
|
265
|
-
|
|
348
|
+
**Node-RED IP**<br/>
|
|
349
|
+
Set IP of your Node-RED machine. Write **AUTODISCOVER** to allow the node to auto discover your IP.
|
|
266
350
|
|
|
267
351
|
**Host Port**<br/>
|
|
268
|
-
Sonos will connect to this port
|
|
352
|
+
The players (Sonos, Google Cast, DLNA/UPnP renderers) will connect to this port to fetch the TTS audio. Default 1980. Choose a free port. Do not use 1880 or any other port already in use on your computer. The port must be reachable from the players on your network.
|
|
269
353
|
|
|
270
354
|
Note: if you use multiple `ttsultimate-config` nodes, each one now keeps its own TTS cache folder; the “purge on restart/deploy” option only affects that config node’s cache.
|
|
271
355
|
|
|
@@ -286,9 +370,9 @@ Leave this field blank for the default.<br/>
|
|
|
286
370
|
<br/>
|
|
287
371
|
<br/>
|
|
288
372
|
|
|
289
|
-
|
|
373
|
+
## TTS-Ultimate Node
|
|
290
374
|
|
|
291
|
-
|
|
375
|
+
### INPUT MESSAGES TO THE NODE <br/>
|
|
292
376
|
|
|
293
377
|
_Examples_
|
|
294
378
|
|
|
@@ -340,7 +424,7 @@ msg.stop = true;
|
|
|
340
424
|
return msg;
|
|
341
425
|
```
|
|
342
426
|
|
|
343
|
-
|
|
427
|
+
### CHANGE CONFIGURATION VIA MSG PROPERTY
|
|
344
428
|
|
|
345
429
|
You can change the configuration of tts-ultimate, _via msg.setConfig_ property.<br/>
|
|
346
430
|
The property is a JSON object.
|
|
@@ -502,7 +586,7 @@ return msg;
|
|
|
502
586
|
<br/>
|
|
503
587
|
<br/>
|
|
504
588
|
|
|
505
|
-
|
|
589
|
+
## OwnFile Node Configuration
|
|
506
590
|
|
|
507
591
|
<img src='https://github.com/Supergiovane/node-red-contrib-tts-ultimate/raw/master/img/of.png' width="80%">
|
|
508
592
|
|
|
@@ -620,7 +704,7 @@ return msg;
|
|
|
620
704
|
|
|
621
705
|
</details>
|
|
622
706
|
|
|
623
|
-
This node
|
|
707
|
+
This node allows you to upload your custom message and play it via TTS Ultimate without the need for an internet connection. You can use it, for example, with your alarm panel to announce a zone breach, a doorbell or similar events.
|
|
624
708
|
|
|
625
709
|
**Name**<br/>
|
|
626
710
|
Node name
|
|
@@ -632,13 +716,13 @@ Select a file to be played. You can upload one or multiple files at the same tim
|
|
|
632
716
|
If set to <b>true</b>, the OwnFile message will cancel the current TTS queue, will stop the current phrase being spoken and the TTS-Ultimate node will play this priority message.<br/>
|
|
633
717
|
Please refer to _msg.priority_ msg input property of TTS-Ultimate for info on how this message will be handled<br/>
|
|
634
718
|
|
|
635
|
-
|
|
719
|
+
### INPUT MESSAGE
|
|
636
720
|
|
|
637
721
|
**msg.payload = true**<br/>
|
|
638
722
|
Begin play of the message <br/>
|
|
639
723
|
|
|
640
724
|
**msg.selectedFile = "Garage door open"** <br/>
|
|
641
|
-
Overrides the selected message and plays the filename you passed in. Please double check the spelling of the filename (must be the same as you can see in the dropdown list of your own files, in the node config window) and do not include the <b>.mp3</b>
|
|
725
|
+
Overrides the selected message and plays the filename you passed in. Please double check the spelling of the filename (must be the same as you can see in the dropdown list of your own files, in the node config window) and do not include the <b>.mp3</b> extension.<br/>
|
|
642
726
|
|
|
643
727
|
**msg.priority**<br/>
|
|
644
728
|
If set to <b>true</b>, the OwnFile message will cancel the current TTS queue, will stop the current phrase being spoken and the TTS-Ultimate node will play this priority message.<br/>
|
|
@@ -647,7 +731,7 @@ Please refer to _msg.priority_ msg input property of TTS-Ultimate for info on ho
|
|
|
647
731
|

|
|
648
732
|
|
|
649
733
|
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg
|
|
650
|
-
[license-url]: https://github.com/Supergiovane/node-red-contrib-tts-ultimate/master/LICENSE
|
|
734
|
+
[license-url]: https://github.com/Supergiovane/node-red-contrib-tts-ultimate/blob/master/LICENSE
|
|
651
735
|
[npm-url]: https://npmjs.org/package/node-red-contrib-tts-ultimate
|
|
652
736
|
[npm-version-image]: https://img.shields.io/npm/v/node-red-contrib-tts-ultimate.svg
|
|
653
737
|
[npm-downloads-month-image]: https://img.shields.io/npm/dm/node-red-contrib-tts-ultimate.svg
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Audio file</title>
|
|
3
|
+
<desc id="desc">A document with an audio waveform, representing output without a player.</desc>
|
|
4
|
+
<rect width="128" height="128" rx="18" fill="#f3f6fb"/>
|
|
5
|
+
<path d="M36 18h38l22 22v70H36z" fill="#ffffff" stroke="#1f2937" stroke-width="6" stroke-linejoin="round"/>
|
|
6
|
+
<path d="M74 18v24h22" fill="none" stroke="#1f2937" stroke-width="6" stroke-linejoin="round"/>
|
|
7
|
+
<path d="M48 72v-8m12 22V50m12 42V58m12 26V66" fill="none" stroke="#2563eb" stroke-width="8" stroke-linecap="round"/>
|
|
8
|
+
</svg>
|
package/img/logo-v2.svg
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="210" viewBox="0 0 900 210" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">TTS Ultimate</title>
|
|
3
|
+
<desc id="desc">TTS Ultimate logo with a speech waveform mark and wordmark.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="markGradient" x1="16" y1="18" x2="172" y2="172" gradientUnits="userSpaceOnUse">
|
|
6
|
+
<stop offset="0" stop-color="#ffb3f0"/>
|
|
7
|
+
<stop offset="0.5" stop-color="#f47adf"/>
|
|
8
|
+
<stop offset="1" stop-color="#d84fc2"/>
|
|
9
|
+
</linearGradient>
|
|
10
|
+
<linearGradient id="textGradient" x1="218" y1="70" x2="620" y2="70" gradientUnits="userSpaceOnUse">
|
|
11
|
+
<stop offset="0" stop-color="#f56bdd"/>
|
|
12
|
+
<stop offset="0.55" stop-color="#e24ac8"/>
|
|
13
|
+
<stop offset="1" stop-color="#b932a8"/>
|
|
14
|
+
</linearGradient>
|
|
15
|
+
<filter id="softShadow" x="-20%" y="-20%" width="140%" height="140%">
|
|
16
|
+
<feDropShadow dx="0" dy="8" stdDeviation="9" flood-color="#172033" flood-opacity="0.16"/>
|
|
17
|
+
</filter>
|
|
18
|
+
</defs>
|
|
19
|
+
|
|
20
|
+
<rect x="10" y="10" width="880" height="190" rx="34" fill="#ffffff"/>
|
|
21
|
+
<rect x="10" y="10" width="880" height="190" rx="34" fill="none" stroke="#d9e1ef" stroke-width="2"/>
|
|
22
|
+
|
|
23
|
+
<g filter="url(#softShadow)">
|
|
24
|
+
<rect x="48" y="34" width="142" height="142" rx="36" fill="url(#markGradient)"/>
|
|
25
|
+
<path d="M78 106c15-24 36-36 61-36 18 0 33 6 46 18" fill="none" stroke="#ffffff" stroke-width="10" stroke-linecap="round"/>
|
|
26
|
+
<path d="M78 106c15 24 36 36 61 36 18 0 33-6 46-18" fill="none" stroke="#ffffff" stroke-width="10" stroke-linecap="round" opacity="0.92"/>
|
|
27
|
+
<path d="M86 108h12m15 18V90m17 54V72m17 62V82m17 38v-28" fill="none" stroke="#ffffff" stroke-width="8" stroke-linecap="round"/>
|
|
28
|
+
<circle cx="75" cy="106" r="8" fill="#ffffff"/>
|
|
29
|
+
<circle cx="186" cy="106" r="5" fill="#ffffff" opacity="0.8"/>
|
|
30
|
+
</g>
|
|
31
|
+
|
|
32
|
+
<g font-family="Inter, Avenir Next, Segoe UI, Arial, sans-serif">
|
|
33
|
+
<text x="220" y="100" fill="url(#textGradient)" font-size="62" font-weight="900" letter-spacing="3">TTS</text>
|
|
34
|
+
<text x="355" y="100" fill="#2a1830" font-size="62" font-weight="850" letter-spacing="2">ULTIMATE</text>
|
|
35
|
+
<text x="224" y="137" fill="#6f5574" font-size="24" font-weight="600" letter-spacing="1">NODE-RED TEXT TO SPEECH</text>
|
|
36
|
+
<path d="M224 153h312" stroke="url(#textGradient)" stroke-width="6" stroke-linecap="round"/>
|
|
37
|
+
<path d="M552 153h34" stroke="#ffb3f0" stroke-width="6" stroke-linecap="round"/>
|
|
38
|
+
</g>
|
|
39
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-tts-ultimate",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Transforms the text in speech and hear it using Sonos
|
|
3
|
+
"version": "3.2.1",
|
|
4
|
+
"description": "Transforms the text in speech and hear it using Sonos, Google Cast (Chromecast / Nest) or DLNA/UPnP players, or generate an audio file to be used with third parties nodes. Works with voices from Google (without credentials as well), Google TTS, ElevenLabs.io TTS, Voice.ai TTS 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": {
|
|
7
7
|
"test": "test",
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
},
|
|
40
40
|
"homepage": "https://github.com/Supergiovane/node-red-contrib-tts-ultimate",
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"sonos": "1.14.1",
|
|
43
|
-
"formidable": "1.2.2",
|
|
44
|
-
"path": ">=0.12.7",
|
|
45
42
|
"@google-cloud/text-to-speech": "4.2.2",
|
|
46
|
-
"
|
|
43
|
+
"castv2-client": "^1.2.0",
|
|
44
|
+
"elevenlabs": "0.18.0",
|
|
47
45
|
"elevenlabs-node": "1.1.3",
|
|
48
|
-
"
|
|
46
|
+
"formidable": "1.2.2",
|
|
47
|
+
"multicast-dns": "^7.2.5",
|
|
48
|
+
"sonos": "1.14.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"eslint": ">=4.18.2",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Discover DLNA / UPnP MediaRenderer devices on the local network.
|
|
2
|
+
// Prints the device description XML URL (LOCATION) to paste into the
|
|
3
|
+
// tts-ultimate "DLNA / UPnP renderer" player configuration.
|
|
4
|
+
//
|
|
5
|
+
// Usage:
|
|
6
|
+
// node scripts/discover-dlna.js (search ~5s)
|
|
7
|
+
// node scripts/discover-dlna.js --timeout 8000
|
|
8
|
+
const { discoverRenderers } = require("../ttsultimate/lib/dlna-discovery");
|
|
9
|
+
|
|
10
|
+
const argValue = (name, fallback) => {
|
|
11
|
+
const idx = process.argv.indexOf(name);
|
|
12
|
+
if (idx === -1) return fallback;
|
|
13
|
+
const value = process.argv[idx + 1];
|
|
14
|
+
return value && !value.startsWith("--") ? value : fallback;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const timeoutMs = Number(argValue("--timeout", 5000)) || 5000;
|
|
18
|
+
|
|
19
|
+
(async () => {
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.log(`Searching for DLNA/UPnP MediaRenderers for ${timeoutMs} ms...\n`);
|
|
22
|
+
const devices = await discoverRenderers({ timeoutMs });
|
|
23
|
+
if (devices.length === 0) {
|
|
24
|
+
// eslint-disable-next-line no-console
|
|
25
|
+
console.log("No MediaRenderer devices found. Make sure a renderer is on and on the same subnet.");
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log(`Found ${devices.length} renderer(s):\n`);
|
|
30
|
+
for (const dev of devices) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.log(" " + (dev.name || "(unknown name)"));
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.log(" Description URL (paste this in the node): " + dev.location);
|
|
35
|
+
if (dev.server) console.log(" Server: " + dev.server);
|
|
36
|
+
// eslint-disable-next-line no-console
|
|
37
|
+
console.log("");
|
|
38
|
+
}
|
|
39
|
+
process.exit(0);
|
|
40
|
+
})().catch((err) => {
|
|
41
|
+
// eslint-disable-next-line no-console
|
|
42
|
+
console.error(err);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Discover DLNA / UPnP MediaRenderer devices on the local network via SSDP.
|
|
2
|
+
// No external dependencies: built-in dgram (UDP multicast) + http only.
|
|
3
|
+
const dgram = require("dgram");
|
|
4
|
+
const http = require("http");
|
|
5
|
+
const { URL } = require("url");
|
|
6
|
+
|
|
7
|
+
const SSDP_ADDR = "239.255.255.250";
|
|
8
|
+
const SSDP_PORT = 1900;
|
|
9
|
+
const SEARCH_TARGET = "urn:schemas-upnp-org:device:MediaRenderer:1";
|
|
10
|
+
|
|
11
|
+
// Reads the <friendlyName> from a device description XML URL (best-effort).
|
|
12
|
+
function fetchFriendlyName(location) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
try {
|
|
15
|
+
const u = new URL(location);
|
|
16
|
+
const req = http.get(
|
|
17
|
+
{ hostname: u.hostname, port: u.port || 80, path: u.pathname + u.search, timeout: 3000 },
|
|
18
|
+
(res) => {
|
|
19
|
+
let body = "";
|
|
20
|
+
res.on("data", (c) => (body += c));
|
|
21
|
+
res.on("end", () => {
|
|
22
|
+
const m = body.match(/<friendlyName>([^<]+)<\/friendlyName>/i);
|
|
23
|
+
resolve(m ? m[1].trim() : "");
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
req.on("error", () => resolve(""));
|
|
28
|
+
req.on("timeout", () => { req.destroy(); resolve(""); });
|
|
29
|
+
} catch (e) {
|
|
30
|
+
resolve("");
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// discoverRenderers({ timeoutMs }) -> Promise<[{ name, location, server }]>
|
|
36
|
+
function discoverRenderers(options) {
|
|
37
|
+
const opts = options || {};
|
|
38
|
+
const timeoutMs = Number(opts.timeoutMs) || 5000;
|
|
39
|
+
const withNames = opts.withNames !== false; // resolve friendlyName by default
|
|
40
|
+
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const found = new Map(); // location -> { location, server }
|
|
43
|
+
const mSearch = Buffer.from(
|
|
44
|
+
"M-SEARCH * HTTP/1.1\r\n" +
|
|
45
|
+
`HOST: ${SSDP_ADDR}:${SSDP_PORT}\r\n` +
|
|
46
|
+
'MAN: "ssdp:discover"\r\n' +
|
|
47
|
+
"MX: 2\r\n" +
|
|
48
|
+
`ST: ${SEARCH_TARGET}\r\n` +
|
|
49
|
+
"\r\n"
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
let socket;
|
|
53
|
+
try {
|
|
54
|
+
socket = dgram.createSocket({ type: "udp4", reuseAddr: true });
|
|
55
|
+
} catch (error) {
|
|
56
|
+
reject(error);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
socket.on("message", (msg) => {
|
|
61
|
+
const text = msg.toString();
|
|
62
|
+
// Keep only genuine MediaRenderer responses (some non-compliant devices,
|
|
63
|
+
// e.g. Hue bridges, answer any M-SEARCH; their ST/USN won't mention it).
|
|
64
|
+
if (!/MediaRenderer/i.test(text)) return;
|
|
65
|
+
const locationMatch = text.match(/LOCATION:\s*(.+)\r/i);
|
|
66
|
+
if (!locationMatch) return;
|
|
67
|
+
const location = locationMatch[1].trim();
|
|
68
|
+
if (found.has(location)) return;
|
|
69
|
+
const serverMatch = text.match(/SERVER:\s*(.+)\r/i);
|
|
70
|
+
found.set(location, { location, server: serverMatch ? serverMatch[1].trim() : "" });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
socket.on("error", (err) => {
|
|
74
|
+
try { socket.close(); } catch (e) { }
|
|
75
|
+
reject(err);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
socket.bind(() => {
|
|
79
|
+
try { socket.setBroadcast(true); } catch (e) { }
|
|
80
|
+
const send = () => { try { socket.send(mSearch, 0, mSearch.length, SSDP_PORT, SSDP_ADDR); } catch (e) { } };
|
|
81
|
+
send();
|
|
82
|
+
setTimeout(send, 500);
|
|
83
|
+
setTimeout(send, 1500);
|
|
84
|
+
|
|
85
|
+
setTimeout(async () => {
|
|
86
|
+
try { socket.close(); } catch (e) { }
|
|
87
|
+
const devices = Array.from(found.values());
|
|
88
|
+
if (!withNames) { resolve(devices); return; }
|
|
89
|
+
for (const dev of devices) {
|
|
90
|
+
dev.name = (await fetchFriendlyName(dev.location)) || "";
|
|
91
|
+
}
|
|
92
|
+
resolve(devices);
|
|
93
|
+
}, timeoutMs);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { discoverRenderers, fetchFriendlyName };
|