@unisphere/models-sdk-js 1.2.1 → 1.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 +450 -80
- package/index.esm.js +61 -2
- package/package.json +1 -1
- package/src/KalturaAvatarSession.d.ts +9 -0
- package/src/control-sdk/AvatarControlSDK.d.ts +6 -0
package/README.md
CHANGED
|
@@ -1,134 +1,504 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Kaltura Avatar SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A TypeScript SDK for integrating with Kaltura's speech-to-video (STV) avatar service.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
7
|
+
- 🎭 **Simple API** - Create sessions and control avatars with just a few lines of code
|
|
8
|
+
- 🎥 **WebRTC Streaming** - Real-time avatar video via WHEP protocol
|
|
9
|
+
- 🗣️ **Text-to-Speech** - Automatic text chunking for optimal processing
|
|
10
|
+
- 🎵 **Audio Support** - Send audio files for avatar to speak
|
|
11
|
+
- 🔄 **Auto-Retry** - Built-in retry logic with exponential backoff
|
|
12
|
+
- 💓 **Keep-Alive** - Automatic session maintenance every 10 seconds
|
|
13
|
+
- 🎬 **Auto-Attach** - Optional automatic video element creation and attachment
|
|
14
|
+
- 🛠️ **TypeScript** - Full type safety and IntelliSense support
|
|
15
|
+
- 📦 **Modular** - Clean SDK-of-SDKs architecture
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
## Installation
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
```bash
|
|
20
|
+
npm install @unisphere/models-sdk-js
|
|
21
|
+
```
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
## Quick Start
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
### Option 1: Auto-Attach Video (Recommended)
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
```typescript
|
|
28
|
+
import { KalturaAvatarSession } from '@unisphere/models-sdk-js';
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
30
|
+
// 1. Create SDK instance
|
|
31
|
+
const session = new KalturaAvatarSession('your-api-key', {
|
|
32
|
+
baseUrl: 'https://api.avatar.example.com/v1/avatar-session'
|
|
33
|
+
});
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
// 2. Create session with auto-attach
|
|
36
|
+
// The SDK will automatically create a video element inside the container
|
|
37
|
+
await session.createSession({
|
|
38
|
+
avatarId: 'avatar-123',
|
|
39
|
+
voiceId: 'voice-456', // optional
|
|
40
|
+
videoContainerId: 'avatar-container' // ID of div element
|
|
41
|
+
});
|
|
30
42
|
|
|
31
|
-
|
|
43
|
+
// 3. Make avatar speak
|
|
44
|
+
await session.sayText('Hello from Kaltura Avatar!');
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
// 4. End session
|
|
47
|
+
await session.endSession();
|
|
48
|
+
```
|
|
34
49
|
|
|
35
|
-
|
|
50
|
+
### Option 2: Manual Attach
|
|
36
51
|
|
|
37
|
-
|
|
52
|
+
```typescript
|
|
53
|
+
import { KalturaAvatarSession } from '@unisphere/models-sdk-js';
|
|
38
54
|
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
// 1. Create SDK instance
|
|
56
|
+
const session = new KalturaAvatarSession('your-api-key', {
|
|
57
|
+
baseUrl: 'https://api.avatar.example.com/v1/avatar-session'
|
|
58
|
+
});
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
// 2. Create session
|
|
61
|
+
await session.createSession({
|
|
62
|
+
avatarId: 'avatar-123',
|
|
63
|
+
voiceId: 'voice-456' // optional
|
|
64
|
+
});
|
|
43
65
|
|
|
44
|
-
|
|
66
|
+
// 3. Attach video to container
|
|
67
|
+
// The SDK will create a video element inside the specified div
|
|
68
|
+
session.attachAvatar('avatar-container');
|
|
45
69
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- Runtimes
|
|
49
|
-
- Applications
|
|
70
|
+
// 4. Make avatar speak
|
|
71
|
+
await session.sayText('Hello from Kaltura Avatar!');
|
|
50
72
|
|
|
51
|
-
|
|
73
|
+
// 5. End session
|
|
74
|
+
await session.endSession();
|
|
75
|
+
```
|
|
52
76
|
|
|
53
|
-
|
|
54
|
-
https://unisphere.kaltura.com/docs/create/overview
|
|
77
|
+
**HTML Setup:**
|
|
55
78
|
|
|
56
|
-
|
|
79
|
+
```html
|
|
80
|
+
<div id="avatar-container" style="width: 512px; height: 512px;"></div>
|
|
81
|
+
```
|
|
57
82
|
|
|
58
|
-
##
|
|
83
|
+
## API Reference
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
https://unisphere.kaltura.com/docs/create/devops/deploy
|
|
85
|
+
### Constructor
|
|
62
86
|
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
```typescript
|
|
88
|
+
new KalturaAvatarSession(apiKey: string, config?: AvatarConfig)
|
|
89
|
+
```
|
|
65
90
|
|
|
66
|
-
|
|
67
|
-
|
|
91
|
+
**Parameters:**
|
|
92
|
+
- `apiKey` - Your Kaltura Avatar API key
|
|
93
|
+
- `config` - Optional configuration:
|
|
94
|
+
- `baseUrl` - Backend API URL (required for non-default endpoints)
|
|
95
|
+
- `iceServers` - Custom ICE servers for WebRTC (optional, defaults to backend-provided TURN servers)
|
|
96
|
+
- `iceTransportPolicy` - 'all' or 'relay' (default: 'all')
|
|
97
|
+
- `retryConfig` - Retry configuration for API calls and connections
|
|
98
|
+
- `logLevel` - 'debug' | 'info' | 'warn' | 'error' (default: 'info')
|
|
99
|
+
|
|
100
|
+
### Methods
|
|
101
|
+
|
|
102
|
+
#### `createSession(options: CreateSessionOptions): Promise<void>`
|
|
103
|
+
|
|
104
|
+
Create a new avatar session and establish WebRTC connection. Optionally auto-attach video to a container.
|
|
105
|
+
|
|
106
|
+
**Options:**
|
|
107
|
+
- `avatarId` (required) - ID of the avatar to use
|
|
108
|
+
- `voiceId` (optional) - ID of the voice to use for TTS
|
|
109
|
+
- `videoContainerId` (optional) - ID of the div element to automatically attach video to
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// With auto-attach
|
|
113
|
+
await session.createSession({
|
|
114
|
+
avatarId: 'avatar-123',
|
|
115
|
+
voiceId: 'voice-456', // optional
|
|
116
|
+
videoContainerId: 'avatar-container' // optional - auto-attach video
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Without auto-attach
|
|
120
|
+
await session.createSession({
|
|
121
|
+
avatarId: 'avatar-123',
|
|
122
|
+
voiceId: 'voice-456' // optional
|
|
123
|
+
});
|
|
124
|
+
```
|
|
68
125
|
|
|
69
|
-
|
|
126
|
+
**Note:** When a session is created, the SDK automatically:
|
|
127
|
+
1. Initializes the backend client
|
|
128
|
+
2. Retrieves WHEP URL and TURN server configuration from the backend
|
|
129
|
+
3. Establishes WebRTC connection
|
|
130
|
+
4. Starts automatic keep-alive (every 10 seconds)
|
|
131
|
+
5. Attaches video if `videoContainerId` is provided
|
|
70
132
|
|
|
71
|
-
|
|
133
|
+
#### `attachAvatar(containerId: string): void`
|
|
72
134
|
|
|
73
|
-
|
|
135
|
+
Attach avatar video to a container div by creating a video element inside it. If a video element already exists in the container, it will be reused.
|
|
74
136
|
|
|
75
|
-
|
|
76
|
-
-
|
|
77
|
-
- Watching and rebuilding artifacts
|
|
78
|
-
- Running applications in isolation or as part of a larger workspace
|
|
137
|
+
**Parameters:**
|
|
138
|
+
- `containerId` - ID of the div element to attach video to
|
|
79
139
|
|
|
80
|
-
|
|
81
|
-
|
|
140
|
+
```typescript
|
|
141
|
+
// The SDK will create/find a video element inside this div
|
|
142
|
+
session.attachAvatar('avatar-container');
|
|
143
|
+
```
|
|
82
144
|
|
|
83
|
-
|
|
145
|
+
**HTML:**
|
|
146
|
+
```html
|
|
147
|
+
<div id="avatar-container" style="width: 512px; height: 512px;">
|
|
148
|
+
<!-- Video element will be created here automatically -->
|
|
149
|
+
</div>
|
|
150
|
+
```
|
|
84
151
|
|
|
85
|
-
|
|
152
|
+
**Note:** The created video element will have:
|
|
153
|
+
- `autoplay` and `playsinline` attributes
|
|
154
|
+
- Width and height set to 100%
|
|
155
|
+
- Appropriate styling for fullscreen display within the container
|
|
86
156
|
|
|
87
|
-
|
|
157
|
+
#### `sayText(text: string): Promise<void>`
|
|
88
158
|
|
|
89
|
-
|
|
90
|
-
Lists all available commands for each artifact in the experience, including how to build and serve runtimes, and how to spawn applications.
|
|
159
|
+
Send text for the avatar to speak. Text is automatically chunked into 3-word segments.
|
|
91
160
|
|
|
92
|
-
```
|
|
93
|
-
|
|
161
|
+
```typescript
|
|
162
|
+
await session.sayText('Hello, how are you doing today?');
|
|
163
|
+
// Internally split into: ['Hello, how are', 'you doing today']
|
|
94
164
|
```
|
|
95
165
|
|
|
166
|
+
#### `say(mp3File: File | Blob): Promise<void>`
|
|
96
167
|
|
|
97
|
-
|
|
98
|
-
Displays information about the current workspace, including detected artifacts, configuration, and environment details.
|
|
168
|
+
Send audio file for the avatar to speak.
|
|
99
169
|
|
|
100
|
-
```
|
|
101
|
-
|
|
170
|
+
```typescript
|
|
171
|
+
const audioFile = new File([audioBlob], 'speech.mp3', { type: 'audio/mpeg' });
|
|
172
|
+
await session.say(audioFile);
|
|
102
173
|
```
|
|
103
174
|
|
|
104
|
-
|
|
105
|
-
Validates the workspace setup and verifies that all required artifacts and configurations are in place.
|
|
175
|
+
#### `interrupt(): Promise<void>`
|
|
106
176
|
|
|
107
|
-
|
|
108
|
-
|
|
177
|
+
Interrupt the avatar's current speech.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
await session.interrupt();
|
|
109
181
|
```
|
|
110
182
|
|
|
111
|
-
|
|
112
|
-
Resets the workspace to a clean state. Useful when experimenting or when local state becomes inconsistent.
|
|
183
|
+
#### `endSession(): Promise<void>`
|
|
113
184
|
|
|
114
|
-
|
|
115
|
-
|
|
185
|
+
End the session and cleanup resources. This will:
|
|
186
|
+
- Stop the automatic keep-alive interval
|
|
187
|
+
- Disconnect the WebRTC connection
|
|
188
|
+
- End the session on the backend
|
|
189
|
+
- Clean up all resources
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
await session.endSession();
|
|
116
193
|
```
|
|
117
194
|
|
|
195
|
+
### State Management
|
|
196
|
+
|
|
197
|
+
#### `getSessionId(): string | null`
|
|
198
|
+
|
|
199
|
+
Get the current session ID.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const sessionId = session.getSessionId();
|
|
203
|
+
console.log('Session ID:', sessionId);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### `getSessionState(): SessionState`
|
|
207
|
+
|
|
208
|
+
Get current session state.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const state = session.getSessionState();
|
|
212
|
+
// States: IDLE, CREATING, READY, ENDED, ERROR
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### `getConnectionState(): ConnectionState`
|
|
216
|
+
|
|
217
|
+
Get current WebRTC connection state.
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const connState = session.getConnectionState();
|
|
221
|
+
// States: DISCONNECTED, CONNECTING, CONNECTED, FAILED, CLOSED
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Events
|
|
225
|
+
|
|
226
|
+
#### `on(event: SessionEvent, callback: Function): void`
|
|
118
227
|
|
|
119
|
-
|
|
228
|
+
Register event handlers.
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// State changes
|
|
232
|
+
session.on('stateChange', (state: SessionState) => {
|
|
233
|
+
console.log('Session state:', state);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Connection changes
|
|
237
|
+
session.on('connectionChange', (state: ConnectionState) => {
|
|
238
|
+
console.log('Connection state:', state);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Errors
|
|
242
|
+
session.on('error', (error: AvatarError) => {
|
|
243
|
+
console.error('Error:', error.message, error.code);
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Complete Example
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import {
|
|
251
|
+
KalturaAvatarSession,
|
|
252
|
+
SessionState,
|
|
253
|
+
ConnectionState,
|
|
254
|
+
AvatarError,
|
|
255
|
+
} from '@unisphere/models-sdk-js';
|
|
256
|
+
|
|
257
|
+
async function runAvatarDemo() {
|
|
258
|
+
// Create SDK instance with custom config
|
|
259
|
+
const session = new KalturaAvatarSession('your-api-key', {
|
|
260
|
+
baseUrl: 'https://api.avatar.example.com/v1/avatar-session',
|
|
261
|
+
logLevel: 'info',
|
|
262
|
+
retryConfig: {
|
|
263
|
+
maxAttempts: 3,
|
|
264
|
+
initialDelayMs: 500,
|
|
265
|
+
maxDelayMs: 5000,
|
|
266
|
+
backoffMultiplier: 2,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Setup event listeners
|
|
271
|
+
session.on('stateChange', (state: SessionState) => {
|
|
272
|
+
console.log('Session state changed:', state);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
session.on('connectionChange', (state: ConnectionState) => {
|
|
276
|
+
console.log('Connection state changed:', state);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
session.on('error', (error: AvatarError) => {
|
|
280
|
+
console.error('SDK Error:', error.message);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
// Create session with auto-attach
|
|
285
|
+
console.log('Creating session...');
|
|
286
|
+
await session.createSession({
|
|
287
|
+
avatarId: 'avatar-123',
|
|
288
|
+
voiceId: 'voice-456',
|
|
289
|
+
videoContainerId: 'avatar-container' // Auto-attach to div
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Session is now ready, keep-alive started automatically
|
|
293
|
+
console.log('Session ID:', session.getSessionId());
|
|
294
|
+
console.log('Session State:', session.getSessionState());
|
|
295
|
+
console.log('Connection State:', session.getConnectionState());
|
|
296
|
+
|
|
297
|
+
// Say some text (automatically chunked)
|
|
298
|
+
await session.sayText('Welcome to Kaltura Avatar! How can I help you today?');
|
|
299
|
+
|
|
300
|
+
// Wait for user interaction...
|
|
301
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
302
|
+
|
|
303
|
+
// Interrupt if needed
|
|
304
|
+
await session.interrupt();
|
|
305
|
+
|
|
306
|
+
// Say something else
|
|
307
|
+
await session.sayText('Thanks for trying the Kaltura Avatar SDK!');
|
|
308
|
+
|
|
309
|
+
// Wait before ending
|
|
310
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
311
|
+
|
|
312
|
+
// End session (automatically stops keep-alive)
|
|
313
|
+
await session.endSession();
|
|
314
|
+
console.log('Session ended successfully');
|
|
315
|
+
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (error instanceof AvatarError) {
|
|
318
|
+
console.error('Avatar Error:', error.code, error.message);
|
|
319
|
+
} else {
|
|
320
|
+
console.error('Unexpected error:', error);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// HTML: <div id="avatar-container" style="width: 512px; height: 512px;"></div>
|
|
326
|
+
runAvatarDemo();
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Architecture
|
|
330
|
+
|
|
331
|
+
The SDK follows an "SDK of SDKs" pattern with clean separation of concerns:
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
KalturaAvatarSession (Public API)
|
|
335
|
+
├── AvatarControlSDK (HTTP/JWT)
|
|
336
|
+
│ ├── Session lifecycle (create, init, end)
|
|
337
|
+
│ ├── Avatar control (sayText, say, interrupt)
|
|
338
|
+
│ └── Keep-alive management
|
|
339
|
+
└── AvatarRTCSDK (WebRTC)
|
|
340
|
+
├── SignalingManager (WHEP protocol)
|
|
341
|
+
└── PeerConnectionManager (RTCPeerConnection)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Session Initialization Flow
|
|
345
|
+
|
|
346
|
+
1. **Create Session** - Establishes backend session and receives JWT token
|
|
347
|
+
2. **Init Client** - Retrieves WHEP URL and TURN server configuration from backend
|
|
348
|
+
3. **Initialize RTC SDK** - Creates RTC SDK with backend-provided configuration
|
|
349
|
+
4. **Connect WebRTC** - Establishes WHEP connection using backend-provided endpoint
|
|
350
|
+
5. **Start Keep-Alive** - Automatically sends keep-alive every 10 seconds
|
|
351
|
+
6. **Auto-Attach (optional)** - Creates video element if `videoContainerId` provided
|
|
352
|
+
|
|
353
|
+
### Benefits
|
|
354
|
+
|
|
355
|
+
- **Separation of Concerns** - Each layer has a single responsibility
|
|
356
|
+
- **Backend-Managed ICE Servers** - TURN configuration provided by backend, not client
|
|
357
|
+
- **Automatic Keep-Alive** - Session maintenance handled automatically
|
|
358
|
+
- **Testability** - Mock and test each layer independently
|
|
359
|
+
- **Extensibility** - Easy to add new features or swap implementations
|
|
360
|
+
- **Maintainability** - Clear boundaries and good TypeScript typing
|
|
361
|
+
|
|
362
|
+
## Advanced Usage
|
|
363
|
+
|
|
364
|
+
### Custom ICE Servers (Override Backend)
|
|
365
|
+
|
|
366
|
+
By default, ICE servers (TURN configuration) are provided by the backend during `initClient`. However, you can override them:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const session = new KalturaAvatarSession('your-api-key', {
|
|
370
|
+
baseUrl: 'https://api.avatar.example.com/v1/avatar-session',
|
|
371
|
+
// Override backend-provided ICE servers
|
|
372
|
+
iceServers: [
|
|
373
|
+
{ urls: 'stun:stun.example.com:19302' },
|
|
374
|
+
{
|
|
375
|
+
urls: ['turn:turn.example.com:80', 'turn:turn.example.com:443'],
|
|
376
|
+
username: 'user',
|
|
377
|
+
credential: 'pass',
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
iceTransportPolicy: 'relay', // Force TURN only
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Note:** The SDK will fall back to your custom ICE servers only if the backend doesn't provide them.
|
|
385
|
+
|
|
386
|
+
### Using Individual SDKs
|
|
387
|
+
|
|
388
|
+
For advanced use cases, you can use the underlying SDKs directly:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import {
|
|
392
|
+
AvatarControlSDK,
|
|
393
|
+
AvatarRTCSDK,
|
|
394
|
+
} from '@unisphere/models-sdk-js';
|
|
395
|
+
|
|
396
|
+
// HTTP control
|
|
397
|
+
const controlSDK = new AvatarControlSDK({
|
|
398
|
+
baseUrl: 'http://localhost:6100/v1/avatar-session',
|
|
399
|
+
apiKey: 'your-api-key',
|
|
400
|
+
logLevel: 'debug',
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Create session
|
|
404
|
+
const createRes = await controlSDK.createSession({
|
|
405
|
+
avatarId: 'avatar-123',
|
|
406
|
+
voiceId: 'voice-456',
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// Initialize client to get WHEP URL and TURN servers
|
|
410
|
+
const initRes = await controlSDK.initClient(createRes.sessionId);
|
|
411
|
+
|
|
412
|
+
// WebRTC - initialized with backend-provided config
|
|
413
|
+
const rtcSDK = new AvatarRTCSDK({
|
|
414
|
+
whepUrl: initRes.whepUrl,
|
|
415
|
+
iceServers: [{
|
|
416
|
+
urls: [
|
|
417
|
+
`turn:${initRes.turn.url}:80?transport=udp`,
|
|
418
|
+
`turn:${initRes.turn.url}:443?transport=udp`,
|
|
419
|
+
`turn:${initRes.turn.url}:80?transport=tcp`,
|
|
420
|
+
],
|
|
421
|
+
username: initRes.turn.username,
|
|
422
|
+
credential: initRes.turn.credential,
|
|
423
|
+
}],
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
await rtcSDK.connect();
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Keep-Alive Management
|
|
430
|
+
|
|
431
|
+
The SDK automatically sends keep-alive signals to the backend every 10 seconds once a session is created. This ensures:
|
|
432
|
+
- Session remains active
|
|
433
|
+
- Backend doesn't terminate idle sessions
|
|
434
|
+
- Resources are properly maintained
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
// Keep-alive is started automatically when session is READY
|
|
438
|
+
await session.createSession({
|
|
439
|
+
avatarId: 'avatar-123',
|
|
440
|
+
videoContainerId: 'avatar-container'
|
|
441
|
+
});
|
|
442
|
+
// Keep-alive now running every 10 seconds
|
|
443
|
+
|
|
444
|
+
// Keep-alive is stopped automatically when session ends
|
|
445
|
+
await session.endSession();
|
|
446
|
+
// Keep-alive stopped
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**Configuration:**
|
|
450
|
+
|
|
451
|
+
The keep-alive interval is set to 10 seconds and cannot be configured. If you need to manually manage keep-alive, use the `AvatarControlSDK` directly:
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
import { AvatarControlSDK } from '@unisphere/models-sdk-js';
|
|
455
|
+
|
|
456
|
+
const controlSDK = new AvatarControlSDK({
|
|
457
|
+
baseUrl: 'https://api.avatar.example.com/v1/avatar-session',
|
|
458
|
+
apiKey: 'your-api-key',
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Manual keep-alive
|
|
462
|
+
await controlSDK.keepAlive(sessionId);
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Error Handling
|
|
466
|
+
|
|
467
|
+
All errors are instances of `AvatarError` with a specific error code:
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
try {
|
|
471
|
+
await session.sayText('Hello');
|
|
472
|
+
} catch (error) {
|
|
473
|
+
if (error instanceof AvatarError) {
|
|
474
|
+
switch (error.code) {
|
|
475
|
+
case AvatarErrorCode.INVALID_STATE:
|
|
476
|
+
console.log('Wrong state - create session first');
|
|
477
|
+
break;
|
|
478
|
+
case AvatarErrorCode.API_REQUEST_FAILED:
|
|
479
|
+
console.log('API request failed - check network');
|
|
480
|
+
break;
|
|
481
|
+
case AvatarErrorCode.RTC_CONNECTION_FAILED:
|
|
482
|
+
console.log('WebRTC connection failed');
|
|
483
|
+
break;
|
|
484
|
+
default:
|
|
485
|
+
console.log('Error:', error.message);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
120
490
|
|
|
121
|
-
##
|
|
491
|
+
## Browser Support
|
|
122
492
|
|
|
123
|
-
|
|
493
|
+
- Chrome/Edge 80+
|
|
494
|
+
- Firefox 75+
|
|
495
|
+
- Safari 14+
|
|
496
|
+
- Requires WebRTC support
|
|
124
497
|
|
|
125
|
-
|
|
126
|
-
[https://unisphere.kaltura.com/docs/create/overview](https://unisphere.kaltura.com/docs/create/overview)
|
|
498
|
+
## License
|
|
127
499
|
|
|
128
|
-
|
|
500
|
+
AGPL-3.0
|
|
129
501
|
|
|
130
|
-
|
|
131
|
-
* Running it locally with a playground
|
|
132
|
-
* Understanding how runtimes, packages, and applications connect together
|
|
502
|
+
## Support
|
|
133
503
|
|
|
134
|
-
|
|
504
|
+
For issues and questions, please visit our [GitHub repository](https://github.com/kaltura/models).
|
package/index.esm.js
CHANGED
|
@@ -4547,6 +4547,29 @@ class AvatarControlSDK {
|
|
|
4547
4547
|
this.logger.info('Avatar interrupted successfully');
|
|
4548
4548
|
});
|
|
4549
4549
|
}
|
|
4550
|
+
/**
|
|
4551
|
+
* Send keep-alive signal to maintain session
|
|
4552
|
+
*
|
|
4553
|
+
* @param sessionId - Session identifier
|
|
4554
|
+
*/
|
|
4555
|
+
keepAlive(sessionId) {
|
|
4556
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4557
|
+
this.logger.debug('Sending keep-alive for session:', sessionId);
|
|
4558
|
+
this.ensureAuthenticated();
|
|
4559
|
+
const response = yield this.makeRequest(`/${sessionId}/keep-alive`, {
|
|
4560
|
+
method: 'POST',
|
|
4561
|
+
headers: {
|
|
4562
|
+
'Content-Type': 'application/json',
|
|
4563
|
+
'Authorization': `Bearer ${this.sessionToken}`
|
|
4564
|
+
},
|
|
4565
|
+
body: JSON.stringify({})
|
|
4566
|
+
});
|
|
4567
|
+
if (!response.success) {
|
|
4568
|
+
throw new AvatarError(response.error || 'Failed to send keep-alive', AvatarErrorCode.API_REQUEST_FAILED);
|
|
4569
|
+
}
|
|
4570
|
+
this.logger.debug('Keep-alive sent successfully');
|
|
4571
|
+
});
|
|
4572
|
+
}
|
|
4550
4573
|
/**
|
|
4551
4574
|
* End the avatar session
|
|
4552
4575
|
*
|
|
@@ -6483,6 +6506,10 @@ function generateTurnId() {
|
|
|
6483
6506
|
* Default base URL for avatar backend API
|
|
6484
6507
|
*/
|
|
6485
6508
|
const DEFAULT_BASE_URL = 'http://localhost:6100/v1/avatar-session';
|
|
6509
|
+
/**
|
|
6510
|
+
* Keep-alive interval in milliseconds (10 seconds)
|
|
6511
|
+
*/
|
|
6512
|
+
const KEEP_ALIVE_INTERVAL_MS = 10000;
|
|
6486
6513
|
/**
|
|
6487
6514
|
* Kaltura Avatar Session - Main SDK class
|
|
6488
6515
|
*/
|
|
@@ -6497,6 +6524,7 @@ class KalturaAvatarSession {
|
|
|
6497
6524
|
this.rtcSDK = null;
|
|
6498
6525
|
this.state = SessionState.IDLE;
|
|
6499
6526
|
this.sessionId = null;
|
|
6527
|
+
this.keepAliveInterval = null;
|
|
6500
6528
|
// Event handlers
|
|
6501
6529
|
this.eventHandlers = new Map();
|
|
6502
6530
|
if (!apiKey) {
|
|
@@ -6577,13 +6605,16 @@ class KalturaAvatarSession {
|
|
|
6577
6605
|
yield this.rtcSDK.connect();
|
|
6578
6606
|
this.setState(SessionState.READY);
|
|
6579
6607
|
this.logger.info('Session ready');
|
|
6580
|
-
// Step 5:
|
|
6608
|
+
// Step 5: Start keep-alive interval (every 10 seconds)
|
|
6609
|
+
this.startKeepAlive();
|
|
6610
|
+
// Step 6: Auto-attach video if container ID provided
|
|
6581
6611
|
if (options.videoContainerId) {
|
|
6582
6612
|
this.logger.info('Auto-attaching avatar to container:', options.videoContainerId);
|
|
6583
6613
|
this.attachAvatar(options.videoContainerId);
|
|
6584
6614
|
}
|
|
6585
6615
|
} catch (error) {
|
|
6586
6616
|
this.logger.error('Failed to create session:', error);
|
|
6617
|
+
this.stopKeepAlive(); // Stop keep-alive if it was started
|
|
6587
6618
|
this.setState(SessionState.ERROR);
|
|
6588
6619
|
this.emit('error', error);
|
|
6589
6620
|
throw error;
|
|
@@ -6690,7 +6721,9 @@ class KalturaAvatarSession {
|
|
|
6690
6721
|
}
|
|
6691
6722
|
this.setState(SessionState.ENDED);
|
|
6692
6723
|
try {
|
|
6693
|
-
//
|
|
6724
|
+
// Stop keep-alive interval
|
|
6725
|
+
this.stopKeepAlive();
|
|
6726
|
+
// Disconnect RTC
|
|
6694
6727
|
if (this.rtcSDK) {
|
|
6695
6728
|
this.rtcSDK.disconnect();
|
|
6696
6729
|
}
|
|
@@ -6773,6 +6806,32 @@ class KalturaAvatarSession {
|
|
|
6773
6806
|
this.setState(SessionState.ERROR);
|
|
6774
6807
|
});
|
|
6775
6808
|
}
|
|
6809
|
+
/**
|
|
6810
|
+
* Start keep-alive interval to maintain session
|
|
6811
|
+
*/
|
|
6812
|
+
startKeepAlive() {
|
|
6813
|
+
// Clear any existing interval
|
|
6814
|
+
this.stopKeepAlive();
|
|
6815
|
+
this.logger.info(`Starting keep-alive interval (every ${KEEP_ALIVE_INTERVAL_MS}ms)`);
|
|
6816
|
+
// Send keep-alive every 10 seconds
|
|
6817
|
+
this.keepAliveInterval = setInterval(() => {
|
|
6818
|
+
if (this.sessionId && this.state === SessionState.READY) {
|
|
6819
|
+
this.controlSDK.keepAlive(this.sessionId).catch(error => {
|
|
6820
|
+
this.logger.warn('Keep-alive failed:', error);
|
|
6821
|
+
});
|
|
6822
|
+
}
|
|
6823
|
+
}, KEEP_ALIVE_INTERVAL_MS);
|
|
6824
|
+
}
|
|
6825
|
+
/**
|
|
6826
|
+
* Stop keep-alive interval
|
|
6827
|
+
*/
|
|
6828
|
+
stopKeepAlive() {
|
|
6829
|
+
if (this.keepAliveInterval) {
|
|
6830
|
+
clearInterval(this.keepAliveInterval);
|
|
6831
|
+
this.keepAliveInterval = null;
|
|
6832
|
+
this.logger.info('Keep-alive interval stopped');
|
|
6833
|
+
}
|
|
6834
|
+
}
|
|
6776
6835
|
/**
|
|
6777
6836
|
* Ensure session is in READY state
|
|
6778
6837
|
* @throws AvatarError if not ready
|
package/package.json
CHANGED
|
@@ -34,6 +34,7 @@ export declare class KalturaAvatarSession {
|
|
|
34
34
|
private readonly config;
|
|
35
35
|
private state;
|
|
36
36
|
private sessionId;
|
|
37
|
+
private keepAliveInterval;
|
|
37
38
|
private eventHandlers;
|
|
38
39
|
/**
|
|
39
40
|
* Create a new Kaltura Avatar Session
|
|
@@ -111,6 +112,14 @@ export declare class KalturaAvatarSession {
|
|
|
111
112
|
* Setup event handlers for RTC SDK
|
|
112
113
|
*/
|
|
113
114
|
private setupRTCEventHandlers;
|
|
115
|
+
/**
|
|
116
|
+
* Start keep-alive interval to maintain session
|
|
117
|
+
*/
|
|
118
|
+
private startKeepAlive;
|
|
119
|
+
/**
|
|
120
|
+
* Stop keep-alive interval
|
|
121
|
+
*/
|
|
122
|
+
private stopKeepAlive;
|
|
114
123
|
/**
|
|
115
124
|
* Ensure session is in READY state
|
|
116
125
|
* @throws AvatarError if not ready
|
|
@@ -71,6 +71,12 @@ export declare class AvatarControlSDK {
|
|
|
71
71
|
* @param sessionId - Session identifier
|
|
72
72
|
*/
|
|
73
73
|
interrupt(sessionId: string): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Send keep-alive signal to maintain session
|
|
76
|
+
*
|
|
77
|
+
* @param sessionId - Session identifier
|
|
78
|
+
*/
|
|
79
|
+
keepAlive(sessionId: string): Promise<void>;
|
|
74
80
|
/**
|
|
75
81
|
* End the avatar session
|
|
76
82
|
*
|