@mingxy/ocosay 1.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.
Files changed (126) hide show
  1. package/README.md +556 -0
  2. package/TECH_PLAN.md +352 -0
  3. package/__mocks__/@opencode-ai/plugin.ts +32 -0
  4. package/dist/config.d.ts +26 -0
  5. package/dist/config.d.ts.map +1 -0
  6. package/dist/config.js +95 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/core/backends/afplay-backend.d.ts +33 -0
  9. package/dist/core/backends/afplay-backend.d.ts.map +1 -0
  10. package/dist/core/backends/afplay-backend.js +144 -0
  11. package/dist/core/backends/afplay-backend.js.map +1 -0
  12. package/dist/core/backends/aplay-backend.d.ts +33 -0
  13. package/dist/core/backends/aplay-backend.d.ts.map +1 -0
  14. package/dist/core/backends/aplay-backend.js +142 -0
  15. package/dist/core/backends/aplay-backend.js.map +1 -0
  16. package/dist/core/backends/base.d.ts +94 -0
  17. package/dist/core/backends/base.d.ts.map +1 -0
  18. package/dist/core/backends/base.js +6 -0
  19. package/dist/core/backends/base.js.map +1 -0
  20. package/dist/core/backends/index.d.ts +29 -0
  21. package/dist/core/backends/index.d.ts.map +1 -0
  22. package/dist/core/backends/index.js +114 -0
  23. package/dist/core/backends/index.js.map +1 -0
  24. package/dist/core/backends/naudiodon-backend.d.ts +52 -0
  25. package/dist/core/backends/naudiodon-backend.d.ts.map +1 -0
  26. package/dist/core/backends/naudiodon-backend.js +123 -0
  27. package/dist/core/backends/naudiodon-backend.js.map +1 -0
  28. package/dist/core/backends/powershell-backend.d.ts +34 -0
  29. package/dist/core/backends/powershell-backend.d.ts.map +1 -0
  30. package/dist/core/backends/powershell-backend.js +154 -0
  31. package/dist/core/backends/powershell-backend.js.map +1 -0
  32. package/dist/core/player.d.ts +97 -0
  33. package/dist/core/player.d.ts.map +1 -0
  34. package/dist/core/player.js +268 -0
  35. package/dist/core/player.js.map +1 -0
  36. package/dist/core/speaker.d.ts +97 -0
  37. package/dist/core/speaker.d.ts.map +1 -0
  38. package/dist/core/speaker.js +218 -0
  39. package/dist/core/speaker.js.map +1 -0
  40. package/dist/core/stream-player.d.ts +107 -0
  41. package/dist/core/stream-player.d.ts.map +1 -0
  42. package/dist/core/stream-player.js +272 -0
  43. package/dist/core/stream-player.js.map +1 -0
  44. package/dist/core/stream-reader.d.ts +86 -0
  45. package/dist/core/stream-reader.d.ts.map +1 -0
  46. package/dist/core/stream-reader.js +172 -0
  47. package/dist/core/stream-reader.js.map +1 -0
  48. package/dist/core/streaming-synthesizer.d.ts +51 -0
  49. package/dist/core/streaming-synthesizer.d.ts.map +1 -0
  50. package/dist/core/streaming-synthesizer.js +103 -0
  51. package/dist/core/streaming-synthesizer.js.map +1 -0
  52. package/dist/core/types.d.ts +141 -0
  53. package/dist/core/types.d.ts.map +1 -0
  54. package/dist/core/types.js +37 -0
  55. package/dist/core/types.js.map +1 -0
  56. package/dist/index.d.ts +40 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +179 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/plugin.d.ts +4 -0
  61. package/dist/plugin.d.ts.map +1 -0
  62. package/dist/plugin.js +151 -0
  63. package/dist/plugin.js.map +1 -0
  64. package/dist/providers/base.d.ts +55 -0
  65. package/dist/providers/base.d.ts.map +1 -0
  66. package/dist/providers/base.js +95 -0
  67. package/dist/providers/base.js.map +1 -0
  68. package/dist/providers/minimax.d.ts +84 -0
  69. package/dist/providers/minimax.d.ts.map +1 -0
  70. package/dist/providers/minimax.js +387 -0
  71. package/dist/providers/minimax.js.map +1 -0
  72. package/dist/tools/tts.d.ts +147 -0
  73. package/dist/tools/tts.d.ts.map +1 -0
  74. package/dist/tools/tts.js +232 -0
  75. package/dist/tools/tts.js.map +1 -0
  76. package/jest.config.js +15 -0
  77. package/package.json +49 -0
  78. package/src/config.ts +121 -0
  79. package/src/core/backends/afplay-backend.ts +162 -0
  80. package/src/core/backends/aplay-backend.ts +160 -0
  81. package/src/core/backends/base.ts +117 -0
  82. package/src/core/backends/index.ts +128 -0
  83. package/src/core/backends/naudiodon-backend.ts +164 -0
  84. package/src/core/backends/powershell-backend.ts +173 -0
  85. package/src/core/player.ts +322 -0
  86. package/src/core/speaker.ts +283 -0
  87. package/src/core/stream-player.ts +326 -0
  88. package/src/core/stream-reader.ts +190 -0
  89. package/src/core/streaming-synthesizer.ts +123 -0
  90. package/src/core/types.ts +185 -0
  91. package/src/index.ts +233 -0
  92. package/src/plugin.ts +166 -0
  93. package/src/providers/base.ts +150 -0
  94. package/src/providers/minimax.ts +515 -0
  95. package/src/tools/tts.ts +277 -0
  96. package/src/types/naudiodon.d.ts +19 -0
  97. package/tests/__mocks__/@opencode-ai/plugin.ts +32 -0
  98. package/tests/backends.test.ts +831 -0
  99. package/tests/index.test.ts +201 -0
  100. package/tests/integration-test.d.ts +6 -0
  101. package/tests/integration-test.d.ts.map +1 -0
  102. package/tests/integration-test.js +84 -0
  103. package/tests/integration-test.js.map +1 -0
  104. package/tests/integration-test.ts +93 -0
  105. package/tests/p1-fixes.test.ts +160 -0
  106. package/tests/plugin.test.ts +311 -0
  107. package/tests/provider.test.d.ts +2 -0
  108. package/tests/provider.test.d.ts.map +1 -0
  109. package/tests/provider.test.js +69 -0
  110. package/tests/provider.test.js.map +1 -0
  111. package/tests/provider.test.ts +87 -0
  112. package/tests/speaker.test.d.ts +2 -0
  113. package/tests/speaker.test.d.ts.map +1 -0
  114. package/tests/speaker.test.js +63 -0
  115. package/tests/speaker.test.js.map +1 -0
  116. package/tests/speaker.test.ts +232 -0
  117. package/tests/stream-player.test.ts +303 -0
  118. package/tests/stream-reader.test.ts +269 -0
  119. package/tests/streaming-synthesizer.test.ts +225 -0
  120. package/tests/tts-tools.test.ts +270 -0
  121. package/tests/types.test.d.ts +2 -0
  122. package/tests/types.test.d.ts.map +1 -0
  123. package/tests/types.test.js +61 -0
  124. package/tests/types.test.js.map +1 -0
  125. package/tests/types.test.ts +63 -0
  126. package/tsconfig.json +22 -0
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Speaker - TTS 统一调用入口
3
+ * 提供简洁的 API 和便捷函数
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { TTSError, TTSErrorCode } from './types';
7
+ import { getProvider, listProviders, hasProvider } from '../providers/base';
8
+ import { AudioPlayer } from './player';
9
+ /**
10
+ * Speaker - TTS 统一调用入口类
11
+ * 封装 Provider 和 Player,提供简洁的 speak/pause/resume/stop API
12
+ */
13
+ export class Speaker extends EventEmitter {
14
+ options;
15
+ currentProvider;
16
+ player;
17
+ currentText;
18
+ isSpeaking = false;
19
+ isPaused = false;
20
+ constructor(options = {}) {
21
+ super();
22
+ this.options = options;
23
+ // 初始化播放器
24
+ const playerEvents = {
25
+ onStart: () => this.emit('start', this.currentText),
26
+ onEnd: () => {
27
+ this.isSpeaking = false;
28
+ this.emit('end', this.currentText);
29
+ },
30
+ onError: (error) => this.emit('error', error),
31
+ onPause: () => {
32
+ this.isPaused = true;
33
+ this.emit('pause');
34
+ },
35
+ onResume: () => {
36
+ this.isPaused = false;
37
+ this.emit('resume');
38
+ },
39
+ onStop: () => {
40
+ this.isSpeaking = false;
41
+ this.isPaused = false;
42
+ this.emit('stop');
43
+ }
44
+ };
45
+ this.player = new AudioPlayer(playerEvents);
46
+ }
47
+ /**
48
+ * 说话 - 核心方法
49
+ * @param text 要说的文本
50
+ * @param options 可选参数
51
+ */
52
+ async speak(text, options = {}) {
53
+ // 参数校验
54
+ if (!text || text.trim().length === 0) {
55
+ throw new TTSError('Text cannot be empty', TTSErrorCode.INVALID_PARAMS, 'speaker');
56
+ }
57
+ // 停止当前播放
58
+ if (this.isSpeaking) {
59
+ await this.stop();
60
+ }
61
+ this.isSpeaking = true;
62
+ this.currentText = text;
63
+ try {
64
+ // 获取 provider
65
+ const providerName = options.provider || this.options.defaultProvider || 'minimax';
66
+ if (!hasProvider(providerName)) {
67
+ throw new TTSError(`Provider "${providerName}" not found`, TTSErrorCode.UNKNOWN, 'speaker');
68
+ }
69
+ this.currentProvider = getProvider(providerName);
70
+ // 调用 provider 生成音频
71
+ const result = await this.currentProvider.speak(text, {
72
+ model: options.model || this.options.defaultModel || 'stream',
73
+ voice: options.voice || this.options.defaultVoice,
74
+ speed: options.speed,
75
+ volume: options.volume,
76
+ pitch: options.pitch,
77
+ sourceVoice: options.sourceVoice
78
+ });
79
+ // 播放音频
80
+ if (this.player) {
81
+ await this.player.play(result.audioData, result.format);
82
+ }
83
+ }
84
+ catch (error) {
85
+ this.isSpeaking = false;
86
+ if (error instanceof TTSError) {
87
+ this.emit('error', error);
88
+ throw error;
89
+ }
90
+ const ttsError = new TTSError('Speak failed', TTSErrorCode.UNKNOWN, 'speaker', error);
91
+ this.emit('error', ttsError);
92
+ throw ttsError;
93
+ }
94
+ }
95
+ /**
96
+ * 暂停播放
97
+ */
98
+ pause() {
99
+ if (this.player && this.isSpeaking && !this.isPaused) {
100
+ this.player.pause();
101
+ }
102
+ }
103
+ /**
104
+ * 恢复播放
105
+ */
106
+ resume() {
107
+ if (this.player && this.isPaused) {
108
+ this.player.resume();
109
+ }
110
+ }
111
+ /**
112
+ * 停止播放
113
+ */
114
+ async stop() {
115
+ this.isSpeaking = false;
116
+ this.isPaused = false;
117
+ if (this.player) {
118
+ await this.player.stop();
119
+ }
120
+ }
121
+ /**
122
+ * 销毁 Speaker,释放资源
123
+ */
124
+ async destroy() {
125
+ this.isSpeaking = false;
126
+ this.isPaused = false;
127
+ if (this.player) {
128
+ await this.player.stop();
129
+ this.player = undefined;
130
+ }
131
+ this.currentProvider = undefined;
132
+ this.currentText = undefined;
133
+ }
134
+ /**
135
+ * 列出可用音色
136
+ */
137
+ async listVoices(providerName) {
138
+ const name = providerName || this.options.defaultProvider || 'minimax';
139
+ const provider = getProvider(name);
140
+ return provider.listVoices();
141
+ }
142
+ /**
143
+ * 获取 Provider 能力
144
+ */
145
+ getCapabilities(providerName) {
146
+ const name = providerName || this.options.defaultProvider || 'minimax';
147
+ const provider = getProvider(name);
148
+ return provider.getCapabilities();
149
+ }
150
+ /**
151
+ * 获取所有已注册的 Provider
152
+ */
153
+ getProviders() {
154
+ return listProviders();
155
+ }
156
+ /**
157
+ * 是否正在播放
158
+ */
159
+ isPlaying() {
160
+ return this.isSpeaking && !this.isPaused;
161
+ }
162
+ /**
163
+ * 是否暂停
164
+ */
165
+ isPausedState() {
166
+ return this.isPaused;
167
+ }
168
+ }
169
+ // ============================================================================
170
+ // 便捷函数 - 默认 Speaker 实例
171
+ // ============================================================================
172
+ let defaultSpeaker;
173
+ /**
174
+ * 获取默认 Speaker 实例(单例)
175
+ */
176
+ export function getDefaultSpeaker() {
177
+ if (!defaultSpeaker) {
178
+ defaultSpeaker = new Speaker();
179
+ }
180
+ return defaultSpeaker;
181
+ }
182
+ /**
183
+ * 说话(便捷函数)
184
+ */
185
+ export async function speak(text, options) {
186
+ const speaker = getDefaultSpeaker();
187
+ return speaker.speak(text, options);
188
+ }
189
+ /**
190
+ * 停止(便捷函数)
191
+ */
192
+ export async function stop() {
193
+ const speaker = getDefaultSpeaker();
194
+ return speaker.stop();
195
+ }
196
+ /**
197
+ * 暂停(便捷函数)
198
+ */
199
+ export function pause() {
200
+ const speaker = getDefaultSpeaker();
201
+ speaker.pause();
202
+ }
203
+ /**
204
+ * 恢复(便捷函数)
205
+ */
206
+ export function resume() {
207
+ const speaker = getDefaultSpeaker();
208
+ speaker.resume();
209
+ }
210
+ /**
211
+ * 列出音色(便捷函数)
212
+ */
213
+ export async function listVoices(providerName) {
214
+ const speaker = getDefaultSpeaker();
215
+ return speaker.listVoices(providerName);
216
+ }
217
+ export default Speaker;
218
+ //# sourceMappingURL=speaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speaker.js","sourceRoot":"","sources":["../../src/core/speaker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * StreamPlayer - 真正的边收边播流式音频播放器
3
+ * 接收音频 chunk,同时写入临时文件并立即启动播放器播放
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ /**
7
+ * StreamPlayer Events - 流式播放器事件回调接口
8
+ */
9
+ export interface StreamPlayerEvents {
10
+ onProgress?: (bytesWritten: number) => void;
11
+ onStart?: () => void;
12
+ onEnd?: () => void;
13
+ onError?: (error: Error) => void;
14
+ onPause?: () => void;
15
+ onResume?: () => void;
16
+ onStop?: () => void;
17
+ }
18
+ /**
19
+ * StreamPlayer Options - 流式播放器配置选项
20
+ */
21
+ export interface StreamPlayerOptions {
22
+ format?: 'mp3' | 'wav' | 'flac';
23
+ events?: StreamPlayerEvents;
24
+ }
25
+ /**
26
+ * StreamPlayer - 边收边播的流式音频播放器
27
+ *
28
+ * 特性:
29
+ * - 写入临时文件的同时立即启动播放器
30
+ * - 支持 pause/resume/stop 控制
31
+ * - 跨平台支持:macOS (afplay), Linux (aplay), Windows (PowerShell)
32
+ */
33
+ export declare class StreamPlayer extends EventEmitter {
34
+ private tempFile;
35
+ private writeStream?;
36
+ private playerProcess?;
37
+ private _bytesWritten;
38
+ private _started;
39
+ private _paused;
40
+ private _stopped;
41
+ private format;
42
+ private events?;
43
+ constructor(options?: StreamPlayerOptions);
44
+ /**
45
+ * 开始播放
46
+ * 创建临时文件,创建写入流,启动播放器进程
47
+ */
48
+ start(): void;
49
+ /**
50
+ * 启动播放器进程
51
+ */
52
+ private startPlayer;
53
+ /**
54
+ * 写入音频数据块(边收边写)
55
+ * 如果尚未 start(),会自动调用
56
+ */
57
+ write(chunk: Buffer): void;
58
+ /**
59
+ * 结束写入
60
+ * 关闭写入流,但不杀播放器进程,让它播完
61
+ */
62
+ end(): void;
63
+ /**
64
+ * 停止播放
65
+ * 杀死播放器进程,删除临时文件
66
+ */
67
+ stop(): void;
68
+ /**
69
+ * 暂停播放
70
+ * 使用 SIGSTOP 暂停播放器进程
71
+ */
72
+ pause(): void;
73
+ /**
74
+ * 恢复播放
75
+ */
76
+ resume(): void;
77
+ /**
78
+ * 是否已启动
79
+ */
80
+ isStarted(): boolean;
81
+ /**
82
+ * 是否暂停
83
+ */
84
+ isPaused(): boolean;
85
+ /**
86
+ * 是否已停止
87
+ */
88
+ isStopped(): boolean;
89
+ /**
90
+ * 获取已写入的字节数
91
+ */
92
+ getBytesWritten(): number;
93
+ /**
94
+ * 获取临时文件路径
95
+ */
96
+ getTempFile(): string;
97
+ /**
98
+ * 处理错误
99
+ */
100
+ private handleError;
101
+ /**
102
+ * 删除临时文件
103
+ */
104
+ private deleteTempFile;
105
+ }
106
+ export default StreamPlayer;
107
+ //# sourceMappingURL=stream-player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-player.d.ts","sourceRoot":"","sources":["../../src/core/stream-player.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAOrC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;IAC/B,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,aAAa,CAAC,CAAc;IACpC,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAoB;gBAEvB,OAAO,GAAE,mBAAwB;IAM7C;;;OAGG;IACH,KAAK,IAAI,IAAI;IA6Bb;;OAEG;IACH,OAAO,CAAC,WAAW;IAkDnB;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IA6B1B;;;OAGG;IACH,GAAG,IAAI,IAAI;IAOX;;;OAGG;IACH,IAAI,IAAI,IAAI;IAgCZ;;;OAGG;IACH,KAAK,IAAI,IAAI;IAiBb;;OAEG;IACH,MAAM,IAAI,IAAI;IAiBd;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,OAAO,CAAC,WAAW;IAKnB;;OAEG;IACH,OAAO,CAAC,cAAc;CAYvB;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,272 @@
1
+ /**
2
+ * StreamPlayer - 真正的边收边播流式音频播放器
3
+ * 接收音频 chunk,同时写入临时文件并立即启动播放器播放
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import { spawn } from 'child_process';
7
+ import fs from 'fs';
8
+ import { createWriteStream } from 'fs';
9
+ import { tmpdir } from 'os';
10
+ import { join } from 'path';
11
+ /**
12
+ * StreamPlayer - 边收边播的流式音频播放器
13
+ *
14
+ * 特性:
15
+ * - 写入临时文件的同时立即启动播放器
16
+ * - 支持 pause/resume/stop 控制
17
+ * - 跨平台支持:macOS (afplay), Linux (aplay), Windows (PowerShell)
18
+ */
19
+ export class StreamPlayer extends EventEmitter {
20
+ tempFile = '';
21
+ writeStream;
22
+ playerProcess;
23
+ _bytesWritten = 0;
24
+ _started = false;
25
+ _paused = false;
26
+ _stopped = false;
27
+ format = 'mp3';
28
+ events;
29
+ constructor(options = {}) {
30
+ super();
31
+ this.format = options.format || 'mp3';
32
+ this.events = options.events;
33
+ }
34
+ /**
35
+ * 开始播放
36
+ * 创建临时文件,创建写入流,启动播放器进程
37
+ */
38
+ start() {
39
+ if (this._started) {
40
+ return;
41
+ }
42
+ // 创建临时文件
43
+ this.tempFile = join(tmpdir(), `ocosay-stream-${Date.now()}.${this.format}`);
44
+ // 创建写入流
45
+ this.writeStream = createWriteStream(this.tempFile, { highWaterMark: 64 * 1024 });
46
+ this.writeStream.on('error', (error) => {
47
+ this.handleError(error);
48
+ });
49
+ this.writeStream.on('finish', () => {
50
+ // 写入完成,但播放器可能还在播放
51
+ });
52
+ // 启动播放器进程
53
+ this.startPlayer();
54
+ this._started = true;
55
+ this._stopped = false;
56
+ this.events?.onStart?.();
57
+ this.emit('start');
58
+ }
59
+ /**
60
+ * 启动播放器进程
61
+ */
62
+ startPlayer() {
63
+ const platform = process.platform;
64
+ let command;
65
+ let args;
66
+ if (platform === 'darwin') {
67
+ // macOS
68
+ command = 'afplay';
69
+ args = [this.tempFile];
70
+ }
71
+ else if (platform === 'linux') {
72
+ // Linux
73
+ command = 'aplay';
74
+ args = [this.tempFile];
75
+ }
76
+ else {
77
+ // Windows - PlaySync is synchronous and blocks the event loop
78
+ // Return error to indicate Windows is not supported for streaming
79
+ this.handleError(new Error('Windows platform is not supported for stream playback. PlaySync() blocks the Node.js event loop.'));
80
+ return;
81
+ }
82
+ this.playerProcess = spawn(command, args, {
83
+ stdio: 'ignore',
84
+ detached: false
85
+ });
86
+ this.playerProcess.on('exit', (code, signal) => {
87
+ // 如果是正常结束或被信号终止,不当作错误
88
+ if (this._stopped) {
89
+ return;
90
+ }
91
+ if (signal === 'SIGTERM' || signal === 'SIGINT') {
92
+ // 被主动停止
93
+ return;
94
+ }
95
+ if (code === 0 || code === null) {
96
+ // 正常播放结束
97
+ this.events?.onEnd?.();
98
+ this.emit('end');
99
+ }
100
+ else {
101
+ this.handleError(new Error(`Player exited with code ${code}`));
102
+ }
103
+ });
104
+ this.playerProcess.on('error', (error) => {
105
+ this.handleError(error);
106
+ });
107
+ }
108
+ /**
109
+ * 写入音频数据块(边收边写)
110
+ * 如果尚未 start(),会自动调用
111
+ */
112
+ write(chunk) {
113
+ // 如果已停止,直接忽略
114
+ if (this._stopped) {
115
+ return;
116
+ }
117
+ // 如果未启动,自动启动
118
+ if (!this._started) {
119
+ this.start();
120
+ }
121
+ // 写入数据到文件
122
+ if (this.writeStream) {
123
+ const canContinue = this.writeStream.write(chunk);
124
+ if (!canContinue) {
125
+ // 写入缓冲区满了,等待 drain 事件
126
+ this.writeStream.once('drain', () => {
127
+ // 可以继续写入
128
+ });
129
+ }
130
+ this._bytesWritten += chunk.length;
131
+ this.events?.onProgress?.(this._bytesWritten);
132
+ this.emit('progress', this._bytesWritten);
133
+ }
134
+ }
135
+ /**
136
+ * 结束写入
137
+ * 关闭写入流,但不杀播放器进程,让它播完
138
+ */
139
+ end() {
140
+ if (this.writeStream) {
141
+ this.writeStream.end();
142
+ this.writeStream = undefined;
143
+ }
144
+ }
145
+ /**
146
+ * 停止播放
147
+ * 杀死播放器进程,删除临时文件
148
+ */
149
+ stop() {
150
+ this._stopped = true;
151
+ this._started = false;
152
+ this._paused = false;
153
+ // 杀死播放器进程
154
+ if (this.playerProcess) {
155
+ try {
156
+ this.playerProcess.kill('SIGTERM');
157
+ }
158
+ catch (e) {
159
+ // 忽略错误
160
+ }
161
+ this.playerProcess = undefined;
162
+ }
163
+ // 关闭写入流
164
+ if (this.writeStream) {
165
+ try {
166
+ this.writeStream.destroy();
167
+ }
168
+ catch (e) {
169
+ // 忽略错误
170
+ }
171
+ this.writeStream = undefined;
172
+ }
173
+ // 删除临时文件
174
+ this.deleteTempFile();
175
+ this.events?.onStop?.();
176
+ this.emit('stop');
177
+ }
178
+ /**
179
+ * 暂停播放
180
+ * 使用 SIGSTOP 暂停播放器进程
181
+ */
182
+ pause() {
183
+ if (!this._started || this._paused || this._stopped) {
184
+ return;
185
+ }
186
+ if (this.playerProcess) {
187
+ try {
188
+ this.playerProcess.kill('SIGSTOP');
189
+ this._paused = true;
190
+ this.events?.onPause?.();
191
+ this.emit('pause');
192
+ }
193
+ catch (e) {
194
+ // 如果 kill 失败,忽略
195
+ }
196
+ }
197
+ }
198
+ /**
199
+ * 恢复播放
200
+ */
201
+ resume() {
202
+ if (!this._paused || this._stopped) {
203
+ return;
204
+ }
205
+ if (this.playerProcess) {
206
+ try {
207
+ this.playerProcess.kill('SIGCONT');
208
+ this._paused = false;
209
+ this.events?.onResume?.();
210
+ this.emit('resume');
211
+ }
212
+ catch (e) {
213
+ // 如果 kill 失败,忽略
214
+ }
215
+ }
216
+ }
217
+ /**
218
+ * 是否已启动
219
+ */
220
+ isStarted() {
221
+ return this._started;
222
+ }
223
+ /**
224
+ * 是否暂停
225
+ */
226
+ isPaused() {
227
+ return this._paused;
228
+ }
229
+ /**
230
+ * 是否已停止
231
+ */
232
+ isStopped() {
233
+ return this._stopped;
234
+ }
235
+ /**
236
+ * 获取已写入的字节数
237
+ */
238
+ getBytesWritten() {
239
+ return this._bytesWritten;
240
+ }
241
+ /**
242
+ * 获取临时文件路径
243
+ */
244
+ getTempFile() {
245
+ return this.tempFile;
246
+ }
247
+ /**
248
+ * 处理错误
249
+ */
250
+ handleError(error) {
251
+ this.events?.onError?.(error);
252
+ this.emit('error', error);
253
+ }
254
+ /**
255
+ * 删除临时文件
256
+ */
257
+ deleteTempFile() {
258
+ if (this.tempFile) {
259
+ try {
260
+ if (fs.existsSync(this.tempFile)) {
261
+ fs.unlinkSync(this.tempFile);
262
+ }
263
+ }
264
+ catch (e) {
265
+ // 忽略删除错误
266
+ }
267
+ this.tempFile = '';
268
+ }
269
+ }
270
+ }
271
+ export default StreamPlayer;
272
+ //# sourceMappingURL=stream-player.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-player.js","sourceRoot":"","sources":["../../src/core/stream-player.ts"],"names":[],"mappings":"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"}