@zeewain/3d-avatar-sdk 1.2.3 → 1.2.4-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/README.md +213 -9
- package/dist/examples/test-vue2/package.json +1 -1
- package/dist/examples/test-vue3/index.html +1 -0
- package/dist/examples/test-vue3/package.json +3 -1
- package/dist/examples/test-vue3/plugins/html.ts +38 -0
- package/dist/examples/test-vue3/pnpm-lock.yaml +291 -90
- package/dist/examples/test-vue3/src/App.vue +4 -2
- package/dist/examples/test-vue3/src/components/BroadcastAPI.vue +38 -20
- package/dist/examples/test-vue3/tsconfig.node.json +2 -1
- package/dist/examples/test-vue3/vite.config.ts +4 -1
- package/dist/index.d.ts +108 -11
- package/dist/index.es5.js +389 -145
- package/dist/index.es5.umd.js +389 -145
- package/dist/index.esm.js +342 -131
- package/dist/index.umd.cjs +342 -131
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,6 +125,17 @@ async function initializeAvatar() {
|
|
|
125
125
|
speed: 1.0,
|
|
126
126
|
isSubtitle: false
|
|
127
127
|
});
|
|
128
|
+
|
|
129
|
+
// 5. 追加播报(演示队列功能)
|
|
130
|
+
await sdk.startBroadcast({
|
|
131
|
+
type: BroadcastType.TEXT,
|
|
132
|
+
humanCode: 'avatar001',
|
|
133
|
+
text: '这里展示了智能播报队列的强大功能。',
|
|
134
|
+
voiceCode: 'VOICE001',
|
|
135
|
+
volume: 1.0,
|
|
136
|
+
speed: 1.0,
|
|
137
|
+
isSubtitle: false
|
|
138
|
+
}, true); // isAppend = true,追加到播报队列
|
|
128
139
|
}
|
|
129
140
|
} catch (error) {
|
|
130
141
|
console.error('初始化失败:', error);
|
|
@@ -204,7 +215,7 @@ class AvatarApp {
|
|
|
204
215
|
}
|
|
205
216
|
|
|
206
217
|
// 文本播报
|
|
207
|
-
async speakText(text, voiceCode = 'VOICE001') {
|
|
218
|
+
async speakText(text, voiceCode = 'VOICE001', isAppend = false) {
|
|
208
219
|
if (!this.isInitialized) {
|
|
209
220
|
console.warn('SDK未初始化');
|
|
210
221
|
return;
|
|
@@ -219,8 +230,35 @@ class AvatarApp {
|
|
|
219
230
|
volume: 1.0,
|
|
220
231
|
speed: 1.0,
|
|
221
232
|
isSubtitle: false
|
|
222
|
-
});
|
|
223
|
-
console.log('播报开始');
|
|
233
|
+
}, isAppend);
|
|
234
|
+
console.log(isAppend ? '播报已追加到队列' : '播报开始');
|
|
235
|
+
} catch (error) {
|
|
236
|
+
this.handleError(error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 批量播报(演示队列功能)
|
|
241
|
+
async speakMultiple(textList, voiceCode = 'VOICE001') {
|
|
242
|
+
if (!this.isInitialized) {
|
|
243
|
+
console.warn('SDK未初始化');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!Array.isArray(textList) || textList.length === 0) {
|
|
248
|
+
console.warn('文本列表为空');
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
// 第一段作为新播报开始
|
|
254
|
+
await this.speakText(textList[0], voiceCode, false);
|
|
255
|
+
|
|
256
|
+
// 其余内容追加到队列
|
|
257
|
+
for (let i = 1; i < textList.length; i++) {
|
|
258
|
+
await this.speakText(textList[i], voiceCode, true);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
console.log(`✅ 已添加 ${textList.length} 段内容到播报队列`);
|
|
224
262
|
} catch (error) {
|
|
225
263
|
this.handleError(error);
|
|
226
264
|
}
|
|
@@ -354,6 +392,16 @@ class AvatarApp {
|
|
|
354
392
|
const text = document.getElementById('text-input').value;
|
|
355
393
|
this.speakText(text);
|
|
356
394
|
});
|
|
395
|
+
document.getElementById('speak-multiple-btn').addEventListener('click', () => {
|
|
396
|
+
// 演示批量播报
|
|
397
|
+
const textList = [
|
|
398
|
+
'欢迎来到ZEE 3D Avatar SDK演示',
|
|
399
|
+
'这里展示了智能播报队列功能',
|
|
400
|
+
'多段内容会按顺序播报',
|
|
401
|
+
'感谢您的观看'
|
|
402
|
+
];
|
|
403
|
+
this.speakMultiple(textList);
|
|
404
|
+
});
|
|
357
405
|
document.getElementById('pause-btn').addEventListener('click', () => this.pauseBroadcast());
|
|
358
406
|
document.getElementById('resume-btn').addEventListener('click', () => this.resumeBroadcast());
|
|
359
407
|
document.getElementById('stop-btn').addEventListener('click', () => this.stopBroadcast());
|
|
@@ -585,10 +633,10 @@ try {
|
|
|
585
633
|
##### startBroadcast()
|
|
586
634
|
|
|
587
635
|
```typescript
|
|
588
|
-
async startBroadcast(params: IBroadcastParams): Promise<void>
|
|
636
|
+
async startBroadcast(params: IBroadcastParams, isAppend?: boolean): Promise<void>
|
|
589
637
|
```
|
|
590
638
|
|
|
591
|
-
|
|
639
|
+
开始播报,支持文本转语音和音频播报两种模式。内置智能队列机制,确保多次追加播报按序执行。
|
|
592
640
|
|
|
593
641
|
**参数:**
|
|
594
642
|
```typescript
|
|
@@ -606,9 +654,19 @@ interface IBroadcastParams {
|
|
|
606
654
|
}
|
|
607
655
|
```
|
|
608
656
|
|
|
657
|
+
- `isAppend`: 是否追加播报,默认false
|
|
658
|
+
- `false`:开始新的播报(会停止当前播报并清空队列)
|
|
659
|
+
- `true`:追加到当前播报队列(按序播报,不会中断当前播报)
|
|
660
|
+
|
|
661
|
+
**🚀 播报队列特性:**
|
|
662
|
+
- **智能排队**:多次追加播报自动按调用顺序排队
|
|
663
|
+
- **顺序保证**:即使网络响应乱序,也确保音频按序播报
|
|
664
|
+
- **高效处理**:支持并发请求,同时保证播报顺序
|
|
665
|
+
- **自动清理**:已播报内容自动从队列中移除,节省内存
|
|
666
|
+
|
|
609
667
|
**使用示例:**
|
|
610
668
|
```javascript
|
|
611
|
-
//
|
|
669
|
+
// 开始新的播报(清空队列)
|
|
612
670
|
await sdk.startBroadcast({
|
|
613
671
|
type: BroadcastType.TEXT,
|
|
614
672
|
humanCode: 'avatar001',
|
|
@@ -619,7 +677,29 @@ await sdk.startBroadcast({
|
|
|
619
677
|
isSubtitle: false
|
|
620
678
|
});
|
|
621
679
|
|
|
622
|
-
//
|
|
680
|
+
// 追加播报(进入队列按序播报)
|
|
681
|
+
await sdk.startBroadcast({
|
|
682
|
+
type: BroadcastType.TEXT,
|
|
683
|
+
humanCode: 'avatar001',
|
|
684
|
+
text: '这是第二段内容',
|
|
685
|
+
voiceCode: 'VOICE001',
|
|
686
|
+
volume: 1.0,
|
|
687
|
+
speed: 1.0,
|
|
688
|
+
isSubtitle: false
|
|
689
|
+
}, true); // isAppend = true
|
|
690
|
+
|
|
691
|
+
// 继续追加播报
|
|
692
|
+
await sdk.startBroadcast({
|
|
693
|
+
type: BroadcastType.TEXT,
|
|
694
|
+
humanCode: 'avatar001',
|
|
695
|
+
text: '这是第三段内容',
|
|
696
|
+
voiceCode: 'VOICE001',
|
|
697
|
+
volume: 1.0,
|
|
698
|
+
speed: 1.0,
|
|
699
|
+
isSubtitle: false
|
|
700
|
+
}, true); // isAppend = true
|
|
701
|
+
|
|
702
|
+
// 音频播报也支持追加
|
|
623
703
|
await sdk.startBroadcast({
|
|
624
704
|
type: BroadcastType.AUDIO,
|
|
625
705
|
humanCode: 'avatar001',
|
|
@@ -627,7 +707,7 @@ await sdk.startBroadcast({
|
|
|
627
707
|
text: '音频字幕内容',
|
|
628
708
|
volume: 1.0,
|
|
629
709
|
isSubtitle: true
|
|
630
|
-
});
|
|
710
|
+
}, true); // isAppend = true
|
|
631
711
|
```
|
|
632
712
|
|
|
633
713
|
##### pauseBroadcast()
|
|
@@ -697,10 +777,53 @@ getBroadcastStatus(): {
|
|
|
697
777
|
hasReceivedAudio: boolean;
|
|
698
778
|
pendingCallbacks: number;
|
|
699
779
|
hasController: boolean;
|
|
780
|
+
queueInfo?: {
|
|
781
|
+
totalTasks: number; // 队列中总任务数
|
|
782
|
+
requestingTasks: number; // 正在请求中的任务数
|
|
783
|
+
completedTasks: number; // 已完成的任务数
|
|
784
|
+
failedTasks: number; // 失败的任务数
|
|
785
|
+
totalPendingResponses: number; // 待发送的响应总数
|
|
786
|
+
currentSendingSequence: number; // 当前发送的序号
|
|
787
|
+
};
|
|
700
788
|
}
|
|
701
789
|
```
|
|
702
790
|
|
|
703
|
-
|
|
791
|
+
获取播报状态信息,包括队列状态监控。
|
|
792
|
+
|
|
793
|
+
**返回值说明:**
|
|
794
|
+
- `isActive`: 播报服务是否活跃(有任务在队列中或正在生成音频)
|
|
795
|
+
- `isGeneratingAudio`: 是否正在生成音频
|
|
796
|
+
- `hasReceivedAudio`: 是否已收到音频
|
|
797
|
+
- `pendingCallbacks`: 待处理的回调数量
|
|
798
|
+
- `hasController`: 是否有活跃的请求控制器
|
|
799
|
+
- `queueInfo`: 队列详细信息
|
|
800
|
+
- `totalTasks`: 队列中的总任务数
|
|
801
|
+
- `requestingTasks`: 正在发起SSE请求的任务数
|
|
802
|
+
- `completedTasks`: 已完成的任务数(即将被清理)
|
|
803
|
+
- `failedTasks`: 失败的任务数
|
|
804
|
+
- `totalPendingResponses`: 所有任务中待发送的响应总数
|
|
805
|
+
- `currentSendingSequence`: 当前正在发送的任务序号
|
|
806
|
+
|
|
807
|
+
**使用示例:**
|
|
808
|
+
```javascript
|
|
809
|
+
// 获取播报状态
|
|
810
|
+
const status = sdk.getBroadcastStatus();
|
|
811
|
+
|
|
812
|
+
console.log('播报服务状态:', {
|
|
813
|
+
活跃状态: status.isActive,
|
|
814
|
+
正在生成: status.isGeneratingAudio,
|
|
815
|
+
队列任务数: status.queueInfo?.totalTasks || 0,
|
|
816
|
+
待发送响应: status.queueInfo?.totalPendingResponses || 0
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
// 监控队列状态
|
|
820
|
+
setInterval(() => {
|
|
821
|
+
const status = sdk.getBroadcastStatus();
|
|
822
|
+
if (status.queueInfo && status.queueInfo.totalTasks > 0) {
|
|
823
|
+
console.log(`队列进度: ${status.queueInfo.completedTasks}/${status.queueInfo.totalTasks} 已完成`);
|
|
824
|
+
}
|
|
825
|
+
}, 1000);
|
|
826
|
+
```
|
|
704
827
|
|
|
705
828
|
#### 工具方法
|
|
706
829
|
|
|
@@ -754,6 +877,87 @@ destroy(): void
|
|
|
754
877
|
|
|
755
878
|
## 🔧 高级功能
|
|
756
879
|
|
|
880
|
+
### 🎯 智能播报队列
|
|
881
|
+
|
|
882
|
+
新版SDK内置智能队列机制,完美解决了多段播报的顺序性和响应乱序问题。
|
|
883
|
+
|
|
884
|
+
#### 核心特性
|
|
885
|
+
|
|
886
|
+
- **顺序保证**:无论网络响应如何乱序,音频始终按调用顺序播报
|
|
887
|
+
- **并发优化**:支持同时发起多个SSE请求,提高响应速度
|
|
888
|
+
- **内存友好**:播报完成的响应立即删除,避免内存积累
|
|
889
|
+
- **自动管理**:无需手动管理队列状态,SDK自动处理
|
|
890
|
+
|
|
891
|
+
#### 使用场景
|
|
892
|
+
|
|
893
|
+
```javascript
|
|
894
|
+
// 场景1:连续播报多段内容
|
|
895
|
+
async function speakMultipleTexts() {
|
|
896
|
+
// 开始新播报
|
|
897
|
+
await sdk.startBroadcast({
|
|
898
|
+
type: BroadcastType.TEXT,
|
|
899
|
+
humanCode: 'avatar001',
|
|
900
|
+
text: '欢迎来到我们的产品介绍',
|
|
901
|
+
voiceCode: 'VOICE001',
|
|
902
|
+
volume: 1.0,
|
|
903
|
+
isSubtitle: false
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
// 追加播报(自动排队)
|
|
907
|
+
await sdk.startBroadcast({
|
|
908
|
+
type: BroadcastType.TEXT,
|
|
909
|
+
humanCode: 'avatar001',
|
|
910
|
+
text: '首先,我们来了解一下产品的核心功能',
|
|
911
|
+
voiceCode: 'VOICE001',
|
|
912
|
+
volume: 1.0,
|
|
913
|
+
isSubtitle: false
|
|
914
|
+
}, true);
|
|
915
|
+
|
|
916
|
+
await sdk.startBroadcast({
|
|
917
|
+
type: BroadcastType.TEXT,
|
|
918
|
+
humanCode: 'avatar001',
|
|
919
|
+
text: '接下来,我将为您演示具体的操作步骤',
|
|
920
|
+
voiceCode: 'VOICE001',
|
|
921
|
+
volume: 1.0,
|
|
922
|
+
isSubtitle: false
|
|
923
|
+
}, true);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// 场景2:混合媒体播报
|
|
927
|
+
async function speakMixedContent() {
|
|
928
|
+
// 文本开场
|
|
929
|
+
await sdk.startBroadcast({
|
|
930
|
+
type: BroadcastType.TEXT,
|
|
931
|
+
humanCode: 'avatar001',
|
|
932
|
+
text: '下面请听一段音频介绍',
|
|
933
|
+
voiceCode: 'VOICE001',
|
|
934
|
+
volume: 1.0,
|
|
935
|
+
isSubtitle: false
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
// 追加音频
|
|
939
|
+
await sdk.startBroadcast({
|
|
940
|
+
type: BroadcastType.AUDIO,
|
|
941
|
+
humanCode: 'avatar001',
|
|
942
|
+
audioUrl: 'https://example.com/intro.mp3',
|
|
943
|
+
text: '这是音频字幕内容',
|
|
944
|
+
volume: 1.0,
|
|
945
|
+
isSubtitle: true
|
|
946
|
+
}, true);
|
|
947
|
+
|
|
948
|
+
// 追加文本总结
|
|
949
|
+
await sdk.startBroadcast({
|
|
950
|
+
type: BroadcastType.TEXT,
|
|
951
|
+
humanCode: 'avatar001',
|
|
952
|
+
text: '以上就是我们的产品介绍,谢谢您的观看',
|
|
953
|
+
voiceCode: 'VOICE001',
|
|
954
|
+
volume: 1.0,
|
|
955
|
+
isSubtitle: false
|
|
956
|
+
}, true);
|
|
957
|
+
}
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
|
|
757
961
|
### 多实例支持
|
|
758
962
|
|
|
759
963
|
新版SDK支持在同一页面创建多个独立的SDK实例,每个实例拥有独立的回调函数命名空间,避免冲突。
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<meta name="description" content="ZEE 3D Avatar SDK Vue3 + TypeScript + Vite 测试项目" />
|
|
8
8
|
<meta name="keywords" content="ZEE, 3D, Avatar, SDK, Vue3, TypeScript, Vite" />
|
|
9
9
|
<meta name="author" content="ZEEWain" />
|
|
10
|
+
<%- copyrightScript %>
|
|
10
11
|
<!-- 样式重置 -->
|
|
11
12
|
<style>
|
|
12
13
|
* {
|
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
"@element-plus/icons-vue": "^2.3.1",
|
|
25
25
|
"@vueuse/core": "^13.5.0",
|
|
26
26
|
"@vueuse/integrations": "^13.5.0",
|
|
27
|
-
"@zeewain/3d-avatar-sdk": "^1.2.
|
|
27
|
+
"@zeewain/3d-avatar-sdk": "^1.2.4-0",
|
|
28
|
+
"dayjs": "^1.11.13",
|
|
28
29
|
"element-plus": "^2.10.4",
|
|
30
|
+
"vite-plugin-html": "^3.2.2",
|
|
29
31
|
"vue": "^3.5.17"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createHtmlPlugin } from 'vite-plugin-html';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import timezone from 'dayjs/plugin/timezone';
|
|
4
|
+
import utc from 'dayjs/plugin/utc';
|
|
5
|
+
import pkg from '../package.json';
|
|
6
|
+
|
|
7
|
+
dayjs.extend(utc);
|
|
8
|
+
dayjs.extend(timezone);
|
|
9
|
+
|
|
10
|
+
export default function createHtml(isBuild: boolean) {
|
|
11
|
+
|
|
12
|
+
let copyrightScript = '';
|
|
13
|
+
const copyright_common_style = 'font-size: 10px; margin-bottom: 2px; padding: 3px 4px; color: #fff;';
|
|
14
|
+
const copyright_main_style = `${copyright_common_style} background: #e24329;`;
|
|
15
|
+
const copyright_sub_style = `${copyright_common_style} background: #707070;`;
|
|
16
|
+
|
|
17
|
+
copyrightScript += `
|
|
18
|
+
<script>
|
|
19
|
+
if ((navigator.language || navigator.browserLanguage).toLowerCase() === 'zh-cn') {
|
|
20
|
+
console.info('%c由%c紫为云%c驱动', '${copyright_sub_style}', '${copyright_main_style}', '${copyright_sub_style}', '\\nhttps://www.zeewain.com');
|
|
21
|
+
} else {
|
|
22
|
+
console.info('%cPowered by%cZeewain', '${copyright_sub_style}', '${copyright_main_style}', '\\nhttps://www.zeewain.com');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.info('%cVERSION:%c${pkg.version}', '${copyright_main_style}', '${copyright_sub_style}');
|
|
26
|
+
console.info('%cPUBLISH:%c${dayjs().tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss')} 时区:${dayjs.tz.guess()}', '${copyright_main_style}', '${copyright_sub_style}');
|
|
27
|
+
</script>
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
return createHtmlPlugin({
|
|
31
|
+
inject: {
|
|
32
|
+
data: {
|
|
33
|
+
copyrightScript
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
minify: isBuild
|
|
37
|
+
});
|
|
38
|
+
}
|