@redseat/api 0.4.6 → 0.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.
- package/dist/interfaces.d.ts +13 -2
- package/dist/library.d.ts +19 -4
- package/dist/library.js +41 -6
- package/dist/server.d.ts +4 -1
- package/dist/server.js +7 -2
- package/dist/sse-fetch.d.ts +1 -0
- package/dist/sse-fetch.js +54 -44
- package/package.json +1 -1
package/dist/interfaces.d.ts
CHANGED
|
@@ -202,6 +202,7 @@ export interface ServerLibraryForUpdate {
|
|
|
202
202
|
settings?: ServerLibrarySettings;
|
|
203
203
|
credentials?: string;
|
|
204
204
|
plugin?: string;
|
|
205
|
+
password?: string;
|
|
205
206
|
}
|
|
206
207
|
export interface ILibrary {
|
|
207
208
|
id?: string;
|
|
@@ -216,6 +217,7 @@ export interface ILibrary {
|
|
|
216
217
|
plugin?: string;
|
|
217
218
|
hidden?: boolean;
|
|
218
219
|
status?: string;
|
|
220
|
+
password?: string;
|
|
219
221
|
}
|
|
220
222
|
export interface ITag {
|
|
221
223
|
id: string;
|
|
@@ -451,6 +453,7 @@ export interface ExternalImage {
|
|
|
451
453
|
voteAverage?: number;
|
|
452
454
|
voteCount?: number;
|
|
453
455
|
width?: number;
|
|
456
|
+
matchType?: RsLookupMatchType;
|
|
454
457
|
}
|
|
455
458
|
export interface Relations {
|
|
456
459
|
peopleDetails?: IPerson[];
|
|
@@ -478,6 +481,7 @@ export interface SearchRelations {
|
|
|
478
481
|
export interface SearchStreamResultBase {
|
|
479
482
|
relations?: SearchRelations;
|
|
480
483
|
images?: ExternalImage[];
|
|
484
|
+
matchType?: RsLookupMatchType;
|
|
481
485
|
}
|
|
482
486
|
export type SearchStreamResult<K extends string, T> = SearchStreamResultBase & {
|
|
483
487
|
metadata?: Partial<Record<K, T>>;
|
|
@@ -490,9 +494,15 @@ export type BookSearchStreamResult = SearchStreamResult<'book', IBook>;
|
|
|
490
494
|
export type MovieSearchStreamResult = SearchStreamResult<'movie', IMovie>;
|
|
491
495
|
export type SerieSearchStreamResult = SearchStreamResult<'serie', ISerie>;
|
|
492
496
|
export type LookupSearchStreamResult = SearchStreamResult<'book' | 'movie' | 'serie', IBook | IMovie | ISerie>;
|
|
493
|
-
export type
|
|
497
|
+
export type RsLookupMatchType = 'exactId' | 'exactText';
|
|
498
|
+
export interface SseSearchStreamEvent<T> {
|
|
499
|
+
sourceId: string;
|
|
500
|
+
sourceName: string;
|
|
501
|
+
results: T[];
|
|
502
|
+
nextPageKey?: string | null;
|
|
503
|
+
}
|
|
494
504
|
export interface SearchStreamCallbacks<T> {
|
|
495
|
-
onResults?: (
|
|
505
|
+
onResults?: (event: SseSearchStreamEvent<T>) => void;
|
|
496
506
|
onFinished?: () => void;
|
|
497
507
|
onError?: (error: unknown) => void;
|
|
498
508
|
}
|
|
@@ -927,6 +937,7 @@ export interface RsGroupDownload {
|
|
|
927
937
|
groupMime?: string;
|
|
928
938
|
requests: RsRequest[];
|
|
929
939
|
infos?: MediaForUpdate;
|
|
940
|
+
matchType?: RsLookupMatchType;
|
|
930
941
|
}
|
|
931
942
|
/**
|
|
932
943
|
* Request processing status from plugin-based download/processing.
|
package/dist/library.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
2
|
import type { AxiosResponse } from 'axios';
|
|
3
|
-
import { IFile, ITag, IPerson, ISerie, IMovie, IBook, MediaRequest, IEpisode, ExternalImage, IBackupFile, ILibrary, DeletedQuery, RsDeleted, MovieSort, BookSort, RsSort, SqlOrder, RsRequest, DetectedFaceResult, UnassignFaceResponse, RsGroupDownload, IViewProgress, IWatched, IRsRequestProcessing, BookSearchStreamResult, MovieSearchStreamResult, SerieSearchStreamResult, BookSearchResult, MovieSearchResult, SerieSearchResult, ItemWithRelations, SearchStreamCallbacks, MediaForUpdate, IChannelUpdate } from './interfaces.js';
|
|
3
|
+
import { IFile, ITag, IPerson, ISerie, IMovie, IBook, MediaRequest, IEpisode, ExternalImage, IBackupFile, ILibrary, DeletedQuery, RsDeleted, MovieSort, BookSort, RsSort, SqlOrder, RsRequest, DetectedFaceResult, UnassignFaceResponse, RsGroupDownload, IViewProgress, IWatched, IRsRequestProcessing, BookSearchStreamResult, MovieSearchStreamResult, SerieSearchStreamResult, LookupSearchStreamResult, BookSearchResult, MovieSearchResult, SerieSearchResult, ItemWithRelations, SearchStreamCallbacks, MediaForUpdate, IChannelUpdate } from './interfaces.js';
|
|
4
4
|
import { SSEMediasEvent, SSEUploadProgressEvent, SSEConvertProgressEvent, SSEEpisodesEvent, SSESeriesEvent, SSEMoviesEvent, SSEBooksEvent, SSEPeopleEvent, SSETagsEvent, SSELibraryStatusEvent, SSEMediaRatingEvent, SSEMediaProgressEvent, SSERequestProcessingEvent } from './sse-types.js';
|
|
5
5
|
import { EncryptFileOptions, EncryptedFile } from './encryption.js';
|
|
6
6
|
export interface LibraryHttpClient {
|
|
@@ -78,6 +78,10 @@ export declare class LibraryApi {
|
|
|
78
78
|
setKey(passPhrase: string): Promise<void>;
|
|
79
79
|
private getUrl;
|
|
80
80
|
private openSearchStream;
|
|
81
|
+
searchAllStream(name: string, callbacks: SearchStreamCallbacks<LookupSearchStreamResult>, options?: {
|
|
82
|
+
source?: string[];
|
|
83
|
+
pageKey?: string;
|
|
84
|
+
}): () => void;
|
|
81
85
|
getTags(query?: {
|
|
82
86
|
name?: string;
|
|
83
87
|
parent?: string;
|
|
@@ -160,7 +164,10 @@ export declare class LibraryApi {
|
|
|
160
164
|
getMovieProgress(movieId: string): Promise<IViewProgress>;
|
|
161
165
|
setMovieProgress(movieId: string, progress: number): Promise<void>;
|
|
162
166
|
searchMovies(name: string): Promise<MovieSearchResult[]>;
|
|
163
|
-
searchMoviesStream(name: string, callbacks: SearchStreamCallbacks<MovieSearchStreamResult
|
|
167
|
+
searchMoviesStream(name: string, callbacks: SearchStreamCallbacks<MovieSearchStreamResult>, options?: {
|
|
168
|
+
source?: string[];
|
|
169
|
+
pageKey?: string;
|
|
170
|
+
}): () => void;
|
|
164
171
|
movieRename(movieId: string, newName: string): Promise<IMovie>;
|
|
165
172
|
updateMoviePoster(movieId: string, poster: FormData, type: string): Promise<void>;
|
|
166
173
|
updateMovieImageFetch(movieId: string, image: ExternalImage): Promise<void>;
|
|
@@ -180,7 +187,10 @@ export declare class LibraryApi {
|
|
|
180
187
|
removeBook(bookId: string): Promise<void>;
|
|
181
188
|
updateBook(bookId: string, updates: Partial<IBook>): Promise<IBook>;
|
|
182
189
|
searchBooks(name: string): Promise<BookSearchResult[]>;
|
|
183
|
-
searchBooksStream(name: string, callbacks: SearchStreamCallbacks<BookSearchStreamResult
|
|
190
|
+
searchBooksStream(name: string, callbacks: SearchStreamCallbacks<BookSearchStreamResult>, options?: {
|
|
191
|
+
source?: string[];
|
|
192
|
+
pageKey?: string;
|
|
193
|
+
}): () => void;
|
|
184
194
|
bookRename(bookId: string, newName: string): Promise<IBook>;
|
|
185
195
|
getBookImages(bookId: string): Promise<ExternalImage[]>;
|
|
186
196
|
updateBookPoster(bookId: string, poster: FormData): Promise<void>;
|
|
@@ -195,12 +205,17 @@ export declare class LibraryApi {
|
|
|
195
205
|
personRemoveAlt(personId: string, alt: string): Promise<IPerson>;
|
|
196
206
|
personAddSocial(personId: string, social: any): Promise<IPerson>;
|
|
197
207
|
personRemoveSocial(personId: string, social: any): Promise<IPerson>;
|
|
208
|
+
personAddOtherId(personId: string, otherId: string): Promise<IPerson>;
|
|
209
|
+
personRemoveOtherId(personId: string, otherId: string): Promise<IPerson>;
|
|
198
210
|
serieRename(serieId: string, newName: string): Promise<ISerie>;
|
|
199
211
|
serieAddAlt(serieId: string, alt: string): Promise<ISerie>;
|
|
200
212
|
serieRemoveAlt(serieId: string, alt: string): Promise<ISerie>;
|
|
201
213
|
updatePersonPortrait(personId: string, portrait: FormData): Promise<void>;
|
|
202
214
|
searchSeries(name: string): Promise<SerieSearchResult[]>;
|
|
203
|
-
searchSeriesStream(name: string, callbacks: SearchStreamCallbacks<SerieSearchStreamResult
|
|
215
|
+
searchSeriesStream(name: string, callbacks: SearchStreamCallbacks<SerieSearchStreamResult>, options?: {
|
|
216
|
+
source?: string[];
|
|
217
|
+
pageKey?: string;
|
|
218
|
+
}): () => void;
|
|
204
219
|
setEpisodeWatched(serieId: string, season: number, number: number, date: number): Promise<void>;
|
|
205
220
|
getEpisodeWatched(serieId: string, season: number, episode: number): Promise<IWatched>;
|
|
206
221
|
getEpisodeProgress(serieId: string, season: number, episode: number): Promise<IViewProgress>;
|
package/dist/library.js
CHANGED
|
@@ -124,6 +124,14 @@ export class LibraryApi {
|
|
|
124
124
|
}
|
|
125
125
|
}, finish, (error) => callbacks.onError?.(error));
|
|
126
126
|
}
|
|
127
|
+
searchAllStream(name, callbacks, options) {
|
|
128
|
+
const params = { name };
|
|
129
|
+
if (options?.source?.length)
|
|
130
|
+
params.source = options.source.join(',');
|
|
131
|
+
if (options?.pageKey)
|
|
132
|
+
params.pageKey = options.pageKey;
|
|
133
|
+
return this.openSearchStream(this.getUrl('/searchstream'), params, callbacks);
|
|
134
|
+
}
|
|
127
135
|
async getTags(query) {
|
|
128
136
|
const params = {};
|
|
129
137
|
if (query) {
|
|
@@ -425,8 +433,13 @@ export class LibraryApi {
|
|
|
425
433
|
const res = await this.client.get(this.getUrl(`/movies/search?name=${name}`));
|
|
426
434
|
return res.data;
|
|
427
435
|
}
|
|
428
|
-
searchMoviesStream(name, callbacks) {
|
|
429
|
-
|
|
436
|
+
searchMoviesStream(name, callbacks, options) {
|
|
437
|
+
const params = { name };
|
|
438
|
+
if (options?.source?.length)
|
|
439
|
+
params.source = options.source.join(',');
|
|
440
|
+
if (options?.pageKey)
|
|
441
|
+
params.pageKey = options.pageKey;
|
|
442
|
+
return this.openSearchStream(this.getUrl('/movies/searchstream'), params, callbacks);
|
|
430
443
|
}
|
|
431
444
|
async movieRename(movieId, newName) {
|
|
432
445
|
const res = await this.client.patch(this.getUrl(`/movies/${movieId}`), {
|
|
@@ -493,8 +506,13 @@ export class LibraryApi {
|
|
|
493
506
|
const res = await this.client.get(this.getUrl(`/books/search?name=${name}`));
|
|
494
507
|
return res.data;
|
|
495
508
|
}
|
|
496
|
-
searchBooksStream(name, callbacks) {
|
|
497
|
-
|
|
509
|
+
searchBooksStream(name, callbacks, options) {
|
|
510
|
+
const params = { name };
|
|
511
|
+
if (options?.source?.length)
|
|
512
|
+
params.source = options.source.join(',');
|
|
513
|
+
if (options?.pageKey)
|
|
514
|
+
params.pageKey = options.pageKey;
|
|
515
|
+
return this.openSearchStream(this.getUrl('/books/searchstream'), params, callbacks);
|
|
498
516
|
}
|
|
499
517
|
async bookRename(bookId, newName) {
|
|
500
518
|
const res = await this.client.patch(this.getUrl(`/books/${bookId}`), {
|
|
@@ -560,6 +578,18 @@ export class LibraryApi {
|
|
|
560
578
|
});
|
|
561
579
|
return res.data;
|
|
562
580
|
}
|
|
581
|
+
async personAddOtherId(personId, otherId) {
|
|
582
|
+
const res = await this.client.patch(this.getUrl(`/people/${personId}`), {
|
|
583
|
+
addOtherids: [otherId]
|
|
584
|
+
});
|
|
585
|
+
return res.data;
|
|
586
|
+
}
|
|
587
|
+
async personRemoveOtherId(personId, otherId) {
|
|
588
|
+
const res = await this.client.patch(this.getUrl(`/people/${personId}`), {
|
|
589
|
+
removeOtherids: [otherId]
|
|
590
|
+
});
|
|
591
|
+
return res.data;
|
|
592
|
+
}
|
|
563
593
|
async serieRename(serieId, newName) {
|
|
564
594
|
const res = await this.client.patch(this.getUrl(`/series/${serieId}`), {
|
|
565
595
|
name: newName
|
|
@@ -585,8 +615,13 @@ export class LibraryApi {
|
|
|
585
615
|
const res = await this.client.get(this.getUrl(`/series/search?name=${name}`));
|
|
586
616
|
return res.data;
|
|
587
617
|
}
|
|
588
|
-
searchSeriesStream(name, callbacks) {
|
|
589
|
-
|
|
618
|
+
searchSeriesStream(name, callbacks, options) {
|
|
619
|
+
const params = { name };
|
|
620
|
+
if (options?.source?.length)
|
|
621
|
+
params.source = options.source.join(',');
|
|
622
|
+
if (options?.pageKey)
|
|
623
|
+
params.pageKey = options.pageKey;
|
|
624
|
+
return this.openSearchStream(this.getUrl('/series/searchstream'), params, callbacks);
|
|
590
625
|
}
|
|
591
626
|
async setEpisodeWatched(serieId, season, number, date) {
|
|
592
627
|
await this.client.post(this.getUrl(`/series/${serieId}/seasons/${season}/episodes/${number}/watched`), { date });
|
package/dist/server.d.ts
CHANGED
|
@@ -15,7 +15,10 @@ export declare class ServerApi {
|
|
|
15
15
|
updateLibrary(libraryId: string, library: ServerLibraryForUpdate): Promise<ILibrary>;
|
|
16
16
|
deleteLibrary(libraryId: string, deleteMediaContent?: boolean): Promise<void>;
|
|
17
17
|
getPlugins(): Promise<IPlugin[]>;
|
|
18
|
-
searchLookupStream(query: string, type: string, callbacks: SearchStreamCallbacks<LookupSearchStreamResult
|
|
18
|
+
searchLookupStream(query: string, type: string, callbacks: SearchStreamCallbacks<LookupSearchStreamResult>, options?: {
|
|
19
|
+
source?: string[];
|
|
20
|
+
pageKey?: string;
|
|
21
|
+
}): () => void;
|
|
19
22
|
getCredentials(): Promise<ICredential[]>;
|
|
20
23
|
saveCredential(credential: ICredential): Promise<ICredential>;
|
|
21
24
|
updateCredential(credential: ICredential): Promise<ICredential>;
|
package/dist/server.js
CHANGED
|
@@ -73,8 +73,13 @@ export class ServerApi {
|
|
|
73
73
|
const res = await this.client.get('/plugins');
|
|
74
74
|
return res.data;
|
|
75
75
|
}
|
|
76
|
-
searchLookupStream(query, type, callbacks) {
|
|
77
|
-
|
|
76
|
+
searchLookupStream(query, type, callbacks, options) {
|
|
77
|
+
const params = { q: query, type };
|
|
78
|
+
if (options?.source?.length)
|
|
79
|
+
params.source = options.source.join(',');
|
|
80
|
+
if (options?.pageKey)
|
|
81
|
+
params.pageKey = options.pageKey;
|
|
82
|
+
return this.openSearchStream('/plugins/lookup/searchstream', params, callbacks);
|
|
78
83
|
}
|
|
79
84
|
async getCredentials() {
|
|
80
85
|
const res = await this.client.get('/credentials');
|
package/dist/sse-fetch.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export interface RawSSEEvent {
|
|
|
10
10
|
/**
|
|
11
11
|
* Parses a raw SSE buffer into discrete events, returning any incomplete trailing data.
|
|
12
12
|
* Follows the SSE spec: events are separated by blank lines, fields are `field: value`.
|
|
13
|
+
* Handles \r\n, \r, and \n line endings per the SSE specification.
|
|
13
14
|
*/
|
|
14
15
|
export declare function parseSSEBuffer(buffer: string): {
|
|
15
16
|
events: RawSSEEvent[];
|
package/dist/sse-fetch.js
CHANGED
|
@@ -1,58 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parses a raw SSE buffer into discrete events, returning any incomplete trailing data.
|
|
3
3
|
* Follows the SSE spec: events are separated by blank lines, fields are `field: value`.
|
|
4
|
+
* Handles \r\n, \r, and \n line endings per the SSE specification.
|
|
4
5
|
*/
|
|
5
6
|
export function parseSSEBuffer(buffer) {
|
|
6
7
|
const events = [];
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (
|
|
8
|
+
const normalised = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
9
|
+
// Split into blocks separated by blank lines (\n\n).
|
|
10
|
+
// Only complete blocks (followed by \n\n) are processed;
|
|
11
|
+
// any trailing incomplete block stays in the buffer.
|
|
12
|
+
let lastEventEnd = 0;
|
|
13
|
+
const blockBoundary = '\n\n';
|
|
14
|
+
let searchFrom = 0;
|
|
15
|
+
while (true) {
|
|
16
|
+
const idx = normalised.indexOf(blockBoundary, searchFrom);
|
|
17
|
+
if (idx === -1)
|
|
17
18
|
break;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
const block = normalised.slice(lastEventEnd, idx);
|
|
20
|
+
lastEventEnd = idx + blockBoundary.length;
|
|
21
|
+
searchFrom = lastEventEnd;
|
|
22
|
+
let eventType = '';
|
|
23
|
+
let eventData = '';
|
|
24
|
+
let eventId;
|
|
25
|
+
let eventRetry;
|
|
26
|
+
for (const line of block.split('\n')) {
|
|
27
|
+
if (line === '' || line.startsWith(':'))
|
|
28
|
+
continue;
|
|
29
|
+
const colonIdx = line.indexOf(':');
|
|
30
|
+
if (colonIdx === -1)
|
|
31
|
+
continue;
|
|
32
|
+
const field = line.slice(0, colonIdx);
|
|
33
|
+
let value = line.slice(colonIdx + 1);
|
|
34
|
+
if (value.startsWith(' '))
|
|
35
|
+
value = value.slice(1);
|
|
36
|
+
switch (field) {
|
|
37
|
+
case 'event':
|
|
38
|
+
eventType = value;
|
|
39
|
+
break;
|
|
40
|
+
case 'data':
|
|
41
|
+
eventData = eventData ? eventData + '\n' + value : value;
|
|
42
|
+
break;
|
|
43
|
+
case 'id':
|
|
44
|
+
eventId = value;
|
|
45
|
+
break;
|
|
46
|
+
case 'retry':
|
|
47
|
+
eventRetry = parseInt(value, 10);
|
|
48
|
+
break;
|
|
24
49
|
}
|
|
25
|
-
eventType = '';
|
|
26
|
-
eventData = '';
|
|
27
|
-
eventId = undefined;
|
|
28
|
-
eventRetry = undefined;
|
|
29
|
-
continue;
|
|
30
50
|
}
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
const colonIdx = line.indexOf(':');
|
|
34
|
-
if (colonIdx === -1)
|
|
35
|
-
continue;
|
|
36
|
-
const field = line.slice(0, colonIdx);
|
|
37
|
-
let value = line.slice(colonIdx + 1);
|
|
38
|
-
if (value.startsWith(' '))
|
|
39
|
-
value = value.slice(1);
|
|
40
|
-
switch (field) {
|
|
41
|
-
case 'event':
|
|
42
|
-
eventType = value;
|
|
43
|
-
break;
|
|
44
|
-
case 'data':
|
|
45
|
-
eventData = eventData ? eventData + '\n' + value : value;
|
|
46
|
-
break;
|
|
47
|
-
case 'id':
|
|
48
|
-
eventId = value;
|
|
49
|
-
break;
|
|
50
|
-
case 'retry':
|
|
51
|
-
eventRetry = parseInt(value, 10);
|
|
52
|
-
break;
|
|
51
|
+
if (eventType) {
|
|
52
|
+
events.push({ event: eventType, data: eventData, id: eventId, retry: eventRetry });
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
// Note: remainingBuffer is from the normalised string. Callers must replace
|
|
56
|
+
// their buffer entirely (not use offset-based splicing on the original input).
|
|
57
|
+
return { events, remainingBuffer: normalised.slice(lastEventEnd) };
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
60
|
* Opens a one-shot fetch-based SSE stream (no reconnection).
|
|
@@ -87,6 +89,14 @@ export function openFetchSSEStream(url, onEvent, onDone, onError) {
|
|
|
87
89
|
while (true) {
|
|
88
90
|
const { done, value } = await reader.read();
|
|
89
91
|
if (done) {
|
|
92
|
+
// Flush any remaining buffered event when the stream closes
|
|
93
|
+
// (server may close without a trailing blank line)
|
|
94
|
+
if (buffer.trim()) {
|
|
95
|
+
const { events: finalEvents } = parseSSEBuffer(buffer + '\n\n');
|
|
96
|
+
for (const event of finalEvents) {
|
|
97
|
+
onEvent(event.event, event.data);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
90
100
|
onDone?.();
|
|
91
101
|
break;
|
|
92
102
|
}
|