@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/CLAUDE.md +1 -1
- package/README.md +137 -132
- package/{AGENTS.md → agents.md} +275 -275
- package/client.md +670 -670
- package/dist/client.d.ts +3 -1
- package/dist/client.js +2 -0
- package/dist/interfaces.d.ts +187 -0
- package/dist/library.d.ts +7 -1
- package/dist/library.js +22 -0
- package/dist/server.d.ts +7 -1
- package/dist/server.js +40 -0
- package/dist/sse-types.d.ts +15 -1
- package/encryption.md +533 -533
- package/firebase.md +602 -602
- package/libraries.md +145 -4
- package/package.json +49 -49
- package/server.md +538 -277
- package/test.md +291 -291
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 } 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 } from './sse-types.js';
|
|
6
6
|
export interface ClientOptions {
|
|
7
7
|
server: IServer;
|
|
8
8
|
getIdToken: () => Promise<string>;
|
|
@@ -45,6 +45,8 @@ export declare class RedseatClient {
|
|
|
45
45
|
readonly mediaRating$: Observable<SSEMediaRatingEvent>;
|
|
46
46
|
readonly mediaProgress$: Observable<SSEMediaProgressEvent>;
|
|
47
47
|
readonly playersList$: Observable<SSEPlayersListEvent>;
|
|
48
|
+
readonly watched$: Observable<SSEWatchedEvent>;
|
|
49
|
+
readonly unwatched$: Observable<SSEUnwatchedEvent>;
|
|
48
50
|
/**
|
|
49
51
|
* Creates a typed observable for a specific SSE event type.
|
|
50
52
|
* Unwraps the nested data structure from the server (e.g., {uploadProgress: {...}} -> {...})
|
package/dist/client.js
CHANGED
|
@@ -55,6 +55,8 @@ export class RedseatClient {
|
|
|
55
55
|
this.mediaRating$ = this.createEventStream('media_rating');
|
|
56
56
|
this.mediaProgress$ = this.createEventStream('media_progress');
|
|
57
57
|
this.playersList$ = this.createEventStream('players-list');
|
|
58
|
+
this.watched$ = this.createEventStream('watched');
|
|
59
|
+
this.unwatched$ = this.createEventStream('unwatched');
|
|
58
60
|
this.server = options.server;
|
|
59
61
|
this.redseatUrl = options.redseatUrl;
|
|
60
62
|
this.getIdToken = options.getIdToken;
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -483,6 +483,193 @@ export declare enum RsSort {
|
|
|
483
483
|
Name = "name",
|
|
484
484
|
Size = "size"
|
|
485
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* Media type for history/watched tracking.
|
|
488
|
+
* Used to categorize watched entries and view progress.
|
|
489
|
+
*/
|
|
490
|
+
export type MediaType = 'episode' | 'movie' | 'book' | 'song' | 'media';
|
|
491
|
+
/**
|
|
492
|
+
* Watch history entry - returned from GET /users/me/history.
|
|
493
|
+
*
|
|
494
|
+
* ## External ID Format
|
|
495
|
+
*
|
|
496
|
+
* Watch history uses **external IDs** (from providers like IMDb, Trakt, TMDb) rather than
|
|
497
|
+
* local database IDs. This design enables:
|
|
498
|
+
* - **Cross-server portability**: History syncs across different Redseat servers
|
|
499
|
+
* - **External service synchronization**: Seamless sync with Trakt, Plex, etc.
|
|
500
|
+
* - **Library independence**: History is global, not tied to a specific library
|
|
501
|
+
*
|
|
502
|
+
* ### ID Format: `provider:value`
|
|
503
|
+
*
|
|
504
|
+
* Examples:
|
|
505
|
+
* - `imdb:tt1234567` - IMDb ID for movies/episodes
|
|
506
|
+
* - `trakt:123456` - Trakt ID
|
|
507
|
+
* - `tmdb:550` - TMDb ID
|
|
508
|
+
* - `tvdb:12345` - TVDB ID for series/episodes
|
|
509
|
+
* - `redseat:abc123` - Local fallback when no external ID is available
|
|
510
|
+
*
|
|
511
|
+
* When marking content as watched via library endpoints (e.g., `setMovieWatched`),
|
|
512
|
+
* the server automatically resolves the local ID to an external ID.
|
|
513
|
+
*/
|
|
514
|
+
export interface IWatched {
|
|
515
|
+
/** Content type (episode, movie, etc.) */
|
|
516
|
+
type: MediaType;
|
|
517
|
+
/**
|
|
518
|
+
* External ID in format `provider:value` (e.g., `imdb:tt1234567`).
|
|
519
|
+
* See interface documentation for details on the ID format.
|
|
520
|
+
*/
|
|
521
|
+
id: string;
|
|
522
|
+
/** User reference (for admin queries) */
|
|
523
|
+
userRef?: string;
|
|
524
|
+
/** Timestamp when content was watched (unix milliseconds) */
|
|
525
|
+
date: number;
|
|
526
|
+
/** Timestamp when entry was last modified (unix milliseconds) */
|
|
527
|
+
modified: number;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Unwatched event data - emitted via SSE when content is removed from watch history.
|
|
531
|
+
*
|
|
532
|
+
* Unlike IWatched which has a single `id`, this uses `ids[]` array containing
|
|
533
|
+
* all possible external IDs (imdb, trakt, tmdb, local) so clients can match
|
|
534
|
+
* against any provider they have cached.
|
|
535
|
+
*
|
|
536
|
+
* This is a user-scoped event - not library-scoped.
|
|
537
|
+
*/
|
|
538
|
+
export interface IUnwatched {
|
|
539
|
+
/** Content type (episode, movie, etc.) */
|
|
540
|
+
type: MediaType;
|
|
541
|
+
/**
|
|
542
|
+
* All possible external IDs for this content.
|
|
543
|
+
* Format: `provider:value`
|
|
544
|
+
* Examples: ["imdb:tt0111161", "trakt:481", "tmdb:278"]
|
|
545
|
+
*/
|
|
546
|
+
ids: string[];
|
|
547
|
+
/** User reference */
|
|
548
|
+
userRef?: string;
|
|
549
|
+
/** Timestamp when entry was removed (unix milliseconds) */
|
|
550
|
+
modified: number;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Request body for adding to watch history via POST /users/me/history.
|
|
554
|
+
*
|
|
555
|
+
* ## External ID Requirement
|
|
556
|
+
*
|
|
557
|
+
* When using the direct history endpoint, you must provide an external ID.
|
|
558
|
+
* The ID format is `provider:value`:
|
|
559
|
+
* - `imdb:tt1234567` - IMDb ID
|
|
560
|
+
* - `trakt:123456` - Trakt ID
|
|
561
|
+
* - `tmdb:550` - TMDb ID
|
|
562
|
+
* - `tvdb:12345` - TVDB ID
|
|
563
|
+
*
|
|
564
|
+
* **Tip**: For easier usage, prefer the library-specific endpoints
|
|
565
|
+
* (`setMovieWatched`, `setEpisodeWatched`) which automatically resolve IDs.
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* ```typescript
|
|
569
|
+
* // Direct history endpoint - requires external ID
|
|
570
|
+
* await serverApi.addToHistory({
|
|
571
|
+
* type: 'movie',
|
|
572
|
+
* id: 'imdb:tt0111161', // The Shawshank Redemption
|
|
573
|
+
* date: Date.now()
|
|
574
|
+
* });
|
|
575
|
+
*
|
|
576
|
+
* // Library endpoint - uses local ID, server resolves to external
|
|
577
|
+
* await libraryApi.setMovieWatched('local-movie-id', Date.now());
|
|
578
|
+
* ```
|
|
579
|
+
*/
|
|
580
|
+
export interface IWatchedForAdd {
|
|
581
|
+
/** Content type (episode, movie, etc.) */
|
|
582
|
+
type: MediaType;
|
|
583
|
+
/**
|
|
584
|
+
* External ID in format `provider:value` (e.g., `imdb:tt1234567`).
|
|
585
|
+
* See interface documentation for details on the ID format.
|
|
586
|
+
*/
|
|
587
|
+
id: string;
|
|
588
|
+
/** Timestamp when content was watched (unix milliseconds) */
|
|
589
|
+
date: number;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* View progress entry - returned from GET /users/me/history/progress/:id.
|
|
593
|
+
*
|
|
594
|
+
* Tracks playback position for resumable viewing.
|
|
595
|
+
* Uses the same external ID format as watch history.
|
|
596
|
+
*/
|
|
597
|
+
export interface IViewProgress {
|
|
598
|
+
/** Content type (episode, movie, etc.) */
|
|
599
|
+
type: MediaType;
|
|
600
|
+
/**
|
|
601
|
+
* External ID in format `provider:value` (e.g., `imdb:tt1234567`).
|
|
602
|
+
*/
|
|
603
|
+
id: string;
|
|
604
|
+
/** User reference */
|
|
605
|
+
userRef: string;
|
|
606
|
+
/** Playback progress in milliseconds */
|
|
607
|
+
progress: number;
|
|
608
|
+
/** Parent ID for episodes (series external ID) */
|
|
609
|
+
parent?: string;
|
|
610
|
+
/** Timestamp when progress was last modified (unix milliseconds) */
|
|
611
|
+
modified: number;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Request body for updating view progress via POST /users/me/history/progress.
|
|
615
|
+
*
|
|
616
|
+
* Uses external IDs in format `provider:value` (e.g., `imdb:tt1234567`).
|
|
617
|
+
*
|
|
618
|
+
* @example
|
|
619
|
+
* ```typescript
|
|
620
|
+
* await serverApi.addProgress({
|
|
621
|
+
* type: 'movie',
|
|
622
|
+
* id: 'imdb:tt0111161',
|
|
623
|
+
* progress: 3600000 // 1 hour in milliseconds
|
|
624
|
+
* });
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
export interface IViewProgressForAdd {
|
|
628
|
+
/** Content type (episode, movie, etc.) */
|
|
629
|
+
type: MediaType;
|
|
630
|
+
/**
|
|
631
|
+
* External ID in format `provider:value` (e.g., `imdb:tt1234567`).
|
|
632
|
+
*/
|
|
633
|
+
id: string;
|
|
634
|
+
/** Parent ID for episodes (series external ID) */
|
|
635
|
+
parent?: string;
|
|
636
|
+
/** Playback progress in milliseconds */
|
|
637
|
+
progress: number;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Query parameters for GET /users/me/history.
|
|
641
|
+
*
|
|
642
|
+
* @example
|
|
643
|
+
* ```typescript
|
|
644
|
+
* // Get recent movie history
|
|
645
|
+
* const history = await serverApi.getHistory({
|
|
646
|
+
* types: ['movie'],
|
|
647
|
+
* order: SqlOrder.DESC,
|
|
648
|
+
* after: Date.now() - 86400000 * 30 // Last 30 days
|
|
649
|
+
* });
|
|
650
|
+
*
|
|
651
|
+
* // Check if specific content was watched
|
|
652
|
+
* const watched = await serverApi.getHistory({
|
|
653
|
+
* id: 'imdb:tt0111161'
|
|
654
|
+
* });
|
|
655
|
+
* ```
|
|
656
|
+
*/
|
|
657
|
+
export interface HistoryQuery {
|
|
658
|
+
/** Sort field */
|
|
659
|
+
sort?: RsSort;
|
|
660
|
+
/** Sort direction */
|
|
661
|
+
order?: SqlOrder;
|
|
662
|
+
/** Filter entries watched before this timestamp (unix ms) */
|
|
663
|
+
before?: number;
|
|
664
|
+
/** Filter entries watched after this timestamp (unix ms) */
|
|
665
|
+
after?: number;
|
|
666
|
+
/** Filter by content types */
|
|
667
|
+
types?: MediaType[];
|
|
668
|
+
/** Filter by specific external ID */
|
|
669
|
+
id?: string;
|
|
670
|
+
/** Pagination key for next page */
|
|
671
|
+
pageKey?: number;
|
|
672
|
+
}
|
|
486
673
|
export type MovieSort = 'modified' | 'added' | 'created' | 'name' | 'digitalairdate';
|
|
487
674
|
export interface DeletedQuery {
|
|
488
675
|
after?: number;
|
package/dist/library.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
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';
|
|
2
|
+
import { IFile, ITag, IPerson, ISerie, IMovie, MediaRequest, IEpisode, ExternalImage, IBackupFile, ILibrary, SerieInMedia, DeletedQuery, RsDeleted, MovieSort, RsSort, SqlOrder, RsRequest, DetectedFaceResult, UnassignFaceResponse, RsGroupDownload, IViewProgress, IWatched } from './interfaces.js';
|
|
3
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 {
|
|
@@ -194,6 +194,9 @@ export declare class LibraryApi {
|
|
|
194
194
|
updateMovie(movieId: string, updates: Partial<IMovie>): Promise<IMovie>;
|
|
195
195
|
getMovieImages(movieId: string): Promise<ExternalImage[]>;
|
|
196
196
|
setMovieWatched(movieId: string, date: number): Promise<void>;
|
|
197
|
+
getMovieWatched(movieId: string): Promise<IWatched>;
|
|
198
|
+
getMovieProgress(movieId: string): Promise<IViewProgress>;
|
|
199
|
+
setMovieProgress(movieId: string, progress: number): Promise<void>;
|
|
197
200
|
searchMovies(name: string): Promise<IMovie[]>;
|
|
198
201
|
movieRename(movieId: string, newName: string): Promise<IMovie>;
|
|
199
202
|
updateMoviePoster(movieId: string, poster: FormData, type: string): Promise<void>;
|
|
@@ -214,6 +217,9 @@ export declare class LibraryApi {
|
|
|
214
217
|
updatePersonPortrait(personId: string, portrait: FormData): Promise<void>;
|
|
215
218
|
searchSeries(name: string): Promise<ISerie[]>;
|
|
216
219
|
setEpisodeWatched(serieId: string, season: number, number: number, date: number): Promise<void>;
|
|
220
|
+
getEpisodeWatched(serieId: string, season: number, episode: number): Promise<IWatched>;
|
|
221
|
+
getEpisodeProgress(serieId: string, season: number, episode: number): Promise<IViewProgress>;
|
|
222
|
+
setEpisodeProgress(serieId: string, season: number, episode: number, progress: number): Promise<void>;
|
|
217
223
|
/**
|
|
218
224
|
* Searches for available media sources for a specific episode.
|
|
219
225
|
* @param serieId - The series identifier
|
package/dist/library.js
CHANGED
|
@@ -368,6 +368,17 @@ export class LibraryApi {
|
|
|
368
368
|
async setMovieWatched(movieId, date) {
|
|
369
369
|
await this.client.post(this.getUrl(`/movies/${movieId}/watched`), { date });
|
|
370
370
|
}
|
|
371
|
+
async getMovieWatched(movieId) {
|
|
372
|
+
const res = await this.client.get(this.getUrl(`/movies/${movieId}/watched`));
|
|
373
|
+
return res.data;
|
|
374
|
+
}
|
|
375
|
+
async getMovieProgress(movieId) {
|
|
376
|
+
const res = await this.client.get(this.getUrl(`/movies/${movieId}/progress`));
|
|
377
|
+
return res.data;
|
|
378
|
+
}
|
|
379
|
+
async setMovieProgress(movieId, progress) {
|
|
380
|
+
await this.client.post(this.getUrl(`/movies/${movieId}/progress`), { progress });
|
|
381
|
+
}
|
|
371
382
|
async searchMovies(name) {
|
|
372
383
|
const res = await this.client.get(this.getUrl(`/movies/search?name=${name}`));
|
|
373
384
|
return res.data;
|
|
@@ -460,6 +471,17 @@ export class LibraryApi {
|
|
|
460
471
|
async setEpisodeWatched(serieId, season, number, date) {
|
|
461
472
|
await this.client.post(this.getUrl(`/series/${serieId}/seasons/${season}/episodes/${number}/watched`), { date });
|
|
462
473
|
}
|
|
474
|
+
async getEpisodeWatched(serieId, season, episode) {
|
|
475
|
+
const res = await this.client.get(this.getUrl(`/series/${serieId}/seasons/${season}/episodes/${episode}/watched`));
|
|
476
|
+
return res.data;
|
|
477
|
+
}
|
|
478
|
+
async getEpisodeProgress(serieId, season, episode) {
|
|
479
|
+
const res = await this.client.get(this.getUrl(`/series/${serieId}/seasons/${season}/episodes/${episode}/progress`));
|
|
480
|
+
return res.data;
|
|
481
|
+
}
|
|
482
|
+
async setEpisodeProgress(serieId, season, episode, progress) {
|
|
483
|
+
await this.client.post(this.getUrl(`/series/${serieId}/seasons/${season}/episodes/${episode}/progress`), { progress });
|
|
484
|
+
}
|
|
463
485
|
/**
|
|
464
486
|
* Searches for available media sources for a specific episode.
|
|
465
487
|
* @param serieId - The series identifier
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RedseatClient } from './client.js';
|
|
2
|
-
import { ILibrary, IPlugin, ICredential } from './interfaces.js';
|
|
2
|
+
import { ILibrary, IPlugin, ICredential, IWatched, IWatchedForAdd, IViewProgress, IViewProgressForAdd, HistoryQuery } from './interfaces.js';
|
|
3
3
|
export declare class ServerApi {
|
|
4
4
|
private client;
|
|
5
5
|
constructor(client: RedseatClient);
|
|
@@ -19,4 +19,10 @@ export declare class ServerApi {
|
|
|
19
19
|
deleteCredential(id: string): Promise<void>;
|
|
20
20
|
saveOAuthCredentials(pluginId: string, params: Record<string, string>): Promise<void>;
|
|
21
21
|
addLibraryCredential(credential: any): Promise<ILibrary>;
|
|
22
|
+
getHistory(query?: HistoryQuery): Promise<IWatched[]>;
|
|
23
|
+
addToHistory(watched: IWatchedForAdd): Promise<void>;
|
|
24
|
+
getProgressById(id: string): Promise<IViewProgress | null>;
|
|
25
|
+
addProgress(progress: IViewProgressForAdd): Promise<void>;
|
|
26
|
+
getAdminHistory(): Promise<IWatched[]>;
|
|
27
|
+
importHistory(watcheds: IWatchedForAdd[]): Promise<void>;
|
|
22
28
|
}
|
package/dist/server.js
CHANGED
|
@@ -48,4 +48,44 @@ export class ServerApi {
|
|
|
48
48
|
const res = await this.client.post('/libraries/credential', credential);
|
|
49
49
|
return res.data;
|
|
50
50
|
}
|
|
51
|
+
// History methods
|
|
52
|
+
async getHistory(query) {
|
|
53
|
+
const params = {};
|
|
54
|
+
if (query) {
|
|
55
|
+
if (query.sort !== undefined)
|
|
56
|
+
params.sort = query.sort;
|
|
57
|
+
if (query.order !== undefined)
|
|
58
|
+
params.order = query.order;
|
|
59
|
+
if (query.before !== undefined)
|
|
60
|
+
params.before = query.before;
|
|
61
|
+
if (query.after !== undefined)
|
|
62
|
+
params.after = query.after;
|
|
63
|
+
if (query.types !== undefined && query.types.length > 0)
|
|
64
|
+
params.types = query.types;
|
|
65
|
+
if (query.id !== undefined)
|
|
66
|
+
params.id = query.id;
|
|
67
|
+
if (query.pageKey !== undefined)
|
|
68
|
+
params.pageKey = query.pageKey;
|
|
69
|
+
}
|
|
70
|
+
const res = await this.client.get('/users/me/history', { params });
|
|
71
|
+
return res.data;
|
|
72
|
+
}
|
|
73
|
+
async addToHistory(watched) {
|
|
74
|
+
await this.client.post('/users/me/history', watched);
|
|
75
|
+
}
|
|
76
|
+
async getProgressById(id) {
|
|
77
|
+
const res = await this.client.get(`/users/me/history/progress/${id}`);
|
|
78
|
+
return res.data;
|
|
79
|
+
}
|
|
80
|
+
async addProgress(progress) {
|
|
81
|
+
await this.client.post('/users/me/history/progress', progress);
|
|
82
|
+
}
|
|
83
|
+
// Admin history methods
|
|
84
|
+
async getAdminHistory() {
|
|
85
|
+
const res = await this.client.get('/users/admin/history');
|
|
86
|
+
return res.data;
|
|
87
|
+
}
|
|
88
|
+
async importHistory(watcheds) {
|
|
89
|
+
await this.client.post('/users/admin/history/import', watcheds);
|
|
90
|
+
}
|
|
51
91
|
}
|
package/dist/sse-types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ILibrary, IFile, IEpisode, ISerie, IMovie, IPerson, ITag, IBackup } from './interfaces.js';
|
|
1
|
+
import { ILibrary, IFile, IEpisode, ISerie, IMovie, IPerson, ITag, IBackup, IWatched, IUnwatched } 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 {
|
|
@@ -134,6 +134,18 @@ export interface SSEPlayersListEvent {
|
|
|
134
134
|
userRef: string;
|
|
135
135
|
players: SSEPlayerEvent[];
|
|
136
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* SSE event emitted when content is marked as watched.
|
|
139
|
+
* This is a user-scoped event - not library-scoped.
|
|
140
|
+
* Uses external IDs (e.g., "imdb:tt0111161").
|
|
141
|
+
*/
|
|
142
|
+
export type SSEWatchedEvent = IWatched;
|
|
143
|
+
/**
|
|
144
|
+
* SSE event emitted when content is removed from watch history.
|
|
145
|
+
* Contains all possible external IDs so clients can match against any cached ID.
|
|
146
|
+
* This is a user-scoped event - not library-scoped.
|
|
147
|
+
*/
|
|
148
|
+
export type SSEUnwatchedEvent = IUnwatched;
|
|
137
149
|
export interface SSEEventMap {
|
|
138
150
|
'library': SSELibraryEvent;
|
|
139
151
|
'library-status': SSELibraryStatusEvent;
|
|
@@ -150,6 +162,8 @@ export interface SSEEventMap {
|
|
|
150
162
|
'media_rating': SSEMediaRatingEvent;
|
|
151
163
|
'media_progress': SSEMediaProgressEvent;
|
|
152
164
|
'players-list': SSEPlayersListEvent;
|
|
165
|
+
'watched': SSEWatchedEvent;
|
|
166
|
+
'unwatched': SSEUnwatchedEvent;
|
|
153
167
|
}
|
|
154
168
|
export type SSEEventName = keyof SSEEventMap;
|
|
155
169
|
export interface SSEEvent<T extends SSEEventName = SSEEventName> {
|