dsh-live-voice 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/asr/asr.worker.js +1 -1
- package/lib/asr/manifest.json +1 -1
- package/lib/client.js +156 -134
- package/lib/server.js +2 -2
- package/lib/vad/manifest.json +2 -2
- package/lib/vad/vad.worker.js +1 -1
- package/package.json +2 -2
- package/src/app/client/apply.tsx +7 -2
- package/src/app/client/i18n/spokenWarning.ts +21 -0
- package/src/modules/core/coordinator.ts +9 -8
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Local-first speech-to-text (STT), text-to-speech (TTS), and meeting audio transc
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
Tested with **DeepSeek Harness v0.2.2-rc.
|
|
7
|
+
Tested with **DeepSeek Harness v0.2.2-rc.3**. The package manifest's `dshTestedVersion` field records the tested version.
|
|
8
8
|
|
|
9
9
|
Install the prebuilt package from npm; no source build is required. The Git repository contains source only.
|
|
10
10
|
|
package/lib/asr/asr.worker.js
CHANGED
|
@@ -36682,7 +36682,7 @@ async function createQwenRuntime(options) {
|
|
|
36682
36682
|
}
|
|
36683
36683
|
|
|
36684
36684
|
// src/modules/recognition/local/assets.ts
|
|
36685
|
-
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.8.
|
|
36685
|
+
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.8.1" : "test") + "/";
|
|
36686
36686
|
|
|
36687
36687
|
// src/modules/recognition/local/modelCatalog.ts
|
|
36688
36688
|
var browserAsrModels = [
|
package/lib/asr/manifest.json
CHANGED
package/lib/client.js
CHANGED
|
@@ -36,89 +36,6 @@ __export(apply_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(apply_exports);
|
|
37
37
|
var import_react80 = __toESM(require("react"), 1);
|
|
38
38
|
|
|
39
|
-
// src/modules/conversation/models/inactivity.ts
|
|
40
|
-
var VoiceInactivity = class {
|
|
41
|
-
constructor(options) {
|
|
42
|
-
this.options = options;
|
|
43
|
-
}
|
|
44
|
-
options;
|
|
45
|
-
snapshot = { phase: "idle", remaining: 0 };
|
|
46
|
-
listeners = /* @__PURE__ */ new Set();
|
|
47
|
-
active = false;
|
|
48
|
-
lastText = 0;
|
|
49
|
-
lastTick = 0;
|
|
50
|
-
deadline = 0;
|
|
51
|
-
busySince = 0;
|
|
52
|
-
warningText = "";
|
|
53
|
-
echoUntil = 0;
|
|
54
|
-
now = () => this.options.now?.() ?? Date.now();
|
|
55
|
-
getSnapshot = () => this.snapshot;
|
|
56
|
-
subscribe = (listener) => {
|
|
57
|
-
this.listeners.add(listener);
|
|
58
|
-
return () => {
|
|
59
|
-
this.listeners.delete(listener);
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
patch(phase, remaining = 0) {
|
|
63
|
-
if (this.snapshot.phase === phase && this.snapshot.remaining === remaining) return;
|
|
64
|
-
this.snapshot = { phase, remaining };
|
|
65
|
-
for (const listener of this.listeners) listener();
|
|
66
|
-
}
|
|
67
|
-
keepAlive = () => {
|
|
68
|
-
this.lastText = this.now();
|
|
69
|
-
this.deadline = 0;
|
|
70
|
-
this.busySince = 0;
|
|
71
|
-
this.patch("idle");
|
|
72
|
-
};
|
|
73
|
-
/** Filter only matching warning echo; unrelated replies remain eligible. */
|
|
74
|
-
recognized(text) {
|
|
75
|
-
const normalize = (value) => value.toLocaleLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
76
|
-
if (!text.trim()) return true;
|
|
77
|
-
const heard = normalize(text);
|
|
78
|
-
const own = normalize(this.warningText);
|
|
79
|
-
if (this.now() < this.echoUntil && heard.length >= 24 && heard.length >= own.length / 2 && own.includes(heard))
|
|
80
|
-
return false;
|
|
81
|
-
this.keepAlive();
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
tick(active, busy, settings) {
|
|
85
|
-
const now2 = this.now();
|
|
86
|
-
const gap = this.lastTick && now2 - this.lastTick > 1e4;
|
|
87
|
-
this.lastTick = now2;
|
|
88
|
-
if (!settings.autoEndEnabled || !active) {
|
|
89
|
-
this.active = false;
|
|
90
|
-
if (this.snapshot.phase !== "ended" || active) this.patch("idle");
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (!this.active) {
|
|
94
|
-
this.active = true;
|
|
95
|
-
this.keepAlive();
|
|
96
|
-
}
|
|
97
|
-
const total = settings.autoEndMinutes * 6e4;
|
|
98
|
-
const warning = settings.autoEndWarningMinutes * 6e4;
|
|
99
|
-
if (gap && now2 - this.lastText >= total - warning) this.lastText = now2 - (total - warning);
|
|
100
|
-
if (now2 - this.lastText < total - warning) return;
|
|
101
|
-
if (!this.deadline || gap) {
|
|
102
|
-
this.deadline = Math.max(this.lastText + total, now2 + warning);
|
|
103
|
-
this.patch("warning", Math.ceil((this.deadline - now2) / 1e3));
|
|
104
|
-
this.echoUntil = now2 + 3e4;
|
|
105
|
-
this.warningText = this.options.warn(this.snapshot.remaining);
|
|
106
|
-
}
|
|
107
|
-
this.patch("warning", Math.max(0, Math.ceil((this.deadline - now2) / 1e3)));
|
|
108
|
-
if (now2 < this.deadline) return;
|
|
109
|
-
if (busy) {
|
|
110
|
-
if (!this.busySince) this.busySince = now2;
|
|
111
|
-
if (now2 - this.busySince < 3e4) return;
|
|
112
|
-
}
|
|
113
|
-
this.active = false;
|
|
114
|
-
this.patch("ended");
|
|
115
|
-
this.options.end();
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// src/modules/conversation/components/InactivityNotice.tsx
|
|
120
|
-
var import_react3 = __toESM(require("react"), 1);
|
|
121
|
-
|
|
122
39
|
// src/app/client/i18n/catalogs/en.ts
|
|
123
40
|
var en = {
|
|
124
41
|
"dsh-live-voice.commons.connection.contactingHost": "Contacting DSH host\u2026",
|
|
@@ -3197,6 +3114,143 @@ var appDictionaries = Object.freeze({
|
|
|
3197
3114
|
zh: zh_default
|
|
3198
3115
|
});
|
|
3199
3116
|
|
|
3117
|
+
// src/app/client/i18n/registerDshLocales.ts
|
|
3118
|
+
var LIVE_VOICE_LOCALE_NAMESPACE = "dsh-live-voice";
|
|
3119
|
+
var LIVE_VOICE_LANGUAGES = liveVoiceLanguageDefinitions;
|
|
3120
|
+
var liveVoiceDictionaries = appDictionaries;
|
|
3121
|
+
function createFallbackTranslator(locale = "en") {
|
|
3122
|
+
return (key, params = {}) => {
|
|
3123
|
+
const template = liveVoiceDictionaries[locale]?.[key] ?? liveVoiceDictionaries.en[key] ?? key;
|
|
3124
|
+
return String(template).replace(
|
|
3125
|
+
/\{([A-Za-z0-9_]+)\}/g,
|
|
3126
|
+
(_2, name) => String(params[name] ?? "{" + name + "}")
|
|
3127
|
+
);
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
function registerDshLocales(ctx) {
|
|
3131
|
+
if (!ctx.locale) return createFallbackTranslator();
|
|
3132
|
+
const registeredLanguages = new Set(
|
|
3133
|
+
(ctx.locale.getSnapshot?.().locales || []).map(
|
|
3134
|
+
(language) => language.id.toLowerCase()
|
|
3135
|
+
)
|
|
3136
|
+
);
|
|
3137
|
+
for (const language of LIVE_VOICE_LANGUAGES) {
|
|
3138
|
+
if (registeredLanguages.has(language.id.toLowerCase())) continue;
|
|
3139
|
+
ctx.effect(
|
|
3140
|
+
() => ctx.locale.addLanguage(language),
|
|
3141
|
+
"dsh-live-voice: " + language.id + " language"
|
|
3142
|
+
);
|
|
3143
|
+
registeredLanguages.add(language.id.toLowerCase());
|
|
3144
|
+
}
|
|
3145
|
+
for (const [locale, dictionary] of Object.entries(liveVoiceDictionaries))
|
|
3146
|
+
ctx.effect(
|
|
3147
|
+
() => ctx.locale.register(LIVE_VOICE_LOCALE_NAMESPACE, locale, dictionary),
|
|
3148
|
+
"dsh-live-voice: " + locale + " dictionary"
|
|
3149
|
+
);
|
|
3150
|
+
return ctx.locale.bind(LIVE_VOICE_LOCALE_NAMESPACE);
|
|
3151
|
+
}
|
|
3152
|
+
var registerLiveVoiceLocales = registerDshLocales;
|
|
3153
|
+
|
|
3154
|
+
// src/app/client/i18n/spokenWarning.ts
|
|
3155
|
+
function warningLanguage(language) {
|
|
3156
|
+
const value = language?.trim().replaceAll("_", "-").toLowerCase();
|
|
3157
|
+
if (!value) return void 0;
|
|
3158
|
+
if (value === "pt" || value.startsWith("pt-")) return "pt-BR";
|
|
3159
|
+
const base = value.split("-")[0];
|
|
3160
|
+
return base && base in appDictionaries ? base : void 0;
|
|
3161
|
+
}
|
|
3162
|
+
function spokenInactivityWarning(ttsLanguage, uiLanguage, seconds) {
|
|
3163
|
+
const spoken = warningLanguage(ttsLanguage);
|
|
3164
|
+
const locale = spoken ?? warningLanguage(uiLanguage) ?? "en";
|
|
3165
|
+
return {
|
|
3166
|
+
text: createFallbackTranslator(locale)("dsh-live-voice.settings.autoEnd.warning", { seconds }),
|
|
3167
|
+
lang: spoken ? ttsLanguage.replaceAll("_", "-") : locale
|
|
3168
|
+
};
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
// src/modules/conversation/models/inactivity.ts
|
|
3172
|
+
var VoiceInactivity = class {
|
|
3173
|
+
constructor(options) {
|
|
3174
|
+
this.options = options;
|
|
3175
|
+
}
|
|
3176
|
+
options;
|
|
3177
|
+
snapshot = { phase: "idle", remaining: 0 };
|
|
3178
|
+
listeners = /* @__PURE__ */ new Set();
|
|
3179
|
+
active = false;
|
|
3180
|
+
lastText = 0;
|
|
3181
|
+
lastTick = 0;
|
|
3182
|
+
deadline = 0;
|
|
3183
|
+
busySince = 0;
|
|
3184
|
+
warningText = "";
|
|
3185
|
+
echoUntil = 0;
|
|
3186
|
+
now = () => this.options.now?.() ?? Date.now();
|
|
3187
|
+
getSnapshot = () => this.snapshot;
|
|
3188
|
+
subscribe = (listener) => {
|
|
3189
|
+
this.listeners.add(listener);
|
|
3190
|
+
return () => {
|
|
3191
|
+
this.listeners.delete(listener);
|
|
3192
|
+
};
|
|
3193
|
+
};
|
|
3194
|
+
patch(phase, remaining = 0) {
|
|
3195
|
+
if (this.snapshot.phase === phase && this.snapshot.remaining === remaining) return;
|
|
3196
|
+
this.snapshot = { phase, remaining };
|
|
3197
|
+
for (const listener of this.listeners) listener();
|
|
3198
|
+
}
|
|
3199
|
+
keepAlive = () => {
|
|
3200
|
+
this.lastText = this.now();
|
|
3201
|
+
this.deadline = 0;
|
|
3202
|
+
this.busySince = 0;
|
|
3203
|
+
this.patch("idle");
|
|
3204
|
+
};
|
|
3205
|
+
/** Filter only matching warning echo; unrelated replies remain eligible. */
|
|
3206
|
+
recognized(text) {
|
|
3207
|
+
const normalize = (value) => value.toLocaleLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
3208
|
+
if (!text.trim()) return true;
|
|
3209
|
+
const heard = normalize(text);
|
|
3210
|
+
const own = normalize(this.warningText);
|
|
3211
|
+
if (this.now() < this.echoUntil && heard.length >= 24 && heard.length >= own.length / 2 && own.includes(heard))
|
|
3212
|
+
return false;
|
|
3213
|
+
this.keepAlive();
|
|
3214
|
+
return true;
|
|
3215
|
+
}
|
|
3216
|
+
tick(active, busy, settings) {
|
|
3217
|
+
const now2 = this.now();
|
|
3218
|
+
const gap = this.lastTick && now2 - this.lastTick > 1e4;
|
|
3219
|
+
this.lastTick = now2;
|
|
3220
|
+
if (!settings.autoEndEnabled || !active) {
|
|
3221
|
+
this.active = false;
|
|
3222
|
+
if (this.snapshot.phase !== "ended" || active) this.patch("idle");
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
if (!this.active) {
|
|
3226
|
+
this.active = true;
|
|
3227
|
+
this.keepAlive();
|
|
3228
|
+
}
|
|
3229
|
+
const total = settings.autoEndMinutes * 6e4;
|
|
3230
|
+
const warning = settings.autoEndWarningMinutes * 6e4;
|
|
3231
|
+
if (gap && now2 - this.lastText >= total - warning) this.lastText = now2 - (total - warning);
|
|
3232
|
+
if (now2 - this.lastText < total - warning) return;
|
|
3233
|
+
if (!this.deadline || gap) {
|
|
3234
|
+
this.deadline = Math.max(this.lastText + total, now2 + warning);
|
|
3235
|
+
this.patch("warning", Math.ceil((this.deadline - now2) / 1e3));
|
|
3236
|
+
this.echoUntil = now2 + 3e4;
|
|
3237
|
+
this.warningText = this.options.warn(this.snapshot.remaining);
|
|
3238
|
+
}
|
|
3239
|
+
this.patch("warning", Math.max(0, Math.ceil((this.deadline - now2) / 1e3)));
|
|
3240
|
+
if (now2 < this.deadline) return;
|
|
3241
|
+
if (busy) {
|
|
3242
|
+
if (!this.busySince) this.busySince = now2;
|
|
3243
|
+
if (now2 - this.busySince < 3e4) return;
|
|
3244
|
+
}
|
|
3245
|
+
this.active = false;
|
|
3246
|
+
this.patch("ended");
|
|
3247
|
+
this.options.end();
|
|
3248
|
+
}
|
|
3249
|
+
};
|
|
3250
|
+
|
|
3251
|
+
// src/modules/conversation/components/InactivityNotice.tsx
|
|
3252
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
3253
|
+
|
|
3200
3254
|
// src/app/client/i18n/DshLanguageBoundary.tsx
|
|
3201
3255
|
var import_react2 = __toESM(require("react"), 1);
|
|
3202
3256
|
|
|
@@ -3261,43 +3315,6 @@ function createTranslationRuntime({ availableLangs: availableLangs2, defaultLang
|
|
|
3261
3315
|
return { TranslationProvider, useTranslation };
|
|
3262
3316
|
}
|
|
3263
3317
|
|
|
3264
|
-
// src/app/client/i18n/registerDshLocales.ts
|
|
3265
|
-
var LIVE_VOICE_LOCALE_NAMESPACE = "dsh-live-voice";
|
|
3266
|
-
var LIVE_VOICE_LANGUAGES = liveVoiceLanguageDefinitions;
|
|
3267
|
-
var liveVoiceDictionaries = appDictionaries;
|
|
3268
|
-
function createFallbackTranslator(locale = "en") {
|
|
3269
|
-
return (key, params = {}) => {
|
|
3270
|
-
const template = liveVoiceDictionaries[locale]?.[key] ?? liveVoiceDictionaries.en[key] ?? key;
|
|
3271
|
-
return String(template).replace(
|
|
3272
|
-
/\{([A-Za-z0-9_]+)\}/g,
|
|
3273
|
-
(_2, name) => String(params[name] ?? "{" + name + "}")
|
|
3274
|
-
);
|
|
3275
|
-
};
|
|
3276
|
-
}
|
|
3277
|
-
function registerDshLocales(ctx) {
|
|
3278
|
-
if (!ctx.locale) return createFallbackTranslator();
|
|
3279
|
-
const registeredLanguages = new Set(
|
|
3280
|
-
(ctx.locale.getSnapshot?.().locales || []).map(
|
|
3281
|
-
(language) => language.id.toLowerCase()
|
|
3282
|
-
)
|
|
3283
|
-
);
|
|
3284
|
-
for (const language of LIVE_VOICE_LANGUAGES) {
|
|
3285
|
-
if (registeredLanguages.has(language.id.toLowerCase())) continue;
|
|
3286
|
-
ctx.effect(
|
|
3287
|
-
() => ctx.locale.addLanguage(language),
|
|
3288
|
-
"dsh-live-voice: " + language.id + " language"
|
|
3289
|
-
);
|
|
3290
|
-
registeredLanguages.add(language.id.toLowerCase());
|
|
3291
|
-
}
|
|
3292
|
-
for (const [locale, dictionary] of Object.entries(liveVoiceDictionaries))
|
|
3293
|
-
ctx.effect(
|
|
3294
|
-
() => ctx.locale.register(LIVE_VOICE_LOCALE_NAMESPACE, locale, dictionary),
|
|
3295
|
-
"dsh-live-voice: " + locale + " dictionary"
|
|
3296
|
-
);
|
|
3297
|
-
return ctx.locale.bind(LIVE_VOICE_LOCALE_NAMESPACE);
|
|
3298
|
-
}
|
|
3299
|
-
var registerLiveVoiceLocales = registerDshLocales;
|
|
3300
|
-
|
|
3301
3318
|
// src/app/client/i18n/runtime.tsx
|
|
3302
3319
|
function interpolate(template, params = {}) {
|
|
3303
3320
|
return template.replace(
|
|
@@ -3497,7 +3514,7 @@ var SessionSpeechMemory = class {
|
|
|
3497
3514
|
};
|
|
3498
3515
|
|
|
3499
3516
|
// src/modules/recognition/vad/assets.ts
|
|
3500
|
-
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.8.
|
|
3517
|
+
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.8.1" : "test") + "/";
|
|
3501
3518
|
async function sileroVadAvailable(fetchImpl = globalThis.fetch) {
|
|
3502
3519
|
try {
|
|
3503
3520
|
const response = await fetchImpl(sileroVadAssetBase() + "availability", {
|
|
@@ -6907,7 +6924,7 @@ async function removeBrowserModel(key, storage = caches) {
|
|
|
6907
6924
|
}
|
|
6908
6925
|
|
|
6909
6926
|
// src/modules/recognition/local/assets.ts
|
|
6910
|
-
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.8.
|
|
6927
|
+
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.8.1" : "test") + "/";
|
|
6911
6928
|
|
|
6912
6929
|
// src/modules/recognition/local/runtimeIdentity.ts
|
|
6913
6930
|
var workerProtocol = "asr-worker-v3-ort-1.31.0-dev.20260914-8d85527a0";
|
|
@@ -11551,9 +11568,9 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
11551
11568
|
}
|
|
11552
11569
|
this.prefetchItems.clear();
|
|
11553
11570
|
}
|
|
11554
|
-
_speechPreparationKey() {
|
|
11571
|
+
_speechPreparationKey(item = {}) {
|
|
11555
11572
|
const { engine, voice, lang, rate, outputDeviceId } = this.snapshot.settings;
|
|
11556
|
-
return JSON.stringify([engine, voice, lang, rate, outputDeviceId]);
|
|
11573
|
+
return JSON.stringify([engine, voice, item.lang ?? lang, rate, outputDeviceId]);
|
|
11557
11574
|
}
|
|
11558
11575
|
_resetStaleSpeechItem(item, key) {
|
|
11559
11576
|
if (item.preparationKey === key) return;
|
|
@@ -11574,13 +11591,13 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
11574
11591
|
lang: this.snapshot.settings.lang,
|
|
11575
11592
|
outputDeviceId: this.snapshot.settings.outputDeviceId
|
|
11576
11593
|
};
|
|
11577
|
-
const key = this._speechPreparationKey();
|
|
11578
11594
|
for (const item of this.queue.slice(0, SPEECH_QUEUE_PREFETCH_LIMIT)) {
|
|
11595
|
+
const key = this._speechPreparationKey(item);
|
|
11579
11596
|
this._resetStaleSpeechItem(item, key);
|
|
11580
11597
|
if (item.prepared || item.preparing) continue;
|
|
11581
11598
|
this.prefetchItems.add(item);
|
|
11582
11599
|
const signal = this.prefetchController.signal;
|
|
11583
|
-
item.preparing = engine.prepare(item.text, { ...options, signal }).then((prepared) => {
|
|
11600
|
+
item.preparing = engine.prepare(item.text, { ...options, lang: item.lang ?? options.lang, signal }).then((prepared) => {
|
|
11584
11601
|
if (item.preparationKey === key) item.preparing = null;
|
|
11585
11602
|
if (signal.aborted || item.preparationKey !== key || !this.queue.includes(item) && !this.speechHistory.includes(item)) {
|
|
11586
11603
|
prepared.dispose?.();
|
|
@@ -11957,15 +11974,15 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
11957
11974
|
this._syncSpeechSegments();
|
|
11958
11975
|
return this.play(first, messageId, true);
|
|
11959
11976
|
}
|
|
11960
|
-
async play(text, messageId, manual = false) {
|
|
11977
|
+
async play(text, messageId, manual = false, speechOptions = {}) {
|
|
11961
11978
|
if (this.disposed || !text.trim()) return;
|
|
11962
11979
|
if (this.snapshot.speaking) {
|
|
11963
|
-
this.queue.push({ text, id: messageId, manual });
|
|
11980
|
+
this.queue.push({ text, id: messageId, manual, lang: speechOptions.lang });
|
|
11964
11981
|
this._prefetchSpeech();
|
|
11965
11982
|
this._syncSpeechSegments();
|
|
11966
11983
|
return;
|
|
11967
11984
|
}
|
|
11968
|
-
const item = this.activeSpeechItem?.text === text ? this.activeSpeechItem : { text, id: messageId, manual };
|
|
11985
|
+
const item = this.activeSpeechItem?.text === text ? this.activeSpeechItem : { text, id: messageId, manual, lang: speechOptions.lang };
|
|
11969
11986
|
if (!this.speechHistory.includes(item)) this.speechHistory.push(item);
|
|
11970
11987
|
this.speechCursor = this.speechHistory.indexOf(item);
|
|
11971
11988
|
const epoch = ++this.speechEpoch;
|
|
@@ -11984,7 +12001,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
11984
12001
|
let playbackStarted = false;
|
|
11985
12002
|
let playbackFinished = false;
|
|
11986
12003
|
const engine = this.engines[this.snapshot.settings.engine];
|
|
11987
|
-
this._resetStaleSpeechItem(item, this._speechPreparationKey());
|
|
12004
|
+
this._resetStaleSpeechItem(item, this._speechPreparationKey(item));
|
|
11988
12005
|
item.engine = engine;
|
|
11989
12006
|
if (!engine) {
|
|
11990
12007
|
this.queue.unshift(item);
|
|
@@ -12040,6 +12057,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
12040
12057
|
this.patch({ speechLoading: false });
|
|
12041
12058
|
}
|
|
12042
12059
|
},
|
|
12060
|
+
lang: item.lang ?? this.snapshot.settings.lang,
|
|
12043
12061
|
voice: this.snapshot.settings.voice || void 0,
|
|
12044
12062
|
rate: this.snapshot.settings.rate,
|
|
12045
12063
|
volume: this.snapshot.settings.volume,
|
|
@@ -15246,8 +15264,8 @@ function createDeviceOptions(devices, kind, systemDefault, fallback) {
|
|
|
15246
15264
|
var import_react42 = __toESM(require("react"), 1);
|
|
15247
15265
|
|
|
15248
15266
|
// src/modules/settings/services/releases.ts
|
|
15249
|
-
var CURRENT_VERSION = "0.8.
|
|
15250
|
-
var TESTED_DSH_VERSION = "0.2.2-rc.
|
|
15267
|
+
var CURRENT_VERSION = "0.8.1";
|
|
15268
|
+
var TESTED_DSH_VERSION = "0.2.2-rc.3";
|
|
15251
15269
|
var REPOSITORY_URL = "https://github.com/victorwads/dsh-live-voice";
|
|
15252
15270
|
var RELEASES_URL = `${REPOSITORY_URL}/releases`;
|
|
15253
15271
|
var LATEST_RELEASE_API_URL = "https://api.github.com/repos/victorwads/dsh-live-voice/releases/latest";
|
|
@@ -18156,11 +18174,15 @@ function apply(ctx) {
|
|
|
18156
18174
|
let inactivityEnding = false;
|
|
18157
18175
|
const inactivity = new VoiceInactivity({
|
|
18158
18176
|
warn: (seconds) => {
|
|
18159
|
-
const text = t("dsh-live-voice.settings.autoEnd.warning", { seconds });
|
|
18160
18177
|
const c = meetingEntry?.controller;
|
|
18178
|
+
const { text, lang } = spokenInactivityWarning(
|
|
18179
|
+
c?.getSnapshot().settings.lang ?? preferences.getSnapshot().lang,
|
|
18180
|
+
ctx.locale?.getSnapshot?.().active,
|
|
18181
|
+
seconds
|
|
18182
|
+
);
|
|
18161
18183
|
const s = c?.getSnapshot();
|
|
18162
18184
|
if (c && !s.speaking && !s.paused && !s.speechRunActive && !c.queue.length)
|
|
18163
|
-
run(c, c.play(text, null, true));
|
|
18185
|
+
run(c, c.play(text, null, true, { lang }));
|
|
18164
18186
|
return text;
|
|
18165
18187
|
},
|
|
18166
18188
|
end: () => {
|
package/lib/server.js
CHANGED
|
@@ -2066,7 +2066,7 @@ var VAD_ASSET_TYPES = {
|
|
|
2066
2066
|
};
|
|
2067
2067
|
var MAX_VAD_ASSET_BYTES = 32 * 1024 * 1024;
|
|
2068
2068
|
function registerVadAssetRoutes(ctx, options = {}) {
|
|
2069
|
-
const version = options.version ?? (true ? "0.8.
|
|
2069
|
+
const version = options.version ?? (true ? "0.8.1" : "test");
|
|
2070
2070
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9.+-]{0,127}$/.test(version))
|
|
2071
2071
|
throw new Error("Invalid VAD asset version");
|
|
2072
2072
|
const directory = options.directory ?? fileURLToPath(new URL("./vad/", import.meta.url));
|
|
@@ -2160,7 +2160,7 @@ import { lstat as lstat2, readFile as readFile5 } from "node:fs/promises";
|
|
|
2160
2160
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2161
2161
|
import { join as join8 } from "node:path";
|
|
2162
2162
|
function registerBrowserAsrAssets(ctx, options = {}) {
|
|
2163
|
-
const version = options.version ?? (true ? "0.8.
|
|
2163
|
+
const version = options.version ?? (true ? "0.8.1" : "test");
|
|
2164
2164
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9.+-]{0,127}$/.test(version)) throw new Error("invalid-version");
|
|
2165
2165
|
const base = "/api/dsh-live-voice/asr/" + encodeURIComponent(version) + "/";
|
|
2166
2166
|
const directory = options.directory ?? fileURLToPath2(new URL("./asr/", import.meta.url));
|
package/lib/vad/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"pluginVersion": "0.8.
|
|
3
|
+
"pluginVersion": "0.8.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@ricky0123/vad-web": "0.0.31",
|
|
6
6
|
"onnxruntime-web": "1.22.0"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"vad.worker.js": {
|
|
43
43
|
"bytes": 79337,
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "7bc4a97041cb5740bb0254387de63f69d5e869c020d9dd82096f5d8b4fbfdf06"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
package/lib/vad/vad.worker.js
CHANGED
|
@@ -1632,7 +1632,7 @@ var vo = Ke;
|
|
|
1632
1632
|
Object.defineProperty(I.versions, "web", { value: $t, enumerable: true });
|
|
1633
1633
|
|
|
1634
1634
|
// src/modules/recognition/vad/assets.ts
|
|
1635
|
-
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.8.
|
|
1635
|
+
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.8.1" : "test") + "/";
|
|
1636
1636
|
|
|
1637
1637
|
// src/modules/recognition/vad/StreamingResampler.ts
|
|
1638
1638
|
var StreamingResampler = class {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-live-voice",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"dshTestedVersion": "0.2.2-rc.
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"dshTestedVersion": "0.2.2-rc.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "node --experimental-strip-types ../../scripts/build.ts --package live-voice",
|
package/src/app/client/apply.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { spokenInactivityWarning } from './i18n/spokenWarning.js';
|
|
3
4
|
import { VoiceInactivity } from '../../modules/conversation/models/inactivity.js';
|
|
4
5
|
import { InactivityNotice } from '../../modules/conversation/components/InactivityNotice.js';
|
|
5
6
|
import {
|
|
@@ -97,12 +98,16 @@ export function apply(ctx) {
|
|
|
97
98
|
let inactivityEnding = false;
|
|
98
99
|
const inactivity = new VoiceInactivity({
|
|
99
100
|
warn: (seconds) => {
|
|
100
|
-
const text = t('dsh-live-voice.settings.autoEnd.warning', { seconds });
|
|
101
101
|
const c = meetingEntry?.controller;
|
|
102
|
+
const { text, lang } = spokenInactivityWarning(
|
|
103
|
+
c?.getSnapshot().settings.lang ?? preferences.getSnapshot().lang,
|
|
104
|
+
ctx.locale?.getSnapshot?.().active,
|
|
105
|
+
seconds,
|
|
106
|
+
);
|
|
102
107
|
// Do not replace assistant playback or a manually paused speech queue.
|
|
103
108
|
const s = c?.getSnapshot();
|
|
104
109
|
if (c && !s.speaking && !s.paused && !s.speechRunActive && !c.queue.length)
|
|
105
|
-
run(c, c.play(text, null, true));
|
|
110
|
+
run(c, c.play(text, null, true, { lang }));
|
|
106
111
|
return text;
|
|
107
112
|
},
|
|
108
113
|
end: () => {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { appDictionaries, type LiveVoiceLocale } from './catalogs/index.js';
|
|
2
|
+
import { createFallbackTranslator } from './registerDshLocales.js';
|
|
3
|
+
export function warningLanguage(language: string | undefined): LiveVoiceLocale | undefined {
|
|
4
|
+
const value = language?.trim().replaceAll('_', '-').toLowerCase();
|
|
5
|
+
if (!value) return undefined;
|
|
6
|
+
if (value === 'pt' || value.startsWith('pt-')) return 'pt-BR';
|
|
7
|
+
const base = value.split('-')[0];
|
|
8
|
+
return base && base in appDictionaries ? (base as LiveVoiceLocale) : undefined;
|
|
9
|
+
}
|
|
10
|
+
export function spokenInactivityWarning(
|
|
11
|
+
ttsLanguage: string | undefined,
|
|
12
|
+
uiLanguage: string | undefined,
|
|
13
|
+
seconds: number,
|
|
14
|
+
) {
|
|
15
|
+
const spoken = warningLanguage(ttsLanguage);
|
|
16
|
+
const locale = spoken ?? warningLanguage(uiLanguage) ?? 'en';
|
|
17
|
+
return {
|
|
18
|
+
text: createFallbackTranslator(locale)('dsh-live-voice.settings.autoEnd.warning', { seconds }),
|
|
19
|
+
lang: spoken ? ttsLanguage!.replaceAll('_', '-') : locale,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1077,9 +1077,9 @@ export class VoiceCoordinator {
|
|
|
1077
1077
|
this.prefetchItems.clear();
|
|
1078
1078
|
}
|
|
1079
1079
|
|
|
1080
|
-
_speechPreparationKey() {
|
|
1080
|
+
_speechPreparationKey(item = {}) {
|
|
1081
1081
|
const { engine, voice, lang, rate, outputDeviceId } = this.snapshot.settings;
|
|
1082
|
-
return JSON.stringify([engine, voice, lang, rate, outputDeviceId]);
|
|
1082
|
+
return JSON.stringify([engine, voice, item.lang ?? lang, rate, outputDeviceId]);
|
|
1083
1083
|
}
|
|
1084
1084
|
_resetStaleSpeechItem(item, key) {
|
|
1085
1085
|
if (item.preparationKey === key) return;
|
|
@@ -1100,14 +1100,14 @@ export class VoiceCoordinator {
|
|
|
1100
1100
|
lang: this.snapshot.settings.lang,
|
|
1101
1101
|
outputDeviceId: this.snapshot.settings.outputDeviceId,
|
|
1102
1102
|
};
|
|
1103
|
-
const key = this._speechPreparationKey();
|
|
1104
1103
|
for (const item of this.queue.slice(0, SPEECH_QUEUE_PREFETCH_LIMIT)) {
|
|
1104
|
+
const key = this._speechPreparationKey(item);
|
|
1105
1105
|
this._resetStaleSpeechItem(item, key);
|
|
1106
1106
|
if (item.prepared || item.preparing) continue;
|
|
1107
1107
|
this.prefetchItems.add(item);
|
|
1108
1108
|
const signal = this.prefetchController.signal;
|
|
1109
1109
|
item.preparing = engine
|
|
1110
|
-
.prepare(item.text, { ...options, signal })
|
|
1110
|
+
.prepare(item.text, { ...options, lang: item.lang ?? options.lang, signal })
|
|
1111
1111
|
.then((prepared) => {
|
|
1112
1112
|
if (item.preparationKey === key) item.preparing = null;
|
|
1113
1113
|
if (
|
|
@@ -1542,10 +1542,10 @@ export class VoiceCoordinator {
|
|
|
1542
1542
|
this._syncSpeechSegments();
|
|
1543
1543
|
return this.play(first, messageId, true);
|
|
1544
1544
|
}
|
|
1545
|
-
async play(text, messageId, manual = false) {
|
|
1545
|
+
async play(text, messageId, manual = false, speechOptions = {}) {
|
|
1546
1546
|
if (this.disposed || !text.trim()) return;
|
|
1547
1547
|
if (this.snapshot.speaking) {
|
|
1548
|
-
this.queue.push({ text, id: messageId, manual });
|
|
1548
|
+
this.queue.push({ text, id: messageId, manual, lang: speechOptions.lang });
|
|
1549
1549
|
this._prefetchSpeech();
|
|
1550
1550
|
this._syncSpeechSegments();
|
|
1551
1551
|
return;
|
|
@@ -1553,7 +1553,7 @@ export class VoiceCoordinator {
|
|
|
1553
1553
|
const item =
|
|
1554
1554
|
this.activeSpeechItem?.text === text
|
|
1555
1555
|
? this.activeSpeechItem
|
|
1556
|
-
: { text, id: messageId, manual };
|
|
1556
|
+
: { text, id: messageId, manual, lang: speechOptions.lang };
|
|
1557
1557
|
if (!this.speechHistory.includes(item)) this.speechHistory.push(item);
|
|
1558
1558
|
this.speechCursor = this.speechHistory.indexOf(item);
|
|
1559
1559
|
const epoch = ++this.speechEpoch;
|
|
@@ -1572,7 +1572,7 @@ export class VoiceCoordinator {
|
|
|
1572
1572
|
let playbackStarted = false;
|
|
1573
1573
|
let playbackFinished = false;
|
|
1574
1574
|
const engine = this.engines[this.snapshot.settings.engine];
|
|
1575
|
-
this._resetStaleSpeechItem(item, this._speechPreparationKey());
|
|
1575
|
+
this._resetStaleSpeechItem(item, this._speechPreparationKey(item));
|
|
1576
1576
|
item.engine = engine;
|
|
1577
1577
|
if (!engine) {
|
|
1578
1578
|
this.queue.unshift(item);
|
|
@@ -1635,6 +1635,7 @@ export class VoiceCoordinator {
|
|
|
1635
1635
|
this.patch({ speechLoading: false });
|
|
1636
1636
|
}
|
|
1637
1637
|
},
|
|
1638
|
+
lang: item.lang ?? this.snapshot.settings.lang,
|
|
1638
1639
|
voice: this.snapshot.settings.voice || undefined,
|
|
1639
1640
|
rate: this.snapshot.settings.rate,
|
|
1640
1641
|
volume: this.snapshot.settings.volume,
|