@tencentcloud/roomkit-electron-vue3 2.7.0 → 2.7.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 (50) hide show
  1. package/es/components/AITools/AISubtitles.vue.mjs +1 -1
  2. package/es/components/AITools/AISubtitles.vue2.mjs +18 -19
  3. package/es/components/AITools/AITranscription.vue.mjs +1 -1
  4. package/es/components/AITools/AITranscription.vue2.mjs +65 -76
  5. package/es/components/ManageMember/MemberControl/index.vue.mjs +1 -1
  6. package/es/components/ManageMember/MemberControl/index.vue2.mjs +6 -4
  7. package/es/components/ManageMember/MemberItemCommon/MemberInfo.vue.mjs +1 -1
  8. package/es/components/RoomFooter/BasicBeauty.vue.mjs +1 -1
  9. package/es/components/RoomFooter/BasicBeauty.vue2.mjs +3 -1
  10. package/es/components/RoomFooter/ChatControl.vue.mjs +2 -0
  11. package/es/components/ScheduleConference/ScheduleConferencePanel/index.vue2.mjs +1 -1
  12. package/es/components/ScheduleConference/ScheduleRoomControl.vue2.mjs +1 -1
  13. package/es/components/ScheduleConference/ScheduleRoomList.vue2.mjs +1 -1
  14. package/es/index.mjs +118 -110
  15. package/es/services/function/aiTask.d.ts +13 -17
  16. package/es/services/function/aiTask.mjs +63 -100
  17. package/es/services/manager/dataReportManager.d.ts +4 -1
  18. package/es/services/manager/dataReportManager.mjs +3 -0
  19. package/es/utils/utils.d.ts +2 -0
  20. package/es/utils/utils.mjs +33 -0
  21. package/lib/components/AITools/AISubtitles.vue.js +1 -1
  22. package/lib/components/AITools/AISubtitles.vue2.js +17 -18
  23. package/lib/components/AITools/AITranscription.vue.js +1 -1
  24. package/lib/components/AITools/AITranscription.vue2.js +64 -75
  25. package/lib/components/ManageMember/MemberControl/index.vue.js +1 -1
  26. package/lib/components/ManageMember/MemberControl/index.vue2.js +5 -3
  27. package/lib/components/ManageMember/MemberItemCommon/MemberInfo.vue.js +1 -1
  28. package/lib/components/RoomFooter/BasicBeauty.vue.js +1 -1
  29. package/lib/components/RoomFooter/BasicBeauty.vue2.js +3 -1
  30. package/lib/components/RoomFooter/ChatControl.vue.js +2 -0
  31. package/lib/components/ScheduleConference/ScheduleConferencePanel/index.vue2.js +1 -1
  32. package/lib/components/ScheduleConference/ScheduleRoomControl.vue2.js +1 -1
  33. package/lib/components/ScheduleConference/ScheduleRoomList.vue2.js +1 -1
  34. package/lib/index.js +118 -110
  35. package/lib/services/function/aiTask.d.ts +13 -17
  36. package/lib/services/function/aiTask.js +63 -100
  37. package/lib/services/manager/dataReportManager.d.ts +4 -1
  38. package/lib/services/manager/dataReportManager.js +3 -0
  39. package/lib/utils/utils.d.ts +2 -0
  40. package/lib/utils/utils.js +33 -0
  41. package/package.json +2 -2
  42. package/src/TUIRoom/components/AITools/AISubtitles.vue +26 -23
  43. package/src/TUIRoom/components/AITools/AITranscription.vue +106 -101
  44. package/src/TUIRoom/components/ManageMember/MemberControl/index.vue +8 -3
  45. package/src/TUIRoom/components/ManageMember/MemberItemCommon/MemberInfo.vue +1 -1
  46. package/src/TUIRoom/components/RoomFooter/BasicBeauty.vue +2 -1
  47. package/src/TUIRoom/components/RoomFooter/ChatControl.vue +2 -1
  48. package/src/TUIRoom/services/function/aiTask.ts +79 -113
  49. package/src/TUIRoom/services/manager/dataReportManager.ts +3 -0
  50. package/src/TUIRoom/utils/utils.ts +47 -0
@@ -108,7 +108,7 @@ import IconButton from '../common/base/IconButton.vue';
108
108
  import SvgIcon from '../common/base/SvgIcon.vue';
109
109
  import BasicBeautyIcon from '../common/icons/BasicBeautyIcon.vue';
110
110
  import { useI18n } from '../../locales';
111
- import { roomService } from '../../services';
111
+ import { roomService, MetricsKey } from '../../services';
112
112
  import Dialog from '../common/base/Dialog/index.vue';
113
113
  import TuiButton from '../common/base/Button.vue';
114
114
  import Slider from '../common/base/Slider.vue';
@@ -197,6 +197,7 @@ const openBeautySettingPanel = async () => {
197
197
  view: 'test-preview',
198
198
  });
199
199
  isLoading.value = false;
200
+ roomService.dataReportManager.reportCount(MetricsKey.setBasicBeauty);
200
201
  };
201
202
 
202
203
  const closeBeautySettingPanel = async () => {
@@ -23,7 +23,7 @@ import { useChatStore } from '../../stores/chat';
23
23
  import { storeToRefs } from 'pinia';
24
24
  import { useI18n } from '../../locales';
25
25
  import TuiBadge from '../common/base/Badge.vue';
26
- import { roomService } from '../../services';
26
+ import { roomService, MetricsKey } from '../../services';
27
27
  const { t } = useI18n();
28
28
  const chatControlConfig = roomService.getComponentConfig('ChatControl');
29
29
  const basicStore = useBasicStore();
@@ -39,5 +39,6 @@ async function toggleChatSidebar() {
39
39
  basicStore.setSidebarOpenStatus(true);
40
40
  basicStore.setSidebarName('chat');
41
41
  chatStore.updateUnReadCount(0);
42
+ roomService.dataReportManager.reportCount(MetricsKey.openChat);
42
43
  }
43
44
  </script>
@@ -1,12 +1,16 @@
1
1
  import { IRoomService } from '../';
2
2
  import mitt from 'mitt';
3
3
  import { isElectron, isMobile } from '../../utils/environment';
4
+ import { findLastIndex } from '../../utils/utils';
4
5
 
5
- interface SubtitleMessage {
6
- userid: string;
6
+ export interface SubtitleMessage {
7
+ sender: string;
7
8
  text: string;
8
- translation_text: string;
9
+ translationText: string;
10
+ end?: boolean;
11
+ startMsTs: number;
9
12
  }
13
+
10
14
  interface DataPayload {
11
15
  end: boolean;
12
16
  text: string;
@@ -34,22 +38,23 @@ export enum AI_TASK {
34
38
 
35
39
  export interface AITaskEvent {
36
40
  [AI_TASK.TRANSCRIPTION_TASK]: {
37
- subtitleMsg: SubtitleMessage[];
38
- subtitleText: { value: string };
39
- transcriptionText: { value: string };
41
+ subtitleMessages: { [key: string]: SubtitleMessage };
42
+ transcribedMessageList: SubtitleMessage[];
40
43
  };
41
44
  [key: string]: unknown;
42
45
  [key: symbol]: unknown;
43
46
  }
44
47
 
48
+ const ASR_EVENT_CODE = 10000;
49
+
45
50
  export class AITask {
46
51
  private emitter = mitt<AITaskEvent>();
47
-
48
52
  private trtc: any;
49
53
  private service: IRoomService;
50
- public subtitleMsg: SubtitleMessage[] = [];
51
- public subtitleText: { value: string } = { value: '' };
52
- public transcriptionText: { value: string } = { value: '' };
54
+ public subtitleMessages: { [key: string]: SubtitleMessage } = {};
55
+ public transcribedMessageList: SubtitleMessage[] = [];
56
+ private subtitleTimeout: { [key: string]: ReturnType<typeof setTimeout> } =
57
+ {};
53
58
 
54
59
  constructor(service: IRoomService) {
55
60
  this.service = service;
@@ -95,14 +100,13 @@ export class AITask {
95
100
  ) {
96
101
  return;
97
102
  }
98
- const trtc = this.service.roomEngine.instance?.getTRTCCloud()._trtc;
99
- this.trtc = trtc;
100
- trtc.on('custom-message', this.handleAIMessage);
103
+ this.trtc = this.service.roomEngine.instance?.getTRTCCloud()._trtc;
104
+ this.trtc.on('custom-message', this.handleAIMessage);
101
105
  }
106
+
102
107
  private handleUnmount() {
103
- this.subtitleMsg = [];
104
- this.subtitleText.value = '';
105
- this.transcriptionText.value = '';
108
+ this.subtitleMessages = {};
109
+ this.transcribedMessageList = [];
106
110
  this.trtc?.off('custom-message', this.handleAIMessage);
107
111
  }
108
112
 
@@ -111,122 +115,84 @@ export class AITask {
111
115
  this.service.lifeCycleManager.on('unmount', this.handleUnmount);
112
116
  }
113
117
 
114
- // todo trtc defines this type as any
118
+ private resetSubtitleTimeout(id: string, fn: () => void) {
119
+ if (this.subtitleTimeout[id]) {
120
+ clearTimeout(this.subtitleTimeout[id]);
121
+ }
122
+ this.subtitleTimeout[id] = setTimeout(fn, 3000);
123
+ }
124
+
115
125
  private handleAIMessage(event: any) {
116
126
  if (event.cmdId !== 1) return;
117
127
  const data = new TextDecoder().decode(event.data);
118
128
  const jsonData = JSON.parse(data);
119
129
  this.handleMessage(jsonData);
120
130
  this.emit(AI_TASK.TRANSCRIPTION_TASK, {
121
- subtitleText: this.subtitleText,
122
- transcriptionText: this.transcriptionText,
123
- subtitleMsg: this.subtitleMsg,
131
+ subtitleMessages: this.subtitleMessages,
132
+ transcribedMessageList: this.transcribedMessageList,
124
133
  });
125
134
  }
126
135
 
127
136
  private handleMessage(data: MessageData): void {
128
- const refreshSubtitle = (): void => {
129
- let displayText = '';
130
- for (let i = 0; i < this.subtitleMsg.length; i++) {
131
- displayText += `${this.service.roomStore.getDisplayName(this.subtitleMsg[i].userid)}: ${this.subtitleMsg[i].text}\n`;
132
- if (this.subtitleMsg[i].translation_text !== '') {
133
- displayText += `${this.service.roomStore.getDisplayName(this.subtitleMsg[i].userid)}: ${this.subtitleMsg[i].translation_text}\n`;
134
- }
135
- }
136
- this.subtitleText.value = displayText;
137
+ if (data.type !== ASR_EVENT_CODE) return;
138
+ const { sender, payload } = data;
139
+ const { end } = payload;
140
+
141
+ const createSubtitleMsg = () => {
142
+ return {
143
+ sender,
144
+ text: payload.text,
145
+ translationText: payload.translation_text,
146
+ startMsTs: data.start_ms_ts,
147
+ end,
148
+ };
137
149
  };
138
150
 
139
- if (data.type === 10000 && data.payload.end === false) {
140
- // Real-time subtitles
141
- let exist = false;
142
- for (let i = 0; i < this.subtitleMsg.length; i++) {
143
- if (data.sender === this.subtitleMsg[i].userid) {
144
- this.subtitleMsg[i].text = data.payload.text;
145
- this.subtitleMsg[i].translation_text = data.payload.translation_text;
146
- exist = true;
147
- break;
148
- }
149
- }
150
- if (!exist) {
151
- this.subtitleMsg.push({
152
- userid: data.sender,
153
- text: data.payload.text,
154
- translation_text: data.payload.translation_text,
155
- });
156
- }
151
+ const updateMsg = (msg: SubtitleMessage) => {
152
+ msg.text = payload.text;
153
+ msg.translationText = payload.translation_text;
154
+ msg.end = end;
155
+ };
157
156
 
158
- refreshSubtitle();
159
- } else if (data.type === 10000 && data.payload.end === true) {
160
- // One sentence recognition completed
161
- let index = 0;
162
- for (let i = 0; i < this.subtitleMsg.length; i++) {
163
- if (data.sender === this.subtitleMsg[i].userid) {
164
- this.subtitleMsg[i].text = data.payload.text;
165
- this.subtitleMsg[i].translation_text = data.payload.translation_text;
166
- index = i;
167
- break;
168
- }
157
+ const appendMsg = <T extends SubtitleMessage>(
158
+ msg: T,
159
+ target: T[] | Record<string, T>
160
+ ) => {
161
+ if (Array.isArray(target)) {
162
+ target.push(msg);
163
+ } else if (typeof target === 'object') {
164
+ const recordTarget = target as Record<string, T>;
165
+ recordTarget[msg.sender] = msg;
166
+ } else {
167
+ throw new Error('Invalid target type');
169
168
  }
170
- refreshSubtitle();
171
- // todo start_ms_ts end_ms_ts
169
+ };
172
170
 
173
- let content = `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.text}\n`;
174
- if (data.payload.translation_text !== '') {
175
- content += `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.translation_text}\n`;
176
- }
177
- this.transcriptionText.value += content;
171
+ const existingSubtitle = this.subtitleMessages[sender];
172
+ if (existingSubtitle) {
173
+ updateMsg(existingSubtitle);
174
+ } else {
175
+ appendMsg(createSubtitleMsg(), this.subtitleMessages);
178
176
  }
179
177
 
180
- // subtitle and transcription is deprecated
181
- if (data.type === 'subtitle') {
182
- let exist = false;
183
- for (let i = 0; i < this.subtitleMsg.length; i++) {
184
- if (data.userid === this.subtitleMsg[i].userid) {
185
- this.subtitleMsg[i].text = data.text;
186
- this.subtitleMsg[i].translation_text = data.translation_text;
187
- exist = true;
188
- break;
189
- }
190
- }
191
- if (!exist) {
192
- this.subtitleMsg.push({
193
- userid: data.userid,
194
- text: data.text,
195
- translation_text: data.translation_text,
196
- });
197
- }
198
-
199
- refreshSubtitle();
200
- } else if (data.type === 'transcription') {
201
- let index = 0;
202
- for (let i = 0; i < this.subtitleMsg.length; i++) {
203
- if (data.userid === this.subtitleMsg[i].userid) {
204
- this.subtitleMsg[i].text = data.text;
205
- this.subtitleMsg[i].translation_text = data.translation_text;
206
- index = i;
207
- break;
208
- }
209
- }
210
- refreshSubtitle();
178
+ const transcriptionIndex = findLastIndex(
179
+ this.transcribedMessageList,
180
+ msg => msg.sender === sender && !msg.end
181
+ );
211
182
 
212
- let content = `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.text}\n`;
213
- if (data.translation_text !== '') {
214
- content += `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.translation_text}\n`;
215
- }
216
- this.transcriptionText.value += content;
183
+ if (transcriptionIndex !== -1) {
184
+ updateMsg(this.transcribedMessageList[transcriptionIndex]);
185
+ } else {
186
+ appendMsg(createSubtitleMsg(), this.transcribedMessageList);
217
187
  }
218
- }
219
188
 
220
- public StartAITranscription(): void {
221
- // Implementation for starting AI transcription
189
+ this.resetSubtitleTimeout(sender, () => {
190
+ if (!end) return;
191
+ delete this.subtitleMessages[sender];
192
+ this.emit(AI_TASK.TRANSCRIPTION_TASK, {
193
+ subtitleMessages: this.subtitleMessages,
194
+ transcribedMessageList: this.transcribedMessageList,
195
+ });
196
+ });
222
197
  }
223
198
  }
224
-
225
- // utils
226
- function formatTimestampToTime(timestamp: number): string {
227
- const date = new Date(timestamp);
228
- const hours = date.getHours().toString().padStart(2, '0');
229
- const minutes = date.getMinutes().toString().padStart(2, '0');
230
- const seconds = date.getSeconds().toString().padStart(2, '0');
231
- return `${hours}:${minutes}:${seconds}`;
232
- }
@@ -16,6 +16,9 @@ export enum MetricsKey {
16
16
  enableWatermark = 106054,
17
17
  enableVirtualBackground = 106055,
18
18
  hideFeatureButton = 106056,
19
+ openChat = 106057,
20
+ setBasicBeauty = 106058,
21
+ aiTask = 106059,
19
22
  }
20
23
 
21
24
  type Task = () => void;
@@ -221,3 +221,50 @@ export function getNanoId(size = 21) {
221
221
  }
222
222
  return id;
223
223
  }
224
+
225
+ export function findLastIndex<T>(
226
+ array: T[],
227
+ predicate: (value: T, index: number, obj: T[]) => boolean,
228
+ thisArg?: any
229
+ ): number {
230
+ const len = array.length >>> 0;
231
+
232
+ let k = len - 1;
233
+ while (k >= 0) {
234
+ const kValue = array[k];
235
+ if (predicate.call(thisArg, kValue, k, array)) {
236
+ return k;
237
+ }
238
+ k = k - 1;
239
+ }
240
+ return -1;
241
+ }
242
+
243
+ export function formatTimestampToTime(
244
+ timestamp: number,
245
+ format = 'MM-DD HH:mm'
246
+ ): string {
247
+ const date = new Date(timestamp);
248
+ const padStart = (value: number, length = 2) =>
249
+ value.toString().padStart(length, '0');
250
+
251
+ const replacements: { [key: string]: string } = {
252
+ YYYY: date.getFullYear().toString(),
253
+ YY: (date.getFullYear() % 100).toString().padStart(2, '0'),
254
+ MM: padStart(date.getMonth() + 1),
255
+ DD: padStart(date.getDate()),
256
+ HH: padStart(date.getHours()),
257
+ hh: padStart(date.getHours() % 12),
258
+ mm: padStart(date.getMinutes()),
259
+ ss: padStart(date.getSeconds()),
260
+ A: date.getHours() >= 12 ? 'PM' : 'AM',
261
+ };
262
+
263
+ // console.log(formatTimestampToTime(Date.now())); // "10-24 16:20"
264
+ // console.log(formatTimestampToTime(Date.now(), 'YYYY-MM-DD HH:mm:ss')); // "2024-10-24 16:20:30"
265
+ // console.log(formatTimestampToTime(Date.now(), 'MM-DD hh:mm A')); // "10-24 04:20 PM"
266
+ return format.replace(
267
+ /YYYY|YY|MM|DD|HH|hh|mm|ss|A/g,
268
+ match => replacements[match]
269
+ );
270
+ }