@redseat/api 0.3.7 → 0.3.12
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/client.d.ts +5 -2
- package/dist/client.js +21 -6
- package/dist/interfaces.d.ts +88 -0
- package/dist/interfaces.js +16 -0
- package/dist/library.d.ts +42 -2
- package/dist/library.js +54 -0
- package/dist/sse-types.d.ts +26 -5
- package/libraries.md +152 -0
- package/package.json +1 -1
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, SSEMediaRatingEvent, SSEMediaProgressEvent, SSEPlayersListEvent, SSEWatchedEvent, SSEUnwatchedEvent } from './sse-types.js';
|
|
5
|
+
import { SSEConnectionState, SSEConnectionOptions, SSEConnectionError, SSELibraryEvent, SSELibraryStatusEvent, SSEMediasEvent, SSEUploadProgressEvent, SSEConvertProgressEvent, SSEEpisodesEvent, SSESeriesEvent, SSEMoviesEvent, SSEPeopleEvent, SSETagsEvent, SSEBackupsEvent, SSEBackupFilesEvent, SSEMediaRatingEvent, SSEMediaProgressEvent, SSEPlayersListEvent, SSEWatchedEvent, SSEUnwatchedEvent, SSERequestProcessingEvent } from './sse-types.js';
|
|
6
6
|
export interface ClientOptions {
|
|
7
7
|
server: IServer;
|
|
8
8
|
getIdToken: () => Promise<string>;
|
|
@@ -20,6 +20,8 @@ export declare class RedseatClient {
|
|
|
20
20
|
private localServerUrl?;
|
|
21
21
|
private tokenData?;
|
|
22
22
|
private tokenRefreshPromise?;
|
|
23
|
+
private localDetectionAbortController?;
|
|
24
|
+
private disposed;
|
|
23
25
|
private sseAbortController?;
|
|
24
26
|
private sseReconnectTimeout?;
|
|
25
27
|
private sseReconnectAttempts;
|
|
@@ -47,6 +49,7 @@ export declare class RedseatClient {
|
|
|
47
49
|
readonly playersList$: Observable<SSEPlayersListEvent>;
|
|
48
50
|
readonly watched$: Observable<SSEWatchedEvent>;
|
|
49
51
|
readonly unwatched$: Observable<SSEUnwatchedEvent>;
|
|
52
|
+
readonly requestProcessing$: Observable<SSERequestProcessingEvent>;
|
|
50
53
|
/**
|
|
51
54
|
* Creates a typed observable for a specific SSE event type.
|
|
52
55
|
* Unwraps the nested data structure from the server (e.g., {uploadProgress: {...}} -> {...})
|
|
@@ -102,7 +105,7 @@ export declare class RedseatClient {
|
|
|
102
105
|
*/
|
|
103
106
|
disconnectSSE(): void;
|
|
104
107
|
/**
|
|
105
|
-
* Disposes of all
|
|
108
|
+
* Disposes of all resources and completes observables.
|
|
106
109
|
* Call this when the client is no longer needed.
|
|
107
110
|
*/
|
|
108
111
|
dispose(): void;
|
package/dist/client.js
CHANGED
|
@@ -17,6 +17,12 @@ export class RedseatClient {
|
|
|
17
17
|
'library-status': 'libraryStatus',
|
|
18
18
|
'backups-files': 'backupsFiles',
|
|
19
19
|
'players-list': 'Players',
|
|
20
|
+
'episodes': 'episodes',
|
|
21
|
+
'series': 'series',
|
|
22
|
+
'movies': 'movies',
|
|
23
|
+
'people': 'people',
|
|
24
|
+
'tags': 'tags',
|
|
25
|
+
'request_processing': 'requestProcessing',
|
|
20
26
|
};
|
|
21
27
|
const wrapperKey = wrapperMap[eventName];
|
|
22
28
|
return this._sseEvents.pipe(filter((event) => event.event === eventName), map(event => {
|
|
@@ -30,6 +36,7 @@ export class RedseatClient {
|
|
|
30
36
|
}));
|
|
31
37
|
}
|
|
32
38
|
constructor(options) {
|
|
39
|
+
this.disposed = false;
|
|
33
40
|
this.sseReconnectAttempts = 0;
|
|
34
41
|
// RxJS subjects for SSE
|
|
35
42
|
this._sseConnectionState = new BehaviorSubject('disconnected');
|
|
@@ -57,6 +64,7 @@ export class RedseatClient {
|
|
|
57
64
|
this.playersList$ = this.createEventStream('players-list');
|
|
58
65
|
this.watched$ = this.createEventStream('watched');
|
|
59
66
|
this.unwatched$ = this.createEventStream('unwatched');
|
|
67
|
+
this.requestProcessing$ = this.createEventStream('request_processing');
|
|
60
68
|
this.server = options.server;
|
|
61
69
|
this.redseatUrl = options.redseatUrl;
|
|
62
70
|
this.getIdToken = options.getIdToken;
|
|
@@ -65,16 +73,19 @@ export class RedseatClient {
|
|
|
65
73
|
this.baseUrl = this.getRegularServerUrl();
|
|
66
74
|
this.axios = axios.create({
|
|
67
75
|
baseURL: this.baseUrl,
|
|
68
|
-
timeout: options.timeout ??
|
|
76
|
+
timeout: options.timeout ?? 30000 // 30 second default
|
|
69
77
|
});
|
|
70
78
|
// Detect local URL asynchronously and update axios instance
|
|
71
|
-
this.
|
|
79
|
+
this.localDetectionAbortController = new AbortController();
|
|
80
|
+
this.detectLocalUrl(this.localDetectionAbortController.signal).then((url) => {
|
|
81
|
+
if (this.disposed)
|
|
82
|
+
return; // Don't update if disposed
|
|
72
83
|
if (url !== this.baseUrl) {
|
|
73
84
|
this.baseUrl = url;
|
|
74
85
|
this.axios.defaults.baseURL = url;
|
|
75
86
|
}
|
|
76
87
|
}).catch(() => {
|
|
77
|
-
// If detection fails, use regular URL (already set)
|
|
88
|
+
// If detection fails or aborted, use regular URL (already set)
|
|
78
89
|
});
|
|
79
90
|
// Request interceptor: check token expiration and refresh if needed
|
|
80
91
|
this.axios.interceptors.request.use(async (config) => {
|
|
@@ -110,7 +121,7 @@ export class RedseatClient {
|
|
|
110
121
|
}
|
|
111
122
|
return `https://${base}`;
|
|
112
123
|
}
|
|
113
|
-
async detectLocalUrl() {
|
|
124
|
+
async detectLocalUrl(signal) {
|
|
114
125
|
let localBase = `local.${this.server.url}`;
|
|
115
126
|
if (this.server.port) {
|
|
116
127
|
localBase = `${localBase}:${this.server.port}`;
|
|
@@ -120,7 +131,8 @@ export class RedseatClient {
|
|
|
120
131
|
console.log('trying local server url', localUrl);
|
|
121
132
|
const response = await axios.get(`${localUrl}/ping`, {
|
|
122
133
|
timeout: 200,
|
|
123
|
-
headers: { "Referrer-Policy": 'origin-when-cross-origin' }
|
|
134
|
+
headers: { "Referrer-Policy": 'origin-when-cross-origin' },
|
|
135
|
+
signal
|
|
124
136
|
});
|
|
125
137
|
if (response.status === 200) {
|
|
126
138
|
console.log('local server detected');
|
|
@@ -301,10 +313,13 @@ export class RedseatClient {
|
|
|
301
313
|
this._sseConnectionState.next('disconnected');
|
|
302
314
|
}
|
|
303
315
|
/**
|
|
304
|
-
* Disposes of all
|
|
316
|
+
* Disposes of all resources and completes observables.
|
|
305
317
|
* Call this when the client is no longer needed.
|
|
306
318
|
*/
|
|
307
319
|
dispose() {
|
|
320
|
+
this.disposed = true;
|
|
321
|
+
this.localDetectionAbortController?.abort();
|
|
322
|
+
this.localDetectionAbortController = undefined;
|
|
308
323
|
this.disconnectSSE();
|
|
309
324
|
this._sseConnectionState.complete();
|
|
310
325
|
this._sseError.complete();
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -731,6 +731,8 @@ export interface RsRequest {
|
|
|
731
731
|
size?: number;
|
|
732
732
|
filename?: string;
|
|
733
733
|
status: RsRequestStatus;
|
|
734
|
+
pluginName?: string;
|
|
735
|
+
pluginId?: string;
|
|
734
736
|
permanent: boolean;
|
|
735
737
|
instant?: boolean;
|
|
736
738
|
jsonBody?: any;
|
|
@@ -768,3 +770,89 @@ export interface RsGroupDownload {
|
|
|
768
770
|
groupMime?: string;
|
|
769
771
|
requests: RsRequest[];
|
|
770
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Request processing status from plugin-based download/processing.
|
|
775
|
+
* Tracks progress of downloads, file processing, etc.
|
|
776
|
+
*/
|
|
777
|
+
export interface IRsRequestProcessing {
|
|
778
|
+
/** Internal nanoid */
|
|
779
|
+
id: string;
|
|
780
|
+
/** Plugin's processing ID */
|
|
781
|
+
processingId: string;
|
|
782
|
+
/** Plugin handling the request */
|
|
783
|
+
pluginId: string;
|
|
784
|
+
/** Progress 0-100 */
|
|
785
|
+
progress: number;
|
|
786
|
+
/** Status: "pending" | "processing" | "paused" | "finished" | "error" */
|
|
787
|
+
status: string;
|
|
788
|
+
/** Error message if status is "error" */
|
|
789
|
+
error?: string;
|
|
790
|
+
/** UTC timestamp (ms) for estimated completion */
|
|
791
|
+
eta?: number;
|
|
792
|
+
/** Reference to media being processed */
|
|
793
|
+
mediaRef?: string;
|
|
794
|
+
/** The original request */
|
|
795
|
+
originalRequest?: RsRequest;
|
|
796
|
+
/** Last modified timestamp */
|
|
797
|
+
modified: number;
|
|
798
|
+
/** Creation timestamp */
|
|
799
|
+
added: number;
|
|
800
|
+
}
|
|
801
|
+
export declare enum VideoOverlayPosition {
|
|
802
|
+
topLeft = "topLeft",
|
|
803
|
+
topRight = "topRight",
|
|
804
|
+
topCenter = "topCenter",
|
|
805
|
+
bottomLeft = "bottomLeft",
|
|
806
|
+
bottomRight = "bottomRight",
|
|
807
|
+
bottomCenter = "bottomCenter",
|
|
808
|
+
Center = "center"
|
|
809
|
+
}
|
|
810
|
+
export declare enum VideoOverlayType {
|
|
811
|
+
watermark = "watermark",
|
|
812
|
+
file = "file"
|
|
813
|
+
}
|
|
814
|
+
export interface VideoOverlay {
|
|
815
|
+
type: VideoOverlayType;
|
|
816
|
+
path: string;
|
|
817
|
+
position?: VideoOverlayPosition;
|
|
818
|
+
margin?: number;
|
|
819
|
+
ratio: number;
|
|
820
|
+
opacity: number;
|
|
821
|
+
}
|
|
822
|
+
export interface VideoTextOverlay {
|
|
823
|
+
text: string;
|
|
824
|
+
fontColor: string;
|
|
825
|
+
font?: string;
|
|
826
|
+
position: VideoOverlayPosition;
|
|
827
|
+
marginHorizontal?: number;
|
|
828
|
+
marginVertical?: number;
|
|
829
|
+
fontSize: number;
|
|
830
|
+
opacity?: number;
|
|
831
|
+
shadowColor: string;
|
|
832
|
+
shadowOpacity?: number;
|
|
833
|
+
start?: number;
|
|
834
|
+
end?: number;
|
|
835
|
+
}
|
|
836
|
+
export interface VideoConvertInterval {
|
|
837
|
+
start: number;
|
|
838
|
+
duration: number;
|
|
839
|
+
/** Defaults to current input */
|
|
840
|
+
input?: string;
|
|
841
|
+
}
|
|
842
|
+
export interface VideoConvertRequest {
|
|
843
|
+
id: string;
|
|
844
|
+
format: string;
|
|
845
|
+
codec?: string;
|
|
846
|
+
crf?: number;
|
|
847
|
+
noAudio?: boolean;
|
|
848
|
+
width?: string;
|
|
849
|
+
height?: string;
|
|
850
|
+
framerate?: number;
|
|
851
|
+
cropWidth?: number;
|
|
852
|
+
cropHeight?: number;
|
|
853
|
+
aspectRatio?: string;
|
|
854
|
+
aspectRatioAlignment?: 'center' | 'left' | 'right';
|
|
855
|
+
overlay?: VideoOverlay;
|
|
856
|
+
texts?: VideoTextOverlay[];
|
|
857
|
+
intervals?: VideoConvertInterval[];
|
|
858
|
+
}
|
package/dist/interfaces.js
CHANGED
|
@@ -88,3 +88,19 @@ export var RsRequestMethod;
|
|
|
88
88
|
RsRequestMethod["Delete"] = "delete";
|
|
89
89
|
RsRequestMethod["Head"] = "head";
|
|
90
90
|
})(RsRequestMethod || (RsRequestMethod = {}));
|
|
91
|
+
// ========== Video Convert Types ==========
|
|
92
|
+
export var VideoOverlayPosition;
|
|
93
|
+
(function (VideoOverlayPosition) {
|
|
94
|
+
VideoOverlayPosition["topLeft"] = "topLeft";
|
|
95
|
+
VideoOverlayPosition["topRight"] = "topRight";
|
|
96
|
+
VideoOverlayPosition["topCenter"] = "topCenter";
|
|
97
|
+
VideoOverlayPosition["bottomLeft"] = "bottomLeft";
|
|
98
|
+
VideoOverlayPosition["bottomRight"] = "bottomRight";
|
|
99
|
+
VideoOverlayPosition["bottomCenter"] = "bottomCenter";
|
|
100
|
+
VideoOverlayPosition["Center"] = "center";
|
|
101
|
+
})(VideoOverlayPosition || (VideoOverlayPosition = {}));
|
|
102
|
+
export var VideoOverlayType;
|
|
103
|
+
(function (VideoOverlayType) {
|
|
104
|
+
VideoOverlayType["watermark"] = "watermark";
|
|
105
|
+
VideoOverlayType["file"] = "file";
|
|
106
|
+
})(VideoOverlayType || (VideoOverlayType = {}));
|
package/dist/library.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import type { AxiosResponse } from 'axios';
|
|
3
|
+
import { IFile, ITag, IPerson, ISerie, IMovie, MediaRequest, IEpisode, ExternalImage, IBackupFile, ILibrary, SerieInMedia, DeletedQuery, RsDeleted, MovieSort, RsSort, SqlOrder, RsRequest, DetectedFaceResult, UnassignFaceResponse, RsGroupDownload, IViewProgress, IWatched, IRsRequestProcessing } from './interfaces.js';
|
|
4
|
+
import { SSEMediasEvent, SSEUploadProgressEvent, SSEConvertProgressEvent, SSEEpisodesEvent, SSESeriesEvent, SSEMoviesEvent, SSEPeopleEvent, SSETagsEvent, SSELibraryStatusEvent, SSEMediaRatingEvent, SSEMediaProgressEvent, SSERequestProcessingEvent } from './sse-types.js';
|
|
4
5
|
import { EncryptFileOptions, EncryptedFile } from './encryption.js';
|
|
5
6
|
export interface MediaForUpdate {
|
|
6
7
|
name?: string;
|
|
@@ -87,6 +88,7 @@ export interface LibraryHttpClient {
|
|
|
87
88
|
readonly libraryStatus$?: Observable<SSELibraryStatusEvent>;
|
|
88
89
|
readonly mediaRating$?: Observable<SSEMediaRatingEvent>;
|
|
89
90
|
readonly mediaProgress$?: Observable<SSEMediaProgressEvent>;
|
|
91
|
+
readonly requestProcessing$?: Observable<SSERequestProcessingEvent>;
|
|
90
92
|
}
|
|
91
93
|
export declare class LibraryApi {
|
|
92
94
|
private client;
|
|
@@ -106,6 +108,7 @@ export declare class LibraryApi {
|
|
|
106
108
|
readonly libraryStatus$: Observable<SSELibraryStatusEvent>;
|
|
107
109
|
readonly mediaRating$: Observable<SSEMediaRatingEvent>;
|
|
108
110
|
readonly mediaProgress$: Observable<SSEMediaProgressEvent>;
|
|
111
|
+
readonly requestProcessing$: Observable<SSERequestProcessingEvent>;
|
|
109
112
|
constructor(client: LibraryHttpClient, libraryId: string, library: ILibrary);
|
|
110
113
|
/**
|
|
111
114
|
* Creates a library-filtered stream from a client stream.
|
|
@@ -253,6 +256,43 @@ export declare class LibraryApi {
|
|
|
253
256
|
checkRequestInstant(request: RsRequest): Promise<{
|
|
254
257
|
instant: boolean;
|
|
255
258
|
}>;
|
|
259
|
+
/**
|
|
260
|
+
* Process an unprocessed RsRequest and return the processed result.
|
|
261
|
+
* Takes a raw request and runs it through the server's plugin processing pipeline.
|
|
262
|
+
* @param request - The unprocessed request to process
|
|
263
|
+
* @returns The processed request with updated status and metadata
|
|
264
|
+
*/
|
|
265
|
+
processRequest(request: RsRequest): Promise<RsRequest>;
|
|
266
|
+
/**
|
|
267
|
+
* Process a request and return the raw HTTP stream response.
|
|
268
|
+
* Use this to stream content directly without processing the full response.
|
|
269
|
+
* @param request - The request to process and stream
|
|
270
|
+
* @returns Raw axios response with stream data - use response.data for the stream
|
|
271
|
+
*/
|
|
272
|
+
processRequestStream(request: RsRequest): Promise<AxiosResponse>;
|
|
273
|
+
/**
|
|
274
|
+
* Add a request to the processing queue.
|
|
275
|
+
* @param request - The request to add for processing
|
|
276
|
+
* @param mediaRef - Optional media reference to associate with the processing
|
|
277
|
+
* @returns The created request processing entry
|
|
278
|
+
*/
|
|
279
|
+
addRequest(request: RsRequest, mediaRef?: string): Promise<IRsRequestProcessing>;
|
|
280
|
+
/**
|
|
281
|
+
* List all active request processings for this library.
|
|
282
|
+
* @returns Array of request processing entries
|
|
283
|
+
*/
|
|
284
|
+
listRequestProcessing(): Promise<IRsRequestProcessing[]>;
|
|
285
|
+
/**
|
|
286
|
+
* Pause a request processing task.
|
|
287
|
+
* @param processingId - The ID of the processing task to pause
|
|
288
|
+
* @returns The updated request processing entry with status "paused"
|
|
289
|
+
*/
|
|
290
|
+
pauseRequestProcessing(processingId: string): Promise<IRsRequestProcessing>;
|
|
291
|
+
/**
|
|
292
|
+
* Delete/remove a request processing task.
|
|
293
|
+
* @param processingId - The ID of the processing task to delete
|
|
294
|
+
*/
|
|
295
|
+
deleteRequestProcessing(processingId: string): Promise<void>;
|
|
256
296
|
/**
|
|
257
297
|
* Get a share token for a request URL.
|
|
258
298
|
* The token can be used to stream/download the resource without authentication.
|
package/dist/library.js
CHANGED
|
@@ -19,6 +19,7 @@ export class LibraryApi {
|
|
|
19
19
|
this.libraryStatus$ = this.createLibraryFilteredStream(client.libraryStatus$);
|
|
20
20
|
this.mediaRating$ = this.createLibraryFilteredStream(client.mediaRating$);
|
|
21
21
|
this.mediaProgress$ = this.createLibraryFilteredStream(client.mediaProgress$);
|
|
22
|
+
this.requestProcessing$ = this.createLibraryFilteredStream(client.requestProcessing$);
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Creates a library-filtered stream from a client stream.
|
|
@@ -525,6 +526,59 @@ export class LibraryApi {
|
|
|
525
526
|
const res = await this.client.post(this.getUrl('/plugins/requests/check-instant'), request);
|
|
526
527
|
return res.data;
|
|
527
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Process an unprocessed RsRequest and return the processed result.
|
|
531
|
+
* Takes a raw request and runs it through the server's plugin processing pipeline.
|
|
532
|
+
* @param request - The unprocessed request to process
|
|
533
|
+
* @returns The processed request with updated status and metadata
|
|
534
|
+
*/
|
|
535
|
+
async processRequest(request) {
|
|
536
|
+
const res = await this.client.post(this.getUrl('/plugins/requests/process'), request);
|
|
537
|
+
return res.data;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Process a request and return the raw HTTP stream response.
|
|
541
|
+
* Use this to stream content directly without processing the full response.
|
|
542
|
+
* @param request - The request to process and stream
|
|
543
|
+
* @returns Raw axios response with stream data - use response.data for the stream
|
|
544
|
+
*/
|
|
545
|
+
async processRequestStream(request) {
|
|
546
|
+
return this.client.post(this.getUrl('/plugins/requests/process/stream'), request, { responseType: 'stream' });
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Add a request to the processing queue.
|
|
550
|
+
* @param request - The request to add for processing
|
|
551
|
+
* @param mediaRef - Optional media reference to associate with the processing
|
|
552
|
+
* @returns The created request processing entry
|
|
553
|
+
*/
|
|
554
|
+
async addRequest(request, mediaRef) {
|
|
555
|
+
const res = await this.client.post(this.getUrl('/plugins/requests/add'), { request, mediaRef });
|
|
556
|
+
return res.data;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* List all active request processings for this library.
|
|
560
|
+
* @returns Array of request processing entries
|
|
561
|
+
*/
|
|
562
|
+
async listRequestProcessing() {
|
|
563
|
+
const res = await this.client.get(this.getUrl('/plugins/requests/processing'));
|
|
564
|
+
return res.data;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Pause a request processing task.
|
|
568
|
+
* @param processingId - The ID of the processing task to pause
|
|
569
|
+
* @returns The updated request processing entry with status "paused"
|
|
570
|
+
*/
|
|
571
|
+
async pauseRequestProcessing(processingId) {
|
|
572
|
+
const res = await this.client.post(this.getUrl(`/plugins/requests/processing/${processingId}/pause`));
|
|
573
|
+
return res.data;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Delete/remove a request processing task.
|
|
577
|
+
* @param processingId - The ID of the processing task to delete
|
|
578
|
+
*/
|
|
579
|
+
async deleteRequestProcessing(processingId) {
|
|
580
|
+
await this.client.delete(this.getUrl(`/plugins/requests/processing/${processingId}`));
|
|
581
|
+
}
|
|
528
582
|
/**
|
|
529
583
|
* Get a share token for a request URL.
|
|
530
584
|
* The token can be used to stream/download the resource without authentication.
|
package/dist/sse-types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ILibrary, IFile, IEpisode, ISerie, IMovie, IPerson, ITag, IBackup, IWatched, IUnwatched } from './interfaces.js';
|
|
1
|
+
import { ILibrary, IFile, IEpisode, ISerie, IMovie, IPerson, ITag, IBackup, IWatched, IUnwatched, IRsRequestProcessing, VideoConvertRequest } from './interfaces.js';
|
|
2
2
|
export type ElementAction = 'Added' | 'Updated' | 'Deleted';
|
|
3
3
|
export type SSEConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
|
|
4
4
|
export interface SSEConnectionOptions {
|
|
@@ -50,12 +50,20 @@ export interface SSEUploadProgressEvent {
|
|
|
50
50
|
progress: RsProgress;
|
|
51
51
|
remainingSecondes?: number;
|
|
52
52
|
}
|
|
53
|
+
export type RsVideoTranscodeStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'canceled';
|
|
54
|
+
export interface ConvertProgress {
|
|
55
|
+
id: string;
|
|
56
|
+
filename: string;
|
|
57
|
+
convertedId: string | null;
|
|
58
|
+
done: boolean;
|
|
59
|
+
percent: number;
|
|
60
|
+
status: RsVideoTranscodeStatus;
|
|
61
|
+
estimatedRemainingSeconds: number | null;
|
|
62
|
+
request: VideoConvertRequest | null;
|
|
63
|
+
}
|
|
53
64
|
export interface SSEConvertProgressEvent {
|
|
54
65
|
library: string;
|
|
55
|
-
|
|
56
|
-
progress: number;
|
|
57
|
-
status: string;
|
|
58
|
-
message?: string;
|
|
66
|
+
progress: ConvertProgress;
|
|
59
67
|
}
|
|
60
68
|
export interface SSEEpisodesEvent {
|
|
61
69
|
library: string;
|
|
@@ -146,6 +154,18 @@ export type SSEWatchedEvent = IWatched;
|
|
|
146
154
|
* This is a user-scoped event - not library-scoped.
|
|
147
155
|
*/
|
|
148
156
|
export type SSEUnwatchedEvent = IUnwatched;
|
|
157
|
+
/**
|
|
158
|
+
* SSE event for request processing status updates.
|
|
159
|
+
* Tracks plugin-based request processing (downloads, file processing, etc.).
|
|
160
|
+
* This is a library-scoped event.
|
|
161
|
+
*/
|
|
162
|
+
export interface SSERequestProcessingEvent {
|
|
163
|
+
library: string;
|
|
164
|
+
processings: {
|
|
165
|
+
action: ElementAction;
|
|
166
|
+
processing: IRsRequestProcessing;
|
|
167
|
+
}[];
|
|
168
|
+
}
|
|
149
169
|
export interface SSEEventMap {
|
|
150
170
|
'library': SSELibraryEvent;
|
|
151
171
|
'library-status': SSELibraryStatusEvent;
|
|
@@ -164,6 +184,7 @@ export interface SSEEventMap {
|
|
|
164
184
|
'players-list': SSEPlayersListEvent;
|
|
165
185
|
'watched': SSEWatchedEvent;
|
|
166
186
|
'unwatched': SSEUnwatchedEvent;
|
|
187
|
+
'request_processing': SSERequestProcessingEvent;
|
|
167
188
|
}
|
|
168
189
|
export type SSEEventName = keyof SSEEventMap;
|
|
169
190
|
export interface SSEEvent<T extends SSEEventName = SSEEventName> {
|
package/libraries.md
CHANGED
|
@@ -1969,6 +1969,158 @@ await libraryApi.clusterFaces('person-id');
|
|
|
1969
1969
|
|
|
1970
1970
|
---
|
|
1971
1971
|
|
|
1972
|
+
## Request Processing
|
|
1973
|
+
|
|
1974
|
+
### `processRequest(request: RsRequest): Promise<RsRequest>`
|
|
1975
|
+
|
|
1976
|
+
Process an unprocessed RsRequest and return the processed result.
|
|
1977
|
+
Takes a raw request and runs it through the server's plugin processing pipeline.
|
|
1978
|
+
|
|
1979
|
+
**Parameters:**
|
|
1980
|
+
|
|
1981
|
+
- `request`: The unprocessed request to process
|
|
1982
|
+
|
|
1983
|
+
**Returns:** Promise resolving to the processed request with updated status and metadata
|
|
1984
|
+
|
|
1985
|
+
**Example:**
|
|
1986
|
+
|
|
1987
|
+
```typescript
|
|
1988
|
+
import { RsRequestStatus, RsRequestMethod } from '@redseat/api';
|
|
1989
|
+
|
|
1990
|
+
const unprocessedRequest: RsRequest = {
|
|
1991
|
+
url: 'https://example.com/video.mp4',
|
|
1992
|
+
method: RsRequestMethod.Get,
|
|
1993
|
+
status: RsRequestStatus.Unprocessed,
|
|
1994
|
+
permanent: false,
|
|
1995
|
+
ignoreOriginDuplicate: false
|
|
1996
|
+
};
|
|
1997
|
+
const processed = await libraryApi.processRequest(unprocessedRequest);
|
|
1998
|
+
console.log(processed.status); // RsRequestStatus.Processed
|
|
1999
|
+
```
|
|
2000
|
+
|
|
2001
|
+
### `processRequestStream(request: RsRequest): Promise<AxiosResponse>`
|
|
2002
|
+
|
|
2003
|
+
Process a request and return the raw HTTP stream response for direct streaming.
|
|
2004
|
+
|
|
2005
|
+
**Parameters:**
|
|
2006
|
+
|
|
2007
|
+
- `request`: The request to process and stream
|
|
2008
|
+
|
|
2009
|
+
**Returns:** Raw axios response - use `response.data` for the stream
|
|
2010
|
+
|
|
2011
|
+
**Example:**
|
|
2012
|
+
|
|
2013
|
+
```typescript
|
|
2014
|
+
const request: RsRequest = {
|
|
2015
|
+
url: 'https://example.com/video.mp4',
|
|
2016
|
+
method: RsRequestMethod.Get,
|
|
2017
|
+
status: RsRequestStatus.Processed,
|
|
2018
|
+
permanent: false,
|
|
2019
|
+
ignoreOriginDuplicate: false
|
|
2020
|
+
};
|
|
2021
|
+
const response = await libraryApi.processRequestStream(request);
|
|
2022
|
+
// response.data is the stream that can be piped directly
|
|
2023
|
+
```
|
|
2024
|
+
|
|
2025
|
+
### `addRequest(request: RsRequest, mediaRef?: string): Promise<IRsRequestProcessing>`
|
|
2026
|
+
|
|
2027
|
+
Add a request to the processing queue for background download/processing.
|
|
2028
|
+
|
|
2029
|
+
**Parameters:**
|
|
2030
|
+
|
|
2031
|
+
- `request`: The request to add for processing
|
|
2032
|
+
- `mediaRef`: Optional media reference to associate with the processing
|
|
2033
|
+
|
|
2034
|
+
**Returns:** Promise resolving to the created `IRsRequestProcessing` entry
|
|
2035
|
+
|
|
2036
|
+
**Example:**
|
|
2037
|
+
|
|
2038
|
+
```typescript
|
|
2039
|
+
import { RsRequestStatus, RsRequestMethod } from '@redseat/api';
|
|
2040
|
+
|
|
2041
|
+
const request: RsRequest = {
|
|
2042
|
+
url: 'https://example.com/large-video.mp4',
|
|
2043
|
+
method: RsRequestMethod.Get,
|
|
2044
|
+
status: RsRequestStatus.Unprocessed,
|
|
2045
|
+
permanent: false,
|
|
2046
|
+
ignoreOriginDuplicate: false
|
|
2047
|
+
};
|
|
2048
|
+
|
|
2049
|
+
// Add to processing queue
|
|
2050
|
+
const processing = await libraryApi.addRequest(request);
|
|
2051
|
+
console.log(processing.id); // Processing ID
|
|
2052
|
+
console.log(processing.status); // "pending" | "processing" | "paused" | "finished" | "error"
|
|
2053
|
+
console.log(processing.progress); // 0-100
|
|
2054
|
+
|
|
2055
|
+
// Associate with existing media
|
|
2056
|
+
const processingWithMedia = await libraryApi.addRequest(request, 'media-id');
|
|
2057
|
+
```
|
|
2058
|
+
|
|
2059
|
+
### `listRequestProcessing(): Promise<IRsRequestProcessing[]>`
|
|
2060
|
+
|
|
2061
|
+
List all active request processings for this library.
|
|
2062
|
+
|
|
2063
|
+
**Returns:** Promise resolving to an array of `IRsRequestProcessing` entries
|
|
2064
|
+
|
|
2065
|
+
**Example:**
|
|
2066
|
+
|
|
2067
|
+
```typescript
|
|
2068
|
+
const processings = await libraryApi.listRequestProcessing();
|
|
2069
|
+
for (const p of processings) {
|
|
2070
|
+
console.log(`${p.id}: ${p.status} (${p.progress}%)`);
|
|
2071
|
+
if (p.error) {
|
|
2072
|
+
console.log(` Error: ${p.error}`);
|
|
2073
|
+
}
|
|
2074
|
+
if (p.mediaRef) {
|
|
2075
|
+
console.log(` Media: ${p.mediaRef}`);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
```
|
|
2079
|
+
|
|
2080
|
+
### `pauseRequestProcessing(processingId: string): Promise<IRsRequestProcessing>`
|
|
2081
|
+
|
|
2082
|
+
Pause a request processing task. This stops the download/processing temporarily.
|
|
2083
|
+
|
|
2084
|
+
**Parameters:**
|
|
2085
|
+
|
|
2086
|
+
- `processingId`: The ID of the processing task to pause
|
|
2087
|
+
|
|
2088
|
+
**Returns:** Promise resolving to the updated `IRsRequestProcessing` entry with status "paused"
|
|
2089
|
+
|
|
2090
|
+
**Example:**
|
|
2091
|
+
|
|
2092
|
+
```typescript
|
|
2093
|
+
const processings = await libraryApi.listRequestProcessing();
|
|
2094
|
+
const activeProcessing = processings.find(p => p.status === 'processing');
|
|
2095
|
+
|
|
2096
|
+
if (activeProcessing) {
|
|
2097
|
+
const paused = await libraryApi.pauseRequestProcessing(activeProcessing.id);
|
|
2098
|
+
console.log(`Paused: ${paused.id}, status: ${paused.status}`); // status: "paused"
|
|
2099
|
+
}
|
|
2100
|
+
```
|
|
2101
|
+
|
|
2102
|
+
### `deleteRequestProcessing(processingId: string): Promise<void>`
|
|
2103
|
+
|
|
2104
|
+
Delete/remove a request processing task from the queue. This cancels and removes the processing entry.
|
|
2105
|
+
|
|
2106
|
+
**Parameters:**
|
|
2107
|
+
|
|
2108
|
+
- `processingId`: The ID of the processing task to delete
|
|
2109
|
+
|
|
2110
|
+
**Example:**
|
|
2111
|
+
|
|
2112
|
+
```typescript
|
|
2113
|
+
const processings = await libraryApi.listRequestProcessing();
|
|
2114
|
+
const processingToRemove = processings.find(p => p.status === 'error');
|
|
2115
|
+
|
|
2116
|
+
if (processingToRemove) {
|
|
2117
|
+
await libraryApi.deleteRequestProcessing(processingToRemove.id);
|
|
2118
|
+
console.log(`Removed processing: ${processingToRemove.id}`);
|
|
2119
|
+
}
|
|
2120
|
+
```
|
|
2121
|
+
|
|
2122
|
+
---
|
|
2123
|
+
|
|
1972
2124
|
## Encryption
|
|
1973
2125
|
|
|
1974
2126
|
These methods are convenience wrappers around the encryption module. For detailed encryption documentation, see [encryption.md](encryption.md).
|