@creativeorange/azure-text-to-speech 2.2.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,23 +1,41 @@
1
1
  {
2
2
  "name": "@creativeorange/azure-text-to-speech",
3
- "version": "2.2.3",
3
+ "version": "3.0.0",
4
4
  "main": "dist/co-azure-tts.umd.js",
5
5
  "browser": "dist/co-azure-tts.es.js",
6
+ "module": "dist/co-azure-tts.es.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/co-azure-tts.es.js",
10
+ "require": "./dist/co-azure-tts.umd.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "src"
16
+ ],
6
17
  "scripts": {
7
- "build": "vite build --watch"
18
+ "build": "vite build",
19
+ "build:watch": "vite build --watch",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\""
8
23
  },
9
24
  "license": "MIT",
10
25
  "author": "Edsardio",
11
26
  "devDependencies": {
12
27
  "@originjs/vite-plugin-commonjs": "^1.0.1",
13
28
  "@typescript-eslint/eslint-plugin": "^5.33.0",
29
+ "@typescript-eslint/parser": "^5.33.0",
14
30
  "build-esm": "^4.2.2",
15
31
  "eslint": "^8.21.0",
16
32
  "eslint-config-google": "^0.14.0",
33
+ "jsdom": "^24.1.0",
17
34
  "microsoft-cognitiveservices-speech-sdk": "^1.22.0",
18
35
  "typescript": "^4.7.4",
19
36
  "vite": "^2.7.2",
20
37
  "vite-plugin-env-compatible": "^1.1.1",
21
- "vite-plugin-eslint": "^1.7.0"
38
+ "vite-plugin-eslint": "^1.7.0",
39
+ "vitest": "^0.34.6"
22
40
  }
23
41
  }
@@ -4,19 +4,36 @@ import {
4
4
  TranslationRecognizer,
5
5
  ResultReason,
6
6
  } from 'microsoft-cognitiveservices-speech-sdk';
7
+ import {
8
+ SpeechAuthenticationOptions,
9
+ SpeechAuthorizationManager,
10
+ toSafeErrorDetail,
11
+ } from './authentication';
12
+
13
+ export type SpeechToTextOptions = SpeechAuthenticationOptions & {
14
+ region?: string;
15
+ sourceLanguage: string;
16
+ targetLanguage?: string;
17
+ };
7
18
 
8
19
  export class SpeechToText {
9
- key: string;
10
20
  region: string;
11
21
  sourceLanguage: string;
12
22
  targetLanguage: string;
13
23
  recognizer: TranslationRecognizer | undefined;
14
24
 
15
- constructor(key: string, region: string, sourceLanguage: string, targetLanguage: string|null = null) {
16
- this.key = key;
17
- this.region = region;
18
- this.sourceLanguage = sourceLanguage;
19
- this.targetLanguage = (targetLanguage !== null) ? targetLanguage : sourceLanguage;
25
+ private readonly authorizationManager: SpeechAuthorizationManager;
26
+
27
+ constructor(options: SpeechToTextOptions) {
28
+ if (!options || typeof options.sourceLanguage !== 'string' ||
29
+ options.sourceLanguage.trim() === '') {
30
+ throw new Error('A sourceLanguage is required.');
31
+ }
32
+
33
+ this.authorizationManager = new SpeechAuthorizationManager(options);
34
+ this.region = options.region?.trim() ?? '';
35
+ this.sourceLanguage = options.sourceLanguage;
36
+ this.targetLanguage = options.targetLanguage ?? options.sourceLanguage;
20
37
  }
21
38
 
22
39
  async start() {
@@ -46,46 +63,70 @@ export class SpeechToText {
46
63
  }
47
64
  }
48
65
 
49
- async handleStartModifier(node: any, attr: Attr) {
50
- node.addEventListener('click', async (_: any) => {
51
- const speechConfig = SpeechTranslationConfig.fromSubscription(this.key, this.region);
52
- speechConfig.speechRecognitionLanguage = this.sourceLanguage;
53
- speechConfig.addTargetLanguage(this.targetLanguage);
66
+ private async createSpeechTranslationConfig():
67
+ Promise<SpeechTranslationConfig> {
68
+ const authorization =
69
+ await this.authorizationManager.getAuthorization();
54
70
 
55
- const audioConfig = AudioConfig.fromDefaultMicrophoneInput();
71
+ const speechConfig =
72
+ SpeechTranslationConfig.fromAuthorizationToken(
73
+ authorization.token,
74
+ authorization.region,
75
+ );
56
76
 
57
- this.recognizer = new TranslationRecognizer(speechConfig, audioConfig);
77
+ speechConfig.speechRecognitionLanguage =
78
+ this.sourceLanguage;
58
79
 
59
- document.dispatchEvent(new CustomEvent('COAzureSTTStartedRecording', {}));
80
+ speechConfig.addTargetLanguage(
81
+ this.targetLanguage,
82
+ );
60
83
 
61
- const prevResults = [];
62
- this.recognizer.recognizing = (sender, event) => {
63
- const result = event.result;
84
+ this.region = authorization.region;
64
85
 
65
- if (result && result.reason === ResultReason.TranslatingSpeech) {
66
- const translation = result.translations.get(this.targetLanguage);
67
- prevResults['result_' +result.privOffset.toString()] = translation;
68
- const totalResult = Object.values(prevResults).join('. ');
86
+ return speechConfig;
87
+ }
69
88
 
70
- const inputElement = document.getElementById(attr.value);
71
- if (inputElement !== null) {
72
- if (inputElement instanceof HTMLInputElement) {
73
- inputElement.value = `${totalResult} `;
74
- } else {
75
- inputElement.innerHTML = `${totalResult} `;
89
+ async handleStartModifier(node: any, attr: Attr) {
90
+ node.addEventListener('click', async (_: any) => {
91
+ try {
92
+ const speechConfig = await this.createSpeechTranslationConfig();
93
+ const audioConfig = AudioConfig.fromDefaultMicrophoneInput();
94
+
95
+ this.recognizer = new TranslationRecognizer(speechConfig, audioConfig);
96
+
97
+ document.dispatchEvent(new CustomEvent('COAzureSTTStartedRecording', {}));
98
+
99
+ const prevResults = [];
100
+ this.recognizer.recognizing = (sender, event) => {
101
+ const result = event.result;
102
+
103
+ if (result && result.reason === ResultReason.TranslatingSpeech) {
104
+ const translation = result.translations.get(this.targetLanguage);
105
+ prevResults['result_' +result.privOffset.toString()] = translation;
106
+ const totalResult = Object.values(prevResults).join('. ');
107
+
108
+ const inputElement = document.getElementById(attr.value);
109
+ if (inputElement !== null) {
110
+ if (inputElement instanceof HTMLInputElement) {
111
+ inputElement.value = `${totalResult} `;
112
+ } else {
113
+ inputElement.innerHTML = `${totalResult} `;
114
+ }
76
115
  }
77
116
  }
78
- }
79
- };
80
-
81
- this.recognizer.startContinuousRecognitionAsync(
82
- (result) => {},
83
- (err) => {
84
- console.log(err);
85
-
86
- this.stop();
87
- }
88
- );
117
+ };
118
+
119
+ this.recognizer.startContinuousRecognitionAsync(
120
+ () => {},
121
+ (err) => {
122
+ this.dispatchError(err, 'RECOGNITION_ERROR');
123
+ this.stop();
124
+ },
125
+ );
126
+ } catch (error) {
127
+ this.dispatchError(error);
128
+ await this.stop();
129
+ }
89
130
  });
90
131
  }
91
132
 
@@ -97,10 +138,35 @@ export class SpeechToText {
97
138
 
98
139
  async stop() {
99
140
  if (this.recognizer !== undefined) {
100
- this.recognizer.stopContinuousRecognitionAsync();
101
- this.recognizer.close();
141
+ try {
142
+ this.recognizer.stopContinuousRecognitionAsync();
143
+ } catch {
144
+ // Recognizer may already be stopping.
145
+ }
146
+
147
+ try {
148
+ this.recognizer.close();
149
+ } catch {
150
+ // Recognizer may already be closed.
151
+ }
152
+
102
153
  this.recognizer = undefined;
103
154
  }
104
155
  document.dispatchEvent(new CustomEvent('COAzureSTTStoppedRecording', {}));
105
156
  }
157
+
158
+ private dispatchError(error: unknown, code?: string) {
159
+ const detail = toSafeErrorDetail(error);
160
+ if (code && !detail.code) {
161
+ detail.code = code;
162
+ }
163
+
164
+ document.dispatchEvent(
165
+ new CustomEvent('COAzureSTTError', {
166
+ detail: {
167
+ error: detail,
168
+ },
169
+ }),
170
+ );
171
+ }
106
172
  }