@mulingai-npm/redis 1.11.2 → 1.13.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/managers/mulingstream-chunk-manager.d.ts +7 -9
- package/dist/managers/mulingstream-room-manager.d.ts +76 -0
- package/dist/managers/mulingstream-room-manager.js +227 -174
- package/dist/managers/mulingstream-room-tracker.d.ts +76 -0
- package/dist/managers/mulingstream-room-tracker.js +227 -0
- package/package.json +1 -1
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { RedisClient } from '../redis-client';
|
|
2
|
-
export type
|
|
3
|
-
export type TranslationStatus = 'INIT' | 'DISCARDED' | 'READY' | 'USED';
|
|
4
|
-
export type TtsStatus = 'INIT' | 'DISCARDED' | 'READY' | 'USED';
|
|
2
|
+
export type StepStatus = 'INIT' | 'DISCARDED' | 'READY' | 'USED';
|
|
5
3
|
export type MulingstreamChunkStatus = 'INIT' | 'STARTED' | 'FINISHED' | 'CANCELED' | 'USED';
|
|
6
4
|
export type MulingstreamChunkStep = 'RECEIVED' | 'STT' | 'TRANSLATION' | 'TTS' | 'EMITTED';
|
|
7
5
|
export type SttProvider = 'azure' | 'whisper' | 'google' | 'aws';
|
|
@@ -25,21 +23,21 @@ export type MulingstreamChunkData = {
|
|
|
25
23
|
processingStart: number;
|
|
26
24
|
};
|
|
27
25
|
stt: {
|
|
28
|
-
[
|
|
26
|
+
[sttProvider: string]: {
|
|
29
27
|
transcription: string;
|
|
30
|
-
status:
|
|
28
|
+
status: StepStatus;
|
|
31
29
|
};
|
|
32
30
|
};
|
|
33
31
|
translation: {
|
|
34
32
|
[language: string]: {
|
|
35
33
|
translation: string;
|
|
36
|
-
status:
|
|
34
|
+
status: StepStatus;
|
|
37
35
|
};
|
|
38
36
|
};
|
|
39
37
|
tts: {
|
|
40
38
|
[language: string]: {
|
|
41
39
|
ttsAudioPath: string;
|
|
42
|
-
status:
|
|
40
|
+
status: StepStatus;
|
|
43
41
|
isEmitted: boolean;
|
|
44
42
|
};
|
|
45
43
|
};
|
|
@@ -67,6 +65,6 @@ export declare class MulingstreamChunkManager {
|
|
|
67
65
|
getByChunkNumber(chunkNumber: number, roomId: string): Promise<MulingstreamChunkData | null>;
|
|
68
66
|
updateMulingstreamChunkStatus(mulingstreamChunkId: string, mulingstreamChunkStep: MulingstreamChunkStep, mulingstreamChunkStatus: MulingstreamChunkStatus): Promise<boolean>;
|
|
69
67
|
updateFinalTranscription(mulingstreamChunkId: string, finalTranscription: string): Promise<boolean>;
|
|
70
|
-
updateSttStatus(mulingstreamChunkId: string, service: SttProvider, status:
|
|
71
|
-
updateTranscription(mulingstreamChunkId: string, service: SttProvider, transcription: string, sttStatus:
|
|
68
|
+
updateSttStatus(mulingstreamChunkId: string, service: SttProvider, status: StepStatus): Promise<boolean>;
|
|
69
|
+
updateTranscription(mulingstreamChunkId: string, service: SttProvider, transcription: string, sttStatus: StepStatus): Promise<boolean>;
|
|
72
70
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { RedisClient } from '../redis-client';
|
|
2
|
+
export type SttProvider = 'azure' | 'whisper' | 'google' | 'aws';
|
|
3
|
+
export type MulingstreamRoomData = {
|
|
4
|
+
mulingstreamRoomTrackerId: string;
|
|
5
|
+
roomId: string;
|
|
6
|
+
lastReadyChunk: number;
|
|
7
|
+
lastUsedChunk: number;
|
|
8
|
+
lastDiscardedChunk: number;
|
|
9
|
+
stt: {
|
|
10
|
+
[sttProvider: string]: {
|
|
11
|
+
lastReadyStt: number;
|
|
12
|
+
lastUsedStt: number;
|
|
13
|
+
lastDiscardedStt: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
translation: {
|
|
17
|
+
[language: string]: {
|
|
18
|
+
lastReadyTranslation: number;
|
|
19
|
+
lastUsedTranslation: number;
|
|
20
|
+
lastDiscardedTranslation: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
tts: {
|
|
24
|
+
[language: string]: {
|
|
25
|
+
lastReadyTts: number;
|
|
26
|
+
lastUsedTts: number;
|
|
27
|
+
lastDiscardedTts: number;
|
|
28
|
+
lastEmittedTts: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare class MulingstreamRoomTracker {
|
|
33
|
+
private redisClient;
|
|
34
|
+
constructor(redisClient: RedisClient);
|
|
35
|
+
private parseHashData;
|
|
36
|
+
/**
|
|
37
|
+
* Initializes a new MulingstreamRoomTracker for a given room.
|
|
38
|
+
* - The ID is `[roomId]`.
|
|
39
|
+
* - All "lastReady", "lastUsed", "lastDiscarded", etc. are set to -1 initially.
|
|
40
|
+
* - The sttProviders array is used to create stt sub-objects.
|
|
41
|
+
* - The targetLanguages array is used to create translation/tts sub-objects.
|
|
42
|
+
*/
|
|
43
|
+
initMulingstreamRoomTracker(params: {
|
|
44
|
+
roomId: string;
|
|
45
|
+
sttProviders: SttProvider[];
|
|
46
|
+
targetLanguages: string[];
|
|
47
|
+
}): Promise<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Retrieves all MulingstreamRoomTrackers by scanning keys `mulingstreamRoomTracker:*`.
|
|
50
|
+
*/
|
|
51
|
+
getMulingstreamRoomTrackers(): Promise<MulingstreamRoomData[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves a single MulingstreamRoomTracker by roomId.
|
|
54
|
+
*/
|
|
55
|
+
getMulingstreamRoomTracker(roomId: string): Promise<MulingstreamRoomData | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Updates the chunk-level tracker fields (lastReadyChunk, lastUsedChunk, lastDiscardedChunk).
|
|
58
|
+
* Any argument passed as null means "do not update that field."
|
|
59
|
+
*/
|
|
60
|
+
updateChunkTracker(roomId: string, lastReadyChunk: number | null, lastUsedChunk: number | null, lastDiscardedChunk: number | null): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Updates the stt tracker for a specific STT provider in the room.
|
|
63
|
+
* If any argument is null, we do not update that field.
|
|
64
|
+
*/
|
|
65
|
+
updateSttTracker(roomId: string, sttProvider: SttProvider, lastReadyStt: number | null, lastUsedStt: number | null, lastDiscardedStt: number | null): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Updates the translation tracker for a specific language in the room.
|
|
68
|
+
* If any argument is null, we do not update that field.
|
|
69
|
+
*/
|
|
70
|
+
updateTranslationTracker(roomId: string, language: string, lastReadyTranslation: number | null, lastUsedTranslation: number | null, lastDiscardedTranslation: number | null): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Updates the TTS tracker for a specific language in the room.
|
|
73
|
+
* If any argument is null, we do not update that field.
|
|
74
|
+
*/
|
|
75
|
+
updateTtsTracker(roomId: string, language: string, lastReadyTts: number | null, lastUsedTts: number | null, lastDiscardedTts: number | null, lastEmittedTts: number | null): Promise<boolean>;
|
|
76
|
+
}
|
|
@@ -1,174 +1,227 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MulingstreamRoomTracker = void 0;
|
|
4
|
+
const EXPIRATION = 48 * 60 * 60; // 48 hours in seconds
|
|
5
|
+
class MulingstreamRoomTracker {
|
|
6
|
+
constructor(redisClient) {
|
|
7
|
+
this.redisClient = redisClient;
|
|
8
|
+
}
|
|
9
|
+
parseHashData(data) {
|
|
10
|
+
const stt = JSON.parse(data.stt);
|
|
11
|
+
const translation = JSON.parse(data.translation);
|
|
12
|
+
const tts = JSON.parse(data.tts);
|
|
13
|
+
return {
|
|
14
|
+
mulingstreamRoomTrackerId: data.mulingstreamRoomTrackerId,
|
|
15
|
+
roomId: data.roomId,
|
|
16
|
+
lastReadyChunk: parseInt(data.lastReadyChunk, 10),
|
|
17
|
+
lastUsedChunk: parseInt(data.lastUsedChunk, 10),
|
|
18
|
+
lastDiscardedChunk: parseInt(data.lastDiscardedChunk, 10),
|
|
19
|
+
stt,
|
|
20
|
+
translation,
|
|
21
|
+
tts
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Initializes a new MulingstreamRoomTracker for a given room.
|
|
26
|
+
* - The ID is `[roomId]`.
|
|
27
|
+
* - All "lastReady", "lastUsed", "lastDiscarded", etc. are set to -1 initially.
|
|
28
|
+
* - The sttProviders array is used to create stt sub-objects.
|
|
29
|
+
* - The targetLanguages array is used to create translation/tts sub-objects.
|
|
30
|
+
*/
|
|
31
|
+
async initMulingstreamRoomTracker(params) {
|
|
32
|
+
const { roomId, sttProviders, targetLanguages } = params;
|
|
33
|
+
// We'll use [roomId] as the unique ID
|
|
34
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
35
|
+
// Build stt object
|
|
36
|
+
// For each provider, set lastReadyStt = -1, lastUsedStt = -1, lastDiscardedStt = -1
|
|
37
|
+
const stt = {};
|
|
38
|
+
for (const provider of sttProviders) {
|
|
39
|
+
stt[provider] = {
|
|
40
|
+
lastReadyStt: -1,
|
|
41
|
+
lastUsedStt: -1,
|
|
42
|
+
lastDiscardedStt: -1
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Build translation object
|
|
46
|
+
// For each target language, set lastReadyTranslation = -1, lastUsedTranslation = -1, lastDiscardedTranslation = -1
|
|
47
|
+
const translation = {};
|
|
48
|
+
for (const lang of targetLanguages) {
|
|
49
|
+
translation[lang] = {
|
|
50
|
+
lastReadyTranslation: -1,
|
|
51
|
+
lastUsedTranslation: -1,
|
|
52
|
+
lastDiscardedTranslation: -1
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Build tts object
|
|
56
|
+
// For each target language, set lastReadyTts = -1, lastUsedTts = -1, lastDiscardedTts = -1, lastEmittedTts = -1
|
|
57
|
+
const tts = {};
|
|
58
|
+
for (const lang of targetLanguages) {
|
|
59
|
+
tts[lang] = {
|
|
60
|
+
lastReadyTts: -1,
|
|
61
|
+
lastUsedTts: -1,
|
|
62
|
+
lastDiscardedTts: -1,
|
|
63
|
+
lastEmittedTts: -1
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Root-level chunk trackers
|
|
67
|
+
const lastReadyChunk = -1;
|
|
68
|
+
const lastUsedChunk = -1;
|
|
69
|
+
const lastDiscardedChunk = -1;
|
|
70
|
+
// Store everything in Redis
|
|
71
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
72
|
+
mulingstreamRoomTrackerId,
|
|
73
|
+
roomId,
|
|
74
|
+
lastReadyChunk: lastReadyChunk.toString(),
|
|
75
|
+
lastUsedChunk: lastUsedChunk.toString(),
|
|
76
|
+
lastDiscardedChunk: lastDiscardedChunk.toString(),
|
|
77
|
+
stt: JSON.stringify(stt),
|
|
78
|
+
translation: JSON.stringify(translation),
|
|
79
|
+
tts: JSON.stringify(tts)
|
|
80
|
+
});
|
|
81
|
+
// set expiration
|
|
82
|
+
await this.redisClient.expire(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, EXPIRATION);
|
|
83
|
+
return mulingstreamRoomTrackerId;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Retrieves all MulingstreamRoomTrackers by scanning keys `mulingstreamRoomTracker:*`.
|
|
87
|
+
*/
|
|
88
|
+
async getMulingstreamRoomTrackers() {
|
|
89
|
+
const pattern = 'mulingstreamRoomTracker:*';
|
|
90
|
+
const keys = await this.redisClient.keys(pattern);
|
|
91
|
+
if (!keys || keys.length === 0) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
const results = [];
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
const data = await this.redisClient.hgetall(key);
|
|
97
|
+
if (data && data.mulingstreamRoomTrackerId) {
|
|
98
|
+
results.push(this.parseHashData(data));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Retrieves a single MulingstreamRoomTracker by roomId.
|
|
105
|
+
*/
|
|
106
|
+
async getMulingstreamRoomTracker(roomId) {
|
|
107
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
108
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
109
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return this.parseHashData(data);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Updates the chunk-level tracker fields (lastReadyChunk, lastUsedChunk, lastDiscardedChunk).
|
|
116
|
+
* Any argument passed as null means "do not update that field."
|
|
117
|
+
*/
|
|
118
|
+
async updateChunkTracker(roomId, lastReadyChunk, lastUsedChunk, lastDiscardedChunk) {
|
|
119
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
120
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
121
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const updates = {};
|
|
125
|
+
if (lastReadyChunk !== null) {
|
|
126
|
+
updates.lastReadyChunk = lastReadyChunk.toString();
|
|
127
|
+
}
|
|
128
|
+
if (lastUsedChunk !== null) {
|
|
129
|
+
updates.lastUsedChunk = lastUsedChunk.toString();
|
|
130
|
+
}
|
|
131
|
+
if (lastDiscardedChunk !== null) {
|
|
132
|
+
updates.lastDiscardedChunk = lastDiscardedChunk.toString();
|
|
133
|
+
}
|
|
134
|
+
if (Object.keys(updates).length > 0) {
|
|
135
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, updates);
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Updates the stt tracker for a specific STT provider in the room.
|
|
141
|
+
* If any argument is null, we do not update that field.
|
|
142
|
+
*/
|
|
143
|
+
async updateSttTracker(roomId, sttProvider, lastReadyStt, lastUsedStt, lastDiscardedStt) {
|
|
144
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
145
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
146
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const stt = JSON.parse(data.stt);
|
|
150
|
+
if (!stt[sttProvider]) {
|
|
151
|
+
return false; // The provider doesn't exist in this room
|
|
152
|
+
}
|
|
153
|
+
if (lastReadyStt !== null) {
|
|
154
|
+
stt[sttProvider].lastReadyStt = lastReadyStt;
|
|
155
|
+
}
|
|
156
|
+
if (lastUsedStt !== null) {
|
|
157
|
+
stt[sttProvider].lastUsedStt = lastUsedStt;
|
|
158
|
+
}
|
|
159
|
+
if (lastDiscardedStt !== null) {
|
|
160
|
+
stt[sttProvider].lastDiscardedStt = lastDiscardedStt;
|
|
161
|
+
}
|
|
162
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
163
|
+
stt: JSON.stringify(stt)
|
|
164
|
+
});
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Updates the translation tracker for a specific language in the room.
|
|
169
|
+
* If any argument is null, we do not update that field.
|
|
170
|
+
*/
|
|
171
|
+
async updateTranslationTracker(roomId, language, lastReadyTranslation, lastUsedTranslation, lastDiscardedTranslation) {
|
|
172
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
173
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
174
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
const translation = JSON.parse(data.translation);
|
|
178
|
+
if (!translation[language]) {
|
|
179
|
+
return false; // The language isn't tracked in this room
|
|
180
|
+
}
|
|
181
|
+
if (lastReadyTranslation !== null) {
|
|
182
|
+
translation[language].lastReadyTranslation = lastReadyTranslation;
|
|
183
|
+
}
|
|
184
|
+
if (lastUsedTranslation !== null) {
|
|
185
|
+
translation[language].lastUsedTranslation = lastUsedTranslation;
|
|
186
|
+
}
|
|
187
|
+
if (lastDiscardedTranslation !== null) {
|
|
188
|
+
translation[language].lastDiscardedTranslation = lastDiscardedTranslation;
|
|
189
|
+
}
|
|
190
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
191
|
+
translation: JSON.stringify(translation)
|
|
192
|
+
});
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Updates the TTS tracker for a specific language in the room.
|
|
197
|
+
* If any argument is null, we do not update that field.
|
|
198
|
+
*/
|
|
199
|
+
async updateTtsTracker(roomId, language, lastReadyTts, lastUsedTts, lastDiscardedTts, lastEmittedTts) {
|
|
200
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
201
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
202
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const tts = JSON.parse(data.tts);
|
|
206
|
+
if (!tts[language]) {
|
|
207
|
+
return false; // The language isn't tracked in this room
|
|
208
|
+
}
|
|
209
|
+
if (lastReadyTts !== null) {
|
|
210
|
+
tts[language].lastReadyTts = lastReadyTts;
|
|
211
|
+
}
|
|
212
|
+
if (lastUsedTts !== null) {
|
|
213
|
+
tts[language].lastUsedTts = lastUsedTts;
|
|
214
|
+
}
|
|
215
|
+
if (lastDiscardedTts !== null) {
|
|
216
|
+
tts[language].lastDiscardedTts = lastDiscardedTts;
|
|
217
|
+
}
|
|
218
|
+
if (lastEmittedTts !== null) {
|
|
219
|
+
tts[language].lastEmittedTts = lastEmittedTts;
|
|
220
|
+
}
|
|
221
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
222
|
+
tts: JSON.stringify(tts)
|
|
223
|
+
});
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
exports.MulingstreamRoomTracker = MulingstreamRoomTracker;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { RedisClient } from '../redis-client';
|
|
2
|
+
export type SttProvider = 'azure' | 'whisper' | 'google' | 'aws';
|
|
3
|
+
export type MulingstreamRoomData = {
|
|
4
|
+
mulingstreamRoomTrackerId: string;
|
|
5
|
+
roomId: string;
|
|
6
|
+
lastReadyChunk: number;
|
|
7
|
+
lastUsedChunk: number;
|
|
8
|
+
lastDiscardedChunk: number;
|
|
9
|
+
stt: {
|
|
10
|
+
[sttProvider: string]: {
|
|
11
|
+
lastReadyStt: number;
|
|
12
|
+
lastUsedStt: number;
|
|
13
|
+
lastDiscardedStt: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
translation: {
|
|
17
|
+
[language: string]: {
|
|
18
|
+
lastReadyTranslation: number;
|
|
19
|
+
lastUsedTranslation: number;
|
|
20
|
+
lastDiscardedTranslation: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
tts: {
|
|
24
|
+
[language: string]: {
|
|
25
|
+
lastReadyTts: number;
|
|
26
|
+
lastUsedTts: number;
|
|
27
|
+
lastDiscardedTts: number;
|
|
28
|
+
lastEmittedTts: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare class MulingstreamRoomTracker {
|
|
33
|
+
private redisClient;
|
|
34
|
+
constructor(redisClient: RedisClient);
|
|
35
|
+
private parseHashData;
|
|
36
|
+
/**
|
|
37
|
+
* Initializes a new MulingstreamRoomTracker for a given room.
|
|
38
|
+
* - The ID is `[roomId]`.
|
|
39
|
+
* - All "lastReady", "lastUsed", "lastDiscarded", etc. are set to -1 initially.
|
|
40
|
+
* - The sttProviders array is used to create stt sub-objects.
|
|
41
|
+
* - The targetLanguages array is used to create translation/tts sub-objects.
|
|
42
|
+
*/
|
|
43
|
+
initMulingstreamRoomTracker(params: {
|
|
44
|
+
roomId: string;
|
|
45
|
+
sttProviders: SttProvider[];
|
|
46
|
+
targetLanguages: string[];
|
|
47
|
+
}): Promise<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Retrieves all MulingstreamRoomTrackers by scanning keys `mulingstreamRoomTracker:*`.
|
|
50
|
+
*/
|
|
51
|
+
getMulingstreamRoomTrackers(): Promise<MulingstreamRoomData[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves a single MulingstreamRoomTracker by roomId.
|
|
54
|
+
*/
|
|
55
|
+
getMulingstreamRoomTracker(roomId: string): Promise<MulingstreamRoomData | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Updates the chunk-level tracker fields (lastReadyChunk, lastUsedChunk, lastDiscardedChunk).
|
|
58
|
+
* Any argument passed as null means "do not update that field."
|
|
59
|
+
*/
|
|
60
|
+
updateChunkTracker(roomId: string, lastReadyChunk: number | null, lastUsedChunk: number | null, lastDiscardedChunk: number | null): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Updates the stt tracker for a specific STT provider in the room.
|
|
63
|
+
* If any argument is null, we do not update that field.
|
|
64
|
+
*/
|
|
65
|
+
updateSttTracker(roomId: string, sttProvider: SttProvider, lastReadyStt: number | null, lastUsedStt: number | null, lastDiscardedStt: number | null): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Updates the translation tracker for a specific language in the room.
|
|
68
|
+
* If any argument is null, we do not update that field.
|
|
69
|
+
*/
|
|
70
|
+
updateTranslationTracker(roomId: string, language: string, lastReadyTranslation: number | null, lastUsedTranslation: number | null, lastDiscardedTranslation: number | null): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Updates the TTS tracker for a specific language in the room.
|
|
73
|
+
* If any argument is null, we do not update that field.
|
|
74
|
+
*/
|
|
75
|
+
updateTtsTracker(roomId: string, language: string, lastReadyTts: number | null, lastUsedTts: number | null, lastDiscardedTts: number | null, lastEmittedTts: number | null): Promise<boolean>;
|
|
76
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MulingstreamRoomTracker = void 0;
|
|
4
|
+
const EXPIRATION = 48 * 60 * 60; // 48 hours in seconds
|
|
5
|
+
class MulingstreamRoomTracker {
|
|
6
|
+
constructor(redisClient) {
|
|
7
|
+
this.redisClient = redisClient;
|
|
8
|
+
}
|
|
9
|
+
parseHashData(data) {
|
|
10
|
+
const stt = JSON.parse(data.stt);
|
|
11
|
+
const translation = JSON.parse(data.translation);
|
|
12
|
+
const tts = JSON.parse(data.tts);
|
|
13
|
+
return {
|
|
14
|
+
mulingstreamRoomTrackerId: data.mulingstreamRoomTrackerId,
|
|
15
|
+
roomId: data.roomId,
|
|
16
|
+
lastReadyChunk: parseInt(data.lastReadyChunk, 10),
|
|
17
|
+
lastUsedChunk: parseInt(data.lastUsedChunk, 10),
|
|
18
|
+
lastDiscardedChunk: parseInt(data.lastDiscardedChunk, 10),
|
|
19
|
+
stt,
|
|
20
|
+
translation,
|
|
21
|
+
tts
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Initializes a new MulingstreamRoomTracker for a given room.
|
|
26
|
+
* - The ID is `[roomId]`.
|
|
27
|
+
* - All "lastReady", "lastUsed", "lastDiscarded", etc. are set to -1 initially.
|
|
28
|
+
* - The sttProviders array is used to create stt sub-objects.
|
|
29
|
+
* - The targetLanguages array is used to create translation/tts sub-objects.
|
|
30
|
+
*/
|
|
31
|
+
async initMulingstreamRoomTracker(params) {
|
|
32
|
+
const { roomId, sttProviders, targetLanguages } = params;
|
|
33
|
+
// We'll use [roomId] as the unique ID
|
|
34
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
35
|
+
// Build stt object
|
|
36
|
+
// For each provider, set lastReadyStt = -1, lastUsedStt = -1, lastDiscardedStt = -1
|
|
37
|
+
const stt = {};
|
|
38
|
+
for (const provider of sttProviders) {
|
|
39
|
+
stt[provider] = {
|
|
40
|
+
lastReadyStt: -1,
|
|
41
|
+
lastUsedStt: -1,
|
|
42
|
+
lastDiscardedStt: -1
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Build translation object
|
|
46
|
+
// For each target language, set lastReadyTranslation = -1, lastUsedTranslation = -1, lastDiscardedTranslation = -1
|
|
47
|
+
const translation = {};
|
|
48
|
+
for (const lang of targetLanguages) {
|
|
49
|
+
translation[lang] = {
|
|
50
|
+
lastReadyTranslation: -1,
|
|
51
|
+
lastUsedTranslation: -1,
|
|
52
|
+
lastDiscardedTranslation: -1
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Build tts object
|
|
56
|
+
// For each target language, set lastReadyTts = -1, lastUsedTts = -1, lastDiscardedTts = -1, lastEmittedTts = -1
|
|
57
|
+
const tts = {};
|
|
58
|
+
for (const lang of targetLanguages) {
|
|
59
|
+
tts[lang] = {
|
|
60
|
+
lastReadyTts: -1,
|
|
61
|
+
lastUsedTts: -1,
|
|
62
|
+
lastDiscardedTts: -1,
|
|
63
|
+
lastEmittedTts: -1
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Root-level chunk trackers
|
|
67
|
+
const lastReadyChunk = -1;
|
|
68
|
+
const lastUsedChunk = -1;
|
|
69
|
+
const lastDiscardedChunk = -1;
|
|
70
|
+
// Store everything in Redis
|
|
71
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
72
|
+
mulingstreamRoomTrackerId,
|
|
73
|
+
roomId,
|
|
74
|
+
lastReadyChunk: lastReadyChunk.toString(),
|
|
75
|
+
lastUsedChunk: lastUsedChunk.toString(),
|
|
76
|
+
lastDiscardedChunk: lastDiscardedChunk.toString(),
|
|
77
|
+
stt: JSON.stringify(stt),
|
|
78
|
+
translation: JSON.stringify(translation),
|
|
79
|
+
tts: JSON.stringify(tts)
|
|
80
|
+
});
|
|
81
|
+
// set expiration
|
|
82
|
+
await this.redisClient.expire(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, EXPIRATION);
|
|
83
|
+
return mulingstreamRoomTrackerId;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Retrieves all MulingstreamRoomTrackers by scanning keys `mulingstreamRoomTracker:*`.
|
|
87
|
+
*/
|
|
88
|
+
async getMulingstreamRoomTrackers() {
|
|
89
|
+
const pattern = 'mulingstreamRoomTracker:*';
|
|
90
|
+
const keys = await this.redisClient.keys(pattern);
|
|
91
|
+
if (!keys || keys.length === 0) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
const results = [];
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
const data = await this.redisClient.hgetall(key);
|
|
97
|
+
if (data && data.mulingstreamRoomTrackerId) {
|
|
98
|
+
results.push(this.parseHashData(data));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Retrieves a single MulingstreamRoomTracker by roomId.
|
|
105
|
+
*/
|
|
106
|
+
async getMulingstreamRoomTracker(roomId) {
|
|
107
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
108
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
109
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return this.parseHashData(data);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Updates the chunk-level tracker fields (lastReadyChunk, lastUsedChunk, lastDiscardedChunk).
|
|
116
|
+
* Any argument passed as null means "do not update that field."
|
|
117
|
+
*/
|
|
118
|
+
async updateChunkTracker(roomId, lastReadyChunk, lastUsedChunk, lastDiscardedChunk) {
|
|
119
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
120
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
121
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const updates = {};
|
|
125
|
+
if (lastReadyChunk !== null) {
|
|
126
|
+
updates.lastReadyChunk = lastReadyChunk.toString();
|
|
127
|
+
}
|
|
128
|
+
if (lastUsedChunk !== null) {
|
|
129
|
+
updates.lastUsedChunk = lastUsedChunk.toString();
|
|
130
|
+
}
|
|
131
|
+
if (lastDiscardedChunk !== null) {
|
|
132
|
+
updates.lastDiscardedChunk = lastDiscardedChunk.toString();
|
|
133
|
+
}
|
|
134
|
+
if (Object.keys(updates).length > 0) {
|
|
135
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, updates);
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Updates the stt tracker for a specific STT provider in the room.
|
|
141
|
+
* If any argument is null, we do not update that field.
|
|
142
|
+
*/
|
|
143
|
+
async updateSttTracker(roomId, sttProvider, lastReadyStt, lastUsedStt, lastDiscardedStt) {
|
|
144
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
145
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
146
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const stt = JSON.parse(data.stt);
|
|
150
|
+
if (!stt[sttProvider]) {
|
|
151
|
+
return false; // The provider doesn't exist in this room
|
|
152
|
+
}
|
|
153
|
+
if (lastReadyStt !== null) {
|
|
154
|
+
stt[sttProvider].lastReadyStt = lastReadyStt;
|
|
155
|
+
}
|
|
156
|
+
if (lastUsedStt !== null) {
|
|
157
|
+
stt[sttProvider].lastUsedStt = lastUsedStt;
|
|
158
|
+
}
|
|
159
|
+
if (lastDiscardedStt !== null) {
|
|
160
|
+
stt[sttProvider].lastDiscardedStt = lastDiscardedStt;
|
|
161
|
+
}
|
|
162
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
163
|
+
stt: JSON.stringify(stt)
|
|
164
|
+
});
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Updates the translation tracker for a specific language in the room.
|
|
169
|
+
* If any argument is null, we do not update that field.
|
|
170
|
+
*/
|
|
171
|
+
async updateTranslationTracker(roomId, language, lastReadyTranslation, lastUsedTranslation, lastDiscardedTranslation) {
|
|
172
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
173
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
174
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
const translation = JSON.parse(data.translation);
|
|
178
|
+
if (!translation[language]) {
|
|
179
|
+
return false; // The language isn't tracked in this room
|
|
180
|
+
}
|
|
181
|
+
if (lastReadyTranslation !== null) {
|
|
182
|
+
translation[language].lastReadyTranslation = lastReadyTranslation;
|
|
183
|
+
}
|
|
184
|
+
if (lastUsedTranslation !== null) {
|
|
185
|
+
translation[language].lastUsedTranslation = lastUsedTranslation;
|
|
186
|
+
}
|
|
187
|
+
if (lastDiscardedTranslation !== null) {
|
|
188
|
+
translation[language].lastDiscardedTranslation = lastDiscardedTranslation;
|
|
189
|
+
}
|
|
190
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
191
|
+
translation: JSON.stringify(translation)
|
|
192
|
+
});
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Updates the TTS tracker for a specific language in the room.
|
|
197
|
+
* If any argument is null, we do not update that field.
|
|
198
|
+
*/
|
|
199
|
+
async updateTtsTracker(roomId, language, lastReadyTts, lastUsedTts, lastDiscardedTts, lastEmittedTts) {
|
|
200
|
+
const mulingstreamRoomTrackerId = `[${roomId}]`;
|
|
201
|
+
const data = await this.redisClient.hgetall(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`);
|
|
202
|
+
if (!data || !data.mulingstreamRoomTrackerId) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const tts = JSON.parse(data.tts);
|
|
206
|
+
if (!tts[language]) {
|
|
207
|
+
return false; // The language isn't tracked in this room
|
|
208
|
+
}
|
|
209
|
+
if (lastReadyTts !== null) {
|
|
210
|
+
tts[language].lastReadyTts = lastReadyTts;
|
|
211
|
+
}
|
|
212
|
+
if (lastUsedTts !== null) {
|
|
213
|
+
tts[language].lastUsedTts = lastUsedTts;
|
|
214
|
+
}
|
|
215
|
+
if (lastDiscardedTts !== null) {
|
|
216
|
+
tts[language].lastDiscardedTts = lastDiscardedTts;
|
|
217
|
+
}
|
|
218
|
+
if (lastEmittedTts !== null) {
|
|
219
|
+
tts[language].lastEmittedTts = lastEmittedTts;
|
|
220
|
+
}
|
|
221
|
+
await this.redisClient.hset(`mulingstreamRoomTracker:${mulingstreamRoomTrackerId}`, {
|
|
222
|
+
tts: JSON.stringify(tts)
|
|
223
|
+
});
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
exports.MulingstreamRoomTracker = MulingstreamRoomTracker;
|