@mingxy/ocosay 1.1.32 → 1.2.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/dist/core/backends/index.js +41 -17
- package/dist/core/backends/playsound-backend.d.ts +1 -1
- package/dist/core/backends/playsound-backend.js +85 -51
- package/dist/core/backends/speaker-backend.js +2 -1
- package/dist/core/notification.d.ts +7 -1
- package/dist/core/notification.js +64 -11
- package/dist/core/player.js +2 -5
- package/dist/core/stream-player.d.ts +3 -2
- package/dist/core/stream-player.js +21 -9
- package/dist/index.js +2 -2
- package/dist/package.json +1 -1
- package/dist/plugin.js +311 -153
- package/dist/providers/minimax.d.ts +0 -1
- package/dist/providers/minimax.js +0 -1
- package/dist/services/notification-service.d.ts +13 -4
- package/dist/services/notification-service.js +61 -11
- package/dist/services/speaker-service.d.ts +1 -2
- package/dist/services/speaker-service.js +1 -3
- package/dist/services/streaming-service.js +3 -3
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -7471,6 +7471,8 @@ import { execFile as execFile3 } from "child_process";
|
|
|
7471
7471
|
import { tmpdir as tmpdir4 } from "os";
|
|
7472
7472
|
import { join as join5 } from "path";
|
|
7473
7473
|
import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync4, existsSync as existsSync5 } from "fs";
|
|
7474
|
+
import { promisify } from "util";
|
|
7475
|
+
var execFileAsync = promisify(execFile3);
|
|
7474
7476
|
var SAFE_PATH_REGEX4 = /^[\w\/\.]+$/;
|
|
7475
7477
|
var PlaySoundBackend = class {
|
|
7476
7478
|
name = "play-sound";
|
|
@@ -7487,7 +7489,7 @@ var PlaySoundBackend = class {
|
|
|
7487
7489
|
constructor(options = {}) {
|
|
7488
7490
|
this.events = options.events;
|
|
7489
7491
|
}
|
|
7490
|
-
start(filePath) {
|
|
7492
|
+
async start(filePath) {
|
|
7491
7493
|
if (this._started) return;
|
|
7492
7494
|
if (!SAFE_PATH_REGEX4.test(filePath)) {
|
|
7493
7495
|
throw new Error(`Invalid file path: ${filePath}`);
|
|
@@ -7496,57 +7498,86 @@ var PlaySoundBackend = class {
|
|
|
7496
7498
|
this._started = true;
|
|
7497
7499
|
this._stopped = false;
|
|
7498
7500
|
this.events?.onStart?.();
|
|
7499
|
-
this.playWithPlaySound(filePath);
|
|
7500
|
-
}
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7501
|
+
await this.playWithPlaySound(filePath);
|
|
7502
|
+
}
|
|
7503
|
+
playWithPlaySound(filePath) {
|
|
7504
|
+
return new Promise((resolve, reject) => {
|
|
7505
|
+
import("play-sound").then((playModule) => {
|
|
7506
|
+
const play = playModule.default;
|
|
7507
|
+
if (process.platform === "linux") {
|
|
7508
|
+
execFileAsync("ffplay", [
|
|
7509
|
+
"-nodisp",
|
|
7510
|
+
// 不显示窗口
|
|
7511
|
+
"-autoexit",
|
|
7512
|
+
// 播放完自动退出
|
|
7513
|
+
"-loglevel",
|
|
7514
|
+
"error",
|
|
7515
|
+
// 减少日志输出
|
|
7516
|
+
filePath
|
|
7517
|
+
]).then(() => {
|
|
7518
|
+
if (this._stopped) {
|
|
7519
|
+
resolve();
|
|
7520
|
+
return;
|
|
7521
|
+
}
|
|
7522
|
+
this._started = false;
|
|
7523
|
+
this.events?.onEnd?.();
|
|
7524
|
+
resolve();
|
|
7525
|
+
}).catch((error) => {
|
|
7526
|
+
if (this._stopped) {
|
|
7527
|
+
resolve();
|
|
7528
|
+
return;
|
|
7529
|
+
}
|
|
7530
|
+
this._started = false;
|
|
7521
7531
|
this.handleError(error);
|
|
7522
|
-
|
|
7523
|
-
}
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7532
|
+
reject(error);
|
|
7533
|
+
});
|
|
7534
|
+
} else {
|
|
7535
|
+
const playerSound = play;
|
|
7536
|
+
const p = playerSound.play(filePath);
|
|
7537
|
+
if (p && p.then) {
|
|
7538
|
+
p.then(() => {
|
|
7539
|
+
if (this._stopped) {
|
|
7540
|
+
resolve();
|
|
7541
|
+
return;
|
|
7542
|
+
}
|
|
7543
|
+
this._started = false;
|
|
7544
|
+
this.events?.onEnd?.();
|
|
7545
|
+
resolve();
|
|
7546
|
+
}).catch((err) => {
|
|
7547
|
+
if (this._stopped) {
|
|
7548
|
+
resolve();
|
|
7549
|
+
return;
|
|
7550
|
+
}
|
|
7551
|
+
this._started = false;
|
|
7552
|
+
this.handleError(err);
|
|
7553
|
+
reject(err);
|
|
7554
|
+
});
|
|
7555
|
+
} else if (p && p.kill) {
|
|
7556
|
+
this.player = p;
|
|
7557
|
+
p.on("error", (error) => {
|
|
7558
|
+
this.handleError(error);
|
|
7559
|
+
reject(error);
|
|
7560
|
+
});
|
|
7561
|
+
p.on("close", () => {
|
|
7562
|
+
if (this._stopped) {
|
|
7563
|
+
resolve();
|
|
7564
|
+
return;
|
|
7565
|
+
}
|
|
7566
|
+
this._started = false;
|
|
7567
|
+
this.events?.onEnd?.();
|
|
7568
|
+
resolve();
|
|
7569
|
+
});
|
|
7570
|
+
} else {
|
|
7571
|
+
resolve();
|
|
7534
7572
|
}
|
|
7535
|
-
this._started = false;
|
|
7536
|
-
this.events?.onEnd?.();
|
|
7537
|
-
});
|
|
7538
|
-
if (p && p.kill) {
|
|
7539
|
-
this.player = p;
|
|
7540
7573
|
}
|
|
7541
|
-
}
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
}
|
|
7547
|
-
}
|
|
7548
|
-
this.handleError(err instanceof Error ? err : new Error(String(err)));
|
|
7549
|
-
}
|
|
7574
|
+
}).catch((err) => {
|
|
7575
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
7576
|
+
error.message = `play-sound load failed: ${error.message}`;
|
|
7577
|
+
this.handleError(error);
|
|
7578
|
+
reject(error);
|
|
7579
|
+
});
|
|
7580
|
+
});
|
|
7550
7581
|
}
|
|
7551
7582
|
write(chunk) {
|
|
7552
7583
|
if (this._stopped) return;
|
|
@@ -7694,7 +7725,7 @@ var SpeakerBackend = class {
|
|
|
7694
7725
|
return false;
|
|
7695
7726
|
}
|
|
7696
7727
|
stripWavHeader(chunk) {
|
|
7697
|
-
return chunk.
|
|
7728
|
+
return chunk.subarray(44);
|
|
7698
7729
|
}
|
|
7699
7730
|
createSpeaker() {
|
|
7700
7731
|
try {
|
|
@@ -7767,6 +7798,130 @@ var SpeakerBackend = class {
|
|
|
7767
7798
|
|
|
7768
7799
|
// src/core/backends/index.ts
|
|
7769
7800
|
import { execSync } from "child_process";
|
|
7801
|
+
|
|
7802
|
+
// src/core/notification.ts
|
|
7803
|
+
var logger3 = createModuleLogger("NotificationService");
|
|
7804
|
+
var SISYPHUS_SPINNER = ["\xB7", "\u2022", "\u25CF", "\u25CB", "\u25CC", "\u25E6", " "];
|
|
7805
|
+
var NotificationService = class {
|
|
7806
|
+
tui = null;
|
|
7807
|
+
pendingToasts = [];
|
|
7808
|
+
retryTimer;
|
|
7809
|
+
setTui(tui) {
|
|
7810
|
+
this.tui = tui;
|
|
7811
|
+
logger3.debug("tui reference set");
|
|
7812
|
+
this.flushPending();
|
|
7813
|
+
}
|
|
7814
|
+
showToast(options) {
|
|
7815
|
+
const title = options.title || this.getTitleForVariant(options.variant || "info");
|
|
7816
|
+
const { message, variant = "info", duration = 5e3 } = options;
|
|
7817
|
+
if (!this.tui) {
|
|
7818
|
+
logger3.debug({ title }, "tui not ready, queueing toast");
|
|
7819
|
+
this.pendingToasts.push({ ...options, title });
|
|
7820
|
+
this.scheduleRetry();
|
|
7821
|
+
return false;
|
|
7822
|
+
}
|
|
7823
|
+
try {
|
|
7824
|
+
this.tui.showToast({
|
|
7825
|
+
body: {
|
|
7826
|
+
title,
|
|
7827
|
+
message,
|
|
7828
|
+
variant,
|
|
7829
|
+
duration
|
|
7830
|
+
}
|
|
7831
|
+
});
|
|
7832
|
+
logger3.debug({ title, variant }, "toast shown");
|
|
7833
|
+
return true;
|
|
7834
|
+
} catch (err) {
|
|
7835
|
+
logger3.warn({ err, title }, "toast call failed, queueing for retry");
|
|
7836
|
+
this.pendingToasts.push({ ...options, title });
|
|
7837
|
+
this.scheduleRetry();
|
|
7838
|
+
return false;
|
|
7839
|
+
}
|
|
7840
|
+
}
|
|
7841
|
+
scheduleRetry() {
|
|
7842
|
+
if (this.retryTimer) return;
|
|
7843
|
+
this.retryTimer = setTimeout(() => {
|
|
7844
|
+
this.retryTimer = void 0;
|
|
7845
|
+
this.flushPending();
|
|
7846
|
+
}, 2e3);
|
|
7847
|
+
}
|
|
7848
|
+
flushPending() {
|
|
7849
|
+
if (this.pendingToasts.length === 0 || !this.tui) return;
|
|
7850
|
+
logger3.info({ count: this.pendingToasts.length }, "flushing pending toasts");
|
|
7851
|
+
const pending = [...this.pendingToasts];
|
|
7852
|
+
this.pendingToasts = [];
|
|
7853
|
+
for (const toast of pending) {
|
|
7854
|
+
try {
|
|
7855
|
+
this.showToast(toast);
|
|
7856
|
+
} catch (err) {
|
|
7857
|
+
this.pendingToasts.push(toast);
|
|
7858
|
+
logger3.warn({ err }, "showToast threw unexpected error, re-queued");
|
|
7859
|
+
}
|
|
7860
|
+
}
|
|
7861
|
+
}
|
|
7862
|
+
success(titleOrMessage, messageOrDuration, duration) {
|
|
7863
|
+
if (typeof messageOrDuration === "string") {
|
|
7864
|
+
return this.showToast({ title: titleOrMessage, message: messageOrDuration, variant: "success", duration });
|
|
7865
|
+
}
|
|
7866
|
+
return this.showToast({ message: titleOrMessage, variant: "success", duration: messageOrDuration });
|
|
7867
|
+
}
|
|
7868
|
+
error(titleOrMessage, messageOrDuration, duration) {
|
|
7869
|
+
if (typeof messageOrDuration === "string") {
|
|
7870
|
+
return this.showToast({ title: titleOrMessage, message: messageOrDuration, variant: "error", duration });
|
|
7871
|
+
}
|
|
7872
|
+
return this.showToast({ message: titleOrMessage, variant: "error", duration: messageOrDuration || 8e3 });
|
|
7873
|
+
}
|
|
7874
|
+
info(titleOrMessage, messageOrDuration, duration) {
|
|
7875
|
+
if (typeof messageOrDuration === "string") {
|
|
7876
|
+
return this.showToast({ title: titleOrMessage, message: messageOrDuration, variant: "info", duration });
|
|
7877
|
+
}
|
|
7878
|
+
return this.showToast({ message: titleOrMessage, variant: "info", duration: messageOrDuration });
|
|
7879
|
+
}
|
|
7880
|
+
warning(titleOrMessage, messageOrDuration, duration) {
|
|
7881
|
+
if (typeof messageOrDuration === "string") {
|
|
7882
|
+
return this.showToast({ title: titleOrMessage, message: messageOrDuration, variant: "warning", duration });
|
|
7883
|
+
}
|
|
7884
|
+
return this.showToast({ message: titleOrMessage, variant: "warning", duration: messageOrDuration });
|
|
7885
|
+
}
|
|
7886
|
+
async showSpinnerToast(title, message, duration = 2e3) {
|
|
7887
|
+
const frameInterval = 100;
|
|
7888
|
+
const totalFrames = Math.ceil(duration / frameInterval);
|
|
7889
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
7890
|
+
const spinner = SISYPHUS_SPINNER[i % SISYPHUS_SPINNER.length];
|
|
7891
|
+
const toastDuration = Math.min(frameInterval + 50, duration - i * frameInterval);
|
|
7892
|
+
if (toastDuration <= 0) break;
|
|
7893
|
+
if (this.tui?.showToast) {
|
|
7894
|
+
try {
|
|
7895
|
+
await this.tui.showToast({
|
|
7896
|
+
body: {
|
|
7897
|
+
title: `${spinner} ${title}`,
|
|
7898
|
+
message,
|
|
7899
|
+
variant: "info",
|
|
7900
|
+
duration: toastDuration
|
|
7901
|
+
}
|
|
7902
|
+
});
|
|
7903
|
+
} catch (err) {
|
|
7904
|
+
logger3.warn({ err }, "showSpinnerToast failed");
|
|
7905
|
+
}
|
|
7906
|
+
}
|
|
7907
|
+
if (i < totalFrames - 1) {
|
|
7908
|
+
await new Promise((resolve) => setTimeout(resolve, frameInterval));
|
|
7909
|
+
}
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
7912
|
+
getTitleForVariant(variant) {
|
|
7913
|
+
const titles = {
|
|
7914
|
+
success: "Success",
|
|
7915
|
+
error: "Error",
|
|
7916
|
+
warning: "Warning",
|
|
7917
|
+
info: "Info"
|
|
7918
|
+
};
|
|
7919
|
+
return titles[variant];
|
|
7920
|
+
}
|
|
7921
|
+
};
|
|
7922
|
+
var notificationService = new NotificationService();
|
|
7923
|
+
|
|
7924
|
+
// src/core/backends/index.ts
|
|
7770
7925
|
function execCmd(cmd) {
|
|
7771
7926
|
try {
|
|
7772
7927
|
const output = execSync(cmd, { stdio: "pipe", encoding: "utf8" });
|
|
@@ -7796,12 +7951,21 @@ function isSpeakerAvailable() {
|
|
|
7796
7951
|
return false;
|
|
7797
7952
|
}
|
|
7798
7953
|
}
|
|
7954
|
+
function isWSL() {
|
|
7955
|
+
if (process.platform !== "linux") return false;
|
|
7956
|
+
try {
|
|
7957
|
+
const output = execSync("uname -r", { stdio: "pipe", encoding: "utf8" });
|
|
7958
|
+
return output.toLowerCase().includes("microsoft");
|
|
7959
|
+
} catch {
|
|
7960
|
+
return false;
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7799
7963
|
function createBackend(type = "auto" /* AUTO */, options = {}) {
|
|
7800
7964
|
const platform = process.platform;
|
|
7801
7965
|
if (type !== "auto" /* AUTO */) {
|
|
7802
7966
|
return createBackendByType(type, options);
|
|
7803
7967
|
}
|
|
7804
|
-
if (isNaudiodonAvailable()) {
|
|
7968
|
+
if (!isWSL() && isNaudiodonAvailable()) {
|
|
7805
7969
|
try {
|
|
7806
7970
|
const naudiodon = __require("naudiodon");
|
|
7807
7971
|
if (naudiodon) {
|
|
@@ -7813,12 +7977,35 @@ function createBackend(type = "auto" /* AUTO */, options = {}) {
|
|
|
7813
7977
|
}
|
|
7814
7978
|
} catch (err) {
|
|
7815
7979
|
logger.error({ err }, "failed to initialize naudiodon backend");
|
|
7980
|
+
notificationService.warning(
|
|
7981
|
+
"naudiodon \u521D\u59CB\u5316\u5931\u8D25",
|
|
7982
|
+
"\u5C06\u4F7F\u7528\u5176\u4ED6\u97F3\u9891\u540E\u7AEF",
|
|
7983
|
+
5e3
|
|
7984
|
+
);
|
|
7816
7985
|
}
|
|
7817
7986
|
}
|
|
7818
7987
|
switch (platform) {
|
|
7819
7988
|
case "darwin":
|
|
7820
7989
|
return new AfplayBackend(options);
|
|
7821
|
-
case "linux":
|
|
7990
|
+
case "linux": {
|
|
7991
|
+
const wsl = isWSL();
|
|
7992
|
+
if (wsl) {
|
|
7993
|
+
logger.debug("Running on WSL, skipping naudiodon (may not work with Windows audio)");
|
|
7994
|
+
}
|
|
7995
|
+
if (!wsl && isNaudiodonAvailable()) {
|
|
7996
|
+
try {
|
|
7997
|
+
const naudiodon = __require("naudiodon");
|
|
7998
|
+
if (naudiodon) {
|
|
7999
|
+
const devices = naudiodon.getDevices();
|
|
8000
|
+
if (devices && devices.length > 0) {
|
|
8001
|
+
return new NaudiodonBackend(options);
|
|
8002
|
+
}
|
|
8003
|
+
logger.debug("naudiodon has no audio devices, skipping");
|
|
8004
|
+
}
|
|
8005
|
+
} catch (err) {
|
|
8006
|
+
logger.error({ err }, "failed to initialize naudiodon backend");
|
|
8007
|
+
}
|
|
8008
|
+
}
|
|
7822
8009
|
if (isCommandAvailable("aplay")) {
|
|
7823
8010
|
const test = execCmd("aplay -l");
|
|
7824
8011
|
if (test.success && !test.output.includes("no soundcards")) {
|
|
@@ -7833,6 +8020,7 @@ function createBackend(type = "auto" /* AUTO */, options = {}) {
|
|
|
7833
8020
|
}
|
|
7834
8021
|
logger.warn("All Linux audio backends failed, using HowlerBackend as fallback");
|
|
7835
8022
|
return new HowlerBackend(options);
|
|
8023
|
+
}
|
|
7836
8024
|
case "win32":
|
|
7837
8025
|
return new PowerShellBackend(options);
|
|
7838
8026
|
default:
|
|
@@ -7966,11 +8154,8 @@ var AudioPlayer = class extends EventEmitter {
|
|
|
7966
8154
|
* 播放音频文件
|
|
7967
8155
|
* 使用 AudioBackend 统一后端播放
|
|
7968
8156
|
*/
|
|
7969
|
-
playFile(filePath, _format) {
|
|
7970
|
-
|
|
7971
|
-
this.backend.start(filePath);
|
|
7972
|
-
resolve();
|
|
7973
|
-
});
|
|
8157
|
+
async playFile(filePath, _format) {
|
|
8158
|
+
await Promise.resolve(this.backend.start(filePath));
|
|
7974
8159
|
}
|
|
7975
8160
|
pause() {
|
|
7976
8161
|
if (!this._playing || this._paused) return;
|
|
@@ -8016,79 +8201,6 @@ var AudioPlayer = class extends EventEmitter {
|
|
|
8016
8201
|
}
|
|
8017
8202
|
};
|
|
8018
8203
|
|
|
8019
|
-
// src/core/notification.ts
|
|
8020
|
-
var logger3 = createModuleLogger("NotificationService");
|
|
8021
|
-
var NotificationService = class {
|
|
8022
|
-
tui = null;
|
|
8023
|
-
pendingToasts = [];
|
|
8024
|
-
retryTimer;
|
|
8025
|
-
setTui(tui) {
|
|
8026
|
-
this.tui = tui;
|
|
8027
|
-
logger3.debug("tui reference set");
|
|
8028
|
-
this.flushPending();
|
|
8029
|
-
}
|
|
8030
|
-
showToast(options) {
|
|
8031
|
-
const { title, message, variant = "info", duration = 5e3 } = options;
|
|
8032
|
-
if (!this.tui) {
|
|
8033
|
-
logger3.debug({ title }, "tui not ready, queueing toast");
|
|
8034
|
-
this.pendingToasts.push(options);
|
|
8035
|
-
this.scheduleRetry();
|
|
8036
|
-
return false;
|
|
8037
|
-
}
|
|
8038
|
-
try {
|
|
8039
|
-
this.tui.showToast({
|
|
8040
|
-
body: {
|
|
8041
|
-
title,
|
|
8042
|
-
message,
|
|
8043
|
-
variant,
|
|
8044
|
-
duration
|
|
8045
|
-
}
|
|
8046
|
-
});
|
|
8047
|
-
logger3.debug({ title, variant }, "toast shown");
|
|
8048
|
-
return true;
|
|
8049
|
-
} catch (err) {
|
|
8050
|
-
logger3.warn({ err, title }, "toast call failed, queueing for retry");
|
|
8051
|
-
this.pendingToasts.push(options);
|
|
8052
|
-
this.scheduleRetry();
|
|
8053
|
-
return false;
|
|
8054
|
-
}
|
|
8055
|
-
}
|
|
8056
|
-
scheduleRetry() {
|
|
8057
|
-
if (this.retryTimer) return;
|
|
8058
|
-
this.retryTimer = setTimeout(() => {
|
|
8059
|
-
this.retryTimer = void 0;
|
|
8060
|
-
this.flushPending();
|
|
8061
|
-
}, 2e3);
|
|
8062
|
-
}
|
|
8063
|
-
flushPending() {
|
|
8064
|
-
if (this.pendingToasts.length === 0 || !this.tui) return;
|
|
8065
|
-
logger3.info({ count: this.pendingToasts.length }, "flushing pending toasts");
|
|
8066
|
-
const pending = [...this.pendingToasts];
|
|
8067
|
-
this.pendingToasts = [];
|
|
8068
|
-
for (const toast of pending) {
|
|
8069
|
-
try {
|
|
8070
|
-
this.showToast(toast);
|
|
8071
|
-
} catch (err) {
|
|
8072
|
-
this.pendingToasts.push(toast);
|
|
8073
|
-
logger3.warn({ err }, "showToast threw unexpected error, re-queued");
|
|
8074
|
-
}
|
|
8075
|
-
}
|
|
8076
|
-
}
|
|
8077
|
-
success(title, message, duration) {
|
|
8078
|
-
return this.showToast({ title, message, variant: "success", duration });
|
|
8079
|
-
}
|
|
8080
|
-
error(title, message, duration) {
|
|
8081
|
-
return this.showToast({ title, message, variant: "error", duration });
|
|
8082
|
-
}
|
|
8083
|
-
info(title, message, duration) {
|
|
8084
|
-
return this.showToast({ title, message, variant: "info", duration });
|
|
8085
|
-
}
|
|
8086
|
-
warning(title, message, duration) {
|
|
8087
|
-
return this.showToast({ title, message, variant: "warning", duration });
|
|
8088
|
-
}
|
|
8089
|
-
};
|
|
8090
|
-
var notificationService = new NotificationService();
|
|
8091
|
-
|
|
8092
8204
|
// src/core/speaker.ts
|
|
8093
8205
|
var logger4 = createModuleLogger("Speaker");
|
|
8094
8206
|
var Speaker2 = class extends EventEmitter2 {
|
|
@@ -8294,12 +8406,10 @@ async function listVoices(providerName) {
|
|
|
8294
8406
|
|
|
8295
8407
|
// src/services/speaker-service.ts
|
|
8296
8408
|
var SpeakerService = class {
|
|
8297
|
-
|
|
8298
|
-
|
|
8409
|
+
speaker;
|
|
8410
|
+
constructor(_options = {}) {
|
|
8299
8411
|
this.speaker = getDefaultSpeaker();
|
|
8300
8412
|
}
|
|
8301
|
-
options;
|
|
8302
|
-
speaker;
|
|
8303
8413
|
async speak(text, options) {
|
|
8304
8414
|
const timestamp = this.getTimestamp();
|
|
8305
8415
|
logger.info(`[Ocosay][${timestamp}][INFO][Speaker] \u5BF9\u5E94\u4E8B\u4EF6{\u64AD\u653E\u5F00\u59CB} - \u6587\u672C\u957F\u5EA6: ${text.length}`);
|
|
@@ -8375,13 +8485,14 @@ var StreamPlayer = class extends EventEmitter3 {
|
|
|
8375
8485
|
_started = false;
|
|
8376
8486
|
_paused = false;
|
|
8377
8487
|
_stopped = false;
|
|
8488
|
+
_starting = false;
|
|
8378
8489
|
format = "mp3";
|
|
8379
8490
|
events;
|
|
8380
8491
|
constructor(options = {}) {
|
|
8381
8492
|
super();
|
|
8382
8493
|
this.format = options.format || "mp3";
|
|
8383
8494
|
this.events = options.events;
|
|
8384
|
-
const backendType = options.backendType || "
|
|
8495
|
+
const backendType = options.backendType || "auto" /* AUTO */;
|
|
8385
8496
|
this.backend = createBackend(backendType, {
|
|
8386
8497
|
format: this.format,
|
|
8387
8498
|
events: {
|
|
@@ -8421,7 +8532,7 @@ var StreamPlayer = class extends EventEmitter3 {
|
|
|
8421
8532
|
* 开始播放
|
|
8422
8533
|
* 初始化后端,准备接收音频数据
|
|
8423
8534
|
*/
|
|
8424
|
-
start() {
|
|
8535
|
+
async start() {
|
|
8425
8536
|
if (this._started) {
|
|
8426
8537
|
return;
|
|
8427
8538
|
}
|
|
@@ -8429,7 +8540,7 @@ var StreamPlayer = class extends EventEmitter3 {
|
|
|
8429
8540
|
this.handleError(new Error("Audio backend not initialized"));
|
|
8430
8541
|
return;
|
|
8431
8542
|
}
|
|
8432
|
-
this.backend.start("");
|
|
8543
|
+
await Promise.resolve(this.backend.start(""));
|
|
8433
8544
|
this._started = true;
|
|
8434
8545
|
this._stopped = false;
|
|
8435
8546
|
this._paused = false;
|
|
@@ -8439,15 +8550,23 @@ var StreamPlayer = class extends EventEmitter3 {
|
|
|
8439
8550
|
* 写入音频数据块(边收边播)
|
|
8440
8551
|
* 如果尚未 start(),会自动调用
|
|
8441
8552
|
*/
|
|
8442
|
-
write(chunk) {
|
|
8553
|
+
async write(chunk) {
|
|
8443
8554
|
if (this._stopped) {
|
|
8444
8555
|
return;
|
|
8445
8556
|
}
|
|
8446
8557
|
if (!this._started) {
|
|
8447
|
-
this.
|
|
8558
|
+
while (this._starting) {
|
|
8559
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
8560
|
+
}
|
|
8561
|
+
this._starting = true;
|
|
8562
|
+
try {
|
|
8563
|
+
await this.start();
|
|
8564
|
+
} finally {
|
|
8565
|
+
this._starting = false;
|
|
8566
|
+
}
|
|
8448
8567
|
}
|
|
8449
8568
|
if (this.backend) {
|
|
8450
|
-
this.backend.write(chunk);
|
|
8569
|
+
await Promise.resolve(this.backend.write(chunk));
|
|
8451
8570
|
this._bytesWritten += chunk.length;
|
|
8452
8571
|
this.events?.onProgress?.(this._bytesWritten);
|
|
8453
8572
|
this.emit("progress", this._bytesWritten);
|
|
@@ -8637,7 +8756,7 @@ var StreamingService = class extends EventEmitter4 {
|
|
|
8637
8756
|
);
|
|
8638
8757
|
}
|
|
8639
8758
|
const player = this.initPlayer();
|
|
8640
|
-
player.start();
|
|
8759
|
+
await player.start();
|
|
8641
8760
|
this._isActive = true;
|
|
8642
8761
|
this._bytesWritten = 0;
|
|
8643
8762
|
const result = await provider.speak(text, {
|
|
@@ -8663,7 +8782,7 @@ var StreamingService = class extends EventEmitter4 {
|
|
|
8663
8782
|
if (result.isStream && result.audioData instanceof ReadableStream) {
|
|
8664
8783
|
await this.streamAudioChunks(result.audioData, player);
|
|
8665
8784
|
} else if (Buffer.isBuffer(result.audioData)) {
|
|
8666
|
-
player.write(result.audioData);
|
|
8785
|
+
await player.write(result.audioData);
|
|
8667
8786
|
player.end();
|
|
8668
8787
|
}
|
|
8669
8788
|
}
|
|
@@ -8680,7 +8799,7 @@ var StreamingService = class extends EventEmitter4 {
|
|
|
8680
8799
|
}
|
|
8681
8800
|
if (value) {
|
|
8682
8801
|
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
8683
|
-
player.write(chunk);
|
|
8802
|
+
await player.write(chunk);
|
|
8684
8803
|
}
|
|
8685
8804
|
}
|
|
8686
8805
|
} finally {
|
|
@@ -8942,7 +9061,6 @@ var MiniMaxProvider = class extends BaseTTSProvider {
|
|
|
8942
9061
|
config;
|
|
8943
9062
|
httpClient;
|
|
8944
9063
|
wsConnection;
|
|
8945
|
-
currentAudioData = [];
|
|
8946
9064
|
audioFormat = "mp3";
|
|
8947
9065
|
constructor(config) {
|
|
8948
9066
|
super();
|
|
@@ -9668,7 +9786,7 @@ function initializeStreamComponents(config) {
|
|
|
9668
9786
|
},
|
|
9669
9787
|
onEnd: () => {
|
|
9670
9788
|
},
|
|
9671
|
-
onProgress: (
|
|
9789
|
+
onProgress: (_bytesWritten) => {
|
|
9672
9790
|
},
|
|
9673
9791
|
onError: (error) => logger.error({ error }, "stream player error"),
|
|
9674
9792
|
onStop: () => {
|
|
@@ -10119,15 +10237,15 @@ async function ensureSpeakerInstalledAsync() {
|
|
|
10119
10237
|
await ensurePlaySoundInstalled();
|
|
10120
10238
|
}
|
|
10121
10239
|
async function initAsync() {
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
}, 100);
|
|
10240
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
10241
|
+
await ensureSpeakerInstalledAsync();
|
|
10125
10242
|
}
|
|
10126
10243
|
async function ensurePlaySoundInstalled() {
|
|
10127
10244
|
const dep = "play-sound";
|
|
10128
10245
|
if (isModuleInstalled(dep)) {
|
|
10129
10246
|
logger8.info("play-sound already installed");
|
|
10130
10247
|
if (await verifyModuleLoad(dep)) {
|
|
10248
|
+
notificationService.success("play-sound \u5DF2\u5C31\u7EEA", "\u97F3\u9891\u540E\u7AEF\u6B63\u5E38", 5e3);
|
|
10131
10249
|
return;
|
|
10132
10250
|
}
|
|
10133
10251
|
}
|
|
@@ -10429,6 +10547,45 @@ var server = (async (input, _options) => {
|
|
|
10429
10547
|
global.__opencode_tui__ = opencodeTui;
|
|
10430
10548
|
notificationService.setTui(opencodeTui);
|
|
10431
10549
|
const config = loadOrCreateConfig();
|
|
10550
|
+
function collectEnvironmentInfo() {
|
|
10551
|
+
const lines = [];
|
|
10552
|
+
lines.push(`Auto-read: ${config.autoRead ? "ON" : "OFF"}`);
|
|
10553
|
+
let naudiodonOk = false;
|
|
10554
|
+
try {
|
|
10555
|
+
pluginRequire.resolve("naudiodon");
|
|
10556
|
+
naudiodonOk = true;
|
|
10557
|
+
} catch {
|
|
10558
|
+
naudiodonOk = false;
|
|
10559
|
+
}
|
|
10560
|
+
const playSoundOk = isModuleInstalled("play-sound");
|
|
10561
|
+
const platform = process.platform;
|
|
10562
|
+
let backend = "unknown";
|
|
10563
|
+
if (naudiodonOk) {
|
|
10564
|
+
backend = "naudiodon";
|
|
10565
|
+
} else if (platform === "linux") {
|
|
10566
|
+
if (checkAlsa()) {
|
|
10567
|
+
backend = "aplay";
|
|
10568
|
+
} else if (checkFFplay()) {
|
|
10569
|
+
backend = "ffplay";
|
|
10570
|
+
} else {
|
|
10571
|
+
backend = "howler";
|
|
10572
|
+
}
|
|
10573
|
+
} else if (platform === "darwin") {
|
|
10574
|
+
backend = "afplay";
|
|
10575
|
+
} else if (platform === "win32") {
|
|
10576
|
+
backend = "powershell";
|
|
10577
|
+
} else {
|
|
10578
|
+
backend = "howler";
|
|
10579
|
+
}
|
|
10580
|
+
const ffplayOk = checkFFplay();
|
|
10581
|
+
const alsaOk = checkAlsa();
|
|
10582
|
+
lines.push(`naudiodon: ${naudiodonOk ? "\u2713" : "\u2717"}`);
|
|
10583
|
+
lines.push(`play-sound: ${playSoundOk ? "\u2713" : "\u2717"}`);
|
|
10584
|
+
lines.push(`backend: ${backend}`);
|
|
10585
|
+
lines.push(`ffplay: ${ffplayOk ? "\u2713" : "\u2717"}`);
|
|
10586
|
+
lines.push(`alsa: ${alsaOk ? "\u2713" : "\u2717"}`);
|
|
10587
|
+
return lines.join("\n");
|
|
10588
|
+
}
|
|
10432
10589
|
try {
|
|
10433
10590
|
await initialize({
|
|
10434
10591
|
autoRead: config.autoRead,
|
|
@@ -10444,21 +10601,22 @@ var server = (async (input, _options) => {
|
|
|
10444
10601
|
initError = err instanceof Error ? err : new Error(String(err));
|
|
10445
10602
|
logger8.error({ error: initError }, "initialization failed");
|
|
10446
10603
|
}
|
|
10447
|
-
initAsync();
|
|
10604
|
+
await initAsync();
|
|
10448
10605
|
await ensureNaudiodonCompiled();
|
|
10449
10606
|
await ensureOptionalDepsInstalled();
|
|
10450
10607
|
await checkAudioEnvironmentForBackend();
|
|
10451
10608
|
setTimeout(() => {
|
|
10609
|
+
const envInfo = collectEnvironmentInfo();
|
|
10452
10610
|
if (initError) {
|
|
10453
10611
|
notificationService.error(
|
|
10454
|
-
`Ocosay v${pluginVersion} Init Failed
|
|
10455
|
-
|
|
10612
|
+
`Ocosay v${pluginVersion} Init Failed
|
|
10613
|
+
Please check your config file`,
|
|
10456
10614
|
8e3
|
|
10457
10615
|
);
|
|
10458
10616
|
} else {
|
|
10459
|
-
notificationService.
|
|
10617
|
+
notificationService.showSpinnerToast(
|
|
10460
10618
|
`Ocosay v${pluginVersion} Ready`,
|
|
10461
|
-
|
|
10619
|
+
envInfo,
|
|
10462
10620
|
5e3
|
|
10463
10621
|
);
|
|
10464
10622
|
}
|