@redseat/api 0.3.5 → 0.3.7

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/server.md CHANGED
@@ -1,277 +1,538 @@
1
- # ServerApi
2
-
3
- The `ServerApi` class provides server-level operations for managing libraries, settings, plugins, and credentials.
4
-
5
- ## Overview
6
-
7
- `ServerApi` handles operations that are not specific to a single library, such as:
8
- - Listing and creating libraries
9
- - Getting current user information
10
- - Managing server settings
11
- - Listing plugins and credentials
12
- - Adding credentials to libraries
13
-
14
- ## Constructor
15
-
16
- ```typescript
17
- new ServerApi(client: RedseatClient)
18
- ```
19
-
20
- **Parameters:**
21
- - `client`: An initialized `RedseatClient` instance
22
-
23
- **Example:**
24
- ```typescript
25
- import { RedseatClient, ServerApi } from '@redseat/api';
26
-
27
- const client = new RedseatClient({ /* ... */ });
28
- const serverApi = new ServerApi(client);
29
- ```
30
-
31
- ## Methods
32
-
33
- ### `getLibraries(): Promise<ILibrary[]>`
34
-
35
- Retrieves all libraries available to the current user.
36
-
37
- **Returns:** Promise resolving to an array of `ILibrary` objects
38
-
39
- **Example:**
40
- ```typescript
41
- const libraries = await serverApi.getLibraries();
42
- libraries.forEach(library => {
43
- console.log(`Library: ${library.name} (${library.type})`);
44
- });
45
- ```
46
-
47
- ### `getMe(): Promise<any>`
48
-
49
- Gets information about the current authenticated user.
50
-
51
- **Returns:** Promise resolving to user information object
52
-
53
- **Example:**
54
- ```typescript
55
- const user = await serverApi.getMe();
56
- console.log(`Logged in as: ${user.name}`);
57
- ```
58
-
59
- ### `addLibrary(library: Partial<ILibrary>): Promise<ILibrary>`
60
-
61
- Creates a new library.
62
-
63
- **Parameters:**
64
- - `library`: Partial library object with required fields:
65
- - `name`: Library name (required)
66
- - `type`: Library type - `'photos'`, `'shows'`, `'movies'`, or `'iptv'` (required)
67
- - `source`: Optional source type
68
- - `crypt`: Optional boolean to enable encryption
69
- - `hidden`: Optional boolean to hide library
70
-
71
- **Returns:** Promise resolving to the created `ILibrary` object with generated `id`
72
-
73
- **Example:**
74
- ```typescript
75
- const newLibrary = await serverApi.addLibrary({
76
- name: 'My Photos',
77
- type: 'photos',
78
- crypt: true // Enable encryption
79
- });
80
- console.log(`Created library with ID: ${newLibrary.id}`);
81
- ```
82
-
83
- ### `getSetting(key: string): Promise<string>`
84
-
85
- Retrieves a server setting value by key.
86
-
87
- **Parameters:**
88
- - `key`: The setting key to retrieve
89
-
90
- **Returns:** Promise resolving to the setting value as a string
91
-
92
- **Example:**
93
- ```typescript
94
- const maxUploadSize = await serverApi.getSetting('max_upload_size');
95
- console.log(`Max upload size: ${maxUploadSize}`);
96
- ```
97
-
98
- ### `getPlugins(): Promise<any[]>`
99
-
100
- Retrieves all available plugins on the server.
101
-
102
- **Returns:** Promise resolving to an array of plugin objects
103
-
104
- **Example:**
105
- ```typescript
106
- const plugins = await serverApi.getPlugins();
107
- plugins.forEach(plugin => {
108
- console.log(`Plugin: ${plugin.name} - ${plugin.description}`);
109
- });
110
- ```
111
-
112
- ### `getCredentials(): Promise<ICredential[]>`
113
-
114
- Retrieves all available credentials configured on the server.
115
-
116
- **Returns:** Promise resolving to an array of `ICredential` objects
117
-
118
- **Example:**
119
- ```typescript
120
- const credentials = await serverApi.getCredentials();
121
- credentials.forEach(cred => {
122
- console.log(`Credential: ${cred.name} (${cred.type.type})`);
123
- });
124
- ```
125
-
126
- ### `saveCredential(credential: ICredential): Promise<ICredential>`
127
-
128
- Creates a new credential on the server.
129
-
130
- **Parameters:**
131
- - `credential`: Credential object with required fields:
132
- - `name`: Credential name (required)
133
- - `source`: Plugin/source name (required)
134
- - `type`: Authentication type object (required)
135
- - `settings`: Settings record for plugin params (required)
136
- - `login`: Optional login/username
137
- - `password`: Optional password/token
138
-
139
- **Returns:** Promise resolving to the created `ICredential` object with generated `id`
140
-
141
- **Example:**
142
- ```typescript
143
- const newCredential = await serverApi.saveCredential({
144
- name: 'My API Key',
145
- source: 'jackett',
146
- type: { type: PluginAuthType.Token },
147
- settings: { url: 'http://localhost:9117' },
148
- password: 'my-api-key'
149
- });
150
- console.log(`Created credential with ID: ${newCredential.id}`);
151
- ```
152
-
153
- ### `updateCredential(credential: ICredential): Promise<ICredential>`
154
-
155
- Updates an existing credential.
156
-
157
- **Parameters:**
158
- - `credential`: Credential object with `id` and fields to update
159
-
160
- **Returns:** Promise resolving to the updated `ICredential` object
161
-
162
- **Example:**
163
- ```typescript
164
- const updated = await serverApi.updateCredential({
165
- id: 'cred-123',
166
- name: 'Updated Name',
167
- source: 'jackett',
168
- type: { type: PluginAuthType.Token },
169
- settings: { url: 'http://localhost:9117' },
170
- password: 'new-api-key'
171
- });
172
- ```
173
-
174
- ### `deleteCredential(id: string): Promise<void>`
175
-
176
- Deletes a credential by ID.
177
-
178
- **Parameters:**
179
- - `id`: The credential ID to delete
180
-
181
- **Returns:** Promise resolving when deletion is complete
182
-
183
- **Example:**
184
- ```typescript
185
- await serverApi.deleteCredential('cred-123');
186
- ```
187
-
188
- ### `saveOAuthCredentials(pluginId: string, params: Record<string, string>): Promise<void>`
189
-
190
- Exchanges OAuth tokens and saves credentials for a plugin.
191
-
192
- **Parameters:**
193
- - `pluginId`: The plugin ID to save credentials for
194
- - `params`: OAuth parameters including tokens and name
195
-
196
- **Returns:** Promise resolving when credentials are saved
197
-
198
- **Example:**
199
- ```typescript
200
- await serverApi.saveOAuthCredentials('trakt', {
201
- name: 'My Trakt Account',
202
- code: 'oauth-code',
203
- access_token: 'token123'
204
- });
205
- ```
206
-
207
- ### `addLibraryCredential(credential: any): Promise<ILibrary>`
208
-
209
- Adds a credential to a library. This is used to configure external service access for libraries.
210
-
211
- **Parameters:**
212
- - `credential`: Credential configuration object (structure depends on credential type)
213
-
214
- **Returns:** Promise resolving to the updated `ILibrary` object
215
-
216
- **Example:**
217
- ```typescript
218
- const library = await serverApi.addLibraryCredential({
219
- libraryId: 'library-123',
220
- credentialId: 'credential-456',
221
- // Additional credential-specific configuration
222
- });
223
- ```
224
-
225
- ## Usage Examples
226
-
227
- ### Complete Workflow: Create Library and Get Info
228
-
229
- ```typescript
230
- import { RedseatClient, ServerApi } from '@redseat/api';
231
-
232
- // Initialize
233
- const client = new RedseatClient({ /* ... */ });
234
- const serverApi = new ServerApi(client);
235
-
236
- // Get current user
237
- const user = await serverApi.getMe();
238
- console.log(`User: ${user.name}`);
239
-
240
- // List existing libraries
241
- const libraries = await serverApi.getLibraries();
242
- console.log(`Found ${libraries.length} libraries`);
243
-
244
- // Create a new encrypted photo library
245
- const newLibrary = await serverApi.addLibrary({
246
- name: 'Encrypted Photos',
247
- type: 'photos',
248
- crypt: true
249
- });
250
-
251
- // Get server settings
252
- const maxUpload = await serverApi.getSetting('max_upload_size');
253
- console.log(`Max upload size: ${maxUpload}`);
254
- ```
255
-
256
- ### Error Handling
257
-
258
- ```typescript
259
- try {
260
- const libraries = await serverApi.getLibraries();
261
- } catch (error) {
262
- if (error.response?.status === 401) {
263
- console.error('Authentication failed');
264
- } else if (error.response?.status === 403) {
265
- console.error('Insufficient permissions');
266
- } else {
267
- console.error('Failed to get libraries:', error.message);
268
- }
269
- }
270
- ```
271
-
272
- ## Related Documentation
273
-
274
- - [RedseatClient Documentation](client.md) - HTTP client used by ServerApi
275
- - [LibraryApi Documentation](libraries.md) - Library-specific operations
276
- - [README](README.md) - Package overview
277
-
1
+ # ServerApi
2
+
3
+ The `ServerApi` class provides server-level operations for managing libraries, settings, plugins, and credentials.
4
+
5
+ ## Overview
6
+
7
+ `ServerApi` handles operations that are not specific to a single library, such as:
8
+ - Listing and creating libraries
9
+ - Getting current user information
10
+ - Managing server settings
11
+ - Listing plugins and credentials
12
+ - Adding credentials to libraries
13
+
14
+ ## Constructor
15
+
16
+ ```typescript
17
+ new ServerApi(client: RedseatClient)
18
+ ```
19
+
20
+ **Parameters:**
21
+ - `client`: An initialized `RedseatClient` instance
22
+
23
+ **Example:**
24
+ ```typescript
25
+ import { RedseatClient, ServerApi } from '@redseat/api';
26
+
27
+ const client = new RedseatClient({ /* ... */ });
28
+ const serverApi = new ServerApi(client);
29
+ ```
30
+
31
+ ## Methods
32
+
33
+ ### `getLibraries(): Promise<ILibrary[]>`
34
+
35
+ Retrieves all libraries available to the current user.
36
+
37
+ **Returns:** Promise resolving to an array of `ILibrary` objects
38
+
39
+ **Example:**
40
+ ```typescript
41
+ const libraries = await serverApi.getLibraries();
42
+ libraries.forEach(library => {
43
+ console.log(`Library: ${library.name} (${library.type})`);
44
+ });
45
+ ```
46
+
47
+ ### `getMe(): Promise<any>`
48
+
49
+ Gets information about the current authenticated user.
50
+
51
+ **Returns:** Promise resolving to user information object
52
+
53
+ **Example:**
54
+ ```typescript
55
+ const user = await serverApi.getMe();
56
+ console.log(`Logged in as: ${user.name}`);
57
+ ```
58
+
59
+ ### `addLibrary(library: Partial<ILibrary>): Promise<ILibrary>`
60
+
61
+ Creates a new library.
62
+
63
+ **Parameters:**
64
+ - `library`: Partial library object with required fields:
65
+ - `name`: Library name (required)
66
+ - `type`: Library type - `'photos'`, `'shows'`, `'movies'`, or `'iptv'` (required)
67
+ - `source`: Optional source type
68
+ - `crypt`: Optional boolean to enable encryption
69
+ - `hidden`: Optional boolean to hide library
70
+
71
+ **Returns:** Promise resolving to the created `ILibrary` object with generated `id`
72
+
73
+ **Example:**
74
+ ```typescript
75
+ const newLibrary = await serverApi.addLibrary({
76
+ name: 'My Photos',
77
+ type: 'photos',
78
+ crypt: true // Enable encryption
79
+ });
80
+ console.log(`Created library with ID: ${newLibrary.id}`);
81
+ ```
82
+
83
+ ### `getSetting(key: string): Promise<string>`
84
+
85
+ Retrieves a server setting value by key.
86
+
87
+ **Parameters:**
88
+ - `key`: The setting key to retrieve
89
+
90
+ **Returns:** Promise resolving to the setting value as a string
91
+
92
+ **Example:**
93
+ ```typescript
94
+ const maxUploadSize = await serverApi.getSetting('max_upload_size');
95
+ console.log(`Max upload size: ${maxUploadSize}`);
96
+ ```
97
+
98
+ ### `getPlugins(): Promise<any[]>`
99
+
100
+ Retrieves all available plugins on the server.
101
+
102
+ **Returns:** Promise resolving to an array of plugin objects
103
+
104
+ **Example:**
105
+ ```typescript
106
+ const plugins = await serverApi.getPlugins();
107
+ plugins.forEach(plugin => {
108
+ console.log(`Plugin: ${plugin.name} - ${plugin.description}`);
109
+ });
110
+ ```
111
+
112
+ ### `getCredentials(): Promise<ICredential[]>`
113
+
114
+ Retrieves all available credentials configured on the server.
115
+
116
+ **Returns:** Promise resolving to an array of `ICredential` objects
117
+
118
+ **Example:**
119
+ ```typescript
120
+ const credentials = await serverApi.getCredentials();
121
+ credentials.forEach(cred => {
122
+ console.log(`Credential: ${cred.name} (${cred.type.type})`);
123
+ });
124
+ ```
125
+
126
+ ### `saveCredential(credential: ICredential): Promise<ICredential>`
127
+
128
+ Creates a new credential on the server.
129
+
130
+ **Parameters:**
131
+ - `credential`: Credential object with required fields:
132
+ - `name`: Credential name (required)
133
+ - `source`: Plugin/source name (required)
134
+ - `type`: Authentication type object (required)
135
+ - `settings`: Settings record for plugin params (required)
136
+ - `login`: Optional login/username
137
+ - `password`: Optional password/token
138
+
139
+ **Returns:** Promise resolving to the created `ICredential` object with generated `id`
140
+
141
+ **Example:**
142
+ ```typescript
143
+ const newCredential = await serverApi.saveCredential({
144
+ name: 'My API Key',
145
+ source: 'jackett',
146
+ type: { type: PluginAuthType.Token },
147
+ settings: { url: 'http://localhost:9117' },
148
+ password: 'my-api-key'
149
+ });
150
+ console.log(`Created credential with ID: ${newCredential.id}`);
151
+ ```
152
+
153
+ ### `updateCredential(credential: ICredential): Promise<ICredential>`
154
+
155
+ Updates an existing credential.
156
+
157
+ **Parameters:**
158
+ - `credential`: Credential object with `id` and fields to update
159
+
160
+ **Returns:** Promise resolving to the updated `ICredential` object
161
+
162
+ **Example:**
163
+ ```typescript
164
+ const updated = await serverApi.updateCredential({
165
+ id: 'cred-123',
166
+ name: 'Updated Name',
167
+ source: 'jackett',
168
+ type: { type: PluginAuthType.Token },
169
+ settings: { url: 'http://localhost:9117' },
170
+ password: 'new-api-key'
171
+ });
172
+ ```
173
+
174
+ ### `deleteCredential(id: string): Promise<void>`
175
+
176
+ Deletes a credential by ID.
177
+
178
+ **Parameters:**
179
+ - `id`: The credential ID to delete
180
+
181
+ **Returns:** Promise resolving when deletion is complete
182
+
183
+ **Example:**
184
+ ```typescript
185
+ await serverApi.deleteCredential('cred-123');
186
+ ```
187
+
188
+ ### `saveOAuthCredentials(pluginId: string, params: Record<string, string>): Promise<void>`
189
+
190
+ Exchanges OAuth tokens and saves credentials for a plugin.
191
+
192
+ **Parameters:**
193
+ - `pluginId`: The plugin ID to save credentials for
194
+ - `params`: OAuth parameters including tokens and name
195
+
196
+ **Returns:** Promise resolving when credentials are saved
197
+
198
+ **Example:**
199
+ ```typescript
200
+ await serverApi.saveOAuthCredentials('trakt', {
201
+ name: 'My Trakt Account',
202
+ code: 'oauth-code',
203
+ access_token: 'token123'
204
+ });
205
+ ```
206
+
207
+ ### `addLibraryCredential(credential: any): Promise<ILibrary>`
208
+
209
+ Adds a credential to a library. This is used to configure external service access for libraries.
210
+
211
+ **Parameters:**
212
+ - `credential`: Credential configuration object (structure depends on credential type)
213
+
214
+ **Returns:** Promise resolving to the updated `ILibrary` object
215
+
216
+ **Example:**
217
+ ```typescript
218
+ const library = await serverApi.addLibraryCredential({
219
+ libraryId: 'library-123',
220
+ credentialId: 'credential-456',
221
+ // Additional credential-specific configuration
222
+ });
223
+ ```
224
+
225
+ ## Watch History API
226
+
227
+ The Watch History API tracks what content users have watched and their playback progress. This is a **global** system that operates independently of specific libraries or servers.
228
+
229
+ ### Key Concepts
230
+
231
+ #### External ID Format
232
+
233
+ Watch history uses **external IDs** (from providers like IMDb, Trakt, TMDb) rather than local database IDs. This design enables:
234
+
235
+ - **Cross-server portability**: Your watch history syncs across different Redseat servers
236
+ - **External service synchronization**: Seamless sync with Trakt, Plex, and other services
237
+ - **Library independence**: History is global, not tied to a specific library
238
+
239
+ **ID Format: `provider:value`**
240
+
241
+ | Provider | Format | Example |
242
+ |----------|--------|---------|
243
+ | IMDb | `imdb:ttXXXXXXX` | `imdb:tt0111161` (The Shawshank Redemption) |
244
+ | Trakt | `trakt:XXXXX` | `trakt:28` |
245
+ | TMDb | `tmdb:XXX` | `tmdb:278` |
246
+ | TVDB | `tvdb:XXXXX` | `tvdb:81189` |
247
+ | Redseat | `redseat:XXXXX` | `redseat:abc123` (local fallback) |
248
+
249
+ #### Library vs Direct History Endpoints
250
+
251
+ There are two ways to mark content as watched:
252
+
253
+ 1. **Library endpoints** (recommended for most use cases):
254
+ - `LibraryApi.setMovieWatched(movieId, date)`
255
+ - `LibraryApi.setEpisodeWatched(serieId, season, number, date)`
256
+ - Uses local database IDs
257
+ - Server automatically resolves to external IDs
258
+
259
+ 2. **Direct history endpoints** (for external sync):
260
+ - `ServerApi.addToHistory({ type, id, date })`
261
+ - Requires external IDs in `provider:value` format
262
+ - Used for importing from external services
263
+
264
+ ### History Methods
265
+
266
+ #### `getHistory(query?: HistoryQuery): Promise<IWatched[]>`
267
+
268
+ Retrieves the current user's watch history.
269
+
270
+ **Parameters:**
271
+ - `query`: Optional query object with filtering parameters:
272
+ - `sort`: Sort key using `RsSort` enum
273
+ - `order`: Sort direction using `SqlOrder` enum (`ASC` or `DESC`)
274
+ - `before`: Filter entries before this timestamp
275
+ - `after`: Filter entries after this timestamp
276
+ - `types`: Array of `MediaType` to filter by (`'episode'`, `'movie'`, `'book'`, `'song'`, `'media'`)
277
+ - `id`: Filter by specific external ID (e.g., `imdb:tt0111161`)
278
+ - `pageKey`: Pagination key
279
+
280
+ **Returns:** Promise resolving to an array of `IWatched` objects
281
+
282
+ **Example:**
283
+ ```typescript
284
+ // Get all history
285
+ const history = await serverApi.getHistory();
286
+
287
+ // Get recent movie watch history
288
+ const movieHistory = await serverApi.getHistory({
289
+ types: ['movie'],
290
+ order: SqlOrder.DESC,
291
+ after: Date.now() - 86400000 * 30 // Last 30 days
292
+ });
293
+
294
+ // Check if specific movie was watched (using external ID)
295
+ const shawshank = await serverApi.getHistory({
296
+ id: 'imdb:tt0111161'
297
+ });
298
+ ```
299
+
300
+ #### `addToHistory(watched: IWatchedForAdd): Promise<void>`
301
+
302
+ Adds an entry to the user's watch history using external IDs.
303
+
304
+ **Parameters:**
305
+ - `watched`: Object containing:
306
+ - `type`: The `MediaType` (`'episode'`, `'movie'`, `'book'`, `'song'`, `'media'`)
307
+ - `id`: **External ID** in `provider:value` format (e.g., `imdb:tt0111161`)
308
+ - `date`: Timestamp when it was watched (unix milliseconds)
309
+
310
+ **Returns:** Promise resolving when the entry is added
311
+
312
+ **Example:**
313
+ ```typescript
314
+ // Mark The Shawshank Redemption as watched using IMDb ID
315
+ await serverApi.addToHistory({
316
+ type: 'movie',
317
+ id: 'imdb:tt0111161',
318
+ date: Date.now()
319
+ });
320
+
321
+ // Mark a Breaking Bad episode as watched using Trakt ID
322
+ await serverApi.addToHistory({
323
+ type: 'episode',
324
+ id: 'trakt:62085', // S01E01
325
+ date: Date.now()
326
+ });
327
+ ```
328
+
329
+ **Note:** For easier usage with local content, prefer the library methods which handle ID resolution automatically:
330
+ ```typescript
331
+ // This is simpler - uses local ID, server resolves to external
332
+ await libraryApi.setMovieWatched('local-movie-id', Date.now());
333
+ ```
334
+
335
+ #### `getProgressById(id: string): Promise<IViewProgress | null>`
336
+
337
+ Gets the view progress for a specific item by external ID.
338
+
339
+ **Parameters:**
340
+ - `id`: External ID in `provider:value` format (e.g., `imdb:tt0111161`)
341
+
342
+ **Returns:** Promise resolving to `IViewProgress` object or `null` if not found
343
+
344
+ **Example:**
345
+ ```typescript
346
+ const progress = await serverApi.getProgressById('imdb:tt0111161');
347
+ if (progress) {
348
+ console.log(`Progress: ${progress.progress}ms`);
349
+ }
350
+ ```
351
+
352
+ #### `addProgress(progress: IViewProgressForAdd): Promise<void>`
353
+
354
+ Adds or updates view progress for an item using external IDs.
355
+
356
+ **Parameters:**
357
+ - `progress`: Object containing:
358
+ - `type`: The `MediaType`
359
+ - `id`: **External ID** in `provider:value` format
360
+ - `progress`: Progress value in milliseconds
361
+ - `parent`: Optional parent external ID (e.g., series ID for episodes)
362
+
363
+ **Returns:** Promise resolving when progress is saved
364
+
365
+ **Example:**
366
+ ```typescript
367
+ // Save playback position at 1 hour
368
+ await serverApi.addProgress({
369
+ type: 'movie',
370
+ id: 'imdb:tt0111161',
371
+ progress: 3600000 // milliseconds
372
+ });
373
+
374
+ // Save episode progress with parent series
375
+ await serverApi.addProgress({
376
+ type: 'episode',
377
+ id: 'imdb:tt0959621', // Breaking Bad S01E01
378
+ parent: 'imdb:tt0903747', // Breaking Bad series
379
+ progress: 1800000 // 30 minutes
380
+ });
381
+ ```
382
+
383
+ ### Admin History Methods
384
+
385
+ #### `getAdminHistory(): Promise<IWatched[]>`
386
+
387
+ **Admin only.** Retrieves watch history for all users.
388
+
389
+ **Returns:** Promise resolving to an array of `IWatched` objects
390
+
391
+ **Example:**
392
+ ```typescript
393
+ const allHistory = await serverApi.getAdminHistory();
394
+ ```
395
+
396
+ #### `importHistory(watcheds: IWatchedForAdd[]): Promise<void>`
397
+
398
+ **Admin only.** Imports watch history entries in bulk. Useful for migrating from external services.
399
+
400
+ **Parameters:**
401
+ - `watcheds`: Array of `IWatchedForAdd` objects with external IDs
402
+
403
+ **Returns:** Promise resolving when import is complete
404
+
405
+ **Example:**
406
+ ```typescript
407
+ // Import watch history from an external service
408
+ await serverApi.importHistory([
409
+ { type: 'movie', id: 'imdb:tt0111161', date: Date.now() - 86400000 },
410
+ { type: 'movie', id: 'tmdb:278', date: Date.now() - 172800000 },
411
+ { type: 'episode', id: 'trakt:62085', date: Date.now() }
412
+ ]);
413
+ ```
414
+
415
+ ### Complete Example: Syncing with External Service
416
+
417
+ ```typescript
418
+ import { RedseatClient, ServerApi, SqlOrder } from '@redseat/api';
419
+
420
+ // Initialize
421
+ const client = new RedseatClient({ /* ... */ });
422
+ const serverApi = new ServerApi(client);
423
+
424
+ // Export: Get all watched movies to sync to external service
425
+ const watchedMovies = await serverApi.getHistory({
426
+ types: ['movie'],
427
+ order: SqlOrder.ASC
428
+ });
429
+
430
+ // The IDs are already in external format for easy sync
431
+ for (const entry of watchedMovies) {
432
+ console.log(`Watched: ${entry.id} on ${new Date(entry.date).toISOString()}`);
433
+ // entry.id is e.g., "imdb:tt0111161"
434
+ }
435
+
436
+ // Import: Bring in history from external service
437
+ const externalHistory = [
438
+ { type: 'movie' as const, id: 'imdb:tt0468569', date: 1609459200000 }, // The Dark Knight
439
+ { type: 'movie' as const, id: 'trakt:120', date: 1612137600000 }, // LOTR
440
+ ];
441
+ await serverApi.importHistory(externalHistory);
442
+ ```
443
+
444
+ ### SSE Events
445
+
446
+ Real-time watch state changes are broadcast via SSE. These are **user-scoped** events (not library-specific).
447
+
448
+ #### `watched$: Observable<SSEWatchedEvent>`
449
+
450
+ Emitted when content is marked as watched.
451
+
452
+ ```typescript
453
+ client.watched$.subscribe(event => {
454
+ console.log(`Watched: ${event.type} ${event.id} at ${new Date(event.date)}`);
455
+ // event.id is external ID, e.g., "imdb:tt0111161"
456
+ });
457
+ ```
458
+
459
+ **Event fields:**
460
+ - `type`: Content type ('movie', 'episode')
461
+ - `id`: External ID (e.g., 'imdb:tt0111161')
462
+ - `date`: Timestamp when watched (unix ms)
463
+ - `userRef`: User reference
464
+ - `modified`: Last modified timestamp
465
+
466
+ #### `unwatched$: Observable<SSEUnwatchedEvent>`
467
+
468
+ Emitted when content is removed from watch history. Contains **all possible IDs** so clients can match against any cached external ID.
469
+
470
+ ```typescript
471
+ client.unwatched$.subscribe(event => {
472
+ console.log(`Unwatched: ${event.type}`);
473
+ // event.ids contains ALL possible external IDs
474
+ for (const id of event.ids) {
475
+ console.log(` - ${id}`); // e.g., "imdb:tt0111161", "trakt:481"
476
+ }
477
+ });
478
+ ```
479
+
480
+ **Event fields:**
481
+ - `type`: Content type ('movie', 'episode')
482
+ - `ids`: Array of all external IDs (e.g., `["imdb:tt0111161", "trakt:481", "tmdb:278"]`)
483
+ - `userRef`: User reference
484
+ - `modified`: Last modified timestamp
485
+
486
+ ## Usage Examples
487
+
488
+ ### Complete Workflow: Create Library and Get Info
489
+
490
+ ```typescript
491
+ import { RedseatClient, ServerApi } from '@redseat/api';
492
+
493
+ // Initialize
494
+ const client = new RedseatClient({ /* ... */ });
495
+ const serverApi = new ServerApi(client);
496
+
497
+ // Get current user
498
+ const user = await serverApi.getMe();
499
+ console.log(`User: ${user.name}`);
500
+
501
+ // List existing libraries
502
+ const libraries = await serverApi.getLibraries();
503
+ console.log(`Found ${libraries.length} libraries`);
504
+
505
+ // Create a new encrypted photo library
506
+ const newLibrary = await serverApi.addLibrary({
507
+ name: 'Encrypted Photos',
508
+ type: 'photos',
509
+ crypt: true
510
+ });
511
+
512
+ // Get server settings
513
+ const maxUpload = await serverApi.getSetting('max_upload_size');
514
+ console.log(`Max upload size: ${maxUpload}`);
515
+ ```
516
+
517
+ ### Error Handling
518
+
519
+ ```typescript
520
+ try {
521
+ const libraries = await serverApi.getLibraries();
522
+ } catch (error) {
523
+ if (error.response?.status === 401) {
524
+ console.error('Authentication failed');
525
+ } else if (error.response?.status === 403) {
526
+ console.error('Insufficient permissions');
527
+ } else {
528
+ console.error('Failed to get libraries:', error.message);
529
+ }
530
+ }
531
+ ```
532
+
533
+ ## Related Documentation
534
+
535
+ - [RedseatClient Documentation](client.md) - HTTP client used by ServerApi
536
+ - [LibraryApi Documentation](libraries.md) - Library-specific operations
537
+ - [README](README.md) - Package overview
538
+