@mulingai-npm/redis 3.4.1 → 3.5.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.
@@ -1,29 +1,29 @@
1
- import { RedisClient } from '../redis-client';
2
- export type MulingstreamListenerData = {
3
- listenerId: string;
4
- roomId: string;
5
- socketId?: string;
6
- token: string;
7
- name?: string;
8
- firstJoined: number;
9
- language?: string;
10
- isActive?: boolean;
11
- };
12
- export declare class MulingstreamListenerManager {
13
- private redisClient;
14
- constructor(redisClient: RedisClient);
15
- private parseHashData;
16
- addListener(listenerData: Omit<MulingstreamListenerData, 'listenerId'>): Promise<string>;
17
- getAllListeners(): Promise<MulingstreamListenerData[]>;
18
- getListenersByRoom(roomId: string): Promise<MulingstreamListenerData[]>;
19
- getListener(listenerIdOrToken: string): Promise<MulingstreamListenerData | null>;
20
- removeListenerByToken(token: string): Promise<boolean>;
21
- updateNameLanguage(listenerIdOrToken: string, name: string, language: string): Promise<boolean>;
22
- updateSocketId(listenerIdOrToken: string, socketId: string): Promise<boolean>;
23
- getTargetSocketIdsByRoomLanguage(roomId: string, language: string): Promise<string[]>;
24
- /**
25
- * Returns an array of unique languages for the given room, from highest frequency to lowest.
26
- * If a listener has no language (empty string / undefined), it is ignored.
27
- */
28
- getUniqueLanguagesByRoom(roomId: string): Promise<string[]>;
29
- }
1
+ import { RedisClient } from '../redis-client';
2
+ export type MulingstreamListenerData = {
3
+ listenerId: string;
4
+ roomId: string;
5
+ socketId?: string;
6
+ token: string;
7
+ name?: string;
8
+ firstJoined: number;
9
+ language?: string;
10
+ isActive?: boolean;
11
+ };
12
+ export declare class MulingstreamListenerManager {
13
+ private redisClient;
14
+ constructor(redisClient: RedisClient);
15
+ private parseHashData;
16
+ addListener(listenerData: Omit<MulingstreamListenerData, 'listenerId'>): Promise<string>;
17
+ getAllListeners(): Promise<MulingstreamListenerData[]>;
18
+ getListenersByRoom(roomId: string): Promise<MulingstreamListenerData[]>;
19
+ getListener(listenerIdOrToken: string): Promise<MulingstreamListenerData | null>;
20
+ removeListenerByToken(token: string): Promise<boolean>;
21
+ updateNameLanguage(listenerIdOrToken: string, name: string, language: string): Promise<boolean>;
22
+ updateSocketId(listenerIdOrToken: string, socketId: string): Promise<boolean>;
23
+ getTargetSocketIdsByRoomLanguage(roomId: string, language: string): Promise<string[]>;
24
+ /**
25
+ * Returns an array of unique languages for the given room, from highest frequency to lowest.
26
+ * If a listener has no language (empty string / undefined), it is ignored.
27
+ */
28
+ getUniqueLanguagesByRoom(roomId: string): Promise<string[]>;
29
+ }
@@ -1,169 +1,169 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MulingstreamListenerManager = void 0;
4
- const uuid_1 = require("uuid");
5
- const EXPIRATION = 24 * 60 * 60; // 24 hours in seconds
6
- class MulingstreamListenerManager {
7
- constructor(redisClient) {
8
- this.redisClient = redisClient;
9
- }
10
- parseHashData(data) {
11
- return {
12
- listenerId: data.listenerId,
13
- roomId: data.roomId,
14
- socketId: data.socketId || '',
15
- token: data.token,
16
- name: data.name || '',
17
- firstJoined: parseInt(data.firstJoined, 10),
18
- language: data.language || '',
19
- isActive: data.isActive === 'true'
20
- };
21
- }
22
- // Creates a new listener.
23
- // -Generates a unique listenerId and adds it to the room's set.
24
- // -Stores the listener data in a Redis hash under `listener:{listenerId}`.
25
- async addListener(listenerData) {
26
- var _a, _b, _c;
27
- // You can combine roomId + uuid, or just a uuid, up to you:
28
- const listenerId = `[${listenerData.roomId}]-[${(0, uuid_1.v4)()}]`;
29
- // Add listenerId to the set for that room
30
- await this.redisClient.sadd(`room:${listenerData.roomId}:listeners`, listenerId);
31
- // Store listener data in a hash
32
- await this.redisClient.hset(`listener:${listenerId}`, {
33
- listenerId,
34
- roomId: listenerData.roomId,
35
- socketId: (_a = listenerData.socketId) !== null && _a !== void 0 ? _a : '',
36
- token: listenerData.token,
37
- name: (_b = listenerData.name) !== null && _b !== void 0 ? _b : '',
38
- firstJoined: listenerData.firstJoined.toString(),
39
- language: (_c = listenerData.language) !== null && _c !== void 0 ? _c : '',
40
- isActive: 'true'
41
- });
42
- // expire listener
43
- await this.redisClient.expire(`listener:${listenerId}`, EXPIRATION);
44
- return listenerId;
45
- }
46
- async getAllListeners() {
47
- // get all keys that match 'listener:*'
48
- // TODO: if we have many keys, consider using SCAN instead of KEYS to avoid performance issues.
49
- const keys = await this.redisClient.keys('listener:*');
50
- if (!keys || keys.length === 0) {
51
- return [];
52
- }
53
- // fetch each hash with HGETALL
54
- const listeners = [];
55
- for (const key of keys) {
56
- const data = await this.redisClient.hgetall(key);
57
- listeners.push(this.parseHashData(data));
58
- }
59
- return listeners;
60
- }
61
- async getListenersByRoom(roomId) {
62
- // get the set of listener IDs
63
- const listenerIds = await this.redisClient.smembers(`room:${roomId}:listeners`);
64
- if (!listenerIds || listenerIds.length === 0) {
65
- return [];
66
- }
67
- const listeners = [];
68
- for (const id of listenerIds) {
69
- // get hash data
70
- const data = await this.redisClient.hgetall(`listener:${id}`);
71
- // if the hash doesn't exist, it may have expired, so remove from the set
72
- if (!data || Object.keys(data).length === 0) {
73
- await this.redisClient.srem(`room:${roomId}:listeners`, id);
74
- continue;
75
- }
76
- // otherwise, parse and keep it
77
- listeners.push(this.parseHashData(data));
78
- }
79
- return listeners;
80
- }
81
- async getListener(listenerIdOrToken) {
82
- // try direct lookup by listenerId:
83
- const directData = await this.redisClient.hgetall(`listener:${listenerIdOrToken}`);
84
- if (directData && directData.listenerId) {
85
- return this.parseHashData(directData);
86
- }
87
- // if not found by listenerId, treat 'listenerIdOrToken' as a token and scan all listeners
88
- const keys = await this.redisClient.keys('listener:*');
89
- if (!keys || keys.length === 0) {
90
- return null;
91
- }
92
- for (const key of keys) {
93
- const data = await this.redisClient.hgetall(key);
94
- if (data && data.token === listenerIdOrToken) {
95
- return this.parseHashData(data);
96
- }
97
- }
98
- return null;
99
- }
100
- async removeListenerByToken(token) {
101
- // find the listener by token
102
- const listener = await this.getListener(token);
103
- if (!listener) {
104
- return false;
105
- }
106
- // remove the listener ID from the room's set
107
- await this.redisClient.srem(`room:${listener.roomId}:listeners`, listener.listenerId);
108
- // remove the listener data (the hash)
109
- await this.redisClient.del(`listener:${listener.listenerId}`);
110
- return true;
111
- }
112
- async updateNameLanguage(listenerIdOrToken, name, language) {
113
- const listener = await this.getListener(listenerIdOrToken);
114
- if (!listener) {
115
- return false;
116
- }
117
- // Update the name/language on the listener’s hash key
118
- await this.redisClient.hset(`listener:${listener.listenerId}`, {
119
- name,
120
- language
121
- });
122
- return true;
123
- }
124
- async updateSocketId(listenerIdOrToken, socketId) {
125
- const listener = await this.getListener(listenerIdOrToken);
126
- if (!listener) {
127
- return false;
128
- }
129
- // Update the name/language on the listener’s hash key
130
- await this.redisClient.hset(`listener:${listener.listenerId}`, {
131
- socketId
132
- });
133
- return true;
134
- }
135
- async getTargetSocketIdsByRoomLanguage(roomId, language) {
136
- const listeners = await this.getListenersByRoom(roomId);
137
- const filteredListeners = listeners.filter((listener) => {
138
- return listener.isActive === true && listener.language === language && listener.socketId && listener.socketId.trim() !== '';
139
- });
140
- return filteredListeners.map((listener) => listener.socketId);
141
- }
142
- /**
143
- * Returns an array of unique languages for the given room, from highest frequency to lowest.
144
- * If a listener has no language (empty string / undefined), it is ignored.
145
- */
146
- async getUniqueLanguagesByRoom(roomId) {
147
- // 1) Fetch all listeners for the room.
148
- // (This includes inactive ones, but feel free to filter out isActive === false if you only want active ones.)
149
- const listeners = await this.getListenersByRoom(roomId);
150
- // if I wanted to get only active listeners
151
- // const listeners = (await this.getListenersByRoom(roomId)).filter(l => l.isActive);
152
- // 2) Count how many times each language appears.
153
- const languageCountMap = {};
154
- for (const listener of listeners) {
155
- // skip blank/unset language
156
- if (!listener.language || !listener.language.trim()) {
157
- continue;
158
- }
159
- // increment count
160
- const lang = listener.language;
161
- languageCountMap[lang] = (languageCountMap[lang] || 0) + 1;
162
- }
163
- // 3) Sort by frequency descending: highest count first
164
- const sortedEntries = Object.entries(languageCountMap).sort(([, countA], [, countB]) => countB - countA);
165
- // 4) Map back to just the language strings in order
166
- return sortedEntries.map(([language]) => language);
167
- }
168
- }
169
- exports.MulingstreamListenerManager = MulingstreamListenerManager;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MulingstreamListenerManager = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const EXPIRATION = 24 * 60 * 60; // 24 hours in seconds
6
+ class MulingstreamListenerManager {
7
+ constructor(redisClient) {
8
+ this.redisClient = redisClient;
9
+ }
10
+ parseHashData(data) {
11
+ return {
12
+ listenerId: data.listenerId,
13
+ roomId: data.roomId,
14
+ socketId: data.socketId || '',
15
+ token: data.token,
16
+ name: data.name || '',
17
+ firstJoined: parseInt(data.firstJoined, 10),
18
+ language: data.language || '',
19
+ isActive: data.isActive === 'true'
20
+ };
21
+ }
22
+ // Creates a new listener.
23
+ // -Generates a unique listenerId and adds it to the room's set.
24
+ // -Stores the listener data in a Redis hash under `listener:{listenerId}`.
25
+ async addListener(listenerData) {
26
+ var _a, _b, _c;
27
+ // You can combine roomId + uuid, or just a uuid, up to you:
28
+ const listenerId = `[${listenerData.roomId}]-[${(0, uuid_1.v4)()}]`;
29
+ // Add listenerId to the set for that room
30
+ await this.redisClient.sadd(`room:${listenerData.roomId}:listeners`, listenerId);
31
+ // Store listener data in a hash
32
+ await this.redisClient.hset(`listener:${listenerId}`, {
33
+ listenerId,
34
+ roomId: listenerData.roomId,
35
+ socketId: (_a = listenerData.socketId) !== null && _a !== void 0 ? _a : '',
36
+ token: listenerData.token,
37
+ name: (_b = listenerData.name) !== null && _b !== void 0 ? _b : '',
38
+ firstJoined: listenerData.firstJoined.toString(),
39
+ language: (_c = listenerData.language) !== null && _c !== void 0 ? _c : '',
40
+ isActive: 'true'
41
+ });
42
+ // expire listener
43
+ await this.redisClient.expire(`listener:${listenerId}`, EXPIRATION);
44
+ return listenerId;
45
+ }
46
+ async getAllListeners() {
47
+ // get all keys that match 'listener:*'
48
+ // TODO: if we have many keys, consider using SCAN instead of KEYS to avoid performance issues.
49
+ const keys = await this.redisClient.keys('listener:*');
50
+ if (!keys || keys.length === 0) {
51
+ return [];
52
+ }
53
+ // fetch each hash with HGETALL
54
+ const listeners = [];
55
+ for (const key of keys) {
56
+ const data = await this.redisClient.hgetall(key);
57
+ listeners.push(this.parseHashData(data));
58
+ }
59
+ return listeners;
60
+ }
61
+ async getListenersByRoom(roomId) {
62
+ // get the set of listener IDs
63
+ const listenerIds = await this.redisClient.smembers(`room:${roomId}:listeners`);
64
+ if (!listenerIds || listenerIds.length === 0) {
65
+ return [];
66
+ }
67
+ const listeners = [];
68
+ for (const id of listenerIds) {
69
+ // get hash data
70
+ const data = await this.redisClient.hgetall(`listener:${id}`);
71
+ // if the hash doesn't exist, it may have expired, so remove from the set
72
+ if (!data || Object.keys(data).length === 0) {
73
+ await this.redisClient.srem(`room:${roomId}:listeners`, id);
74
+ continue;
75
+ }
76
+ // otherwise, parse and keep it
77
+ listeners.push(this.parseHashData(data));
78
+ }
79
+ return listeners;
80
+ }
81
+ async getListener(listenerIdOrToken) {
82
+ // try direct lookup by listenerId:
83
+ const directData = await this.redisClient.hgetall(`listener:${listenerIdOrToken}`);
84
+ if (directData && directData.listenerId) {
85
+ return this.parseHashData(directData);
86
+ }
87
+ // if not found by listenerId, treat 'listenerIdOrToken' as a token and scan all listeners
88
+ const keys = await this.redisClient.keys('listener:*');
89
+ if (!keys || keys.length === 0) {
90
+ return null;
91
+ }
92
+ for (const key of keys) {
93
+ const data = await this.redisClient.hgetall(key);
94
+ if (data && data.token === listenerIdOrToken) {
95
+ return this.parseHashData(data);
96
+ }
97
+ }
98
+ return null;
99
+ }
100
+ async removeListenerByToken(token) {
101
+ // find the listener by token
102
+ const listener = await this.getListener(token);
103
+ if (!listener) {
104
+ return false;
105
+ }
106
+ // remove the listener ID from the room's set
107
+ await this.redisClient.srem(`room:${listener.roomId}:listeners`, listener.listenerId);
108
+ // remove the listener data (the hash)
109
+ await this.redisClient.del(`listener:${listener.listenerId}`);
110
+ return true;
111
+ }
112
+ async updateNameLanguage(listenerIdOrToken, name, language) {
113
+ const listener = await this.getListener(listenerIdOrToken);
114
+ if (!listener) {
115
+ return false;
116
+ }
117
+ // Update the name/language on the listener’s hash key
118
+ await this.redisClient.hset(`listener:${listener.listenerId}`, {
119
+ name,
120
+ language
121
+ });
122
+ return true;
123
+ }
124
+ async updateSocketId(listenerIdOrToken, socketId) {
125
+ const listener = await this.getListener(listenerIdOrToken);
126
+ if (!listener) {
127
+ return false;
128
+ }
129
+ // Update the name/language on the listener’s hash key
130
+ await this.redisClient.hset(`listener:${listener.listenerId}`, {
131
+ socketId
132
+ });
133
+ return true;
134
+ }
135
+ async getTargetSocketIdsByRoomLanguage(roomId, language) {
136
+ const listeners = await this.getListenersByRoom(roomId);
137
+ const filteredListeners = listeners.filter((listener) => {
138
+ return listener.isActive === true && listener.language === language && listener.socketId && listener.socketId.trim() !== '';
139
+ });
140
+ return filteredListeners.map((listener) => listener.socketId);
141
+ }
142
+ /**
143
+ * Returns an array of unique languages for the given room, from highest frequency to lowest.
144
+ * If a listener has no language (empty string / undefined), it is ignored.
145
+ */
146
+ async getUniqueLanguagesByRoom(roomId) {
147
+ // 1) Fetch all listeners for the room.
148
+ // (This includes inactive ones, but feel free to filter out isActive === false if you only want active ones.)
149
+ const listeners = await this.getListenersByRoom(roomId);
150
+ // if I wanted to get only active listeners
151
+ // const listeners = (await this.getListenersByRoom(roomId)).filter(l => l.isActive);
152
+ // 2) Count how many times each language appears.
153
+ const languageCountMap = {};
154
+ for (const listener of listeners) {
155
+ // skip blank/unset language
156
+ if (!listener.language || !listener.language.trim()) {
157
+ continue;
158
+ }
159
+ // increment count
160
+ const lang = listener.language;
161
+ languageCountMap[lang] = (languageCountMap[lang] || 0) + 1;
162
+ }
163
+ // 3) Sort by frequency descending: highest count first
164
+ const sortedEntries = Object.entries(languageCountMap).sort(([, countA], [, countB]) => countB - countA);
165
+ // 4) Map back to just the language strings in order
166
+ return sortedEntries.map(([language]) => language);
167
+ }
168
+ }
169
+ exports.MulingstreamListenerManager = MulingstreamListenerManager;
@@ -1,37 +1,44 @@
1
- import { RedisClient } from '../redis-client';
2
- export type MulingstreamSpeakerData = {
3
- speakerId: string;
4
- userId: string;
5
- roomId: string;
6
- socketId: string;
7
- sourceLanguage: string;
8
- theme: string;
9
- prompts: string;
10
- recordingsDuration: number;
11
- targetLanguages: string[];
12
- timestamp: string;
13
- };
14
- export declare class MulingstreamSpeakerManager {
15
- private redisClient;
16
- constructor(redisClient: RedisClient);
17
- private parseHash;
18
- private serialize;
19
- private buildId;
20
- private buildKey;
21
- addSpeaker(payload: Omit<MulingstreamSpeakerData, 'speakerId' | 'timestamp'>): Promise<string>;
22
- removeSpeakerBySocketId(socketId: string): Promise<boolean>;
23
- removeSpeakersByUserId(userId: string): Promise<number>;
24
- removeSpeakersByRoomId(roomId: string): Promise<number>;
25
- private removeSpeakerById;
26
- getSpeakerBySpeakerId(speakerId: string): Promise<MulingstreamSpeakerData | null>;
27
- getSpeakerBySocketId(socketId: string): Promise<MulingstreamSpeakerData | null>;
28
- getSpeakersByRoomId(roomId: string): Promise<MulingstreamSpeakerData[]>;
29
- getSpeakersByUserId(userId: string): Promise<MulingstreamSpeakerData[]>;
30
- getSpeakerByUserRoom(roomId: string, userId: string): Promise<MulingstreamSpeakerData | null>;
31
- getSpeakersLength(roomId: string): Promise<number>;
32
- isRoomEmpty(roomId: string): Promise<boolean>;
33
- updateSourceLanguage(socketId: string, newLang: string): Promise<boolean>;
34
- updateTargetLanguages(socketId: string, languages: string[]): Promise<boolean>;
35
- increaseRecordingDuration(socketId: string, delta: number): Promise<number>;
36
- private cleanIndexes;
37
- }
1
+ import { RedisClient } from '../redis-client';
2
+ export type MulingstreamSpeakerData = {
3
+ speakerId: string;
4
+ userId: string;
5
+ roomId: string;
6
+ socketId: string;
7
+ sourceLanguage: string;
8
+ theme: string;
9
+ prompts: string;
10
+ recordingsDuration: number;
11
+ targetLanguages: string[];
12
+ timestamp: string;
13
+ };
14
+ export declare class MulingstreamSpeakerManager {
15
+ private redisClient;
16
+ constructor(redisClient: RedisClient);
17
+ private buildLockKey;
18
+ /** Try to claim the speaker slot for this room.
19
+ * @returns true when the lock was acquired, false if someone else holds it.
20
+ */
21
+ acquireRoomLock(roomId: string, socketId: string, ttl?: number): Promise<boolean>;
22
+ /** Release the lock if we still own it. */
23
+ releaseRoomLock(roomId: string, socketId: string): Promise<void>;
24
+ private parseHash;
25
+ private serialize;
26
+ private buildId;
27
+ private buildKey;
28
+ addSpeaker(payload: Omit<MulingstreamSpeakerData, 'speakerId' | 'timestamp'>): Promise<string>;
29
+ removeSpeakerBySocketId(socketId: string): Promise<boolean>;
30
+ removeSpeakersByUserId(userId: string): Promise<number>;
31
+ removeSpeakersByRoomId(roomId: string): Promise<number>;
32
+ private removeSpeakerById;
33
+ getSpeakerBySpeakerId(speakerId: string): Promise<MulingstreamSpeakerData | null>;
34
+ getSpeakerBySocketId(socketId: string): Promise<MulingstreamSpeakerData | null>;
35
+ getSpeakersByRoomId(roomId: string): Promise<MulingstreamSpeakerData[]>;
36
+ getSpeakersByUserId(userId: string): Promise<MulingstreamSpeakerData[]>;
37
+ getSpeakerByUserRoom(roomId: string, userId: string): Promise<MulingstreamSpeakerData | null>;
38
+ getSpeakersLength(roomId: string): Promise<number>;
39
+ isRoomEmpty(roomId: string): Promise<boolean>;
40
+ updateSourceLanguage(socketId: string, newLang: string): Promise<boolean>;
41
+ updateTargetLanguages(socketId: string, languages: string[]): Promise<boolean>;
42
+ increaseRecordingDuration(socketId: string, delta: number): Promise<number>;
43
+ private cleanIndexes;
44
+ }