@redseat/api 0.3.0 → 0.3.5
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/client.md +54 -0
- package/dist/client.d.ts +4 -1
- package/dist/client.js +14 -2
- package/dist/interfaces.d.ts +47 -0
- package/dist/interfaces.js +7 -0
- package/dist/library.d.ts +13 -1
- package/dist/library.js +11 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.js +14 -0
- package/dist/sse-types.d.ts +30 -0
- package/libraries.md +34 -0
- package/package.json +1 -1
- package/server.md +84 -3
package/client.md
CHANGED
|
@@ -560,6 +560,60 @@ client.backupFiles$.subscribe(event => {
|
|
|
560
560
|
});
|
|
561
561
|
```
|
|
562
562
|
|
|
563
|
+
#### `mediaRating$: Observable<SSEMediaRatingEvent>`
|
|
564
|
+
|
|
565
|
+
Emits when a user rates a media item.
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
client.mediaRating$.subscribe(event => {
|
|
569
|
+
console.log(`User ${event.rating.userRef} rated media ${event.rating.mediaRef}: ${event.rating.rating}`);
|
|
570
|
+
});
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
**Event structure:**
|
|
574
|
+
- `library`: Library ID where the media is located
|
|
575
|
+
- `rating.userRef`: User who rated
|
|
576
|
+
- `rating.mediaRef`: Media that was rated
|
|
577
|
+
- `rating.rating`: Rating value (0-5)
|
|
578
|
+
- `rating.modified`: Timestamp of the rating
|
|
579
|
+
|
|
580
|
+
#### `mediaProgress$: Observable<SSEMediaProgressEvent>`
|
|
581
|
+
|
|
582
|
+
Emits when a user's playback progress is updated.
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
client.mediaProgress$.subscribe(event => {
|
|
586
|
+
console.log(`User ${event.progress.userRef} watched ${event.progress.mediaRef} to ${event.progress.progress}ms`);
|
|
587
|
+
});
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
**Event structure:**
|
|
591
|
+
- `library`: Library ID where the media is located
|
|
592
|
+
- `progress.userRef`: User whose progress updated
|
|
593
|
+
- `progress.mediaRef`: Media being tracked
|
|
594
|
+
- `progress.progress`: Current playback position in milliseconds
|
|
595
|
+
- `progress.modified`: Timestamp of the update
|
|
596
|
+
|
|
597
|
+
#### `playersList$: Observable<SSEPlayersListEvent>`
|
|
598
|
+
|
|
599
|
+
Emits the full list of available media players for the user when it changes.
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
client.playersList$.subscribe(event => {
|
|
603
|
+
console.log(`Available players for ${event.userRef}:`);
|
|
604
|
+
for (const player of event.players) {
|
|
605
|
+
console.log(` - ${player.name} (${player.player})`);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**Event structure:**
|
|
611
|
+
- `userRef`: User ID
|
|
612
|
+
- `players`: Array of available players
|
|
613
|
+
- `id`: Player socket ID (for casting)
|
|
614
|
+
- `name`: Player display name
|
|
615
|
+
- `player`: Player type identifier
|
|
616
|
+
|
|
563
617
|
### Complete SSE Example
|
|
564
618
|
|
|
565
619
|
```typescript
|
package/dist/client.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Method, AxiosRequestConfig } from 'axios';
|
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { IToken } from './auth.js';
|
|
4
4
|
import { IServer } from './interfaces.js';
|
|
5
|
-
import { SSEConnectionState, SSEConnectionOptions, SSEConnectionError, SSELibraryEvent, SSELibraryStatusEvent, SSEMediasEvent, SSEUploadProgressEvent, SSEConvertProgressEvent, SSEEpisodesEvent, SSESeriesEvent, SSEMoviesEvent, SSEPeopleEvent, SSETagsEvent, SSEBackupsEvent, SSEBackupFilesEvent } from './sse-types.js';
|
|
5
|
+
import { SSEConnectionState, SSEConnectionOptions, SSEConnectionError, SSELibraryEvent, SSELibraryStatusEvent, SSEMediasEvent, SSEUploadProgressEvent, SSEConvertProgressEvent, SSEEpisodesEvent, SSESeriesEvent, SSEMoviesEvent, SSEPeopleEvent, SSETagsEvent, SSEBackupsEvent, SSEBackupFilesEvent, SSEMediaRatingEvent, SSEMediaProgressEvent, SSEPlayersListEvent } from './sse-types.js';
|
|
6
6
|
export interface ClientOptions {
|
|
7
7
|
server: IServer;
|
|
8
8
|
getIdToken: () => Promise<string>;
|
|
@@ -42,6 +42,9 @@ export declare class RedseatClient {
|
|
|
42
42
|
readonly tags$: Observable<SSETagsEvent>;
|
|
43
43
|
readonly backups$: Observable<SSEBackupsEvent>;
|
|
44
44
|
readonly backupFiles$: Observable<SSEBackupFilesEvent>;
|
|
45
|
+
readonly mediaRating$: Observable<SSEMediaRatingEvent>;
|
|
46
|
+
readonly mediaProgress$: Observable<SSEMediaProgressEvent>;
|
|
47
|
+
readonly playersList$: Observable<SSEPlayersListEvent>;
|
|
45
48
|
/**
|
|
46
49
|
* Creates a typed observable for a specific SSE event type.
|
|
47
50
|
* Unwraps the nested data structure from the server (e.g., {uploadProgress: {...}} -> {...})
|
package/dist/client.js
CHANGED
|
@@ -9,14 +9,18 @@ export class RedseatClient {
|
|
|
9
9
|
createEventStream(eventName) {
|
|
10
10
|
// Map event names to their wrapper property names (snake_case -> camelCase)
|
|
11
11
|
const wrapperMap = {
|
|
12
|
+
'medias': 'medias',
|
|
12
13
|
'upload_progress': 'uploadProgress',
|
|
13
14
|
'convert_progress': 'convertProgress',
|
|
14
15
|
'media_progress': 'mediaProgress',
|
|
16
|
+
'media_rating': 'mediaRating',
|
|
15
17
|
'library-status': 'libraryStatus',
|
|
16
18
|
'backups-files': 'backupsFiles',
|
|
19
|
+
'players-list': 'Players',
|
|
17
20
|
};
|
|
18
21
|
const wrapperKey = wrapperMap[eventName];
|
|
19
22
|
return this._sseEvents.pipe(filter((event) => event.event === eventName), map(event => {
|
|
23
|
+
//console.log("EVENT", event)
|
|
20
24
|
// If there's a wrapper, unwrap it; otherwise return data as-is
|
|
21
25
|
const data = event.data;
|
|
22
26
|
if (wrapperKey && data && typeof data === 'object' && wrapperKey in data) {
|
|
@@ -48,6 +52,9 @@ export class RedseatClient {
|
|
|
48
52
|
this.tags$ = this.createEventStream('tags');
|
|
49
53
|
this.backups$ = this.createEventStream('backups');
|
|
50
54
|
this.backupFiles$ = this.createEventStream('backups-files');
|
|
55
|
+
this.mediaRating$ = this.createEventStream('media_rating');
|
|
56
|
+
this.mediaProgress$ = this.createEventStream('media_progress');
|
|
57
|
+
this.playersList$ = this.createEventStream('players-list');
|
|
51
58
|
this.server = options.server;
|
|
52
59
|
this.redseatUrl = options.redseatUrl;
|
|
53
60
|
this.getIdToken = options.getIdToken;
|
|
@@ -341,8 +348,12 @@ export class RedseatClient {
|
|
|
341
348
|
}
|
|
342
349
|
this._sseConnectionState.next('connected');
|
|
343
350
|
this.sseReconnectAttempts = 0;
|
|
344
|
-
// Process the stream
|
|
345
|
-
|
|
351
|
+
// Process the stream in the background (don't await - it runs forever until disconnected)
|
|
352
|
+
this.processSSEStream(response.body).catch(err => {
|
|
353
|
+
if (err?.name !== 'AbortError') {
|
|
354
|
+
this.handleSSEError(err);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
346
357
|
}
|
|
347
358
|
catch (error) {
|
|
348
359
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
@@ -384,6 +395,7 @@ export class RedseatClient {
|
|
|
384
395
|
const { events, remainingBuffer } = this.parseSSEBuffer(buffer);
|
|
385
396
|
buffer = remainingBuffer;
|
|
386
397
|
for (const event of events) {
|
|
398
|
+
//console.log("event process", JSON.stringify(event))
|
|
387
399
|
this._sseEvents.next(event);
|
|
388
400
|
}
|
|
389
401
|
}
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -414,6 +414,52 @@ export interface ClusterFacesResponse {
|
|
|
414
414
|
export interface IChannelUpdate {
|
|
415
415
|
[key: string]: any;
|
|
416
416
|
}
|
|
417
|
+
export declare enum PluginAuthType {
|
|
418
|
+
OAUTH = "oauth",
|
|
419
|
+
URL = "url",
|
|
420
|
+
Token = "token",
|
|
421
|
+
LoginPassword = "password"
|
|
422
|
+
}
|
|
423
|
+
export interface IPluginParam {
|
|
424
|
+
name: string;
|
|
425
|
+
param: Record<string, any>;
|
|
426
|
+
description?: string;
|
|
427
|
+
required?: boolean;
|
|
428
|
+
}
|
|
429
|
+
export interface IPlugin {
|
|
430
|
+
id: string;
|
|
431
|
+
name: string;
|
|
432
|
+
libraries: string[];
|
|
433
|
+
description: string;
|
|
434
|
+
credential: string;
|
|
435
|
+
credentialType: {
|
|
436
|
+
type: PluginAuthType;
|
|
437
|
+
url?: string;
|
|
438
|
+
};
|
|
439
|
+
params?: IPluginParam[];
|
|
440
|
+
oauthUrl?: string;
|
|
441
|
+
repo?: string;
|
|
442
|
+
publisher?: string;
|
|
443
|
+
version?: number;
|
|
444
|
+
installed: boolean;
|
|
445
|
+
capabilities?: string[];
|
|
446
|
+
local?: boolean;
|
|
447
|
+
}
|
|
448
|
+
export interface ICredential {
|
|
449
|
+
id?: string;
|
|
450
|
+
name: string;
|
|
451
|
+
source: string;
|
|
452
|
+
type: {
|
|
453
|
+
type: PluginAuthType;
|
|
454
|
+
url?: string;
|
|
455
|
+
};
|
|
456
|
+
login?: string;
|
|
457
|
+
password?: string;
|
|
458
|
+
settings: Record<string, any>;
|
|
459
|
+
user_ref?: string;
|
|
460
|
+
refreshtoken?: string;
|
|
461
|
+
expires?: number;
|
|
462
|
+
}
|
|
417
463
|
export declare enum ElementType {
|
|
418
464
|
Tag = "tag",
|
|
419
465
|
Person = "person",
|
|
@@ -499,6 +545,7 @@ export interface RsRequest {
|
|
|
499
545
|
filename?: string;
|
|
500
546
|
status: RsRequestStatus;
|
|
501
547
|
permanent: boolean;
|
|
548
|
+
instant?: boolean;
|
|
502
549
|
jsonBody?: any;
|
|
503
550
|
method: RsRequestMethod;
|
|
504
551
|
referer?: string;
|
package/dist/interfaces.js
CHANGED
|
@@ -36,6 +36,13 @@ export var LinkType;
|
|
|
36
36
|
LinkType["post"] = "post";
|
|
37
37
|
LinkType["other"] = "other";
|
|
38
38
|
})(LinkType || (LinkType = {}));
|
|
39
|
+
export var PluginAuthType;
|
|
40
|
+
(function (PluginAuthType) {
|
|
41
|
+
PluginAuthType["OAUTH"] = "oauth";
|
|
42
|
+
PluginAuthType["URL"] = "url";
|
|
43
|
+
PluginAuthType["Token"] = "token";
|
|
44
|
+
PluginAuthType["LoginPassword"] = "password";
|
|
45
|
+
})(PluginAuthType || (PluginAuthType = {}));
|
|
39
46
|
export var ElementType;
|
|
40
47
|
(function (ElementType) {
|
|
41
48
|
ElementType["Tag"] = "tag";
|
package/dist/library.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
2
|
import { IFile, ITag, IPerson, ISerie, IMovie, MediaRequest, IEpisode, ExternalImage, IBackupFile, ILibrary, SerieInMedia, DeletedQuery, RsDeleted, MovieSort, RsSort, SqlOrder, RsRequest, DetectedFaceResult, UnassignFaceResponse, RsGroupDownload } from './interfaces.js';
|
|
3
|
-
import { SSEMediasEvent, SSEUploadProgressEvent, SSEConvertProgressEvent, SSEEpisodesEvent, SSESeriesEvent, SSEMoviesEvent, SSEPeopleEvent, SSETagsEvent, SSELibraryStatusEvent } from './sse-types.js';
|
|
3
|
+
import { SSEMediasEvent, SSEUploadProgressEvent, SSEConvertProgressEvent, SSEEpisodesEvent, SSESeriesEvent, SSEMoviesEvent, SSEPeopleEvent, SSETagsEvent, SSELibraryStatusEvent, SSEMediaRatingEvent, SSEMediaProgressEvent } from './sse-types.js';
|
|
4
4
|
import { EncryptFileOptions, EncryptedFile } from './encryption.js';
|
|
5
5
|
export interface MediaForUpdate {
|
|
6
6
|
name?: string;
|
|
@@ -85,6 +85,8 @@ export interface LibraryHttpClient {
|
|
|
85
85
|
readonly people$?: Observable<SSEPeopleEvent>;
|
|
86
86
|
readonly tags$?: Observable<SSETagsEvent>;
|
|
87
87
|
readonly libraryStatus$?: Observable<SSELibraryStatusEvent>;
|
|
88
|
+
readonly mediaRating$?: Observable<SSEMediaRatingEvent>;
|
|
89
|
+
readonly mediaProgress$?: Observable<SSEMediaProgressEvent>;
|
|
88
90
|
}
|
|
89
91
|
export declare class LibraryApi {
|
|
90
92
|
private client;
|
|
@@ -102,6 +104,8 @@ export declare class LibraryApi {
|
|
|
102
104
|
readonly people$: Observable<SSEPeopleEvent>;
|
|
103
105
|
readonly tags$: Observable<SSETagsEvent>;
|
|
104
106
|
readonly libraryStatus$: Observable<SSELibraryStatusEvent>;
|
|
107
|
+
readonly mediaRating$: Observable<SSEMediaRatingEvent>;
|
|
108
|
+
readonly mediaProgress$: Observable<SSEMediaProgressEvent>;
|
|
105
109
|
constructor(client: LibraryHttpClient, libraryId: string, library: ILibrary);
|
|
106
110
|
/**
|
|
107
111
|
* Creates a library-filtered stream from a client stream.
|
|
@@ -235,6 +239,14 @@ export declare class LibraryApi {
|
|
|
235
239
|
* @throws Error if the URL is not available or cannot be made permanent
|
|
236
240
|
*/
|
|
237
241
|
checkRequestPermanent(request: RsRequest): Promise<RsRequest>;
|
|
242
|
+
/**
|
|
243
|
+
* Checks if a request can be processed instantly (immediate availability).
|
|
244
|
+
* @param request - The request to check for instant status
|
|
245
|
+
* @returns Object with instant boolean indicating if the request can be processed immediately
|
|
246
|
+
*/
|
|
247
|
+
checkRequestInstant(request: RsRequest): Promise<{
|
|
248
|
+
instant: boolean;
|
|
249
|
+
}>;
|
|
238
250
|
/**
|
|
239
251
|
* Get a share token for a request URL.
|
|
240
252
|
* The token can be used to stream/download the resource without authentication.
|
package/dist/library.js
CHANGED
|
@@ -17,6 +17,8 @@ export class LibraryApi {
|
|
|
17
17
|
this.people$ = this.createLibraryFilteredStream(client.people$);
|
|
18
18
|
this.tags$ = this.createLibraryFilteredStream(client.tags$);
|
|
19
19
|
this.libraryStatus$ = this.createLibraryFilteredStream(client.libraryStatus$);
|
|
20
|
+
this.mediaRating$ = this.createLibraryFilteredStream(client.mediaRating$);
|
|
21
|
+
this.mediaProgress$ = this.createLibraryFilteredStream(client.mediaProgress$);
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* Creates a library-filtered stream from a client stream.
|
|
@@ -492,6 +494,15 @@ export class LibraryApi {
|
|
|
492
494
|
const res = await this.client.post(this.getUrl('/plugins/requests/permanent'), request);
|
|
493
495
|
return res.data;
|
|
494
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Checks if a request can be processed instantly (immediate availability).
|
|
499
|
+
* @param request - The request to check for instant status
|
|
500
|
+
* @returns Object with instant boolean indicating if the request can be processed immediately
|
|
501
|
+
*/
|
|
502
|
+
async checkRequestInstant(request) {
|
|
503
|
+
const res = await this.client.post(this.getUrl('/plugins/requests/check-instant'), request);
|
|
504
|
+
return res.data;
|
|
505
|
+
}
|
|
495
506
|
/**
|
|
496
507
|
* Get a share token for a request URL.
|
|
497
508
|
* The token can be used to stream/download the resource without authentication.
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RedseatClient } from './client.js';
|
|
2
|
-
import { ILibrary } from './interfaces.js';
|
|
2
|
+
import { ILibrary, IPlugin, ICredential } from './interfaces.js';
|
|
3
3
|
export declare class ServerApi {
|
|
4
4
|
private client;
|
|
5
5
|
constructor(client: RedseatClient);
|
|
@@ -12,7 +12,11 @@ export declare class ServerApi {
|
|
|
12
12
|
}): Promise<any>;
|
|
13
13
|
addLibrary(library: Partial<ILibrary>): Promise<ILibrary>;
|
|
14
14
|
getSetting(key: string): Promise<string>;
|
|
15
|
-
getPlugins(): Promise<
|
|
16
|
-
getCredentials(): Promise<
|
|
15
|
+
getPlugins(): Promise<IPlugin[]>;
|
|
16
|
+
getCredentials(): Promise<ICredential[]>;
|
|
17
|
+
saveCredential(credential: ICredential): Promise<ICredential>;
|
|
18
|
+
updateCredential(credential: ICredential): Promise<ICredential>;
|
|
19
|
+
deleteCredential(id: string): Promise<void>;
|
|
20
|
+
saveOAuthCredentials(pluginId: string, params: Record<string, string>): Promise<void>;
|
|
17
21
|
addLibraryCredential(credential: any): Promise<ILibrary>;
|
|
18
22
|
}
|
package/dist/server.js
CHANGED
|
@@ -30,6 +30,20 @@ export class ServerApi {
|
|
|
30
30
|
const res = await this.client.get('/credentials');
|
|
31
31
|
return res.data;
|
|
32
32
|
}
|
|
33
|
+
async saveCredential(credential) {
|
|
34
|
+
const res = await this.client.post('/credentials', credential);
|
|
35
|
+
return res.data;
|
|
36
|
+
}
|
|
37
|
+
async updateCredential(credential) {
|
|
38
|
+
const res = await this.client.patch(`/credentials/${credential.id}`, credential);
|
|
39
|
+
return res.data;
|
|
40
|
+
}
|
|
41
|
+
async deleteCredential(id) {
|
|
42
|
+
await this.client.delete(`/credentials/${id}`);
|
|
43
|
+
}
|
|
44
|
+
async saveOAuthCredentials(pluginId, params) {
|
|
45
|
+
await this.client.post(`/plugins/${pluginId}/oauthtoken`, params);
|
|
46
|
+
}
|
|
33
47
|
async addLibraryCredential(credential) {
|
|
34
48
|
const res = await this.client.post('/libraries/credential', credential);
|
|
35
49
|
return res.data;
|
package/dist/sse-types.d.ts
CHANGED
|
@@ -107,6 +107,33 @@ export interface SSEBackupFilesEvent {
|
|
|
107
107
|
status?: string;
|
|
108
108
|
message?: string;
|
|
109
109
|
}
|
|
110
|
+
export interface SSEMediaRatingEvent {
|
|
111
|
+
library: string;
|
|
112
|
+
rating: {
|
|
113
|
+
userRef: string;
|
|
114
|
+
mediaRef: string;
|
|
115
|
+
rating: number;
|
|
116
|
+
modified: number;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export interface SSEMediaProgressEvent {
|
|
120
|
+
library: string;
|
|
121
|
+
progress: {
|
|
122
|
+
userRef: string;
|
|
123
|
+
mediaRef: string;
|
|
124
|
+
progress: number;
|
|
125
|
+
modified: number;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export interface SSEPlayerEvent {
|
|
129
|
+
id: string;
|
|
130
|
+
name: string;
|
|
131
|
+
player: string;
|
|
132
|
+
}
|
|
133
|
+
export interface SSEPlayersListEvent {
|
|
134
|
+
userRef: string;
|
|
135
|
+
players: SSEPlayerEvent[];
|
|
136
|
+
}
|
|
110
137
|
export interface SSEEventMap {
|
|
111
138
|
'library': SSELibraryEvent;
|
|
112
139
|
'library-status': SSELibraryStatusEvent;
|
|
@@ -120,6 +147,9 @@ export interface SSEEventMap {
|
|
|
120
147
|
'tags': SSETagsEvent;
|
|
121
148
|
'backups': SSEBackupsEvent;
|
|
122
149
|
'backups-files': SSEBackupFilesEvent;
|
|
150
|
+
'media_rating': SSEMediaRatingEvent;
|
|
151
|
+
'media_progress': SSEMediaProgressEvent;
|
|
152
|
+
'players-list': SSEPlayersListEvent;
|
|
123
153
|
}
|
|
124
154
|
export type SSEEventName = keyof SSEEventMap;
|
|
125
155
|
export interface SSEEvent<T extends SSEEventName = SSEEventName> {
|
package/libraries.md
CHANGED
|
@@ -190,6 +190,40 @@ libraryApi.libraryStatus$.subscribe(event => {
|
|
|
190
190
|
});
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
+
#### `mediaRating$: Observable<SSEMediaRatingEvent>`
|
|
194
|
+
|
|
195
|
+
Emits when a user rates a media item in this library.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
libraryApi.mediaRating$.subscribe(event => {
|
|
199
|
+
console.log(`Rating for ${event.rating.mediaRef}: ${event.rating.rating}`);
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Event structure:**
|
|
204
|
+
- `library`: Library ID
|
|
205
|
+
- `rating.userRef`: User who rated
|
|
206
|
+
- `rating.mediaRef`: Media that was rated
|
|
207
|
+
- `rating.rating`: Rating value (0-5)
|
|
208
|
+
- `rating.modified`: Timestamp of the rating
|
|
209
|
+
|
|
210
|
+
#### `mediaProgress$: Observable<SSEMediaProgressEvent>`
|
|
211
|
+
|
|
212
|
+
Emits when a user's playback progress is updated for a media item in this library.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
libraryApi.mediaProgress$.subscribe(event => {
|
|
216
|
+
console.log(`Progress for ${event.progress.mediaRef}: ${event.progress.progress}ms`);
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Event structure:**
|
|
221
|
+
- `library`: Library ID
|
|
222
|
+
- `progress.userRef`: User whose progress updated
|
|
223
|
+
- `progress.mediaRef`: Media being tracked
|
|
224
|
+
- `progress.progress`: Current playback position in milliseconds
|
|
225
|
+
- `progress.modified`: Timestamp of the update
|
|
226
|
+
|
|
193
227
|
### Complete SSE Example with LibraryApi
|
|
194
228
|
|
|
195
229
|
```typescript
|
package/package.json
CHANGED
package/server.md
CHANGED
|
@@ -109,17 +109,98 @@ plugins.forEach(plugin => {
|
|
|
109
109
|
});
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
### `getCredentials(): Promise<
|
|
112
|
+
### `getCredentials(): Promise<ICredential[]>`
|
|
113
113
|
|
|
114
114
|
Retrieves all available credentials configured on the server.
|
|
115
115
|
|
|
116
|
-
**Returns:** Promise resolving to an array of
|
|
116
|
+
**Returns:** Promise resolving to an array of `ICredential` objects
|
|
117
117
|
|
|
118
118
|
**Example:**
|
|
119
119
|
```typescript
|
|
120
120
|
const credentials = await serverApi.getCredentials();
|
|
121
121
|
credentials.forEach(cred => {
|
|
122
|
-
console.log(`Credential: ${cred.name} (${cred.type})`);
|
|
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'
|
|
123
204
|
});
|
|
124
205
|
```
|
|
125
206
|
|