@ctrl/plex 3.12.1 → 4.0.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/src/media.js CHANGED
@@ -50,7 +50,7 @@ export class MediaPart extends PlexObject {
50
50
  const params = new URLSearchParams({ allParts: '1' });
51
51
  const streamId = typeof stream === 'number' ? stream : stream.id;
52
52
  params.set('audioStreamID', streamId.toString());
53
- await this.server.query(`${key}?${params.toString()}`, 'put');
53
+ await this.server.query({ path: `${key}?${params.toString()}`, method: 'put' });
54
54
  return this;
55
55
  }
56
56
  /**
@@ -62,7 +62,7 @@ export class MediaPart extends PlexObject {
62
62
  const params = new URLSearchParams({ allParts: '1' });
63
63
  const streamId = typeof stream === 'number' ? stream : stream.id;
64
64
  params.set('subtitleStreamID', streamId.toString());
65
- await this.server.query(`${key}?${params.toString()}`, 'put');
65
+ await this.server.query({ path: `${key}?${params.toString()}`, method: 'put' });
66
66
  return this;
67
67
  }
68
68
  /**
@@ -255,7 +255,7 @@ export class Optimized extends PlexObject {
255
255
  */
256
256
  async remove() {
257
257
  const key = `${this.key}/${this.id}`;
258
- await this.server.query(key, 'delete');
258
+ await this.server.query({ path: key, method: 'delete' });
259
259
  }
260
260
  /**
261
261
  * Rename this Optimized item.
@@ -263,7 +263,7 @@ export class Optimized extends PlexObject {
263
263
  */
264
264
  async rename(title) {
265
265
  const key = `${this.key}/${this.id}?Item[title]=${encodeURIComponent(title)}`;
266
- await this.server.query(key, 'put');
266
+ await this.server.query({ path: key, method: 'put' });
267
267
  }
268
268
  /**
269
269
  * Reprocess a removed Conversion item that is still a listed Optimize item.
@@ -271,7 +271,7 @@ export class Optimized extends PlexObject {
271
271
  */
272
272
  async reprocess(ratingKey) {
273
273
  const key = `${this.key}/${this.id}/${ratingKey}/enable`;
274
- await this.server.query(key, 'put');
274
+ await this.server.query({ path: key, method: 'put' });
275
275
  }
276
276
  _loadData(data) {
277
277
  this.id = data.id;
@@ -312,7 +312,7 @@ class BaseResource extends PlexObject {
312
312
  const key = this.key.slice(0, -1);
313
313
  const params = new URLSearchParams();
314
314
  params.append('url', this.ratingKey);
315
- return this.server.query(`${key}?${params.toString()}`, 'put');
315
+ return this.server.query({ path: `${key}?${params.toString()}`, method: 'put' });
316
316
  }
317
317
  resourceFilepath() {
318
318
  if (this.ratingKey.startsWith('media://')) {
@@ -437,7 +437,7 @@ export class Image extends PlexObject {
437
437
  this.alt = data.alt;
438
438
  this.type = data.type;
439
439
  // Assuming server.url is needed to make this a complete URL
440
- this.url = data.url ? this.server.url(data.url, true)?.toString() : undefined;
440
+ this.url = data.url ? this.server.url(data.url, { includeToken: true })?.toString() : undefined;
441
441
  }
442
442
  }
443
443
  /** Represents a single Field. */
@@ -9,12 +9,6 @@ import { PlexServer } from './server.js';
9
9
  * and return this object.
10
10
  */
11
11
  export declare class MyPlexAccount {
12
- baseUrl: string | null;
13
- username?: string;
14
- password?: string;
15
- token?: string;
16
- timeout: number;
17
- server?: PlexServer;
18
12
  static key: string;
19
13
  /**
20
14
  * This follows the outline described in https://forums.plex.tv/t/authenticating-with-plex/609370
@@ -28,7 +22,9 @@ export declare class MyPlexAccount {
28
22
  * Pass in the `webLogin` object obtained from `getWebLogin()` and this will poll Plex to see if
29
23
  * the user agreed. It returns a connected `MyPlexAccount` or throws an error.
30
24
  */
31
- static webLoginCheck(webLogin: WebLogin, timeoutSeconds?: number): Promise<MyPlexAccount>;
25
+ static webLoginCheck(webLogin: WebLogin, { timeoutSeconds }?: {
26
+ timeoutSeconds?: number;
27
+ }): Promise<MyPlexAccount>;
32
28
  FRIENDINVITE: string;
33
29
  HOMEUSERCREATE: string;
34
30
  EXISTINGUSER: string;
@@ -86,16 +82,23 @@ export declare class MyPlexAccount {
86
82
  subscriptionFeatures?: string[];
87
83
  /** List of devices your allowed to use with this account */
88
84
  entitlements?: string[];
85
+ baseUrl: string | null;
86
+ username?: string;
87
+ password?: string;
88
+ token?: string;
89
+ timeout: number;
90
+ server?: PlexServer;
89
91
  /**
90
- *
91
- * @param username Your MyPlex username
92
- * @param password Your MyPlex password
93
- * @param token Token used to access this client.
94
- * @param session Use your own session object if you want to cache the http responses from PMS
95
- * @param timeout timeout in seconds on initial connect to myplex
96
- * @param server not often available
92
+ * @param options Connection + auth options.
97
93
  */
98
- constructor(baseUrl?: string | null, username?: string, password?: string, token?: string, timeout?: number, server?: PlexServer);
94
+ constructor({ baseUrl, username, password, token, timeout, server, }?: {
95
+ baseUrl?: string | null;
96
+ username?: string;
97
+ password?: string;
98
+ token?: string;
99
+ timeout?: number;
100
+ server?: PlexServer;
101
+ });
99
102
  /**
100
103
  * Returns a new :class:`~server.PlexServer` or :class:`~client.PlexClient` object.
101
104
  * Often times there is more than one address specified for a server or client.
@@ -124,11 +127,15 @@ export declare class MyPlexAccount {
124
127
  * ElementTree object. Returns None if no data exists in the response.
125
128
  * TODO: use headers
126
129
  * @param path
127
- * @param method
128
- * @param headers
129
- * @param timeout
130
+ * @param options
130
131
  */
131
- query<T = any>(url: string, method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete', headers?: any, username?: string, password?: string): Promise<T>;
132
+ query<T = any>({ url, method, headers, username, password, }: {
133
+ url: string;
134
+ method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';
135
+ headers?: any;
136
+ username?: string;
137
+ password?: string;
138
+ }): Promise<T>;
132
139
  /**
133
140
  * Returns a str, a new "claim-token", which you can use to register your new Plex Server instance to your account.
134
141
  * @link https://hub.docker.com/r/plexinc/pms-docker/
@@ -13,12 +13,6 @@ import { PlexServer } from './server.js';
13
13
  * and return this object.
14
14
  */
15
15
  export class MyPlexAccount {
16
- baseUrl;
17
- username;
18
- password;
19
- token;
20
- timeout;
21
- server;
22
16
  static key = 'https://plex.tv/api/v2/user';
23
17
  /**
24
18
  * This follows the outline described in https://forums.plex.tv/t/authenticating-with-plex/609370
@@ -50,7 +44,7 @@ export class MyPlexAccount {
50
44
  * Pass in the `webLogin` object obtained from `getWebLogin()` and this will poll Plex to see if
51
45
  * the user agreed. It returns a connected `MyPlexAccount` or throws an error.
52
46
  */
53
- static async webLoginCheck(webLogin, timeoutSeconds = 60) {
47
+ static async webLoginCheck(webLogin, { timeoutSeconds = 60 } = {}) {
54
48
  const recheckMs = 3000;
55
49
  const clientIdentifier = BASE_HEADERS['X-Plex-Client-Identifier'];
56
50
  const uri = `https://plex.tv/api/v2/pins/${webLogin.id}`;
@@ -71,7 +65,7 @@ export class MyPlexAccount {
71
65
  retryDelay: recheckMs,
72
66
  });
73
67
  if (tokenResponse.authToken) {
74
- const myPlexAccount = new MyPlexAccount(null, '', '', tokenResponse.authToken);
68
+ const myPlexAccount = new MyPlexAccount({ token: tokenResponse.authToken });
75
69
  return await myPlexAccount.connect();
76
70
  }
77
71
  await sleep(recheckMs);
@@ -97,16 +91,16 @@ export class MyPlexAccount {
97
91
  REQUESTS = 'https://plex.tv/api/invites/requests'; // get
98
92
  SIGNIN = 'https://plex.tv/users/sign_in.json'; // get with auth
99
93
  WEBHOOKS = 'https://plex.tv/api/v2/user/webhooks'; // get, post with data
94
+ baseUrl = null;
95
+ username;
96
+ password;
97
+ token;
98
+ timeout = TIMEOUT;
99
+ server;
100
100
  /**
101
- *
102
- * @param username Your MyPlex username
103
- * @param password Your MyPlex password
104
- * @param token Token used to access this client.
105
- * @param session Use your own session object if you want to cache the http responses from PMS
106
- * @param timeout timeout in seconds on initial connect to myplex
107
- * @param server not often available
101
+ * @param options Connection + auth options.
108
102
  */
109
- constructor(baseUrl = null, username, password, token, timeout = TIMEOUT, server) {
103
+ constructor({ baseUrl = null, username, password, token, timeout = TIMEOUT, server, } = {}) {
110
104
  this.baseUrl = baseUrl;
111
105
  this.username = username;
112
106
  this.password = password;
@@ -141,7 +135,7 @@ export class MyPlexAccount {
141
135
  this._loadData(data);
142
136
  return this;
143
137
  }
144
- const data = await this.query(MyPlexAccount.key);
138
+ const data = await this.query({ url: MyPlexAccount.key });
145
139
  this._loadData(data);
146
140
  return this;
147
141
  }
@@ -157,7 +151,7 @@ export class MyPlexAccount {
157
151
  throw new Error(`Unable to find resource ${name}`);
158
152
  }
159
153
  async resources() {
160
- const data = await this.query(MyPlexResource.key);
154
+ const data = await this.query({ url: MyPlexResource.key });
161
155
  return data.map(device => new MyPlexResource(this, device, this.baseUrl));
162
156
  }
163
157
  /**
@@ -176,7 +170,9 @@ export class MyPlexAccount {
176
170
  * Returns a list of all :class:`~plexapi.myplex.MyPlexDevice` objects connected to the server.
177
171
  */
178
172
  async devices() {
179
- const response = await this.query(MyPlexDevice.key);
173
+ const response = await this.query({
174
+ url: MyPlexDevice.key,
175
+ });
180
176
  return response.MediaContainer.Device.map(data => new MyPlexDevice(this.server, data));
181
177
  }
182
178
  /**
@@ -185,16 +181,17 @@ export class MyPlexAccount {
185
181
  * ElementTree object. Returns None if no data exists in the response.
186
182
  * TODO: use headers
187
183
  * @param path
188
- * @param method
189
- * @param headers
190
- * @param timeout
184
+ * @param options
191
185
  */
192
- async query(url, method = 'get', headers, username, password) {
186
+ async query({ url, method = 'get', headers, username, password, }) {
193
187
  const requestHeaders = this._headers();
194
188
  if (username && password) {
195
189
  const credentials = Buffer.from(`${username}:${password}`).toString('base64');
196
190
  requestHeaders.Authorization = `Basic ${credentials}`;
197
191
  }
192
+ if (headers) {
193
+ Object.assign(requestHeaders, headers);
194
+ }
198
195
  if (!url.includes('xml')) {
199
196
  requestHeaders.accept = 'application/json';
200
197
  }
@@ -220,7 +217,7 @@ export class MyPlexAccount {
220
217
  */
221
218
  async claimToken() {
222
219
  const url = 'https://plex.tv/api/claim/token.json';
223
- const response = await this.query(url, 'get');
220
+ const response = await this.query({ url });
224
221
  return response.token;
225
222
  }
226
223
  /**
@@ -250,7 +247,12 @@ export class MyPlexAccount {
250
247
  return headers;
251
248
  }
252
249
  async _signin(username, password) {
253
- const data = await this.query(this.SIGNIN, 'post', undefined, username, password);
250
+ const data = await this.query({
251
+ url: this.SIGNIN,
252
+ method: 'post',
253
+ username,
254
+ password,
255
+ });
254
256
  return data.user;
255
257
  }
256
258
  _loadData(user) {
@@ -397,7 +399,7 @@ export class MyPlexDevice extends PlexObject {
397
399
  */
398
400
  async delete() {
399
401
  const key = `https://plex.tv/devices/${this.id}.xml`;
400
- await this.server.query(key, 'delete');
402
+ await this.server.query({ path: key, method: 'delete' });
401
403
  }
402
404
  _loadData(data) {
403
405
  this.name = data.$.name;
@@ -65,7 +65,7 @@ export class Playlist extends Playable {
65
65
  params.set('summary', options.summary);
66
66
  }
67
67
  const key = `/playlists/${ratingKey}?${params.toString()}`;
68
- await server.query(key, 'put');
68
+ await server.query({ path: key, method: 'put' });
69
69
  }
70
70
  /** Create a smart playlist. */
71
71
  // private static _createSmart(server: PlexServer, title: string, options: CreatePlaylistOptions) {}
@@ -83,7 +83,7 @@ export class Playlist extends Playable {
83
83
  smart: '0',
84
84
  });
85
85
  const key = `/playlists?${params.toString()}`;
86
- const data = await server.query(key, 'post');
86
+ const data = await server.query({ path: key, method: 'post' });
87
87
  return new Playlist(server, data.MediaContainer.Metadata[0], key);
88
88
  }
89
89
  TYPE = 'playlist';
@@ -92,7 +92,7 @@ export class Playlist extends Playable {
92
92
  async _edit(args) {
93
93
  const searchparams = new URLSearchParams(args);
94
94
  const key = `${this.key}?${searchparams.toString()}`;
95
- await this.server.query(key, 'put');
95
+ await this.server.query({ path: key, method: 'put' });
96
96
  }
97
97
  async edit(changeObj) {
98
98
  await this._edit(changeObj);
@@ -130,7 +130,7 @@ export class Playlist extends Playable {
130
130
  uri: `${this.server._uriRoot()}/library/metadata/${ratingKeys.join(',')}`,
131
131
  });
132
132
  const key = `${this.key}/items?${params.toString()}`;
133
- await this.server.query(key, 'put');
133
+ await this.server.query({ path: key, method: 'put' });
134
134
  }
135
135
  /** Remove an item from a playlist. */
136
136
  async removeItems(items) {
@@ -140,12 +140,12 @@ export class Playlist extends Playable {
140
140
  for (const item of items) {
141
141
  const playlistItemId = await this._getPlaylistItemID(item);
142
142
  const key = `${this.key}/items/${playlistItemId}`;
143
- await this.server.query(key, 'delete');
143
+ await this.server.query({ path: key, method: 'delete' });
144
144
  }
145
145
  }
146
146
  /** Delete the playlist. */
147
147
  async delete() {
148
- await this.server.query(this.key, 'delete');
148
+ await this.server.query({ path: this.key, method: 'delete' });
149
149
  }
150
150
  _loadData(data) {
151
151
  this.key = data.key.replace('/items', '');
@@ -32,7 +32,7 @@ export class PlayQueue extends PlexObject {
32
32
  params.set(key, value.toString());
33
33
  }
34
34
  const path = `/playQueues/${playQueueID}?${params.toString()}`;
35
- const data = await server.query(path, 'get');
35
+ const data = await server.query({ path });
36
36
  const playQueue = new PlayQueue(server, data.MediaContainer, path);
37
37
  return playQueue;
38
38
  }
@@ -73,7 +73,7 @@ export class PlayQueue extends PlexObject {
73
73
  params.set(key, value.toString());
74
74
  }
75
75
  const path = `/playQueues?${params.toString()}`;
76
- const data = await server.query(path, 'post');
76
+ const data = await server.query({ path, method: 'post' });
77
77
  const playQueue = new PlayQueue(server, data.MediaContainer, path);
78
78
  return playQueue;
79
79
  }
@@ -92,7 +92,7 @@ export class PlayQueue extends PlexObject {
92
92
  params.set(key, value.toString());
93
93
  }
94
94
  const path = `/playQueues?${params.toString()}`;
95
- const data = await server.query(path, 'post');
95
+ const data = await server.query({ path, method: 'post' });
96
96
  const playQueue = new PlayQueue(server, data.MediaContainer, path);
97
97
  return playQueue;
98
98
  }
@@ -171,7 +171,7 @@ export class PlayQueue extends PlexObject {
171
171
  params.set(key, value.toString());
172
172
  }
173
173
  const path = `/playQueues/${this.playQueueID}?${params.toString()}`;
174
- const data = await this.server.query(path, 'put');
174
+ const data = await this.server.query({ path, method: 'put' });
175
175
  this._invalidateCacheAndLoadData(data.MediaContainer);
176
176
  return this;
177
177
  }
@@ -204,7 +204,7 @@ export class PlayQueue extends PlexObject {
204
204
  }
205
205
  path += `?${params.toString()}`;
206
206
  }
207
- const data = await this.server.query(path, 'put');
207
+ const data = await this.server.query({ path, method: 'put' });
208
208
  this._invalidateCacheAndLoadData(data.MediaContainer);
209
209
  return this;
210
210
  }
@@ -220,7 +220,7 @@ export class PlayQueue extends PlexObject {
220
220
  queueItem = this.getQueueItem(item);
221
221
  }
222
222
  const path = `/playQueues/${this.playQueueID}/items/${queueItem.playQueueItemID}`;
223
- const data = await this.server.query(path, 'delete');
223
+ const data = await this.server.query({ path, method: 'delete' });
224
224
  this._invalidateCacheAndLoadData(data.MediaContainer);
225
225
  return this;
226
226
  }
@@ -229,7 +229,7 @@ export class PlayQueue extends PlexObject {
229
229
  */
230
230
  async clear() {
231
231
  const path = `/playQueues/${this.playQueueID}/items`;
232
- const data = await this.server.query(path, 'delete');
232
+ const data = await this.server.query({ path, method: 'delete' });
233
233
  this._invalidateCacheAndLoadData(data.MediaContainer);
234
234
  return this;
235
235
  }
@@ -238,7 +238,7 @@ export class PlayQueue extends PlexObject {
238
238
  */
239
239
  async refresh() {
240
240
  const path = `/playQueues/${this.playQueueID}`;
241
- const data = await this.server.query(path, 'get');
241
+ const data = await this.server.query({ path });
242
242
  this._invalidateCacheAndLoadData(data.MediaContainer);
243
243
  }
244
244
  _loadData(data) {
@@ -8,7 +8,7 @@ import type { Playlist } from './playlist.js';
8
8
  import { PlayQueue } from './playqueue.js';
9
9
  import type { CreatePlayQueueOptions } from './playqueue.types.js';
10
10
  import { Agent, SEARCHTYPES } from './search.js';
11
- import type { HistoryResult } from './server.types.js';
11
+ import type { HistoryResult, HistoryOptions } from './server.types.js';
12
12
  import { Settings } from './settings.js';
13
13
  /**
14
14
  * This is the main entry point to interacting with a Plex server. It allows you to
@@ -162,35 +162,37 @@ export declare class PlexServer {
162
162
  * 'Arnold' you’ll get a result for the actor, but also the most recently added
163
163
  * movies he’s in.
164
164
  * @param query Query to use when searching your library.
165
- * @param mediatype Optionally limit your search to the specified media type.
166
- * @param limit Optionally limit to the specified number of results per Hub.
165
+ * @param options Search options.
167
166
  */
168
- search(query: string, mediatype?: keyof typeof SEARCHTYPES, limit?: number): Promise<Hub[]>;
167
+ search(query: string, { mediatype, limit, }?: {
168
+ /** Optionally limit your search to the specified media type. */
169
+ mediatype?: keyof typeof SEARCHTYPES;
170
+ /** Optionally limit to the specified number of results per Hub. */
171
+ limit?: number;
172
+ }): Promise<Hub[]>;
169
173
  /**
170
174
  * Main method used to handle HTTPS requests to the Plex server. This method helps
171
175
  * by encoding the response to utf-8 and parsing the returned XML into and
172
176
  * ElementTree object. Returns None if no data exists in the response.
173
177
  * TODO: use headers
174
- * @param path
175
- * @param method
176
- * @param headers
178
+ * @param options
177
179
  */
178
- query<T = any>(path: string, method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete', options?: {
180
+ query<T = any>({ path, method, headers, body, username, password, }: {
181
+ path: string;
182
+ method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';
179
183
  headers?: Record<string, string>;
180
184
  body?: Uint8Array;
181
- }, username?: string, password?: string): Promise<T>;
185
+ username?: string;
186
+ password?: string;
187
+ }): Promise<T>;
182
188
  /**
183
189
  * Returns a list of media items from watched history. If there are many results, they will
184
190
  * be fetched from the server in batches of X_PLEX_CONTAINER_SIZE amounts. If you're only
185
- * looking for the first <num> results, it would be wise to set the maxresults option to that
191
+ * looking for the first <num> results, it would be wise to set the maxResults option to that
186
192
  * amount so this functions doesn't iterate over all results on the server.
187
- * @param maxresults Only return the specified number of results (optional).
188
- * @param mindate Min datetime to return results from. This really helps speed up the result listing. For example: datetime.now() - timedelta(days=7)
189
- * @param ratingKey request history for a specific ratingKey item.
190
- * @param accountId request history for a specific account ID.
191
- * @param librarySectionId request history for a specific library section ID.
193
+ * @param options Filter and paging options.
192
194
  */
193
- history(maxresults?: number, mindate?: Date, ratingKey?: number | string, accountId?: number | string, librarySectionId?: number | string): Promise<HistoryResult[]>;
195
+ history({ maxResults, minDate, ratingKey, accountId, librarySectionId, }?: HistoryOptions): Promise<HistoryResult[]>;
194
196
  settings(): Promise<Settings>;
195
197
  /**
196
198
  * Creates and returns a new PlayQueue.
@@ -213,15 +215,22 @@ export declare class PlexServer {
213
215
  * Build a URL string with proper token argument. Token will be appended to the URL
214
216
  * if either includeToken is True or TODO: CONFIG.log.show_secrets is 'true'.
215
217
  */
216
- url(key: string, includeToken?: boolean, params?: URLSearchParams): URL;
218
+ url(key: string, { includeToken, params, }?: {
219
+ includeToken?: boolean;
220
+ params?: URLSearchParams;
221
+ }): URL;
217
222
  /**
218
223
  * Build the Plex Web URL for the object.
219
- * @param base The base URL before the fragment (``#!``).
220
- * Default is https://app.plex.tv/desktop.
221
- * @param endpoint The Plex Web URL endpoint.
222
- * None for server, 'playlist' for playlists, 'details' for all other media types.
223
- */
224
- _buildWebURL(base?: string, endpoint?: string, params?: URLSearchParams): string;
224
+ * @param options Options for the URL.
225
+ */
226
+ _buildWebURL({ base, endpoint, params, }?: {
227
+ /** The base URL before the fragment (``#!``). Default is https://app.plex.tv/desktop. */
228
+ base?: string;
229
+ /** The Plex Web URL endpoint. None for server, 'playlist' for playlists, 'details' for all other media types. */
230
+ endpoint?: string;
231
+ /** URL parameters to append. */
232
+ params?: URLSearchParams;
233
+ }): string;
225
234
  _uriRoot(): string;
226
235
  private _headers;
227
236
  private _loadData;