From d68e4e97a6b430877d45c34a9c4f3f4047bb90cf Mon Sep 17 00:00:00 2001 From: zhongjin Date: Sun, 11 Nov 2018 13:26:55 +0800 Subject: [PATCH] Initial commit --- .gitignore | 8 + .npmignore | 12 + .travis.yml | 23 + LICENSE | 22 + README.md | 408 +++++++ admin/blockly.js | 242 +++++ admin/engines.js | 139 +++ admin/i18n/de/translations.json | 42 + admin/i18n/en/translations.json | 42 + admin/i18n/es/translations.json | 42 + admin/i18n/fr/translations.json | 42 + admin/i18n/it/translations.json | 42 + admin/i18n/nl/translations.json | 42 + admin/i18n/pl/translations.json | 42 + admin/i18n/pt/translations.json | 42 + admin/i18n/ru/translations.json | 42 + admin/index.html | 650 ++++++++++++ admin/index_m.html | 811 ++++++++++++++ admin/sayit.png | Bin 0 -> 26669 bytes admin/words.js | 50 + appveyor.yml | 25 + cmdmp3/cmdmp3.exe | Bin 0 -> 28160 bytes cmdmp3/cmdmp3win.exe | Bin 0 -> 28672 bytes cmdmp3/readme.txt | 2 + gulpfile.js | 401 +++++++ io-package.json | 160 +++ lib/speech2device.js | 518 +++++++++ lib/text2speech.js | 484 +++++++++ lib/utils.js | 83 ++ main.js | 636 +++++++++++ mp3/gong.mp3 | Bin 0 -> 42536 bytes mp3/scifi.mp3 | Bin 0 -> 21281 bytes ...md - Windows command line tool- ReadMe.htm | 998 ++++++++++++++++++ nircmd/NirCmd.chm | Bin 0 -> 45977 bytes nircmd/license.txt | 6 + nircmd/nircmd.exe | Bin 0 -> 44032 bytes nircmd/nircmdc.exe | Bin 0 -> 43520 bytes package.json | 43 + say/SayStatic.exe | Bin 0 -> 72704 bytes test/lib/setup.js | 728 +++++++++++++ test/testAdapter.js | 142 +++ test/testPackageFiles.js | 91 ++ 42 files changed, 7060 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 admin/blockly.js create mode 100644 admin/engines.js create mode 100644 admin/i18n/de/translations.json create mode 100644 admin/i18n/en/translations.json create mode 100644 admin/i18n/es/translations.json create mode 100644 admin/i18n/fr/translations.json create mode 100644 admin/i18n/it/translations.json create mode 100644 admin/i18n/nl/translations.json create mode 100644 admin/i18n/pl/translations.json create mode 100644 admin/i18n/pt/translations.json create mode 100644 admin/i18n/ru/translations.json create mode 100644 admin/index.html create mode 100644 admin/index_m.html create mode 100644 admin/sayit.png create mode 100644 admin/words.js create mode 100644 appveyor.yml create mode 100644 cmdmp3/cmdmp3.exe create mode 100644 cmdmp3/cmdmp3win.exe create mode 100644 cmdmp3/readme.txt create mode 100644 gulpfile.js create mode 100644 io-package.json create mode 100644 lib/speech2device.js create mode 100644 lib/text2speech.js create mode 100644 lib/utils.js create mode 100644 main.js create mode 100644 mp3/gong.mp3 create mode 100644 mp3/scifi.mp3 create mode 100644 nircmd/NirCmd - Windows command line tool- ReadMe.htm create mode 100644 nircmd/NirCmd.chm create mode 100644 nircmd/license.txt create mode 100644 nircmd/nircmd.exe create mode 100644 nircmd/nircmdc.exe create mode 100644 package.json create mode 100644 say/SayStatic.exe create mode 100644 test/lib/setup.js create mode 100644 test/testAdapter.js create mode 100644 test/testPackageFiles.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97bd036 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules +.idea +tmp +admin/i18n/*/flat.txt +admin/i18n/flat.txt +iob_npm.done +package-lock.json +/.project diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..4c9312b --- /dev/null +++ b/.npmignore @@ -0,0 +1,12 @@ +Gruntfile.js +gulpfile.js +test +tmp +docs +.idea +.travis.yml +.git +appveyor.yml +admin/i18n +iob_npm.done +package-lock.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5a5145d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +os: + - linux + - osx +language: node_js +node_js: + - '4' + - '6' + - '8' + - '10' +before_script: + - export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1) + - 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm@5; fi' + - npm -v + - npm install winston@2.3.1 + - 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production' +env: + - CXX=g++-4.8 +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6c380bb --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015-2017 bluefox + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..5417bdd --- /dev/null +++ b/README.md @@ -0,0 +1,408 @@ +![Logo](admin/sayit.png) +yunkong2 sayit adapter +================= +[![NPM version](http://img.shields.io/npm/v/yunkong2.sayit.svg)](https://www.npmjs.com/package/yunkong2.sayit) +[![Downloads](https://img.shields.io/npm/dm/yunkong2.sayit.svg)](https://www.npmjs.com/package/yunkong2.sayit) + +[![NPM](https://nodei.co/npm/yunkong2.sayit.png?downloads=true)](https://nodei.co/npm/yunkong2.sayit/) + + +SayIt Adapter can convert text to speech and play it on some device. + +## Configuration +Actual following outputs are supported: + +- *Browser* - the text will be played by browser with opened yunkong2.vis page. It is supported almost by every desktop browser and by few mobily Browsers. + + +- *[Home24- MediaPlayer](http://www.home-24.net/index.php?app=media)* - the text will be sent and played to the Android device with Home24 - MediaPlayer installed. For this will be used build in Android text to speech engine. The port cannot be changed and set to 50000. + + +- *Home24 - MediaPlayer and [FTP Server](https://play.google.com/store/apps/details?id=lutey.FTPServer)* - the text will be sent and played on the Android device with Home24 - MediaPlayer installed. For this will be used the Google text to speech engine. Generated mp3 file will be copied over FTP to android device and played with Home24 - MediaPlayer. + Both apps have to have same home directories. (E.g. root directory of \"sd card\"). + + +- *System* - the text will be played by OS, where the yunkong2 adapter runs. Following OS are supported: Windows, linux, Mac OSx. + + +- *Windows engine* - the text will be played by windows, where the sayIt adapter runs. For this will be used windows text to speech engine, that should be preconfigured by user. You can check [here](http://windows.microsoft.com/en-us/windows/setting-speech-options#1TC=windows-7) how to setup it. + + +- *Sonos* - play text on sonos device. Be sure the Web Adapter is enabled. It is required to enable SONOS to read the generated mp3 files. + +- *Chromecast* - play text on Chromecast device. + +- *MPD* - play text on Music Player Daemon. Use only **http** for web adapter, don't use https. + +To enable the text to speech on RaspberryPI or linux system call one time following command ```sudo apt-get -y install mpg321``` to install mpg321. + +The mp3/wav files can be played to by writing its name into the object. (e.g. "/vis.0/main/img/door-bell.mp3") + +The file must be first loaded. + +### TTS engines +online: +- Google: English, German, Russian, Italian, Spanish, French +- Yandex: Russian + To use Yandex voices you must request the API key here: [https://tech.yandex.ru/speechkit/cloud/doc/dg/concepts/About-docpage/](https://tech.yandex.ru/speechkit/cloud/doc/dg/concepts/About-docpage/). +- Ivona: English, German, Russian, Italian, Spanish, French, Dansk, Welsh, Icelandic, Dutch, Polish, Portuguese, Romanian, Swedish, Turkish + To use Amazon(Ivona) voices you need to get access key and secret key [here](http://www.ivona.com/us/for-business/speech-cloud/). +- Cloud: + To use Cloud voices you need configured cloud adapter. (It can be disabled, but must be configured). This service use AWS Polly and it can be used directly. +- Amazon Web Services Polly: + To use AWS Polly voices you need to create access key and secret key [here](https://console.aws.amazon.com/iam/home). The Amazon documentation can you find [here](http://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html). + +offline: +- PicoTTS (linux only): English, German, Italian, Spanish, French + For PicoTTS it is necessary to install the following packages: libttspico-utils and lame. + Installation command: 'sudo apt-get install libttspico-utils lame' + +### Cloud and Amazon Web Services Polly text formatting +You can format your text with [Speech Synthesis Markup Language](http://docs.aws.amazon.com/polly/latest/dg/ssml.html). + +Most useful features: +- ``````- make a pause for x seconds (max 10 seconds). +- ``` big ``` - make an emphasis on some word. +- ```I am speaking this``` - control speed and volume parameters. +- ```12345``` - say every digit separately. + +More [info](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speech-synthesis-markup-language-ssml-reference). + +### System command +If you have some program, that can play audio files locally or somewhere else, you can write this command here. E.g. + +```myCustomPlayer --option``` + +If **System** output is selected, the sayit adapter will execute following command on local system: + +```myCustomPlayer --option /opt/yunkong2/node_modules/yunkong2.sayit/say.mp3``` + +If file name must stay somewhere in the middle you can use *%s* to specify where the file name must be placed: + +```myCustomPlayer --option "%s" > /dev/null``` + +sayIt will make ```myCustomPlayer --option "/opt/yunkong2/node_modules/yunkong2.sayit/say.mp3" > /dev/null``` from it. + +## Usage +SayIt adapter cannot be used alone. It must be controlled from javascript adapter or from "vis" with specific widget. +After creation of adapter instance will can find following objects: +- sayit.N.tts.text: Phrase to be spoken. +- sayit.N.tts.volume: volume which will be used by playing of the phrase. +- sayit.N.tts.playing: true if text is now playing and false if not. Supported only for "windows" and "system" play mode. +- sayit.N.tts.cachetext: Phrase to be cached and then it can be used without internet. + E.g. you can enter here manually "No internet" and if ping to google.com is negative, write "No internet" to "tts.text" and it will pronounced. Of course cache must be enabled. + +State **tts.text** supports extended syntax, so the langugage/engine and volume can be defined together with text. It is used to enable multi-language text2speech engines. +E.g. if adapter has engine "Google-english", it is possible with phrase ```de:Sag es``` to force to use Google-Deustch speech engine. + +With ```ru;75;Погода хорошая``` we can force to use russian language and volume 75%. + +You can specify the volume of announcement in percent from current or given volume (not from maximal). E.g. if command is ```de;75;Gutes Wetter```and "announce volume" is 50%, the announce will be played with volume 38% from 100% possible. + +The system command to play the mp3 file can be specified too. If you leave it blank, the default settings will be used: windows - cmdmp3.exe, OSX - /usr/bin/afplay, linux - mpg321 or omxplayer (recommended). + +To install omxplayer write ```sudo apt-get install omxplayer``` or write ```sudo apt-get install mpg321``` to install mpg321. + +**Note:** The default announce selection will be possible only after start of the instance. + +### Engines +Following values for engines are possible: + +#### Google +- **en** - English +- **de** - Deutsch +- **ru** - Русский +- **it** - Italiano +- **es** - Espaniol +- **fr** - Français + +#### Yandex +- **ru_YA:Yandex** - Русский + +#### Amazon polly via cloud +- **ru-RU_CLOUD_Female** - Русский - Татьяна +- **ru-RU_CLOUD_Male** - Русский - Максим +- **de-DE_CLOUD_Female** - Deutsch - Marlene +- **de-DE_CLOUD_Male** - Deutsch - Hans +- **en-US_CLOUD_Female** - en-US - Female - Salli +- **en-US_CLOUD_Male** - en-US - Male - Joey +- **da-DK_CLOUD_Female** - da-DK - Female - Naja +- **da-DK_CLOUD_Male** - da-DK - Male - Mads +- **en-AU_CLOUD_Female** - en-AU - Female - Nicole +- **en-AU_CLOUD_Male** - en-AU - Male - Russell +- **en-GB_CLOUD_Female_Amy** - en-GB - Female - Amy +- **en-GB_CLOUD_Male** - en-GB - Male - Brian +- **en-GB_CLOUD_Female_Emma** - en-GB - Female - Emma +- **en-GB-WLS_CLOUD_Female** - en-GB-WLS - Female - Gwyneth +- **en-GB-WLS_CLOUD_Male** - en-GB-WLS - Male - Geraint +- **cy-GB_CLOUD_Female** - cy-GB - Female - Gwyneth +- **cy-GB_CLOUD_Male** - cy-GB - Male - Geraint +- **en-IN_CLOUD_Female** - en-IN - Female - Raveena +- **en-US_CLOUD_Male_Chipmunk** - en-US - Male - Chipmunk +- **en-US_CLOUD_Male_Eric** - en-US - Male - Eric +- **en-US_CLOUD_Female_Ivy** - en-US - Female - Ivy +- **en-US_CLOUD_Female_Jennifer** - en-US - Female - Jennifer +- **en-US_CLOUD_Male_Justin** - en-US - Male - Justin +- **en-US_CLOUD_Female_Kendra** - en-US - Female - Kendra +- **en-US_CLOUD_Female_Kimberly** - en-US - Female - Kimberly +- **es-ES_CLOUD_Female** - es-ES - Female - Conchita +- **es-ES_CLOUD_Male** - es-ES - Male - Enrique +- **es-US_CLOUD_Female** - es-US - Female - Penelope +- **es-US_CLOUD_Male** - es-US - Male - Miguel +- **fr-CA_CLOUD_Female** - fr-CA - Female - Chantal +- **fr-FR_CLOUD_Female** - fr-FR - Female - Celine +- **fr-FR_CLOUD_Male** - fr-FR - Male - Mathieu +- **is-IS_CLOUD_Female** - is-IS - Female - Dora +- **is-IS_CLOUD_Male** - is-IS - Male - Karl +- **it-IT_CLOUD_Female** - it-IT - Female - Carla +- **it-IT_CLOUD_Male** - it-IT - Male - Giorgio +- **nb-NO_CLOUD_Female** - nb-NO - Female - Liv +- **nl-NL_CLOUD_Female** - nl-NL - Female - Lotte +- **nl-NL_CLOUD_Male** - nl-NL - Male - Ruben +- **pl-PL_CLOUD_Female_Agnieszka** - pl-PL - Female - Agnieszka +- **pl-PL_CLOUD_Male_Jacek** - pl-PL - Male - Jacek +- **pl-PL_CLOUD_Female_Ewa** - pl-PL - Female - Ewa +- **pl-PL_CLOUD_Male_Jan** - pl-PL - Male - Jan +- **pl-PL_CLOUD_Female** - pl-PL - Female - Maja +- **pt-BR_CLOUD_Female** - pt-BR - Female - Vitoria +- **pt-BR_CLOUD_Male** - pt-BR - Male - Ricardo +- **pt-PT_CLOUD_Male** - pt-PT - Male - Cristiano +- **pt-PT_CLOUD_Female** - pt-PT - Female - Ines +- **ro-RO_CLOUD_Female** - ro-RO - Female - Carmen +- **sv-SE_CLOUD_Female** - sv-SE - Female - Astrid +- **tr-TR_CLOUD_Female** - tr-TR - Female - Filiz + +#### Pico TTS +- **en-US** - Englisch US +- **en-GB** - Englisch GB +- **de-DE** - Deutsch +- **it-IT** - Italiano +- **es-ES** - Espaniol +- **fr-FR** - Français + +#### Amazon polly direct +- **(ru-RU_AP_Female)** - Русский - Татьяна +- **(ru-RU_AP_Male)** - Русский - Максим +- **(de-DE_AP_Female)** - Deutsch - Marlene +- **(de-DE_AP_Male)** - Deutsch - Hans +- **(en-US_AP_Female)** - en-US - Female - Salli +- **(en-US_AP_Male)** - en-US - Male - Joey +- **(da-DK_AP_Female)** - da-DK - Female - Naja +- **(da-DK_AP_Male)** - da-DK - Male - Mads +- **(en-AU_AP_Female)** - en-AU - Female - Nicole +- **(en-AU_AP_Male)** - en-AU - Male - Russell +- **(en-GB_AP_Female_Amy)** - en-GB - Female - Amy +- **(en-GB_AP_Male)** - en-GB - Male - Brian +- **(en-GB_AP_Female_Emma)** - en-GB - Female - Emma +- **(en-GB-WLS_AP_Female)** - en-GB-WLS - Female - Gwyneth +- **(en-GB-WLS_AP_Male)** - en-GB-WLS - Male - Geraint +- **(cy-GB_AP_Female)** - cy-GB - Female - Gwyneth +- **(cy-GB_AP_Male)** - cy-GB - Male - Geraint +- **(en-IN_AP_Female)** - en-IN - Female - Raveena +- **(en-US_AP_Male_Chipmunk)** - en-US - Male - Chipmunk +- **(en-US_AP_Male_Eric)** - en-US - Male - Eric +- **(en-US_AP_Female_Ivy)** - en-US - Female - Ivy +- **(en-US_AP_Female_Jennifer)** - en-US - Female - Jennifer +- **(en-US_AP_Male_Justin)** - en-US - Male - Justin +- **(en-US_AP_Female_Kendra)** - en-US - Female - Kendra +- **(en-US_AP_Female_Kimberly)** - en-US - Female - Kimberly +- **(es-ES_AP_Female)** - es-ES - Female - Conchita +- **(es-ES_AP_Male)** - es-ES - Male - Enrique +- **(es-US_AP_Female)** - es-US - Female - Penelope +- **(es-US_AP_Male)** - es-US - Male - Miguel +- **(fr-CA_AP_Female)** - fr-CA - Female - Chantal +- **(fr-FR_AP_Female)** - fr-FR - Female - Celine +- **(fr-FR_AP_Male)** - fr-FR - Male - Mathieu +- **(is-IS_AP_Female)** - is-IS - Female - Dora +- **(is-IS_AP_Male)** - is-IS - Male - Karl +- **(it-IT_AP_Female)** - it-IT - Female - Carla +- **(it-IT_AP_Male)** - it-IT - Male - Giorgio +- **(nb-NO_AP_Female)** - nb-NO - Female - Liv +- **(nl-NL_AP_Female)** - nl-NL - Female - Lotte +- **(nl-NL_AP_Male)** - nl-NL - Male - Ruben +- **(pl-PL_AP_Female_Agnieszka)** - pl-PL - Female - Agnieszka +- **(pl-PL_AP_Male_Jacek)** - pl-PL - Male - Jacek +- **(pl-PL_AP_Female_Ewa)** - pl-PL - Female - Ewa +- **(pl-PL_AP_Male_Jan)** - pl-PL - Male - Jan +- **(pl-PL_AP_Female)** - pl-PL - Female - Maja +- **(pt-BR_AP_Female)** - pt-BR - Female - Vitoria +- **(pt-BR_AP_Male)** - pt-BR - Male - Ricardo +- **(pt-PT_AP_Male)** - pt-PT - Male - Cristiano +- **(pt-PT_AP_Female)** - pt-PT - Female - Ines +- **(ro-RO_AP_Female)** - ro-RO - Female - Carmen +- **(sv-SE_AP_Female)** - sv-SE - Female - Astrid +- **(tr-TR_AP_Female)** - tr-TR - Female - Filiz + +## Changelog +### 1.7.1 (2018-09-19) +* (BuZZy1337) fixed error in Blockly-Block + +### 1.7.0 (2018-06-08) +* (bluefox) Ivona removed +* (bluefox) Error was fixed by upload of file to FTP +* (bluefox) admin3 + +### 1.6.8 (2018-04-11) +* (BuZZy1337) Generate separate mp3 files for each instance. +* Fixes [Issue#34](https://git.spacen.net/yunkong2/yunkong2.sayit/issues/34) +* (BuZZy1337) Always upload mp3 files to the state sayit.X.tts.mp3 + +### 1.6.7 (2018-02-05) +* (Apollon77) Remove unneeded logging +* (bondrogeen) Admin3 Fixes + +### 1.6.6 (2017-11-27) +* (angelnu) Wait for Google Home announcement to complete + +### 1.6.5 (2017-11-04) +* (bluefox) Fix cloud .pro + +### 1.6.4 (2017-10-18) +* (bluefox) Fix system commands + +### 1.6.3 (2017-10-04) +* (bluefox) Code refactoring +* (bluefox) Add google home as output +* (bluefox) Remove ivona because not more supported + +### 1.5.2 (2017-03-09) +* (bluefox) Catch error if some directory in mp3 folder + +### 1.5.1 (2017-02-15) +* (bluefox) Fix blockly language + +### 1.5.0 (2017-01-27) +* (DarkChaos) Add AWS Polly as source +* (bluefox) Add cloud as source + +### 1.4.0 (2017-01-16) +* (bluefox) fix install problem +* (bluefox) add PicoTTS as source + +### 1.3.3 (2017-01-13) +* (bluefox) show only installed instances in blockly + +### 1.3.2 (2017-01-10) +* (angelnu) changes for new chromecast tts + +### 1.3.1 (2016-12-27) +* (bluefox) small fix of config dialog +* (AirKing555) Fix Volume change + +### 1.3.0 (2016-12-20) +* (instalator) add mpd + +### 1.2.1 (2016-10-31) +* (bluefox) Fix cache + +### 1.2.0 (2016-10-28) +* (bluefox) Finish sayit + +### 1.1.3 (2016-10-24) +* (bluefox) Fix changing of engine + +### 1.1.2 (2016-10-20) +* (bluefox) Add omxplayer option + +### 1.0.1 (2016-10-12) +* (bluefox) support of blockly + +### 1.0.0 (2016-05-14) +* (bluefox) Make the type of mp3 as file + +### 0.3.16 (2015-12-26) +* (Vegetto) Support for Chromecast devices + +### 0.3.16 (2015-12-26) +* (bluefox) enable play of mp3 files from disk + +### 0.3.15 (2015-11-10) +* (bluefox) fill default settings by first start + +### 0.3.14 (2015-11-01) +* (bluefox) fix error with sayItWindows + +### 0.3.13 (2015-10-27) +* (bluefox) fix error with sayItSystem + +### 0.3.12 (2015-10-06) +* (bluefox) fix error if received mp3 file is too short +* (bluefox) try to implement cache datapoint (you can use sayit.0.tts.cachetext to create cache for phrases and use sayit without internet) + +### 0.3.11 (2015-08-03) +* (bluefox) change google requests from http to https + +### 0.3.10 (2015-07-26) +* (bluefox) add new voice Russian-Maxim +* (bluefox) fix error with mp24ftp + +### 0.3.9 (2015-07-09) +* (bluefox) fix error by mediaplayer24 + +### 0.3.8 (2015-06-09) +* (bluefox) make the volume for announce configurable +* (bluefox) make the command for "system" configurable + +### 0.3.7 (2015-05-28) +* (bluefox) fix volume for announce +* (bluefox) support of play files from internal filesystem, like "/sayit.0/tts.userfiles/myGong.mp3" + +### 0.3.6 (2015-03-24) +* (bluefox) fix error with volume by sonos + +### 0.3.5 (2015-03-22) +* (bluefox) fix error in announcement + +### 0.3.4 (2015-03-20) +* (bluefox) fix error in announcement + +### 0.3.3 (2015-03-20) +* (bluefox) enable announcement + +### 0.3.2 (2015-03-16) +* (bluefox) clear cache if engine changed + +### 0.3.1 (2015-03-15) +* (bluefox) fix small error with log + +### 0.3.0 (2015-03-08) +* (bluefox) add ivona/Amazon voices + +### 0.2.2 (2015-03-08) +* (bluefox) fix error by buffering of non-generated texts. + +### 0.2.1 (2015-03-07) +* (bluefox) fix error by buffering of non-generated texts. + +### 0.2.0 (2015-03-02) +* (bluefox) add yandex-russian support + +### 0.1.0 (2015-03-02) +* (bluefox) queue texts + +### 0.0.1 (2015-02-06) +* (bluefox) initial commit + +## License + +The MIT License (MIT) + +Copyright (c) 2014-2017, bluefox + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/admin/blockly.js b/admin/blockly.js new file mode 100644 index 0000000..e87d873 --- /dev/null +++ b/admin/blockly.js @@ -0,0 +1,242 @@ +'use strict'; + +goog.provide('Blockly.JavaScript.Sendto'); + +goog.require('Blockly.JavaScript'); + +// --- SayIt -------------------------------------------------- +Blockly.Words['sayit'] = {'en': 'say text', 'de': 'aussprechen', 'ru': 'произнести'}; +Blockly.Words['sayit_message'] = {'en': 'message', 'de': 'Meldung', 'ru': 'сообщение'}; +Blockly.Words['sayit_volume'] = {'en': 'volume (optional)', 'de': 'Lautstärke (optional)', 'ru': 'громкость (не обяз.)'}; +Blockly.Words['sayit_tooltip'] = {'en': 'Text to speech', 'de': 'Text zu Sprache', 'ru': 'Произнести сообщение'}; +Blockly.Words['sayit_help'] = {'en': 'https://git.spacen.net/yunkong2/yunkong2.sayit/blob/master/README.md', 'de': 'http://www.yunkong2.net/?page_id=178&lang=de', 'ru': 'http://www.yunkong2.net/?page_id=4262&lang=ru'}; +Blockly.Words['sayit_configured'] = {'en': 'configured', 'de': 'standart', 'ru': 'настроенный'}; + +Blockly.Words['sayit_log'] = {'en': 'log level', 'de': 'Loglevel', 'ru': 'Протокол'}; +Blockly.Words['sayit_log_none'] = {'en': 'none', 'de': 'keins', 'ru': 'нет'}; +Blockly.Words['sayit_log_info'] = {'en': 'info', 'de': 'info', 'ru': 'инфо'}; +Blockly.Words['sayit_log_debug'] = {'en': 'debug', 'de': 'debug', 'ru': 'debug'}; +Blockly.Words['sayit_log_warn'] = {'en': 'warning', 'de': 'warning', 'ru': 'warning'}; +Blockly.Words['sayit_log_error'] = {'en': 'error', 'de': 'error', 'ru': 'ошибка'}; + + +// this is copy of engines.js +var sayitEngines = { + "en": {name: "Google - English", engine: "google", params: []}, + "de": {name: "Google - Deutsch", engine: "google", params: []}, + "ru": {name: "Google - Русский", engine: "google", params: []}, + "it": {name: "Google - Italiano", engine: "google", params: []}, + "es": {name: "Google - Espaniol", engine: "google", params: []}, + "fr": {name: "Google - Français", engine: "google", params: []}, + "ru_YA": {name: "Yandex - Русский", engine: "yandex", params: ['key', 'voice', 'emotion', 'ill', 'drunk', 'robot'], voice: ['jane', 'zahar'], emotion: ['none', 'good', 'neutral', 'evil', 'mixed']}, + + "en-US": {name: "PicoTTS - Englisch US", engine: "PicoTTS", params: []}, + "en-GB": {name: "PicoTTS - Englisch GB", engine: "PicoTTS", params: []}, + "de-DE": {name: "PicoTTS - Deutsch", engine: "PicoTTS", params: []}, + "it-IT": {name: "PicoTTS - Italiano", engine: "PicoTTS", params: []}, + "es-ES": {name: "PicoTTS - Espaniol", engine: "PicoTTS", params: []}, + "fr-FR": {name: "PicoTTS - Français", engine: "PicoTTS", params: []}, + + "ru-RU_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "ru-RU", ename: "Tatyana", ssml: true, name: "Cloud - Русский - Татьяна"}, + "ru-RU_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "ru-RU", ename: "Maxim", ssml: true, name: "Cloud - Русский - Максим"}, + "de-DE_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "de-DE", ename: "Marlene", ssml: true, name: "Cloud - Deutsch - Marlene"}, + "de-DE_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "de-DE", ename: "Hans", ssml: true, name: "Cloud - Deutsch - Hans"}, + "en-US_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Salli", ssml: true, name: "Cloud - en-US - Female - Salli"}, + "en-US_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Joey", ssml: true, name: "Cloud - en-US - Male - Joey"}, + "da-DK_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "da-DK", ename: "Naja", ssml: true, name: "Cloud - da-DK - Female - Naja"}, + "da-DK_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "da-DK", ename: "Mads", ssml: true, name: "Cloud - da-DK - Male - Mads"}, + "en-AU_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-AU", ename: "Nicole", ssml: true, name: "Cloud - en-AU - Female - Nicole"}, + "en-AU_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-AU", ename: "Russell", ssml: true, name: "Cloud - en-AU - Male - Russell"}, + "en-GB_CLOUD_Female_Amy": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-GB", ename: "Amy", ssml: true, name: "Cloud - en-GB - Female - Amy"}, + "en-GB_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-GB", ename: "Brian", ssml: true, name: "Cloud - en-GB - Male - Brian"}, + "en-GB_CLOUD_Female_Emma": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-GB", ename: "Emma", ssml: true, name: "Cloud - en-GB - Female - Emma"}, + "en-GB-WLS_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-GB-WLS", ename: "Gwyneth", ssml: true, name: "Cloud - en-GB-WLS - Female - Gwyneth"}, + "en-GB-WLS_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-GB-WLS", ename: "Geraint", ssml: true, name: "Cloud - en-GB-WLS - Male - Geraint"}, + "cy-GB_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "cy-GB", ename: "Gwyneth", ssml: true, name: "Cloud - cy-GB - Female - Gwyneth"}, + "cy-GB_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "cy-GB", ename: "Geraint", ssml: true, name: "Cloud - cy-GB - Male - Geraint"}, + "en-IN_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-IN", ename: "Raveena", ssml: true, name: "Cloud - en-IN - Female - Raveena"}, + "en-US_CLOUD_Male_Chipmunk":{gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Chipmunk", ssml: true, name: "Cloud - en-US - Male - Chipmunk"}, + "en-US_CLOUD_Male_Eric": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Eric", ssml: true, name: "Cloud - en-US - Male - Eric"}, + "en-US_CLOUD_Female_Ivy": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Ivy", ssml: true, name: "Cloud - en-US - Female - Ivy"}, + "en-US_CLOUD_Female_Jennifer": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Jennifer", ssml: true, name: "Cloud - en-US - Female - Jennifer"}, + "en-US_CLOUD_Male_Justin": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Justin", ssml: true, name: "Cloud - en-US - Male - Justin"}, + "en-US_CLOUD_Female_Kendra": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Kendra", ssml: true, name: "Cloud - en-US - Female - Kendra"}, + "en-US_CLOUD_Female_Kimberly": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Kimberly", ssml: true, name: "Cloud - en-US - Female - Kimberly"}, + "es-ES_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "es-ES", ename: "Conchita", ssml: true, name: "Cloud - es-ES - Female - Conchita"}, + "es-ES_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "es-ES", ename: "Enrique", ssml: true, name: "Cloud - es-ES - Male - Enrique"}, + "es-US_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "es-US", ename: "Penelope", ssml: true, name: "Cloud - es-US - Female - Penelope"}, + "es-US_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "es-US", ename: "Miguel", ssml: true, name: "Cloud - es-US - Male - Miguel"}, + "fr-CA_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "fr-CA", ename: "Chantal", ssml: true, name: "Cloud - fr-CA - Female - Chantal"}, + "fr-FR_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "fr-FR", ename: "Celine", ssml: true, name: "Cloud - fr-FR - Female - Celine"}, + "fr-FR_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "fr-FR", ename: "Mathieu", ssml: true, name: "Cloud - fr-FR - Male - Mathieu"}, + "is-IS_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "is-IS", ename: "Dora", ssml: true, name: "Cloud - is-IS - Female - Dora"}, + "is-IS_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "is-IS", ename: "Karl", ssml: true, name: "Cloud - is-IS - Male - Karl"}, + "it-IT_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "it-IT", ename: "Carla", ssml: true, name: "Cloud - it-IT - Female - Carla"}, + "it-IT_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "it-IT", ename: "Giorgio", ssml: true, name: "Cloud - it-IT - Male - Giorgio"}, + "nb-NO_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "nb-NO", ename: "Liv", ssml: true, name: "Cloud - nb-NO - Female - Liv"}, + "nl-NL_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "nl-NL", ename: "Lotte", ssml: true, name: "Cloud - nl-NL - Female - Lotte"}, + "nl-NL_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "nl-NL", ename: "Ruben", ssml: true, name: "Cloud - nl-NL - Male - Ruben"}, + "pl-PL_CLOUD_Female_Agnieszka":{gender: "Female", engine: "cloud",params: ['cloud'], language: "pl-PL", ename: "Agnieszka", ssml: true, name: "Cloud - pl-PL - Female - Agnieszka"}, + "pl-PL_CLOUD_Male_Jacek": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Jacek", ssml: true, name: "Cloud - pl-PL - Male - Jacek"}, + "pl-PL_CLOUD_Female_Ewa": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Ewa", ssml: true, name: "Cloud - pl-PL - Female - Ewa"}, + "pl-PL_CLOUD_Male_Jan": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Jan", ssml: true, name: "Cloud - pl-PL - Male - Jan"}, + "pl-PL_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Maja", ssml: true, name: "Cloud - pl-PL - Female - Maja"}, + "pt-BR_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pt-BR", ename: "Vitoria", ssml: true, name: "Cloud - pt-BR - Female - Vitoria"}, + "pt-BR_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pt-BR", ename: "Ricardo", ssml: true, name: "Cloud - pt-BR - Male - Ricardo"}, + "pt-PT_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pt-PT", ename: "Cristiano", ssml: true, name: "Cloud - pt-PT - Male - Cristiano"}, + "pt-PT_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pt-PT", ename: "Ines", ssml: true, name: "Cloud - pt-PT - Female - Ines"}, + "ro-RO_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "ro-RO", ename: "Carmen", ssml: true, name: "Cloud - ro-RO - Female - Carmen"}, + "sv-SE_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "sv-SE", ename: "Astrid", ssml: true, name: "Cloud - sv-SE - Female - Astrid"}, + "tr-TR_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "tr-TR", ename: "Filiz", ssml: true, name: "Cloud - tr-TR - Female - Filiz"}, + + "ru-RU_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "ru-RU", ename: "Tatyana", ssml: true, name: "AWS Polly - Русский - Татьяна"}, + "ru-RU_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "ru-RU", ename: "Maxim", ssml: true, name: "AWS Polly - Русский - Максим"}, + "de-DE_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "de-DE", ename: "Marlene", ssml: true, name: "AWS Polly - Deutsch - Marlene"}, + "de-DE_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "de-DE", ename: "Hans", ssml: true, name: "AWS Polly - Deutsch - Hans"}, + "en-US_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Salli", ssml: true, name: "AWS Polly - en-US - Female - Salli"}, + "en-US_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Joey", ssml: true, name: "AWS Polly - en-US - Male - Joey"}, + "da-DK_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "da-DK", ename: "Naja", ssml: true, name: "AWS Polly - da-DK - Female - Naja"}, + "da-DK_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "da-DK", ename: "Mads", ssml: true, name: "AWS Polly - da-DK - Male - Mads"}, + "en-AU_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-AU", ename: "Nicole", ssml: true, name: "AWS Polly - en-AU - Female - Nicole"}, + "en-AU_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-AU", ename: "Russell", ssml: true, name: "AWS Polly - en-AU - Male - Russell"}, + "en-GB_AP_Female_Amy": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB", ename: "Amy", ssml: true, name: "AWS Polly - en-GB - Female - Amy"}, + "en-GB_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB", ename: "Brian", ssml: true, name: "AWS Polly - en-GB - Male - Brian"}, + "en-GB_AP_Female_Emma": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB", ename: "Emma", ssml: true, name: "AWS Polly - en-GB - Female - Emma"}, + "en-GB-WLS_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB-WLS", ename: "Gwyneth", ssml: true, name: "AWS Polly - en-GB-WLS - Female - Gwyneth"}, + "en-GB-WLS_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB-WLS", ename: "Geraint", ssml: true, name: "AWS Polly - en-GB-WLS - Male - Geraint"}, + "cy-GB_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "cy-GB", ename: "Gwyneth", ssml: true, name: "AWS Polly - cy-GB - Female - Gwyneth"}, + "cy-GB_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "cy-GB", ename: "Geraint", ssml: true, name: "AWS Polly - cy-GB - Male - Geraint"}, + "en-IN_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-IN", ename: "Raveena", ssml: true, name: "AWS Polly - en-IN - Female - Raveena"}, + "en-US_AP_Male_Chipmunk": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Chipmunk", ssml: true, name: "AWS Polly - en-US - Male - Chipmunk"}, + "en-US_AP_Male_Eric": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Eric", ssml: true, name: "AWS Polly - en-US - Male - Eric"}, + "en-US_AP_Female_Ivy": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Ivy", ssml: true, name: "AWS Polly - en-US - Female - Ivy"}, + "en-US_AP_Female_Jennifer": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Jennifer", ssml: true, name: "AWS Polly - en-US - Female - Jennifer"}, + "en-US_AP_Male_Justin": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Justin", ssml: true, name: "AWS Polly - en-US - Male - Justin"}, + "en-US_AP_Female_Kendra": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Kendra", ssml: true, name: "AWS Polly - en-US - Female - Kendra"}, + "en-US_AP_Female_Kimberly": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Kimberly", ssml: true, name: "AWS Polly - en-US - Female - Kimberly"}, + "es-ES_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-ES", ename: "Conchita", ssml: true, name: "AWS Polly - es-ES - Female - Conchita"}, + "es-ES_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-ES", ename: "Enrique", ssml: true, name: "AWS Polly - es-ES - Male - Enrique"}, + "es-US_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-US", ename: "Penelope", ssml: true, name: "AWS Polly - es-US - Female - Penelope"}, + "es-US_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-US", ename: "Miguel", ssml: true, name: "AWS Polly - es-US - Male - Miguel"}, + "fr-CA_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "fr-CA", ename: "Chantal", ssml: true, name: "AWS Polly - fr-CA - Female - Chantal"}, + "fr-FR_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "fr-FR", ename: "Celine", ssml: true, name: "AWS Polly - fr-FR - Female - Celine"}, + "fr-FR_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "fr-FR", ename: "Mathieu", ssml: true, name: "AWS Polly - fr-FR - Male - Mathieu"}, + "is-IS_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "is-IS", ename: "Dora", ssml: true, name: "AWS Polly - is-IS - Female - Dora"}, + "is-IS_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "is-IS", ename: "Karl", ssml: true, name: "AWS Polly - is-IS - Male - Karl"}, + "it-IT_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "it-IT", ename: "Carla", ssml: true, name: "AWS Polly - it-IT - Female - Carla"}, + "it-IT_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "it-IT", ename: "Giorgio", ssml: true, name: "AWS Polly - it-IT - Male - Giorgio"}, + "nb-NO_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "nb-NO", ename: "Liv", ssml: true, name: "AWS Polly - nb-NO - Female - Liv"}, + "nl-NL_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "nl-NL", ename: "Lotte", ssml: true, name: "AWS Polly - nl-NL - Female - Lotte"}, + "nl-NL_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "nl-NL", ename: "Ruben", ssml: true, name: "AWS Polly - nl-NL - Male - Ruben"}, + "pl-PL_AP_Female_Agnieszka":{gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Agnieszka", ssml: true, name: "AWS Polly - pl-PL - Female - Agnieszka"}, + "pl-PL_AP_Male_Jacek": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Jacek", ssml: true, name: "AWS Polly - pl-PL - Male - Jacek"}, + "pl-PL_AP_Female_Ewa": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Ewa", ssml: true, name: "AWS Polly - pl-PL - Female - Ewa"}, + "pl-PL_AP_Male_Jan": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Jan", ssml: true, name: "AWS Polly - pl-PL - Male - Jan"}, + "pl-PL_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Maja", ssml: true, name: "AWS Polly - pl-PL - Female - Maja"}, + "pt-BR_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-BR", ename: "Vitoria", ssml: true, name: "AWS Polly - pt-BR - Female - Vitoria"}, + "pt-BR_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-BR", ename: "Ricardo", ssml: true, name: "AWS Polly - pt-BR - Male - Ricardo"}, + "pt-PT_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-PT", ename: "Cristiano", ssml: true, name: "AWS Polly - pt-PT - Male - Cristiano"}, + "pt-PT_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-PT", ename: "Ines", ssml: true, name: "AWS Polly - pt-PT - Female - Ines"}, + "ro-RO_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "ro-RO", ename: "Carmen", ssml: true, name: "AWS Polly - ro-RO - Female - Carmen"}, + "sv-SE_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "sv-SE", ename: "Astrid", ssml: true, name: "AWS Polly - sv-SE - Female - Astrid"}, + "tr-TR_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "tr-TR", ename: "Filiz", ssml: true, name: "AWS Polly - tr-TR - Female - Filiz"} +}; + +// Blockly.Sendto is global variable and defined in javascript/admin/google-blockly/own/blocks_sendto.js + +Blockly.Sendto.blocks['sayit'] = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' text' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + +Blockly.Blocks['sayit'] = { + init: function() { + var options = []; + if (typeof main !== 'undefined' && main.instances) { + for (var i = 0; i < main.instances.length; i++) { + var m = main.instances[i].match(/^system.adapter.sayit.(\d+)$/); + if (m) { + var n = parseInt(m[1], 10); + options.push(['sayit.' + n, '.' + n]); + } + } + if (options.length === 0) { + for (var u = 0; u <= 4; u++) { + options.push(['sayit.' + u, '.' + u]); + } + } + } else { + for (var u = 0; u <= 4; u++) { + options.push(['sayit.' + u, '.' + u]); + } + } + + this.appendDummyInput('INSTANCE') + .appendField(Blockly.Words['sayit'][systemLang]) + .appendField(new Blockly.FieldDropdown(options), 'INSTANCE'); + + var languages = [[Blockly.Words['sayit_configured'][systemLang], '']]; + for (var l in sayitEngines) { + if (sayitEngines.hasOwnProperty(l)) languages.push([sayitEngines[l].name, l]); + } + + this.appendDummyInput('LANGUAGE') + .appendField(new Blockly.FieldDropdown(languages), 'LANGUAGE'); + + var input = this.appendValueInput('VOLUME') + .setCheck('Number') + .appendField(Blockly.Words['sayit_volume'][systemLang]); + if (input.connection) input.connection._optional = true; + + this.appendValueInput('MESSAGE') + .appendField(Blockly.Words['sayit_message'][systemLang]); + + this.appendDummyInput('LOG') + .appendField(Blockly.Words['sayit_log'][systemLang]) + .appendField(new Blockly.FieldDropdown([ + [Blockly.Words['sayit_log_none'][systemLang], ''], + [Blockly.Words['sayit_log_info'][systemLang], 'log'], + [Blockly.Words['sayit_log_debug'][systemLang], 'debug'], + [Blockly.Words['sayit_log_warn'][systemLang], 'warn'], + [Blockly.Words['sayit_log_error'][systemLang], 'error'] + ]), 'LOG'); + + this.setInputsInline(false); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + + this.setColour(Blockly.Sendto.HUE); + this.setTooltip(Blockly.Words['sayit_tooltip'][systemLang]); + this.setHelpUrl(Blockly.Words['sayit_help'][systemLang]); + } +}; + +Blockly.JavaScript['sayit'] = function(block) { + var dropdown_instance = block.getFieldValue('INSTANCE'); + var dropdown_language = block.getFieldValue('LANGUAGE'); + var value_message = Blockly.JavaScript.valueToCode(block, 'MESSAGE', Blockly.JavaScript.ORDER_ATOMIC); + var value_volume = Blockly.JavaScript.valueToCode(block, 'VOLUME', Blockly.JavaScript.ORDER_ATOMIC); + var logLevel = block.getFieldValue('LOG'); + + var logText; + if (logLevel) { + logText = 'console.' + logLevel + '("sayIt' + (dropdown_language ? '[' + dropdown_language + ']' : '') + (value_volume ? '[Volume - ' + value_volume + ']' : '') + ': " + ' + value_message + ');\n' + } else { + logText = ''; + } + + return 'setState("sayit' + dropdown_instance + '.tts.text", "' + (dropdown_language ? dropdown_language + ';' : '') + (value_volume !== null && value_volume !== '' ? value_volume + ';' : '') + '" + ' + value_message + ');\n' + + logText; +}; diff --git a/admin/engines.js b/admin/engines.js new file mode 100644 index 0000000..685ccff --- /dev/null +++ b/admin/engines.js @@ -0,0 +1,139 @@ +// DONT Forget to update sayitEngines in blockly.js (If you change this table) + +var sayitEngines = { + "en": {name: "Google - English", engine: "google", params: []}, + "de": {name: "Google - Deutsch", engine: "google", params: []}, + "ru": {name: "Google - Русский", engine: "google", params: []}, + "it": {name: "Google - Italiano", engine: "google", params: []}, + "es": {name: "Google - Espaniol", engine: "google", params: []}, + "fr": {name: "Google - Français", engine: "google", params: []}, + "ru_YA": {name: "Yandex - Русский", engine: "yandex", params: ['key', 'voice', 'emotion', 'ill', 'drunk', 'robot'], voice: ['jane', 'zahar'], emotion: ['none', 'good', 'neutral', 'evil', 'mixed']}, + + "ru-RU_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "ru-RU", ename: "Tatyana", ssml: true, name: "Cloud - Русский - Татьяна"}, + "ru-RU_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "ru-RU", ename: "Maxim", ssml: true, name: "Cloud - Русский - Максим"}, + "de-DE_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "de-DE", ename: "Marlene", ssml: true, name: "Cloud - Deutsch - Marlene"}, + "de-DE_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "de-DE", ename: "Hans", ssml: true, name: "Cloud - Deutsch - Hans"}, + "en-US_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Salli", ssml: true, name: "Cloud - en-US - Female - Salli"}, + "en-US_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Joey", ssml: true, name: "Cloud - en-US - Male - Joey"}, + "da-DK_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "da-DK", ename: "Naja", ssml: true, name: "Cloud - da-DK - Female - Naja"}, + "da-DK_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "da-DK", ename: "Mads", ssml: true, name: "Cloud - da-DK - Male - Mads"}, + "en-AU_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-AU", ename: "Nicole", ssml: true, name: "Cloud - en-AU - Female - Nicole"}, + "en-AU_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-AU", ename: "Russell", ssml: true, name: "Cloud - en-AU - Male - Russell"}, + "en-GB_CLOUD_Female_Amy": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-GB", ename: "Amy", ssml: true, name: "Cloud - en-GB - Female - Amy"}, + "en-GB_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-GB", ename: "Brian", ssml: true, name: "Cloud - en-GB - Male - Brian"}, + "en-GB_CLOUD_Female_Emma": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-GB", ename: "Emma", ssml: true, name: "Cloud - en-GB - Female - Emma"}, + "en-GB-WLS_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-GB-WLS", ename: "Gwyneth", ssml: true, name: "Cloud - en-GB-WLS - Female - Gwyneth"}, + "en-GB-WLS_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-GB-WLS", ename: "Geraint", ssml: true, name: "Cloud - en-GB-WLS - Male - Geraint"}, + "cy-GB_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "cy-GB", ename: "Gwyneth", ssml: true, name: "Cloud - cy-GB - Female - Gwyneth"}, + "cy-GB_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "cy-GB", ename: "Geraint", ssml: true, name: "Cloud - cy-GB - Male - Geraint"}, + "en-IN_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-IN", ename: "Raveena", ssml: true, name: "Cloud - en-IN - Female - Raveena"}, + "en-US_CLOUD_Male_Chipmunk":{gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Chipmunk", ssml: true, name: "Cloud - en-US - Male - Chipmunk"}, + "en-US_CLOUD_Male_Eric": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Eric", ssml: true, name: "Cloud - en-US - Male - Eric"}, + "en-US_CLOUD_Female_Ivy": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Ivy", ssml: true, name: "Cloud - en-US - Female - Ivy"}, + "en-US_CLOUD_Female_Jennifer": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Jennifer", ssml: true, name: "Cloud - en-US - Female - Jennifer"}, + "en-US_CLOUD_Male_Justin": {gender: "Male", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Justin", ssml: true, name: "Cloud - en-US - Male - Justin"}, + "en-US_CLOUD_Female_Kendra": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Kendra", ssml: true, name: "Cloud - en-US - Female - Kendra"}, + "en-US_CLOUD_Female_Kimberly": {gender: "Female", engine: "cloud", params: ['cloud'], language: "en-US", ename: "Kimberly", ssml: true, name: "Cloud - en-US - Female - Kimberly"}, + "es-ES_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "es-ES", ename: "Conchita", ssml: true, name: "Cloud - es-ES - Female - Conchita"}, + "es-ES_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "es-ES", ename: "Enrique", ssml: true, name: "Cloud - es-ES - Male - Enrique"}, + "es-US_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "es-US", ename: "Penelope", ssml: true, name: "Cloud - es-US - Female - Penelope"}, + "es-US_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "es-US", ename: "Miguel", ssml: true, name: "Cloud - es-US - Male - Miguel"}, + "fr-CA_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "fr-CA", ename: "Chantal", ssml: true, name: "Cloud - fr-CA - Female - Chantal"}, + "fr-FR_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "fr-FR", ename: "Celine", ssml: true, name: "Cloud - fr-FR - Female - Celine"}, + "fr-FR_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "fr-FR", ename: "Mathieu", ssml: true, name: "Cloud - fr-FR - Male - Mathieu"}, + "is-IS_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "is-IS", ename: "Dora", ssml: true, name: "Cloud - is-IS - Female - Dora"}, + "is-IS_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "is-IS", ename: "Karl", ssml: true, name: "Cloud - is-IS - Male - Karl"}, + "it-IT_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "it-IT", ename: "Carla", ssml: true, name: "Cloud - it-IT - Female - Carla"}, + "it-IT_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "it-IT", ename: "Giorgio", ssml: true, name: "Cloud - it-IT - Male - Giorgio"}, + "nb-NO_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "nb-NO", ename: "Liv", ssml: true, name: "Cloud - nb-NO - Female - Liv"}, + "nl-NL_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "nl-NL", ename: "Lotte", ssml: true, name: "Cloud - nl-NL - Female - Lotte"}, + "nl-NL_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "nl-NL", ename: "Ruben", ssml: true, name: "Cloud - nl-NL - Male - Ruben"}, + "pl-PL_CLOUD_Female_Agnieszka":{gender: "Female", engine: "cloud",params: ['cloud'], language: "pl-PL", ename: "Agnieszka", ssml: true, name: "Cloud - pl-PL - Female - Agnieszka"}, + "pl-PL_CLOUD_Male_Jacek": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Jacek", ssml: true, name: "Cloud - pl-PL - Male - Jacek"}, + "pl-PL_CLOUD_Female_Ewa": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Ewa", ssml: true, name: "Cloud - pl-PL - Female - Ewa"}, + "pl-PL_CLOUD_Male_Jan": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Jan", ssml: true, name: "Cloud - pl-PL - Male - Jan"}, + "pl-PL_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pl-PL", ename: "Maja", ssml: true, name: "Cloud - pl-PL - Female - Maja"}, + "pt-BR_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pt-BR", ename: "Vitoria", ssml: true, name: "Cloud - pt-BR - Female - Vitoria"}, + "pt-BR_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pt-BR", ename: "Ricardo", ssml: true, name: "Cloud - pt-BR - Male - Ricardo"}, + "pt-PT_CLOUD_Male": {gender: "Male", engine: "cloud", params: ['cloud'], language: "pt-PT", ename: "Cristiano", ssml: true, name: "Cloud - pt-PT - Male - Cristiano"}, + "pt-PT_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "pt-PT", ename: "Ines", ssml: true, name: "Cloud - pt-PT - Female - Ines"}, + "ro-RO_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "ro-RO", ename: "Carmen", ssml: true, name: "Cloud - ro-RO - Female - Carmen"}, + "sv-SE_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "sv-SE", ename: "Astrid", ssml: true, name: "Cloud - sv-SE - Female - Astrid"}, + "tr-TR_CLOUD_Female": {gender: "Female", engine: "cloud", params: ['cloud'], language: "tr-TR", ename: "Filiz", ssml: true, name: "Cloud - tr-TR - Female - Filiz"}, + + "en-US": {name: "PicoTTS - Englisch US", engine: "PicoTTS", params: []}, + "en-GB": {name: "PicoTTS - Englisch GB", engine: "PicoTTS", params: []}, + "de-DE": {name: "PicoTTS - Deutsch", engine: "PicoTTS", params: []}, + "it-IT": {name: "PicoTTS - Italiano", engine: "PicoTTS", params: []}, + "es-ES": {name: "PicoTTS - Espaniol", engine: "PicoTTS", params: []}, + "fr-FR": {name: "PicoTTS - Français", engine: "PicoTTS", params: []}, + + "ru-RU_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "ru-RU", ename: "Tatyana", ssml: true, name: "AWS Polly - Русский - Татьяна"}, + "ru-RU_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "ru-RU", ename: "Maxim", ssml: true, name: "AWS Polly - Русский - Максим"}, + "de-DE_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "de-DE", ename: "Marlene", ssml: true, name: "AWS Polly - Deutsch - Marlene"}, + "de-DE_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "de-DE", ename: "Hans", ssml: true, name: "AWS Polly - Deutsch - Hans"}, + "en-US_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Salli", ssml: true, name: "AWS Polly - en-US - Female - Salli"}, + "en-US_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Joey", ssml: true, name: "AWS Polly - en-US - Male - Joey"}, + "da-DK_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "da-DK", ename: "Naja", ssml: true, name: "AWS Polly - da-DK - Female - Naja"}, + "da-DK_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "da-DK", ename: "Mads", ssml: true, name: "AWS Polly - da-DK - Male - Mads"}, + "en-AU_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-AU", ename: "Nicole", ssml: true, name: "AWS Polly - en-AU - Female - Nicole"}, + "en-AU_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-AU", ename: "Russell", ssml: true, name: "AWS Polly - en-AU - Male - Russell"}, + "en-GB_AP_Female_Amy": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB", ename: "Amy", ssml: true, name: "AWS Polly - en-GB - Female - Amy"}, + "en-GB_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB", ename: "Brian", ssml: true, name: "AWS Polly - en-GB - Male - Brian"}, + "en-GB_AP_Female_Emma": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB", ename: "Emma", ssml: true, name: "AWS Polly - en-GB - Female - Emma"}, + "en-GB-WLS_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB-WLS", ename: "Gwyneth", ssml: true, name: "AWS Polly - en-GB-WLS - Female - Gwyneth"}, + "en-GB-WLS_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-GB-WLS", ename: "Geraint", ssml: true, name: "AWS Polly - en-GB-WLS - Male - Geraint"}, + "cy-GB_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "cy-GB", ename: "Gwyneth", ssml: true, name: "AWS Polly - cy-GB - Female - Gwyneth"}, + "cy-GB_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "cy-GB", ename: "Geraint", ssml: true, name: "AWS Polly - cy-GB - Male - Geraint"}, + "en-IN_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-IN", ename: "Raveena", ssml: true, name: "AWS Polly - en-IN - Female - Raveena"}, + "en-US_AP_Male_Chipmunk": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Chipmunk", ssml: true, name: "AWS Polly - en-US - Male - Chipmunk"}, + "en-US_AP_Male_Eric": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Eric", ssml: true, name: "AWS Polly - en-US - Male - Eric"}, + "en-US_AP_Female_Ivy": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Ivy", ssml: true, name: "AWS Polly - en-US - Female - Ivy"}, + "en-US_AP_Female_Jennifer": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Jennifer", ssml: true, name: "AWS Polly - en-US - Female - Jennifer"}, + "en-US_AP_Male_Justin": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Justin", ssml: true, name: "AWS Polly - en-US - Male - Justin"}, + "en-US_AP_Female_Kendra": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Kendra", ssml: true, name: "AWS Polly - en-US - Female - Kendra"}, + "en-US_AP_Female_Kimberly": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "en-US", ename: "Kimberly", ssml: true, name: "AWS Polly - en-US - Female - Kimberly"}, + "es-ES_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-ES", ename: "Conchita", ssml: true, name: "AWS Polly - es-ES - Female - Conchita"}, + "es-ES_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-ES", ename: "Enrique", ssml: true, name: "AWS Polly - es-ES - Male - Enrique"}, + "es-US_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-US", ename: "Penelope", ssml: true, name: "AWS Polly - es-US - Female - Penelope"}, + "es-US_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "es-US", ename: "Miguel", ssml: true, name: "AWS Polly - es-US - Male - Miguel"}, + "fr-CA_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "fr-CA", ename: "Chantal", ssml: true, name: "AWS Polly - fr-CA - Female - Chantal"}, + "fr-FR_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "fr-FR", ename: "Celine", ssml: true, name: "AWS Polly - fr-FR - Female - Celine"}, + "fr-FR_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "fr-FR", ename: "Mathieu", ssml: true, name: "AWS Polly - fr-FR - Male - Mathieu"}, + "is-IS_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "is-IS", ename: "Dora", ssml: true, name: "AWS Polly - is-IS - Female - Dora"}, + "is-IS_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "is-IS", ename: "Karl", ssml: true, name: "AWS Polly - is-IS - Male - Karl"}, + "it-IT_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "it-IT", ename: "Carla", ssml: true, name: "AWS Polly - it-IT - Female - Carla"}, + "it-IT_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "it-IT", ename: "Giorgio", ssml: true, name: "AWS Polly - it-IT - Male - Giorgio"}, + "nb-NO_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "nb-NO", ename: "Liv", ssml: true, name: "AWS Polly - nb-NO - Female - Liv"}, + "nl-NL_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "nl-NL", ename: "Lotte", ssml: true, name: "AWS Polly - nl-NL - Female - Lotte"}, + "nl-NL_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "nl-NL", ename: "Ruben", ssml: true, name: "AWS Polly - nl-NL - Male - Ruben"}, + "pl-PL_AP_Female_Agnieszka":{gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Agnieszka", ssml: true, name: "AWS Polly - pl-PL - Female - Agnieszka"}, + "pl-PL_AP_Male_Jacek": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Jacek", ssml: true, name: "AWS Polly - pl-PL - Male - Jacek"}, + "pl-PL_AP_Female_Ewa": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Ewa", ssml: true, name: "AWS Polly - pl-PL - Female - Ewa"}, + "pl-PL_AP_Male_Jan": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Jan", ssml: true, name: "AWS Polly - pl-PL - Male - Jan"}, + "pl-PL_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pl-PL", ename: "Maja", ssml: true, name: "AWS Polly - pl-PL - Female - Maja"}, + "pt-BR_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-BR", ename: "Vitoria", ssml: true, name: "AWS Polly - pt-BR - Female - Vitoria"}, + "pt-BR_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-BR", ename: "Ricardo", ssml: true, name: "AWS Polly - pt-BR - Male - Ricardo"}, + "pt-PT_AP_Male": {gender: "Male", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-PT", ename: "Cristiano", ssml: true, name: "AWS Polly - pt-PT - Male - Cristiano"}, + "pt-PT_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "pt-PT", ename: "Ines", ssml: true, name: "AWS Polly - pt-PT - Female - Ines"}, + "ro-RO_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "ro-RO", ename: "Carmen", ssml: true, name: "AWS Polly - ro-RO - Female - Carmen"}, + "sv-SE_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "sv-SE", ename: "Astrid", ssml: true, name: "AWS Polly - sv-SE - Female - Astrid"}, + "tr-TR_AP_Female": {gender: "Female", engine: "polly", params: ['accessKey', 'secretKey', 'region'], language: "tr-TR", ename: "Filiz", ssml: true, name: "AWS Polly - tr-TR - Female - Filiz"} +}; + +var sayitOptions = { + "browser": {name: "Browser", params: ['engine', 'cache', 'instance'], mp3Required: true, checkLength: true, func: (typeof sayItBrowser !== 'undefined') ? sayItBrowser : null, server: true, libs: ['fs', 'crypto', 'http']}, + "mp24ftp": {name: "MediaPlayer24+FTP", params: ['engine', 'cache', 'server', 'port', 'user', 'pass'], mp3Required: true, checkLength: true, func: (typeof sayItMP24ftp !== 'undefined') ? sayItMP24ftp : null, server: false, libs: ['fs', 'crypto', 'http', 'jsftp']}, + "mp24": {name: "MediaPlayer24", params: ['server'], mp3Required: false, checkLength: true, func: (typeof sayItMP24 !== 'undefined') ? sayItMP24 : null, server: false, libs: ['fs', 'crypto', 'http']}, + "system": {name: "System", params: ['engine', 'cache'], mp3Required: true, checkLength: false, func: (typeof sayItSystem !== 'undefined') ? sayItSystem : null, server: false, libs: ['fs', 'crypto', 'http', 'child_process', 'os']}, + "windows": {name: "Windows default", params: [], mp3Required: false, checkLength: true, func: (typeof sayItWindows !== 'undefined') ? sayItWindows : null, server: false, libs: ['fs', 'child_process']}, + "sonos": {name: "Sonos", params: ['engine', 'cache', 'device', 'web', 'webServer'], mp3Required: true, checkLength: true, func: (typeof sayItSonos !== 'undefined') ? sayItSonos : null, server: true, libs: ['fs', 'crypto', 'http']}, + "chromecast": {name: "Chromecast", params: ['engine', 'cache', 'cDevice', 'web', 'webServer'], mp3Required: true, checkLength: true, func: (typeof sayItChromecast !== 'undefined') ? sayItChromecast : null, server: true, libs: ['fs', 'crypto', 'http']}, + "mpd": {name: "MPD", params: ['engine', 'cache', 'mpd_device', 'web', 'webServer'], mp3Required: true, checkLength: true, func: (typeof sayItMpd !== 'undefined') ? sayItMpd : null, server: true, libs: ['fs', 'crypto', 'http']}, + "googleHome": {name: "Google Home", params: ['engine', 'cache', 'server', 'web', 'webServer'], mp3Required: true, checkLength: true, func: (typeof sayItGoogleHome !== 'undefined') ? sayItGoogleHome : null, server: true, libs: ['fs', 'crypto', 'http', 'castv2-client']} +}; + +if (typeof module !== 'undefined' && module.parent) { + module.exports.sayitEngines = sayitEngines; + module.exports.sayitOptions = sayitOptions; +} \ No newline at end of file diff --git a/admin/i18n/de/translations.json b/admin/i18n/de/translations.json new file mode 100644 index 0000000..57d3ff2 --- /dev/null +++ b/admin/i18n/de/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "sayIt Einstellungen", + "Language:": "Sprache", + "Type:": "Ausgabe durch", + "Cache:": "Cache benutzen", + "Server:": "Server", + "Port:": "Port", + "User:": "Anwender", + "Password:": "Kennwort", + "Browser instance:": "Browser-Instanz", + "Device:": "Gerät", + "Web server IP:": "Web server IP", + "All": "Alle", + "Web instance:": "Web instance", + "Voice:": "Stimme", + "API Key:": "API Key", + "Emotion:": "Emotion", + "Drunk:": "Betrunken", + "Ill:": "Krank", + "Robot:": "Roboter", + "Announce:": "Ankündigung", + "place here": "Dateien hier ablegen", + "Announce timeout (sec):": "Ankündigungstimeout (Sek)", + "Announce length (sec):": "Ankündigunglänge (Sek)", + "API Key is not set!": "API Key ist leer!", + "System command:": "Systemkommando", + "Announce volume (%):": "Ankündigungslautstärke(%)", + "Linux player:": "Linux-Player", + "Ignore for non linux OS": "Ignorieren für nicht linux OS", + "Access Key:": "Access Key", + "Secret Key:": "Secret Key", + "AWS Region:": "AWS Region", + "Cloud instance:": "Cloud-Intsanz", + "Cache-Expiry:": "Cache-Expiry", + "Browse": "Suchen", + "select": "auswählen", + "error": "Fehler", + "IP not accessible for server": "IP ist für dieses Gerät nicht erreichbar", + "Install first cloud adapter": "Erst muss cloud-Adapter installiert werden", + "tooltip_type": "Damit wird gesetzt, wo die Sprache ausgegeben wird", + "tooltip_command": "Dieses Kommando wird ausgeführt, um eigenen System-Player zu benutzen" +} \ No newline at end of file diff --git a/admin/i18n/en/translations.json b/admin/i18n/en/translations.json new file mode 100644 index 0000000..2bf0bc4 --- /dev/null +++ b/admin/i18n/en/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "sayIt adapter settings", + "Language:": "Language", + "Type:": "Output in", + "Cache:": "Cache received audio files", + "Server:": "Server", + "Port:": "Port", + "User:": "User", + "Password:": "Password", + "Browser instance:": "Browser instance", + "Device:": "Device", + "Web server IP:": "Web server IP", + "All": "All", + "Web instance:": "Web instance", + "Voice:": "Voice", + "API Key:": "API Key", + "Emotion:": "Emotion", + "Drunk:": "Drunk", + "Ill:": "Ill", + "Robot:": "Robot", + "Announce:": "Announce", + "place here": "Place files here", + "Announce timeout (sec):": "Announce timeout (sec)", + "Announce length (sec):": "Announce length (sec)", + "API Key is not set!": "API Key is not set!", + "System command:": "System command", + "Announce volume (%):": "Announce volume(%)", + "Linux player:": "Linux player", + "Ignore for non linux OS": "Ignore for non linux OS", + "Access Key:": "Access Key", + "Secret Key:": "Secret Key", + "AWS Region:": "AWS Region", + "Cloud instance:": "Cloud instance", + "Cache-Expiry:": "Cache-Expiry", + "Browse": "Browse", + "select": "select", + "error": "error", + "IP not accessible for server": "IP is not accessible for the device", + "Install first cloud adapter": "Install first cloud adapter", + "tooltip_type": "Specify where the voice must be played", + "tooltip_command": "This command will be executed, to use the custom system player" +} \ No newline at end of file diff --git a/admin/i18n/es/translations.json b/admin/i18n/es/translations.json new file mode 100644 index 0000000..4b950bb --- /dev/null +++ b/admin/i18n/es/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "sayIt configuración del adaptador", + "Language:": "Idioma", + "Type:": "Salida en", + "Cache:": "Caché recibió archivos de audio", + "Server:": "Servidor", + "Port:": "Puerto", + "User:": "Usuario", + "Password:": "Contraseña", + "Browser instance:": "Instancia del navegador", + "Device:": "Dispositivo", + "Web server IP:": "Servidor web IP", + "All": "Todas", + "Web instance:": "Instancia web", + "Voice:": "Voz", + "API Key:": "Clave API", + "Emotion:": "Emoción", + "Drunk:": "Borracho", + "Ill:": "Enfermo", + "Robot:": "Robot", + "Announce:": "Anunciar", + "place here": "Coloque los archivos aquí", + "Announce timeout (sec):": "Anunciar tiempo de espera (sec)", + "Announce length (sec):": "Anunciar longitud (sec)", + "API Key is not set!": "¡La clave API no está configurada!", + "System command:": "Comando del sistema", + "Announce volume (%):": "Anunciar volumen (%)", + "Linux player:": "Reproductor de Linux", + "Ignore for non linux OS": "Ignorar para el sistema operativo no Linux", + "Access Key:": "Llave de acceso", + "Secret Key:": "Llave secreta", + "AWS Region:": "Región de AWS", + "Cloud instance:": "Instancia de la nube", + "Cache-Expiry:": "Caché-expiración", + "Browse": "Vistazo", + "select": "seleccionar", + "error": "error", + "IP not accessible for server": "IP no es accesible para el dispositivo", + "Install first cloud adapter": "Instale el primer adaptador de nube", + "tooltip_type": "Especifique dónde debe reproducirse la voz", + "tooltip_command": "Este comando se ejecutará para usar el reproductor de sistema personalizado" +} \ No newline at end of file diff --git a/admin/i18n/fr/translations.json b/admin/i18n/fr/translations.json new file mode 100644 index 0000000..ec26e9d --- /dev/null +++ b/admin/i18n/fr/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "sayIt paramètres de l'adaptateur", + "Language:": "La langue", + "Type:": "Sortie dans", + "Cache:": "Cache les fichiers audio reçus", + "Server:": "Serveur", + "Port:": "Port", + "User:": "Utilisateur", + "Password:": "Mot de passe", + "Browser instance:": "Instance du navigateur", + "Device:": "Dispositif", + "Web server IP:": "Adresse IP du serveur Web", + "All": "Tout", + "Web instance:": "Instance Web", + "Voice:": "Voix", + "API Key:": "clé API", + "Emotion:": "Émotion", + "Drunk:": "Ivre", + "Ill:": "Mauvais", + "Robot:": "Robot", + "Announce:": "Annoncer", + "place here": "Placez les fichiers ici", + "Announce timeout (sec):": "Annoncer le délai (sec)", + "Announce length (sec):": "Annoncer la longueur (sec)", + "API Key is not set!": "La clé de l'API n'est pas définie!", + "System command:": "Commande système", + "Announce volume (%):": "Annoncer le volume (%)", + "Linux player:": "Lecteur Linux", + "Ignore for non linux OS": "Ignorer pour les systèmes d'exploitation autres que Linux", + "Access Key:": "Clef d'accès", + "Secret Key:": "Clef secrète", + "AWS Region:": "Région AWS", + "Cloud instance:": "Instance de cloud", + "Cache-Expiry:": "Cache-Expiration", + "Browse": "Feuilleter", + "select": "sélectionner", + "error": "Erreur", + "IP not accessible for server": "L'adresse IP n'est pas accessible pour l'appareil", + "Install first cloud adapter": "Installez le premier adaptateur de cloud", + "tooltip_type": "Précisez où la voix doit être jouée", + "tooltip_command": "Cette commande sera exécutée pour utiliser le lecteur système personnalisé" +} \ No newline at end of file diff --git a/admin/i18n/it/translations.json b/admin/i18n/it/translations.json new file mode 100644 index 0000000..55abc10 --- /dev/null +++ b/admin/i18n/it/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "impostazioni dell'adattatore sayIt", + "Language:": "linguaggio", + "Type:": "Uscita in", + "Cache:": "La cache ha ricevuto file audio", + "Server:": "server", + "Port:": "Porta", + "User:": "Utente", + "Password:": "Parola d'ordine", + "Browser instance:": "Istanza del browser", + "Device:": "Dispositivo", + "Web server IP:": "IP del server Web", + "All": "Tutti", + "Web instance:": "Istanza Web", + "Voice:": "Voce", + "API Key:": "Chiave API", + "Emotion:": "Emozione", + "Drunk:": "Ubriaco", + "Ill:": "Malato", + "Robot:": "Robot", + "Announce:": "Annunciare", + "place here": "Inserisci i file qui", + "Announce timeout (sec):": "Annuncia timeout (sec)", + "Announce length (sec):": "Annunciare la lunghezza (sec)", + "API Key is not set!": "Chiave API non impostata!", + "System command:": "Comando di sistema", + "Announce volume (%):": "Annuncia volume (%)", + "Linux player:": "Lettore Linux", + "Ignore for non linux OS": "Ignora per sistema operativo non Linux", + "Access Key:": "Chiave di accesso", + "Secret Key:": "Chiave segreta", + "AWS Region:": "Regione AWS", + "Cloud instance:": "Istanza Cloud", + "Cache-Expiry:": "Cache-scadenza", + "Browse": "Navigare", + "select": "selezionare", + "error": "errore", + "IP not accessible for server": "L'IP non è accessibile per il dispositivo", + "Install first cloud adapter": "Installa il primo adattatore cloud", + "tooltip_type": "Specificare dove deve essere suonata la voce", + "tooltip_command": "Questo comando verrà eseguito, per utilizzare il player di sistema personalizzato" +} \ No newline at end of file diff --git a/admin/i18n/nl/translations.json b/admin/i18n/nl/translations.json new file mode 100644 index 0000000..846e343 --- /dev/null +++ b/admin/i18n/nl/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "sayIt adapter-instellingen", + "Language:": "Taal", + "Type:": "Uitgang in", + "Cache:": "Cache ontvangen audiobestanden", + "Server:": "Server", + "Port:": "Haven", + "User:": "Gebruiker", + "Password:": "Wachtwoord", + "Browser instance:": "Browserinstantie", + "Device:": "Apparaat", + "Web server IP:": "Webserver IP", + "All": "Alle", + "Web instance:": "Web-instantie", + "Voice:": "Stem", + "API Key:": "API sleutel", + "Emotion:": "Emotie", + "Drunk:": "Dronken", + "Ill:": "Ziek", + "Robot:": "Robot", + "Announce:": "aankondigen", + "place here": "Plaats hier bestanden", + "Announce timeout (sec):": "Announce time-out (sec)", + "Announce length (sec):": "Kondig lengte (sec)", + "API Key is not set!": "API-sleutel is niet ingesteld!", + "System command:": "Systeemopdracht", + "Announce volume (%):": "Volume aankondigen (%)", + "Linux player:": "Linux-speler", + "Ignore for non linux OS": "Negeren voor niet-Linux OS", + "Access Key:": "Toegangssleutel", + "Secret Key:": "Geheime sleutel", + "AWS Region:": "AWS-regio", + "Cloud instance:": "Cloud instantie", + "Cache-Expiry:": "Cache-Vervaldatum", + "Browse": "Blader", + "select": "kiezen", + "error": "fout", + "IP not accessible for server": "IP is niet toegankelijk voor het apparaat", + "Install first cloud adapter": "Installeer de eerste cloudadapter", + "tooltip_type": "Geef op waar de stem moet worden afgespeeld", + "tooltip_command": "Deze opdracht wordt uitgevoerd om de aangepaste systeemspeler te gebruiken" +} \ No newline at end of file diff --git a/admin/i18n/pl/translations.json b/admin/i18n/pl/translations.json new file mode 100644 index 0000000..541b577 --- /dev/null +++ b/admin/i18n/pl/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "sayTo ustawienia adaptera", + "Language:": "Język", + "Type:": "Wyjście w", + "Cache:": "Pamięć podręczna otrzymała pliki audio", + "Server:": "serwer", + "Port:": "Port", + "User:": "Użytkownik", + "Password:": "Hasło", + "Browser instance:": "Browser instance", + "Device:": "Urządzenie", + "Web server IP:": "Adres IP serwera WWW", + "All": "Wszystko", + "Web instance:": "Instancja internetowa", + "Voice:": "Głos", + "API Key:": "Klucz API", + "Emotion:": "Emocja", + "Drunk:": "Pijany", + "Ill:": "Chory", + "Robot:": "Robot", + "Announce:": "Ogłosić", + "place here": "Umieść pliki tutaj", + "Announce timeout (sec):": "Ogłaszaj limit czasu (sek)", + "Announce length (sec):": "Ogłaszaj długość (s)", + "API Key is not set!": "Klucz API nie jest ustawiony!", + "System command:": "Polecenie systemowe", + "Announce volume (%):": "Ogłaszaj objętość (%)", + "Linux player:": "Odtwarzacz Linux", + "Ignore for non linux OS": "Zignoruj dla systemu operacyjnego non linux", + "Access Key:": "Klucz dostępu", + "Secret Key:": "Sekretny klucz", + "AWS Region:": "Region AWS", + "Cloud instance:": "Instancja chmury", + "Cache-Expiry:": "Cache-Expiry", + "Browse": "Paść się", + "select": "Wybierz", + "error": "błąd", + "IP not accessible for server": "Adres IP jest niedostępny dla urządzenia", + "Install first cloud adapter": "Zainstaluj pierwszy adapter chmury", + "tooltip_type": "Określ, gdzie musi być odtwarzany głos", + "tooltip_command": "To polecenie zostanie wykonane, aby użyć niestandardowego odtwarzacza systemu" +} \ No newline at end of file diff --git a/admin/i18n/pt/translations.json b/admin/i18n/pt/translations.json new file mode 100644 index 0000000..d96f888 --- /dev/null +++ b/admin/i18n/pt/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "configurações do adaptador sayIt", + "Language:": "Língua", + "Type:": "Saída em", + "Cache:": "Cache recebido arquivos de áudio", + "Server:": "Servidor", + "Port:": "Porta", + "User:": "Do utilizador", + "Password:": "Senha", + "Browser instance:": "Instância do navegador", + "Device:": "Dispositivo", + "Web server IP:": "IP do servidor da Web", + "All": "Todos", + "Web instance:": "Instância da Web", + "Voice:": "Voz", + "API Key:": "Chave API", + "Emotion:": "Emoção", + "Drunk:": "Bêbado", + "Ill:": "Eu vou", + "Robot:": "Robô", + "Announce:": "Anunciar", + "place here": "Coloque arquivos aqui", + "Announce timeout (sec):": "Anular o tempo limite (seg)", + "Announce length (sec):": "Anunciar o comprimento (seg)", + "API Key is not set!": "A chave da API não está definida!", + "System command:": "Comando do sistema", + "Announce volume (%):": "Anunciar volume (%)", + "Linux player:": "Jogador Linux", + "Ignore for non linux OS": "Ignorar para SO não linux", + "Access Key:": "Chave de acesso", + "Secret Key:": "Chave secreta", + "AWS Region:": "Região da AWS", + "Cloud instance:": "Instância de nuvem", + "Cache-Expiry:": "Cache-Expiração", + "Browse": "Squeaky toy", + "select": "selecione", + "error": "erro", + "IP not accessible for server": "IP não está acessível para o dispositivo", + "Install first cloud adapter": "Instalar o primeiro adaptador de nuvem", + "tooltip_type": "Especifique onde a voz deve ser tocada", + "tooltip_command": "Este comando será executado para usar o player do sistema personalizado" +} \ No newline at end of file diff --git a/admin/i18n/ru/translations.json b/admin/i18n/ru/translations.json new file mode 100644 index 0000000..90a74a9 --- /dev/null +++ b/admin/i18n/ru/translations.json @@ -0,0 +1,42 @@ +{ + "sayIt adapter settings": "sayIt Настройки", + "Language:": "Язык", + "Type:": "Вывод звука", + "Cache:": "Кэшировать полученные аудио-файлы", + "Server:": "Сервер", + "Port:": "Порт", + "User:": "Пользователь", + "Password:": "Пароль", + "Browser instance:": "ID броузера", + "Device:": "Устройство", + "Web server IP:": "IP Веб сервера", + "All": "Все", + "Web instance:": "Web instance", + "Voice:": "Голос", + "API Key:": "API Key", + "Emotion:": "Емоциональный окрас", + "Drunk:": "Пьяный голос", + "Ill:": "Больной голос", + "Robot:": "Голос робота", + "Announce:": "Извещение", + "place here": "Перетащите файлы сюда", + "Announce timeout (sec):": "Таймаут извещения (сек)", + "Announce length (sec):": "Длительность извещения (сек)", + "API Key is not set!": "API Key отсутствует!", + "System command:": "Системный вызов", + "Announce volume (%):": "Громкость извещения(%)", + "Linux player:": "Linux плейер", + "Ignore for non linux OS": "Игнорировать для не linux OS", + "Access Key:": "Access Key", + "Secret Key:": "Secret Key", + "AWS Region:": "AWS регион", + "Cloud instance:": "Cloud драйвер", + "Cache-Expiry:": "Срок хранения кэш", + "Browse": "Искать", + "select": "выбрать", + "error": "ошибка", + "IP not accessible for server": "Этот IP недоступен для устройства", + "Install first cloud adapter": "Сначала установите cloud драйвер", + "tooltip_type": "Укажите как и где будет выводится голос", + "tooltip_command": "Эта команда выполнится для проигрывания с собственным плеером" +} \ No newline at end of file diff --git a/admin/index.html b/admin/index.html new file mode 100644 index 0000000..023c8f7 --- /dev/null +++ b/admin/index.html @@ -0,0 +1,650 @@ + + + + + + + + + + + + + + + + + +
+ +

sayIt adapter settings

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Ignore for non linux OS +
 
  
+
+ +
+ + diff --git a/admin/index_m.html b/admin/index_m.html new file mode 100644 index 0000000..bf73870 --- /dev/null +++ b/admin/index_m.html @@ -0,0 +1,811 @@ + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +  play +
+
+
+ File + +
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+ + +
+
+ + +
+
+
+
+ + + + Browse +
+
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+ + diff --git a/admin/sayit.png b/admin/sayit.png new file mode 100644 index 0000000000000000000000000000000000000000..c0ab8dd35bd918a601731957a04113c419ce5cc4 GIT binary patch literal 26669 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

8@gYD2q5ug>DVhs{yp5R!S(!OB$NbR7b0bu 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 IM6N<$f)H8ic>n+a literal 0 HcmV?d00001 diff --git a/admin/words.js b/admin/words.js new file mode 100644 index 0000000..a9ece60 --- /dev/null +++ b/admin/words.js @@ -0,0 +1,50 @@ +// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n +/*global systemDictionary:true */ +'use strict'; + +systemDictionary = { + "sayIt adapter settings": { "en": "sayIt adapter settings", "de": "sayIt Einstellungen", "ru": "sayIt Настройки", "pt": "configurações do adaptador sayIt", "nl": "sayIt adapter-instellingen", "fr": "sayIt paramètres de l'adaptateur", "it": "impostazioni dell'adattatore sayIt", "es": "sayIt configuración del adaptador", "pl": "sayTo ustawienia adaptera"}, + "Language:": { "en": "Language", "de": "Sprache", "ru": "Язык", "pt": "Língua", "nl": "Taal", "fr": "La langue", "it": "linguaggio", "es": "Idioma", "pl": "Język"}, + "Type:": { "en": "Output in", "de": "Ausgabe durch", "ru": "Вывод звука", "pt": "Saída em", "nl": "Uitgang in", "fr": "Sortie dans", "it": "Uscita in", "es": "Salida en", "pl": "Wyjście w"}, + "Cache:": { "en": "Cache received audio files", "de": "Cache benutzen", "ru": "Кэшировать полученные аудио-файлы", "pt": "Cache recebido arquivos de áudio", "nl": "Cache ontvangen audiobestanden", "fr": "Cache les fichiers audio reçus", "it": "La cache ha ricevuto file audio", "es": "Caché recibió archivos de audio", "pl": "Pamięć podręczna otrzymała pliki audio"}, + "Server:": { "en": "Server", "de": "Server", "ru": "Сервер", "pt": "Servidor", "nl": "Server", "fr": "Serveur", "it": "server", "es": "Servidor", "pl": "serwer"}, + "Port:": { "en": "Port", "de": "Port", "ru": "Порт", "pt": "Porta", "nl": "Haven", "fr": "Port", "it": "Porta", "es": "Puerto", "pl": "Port"}, + "User:": { "en": "User", "de": "Anwender", "ru": "Пользователь", "pt": "Do utilizador", "nl": "Gebruiker", "fr": "Utilisateur", "it": "Utente", "es": "Usuario", "pl": "Użytkownik"}, + "Password:": { "en": "Password", "de": "Kennwort", "ru": "Пароль", "pt": "Senha", "nl": "Wachtwoord", "fr": "Mot de passe", "it": "Parola d'ordine", "es": "Contraseña", "pl": "Hasło"}, + "Browser instance:": { "en": "Browser instance", "de": "Browser-Instanz", "ru": "ID броузера", "pt": "Instância do navegador", "nl": "Browserinstantie", "fr": "Instance du navigateur", "it": "Istanza del browser", "es": "Instancia del navegador", "pl": "Browser instance"}, + "Device:": { "en": "Device", "de": "Gerät", "ru": "Устройство", "pt": "Dispositivo", "nl": "Apparaat", "fr": "Dispositif", "it": "Dispositivo", "es": "Dispositivo", "pl": "Urządzenie"}, + "Web server IP:": { "en": "Web server IP", "de": "Web server IP", "ru": "IP Веб сервера", "pt": "IP do servidor da Web", "nl": "Webserver IP", "fr": "Adresse IP du serveur Web", "it": "IP del server Web", "es": "Servidor web IP", "pl": "Adres IP serwera WWW"}, + "All": { "en": "All", "de": "Alle", "ru": "Все", "pt": "Todos", "nl": "Alle", "fr": "Tout", "it": "Tutti", "es": "Todas", "pl": "Wszystko"}, + "Web instance:": { "en": "Web instance", "de": "Web instance", "ru": "Web instance", "pt": "Instância da Web", "nl": "Web-instantie", "fr": "Instance Web", "it": "Istanza Web", "es": "Instancia web", "pl": "Instancja internetowa"}, + "Voice:": { "en": "Voice", "de": "Stimme", "ru": "Голос", "pt": "Voz", "nl": "Stem", "fr": "Voix", "it": "Voce", "es": "Voz", "pl": "Głos"}, + "API Key:": { "en": "API Key", "de": "API Key", "ru": "API Key", "pt": "Chave API", "nl": "API sleutel", "fr": "clé API", "it": "Chiave API", "es": "Clave API", "pl": "Klucz API"}, + "Emotion:": { "en": "Emotion", "de": "Emotion", "ru": "Емоциональный окрас", "pt": "Emoção", "nl": "Emotie", "fr": "Émotion", "it": "Emozione", "es": "Emoción", "pl": "Emocja"}, + "Drunk:": { "en": "Drunk", "de": "Betrunken", "ru": "Пьяный голос", "pt": "Bêbado", "nl": "Dronken", "fr": "Ivre", "it": "Ubriaco", "es": "Borracho", "pl": "Pijany"}, + "Ill:": { "en": "Ill", "de": "Krank", "ru": "Больной голос", "pt": "Eu vou", "nl": "Ziek", "fr": "Mauvais", "it": "Malato", "es": "Enfermo", "pl": "Chory"}, + "Robot:": { "en": "Robot", "de": "Roboter", "ru": "Голос робота", "pt": "Robô", "nl": "Robot", "fr": "Robot", "it": "Robot", "es": "Robot", "pl": "Robot"}, + "Announce:": { "en": "Announce", "de": "Ankündigung", "ru": "Извещение", "pt": "Anunciar", "nl": "aankondigen", "fr": "Annoncer", "it": "Annunciare", "es": "Anunciar", "pl": "Ogłosić"}, + "place here": { "en": "Place files here", "de": "Dateien hier ablegen", "ru": "Перетащите файлы сюда", "pt": "Coloque arquivos aqui", "nl": "Plaats hier bestanden", "fr": "Placez les fichiers ici", "it": "Inserisci i file qui", "es": "Coloque los archivos aquí", "pl": "Umieść pliki tutaj"}, + "Announce timeout (sec):": { "en": "Announce timeout (sec)", "de": "Ankündigungstimeout (Sek)", "ru": "Таймаут извещения (сек)", "pt": "Anular o tempo limite (seg)", "nl": "Announce time-out (sec)", "fr": "Annoncer le délai (sec)", "it": "Annuncia timeout (sec)", "es": "Anunciar tiempo de espera (sec)", "pl": "Ogłaszaj limit czasu (sek)"}, + "Announce length (sec):": { "en": "Announce length (sec)", "de": "Ankündigunglänge (Sek)", "ru": "Длительность извещения (сек)", "pt": "Anunciar o comprimento (seg)", "nl": "Kondig lengte (sec)", "fr": "Annoncer la longueur (sec)", "it": "Annunciare la lunghezza (sec)", "es": "Anunciar longitud (sec)", "pl": "Ogłaszaj długość (s)"}, + "API Key is not set!": { "en": "API Key is not set!", "de": "API Key ist leer!", "ru": "API Key отсутствует!", "pt": "A chave da API não está definida!", "nl": "API-sleutel is niet ingesteld!", "fr": "La clé de l'API n'est pas définie!", "it": "Chiave API non impostata!", "es": "¡La clave API no está configurada!", "pl": "Klucz API nie jest ustawiony!"}, + "System command:": { "en": "System command", "de": "Systemkommando", "ru": "Системный вызов", "pt": "Comando do sistema", "nl": "Systeemopdracht", "fr": "Commande système", "it": "Comando di sistema", "es": "Comando del sistema", "pl": "Polecenie systemowe"}, + "Announce volume (%):": { "en": "Announce volume(%)", "de": "Ankündigungslautstärke(%)", "ru": "Громкость извещения(%)", "pt": "Anunciar volume (%)", "nl": "Volume aankondigen (%)", "fr": "Annoncer le volume (%)", "it": "Annuncia volume (%)", "es": "Anunciar volumen (%)", "pl": "Ogłaszaj objętość (%)"}, + "Linux player:": { "en": "Linux player", "de": "Linux-Player", "ru": "Linux плейер", "pt": "Jogador Linux", "nl": "Linux-speler", "fr": "Lecteur Linux", "it": "Lettore Linux", "es": "Reproductor de Linux", "pl": "Odtwarzacz Linux"}, + "Ignore for non linux OS": { "en": "Ignore for non linux OS", "de": "Ignorieren für nicht linux OS", "ru": "Игнорировать для не linux OS", "pt": "Ignorar para SO não linux", "nl": "Negeren voor niet-Linux OS", "fr": "Ignorer pour les systèmes d'exploitation autres que Linux", "it": "Ignora per sistema operativo non Linux", "es": "Ignorar para el sistema operativo no Linux", "pl": "Zignoruj dla systemu operacyjnego non linux"}, + "Access Key:": { "en": "Access Key", "de": "Access Key", "ru": "Access Key", "pt": "Chave de acesso", "nl": "Toegangssleutel", "fr": "Clef d'accès", "it": "Chiave di accesso", "es": "Llave de acceso", "pl": "Klucz dostępu"}, + "Secret Key:": { "en": "Secret Key", "de": "Secret Key", "ru": "Secret Key", "pt": "Chave secreta", "nl": "Geheime sleutel", "fr": "Clef secrète", "it": "Chiave segreta", "es": "Llave secreta", "pl": "Sekretny klucz"}, + "AWS Region:": { "en": "AWS Region", "de": "AWS Region", "ru": "AWS регион", "pt": "Região da AWS", "nl": "AWS-regio", "fr": "Région AWS", "it": "Regione AWS", "es": "Región de AWS", "pl": "Region AWS"}, + "Cloud instance:": { "en": "Cloud instance", "de": "Cloud-Instanz", "ru": "Cloud драйвер", "pt": "Instância de nuvem", "nl": "Cloud instantie", "fr": "Instance de cloud", "it": "Istanza Cloud", "es": "Instancia de la nube", "pl": "Instancja chmury"}, + "Cache-Expiry:": { "en": "Cache-Expiry", "de": "Cache-Ablauf", "ru": "Срок хранения кэш", "pt": "Cache-Expiração", "nl": "Cache-Vervaldatum", "fr": "Cache-Expiration", "it": "Cache-scadenza", "es": "Caché-expiración", "pl": "Cache-Expiry"}, + "Browse": { "en": "Browse", "de": "Suchen", "ru": "Искать", "pt": "Squeaky toy", "nl": "Blader", "fr": "Feuilleter", "it": "Navigare", "es": "Vistazo", "pl": "Paść się"}, + "select": { "en": "select", "de": "auswählen", "ru": "выбрать", "pt": "selecione", "nl": "kiezen", "fr": "sélectionner", "it": "selezionare", "es": "seleccionar", "pl": "Wybierz"}, + "error": { "en": "error", "de": "Fehler", "ru": "ошибка", "pt": "erro", "nl": "fout", "fr": "Erreur", "it": "errore", "es": "error", "pl": "błąd"}, + "IP not accessible for server": { "en": "IP is not accessible for the device", "de": "IP ist für dieses Gerät nicht erreichbar", "ru": "Этот IP недоступен для устройства", "pt": "IP não está acessível para o dispositivo", "nl": "IP is niet toegankelijk voor het apparaat", "fr": "L'adresse IP n'est pas accessible pour l'appareil", "it": "L'IP non è accessibile per il dispositivo", "es": "IP no es accesible para el dispositivo", "pl": "Adres IP jest niedostępny dla urządzenia"}, + "Install first cloud adapter": { "en": "Install cloud adapter at first", "de": "Installieren Sie zuerst den Cloud-Adapter", "ru": "Сначала установить облачный адаптер", "pt": "Instale o adaptador de nuvem primeiro", "nl": "Installeer eerst de cloud-adapter", "fr": "Installer l'adaptateur cloud au début", "it": "Installare inizialmente l'adattatore cloud", "es": "Instalar el adaptador de nube al principio", "pl": "Najpierw zainstaluj adapter chmury"}, + "tooltip_type": { "en": "Specify where the voice must be played", "de": "Damit wird gesetzt, wo die Sprache ausgegeben wird", "ru": "Укажите как и где будет выводится голос", "pt": "Especifique onde a voz deve ser tocada", "nl": "Geef op waar de stem moet worden afgespeeld", "fr": "Précisez où la voix doit être jouée", "it": "Specificare dove deve essere suonata la voce", "es": "Especifique dónde debe reproducirse la voz", "pl": "Określ, gdzie musi być odtwarzany głos"}, + "tooltip_command": { "en": "This command will be executed, to use the custom system player", "de": "Dieses Kommando wird ausgeführt, um eigenen System-Player zu benutzen", "ru": "Эта команда выполнится для проигрывания с собственным плеером", "pt": "Este comando será executado para usar o player do sistema personalizado", "nl": "Deze opdracht wordt uitgevoerd om de aangepaste systeemspeler te gebruiken", "fr": "Cette commande sera exécutée pour utiliser le lecteur système personnalisé", "it": "Questo comando verrà eseguito, per utilizzare il player di sistema personalizzato", "es": "Este comando se ejecutará para usar el reproductor de sistema personalizado", "pl": "To polecenie zostanie wykonane, aby użyć niestandardowego odtwarzacza systemu"}, + "File": { "en": "File", "de": "Datei", "ru": "файл", "pt": "Arquivo", "nl": "het dossier", "fr": "Fichier", "it": "File", "es": "Expediente", "pl": "Plik"}, + "tree": { "en": "tree", "de": "Baum", "ru": "дерево", "pt": "árvore", "nl": "boom", "fr": "arbre", "it": "albero", "es": "árbol", "pl": "drzewo"}, + "play": { "en": "play", "de": "abspielen", "ru": "играть", "pt": "Toque", "nl": "spelen", "fr": "jouer", "it": "giocare", "es": "jugar", "pl": "grać"}, + "Adapter must be enabled": { "en": "Adapter must be enabled", "de": "Adapter muss aktiviert sein", "ru": "Адаптер должен быть включен", "pt": "O adaptador deve estar ativado", "nl": "Adapter moet zijn ingeschakeld", "fr": "L'adaptateur doit être activé", "it": "L'adattatore deve essere abilitato", "es": "El adaptador debe estar habilitado", "pl": "Adapter musi być włączony"} +}; \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..d278435 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,25 @@ +version: 'test-{build}' +environment: + matrix: + - nodejs_version: '4' + - nodejs_version: '6' + - nodejs_version: '8' + - nodejs_version: '10' +platform: + - x86 + - x64 +clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%' +install: + - ps: 'Install-Product node $env:nodejs_version $env:platform' + - ps: '$NpmVersion = (npm -v).Substring(0,1)' + - ps: 'if($NpmVersion -eq 5) { npm install -g npm@5 }' + - ps: npm --version + - npm install + - npm install winston@2.3.1 + - 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production' +test_script: + - echo %cd% + - node --version + - npm --version + - npm test +build: 'off' diff --git a/cmdmp3/cmdmp3.exe b/cmdmp3/cmdmp3.exe new file mode 100644 index 0000000000000000000000000000000000000000..147073c0622d609c0ca596900da2fc776421d26f GIT binary patch literal 28160 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 literal 0 HcmV?d00001 diff --git a/cmdmp3/cmdmp3win.exe b/cmdmp3/cmdmp3win.exe new file mode 100644 index 0000000000000000000000000000000000000000..758292f0c77acbb37ff20d35731434d4d46efd95 GIT binary patch literal 28672 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 literal 0 HcmV?d00001 diff --git a/cmdmp3/readme.txt b/cmdmp3/readme.txt new file mode 100644 index 0000000..279f79a --- /dev/null +++ b/cmdmp3/readme.txt @@ -0,0 +1,2 @@ +For more information describing the files in this archive, please search my blog ( http://jimlawless.wordpress.com ) for the corresponding entry. + diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..fd48c48 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,401 @@ +'use strict'; + +var gulp = require('gulp'); +var fs = require('fs'); +var pkg = require('./package.json'); +var iopackage = require('./io-package.json'); +var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version; +/*var appName = getAppName(); + +function getAppName() { + var parts = __dirname.replace(/\\/g, '/').split('/'); + return parts[parts.length - 1].split('.')[0].toLowerCase(); +} +*/ +const fileName = 'words.js'; +var languages = { + en: {}, + de: {}, + ru: {}, + pt: {}, + nl: {}, + fr: {}, + it: {}, + es: {}, + pl: {} +}; + +function lang2data(lang, isFlat) { + var str = isFlat ? '' : '{\n'; + var count = 0; + for (var w in lang) { + if (lang.hasOwnProperty(w)) { + count++; + if (isFlat) { + str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n'; + } else { + var key = ' "' + w.replace(/"/g, '\\"') + '": '; + str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n'; + } + } + } + if (!count) return isFlat ? '' : '{\n}'; + if (isFlat) { + return str; + } else { + return str.substring(0, str.length - 2) + '\n}'; + } +} + +function readWordJs(src) { + try { + var words; + if (fs.existsSync(src + 'js/' + fileName)) { + words = fs.readFileSync(src + 'js/' + fileName).toString(); + } else { + words = fs.readFileSync(src + fileName).toString(); + } + + var lines = words.split(/\r\n|\r|\n/g); + var i = 0; + while (!lines[i].match(/^systemDictionary = {/)) { + i++; + } + lines.splice(0, i); + + // remove last empty lines + i = lines.length - 1; + while (!lines[i]) { + i--; + } + if (i < lines.length - 1) { + lines.splice(i + 1); + } + + lines[0] = lines[0].replace('systemDictionary = ', ''); + lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}'); + words = lines.join('\n'); + var resultFunc = new Function('return ' + words + ';'); + + return resultFunc(); + } catch (e) { + return null; + } +} +function padRight(text, totalLength) { + return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : ''); +} +function writeWordJs(data, src) { + var text = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n'; + text += '/*global systemDictionary:true */\n'; + text += '\'use strict\';\n\n'; + + text += 'systemDictionary = {\n'; + for (var word in data) { + if (data.hasOwnProperty(word)) { + text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50); + var line = ''; + for (var lang in data[word]) { + if (data[word].hasOwnProperty(lang)) { + line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' '; + } + } + if (line) { + line = line.trim(); + line = line.substring(0, line.length - 1); + } + text += line + '},\n'; + } + } + text = text.replace(/},\n$/, '}\n'); + text += '};'; + + if (fs.existsSync(src + 'js/' + fileName)) { + fs.writeFileSync(src + 'js/' + fileName, text); + } else { + fs.writeFileSync(src + '' + fileName, text); + } +} + +const EMPTY = ''; + +function words2languages(src) { + var langs = Object.assign({}, languages); + var data = readWordJs(src); + if (data) { + for (var word in data) { + if (data.hasOwnProperty(word)) { + for (var lang in data[word]) { + if (data[word].hasOwnProperty(lang)) { + langs[lang][word] = data[word][lang]; + // pre-fill all other languages + for (var j in langs) { + if (langs.hasOwnProperty(j)) { + langs[j][word] = langs[j][word] || EMPTY; + } + } + } + } + } + } + if (!fs.existsSync(src + 'i18n/')) { + fs.mkdirSync(src + 'i18n/'); + } + for (var l in langs) { + if (!langs.hasOwnProperty(l)) continue; + var keys = Object.keys(langs[l]); + //keys.sort(); + var obj = {}; + for (var k = 0; k < keys.length; k++) { + obj[keys[k]] = langs[l][keys[k]]; + } + if (!fs.existsSync(src + 'i18n/' + l)) { + fs.mkdirSync(src + 'i18n/' + l); + } + + fs.writeFileSync(src + 'i18n/' + l + '/translations.json', lang2data(obj)); + } + } else { + console.error('Cannot read or parse ' + fileName); + } +} +function words2languagesFlat(src) { + var langs = Object.assign({}, languages); + var data = readWordJs(src); + if (data) { + for (var word in data) { + if (data.hasOwnProperty(word)) { + for (var lang in data[word]) { + if (data[word].hasOwnProperty(lang)) { + langs[lang][word] = data[word][lang]; + // pre-fill all other languages + for (var j in langs) { + if (langs.hasOwnProperty(j)) { + langs[j][word] = langs[j][word] || EMPTY; + } + } + } + } + } + } + var keys = Object.keys(langs.en); + //keys.sort(); + for (var l in langs) { + if (!langs.hasOwnProperty(l)) continue; + var obj = {}; + for (var k = 0; k < keys.length; k++) { + obj[keys[k]] = langs[l][keys[k]]; + } + langs[l] = obj; + } + if (!fs.existsSync(src + 'i18n/')) { + fs.mkdirSync(src + 'i18n/'); + } + for (var ll in langs) { + if (!langs.hasOwnProperty(ll)) continue; + if (!fs.existsSync(src + 'i18n/' + ll)) { + fs.mkdirSync(src + 'i18n/' + ll); + } + + fs.writeFileSync(src + 'i18n/' + ll + '/flat.txt', lang2data(langs[ll], langs.en)); + } + fs.writeFileSync(src + 'i18n/flat.txt', keys.join('\n')); + } else { + console.error('Cannot read or parse ' + fileName); + } +} +function languagesFlat2words(src) { + var dirs = fs.readdirSync(src + 'i18n/'); + var langs = {}; + var bigOne = {}; + var order = Object.keys(languages); + dirs.sort(function (a, b) { + var posA = order.indexOf(a); + var posB = order.indexOf(b); + if (posA === -1 && posB === -1) { + if (a > b) return 1; + if (a < b) return -1; + return 0; + } else if (posA === -1) { + return -1; + } else if (posB === -1) { + return 1; + } else { + if (posA > posB) return 1; + if (posA < posB) return -1; + return 0; + } + }); + var keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n'); + + for (var l = 0; l < dirs.length; l++) { + if (dirs[l] === 'flat.txt') continue; + var lang = dirs[l]; + var values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n'); + langs[lang] = {}; + keys.forEach(function (word, i) { + langs[lang][word] = values[i].replace(/<\/ i>/g, '').replace(/<\/ b>/g, '').replace(/<\/ span>/g, '').replace(/% s/g, ' %s'); + }); + + var words = langs[lang]; + for (var word in words) { + if (words.hasOwnProperty(word)) { + bigOne[word] = bigOne[word] || {}; + if (words[word] !== EMPTY) { + bigOne[word][lang] = words[word]; + } + } + } + } + // read actual words.js + var aWords = readWordJs(); + + var temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt']; + if (aWords) { + // Merge words together + for (var w in aWords) { + if (aWords.hasOwnProperty(w)) { + if (!bigOne[w]) { + console.warn('Take from actual words.js: ' + w); + bigOne[w] = aWords[w] + } + dirs.forEach(function (lang) { + if (temporaryIgnore.indexOf(lang) !== -1) return; + if (!bigOne[w][lang]) { + console.warn('Missing "' + lang + '": ' + w); + } + }); + } + } + + } + + writeWordJs(bigOne, src); +} +function languages2words(src) { + var dirs = fs.readdirSync(src + 'i18n/'); + var langs = {}; + var bigOne = {}; + var order = Object.keys(languages); + dirs.sort(function (a, b) { + var posA = order.indexOf(a); + var posB = order.indexOf(b); + if (posA === -1 && posB === -1) { + if (a > b) return 1; + if (a < b) return -1; + return 0; + } else if (posA === -1) { + return -1; + } else if (posB === -1) { + return 1; + } else { + if (posA > posB) return 1; + if (posA < posB) return -1; + return 0; + } + }); + for (var l = 0; l < dirs.length; l++) { + if (dirs[l] === 'flat.txt') continue; + var lang = dirs[l]; + langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString(); + langs[lang] = JSON.parse(langs[lang]); + var words = langs[lang]; + for (var word in words) { + if (words.hasOwnProperty(word)) { + bigOne[word] = bigOne[word] || {}; + if (words[word] !== EMPTY) { + bigOne[word][lang] = words[word]; + } + } + } + } + // read actual words.js + var aWords = readWordJs(); + + var temporaryIgnore = ['pt', 'fr', 'nl', 'it']; + if (aWords) { + // Merge words together + for (var w in aWords) { + if (aWords.hasOwnProperty(w)) { + if (!bigOne[w]) { + console.warn('Take from actual words.js: ' + w); + bigOne[w] = aWords[w] + } + dirs.forEach(function (lang) { + if (temporaryIgnore.indexOf(lang) !== -1) return; + if (!bigOne[w][lang]) { + console.warn('Missing "' + lang + '": ' + w); + } + }); + } + } + + } + + writeWordJs(bigOne, src); +} + +gulp.task('adminWords2languages', function (done) { + words2languages('./admin/'); + done(); +}); + +gulp.task('adminWords2languagesFlat', function (done) { + words2languagesFlat('./admin/'); + done(); +}); + +gulp.task('adminLanguagesFlat2words', function (done) { + languagesFlat2words('./admin/'); + done(); +}); + +gulp.task('adminLanguages2words', function (done) { + languages2words('./admin/'); + done(); +}); + + +gulp.task('updatePackages', function (done) { + iopackage.common.version = pkg.version; + iopackage.common.news = iopackage.common.news || {}; + if (!iopackage.common.news[pkg.version]) { + var news = iopackage.common.news; + var newNews = {}; + + newNews[pkg.version] = { + en: 'news', + de: 'neues', + ru: 'новое' + }; + iopackage.common.news = Object.assign(newNews, news); + } + fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4)); + done(); +}); + +gulp.task('updateReadme', function (done) { + var readme = fs.readFileSync('README.md').toString(); + var pos = readme.indexOf('## Changelog\n'); + if (pos !== -1) { + var readmeStart = readme.substring(0, pos + '## Changelog\n'.length); + var readmeEnd = readme.substring(pos + '## Changelog\n'.length); + + if (readme.indexOf(version) === -1) { + var timestamp = new Date(); + var date = timestamp.getFullYear() + '-' + + ('0' + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' + + ('0' + (timestamp.getDate()).toString(10)).slice(-2); + + var news = ''; + if (iopackage.common.news && iopackage.common.news[pkg.version]) { + news += '* ' + iopackage.common.news[pkg.version].en; + } + + fs.writeFileSync('README.md', readmeStart + '### ' + version + ' (' + date + ')\n' + (news ? news + '\n\n' : '\n') + readmeEnd); + } + } + done(); +}); + +gulp.task('default', ['updatePackages', 'updateReadme']); \ No newline at end of file diff --git a/io-package.json b/io-package.json new file mode 100644 index 0000000..9ac765d --- /dev/null +++ b/io-package.json @@ -0,0 +1,160 @@ +{ + "common": { + "name": "sayit", + "version": "1.7.1", + "title": "Text to speech", + "titleLang": { + "en": "Text to speech", + "de": "Text zu Sprache", + "ru": "Текст в речь (Text to speech)", + "pt": "Texto para fala (Text to speech)", + "nl": "Tekst naar spraak (Text to speech)", + "fr": "Texte pour parler (Text to speech)", + "it": "Sintesi vocale (Text to speech)", + "es": "Texto a voz (Text to speech)", + "pl": "Tekst na mowę (Text to speech)" + }, + "desc": { + "en": "Text to speech in a cloud or local. Runs on windows, linux and OSX. It can play mp3/wav too.", + "de": "Text to speech in Cloud oder lokal für windows, linux und OSX. Mp3 und Wav können auch abgespielt werden.", + "ru": "Синтезатор речи локально или в облаке работает под windows, linux и OSX. Mp3 и Wav можно тоже проигрывать.", + "pt": "Texto para fala em uma nuvem ou local. Funciona no windows, linux e OSX. Pode reproduzir mp3 / wav também.", + "nl": "Tekst naar spraak in een cloud of lokaal. Werkt op Windows, Linux en OSX. Het kan ook mp3 / wav spelen.", + "fr": "Text to speech dans un nuage ou local. Fonctionne sur Windows, Linux et OSX. Il peut aussi jouer au format mp3 / wav.", + "it": "Sintesi vocale in un cloud o locale. Funziona su Windows, Linux e OSX. Può riprodurre anche mp3 / wav.", + "es": "Texto a voz en una nube o local. Se ejecuta en Windows, Linux y OSX. Puede reproducir mp3 / wav también.", + "pl": "Tekst na mowę w chmurze lub lokalnie. Działa w systemie Windows, Linux i OSX. Może również odtwarzać mp3 / wav." + }, + "authors": [ + "bluefox " + ], + "license": "MIT", + "platform": "Javascript/Node.js", + "mode": "daemon", + "readme": "https://git.spacen.net/yunkong2/yunkong2.sayit/blob/master/README.md", + "loglevel": "info", + "messagebox": true, + "supportStopInstance": true, + "stopBeforeUpdate": true, + "icon": "sayit.png", + "materialize": true, + "enabled": true, + "keywords": [ + "TTS", + "voice", + "speech" + ], + "extIcon": "https://git.spacen.net/yunkong2/yunkong2.sayit/master/admin/sayit.png", + "type": "multimedia", + "blockly": true + }, + "native": { + "type": "system", + "command": "", + "player": "", + "cache": true, + "engine": "", + "sonos": "", + "server": "", + "port": 0, + "user": "", + "pass": "", + "instance": "", + "web": "", + "webServer": "", + "cacheDir": "../../cache/", + "key": "", + "announce": "", + "annoTimeout": 15, + "annoDuration": 0, + "annoVolume": "70", + "cloud": "" + }, + "instanceObjects": [ + { + "_id": "tts.userfiles", + "type": "meta", + "common": { + "name": "Gong files", + "role": "meta.user" + }, + "native": {} + }, + { + "_id": "tts", + "type": "channel", + "common": { + "name": "Google text to speech", + "role": "media.tts" + }, + "native": {} + }, + { + "_id": "tts.volume", + "type": "state", + "common": { + "role": "level.volume", + "name": "TTS Volume", + "min": 0, + "max": 100, + "def": 70, + "type": "number", + "read": true, + "write": true, + "desc": "System volume or remote device volume" + }, + "native": {} + }, + { + "_id": "tts.text", + "type": "state", + "common": { + "role": "media.tts.text", + "name": "Text to speech", + "type": "string", + "read": true, + "write": true, + "desc": "This text will be converted to speech" + }, + "native": {} + }, + { + "_id": "tts.cachetext", + "type": "state", + "common": { + "role": "media.tts.cachetext", + "name": "Text to cache", + "type": "string", + "read": true, + "write": true, + "desc": "This text will be cached on disk to play it later" + }, + "native": {} + }, + { + "_id": "tts.playing", + "type": "state", + "common": { + "role": "media.tts.text", + "name": "Is now speaking", + "type": "boolean", + "read": true, + "write": false, + "desc": "Is true while speaking" + }, + "native": {} + }, + { + "_id": "tts.mp3", + "type": "state", + "common": { + "name": "Binary mp3 file", + "type": "file", + "read": true, + "write": false, + "desc": "Can be accessed from web server under http://ip:8082/state/sayit.0.tts.mp3" + }, + "native": {} + } + ] +} diff --git a/lib/speech2device.js b/lib/speech2device.js new file mode 100644 index 0000000..bb71c22 --- /dev/null +++ b/lib/speech2device.js @@ -0,0 +1,518 @@ +'use strict'; +const path = require('path'); + +function Speech2Device(adapter, libs, options) { + const sayitFuncs = { + browser: {func: sayItBrowser }, + mp24ftp: {func: sayItMP24ftp }, + mp24: {func: sayItMP24 }, + system: {func: sayItSystem }, + windows: {func: sayItWindows }, + sonos: {func: sayItSonos }, + chromecast: {func: sayItChromecast }, + mpd: {func: sayItMpd }, + googleHome: {func: sayItGoogleHome } + }; + const MP3FILE = path.normalize(__dirname + '/../' + adapter.namespace + '.say.mp3'); + const that = this; + + // Function to fill the .tts.mp3 state + function uploadToStates(text, callback) { + let fileData; + if (isPlayFile(text)) { + text = path.normalize(text); + try { + fileData = libs.fs.readFileSync(text); + } catch (e) { + callback && callback('Cannot upload file "' + text + '" to state: ' + e.toString()); + return; + } + } else { + try { + fileData = libs.fs.readFileSync(MP3FILE); + } catch (e) { + callback && callback('Cannot upload file "' + MP3FILE + '" to state: ' + e.toString()); + return; + } + } + adapter.setBinaryState(adapter.namespace + '.tts.mp3', fileData, callback); + } + + // Google home + let Client; + let DefaultMediaReceiver; + + function isPlayFile(text) { + if (text.length > 4) { + const ext = text.substring(text.length - 4).toLowerCase(); + if (ext === '.mp3' || ext === '.wav') { + return true; + } + } + return false; + } + + // If text is gong.mp3 or bong.wav + function sayItGetFileName(text) { + if (isPlayFile(text)) { + if (libs.fs.existsSync(text)) { + return text; + } else { + return __dirname + '/' + text; + } + } + + return MP3FILE; + } + + function sayItBrowser(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + adapter.setForeignState('vis.0.control.instance', adapter.config.instance); + adapter.setForeignState('vis.0.control.data', '/state/' + adapter.namespace + '.tts.mp3'); + adapter.setForeignState('vis.0.control.command', 'playSound'); + callback && callback(null, duration); + }); + } + + function sayItSonos(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + + volume = volume || options.sayLastVolume; + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + if (volume === 'null') volume = 0; + + if (adapter.config.device && options.webLink) { + adapter.log.info('Set "' + adapter.config.device + '.tts: ' + (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + adapter.setForeignState(adapter.config.device + '.tts', (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + } else if (options.webLink) { + adapter.log.info('Send to sonos ' + (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + adapter.sendTo('sonos', 'send', (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + } else { + adapter.log.warn('Web server is unavailable!'); + } + callback && callback(null, duration); + }); + } + + function sayItMpd(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + + volume = volume || options.sayLastVolume; + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + if (volume === 'null') volume = 0; + + if (adapter.config.mpd_device && options.webLink) { + adapter.log.info('Set "' + adapter.config.mpd_device + '.say: ' + (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + adapter.setForeignState(adapter.config.mpd_device + '.say', (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + } else if (options.webLink) { + adapter.log.info('Send to MPD ' + (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + adapter.sendTo('mpd', 'say', (volume ? (volume + ';') : '') + options.webLink + '/state/' + adapter.namespace + '.tts.mp3'); + } else { + adapter.log.warn('Web server is unavailable!'); + } + callback && callback(null, duration); + }); + } + + function sayItChromecast(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + + volume = volume || options.sayLastVolume; + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + if (volume === 'null') volume = 0; + + //Create announcement JSON + const announcement = { + url: options.webLink + '/state/' + adapter.namespace + '.tts.mp3' + }; + if (volume) { + announcement.volume = volume; + } + const announcementJSON = JSON.stringify(announcement); + + if (adapter.config.cDevice && options.webLink) { + const chromecastAnouncementDev = adapter.config.cDevice + '.player.announcement'; + adapter.log.info('Set "' + chromecastAnouncementDev + ' to ' + announcementJSON); + adapter.setForeignState(chromecastAnouncementDev, announcementJSON, () => { + //Check every 500 ms if the announcement has finished playing + let intervalHandler = setInterval(() => { + adapter.getForeignState(chromecastAnouncementDev, (err, state) => { + if (!err && state.ack) { + adapter.log.debug(chromecastAnouncementDev + ' finished playing announcement: ' + announcementJSON); + clearInterval(intervalHandler); + intervalHandler = null; + callback(); + } + }); + }, 500); + }); + } else { + adapter.log.warn('Web server is unavailable!'); + } + callback && callback(null, duration); + }); + } + + function launchGoogleHome(client, url, callback) { + client.launch(DefaultMediaReceiver, (err, player) => { + const media = { + contentId: url, + contentType: 'audio/mp3', + streamType: 'BUFFERED' // or LIVE + }; + player.load(media, {autoplay: true}, (err /*, status */) => { + if (err) { + adapter.log.error(err); + } + client.close(); + callback(err); + }); + }); + } + + function sendToGoogleHome(host, url, volume, callback) { + const client = new Client(); + client.connect(host, () => { + if (volume !== undefined) { + client.setVolume({ level: volume / 100 }, (err /* , newvol */) => { + if (err) adapter.log.error('there was an error setting the volume: ' + err); + launchGoogleHome(client, url, callback); + }); + } else { + launchGoogleHome(client, url, callback); + } + }); + + client.on('error', err => { + client.close(); + callback && callback(err); + }); + } + + function sayItGoogleHome(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + + Client = Client || require('castv2-client').Client; + DefaultMediaReceiver = DefaultMediaReceiver || require('castv2-client').DefaultMediaReceiver; + + volume = volume || options.sayLastVolume; + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + if (volume === 'null' || volume === null) volume = 0; + + if (adapter.config.server && options.webLink) { + const url = options.webLink + '/state/' + adapter.namespace + '.tts.mp3'; + adapter.log.debug('Send to google home "' + adapter.config.server + '": ' + url); + + sendToGoogleHome(adapter.config.server, url, volume, err => err && adapter.log.error(err)); + } else { + error = 'Web server is unavailable!'; + } + callback && callback(error, duration); + }); + } + + function sayItMP24(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + if (adapter.config.server && !isPlayFile(text)) { + adapter.log.debug('Request MediaPlayer24 "http://' + adapter.config.server + ':50000/tts=' + encodeURI(text) + '"'); + const opts = { + host: adapter.config.server, + port: 50000, + path: '/tts=' + encodeURI(text) + }; + libs.http.get(opts, res => { + let body = ''; + res.on('data', chunk => body += chunk); + // all data has been downloaded + res.on('end', () => adapter.log.debug('Response from MediaPlayer24 "' + adapter.config.server + '": ' + body)); + res.on('error', e => adapter.log.error('Cannot say text on MediaPlayer24 "' + adapter.config.server + '": ' + e.message)); + }).on('error', e => { + if (e.message === 'Parse Error') { + adapter.log.debug('Played successfully'); + } else { + error = 'Cannot say text on MediaPlayer24 "' + adapter.config.server + '":' + e.message; + } + }); + } + + callback && callback(error, duration + 2); + }); + } + + function sayItMP24ftp(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + // Copy mp3 file to android device to play it later with MediaPlayer + if (adapter.config.port && adapter.config.server) { + + const file = sayItGetFileName(text); + libs.jsftp = libs.jsftp || require('jsftp'); + + const ftp = new libs.jsftp({ + host: adapter.config.server, + port: parseInt(adapter.config.port, 10), // defaults to 21 + user: adapter.config.user || 'anonymous', // defaults to 'anonymous' + pass: adapter.config.pass || 'anonymous' // defaults to 'anonymous' + }); + + try { + // Copy file to FTP server + ftp.put(file, adapter.namespace + '.say.mp3', hadError => { + if (!hadError) { + const opts = { + host: adapter.config.server, + port: 50000, + path: '/track=' + adapter.namespace + '.say.mp3' + }; + + libs.http.get(opts, res => { + let body = ''; + res.on('data', chunk =>body += chunk); + // all data has been downloaded + res.on('end', () => adapter.log.debug('Response from MediaPlayer24 "' + adapter.config.server + '": ' + body)); + res.on('error', e => adapter.log.error('Cannot say text on MediaPlayer24 "' + adapter.config.server + '":' + e.message)); + }).on('error', e => { + if (e.message === 'Parse Error') { + adapter.log.debug('Played successfully'); + } else { + adapter.log.error('Cannot say text on MediaPlayer24 "' + adapter.config.server + '":' + e.message); + } + }); + } else { + adapter.log.error ('FTP error:' + hadError); + } + ftp.raw('quit', (err /* , data */) => { + if (err) adapter.log.error(err); + ftp.destroy(); + }); + }); + } catch (e) { + error = 'Cannot upload file to ' + adapter.config.server + ':' + adapter.config.port; + } + } + callback && callback(error, duration + 2); + }); + } + + function sayItSystem(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + if (!libs.os) libs.os = require('os'); + + uploadToStates(text, err => { + if (err) { + return callback && callback(err); + } + + const p = libs.os.platform(); + let ls = null; + const file = sayItGetFileName(text); + let cmd; + + that.sayItSystemVolume(volume); + + if (adapter.config.command) { + //custom command + adapter.setState('tts.playing', true); + if (adapter.config.command.indexOf('%s') !== -1) { + cmd = adapter.config.command.replace('%s', file); + } else { + if (p.match(/^win/)) { + cmd = adapter.config.command + ' "' + file + '"'; + } else { + cmd = adapter.config.command + ' ' + file; + } + } + ls = libs.child_process.exec(cmd, (error /* , stdout, stderr */) => { + if (error) adapter.log.error('Cannot play:' + error); + adapter.setState('tts.playing', false); + }); + } else { + if (p === 'linux') { + //linux + adapter.setState('tts.playing', true); + + if (adapter.config.player === 'omxplayer') { + cmd = 'omxplayer -o local ' + file; + } else { + cmd = 'mpg321 -g ' + options.sayLastVolume + ' ' + file; + } + ls = libs.child_process.exec(cmd, (error /* , stdout, stderr */) => { + if (error) adapter.log.error('Cannot play:' + error); + adapter.setState('tts.playing', false); + }); + } else if (p.match(/^win/)) { + //windows + adapter.setState('tts.playing', true); + ls = libs.child_process.exec('cmdmp3.exe "' + file + '"', {cwd: __dirname + '/../cmdmp3/'}, (error /* , stdout, stderr */) => { + if (error) adapter.log.error('Cannot play:' + error); + adapter.setState('tts.playing', false); + }); + } else if (p === 'darwin') { + //mac osx + adapter.setState('tts.playing', true); + ls = libs.child_process.exec('/usr/bin/afplay ' + file, (error /* , stdout, stderr */) => { + if (error) adapter.log.error('Cannot play:' + error); + adapter.setState('tts.playing', false); + }); + } + } + + if (ls) { + ls.on('error', e => { + throw new Error('sayIt.play: there was an error while playing the mp3 file:' + e); + }); + } + if (text === adapter.config.announce) { + callback && callback(null, duration); + } else { + callback && callback(null, duration + 2); + } + }); + } + + function sayItWindows(error, text, language, volume, duration, callback) { + if (error) { + callback && callback(error, 0); + return; + } + + // If mp3 file + if (isPlayFile(text)) { + sayItSystem(text, language, volume); + return; + } + + if (!libs.os) libs.os = require('os'); + + // Call windows own text 2 speech + const p = libs.os.platform(); + let ls = null; + // var file = sayItGetFileName(text); + + if (volume || volume === 0) that.sayItSystemVolume(volume); + + if (p.match(/^win/)) { + //windows + adapter.setState('tts.playing', true); + ls = libs.child_process.exec(__dirname + '/../say/SayStatic.exe ' + text, (error /* , stdout, stderr */) => { + if (error) adapter.log.error ('sayItWindows: ' + error); + adapter.setState('tts.playing', false); + }); + } else { + adapter.log.error ('sayItWindows: only windows OS is supported for Windows default mode'); + } + + if (ls) { + ls.on('error', e => { + throw new Error('sayIt.play: there was an error while text2speech on window:' + e); + }); + } + callback && callback(null, duration + 2); + } + + this.sayItSystemVolume = function (level) { + if ((!level && level !== 0) || level === 'null') return; + + level = parseInt(level); + if (level < 0) level = 0; + if (level > 100) level = 100; + + if (level === options.sayLastVolume) return; + if (!libs.os) libs.os = require('os'); + + adapter.setState('tts.volume', level, true); + + options.sayLastVolume = level; + + const p = libs.os.platform(); + let ls = null; + + if (p === 'linux' && adapter.config.player !== 'mpg321') { + //linux + try { + ls = libs.child_process.spawn('amixer', ['cset', 'name="Master Playback Volume"', '--', level + '%']); + } catch (err) { + adapter.log.error('amixer is not available, so you may hear no audio. Install manually!'); + ls = null; + } + } else if (p.match(/^win/)) { + //windows + // windows volume is from 0 to 65535 + level = Math.round((65535 * level) / 100); // because this level is from 0 to 100 + ls = libs.child_process.spawn(__dirname + '/../nircmd/nircmdc.exe', ['setsysvolume', level]); + } else if (p === 'darwin') { + //mac osx + ls = libs.child_process.spawn('sudo', ['osascript', '-e', '"set Volume ' + Math.round(level / 10) + '"']); + } + + if (ls) { + ls.on('error', e => { + adapter.log.error('sayIt.play: there was an error while playing the mp3 file:' + e); + }); + } + }; + + this.getFunction = function (type) { + if (sayitFuncs[type]) { + return sayitFuncs[type].func; + } else { + adapter.log.error('No processor for "' + type + '"'); + return null; + } + }; + + this.sayItIsPlayFile = isPlayFile; + + return this; +} + +module.exports = Speech2Device; diff --git a/lib/text2speech.js b/lib/text2speech.js new file mode 100644 index 0000000..f0d50bc --- /dev/null +++ b/lib/text2speech.js @@ -0,0 +1,484 @@ +'use strict'; + +function Text2Speech(adapter, libs, options, sayIt) { + const that = this; + const engines = require(__dirname + '/../admin/engines.js'); + const sayitEngines = engines.sayitEngines; + const MP3FILE = __dirname + '/../' + adapter.namespace + '.say.mp3'; + let appkey = null; + let cloudUrl = null; + let polly = null; + + function splitText(text, max) { + if (!max) max = 70; + if (text.length > max) { + const parts = text.split(/,|.|;|:/); + const result = []; + for (let p = 0; p < parts.length; p++) { + if (parts[p].length < max) { + result.push(parts[p]); + continue; + } + + const _parts = parts[p].split(' '); + let i = 0; + for (let w = 0; w < _parts.length; w++) { + if (_parts[i] && ((result[i] || '') + ' ' + _parts[w]).length > max) { + i++; + } + if (!result[i]) { + result.push(_parts[w]); + } else { + result[i] += ' ' + _parts[w]; + } + } + } + return result; + } else { + return [text]; + } + } + + function copyFile(text, language, volume, source, dest, callback) { + libs.fs = libs.fs || require('fs'); + + try { + const input = libs.fs.createReadStream(source); // Input stream + const output = libs.fs.createWriteStream(dest); // Output stream + + input.on('data', d =>output.write(d)); + // Copy in to out + input.on('error', err => { + throw err; + }); + + // Raise errors + input.on('end', () => { // When input ends + output.end(); // close output + adapter.log.info('Copied file "' + source + '" to "' + dest + '"'); + if (callback) callback(null, text, language, volume); // And notify callback + }); + } catch (e) { + if (callback) callback('Cannot copy file "' + source + '" to "' + dest + '": ' + e, '', '', volume); // And notify callback + } + } + + function cacheFile(error, text, language, volume, seconds, callback) { + libs.fs = libs.fs || require('fs'); + libs.path = libs.path || require('path'); + + if (!error) { + if (adapter.config.cache) { + const md5filename = libs.path.join(options.cacheDir, libs.crypto.createHash('md5').update(language + ';' + text).digest('hex') + '.mp3'); + + const stat = libs.fs.statSync(MP3FILE); + if (stat.size < 100) { + adapter.log.warn('Received file is too short: ' + libs.fs.readFileSync(MP3FILE).toString()); + } else { + copyFile(text, language, volume, MP3FILE, md5filename, (error, text, language, volume) => { + if (error) adapter.log.error(error); + }); + } + } + } + + callback(error, text, language, volume, seconds); + } + + function sayItGetSpeechGoogle(text, language, volume, callback) { + if (typeof volume === 'function') { + callback = volume; + volume = undefined; + } + + if (text.length === 0) { + if (callback) callback(null, '', language, volume, 0); + return; + } + + if (text.length > 70) { + const parts = splitText(text); + for (let t = 1; t < parts.length; t++) { + sayIt(parts[t], language, volume); + } + text = parts[0]; + } + + language = language || adapter.config.engine; + + // https://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&q=%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D0%B8%D1%82%D1%8C%207&tl=ru&total=1&idx=0&textlen=10 + const options = { + host: 'translate.google.com', + //port: 443, + path: '/translate_tts?ie=UTF-8&client=tw-ob&q=' + encodeURI(text) + '&tl=' + language + '&total=1&idx=0&textlen=' + text.length // + }; + + if (language === 'ru') { + options.headers = { + 'Accept-Encoding': 'identity;q=1, *;q=0', + Range: 'bytes=0-', + Referer: 'https://www.google.de/', + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36' + //"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0", + //"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + //"Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", + //"Accept-Encoding": "gzip, deflate"// + }; + } + + if (!libs.https) libs.https = require('https'); + if (!libs.fs) libs.fs = require('fs'); + + let sounddata = ''; + libs.https.get(options, res => { + res.setEncoding('binary'); + + res.on('data', chunk => sounddata += chunk); + + res.on('end', () => { + if (sounddata.length < 100) { + if (callback) callback('Cannot get file: received file is too short', text, language, volume, 0); + return; + } + + if (sounddata.toString().indexOf('302 Moved') !== -1) { + if (callback) callback('http://' + options.host + options.path + '\nCannot get file: ' + sounddata, text, language, volume, 0); + return; + } + + libs.fs.writeFile(MP3FILE, sounddata, 'binary', err => { + if (err) { + if (callback) callback('File error: ' + err, text, language, volume, 0); + } else { + that.getLength(MP3FILE, (error, seconds) => { + if (callback) callback(error, text, language, volume, seconds); + }); + } + }); + }); + }).on('error', err => { + sounddata = ''; + if (callback) callback('Cannot get file:' + err, text, language, volume, 0); + }); + } + + function sayItGetSpeechAcapela(text, language, volume, callback) { + const options = { + host: 'vaassl3.acapela-group.com', + path: '/Services/Synthesizer?prot_vers=2&req_voice=' + language + '22k&cl_env=FLASH_AS_3.0&req_text=%5Cvct%3D100%5C+%5Cspd%3D180%5C+' + + encodeURI(text) + '&req_asw_type=STREAM&cl_vers=1-30&req_err_as_id3=yes&cl_login=ACAPELA_BOX&cl_app=PROD&cl_pwd=0g7znor2aa' + }; + + if (!libs.https) libs.https = require('https'); + + let sounddata = ''; + libs.https.get(options, res => { + res.setEncoding('binary'); + + res.on('data', chunk => sounddata += chunk); + + res.on('end', () => { + if (sounddata.length < 100) { + if (callback) callback('Cannot get file: received file is too short', text, language, volume); + return; + } + + libs.fs.writeFile(MP3FILE, sounddata, 'binary', err => { + if (err) { + if (callback) callback('File error:' + err, text, language, volume); + } else { + console.log('File saved.'); + if (callback) callback(null, text, language, volume); + } + }); + }); + }).on('error', err => { + sounddata = ''; + if (callback) callback('Cannot get file:' + err, text, language, volume); + }); + } + + function sayItGetSpeechYandex(text, language, volume, callback) { + if (language === 'ru' || language === 'ru_YA') { + language = 'ru-RU'; + } + + if (!libs.https) libs.https = require('https'); + + /*emotion: good, neutral, evil, mixed + drunk: true, false + ill: true, false + robot: true, false + */ + const options = { + host: 'tts.voicetech.yandex.net', + path: '/generate?lang=' + language + '&format=mp3&speaker=' + adapter.config.voice + '&key=' + adapter.config.key + + '&text=' + encodeURI(text.trim()) + }; + + if (adapter.config.emotion && adapter.config.emotion !== 'none') options.path += '&emotion=' + adapter.config.emotion; + if (adapter.config.drunk === 'true' || adapter.config.drunk === true) options.path += '&drunk=true'; + if (adapter.config.ill === 'true' || adapter.config.ill === true) options.path += '&ill=true'; + if (adapter.config.robot === 'true' || adapter.config.robot === true) options.path += '&robot=true'; + + let sounddata = ''; + libs.https.get(options, res => { + res.setEncoding('binary'); + + res.on('data', chunk => sounddata += chunk); + + res.on('end', () => { + if (sounddata.length < 100) { + if (callback) callback('Cannot get file: received file is too short', text, language, volume, 0); + return; + } + + libs.fs.writeFile(MP3FILE, sounddata, 'binary', err => { + if (err) { + if (callback) callback('File error: ' + err, text, language, volume, 0); + } else { + if (callback) callback(null, text, language, volume); + } + }); + }); + }).on('error', err => { + sounddata = ''; + if (callback) callback('Cannot get file: ' + err, text, language, volume); + }); + } + + function sayItGetSpeechPolly(text, language, volume, callback) { + if (!libs.aws) libs.aws = require('aws-sdk'); + if (!libs.fs) libs.fs = require('fs'); + + try { + polly = polly || new libs.aws.Polly({ + accessKeyId: adapter.config.accessKey, + secretAccessKey: adapter.config.secretKey, + region: adapter.config.region + }); + let type = 'text'; + if (text.match(/<[-+\w\s'"=]+>/)) { + if (!text.match(/^/)) text = '' + text + ''; + type = 'ssml'; + } + const params = { + Text: text, + OutputFormat: 'mp3', + TextType: type, + VoiceId: sayitEngines[language].ename + }; + + polly.synthesizeSpeech(params, (err, data) => { + if (err) { + if (callback) callback('Cannot get answer: ' + JSON.stringify(err), text, language, volume, 0); + } else if (data) { + if (data.AudioStream instanceof Buffer) { + libs.fs.writeFile(MP3FILE, data.AudioStream, 'binary', err => { + if (err) { + if (callback) callback('File error: ' + err, text, language, volume, 0); + } else { + that.getLength(MP3FILE, (error, seconds) => { + if (callback) callback(error, text, language, volume, seconds); + }); + } + }); + } else { + if (callback) callback('Answer in invalid format: ' + (data ? data.toString() : 'null'), text, language, volume, 0); + } + } + }); + } catch (e) { + if (callback) callback(e.toString(), text, language, volume); + } + } + + function sayItGetSpeechCloud(text, language, volume, callback) { + if (!libs.https) libs.https = require('https'); + if (!libs.fs) libs.fs = require('fs'); + + if (!appkey) { + adapter.getForeignObject('system.adapter.' + adapter.config.cloud, (err, obj) => { + appkey = obj && obj.native && obj.native.apikey ? obj.native.apikey : 'error'; + cloudUrl = (obj && obj.native && obj.native.cloudUrl) || ''; + if (appkey && appkey.match(/^@pro_/)) { + if (cloudUrl.indexOf('https://yunkong2.pro:') === -1 && + cloudUrl.indexOf('https://yunkong2.info:') === -1) { + cloudUrl = 'https://yunkong2.pro:10555'; + } + } + cloudUrl = cloudUrl.replace(/https\:\/\//, '').replace(/:\d+/, ''); + sayItGetSpeechCloud(text, language, volume, callback); + }); + return; + } + + if (appkey === 'error') { + if (callback) callback('No app key found in "' + adapter.config.cloud + '".', text, language, volume, 0); + return; + } + + try { + let type = 'text'; + if (text.match(/<[-+\w\s'"=]+>/)) { + if (!text.match(/^/)) text = '' + text + ''; + type = 'ssml'; + } + const postData = JSON.stringify({ + Text: text, + appkey: appkey, + TextType: type, + VoiceId: sayitEngines[language].ename + }); + + const postOptions = { + host: cloudUrl, + port: 443, + path: '/polly/', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(postData) + } + }; + // Set up the request + const postReq = libs.https.request(postOptions, (res) => { + adapter.log.debug('Status code: ' + res.statusCode); + if (res.statusCode === 200) { + res.setEncoding('binary'); + } else { + res.setEncoding('utf8'); + } + let data = []; + res.on('data', chunk => { + if (typeof chunk === 'string') chunk = new Buffer(chunk, 'binary'); + data.push(chunk); + }); + + res.on('error', error => { + if (callback) { + callback('Cannot read: ' + error, text, language, volume, 0); + callback = null; + } + }); + + res.on('end', () => { + data = Buffer.concat(data); + if (data instanceof Buffer && data.length > 500) { + libs.fs.writeFile(MP3FILE, data, 'binary', err => { + if (err) { + if (callback) callback('File error: ' + err, text, language, volume, 0); + } else { + that.getLength(MP3FILE, (error, seconds) => { + if (callback) callback(error, text, language, volume, seconds); + }); + } + }); + } else { + if (callback) callback('Answer in invalid format: ' + (data ? data.toString().substring(0, 100) : 'null'), text, language, volume, 0); + } + }); + }); + + // post the data + postReq.write(postData); + postReq.end(); + } catch (e) { + if (callback) callback(e.toString(), text, language, volume); + } + } + + function sayItGetSpeechPicoTTS(text, language, volume, callback) { + if (!libs.fs) libs.fs = require('fs'); + if (!libs.exec) libs.exec = require('child_process').exec; + + try { + const cmd = 'pico2wave -l ' + language + ' -w ' + __dirname + '/say.wav "' + text + '"'; + libs.exec(cmd, (error, stdout, stderr) => { + if (error) { + if (callback) callback('Cannot create (pico2wave) "say.wav": ' + error, text, language, volume); + } else { + libs.exec('lame ' + __dirname + '/say.wav ' + MP3FILE, (error, stdout, stderr) => { + if (callback) callback(error ? 'Cannot create (lame) "say.mp3": ' + error : null, text, language, volume); + }); + } + }); + } catch (e) { + if (callback) callback(e.toString(), text, language, volume); + } + } + + this.getLength = function (fileName, callback) { + libs.fs = libs.fs || require('fs'); + + // create a new parser from a node ReadStream + if (fileName === adapter.config.announce && adapter.config.annoDuration) { + if (callback) callback(null, adapter.config.annoDuration - 1); + return; + } + + if (libs.fs.existsSync(fileName)) { + try { + const stat = libs.fs.statSync(fileName); + const size = stat.size; + if (callback) callback(null, Math.ceil(size / 4096)); + } catch (e) { + adapter.log.warn('Cannot read length of file ' + fileName); + if (callback) callback(null, 0); + } + } else { + if (callback) callback(null, 0); + } + }; + + const ENGINES = { + 'google': sayItGetSpeechGoogle, + 'yandex': sayItGetSpeechYandex, + 'acapela': sayItGetSpeechAcapela, + 'polly': sayItGetSpeechPolly, + 'cloud': sayItGetSpeechCloud, + 'PicoTTS': sayItGetSpeechPicoTTS + }; + + this.sayItGetSpeech = function (text, language, volume, callback) { + if (adapter.config.cache) { + libs.path = libs.path || require('path'); + const md5filename = libs.path.join(options.cacheDir, libs.crypto.createHash('md5').update(language + ';' + text).digest('hex') + '.mp3'); + if (libs.fs.existsSync(md5filename)) { + let cacheFileValid = true; + if (adapter.config.cacheExpiryDays) { + const fileStat = libs.fs.statSync(md5filename); + if (fileStat.ctime && (new Date().getTime()-new Date(fileStat.ctime).getTime() > adapter.config.cacheExpiryDays*1000*60*60*24)) { + cacheFileValid = false; + adapter.log.info('Cached File expired, remove and re-generate'); + libs.fs.unlinkSync(md5filename); + } + } + if (cacheFileValid) { + that.getLength(md5filename, (error, seconds) => { + if (callback) callback(error, md5filename, language, volume, seconds); + }); + return; + } + } + } + + if (sayitEngines[language] && sayitEngines[language].engine) { + if (!sayitEngines[language].ssml) text = text.replace(/<\/?[-+\w\s'"=]+>/g, ''); // remove SSML + + if (!ENGINES[sayitEngines[language].engine]) { + callback && callback('Engine ' + sayitEngines[language].engine + ' not yet supported.', text, language, volume, 0); + } else { + ENGINES[sayitEngines[language].engine](text, language, volume, + (error, _text, _language, _volume, seconds) => cacheFile(error, _text, _language, _volume, seconds, callback)); + } + } else { + sayItGetSpeechGoogle(text, language, volume, + (error, _text, _language, _volume, seconds) => cacheFile(error, _text, _language, _volume, seconds, callback)); + } + }; + + return this; +} + +module.exports = Text2Speech; diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..6af37ff --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,83 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +let controllerDir; +let appName; + +/** + * returns application name + * + * The name of the application can be different and this function finds it out. + * + * @returns {string} + */ + function getAppName() { + const parts = __dirname.replace(/\\/g, '/').split('/'); + return parts[parts.length - 2].split('.')[0]; +} + +/** + * looks for js-controller home folder + * + * @param {boolean} isInstall + * @returns {string} + */ +function getControllerDir(isInstall) { + // Find the js-controller location + const possibilities = [ + 'yunkong2.js-controller', + 'yunkong2.js-controller', + ]; + /** @type {string} */ + let controllerPath; + for (const pkg of possibilities) { + try { + const possiblePath = require.resolve(pkg); + if (fs.existsSync(possiblePath)) { + controllerPath = possiblePath; + break; + } + } catch (e) { /* not found */ } + } + if (controllerPath == null) { + if (!isInstall) { + console.log('Cannot find js-controller'); + process.exit(10); + } else { + process.exit(); + } + } + // we found the controller + return path.dirname(controllerPath); +} + +/** + * reads controller base settings + * + * @alias getConfig + * @returns {object} + */ + function getConfig() { + let configPath; + if (fs.existsSync( + configPath = path.join(controllerDir, 'conf', appName + '.json') + )) { + return JSON.parse(fs.readFileSync(configPath, 'utf8')); + } else if (fs.existsSync( + configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json') + )) { + return JSON.parse(fs.readFileSync(configPath, 'utf8')); + } else { + throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json'); + } +} +appName = getAppName(); +controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1); +const adapter = require(path.join(controllerDir, 'lib/adapter.js')); + +exports.controllerDir = controllerDir; +exports.getConfig = getConfig; +exports.Adapter = adapter; +exports.appName = appName; diff --git a/main.js b/main.js new file mode 100644 index 0000000..46ee643 --- /dev/null +++ b/main.js @@ -0,0 +1,636 @@ +/* jshint -W097 */ +/* jshint strict: false */ +/* jslint node: true */ +'use strict'; +const utils = require(__dirname + '/lib/utils'); // Get common adapter utils +const engines = require(__dirname + '/admin/engines.js'); +const Text2Speech = require(__dirname + '/lib/text2speech'); +const Speech2Device = require(__dirname + '/lib/speech2device'); +const sayitOptions = engines.sayitOptions; +const libs = {}; + +const adapter = new utils.Adapter({ + name: 'sayit', + unload: stop +}); + +process.on('SIGINT', stop); + +adapter.on('stateChange', (id, state) => { + if (state && !state.ack) { + if (id === adapter.namespace + '.tts.volume') { + if (adapter.config.type === 'system') { + speech2device.sayItSystemVolume(state.val); + } else { + options.sayLastVolume = state.val; + } + } else if (id === adapter.namespace + '.tts.text') { + if (typeof state.val !== 'string') { + if (state.val === null || state.val === undefined || state.val === '') { + adapter.log.warn('Cannot cache empty text'); + return; + } + state.val = state.val.toString(); + } + + sayIt(state.val); + } else if (id === adapter.namespace + '.tts.cachetext') { + + if (typeof state.val !== 'string') { + if (state.val === null || state.val === undefined || state.val === '') { + adapter.log.warn('Cannot cache empty text'); + return; + } + state.val = state.val.toString(); + } + + cacheIt(state.val); + } + } +}); + +adapter.on('ready', main); + +adapter.on('message', obj => { + if (obj) processMessage(obj); + processMessages(); +}); + +function processMessage(obj) { + if (obj) { + if (obj.command === 'stopInstance') { + stop(() => { + if (obj.callback) { + adapter.sendTo(obj.from, obj.command, null, obj.callback); + } + }); + } else if (obj.command === 'browseChromecast') { + try { + const mdns = require('mdns'); + + let browser = mdns.createBrowser(mdns.tcp('googlecast')); + + const result = []; + browser.on('serviceUp', service => result.push({name: service.name, ip: service.addresses[0]})); + setTimeout(() => { + browser.stop(); + browser = null; + if (obj.callback) { + adapter.sendTo(obj.from, obj.command, result, obj.callback); + } + }, 2000); + + browser.start(); + } catch (e) { + adapter.log.error(e); + if (obj.callback) adapter.sendTo(obj.from, obj.command, null, obj.callback); + } + } + } +} + +function processMessages() { + adapter.getMessage((err, obj) => { + if (obj) setTimeout(processMessages, 0); + }); +} + +function stop(callback) { + try { + if (adapter && adapter.log && adapter.log.info) { + adapter.log.info('stopping...'); + } + setTimeout(() => process.exit(), 1000); + + if (typeof callback === 'function') callback(); + } catch (e) { + process.exit(); + } +} + +const options = { + sayLastVolume: null, + webLink: '', + cacheDir: '' +}; + +let sayLastGeneratedText = ''; +let list = []; +let lastSay = null; +const text2speech = new Text2Speech(adapter, libs, options, sayIt); +const speech2device = new Speech2Device(adapter, libs, options); +const MP3FILE = __dirname + '/' + adapter.namespace + '.say.mp3'; + + +function mkpathSync(rootpath, dirpath) { + libs.fs = libs.fs || require('fs'); + // Remove filename + dirpath = dirpath.split('/'); + dirpath.pop(); + if (!dirpath.length) return; + + for (let i = 0; i < dirpath.length; i++) { + rootpath += dirpath[i] + '/'; + if (!libs.fs.existsSync(rootpath)) { + if (dirpath[i] !== '..') { + libs.fs.mkdirSync(rootpath); + } else { + throw 'Cannot create ' + rootpath + dirpath.join('/'); + } + } + } +} + +function sayFinished(error, duration) { + if (error) { + adapter.log.error(error); + } + duration = duration || 0; + if (list.length) { + adapter.log.debug('Duration "' + list[0].text + '": ' + duration); + } + setTimeout(() => { + // Remember when last text finished + lastSay = Date.now(); + if (list.length) list.shift(); + if (list.length) { + sayIt(list[0].text, list[0].language, list[0].volume, true); + } + }, duration * 1000); +} + +let cacheRunning = false; +let cacheFiles = []; + +function cacheIt(text, language) { + // process queue + if (text === true) { + if (!cacheFiles.length) { + cacheRunning = false; + return; + } + // get next queued text + const toCache = cacheFiles.shift(); + + text = toCache.text; + language = toCache.language; + } else { + // new text to cache + if (!adapter.config.cache) { + adapter.log.warn('Cache is not enabled. Unable to cache: ' + text); + return; + } + + // Extract language from "en;volume;Text to say" + if (text.indexOf(';') !== -1) { + const arr = text.split(';', 3); + // If language;text or volume;text + if (arr.length === 2) { + // If number + if (parseInt(arr[0]).toString() !== arr[0]) { + language = arr[0]; + } + text = arr[1]; + } else if (arr.length === 3) { + // If language;volume;text or volume;language;text + // If number + if (parseInt(arr[0]).toString() === arr[0]) { + language = arr[1]; + } else { + language = arr[0]; + } + text = arr[2]; + } + } + // if no text => do not process + if (!text.length) { + return; + } + + // Check: may be it is file from DB filesystem, like /vis.0/main/img/door-bell.mp3 + if (text[0] === '/') { + adapter.log.warn('mp3 file must not be cached: ' + text); + return; + } + + let isGenerate = false; + if (!language) language = adapter.config.engine; + + // find out if say.mp3 must be generated + if (!speech2device.sayItIsPlayFile(text)) isGenerate = sayitOptions[adapter.config.type].mp3Required; + + if (!isGenerate) { + if (speech2device.sayItIsPlayFile(text)) { + adapter.log.warn('mp3 file must not be cached: ' + text); + } else { + adapter.log.warn('Cache does not required for this engine: ' + adapter.config.engine); + } + return; + } + + const md5filename = libs.path.join(options.cacheDir, libs.crypto.createHash('md5').update(language + ';' + text).digest('hex') + '.mp3'); + libs.fs = libs.fs || require('fs'); + + if (libs.fs.existsSync(md5filename)) { + adapter.log.debug('Text is yet cached: ' + text); + return; + } + + if (cacheRunning) { + cacheFiles.push({text: text, language: language}); + return; + } + } + + cacheRunning = true; + + text2speech.sayItGetSpeech(text, language, false, (error, md5filename, _language, volume, seconds) => { + if (error) { + adapter.log.error('Cannot cache text: "' + error); + } else { + adapter.log.debug('Text is cached: "' + text + '" under ' + md5filename); + } + setTimeout(function () { + cacheIt(true); + }, 2000); + }); +} + +function sayIt(text, language, volume, process) { + let md5filename; + + // Extract language from "en;volume;Text to say" + if (text.indexOf(';') !== -1) { + const arr = text.split(';', 3); + // If language;text or volume;text + if (arr.length === 2) { + // If number + if (parseInt(arr[0]).toString() === arr[0].toString()) { + volume = arr[0]; + } else { + language = arr[0]; + } + text = arr[1]; + } else if (arr.length === 3) { + // If language;volume;text or volume;language;text + // If number + if (parseInt(arr[0]).toString() === arr[0].toString()) { + volume = arr[0]; + language = arr[1]; + } else { + volume = arr[1]; + language = arr[0]; + } + text = arr[2]; + } + } + + // if no text => do not process + if (!text.length) { + sayFinished(0); + return; + } + + // Check: may be it is file from DB filesystem, like /vis.0/main/img/door-bell.mp3 + if (text[0] === '/') { + let cached = false; + if (adapter.config.cache) { + md5filename = libs.path.join(options.cacheDir, libs.crypto.createHash('md5').update(text).digest('hex') + '.mp3'); + + if (libs.fs.existsSync(md5filename)) { + cached = true; + text = md5filename; + } + } + if (!cached) { + const parts = text.split('/'); + const adap = parts[0]; + parts.splice(0, 1); + const _path = parts.join('/'); + + adapter.readFile(adap, _path, (err, data) => { + if (data) { + try { + // Cache the file + if (md5filename) libs.fs.writeFileSync(md5filename, data); + libs.fs.writeFileSync(MP3FILE, data); + sayIt(MP3FILE, language, volume, process); + } catch (e) { + adapter.log.error('Cannot write file "' + MP3FILE + '": ' + e.toString()); + sayFinished(0); + } + } else { + // may be file from real FS + if (libs.fs.existsSync(text)) { + try { + data = libs.fs.readFileSync(text); + } catch (e) { + adapter.log.error('Cannot read file "' + text + '": ' + e.toString()); + sayFinished(0); + } + // Cache the file + if (md5filename) libs.fs.writeFileSync(md5filename, data); + libs.fs.writeFileSync(MP3FILE, data); + sayIt(MP3FILE, language, volume, process); + } else { + adapter.log.warn('File "' + text + '" not found'); + sayFinished(0); + } + } + }); + return; + } + } + + if (!process) { + const time = Date.now(); + + // Workaround for double text + if (list.length > 1 && (list[list.length - 1].text === text) && (time - list[list.length - 1].time < 500)) { + adapter.log.warn('Same text in less than half a second.. Strange. Ignore it.'); + return; + } + // If more time than 15 seconds + if (adapter.config.announce && !list.length && (!lastSay || (time - lastSay > adapter.config.annoTimeout * 1000))) { + // place as first the announce mp3 + list.push({text: adapter.config.announce, language: language, volume: (volume || adapter.config.volume) / 2, time: time}); + // and then text + list.push({text: text, language: language, volume: (volume || adapter.config.volume), time: time}); + text = adapter.config.announce; + volume = Math.round((volume || adapter.config.volume) / 100 * adapter.config.annoVolume); + } else { + list.push({text: text, language: language, volume: (volume || adapter.config.volume), time: time}); + if (list.length > 1) return; + } + } + + adapter.log.info('saying: ' + text); + + let isGenerate = false; + if (!language) { + language = adapter.config.engine; + } + if (!volume && adapter.config.volume) volume = adapter.config.volume; + + // find out if say.mp3 must be generated + if (!speech2device.sayItIsPlayFile(text)) { + isGenerate = sayitOptions[adapter.config.type].mp3Required; + } + + const speechFunction = speech2device.getFunction(adapter.config.type); + + // If text first must be generated + if (isGenerate && sayLastGeneratedText !== '[' + language + ']' + text) { + sayLastGeneratedText = '[' + language + ']' + text; + text2speech.sayItGetSpeech(text, language, volume, (error, text, language, volume, duration) => { + speechFunction(error, text, language, volume, duration, sayFinished); + }); + } else { + if (speech2device.sayItIsPlayFile(text)) { + text2speech.getLength(text, (error, duration) => { + speechFunction(error, text, language, volume, duration, sayFinished); + }); + } else { + if (!isGenerate) { + speechFunction(null, text, language, volume, 0, sayFinished); + } else if (adapter.config.cache) { + md5filename = libs.path.join(options.cacheDir, libs.crypto.createHash('md5').update(language + ';' + text).digest('hex') + '.mp3'); + if (libs.fs.existsSync(md5filename)) { + text2speech.getLength(md5filename, (error, duration) => { + speechFunction(error, md5filename, language, volume, duration, sayFinished); + }); + } else { + sayLastGeneratedText = '[' + language + ']' + text; + text2speech.sayItGetSpeech(text, language, volume, (error, text, language, volume, duration) => { + speechFunction(error, text, language, volume, duration, sayFinished); + }); + } + } else { + text2speech.getLength(MP3FILE, (error, duration) => { + speechFunction(error, text, language, volume, duration, sayFinished); + }); + } + } + } +} + +function uploadFile(file, callback) { + try { + const stat = libs.fs.statSync(libs.path.join(__dirname + '/mp3/', file)); + if (!stat.isFile()) { + // ignore not a file + if (callback) callback(); + return; + } + } catch (e) { + // ignore not a file + if (callback) callback(); + return; + } + + + adapter.readFile(adapter.namespace, 'tts.userfiles/' + file, (err, data) => { + if (err || !data) { + try { + adapter.writeFile(adapter.namespace, 'tts.userfiles/' + file, libs.fs.readFileSync(libs.path.join(__dirname + '/mp3/', file)), () => { + if (callback) callback(); + }); + } catch (e) { + adapter.log.error('Cannot read file "' + __dirname + '/mp3/' + file + '": ' + e.toString()); + if (callback) callback(); + } + } else { + if (callback) callback(); + } + }); +} + +function _uploadFiles(files, callback) { + if (!files || !files.length) { + adapter.log.info('All files uploaded'); + if (callback) callback(); + return; + } + + uploadFile(files.pop(), () => setTimeout(_uploadFiles, 0, files, callback)); +} +function uploadFiles(callback) { + if (libs.fs.existsSync(__dirname + '/mp3')) { + adapter.log.info('Upload announce mp3 files'); + _uploadFiles(libs.fs.readdirSync(__dirname + '/mp3'), callback); + } else if (callback) { + callback(); + } +} + +function start() { + if (adapter.config.announce) { + adapter.config.annoDuration = parseInt(adapter.config.annoDuration) || 0; + adapter.config.annoTimeout = parseInt(adapter.config.annoTimeout) || 15; + adapter.config.annoVolume = parseInt(adapter.config.annoVolume) || 70; // percent from actual volume + + if (!libs.fs.existsSync(libs.path.join(__dirname, adapter.config.announce))) { + adapter.readFile(adapter.namespace, 'tts.userfiles/' + adapter.config.announce, (err, data) => { + if (data) { + try { + libs.fs.writeFileSync(libs.path.join(__dirname, adapter.config.announce), data); + adapter.config.announce = libs.path.join(__dirname, adapter.config.announce); + } catch (e) { + adapter.log.error('Cannot write file: ' + e.toString()); + adapter.config.announce = ''; + } + } + }); + } else { + adapter.config.announce = __dirname + '/' + adapter.config.announce; + } + } + + // If cache enabled + if (adapter.config.cache) { + if (adapter.config.cacheDir && (adapter.config.cacheDir[0] === '/' || adapter.config.cacheDir[0] === '\\')) { + adapter.config.cacheDir = adapter.config.cacheDir.substring(1); + } + options.cacheDir = libs.path.join(__dirname, adapter.config.cacheDir); + if (options.cacheDir) { + options.cacheDir = options.cacheDir.replace(/\\/g, '/'); + if (options.cacheDir[options.cacheDir.length - 1] === '/') { + options.cacheDir = options.cacheDir.substring(0, options.cacheDir.length - 1); + } + } else { + options.cacheDir = ''; + } + + const parts = options.cacheDir.split('/'); + let i = 0; + while (i < parts.length) { + if (parts[i] === '..') { + parts.splice(i - 1, 2); + i--; + } else { + i++; + } + } + options.cacheDir = parts.join('/'); + // Create cache dir if does not exist + if (!libs.fs.existsSync(options.cacheDir)) { + try { + mkpathSync(__dirname + '/', adapter.config.cacheDir); + } catch (e) { + adapter.log.error('Cannot create "' + options.cacheDir + '": ' + e.message); + } + } else { + let engine = ''; + // Read the old engine + if (libs.fs.existsSync(libs.path.join(options.cacheDir, 'engine.txt'))) { + try { + engine = libs.fs.readFileSync(libs.path.join(options.cacheDir, 'engine.txt')).toString(); + } catch (e) { + adapter.log.error('Cannot read file "' + libs.path.join(options.cacheDir, 'engine.txt') + ': ' + e.toString()); + } + } + // If engine changed + if (engine !== adapter.config.engine) { + // Delete all files in this directory + const files = libs.fs.readdirSync(options.cacheDir); + for (let f = 0; f < files.length; f++) { + if (files[f] === 'engine.txt') continue; + if (libs.fs.existsSync(libs.path.join(options.cacheDir, files[f])) && libs.fs.lstatSync(libs.path.join(options.cacheDir, files[f])).isDirectory()) { + libs.fs.unlinkSync(libs.path.join(options.cacheDir, files[f])); + } + } + try { + libs.fs.writeFileSync(libs.path.join(options.cacheDir, 'engine.txt'), adapter.config.engine); + } catch (e) { + adapter.log.error('Cannot write file "' + libs.path.join(options.cacheDir, 'engine.txt') + ': ' + e.toString()); + } + } + } + } + + // Load libs + for (let j = 0; j < sayitOptions[adapter.config.type].libs.length; j++) { + libs[sayitOptions[adapter.config.type].libs[j]] = require(sayitOptions[adapter.config.type].libs[j]); + } + + adapter.getState('tts.text', (err, state) => { + if (err || !state) { + adapter.setState('tts.text', '', true); + } + }); + + adapter.getState('tts.volume', (err, state) => { + if (err || !state) { + adapter.setState('tts.volume', 70, true); + if (adapter.config.type !== 'system') options.sayLastVolume = 70; + } else { + if (adapter.config.type !== 'system') options.sayLastVolume = state.val; + } + }); + + adapter.getState('tts.playing', (err, state) => { + if (err || !state) { + adapter.setState('tts.playing', false, true); + } + }); + + if (adapter.config.type === 'system') { + // Read volume + adapter.getState('tts.volume', (err, state) => { + if (!err && state) { + speech2device.sayItSystemVolume(state.val); + } else { + speech2device.sayItSystemVolume(70); + } + }); + } + + // calculate weblink for devices that require it + if ((adapter.config.type === 'sonos') || + (adapter.config.type === 'chromecast') || + (adapter.config.type === 'mpd') || + (adapter.config.type === 'googleHome')) { + + adapter.getForeignObject('system.adapter.' + adapter.config.web, (err, obj) => { + if (!err && obj && obj.native) { + options.webLink = 'http'; + if (obj.native.auth) { + adapter.log.error('Cannot use server "' + adapter.config.web + '" with authentication for sonos/chromecast. Select other or create another one.'); + } else { + if (obj.native.secure) options.webLink += 's'; + options.webLink += '://'; + if (obj.native.bind === 'localhost' || obj.native.bind === '127.0.0.1') { + adapter.log.error('Selected web server "' + adapter.config.web + '" is only on local device available. Select other or create another one.'); + } else { + if (obj.native.bind === '0.0.0.0') { + options.webLink += adapter.config.webServer; + } else { + options.webLink += obj.native.bind; + } + } + + options.webLink += ':' + obj.native.port; + } + } else { + adapter.log.error('Cannot read information about "' + adapter.config.web + '". No web server is active'); + } + }); + } + + adapter.subscribeStates('*'); +} + +function main() { + libs.fs = require('fs'); + libs.path = require('path'); + + if ((process.argv && process.argv.indexOf('--install') !== -1) || + ((!process.argv || process.argv.indexOf('--force') === -1) && (!adapter.common || !adapter.common.enabled))) { + adapter.log.info('Install process. Upload files and stop.'); + // Check if files exists in datastorage + uploadFiles(() => adapter.stop ? adapter.stop() : process.exit()); + } else { + // Check if files exists in datastorage + uploadFiles(start); + } +} diff --git a/mp3/gong.mp3 b/mp3/gong.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ff9ebbd10a1f02c011ed2fb2fa6f4ed196957aa7 GIT binary patch literal 42536 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 %B%a+jC1IaQ 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 literal 0 HcmV?d00001 diff --git a/nircmd/NirCmd - Windows command line tool- ReadMe.htm b/nircmd/NirCmd - Windows command line tool- ReadMe.htm new file mode 100644 index 0000000..ecb4907 --- /dev/null +++ b/nircmd/NirCmd - Windows command line tool- ReadMe.htm @@ -0,0 +1,998 @@ + + + +NirCmd - Windows command line tool + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
  + + + + + +
NirCmd v2.75 +
+Copyright (c) 2003 - 2013 Nir Sofer +
+ +

+

See Also

+
    +
  • NK2Edit - Edit, merge and fix the AutoComplete files (.NK2) of Microsoft Outlook. +
+ + +

+

Description

+NirCmd is a small command-line utility that allows you to do some useful tasks without +displaying any user interface. By running NirCmd with simple command-line option, you can +write and delete values and keys in the Registry, write values into INI file, dial to your +internet account or connect to a VPN network, restart windows or shut down the computer, +create shortcut to a file, change the created/modified date of a file, change your display +settings, turn off your monitor, open the door of your CD-ROM drive, and more... + + +

+

Examples of what you can do with NirCmd

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Open the door of J: CD-ROM drive + + +nircmd.exe cdrom open j: + + +
Close the door of Y: CD-ROM drive + +nircmd.exe cdrom close y: + +
Speaks the text currently in the clipboard (For Windows XP/Vista/7/8). + +speak text ~$clipboard$ + +
Speaks the text stored inside speak.txt into speak.wav filename (For Windows XP/Vista/7/8). + +speak file "c:\temp\speak.txt" 0 100 "c:\temp\speak.wav" 48kHz16BitStereo + + + +
Increase the system volume by 2000 units (out of 65535) + +nircmd.exe changesysvolume 2000 + +
Decrease the system volume by 5000 units (out of 65535) + +nircmd.exe changesysvolume -5000 + + +
Set the volume to the highest value + +nircmd.exe setsysvolume 65535 + +
Mute the system volume + +nircmd.exe mutesysvolume 1 + +
Unmute the system volume + +nircmd.exe mutesysvolume 0 + +
Switch the system volume between the mute and normal state. + +nircmd.exe mutesysvolume 2 + +
Create a shortcut on your desktop that switch the system volume between the mute and normal state. + +nircmd.exe cmdshortcut "~$folder.desktop$" "Switch Volume" mutesysvolume 2 + +
Turn off the monitor + +nircmd.exe monitor off + +
Start the default screen saver + +nircmd.exe screensaver + +
Put your computer in 'standby' mode + +nircmd.exe standby + +
+log off the current user + +nircmd.exe exitwin logoff + +
+Ask if you want to reboot, and if you answer 'Yes', reboot the computer. + +nircmd.exe qboxcom "Do you want to reboot ?" "question" exitwin reboot + +
+Turn off your computer + +nircmd.exe exitwin poweroff + + +
+Turn off all computers specified in computers.txt ! + +multiremote copy "c:\temp\computers.txt" exitwin poweroff force + +
Dial to "My Internet" connection + +nircmd.exe rasdial "My Internet" + +
Disconnect the "My Internet" connection + +nircmd.exe rashangup "My Internet" + +
Make your Internet Explorer windows 75% transparent ! (192 / 256) + +nircmd.exe win trans ititle "internet explorer" 192 + +
Minimize all your Internet Explorer windows + +nircmd.exe win min class "IEFrame" + +
Close all your Internet Explorer windows + +nircmd.exe win close class "IEFrame" + +
Close all your Explorer windows (My Computer, folders, and so on) + +nircmd.exe win close class "CabinetWClass" + +
Hide all your Internet Explorer windows + +nircmd.exe win hide class "IEFrame" + +
Show all your Internet Explorer windows (after you made them hidden with previous example) + +nircmd.exe win show class "IEFrame" + +
Center all top-level windows + +nircmd.exe win center alltop + +
Remove the title bar of My Computer window. + +nircmd.exe win -style title "my computer" 0x00C00000 + +
Return the title bar of My Computer window that we removed in the previous example. + +nircmd.exe win +style title "my computer" 0x00C00000 + + +
Set the My Computer window to right-to-left order (For hebrew and arabic languages) + +nircmd win +exstyle title "my computer" 0x00400000 + + +
Set all child windows of My Computer window to right-to-left order (For hebrew and arabic languages) + +nircmd win child title "my computer" +exstyle all 0x00400000 + +
Create a shortcut on your desktop that closes all your Internet Explorer windows + +nircmd.exe cmdshortcut " "~$folder.desktop$ "Close All IE" win close class "IEFrame" + +
Create a shortcut on your desktop that hides all your Internet Explorer windows + +nircmd.exe cmdshortcut " "~$folder.desktop$ "Hide All IE" win hide class "IEFrame" + +
Create a shortcut on your desktop that shows back all your Internet Explorer windows + +nircmd.exe cmdshortcut " "~$folder.desktop$ "Show All IE" win show class "IEFrame" + +
Set the Windows Calculator as top-most window (above all other windows) + +nircmd.exe win settopmost title "Calculator" 1 + +
Set the Windows Calculator back to regular window (non top-most window) + +nircmd.exe win settopmost title "Calculator" 0 + +
Create a shortcut to Windows calculator under Start Menu->Programs->Calculators + +nircmd.exe shortcut "f:\winnt\system32\calc.exe" "~$folder.programs$\Calculators" "Windows Calculator" + +
Hide the desktop window + +nircmd.exe win hide class progman + +
Show the desktop window (After hiding it in previous example) + +nircmd.exe win show class progman + +
Hide the start button on the system tray + +nircmd.exe win child class "Shell_TrayWnd" hide class "button" + + +
Show the start button on the system tray + +nircmd.exe win child class "Shell_TrayWnd" show class "button" + + +
Hide the clock on the system tray + +nircmd.exe win child class "Shell_TrayWnd" hide class "TrayClockWClass" + +
Show the clock on the system tray + +nircmd.exe win child class "Shell_TrayWnd" show class "TrayClockWClass" + +
Kill (terminate) all instance of Internet Explorer processes + +nircmd.exe killprocess iexplore.exe + + +
Create a shortcut on your desktop that opens the door of K: CDROM drive when you run it. + +nircmd.exe cmdshortcut "~$folder.desktop$" "Open CDROM" cdrom open k: + +
Create a shortcut to NirSoft Web site on your desktop + +nircmd.exe urlshortcut "http://www.nirsoft.net" "~$folder.desktop$" "NirSoft" + +
Add NirSoft Web site to your Favorities under Links folder. + +nircmd.exe urlshortcut "http://www.nirsoft.net" "~$folder.favorites$\Links" "NirSoft" + +
Create a shortcut to NirSoft Web site on the desktop of all computers listed in computers.txt + +nircmd.exe multiremote copy "c:\temp\computers.txt" urlshortcut "http://www.nirsoft.net" "~$folder.common_desktop$" "NirSoft" + +
Set the display mode to 800x600x24bit colors + +nircmd.exe setdisplay 800 600 24 + +
Create a shortcut on the desktop that set the display mode to 800x600x24bit colors + +nircmd.exe cmdshortcut "~$folder.desktop$" "800x600x24" setdisplay 800 600 24 + +
Copy all shortcuts on your desktop to another folder (f:\temp\desktop). + +nircmd.exe execmd copy "~$folder.desktop$\*.lnk" f:\temp\desktop + +
Restart your Apache server (under Windows NT/2000/XP/2003) + +nircmd.exe service restart apache + +
Create a shortcut on your desktop that restarts the Apache server + +nircmd.exe cmdshortcut "~$folder.desktop$" "Restart Apache" service restart apache + +
Restart your IIS + +nircmd.exe service restart w3svc + +
Restart MySql + +nircmd.exe service restart MySql + +
Open the desired Registry key/value in RegEdit + +nircmd.exe regedit "HKLM\Software\Microsoft\Windows\CurrentVersion" "CommonFilesDir" + + +
Open the Registry key that you copied to the clipboard in RegEdit. + +nircmd regedit "~$clipboard$" + +
Disable the screen saver + +nircmd.exe regsetval sz "HKCU\control panel\desktop" "ScreenSaveActive" 0 + + +
Enable the screen saver + +nircmd.exe regsetval sz "HKCU\control panel\desktop" "ScreenSaveActive" 1 + + +
Change the date/time of the specified filename (creation time and modified time) + +nircmd.exe setfiletime "c:\temp\myfile.txt" "24-06-2003 17:57:11" "22-11-2005 10:21:56" + + +
Copy your desktop folder path to the clipboard + +nircmd.exe clipboard set ~$folder.desktop$ + + +
Copy your start menu folder path to the clipboard + +nircmd.exe clipboard set ~$folder.start_menu$ + +
Copy the content of info1.txt (simple text file) to the clipboard + +nircmd.exe clipboard readfile "c:\My Files\info1.txt" + +
Add the text content of clipboard to info1.txt + +nircmd.exe clipboard addfile "c:\My Files\info1.txt" + +
Clear the clipboard + +nircmd.exe clipboard clear + +
Create all folders specified in "c:\temp\folders.txt". The folder path names are +separated by CRLF characters. + +nircmd.exe paramsfile "c:\temp\folders.txt" "" "" execmd md ~$fparam.1$ + + +
Install the specified .NET assembly in the global assembly cache (like gacutil) + +nircmd.exe gac install "C:\temp\MyAssembly\bin\MyAssembly.dll" + + +
Empty the recycle bin in all drives. + +nircmd.exe emptybin + + + +
Answer 'Yes' to a standard Windows message-box. + +nircmd.exe dlg "" "" click yes + +
Wait 2 seconds, and then save the current screen to shot.png + +nircmd.exe cmdwait 2000 savescreenshot "f:\temp\shot.png" + +
Save 10 screenshots in a loop, and wait 60 seconds between the screenshot save calls. +The filenames of the screenshot will contain the time and date of the saved screenshot. + + +nircmd.exe loop 10 60000 savescreenshot c:\temp\scr~$currdate.MM_dd_yyyy$-~$currtime.HH_mm_ss$.png + +
+Wait until Firefox is closed, and then say "Firefox was closed" + +nircmd.exe waitprocess firefox.exe speak text "Firefox was closed" + + +
+Run RegEdit with SYSTEM user (On Windows 7/8/Vista/2008), which allows you to view all hidden system Registry keys + +nircmd.exe elevatecmd runassystem c:\windows\regedit.exe + + +
+ +

+

System Requirements

+This utility can work in all versions of Windows operating system: Windows 9x/ME, +Windows NT, Windows 2000, Windows XP, Windows Server 2003, Windows Vista, Windows Server 2008, Windows 7, and Windows 8. + + +

Versions History

+ + +
Date Version Description + +
10/08/20132.75 +
    +
  • Added new commands: moverecyclebin, suspendprocess , resumeprocess. +
  • Added etitle option to the find parameter of win command. +
  • 'clipboard readfile' command now reads Unicode files (UTF-8 and UTF-16). +
+ +
29/10/20122.71 +
    +
  • Added runassystem command, which allows you to run a process with SYSTEM user (Works on Windows 7/8/Vista/2008) +
+ +
12/09/20122.70 +
    +
  • Added new option to speak command: You can now save the entire speach into a .wav file that can be played with any +media player (Instead of playing it directly to your sound card). +
  • Added restartexplorer command, which simply restarts Windows Explorer gracefully. +
  • Added screensavertimeout command, which allows you to set the screensaver timeout, in seconds. +
  • Added togglemax, togglemin, togglehide, toggledisable actions to win command. +
  • Added SystemSounds support for setappvolume, changeappvolume, and muteappvolume commands. +
  • Fixed issue on Windows 7: 'win close alltopnodesktop' command caused a shutdown dialog-box to appear. +
  • Fixed the setprimarydisplay to work properly. +
+ + + +
28/06/20112.65 +
    +
  • Added setdefaultsounddevice command (for Windows 7/Vista/2008 only), which +allows you to set the default sound device. +
  • Added setsubunitvolumedb command (for Windows 7/Vista/2008 only), which +allows you to set the volume of sound device subunits (Line In, Microphone, and so on) +
  • Added mutesubunitvolume command (for Windows 7/Vista/2008 only), which +allows you to mute/unmute the volume of sound device subunits (Line In, Microphone, and so on) +
+ +
21/06/20112.60 +
    +
  • Added mediaplay command for playing audio file (.mp3, .wav, and so on) for the +specified number of milliseconds. +
  • Added changebrightness and setbrightness commands for setting the +screen brightness on laptops and netbook computers. +
+ +
08/06/20112.55 +
    +
  • On savescreenshot, savescreenshotfull, and savescreenshotwin commands, +you can now specify *clipboard* as a filename, in order to save the screenshow into the clipboard +
  • On savescreenshot command, you can now specify a rectangle to capture as x, y, width, and height instead of +capturing the entire screen. +
  • Fixed savescreenshotfull command to work properly on multiple screen system with negative coordinates. + +
+ +
27/03/20112.52 +
    +
  • Added 'runas' command, which allows you to run a new process with the specified user name/password. +
  • Added 'Flash' action to the win command, with allows you to flash the specified window. +
+ +
13/03/20112.51 +
    +
  • Added 'focused' option for setappvolume, changeappvolume, and muteappvolume commands, which allow you to easily +change the volume of the application that is currently in focus, on Windows 7/Vista/2008. +
  • Added 'default_record' option for +setsysvolume, changesysvolume, setsysvolume2, changesysvolume2, and mutesysvolume commands, which allows you set easily +change the volume of default recording device, on Windows 7/Vista/2008. +
+ + +
24/02/20112.50 +
    +
  • Added 3 new sound volume commands to change or mute application volume on Windows 7/Vista/2008: +setappvolume, changeappvolume, muteappvolume +
  • Added new information command that shows you a message box with all sound devices on your Windows 7/Vista/2008 system: +showsounddevices. +
+ + +
22/02/20112.48 +
    +
  • Fixed the problems with the timeout of the tray balloon in trayballoon command. + +
+ +
20/01/20112.47 +
    +
  • Fixed bug: The sendkeypress command didn't work properly with Fxx keys (F1, F2, and so on). +
+ + +
28/11/20102.46 +
    +
  • Added option to locate window by process in win command. +
  • Added max and min options (Minimized and maximized windows) into exec and exec2 commands. +
  • Added x64 builds of nircmd.exe and nircmdc.exe +
+ + +
18/10/20102.45 +
    +
  • Added trayballoon command - displays a tray balloon with your desired text and icon. +
  • Added sendkeypress command - Alternative to the sendkey command. Allows you to send key press +combination in much easier way, for example: sendkeypress ctrl+shift+esc +
  • Added clonefiletime command to clone the date/time of existing file into one or more files. +
  • Added setconsolemode command to set the console display mode - window or full screen. +
  • Added setconsolecolor command to set the foreground and background colors of the console text. +
  • Added consolewrite command to write text into the console window. +
  • Added debugwrite for writing text into the debug output. +
  • Added ~$folder.mydocuments$ variable. +
+
21/02/20102.41 +
    +
  • Fixed a bug in elevate command: when running a program without parameters, NirCmd sent the elevate command string as a parameter of the program. +
+ +
16/02/20102.40 +
    +
  • Added elevate and elevatecmd commands to run and execute commands with administrator rights under Windows 7/Vista/2008. +
  • Added monitor parameter to setdisplay command, for using it with multiple monitors. +
  • Added setprimarydisplay command. +
  • Added folder.programfiles and folder.common_programfiles variables. +
  • Added async_off, async_on, async_low for monitor command. You can try them if NirCmd.exe remains in memory +when you use monitor on/off command. +
+ + +
26/05/20092.37 +
    +
  • Improved the help .chm file - Now it contains a link to every command. +
  • Added help command - Automatically opens the reference of the desired command in http://nircmd.nirsoft.net. +For example, if you type 'nircmd.exe help speak', the reference page of speak command - http://nircmd.nirsoft.net/speak.html will be opened in your default browser. +
+ +
19/05/20092.36 +
    +
  • Fixed bug in script: Sometimes the last line in the script was not executed properly. +
+ +
20/04/20092.35 +
    +
  • Added loop command - execute a command multiple times. +
  • Added currtime and currdate variables. +
  • waitprocess - Added option to execute a NirCmd command after the process was closed. +
  • Added rem (or ;) command, just for adding remarks in a script. +
+ + +
01/04/20092.30 +
    +
  • Added speak command - Speaks the contents of the text or file that you specify, by using the Speech library (SAPI) that comes with Windows XP and Windows Vista. +
  • Added savescreenshotfull command. +
+ +
02/02/20092.25 +
    +
  • New actions for clipboard command - loadclp and saveclp (load/save in Windows .clp format) +
  • New command: setprocessaffinity +
+ +
01/01/20092.20 +
    +
  • New commands: waitprocess, setprocesspriority, qboxtop, qboxcomtop. +
  • New action in clipboard command: copyimage (Copy image file to the clipboard) +
+ +
25/06/20082.15 +
    +
  • New commands: setcursorwin, savescreenshotwin, filldelete +
+ +
12/04/20082.10 +
    +
  • New commands: shellcopy, savescreenshot +
+ +
09/02/20082.05 +
    +
  • New commands: shellrefresh, convertimage, convertimages +
  • New option in clipboard command - saveimage +
+ + +
16/06/20072.00 +
    +
  • All sound volume commands and monitor command now works under Vista. +
  • New commands: dlg, dlgany, sendkey, sendmouse, returnval +
  • New actions in win command - dlgclick, dlgsettext, dlgsetfocus +
+ + +
24/07/20061.85 +
    +
  • New command: cmdshortcutkey +
+ + +
15/07/20061.84 +
    +
  • Starting from this version, if you don't specify a sound device index in setsysvolume, changesysvolume, setsysvolume2, changesysvolume2 and mutesysvolume commands, +the default sound device in control panel ("preferred device") is used. +
  • New find options in win command: active, foreground, desktop +
+ +
20/05/20061.83 +
    +
  • Fixed bug with parameter variables: parameter dialog-box appear more than once. +
  • New find option in Win command: alltopnodesktop +
+ +
18/02/20061.82 +
    +
  • New win actions: postmsg and sendmsg. +
  • New command: closeprocess +
  • NirCmd now returns a non-zero value on error. +
+ +
19/11/20051.81 +
    +
  • New command: emptybin - Empty the Recycle Bin. +
  • New optional parameter in setdisplay command: refresh rate. +
  • rashangup command without parameter - disconnect all active connections. +
+ +
24/09/20051.80 +
    +
  • New commands: exec2, cmdwait. +
  • New options in clipboard command. +
  • Volume commands (setsysvolume, changesysvolume, setsysvolume2, changesysvolume2, mutesysvolume) now allows you to change the volume of non-default sound card. +
+ +
07/07/20051.70 +
    +
  • New win actions: activate, hideshow, +style, -style, +exstyle, -exstyle +
  • New commands: movecursor, setcursor. +
  • New command: setfilefoldertime - allows you to change the date/time of folders, under Windows 2000/XP only. +
+ +
10/06/20051.62 +
    +
  • New actions in win command: focus, enable, disable +
+ +
26/05/20051.61 +
    +
  • new command: sysrefresh - make a general system refresh after modifying your system settings in the Registry. +
+ + +
07/05/20051.60 +
    +
  • New paramsfile command: Allows you to execute a command multiple times by loading the parameters from a text file. +
  • New actions in win command: move, center, settext. +
  • New 'child' action in win command: Allows you to move/hide child windows (For example: the start button in the system tray) +
  • New clipboard command: Allows you to put a string in the clipboard, and clear the clipboard. +
  • New gac command: Allows you to install assemblies in Global Assembly Cache. + +
+ +
28/12/20041.56 +
    +
  • New variable: ~$clipboard$ - paste the text that you copied to the clipboard into the command-line. +
  • Parameter variables: ~$param.ParamName$ - Display an input window for typing the text that you want insert into the command-line. +
+ +
07/12/20041.55 +Added RegEdit command. + +
18/11/20041.54 +
    +
  • On reg commands (regsetval, regdelval, regdelkey), you can now also use the following +abbreviated key name: HKCU for HKEY_CURRENT_USER, HKLM for HKEY_LOCAL_MACHINE, HKCR for HKEY_CLASSES_ROOT, HKU for HKEY_USERS, +HKCC for HKEY_CURRENT_CONFIG. +
  • redraw action for win command. +
+ +
03/11/20041.53 +Fixed 'monitor off' problem in XP SP2. + +
09/10/20041.52 +rasapi32.dll is now loaded only when using the dial commands (rasdial, rasdialdlg, rashangup). +In previous versions, NirCmd was statically linked to rasapi32.dll, and that caused problems in old NT systems. + +
29/09/20041.51 +Variable names are now enclosed with '$' char instead of '%' char. +
+I made this change because the '%' char causes problems when running NirCmd from cmd/bat file. +using the '%' char for variable names is still supported for backward compatibility. + + + +
22/09/20041.50 +
    +
  • New commands: cmdshortcut, regsvr, mutesysvolume, changesysvolume, changesysvolume2, setsysvolume2. +
  • New actions in win command: trans, setsize, settopmost, ititle. +
  • New variables: folder.nircmd, nir.exefile +
  • nircmdc.exe - console version of NirCmd. +
  • Changed name - from NirComLime to NirCmd. +
+ +
05/05/20041.40 +
    +
  • New commands: wait, setvolume, setsysvolume, execmd, exec. +
  • New commands for executing NirCmd on remote machines: remote and multiremote. +
  • Folder and system variables. +
  • New option in setfiletime command - set the file time to the current time (now parameter) +
  • New parameter in shortcut command - hot key. + +
+ +
01/03/20041.30 +
    +
  • New commands: qboxcom, beep, stdbeep, cdrom, urlshortcut, monitor, screensaver, standby, and hibernate. +
  • Fixed the bug with ~x string sequences. +
+ +
08/01/20041.20 +
    +
  • New commands: setdialuplogon, script +
  • Special string sequences (~q, ~t, ~n and so on) to allow you to embed quotes and new line characters into the parameters of any command. +
+
19/11/20031.11 +New options in shortcut command + +
10/10/20031.10 +New commands: killprocess, service, memdump, win, lockws. +
09/09/20031.00 +First release. +
+ +

+

License

+This utility is released as freeware. +You are allowed to freely distribute this utility via floppy disk, CD-ROM, +Internet, or in any other way, as long as you don't charge anything for this. +If you distribute this utility, you must include all files in +the distribution package, without any modification ! + + + + +

+ +
+View NirCmd Full Help File + +
+Download NirCmd + + +
+Download NirCmd 64-bit + +
+ +

+ + +

+ + + +
+  +
+

+ + + +
+  +
+

+ + + +

+ + + +
+ + + +
+ +
+ +

+ + + + +
+  + +
+

+ + + + + + + + + + +

+ + + + + + + +

  
+ +

+

+ + \ No newline at end of file diff --git a/nircmd/NirCmd.chm b/nircmd/NirCmd.chm new file mode 100644 index 0000000000000000000000000000000000000000..546832a25dfa648670b29a782acf94ee9aad9cdd GIT binary patch literal 45977 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 literal 0 HcmV?d00001 diff --git a/nircmd/license.txt b/nircmd/license.txt new file mode 100644 index 0000000..05f3914 --- /dev/null +++ b/nircmd/license.txt @@ -0,0 +1,6 @@ +NirCmd v2.75 +Copyright (c) 2003 - 2013 Nir Sofer + +License + +This utility is released as freeware. You are allowed to freely distribute this utility via floppy disk, CD-ROM, Internet, or in any other way, as long as you don't charge anything for this. If you distribute this utility, you must include all files in the distribution package, without any modification ! \ No newline at end of file diff --git a/nircmd/nircmd.exe b/nircmd/nircmd.exe new file mode 100644 index 0000000000000000000000000000000000000000..54f843685c2bf8af105dec83e6ea91aef9daa702 GIT binary patch literal 44032 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ⅆ$i*JlsxEp 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 QVnSPLp!WZk|0x;xKS%v+XaE2J literal 0 HcmV?d00001 diff --git a/package.json b/package.json new file mode 100644 index 0000000..87dc5f9 --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "yunkong2.sayit", + "version": "1.7.1", + "description": "Text to speech interface for yunkong2.", + "author": { + "name": "bluefox", + "email": "dogafox@gmail.com" + }, + "homepage": "https://git.spacen.net/yunkong2/yunkong2.sayit", + "keywords": [ + "yunkong2", + "text to speech", + "TTS", + "Voice synthesis" + ], + "repository": { + "type": "git", + "url": "https://git.spacen.net/yunkong2/yunkong2.sayit" + }, + "optionalDependencies": { + "castv2-client": "^1.2.0", + "mdns": "^2.4.0" + }, + "dependencies": { + "jsftp": "^2.1.3", + "aws-sdk": "~2.253.1" + }, + "devDependencies": { + "gulp": "^3.9.1", + "mocha": "^4.1.0", + "chai": "^4.1.2" + }, + "bugs": { + "url": "https://git.spacen.net/yunkong2/yunkong2.sayit/issues" + }, + "main": "main.js", + "scripts": { + "test": "node node_modules/mocha/bin/mocha --exit", + "install": "node main.js --install" + }, + "license": "MIT", + "readmeFilename": "README.md" +} diff --git a/say/SayStatic.exe b/say/SayStatic.exe new file mode 100644 index 0000000000000000000000000000000000000000..bb62fe69495c4133af91f612172234c51ff35155 GIT binary patch literal 72704 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 " + targetFile + " (error ignored)"); + } +} + +function copyFolderRecursiveSync(source, target, ignore) { + var files = []; + + var base = path.basename(source); + if (base === adapterName) { + base = pkg.name; + } + //check if folder needs to be created or integrated + var targetFolder = path.join(target, base); + if (!fs.existsSync(targetFolder)) { + fs.mkdirSync(targetFolder); + } + + //copy + if (fs.lstatSync(source).isDirectory()) { + files = fs.readdirSync(source); + files.forEach(function (file) { + if (ignore && ignore.indexOf(file) !== -1) { + return; + } + + var curSource = path.join(source, file); + var curTarget = path.join(targetFolder, file); + if (fs.lstatSync(curSource).isDirectory()) { + // ignore grunt files + if (file.indexOf('grunt') !== -1) return; + if (file === 'chai') return; + if (file === 'mocha') return; + copyFolderRecursiveSync(curSource, targetFolder, ignore); + } else { + copyFileSync(curSource, curTarget); + } + }); + } +} + +if (!fs.existsSync(rootDir + 'tmp')) { + fs.mkdirSync(rootDir + 'tmp'); +} + +function storeOriginalFiles() { + console.log('Store original files...'); + var dataDir = rootDir + 'tmp/' + appName + '-data/'; + + var f = fs.readFileSync(dataDir + 'objects.json'); + var objects = JSON.parse(f.toString()); + if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) { + objects['system.adapter.admin.0'].common.enabled = false; + } + if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) { + objects['system.adapter.admin.1'].common.enabled = false; + } + + fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects)); + try { + f = fs.readFileSync(dataDir + 'states.json'); + fs.writeFileSync(dataDir + 'states.json.original', f); + } + catch (err) { + console.log('no states.json found - ignore'); + } +} + +function restoreOriginalFiles() { + console.log('restoreOriginalFiles...'); + var dataDir = rootDir + 'tmp/' + appName + '-data/'; + + var f = fs.readFileSync(dataDir + 'objects.json.original'); + fs.writeFileSync(dataDir + 'objects.json', f); + try { + f = fs.readFileSync(dataDir + 'states.json.original'); + fs.writeFileSync(dataDir + 'states.json', f); + } + catch (err) { + console.log('no states.json.original found - ignore'); + } + +} + +function checkIsAdapterInstalled(cb, counter, customName) { + customName = customName || pkg.name.split('.').pop(); + counter = counter || 0; + var dataDir = rootDir + 'tmp/' + appName + '-data/'; + console.log('checkIsAdapterInstalled...'); + + try { + var f = fs.readFileSync(dataDir + 'objects.json'); + var objects = JSON.parse(f.toString()); + if (objects['system.adapter.' + customName + '.0']) { + console.log('checkIsAdapterInstalled: ready!'); + setTimeout(function () { + if (cb) cb(); + }, 100); + return; + } else { + console.warn('checkIsAdapterInstalled: still not ready'); + } + } catch (err) { + + } + + if (counter > 20) { + console.error('checkIsAdapterInstalled: Cannot install!'); + if (cb) cb('Cannot install'); + } else { + console.log('checkIsAdapterInstalled: wait...'); + setTimeout(function() { + checkIsAdapterInstalled(cb, counter + 1); + }, 1000); + } +} + +function checkIsControllerInstalled(cb, counter) { + counter = counter || 0; + var dataDir = rootDir + 'tmp/' + appName + '-data/'; + + console.log('checkIsControllerInstalled...'); + try { + var f = fs.readFileSync(dataDir + 'objects.json'); + var objects = JSON.parse(f.toString()); + if (objects['system.adapter.admin.0']) { + console.log('checkIsControllerInstalled: installed!'); + setTimeout(function () { + if (cb) cb(); + }, 100); + return; + } + } catch (err) { + + } + + if (counter > 20) { + console.log('checkIsControllerInstalled: Cannot install!'); + if (cb) cb('Cannot install'); + } else { + console.log('checkIsControllerInstalled: wait...'); + setTimeout(function() { + checkIsControllerInstalled(cb, counter + 1); + }, 1000); + } +} + +function installAdapter(customName, cb) { + if (typeof customName === 'function') { + cb = customName; + customName = null; + } + customName = customName || pkg.name.split('.').pop(); + console.log('Install adapter...'); + var startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js'; + // make first install + if (debug) { + child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', { + cwd: rootDir + 'tmp', + stdio: [0, 1, 2] + }); + checkIsAdapterInstalled(function (error) { + if (error) console.error(error); + console.log('Adapter installed.'); + if (cb) cb(); + }); + } else { + // add controller + var _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], { + cwd: rootDir + 'tmp', + stdio: [0, 1, 2, 'ipc'] + }); + + waitForEnd(_pid, function () { + checkIsAdapterInstalled(function (error) { + if (error) console.error(error); + console.log('Adapter installed.'); + if (cb) cb(); + }); + }); + } +} + +function waitForEnd(_pid, cb) { + if (!_pid) { + cb(-1, -1); + return; + } + _pid.on('exit', function (code, signal) { + if (_pid) { + _pid = null; + cb(code, signal); + } + }); + _pid.on('close', function (code, signal) { + if (_pid) { + _pid = null; + cb(code, signal); + } + }); +} + +function installJsController(cb) { + console.log('installJsController...'); + if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') || + !fs.existsSync(rootDir + 'tmp/' + appName + '-data')) { + // try to detect appName.js-controller in node_modules/appName.js-controller + // travis CI installs js-controller into node_modules + if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) { + console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"'); + // copy all + // stop controller + console.log('Stop controller if running...'); + var _pid; + if (debug) { + // start controller + _pid = child_process.exec('node ' + appName + '.js stop', { + cwd: rootDir + 'node_modules/' + appName + '.js-controller', + stdio: [0, 1, 2] + }); + } else { + _pid = child_process.fork(appName + '.js', ['stop'], { + cwd: rootDir + 'node_modules/' + appName + '.js-controller', + stdio: [0, 1, 2, 'ipc'] + }); + } + + waitForEnd(_pid, function () { + // copy all files into + if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp'); + if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules'); + + if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){ + console.log('Copy js-controller...'); + copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/'); + } + + console.log('Setup js-controller...'); + var __pid; + if (debug) { + // start controller + _pid = child_process.exec('node ' + appName + '.js setup first --console', { + cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', + stdio: [0, 1, 2] + }); + } else { + __pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], { + cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', + stdio: [0, 1, 2, 'ipc'] + }); + } + waitForEnd(__pid, function () { + checkIsControllerInstalled(function () { + // change ports for object and state DBs + var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); + config.objects.port = 19001; + config.states.port = 19000; + fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2)); + console.log('Setup finished.'); + + copyAdapterToController(); + + installAdapter(function () { + storeOriginalFiles(); + if (cb) cb(true); + }); + }); + }); + }); + } else { + // check if port 9000 is free, else admin adapter will be added to running instance + var client = new require('net').Socket(); + client.connect(9000, '127.0.0.1', function() { + console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.'); + process.exit(0); + }); + + setTimeout(function () { + client.destroy(); + if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) { + console.log('installJsController: no js-controller => install from git'); + + child_process.execSync('npm install https://git.spacen.net/' + appName + '/' + appName + '.js-controller/tarball/master --prefix ./ --production', { + cwd: rootDir + 'tmp/', + stdio: [0, 1, 2] + }); + } else { + console.log('Setup js-controller...'); + var __pid; + if (debug) { + // start controller + child_process.exec('node ' + appName + '.js setup first', { + cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', + stdio: [0, 1, 2] + }); + } else { + child_process.fork(appName + '.js', ['setup', 'first'], { + cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', + stdio: [0, 1, 2, 'ipc'] + }); + } + } + + // let npm install admin and run setup + checkIsControllerInstalled(function () { + var _pid; + + if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) { + _pid = child_process.fork(appName + '.js', ['stop'], { + cwd: rootDir + 'node_modules/' + appName + '.js-controller', + stdio: [0, 1, 2, 'ipc'] + }); + } + + waitForEnd(_pid, function () { + // change ports for object and state DBs + var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); + config.objects.port = 19001; + config.states.port = 19000; + fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2)); + + copyAdapterToController(); + + installAdapter(function () { + storeOriginalFiles(); + if (cb) cb(true); + }); + }); + }); + }, 1000); + } + } else { + setTimeout(function () { + console.log('installJsController: js-controller installed'); + if (cb) cb(false); + }, 0); + } +} + +function copyAdapterToController() { + console.log('Copy adapter...'); + // Copy adapter to tmp/node_modules/appName.adapter + copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']); + console.log('Adapter copied.'); +} + +function clearControllerLog() { + var dirPath = rootDir + 'tmp/log'; + var files; + try { + if (fs.existsSync(dirPath)) { + console.log('Clear controller log...'); + files = fs.readdirSync(dirPath); + } else { + console.log('Create controller log directory...'); + files = []; + fs.mkdirSync(dirPath); + } + } catch(e) { + console.error('Cannot read "' + dirPath + '"'); + return; + } + if (files.length > 0) { + try { + for (var i = 0; i < files.length; i++) { + var filePath = dirPath + '/' + files[i]; + fs.unlinkSync(filePath); + } + console.log('Controller log cleared'); + } catch (err) { + console.error('cannot clear log: ' + err); + } + } +} + +function clearDB() { + var dirPath = rootDir + 'tmp/yunkong2-data/sqlite'; + var files; + try { + if (fs.existsSync(dirPath)) { + console.log('Clear sqlite DB...'); + files = fs.readdirSync(dirPath); + } else { + console.log('Create controller log directory...'); + files = []; + fs.mkdirSync(dirPath); + } + } catch(e) { + console.error('Cannot read "' + dirPath + '"'); + return; + } + if (files.length > 0) { + try { + for (var i = 0; i < files.length; i++) { + var filePath = dirPath + '/' + files[i]; + fs.unlinkSync(filePath); + } + console.log('Clear sqlite DB'); + } catch (err) { + console.error('cannot clear DB: ' + err); + } + } +} + +function setupController(cb) { + installJsController(function (isInited) { + clearControllerLog(); + clearDB(); + + if (!isInited) { + restoreOriginalFiles(); + copyAdapterToController(); + } + // read system.config object + var dataDir = rootDir + 'tmp/' + appName + '-data/'; + + var objs; + try { + objs = fs.readFileSync(dataDir + 'objects.json'); + objs = JSON.parse(objs); + } + catch (e) { + console.log('ERROR reading/parsing system configuration. Ignore'); + objs = {'system.config': {}}; + } + if (!objs || !objs['system.config']) { + objs = {'system.config': {}}; + } + + if (cb) cb(objs['system.config']); + }); +} + +function startAdapter(objects, states, callback) { + if (adapterStarted) { + console.log('Adapter already started ...'); + if (callback) callback(objects, states); + return; + } + adapterStarted = true; + console.log('startAdapter...'); + if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) { + try { + if (debug) { + // start controller + pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', { + cwd: rootDir + 'tmp', + stdio: [0, 1, 2] + }); + } else { + // start controller + pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], { + cwd: rootDir + 'tmp', + stdio: [0, 1, 2, 'ipc'] + }); + } + } catch (error) { + console.error(JSON.stringify(error)); + } + } else { + console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main); + } + if (callback) callback(objects, states); +} + +function startController(isStartAdapter, onObjectChange, onStateChange, callback) { + if (typeof isStartAdapter === 'function') { + callback = onStateChange; + onStateChange = onObjectChange; + onObjectChange = isStartAdapter; + isStartAdapter = true; + } + + if (onStateChange === undefined) { + callback = onObjectChange; + onObjectChange = undefined; + } + + if (pid) { + console.error('Controller is already started!'); + } else { + console.log('startController...'); + adapterStarted = false; + var isObjectConnected; + var isStatesConnected; + + var Objects = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/objects/objectsInMemServer'); + objects = new Objects({ + connection: { + "type" : "file", + "host" : "127.0.0.1", + "port" : 19001, + "user" : "", + "pass" : "", + "noFileCache": false, + "connectTimeout": 2000 + }, + logger: { + silly: function (msg) { + console.log(msg); + }, + debug: function (msg) { + console.log(msg); + }, + info: function (msg) { + console.log(msg); + }, + warn: function (msg) { + console.warn(msg); + }, + error: function (msg) { + console.error(msg); + } + }, + connected: function () { + isObjectConnected = true; + if (isStatesConnected) { + console.log('startController: started!'); + if (isStartAdapter) { + startAdapter(objects, states, callback); + } else { + if (callback) { + callback(objects, states); + callback = null; + } + } + } + }, + change: onObjectChange + }); + + // Just open in memory DB itself + var States = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/states/statesInMemServer'); + states = new States({ + connection: { + type: 'file', + host: '127.0.0.1', + port: 19000, + options: { + auth_pass: null, + retry_max_delay: 15000 + } + }, + logger: { + silly: function (msg) { + console.log(msg); + }, + debug: function (msg) { + console.log(msg); + }, + info: function (msg) { + console.log(msg); + }, + warn: function (msg) { + console.log(msg); + }, + error: function (msg) { + console.log(msg); + } + }, + connected: function () { + isStatesConnected = true; + if (isObjectConnected) { + console.log('startController: started!!'); + if (isStartAdapter) { + startAdapter(objects, states, callback); + } else { + if (callback) { + callback(objects, states); + callback = null; + } + } + } + }, + change: onStateChange + }); + } +} + +function stopAdapter(cb) { + if (!pid) { + console.error('Controller is not running!'); + if (cb) { + setTimeout(function () { + cb(false); + }, 0); + } + } else { + adapterStarted = false; + pid.on('exit', function (code, signal) { + if (pid) { + console.log('child process terminated due to receipt of signal ' + signal); + if (cb) cb(); + pid = null; + } + }); + + pid.on('close', function (code, signal) { + if (pid) { + if (cb) cb(); + pid = null; + } + }); + + pid.kill('SIGTERM'); + } +} + +function _stopController() { + if (objects) { + objects.destroy(); + objects = null; + } + if (states) { + states.destroy(); + states = null; + } +} + +function stopController(cb) { + var timeout; + if (objects) { + console.log('Set system.adapter.' + pkg.name + '.0'); + objects.setObject('system.adapter.' + pkg.name + '.0', { + common:{ + enabled: false + } + }); + } + + stopAdapter(function () { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + + _stopController(); + + if (cb) { + cb(true); + cb = null; + } + }); + + timeout = setTimeout(function () { + timeout = null; + console.log('child process NOT terminated'); + + _stopController(); + + if (cb) { + cb(false); + cb = null; + } + pid = null; + }, 5000); +} + +// Setup the adapter +function setAdapterConfig(common, native, instance) { + var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString()); + var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0); + if (common) objects[id].common = common; + if (native) objects[id].native = native; + fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects)); +} + +// Read config of the adapter +function getAdapterConfig(instance) { + var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString()); + var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0); + return objects[id]; +} + +if (typeof module !== undefined && module.parent) { + module.exports.getAdapterConfig = getAdapterConfig; + module.exports.setAdapterConfig = setAdapterConfig; + module.exports.startController = startController; + module.exports.stopController = stopController; + module.exports.setupController = setupController; + module.exports.stopAdapter = stopAdapter; + module.exports.startAdapter = startAdapter; + module.exports.installAdapter = installAdapter; + module.exports.appName = appName; + module.exports.adapterName = adapterName; + module.exports.adapterStarted = adapterStarted; +} diff --git a/test/testAdapter.js b/test/testAdapter.js new file mode 100644 index 0000000..85aae6f --- /dev/null +++ b/test/testAdapter.js @@ -0,0 +1,142 @@ +/* jshint -W097 */// jshint strict:false +/*jslint node: true */ +var expect = require('chai').expect; +var setup = require(__dirname + '/lib/setup'); + +var objects = null; +var states = null; +var onStateChanged = null; +var onObjectChanged = null; +var sendToID = 1; + +var adapterShortName = setup.adapterName.substring(setup.adapterName.indexOf('.') + 1); +var runningMode = require(__dirname + '/../io-package.json').common.mode; + +function checkConnectionOfAdapter(cb, counter) { + counter = counter || 0; + console.log('Try check #' + counter); + if (counter > 30) { + if (cb) cb('Cannot check connection'); + return; + } + + states.getState('system.adapter.' + adapterShortName + '.0.alive', function (err, state) { + if (err) console.error(err); + if (state && state.val) { + if (cb) cb(); + } else { + setTimeout(function () { + checkConnectionOfAdapter(cb, counter + 1); + }, 1000); + } + }); +} + +function checkValueOfState(id, value, cb, counter) { + counter = counter || 0; + if (counter > 20) { + if (cb) cb('Cannot check value Of State ' + id); + return; + } + + states.getState(id, function (err, state) { + if (err) console.error(err); + if (value === null && !state) { + if (cb) cb(); + } else + if (state && (value === undefined || state.val === value)) { + if (cb) cb(); + } else { + setTimeout(function () { + checkValueOfState(id, value, cb, counter + 1); + }, 500); + } + }); +} + +function sendTo(target, command, message, callback) { + onStateChanged = function (id, state) { + if (id === 'messagebox.system.adapter.test.0') { + callback(state.message); + } + }; + + states.pushMessage('system.adapter.' + target, { + command: command, + message: message, + from: 'system.adapter.test.0', + callback: { + message: message, + id: sendToID++, + ack: false, + time: (new Date()).getTime() + } + }); +} + +describe('Test ' + adapterShortName + ' adapter', function() { + before('Test ' + adapterShortName + ' adapter: Start js-controller', function (_done) { + this.timeout(600000); // because of first install from npm + + setup.setupController(function () { + var config = setup.getAdapterConfig(); + // enable adapter + config.common.enabled = true; + config.common.loglevel = 'debug'; + + config.native.announce = 'gong.mp3'; + + setup.setAdapterConfig(config.common, config.native); + + setup.startController(true, function(id, obj) {}, function (id, state) { + if (onStateChanged) onStateChanged(id, state); + }, + function (_objects, _states) { + objects = _objects; + states = _states; + _done(); + }); + }); + }); + + it('Test ' + adapterShortName + ' instance object: it must exists', function (done) { + objects.getObject('system.adapter.' + adapterShortName + '.0', function (err, obj) { + expect(err).to.be.null; + expect(obj).to.be.an('object'); + expect(obj).not.to.be.null; + done(); + }); + }); + + it('Test ' + adapterShortName + ' adapter: Check if adapter started', function (done) { + this.timeout(60000); + checkConnectionOfAdapter(function (res) { + if (res) console.log(res); + if (runningMode === 'daemon') { + expect(res).not.to.be.equal('Cannot check connection'); + } else { + //?? + } + done(); + }); + }); +/**/ + +/* + PUT YOUR OWN TESTS HERE USING + it('Testname', function ( done) { + ... + }); + + You can also use "sendTo" method to send messages to the started adapter +*/ + + after('Test ' + adapterShortName + ' adapter: Stop js-controller', function (done) { + this.timeout(10000); + + setup.stopController(function (normalTerminated) { + console.log('Adapter normal terminated: ' + normalTerminated); + done(); + }); + }); +}); diff --git a/test/testPackageFiles.js b/test/testPackageFiles.js new file mode 100644 index 0000000..d0759c0 --- /dev/null +++ b/test/testPackageFiles.js @@ -0,0 +1,91 @@ +/* jshint -W097 */ +/* jshint strict:false */ +/* jslint node: true */ +/* jshint expr: true */ +var expect = require('chai').expect; +var fs = require('fs'); + +describe('Test package.json and io-package.json', function() { + it('Test package files', function (done) { + console.log(); + + var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8'); + var ioPackage = JSON.parse(fileContentIOPackage); + + var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8'); + var npmPackage = JSON.parse(fileContentNPMPackage); + + expect(ioPackage).to.be.an('object'); + expect(npmPackage).to.be.an('object'); + + expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist; + expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist; + + expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version); + + if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) { + console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!'); + console.log(); + } + + expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist; + expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist; + + if (ioPackage.common.name.indexOf('template') !== 0) { + if (Array.isArray(ioPackage.common.authors)) { + expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0); + if (ioPackage.common.authors.length === 1) { + expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name '); + } + } + else { + expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name '); + } + } + else { + console.log('WARNING: Testing for set authors field in io-package skipped because template adapter'); + console.log(); + } + expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true; + if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') { + console.log('WARNING: titleLang is not existing in io-package.json. Please add'); + console.log(); + } + if ( + ioPackage.common.title.indexOf('yunkong2') !== -1 || + ioPackage.common.title.indexOf('yunkong2') !== -1 || + ioPackage.common.title.indexOf('adapter') !== -1 || + ioPackage.common.title.indexOf('Adapter') !== -1 + ) { + console.log('WARNING: title contains Adapter or yunkong2. It is clear anyway, that it is adapter for yunkong2.'); + console.log(); + } + + if (ioPackage.common.name.indexOf('vis-') !== 0) { + if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) { + console.log('WARNING: Admin3 support is missing! Please add it'); + console.log(); + } + if (ioPackage.common.materialize) { + expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true; + } + } + + var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE'); + var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8'); + if (fileContentReadme.indexOf('## Changelog') === -1) { + console.log('Warning: The README.md should have a section ## Changelog'); + console.log(); + } + expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true; + if (!licenseFileExists) { + console.log('Warning: The License should also exist as LICENSE file'); + console.log(); + } + if (fileContentReadme.indexOf('## License') === -1) { + console.log('Warning: The README.md should also have a section ## License to be shown in Admin3'); + console.log(); + } + done(); + }); +});