@mingxy/ocosay 1.0.35 → 1.1.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.
Files changed (72) hide show
  1. package/dist/config.js +4 -1
  2. package/dist/core/backends/index.js +8 -3
  3. package/dist/core/notification.d.ts +27 -0
  4. package/dist/core/notification.js +86 -0
  5. package/dist/core/speaker.js +6 -35
  6. package/dist/core/stream-reader.js +2 -1
  7. package/dist/core/streaming-synthesizer.js +2 -0
  8. package/dist/index.d.ts +1 -13
  9. package/dist/index.js +4 -23
  10. package/dist/package.json +2 -2
  11. package/dist/plugin.js +990 -586
  12. package/dist/providers/minimax.js +43 -20
  13. package/dist/services/notification-service.d.ts +17 -0
  14. package/dist/services/notification-service.js +74 -0
  15. package/dist/services/speaker-service.d.ts +34 -0
  16. package/dist/services/speaker-service.js +74 -0
  17. package/dist/services/streaming-service.d.ts +109 -0
  18. package/dist/services/streaming-service.js +281 -0
  19. package/dist/tools/tts.js +32 -19
  20. package/dist/utils/logger.d.ts +6 -0
  21. package/dist/utils/logger.js +42 -6
  22. package/package.json +2 -2
  23. package/.idea/UniappTool.xml +0 -10
  24. package/.idea/inspectionProfiles/profiles_settings.xml +0 -5
  25. package/.idea/modules.xml +0 -8
  26. package/.idea/ocosay.iml +0 -12
  27. package/.idea/vcs.xml +0 -6
  28. package/.sisyphus/boulder.json +0 -23
  29. package/dist/config.d.ts.map +0 -1
  30. package/dist/config.js.map +0 -1
  31. package/dist/core/backends/afplay-backend.d.ts.map +0 -1
  32. package/dist/core/backends/afplay-backend.js.map +0 -1
  33. package/dist/core/backends/aplay-backend.d.ts.map +0 -1
  34. package/dist/core/backends/aplay-backend.js.map +0 -1
  35. package/dist/core/backends/base.d.ts.map +0 -1
  36. package/dist/core/backends/base.js.map +0 -1
  37. package/dist/core/backends/howler-backend.d.ts.map +0 -1
  38. package/dist/core/backends/howler-backend.js.map +0 -1
  39. package/dist/core/backends/index.d.ts.map +0 -1
  40. package/dist/core/backends/index.js.map +0 -1
  41. package/dist/core/backends/naudiodon-backend.d.ts.map +0 -1
  42. package/dist/core/backends/naudiodon-backend.js.map +0 -1
  43. package/dist/core/backends/powershell-backend.d.ts.map +0 -1
  44. package/dist/core/backends/powershell-backend.js.map +0 -1
  45. package/dist/core/logger.d.ts.map +0 -1
  46. package/dist/core/logger.js.map +0 -1
  47. package/dist/core/player.d.ts.map +0 -1
  48. package/dist/core/player.js.map +0 -1
  49. package/dist/core/speaker.d.ts.map +0 -1
  50. package/dist/core/speaker.js.map +0 -1
  51. package/dist/core/stream-player.d.ts.map +0 -1
  52. package/dist/core/stream-player.js.map +0 -1
  53. package/dist/core/stream-reader.d.ts.map +0 -1
  54. package/dist/core/stream-reader.js.map +0 -1
  55. package/dist/core/streaming-synthesizer.d.ts.map +0 -1
  56. package/dist/core/streaming-synthesizer.js.map +0 -1
  57. package/dist/core/types.d.ts.map +0 -1
  58. package/dist/core/types.js.map +0 -1
  59. package/dist/index.d.ts.map +0 -1
  60. package/dist/index.js.map +0 -1
  61. package/dist/plugin.d.ts.map +0 -1
  62. package/dist/plugin.js.map +0 -7
  63. package/dist/providers/base.d.ts.map +0 -1
  64. package/dist/providers/base.js.map +0 -1
  65. package/dist/providers/minimax.d.ts.map +0 -1
  66. package/dist/providers/minimax.js.map +0 -1
  67. package/dist/tools/tts.d.ts.map +0 -1
  68. package/dist/tools/tts.js.map +0 -1
  69. package/dist/types/config.d.ts.map +0 -1
  70. package/dist/types/config.js.map +0 -1
  71. package/dist/utils/logger.d.ts.map +0 -1
  72. package/dist/utils/logger.js.map +0 -1
@@ -98,36 +98,59 @@ export class MiniMaxProvider extends BaseTTSProvider {
98
98
  });
99
99
  const stream = response.data;
100
100
  const audioChunks = [];
101
+ let lineBuffer = '';
101
102
  return new Promise((resolve, reject) => {
102
103
  stream.on('data', (chunk) => {
103
- try {
104
- const lines = chunk.toString().split('\n');
105
- for (const line of lines) {
106
- if (line.startsWith('data:')) {
107
- const data = JSON.parse(line.slice(5));
108
- if (data.data?.audio) {
109
- audioChunks.push(Buffer.from(data.data.audio, 'hex'));
110
- }
111
- if (data.data?.status === 2) {
112
- const fullAudio = Buffer.concat(audioChunks);
113
- resolve({
114
- audioData: fullAudio,
115
- format: this.audioFormat,
116
- isStream: true,
117
- duration: this.estimateDuration(fullAudio.length)
118
- });
119
- }
104
+ lineBuffer += chunk.toString();
105
+ while (true) {
106
+ const lineEnd = lineBuffer.indexOf('\n');
107
+ if (lineEnd === -1)
108
+ break;
109
+ const line = lineBuffer.slice(0, lineEnd).trim();
110
+ lineBuffer = lineBuffer.slice(lineEnd + 1);
111
+ if (!line || !line.startsWith('data:'))
112
+ continue;
113
+ const jsonStr = line.slice(5).trim();
114
+ if (!jsonStr)
115
+ continue;
116
+ try {
117
+ const data = JSON.parse(jsonStr);
118
+ if (data.audio) {
119
+ audioChunks.push(Buffer.from(data.audio, 'hex'));
120
+ }
121
+ if (data.is_final === true) {
122
+ const fullAudio = Buffer.concat(audioChunks);
123
+ resolve({
124
+ audioData: fullAudio,
125
+ format: this.audioFormat,
126
+ isStream: true,
127
+ duration: this.estimateDuration(fullAudio.length)
128
+ });
120
129
  }
121
130
  }
122
- }
123
- catch (e) {
124
- audioChunks.push(chunk);
131
+ catch (e) {
132
+ // ignore
133
+ }
125
134
  }
126
135
  });
127
136
  stream.on('error', (err) => {
128
137
  reject(new TTSError('Stream error', TTSErrorCode.NETWORK, this.name, err));
129
138
  });
130
139
  stream.on('end', () => {
140
+ if (lineBuffer.trim() && lineBuffer.startsWith('data:')) {
141
+ const jsonStr = lineBuffer.slice(5).trim();
142
+ if (jsonStr) {
143
+ try {
144
+ const data = JSON.parse(jsonStr);
145
+ if (data.audio) {
146
+ audioChunks.push(Buffer.from(data.audio, 'hex'));
147
+ }
148
+ }
149
+ catch (e) {
150
+ // ignore
151
+ }
152
+ }
153
+ }
131
154
  if (audioChunks.length > 0) {
132
155
  const fullAudio = Buffer.concat(audioChunks);
133
156
  resolve({
@@ -0,0 +1,17 @@
1
+ export type ToastType = 'success' | 'error' | 'warning' | 'info';
2
+ export declare class NotificationService {
3
+ private constructor();
4
+ static getInstance(): NotificationService;
5
+ initialize(tuiInstance: any): void;
6
+ isReady(): boolean;
7
+ showToast(message: string, type?: ToastType): void;
8
+ success(message: string): void;
9
+ error(message: string): void;
10
+ warning(message: string): void;
11
+ info(message: string): void;
12
+ private getTitleForType;
13
+ private fallbackLog;
14
+ }
15
+ export declare function showToast(message: string, type?: ToastType): void;
16
+ export declare function initializeNotificationService(tuiInstance: any): void;
17
+ //# sourceMappingURL=notification-service.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { logger } from '../utils/logger.js';
2
+ let instance;
3
+ let tui = null;
4
+ let initialized = false;
5
+ export class NotificationService {
6
+ constructor() { }
7
+ static getInstance() {
8
+ if (!instance) {
9
+ instance = new NotificationService();
10
+ }
11
+ return instance;
12
+ }
13
+ initialize(tuiInstance) {
14
+ if (initialized)
15
+ return;
16
+ tui = tuiInstance;
17
+ initialized = true;
18
+ logger.debug('NotificationService initialized');
19
+ }
20
+ isReady() {
21
+ return initialized && tui !== null;
22
+ }
23
+ showToast(message, type = 'info') {
24
+ const title = this.getTitleForType(type);
25
+ if (tui?.showToast) {
26
+ try {
27
+ tui.showToast({
28
+ title,
29
+ message,
30
+ variant: type,
31
+ duration: type === 'error' ? 8000 : 5000
32
+ });
33
+ return;
34
+ }
35
+ catch (err) {
36
+ logger.warn({ err }, 'tui.showToast failed');
37
+ }
38
+ }
39
+ this.fallbackLog(type, title, message);
40
+ }
41
+ success(message) { this.showToast(message, 'success'); }
42
+ error(message) { this.showToast(message, 'error'); }
43
+ warning(message) { this.showToast(message, 'warning'); }
44
+ info(message) { this.showToast(message, 'info'); }
45
+ getTitleForType(type) {
46
+ const titles = {
47
+ success: 'Success',
48
+ error: 'Error',
49
+ warning: 'Warning',
50
+ info: 'Info'
51
+ };
52
+ return titles[type];
53
+ }
54
+ fallbackLog(type, title, message) {
55
+ const timestamp = new Date().toISOString();
56
+ switch (type) {
57
+ case 'error':
58
+ logger.error({ title, message, timestamp }, 'Toast (fallback)');
59
+ break;
60
+ case 'warning':
61
+ logger.warn({ title, message, timestamp }, 'Toast (fallback)');
62
+ break;
63
+ default:
64
+ logger.info({ title, message, timestamp }, 'Toast (fallback)');
65
+ }
66
+ }
67
+ }
68
+ export function showToast(message, type = 'info') {
69
+ NotificationService.getInstance().showToast(message, type);
70
+ }
71
+ export function initializeNotificationService(tuiInstance) {
72
+ NotificationService.getInstance().initialize(tuiInstance);
73
+ }
74
+ //# sourceMappingURL=notification-service.js.map
@@ -0,0 +1,34 @@
1
+ import { Voice, SpeakOptions } from '../core/types';
2
+ export interface speakerServiceOptions {
3
+ defaultProvider?: string;
4
+ defaultModel?: 'sync' | 'async' | 'stream';
5
+ defaultVoice?: string;
6
+ }
7
+ export declare class SpeakerService {
8
+ private options;
9
+ private speaker;
10
+ constructor(options?: speakerServiceOptions);
11
+ speak(text: string, options?: SpeakOptions & {
12
+ provider?: string;
13
+ }): Promise<void>;
14
+ pause(): void;
15
+ resume(): void;
16
+ stop(): Promise<void>;
17
+ listVoices(providerName?: string): Promise<Voice[]>;
18
+ getCapabilities(providerName?: string): import("../core/types").TTSCapabilities;
19
+ getProviders(): string[];
20
+ isPlaying(): boolean;
21
+ isPausedState(): boolean;
22
+ destroy(): Promise<void>;
23
+ private getTimestamp;
24
+ }
25
+ export declare function getDefaultSpeakerService(): SpeakerService;
26
+ export declare function speak(text: string, options?: SpeakOptions & {
27
+ provider?: string;
28
+ }): Promise<void>;
29
+ export declare function stop(): Promise<void>;
30
+ export declare function pause(): void;
31
+ export declare function resume(): void;
32
+ export declare function listVoices(providerName?: string): Promise<Voice[]>;
33
+ export default SpeakerService;
34
+ //# sourceMappingURL=speaker-service.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { getDefaultSpeaker, speak as coreSpeak, stop as coreStop, pause as corePause, resume as coreResume, listVoices as coreListVoices } from '../core/speaker';
2
+ import { logger } from '../utils/logger';
3
+ export class SpeakerService {
4
+ options;
5
+ speaker;
6
+ constructor(options = {}) {
7
+ this.options = options;
8
+ this.speaker = getDefaultSpeaker();
9
+ }
10
+ async speak(text, options) {
11
+ const timestamp = this.getTimestamp();
12
+ logger.info(`[Ocosay][${timestamp}][INFO][Speaker] 对应事件{播放开始} - 文本长度: ${text.length}`);
13
+ return this.speaker.speak(text, options);
14
+ }
15
+ pause() {
16
+ this.speaker.pause();
17
+ }
18
+ resume() {
19
+ this.speaker.resume();
20
+ }
21
+ async stop() {
22
+ return this.speaker.stop();
23
+ }
24
+ async listVoices(providerName) {
25
+ return this.speaker.listVoices(providerName);
26
+ }
27
+ getCapabilities(providerName) {
28
+ return this.speaker.getCapabilities(providerName);
29
+ }
30
+ getProviders() {
31
+ return this.speaker.getProviders();
32
+ }
33
+ isPlaying() {
34
+ return this.speaker.isPlaying();
35
+ }
36
+ isPausedState() {
37
+ return this.speaker.isPausedState();
38
+ }
39
+ async destroy() {
40
+ return this.speaker.destroy();
41
+ }
42
+ getTimestamp() {
43
+ const now = new Date();
44
+ const pad = (n) => n.toString().padStart(2, '0');
45
+ return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
46
+ }
47
+ }
48
+ let defaultSpeakerService;
49
+ export function getDefaultSpeakerService() {
50
+ if (!defaultSpeakerService) {
51
+ defaultSpeakerService = new SpeakerService();
52
+ }
53
+ return defaultSpeakerService;
54
+ }
55
+ export async function speak(text, options) {
56
+ if (options) {
57
+ return coreSpeak(text, options);
58
+ }
59
+ return coreSpeak(text);
60
+ }
61
+ export async function stop() {
62
+ return coreStop();
63
+ }
64
+ export function pause() {
65
+ corePause();
66
+ }
67
+ export function resume() {
68
+ coreResume();
69
+ }
70
+ export async function listVoices(providerName) {
71
+ return coreListVoices(providerName);
72
+ }
73
+ export default SpeakerService;
74
+ //# sourceMappingURL=speaker-service.js.map
@@ -0,0 +1,109 @@
1
+ /**
2
+ * StreamingService - 流式TTS服务(Service层)
3
+ *
4
+ * 功能:
5
+ * - 调用Provider层获取流式TTS
6
+ * - 调用Backend层播放音频
7
+ * - 支持边接收边播放(豆包模式)
8
+ *
9
+ * 数据流:
10
+ * stream(text) → MiniMaxProvider.speak(stream) → StreamPlayer (边收边播)
11
+ */
12
+ import { EventEmitter } from 'events';
13
+ import { BackendType } from '../core/backends';
14
+ export interface StreamingServiceOptions {
15
+ provider?: string;
16
+ voice?: string;
17
+ speed?: number;
18
+ volume?: number;
19
+ pitch?: number;
20
+ backendType?: BackendType;
21
+ }
22
+ export interface StreamingServiceStatus {
23
+ isActive: boolean;
24
+ bytesWritten: number;
25
+ state: string;
26
+ }
27
+ export declare class StreamingService extends EventEmitter {
28
+ private player;
29
+ private providerName;
30
+ private voice?;
31
+ private speed?;
32
+ private volume?;
33
+ private pitch?;
34
+ private backendType;
35
+ private _isActive;
36
+ private _bytesWritten;
37
+ constructor(options?: StreamingServiceOptions);
38
+ /**
39
+ * 获取时间戳
40
+ */
41
+ private getTimestamp;
42
+ /**
43
+ * 初始化播放器
44
+ */
45
+ private initPlayer;
46
+ /**
47
+ * 流式播放文本
48
+ * 边接收边播放(豆包模式)
49
+ */
50
+ stream(text: string): Promise<void>;
51
+ /**
52
+ * 处理音频结果
53
+ */
54
+ private processAudioResult;
55
+ /**
56
+ * 流式处理音频chunk
57
+ */
58
+ private streamAudioChunks;
59
+ /**
60
+ * 停止流式播放
61
+ */
62
+ stop(): void;
63
+ /**
64
+ * 暂停流式播放
65
+ */
66
+ pause(): void;
67
+ /**
68
+ * 恢复流式播放
69
+ */
70
+ resume(): void;
71
+ /**
72
+ * 获取流式播放状态
73
+ */
74
+ getStatus(): StreamingServiceStatus;
75
+ /**
76
+ * 是否处于活跃状态
77
+ */
78
+ isActive(): boolean;
79
+ /**
80
+ * 销毁服务
81
+ */
82
+ destroy(): Promise<void>;
83
+ }
84
+ /**
85
+ * 获取默认流式服务实例
86
+ */
87
+ export declare function getDefaultStreamingService(): StreamingService;
88
+ /**
89
+ * 导出 stream 方法
90
+ */
91
+ export declare function stream(text: string, options?: StreamingServiceOptions): Promise<void>;
92
+ /**
93
+ * 导出 stop 方法
94
+ */
95
+ export declare function streamStop(): void;
96
+ /**
97
+ * 导出 pause 方法
98
+ */
99
+ export declare function streamPause(): void;
100
+ /**
101
+ * 导出 resume 方法
102
+ */
103
+ export declare function streamResume(): void;
104
+ /**
105
+ * 导出 getStreamStatus 方法
106
+ */
107
+ export declare function getStreamStatus(): StreamingServiceStatus;
108
+ export default StreamingService;
109
+ //# sourceMappingURL=streaming-service.d.ts.map
@@ -0,0 +1,281 @@
1
+ /**
2
+ * StreamingService - 流式TTS服务(Service层)
3
+ *
4
+ * 功能:
5
+ * - 调用Provider层获取流式TTS
6
+ * - 调用Backend层播放音频
7
+ * - 支持边接收边播放(豆包模式)
8
+ *
9
+ * 数据流:
10
+ * stream(text) → MiniMaxProvider.speak(stream) → StreamPlayer (边收边播)
11
+ */
12
+ import { EventEmitter } from 'events';
13
+ import { getProvider } from '../providers/base';
14
+ import { TTSError, TTSErrorCode } from '../core/types';
15
+ import { StreamPlayer } from '../core/stream-player';
16
+ import { BackendType } from '../core/backends';
17
+ import { logger } from '../utils/logger';
18
+ export class StreamingService extends EventEmitter {
19
+ player = null;
20
+ providerName;
21
+ voice;
22
+ speed;
23
+ volume;
24
+ pitch;
25
+ backendType;
26
+ _isActive = false;
27
+ _bytesWritten = 0;
28
+ constructor(options = {}) {
29
+ super();
30
+ this.providerName = options.provider || 'minimax';
31
+ this.voice = options.voice;
32
+ this.speed = options.speed;
33
+ this.volume = options.volume;
34
+ this.pitch = options.pitch;
35
+ this.backendType = options.backendType || BackendType.NAUDIODON;
36
+ }
37
+ /**
38
+ * 获取时间戳
39
+ */
40
+ getTimestamp() {
41
+ const now = new Date();
42
+ const pad = (n) => n.toString().padStart(2, '0');
43
+ return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
44
+ }
45
+ /**
46
+ * 初始化播放器
47
+ */
48
+ initPlayer() {
49
+ if (this.player) {
50
+ this.player.stop();
51
+ this.player = null;
52
+ }
53
+ const playerOptions = {
54
+ format: 'mp3',
55
+ backendType: this.backendType,
56
+ events: {
57
+ onStart: () => {
58
+ const timestamp = this.getTimestamp();
59
+ logger.info(`[Ocosay][${timestamp}][INFO][Streaming] 对应事件{流式播放开始}`);
60
+ this.emit('start');
61
+ },
62
+ onEnd: () => {
63
+ const timestamp = this.getTimestamp();
64
+ logger.info(`[Ocosay][${timestamp}][INFO][Streaming] 对应事件{流式播放结束}`);
65
+ this._isActive = false;
66
+ this.emit('end');
67
+ },
68
+ onError: (error) => {
69
+ const timestamp = this.getTimestamp();
70
+ logger.error(`[Ocosay][${timestamp}][ERROR][Streaming] 对应事件{流式播放错误} - ${error.message}`);
71
+ this._isActive = false;
72
+ this.emit('error', error);
73
+ },
74
+ onProgress: (bytes) => {
75
+ this._bytesWritten = bytes;
76
+ this.emit('progress', bytes);
77
+ },
78
+ onPause: () => {
79
+ const timestamp = this.getTimestamp();
80
+ logger.info(`[Ocosay][${timestamp}][INFO][Streaming] 对应事件{流式播放暂停}`);
81
+ this.emit('pause');
82
+ },
83
+ onResume: () => {
84
+ const timestamp = this.getTimestamp();
85
+ logger.info(`[Ocosay][${timestamp}][INFO][Streaming] 对应事件{流式播放恢复}`);
86
+ this.emit('resume');
87
+ },
88
+ onStop: () => {
89
+ const timestamp = this.getTimestamp();
90
+ logger.info(`[Ocosay][${timestamp}][INFO][Streaming] 对应事件{流式播放停止}`);
91
+ this._isActive = false;
92
+ this._bytesWritten = 0;
93
+ this.emit('stop');
94
+ }
95
+ }
96
+ };
97
+ this.player = new StreamPlayer(playerOptions);
98
+ return this.player;
99
+ }
100
+ /**
101
+ * 流式播放文本
102
+ * 边接收边播放(豆包模式)
103
+ */
104
+ async stream(text) {
105
+ if (!text || text.trim().length === 0) {
106
+ const timestamp = this.getTimestamp();
107
+ logger.warn(`[Ocosay][${timestamp}][WARNING][Streaming] 对应事件{空文本跳过}`);
108
+ return;
109
+ }
110
+ const timestamp = this.getTimestamp();
111
+ logger.info(`[Ocosay][${timestamp}][INFO][Streaming] 对应事件{流式播放开始} - 文本长度: ${text.length}`);
112
+ try {
113
+ // 获取 Provider
114
+ const provider = getProvider(this.providerName);
115
+ if (!provider) {
116
+ throw new TTSError(`Provider ${this.providerName} not found`, TTSErrorCode.UNKNOWN, this.providerName);
117
+ }
118
+ // 初始化播放器
119
+ const player = this.initPlayer();
120
+ player.start();
121
+ this._isActive = true;
122
+ this._bytesWritten = 0;
123
+ // 调用 Provider 的流式合成
124
+ const result = await provider.speak(text, {
125
+ voice: this.voice,
126
+ model: 'stream',
127
+ speed: this.speed,
128
+ volume: this.volume,
129
+ pitch: this.pitch
130
+ });
131
+ // 处理音频结果
132
+ await this.processAudioResult(result, player);
133
+ }
134
+ catch (error) {
135
+ const ts = this.getTimestamp();
136
+ const errorMsg = error instanceof Error ? error.message : String(error);
137
+ logger.error(`[Ocosay][${ts}][ERROR][Streaming] 对应事件{流式播放错误} - ${errorMsg}`);
138
+ this._isActive = false;
139
+ throw error;
140
+ }
141
+ }
142
+ /**
143
+ * 处理音频结果
144
+ */
145
+ async processAudioResult(result, player) {
146
+ if (result.isStream && result.audioData instanceof ReadableStream) {
147
+ // 流式数据:边收边播
148
+ await this.streamAudioChunks(result.audioData, player);
149
+ }
150
+ else if (Buffer.isBuffer(result.audioData)) {
151
+ // 非流式数据:直接写入
152
+ player.write(result.audioData);
153
+ player.end();
154
+ }
155
+ }
156
+ /**
157
+ * 流式处理音频chunk
158
+ */
159
+ async streamAudioChunks(stream, player) {
160
+ const reader = stream.getReader();
161
+ try {
162
+ while (true) {
163
+ const { done, value } = await reader.read();
164
+ if (done) {
165
+ break;
166
+ }
167
+ if (value) {
168
+ const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
169
+ player.write(chunk);
170
+ }
171
+ }
172
+ }
173
+ finally {
174
+ reader.releaseLock();
175
+ player.end();
176
+ }
177
+ }
178
+ /**
179
+ * 停止流式播放
180
+ */
181
+ stop() {
182
+ if (this.player) {
183
+ this.player.stop();
184
+ }
185
+ this._isActive = false;
186
+ this._bytesWritten = 0;
187
+ }
188
+ /**
189
+ * 暂停流式播放
190
+ */
191
+ pause() {
192
+ if (this.player) {
193
+ this.player.pause();
194
+ }
195
+ }
196
+ /**
197
+ * 恢复流式播放
198
+ */
199
+ resume() {
200
+ if (this.player) {
201
+ this.player.resume();
202
+ }
203
+ }
204
+ /**
205
+ * 获取流式播放状态
206
+ */
207
+ getStatus() {
208
+ return {
209
+ isActive: this._isActive,
210
+ bytesWritten: this._bytesWritten,
211
+ state: this.player?.isStopped() ? 'stopped' :
212
+ this.player?.isPaused() ? 'paused' :
213
+ this._isActive ? 'playing' : 'idle'
214
+ };
215
+ }
216
+ /**
217
+ * 是否处于活跃状态
218
+ */
219
+ isActive() {
220
+ return this._isActive;
221
+ }
222
+ /**
223
+ * 销毁服务
224
+ */
225
+ async destroy() {
226
+ if (this.player) {
227
+ this.player.stop();
228
+ this.player = null;
229
+ }
230
+ this._isActive = false;
231
+ this._bytesWritten = 0;
232
+ }
233
+ }
234
+ // 单例实例
235
+ let defaultStreamingService;
236
+ /**
237
+ * 获取默认流式服务实例
238
+ */
239
+ export function getDefaultStreamingService() {
240
+ if (!defaultStreamingService) {
241
+ defaultStreamingService = new StreamingService();
242
+ }
243
+ return defaultStreamingService;
244
+ }
245
+ /**
246
+ * 导出 stream 方法
247
+ */
248
+ export async function stream(text, options) {
249
+ const service = options ? new StreamingService(options) : getDefaultStreamingService();
250
+ return service.stream(text);
251
+ }
252
+ /**
253
+ * 导出 stop 方法
254
+ */
255
+ export function streamStop() {
256
+ const service = getDefaultStreamingService();
257
+ service.stop();
258
+ }
259
+ /**
260
+ * 导出 pause 方法
261
+ */
262
+ export function streamPause() {
263
+ const service = getDefaultStreamingService();
264
+ service.pause();
265
+ }
266
+ /**
267
+ * 导出 resume 方法
268
+ */
269
+ export function streamResume() {
270
+ const service = getDefaultStreamingService();
271
+ service.resume();
272
+ }
273
+ /**
274
+ * 导出 getStreamStatus 方法
275
+ */
276
+ export function getStreamStatus() {
277
+ const service = getDefaultStreamingService();
278
+ return service.getStatus();
279
+ }
280
+ export default StreamingService;
281
+ //# sourceMappingURL=streaming-service.js.map