@ctrl/plex 3.6.1 → 3.8.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/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # @ctrl/plex
2
2
 
3
3
  [![npm](https://badgen.net/npm/v/@ctrl/plex)](https://www.npmjs.com/package/@ctrl/plex)
4
- [![coverage](https://badgen.net/codecov/c/github/scttcper/plex)](https://codecov.io/gh/scttcper/plex)
5
4
 
6
- > A TypeScript [Plex](https://www.plex.tv/) API client based on [pkkid/python-plexapi](https://github.com/pkkid/python-plexapi)
5
+ > A TypeScript [Plex](https://www.plex.tv/) API client using [ofetch](https://github.com/unjs/ofetch) based on [pkkid/python-plexapi](https://github.com/pushingkarmaorg/python-plexapi)
7
6
 
8
7
  ### Install
9
8
 
@@ -13,11 +12,12 @@ npm install @ctrl/plex
13
12
 
14
13
  ### Docs
15
14
 
16
- https://ctrl-plex.vercel.app
15
+ https://ctrl-plex.vercel.app
17
16
 
18
17
  ### Use
19
18
 
20
19
  Create a plex connection
20
+
21
21
  ```ts
22
22
  import { MyPlexAccount } from '@ctrl/plex';
23
23
 
@@ -28,6 +28,7 @@ const library = await plex.library();
28
28
  ```
29
29
 
30
30
  ###### Example 1: List all unwatched movies.
31
+
31
32
  ```ts
32
33
  import { MovieSection } from '@ctrl/plex';
33
34
 
@@ -38,6 +39,7 @@ const results = await section.search({ unwatched: true });
38
39
  ```
39
40
 
40
41
  ###### Example 2: Search for a list of movies containing a title
42
+
41
43
  ```ts
42
44
  const library = await plex.library();
43
45
  const section = await library.section<MovieSection>('Movies');
@@ -45,6 +47,7 @@ const results = await section.search({ title: 'Rush Hour' });
45
47
  ```
46
48
 
47
49
  ###### Example 3: List all content containing a specific query
50
+
48
51
  ```ts
49
52
  const results = await plex.search('Arnold');
50
53
  // Each hub represents a single Hub (or category) in the PlexServer search (movie, actor, etc)
@@ -55,6 +58,7 @@ for (const hub of results) {
55
58
  ```
56
59
 
57
60
  ###### Example 4: List all episodes of a tv show.
61
+
58
62
  ```ts
59
63
  import { ShowSection } from '@ctrl/plex';
60
64
 
@@ -66,7 +70,8 @@ const episodes = await results[0].episodes();
66
70
  ```
67
71
 
68
72
  ### Differences from python plex client
69
- JS is a different language and some methods of the api were not possible.
73
+
74
+ JS is a different language and some methods of the api were not possible.
70
75
  Chaining functions with requests must be awaited mostly individually. Constructors in JS don't typically make requests
71
76
  and accessing properties normally cannot make requests either.
72
77
 
@@ -99,13 +104,13 @@ Post testing, remove plex server from account. Warning this is destructive. Do n
99
104
  npm run test-cleanup
100
105
  ```
101
106
 
102
-
103
107
  ### Running tests locally (mostly for myself)
104
108
 
105
109
  get a claim token from https://www.plex.tv/claim/
106
110
  export PLEX_CLAIM_TOKEN=claim-token
107
111
 
108
- start plex container for testing
112
+ Start plex container for testing. Replace `/Users/scooper/gh/plex` with the path to this repo's directory.
113
+
109
114
  ```console
110
115
  docker run -d \
111
116
  --name=plex \
@@ -134,11 +139,13 @@ docker run -d \
134
139
  ```
135
140
 
136
141
  Pull latest plex container if needed
142
+
137
143
  ```console
138
144
  docker pull lscr.io/linuxserver/plex:latest
139
145
  ```
140
146
 
141
- bootstrap plex server with test media
147
+ bootstrap plex server with test media. This assumes you have set the `PLEX_PASSWORD` and `PLEX_USERNAME` environment variables.
148
+
142
149
  ```console
143
- NODE_OPTIONS="--loader ts-node/esm" node scripts/bootstraptest.ts --no-docker --server-name=orbstack --password=$PLEX_PASSWORD --username=$PLEX_USERNAME
150
+ npx tsx scripts/bootstraptest.ts --no-docker --server-name=orbstack --password=$PLEX_PASSWORD --username=$PLEX_USERNAME
144
151
  ```
@@ -0,0 +1,295 @@
1
+ import { PartialPlexObject } from './base/partialPlexObject.js';
2
+ import { PlexObject } from './base/plexObject.js';
3
+ import type { AlbumData, ArtistData, TrackData } from './audio.types.js';
4
+ import { Chapter, Collection, Country, Field, Format, Genre, Guid, Image, Label, Media, Mood, Similar, Style, Subformat } from './media.js';
5
+ import type { PlexServer } from './server.js';
6
+ /**
7
+ * Base class for all audio objects including Artist, Album, and Track.
8
+ */
9
+ export declare class Audio extends PartialPlexObject {
10
+ /** Default metadata type for audio sync items. */
11
+ static METADATA_TYPE: string;
12
+ /** Hardcoded list type for filtering. */
13
+ get listType(): 'audio';
14
+ addedAt?: Date;
15
+ art?: string;
16
+ artBlurHash?: string;
17
+ /** Sonic distance from a seed item, used in sonically similar results. */
18
+ distance?: number;
19
+ guid?: string;
20
+ /** Plex index number (often the track number for tracks). */
21
+ index?: number;
22
+ lastRatedAt?: Date;
23
+ lastViewedAt?: Date;
24
+ /** Key of the library section this item belongs to. */
25
+ librarySectionKey?: string;
26
+ /** Title of the library section this item belongs to. */
27
+ librarySectionTitle?: string;
28
+ /** Plex music analysis version (1 indicates sonic analysis complete). */
29
+ musicAnalysisVersion?: number;
30
+ summary?: string;
31
+ thumb?: string;
32
+ thumbBlurHash?: string;
33
+ /** Title used for sorting (defaults to title). */
34
+ titleSort?: string;
35
+ updatedAt?: Date;
36
+ /** User rating (0.0-10.0). */
37
+ userRating?: number;
38
+ /** Count of times the item was played. */
39
+ viewCount?: number;
40
+ /** Store the raw data from the Plex API for lazy loading related items. */
41
+ protected _data?: any;
42
+ /** List of field objects. */
43
+ fields?: Field[];
44
+ /** List of image objects. */
45
+ images?: Image[];
46
+ /** List of mood objects. */
47
+ moods?: Mood[];
48
+ /**
49
+ * @protected Should not be called directly. Use `server.fetchItem()`.
50
+ * Initializes a new instance of the Audio class.
51
+ * @param server The PlexServer instance used for communication.
52
+ * @param data The raw data object received from the Plex API.
53
+ * @param initpath The path used to fetch this item initially.
54
+ */
55
+ constructor(server: PlexServer, data: any, initpath: string | undefined, parent: PlexObject | undefined);
56
+ /**
57
+ * Returns the full URL for a given part (like a media stream) relative to the item's key.
58
+ * Includes the authentication token.
59
+ * @param part The relative path or resource identifier.
60
+ * @returns The full URL string including the server address and token, or undefined if part is empty.
61
+ */
62
+ url(part: string): string | undefined;
63
+ /** Indicates if the audio item has undergone sonic analysis. */
64
+ get hasSonicAnalysis(): boolean;
65
+ /**
66
+ * Fetches a list of sonically similar audio items from the Plex server.
67
+ * The returned items will be instances of the same class as the current item
68
+ * (e.g., calling `sonicallySimilar` on a `Track` instance returns `Track[]`).
69
+ * @param limit Maximum number of similar items to return. Server default is 50.
70
+ * @param maxDistance Maximum sonic distance (0.0 - 1.0) between items. Server default is 0.25.
71
+ * @param options Optional additional filters to apply to the results.
72
+ * @returns A promise resolving to an array of sonically similar Audio items.
73
+ */
74
+ sonicallySimilar(limit?: number, maxDistance?: number, options?: Record<string, string | number>): Promise<Audio[]>;
75
+ /**
76
+ * Provides a default title for Sync operations based on the item's title.
77
+ * @returns The item's title, or undefined if the title is not set.
78
+ * @protected
79
+ */
80
+ protected _defaultSyncTitle(): string | undefined;
81
+ /**
82
+ * Populates the object's properties from the provided Plex API data.
83
+ * This method is called by the constructor and _loadFullData.
84
+ * @param data The raw data object from the Plex API.
85
+ * @protected
86
+ */
87
+ protected _loadData(data: Record<string, any>): void;
88
+ /**
89
+ * Overrides `PartialPlexObject._loadFullData` to apply Audio-specific attributes
90
+ * after fetching the full item data.
91
+ * @param data The raw data object representing the full item from the Plex API.
92
+ * @protected
93
+ */
94
+ protected _loadFullData(data: any): void;
95
+ }
96
+ /**
97
+ * Represents a single Track.
98
+ */
99
+ export declare class Track extends Audio {
100
+ static TAG: string;
101
+ static TYPE: string;
102
+ audienceRating?: number;
103
+ chapterSource?: string;
104
+ duration?: number;
105
+ grandparentArt?: string;
106
+ grandparentGuid?: string;
107
+ grandparentKey?: string;
108
+ grandparentRatingKey?: number;
109
+ grandparentTheme?: string;
110
+ grandparentThumb?: string;
111
+ grandparentTitle?: string;
112
+ originalTitle?: string;
113
+ parentGuid?: string;
114
+ parentIndex?: number;
115
+ parentKey?: string;
116
+ parentRatingKey?: number;
117
+ parentThumb?: string;
118
+ parentTitle?: string;
119
+ primaryExtraKey?: string;
120
+ rating?: number;
121
+ skipCount?: number;
122
+ sourceURI?: string;
123
+ viewOffset?: number;
124
+ chapters?: Chapter[];
125
+ collections?: Collection[];
126
+ genres?: Genre[];
127
+ guids?: Guid[];
128
+ labels?: Label[];
129
+ media?: Media[];
130
+ constructor(server: PlexServer, data: any, initpath: string | undefined, parent: PlexObject | undefined);
131
+ /**
132
+ * @returns List of file paths where the track is found on disk.
133
+ */
134
+ get locations(): string[];
135
+ /**
136
+ * @returns The track number.
137
+ */
138
+ get trackNumber(): number | undefined;
139
+ /**
140
+ * @returns A filename for use in download.
141
+ */
142
+ prettyfilename(): string;
143
+ /**
144
+ * Return the track's Album.
145
+ */
146
+ album(): Promise<Album>;
147
+ /**
148
+ * Return the track's Artist.
149
+ */
150
+ artist(): Promise<Artist>;
151
+ /**
152
+ * @returns Default title for a new syncItem.
153
+ */
154
+ _defaultSyncTitle(): string;
155
+ /**
156
+ * Returns the Plex Web URL pointing to the album details page for this track.
157
+ */
158
+ getWebURL(base?: string): string;
159
+ /**
160
+ * Returns a sonic adventure from the current track to the specified track.
161
+ * @param to The target track for the sonic adventure.
162
+ */
163
+ sonicAdventure(to: Track): Promise<Track[]>;
164
+ /**
165
+ * Populates the object's properties from the provided Plex API data,
166
+ * overriding the base Audio class method to add Track-specific attributes.
167
+ * @param data The raw data object from the Plex API.
168
+ * @protected
169
+ */
170
+ _loadData(data: TrackData): void;
171
+ }
172
+ /**
173
+ * Represents a single Artist.
174
+ */
175
+ export declare class Artist extends Audio {
176
+ static TAG: string;
177
+ static TYPE: string;
178
+ albumSort?: number;
179
+ audienceRating?: number;
180
+ rating?: number;
181
+ theme?: string;
182
+ countries?: Country[];
183
+ genres?: Genre[];
184
+ guids?: Guid[];
185
+ labels?: Label[];
186
+ similar?: Similar[];
187
+ styles?: Style[];
188
+ collections?: Collection[];
189
+ get locations(): string[];
190
+ constructor(server: PlexServer, data: any, initpath?: string, parent?: PlexObject);
191
+ /**
192
+ * Returns a list of Album objects by the artist.
193
+ * @param options Additional search options.
194
+ */
195
+ albums(options?: Record<string, unknown>): Promise<Album[]>;
196
+ /**
197
+ * Returns the Album that matches the specified title for this artist.
198
+ * Case-insensitive exact match on title.
199
+ */
200
+ album(title: string): Promise<Album | undefined>;
201
+ /**
202
+ * Returns the Track that matches the specified criteria.
203
+ * @param title Title of the track.
204
+ * @param album Album name (required if title not specified).
205
+ * @param track Track number (required if title not specified).
206
+ */
207
+ track(args: {
208
+ title: string;
209
+ } | {
210
+ album: string;
211
+ track: number;
212
+ }): Promise<Track | undefined>;
213
+ /**
214
+ * Returns a list of Track objects by the artist.
215
+ * @param options Additional fetch options.
216
+ */
217
+ tracks(options?: Record<string, string | number>): Promise<Track[]>;
218
+ /** Alias of track(). */
219
+ get(args: {
220
+ title: string;
221
+ } | {
222
+ album: string;
223
+ track: number;
224
+ }): Promise<Track | undefined>;
225
+ popularTracks(): Promise<Track[]>;
226
+ /**
227
+ * Load attribute values from Plex XML response.
228
+ * @protected
229
+ */
230
+ _loadData(data: ArtistData): void;
231
+ }
232
+ /**
233
+ * Represents a single Album.
234
+ */
235
+ export declare class Album extends Audio {
236
+ static TAG: string;
237
+ static TYPE: string;
238
+ audienceRating?: number;
239
+ collections?: Collection[];
240
+ formats?: Format[];
241
+ genres?: Genre[];
242
+ guids?: Guid[];
243
+ labels?: Label[];
244
+ leafCount?: number;
245
+ loudnessAnalysisVersion?: number;
246
+ originallyAvailableAt?: Date;
247
+ /**
248
+ * Artist GUID
249
+ */
250
+ parentGuid?: string;
251
+ /**
252
+ * Artist Key
253
+ */
254
+ parentKey?: string;
255
+ /**
256
+ * Artist Rating Key
257
+ */
258
+ parentRatingKey?: number;
259
+ /**
260
+ * Artist Theme
261
+ */
262
+ parentTheme?: string;
263
+ /**
264
+ * Artist Thumb
265
+ */
266
+ parentThumb?: string;
267
+ /**
268
+ * Artist Title
269
+ */
270
+ parentTitle?: string;
271
+ rating?: number;
272
+ studio?: string;
273
+ styles?: Style[];
274
+ subformats?: Subformat[];
275
+ viewedLeafCount?: number;
276
+ constructor(server: PlexServer, data: any, initpath: string | undefined, parent: PlexObject | undefined);
277
+ /**
278
+ * Returns a list of Track objects in the album.
279
+ * @param options Additional fetch options.
280
+ */
281
+ tracks(options?: Record<string, string | number>): Promise<Track[]>;
282
+ /**
283
+ * Return the album's Artist.
284
+ */
285
+ artist(): Promise<Artist>;
286
+ /**
287
+ * Returns the default title for a sync item.
288
+ */
289
+ _defaultSyncTitle(): string;
290
+ /**
291
+ * Load attribute values from Plex XML response.
292
+ * @protected
293
+ */
294
+ _loadData(data: AlbumData): void;
295
+ }