@ctrl/plex 3.1.0 → 3.3.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.
@@ -200,4 +200,46 @@ export declare class Rating extends PlexObject {
200
200
  value: number;
201
201
  protected _loadData(data: any): void;
202
202
  }
203
+ /**
204
+ * Base class for all Art, Poster, and Theme objects.
205
+ */
206
+ declare abstract class BaseResource extends PlexObject {
207
+ /**
208
+ * The source of the resource. 'local' for local files (e.g. theme.mp3),
209
+ */
210
+ provider: string;
211
+ /**
212
+ * Unique key identifying the resource.
213
+ */
214
+ ratingKey: string;
215
+ /**
216
+ * True if the resource is currently selected.
217
+ */
218
+ selected: boolean;
219
+ /**
220
+ * The URL to retrieve the resource thumbnail.
221
+ */
222
+ thumb: string;
223
+ select(): Promise<any>;
224
+ resourceFilepath(): string;
225
+ protected _loadData(data: any): void;
226
+ }
227
+ /**
228
+ * Represents a single Art object.
229
+ */
230
+ export declare class Art extends BaseResource {
231
+ static TAG: string;
232
+ }
233
+ /**
234
+ * Represents a single Poster object.
235
+ */
236
+ export declare class Poster extends BaseResource {
237
+ static TAG: string;
238
+ }
239
+ /**
240
+ * Represents a single Theme object.
241
+ */
242
+ export declare class Theme extends BaseResource {
243
+ static TAG: string;
244
+ }
203
245
  export {};
package/dist/src/media.js CHANGED
@@ -200,3 +200,52 @@ export class Rating extends PlexObject {
200
200
  this.value = data.value;
201
201
  }
202
202
  }
203
+ /**
204
+ * Base class for all Art, Poster, and Theme objects.
205
+ */
206
+ class BaseResource extends PlexObject {
207
+ async select() {
208
+ const key = this.key.slice(0, -1);
209
+ const params = new URLSearchParams();
210
+ params.append('url', this.ratingKey);
211
+ return this.server.query(`${key}?${params.toString()}`, 'put');
212
+ }
213
+ resourceFilepath() {
214
+ if (this.ratingKey.startsWith('media://')) {
215
+ return `Media/localhost/${this.ratingKey.split('://')[1]}`;
216
+ }
217
+ const parent = this.parent?.deref();
218
+ if (this.ratingKey.startsWith('metadata://') && parent) {
219
+ return `${parent.metadataDirectory}/Contents/_combined/${this.ratingKey.split('://')[1]}`;
220
+ }
221
+ if (this.ratingKey.startsWith('upload://') && parent) {
222
+ return `${parent.metadataDirectory}/Uploads/${this.ratingKey.split('://')[1]}`;
223
+ }
224
+ return this.ratingKey;
225
+ }
226
+ _loadData(data) {
227
+ this.key = data.key;
228
+ this.provider = data.provider;
229
+ this.ratingKey = data.ratingKey;
230
+ this.selected = data.selected;
231
+ this.thumb = data.thumb;
232
+ }
233
+ }
234
+ /**
235
+ * Represents a single Art object.
236
+ */
237
+ export class Art extends BaseResource {
238
+ static { this.TAG = 'Art'; }
239
+ }
240
+ /**
241
+ * Represents a single Poster object.
242
+ */
243
+ export class Poster extends BaseResource {
244
+ static { this.TAG = 'Photo'; }
245
+ }
246
+ /**
247
+ * Represents a single Theme object.
248
+ */
249
+ export class Theme extends BaseResource {
250
+ static { this.TAG = 'Theme'; }
251
+ }
@@ -172,7 +172,10 @@ export declare class PlexServer {
172
172
  * @param method
173
173
  * @param headers
174
174
  */
175
- query<T = any>(path: string, method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete', headers?: any, username?: string, password?: string): Promise<T>;
175
+ query<T = any>(path: string, method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete', options?: {
176
+ headers?: Record<string, string>;
177
+ body?: Uint8Array;
178
+ }, username?: string, password?: string): Promise<T>;
176
179
  /**
177
180
  * Returns a list of media items from watched history. If there are many results, they will
178
181
  * be fetched from the server in batches of X_PLEX_CONTAINER_SIZE amounts. If you're only
@@ -99,7 +99,7 @@ export class PlexServer {
99
99
  * @param method
100
100
  * @param headers
101
101
  */
102
- async query(path, method = 'get', headers, username, password) {
102
+ async query(path, method = 'get', options = {}, username, password) {
103
103
  const requestHeaders = this._headers();
104
104
  if (username && password) {
105
105
  const credentials = Buffer.from(`${username}:${password}`).toString('base64');
@@ -113,6 +113,7 @@ export class PlexServer {
113
113
  method,
114
114
  headers: requestHeaders,
115
115
  timeout: this.timeout ?? TIMEOUT,
116
+ body: options.body,
116
117
  retry: 0,
117
118
  responseType: 'json',
118
119
  });
@@ -2,7 +2,7 @@
2
2
  import { URL } from 'url';
3
3
  import { Playable } from './base/playable.js';
4
4
  import { ExtrasData, FullShowData, MovieData, ShowData } from './library.types.js';
5
- import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Producer, Rating, Role, Similar, Writer } from './media.js';
5
+ import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Poster, Producer, Rating, Role, Similar, Writer } from './media.js';
6
6
  import { ChapterSource, EpisodeMetadata, FullMovieResponse } from './video.types.js';
7
7
  export type VideoType = Movie | Show;
8
8
  declare abstract class Video extends Playable {
@@ -24,6 +24,14 @@ declare abstract class Video extends Playable {
24
24
  viewCount?: number;
25
25
  art?: string;
26
26
  grandparentArt?: string;
27
+ /**
28
+ * BlurHash string for artwork image.
29
+ */
30
+ artBlurHash?: string;
31
+ /**
32
+ * BlurHash string for thumbnail image.
33
+ */
34
+ thumbBlurHash?: string;
27
35
  /**
28
36
  * Returns True if this video is watched.
29
37
  */
@@ -44,6 +52,19 @@ declare abstract class Video extends Playable {
44
52
  markUnwatched(): Promise<void>;
45
53
  rate(rate: number): Promise<void>;
46
54
  extras(): Promise<Extra[]>;
55
+ /**
56
+ * Returns list of available Poster objects.
57
+ */
58
+ posters(): Promise<Poster[]>;
59
+ /**
60
+ * Set the poster for a Plex object.
61
+ * @param poster The poster object to select.
62
+ */
63
+ setPoster(poster: Poster): Promise<this>;
64
+ /**
65
+ * I haven't tested this yet. It may not work.
66
+ */
67
+ uploadPoster(url?: string, file?: Uint8Array): Promise<void>;
47
68
  protected _loadData(data: MovieData | ShowData | EpisodeMetadata): void;
48
69
  }
49
70
  /**
package/dist/src/video.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Playable } from './base/playable.js';
2
2
  import { fetchItem, fetchItems, findItems } from './baseFunctionality.js';
3
- import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Producer, Rating, Role, Similar, Writer, } from './media.js';
3
+ import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Poster, Producer, Rating, Role, Similar, Writer, } from './media.js';
4
4
  class Video extends Playable {
5
5
  constructor() {
6
6
  super(...arguments);
@@ -53,6 +53,35 @@ class Video extends Playable {
53
53
  const data = await this.server.query(this._detailsKey);
54
54
  return findItems(data.MediaContainer.Metadata[0].Extras?.Metadata, undefined, Extra, this.server, this);
55
55
  }
56
+ /**
57
+ * Returns list of available Poster objects.
58
+ */
59
+ async posters() {
60
+ return fetchItems(this.server, `/library/metadata/${this.ratingKey}/posters`, undefined, Poster);
61
+ }
62
+ /**
63
+ * Set the poster for a Plex object.
64
+ * @param poster The poster object to select.
65
+ */
66
+ async setPoster(poster) {
67
+ await poster.select();
68
+ return this;
69
+ }
70
+ /**
71
+ * I haven't tested this yet. It may not work.
72
+ */
73
+ async uploadPoster(url, file) {
74
+ if (url) {
75
+ const key = `/library/metadata/${this.ratingKey}/posters?url=${encodeURIComponent(url)}`;
76
+ await this.server.query(key, 'post');
77
+ }
78
+ else if (file) {
79
+ const key = `/library/metadata/${this.ratingKey}/posters`;
80
+ await this.server.query(key, 'post', {
81
+ body: file,
82
+ });
83
+ }
84
+ }
56
85
  _loadData(data) {
57
86
  this.key = data.key;
58
87
  this.ratingKey = data.ratingKey;
@@ -71,6 +100,9 @@ class Video extends Playable {
71
100
  this.titleSort = data.titleSort ?? this.title;
72
101
  this.viewCount = data.viewCount;
73
102
  this.playlistItemID = data.playlistItemID;
103
+ // todo: update one of them with this property
104
+ this.artBlurHash = data.artBlurHash;
105
+ this.thumbBlurHash = data.thumbBlurHash;
74
106
  }
75
107
  }
76
108
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctrl/plex",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "plex api client in typescript",
5
5
  "author": "Scott Cooper <scttcper@gmail.com>",
6
6
  "publishConfig": {